std::vector<Hero::SKYCS> Hero::getConvertibleSkyCS() { if( m_ast.origWcsInfo == AST__NULL) { return { SKYCS::PIXEL }; } AstErrorGuard guard( this); // intercept errors AstGCGuard gcGuard; // go through all known systems and find out which can be used std::vector<SKYCS> res; for( auto & cs : getAllKnownCS()) { if( cs == m_originalSkyCs) { res.push_back( cs); continue; } void * testFrame = astCopy( m_ast.origWcsInfo); std::string str = QString("System=%1").arg( skycs2string(cs)).toStdString(); dbg(1) << "Trying " << str; AstWrappers::set( testFrame, QString("System=%1").arg( skycs2string(cs))); if ( ! astOK ) { clearErrors(); astClearStatus; } else { res.push_back( cs); } } astClearStatus; return res; }
void smf_coords_lut( smfData *data, int tstep, dim_t itime_lo, dim_t itime_hi, AstSkyFrame *abskyfrm, AstMapping *oskymap, int moving, int olbnd[ 2 ], int oubnd[ 2 ], fts2Port fts_port, int *lut, double *angle, double *lon, double *lat, int *status ) { /* Local Variables */ AstCmpMap *bsmap = NULL; /* Tracking -> output grid Mapping */ AstFrame *trfrm = NULL; /* Tracking Frame */ AstFrameSet *fs = NULL; /* Tracking -> output sky FrameSet */ AstMapping *fullmap = NULL; /* Full Mapping from bolo GRID to output map GRID */ AstMapping *offmap = NULL; /* Mapping from absolute to offset sky coords */ AstMapping *tr2skyabs = NULL;/* Tracking -> output sky Mapping */ AstSkyFrame *offsky = NULL; /* Offset sky frame */ JCMTState *state; /* Pointer to telescope info for time slice */ dim_t ibolo; /* Vector index of bolometer */ dim_t idimx; /* Bolometers per row */ dim_t idimy; /* Bolometers per column */ dim_t ilut; /* Index of LUT element */ dim_t itime0; /* Time slice index at next full calculation */ dim_t itime; /* Time slice index */ dim_t nbolo; /* Total number of bolometers */ double *outmapcoord; /* Array holding output map GRID coords */ double *px; /* Pointer to next output map X GRID coord */ double *py; /* Pointer to next output map Y GRID coord */ double *pgx; /* Pointer to next X output grid coords */ double *pgy; /* Pointer to next Y output grid coords */ double *wgx = NULL; /* Work space to hold X output grid coords */ double *wgy = NULL; /* Work space to hold Y output grid coords */ double bsx0; /* Boresight output map GRID X at previous full calc */ double bsx; /* Boresight output map GRID X at current time slice */ double bsxlast; /* Boresight output map GRID X at previous time slice*/ double bsy0; /* Boresight output map GRID Y at previous full calc */ double bsy; /* Boresight output map GRID Y at current time slice */ double bsylast; /* Boresight output map GRID Y at previous time slice*/ double dx; /* Offset in GRID X from previous full calc */ double dxlast; /* Offset in GRID X from previous time slice */ double dy; /* Offset in GRID Y from previous full calc */ double dylast; /* Offset in GRID Y from previous time slice */ double shift[ 2 ]; /* Shift from PIXEL to GRID in output map */ double x; /* Output GRID X at current bolo in current row */ double xin[ 2 ]; /* Input X values */ double xout[ 2 ]; /* Output X values */ double y; /* Output GRID Y at current bolo in current row */ double yin[ 2 ]; /* Input Y values */ double yout[ 2 ]; /* Output Y values */ int lbnd_in[ 2 ]; /* Lower bounds of input array */ int np; /* Number of positions to transform */ int odimx; /* Output map X dimension in pixels */ int odimy; /* Output map Y dimension in pixels */ int ox; /* Output X GRID index (-1) containing current bolo */ int oy; /* Output Y GRID index (-1) containing current bolo */ int ubnd_in[ 2 ]; /* Upper bounds of input array */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Begin an AST context. */ astBegin; /* Get the dimensions of the output map. */ odimx = oubnd[ 0 ] - olbnd[ 0 ] + 1; odimy = oubnd[ 1 ] - olbnd[ 1 ] + 1; /* Get the dimensions of the bolometer array. */ idimx = (data->dims)[ 0 ]; idimy = (data->dims)[ 1 ]; /* Store integer bounds within the input bolometer GRID system. */ lbnd_in[ 0 ] = 1; lbnd_in[ 1 ] = 1; ubnd_in[ 0 ] = idimx; ubnd_in[ 1 ] = idimy; /* Determine the number of bolometers. */ nbolo = idimx*idimy; /* Initialise the index of the next LUT element to write. */ ilut = 0; /* Ensure tstep is at least one. */ if( tstep < 1 ) tstep = 1; /* Get the time slice index at which to do the next full calculation. */ itime0 = itime_lo; /* We only need the following AST objects if we will be approximating some caclulations. */ if( tstep > 1 ) { /* We need to find the first good TCS index in order to get the tracking system (Which for SCUBA-2 won't be changing in the sequence) */ itime0 = VAL__BADI; for (itime = itime_lo; itime <= itime_hi; itime++) { JCMTState * slice = &((data->hdr->allState)[itime]); if (!(slice->jos_drcontrol >= 0 && slice->jos_drcontrol & DRCNTRL__POSITION)) { itime0 = itime; break; } } if ((int)itime0 != VAL__BADI) { /* We need a Frame describing absolute tracking system coords. Take a copy of the supplied skyframe (to inherit obslat, obslon, epoch, etc), and then set its system to the tracking system. */ trfrm = astCopy( abskyfrm ); astSetC( trfrm, "System", sc2ast_convert_system( (data->hdr->allState)[itime0].tcs_tr_sys, status ) ); /* Get the Mapping from the tracking system to the output (absolute, since abskyfrm is absolute) sky system. */ fs = astConvert( trfrm, abskyfrm, " " ); if( !fs && *status == SAI__OK ) { *status = SAI__ERROR; errRep( " ", "smf_coords_lut: Failed to convert from " "tracking system to output WCS system.", status ); } tr2skyabs = astSimplify( astGetMapping( fs, AST__BASE, AST__CURRENT ) ); /* For moving targets, we also need a Frame describing offset sky coordinates in the output map, in which the reference point is the current telescope base position. This will involve changing the SkyRef attribute of the Frame for every time slice, so take a copy of the supplied SkyFrame to avoid changing it. */ if( moving ) { offsky = astCopy( abskyfrm ); astSet( offsky, "SkyRefIs=Origin" ); } /* Create the Mapping from offsets within the output map sky coordinate system output to map GRID coords to output map GRID coords. This uses a ShiftMap to convert from output PIXEL coords (produced by "oskymap") to output GRID coords. Note, if the target is moving, "oskymap" maps from sky *offsets* to output map PIXEL coords. */ shift[ 0 ] = 1.5 - olbnd[ 0 ]; shift[ 1 ] = 1.5 - olbnd[ 1 ]; bsmap = astCmpMap( oskymap, astShiftMap( 2, shift, " " ), 1, " " ); } else { /* We did not find any good TCS data so force us into tstep==1 case and end up with VAL__BADI for every element. */ tstep = 1; itime0 = itime_lo; msgOutiff(MSG__VERB, "", "All time slices from %zu -- %zu had bad TCS data.", status, (size_t)itime_lo, (size_t)itime_hi ); } } /* Allocate memory to hold the (x,y) output map grid coords at each bolo for a single time slice. */ outmapcoord = astMalloc( sizeof( *outmapcoord )*2*nbolo ); /* Initialise boresight position for the benefit of the tstep == 1 case. */ bsx = bsy = 0.0; bsx0 = bsy0 = AST__BAD; bsxlast = bsylast = AST__BAD; /* If lon and lat arrays are to be returned, allocate memory to hold the grid coords of every bolometer for a single sample. */ if( lon && lat ) { wgx = astMalloc( nbolo*sizeof( *wgx ) ); wgy = astMalloc( nbolo*sizeof( *wgy ) ); } /* Loop round each time slice. */ state = data->hdr->allState + itime_lo; for( itime = itime_lo; itime <= itime_hi && *status == SAI__OK; itime++,state++ ) { /* No need to get the boresight position if we are doing full calculations at every time slice. If this time slice has bad TCS data then the problem will be caught in smf_rebin_totmap. */ if( tstep > 1 ) { /* Transform the current boresight and base (if moving) positions from tracking coords to absolute output map sky coords. */ xin[ 0 ] = state->tcs_tr_ac1; yin[ 0 ] = state->tcs_tr_ac2; if( moving ) { xin[ 1 ] = state->tcs_tr_bc1; yin[ 1 ] = state->tcs_tr_bc2; np = 2; } else { np = 1; } astTran2( tr2skyabs, np, xin, yin, 1, xout, yout ); /* If the target is moving, find the offsets within the output map sky coordinate system, from base to boresight at the current time slice. These offsets become the new "boresight" position (in xin/yin). Guard against assigning bad values to the ref pos, which can cause AST to report errors. */ if( moving && xout[ 1 ] != AST__BAD && yout[ 1 ] != AST__BAD ) { /* Set the current telescope base position as the reference point in "offsky". Then get the Mapping from absolute to offset sky coords. */ astSetD( offsky, "SkyRef(1)", xout[ 1 ] ); astSetD( offsky, "SkyRef(2)", yout[ 1 ] ); offmap = astSkyOffsetMap( offsky ); /* Use this Mapping to convert the current boresight position from absolute sky coords to offsets from the current base position. */ astTran2( offmap, 1, xout, yout, 1, xin, yin ); /* Annul the Mapping to avoid keep the number of AST objects to a minimum. */ offmap = astAnnul( offmap ); /* If the target is stationary, we can just use the absolute boresight position as it is. */ } else { xin[ 0 ] = xout[ 0 ]; yin[ 0 ] = yout[ 0 ]; } /* Transform the above boresight position from output map sky coords to output map GRID coords. */ astTran2( bsmap, 1, xin, yin, 1, &bsx, &bsy ); } /* If we have reached the next full calculation... */ if( itime == itime0 ) { /* Calculate the full bolometer to map-pixel transformation for the current time slice */ fullmap = smf_rebin_totmap( data, itime, abskyfrm, oskymap, moving, fts_port, status ); /* If succesful, use it to transform every bolometer position from bolo GRID coords to output map GRID coords. */ if( fullmap ) { itime0 += tstep; astTranGrid( fullmap, 2, lbnd_in, ubnd_in, 0.1, 1000000, 1, 2, nbolo, outmapcoord ); fullmap = astAnnul( fullmap ); /* Record the boresight grid coords at this time slice. */ bsx0 = bsx; bsy0 = bsy; /* If we cannot determine a full Mapping for this time slice, move the node to the next time slice (otherwise we would loose all the data to the next node), and set the boresight position bad to indicate we have no mapping for this time slice. */ } else { itime0++; bsx0 = AST__BAD; bsy0 = AST__BAD; } } /* Get the offset from the boresight position at the previous full calculation and the current boresight position, in output map GRID coords. */ dx = ( bsx != AST__BAD && bsx0 != AST__BAD ) ? bsx - bsx0 : AST__BAD; dy = ( bsy != AST__BAD && bsy0 != AST__BAD ) ? bsy - bsy0 : AST__BAD; /* Work out the scan direction based on the GRID offsets between this and the previous time slice. Angles are calculated using atan2, with values ranging from -pi to +pi. */ if( angle ) { double theta = AST__BAD; dxlast = ( bsx != AST__BAD && bsxlast != AST__BAD ) ? bsx - bsxlast : AST__BAD; dylast = ( bsy != AST__BAD && bsylast != AST__BAD ) ? bsy - bsylast : AST__BAD; if( dxlast != AST__BAD && dylast != AST__BAD && !( !dxlast && !dylast ) ) { theta = atan2( dylast, dxlast ); } angle[itime-itime_lo] = theta; bsxlast = bsx; bsylast = bsy; } /* Initialise pointers to the place to store the final grid coords for each bolometer. */ pgx = wgx; pgy = wgy; /* Loop round all bolometers. */ px = outmapcoord; py = outmapcoord + nbolo; for( ibolo = 0; ibolo < nbolo; ibolo++ ){ /* If good, get the x and y output map GRID coords for this bolometer. */ if( dx != AST__BAD && dy != AST__BAD ) { x = *(px++) + dx; y = *(py++) + dy; /* If required, store them so that we can convert them into lon/lat values later. */ if( pgx && pgy ) { *(pgx++) = x; *(pgy++) = y; } /* Find the grid indices (minus one) of the output map pixel containing the mapped bolo grid coords. One is subtracted in order to simplify the subsequent calculation of the vector index. */ ox = (int) ( x - 0.5 ); oy = (int) ( y - 0.5 ); /* Check it is within the output map */ if( ox >= 0 && ox < odimx && oy >= 0 && oy < odimy ) { /* Find the 1-dimensional vector index into the output array for this pixel and store in the next element of the returned LUT. */ lut[ ilut++ ] = ox + oy*odimx; /* Store a bad value for points that are off the edge of the output map. */ } else { lut[ ilut++ ] = VAL__BADI; } /* If good, store a bad index in the LUT and move on to the next pixel. */ } else { lut[ ilut++ ] = VAL__BADI; px++; py++; if( pgx && pgy ) { *(pgx++) = AST__BAD; *(pgy++) = AST__BAD; } } } /* If required transform the grid coords into (lon,lat) coords and store in the relevant elements of the supplied "lon" and "lat" arrays. */ if( wgx && wgy ) { astTran2( oskymap, nbolo, wgx, wgy, 0, lon + itime*nbolo, lat + itime*nbolo ); /* Convert from rads to degs. */ pgx = lon + itime*nbolo; pgy = lat + itime*nbolo; for( ibolo = 0; ibolo < nbolo; ibolo++ ) { if( *pgx != AST__BAD && *pgy != AST__BAD ) { *(pgx++) *= AST__DR2D; *(pgy++) *= AST__DR2D; } else { pgx++; pgy++; } } } } /* To obtain a reasonable value for the first entry of angle, we simply duplicate the second value. */ if( angle && (itime_hi > itime_lo) ) { angle[0] = angle[1]; } /* Free remaining work space. */ outmapcoord = astFree( outmapcoord ); wgx = astFree( wgx ); wgy = astFree( wgy ); /* Export the WCS pointer in the data header since it will be annulled at a higher level. Note the FrameSet pointer may be null if the last full calculation was for a slice with bad telescope data. */ if( data->hdr->wcs ) astExport( data->hdr->wcs ); /* End the AST context. */ astEnd; }
static void smf1_correct_extinction( void *job_data_ptr, int *status ) { /* * Name: * smf1_correct_extinction * Purpose: * Executed in a worker thread to do various calculations for * smf_correct_extinction. * Invocation: * smf1_correct_extinction( void *job_data_ptr, int *status ) * Arguments: * job_data_ptr = SmfCorrectExtinctionData * (Given) * Data structure describing the job to be performed by the worker * thread. * status = int * (Given and Returned) * Inherited status. */ /* Local Variables: */ SmfCorrectExtinctionData *pdata; double airmass; /* Airmass */ double state_airmass; /* Airmass read from header */ double state_az_ac2; /* Elevation read from header */ double amprev; /* Previous airmass in loop */ double *azel = NULL; /* AZEL coordinates */ size_t base; /* Offset into 3d data array */ double extcorr = 1.0; /* Extinction correction factor */ dim_t i; /* Loop counter */ dim_t k; /* Loop counter */ AstFrameSet *wcs = NULL; /* Pointer to AST WCS frameset */ AstFrameSet *state_wcs = NULL; /* Pointer to copy of frameset from header */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Get a pointer that can be used for accessing the required items in the supplied structure. */ pdata = (SmfCorrectExtinctionData *) job_data_ptr; /* It is more efficient to call astTranGrid than astTran2 Allocate memory in adaptive mode just in case. */ if (pdata->method == SMF__EXTMETH_FULL || pdata->method == SMF__EXTMETH_ADAPT ) { azel = astMalloc( (2*pdata->npts)*sizeof(*azel) ); } amprev = pdata->amfirst; /* Assume we are using quick mode for all time slices. */ pdata->allquick = 1; for ( k=pdata->f1; k<=pdata->f2 && (*status == SAI__OK) ; k++) { /* Flags to indicate which mode we are using for this time slice */ int quick = 0; /* use single airmass */ int adaptive = 0; /* switch from quick to full if required */ if (pdata->method == SMF__EXTMETH_SINGLE) { quick = 1; } else if (pdata->method == SMF__EXTMETH_ADAPT) { quick = 1; adaptive = 1; } else { pdata->allquick = 0; } /* Call tslice_ast to update the header for the particular timeslice. If we're in QUICK mode then we don't need the WCS. Use a mutex to prevent multiple threads writing to the header at the same time. */ thrMutexLock( &data_mutex, status ); smf_lock_data( pdata->data, 1, status ); smf_tslice_ast( pdata->data, k, !quick, NO_FTS, status ); /* Copy the required bit of the header into thread-local storage. */ if( *status == SAI__OK ) { state_airmass = pdata->hdr->state->tcs_airmass; state_az_ac2 = pdata->hdr->state->tcs_az_ac2; if( !quick && pdata->tau != VAL__BADD && pdata->hdr->wcs) state_wcs = astCopy( pdata->hdr->wcs ); } /* Unlock the AST Objects in the smfData then unlock the local mutex. */ smf_lock_data( pdata->data, 0, status ); thrMutexUnlock( &data_mutex, status ); /* Abort if an error has occurred. */ if( *status != SAI__OK ) break; /* Read the WVM tau value if required */ if (pdata->tausrc == SMF__TAUSRC_WVMRAW) { pdata->tau = pdata->wvmtau[k]; } /* in all modes we need to keep track of the previous airmass in case we need to gap fill bad telescope data */ if ( quick && pdata->ndims == 2 ) { /* for 2-D we use the FITS header directly */ /* This may change depending on exact FITS keyword */ airmass = pdata->amstart; /* speed is not an issue for a 2d image */ adaptive = 0; } else { /* Else use airmass value in state structure */ airmass = state_airmass; /* if things have gone bad use the previous value else store this value. We also need to switch to quick mode and disable adaptive. */ if (airmass == VAL__BADD || airmass == 0.0 ) { if ( state_az_ac2 != VAL__BADD ) { /* try the elevation */ airmass = palAirmas( M_PI_2 - state_az_ac2 ); } else { airmass = amprev; quick = 1; adaptive = 0; } } else { amprev = airmass; } } /* If we're using the FAST application method, we assume a single airmass and tau for the whole array but we have to consider adaptive mode. If the tau is bad the extinction correction must also be bad. */ if( pdata->tau == VAL__BADD) { extcorr = VAL__BADD; } else if (quick) { /* we have an airmass, see if we need to provide per-pixel correction */ if (adaptive) { if (is_large_delta_atau( airmass, pdata->hdr->state->tcs_az_ac2, pdata->tau, status) ) { /* we need WCS if we disable fast mode */ quick = 0; pdata->allquick = 0; thrMutexLock( &data_mutex, status ); smf_lock_data( pdata->data, 1, status ); smf_tslice_ast( pdata->data, k, 1, NO_FTS, status ); state_airmass = pdata->hdr->state->tcs_airmass; state_az_ac2 = pdata->hdr->state->tcs_az_ac2; if (pdata->hdr->wcs) state_wcs = astCopy( pdata->hdr->wcs ); smf_lock_data( pdata->data, 0, status ); thrMutexUnlock( &data_mutex, status ); } } if (quick) extcorr = exp(airmass*pdata->tau); } /* The previous test may have forced quick off so we can not combine the tests in one if-then-else block */ if (!quick && pdata->tau != VAL__BADD ) { /* Not using quick so retrieve WCS to obtain elevation info */ wcs = state_wcs; /* Check current frame, store it and then select the AZEL coordinate system */ if (wcs != NULL) { if (strcmp(astGetC(wcs,"SYSTEM"), "AZEL") != 0) { astSet( wcs, "SYSTEM=AZEL" ); } /* Transfrom from pixels to AZEL */ astTranGrid( wcs, 2, pdata->lbnd, pdata->ubnd, 0.1, 1000000, 1, 2, pdata->npts, azel ); } else { /* this time slice may have bad telescope data so we trap for this and re-enable "quick" with a default value. We'll only get here if airmass was good but SMU was bad so we use the good airmass. The map-maker won't be using this data but we need to use something plausible so that we do not throw off the FFTs */ quick = 1; extcorr = exp(airmass*pdata->tau); } } /* Loop over data in time slice. Start counting at 1 since this is the GRID coordinate frame */ base = pdata->npts * k; /* Offset into 3d data array (time-ordered) */ for (i=0; i < pdata->npts && ( *status == SAI__OK ); i++ ) { /* calculate array indices - assumes that astTranGrid fills up azel[] array in same order as bolometer data are aligned */ size_t index; if ( pdata->isTordered ) { index = base + i; } else { index = k + (pdata->nframes * i); } if (!quick) { if (pdata->tau != VAL__BADD) { double zd; zd = M_PI_2 - azel[pdata->npts+i]; airmass = palAirmas( zd ); extcorr = exp(airmass*pdata->tau); } else { extcorr = VAL__BADD; } } if( pdata->allextcorr ) { /* Store extinction correction factor */ pdata->allextcorr[index] = extcorr; } else { /* Otherwise Correct the data */ if (extcorr != VAL__BADD) { if( pdata->indata && (pdata->indata[index] != VAL__BADD) ) { pdata->indata[index] *= extcorr; } /* Correct the variance */ if( pdata->vardata && (pdata->vardata[index] != VAL__BADD) ) { pdata->vardata[index] *= extcorr * extcorr; } } else { if (pdata->indata) pdata->indata[index] = VAL__BADD; if (pdata->vardata) pdata->vardata[index] = VAL__BADD; } } } /* Note that we do not need to free "wcs" or revert its SYSTEM since smf_tslice_ast will replace the object immediately. */ } /* End loop over timeslice */ azel = astFree( azel ); }
void smf_open_ndfname( const HDSLoc *loc, const char accmode[], const char extname[], const char state[], const char dattype[], const int ndims, const int lbnd[], const int ubnd[], const char datalabel[], const char dataunits[], const AstFrameSet* wcs, smfData **ndfdata, int *status) { /* Local variables */ void *datarr[] = { NULL, NULL }; /* Pointers for data */ int dims[NDF__MXDIM]; /* Extent of each dimension */ smf_dtype dtype; /* Data type */ int flags = 0; /* Flags for creating smfDA, smfFile and smfHead components in the output smfData */ int i; int ndat; /* Number of elements mapped in the requested NDF */ char ndfaccmode[NDF__SZMMD+1];/* Access mode to use to open the file */ int ndimsmapped; /* Number of dimensions in mapped NDF */ int ndfid; /* NDF identifier */ AstFrameSet *ndfwcs = NULL; /* Copy of input FrameSet to write to NDF */ smfFile *newfile = NULL; /* New smfFile with details of requested NDF */ int place; /* Placeholder for NDF */ int updating = 0; /* True if the extension is being updated */ /* Initialize the output smfData to NULL pointer */ *ndfdata = NULL; if ( *status != SAI__OK ) return; /* Check to see if the HDS Locator is null and retrieve the NDF id */ if ( loc == NULL ) { errRep( FUNC_NAME, "Given HDS locator is NULL", status ); return; } /* Start be assuming the requested access mode can be used for mapping and file opening */ one_strlcpy( ndfaccmode, accmode, sizeof(ndfaccmode), status ); /* Note: write access clears the contents of the NDF */ if ( strncmp( accmode, "WRITE", 5 ) == 0 ) { msgOutif(MSG__DEBUG," ", "Opening NDF with WRITE access: this will clear the current contents if the NDF exists.", status); updating = 1; /* We can have WRITE/ZERO or WRITE/BAD so we need to force WRITE into the NDF open access mode */ one_strlcpy( ndfaccmode, "WRITE", sizeof(ndfaccmode), status ); } else if ( strncmp( accmode, "UPDATE", 6) == 0) { updating = 1; } ndfOpen( loc, extname, ndfaccmode, state, &ndfid, &place, status ); if ( *status != SAI__OK ) { errRep( FUNC_NAME, "Call to ndfOpen failed: unable to obtain an NDF identifier", status ); return; } /* No placeholder => NDF exists */ if ( place != NDF__NOPL ) { /* Define properties of NDF */ ndfNew( dattype, ndims, lbnd, ubnd, &place, &ndfid, status ); if ( *status != SAI__OK ) { errRep( FUNC_NAME, "Unable to create a new NDF", status ); return; } } /* Convert the data type string to SMURF dtype */ dtype = smf_dtype_fromstring( dattype, status ); /* First step is to create an empty smfData with no extra components */ flags |= SMF__NOCREATE_DA; flags |= SMF__NOCREATE_FTS; flags |= SMF__NOCREATE_HEAD; flags |= SMF__NOCREATE_FILE; *ndfdata = smf_create_smfData( flags, status); /* Set the requested data type */ (*ndfdata)->dtype = dtype; /* OK, now map the data array */ ndfMap( ndfid, "DATA", dattype, accmode, &datarr[0], &ndat, status ); if ( *status != SAI__OK ) { errRep( FUNC_NAME, "Unable to map data array: invalid NDF identifier?", status ); } /* Retrieve dimensions of mapped array */ ndfDim( ndfid, NDF__MXDIM, dims, &ndimsmapped, status ); if ( *status != SAI__OK ) { errRep( FUNC_NAME, "Problem identifying dimensions of requested NDF", status ); } /* Consistency check */ if ( ndimsmapped != ndims ) { if ( *status == SAI__OK ) { *status = SAI__ERROR; errRep( FUNC_NAME, "Number of dimensions in new NDF not equal to number of dimensions specified", status ); } } if (*status == SAI__OK) { for (i=0; i<ndims; i++) { ((*ndfdata)->dims)[i] = dims[i]; ((*ndfdata)->lbnd)[i] = lbnd[i]; } } /* Allow for label, units and WCS to be written */ if (updating) { if (datalabel) ndfCput( datalabel, ndfid, "Label", status ); if (dataunits) ndfCput( dataunits, ndfid, "Unit", status ); if (wcs) { /* Take a copy of the input WCS and modify if necessary that before writing to the NDF */ ndfwcs = astCopy( wcs ); smf_set_moving( (AstFrame *) ndfwcs, NULL, status ); ndfPtwcs( ndfwcs, ndfid, status ); if (ndfwcs) ndfwcs = astAnnul( ndfwcs ); } } /* Create the smfFile */ newfile = smf_construct_smfFile( newfile, ndfid, 0, 0, NULL, status ); if ( *status != SAI__OK ) { errRep( FUNC_NAME, "Unable to construct new smfFile", status ); } /* And populate the new smfData */ *ndfdata = smf_construct_smfData( *ndfdata, newfile, NULL, NULL, NULL, dtype, datarr, NULL, SMF__QFAM_NULL, NULL, 0, 1, (*ndfdata)->dims, (*ndfdata)->lbnd, ndims, 0, 0, NULL, NULL, status ); }
void smf_mapbounds( int fast, Grp *igrp, int size, const char *system, AstFrameSet *spacerefwcs, int alignsys, int *lbnd_out, int *ubnd_out, AstFrameSet **outframeset, int *moving, smfBox ** boxes, fts2Port fts_port, int *status ) { /* Local Variables */ AstSkyFrame *abskyframe = NULL; /* Output Absolute SkyFrame */ int actval; /* Number of parameter values supplied */ AstMapping *bolo2map = NULL; /* Combined mapping bolo->map coordinates, WCS->GRID Mapping from input WCS FrameSet */ smfBox *box = NULL; /* smfBox for current file */ smfData *data = NULL; /* pointer to SCUBA2 data struct */ double dlbnd[ 2 ]; /* Floating point lower bounds for output map */ drcntrl_bits drcntrl_mask = 0;/* Mask to use for DRCONTROL on this instrument */ double dubnd[ 2 ]; /* Floating point upper bounds for output map */ AstMapping *fast_map = NULL; /* Mapping from tracking to absolute map coords */ smfFile *file = NULL; /* SCUBA2 data file information */ int first; /* Is this the first good subscan ? */ AstFitsChan *fitschan = NULL;/* Fits channels to construct WCS header */ AstFrameSet *fs = NULL; /* A general purpose FrameSet pointer */ smfHead *hdr = NULL; /* Pointer to data header this time slice */ int i; /* Loop counter */ dim_t j; /* Loop counter */ AstSkyFrame *junksky = NULL; /* Unused SkyFrame argument */ dim_t k; /* Loop counter */ int lbnd0[ 2 ]; /* Defaults for LBND parameter */ double map_pa=0; /* Map PA in output coord system (rads) */ dim_t maxloop; /* Number of times to go round the time slice loop */ dim_t nbadt = 0; /* Number of bad time slices */ dim_t ngoodt = 0; /* Number of good time slices */ double par[7]; /* Projection parameters */ double shift[ 2 ]; /* Shifts from PIXEL to GRID coords */ AstMapping *oskymap = NULL; /* Mapping celestial->map coordinates, Sky <> PIXEL mapping in output FrameSet */ AstSkyFrame *oskyframe = NULL;/* Output SkyFrame */ char *refsys = NULL; /* Sky system from supplied reference FrameSet */ dim_t textreme[4]; /* Time index corresponding to minmax TCS posn */ AstFrame *skyin = NULL; /* Sky Frame in input FrameSet */ double skyref[ 2 ]; /* Values for output SkyFrame SkyRef attribute */ struct timeval tv1; /* Timer */ struct timeval tv2; /* Timer */ AstMapping *tmap; /* Temporary Mapping */ int trim; /* Trim borders of bad pixels from o/p image? */ int ubnd0[ 2 ]; /* Defaults for UBND parameter */ double x_array_corners[4]; /* X-Indices for corner bolos in array */ double x_map[4]; /* Projected X-coordinates of corner bolos */ double y_array_corners[4]; /* Y-Indices for corner pixels in array */ double y_map[4]; /* Projected X-coordinates of corner bolos */ /* Main routine */ if (*status != SAI__OK) return; /* Start a timer to see how long this takes */ smf_timerinit( &tv1, &tv2, status ); /* Initialize pointer to output FrameSet and moving-source flag */ *outframeset = NULL; *moving = 0; /* initialize double precision output bounds and the proj pars */ for( i = 0; i < 7; i++ ) par[ i ] = AST__BAD; dlbnd[ 0 ] = VAL__MAXD; dlbnd[ 1 ] = VAL__MAXD; dubnd[ 0 ] = VAL__MIND; dubnd[ 1 ] = VAL__MIND; /* If we have a supplied reference WCS we can use that directly without having to calculate it from the data. Replace the requested system with the system from the reference FrameSet (take a copy of the string since astGetC may re-use its buffer). */ if (spacerefwcs) { oskyframe = astGetFrame( spacerefwcs, AST__CURRENT ); int nc = 0; refsys = astAppendString( NULL, &nc, astGetC( oskyframe, "System" ) ); system = refsys; } /* Create array of returned smfBox structures and store a pointer to the next one to be initialised. */ *boxes = astMalloc( sizeof( smfBox ) * size ); box = *boxes; astBegin; /* Loop over all files in the Grp */ first = 1; for( i=1; i<=size; i++, box++ ) { /* Initialise the spatial bounds of section of the the output cube that is contributed to by the current ionput file. */ box->lbnd[ 0 ] = VAL__MAXD; box->lbnd[ 1 ] = VAL__MAXD; box->ubnd[ 0 ] = VAL__MIND; box->ubnd[ 1 ] = VAL__MIND; /* Read data from the ith input file in the group */ smf_open_file( NULL, igrp, i, "READ", SMF__NOCREATE_DATA, &data, status ); if (*status != SAI__OK) { msgSeti( "I", i ); errRep( "smf_mapbounds", "Could not open data file no ^I.", status ); break; } else { if( *status == SAI__OK ) { 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; } else if( data->hdr->fitshdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No FITS header associated with smfHead.", status ); break; } } } /* convenience pointers */ file = data->file; hdr = data->hdr; /* report name of the input file */ smf_smfFile_msg( file, "FILE", 1, "<unknown>" ); msgSeti("I", i); msgSeti("N", size); msgOutif(MSG__VERB, " ", "SMF_MAPBOUNDS: Processing ^I/^N ^FILE", status); /* Check that there are 3 pixel axes. */ if( data->ndims != 3 ) { smf_smfFile_msg( file, "FILE", 1, "<unknown>" ); msgSeti( "NDIMS", data->ndims ); *status = SAI__ERROR; errRep( FUNC_NAME, "^FILE has ^NDIMS pixel axes, should be 3.", status ); break; } /* Check that the data dimensions are 3 (for time ordered data) */ if( *status == SAI__OK ) { /* If OK Decide which detectors (GRID coord) to use for checking bounds, depending on the instrument in use. */ switch( hdr->instrument ) { case INST__SCUBA2: drcntrl_mask = DRCNTRL__POSITION; /* 4 corner bolometers of the subarray */ x_array_corners[0] = 1; x_array_corners[1] = 1; x_array_corners[2] = (data->dims)[0]; x_array_corners[3] = (data->dims)[0]; y_array_corners[0] = 1; y_array_corners[1] = (data->dims)[1]; y_array_corners[2] = 1; y_array_corners[3] = (data->dims)[1]; break; case INST__AZTEC: /* Rough guess for extreme bolometers around the edge */ x_array_corners[0] = 22; x_array_corners[1] = 65; x_array_corners[2] = 73; x_array_corners[3] = 98; y_array_corners[0] = 1; /* Always 1 for AzTEC */ y_array_corners[1] = 1; y_array_corners[2] = 1; y_array_corners[3] = 1; break; case INST__ACSIS: smf_find_acsis_corners( data, x_array_corners, y_array_corners, status); break; default: *status = SAI__ERROR; errRep(FUNC_NAME, "Don't know how to calculate mapbounds for data created with this instrument", status); } } if( *status == SAI__OK) { size_t goodidx = SMF__BADSZT; /* Need to build up a frameset based on good telescope position. We can not assume that we the first step will be a good TCS position so we look for one. If we can not find anything we skip to the next file. */ maxloop = (data->dims)[2]; for (j=0; j<maxloop; j++) { JCMTState state = (hdr->allState)[j]; if (state.jos_drcontrol >= 0 && state.jos_drcontrol & drcntrl_mask ) { /* bad TCS - so try again */ } else { /* Good tcs */ goodidx = j; break; } } if (goodidx == SMF__BADSZT) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>"); msgOutif( MSG__QUIET, "", "No good telescope positions found in file ^FILE. Ignoring", status ); smf_close_file( NULL, &data, status ); continue; } /* If we are dealing with the first good file, create the output SkyFrame. */ if( first ) { first = 0; /* Create output SkyFrame if it has not come from a reference */ if ( oskyframe == NULL ) { /* smf_tslice_ast only needs to get called once to set up framesets */ if( hdr->wcs == NULL ) { smf_tslice_ast( data, goodidx, 1, fts_port, status); } /* Retrieve input SkyFrame */ skyin = astGetFrame( hdr->wcs, AST__CURRENT ); smf_calc_skyframe( skyin, system, hdr, alignsys, &oskyframe, skyref, moving, status ); /* Get the orientation of the map vertical within the output celestial coordinate system. This is derived form the MAP_PA FITS header, which gives the orientation of the map vertical within the tracking system. */ map_pa = smf_calc_mappa( hdr, system, skyin, status ); /* Provide a sensible default for the pixel size based on wavelength */ par[4] = smf_calc_telres( hdr->fitshdr, status ); par[4] *= AST__DD2R/3600.0; par[5] = par[4]; /* Calculate the projection parameters. We do not enable autogrid determination for SCUBA-2 so we do not need to obtain all the data before calculating projection parameters. */ smf_get_projpar( oskyframe, skyref, *moving, 0, 0, NULL, 0, map_pa, par, NULL, NULL, status ); if (skyin) skyin = astAnnul( skyin ); /* If the output skyframe has been supplied, we still need to determine whether the source is moving or not, and set the reference position. */ } else { /* smf_tslice_ast only needs to get called once to set up framesets */ if( hdr->wcs == NULL ) { smf_tslice_ast( data, goodidx, 1, fts_port, status); } /* Retrieve input SkyFrame */ skyin = astGetFrame( hdr->wcs, AST__CURRENT ); smf_calc_skyframe( skyin, system, hdr, alignsys, &junksky, skyref, moving, status ); /* Store the sky reference position. If the target is moving, ensure the returned SkyFrame represents offsets from the reference position rather than absolute coords. */ astSetD( oskyframe, "SkyRef(1)", skyref[ 0 ] ); astSetD( oskyframe, "SkyRef(2)", skyref[ 1 ] ); if( *moving ) astSet( oskyframe, "SkyRefIs=Origin" ); /* Ensure the Epoch attribute in the map is set to the date of the first data in the map, rather than the date in supplied reference WCS. */ astSetD( oskyframe, "Epoch", astGetD( junksky, "Epoch" ) ); } if ( *outframeset == NULL && oskyframe != NULL && (*status == SAI__OK)){ /* Now created a spatial Mapping. Use the supplied reference frameset if supplied */ if (spacerefwcs) { oskymap = astGetMapping( spacerefwcs, AST__BASE, AST__CURRENT ); } else { /* Now populate a FitsChan with FITS-WCS headers describing the required tan plane projection. The longitude and latitude axis types are set to either (RA,Dec) or (AZ,EL) to get the correct handedness. */ fitschan = astFitsChan ( NULL, NULL, " " ); smf_makefitschan( astGetC( oskyframe, "System"), &(par[0]), &(par[2]), &(par[4]), par[6], fitschan, status ); astClear( fitschan, "Card" ); fs = astRead( fitschan ); /* Extract the output PIXEL->SKY Mapping. */ oskymap = astGetMapping( fs, AST__BASE, AST__CURRENT ); /* Tidy up */ fs = astAnnul( fs ); } /* Create the output FrameSet */ *outframeset = astFrameSet( astFrame(2, "Domain=GRID"), " " ); /* Now add the SkyFrame to it */ astAddFrame( *outframeset, AST__BASE, oskymap, oskyframe ); /* Now add a POLANAL Frame if required (i.e. if the input time series are POL-2 Q/U values). */ smf_addpolanal( *outframeset, hdr, status ); /* Invert the oskymap mapping */ astInvert( oskymap ); } /* End WCS FrameSet construction */ } /* Get a copy of the output SkyFrame and ensure it represents absolute coords rather than offset coords. */ abskyframe = astCopy( oskyframe ); astClear( abskyframe, "SkyRefIs" ); astClear( abskyframe, "AlignOffset" ); maxloop = (data->dims)[2]; if (fast) { /* For scan map we scan through looking for largest telescope moves. For dream/stare we just look at the start and end time slices to account for sky rotation. */ if (hdr->obsmode != SMF__OBS_SCAN) { textreme[0] = 0; textreme[1] = (data->dims)[2] - 1; maxloop = 2; } else { const char *tracksys; double *ac1list, *ac2list, *bc1list, *bc2list, *p1, *p2, *p3, *p4; double flbnd[4], fubnd[4]; JCMTState state; /* If the output and tracking systems are different, get a Mapping between them. */ tracksys = sc2ast_convert_system( (hdr->allState)[goodidx].tcs_tr_sys, status ); if( strcmp( system, tracksys ) ) { AstSkyFrame *tempsf = astCopy( abskyframe ); astSetC( tempsf, "System", tracksys ); AstFrameSet *tempfs = astConvert( tempsf, abskyframe, "" ); tmap = astGetMapping( tempfs, AST__BASE, AST__CURRENT ); fast_map = astSimplify( tmap ); tmap = astAnnul( tmap ); tempsf = astAnnul( tempsf ); tempfs = astAnnul( tempfs ); } else { fast_map = NULL; } /* Copy all ac1/2 positions into two array, and transform them from tracking to absolute output sky coords. */ ac1list = astMalloc( maxloop*sizeof( *ac1list ) ); ac2list = astMalloc( maxloop*sizeof( *ac2list ) ); if( *status == SAI__OK ) { p1 = ac1list; p2 = ac2list; for( j = 0; j < maxloop; j++ ) { state = (hdr->allState)[ j ]; *(p1++) = state.tcs_tr_ac1; *(p2++) = state.tcs_tr_ac2; } if( fast_map ) astTran2( fast_map, maxloop, ac1list, ac2list, 1, ac1list, ac2list ); } /* If the target is moving, we need to adjust these ac1/2 values to represent offsets from the base position. */ if( *moving ) { /* Copy all bc1/2 positions into two arrays. */ bc1list = astMalloc( maxloop*sizeof( *bc1list ) ); bc2list = astMalloc( maxloop*sizeof( *bc2list ) ); if( *status == SAI__OK ) { p1 = bc1list; p2 = bc2list; for( j = 0; j < maxloop; j++ ) { state = (hdr->allState)[ j ]; *(p1++) = state.tcs_tr_bc1; *(p2++) = state.tcs_tr_bc2; } /* Transform them from tracking to absolute output sky coords. */ if( fast_map ) astTran2( fast_map, maxloop, bc1list, bc2list, 1, bc1list, bc2list ); /* Replace each ac1/2 position with the offsets from the corresponding base position. */ p1 = bc1list; p2 = bc2list; p3 = ac1list; p4 = ac2list; for( j = 0; j < maxloop; j++ ) { smf_offsets( *(p1++), *(p2++), p3++, p4++, status ); } } /* We no longer need the base positions. */ bc1list = astFree( bc1list ); bc2list = astFree( bc2list ); } /* Transform the ac1/2 position from output sky coords to output pixel coords. */ astTran2( oskymap, maxloop, ac1list, ac2list, 1, ac1list, ac2list ); /* Find the bounding box containing these pixel coords and the time slices at which the boresight touches each edge of this box. */ flbnd[ 0 ] = VAL__MAXD; flbnd[ 1 ] = VAL__MAXD; fubnd[ 0 ] = VAL__MIND; fubnd[ 1 ] = VAL__MIND; for( j = 0; j < 4; j++ ) textreme[ j ] = (dim_t) VAL__BADI; if( *status == SAI__OK ) { p1 = ac1list; p2 = ac2list; for( j = 0; j < maxloop; j++,p1++,p2++ ) { if( *p1 != VAL__BADD && *p2 != VAL__BADD ){ if ( *p1 < flbnd[0] ) { flbnd[0] = *p1; textreme[0] = j; } if ( *p2 < flbnd[1] ) { flbnd[1] = *p2; textreme[1] = j; } if ( *p1 > fubnd[0] ) { fubnd[0] = *p1; textreme[2] = j; } if ( *p2 > fubnd[1] ) { fubnd[1] = *p2; textreme[3] = j; } } } } maxloop = 4; msgSetd("X1", textreme[0]); msgSetd("X2", textreme[1]); msgSetd("X3", textreme[2]); msgSetd("X4", textreme[3]); msgOutif( MSG__DEBUG, " ", "Extrema time slices are ^X1, ^X2, ^X3 and ^X4", status); ac1list = astFree( ac1list ); ac2list = astFree( ac2list ); } } /* Get the astrometry for all the relevant time slices in this data file */ for( j=0; j<maxloop; j++ ) { dim_t ts; /* Actual time slice to use */ /* if we are doing the fast loop, we need to read the time slice index from textreme. Else we just use the index */ if (fast) { /* get the index but make sure it is good */ ts = textreme[j]; if (ts == (dim_t)VAL__BADI) continue; } else { ts = j; } /* Calculate the bolo to map-pixel transformation for this tslice */ bolo2map = smf_rebin_totmap( data, ts, abskyframe, oskymap, *moving, fts_port, status ); if ( *status == SAI__OK ) { /* skip if we did not get a mapping this time round */ if (!bolo2map) continue; /* Check corner pixels in the array for their projected extent on the sky to set the pixel bounds */ astTran2( bolo2map, 4, x_array_corners, y_array_corners, 1, x_map, y_map ); /* Update min/max for this time slice */ for( k=0; k<4; k++ ) { if( x_map[k] != AST__BAD && y_map[k] != AST__BAD ) { if( x_map[k] < dlbnd[0] ) dlbnd[0] = x_map[k]; if( y_map[k] < dlbnd[1] ) dlbnd[1] = y_map[k]; if( x_map[k] > dubnd[0] ) dubnd[0] = x_map[k]; if( y_map[k] > dubnd[1] ) dubnd[1] = y_map[k]; if( x_map[k] < box->lbnd[0] ) box->lbnd[0] = x_map[k]; if( y_map[k] < box->lbnd[1] ) box->lbnd[1] = y_map[k]; if( x_map[k] > box->ubnd[0] ) box->ubnd[0] = x_map[k]; if( y_map[k] > box->ubnd[1] ) box->ubnd[1] = y_map[k]; } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( FUNC_NAME, "Extreme positions are bad.", status ); break; } } } /* Explicitly annul these mappings each time slice for reduced memory usage */ if (bolo2map) bolo2map = astAnnul( bolo2map ); if (fs) fs = astAnnul( fs ); /* Break out of loop over time slices if bad status */ if (*status != SAI__OK) goto CLEANUP; } /* Annul any remaining Ast objects before moving on to the next file */ if (fs) fs = astAnnul( fs ); if (bolo2map) bolo2map = astAnnul( bolo2map ); } /* Close the data file */ smf_close_file( NULL, &data, status); /* Break out of loop over data files if bad status */ if (*status != SAI__OK) goto CLEANUP; } /* make sure we got values - should not be possible with good status */ if (dlbnd[0] == VAL__MAXD || dlbnd[1] == VAL__MAXD) { if (*status == SAI__OK) { *status = SAI__ERROR; errRep( " ", "Unable to find any valid map bounds", status ); } } if (nbadt > 0) { msgOutf( "", " Processed %zu time slices to calculate bounds," " of which %zu had bad telescope data and were skipped", status, (size_t)(ngoodt+nbadt), (size_t)nbadt ); } /* If spatial reference wcs was supplied, store par values that result in no change to the pixel origin. */ if( spacerefwcs ){ par[ 0 ] = 0.5; par[ 1 ] = 0.5; } /* Need to re-align with the interim GRID coordinates */ lbnd_out[0] = ceil( dlbnd[0] - par[0] + 0.5 ); ubnd_out[0] = ceil( dubnd[0] - par[0] + 0.5 ); lbnd_out[1] = ceil( dlbnd[1] - par[1] + 0.5 ); ubnd_out[1] = ceil( dubnd[1] - par[1] + 0.5 ); /* Do the same with the individual input file bounding boxes */ box = *boxes; for (i = 1; i <= size; i++, box++) { box->lbnd[0] = ceil( box->lbnd[0] - par[0] + 0.5); box->ubnd[0] = ceil( box->ubnd[0] - par[0] + 0.5); box->lbnd[1] = ceil( box->lbnd[1] - par[1] + 0.5); box->ubnd[1] = ceil( box->ubnd[1] - par[1] + 0.5); } /* Apply a ShiftMap to the output FrameSet to re-align the GRID coordinates */ shift[0] = 2.0 - par[0] - lbnd_out[0]; shift[1] = 2.0 - par[1] - lbnd_out[1]; astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) ); /* Set the dynamic defaults for lbnd/ubnd */ lbnd0[ 0 ] = lbnd_out[ 0 ]; lbnd0[ 1 ] = lbnd_out[ 1 ]; parDef1i( "LBND", 2, lbnd0, status ); ubnd0[ 0 ] = ubnd_out[ 0 ]; ubnd0[ 1 ] = ubnd_out[ 1 ]; parDef1i( "UBND", 2, ubnd0, status ); parGet1i( "LBND", 2, lbnd_out, &actval, status ); if( actval == 1 ) lbnd_out[ 1 ] = lbnd_out[ 0 ]; parGet1i( "UBND", 2, ubnd_out, &actval, status ); if( actval == 1 ) ubnd_out[ 1 ] = ubnd_out[ 0 ]; /* Ensure the bounds are the right way round. */ if( lbnd_out[ 0 ] > ubnd_out[ 0 ] ) { int itmp = lbnd_out[ 0 ]; lbnd_out[ 0 ] = ubnd_out[ 0 ]; ubnd_out[ 0 ] = itmp; } if( lbnd_out[ 1 ] > ubnd_out[ 1 ] ) { int itmp = lbnd_out[ 1 ]; lbnd_out[ 1 ] = ubnd_out[ 1 ]; ubnd_out[ 1 ] = itmp; } /* If borders of bad pixels are being trimmed from the output image, then do not allow the user-specified bounds to extend outside the default bounding box (since we know that the default bounding box encloses all available data). */ parGet0l( "TRIM", &trim, status ); if( trim ) { if( lbnd_out[ 0 ] < lbnd0[ 0 ] ) lbnd_out[ 0 ] = lbnd0[ 0 ]; if( lbnd_out[ 1 ] < lbnd0[ 1 ] ) lbnd_out[ 1 ] = lbnd0[ 1 ]; if( ubnd_out[ 0 ] > ubnd0[ 0 ] ) ubnd_out[ 0 ] = ubnd0[ 0 ]; if( ubnd_out[ 1 ] > ubnd0[ 1 ] ) ubnd_out[ 1 ] = ubnd0[ 1 ]; } /* Modify the returned FrameSet to take account of the new pixel origin. */ shift[ 0 ] = lbnd0[ 0 ] - lbnd_out[ 0 ]; shift[ 1 ] = lbnd0[ 1 ] - lbnd_out[ 1 ]; if( shift[ 0 ] != 0.0 || shift[ 1 ] != 0.0 ) { astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) ); } /* Report the pixel bounds of the cube. */ if( *status == SAI__OK ) { msgOutif( MSG__NORM, " ", " ", status ); msgSeti( "XL", lbnd_out[ 0 ] ); msgSeti( "YL", lbnd_out[ 1 ] ); msgSeti( "XU", ubnd_out[ 0 ] ); msgSeti( "YU", ubnd_out[ 1 ] ); msgOutif( MSG__NORM, " ", " Output map pixel bounds: ( ^XL:^XU, ^YL:^YU )", status ); if( ( ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1 ) > MAX_DIM || ( ubnd_out[ 1 ] - lbnd_out[ 1 ] + 1 ) > MAX_DIM ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": The map is too big. Check your list of input " "data files does not include widely separated observations.", status ); } } /* If no error has occurred, export the returned FrameSet pointer from the current AST context so that it will not be annulled when the AST context is ended. Otherwise, ensure a null pointer is returned. */ if( *status == SAI__OK ) { astExport( *outframeset ); } else { *outframeset = astAnnul( *outframeset ); } msgOutiff( SMF__TIMER_MSG, "", "Took %.3f s to calculate map bounds", status, smf_timerupdate( &tv1, &tv2, status ) ); /* Clean Up */ CLEANUP: if (*status != SAI__OK) { errRep(FUNC_NAME, "Unable to determine map bounds", status); } if (oskymap) oskymap = astAnnul( oskymap ); if (bolo2map) bolo2map = astAnnul( bolo2map ); if (fitschan) fitschan = astAnnul( fitschan ); if( data != NULL ) smf_close_file( NULL, &data, status ); refsys = astFree( refsys ); astEnd; }
void smf_filter2d_execute( ThrWorkForce *wf, smfData *data, smfFilter *filt, int complement, int *status ) { double *data_i=NULL; /* Imaginary part of the transformed data */ double *data_r=NULL; /* Real part of the transformed data */ smfData *fdata=NULL; /* Transform of data */ dim_t fdims[2]={0,0}; /* Frequency dimensions */ size_t i; /* loop counter */ size_t ndims=0; /* Number of real-space dimensions */ size_t nfdata; /* Total number of frequency data points */ smfData *varfilt=NULL; /* real-space square of supplied filter for var */ AstFrameSet *wcs=NULL; /* Copy of real-space WCS */ /* Main routine */ if (*status != SAI__OK) return; /* Check for NULL pointers */ if( !data ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": NULL smfData pointer", status ); return; } if( !filt ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": NULL smfFilter pointer", status ); return; } if( filt->ndims != 2 ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": Filter must be 2-dimensional", status ); return; } if( smf_isfft( data, NULL, NULL, fdims, NULL, &ndims, status ) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": FFT'd data supplied!", status ); return; } if( ndims != 2 ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": supplied data are not a 2-d map", status ); return; } /* Check that the filter dimensions are appropriate for the data */ for( i=0; i<ndims; i++ ) { if( fdims[i] != filt->fdims[i] ) { *status = SAI__ERROR; errRepf( "", FUNC_NAME ": Filter axis %zu has length %zu, doesn't match data %zu", status, i, filt->fdims[i], fdims[i] ); return; } } /* Dimensions of the transformed data */ nfdata = 1; for( i=0; i<ndims; i++ ) { if( filt->fdims[i] != fdims[i] ) { *status = SAI__ERROR; errRepf( "", FUNC_NAME ": Filter axis %zu has dim %zu doesn't match data dim %zu", status, i, filt->fdims[i], fdims[i]); return; } nfdata *= fdims[i]; } /* Using complement of the filter? */ if( complement ) smf_filter_complement( filt, status ); /* Get a copy of the wcs of the input map */ if( data->hdr && data->hdr->wcs ) { wcs = astCopy( data->hdr->wcs ); } /* Transform the data */ fdata = smf_fft_data( wf, data, NULL, 0, 0, status ); /* Copy the FFT of the data if we will also be filtering the VARIANCE since this will get us a useful container of the correct dimensions for the squared filter */ if( data->pntr[1] ) { varfilt = smf_deepcopy_smfData( wf, fdata, 0, SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY | SMF__NOCREATE_FILE | SMF__NOCREATE_DA, 0, 0, status ); } /* Apply the frequency-domain filter. */ if( *status == SAI__OK ) { data_r = fdata->pntr[0]; data_i = data_r + nfdata; if( filt->isComplex ) { double ac, bd, aPb, cPd; for( i=0; i<nfdata; i++ ) { ac = data_r[i] * filt->real[i]; bd = data_i[i] * filt->imag[i]; aPb = data_r[i] + data_i[i]; cPd = filt->real[i] + filt->imag[i]; } } else { for( i=0; i<nfdata; i++ ) { data_r[i] *= filt->real[i]; data_i[i] *= filt->real[i]; } } } /* Transform back */ smf_fft_data( wf, fdata, data, 1, 0, status ); /* Insert the copy of original real-space WCS into the smfHead since smf_fft_data does not currently calculate it for inverse transforms. */ if( data->hdr ) { /* Annul current wcs if it exists */ if( data->hdr->wcs ) { data->hdr->wcs = astAnnul( data->hdr->wcs ); } data->hdr->wcs = wcs; } /* If we have a VARIANCE component we also need to smooth it. This is slightly complicated because we have to do the equivalent of a real-space convolution between the variance map and the element-wise square of the real-space filter. So we first stuff the supplied filter into a smfData (frequency space), take its inverse to real space and square it. We then transform back to frequency space, and run it through smf_filter2d_execute to apply it to the VARIANCE map (which is also stuffed into its own smfData and then copied into the correct location of the supplied smfData when finished. */ if( (data->pntr[1]) && (*status==SAI__OK) ) { dim_t ndata; double *ptr=NULL; smfData *realfilter=NULL; /* Real space smfData container for var filter */ smfData *vardata=NULL; /* smfData container for variance only */ smfFilter *vfilt=NULL; /* The var filter */ /* Copy the filter into the smfData container and transform into the time domain. */ ptr = varfilt->pntr[0]; memcpy(ptr, filt->real, nfdata*sizeof(*ptr)); if( filt->imag) { memcpy(ptr+nfdata, filt->imag, nfdata*sizeof(*ptr)); } else { memset(ptr+nfdata, 0, nfdata*sizeof(*ptr)); } realfilter = smf_fft_data( wf, varfilt, NULL, 1, 0, status ); smf_close_file( wf, &varfilt, status ); /* Square each element of the real-space filter and then transform back to the frequency domain and stuff into a smfFilter (vfilt). We just point the real and imaginary parts of the smfFilter to the respective regions of the smfData to save memory/time, but we need to be careful when freeing at the end. */ if( *status == SAI__OK ) { ptr = realfilter->pntr[0]; smf_get_dims( realfilter, NULL, NULL, NULL, NULL, &ndata, NULL, NULL, status ); if( *status == SAI__OK ) { double norm = 1. / (double) ndata; for(i=0; i<ndata; i++) { /* Note that we need an additional normalization of N samples */ ptr[i] *= ptr[i] * norm; } } } varfilt = smf_fft_data( wf, realfilter, NULL, 0, 0, status ); if( *status == SAI__OK ) { ptr = varfilt->pntr[0]; vfilt = smf_create_smfFilter( data, status ); vfilt->real = ptr; if( filt->isComplex ) { /* Only worry about imaginary part if the original filter was complex. */ vfilt->isComplex = 1; vfilt->imag = ptr + nfdata; } } /* Now stuff the variance array into a smfData and filter it. */ vardata = smf_deepcopy_smfData( wf, data, 0, SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY | SMF__NOCREATE_FILE | SMF__NOCREATE_DA, 0, 0, status ); if( *status == SAI__OK ) { ptr = vardata->pntr[0]; memcpy( ptr, data->pntr[1], ndata*sizeof(*ptr) ); smf_filter2d_execute( wf, vardata, vfilt, 0, status ); } /* Finally, copy the filtered variance into our output filtered smfData */ if( *status == SAI__OK ) { ptr = data->pntr[1]; memcpy( ptr, vardata->pntr[0], ndata*sizeof(*ptr) ); } /* Clean up */ if( realfilter ) smf_close_file( wf, &realfilter, status ); if( vardata ) smf_close_file( wf, &vardata, status ); if( vfilt ) { vfilt->real = NULL; vfilt->imag = NULL; vfilt = smf_free_smfFilter( vfilt, status ); } } /* Return the filter to its original state if required */ if( complement == -1 ) smf_filter_complement( filt, status ); /* Clean up */ if( varfilt ) smf_close_file( wf, &varfilt, status ); if( fdata ) smf_close_file( wf, &fdata, status ); }
void smf_calc_smoothedwvm ( ThrWorkForce *wf, const smfArray * alldata, const smfData * adata, AstKeyMap* extpars, double **wvmtau, size_t *nelems, size_t *ngoodvals, int * status ) { size_t i; size_t nrelated = 0; /* Number of entries in smfArray */ size_t nframes = 0; /* Number of timeslices */ size_t ngood = 0; /* Number of elements with good tau */ double *taudata = NULL; /* Local version of WVM tau */ const smfArray * thesedata = NULL; /* Collection of smfDatas to analyse */ smfArray * tmpthesedata = NULL; /* Local version of adata in a smfArray */ if (*status != SAI__OK) return; if (alldata && adata) { *status = SAI__ERROR; errRep("", "smf_calc_smoothedwvm can not be given non-NULL alldata and non-NULL adata arguments" " (possible programming error)", status ); return; } if (!alldata && !adata) { *status = SAI__ERROR; errRep("", "smf_calc_smoothedwvm: One of alldata or adata must be non-NULL", status); return; } if (!wvmtau) { *status = SAI__ERROR; errRep("", "Must supply a non-NULL pointer for wvmtau argument" " (possible programming error)", status ); return; } /* if we have a single smfData put it in a smfArray */ if (alldata) { if (alldata->ndat == 0 ) { *status = SAI__ERROR; errRep("", "No smfDatas present in supplied smfArray for WVM smoothing" " (possible programming error)", status ); return; } thesedata = alldata; } else { tmpthesedata = smf_create_smfArray( status ); if (tmpthesedata) { tmpthesedata->owndata = 0; /*not owned by the smfArray */ /* we know that the smfData here will not be touched in this function so we do the BAD thing of casting const to non-const */ smf_addto_smfArray( tmpthesedata, (smfData *)adata, status ); } thesedata = tmpthesedata; } /* Check that we have headers and that the smfData are the same length */ nrelated = thesedata->ndat; for (i = 0; i < nrelated; i++ ) { smfData * data = (thesedata->sdata)[i]; smfHead * hdr = data->hdr; dim_t thisframes = 0; if ( !hdr) { *status = SAI__ERROR; errRepf( "", "smfData %zu has no header. Aborting WVM smoothing", status, i ); return; } smf_get_dims( data, NULL, NULL, NULL, &thisframes, NULL, NULL, NULL, status ); if (!nframes) nframes = thisframes; if (thisframes != nframes) { *status = SAI__ERROR; errRepf( "", "smfData %zu has different length. Aborting WVM smoothing", status, i ); return; } } /* We will need the earliest and last airmass value in order to calculate a zenith tau */ /* As a first step, just fill the time series with calculated WVM tau values even though we know there are about 240 fewer tau readings in reality. This initial approach will make it easier to use the smoothed data directly rather than having to interpolate from the 1.2 second data back into the 200 Hz data. */ taudata = astCalloc( nframes, sizeof(*taudata) ); if (*status == SAI__OK) { double amprev = VAL__BADD; double steptime; size_t maxgap; struct timeval tv1; struct timeval tv2; smfCalcWvmJobData *job_data = NULL; int nworker; /* We need to know the steptime so we can define the max good gap in seconds and convert it to steps*/ steptime = (thesedata->sdata)[0]->hdr->steptime; maxgap = (size_t)( 5.0 / steptime ); /* 5 seconds is just larger than 2 WVM readings */ /* Assume all files have the same airmass information */ smf_find_airmass_interval( (thesedata->sdata)[0]->hdr, &rev, NULL, NULL, NULL, status ); smf_timerinit( &tv1, &tv2, status ); /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); if (*status == SAI__OK) { dim_t tstep; int iworker; smfCalcWvmJobData *pdata = NULL; /* Get the number of time slices to process in each thread. */ if( nworker > (int) nframes ) { tstep = 1; } else { tstep = nframes/nworker; } /* to return the same values for one thread and multiple threads we need to break the threads on wvm sample boundaries wherever possible. We make an initial estimate of the number of WVM measurements by assuming one every two seconds. */ { smfData * curdata = NULL; size_t nwvm = 0; double prevtime = VAL__BADD; double curtime; size_t *boundaries = astGrow(NULL, nframes*(size_t)(steptime/2.0), sizeof(*boundaries)); for (i=0; i<nframes; i++) { if (!curdata) { SELECT_DATA( thesedata, curdata, VAL__BADD, wvm_time, i ); } if (curdata) smf_tslice_ast( curdata, i, 0, NO_FTS, status ); if ( !curdata || curdata->hdr->state->wvm_time == VAL__BADD ) { /* Try the other datas */ SELECT_DATA( thesedata, curdata, VAL__BADD, wvm_time, i ); } if (*status != SAI__OK) break; if (!curdata) { curtime = VAL__BADD; } else { curtime = curdata->hdr->state->wvm_time; } if (curtime != prevtime || nwvm == 0 ) { /* Store the index in the boundaries array */ nwvm++; boundaries = astGrow(boundaries, nwvm, sizeof(*boundaries)); if (!boundaries) { /* this is serious */ if (*status == SAI__OK) *status = SAI__ERROR; errRep("", "Error allocating temporary memory for WVM calculation\n", status ); break; } boundaries[nwvm-1] = i; prevtime = curtime; } } /* No point using too many threads */ if (*status == SAI__OK) { if (nworker >= (int)nwvm) { nworker = nwvm; /* Allocate a measurement per thread */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->t1 = boundaries[iworker]; if (iworker+1 < nworker) pdata->t2 = boundaries[iworker+1]-1; } /* Ensure that the last thread picks up any left-over time slices */ pdata->t2 = nframes - 1; } else { /* Allocate the workers to slices of approximate size tstep */ size_t prevend = 0; /* End of previous slice */ size_t prevbnd = 0; /* Index into previous boundaries[] array selection */ for( iworker = 0; iworker < nworker; iworker++ ) { size_t belowidx = prevend+1; size_t aboveidx = nframes; size_t lbnd; size_t ubnd; size_t j; size_t guess; pdata = job_data + iworker; if (iworker == 0) { /* always start at the beginning */ pdata->t1 = 0; } else { /* Start one after the previous block */ pdata->t1 = prevend + 1; } /* Now we have to find the end of this slice */ guess = (iworker*tstep) + tstep - 1; if (guess <= pdata->t1) guess = pdata->t1 + tstep; /* find nearest boundaries */ for (j=prevbnd; j<nwvm; j++) { if ( boundaries[j] > guess ) { aboveidx = boundaries[j]; ubnd = j; if (j>0) { belowidx = boundaries[j-1]; lbnd = j -1 ; } else { lbnd = 0; } break; } } /* Choose the closest, making sure that we are not choosing t1 */ if ( (guess - belowidx < aboveidx - guess) && belowidx > pdata->t1 ) { pdata->t2 = belowidx - 1; prevbnd = lbnd; } else { pdata->t2 = aboveidx - 1; prevbnd = ubnd; } prevend = pdata->t2; if (prevend == nframes - 1 && iworker < nworker-1 ) { /* we have run out of slices so just use fewer workers */ nworker = iworker + 1; break; } } /* Ensure that the last thread picks up any left-over time slices */ pdata->t2 = nframes - 1; } /* Tidy up */ boundaries = astFree( boundaries ); } } /* Store all the other info needed by the worker threads, and submit the jobs to fix the steps in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { smfArray *thrdata = NULL; pdata = job_data + iworker; pdata->nframes = nframes; pdata->airmass = amprev; /* really need to get it from the start of each chunk */ pdata->taudata = taudata; pdata->maxgap = maxgap; /* Need to copy the smfDatas and create a new smfArray for each thread */ thrdata = smf_create_smfArray( status ); for (i=0;i<nrelated;i++) { smfData *tmpdata = NULL; tmpdata = smf_deepcopy_smfData( wf, (thesedata->sdata)[i], 0, SMF__NOCREATE_FILE | SMF__NOCREATE_DA | SMF__NOCREATE_FTS | SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY, 0, 0, status ); smf_lock_data( tmpdata, 0, status ); smf_addto_smfArray( thrdata, tmpdata, status ); } pdata->thesedata = thrdata; /* Need to do a deep copy of ast data and unlock them */ pdata->extpars = astCopy(extpars); astUnlock( pdata->extpars, 1 ); /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf__calc_wvm_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); /* Now free the resources we allocated during job creation and calculate the number of good values */ for( iworker = 0; iworker < nworker; iworker++ ) { smfArray * thrdata; pdata = job_data + iworker; astLock( pdata->extpars, 0 ); pdata->extpars = astAnnul( pdata->extpars ); thrdata = pdata->thesedata; for (i=0;i<thrdata->ndat;i++) { smf_lock_data( (thrdata->sdata)[i], 1, status ); } smf_close_related( wf, &thrdata, status ); ngood += pdata->ngood; } } job_data = astFree( job_data ); msgOutiff( MSG__NORM, "", FUNC_NAME ": %f s to calculate unsmoothed WVM tau values", status, smf_timerupdate(&tv1,&tv2,status) ); } if (*status == SAI__OK && extpars) { /* Read extpars to see if we need to smooth */ double smoothtime = VAL__BADD; if (astMapGet0D( extpars, "SMOOTHWVM", &smoothtime ) ) { if (smoothtime != VAL__BADD && smoothtime > 0.0) { smfData * data = (thesedata->sdata)[0]; double steptime = data->hdr->steptime; dim_t boxcar = (dim_t)( smoothtime / steptime ); msgOutiff( MSG__VERB, "", "Smoothing WVM data with %f s tophat function", status, smoothtime ); smf_tophat1D( taudata, nframes, boxcar, NULL, 0, 0.0, status ); /* The tophat smoothing puts a bad value at the start and end of the time series so we replace that with the adjacent value since the step time is much smaller than WVM readout time. If more than one value is bad we do not try to find the good value. */ taudata[0] = taudata[1]; taudata[nframes-1] = taudata[nframes-2]; } } } /* Use this to get the raw WVM output for debugging */ /* if (*status == SAI__OK) { smfData *data = (thesedata->sdata)[0]; smfHead *hdr = data->hdr; printf("# IDX TAU RTS_NUM RTS_END WVM_TIME\n"); for (i=0; i<nframes;i++) { JCMTState * state; state = &(hdr->allState)[i]; printf("%zu %.*g %d %.*g %.*g\n", i, DBL_DIG, taudata[i], state->rts_num, DBL_DIG, state->rts_end, DBL_DIG, state->wvm_time); } } */ /* Free resources */ if (tmpthesedata) smf_close_related( wf, &tmpthesedata, status ); if (*status != SAI__OK) { if (taudata) taudata = astFree( taudata ); *nelems = 0; *ngoodvals = 0; } else { *wvmtau = taudata; *nelems = nframes; *ngoodvals = ngood; } }
void smf_calc_iqu( ThrWorkForce *wf, smfData *data, int block_start, int block_end, int ipolcrd, int qplace, int uplace, int iplace, NdgProvenance *oprov, AstFitsChan *fc, int pasign, double paoff, double angrot, int submean, int *status ){ /* Local Variables: */ AstFrameSet *wcs; /* WCS FrameSet for output NDFs */ AstWinMap *wm; /* Mapping to reverse the X GRID axis */ const JCMTState *state; /* JCMTState info for current time slice */ dim_t nbolo; /* No. of bolometers */ dim_t ncol; /* No. of columns of bolometers */ dim_t nrow; /* No. of rows of bolometers */ dim_t ntslice; /* Number of time-slices in data */ double *ipi; /* Pointer to output I array */ double *ipq; /* Pointer to output Q array */ double *ipu; /* Pointer to output U array */ double ina[ 2 ]; /* Bolometer coords at bottom left */ double inb[ 2 ]; /* Bolometer coords at top right */ double outa[ 2 ]; /* NDF GRID coords at bottom left */ double outb[ 2 ]; /* NDF GRID coords at top right */ int bstep; /* Bolometer step between threads */ int el; /* Number of mapped array elements */ int indfi; /* Identifier for NDF holding I values */ int indfq; /* Identifier for NDF holding Q values */ int indfu; /* Identifier for NDF holding Q values */ int itime; /* Time slice index */ int iworker; /* Index of a worker thread */ int lbnd[ 2 ]; /* Lower pixel bounds of output NDF */ int ntime; /* Time slices to check */ int nworker; /* No. of worker threads */ int old; /* Data has old-style POL_ANG values? */ int ubnd[ 2 ]; /* Upper pixel bounds of output NDF */ size_t bstride; /* Stride between adjacent bolometer values */ size_t tstride; /* Stride between adjacent time slice values */ smfCalcIQUJobData *job_data = NULL; /* Pointer to all job data */ smfCalcIQUJobData *pdata = NULL;/* Pointer to next job data */ smfHead *hdr; /* Pointer to data header this time slice */ double *mean; int tstep; /* Time slice step between threads */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Convenience pointers. */ hdr = data->hdr; /* Obtain number of time slices - will also check for 3d-ness. Also get the dimensions of the bolometer array and the strides between adjacent bolometer values. */ smf_get_dims( data, &nrow, &ncol, &nbolo, &ntslice, NULL, &bstride, &tstride, status ); /* Report an error if the block of time slices extends of either end. */ if( block_start < 0 || block_end >= (int) ntslice ) { if( *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "S", block_start ); msgSeti( "E", block_end ); msgSeti( "N", ntslice ); errRep( " ", "smf_calc_iqu: invalid block of time slices - ^S to " "^E (^N time slices are available).", status ); } } /* Create the output NDFs. Each one is a 2D array with dimensions equal to the bolometer array. */ lbnd[ 0 ] = 1; lbnd[ 1 ] = 1; ubnd[ 0 ] = ncol; ubnd[ 1 ] = nrow; ndfNew( "_DOUBLE", 2, lbnd, ubnd, &qplace, &indfq, status ); ndfNew( "_DOUBLE", 2, lbnd, ubnd, &uplace, &indfu, status ); if( iplace != NDF__NOPL ) { ndfNew( "_DOUBLE", 2, lbnd, ubnd, &iplace, &indfi, status ); } else { indfi = NDF__NOID; } /* Store any supplied provenance in all NDFs. */ if( oprov ) { ndgWriteProv( oprov, indfq, 1, status ); ndgWriteProv( oprov, indfu, 1, status ); if( indfi != NDF__NOID ) ndgWriteProv( oprov, indfi, 1, status ); } /* Store any supplied FITS headers in all NDFs.*/ if( fc && astGetI( fc, "NCard" ) > 0 ) { kpgPtfts( indfq, fc, status ); kpgPtfts( indfu, fc, status ); if( indfi != NDF__NOID ) kpgPtfts( indfi, fc, status ); } /* Store the WCS frameSet in all NDFs. First get the FrameSet for the central time slice in the block, and set its current Frame to the tracking frame. */ smf_tslice_ast( data, ( block_start + block_end )/2, 1, status); astSetC( hdr->wcs, "System", sc2ast_convert_system( (data->hdr->allState)[0].tcs_tr_sys, status ) ); /* Take a copy and then reverse the X axis of the GRID Frame by remaping the base Frame using a WinMap. This produces a pixel grid such as you would see by looking up at the sky from underneath the array, rather than looking down at the ground from above the array. */ wcs = astCopy( hdr->wcs ); ina[ 0 ] = 1.0; inb[ 0 ] = ncol; ina[ 1 ] = 1.0; inb[ 1 ] = nrow; outa[ 0 ] = ncol; outb[ 0 ] = 1.0; outa[ 1 ] = 1.0; outb[ 1 ] = nrow; wm = astWinMap( 2, ina, inb, outa, outb, " " ); astRemapFrame( wcs, AST__BASE, wm ); wm = astAnnul( wm ); /* Store the FrameSet in the output NDFs, then annull the copy. */ ndfPtwcs( wcs, indfq, status ); ndfPtwcs( wcs, indfu, status ); if( indfi != NDF__NOID ) ndfPtwcs( wcs, indfi, status ); wcs = astAnnul( wcs ); /* Map the Data array in each NDF. */ ndfMap( indfq, "Data", "_DOUBLE", "WRITE", (void **) &ipq, &el, status ); ndfMap( indfu, "Data", "_DOUBLE", "WRITE", (void **) &ipu, &el, status ); if( indfi != NDF__NOID ) { ndfMap( indfi, "Data", "_DOUBLE", "WRITE", (void **) &ipi, &el, status ); } else { ipi = NULL; } /* If required, allocate memory to hold the mean bolometer value at each time slice. */ mean = submean ? astMalloc( ntslice*sizeof( *mean ) ) : NULL; /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); /* Check the above pointers can be used safely. */ if( *status == SAI__OK ) { /* Go through the first thousand POL_ANG values to see if they are in units of radians (new data) or arbitrary encoder units (old data). They are assumed to be in radians if no POL_ANG value is larger than 20. */ old = 0; state = hdr->allState; ntime = ( ntslice > 1000 ) ? 1000 : ntslice; for( itime = 0; itime < ntime; itime++,state++ ) { if( state->pol_ang > 20 ) { old = 1; msgOutif( MSG__VERB, ""," POL2 data contains POL_ANG values " "in encoder units - converting to radians.", status ); break; } } /* If required, find the mean bolometer value at each time slice. */ if( submean ) { /* Determine which time-slices are to be processed by which threads. */ tstep = ntslice/nworker; if( tstep < 1 ) tstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->block_start = iworker*tstep; if( iworker < nworker - 1 ) { pdata->block_end = pdata->block_start + tstep - 1; } else { pdata->block_end = ntslice - 1; } } /* Store all the other info needed by the worker threads, and submit the jobs to calculate the Q and U values in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->bstride = bstride; pdata->dat = data->pntr[0]; pdata->nbolo = nbolo; pdata->qua = smf_select_qualpntr( data, NULL, status );; pdata->tstride = tstride; pdata->mean = mean; pdata->action = 1; /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calc_iqu_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); } /* Determine which bolometers are to be processed by which threads. */ bstep = nbolo/nworker; if( bstep < 1 ) bstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->b1 = iworker*bstep; pdata->b2 = pdata->b1 + bstep - 1; } /* Ensure that the last thread picks up any left-over bolometers */ pdata->b2 = nbolo - 1; /* Store all the other info needed by the worker threads, and submit the jobs to calculate the Q and U values in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->bstride = bstride; pdata->dat = data->pntr[0];; pdata->nbolo = nbolo; pdata->qua = smf_select_qualpntr( data, NULL, status );; pdata->tstride = tstride; pdata->allstates = hdr->allState; pdata->ipq = ipq; pdata->ipu = ipu; pdata->ipi = ipi; pdata->ipolcrd = ipolcrd; pdata->block_start = block_start; pdata->block_end = block_end; pdata->old = old; pdata->ncol = ncol; pdata->pasign = pasign ? +1: -1; pdata->paoff = paoff; pdata->angrot = angrot; pdata->action = 0; pdata->mean = mean; /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calc_iqu_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); } /* Add POLANAL Frames to the WCS FrameSet in each output NDF. This Frame is used by POLPACK to determine the reference direction of the Stokes vectors (focal plane Y in this case, i.e. zero-based axis 1 ). */ smf_polext( indfq, 0, 0.0, "FPLANE", 1, status ); smf_polext( indfu, 0, 0.0, "FPLANE", 1, status ); if( ipi ) smf_polext( indfi, 0, 0.0, "FPLANE", 1, status ); /* Free the two output NDFs. */ ndfAnnul( &indfq, status ); ndfAnnul( &indfu, status ); if( ipi ) ndfAnnul( &indfi, status ); /* Free other resources. */ job_data = astFree( job_data ); mean = astFree( mean ); }
void smf_store_image( smfData *data, HDSLoc *scu2redloc, int cycle, int ndim, int dims[], int nsampcycle, int vxmin, int vymin, double *image, double *zero, int *status) { smfHead *hdr = NULL; /* Pointer to header struct */ HDSLoc *bz_imloc = NULL; /* HDS locator */ int bzindf; /* NDF identifier for bolometer zero points */ double *bzptr = NULL; /* Pointer to mapped space for zero points */ int el; /* Number of elements mapped */ int frame; /* Mean timeslice index for image */ AstFitsChan *imfits=NULL; /* FITS header for each reconstructed image */ char imname[DAT__SZNAM]; /* Name of structure for image */ double *imptr = NULL; /* Pointer to mapped space for image */ int j; /* Loop counter */ int lbnd[2]; /* Lower dimension bounds */ int ntot; /* Total number of elements */ int place; /* NDF placeholder */ char prvname[2*PAR__SZNAM +1]; /* Provenance creator */ int seqend; /* End index */ int seqstart; /* Starting index */ int slice; /* Index of current time slice */ int strnum; /* Structure element number */ sc2ast_subarray_t subnum; /* Subarray index number */ int ubnd[2]; /* Upper dimension bounds */ int uindf; /* NDF identifier */ AstFrameSet *wcs = NULL; /* WCS info */ if ( *status != SAI__OK ) return; seqstart = cycle * nsampcycle; seqend = seqstart + nsampcycle - 1; slice = (int)( (seqstart + seqend ) /2); smf_tslice_ast( data, slice, 1, status); astBegin; /* Old beginning */ /* Get structure for nth constructed image */ strnum = cycle + 1; sprintf ( imname, "I%d", strnum ); ntot = 1; for ( j=0; j<ndim; j++ ) { lbnd[j] = 1; ubnd[j] = lbnd[j] + dims[j] - 1; ntot *= dims[j]; } ndfPlace ( scu2redloc, imname, &place, status ); ndfNew ( "_DOUBLE", ndim, lbnd, ubnd, &place, &uindf, status ); ndfHcre ( uindf, status ); /* Map the data array */ ndfMap ( uindf, "DATA", "_DOUBLE", "WRITE", (void *)&imptr, &el, status ); /* Copy image array */ if ( *status == SAI__OK ) { memcpy( imptr, image, ntot*sizeof(*imptr)); } /* Derive WCS */ hdr = data->hdr; smf_find_subarray(hdr, NULL, 0, &subnum, status ); sc2ast_createwcs( subnum, hdr->state, hdr->instap, hdr->telpos, &wcs, status ); /* Shift the coord frame is either vxmin or vymin is non-zero */ if ( vxmin != 0 || vymin != 0 ) { sc2ast_moveframe ( -(double)vxmin, -(double)vymin, wcs, status ); } /* This should probably be a user-option but ICRS is probably a safe assumption */ sc2ast_set_output_system( hdr->state->tcs_tr_sys, wcs, status ); /* Sort out provenance. */ smf_get_taskname( NULL, prvname, status ); smf_updateprov( uindf, data, NDF__NOID, prvname, NULL, status ); /* Store world coordinate transformations */ ndfPtwcs ( wcs, uindf, status ); /* Store the bolometer zero points as an NDF in the extension */ if (zero) { ndfXnew ( uindf, "BOLZERO", "SCUBA2_ZER_ARR", 0, 0, &bz_imloc, status ); ndfPlace ( bz_imloc, "ZERO", &place, status ); /* Create the array for bolometer zeros */ lbnd[0] = SC2STORE__BOL_LBND; ubnd[0] = lbnd[0] + dims[0] - 1; lbnd[1] = SC2STORE__BOL_LBND; ubnd[1] = lbnd[1] + dims[1] - 1; ndfNew ( "_DOUBLE", 2, lbnd, ubnd, &place, &bzindf, status ); ndfHcre ( bzindf, status ); /* Map the data array */ ndfMap ( bzindf, "DATA", "_DOUBLE", "WRITE", (void *)&bzptr, &el, status ); /* Copy image array */ if ( *status == SAI__OK ) { memcpy( bzptr, zero, dims[0]*dims[1]*sizeof(*zero)); } /* Unmap the data array */ ndfUnmap ( bzindf, "DATA", status ); ndfAnnul ( &bzindf, status ); /* Free the locators for the frame */ datAnnul ( &bz_imloc, status ); } /* Store the FITS headers */ frame = (int)( (seqstart + seqend ) /2); /* Quick and dirty method - just copy the full FITS header */ imfits = astCopy( hdr->fitshdr ); astSetFitsI( imfits, "SUBSCAN", strnum, "Subscan number of reconstructed image", 0); kpgPtfts( uindf, imfits, status ); /* Unmap the data array */ ndfUnmap ( uindf, "DATA", status ); ndfAnnul ( &uindf, status ); astEnd; }
void smf_calc_iqu( ThrWorkForce *wf, smfData *data, int block_start, int block_end, int ipolcrd, int qplace, int uplace, int iplace, NdgProvenance *oprov, AstFitsChan *fc, int pasign, double paoff, double angrot, int submean, int harmonic, int *status ){ /* Local Variables: */ AstCmpMap *cm1; AstCmpMap *cm2; AstFrameSet *wcs; /* WCS FrameSet for output NDFs */ AstMapping *fpmap1; AstMapping *fpmap2; AstMapping *oskymap; AstMapping *totmap; AstSkyFrame *oskyfrm; AstWinMap *wm; /* Mapping to reverse the X GRID axis */ const JCMTState *state; /* JCMTState info for current time slice */ const char *usesys; /* Used system string */ dim_t itime; /* Time slice index */ dim_t nbolo; /* No. of bolometers */ dim_t ncol; /* No. of columns of bolometers */ dim_t nrow; /* No. of rows of bolometers */ dim_t ntime; /* Time slices to check */ dim_t ntslice; /* Number of time-slices in data */ double *ipi; /* Pointer to output I array */ double *ipiv; /* Pointer to output I variance array */ double *ipq; /* Pointer to output Q array */ double *ipqv; /* Pointer to output Q variance array */ double *ipu; /* Pointer to output U array */ double *ipuv; /* Pointer to output U variance array */ double *mean; double ang_data[2]; double fox[2]; double foy[2]; double fpr0; double fprinc; double fx[2]; double fy[2]; double ina[ 2 ]; /* Bolometer coords at bottom left */ double inb[ 2 ]; /* Bolometer coords at top right */ double outa[ 2 ]; /* NDF GRID coords at bottom left */ double outb[ 2 ]; /* NDF GRID coords at top right */ int bstep; /* Bolometer step between threads */ int el; /* Number of mapped array elements */ int gotvar; /* Were any output variances created? */ int indfi; /* Identifier for NDF holding I values */ int indfq; /* Identifier for NDF holding Q values */ int indfu; /* Identifier for NDF holding Q values */ int iworker; /* Index of a worker thread */ int lbnd[ 2 ]; /* Lower pixel bounds of output NDF */ int moving; int nworker; /* No. of worker threads */ int old; /* Data has old-style POL_ANG values? */ int tstep; /* Time slice step between threads */ int ubnd[ 2 ]; /* Upper pixel bounds of output NDF */ size_t bstride; /* Stride between adjacent bolometer values */ size_t tstride; /* Stride between adjacent time slice values */ smfCalcIQUJobData *job_data = NULL; /* Pointer to all job data */ smfCalcIQUJobData *pdata = NULL;/* Pointer to next job data */ smfHead *hdr; /* Pointer to data header this time slice */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Convenience pointers. */ hdr = data->hdr; /* Obtain number of time slices - will also check for 3d-ness. Also get the dimensions of the bolometer array and the strides between adjacent bolometer values. */ smf_get_dims( data, &nrow, &ncol, &nbolo, &ntslice, NULL, &bstride, &tstride, status ); /* Report an error if the block of time slices extends of either end. */ if( block_start < 0 || block_end >= (int) ntslice ) { if( *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "S", block_start ); msgSeti( "E", block_end ); msgSeti( "N", ntslice ); errRep( " ", "smf_calc_iqu: invalid block of time slices - ^S to " "^E (^N time slices are available).", status ); } } /* Create the output NDFs. Each one is a 2D array with dimensions equal to the bolometer array. */ lbnd[ 0 ] = 1; lbnd[ 1 ] = 1; ubnd[ 0 ] = ncol; ubnd[ 1 ] = nrow; ndfNew( "_DOUBLE", 2, lbnd, ubnd, &qplace, &indfq, status ); ndfNew( "_DOUBLE", 2, lbnd, ubnd, &uplace, &indfu, status ); if( iplace != NDF__NOPL ) { ndfNew( "_DOUBLE", 2, lbnd, ubnd, &iplace, &indfi, status ); } else { indfi = NDF__NOID; } /* Store any supplied provenance in all NDFs. */ if( oprov ) { ndgWriteProv( oprov, indfq, 1, status ); ndgWriteProv( oprov, indfu, 1, status ); if( indfi != NDF__NOID ) ndgWriteProv( oprov, indfi, 1, status ); } /* Store any supplied FITS headers in all NDFs.*/ if( fc && astGetI( fc, "NCard" ) > 0 ) { kpgPtfts( indfq, fc, status ); kpgPtfts( indfu, fc, status ); if( indfi != NDF__NOID ) kpgPtfts( indfi, fc, status ); } /* Store the WCS frameSet in all NDFs. First get the FrameSet for the central time slice in the block, and set its current Frame to the tracking frame. */ smf_tslice_ast( data, ( block_start + block_end )/2, 1, NO_FTS, status); usesys = sc2ast_convert_system( (data->hdr->allState)[0].tcs_tr_sys, status ); astSetC( hdr->wcs, "System", usesys ); /* Get the Mapping from focal plane coords to bolometer grid coords. This is the same for all time slices. sc2ast ensures that frame 3 is FPLANE. */ fpmap1 = astGetMapping( hdr->wcs, 3, AST__BASE ); /* Take a copy and then reverse the X axis of the GRID Frame by remaping the base Frame using a WinMap. This produces a pixel grid such as you would see by looking up at the sky from underneath the array, rather than looking down at the ground from above the array. */ wcs = astCopy( hdr->wcs ); ina[ 0 ] = 1.0; inb[ 0 ] = ncol; ina[ 1 ] = 1.0; inb[ 1 ] = nrow; outa[ 0 ] = ncol; outb[ 0 ] = 1.0; outa[ 1 ] = 1.0; outb[ 1 ] = nrow; wm = astWinMap( 2, ina, inb, outa, outb, " " ); astRemapFrame( wcs, AST__BASE, wm ); wm = astAnnul( wm ); /* Get the Mapping from output grid coords to focal plane coords. */ fpmap2 = astGetMapping( wcs, AST__BASE, 3 ); /* If the target is moving (assumed to be the case if the tracking system is AZEL or GAPPT), make the FrameSet current Frame represent offsets from the reference position (i.e. the moving target), and indicate that the offset coord system should be used for alignment. */ if( !strcmp( usesys, "AZEL" ) || !strcmp( usesys, "GAPPT" ) ){ astSet( wcs, "SkyRefIs=Origin,AlignOffset=1" ); moving = 1; } else { moving = 0; } /* Store the FrameSet in the output NDFs. */ ndfPtwcs( wcs, indfq, status ); ndfPtwcs( wcs, indfu, status ); if( indfi != NDF__NOID ) ndfPtwcs( wcs, indfi, status ); /* Map the Data array in each NDF. */ ndfMap( indfq, "Data", "_DOUBLE", "WRITE", (void **) &ipq, &el, status ); ndfMap( indfu, "Data", "_DOUBLE", "WRITE", (void **) &ipu, &el, status ); if( indfi != NDF__NOID ) { ndfMap( indfi, "Data", "_DOUBLE", "WRITE", (void **) &ipi, &el, status ); } else { ipi = NULL; } /* Map the Variance array in each NDF. */ ndfMap( indfq, "Variance", "_DOUBLE", "WRITE", (void **) &ipqv, &el, status ); ndfMap( indfu, "Variance", "_DOUBLE", "WRITE", (void **) &ipuv, &el, status ); if( indfi != NDF__NOID ) { ndfMap( indfi, "Variance", "_DOUBLE", "WRITE", (void **) &ipiv, &el, status ); } else { ipiv = NULL; } /* If required, allocate memory to hold the mean bolometer value at each time slice. */ mean = submean ? astMalloc( ntslice*sizeof( *mean ) ) : NULL; /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); /* Check the above pointers can be used safely. */ if( *status == SAI__OK ) { /* Go through the first thousand POL_ANG values to see if they are in units of radians (new data) or arbitrary encoder units (old data). They are assumed to be in radians if no POL_ANG value is larger than 20. */ old = 0; state = hdr->allState; ntime = ( ntslice > 1000 ) ? 1000 : ntslice; for( itime = 0; itime < ntime; itime++,state++ ) { if( state->pol_ang > 20 ) { old = 1; msgOutif( MSG__VERB, ""," POL2 data contains POL_ANG values " "in encoder units - converting to radians.", status ); break; } } /* If required, find the mean bolometer value at each time slice. */ if( submean ) { /* Determine which time-slices are to be processed by which threads. */ tstep = ntslice/nworker; if( tstep < 1 ) tstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->block_start = iworker*tstep; if( iworker < nworker - 1 ) { pdata->block_end = pdata->block_start + tstep - 1; } else { pdata->block_end = ntslice - 1; } } /* Store all the other info needed by the worker threads, and submit the jobs to calculate the Q and U values in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->bstride = bstride; pdata->dat = data->pntr[0]; pdata->nbolo = nbolo; pdata->qua = smf_select_qualpntr( data, NULL, status );; pdata->tstride = tstride; pdata->mean = mean; pdata->action = 1; /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calc_iqu_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); } /* Get the Frame representing absolute sky coords in the output NDF, and the Mapping from sky to grid in the output NDF. */ oskyfrm = astCopy( astGetFrame( wcs, AST__CURRENT ) ); astSet( oskyfrm, "SkyRefIs=Ignored" ); oskymap = astGetMapping( wcs, AST__CURRENT, AST__BASE ); wcs = astAnnul( wcs ); /* Find the first and last time slices, calculate the angle between the focal pane Y axis at the time slice, and the focal plane Y axis in the output NDF. For intervening time-slices, the angle is found by linear interpolation between the extreme time slices. */ for( el = 0; el < 2; el++ ) { /* Get the mapping from GRID coords in the input time slice to GRID coords in the output. */ totmap = smf_rebin_totmap( data, el?ntslice-1:0, oskyfrm, oskymap, moving, NO_FTS, status ); /* Modify it to be the Mapping from focal plane coords in the input time slice to focal plane coords in the output. */ cm1 = astCmpMap( fpmap1, totmap, 1, " " ); cm2 = astCmpMap( cm1, fpmap2, 1, " " ); /* Use this Mapping to convert two points on the focal plane Y axis from the input to the output. */ fx[0] = 0.0; fy[0] = 0.0; fx[1] = 0.0; fy[1] = 4.0; astTran2( cm2, 2, fx, fy, 1, fox, foy ); /* The angle from the focal plane Y axis in the output to the focal plane Y axis in the input time slice, measured positive in sense of rotation from Fy to Fx. */ ang_data[ el ] = atan2( fox[1]-fox[0], foy[1]-foy[0] ); /* Free resources for this time slice. */ totmap = astAnnul( totmap ); cm1 = astAnnul( cm1 ); cm2 = astAnnul( cm2 ); } /* Annul objects. */ oskymap = astAnnul( oskymap ); oskyfrm = astAnnul( oskyfrm ); fpmap1 = astAnnul( fpmap1 ); fpmap2 = astAnnul( fpmap2 ); /* Get the constants of the linear relationship between focal plane rotation and time slice index "fpr = fpr0 + itime*fprinc". */ fpr0 = ang_data[ 0 ]; fprinc = ( ang_data[ 1 ] - fpr0 )/( ntslice - 1 ); /* Determine which bolometers are to be processed by which threads. */ bstep = nbolo/nworker; if( bstep < 1 ) bstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->b1 = iworker*bstep; pdata->b2 = pdata->b1 + bstep - 1; } /* Ensure that the last thread picks up any left-over bolometers */ pdata->b2 = nbolo - 1; /* Store all the other info needed by the worker threads, and submit the jobs to calculate the Q and U values in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->bstride = bstride; pdata->dat = data->pntr[0];; pdata->nbolo = nbolo; pdata->qua = smf_select_qualpntr( data, NULL, status );; pdata->tstride = tstride; pdata->allstates = hdr->allState; pdata->ipq = ipq; pdata->ipu = ipu; pdata->ipi = ipi; pdata->ipqv = ipqv; pdata->ipuv = ipuv; pdata->ipiv = ipiv; pdata->ipolcrd = ipolcrd; pdata->block_start = block_start; pdata->block_end = block_end; pdata->old = old; pdata->ncol = ncol; pdata->pasign = pasign ? +1: -1; pdata->paoff = paoff; pdata->angrot = angrot; pdata->fpr0 = fpr0; pdata->fprinc = fprinc; pdata->angfac = harmonic/4.0; pdata->action = 0; pdata->mean = mean; /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calc_iqu_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); /* See if any thread produced non-bad variance values. */ gotvar = 0; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; if( pdata->gotvar ) gotvar = 1; } /* If no variances were created, erase the Variance component and tell the user. */ ndfUnmap( indfq, "*", status ); ndfUnmap( indfu, "*", status ); if( ipi ) ndfUnmap( indfi, "*", status ); if( !gotvar ) { ndfReset( indfq, "Variance", status ); ndfReset( indfu, "Variance", status ); if( ipi ) ndfReset( indfi, "Variance", status ); msgOut( "", "Warning: Insufficient input data to produce variances", status ); } } /* Add POLANAL Frames to the WCS FrameSet in each output NDF. This Frame is used by POLPACK to determine the reference direction of the Stokes vectors (focal plane Y in this case, i.e. zero-based axis 1 ). */ smf_polext( indfq, 0, 0.0, "FPLANE", 1, status ); smf_polext( indfu, 0, 0.0, "FPLANE", 1, status ); if( ipi ) smf_polext( indfi, 0, 0.0, "FPLANE", 1, status ); /* Free the two output NDFs. */ ndfAnnul( &indfq, status ); ndfAnnul( &indfu, status ); if( ipi ) ndfAnnul( &indfi, status ); /* Free other resources. */ job_data = astFree( job_data ); mean = astFree( mean ); }
void smf_addpolanal( AstFrameSet *fset, smfHead *hdr, AstKeyMap *config, int *status ){ /* Local Variables */ AstCmpMap *tmap; AstFrame *cfrm; AstFrame *pfrm; AstFrame *tfrm; AstFrameSet *tfs; AstPermMap *pm; char *polnorth = NULL; const char *cursys; const char *trsys; int aloff; int icurr; int inperm[2]; int outperm[2]; int pol2fp; /* Check inherited status, and also check the supplied angle is not bad. */ if( *status != SAI__OK ) return; /* Begin an AST object context. */ astBegin; /* Get the value of the POLNORTH FITS keyword from the supplied header. The rest only happens if the keyword is found. */ if( astGetFitsS( hdr->fitshdr, "POLNORTH", &polnorth ) ) { /* Normally, we do not allow maps to be made from Q/U time streams that use focal plane Y as the reference direction (because of the problems of sky rotation). Therefore we report an error. However, we do need to make such maps as part of the process of determining the parameters of the Instrumental Polarisation (IP) model. So only report the error if "pol2fp" config parameter is non-zero. */ if( !strcmp( polnorth, "FPLANE" ) ) { astMapGet0I( config, "POL2FP", &pol2fp ); if( pol2fp ) { msgBlank( status ); msgOut( "", "WARNING: The input NDFs hold POL-2 Q/U data specified " "with respect to focal plane Y.",status ); msgOut( "", "Maps should normally be made from POL-2 data specified " "with respect to celestial north.",status ); msgOut( "", "The output map will not contain a POLANAL Frame and " "so will be unusable by POLPACK applications.",status ); msgBlank( status ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "The input NDFs hold POL-2 Q/U data specified with " "respect to focal plane Y.",status ); errRep( "", "Maps can only be made from POL-2 data specified with " "respect to celestial north.",status ); } /* If the ref. direction is celestial north, create a suitable Frame and Mapping and add them into the supplied FrameSet. */ } else { /* Check the current Frame is a SkyFrame. */ cfrm = astGetFrame( fset, AST__CURRENT ); if( astIsASkyFrame( cfrm ) ) { /* Create a POLANAL Frame. */ pfrm = astFrame( 2, "Domain=POLANAL" ); astSet( pfrm, "Title=Polarimetry reference frame" ); astSet( pfrm, "Label(1)=Polarimetry reference direction" ); astSet( pfrm, "Label(2)=" ); /* Create a PermMap that ensures that axis 1 of the POLANAL Frame is parallel to the latitude axis (i.e. north) of the curent Frame (the current Frame axes may have been swapped). */ outperm[ 0 ] = astGetI( cfrm, "LatAxis" ); outperm[ 1 ] = astGetI( cfrm, "LonAxis" ); inperm[ outperm[ 0 ] - 1 ] = 1; inperm[ outperm[ 1 ] - 1 ] = 2; pm = astPermMap( 2, inperm, 2, outperm, NULL, " " ); /* Record the index of the original current Frame. */ icurr = astGetI( fset, "Current" ); /* Determine the system to use. */ if( !strcmp( polnorth, "TRACKING" ) ) { trsys = sc2ast_convert_system( hdr->state->tcs_tr_sys, status ); } else { trsys = polnorth; } /* If the current Frame in the supplied FrameSet has this system. Then we use the above PermMap to connect the POLANAL Frame directly to the current Frame. */ cursys = astGetC( cfrm, "System" ); if( trsys && cursys && !strcmp( cursys, trsys ) ) { astAddFrame( fset, AST__CURRENT, pm, pfrm ); /* Otherwise we need to get a Mapping from the current Frame to the required frame. */ } else { /* Take a copy of the current Frame (in order to pick up epoch, observatory position, etc), and set its System to the required system. */ tfrm = astCopy( cfrm ); astSetC( tfrm, "System", trsys ); /* Get the Mapping from the original current Frame to this modified copy. Ensure alignment happens in absolute coords (alignment in offset coords is always a unit mapping and so no rotation occurs). */ aloff = astGetI( cfrm, "AlignOffset" ); if( aloff ) { astSetI( cfrm, "AlignOffset", 0 ); astSetI( tfrm, "AlignOffset", 0 ); } tfs = astConvert( cfrm, tfrm, "SKY" ); if( aloff ) { astSetI( cfrm, "AlignOffset", 1 ); astSetI( tfrm, "AlignOffset", 1 ); } if( tfs ) { /* Use it, in series with with the above PermMap, to connect the POLANAL frame to the current Frame. */ tmap = astCmpMap( astGetMapping( tfs, AST__BASE, AST__CURRENT ), pm, 1, " " ); astAddFrame( fset, AST__CURRENT, astSimplify( tmap ), pfrm ); /* Report an error if the mapping from current to required system could not be found. */ } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "smf_addpolanal: Could not convert Frame " "from %s to %s (prgramming error).", status, cursys, trsys ); } } /* Re-instate the original current Frame. */ astSetI( fset, "Current", icurr ); /* Report an error if the current Frame is not a SkyFrame. */ } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_addpolanal: The current Frame in the " "supplied FrameSet is not a SkyFrame (prgramming " "error).", status ); } } } /* End the AST object context. */ astEnd; }
void gsdac_getDateVars ( const gsdVars *gsdVars, const char *backend, const int obsNum, dateVars *dateVars, int *status ) { /* Local variables */ char dateString[SZFITSTR]; /* temporary string for date conversions. */ int day; /* days */ double dLST; /* difference in LST */ double dut1; /* UT1-UTC correction */ int hour; /* hours */ int min; /* minutes */ int month; /* months */ float sec; /* seconds */ int tableDims; /* dimensionality of data table */ int tableSize; /* number of elements of data table */ int tableElement; /* index of element in data table */ AstTimeFrame *tempFrame = NULL; /* AstTimeFrame for UT1-UTC conversion */ const char *tempString; /* temporary string */ AstTimeFrame *tFrame = NULL; /* AstTimeFrame for UT1-UTC conversion */ double utcEnd; /* end UTC time */ double HSTend; /* end HST time */ double HSTstart; /* start HST time */ double utcStart; /* start UTC time */ int year; /* years */ /* Check inherited status */ if ( *status != SAI__OK ) return; /* Get the DATE-OBS. */ /* Parse date to get year/month/day. */ sprintf ( dateString, "%8.4f", gsdVars->obsUT1d ); sscanf ( dateString, "%04d.%02d%02d", &year, &month, &day ); /* Parse time to get hour/min/sec: can not use usual (int) scoping because of danger to get hh:05:60.00 instead of hh:06:00.00 */ hour = (int) gsdVars->obsUT1h; min = 60.0 * fmodf( gsdVars->obsUT1h, 1.0 ); sec = 60.0 * fmodf( 60.0*gsdVars->obsUT1h, 1.0 ); /* Set up the timeframe. */ tFrame = astTimeFrame ( "timescale=UT1" ); astSet ( tFrame, "TimeOrigin=%04d-%02d-%02dT%02d:%02d:%f", year, month, day, hour, min, sec ); /* Apply the UT1-UTC correction. */ dut1 = gsdVars->obsUT1C * 86400.0; astSet ( tFrame, "DUT1=%f", dut1 ); astSet ( tFrame, "timescale=UTC" ); utcStart = astGetD ( tFrame, "timeOrigin" ); tempFrame = astCopy ( tFrame ); astClear ( tempFrame, "timeOrigin" ); astSet ( tempFrame, "format(1)=iso.2" ); tempString = astFormat ( tempFrame, 1, utcStart ); /* Copy the UTC date string. */ strncpy ( dateVars->dateObs, tempString, 10 ); dateVars->dateObs[10] = 'T'; strcpy ( &(dateVars->dateObs[11]), &(tempString[11]) ); /* Get the OBSID. */ /* Check to see that the backend is DAS. */ if ( strncmp ( backend, "DAS", 3 ) != 0 && strncmp ( backend, "AOSC", 4 ) != 0 ) { *status = SAI__ERROR; msgSetc ( "BACKEND", backend ); errRep ( "gsdac_getDateVars", "Backend ^BACKEND not supported", status ); return; } sprintf ( dateVars->obsID, "%s_%05d_%04d%02d%02dT%02d%02d%02d", backend, obsNum, year, month, day, hour, min, (int)sec ); /* Convert to lowercase to give a consistent format for the JSA. */ astChrCase( NULL, dateVars->obsID, 0, 0 ); /* Get the DATE-END. This will be DATE-OBS + ( last LST - first LST ). */ tableSize = gsdVars->nScanVars1 * gsdVars->nScan; tableDims = gsdVars->nScanVars1; tableElement = 0; /* In case unfilled table */ if ( (tableSize-tableDims) >= 0 ) { tableElement = tableSize-tableDims; } dLST = ( gsdVars->scanTable1[tableElement] - gsdVars->scanTable1[0] ) / 24.0; /* Correct for difference between solar and sidereal time. */ utcEnd = utcStart + ( dLST / SOLSID ); tempString = astFormat ( tempFrame, 1, utcEnd ); /* Copy the UTC date string. */ strncpy ( dateVars->dateEnd, tempString, 10 ); dateVars->dateEnd[10] = 'T'; strcpy ( &(dateVars->dateEnd[11]), &(tempString[11]) ); /* Get the LSTstart. */ hour = (int) gsdVars->scanTable1[0]; min = 60.0 * fmodf( gsdVars->scanTable1[0], 1.0 ); sec = 60.0 * fmodf( 60.0*gsdVars->scanTable1[0], 1.0 ); sprintf ( dateVars->LSTstart, "%02d:%02d:%07.4f", hour, min, sec ); /* Get the LSTend. */ hour = (int) gsdVars->scanTable1[tableElement]; min = 60.0 * fmodf( gsdVars->scanTable1[tableElement], 1.0 ); sec = 60.0 * fmodf( 60.0*gsdVars->scanTable1[tableElement], 1.0 ); sprintf ( dateVars->LSTend, "%02d:%02d:%07.4f", hour, min, sec ); /* Get the HSTstart and HSTend. */ HSTstart = utcStart - 10.0 / 24.0; HSTend = utcEnd - 10.0 / 24.0; tempString = astFormat ( tempFrame, 1, HSTstart ); /* Copy the HST date string. */ strncpy ( dateVars->HSTstart, tempString, 10 ); dateVars->HSTstart[10] = 'T'; strcpy ( &(dateVars->HSTstart[11]), &(tempString[11]) ); tempString = astFormat ( tempFrame, 1, HSTend ); /* Copy the HST date string. */ strncpy ( dateVars->HSTend, tempString, 10 ); dateVars->HSTend[10] = 'T'; strcpy ( &(dateVars->HSTend[11]), &(tempString[11]) ); }
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 smf_flat_malloc( size_t nheat, const smfData * refdata, smfData **powvald, smfData **bolvald, int *status ) { size_t rowidx = SC2STORE__ROW_INDEX; size_t colidx = SC2STORE__COL_INDEX; double * bolval = NULL; /* Data array inside bolrefd */ double * bolvalvar = NULL; /* Variance inside bolrefd */ dim_t dims[] = { 1, 1, 1 }; /* Default dimensions */ smfHead * hdr = NULL; /* New header */ int lbnd[] = { 1, 1, 1 }; /* Default pixel lower bounds */ size_t nelem = 0; /* Number of elements in first two dimensions of refdims */ smfHead * oldhdr = NULL; /* header from refdata */ void *pntr[] = { NULL, NULL }; /* pointers for smfData */ double * powval = NULL; /* Data array inside powrefd */ const char *dom; /* Domain of axis 1 */ AstFrameSet *new_fs; /* New FrameSet for returned *bolvald */ AstMapping *map; /* Mapping from pixel index 3 to heater index */ AstFrame *frm; /* Frame describing heater index */ int ubnd[ 1 ]; /* Upper bound on heater index */ if (bolvald) *bolvald = NULL; if (powvald) *powvald = NULL; if ( *status != SAI__OK ) return; if ( !bolvald && !powvald) { *status = SAI__ERROR; errRep( "", "Must provide at least one non-NULL pointer to smf_flat_malloc" " (possible programming error)", status ); return; } /* Sanity check */ if ( nheat == 0 ) { *status = SAI__ERROR; errRep( "", "No flatfield information present for creating new smfData", status ); return; } if ( !smf_validate_smfData( refdata, 1, 0, status ) ) return; oldhdr = refdata->hdr; if (powvald) { powval = astCalloc( nheat, sizeof(*powval) ); pntr[0] = powval; pntr[1] = NULL; dims[0] = nheat; *powvald = smf_construct_smfData( NULL, NULL, NULL, NULL, NULL, SMF__DOUBLE, pntr, NULL, SMF__QFAM_NULL, NULL, 0, 1, dims, NULL, 1, 0, 0, NULL, NULL, status ); } if (bolvald) { /* Handle data ordering */ if ( ! refdata->isTordered ) { rowidx++; colidx++; } nelem = refdata->dims[rowidx] * refdata->dims[colidx]; bolval = astCalloc( nheat * nelem, sizeof(*bolval) ); bolvalvar = astCalloc( nheat * nelem, sizeof(*bolvalvar) ); pntr[0] = bolval; pntr[1] = bolvalvar; dims[SC2STORE__ROW_INDEX] = refdata->dims[rowidx]; dims[SC2STORE__COL_INDEX] = refdata->dims[colidx]; dims[2] = nheat; lbnd[SC2STORE__ROW_INDEX] = refdata->lbnd[rowidx]; lbnd[SC2STORE__COL_INDEX] = refdata->lbnd[colidx]; lbnd[2] = 1; /* Create a header to attach to the bolometer data. We only want the basic 2-d information to propagate. */ hdr = smf_construct_smfHead( NULL, oldhdr->instrument, NULL, NULL, astCopy( oldhdr->fitshdr ), NULL, 0, oldhdr->instap, nheat, oldhdr->steptime, oldhdr->scanvel, oldhdr->obsmode, oldhdr->swmode, oldhdr->obstype, oldhdr->seqtype, oldhdr->inbeam, 0, NULL, NULL, NULL, NULL, 0, NULL, "Flatfield measurement", "Response", oldhdr->units, oldhdr->telpos, NULL, oldhdr->obsidss, status ); *bolvald = smf_construct_smfData( NULL, NULL, hdr, NULL, NULL, SMF__DOUBLE, pntr, NULL, SMF__QFAM_TSERIES, NULL, 0, 1, dims, lbnd, 3, 0, 0, NULL, NULL, status ); /* Assign a 3D WCS FRameSet in which the third axis represents heater value index (note, not actual heater value, since we do not yet know what the heater values are). First split the supplied time-series WCS FrameSet to extract a FrameSet in which the current Frame contains only the axes within the ame Domain as the first axis (this is safe because the first axis is always a spatial axis). */ if( oldhdr->tswcs ) { dom = astGetC( oldhdr->tswcs, "Domain(1)" ); new_fs = atlFrameSetSplit( oldhdr->tswcs, dom, NULL, NULL, status ); /* Check this FrameSet is 2D, and if so, add in a third axis describing heater value index. */ if( new_fs && astGetI( new_fs, "Naxes" ) == 2 ) { map = (AstMapping *) astUnitMap( 1, " " ); frm = astFrame( 1, "Domain=HEATER_INDEX" ); ubnd[ 0 ] = nheat; atlAddWcsAxis( new_fs, map, frm, NULL, ubnd, status ); map = astAnnul( map ); frm = astAnnul( frm ); /* Hand over the FrameSet pointer to the returned smfData. */ (*bolvald)->hdr->tswcs = new_fs; } } } return; }
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 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_flat_malloc( size_t nheat, const smfData * refdata, smfData **powvald, smfData **bolvald, int *status ) { size_t rowidx = SC2STORE__ROW_INDEX; size_t colidx = SC2STORE__COL_INDEX; double * bolval = NULL; /* Data array inside bolrefd */ double * bolvalvar = NULL; /* Variance inside bolrefd */ dim_t dims[] = { 1, 1, 1 }; /* Default dimensions */ smfHead * hdr = NULL; /* New header */ int lbnd[] = { 1, 1, 1 }; /* Default pixel lower bounds */ size_t nelem = 0; /* Number of elements in first two dimensions of refdims */ smfHead * oldhdr = NULL; /* header from refdata */ void *pntr[] = { NULL, NULL }; /* pointers for smfData */ double * powval = NULL; /* Data array inside powrefd */ if (bolvald) *bolvald = NULL; if (powvald) *powvald = NULL; if ( *status != SAI__OK ) return; if ( !bolvald && !powvald) { *status = SAI__ERROR; errRep( "", "Must provide at least one non-NULL pointer to smf_flat_malloc" " (possible programming error)", status ); return; } /* Sanity check */ if ( nheat == 0 ) { *status = SAI__ERROR; errRep( "", "No flatfield information present for creating new smfData", status ); return; } if ( !smf_validate_smfData( refdata, 1, 0, status ) ) return; oldhdr = refdata->hdr; if (powvald) { powval = astCalloc( nheat, sizeof(*powval) ); pntr[0] = powval; pntr[1] = NULL; dims[0] = nheat; *powvald = smf_construct_smfData( NULL, NULL, NULL, NULL, NULL, SMF__DOUBLE, pntr, NULL, SMF__QFAM_NULL, NULL, 0, 1, dims, NULL, 1, 0, 0, NULL, NULL, status ); } if (bolvald) { /* Handle data ordering */ if ( ! refdata->isTordered ) { rowidx++; colidx++; } nelem = refdata->dims[rowidx] * refdata->dims[colidx]; bolval = astCalloc( nheat * nelem, sizeof(*bolval) ); bolvalvar = astCalloc( nheat * nelem, sizeof(*bolvalvar) ); pntr[0] = bolval; pntr[1] = bolvalvar; dims[SC2STORE__ROW_INDEX] = refdata->dims[rowidx]; dims[SC2STORE__COL_INDEX] = refdata->dims[colidx]; dims[2] = nheat; lbnd[SC2STORE__ROW_INDEX] = refdata->lbnd[rowidx]; lbnd[SC2STORE__COL_INDEX] = refdata->lbnd[colidx]; lbnd[2] = 1; /* Create a header to attach to the bolometer data. We only want the basic 2-d information to propagate. */ hdr = smf_construct_smfHead( NULL, oldhdr->instrument, NULL, NULL, astCopy( oldhdr->fitshdr ), NULL, 0, oldhdr->instap, nheat, oldhdr->steptime, oldhdr->scanvel, oldhdr->obsmode, oldhdr->swmode, oldhdr->obstype, oldhdr->seqtype, oldhdr->inbeam, 0, NULL, NULL, NULL, NULL, 0, NULL, "Flatfield measurement", "Response", oldhdr->units, oldhdr->telpos, NULL, oldhdr->obsidss, status ); *bolvald = smf_construct_smfData( NULL, NULL, hdr, NULL, NULL, SMF__DOUBLE, pntr, NULL, SMF__QFAM_TSERIES, NULL, 0, 1, dims, lbnd, 3, 0, 0, NULL, NULL, status ); } return; }
void smf_flat_write( smf_flatmeth flatmeth, const char * flatname, double refres, const smfData * bolval, const smfData * powref, const smfData * bolref, const smfData * polyfit, const Grp * prvgrp, int * status ) { size_t colsize; /* number of columns */ double *dbuf = NULL; /* input double buffer for mean data */ double *dvar = NULL; /* input double buffer for variance of data */ char fitsrec[SC2STORE__MAXFITS*SZFITSCARD+1]; /* Store for FITS records */ int *ibuf = NULL; /* int buffer for mean data */ int indf = NDF__NOID; /* NDF identifier for output file */ size_t ncards; /* number of fits cards */ size_t numbols; /* number of bolometers */ double *outvar = NULL; /* buffer for variance of data */ int place = NDF__NOPL; /* Dummy placeholder for NDF */ size_t rowsize; /* number of rows */ JCMTState *state = NULL; /* State for this flatfield */ sc2ast_subarray_t subnum; /* subarray number */ AstFrameSet *result, *spacefset; AstLutMap *heatmap; AstFrame *heatfrm; int *dksquid; /* pointer to dummy dark SQUID data */ size_t j; /* loop counter */ int jig_vert[1][2]; /* dummy jiggle vertices */ double jig_path[1][2]; /* dummy jiggle path */ size_t nframes = 0; /* Number of frames in bolval */ int npath = 0; /* size of jiggle path */ int nvert = 0; /* number of jiggle vertices */ char *xmlfile = NULL; /* dummy xmlfile name */ if (*status != SAI__OK) return; if (!bolval->da) { *status = SAI__ERROR; errRep( "", "No flatfield solution provided for writing", status ); return; } if (!bolval->da->heatval) { *status = SAI__ERROR; errRep( "", "Must provide heater values in DA struct to smf_flat_write" " (possible programming error)", status ); return; } /* note that colsize is the number of rows and rowsize is the number of columns */ colsize = (bolval->dims)[SC2STORE__ROW_INDEX]; rowsize = (bolval->dims)[SC2STORE__COL_INDEX]; numbols = colsize * rowsize; nframes = (bolval->dims)[2]; /* Make sure we have a FLAT header that reflects this file as the flatfield solution */ smf_fits_updateS( bolval->hdr, "FLAT", flatname, "Name of flat-field file", status ); /* Create a FITS header for DA */ smf_fits_export2DA( bolval->hdr->fitshdr, &ncards, fitsrec, status ); /* Copy the data as integers so it can be written to data file. To prevent overflow in the variance we store that as doubles */ ibuf = astMalloc( (numbols * nframes)*sizeof(*ibuf) ); outvar = astMalloc( (numbols * nframes)*sizeof(*outvar) ); dbuf = (bolval->pntr)[0]; dvar = (bolval->pntr)[1]; if (*status == SAI__OK) { for (j = 0; j < (nframes * numbols); j++) { /* These started off as integers so the mean value must fit in an integer */ if ( dbuf[j] == VAL__BADD) { ibuf[j] = VAL__BADI; } else { ibuf[j] = (int)dbuf[j]; } /* Same data type so no need to convert bad values */ if (dvar) { outvar[j] = dvar[j]; } else { outvar[j] = VAL__BADD; } } } /* get subarray number */ smf_find_subarray( bolval->hdr, NULL, 0, &subnum, status ); /* Create dummy components for output file */ dksquid = astCalloc ( rowsize* nframes, sizeof(*dksquid) ); jig_vert[0][0] = 0; jig_vert[0][1] = 0; jig_path[0][0] = 0.0; jig_path[0][1] = 0.0; sc2store_setcompflag ( SC2STORE__NONE, status ); sc2store_wrtstream ( flatname, subnum, ncards, fitsrec, colsize, rowsize, nframes, (bolref->dims)[2], refres, 0, smf_flat_methstring( flatmeth, status ), bolval->hdr->allState, NULL, ibuf, dksquid, (bolref->pntr)[0], (powref->pntr)[0], "FLATCAL", NULL, NULL, jig_vert, nvert, jig_path, npath, xmlfile, status ); sc2store_free ( status ); /* To copy in the variance and modify fix up the WCS we need to reopen the file */ ndfOpen( NULL, flatname, "UPDATE", "OLD", &indf, &place, status ); /* make sure that history is not written twice */ ndfHsmod( "SKIP", indf, status ); if (outvar) { void *pntr[3]; int el; ndfStype( "_DOUBLE", indf, "VARIANCE", status ); ndfMap( indf, "VARIANCE", "_DOUBLE", "WRITE", pntr, &el, status ); if (*status == SAI__OK) { memcpy( pntr[0], outvar, sizeof(*outvar)*el ); } } /* For the WCS a time frame is less relevant than heater settings */ astBegin; /* Create frame for focal plane coordinates */ sc2ast_createwcs( subnum, NULL, NULL, NULL, NO_FTS, &spacefset, status ); /* Copy it to make sure we do not mess with the cache */ result = astCopy( spacefset ); /* and switch to BOLO frame which is best for bolometer analysis */ { int frnum = AST__NOFRAME; kpg1Asffr( result, "BOLO", &frnum, status ); if (frnum != AST__NOFRAME) astSetI( result, "CURRENT", frnum ); } /* Create a simple frame for heater settings */ heatfrm = astFrame( 1, "Domain=HEATER,Label(1)=Heater Setting" ); heatmap = astLutMap( nframes, bolval->da->heatval, 1.0, 1.0, " " ); /* Append the heater axis to the spatial frameset */ atlAddWcsAxis( result, (AstMapping *)heatmap, (AstFrame *) heatfrm, NULL, NULL, status ); /* write it to the NDF */ ndfPtwcs( result, indf, status ); /* Write provenance information */ if (prvgrp) { size_t size = grpGrpsz( prvgrp, status ); char prvname[ 2 * PAR__SZNAM + 1]; smf_get_taskname( NULL, prvname, status ); for (j=1; j<=size; j++) { smf_accumulate_prov( NULL, prvgrp, j, indf, prvname, NULL, status ); } } /* Write the polynomial expansion into an extension */ if (polyfit) { char fitfile[GRP__SZNAM+1]; int fndf = NDF__NOID; place = NDF__NOPL; one_strlcpy( fitfile, flatname, sizeof(fitfile), status ); one_strlcat( fitfile, ".MORE.SMURF.FLATFIT", sizeof(fitfile), status ); /* create the file */ smf_write_smfData( polyfit, NULL, fitfile, NULL, 0, NDF__NOID, MSG__VERB, 0, status ); /* Same WCS as the main file */ ndfOpen( NULL, fitfile, "UPDATE", "OLD", &fndf, &place, status ); ndfPtwcs( result, fndf, status ); ndfAnnul( &fndf, status ); } astEnd; ndfAnnul( &indf, status); if (ibuf) ibuf = astFree( ibuf ); if (outvar) outvar = astFree( outvar ); if (dksquid) dksquid = astFree( dksquid ); if (state) state = astFree( state ); }
bool Hero::setSkyCS(Hero::SKYCS skyCS) { if( m_currentSkyCs == skyCS) { // nothing to do return true; } if( m_ast.origWcsInfo == AST__NULL) { addError( "No wcsinfo from astlib"); return false; } AstErrorGuard guard( this); // intercept errors AstGCGuard gcguard; // make a clone void * newWcsInfo = AST__NULL; if( m_originalSkyCs == skyCS) { // original skycs is the same as requested skycs, so // clone only a pointer to the original frame newWcsInfo = astClone( m_ast.origWcsInfo); } else { // otherwise make a full clone of the frame and set it's system newWcsInfo = astCopy( m_ast.origWcsInfo); if( ! astOK) { addError( "Could not clone the original astFrameSet"); return false; } // set the new system for the clone AstWrappers::set( newWcsInfo, QString( "System=%1").arg( skycs2string(skyCS))); if( ! astOK) { addError( "Could not convert to this coordinate system " + skycs2string( skyCS)); return false; } astClear( newWcsInfo, "Epoch,Equinox"); } // free up the old ast pointer if( m_ast.currWcsInfo != AST__NULL) { astAnnul( m_ast.currWcsInfo); m_ast.currWcsInfo = AST__NULL; } m_ast.currWcsInfo = newWcsInfo; astExempt( m_ast.currWcsInfo); m_currentSkyCs = skyCS; parseAxesInfo(); QString title = AstWrappers::getC( m_ast.currWcsInfo, "Title(1)"); dbg(1) << "Title = " << title << "[" << AstWrappers::getC( m_ast.currWcsInfo, "LonAxis") << "," << AstWrappers::getC( m_ast.currWcsInfo, "LatAxis") << "]"; // clear up ast errors if any astClearStatus; return true; }
HDSLoc *cupidGaussClumps( int type, int ndim, int *slbnd, int *subnd, void *ipd, double *ipv, double rms, AstKeyMap *config, int velax, double beamcorr[ 3 ], int *status ){ /* *+ * Name: * cupidGaussClumps * Purpose: * Identify clumps of emission within a 1, 2 or 3 dimensional NDF using * the GAUSSCLUMPS algorithm. * Language: * Starlink C * Synopsis: * HDSLoc *cupidGaussClumps( int type, int ndim, int *slbnd, int *subnd, * void *ipd, double *ipv, double rms, * AstKeyMap *config, int velax, * double beamcorr[ 3 ], int *status ) * Description: * This function identifies clumps within a 1, 2 or 3 dimensional data * array using the GAUSSCLUMPS algorithm, described by Stutski & Gusten * (1990, ApJ 356, 513). This algorithm proceeds by fitting a Gaussian * profile to the brightest peak in the data. It then subtracts the fit * from the data and iterates, fitting a new ellipse to the brightest peak * in the residuals. This continues until a termination criterion is * reached. The main termination criterion in this implementation is * not quite the same as in the Stutski & Gusten paper. They had two main * termination criteria; 1) the total data sum of the fitted gaussians * is close to the total data sum of the original data, and 2) the peak * residual is less than a given multiple of the RMS noise in the data. * However, 1) is very sensitive to errors in the estimation of the * background level in the data, and 2) may never be achieved because * the expected residuals depend not only on the RMS noise in the data * but also on how accurately gaussian the clumps are, which is not * known. Therefore, this implementation instead terminates when the * peak amplitude of the fitted clumps falls below a given fraction of * the first (i.e. largest) fitted peak. * * Two additional termination criteria are used; 1) If there are many * failed attempts to fit a clump to the peak residual or if 2) a * specified maximum number of clumps are found, then the process * terminates early. * Parameters: * type * An integer identifying the data type of the array values pointed to * by "ipd". Must be either CUPID__DOUBLE or CUPID__FLOAT (defined in * cupid.h). * ndim * The number of dimensions in the data array. Must be 1, 2 or 3. * slbnd * Pointer to an array holding the lower pixel index bound of the * data array on each axis. * subnd * Pointer to an array holding the upper pixel index bound of the * data array on each axis. * ipd * Pointer to the data array. The elements should be stored in * Fortran order. The data type of this array is given by "itype". * ipv * Pointer to the input Variance array, or NULL if there is no Variance * array. The elements should be stored in Fortran order. The data * type of this array is "double". * rms * The default value for the global RMS error in the data array. * config * An AST KeyMap holding tuning parameters for the algorithm. * velax * The index of the velocity axis in the data array (if any). Only * used if "ndim" is 3. * beamcorr * An array in which is returned the FWHM (in pixels) describing the * instrumental smoothing along each pixel axis. The clump widths * stored in the output catalogue are reduced to correct for this * smoothing. * status * Pointer to the inherited status value. * Notes: * - The specific form of algorithm used here is informed by a Fortran * implementation of GaussClumps obtained on 27/9/05 from * ftp.astro.uni-bonn.de/pub/heith/gaussclumps. * - Most of the "cupid..." functions used in this file which start * with a "type" parameter (e.g. cupidFindMax, cupidUpdateArrays, etc) are * actually not functions at all, but macros defined in cupid.h. These * macros are wrappers which invoke a type-specific function (e.g. * cupidFindMaxD, cupidFindMaxF) appropriate to the specific data type * being used (as indicated by the "type" parameter). Macros are used in * order to simplify the code here, and thus make the flow of the * algorithm clearer. The source code for the type-specific functions * are generated automatically at build time from equivalent files which * have file type ".cupid". For instance, the files cupidfindmaxD.c and * cupidfindmaxF.c are generated automatically from cupidfindmax.cupid. * Also, the rlevant macros definitions and prototypes within cupid.h * are generated automatically at build time from these ".cupid" files. * Returned Value: * A locator for a new HDS object which is an array of NDF structures. * Each NDF will hold the data values associated with a single clump and * will be the smallest possible NDF that completely contains the * corresponding clump. Pixels not in the clump will be set bad. The * pixel origin is set to the same value as the supplied NDF. * Copyright: * Copyright (C) 2009 Science & Technology Facilities Council. * 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: * 29-SEP-2005 (DSB): * Original version. * 7-MAR-2007 (DSB): * Use VELORES instead of FWHMBEAM if the data is 1D. * 7-MAR-2007 (DSB): * Guard against segvio caused by use of null pointers that are * returned by astMalloc if an error has occurred. * 14-JAN-2009 (TIMJ): * Use MERS for message filtering. * {enter_further_changes_here} * Bugs: * {note_any_bugs_here} *- */ /* Local Variables: */ AstKeyMap *gcconfig; /* Configuration parameters for this algorithm */ char buf[30]; /* File name buffer */ HDSLoc *ret; /* Locator for the returned array of NDFs */ double *peaks; /* Holds the "npeak" most recently fitted peak values */ double chisq; /* Chi-squared value of most recently fitted Gaussian */ double mean_peak; /* The mean of the values within "peaks" */ double mlim; /* Truncation level for Gaussians */ double new_peak; /* The value most recently added to "peaks" */ double nsig; /* No.of standard deviations at which to reject peaks */ double old_peak; /* The oldest value within "peaks" */ double peak_thresh; /* The lower threshold for clump peak values */ double sigma_peak; /* The standard deviation of the values within "peaks" */ double sumdata; /* Sum of the supplied data values */ double sum_peak2; /* Sum of the squares of the values in "peaks" */ double sum_peak; /* Sum of the values in "peaks" */ double sumclumps; /* Sum of the values in all the used clumps so far */ double x[ CUPID__GCNP3 ]; /* Parameters describing new Gaussian clump */ int *dims; /* Pointer to array of array dimensions */ int allbad; /* Are all the residuals bad? */ int area; /* Number of pixels contributing to the clump */ int area_thresh; /* The lower threshold for clump areas */ int excols; /* Are extra output columns required? */ int el; /* Number of elements in array */ size_t i; /* Loop count */ size_t iclump; /* Number of clumps found so far */ int imax; /* Index of element with largest residual */ size_t ipeak; /* Index within "peaks" at which to store the new peak */ int iter; /* Continue finding more clumps? */ double maxbad; /* Max fraction of bad pixels allowed in a clump */ size_t maxclump; /* Max no. of clumps */ int maxskip; /* Max no. of failed fits between good fits */ size_t nclump; /* Number of usable clumps */ int niter; /* Iterations performed so far */ int npad; /* No. of peaks below threshold for temination */ size_t npeak; /* The number of elements in the "peaks" array. */ int nskip; /* No. of failed fits since last good fit */ int area_below; /* Count of consecutive clump areas below the threshold */ int peaks_below; /* Count of consecutive peaks below the threshold */ void *res; /* Pointer to residuals array */ /* Initialise */ ret = NULL; /* Abort if an error has already occurred. */ if( *status != SAI__OK ) return ret; /* Initialise things to avoid compiler warnings. */ mean_peak = 0.0; sigma_peak = 0.0; new_peak = 0.0; /* Say which method is being used. */ msgBlankif( MSG__NORM, status ); msgOutif( MSG__NORM, "", "GaussClumps:", status ); msgBlankif( MSG__VERB, status ); /* Get the AST KeyMap holding the configuration parameters for this algorithm. */ if( !astMapGet0A( config, "GAUSSCLUMPS", (AstObject *) &gcconfig ) ) { gcconfig = astKeyMap( " " ); astMapPut0A( config, "GAUSSCLUMPS", gcconfig, " " ); } /* The configuration file can optionally omit the algorithm name. In this case the "config" KeyMap may contain values which should really be in the "gcconfig" KeyMap. Add a copy of the "config" KeyMap into "gcconfig" so that it can be searched for any value which cannot be found in the "gcconfig" KeyMap. */ astMapPut0A( gcconfig, CUPID__CONFIG, astCopy( config ), NULL ); /* Return the instrumental smoothing FWHMs. For 1D data, we assume the axis is spectral and so use VELORES instead of FWHMBEAM. */ if( ndim == 1 ) { beamcorr[ 0 ] = cupidConfigD( gcconfig, "VELORES", 2.0, status ); } else { beamcorr[ 0 ]= cupidConfigD( gcconfig, "FWHMBEAM", 2.0, status ); beamcorr[ 1 ] = beamcorr[ 0 ]; if( ndim == 3 ) { beamcorr[ 2 ] = beamcorr[ 0 ]; beamcorr[ velax ]= cupidConfigD( gcconfig, "VELORES", 2.0, status ); } } /* See if extra diagnostic info is required. */ excols = cupidConfigI( gcconfig, "EXTRACOLS", 0, status ); /* Get the maximum allowed number of failed fits between succesful fits. */ maxskip = cupidConfigI( gcconfig, "MAXSKIP", 10, status ); /* Get the maximum allowed number of failed fits between succesful fits. */ maxclump = cupidConfigI( gcconfig, "MAXCLUMPS", VAL__MAXI, status ); /* The iterative process ends when "npad" consecutive clumps all had peak values below "peak_thresh" or all had areas below "area_thresh". */ npad = cupidConfigI( gcconfig, "NPAD", 10, status ); /* Get the RMS noise level to use. */ rms = cupidConfigD( gcconfig, "RMS", rms, status ); /* Find the size of each dimension of the data array, and the total number of elements in the array. We use the memory management functions of the AST library since they provide greater security and functionality than direct use of malloc, etc. */ dims = astMalloc( sizeof( *dims )*(size_t) ndim ); el = 1; if( dims ) { for( i = 0; i < (size_t)ndim; i++ ) { dims[ i ] = subnd[ i ] - slbnd[ i ] + 1; el *= dims[ i ]; } } /* Copy the supplied data array into a work array which will hold the residuals remaining after subtraction of the fitted Gaussians. The cupidStore macro is a wrapper around the astStore function. */ res = cupidStore( NULL, ipd, el, type, "cupidGaussClumps" ); if( res ) { /* Set the lower threshold for clump peaks to a user-specified multiple of the RMS noise. */ peak_thresh = cupidConfigD( gcconfig, "THRESH", 2.0, status ); /* Set the lower threshold for clump area to a user-specified number of pixels. */ area_thresh = cupidConfigI( gcconfig, "MINPIX", 3, status ); /* Get the lowest value (normalised to the RMS noise level) at which model Gaussians should be evaluated. */ mlim = cupidConfigD( gcconfig, "MODELLIM", 0.5, status ); /* Get the max allowed fraction of bad pixels in a clump. */ maxbad = cupidConfigD( gcconfig, "MAXBAD", 0.05, status ); /* Initialise the number of clumps found so far. */ iclump = 0; /* Indicate that no peaks have been found below the lower threshold for clump peak values, or below the lower area threshold. */ peaks_below = 0; area_below = 0; /* Initialise the variables used to keep track of the mean and standard deviation of the most recent "npeak" fitted peak values. */ nsig = cupidConfigD( gcconfig, "NSIGMA", 3.0, status ); npeak = cupidConfigI( gcconfig, "NPEAK", 9, status ); ipeak = 0; sum_peak = 0.0; sum_peak2 = 0.0; iter = 1; niter = 0; nskip = 0; sumclumps = 0.0; sumdata = VAL__BADD; peaks = astMalloc( sizeof( *peaks )*npeak ); if( peaks ) { for( i = 0; i < npeak; i++ ) peaks[ i ] = 0.0; /* Use the setjmp function to define here to be the place to which the signal handling function will jump when a signal is detected. Zero is returned on the first invocation of setjmp. If a signal is detected, a jump is made into setjmp which then returns a positive signal identifier. */ if( setjmp( CupidGCHere ) ) { iter = 0; msgBlankif( MSG__QUIET, status ); msgOutif( MSG__QUIET, "", "Interupt detected. Clumps found so far will be saved", status ); msgBlankif( MSG__QUIET, status ); } } /* Set up a signal handler for the SIGINT (interupt) signal. If this signal occurs, the function "cupidGCHandler" will be called. */ signal( SIGINT, cupidGCHandler ); /* Loop round fitting a gaussian to the largest remaining peak in the residuals array. */ while( iter && *status == SAI__OK ) { /* Report the iteration number to the user if required. */ ++niter; msgBlankif( MSG__DEBUG1, status ); msgOutiff( MSG__DEBUG1, "", "Iteration %d:", status, niter ); /* Find the 1D vector index of the elements with the largest value in the residuals array. */ allbad = cupidGCFindMax( type, res, el, &imax, &sumdata, status ); /* Finish iterating if all the residuals are bad, or if too many iterations have been performed since the last succesfully fitted clump. */ if( allbad ) { iter = 0; niter--; msgBlankif( MSG__DEBUG, status ); msgOutif( MSG__DEBUG1, "", "There are no good pixels left to be fitted.", status ); msgBlankif( MSG__DEBUG1, status ); } else if( nskip > maxskip ){ iter = 0; niter--; msgBlankif( MSG__DEBUG, status ); msgOutiff( MSG__DEBUG1, "", "The previous %d fits were unusable.", status, maxskip ); msgBlankif( MSG__DEBUG1, status ); } /* If not, make an initial guess at the Gaussian clump parameters centred on the current peak. */ if( iter ) { cupidGCSetInit( type, res, ipv, ndim, dims, imax, rms, gcconfig, ( niter == 1 ), velax, x, slbnd, status ); /* Find the best fitting parameters, starting from the above initial guess. This returns a function value of zero if no fit could be performed. */ if( cupidGCFit( type, res, imax, x, &chisq, status ) ) { /* Skip this fit if we have an estimate of the standard deviation of the "npeak" most recent clump peak values, and the peak value of the clump just fitted is a long way (more than NSIGMA standard deviations) from the peak value of the previously fitted clump. Also skip it if the peak value is less than the "mlim" value. */ if( ( npeak == 0 || iclump < npeak || fabs( x[ 0 ] - new_peak ) < nsig*sigma_peak ) && x[ 0 ] > mlim ) { /* Record the new peak value for use with the next peak, and update the standard deviation of the "npeak" most recent peaks. These values are stored cyclically in the "peaks" array. */ if( npeak > 0 ) { new_peak = x[ 0 ]; old_peak = peaks[ ipeak ]; peaks[ ipeak ] = new_peak; if( ++ipeak == npeak ) ipeak = 0; sum_peak += new_peak - old_peak; sum_peak2 += new_peak*new_peak - old_peak*old_peak; if( sum_peak2 < 0.0 ) sum_peak2 = 0.0; mean_peak = sum_peak/npeak; sigma_peak = sqrt( sum_peak2/npeak - mean_peak*mean_peak ); } /* Increment the number of peaks found. */ iclump++; /* Reset the number of failed fits since the last good fit. */ nskip = 0; /* Remove the model fit (excluding the background) from the residuals array. This also creates an NDF containing the data values associated with the clump. This NDF is stored in the HDS array of NDFs in the returned HDS object. The standard deviation of the new residuals is returned. */ cupidGCUpdateArrays( type, res, ipd, el, ndim, dims, x, rms, mlim, imax, peak_thresh, slbnd, &ret, iclump, excols, mean_peak, maxbad, &area, &sumclumps, status ); /* Dump the modified residuals if required. */ sprintf( buf, "residuals%lu", iclump ); cupidGCDump( type, MSG__DEBUG3, res, ndim, dims, buf, status ); /* Display the clump parameters on the screen if required. */ cupidGCListClump( iclump, ndim, x, chisq, slbnd, rms, status ); /* If this clump has a peak value which is below the threshold, increment the count of consecutive clumps with peak value below the threshold. Otherwise, reset this count to zero. */ if( x[ 0 ] < peak_thresh ) { peaks_below++; } else { peaks_below = 0; } /* If this clump has an area which is below the threshold, increment the count of consecutive clumps with area below the threshold. Otherwise, reset this count to zero. */ if( area < area_thresh ) { area_below++; } else { area_below = 0; } /* If the maximum number of clumps have now been found, exit.*/ if( iclump == maxclump ) { iter = 0; msgBlankif( MSG__DEBUG, status ); msgOutiff( MSG__DEBUG1, "", "The specified maximum number of " "clumps (%lu) have been found.", status, maxclump ); msgBlankif( MSG__DEBUG1, status ); /* If the integrated data sum in the fitted gaussians exceeds or equals the integrated data sum in th einput, exit. */ } else if( sumclumps >= sumdata ) { iter = 0; msgBlankif( MSG__DEBUG, status ); msgOutiff( MSG__DEBUG1,"", "The total data sum of the fitted " "Gaussians (%g) has reached the total " "data sum in the supplied data (%g).", status, (float)sumclumps, (float)sumdata ); msgBlankif( MSG__DEBUG1, status ); /* If the count of consecutive peaks below the threshold has reached "Npad", terminate. */ } else if( peaks_below == npad ) { iter = 0; msgBlankif( MSG__DEBUG, status ); msgOutiff( MSG__DEBUG1, "", "The previous %d clumps all had peak " "values below the threshold.", status, npad ); msgBlankif( MSG__DEBUG1, status ); /* If the count of consecutive clumps with area below the threshold has reached "Npad", terminate. */ } else if( area_below == npad ) { iter = 0; msgBlankif( MSG__DEBUG, status ); msgOutiff( MSG__DEBUG1, "", "The previous %d clumps all had areas " "below the threshold.", status, npad ); msgBlankif( MSG__DEBUG1, status ); } /* If the peak value fitted is very different from the previous fitted peak value, set the residuals array element bad in order to prevent the algorithm from trying to fit a peak to the same pixel again. */ } else { if( type == CUPID__DOUBLE ) { ((double *)res)[ imax ] = VAL__BADD; } else { ((float *)res)[ imax ] = VAL__BADR; } new_peak = 0.5*( new_peak + x[ 0 ] ); nskip++; msgOutif( MSG__DEBUG1, "", " Clump rejected due to " "aberrant peak value.", status ); } /* Tell the user if no clump could be fitted around the current peak pixel value */ } else { nskip++; msgOutif( MSG__DEBUG1, "", " No clump fitted.", status ); /* Set the specified element of the residuals array bad if no fit was performed. This prevents the any subsequent attempt to fit a Gaussian to the same peak value.*/ if( type == CUPID__DOUBLE ) { ((double *)res)[ imax ] = VAL__BADD; } else { ((float *)res)[ imax ] = VAL__BADR; } } /* Tell the user if one of the trmination criteria has ben met. */ } else { msgOutif( MSG__DEBUG1, "", " At least one termination criterion has been reached.", status ); msgBlankif( MSG__DEBUG1, status ); } } /* Tell the user how clumps are being returned. */ if( ret ) { datSize( ret, &nclump, status ); } else { nclump = 0; } if( nclump == 0 ) msgOutif( MSG__NORM, "", "No usable clumps found.", status ); if( iclump - nclump == 1 ) { msgOutif( MSG__NORM, "", "1 clump rejected because it touches an edge of " "the data array.", status ); } else if( iclump - nclump > 1 ) { msgOutiff( MSG__NORM, "", "%d clumps rejected because they touch an edge of " "the data array.", status, (int)( iclump - nclump ) ); } /* Tell the user how many iterations have been performed (i.e. how many attempts there have been to fit a Gaussian peak). */ if( niter == 1 ){ msgOutif( MSG__DEBUG1, "", "No fit attempted.", status ); } else { msgOutiff( MSG__DEBUG1, "", "Fits attempted for %d candidate clumps (%d failed).", status, (int)( niter - iclump ), niter ); } /* Free resources */ peaks = astFree( peaks ); } /* Remove the secondary KeyMap added to the KeyMap containing configuration parameters for this algorithm. This prevents the values in the secondary KeyMap being written out to the CUPID extension when cupidStoreConfig is called. */ astMapRemove( gcconfig, CUPID__CONFIG ); /* Free resources */ res = astFree( res ); dims = astFree( dims ); cupidGC.data = astFree( cupidGC.data ); cupidGC.weight = astFree( cupidGC.weight ); cupidGC.res = astFree( cupidGC.res ); cupidGC.resu = astFree( cupidGC.resu ); cupidGC.initmodel = astFree( cupidGC.initmodel ); cupidGC.model = astFree( cupidGC.model ); cupidGC.resids = astFree( cupidGC.resids ); gcconfig = astAnnul( gcconfig ); /* Return the list of clump NDFs. */ return ret; }
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_fits_outhdr( AstFitsChan * inhdr, AstFitsChan ** outhdr, int * status ) { /* Local Variables: */ AstFitsChan *temphdr = NULL; /* FitsChan holding temporary FITS headers */ /* List of BEGIN headers that are retained even if different */ const char * begin_items[] = { "DATE-OBS", "DUT1", "LOFREQS", "AMSTART", "AZSTART", "ELSTART", "HSTSTART", "LSTSTART", "TSPSTART", "ATSTART", "HUMSTART", "BPSTART", "WNDSPDST", "WNDDIRST", "TAU225ST", "TAUDATST", "WVMTAUST", "WVMDATST", "SEEINGST", "FRLEGTST", "BKLEGTST", "SEQSTART", NULL }; const char * end_items[] = { "DATE-END", "LOFREQE", "AMEND", "AZEND", "ELEND", "HSTEND", "LSTEND", "TSPEND", "ATEND", "HUMEND", "BPEND", "WNDSPDEN", "WNDDIREN", "TAU225EN", "TAUDATEN", "WVMTAUEN", "WVMDATEN", "SEEINGEN", "FRLEGTEN", "BKLEGTEN", "SEQEND", "OBSGEO-X", "OBSGEO-Y", "OBSGEO-Z", NULL }; /* Check inherited status. */ if ( *status != SAI__OK ) return; /* If this is the first file, get a copy of the input NDFs FITS extension (held in a FitsChan). This FitsChan will be used to hold the FITS header for the output NDF. Also remove contiguous blank lines. */ if( *outhdr == NULL ) { *outhdr = astCopy( inhdr ); atlRmblft( *outhdr, status ); /* If this is not the first file, merge the input NDF's FITS extension into the output NDF's FITS extension by removing any headers from the output FITS extension that do not have identical values in the input FITS extension. */ } else { smfHead hdr; double mjdnew = 0.0; double mjdref = 0.0; AstFitsChan * begfits = NULL; AstFitsChan * endfits = NULL; /* need to make sure that the merging will not remove headers that need to be retained covering start and end state. This means that we take a copy of the input header and manually synchronize END/START headers before calling the ATL merge routine. */ /* Do not have access to smfData so need to set one up or duplicate code in smf_find_dateobs */ if (*status == SAI__OK) { hdr.allState = NULL; hdr.fitshdr = inhdr; smf_find_dateobs( &hdr, &mjdnew, NULL, status ); hdr.fitshdr = *outhdr; smf_find_dateobs( &hdr, &mjdref, NULL, status ); if (*status == SMF__NOKWRD) { /* if there is no date information we just do what we can */ errAnnul( status ); mjdnew = 0.0; mjdref = 0.0; } } if (mjdnew < mjdref) { /* input header is older than merged header: Copy beginfits from INPUT to MERGE Copy endfits from MERGE to INPUT */ begfits = astCopy( inhdr ); endfits = astCopy( *outhdr ); } else { /* input header is newer than merged header: Copy beginfits from MERGE to INPUT Copy endfits from INPUT to MERGE Do this even if dates are identical or if we could not read a date. */ begfits = astCopy( *outhdr ); endfits = astCopy( inhdr ); } /* oldfits gets the END items from newfits. newfits gets the BEGIN items from oldfits*/ smf__fits_copy_items( begfits, endfits, begin_items, status ); smf__fits_copy_items( endfits, begfits, end_items, status ); /* now we can merge oldfits and newfits */ atlMgfts( 3, begfits, endfits, &temphdr, status ); (void) astAnnul( begfits ); (void) astAnnul( endfits ); (void) astAnnul( *outhdr ); *outhdr = temphdr; } /* Remove any ASTWARN cards from the output header, but retain them within the input header. Any such warnings in the input header will be displayed when the input NDF is closed using smf_close_file. This helps to track down bugs caused by keywords unintentionally having undefined values in an input NDF. */ astClear( *outhdr, "Card" ); while( astFindFits( *outhdr, "ASTWARN", NULL, 0 ) ){ astDelFits( *outhdr ); } }
void smf_calc_mapcoord( ThrWorkForce *wf, AstKeyMap *config, smfData *data, AstFrameSet *outfset, int moving, int *lbnd_out, int *ubnd_out, fts2Port fts_port, int flags, int *status ) { /* Local Variables */ AstSkyFrame *abskyfrm = NULL;/* Output SkyFrame (always absolute) */ AstMapping *bolo2map=NULL; /* Combined mapping bolo->map coordinates */ int bndndf=NDF__NOID; /* NDF identifier for map bounds */ void *data_pntr[1]; /* Array of pointers to mapped arrays in ndf */ int *data_index; /* Mapped DATA_ARRAY part of NDF */ int docalc=1; /* If set calculate the LUT */ int doextension=0; /* Try to write LUT to MAPCOORD extension */ smfFile *file=NULL; /* smfFile pointer */ AstObject *fstemp = NULL; /* AstObject version of outfset */ int ii; /* loop counter */ int indf_lat = NDF__NOID; /* Identifier for NDF to receive lat values */ int indf_lon = NDF__NOID; /* Identifier for NDF to receive lon values */ smfCalcMapcoordData *job_data=NULL; /* Array of job */ int lbnd[1]; /* Pixel bounds for 1d pointing array */ int lbnd_old[2]; /* Pixel bounds for existing LUT */ int lbnd_temp[1]; /* Bounds for bounds NDF component */ int lutndf=NDF__NOID; /* NDF identifier for coordinates */ AstMapping *map2sky_old=NULL;/* Existing mapping map->celestial coord. */ HDSLoc *mapcoordloc=NULL; /* HDS locator to the MAPCOORD extension */ int nw; /* Number of worker threads */ AstFrameSet *oldfset=NULL; /* Pointer to existing WCS info */ AstSkyFrame *oskyfrm = NULL; /* SkyFrame from the output WCS Frameset */ smfCalcMapcoordData *pdata=NULL; /* Pointer to job data */ double *lat_ptr = NULL; /* Pointer to array to receive lat values */ double *lon_ptr = NULL; /* Pointer to array to receive lon values */ int ubnd[1]; /* Pixel bounds for 1d pointing array */ int ubnd_old[2]; /* Pixel bounds for existing LUT */ int ubnd_temp[1]; /* Bounds for bounds NDF component */ int *lut = NULL; /* The lookup table */ dim_t nbolo=0; /* Number of bolometers */ dim_t ntslice=0; /* Number of time slices */ int nmap; /* Number of mapped elements */ AstMapping *sky2map=NULL; /* Mapping celestial->map coordinates */ size_t step; /* step size for dividing up work */ AstCmpMap *testcmpmap=NULL; /* Combined forward/inverse mapping */ AstMapping *testsimpmap=NULL;/* Simplified testcmpmap */ double *theta = NULL; /* Scan direction at each time slice */ int tstep; /* Time slices between full Mapping calculations */ int exportlonlat; /* Dump longitude and latitude values? */ /* Main routine */ if (*status != SAI__OK) return; /* How many threads do we get to play with */ nw = wf ? wf->nworker : 1; /* Initialize bounds to avoid compiler warnings */ lbnd_old[0] = 0; lbnd_old[1] = 0; ubnd_old[0] = 0; ubnd_old[1] = 0; /* Check for pre-existing LUT and de-allocate it. This will only waste time if the MAPCOORD extension is found to be valid and it has to be re-loaded from disk. */ smf_close_mapcoord( data, status ); /* Assert ICD data order */ smf_dataOrder( data, 1, status ); /* Get the data dimensions */ smf_get_dims( data, NULL, NULL, &nbolo, &ntslice, NULL, NULL, NULL, status ); /* If SMF__NOCREATE_FILE is not set, and file associated with an NDF, map a new MAPCOORD extension (or verify an existing one) */ if( !(flags & SMF__NOCREATE_FILE) && data->file ) { doextension = 1; } else { doextension = 0; docalc = 1; } /* Create / check for existing MAPCOORD extension */ if( doextension ) { file = data->file; /* Check type of file before proceeding */ if( file->isSc2store ) { *status = SAI__ERROR; errRep(FUNC_NAME, "File was opened by sc2store library (raw data?)", status); } if( !file->isTstream ) { *status = SAI__ERROR; errRep(FUNC_NAME, "File does not contain time stream data",status); } /* Get HDS locator to the MAPCOORD extension */ mapcoordloc = smf_get_xloc( data, "MAPCOORD", "MAP_PROJECTION", "UPDATE", 0, 0, status ); /* Obtain NDF identifier/placeholder for LUT in MAPCOORD extension*/ lbnd[0] = 0; ubnd[0] = nbolo*ntslice-1; lutndf = smf_get_ndfid( mapcoordloc, "LUT", "UPDATE", "UNKNOWN", "_INTEGER", 1, lbnd, ubnd, status ); if( *status == SAI__OK ) { /* store the NDF identifier */ file->mapcoordid = lutndf; /* Create sky to output grid mapping using the base coordinates to get the coordinates of the tangent point if it hasn't been done yet. */ sky2map = astGetMapping( outfset, AST__CURRENT, AST__BASE ); } /* Before mapping the LUT, first check for existing WCS information and LBND/UBND for the output map. If they are already correct don't bother re-calculating the LUT! */ if( *status == SAI__OK ) { /* Try reading in the WCS information */ kpg1Wread( mapcoordloc, "WCS", &fstemp, status ); oldfset = (AstFrameSet*)fstemp; if( *status == SAI__OK ) { /* Check that the old and new mappings are the same by checking that combining one with the inverse of the other reduces to a UnitMap. */ map2sky_old = astGetMapping( oldfset, AST__BASE, AST__CURRENT ); testcmpmap = astCmpMap( map2sky_old, sky2map, 1, " " ); testsimpmap = astSimplify( testcmpmap ); if( astIsAUnitMap( testsimpmap ) ) { /* The mappings are the same, now just check the pixel bounds in the output map */ lbnd_temp[0] = 1; ubnd_temp[0] = 2; bndndf = smf_get_ndfid( mapcoordloc, "LBND", "READ", "UNKNOWN", "_INTEGER", 1, lbnd_temp, ubnd_temp, status ); if( *status == SAI__OK ) { ndfMap( bndndf, "DATA", "_INTEGER", "READ", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { lbnd_old[0] = data_index[0]; lbnd_old[1] = data_index[1]; } ndfAnnul( &bndndf, status ); } bndndf = smf_get_ndfid( mapcoordloc, "UBND", "READ", "UNKNOWN", "_INTEGER", 1, lbnd_temp, ubnd_temp, status ); if( *status == SAI__OK ) { ndfMap( bndndf, "DATA", "_INTEGER", "READ", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { ubnd_old[0] = data_index[0]; ubnd_old[1] = data_index[1]; } ndfAnnul( &bndndf, status ); } if( *status == SAI__OK ) { /* If we get this far finally do the bounds check! */ if( (lbnd_old[0] == lbnd_out[0]) && (lbnd_old[1] == lbnd_out[1]) && (ubnd_old[0] == ubnd_out[0]) && (ubnd_old[1] == ubnd_out[1]) ) { docalc = 0; /* We don't have to re-calculate the LUT */ msgOutif(MSG__VERB," ",FUNC_NAME ": Existing LUT OK", status); } } } /* Bad status / AST errors at this point due to problems with MAPCOORD. Annul and continue calculating new MAPCOORD extension. */ astClearStatus; errAnnul(status); } else { /* Bad status due to non-existence of MAPCOORD. Annul and continue */ errAnnul(status); } } } /* If we need to calculate the LUT do it here */ if( docalc && (*status == SAI__OK) ) { msgOutif(MSG__VERB," ", FUNC_NAME ": Calculate new LUT", status); /* Get the increment in time slices between full Mapping calculations. The Mapping for intermediate time slices will be approximated. */ dim_t dimval; smf_get_nsamp( config, "TSTEP", data, &dimval, status ); tstep = dimval; /* Get space for the LUT */ if( doextension ) { /* Map the LUT array */ ndfMap( lutndf, "DATA", "_INTEGER", "WRITE", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { lut = data_index; } else { errRep( FUNC_NAME, "Unable to map LUT in MAPCOORD extension", status); } } else { /* alloc the LUT and THETA arrays */ lut = astMalloc( (nbolo*ntslice)*sizeof(*(data->lut)) ); theta = astMalloc( ntslice*sizeof(*(data->theta)) ); } /* Retrieve the sky2map mapping from the output frameset (actually map2sky) */ oskyfrm = astGetFrame( outfset, AST__CURRENT ); sky2map = astGetMapping( outfset, AST__BASE, AST__CURRENT ); /* If the longitude and latitude is being dumped, create new NDFs to hold them, and map them. */ if( config ) { astMapGet0I( config, "EXPORTLONLAT", &exportlonlat ); if( exportlonlat ) { lon_ptr = smf1_calc_mapcoord1( data, nbolo, ntslice, oskyfrm, &indf_lon, 1, status ); lat_ptr = smf1_calc_mapcoord1( data, nbolo, ntslice, oskyfrm, &indf_lat, 2, status ); } } /* Invert the mapping to get Output SKY to output map coordinates */ astInvert( sky2map ); /* Create a SkyFrame in absolute coordinates */ abskyfrm = astCopy( oskyfrm ); astClear( abskyfrm, "SkyRefIs" ); astClear( abskyfrm, "SkyRef(1)" ); astClear( abskyfrm, "SkyRef(2)" ); if( *status == SAI__OK ) { /* --- Begin parellelized portion ------------------------------------ */ /* Start a new job context. Each call to thrWait within this context will wait until all jobs created within the context have completed. Jobs created in higher contexts are ignored by thrWait. */ thrBeginJobContext( wf, status ); /* Allocate job data for threads */ job_data = astCalloc( nw, sizeof(*job_data) ); if( *status == SAI__OK ) { /* Set up job data, and start calculating pointing for blocks of time slices in different threads */ if( nw > (int) ntslice ) { step = 1; } else { step = ntslice/nw; } for( ii=0; (*status==SAI__OK)&&(ii<nw); ii++ ) { pdata = job_data + ii; /* Blocks of time slices */ pdata->t1 = ii*step; pdata->t2 = (ii+1)*step-1; /* Ensure that the last thread picks up any left-over tslices */ if( (ii==(nw-1)) && (pdata->t1<(ntslice-1)) ) { pdata->t2=ntslice-1; } pdata->ijob = -1; pdata->lut = lut; pdata->theta = theta; pdata->lbnd_out = lbnd_out; pdata->moving = moving; pdata->ubnd_out = ubnd_out; pdata->tstep = tstep; pdata->lat_ptr = lat_ptr; pdata->lon_ptr = lon_ptr; pdata->fts_port = fts_port; /* Make deep copies of AST objects and unlock them so that each thread can then lock them for their own exclusive use */ pdata->abskyfrm = astCopy( abskyfrm ); astUnlock( pdata->abskyfrm, 1 ); pdata->sky2map = astCopy( sky2map ); astUnlock( pdata->sky2map, 1 ); /* Similarly, make a copy of the smfData, including only the header information which each thread will need in order to make calls to smf_rebin_totmap */ pdata->data = smf_deepcopy_smfData( data, 0, SMF__NOCREATE_FILE | SMF__NOCREATE_DA | SMF__NOCREATE_FTS | SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY, 0, 0, status ); smf_lock_data( pdata->data, 0, status ); } for( ii=0; ii<nw; ii++ ) { /* Submit the job */ pdata = job_data + ii; pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata, smfCalcMapcoordPar, 0, NULL, status ); } /* Wait until all of the jobs submitted within the current job context have completed */ thrWait( wf, status ); } /* End the current job context. */ thrEndJobContext( wf, status ); /* --- End parellelized portion -------------------------------------- */ /* Set the lut pointer in data to the buffer */ data->lut = lut; data->theta = theta; /* Write the WCS for the projection to the extension */ if( doextension ) { kpg1Wwrt( (AstObject*)outfset, "WCS", mapcoordloc, status ); /* Write the pixel bounds for the map to the extension */ lbnd_temp[0] = 1; /* Don't get confused! Bounds for NDF that will */ ubnd_temp[0] = 2; /* contain the bounds for the output 2d map! */ bndndf = smf_get_ndfid( mapcoordloc, "LBND", "UPDATE", "UNKNOWN", "_INTEGER", 1, lbnd_temp, ubnd_temp, status ); ndfMap( bndndf, "DATA", "_INTEGER", "WRITE", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { data_index[0] = lbnd_out[0]; data_index[1] = lbnd_out[1]; } else { errRep( FUNC_NAME, "Unable to map LBND in MAPCOORD extension", status); } ndfAnnul( &bndndf, status ); bndndf = smf_get_ndfid( mapcoordloc, "UBND", "UPDATE", "UNKNOWN", "_INTEGER", 1, lbnd_temp, ubnd_temp, status ); ndfMap( bndndf, "DATA", "_INTEGER", "WRITE", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { data_index[0] = ubnd_out[0]; data_index[1] = ubnd_out[1]; } else { errRep( FUNC_NAME, "Unable to map UBND in MAPCOORD extension", status); } ndfAnnul( &bndndf, status ); } } } /* Clean Up */ if( testsimpmap ) testsimpmap = astAnnul( testsimpmap ); if( testcmpmap ) testcmpmap = astAnnul( testcmpmap ); if( map2sky_old ) map2sky_old = astAnnul( map2sky_old ); if( oldfset ) oldfset = astAnnul( oldfset ); if (sky2map) sky2map = astAnnul( sky2map ); if (bolo2map) bolo2map = astAnnul( bolo2map ); if( abskyfrm ) abskyfrm = astAnnul( abskyfrm ); if( oskyfrm ) oskyfrm = astAnnul( oskyfrm ); if( mapcoordloc ) datAnnul( &mapcoordloc, status ); if( indf_lat != NDF__NOID ) ndfAnnul( &indf_lat, status ); if( indf_lon != NDF__NOID ) ndfAnnul( &indf_lon, status ); /* If we get this far, docalc=0, and status is OK, there must be a good LUT in there already. Map it so that it is accessible to the caller; "UPDATE" so that the caller can modify it if desired. */ if( (*status == SAI__OK) && (docalc == 0) ) { smf_open_mapcoord( data, "UPDATE", status ); } /* Clean up job data */ if( job_data ) { for( ii=0; (*status==SAI__OK)&&(ii<nw); ii++ ) { pdata = job_data + ii; if( pdata->data ) { smf_lock_data( pdata->data, 1, status ); smf_close_file( &(pdata->data), status ); } astLock( pdata->abskyfrm, 0 ); pdata->abskyfrm = astAnnul( pdata->abskyfrm ); astLock( pdata->sky2map, 0 ); pdata->sky2map = astAnnul( pdata->sky2map ); } job_data = astFree( job_data ); } }
double smf_calc_mappa( smfHead *hdr, const char *system, AstFrame *sf, int *status ){ /* Local Variables */ AstFrameSet *fs = NULL; /* FrameSet joining tracking and requested systems */ AstFrame *sf2 = NULL; /* Frame in requested system */ const char *oldsys = NULL; /* Original System value for supplied Frame */ const char *trsys = NULL; /* AST tracking system */ const char *usesys = NULL; /* AST system for output cube */ double map_pa; /* MAP_PA in tracking system (degs) */ double p1[ 2 ]; /* Base pointing position */ double p2[ 2 ]; /* A point on the map vertical axis */ double p3[ 2 ]; /* A point north of the base pointing position */ double result; /* The returned angle */ double xin[ 2 ]; /* Longitude values in tracking system */ double xout[ 2 ]; /* Longitude values in requested system */ double yin[ 2 ]; /* Latitude values in tracking system */ double yout[ 2 ]; /* Latitude values in requested system */ /* Check inherited status */ if (*status != SAI__OK) return 0.0; /* Determine the tracking system, and choose the celestial coordinate system for the output cube. */ trsys = sc2ast_convert_system( hdr->state->tcs_tr_sys, status ); if( !strcmp( system, "TRACKING" ) ) { usesys = trsys; } else { usesys = system; } /* Save the System value of the supplied Frame, and set it to the tracking system. */ oldsys = astGetC( sf, "System" ); astSetC( sf, "System", trsys ); /* Get the base pointing position in the tracking system. */ p1[ 0 ] = hdr->state->tcs_tr_bc1; p1[ 1 ] = hdr->state->tcs_az_bc2; /* Move along the map "vertical" axis (as specified by the MAP_PA FITS header) for 1 arc-minute from the base pointing position. Set up a suitable default first in case the MAP_PA value is undefined. */ map_pa = 0.0; smf_getfitsd( hdr, "MAP_PA", &map_pa, status ); (void) astOffset2( sf, p1, map_pa*AST__DD2R, AST__DD2R/60.0, p2 ); /* Take a copy of the Frame and set its System value to the requested system. */ sf2 = astCopy( sf ); astSetC( sf2, "System", usesys ); /* Find a Mapping from the tracking system to the requested system. */ fs = astConvert( sf, sf2, "" ); /* Use this Mapping to transform the above two positions from tracking to the requested system. */ xin[ 0 ] = p1[ 0 ]; yin[ 0 ] = p1[ 1 ]; xin[ 1 ] = p2[ 0 ]; yin[ 1 ] = p2[ 1 ]; astTran2( fs, 2, xin, yin, 1, xout, yout ); p1[ 0 ] = xout[ 0 ]; p1[ 1 ] = yout[ 0 ]; p2[ 0 ] = xout[ 1 ]; p2[ 1 ] = yout[ 1 ]; /* Create a 3rd position which 1 arc-minute to the north of the base pointing position in the requested system. */ p3[ 0 ] = p1[ 0 ]; p3[ 1 ] = p1[ 1 ] + AST__DD2R/60.0; /* Find the angle subtended at p1 by p2 and p3. */ result = astAngle( sf2, p3, p1, p2 ); /* Re-instate the original System value in the supplied Frame. */ astSetC( sf, "System", oldsys ); /* Free resources. */ fs = astAnnul( fs ); sf2 = astAnnul( sf2 ); /* Return the required angle. */ return result; }
void smf_collapse_tseries( const smfData *indata, int nclip, const float clip[], double snrlim, int flagconst, smf_dtype dtype, smfData **outdata, int *status ) { /* Per type pointers */ double *avg_d = NULL; double *var_d = NULL; int *avg_i = NULL; int *var_i = NULL; dim_t dims[2]; /* dimensions of data array */ smfHead *hdr = NULL; /* copy of header */ AstKeyMap * history = NULL; /* history */ size_t nbperel; /* Number of bytes in dtype */ size_t nelem; /* number of elements in mean image */ void *pntr[] = { NULL, NULL }; /* pointers to data */ if (*status != SAI__OK) return; /* see if we have a 2d input (likely reduced) */ if (indata->ndims == 2 || (indata->ndims == 3 && (indata->dims)[2] == 1) ) { *outdata = NULL; return; } /* Trap SMF__NULL */ if (dtype == SMF__NULL) dtype = indata->dtype; /* Get some memory of the right type - data and variance */ dims[0] = (indata->dims)[0]; dims[1] = (indata->dims)[1]; nelem = dims[0] * dims[1]; nbperel = smf_dtype_sz(dtype, status); pntr[0] = astMalloc( nelem*nbperel ); pntr[1] = astMalloc( nelem*nbperel ); /* Assign the pointers */ smf_select_pntr( pntr, dtype, &avg_d, &var_d, &avg_i, &var_i, status ); if (*status == SAI__OK) { dim_t i,j; /* get statistics for each bolometer */ for (i = 0; i < dims[0]; i++) { for (j = 0; j < dims[1]; j++) { double mean = VAL__BADD; double stdev = VAL__BADD; double variance = VAL__BADD; dim_t index; index = ( j * dims[0] ) + i; smf_calc_stats( indata, "b", index, 0, 0, nclip, clip, &mean, &stdev, status ); if (flagconst && stdev == 0.0) { mean = VAL__BADD; stdev = VAL__BADD; } if (snrlim > 0 && mean != VAL__BADD && stdev != VAL__BADD && stdev != 0.0) { double snr; snr = fabs(mean/stdev); if (snr < snrlim) { mean = VAL__BADD; stdev = VAL__BADD; } } if (stdev != VAL__BADD) { variance = stdev * stdev; } switch (dtype) { case SMF__DOUBLE: avg_d[index] = mean; var_d[index] = variance; break; case SMF__INTEGER: if (isnan(mean) || mean == VAL__BADD ) { avg_i[index] = VAL__BADI; } else { avg_i[index] = (int)mean; } if (isnan(stdev) || stdev == VAL__BADD) { var_i[index] = VAL__BADI; } else if ( variance > (double)VAL__MAXI ) { /* overflow. Convert to BAD */ var_i[index] = VAL__BADI; avg_i[index] = VAL__BADI; } else { var_i[index] = (int)variance; } break; default: *status = SAI__ERROR; errRep( " ", "Should be impossible to get here", status ); goto L999; } } } } L999: /* now create a new smfData - we need to copy the header info */ hdr = smf_deepcopy_smfHead( indata->hdr, status ); if (indata->history) history = astCopy( indata->history ); *outdata = smf_construct_smfData( NULL, NULL, hdr, NULL, NULL, dtype, pntr, NULL, SMF__QFAM_TSERIES, NULL, 0, 1, dims, indata->lbnd, 2, 0, 0, NULL, history, status ); /* must free the data if outdata is null */ if (*outdata == NULL) { pntr[0] = astFree( pntr[0] ); pntr[1] = astFree( pntr[1] ); } return; }
HDSLoc *cupidReinhold( int type, int ndim, int *slbnd, int *subnd, void *ipd, double *ipv, double rms, AstKeyMap *config, int velax, double beamcorr[ 3 ], int *status ){ /* *+ * Name: * cupidReinhold * Purpose: * Identify clumps of emission within a 1, 2 or 3 dimensional NDF using * the REINHOLD algorithm. * Language: * Starlink C * Synopsis: * HDSLoc *cupidReinhold( int type, int ndim, int *slbnd, int *subnd, * void *ipd, double *ipv, double rms, * AstKeyMap *config, int velax, * double beamcorr[ 3 ], int *status ) * Description: * This function identifies clumps within a 1, 2 or 3 dimensional data * array using the REINHOLD algorithm, developed by Kim Reinhold at * JAC. This algorithm identifies the boundaries between clumps by * looking for minima in 1D sections through the data. No a priori clump * profile is assumed. In this algorithm, clumps never overlap. * Parameters: * type * An integer identifying the data type of the array values pointed to * by "ipd". Must be either CUPID__DOUBLE or CUPID__FLOAT (defined in * cupid.h). * ndim * The number of dimensions in the data array. Must be 2 or 3. * slbnd * Pointer to an array holding the lower pixel index bound of the * data array on each axis. * subnd * Pointer to an array holding the upper pixel index bound of the * data array on each axis. * ipd * Pointer to the data array. The elements should be stored in * Fortran order. The data type of this array is given by "itype". * ipv * Pointer to the input Variance array, or NULL if there is no Variance * array. The elements should be stored in Fortran order. The data * type of this array is "double". * rms * The default value for the global RMS error in the data array. * config * An AST KeyMap holding tuning parameters for the algorithm. * velax * The index of the velocity axis in the data array (if any). Only * used if "ndim" is 3. * beamcorr * An array in which is returned the FWHM (in pixels) describing the * instrumental smoothing along each pixel axis. The clump widths * stored in the output catalogue are reduced to correct for this * smoothing. * status * Pointer to the inherited status value. * Returned Value: * A locator for a new HDS object which is an array of NDF structures. * Each NDF will hold the data values associated with a single clump * and will be the smallest possible NDF that completely contains the * corresponding clump. Pixels not in the clump will be set bad. The * pixel origin is set to the same value as the supplied NDF. * Copyright: * Copyright (C) 2009 Science & Technology Facilities Council. * Copyright (C) 2006 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: * 16-JAN-2006 (DSB): * Original version. * 14-JAN-2009 (TIMJ): * Use MERS for message filtering. * {enter_further_changes_here} * Bugs: * {note_any_bugs_here} *- */ /* Local Variables: */ AstKeyMap *rconfig; /* Configuration parameters for this algorithm */ HDSLoc *ret; /* Locator for the returned array of NDFs */ double *pd; /* Pointer to next element of data array */ double *peakvals; /* Pointer to array holding clump peak values */ double cathresh; /* Threshold for second cellular automata */ double flatslope; /* Minimum significant slope at edge of a peak */ double noise; /* Noise level */ double pv; /* Pixel value */ double thresh; /* Minimum peak value to be considered */ float *pf; /* Pointer to next element of data array */ int *clbnd; /* Array holding lower axis bounds of all clumps */ int *cubnd; /* Array holding upper axis bounds of all clumps */ int *igood; /* Pointer to array holding usable clump indices */ int *m1; /* Pointer to mask array */ int *m2; /* Pointer to mask array */ int *m3; /* Pointer to mask array */ int *mask2; /* Pointer to array marking out edge pixels */ int *mask; /* Pointer to array marking out edge pixels */ int *nrem; /* Pointer to array marking out edge pixels */ int *pa; /* Pointer to next element in the pixel assignment array */ int caiter; /* The number of CA iterations to perform */ int dims[3]; /* Pointer to array of array dimensions */ int el; /* Number of elements in array */ int fixiter; /* The number of CA iterations to perform */ int i; /* Loop count */ int ii; /* Loop count */ int ix; /* Grid index on 1st axis */ int iy; /* Grid index on 2nd axis */ int iz; /* Grid index on 3rd axis */ int j; /* Loop count */ int maxid; /* Largest id for any peak (smallest is zero) */ int minlen; /* Minimum size of a clump in pixels along one dimension*/ int minpix; /* Minimum total size of a clump in pixels */ int more; /* Continue looping?*/ int ngood; /* Number of good clumps */ int nsmall; /* Number of clumps that are too small */ int nthin; /* Number of clumps that span only a single pixel */ int peakval; /* Minimum value used to flag peaks */ int skip[3]; /* Pointer to array of axis skips */ /* Initialise */ ret = NULL; /* Abort if an error has already occurred. */ if( *status != SAI__OK ) return ret; /* Say which method is being used. */ msgBlankif( MSG__NORM, status ); msgOutif( MSG__NORM, "", "Reinhold:", status ); /* Get the AST KeyMap holding the configuration parameters for this algorithm. */ if( !astMapGet0A( config, "REINHOLD", (AstObject *) &rconfig ) ) { rconfig = astKeyMap( " " ); astMapPut0A( config, "REINHOLD", rconfig, " " ); } /* The configuration file can optionally omit the algorithm name. In this case the "config" KeyMap may contain values which should really be in the "rconfig" KeyMap. Add a copy of the "config" KeyMap into "rconfig" so that it can be searched for any value which cannot be found in the "rconfig" KeyMap. */ astMapPut0A( rconfig, CUPID__CONFIG, astCopy( config ), NULL ); /* Return the instrumental smoothing FWHMs */ beamcorr[ 0 ] = cupidConfigD( rconfig, "FWHMBEAM", 2.0, status ); beamcorr[ 1 ] = beamcorr[ 0 ]; if( ndim == 3 ) { beamcorr[ 2 ] = beamcorr[ 0 ]; beamcorr[ velax ]= cupidConfigD( rconfig, "VELORES", 2.0, status ); } /* Find the size of each dimension of the data array, and the total number of elements in the array, and the skip in 1D vector index needed to move by pixel along an axis. We use the memory management functions of the AST library since they provide greater security and functionality than direct use of malloc, etc. */ el = 1; for( i = 0; i < ndim; i++ ) { dims[ i ] = subnd[ i ] - slbnd[ i ] + 1; el *= dims[ i ]; skip[ i ] = ( i == 0 ) ? 1 : skip[ i - 1 ]*dims[ i - 1 ]; } for( ; i < 3; i++ ) { dims[ i ] = 1; skip[ i ] = 0; } /* Get various configuration parameters. */ rms = cupidConfigD( rconfig, "RMS", rms, status ); minlen = cupidConfigI( rconfig, "MINLEN", 4, status ); noise = cupidConfigRMS( rconfig, "NOISE", rms, 2*rms, status ); thresh = cupidConfigRMS( rconfig, "THRESH", rms, noise + 2*rms, status ); flatslope = cupidConfigRMS( rconfig, "FLATSLOPE", rms, rms, status ); cathresh = pow( 3, ndim ) - 1.0; cathresh = cupidConfigI( rconfig, "CATHRESH", (int) cathresh, status ); caiter = cupidConfigI( rconfig, "CAITERATIONS", 1, status ); fixiter = cupidConfigI( rconfig, "FIXCLUMPSITERATIONS", 1, status ); /* Get the minimum allowed number of pixels in a clump. */ minpix = cupidDefMinPix( ndim, beamcorr, noise, thresh, status ); minpix = cupidConfigI( rconfig, "MINPIX", minpix, status ); /* Convert CATHRESH from a number of pixels to a fraction. */ cathresh = cathresh/pow( 3, ndim ); if( cathresh > 0.98 ) cathresh = 0.98; if( cathresh < 0 ) cathresh = 0.0; cathresh += 0.01; /* Get a mask which is the same size and shape as the data array and which holds CUPID__KEDGE at every pixel thought to be on the edge of a clump. This is done by scanning the data cube using sets of parallel lines in different directions. Peaks are searched for in each line, and then the edges found by following the curve down from each peak until the gradient becomes zero or positive, or until the data value drops below a threshold value. Pixels which correspond to peaks in the data cube are flagged with the value greater than or equal to the returned "*peakval" value. All other pixels are set to some other value (which will usually be CUPID__KBACK but will be something else at positions of peaks which were not peaks in all scan directions). */ msgOutif( MSG__DEBUG, "", "Finding clump edges...", status ); mask = cupidRInitEdges( type, ipd, el, ndim, dims, skip, minlen, thresh, noise, rms, flatslope, &peakval, status ); /* Dilate the edge regions using a cellular automata. This creates a new mask array in which a pixel is marked as an edge pixel if any of its neighbours are marked as edge pixels in the mask array created above. */ msgOutif( MSG__DEBUG, "", "Dilating clump edges...", status ); mask2 = cupidRCA( mask, NULL, el, dims, skip, 0.0, peakval, CUPID__KEDGE, CUPID__KBACK, 0, status ); /* Erode the edge regions using a second cellular automata. This over-writes the original mask array so that a pixel is marked as an edge pixel if a fraction greater than "cathresh" of neighbouring pixels are marked as edge pixels in "mask2". We loop doing this "CAiteration" times. */ if( caiter > 0 ) msgOutif( MSG__DEBUG,"", "Eroding clump edges...", status ); m1 = mask; m2 = mask2; for( i = 0; i < caiter; i++ ) { m1 = cupidRCA( m2, m1, el, dims, skip, cathresh, peakval, CUPID__KEDGE, CUPID__KBACK, 0, status ); m3 = m1; m1 = m2; m2 = m3; } /* Fill the volume around each peak with integer values which indicate which peak they are close to. All the pixels around one peak form one clump. */ msgOutif( MSG__DEBUG, "", "Filling clumps...", status ); maxid = cupidRFillClumps( m2, m1, el, ndim, skip, dims, peakval, status ); /* Abort if no clumps found. */ if( maxid < 0 ) { msgOutif( MSG__NORM, "", "No usable clumps found.", status ); msgBlankif( MSG__NORM, status ); goto L10; } /* Smooth the boundaries between the clumps. This cellular automata replaces each output pixel by the most commonly occuring value within a 3x3x3 cube of input pixels centred on the output pixel. Put the smoothed results back into the supplied "m1" array. */ if( fixiter >0 ) msgOutif( MSG__DEBUG, "", "Smoothing clump boundaries...", status ); for( i = 0; i < fixiter; i++ ) { m2 = cupidRCA2( m1, m2, el, dims, skip, status ); m3 = m2; m2 = m1; m1 = m3; } /* Allocate an array used to store the number of pixels remaining in each clump. */ nrem = astMalloc( sizeof( int )*( maxid + 1 ) ); /* Allocate an array used to store the peak value in every clump. */ peakvals = astMalloc( sizeof( double )*( maxid + 1 ) ); /* Determine the bounding box of every clump. First allocate memory to hold the bounding boxes. */ clbnd = astMalloc( sizeof( int )*( maxid + 1 )*3 ); cubnd = astMalloc( sizeof( int )*( maxid + 1 )*3 ); igood = astMalloc( sizeof( int )*( maxid + 1 ) ); if( igood ) { /* Initialise a list to hold zero for every clump id. These values are used to count the number of pixels remaining in each clump. Also initialise the peak values to a very negative value. */ for( i = 0; i <= maxid; i++ ) { nrem[ i ] = 0; peakvals[ i ] = VAL__MIND; } /* Initialise the bounding boxes. */ for( i = 0; i < 3*( maxid + 1 ); i++ ) { clbnd[ i ] = VAL__MAXI; cubnd[ i ] = VAL__MINI; } /* Loop round every pixel in the final pixel assignment array. */ if( type == CUPID__DOUBLE ) { pd = (double *) ipd; pf = NULL; } else { pf = (float *) ipd; pd = NULL; } pa = m1; for( iz = 1; iz <= dims[ 2 ]; iz++ ){ for( iy = 1; iy <= dims[ 1 ]; iy++ ){ for( ix = 1; ix <= dims[ 0 ]; ix++, pa++ ){ /* Get the data value at this pixel */ if( type == CUPID__DOUBLE ) { pv = *(pd++); } else { pv = (double) *(pf++); } /* Skip pixels which are not in any clump. */ if( *pa >= 0 ) { /* Increment the number of pixels in this clump. */ ++( nrem[ *pa ] ); /* If this pixel value is larger than the current peak value for this clump, record it. */ if( pv > (double) peakvals[ *pa ] ) peakvals[ *pa ] = pv; /* Get the index within the clbnd and cubnd arrays of the current bounds on the x axis for this clump. */ i = 3*( *pa ); /* Update the bounds for the x axis, then increment to get the index of the y axis bounds. */ if( ix < clbnd[ i ] ) clbnd[ i ] = ix; if( ix > cubnd[ i ] ) cubnd[ i ] = ix; i++; /* Update the bounds for the y axis, then increment to get the index of the z axis bounds. */ if( iy < clbnd[ i ] ) clbnd[ i ] = iy; if( iy > cubnd[ i ] ) cubnd[ i ] = iy; i++; /* Update the bounds for the z axis. */ if( iz < clbnd[ i ] ) clbnd[ i ] = iz; if( iz > cubnd[ i ] ) cubnd[ i ] = iz; } } } } /* Loop round counting the clumps which are too small or too low. Put the indices of usable clumps into another array. */ nsmall = 0; ngood = 0; nthin = 0; for( i = 0; i <= maxid; i++ ) { j = 3*i; if( nrem[ i ] <= minpix ) { nsmall++; } else if( clbnd[ j ] == cubnd[ j ] || ( clbnd[ j + 1 ] == cubnd[ j + 1 ] && ndim > 1 ) || ( clbnd[ j + 2 ] == cubnd[ j + 2 ] && ndim > 2 ) ) { nthin++; } else { igood[ ngood++ ] = i; } } if( ngood == 0 ) msgOutif( MSG__NORM,"", "No usable clumps found.", status ); if( nsmall == 1 ){ msgOutif( MSG__NORM, "", "One clump rejected because it contains too few pixels.", status ); } else if( nsmall > 1 ) { msgSeti( "N", nsmall ); msgOutif( MSG__NORM, "", "^N clumps rejected because they contain too few pixels.", status ); } if( nthin == 1 ) { msgOutif( MSG__NORM, "", "1 clump rejected because it spans only a single " "pixel along one or more axes.", status ); } else if( nthin > 1 ) { msgSeti( "N", nthin ); msgOutif( MSG__NORM, "", "^N clumps rejected because they spans only a single " "pixel along one or more axes.", status ); } /* Sort the clump indices into descending order of peak value. */ j = ngood; more = 1; while( more ) { j--; more = 0; for( i = 0; i < j; i++ ) { if( peakvals[ igood[ i ] ] < peakvals[ igood[ i + 1 ] ] ) { ii = igood[ i + 1 ]; igood[ i + 1 ] = igood[ i ]; igood[ i ] = ii; more = 1; } } } /* Loop round creating an NDF describing each usable clump. */ for( j = 0; j < ngood; j++ ) { i = igood[ j ]; ret = cupidNdfClump( type, ipd, m1, el, ndim, dims, skip, slbnd, i, clbnd + 3*i, cubnd + 3*i, NULL, ret, cupidConfigD( rconfig, "MAXBAD", 0.05, status ), status ); } /* Free resources */ clbnd = astFree( clbnd ); cubnd = astFree( cubnd ); igood = astFree( igood ); } peakvals = astFree( peakvals ); nrem = astFree( nrem ); L10:; /* Remove the secondary KeyMap added to the KeyMap containing configuration parameters for this algorithm. This prevents the values in the secondary KeyMap being written out to the CUPID extension when cupidStoreConfig is called. */ astMapRemove( rconfig, CUPID__CONFIG ); /* Free resources */ rconfig = astAnnul( rconfig ); mask = astFree( mask ); mask2 = astFree( mask2 ); /* Return the list of clump NDFs. */ return ret; }
void smf_rebinsparse( smfData *data, int first, int *ptime, AstFrame *ospecfrm, AstMapping *ospecmap, AstSkyFrame *oskyframe, Grp *detgrp, int lbnd_out[ 3 ], int ubnd_out[ 3 ], int genvar, float *data_array, float *var_array, int *ispec, float *texp_array, float *teff_array, double *fcon, int *status ){ /* Local Variables */ AstCmpMap *fmap = NULL; /* Mapping from spectral grid to topo freq Hz */ AstCmpMap *ssmap = NULL; /* I/p GRID-> o/p PIXEL Mapping for spectral axis */ AstFitsChan *fc = NULL; /* Storage for FITS headers */ AstFrame *specframe = NULL; /* Spectral Frame in input FrameSet */ AstFrame *specframe2 = NULL; /* Temporary copy of SpecFrame in input WCS */ AstFrameSet *fs = NULL; /* A general purpose FrameSet pointer */ AstFrameSet *swcsin = NULL; /* FrameSet describing spatial input WCS */ AstMapping *fsmap = NULL; /* Base->Current Mapping extracted from a FrameSet */ AstMapping *specmap = NULL; /* PIXEL -> Spec mapping in input FrameSet */ char *fftwin = NULL; /* Name of FFT windowing function */ const char *name = NULL; /* Pointer to current detector name */ const double *tsys=NULL; /* Pointer to Tsys value for first detector */ dim_t timeslice_size; /* No of detector values in one time slice */ double *spectab = NULL;/* Workspace for spectral output grid positions */ double *xin = NULL; /* Workspace for detector input grid positions */ double *xout = NULL; /* Workspace for detector output pixel positions */ double *yin = NULL; /* Workspace for detector input grid positions */ double *yout = NULL; /* Workspace for detector output pixel positions */ double at; /* Frequency at which to take the gradient */ double dnew; /* Channel width in Hz */ double fcon2; /* Variance factor for whole file */ double k; /* Back-end degradation factor */ double tcon; /* Variance factor for whole time slice */ float *pdata = NULL; /* Pointer to next data sample */ float *qdata = NULL; /* Pointer to next data sample */ float rtsys; /* Tsys value */ float teff; /* Effective integration time, times 4 */ float texp; /* Total time ( = ton + toff ) */ float toff; /* Off time */ float ton; /* On time */ int *nexttime = NULL; /* Pointer to next time slice index to use */ int dim[ 3 ]; /* Output array dimensions */ int found; /* Was current detector name found in detgrp? */ int good; /* Are there any good detector samples? */ int ibasein; /* Index of base Frame in input FrameSet */ int ichan; /* Index of current channel */ int iv; /* Offset to next element */ int iz; /* Output grid index on axis 3 */ int nchan; /* Number of input spectral channels */ int pixax[ 3 ]; /* The output fed by each selected mapping input */ int specax; /* Index of spectral axis in input FrameSet */ size_t irec; /* Index of current input detector */ size_t itime; /* Index of current time slice */ smfHead *hdr = NULL; /* Pointer to data header for this time slice */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context.*/ astBegin; /* Store a pointer to the input NDFs smfHead structure. */ hdr = data->hdr; /* Store the dimensions of the output array. */ dim[ 0 ] = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1; dim[ 1 ] = ubnd_out[ 1 ] - lbnd_out[ 1 ] + 1; dim[ 2 ] = ubnd_out[ 2 ] - lbnd_out[ 2 ] + 1; /* Store the number of pixels in one time slice */ timeslice_size = (data->dims)[ 0 ]*(data->dims)[ 1 ]; /* We want a description of the spectral WCS axis in the input file. If the input file has a WCS FrameSet containing a SpecFrame, use it, otherwise we will obtain it from the FITS header later. NOTE, if we knew that all the input NDFs would have the same spectral axis calibration, then the spectral WCS need only be obtained from the first NDF. However, in the general case, I presume that data files may be combined that use different spectral axis calibrations, and so these differences need to be taken into account. */ if( hdr->tswcs ) { fs = astClone( hdr->tswcs ); /* The first axis should be a SpecFrame. See if this is so. If not annul the specframe pointer. */ specax = 1; specframe = astPickAxes( fs, 1, &specax, NULL ); if( !astIsASpecFrame( specframe ) ) specframe = astAnnul( specframe ); } /* If the above did not yield a SpecFrame, use the FITS-WCS headers in the FITS extension of the input NDF. Take a copy of the FITS header (so that the contents of the header are not changed), and then read a FrameSet out of it. */ if( !specframe ) { fc = astCopy( hdr->fitshdr ); astClear( fc, "Card" ); fs = astRead( fc ); /* Extract the SpecFrame that describes the spectral axis from the current Frame of this FrameSet. This is assumed to be the third WCS axis (NB the different axis number). */ specax = 3; specframe = astPickAxes( fs, 1, &specax, NULL ); } /* Split off the 1D Mapping for this single axis from the 3D Mapping for the whole WCS. This results in "specmap" holding the Mapping from SpecFrame value to GRID value. */ fsmap = astGetMapping( fs, AST__CURRENT, AST__BASE ); astMapSplit( fsmap, 1, &specax, pixax, &specmap ); /* Invert the Mapping for the spectral axis so that it goes from input GRID coord to spectral coord. */ astInvert( specmap ); /* Get a Mapping that converts values in the input spectral system to the corresponding values in the output spectral system. */ fs = astConvert( specframe, ospecfrm, "" ); /* Concatenate these Mappings with the supplied spectral Mapping to get a Mapping from the input spectral grid axis (pixel axis 1) to the output spectral grid axis (pixel axis 3). Simplify the Mapping. */ ssmap = astCmpMap( astCmpMap( specmap, astGetMapping( fs, AST__BASE, AST__CURRENT ), 1, " " ), ospecmap, 1, " " ); ssmap = astSimplify( ssmap ); /* Create a table with one element for each channel in the input array, holding the index of the nearest corresponding output channel. */ nchan = (data->dims)[ 0 ]; spectab = astMalloc( sizeof( *spectab )*nchan ); if( spectab ) { for( ichan = 0; ichan < nchan; ichan++ ) spectab[ ichan ] = ichan + 1; astTran1( ssmap, nchan, spectab, 1, spectab ); for( ichan = 0; ichan < nchan; ichan++ ) { if( spectab[ ichan ] != AST__BAD ) { iz = floor( spectab[ ichan ] + 0.5 ); if( iz >= 1 && iz <= dim[ 2 ] ) { spectab[ ichan ] = iz; } else { spectab[ ichan ] = 0; } } else { spectab[ ichan ] = 0; } } } /* Allocate work arrays big enough to hold the coords of all the detectors in the current input file.*/ xin = astMalloc( (data->dims)[ 1 ] * sizeof( *xin ) ); yin = astMalloc( (data->dims)[ 1 ] * sizeof( *yin ) ); xout = astMalloc( (data->dims)[ 1 ] * sizeof( *xout ) ); yout = astMalloc( (data->dims)[ 1 ] * sizeof( *yout ) ); /* Initialise a string to point to the name of the first detector for which data is available */ name = hdr->detname; /* Store input coords for the detectors. Axis 1 is the detector index, and axis 2 is a dummy axis that always has the value 1. */ for( irec = 0; irec < (data->dims)[ 1 ]; irec++ ) { xin[ irec ] = irec + 1.0; yin[ irec ] = 1.0; /* If a group of detectors to be used was supplied, search the group for the name of the current detector. If not found, set the GRID coords bad. */ if( detgrp ) { found = grpIndex( name, detgrp, 1, status ); if( !found ) { xin[ irec ] = AST__BAD; yin[ irec ] = AST__BAD; } } /* Move on to the next available detector name. */ name += strlen( name ) + 1; } /* Find the constant factor associated with the current input file. This is the squared backend degradation factor, divided by the noise bandwidth. Get the required FITS headers, checking they were found. */ if( astGetFitsF( hdr->fitshdr, "BEDEGFAC", &k ) && astGetFitsS( hdr->fitshdr, "FFT_WIN", &fftwin ) ){ /* Get a Mapping that converts values in the input spectral system to topocentric frequency in Hz, and concatenate this Mapping with the Mapping from input GRID coord to the input spectral system. The result is a Mapping from input GRID coord to topocentric frequency in Hz. */ specframe2 = astCopy( specframe ); astSet( specframe2, "system=freq,stdofrest=topo,unit=Hz" ); fmap = astCmpMap( specmap, astGetMapping( astConvert( specframe, specframe2, "" ), AST__BASE, AST__CURRENT ), 1, " " ); /* Differentiate this Mapping at the mid channel position to get the width of an input channel in Hz. */ at = 0.5*nchan; dnew = astRate( fmap, &at, 1, 1 ); /* Modify the channel width to take account of the effect of the FFT windowing function. Allow undef value because FFT_WIN for old data had a broken value in hybrid subband modes. */ if( dnew != AST__BAD ) { dnew = fabs( dnew ); if( !strcmp( fftwin, "truncate" ) ) { dnew *= 1.0; } else if( !strcmp( fftwin, "hanning" ) ) { dnew *= 1.5; } else if( !strcmp( fftwin, "<undefined>" ) ) { /* Deal with broken data - make an assumption */ dnew *= 1.0; } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSetc( "W", fftwin ); errRep( FUNC_NAME, "FITS header FFT_WIN has unknown value " "'^W' (programming error).", status ); } /* Form the required constant. */ fcon2 = k*k/dnew; } else { fcon2 = VAL__BADD; } } else { fcon2 = VAL__BADD; } /* Return the factor needed for calculating Tsys from the variance. */ if( first ) { *fcon = fcon2; } else if( fcon2 != *fcon ) { *fcon = VAL__BADD; } /* Initialise a pointer to the next time slice index to be used. */ nexttime = ptime; /* Loop round all the time slices in the input file. */ for( itime = 0; itime < (data->dims)[ 2 ] && *status == SAI__OK; itime++ ) { /* If this time slice is not being pasted into the output cube, pass on. */ if( nexttime ){ if( *nexttime != itime ) continue; nexttime++; } /* Store a pointer to the first input data value in this time slice. */ pdata = ( (float *) (data->pntr)[ 0 ] ) + itime*timeslice_size; /* Get a FrameSet describing the spatial coordinate systems associated with the current time slice of the current input data file. The base frame in the FrameSet will be a 2D Frame in which axis 1 is detector number and axis 2 is unused. The current Frame will be a SkyFrame (the SkyFrame System may be any of the JCMT supported systems). The Epoch will be set to the epoch of the time slice. */ smf_tslice_ast( data, itime, 1, NO_FTS, status ); swcsin = hdr->wcs; /* Note the total exposure time (texp) for all the input spectra produced by this time slice. */ ton = hdr->state->acs_exposure; if( ton == 0.0 ) ton = VAL__BADR; toff = hdr->state->acs_offexposure; if( toff == 0.0 ) toff = VAL__BADR; if( ton != VAL__BADR && toff != VAL__BADR ) { texp = ton + toff; teff = 4*ton*toff/( ton + toff ); } else { texp = VAL__BADR; teff = VAL__BADR; } /* If output variances are being calculated on the basis of Tsys values in the input, find the constant factor associated with the current time slice. */ tcon = AST__BAD; if( genvar == 2 && fcon2 != AST__BAD && texp != VAL__BADR ) { tcon = fcon2*( 1.0/ton + 1.0/toff ); /* Get a pointer to the start of the Tsys values for this time slice. */ tsys = hdr->tsys + hdr->ndet*itime; } /* We now create a Mapping from detector index to position in oskyframe. */ astInvert( swcsin ); ibasein = astGetI( swcsin, "Base" ); fs = astConvert( swcsin, oskyframe, "SKY" ); astSetI( swcsin, "Base", ibasein ); astInvert( swcsin ); if( fs == NULL ) { if( *status == SAI__OK ) { if (data->file) { smf_smfFile_msg(data->file, "FILE", 1, "<unknown>"); } else { msgSetc( "FILE", "<unknown>" ); } *status = SAI__ERROR; errRep( FUNC_NAME, "The spatial coordinate system in ^FILE " "is not compatible with the spatial coordinate " "system in the first input file.", status ); } break; } /* Transform the positions of the detectors from input GRID to oskyframe coords. */ astTran2( fs, (data->dims)[ 1 ], xin, yin, 1, xout, yout ); /* Loop round all detectors. */ for( irec = 0; irec < (data->dims)[ 1 ]; irec++ ) { /* If the detector has a valid position, see if it produced any good data values. */ if( xout[ irec ] != AST__BAD && yout[ irec ] != AST__BAD ) { qdata = pdata; good = 0; for( ichan = 0; ichan < nchan; ichan++ ){ if( *(qdata++) != VAL__BADR ) { good = 1; break; } } /* If it did, calculate the variance associated with each detector sample (if required), based on the input Tsys values, and copy the spectrum to the output NDF. */ if( good ) { if( *ispec < dim[ 0 ] ){ rtsys = tsys ? (float) tsys[ irec ] : VAL__BADR; if( rtsys <= 0.0 ) rtsys = VAL__BADR; if( tcon != AST__BAD && genvar == 2 && rtsys != VAL__BADR ) { var_array[ *ispec ] = tcon*rtsys*rtsys; } else if( var_array ) { var_array[ *ispec ] = VAL__BADR; } if( texp != VAL__BADR ) { texp_array[ *ispec ] = texp; teff_array[ *ispec ] = teff; } for( ichan = 0; ichan < nchan; ichan++, pdata++ ) { iz = spectab[ ichan ] - 1; if( iz >= 0 && iz < dim[ 2 ] ) { iv = *ispec + dim[ 0 ]*iz; data_array[ iv ] = *pdata; } } (*ispec)++; } else if( *status == SAI__OK ){ *status = SAI__ERROR; msgSeti( "DIM", dim[ 0 ] ); errRep( " ", "Too many spectra (more than ^DIM) for " "the output NDF (programming error).", status ); break; } /* If this detector does not have any valid data values, increment the data pointer to point at the first sample for the next detector. */ } else { pdata += nchan; } /* If this detector does not have a valid position, increment the data pointer to point at the first sample for the next detector. */ } else { pdata += nchan; } } /* For efficiency, explicitly annul the AST Objects created in this tight loop. */ fs = astAnnul( fs ); } /* Free resources */ spectab = astFree( spectab ); xin = astFree( xin ); yin = astFree( yin ); xout = astFree( xout ); yout = astFree( yout ); /* End the AST context. This will annul all AST objects created within the context (except for those that have been exported from the context). */ astEnd; }
void smf_fit_qui( ThrWorkForce *wf, smfData *idata, smfData **odataq, smfData **odatau, smfData **odatai, dim_t box, int ipolcrd, int pasign, double paoff, double angrot, int north, int *status ){ /* Local Variables: */ AstFrameSet *wcs; /* WCS FrameSet for current time slice */ JCMTState *instate=NULL; /* Pointer to input JCMTState */ JCMTState *outstate=NULL;/* Pointer to output JCMTState */ const char *usesys; /* Tracking system */ dim_t *box_starts; /* Array holding time slice at start of each box */ dim_t box_size; /* First time slice in box */ dim_t intslice; /* ntslice of idata */ dim_t istart; /* Input time index at start of fitting box */ dim_t itime; /* Time slice index */ dim_t nbolo; /* No. of bolometers */ dim_t ncol; /* No. of columns of bolometers in the array */ dim_t ntime; /* Time slices to check */ dim_t ondata; /* ndata of odata */ dim_t ontslice; /* ntslice of odata */ double scale; /* how much longer new samples are */ int bstep; /* Bolometer step between threads */ int iworker; /* Index of a worker thread */ int nworker; /* No. of worker threads */ size_t i; /* loop counter */ smfData *indksquid=NULL; /* Pointer to input dksquid data */ smfFitQUIJobData *job_data = NULL; /* Pointer to all job data */ smfFitQUIJobData *pdata = NULL;/* Pointer to next job data */ smfHead *hdr; /* Pointer to data header this time slice */ smf_qual_t *qua; /* Input quality pointer */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Check supplied arguments. */ if( !idata || !odataq || !odatau ) { *status = SAI__ERROR; errRep( "", "smf_fit_qui: NULL inputs supplied", status ); return; } if( idata->ndims != 3 ) { *status = SAI__ERROR; errRep( "", "smf_fit_qui: idata is not 3-dimensional", status ); return; } /* Ensure the supplied smfData is time-ordered. So "bstride" is 1 and "tstride" is nbolo. */ smf_dataOrder( wf, idata, 1, status ); /* Dimensions of input. */ smf_get_dims( idata, NULL, &ncol, &nbolo, &intslice, NULL, NULL, NULL, status ); /* Store a pointer to the quality array for the input smfData. */ qua = smf_select_qualpntr( idata, NULL, status );; /* Go through the first thousand POL_ANG values to see if they are in units of radians (new data) or arbitrary encoder units (old data). They are assumed to be in radians if no POL_ANG value is larger than 20. This function can only handle new data. */ hdr = idata->hdr; instate = hdr->allState; ntime = ( intslice > 1000 ) ? 1000 : intslice; for( itime = 0; itime < ntime; itime++,instate++ ) { if( instate->pol_ang > 20 ) { *status = SAI__ERROR; errRep( " "," POL2 data contains POL_ANG values in encoder " "units - connot fit to such old data.", status ); break; } } /* Find the input time slice at which each fitting box starts, and the length of the output time axis (in time-slices). */ smf1_find_boxes( intslice, hdr->allState, box, &ontslice, &box_starts, status ); /* Time axis scaling factor. */ scale = (double) intslice / (double) ontslice; /* First copy everything from input to output except for the data that needs to be downsampled */ /* We want to copy everything in the smfHead except for allState. So we make a copy of the allState pointer, and then set it to NULL in the header before the copy */ if( idata->hdr ) { instate = idata->hdr->allState; idata->hdr->allState = NULL; } /* Similarly, we want everything in the smfDa except for the dksquid. */ if( idata->da ) { indksquid = idata->da->dksquid; idata->da->dksquid = NULL; } /* Create copies, storing them in the supplied output smfData structures. Omit the header for U and I, as we will be copying the Q header into them. */ *odataq = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY, 0, 0, status ); *odatau = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY | SMF__NOCREATE_HEAD, 0, 0, status ); if( odatai ) { *odatai = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY | SMF__NOCREATE_HEAD, 0, 0, status ); } /* Restore values in idata now that we're done */ if( instate ) idata->hdr->allState = instate; if( indksquid ) idata->da->dksquid = indksquid; /* Store the required length for the output time axis. The time axis is axis two because the data is time-ordered. */ (*odataq)->dims[ 2 ] = ontslice; (*odatau)->dims[ 2 ] = ontslice; if( odatai) (*odatai)->dims[ 2 ] = ontslice; /* Get output dimensions - assumed to be the same for all three outputs. */ ondata = ontslice*idata->dims[0]*idata->dims[1]; /* Allocate the data arrays for the outputs. */ (*odataq)->pntr[0] = astCalloc( ondata, sizeof(double) ); (*odatau)->pntr[0] = astCalloc( ondata, sizeof(double) ); if( odatai ) (*odatai)->pntr[0] = astCalloc( ondata, sizeof(double) ); /* Allocate arrays for the output variances. */ (*odataq)->pntr[1] = astCalloc( ondata, sizeof(double) ); (*odatau)->pntr[1] = astCalloc( ondata, sizeof(double) ); if( odatai ) (*odatai)->pntr[1] = astCalloc( ondata, sizeof(double) ); /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); if( *status == SAI__OK ) { /* Determine which bolometers are to be processed by which threads. */ bstep = nbolo/nworker; if( bstep < 1 ) bstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->b1 = iworker*bstep; pdata->b2 = pdata->b1 + bstep - 1; } /* Ensure that the last thread picks up any left-over bolometers */ pdata->b2 = nbolo - 1; /* Loop round all output time slices. */ for( itime = 0; itime < ontslice; itime++ ) { /* Get the index of the first input time slice that contributes to the current output time slice. */ istart = box_starts[ itime ]; /* Get the number of input time slices that contribute to the output time slice. */ box_size = box_starts[ itime + 1 ] - istart; /* If we are using north as the reference direction, get the WCS FrameSet for the input time slice that is at the middle of the output time slice, and set its current Frame to the tracking frame. */ if( north ) { smf_tslice_ast( idata, istart + box_size/2, 1, NO_FTS, status ); wcs = idata->hdr->wcs; usesys = sc2ast_convert_system( (idata->hdr->allState)[0].tcs_tr_sys, status ); astSetC( wcs, "System", usesys ); } else { wcs = NULL; } /* Now enter the parellel code in which each thread calculates the values for a range of bolometers at the current output slice. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->dat = ((double *) idata->pntr[0] ) + istart*nbolo; pdata->qua = qua + istart*nbolo; pdata->allstates = hdr->allState + istart; pdata->ipi = odatai ? ( (double*) (*odatai)->pntr[0] ) + itime*nbolo : NULL; pdata->ipq = ( (double*) (*odataq)->pntr[0] ) + itime*nbolo; pdata->ipu = ( (double*) (*odatau)->pntr[0] ) + itime*nbolo; pdata->ipv = ( (double*) (*odataq)->pntr[1] ) + itime*nbolo; pdata->nbolo = nbolo; pdata->ncol = ncol; pdata->box_size = box_size; pdata->ipolcrd = ipolcrd; pdata->pasign = pasign ? +1: -1; pdata->paoff = paoff; pdata->angrot = angrot; if( wcs ) { pdata->wcs = astCopy( wcs ); astUnlock( pdata->wcs, 1 ); } else { pdata->wcs = NULL; } /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_fit_qui_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); /* Lock and annul the AST objects used by each thread. */ if( wcs ) { for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; astLock( pdata->wcs, 0 ); pdata->wcs = astAnnul( pdata->wcs ); } } } /* Down-sample the smfHead -------------------------------------------------*/ smfHead *hdr = (*odataq)->hdr; hdr->curframe = (dim_t) (((double) hdr->curframe + 0.5) / scale); hdr->nframes = ontslice; hdr->steptime *= scale; strcpy( hdr->dlabel, "Q" ); strncpy( hdr->title, "POL-2 Stokes parameter Q", SMF__CHARLABEL ); /* Down-sample all the JCMTState values using nearest neighbours */ instate = idata->hdr->allState; if( instate ) { hdr->allState = astCalloc( ontslice, sizeof(*instate) ); outstate = hdr->allState; if( *status == SAI__OK ) { size_t frame; /* index of nearest neighbour JCMTState */ for( i=0; i<ontslice; i++ ) { frame = (size_t) round(((double) i + 0.5)*scale); memcpy( outstate + i, instate + frame, sizeof(*instate) ); } /* Then go back and properly down-sample the more important fast-changing fields like pointing. Note that since there are approximate values there already we need to explicitly re-initialize to 0. */ RESAMPSTATE(instate, outstate, rts_end, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_az_jig_x, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_az_jig_y, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_az_chop_x, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_az_chop_y, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_tr_jig_x, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_tr_jig_y, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_tr_chop_x, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_tr_chop_y, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_tai, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_airmass, intslice, ontslice, 0); /* Second coordinates (Dec, El etc) can not wrap 0 to 360 so we do not need to test for those cases */ RESAMPSTATE(instate, outstate, tcs_az_ang, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_az_ac1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_az_ac2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_az_dc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_az_dc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_az_bc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_az_bc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_tr_ang, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_tr_ac1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_tr_ac2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_tr_dc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_tr_dc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_tr_bc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_tr_bc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_en_dc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_en_dc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_dm_abs, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_dm_rel, intslice, ontslice, 0); /* Wait for all the above smf_downsamp1 jobs to finish. */ thrWait( wf, status ); } } /* Add a keyword to the Q header indicating the polarimetric reference direction. */ smf_fits_updateL( (*odataq)->hdr, "POLNORTH", north, north ? "Pol ref dir is tracking north" : "Pol ref dir is focal plane Y", status ); /* Copy the Q header to the other outputs. */ hdr = smf_deepcopy_smfHead( (*odataq)->hdr, status ); (*odatau)->hdr = hdr; if( *status == SAI__OK ) { strcpy( hdr->dlabel, "U" ); strncpy( hdr->title, "POL-2 Stokes parameter U", SMF__CHARLABEL ); } if( odatai ) { hdr = smf_deepcopy_smfHead( (*odataq)->hdr, status ); (*odatai)->hdr = hdr; if( *status == SAI__OK ) { strcpy( hdr->dlabel, "I" ); strncpy( hdr->title, "POL-2 Stokes parameter I", SMF__CHARLABEL ); } } } /* Copy the variances from the Q smfData into the U and (and I) smfData. */ if( *odataq && *status == SAI__OK ) { if( *odatau ) { memcpy( (*odatau)->pntr[1], (*odataq)->pntr[1], ondata*sizeof(double)); } if( odatai && *odatai ) { memcpy( (*odatai)->pntr[1], (*odataq)->pntr[1], ondata*sizeof(double)); } } /* Ensure all smfDatas are time-ordered. */ smf_dataOrder( wf, idata, 1, status ); if( odatai && *odatai ) smf_dataOrder( wf, *odatai, 1, status ); if( *odataq ) smf_dataOrder( wf, *odataq, 1, status ); if( *odatau ) smf_dataOrder( wf, *odatau, 1, status ); /* Free resources. */ job_data = astFree( job_data ); box_starts = astFree( box_starts ); }