Hero::~Hero() { dbg(1) << "XXXX destroying hero"; if( m_ast.origWcsInfo != AST__NULL) { astAnnul( m_ast.origWcsInfo); } if( m_ast.currWcsInfo != AST__NULL) { astAnnul( m_ast.currWcsInfo); } }
static PyObject* pyndf_gtwcs(NDF *self) { AstFrameSet *wcs = NULL; char *string; PyObject *pywcs; PyObject *result = NULL; int status = SAI__OK; #if HAVE_AST errBegin(&status); astBegin; ndfGtwcs(self->_ndfid, &wcs, &status ); if( wcs ) { string = astToString( wcs ); wcs = astAnnul( wcs ); pywcs = PyAst_FromString( string ); string = astFree( string ); if( pywcs ) result = Py_BuildValue("O",pywcs); Py_XDECREF(pywcs); } astEnd; if (raiseNDFException(&status)) return NULL; #else PyErr_SetString( PyExc_NotImplementedError, "starlink.Ast must be available for WCS to be retrieved from an NDF"); #endif return result; };
AstKeyMap *smf_subinst_keymap( smf_subinst_t subinst, const smfData * indata, const Grp * igrp, size_t idx, int * status ) { const smfHead * hdr = NULL; /* Header of file to be examined */ size_t i; smfData * sub_data = NULL; /* File to be examined */ AstKeyMap * sub_instruments; /* Keymap to be filled */ if (*status != SAI__OK) return NULL; if (subinst == SMF__SUBINST_NONE && !indata && !igrp) { *status = SAI__ERROR; errRep( "", "Must supply either a subinst, a smfData or a Grp" " (possible programming error)", status ); return NULL; } /* Create new keymap */ sub_instruments = astKeyMap( " " ); /* prefill with the list of known sub-instruments. */ for (i = 0; i < SMF__SUBINST_NSUBINST; i++ ) { const char * substr = smf_subinst_str( i, status ); if (substr) astMapPut0I( sub_instruments, substr, 0, NULL ); } /* If the current sub-instrument has not been supplied, get it from the file. Use indata in preference to the group */ if( subinst == SMF__SUBINST_NONE ) { if (indata) { hdr = indata->hdr; } else { smf_open_file( igrp, idx, "READ", SMF__NOCREATE_DATA, &sub_data, status ); if (sub_data) { hdr = sub_data->hdr; } } if (hdr) subinst = smf_calc_subinst( hdr, status ); } /* flag this as being the relevant sub-instrument */ if (subinst != SMF__SUBINST_NSUBINST ) { const char * substr = smf_subinst_str( subinst, status ); if (substr) { astMapPut0I( sub_instruments, substr, 1, NULL ); } } if (sub_data) smf_close_file( &sub_data, status ); /* Free the keymap if we have bad status */ if (*status != SAI__OK && sub_instruments) { sub_instruments = astAnnul( sub_instruments ); } return sub_instruments; }
double smf_raw2current( smfHead *hdr, int *status ) { /* Local Variables */ double raw2current=VAL__BADD; /* Conversion factor */ double dateobs=VAL__BADD; /* UTC MJD at observation start */ double dateconst=VAL__BADD; /* UTC MJD when constant changes */ AstTimeFrame *tf = NULL; /* time frame for date conversion */ /* Main routine */ if( *status != SAI__OK ) return VAL__BADD; /* Get the MJD for start of the observation */ smf_find_dateobs( hdr, &dateobs, NULL, status ); /* Convert ISO dates for when the values changed to MJDs for comparison */ tf = astTimeFrame( " " ); astSet( tf, "TimeScale=UTC" ); astSet( tf, "TimeOrigin=%s", "2011-06-04T00:00:00" ); dateconst = astGetD( tf, "TimeOrigin" ); if( (*status==SAI__OK) && (dateobs!=VAL__BADD) && (dateconst!=VAL__BADD) ) { if( dateobs < dateconst) { /* Prior to 2011-06-04 we use the following value. According to Dan 1.52e-13 was the measured factor for mce mode 1 (unfiltered output) on some old prototype array. The 3.3 multiplier corrected the conversion for mce mode 2 (filtered output). Note that SIMULT is defined in smurf_par.h */ raw2current = SIMULT * 3.3 * 1.52e-13; } else { /* Later the MCE low-pass filter was modified to accomodate a faster sample rate. With the new firmware on s8a the measured factor for mce mode 1 (unfiltered output) is 1.56e-13 and to correct for mce mode 2 (filtered output) requires a 1/1.15 multiplier. */ raw2current = SIMULT * 1.56e-13 / 1.15; } } if( (*status==SAI__OK) && (raw2current==VAL__BADD) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": was unable to determine conversion factor", status ); } msgOutiff( MSG__DEBUG, "", FUNC_NAME ": calculated a value of %lf", status, raw2current ); /* Clean up */ tf = astAnnul( tf ); return raw2current; }
void smf_tswcsOrder( AstFrameSet **tswcs, int isTordered, int *status ){ /* Local Variables*/ AstFrameSet *newfs; const char *domain; int perm[ 3 ]; int swap; /* Check inherited status */ if (*status != SAI__OK) return; /* Check if the axes in the FrameSet are already in the right order. */ domain = astGetC( *tswcs, "Domain(3)" ); if( *status == SAI__OK ) { if( !strcmp( domain, "TIME" ) ) { swap = ( isTordered == 0 ); } else { swap = ( isTordered != 0 ); } /* Swap axes if required. */ if( swap ) { /* Get the a list of the old WCS axis indices in their new order. */ if( isTordered ) { perm[ 0 ] = 2; perm[ 1 ] = 3; perm[ 2 ] = 1; } else { perm[ 0 ] = 3; perm[ 1 ] = 1; perm[ 2 ] = 2; } /* Permute the axes in the current Frame of the WCS FrmeSet. This also adjusts the mappings that connects the current Frame to the other Frames. */ astPermAxes( *tswcs, perm ); /* We also need to permute the Mapping that connects the base Frame (i.e. grid coords) to the other Frames in the same way. Since astPermAxes operates on the current Frame of a FrameSet, we need to invert the FrameSet temporarily. */ astInvert( *tswcs ); astPermAxes( *tswcs, perm ); astInvert( *tswcs ); /* Simplify the modified FrameSet. */ newfs = astSimplify( *tswcs ); (void) astAnnul( *tswcs ); *tswcs = newfs; } } }
/* private routine for scanning a JCMTSTATE TAI or date FITS header */ static double smf__find_utc( const smfHead *hdr, int first, int *status) { double utc = VAL__BADD; AstTimeFrame *tf = NULL; if (*status != SAI__OK) return VAL__BADD; tf = astTimeFrame( " " ); if (hdr->allState) { dim_t index; if (first) { index = 0; } else { index = hdr->nframes; } astSet( tf, "TimeScale=TAI" ); astSet( tf, "TimeOrigin=MJD %.*g", DBL_DIG, (hdr->allState)[index].rts_end); astSet( tf, "TimeScale=UTC" ); /* we need UTC */ } else if (hdr->fitshdr) { /* look for DATE-OBS */ char iso[81]; const char * fitscard = NULL; astSet( tf, "TimeScale=UTC" ); if (first) { fitscard = "DATE-OBS"; } else { fitscard = "DATE-END"; } smf_fits_getS( hdr, fitscard, iso, sizeof(iso), status ); astSet( tf, "TimeOrigin=%s", iso ); } else { *status = SAI__ERROR; errRep( " ","Can not find date of observation without FITS header " "or JCMTSTATE extension", status ); } /* now read back the TimeOrigin as an MJD */ if (*status == SAI__OK) utc = astGetD( tf, "TimeOrigin" ); /* Clean up */ tf = astAnnul( tf ); return utc; }
/* Main entry */ void smf_jsadicer( int indf, const char *base, int trim, smf_inst_t instrument, smf_jsaproj_t proj, size_t *ntile, Grp *grp, int *status ){ /* Local Variables: */ AstBox *box; AstFitsChan *fc; AstFrame *specfrm = NULL; AstFrame *tile_frm = NULL; AstFrameSet *iwcs; AstFrameSet *tfs = NULL; AstFrameSet *tile_wcs; AstMapping *ndf_map = NULL; AstMapping *p2pmap = NULL; AstMapping *specmap = NULL; AstMapping *tile_map = NULL; AstRegion *region; Grp *grpt = NULL; char *path; char dtype[ NDF__SZFTP + 1 ]; char jsatile_comment[45]; char type[ NDF__SZTYP + 1 ]; const char *dom = NULL; const char *keyword; const char *latsys = NULL; const char *lonsys = NULL; double *pd; double dlbnd[3]; double dubnd[3]; double gcen[3]; double lbnd_in[3]; double lbnd_out[3]; double ubnd_in[3]; double ubnd_out[3]; float *pf; int *created_tiles = NULL; int *tiles; int axlat; int axlon; int axspec; int bbox[ 6 ]; int i; int ifrm; int igrid; int indfo; int indfs; int indfx; int inperm[3]; int ipixel; int ishpx; int isxph; int itile; int ix; int iy; int iz; int junk; int latax = -1; int lbnd[3]; int lbnd_tile[ 3 ]; int lbndx[ NDF__MXDIM ]; int lonax = -1; int nbase; int ndim; int ndimx; int nfrm; int nsig; int ntiles; int olbnd[ 3 ]; int oubnd[ 3 ]; int outperm[ 3 ]; int place; int qual; int tile_index; int tile_lbnd[2]; int tile_ubnd[2]; int ubnd[3]; int ubnd_tile[ 3 ]; int ubndx[ NDF__MXDIM ]; int var; size_t iext; size_t size; smfJSATiling tiling; unsigned char *ipq = NULL; void *ipd = NULL; void *ipv = NULL; /* Initialise */ *ntile = 0; /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context. */ astBegin; /* Begin an NDF context. */ ndfBegin(); /* Note the used length of the supplied base string. If it ends with ".sdf", reduce it by 4. */ nbase = astChrLen( base ); if( !strcmp( base + nbase - 4, ".sdf" ) ) nbase -= 4; /* Allocate a buffer large enough to hold the full path for an output NDF. */ path = astMalloc( nbase + 25 ); /* Get the WCS from the NDF. */ kpg1Gtwcs( indf, &iwcs, status ); /* Note if the NDF projection is HPX or XPH. */ ishpx = astChrMatch( astGetC( iwcs, "Projection" ), "HEALPix" ); isxph = astChrMatch( astGetC( iwcs, "Projection" ), "polar HEALPix" ); /* Report an error if the NDFs projection is neither of these. */ if( !ishpx && !isxph && *status == SAI__OK ) { ndfMsg( "N", indf ); *status = SAI__ERROR; errRep( "", "The input NDF (^N) does not appear to be gridded " "on the JSA all-sky pixel grid.", status ); } /* Get the bounds of the NDF in pixel indices and the the corresponding double precision GRID bounds (reduce the size of the grid by a small amount to avoid problems with tiles that are on the edge of the valid sky regions - astMapRegion can report an error for such tiles). Also store the GRID coords of the centre. Also count the number of significant pixel axes. */ ndfBound( indf, 3, lbnd, ubnd, &ndim, status ); nsig = 0; for( i = 0; i < ndim; i++ ) { dlbnd[ i ] = 0.5 + 0.1; dubnd[ i ] = ubnd[ i ] - lbnd[ i ] + 1.5 - 0.1; gcen[ i ] = 0.5*( dlbnd[ i ] + dubnd[ i ] ); if( ubnd[ i ] > lbnd[ i ] ) nsig++; } /* Find the one-based indices of the RA, Dec and spectral axes in the current Frame of the NDF. */ axlon = 0; if( astGetI( iwcs, "IsLonAxis(1)" ) ) { axlon = 1; lonsys = astGetC( iwcs, "System(1)" ); } else if( astGetI( iwcs, "IsLonAxis(2)" ) ) { axlon = 2; lonsys = astGetC( iwcs, "System(2)" ); } else if( ndim == 3 && astGetI( iwcs, "IsLonAxis(3)" ) ) { axlon = 3; lonsys = astGetC( iwcs, "System(3)" ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_jsadicer: Cannot find the longitude axis in the " "input NDF.", status ); } axlat = 0; if( astGetI( iwcs, "IsLatAxis(1)" ) ) { axlat = 1; latsys = astGetC( iwcs, "System(1)" ); } else if( astGetI( iwcs, "IsLatAxis(2)" ) ) { axlat = 2; latsys = astGetC( iwcs, "System(2)" ); } else if( ndim == 3 && astGetI( iwcs, "IsLatAxis(3)" ) ) { axlat = 3; latsys = astGetC( iwcs, "System(3)" ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_jsadicer: Cannot find the latitude axis in the " "input NDF.", status ); } axspec = 6 - axlon - axlat; /* Report an error if the spatial axes are not ICRS RA and Dec. */ if( ( lonsys && strcmp( lonsys, "ICRS" ) ) || ( latsys && strcmp( latsys, "ICRS" ) ) ) { if( *status == SAI__OK ) { *status = SAI__ERROR; ndfMsg( "N", indf ); errRep( "", "smf_jsadicer: The spatial axes in '^N' are not " "ICRS RA and Dec.", status ); } } /* Create a Box describing the region covered by the NDF pixel grid in GRID coords. */ box = astBox( astGetFrame( iwcs, AST__BASE ), 1, dlbnd, dubnd, AST__NULL, " " ); /* Map this Box into the current WCS Frame of the NDF. */ region = astMapRegion( box, iwcs, iwcs ); /* If no instrument was specified, we will determine the instrument from the contexts of the FITS extension. Copy the NDF FITS extension to a FitsChan. Annul the error if the NDF no FITS extension. */ if( instrument == SMF__INST_NONE && *status == SAI__OK ) { kpgGtfts( indf, &fc, status ); if( *status == KPG__NOFTS ) { errAnnul( status ); fc = NULL; } } else { fc = NULL; } /* Get the parameters of the required tiling scheme. */ smf_jsainstrument( NULL, fc, instrument, &tiling, status ); /* Get a list of the JSA tiles touched by the supplied NDF. */ tiles = smf_jsatiles_region( region, &tiling, &ntiles, status ); if( ntiles == 0 && *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_jsadicer: No JSA tiles found touching supplied NDF " "(programming error).", status ); } /* Does the input NDF have a Variance component? */ ndfState( indf, "Variance", &var, status ); /* Does the input NDF have a Quality component? */ ndfState( indf, "Quality", &qual, status ); /* Decide on the data type to use: _REAL or _DOUBLE. */ ndfMtype( "_REAL,_DOUBLE", indf, indf, "Data", type, sizeof(type), dtype, sizeof(dtype), status ); /* Tell the user what is happening. */ msgBlank( status ); msgOutf( "", "Dicing %s into JSA tiles:", status, ( nsig == 2 ) ? "map" : "cube" ); /* Loop round all tiles that overlap the supplied NDF. */ for( itile = 0; itile < ntiles && *status == SAI__OK; itile++ ) { tile_index = tiles[ itile ]; /* Get the spatial pixel bounds of the current tile within the requested JSA all-sky projection. Also get the (2D) WCS FrameSet for the tile. */ smf_jsatile( tile_index, &tiling, 0, proj, NULL, &tile_wcs, NULL, tile_lbnd, tile_ubnd, status ); /* Extract the tile pixel->WCS mapping and WCS Frame. We know the indices of the required Frames because they are hard-wired in smf_jsatile. */ tile_map = astGetMapping( tile_wcs, 3, 2 ); tile_frm = astGetFrame( tile_wcs, 2 ); /* Find the indices of the grid and pixel frames in the input NDF. */ ipixel = -1; igrid = astGetI( iwcs, "Base" ); nfrm = astGetI( iwcs, "NFrame" ); for( ifrm = 0; ifrm < nfrm; ifrm++ ) { dom = astGetC( astGetFrame( iwcs, ifrm + 1 ), "Domain" ); if( astChrMatch( dom, "PIXEL" ) ) ipixel = ifrm + 1; } /* If required, extract the pixel->spectral mapping and spectral frame in the input NDF, and add it in parallel with the above tile mapping. */ if( ndim == 3 ) { astSetI( iwcs, "Base", ipixel ); tfs = atlFrameSetSplit( iwcs, "DSBSPECTRUM SPECTRUM", NULL, NULL, status ); astSetI( iwcs, "Base", igrid ); if( tfs ) { specmap = astGetMapping( tfs, AST__BASE, AST__CURRENT ); specfrm = astGetFrame( tfs, AST__CURRENT ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; ndfMsg( "N", indf ); errRep( "", "smf_jsadicer: Cannot find the spectral axis " "in '^N'.", status ); } tile_map = (AstMapping *) astCmpMap( tile_map, specmap, 0, " " ); tile_frm = (AstFrame *) astCmpFrame( tile_frm, specfrm, " " ); } /* Ensure the Epoch is inherited form the input NDF. */ astSetD( tile_frm, "Epoch", astGetD( iwcs, "Epoch" ) ); /* Currently tile axis 1 is RA, axis 2 is Dec and axis 3 (if present) is spectral. Append a PermMap that re-orders these tile WCS axes to match those of the NDF. */ outperm[ axlon - 1 ] = 1; outperm[ axlat - 1 ] = 2; outperm[ axspec - 1 ] = 3; inperm[ 0 ] = axlon; inperm[ 1 ] = axlat; inperm[ 2 ] = axspec; tile_map = (AstMapping *) astCmpMap( tile_map, astPermMap( ndim, inperm, ndim, outperm, NULL, " " ), 1, " " ); tile_map = astSimplify( tile_map ); /* Also re-order the WCS axes in the tile frame. */ astPermAxes( tile_frm, outperm ); /* We want the zero-based indicies of the input pixel axes corresponding to ra, dec and spectral. So find the indicies of the pixel axes in the supplied NDF that are most closely aligned with each WCS axis. */ atlPairAxes( iwcs, NULL, gcen, NULL, inperm, status ); if( inperm[ 0 ] == axlon ) { lonax = 0; } else if( inperm[ 1 ] == axlon ) { lonax = 1; } else { lonax = 2; } if( inperm[ 0 ] == axlat ) { latax = 0; } else if( inperm[ 1 ] == axlat ) { latax = 1; } else { latax = 2; } /* To get the mapping from pixel coords in the input NDF to pixel coords in the output NDF, we invert the above mapping so that it goes from WCS to pixel, and append it to the end of the NDF pixel->WCS mapping. */ ndf_map = astGetMapping( iwcs, ipixel, AST__CURRENT ); astInvert( tile_map ); p2pmap = (AstMapping *) astCmpMap( ndf_map, tile_map, 1, " " ); p2pmap = astSimplify( p2pmap ); astInvert( tile_map ); /* Show the bounds of the tile within the input NDF. */ msgOutiff( MSG__DEBUG, "", " tile %d has bounds (%d:%d,%d:%d) " "within the output NDF.", status, tile_index, tile_lbnd[ 0 ], tile_ubnd[ 0 ], tile_lbnd[ 1 ], tile_ubnd[ 1 ] ); /* Next job is to find the pixel bounds of the output NDF to create which will hold data for the current tile. First map the pixel bounds of the whole tile from output to input. */ lbnd_in[ 0 ] = tile_lbnd[ 0 ] - 0.5; lbnd_in[ 1 ] = tile_lbnd[ 1 ] - 0.5; lbnd_in[ 2 ] = lbnd[ 2 ] - 0.5; ubnd_in[ 0 ] = tile_ubnd[ 0 ] - 0.5; ubnd_in[ 1 ] = tile_ubnd[ 1 ] - 0.5; ubnd_in[ 2 ] = ubnd[ 2 ] - 0.5; astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 1, lbnd_out + 0, ubnd_out + 0, NULL, NULL ); astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 2, lbnd_out + 1, ubnd_out + 1, NULL, NULL ); if( ndim == 3 ) astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 3, lbnd_out + 2, ubnd_out + 2, NULL, NULL ); lbnd_tile[ 0 ] = floor( lbnd_out[ 0 ] ) + 1; lbnd_tile[ 1 ] = floor( lbnd_out[ 1 ] ) + 1; lbnd_tile[ 2 ] = floor( lbnd_out[ 2 ] ) + 1; ubnd_tile[ 0 ] = floor( ubnd_out[ 0 ] ) + 1; ubnd_tile[ 1 ] = floor( ubnd_out[ 1 ] ) + 1; ubnd_tile[ 2 ] = floor( ubnd_out[ 2 ] ) + 1; /* Show the bounds of the tile within the input NDF. */ msgOutiff( MSG__DEBUG, "", " tile %d has bounds (%d:%d,%d:%d) " "within the input NDF.", status, tile_index, lbnd_tile[ 0 ], ubnd_tile[ 0 ], lbnd_tile[ 1 ], ubnd_tile[ 1 ] ); /* If required, trim the bounds to the extent of the input NDF. */ if( trim ) { if( lbnd_tile[ 0 ] < lbnd[ 0 ] ) lbnd_tile[ 0 ] = lbnd[ 0 ]; if( lbnd_tile[ 1 ] < lbnd[ 1 ] ) lbnd_tile[ 1 ] = lbnd[ 1 ]; if( lbnd_tile[ 2 ] < lbnd[ 2 ] ) lbnd_tile[ 2 ] = lbnd[ 2 ]; if( ubnd_tile[ 0 ] > ubnd[ 0 ] ) ubnd_tile[ 0 ] = ubnd[ 0 ]; if( ubnd_tile[ 1 ] > ubnd[ 1 ] ) ubnd_tile[ 1 ] = ubnd[ 1 ]; if( ubnd_tile[ 2 ] > ubnd[ 2 ] ) ubnd_tile[ 2 ] = ubnd[ 2 ]; } /* Check there is some overlap. */ if( lbnd_tile[ 0 ] <= ubnd_tile[ 0 ] && lbnd_tile[ 1 ] <= ubnd_tile[ 1 ] && lbnd_tile[ 2 ] <= ubnd_tile[ 2 ] ){ /* Now need to check if this section of the input NDF contains any good values. We also find the bounding box of the good values (within the input pixel coordinate system). So first obtain and map the required section of the input NDF. */ ndfSect( indf, ndim, lbnd_tile, ubnd_tile, &indfs, status ); ndfMap( indfs, "Data", type, "Read", &ipd, &junk, status ); if( var ) ndfMap( indfs, "Variance", type, "Read", &ipv, &junk, status ); if( qual ) ndfMap( indfs, "Quality", "_UBYTE", "Read", (void **) &ipq, &junk, status ); /* Initialise the pixel bounds (within the input NDF) of the box holding good data values for the current tile. */ bbox[ 0 ] = INT_MAX; bbox[ 1 ] = INT_MAX; bbox[ 2 ] = INT_MAX; bbox[ 3 ] = -INT_MAX; bbox[ 4 ] = -INT_MAX; bbox[ 5 ] = -INT_MAX; /* Loop round all pixels in the section. */ if( *status == SAI__OK ) { if( !strcmp( type, "_REAL" ) ) { pf = (float *) ipd; for( iz = lbnd_tile[ 2 ]; iz <= ubnd_tile[ 2 ]; iz++ ) { for( iy = lbnd_tile[ 1 ]; iy <= ubnd_tile[ 1 ]; iy++ ) { for( ix = lbnd_tile[ 0 ]; ix <= ubnd_tile[ 0 ]; ix++ ) { if( *(pf++) != VAL__BADR ) { if( ix < bbox[ 0 ] ) bbox[ 0 ] = ix; if( iy < bbox[ 1 ] ) bbox[ 1 ] = iy; if( iz < bbox[ 2 ] ) bbox[ 2 ] = iz; if( ix > bbox[ 3 ] ) bbox[ 3 ] = ix; if( iy > bbox[ 4 ] ) bbox[ 4 ] = iy; if( iz > bbox[ 5 ] ) bbox[ 5 ] = iz; } } } } } else { pd = (double *) ipd; for( iz = lbnd_tile[ 2 ]; iz <= ubnd_tile[ 2 ]; iz++ ) { for( iy = lbnd_tile[ 1 ]; iy <= ubnd_tile[ 1 ]; iy++ ) { for( ix = lbnd_tile[ 0 ]; ix <= ubnd_tile[ 0 ]; ix++ ) { if( *(pd++) != VAL__BADD ) { if( ix < bbox[ 0 ] ) bbox[ 0 ] = ix; if( iy < bbox[ 1 ] ) bbox[ 1 ] = iy; if( iz < bbox[ 2 ] ) bbox[ 2 ] = iz; if( ix > bbox[ 3 ] ) bbox[ 3 ] = ix; if( iy > bbox[ 4 ] ) bbox[ 4 ] = iy; if( iz > bbox[ 5 ] ) bbox[ 5 ] = iz; } } } } } /* Skip empty tiles. */ if( bbox[ 0 ] != INT_MAX ) { msgOutf( "", " tile %d", status, tile_index ); /* If required, trim the bounds to the edges of the bounding box. */ if( trim >= 2 ) { olbnd[ 0 ] = bbox[ 0 ]; olbnd[ 1 ] = bbox[ 1 ]; olbnd[ 2 ] = bbox[ 2 ]; oubnd[ 0 ] = bbox[ 3 ]; oubnd[ 1 ] = bbox[ 4 ]; oubnd[ 2 ] = bbox[ 5 ]; } else { olbnd[ 0 ] = lbnd_tile[ 0 ]; olbnd[ 1 ] = lbnd_tile[ 1 ]; olbnd[ 2 ] = lbnd_tile[ 2 ]; oubnd[ 0 ] = ubnd_tile[ 0 ]; oubnd[ 1 ] = ubnd_tile[ 1 ]; oubnd[ 2 ] = ubnd_tile[ 2 ]; } /* Modify these pixel bounds so that they refer to the output NDF. */ lbnd_in[ 0 ] = olbnd[ 0 ] - 0.5; lbnd_in[ 1 ] = olbnd[ 1 ] - 0.5; lbnd_in[ 2 ] = olbnd[ 2 ] - 0.5; ubnd_in[ 0 ] = oubnd[ 0 ] - 0.5; ubnd_in[ 1 ] = oubnd[ 1 ] - 0.5; ubnd_in[ 2 ] = oubnd[ 2 ] - 0.5; astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 1, lbnd_out + 0, ubnd_out + 0, NULL, NULL ); astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 2, lbnd_out + 1, ubnd_out + 1, NULL, NULL ); if( ndim == 3 ) astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 3, lbnd_out + 2, ubnd_out + 2, NULL, NULL ); olbnd[ 0 ] = floor( lbnd_out[ 0 ] ) + 1; olbnd[ 1 ] = floor( lbnd_out[ 1 ] ) + 1; olbnd[ 2 ] = floor( lbnd_out[ 2 ] ) + 1; oubnd[ 0 ] = floor( ubnd_out[ 0 ] ) + 1; oubnd[ 1 ] = floor( ubnd_out[ 1 ] ) + 1; oubnd[ 2 ] = floor( ubnd_out[ 2 ] ) + 1; /* Get the full path to the output NDF for the current tile, and create an NDF placeholder for it. */ sprintf( path, "%.*s_%d", nbase, base, tile_index ); ndfPlace( NULL, path, &place, status ); /* Create a new output NDF by copying the meta-data from the input NDF section. */ ndfScopy( indfs, "Units", &place, &indfo, status ); /* Set the pixel bounds of the output NDF to the values found above and copy the input data for the current tile into it. */ smf1_jsadicer( indfo, olbnd, oubnd, tile_map, tile_frm, p2pmap, ipd, ipv, ipq, status ); /* Add the name of this output NDF to the group holding the names of the output NDFs that have actually been created. */ if( grp ) grpPut1( grp, path, 0, status ); /* Add a TILENUM header to the output FITS extension. */ kpgGtfts( indfo, &fc, status ); if( *status == KPG__NOFTS ) { errAnnul( status ); fc = astFitsChan( NULL, NULL, " " ); /* If the last card is "END", remove it. */ } else { astSetI( fc, "Card", astGetI( fc, "NCARD" ) ); keyword = astGetC( fc, "CardName" ); if( keyword && !strcmp( keyword, "END" ) ) astDelFits( fc ); } one_snprintf(jsatile_comment, 45, "JSA all-sky tile index (Nside=%i)", status, tiling.ntpf); atlPtfti( fc, "TILENUM", tile_index, jsatile_comment, status ); kpgPtfts( indfo, fc, status ); fc = astAnnul( fc ); /* Now store an STC-S polygon that describes the shortest boundary enclosing the good data in the output NDF, and store it as an NDF extension. */ kpgPutOutline( indfo, 0.5, 1, status ); /* We now reshape any extension NDFs contained within the output NDF to have the same spatial bounds as the main NDF (but only for extension NDFs that originally have the same spatial bounds as the supplied NDF). Get a group containing paths to all extension NDFs in the output NDF. */ ndgMoreg( indfo, &grpt, &size, status ); /* Loop round each output extension NDF. */ for( iext = 1; iext <= size && *status == SAI__OK; iext++ ) { ndgNdfas( grpt, iext, "Update", &indfx, status ); /* Get its bounds. */ ndfBound( indfx, NDF__MXDIM, lbndx, ubndx, &ndimx, status ); /* See if this extension NDF has the same bounds on the spatial axes as the supplied NDF. */ if( ndimx > 1 && lbndx[ lonax ] == lbnd[ lonax ] && lbndx[ latax ] == lbnd[ latax ] && ubndx[ lonax ] == ubnd[ lonax ] && ubndx[ latax ] == ubnd[ latax ] ) { /* If so, change the bounds of the output extension NDF so that they are the same as the main NDF on the spatial axes, and map the original contents of the NDF onto the new pixel grid. */ smf1_jsadicer( indfx, olbnd, oubnd, tile_map, tile_frm, p2pmap, NULL, NULL, NULL, status ); } /* Annul the extension NDF identifier. */ ndfAnnul( &indfx, status ); } /* Free resources associated with the current tile. */ grpDelet( &grpt, status ); ndfAnnul( &indfo, status ); /* Issue warnings about empty tiles. */ } else { msgOutiff( MSG__VERB, "", " tile %d is empty and so will not be " "created", status, tile_index ); } } /* Free the section of the input NDF. */ ndfAnnul( &indfs, status ); /* Append the index of this tile in the list of tiles to be created. */ created_tiles = astGrow( created_tiles, ++(*ntile), sizeof( *created_tiles ) ); if( *status == SAI__OK ) created_tiles[ *ntile - 1 ] = tile_index; } else { msgOutiff( MSG__DEBUG, "", " Tile %d does not overlap the input " "NDF after trimming.", status, tile_index ); } } msgBlank( status ); /* Write the indicies of the created tiles out to a parameter. */ if( *ntile ) parPut1i( "JSATILELIST", *ntile, created_tiles, status ); /* Free resources. */ created_tiles = astFree( created_tiles ); tiles = astFree( tiles ); path = astFree( path ); /* End the NDF context. */ ndfEnd( status ); /* End the AST context. */ astEnd; }
void 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_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_calcmodel_gai( ThrWorkForce *wf, smfDIMMData *dat, int chunk, AstKeyMap *keymap, smfArray **allmodel, int flags, int *status) { /* Local Variables */ size_t bstride; /* bolometer stride */ dim_t gain_box=0; /* No. of time slices in a block */ size_t gbstride; /* GAIn bolo stride */ size_t gcstride; /* GAIn coeff stride */ int gflat=0; /* correct flatfield using GAI */ dim_t i; /* Loop counter */ dim_t idx=0; /* Index within subgroup */ dim_t j; /* Loop counter */ AstKeyMap *kmap=NULL; /* Local GAIn keymap */ smfArray *model=NULL; /* Pointer to model at chunk */ double *model_data=NULL; /* Pointer to DATA component of model */ dim_t nblock; /* No. of time slice blocks */ dim_t nbolo; /* Number of bolometers */ dim_t ndata; /* Number of data points */ smfArray *noi=NULL; /* Pointer to NOI at chunk */ double *noi_data=NULL; /* Pointer to DATA component of model */ size_t noibstride; /* bolo stride for noise */ dim_t nointslice; /* number of time slices for noise */ size_t noitstride; /* Time stride for noise */ dim_t npar; /* No. of parameters per bolometer */ dim_t ntslice; /* Number of time slices */ int oldalg = 1; /* Is the old COM algorithm being used? */ smfArray *qua=NULL; /* Pointer to QUA at chunk */ smf_qual_t *qua_data=NULL; /* Pointer to quality data */ smfArray *res=NULL; /* Pointer to RES at chunk */ double *res_data=NULL; /* Pointer to DAT */ double *scale; /* Pointer to scale factor */ size_t tstride; /* time slice stride */ double *wg; /* Workspace holding time slice gains */ double *woff; /* Workspace holding time slice offsets */ /* Main routine */ if( *status != SAI__OK ) return; if( !(flags&SMF__DIMM_INVERT) ) return; /* See if the new sigma-clipping COM algorithm is being used. */ astMapGet0A( keymap, "COM", &kmap ); astMapGet0I( kmap, "OLDALG", &oldalg ); kmap = astAnnul( kmap ); /* Obtain pointer to sub-keymap containing GAI parameters */ if( !astMapHasKey( keymap, "GAI" ) ) return; astMapGet0A( keymap, "GAI", &kmap ); astMapGet0I( kmap, "FLATFIELD", &gflat ); if( kmap ) kmap = astAnnul( kmap ); /* Report an error if gai.flatfield is used with the new COM algorithm. */ if( !oldalg && gflat && *status == SAI__OK ) { errRep( "", "Cannot use GAI.FLATFIELD with new COM algorithm.", status ); } /* Only have to do something if gai.flatfield set */ if( !gflat || *status != SAI__OK ) return; /* Obtain pointers to relevant smfArrays for this chunk */ res = dat->res[chunk]; qua = dat->qua[chunk]; model = allmodel[chunk]; if(dat->noi) noi = dat->noi[chunk]; /* Get the number of blocks into which to split each time series. Each box (except possibly the last one contains "gain_box" time slices. */ astMapGet0A( keymap, "COM", &kmap ); smf_get_nsamp( kmap, "GAIN_BOX", res->sdata[0], &gain_box, status ); if (kmap) kmap = astAnnul( kmap ); if (*status != SAI__OK) return; /* Ensure everything is in bolo-order */ smf_model_dataOrder( wf, dat, allmodel, chunk, SMF__RES|SMF__QUA|SMF__NOI, 0, status ); /* Loop over index in subgrp (subarray) */ for( idx=0; idx<res->ndat; idx++ ) { /* Get pointers to DATA components */ res_data = (res->sdata[idx]->pntr)[0]; qua_data = (qua->sdata[idx]->pntr)[0]; model_data = (model->sdata[idx]->pntr)[0]; if( noi ) { smf_get_dims( noi->sdata[idx], NULL, NULL, NULL, &nointslice, NULL, &noibstride, &noitstride, status); noi_data = (double *)(noi->sdata[idx]->pntr)[0]; } if( (res_data == NULL) || (model_data == NULL) || (qua_data == NULL) ) { *status = SAI__ERROR; errRep("", FUNC_NAME ": Null data in inputs", status); } else { /* Get the raw data dimensions */ smf_get_dims( res->sdata[idx], NULL, NULL, &nbolo, &ntslice, &ndata, &bstride, &tstride, status); smf_get_dims( model->sdata[idx], NULL, NULL, NULL, &npar, NULL, &gbstride, &gcstride, status); /* If com.gain_box is zero, use a value of ntslice, so that a single box will be used covering the whoel time stream. */ if( gain_box == 0 ) gain_box = ntslice; /* Allocate work space for the gain and offset for each time slice. */ woff = astMalloc( ntslice*sizeof( *woff ) ); wg = astMalloc( ntslice*sizeof( *wg ) ); /* Get the number of blocks into which the time stream is divided. Each block has a separate gain, offset and correlation factor for each bolometer. */ nblock = npar/3; /* Undo the gain correction stored in GAI (the gain is applied to the signal and noise in smf_calcmodel_com) */ for( i=0; i<nbolo; i++ ) { if( !(qua_data[i*bstride]&SMF__Q_BADB) ) { /* Get the gain and offset for each time slice of this bolometer. */ smf_gandoff( i, 0, ntslice - 1, ntslice, gbstride, gcstride, model_data, nblock, gain_box, wg, woff, NULL, status ); /* First undo the flatfield correction to the signal */ scale = wg; for( j=0; j<ntslice; j++,scale++ ) { if( !(qua_data[i*bstride + j*tstride]&SMF__Q_MOD) && *scale != VAL__BADD && *scale > 0.0 ) { res_data[i*bstride + j*tstride] *= *scale; } } /* Then scale the noise. */ if( noi ) { scale = wg; for( j=0; j<nointslice; j++,scale++ ) { if( noi_data[i*noibstride + j*noitstride] != VAL__BADD && *scale != VAL__BADD && *scale > 0.0 ) { noi_data[i*noibstride + j*noitstride] *= (*scale) * (*scale); } } } } } /* Free work space. */ woff = astFree( woff ); wg = astFree( wg ); } } }
void smurf_calcqu( int *status ) { /* Local Variables: */ AstFitsChan *fc; /* Holds FITS headers for output NDFs */ AstKeyMap *config; /* Holds all cleaning parameters */ AstKeyMap *dkpars; /* Holds dark squid cleaning parameters */ AstKeyMap *heateffmap = NULL; /* Heater efficiency data */ AstKeyMap *sub_instruments;/* Indicates which instrument is being used */ Grp *bgrp = NULL; /* Group of base names for each chunk */ Grp *igrp = NULL; /* Group of input files */ Grp *ogrp = NULL; /* Group of output files */ Grp *sgrp = NULL; /* Group of science files */ HDSLoc *loci = NULL; /* Locator for output I container file */ HDSLoc *locq = NULL; /* Locator for output Q container file */ HDSLoc *locu = NULL; /* Locator for output U container file */ NdgProvenance *oprov; /* Provenance to store in each output NDF */ ThrWorkForce *wf; /* Pointer to a pool of worker threads */ char headval[ 81 ]; /* FITS header value */ char ndfname[ 30 ]; /* Name of output Q or U NDF */ char polcrd[ 81 ]; /* FITS 'POL_CRD' header value */ char subarray[ 10 ]; /* Subarray name (e.g. "s4a", etc) */ double angrot; /* Angle from focal plane X axis to fixed analyser */ double paoff; /* WPLATE value corresponding to POL_ANG=0.0 */ float arcerror; /* Max acceptable error (arcsec) in one block */ int block_end; /* Index of last time slice in block */ int block_start; /* Index of first time slice in block */ int dkclean; /* Clean dark squids? */ int fix; /* Fix the POL-2 triggering issue? */ int iblock; /* Index of current block */ int iplace; /* NDF placeholder for current block's I image */ int ipolcrd; /* Reference direction for waveplate angles */ int maxsize; /* Max no. of time slices in a block */ int minsize; /* Min no. of time slices in a block */ int nc; /* Number of characters written to a string */ int pasign; /* +1 or -1 indicating sense of POL_ANG value */ int qplace; /* NDF placeholder for current block's Q image */ int submean; /* Subtract mean value from each time slice? */ int uplace; /* NDF placeholder for current block's U image */ size_t ichunk; /* Continuous chunk counter */ size_t idx; /* Subarray counter */ size_t igroup; /* Index for group of related input NDFs */ size_t inidx; /* Index into group of science input NDFs */ size_t nchunk; /* Number continuous chunks outside iter loop */ size_t ssize; /* Number of science files in input group */ smfArray *concat = NULL; /* Pointer to smfArray holding bolometer data */ smfArray *darks = NULL; /* dark frames */ smfArray *dkarray = NULL; /* Pointer to smfArray holding dark squid data */ smfArray *flatramps = NULL;/* Flatfield ramps */ smfData *data = NULL; /* Concatenated data for one subarray */ smfData *dkdata = NULL; /* Concatenated dark squid data for one subarray */ smfGroup *sgroup = NULL; /* smfGroup corresponding to sgrp */ /* Check inhereited status */ if( *status != SAI__OK ) return; /* Start new AST and NDF contexts. */ astBegin; ndfBegin(); /* Find the number of cores/processors available and create a work force holding the same number of threads. */ wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status ); /* Get a group of input files */ kpg1Rgndf( "IN", 0, 1, " Give more NDFs...", &igrp, &ssize, status ); /* Get a group containing just the files holding science data. */ smf_find_science( igrp, &sgrp, 0, NULL, NULL, 1, 1, SMF__NULL, &darks, &flatramps, &heateffmap, NULL, status ); /* Check we have at least once science file. */ ssize = grpGrpsz( sgrp, status ); if( ssize == 0 ) { msgOutif( MSG__NORM, " ", "All supplied input frames were DARK.", status ); } else { /* See if a correction should be made for the POL2 triggering issue. */ parGet0l( "FIX", &fix, status ); /* Create HDS container files to hold the output NDFs. */ datCreat( "OUTQ", "CALCQU", 0, 0, status ); datCreat( "OUTU", "CALCQU", 0, 0, status ); /* Associate the locators with the structures. */ datAssoc( "OUTQ", "WRITE", &locq, status ); datAssoc( "OUTU", "WRITE", &locu, status ); /* The I images are optional. */ if( *status == SAI__OK ) { datCreat( "OUTI", "CALCQU", 0, 0, status ); datAssoc( "OUTI", "WRITE", &loci, status ); if( *status == PAR__NULL ) { errAnnul( status ); loci = NULL; } } /* Group the input files so that all files within a single group have the same wavelength and belong to the same subscan of the same observation. Also identify chunks of data that are contiguous in time, and determine to which such chunk each group belongs. All this information is returned in a smfGroup structure ("*sgroup"). */ smf_grp_related( sgrp, ssize, 1, 1, 0, NULL, NULL, NULL, NULL, &sgroup, &bgrp, NULL, status ); /* Obtain the number of contiguous chunks. */ if( *status == SAI__OK ) { nchunk = sgroup->chunk[ sgroup->ngroups - 1 ] + 1; } else { nchunk = 0; } /* Indicate we have not yet found a value for the ARCERROR parameter. */ arcerror = 0.0; /* Loop over all contiguous chunks */ for( ichunk = 0; ichunk < nchunk && *status == SAI__OK; ichunk++ ) { /* Display the chunk number. */ if( nchunk > 1 ) { msgOutiff( MSG__VERB, "", " Doing chunk %d of %d.", status, (int) ichunk + 1, (int) nchunk ); } /* Concatenate the data within this contiguous chunk. This produces a smfArray ("concat") containing a smfData for each subarray present in the chunk. Each smfData holds the concatenated data for a single subarray. */ smf_concat_smfGroup( wf, NULL, sgroup, darks, NULL, flatramps, heateffmap, ichunk, 1, 1, NULL, 0, NULL, NULL, 0, 0, 0, &concat, NULL, status ); /* Get a KeyMap holding values for the configuration parameters. Since we sorted by wavelength when calling smf_grp_related, we know that all smfDatas in the current smfArray (i.e. chunk) will relate to the same wavelength. Therefore we can use the same parameters for all smfDatas in the current smfArray. */ sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE, concat->sdata[ 0 ], NULL, 0, status ); config = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_calcqu.def", sub_instruments, status ); sub_instruments = astAnnul( sub_instruments ); /* Get the CALCQU specific parameters. */ if( !astMapGet0I( config, "PASIGN", &pasign ) ) pasign = 1; msgOutiff( MSG__VERB, "", "PASIGN=%d", status, pasign ); if( !astMapGet0D( config, "PAOFF", &paoff ) ) paoff = 0.0; msgOutiff( MSG__VERB, "", "PAOFF=%g", status, paoff ); if( !astMapGet0D( config, "ANGROT", &angrot ) ) angrot = 90.0; msgOutiff( MSG__VERB, "", "ANGROT=%g", status, angrot ); if( !astMapGet0I( config, "SUBMEAN", &submean ) ) submean = 0; msgOutiff( MSG__VERB, "", "SUBMEAN=%d", status, submean ); /* See if the dark squids should be cleaned. */ if( !astMapGet0I( config, "DKCLEAN", &dkclean ) ) dkclean = 0; /* If required, clean the dark squids now since we might need to use them to clean the bolometer data. */ if( dkclean ) { /* Create a smfArray containing the dark squid data. For each one, store a pointer to the main header so that smf_clean_smfArray can get at the JCMTState information. */ dkarray = smf_create_smfArray( status ); for( idx = 0; idx < concat->ndat && *status == SAI__OK; idx++ ) { data = concat->sdata[ idx ]; if( data && data->da && data->da->dksquid ) { dkdata = data->da->dksquid; dkdata->hdr = data->hdr; smf_addto_smfArray( dkarray, dkdata, status ); } } /* Clean the smfArray containing the dark squid data. Use the "CLEANDK.*" parameters. */ (void) astMapGet0A( config, "CLEANDK", &dkpars ); smf_clean_smfArray( wf, dkarray, NULL, NULL, NULL, dkpars, status ); dkpars = astAnnul( dkpars ); /* Nullify the header pointers so that we don't accidentally close any. */ if( dkarray ) { for( idx = 0; idx < dkarray->ndat; idx++ ) { dkdata = dkarray->sdata[ idx ]; dkdata->hdr = NULL; } /* Free the smfArray holding the dark squid data, but do not free the individual smfDatas within it. */ dkarray->owndata = 0; smf_close_related( &dkarray, status ); } } /* Now clean the bolometer data */ smf_clean_smfArray( wf, concat, NULL, NULL, NULL, config, status ); /* If required correct for the POL2 triggering issue. */ if( fix ) smf_fix_pol2( wf, concat, status ); /* Loop round each sub-array in the current contiguous chunk of data. */ for( idx = 0; idx < concat->ndat && *status == SAI__OK; idx++ ) { data = concat->sdata[ idx ]; /* Find the name of the subarray that generated the data. */ smf_find_subarray( data->hdr, subarray, sizeof(subarray), NULL, status ); /* Display the sub-array. */ if( concat->ndat > 1 ) { msgOutiff( MSG__VERB, "", " Doing sub-array %s.", status, subarray ); } /* Create an empty provenance structure. Each input NDF that contributes to the current chunk and array will be added as an ancestor to this structure, which will later be stored in each output NDF created for this chunk and array. */ oprov = ndgReadProv( NDF__NOID, "SMURF:CALCQU", status ); /* Indicate we do not yet have any FITS headers for the output NDFs */ fc = NULL; /* Indicate we do not yet know the coordinate reference frame for the half-waveplate angle. */ polcrd[ 0 ] = 0; ipolcrd = 0; /* Go through the smfGroup looking for groups of related input NDFs that contribute to the current chunk. */ for( igroup = 0; igroup < sgroup->ngroups; igroup++ ) { if( sgroup->chunk[ igroup ] == ichunk ) { /* Get the integer index into the GRP group (sgrp) that holds the input NDFs. This index identifies the input NDF that provides the data for the current chunk and subarray. This assumes that the order in which smf_concat_smfGroup stores arrays in the "concat" smfArray matches the order in which smf_grp_related stores arrays within the sgroup->subgroups. */ inidx = sgroup->subgroups[ igroup ][ idx ]; /* Add this input NDF as an ancestor into the output provenance structure. */ smf_accumulate_prov( NULL, sgrp, inidx, NDF__NOID, "SMURF:CALCQU", &oprov, status ); /* Merge the FITS headers from the current input NDF into the FitsChan that holds headers for the output NDFs. The merging retains only those headers which have the same value in all input NDFs. */ smf_fits_outhdr( data->hdr->fitshdr, &fc, status ); /* Get the polarimetry related FITS headers and check that all input NDFs have usabie values. */ headval[ 0 ] = 0; smf_getfitss( data->hdr, "POL_MODE", headval, sizeof(headval), status ); if( strcmp( headval, "CONSTANT" ) && *status == SAI__OK ) { *status = SAI__ERROR; grpMsg( "N", sgrp, inidx ); errRep( " ", "Input NDF ^N does not contain " "polarimetry data obtained with a spinning " "half-waveplate.", status ); } headval[ 0 ] = 0; smf_getfitss( data->hdr, "POLWAVIN", headval, sizeof(headval), status ); if( strcmp( headval, "Y" ) && *status == SAI__OK ) { *status = SAI__ERROR; grpMsg( "N", sgrp, inidx ); errRep( " ", "Half-waveplate was not in the beam for " "input NDF ^N.", status ); } headval[ 0 ] = 0; smf_getfitss( data->hdr, "POLANLIN", headval, sizeof(headval), status ); if( strcmp( headval, "Y" ) && *status == SAI__OK ) { *status = SAI__ERROR; grpMsg( "N", sgrp, inidx ); errRep( " ", "Analyser was not in the beam for input " "NDF ^N.", status ); } if( polcrd[ 0 ] ) { headval[ 0 ] = 0; smf_getfitss( data->hdr, "POL_CRD", headval, sizeof(headval), status ); if( strcmp( headval, polcrd ) && *status == SAI__OK ) { *status = SAI__ERROR; errRep( " ", "Input NDFs have differing values for " "FITS header 'POL_CRD'.", status ); } } else { smf_getfitss( data->hdr, "POL_CRD", polcrd, sizeof(polcrd), status ); if( !strcmp( polcrd, "FPLANE" ) ) { ipolcrd = 0; } else if( !strcmp( polcrd, "AZEL" ) ) { ipolcrd = 1; } else if( !strcmp( polcrd, "TRACKING" ) ) { ipolcrd = 2; } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSetc( "N", data->file->name ); msgSetc( "V", polcrd ); errRep( " ", "Input NDF ^N contains unknown value " "'^V' for FITS header 'POL_CRD'.", status ); } } } } /* If not already done, get the maximum spatial drift (in arc-seconds) that can be tolerated whilst creating a single I/Q/U image. The default value is half the makemap default pixel size. Also get limits on the number of time slices in any block. */ if( arcerror == 0.0 ) { parDef0d( "ARCERROR", 0.5*smf_calc_telres( data->hdr->fitshdr, status ), status ); parGet0r( "ARCERROR", &arcerror, status ); parGet0i( "MAXSIZE", &maxsize, status ); parGet0i( "MINSIZE", &minsize, status ); if( maxsize > 0 && maxsize < minsize && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "Value of parameter MAXSIZE (%d) is less " "than value of parameter MINSIZE (%d)", status, maxsize, minsize ); } } /* The algorithm that calculates I, Q and U assumes that all samples for a single bolometer measure flux from the same point on the sky. Due to sky rotation, this will not be the case - each bolometer will drift slowly across the sky. However, since the drift is (or should be) slow we can apply the I/Q/U algorithm to blocks of contiguous data over which the bolometers do not move significantly. We produce a separate I, Q and U image for each such block. The first block starts at the first time slice in the smfData. */ block_start = 0; /* Find the time slice at which the corner bolometers have moved a critical distance (given by parameter ARCERROR) from their positions at the start of the block. Then back off some time slices to ensure that the block holds an integral number of half-waveplate rotations. */ block_end = smf_block_end( data, block_start, ipolcrd, arcerror, maxsize, status ); /* Loop round creating I/Q/U images for each block. Count them. */ iblock = 0; while( block_end >= 0 && *status == SAI__OK ) { /* Skip very short blocks. */ if( block_end - block_start > minsize ) { /* Display the start and end of the block. */ msgOutiff( MSG__VERB, "", " Doing time slice block %d " "-> %d", status, (int) block_start, (int) block_end ); /* Get the name for the Q NDF for this block. Start of with "Q" followed by the block index. */ iblock++; nc = sprintf( ndfname, "Q%d", iblock ); /* Append the subarray name to the NDF name. */ nc += sprintf( ndfname + nc, "_%s", subarray ); /* Append the chunk index to the NDF name. */ nc += sprintf( ndfname + nc, "_%d", (int) ichunk ); /* Get NDF placeholder for the Q NDF. The NDFs are created inside the output container file. */ ndfPlace( locq, ndfname, &qplace, status ); /* The name of the U NDF is the same except the initial "Q" is changed to "U". */ ndfname[ 0 ] = 'U'; ndfPlace( locu, ndfname, &uplace, status ); /* The name of the I NDF is the same except the initial "Q" is changed to "I". */ if( loci ) { ndfname[ 0 ] = 'I'; ndfPlace( loci, ndfname, &iplace, status ); } else { iplace = NDF__NOPL; } /* Store the chunk and block numbers as FITS headers. */ atlPtfti( fc, "POLCHUNK", (int) ichunk, "Chunk index used by CALCQU", status ); atlPtfti( fc, "POLBLOCK", iblock, "Block index used by CALCQU", status ); /* Create the Q and U images for the current block of time slices from the subarray given by "idx", storing them in the output container file. */ smf_calc_iqu( wf, data, block_start, block_end, ipolcrd, qplace, uplace, iplace, oprov, fc, pasign, AST__DD2R*paoff, AST__DD2R*angrot, submean, status ); /* Warn about short blocks. */ } else { msgOutiff( MSG__VERB, "", " Skipping short block of %d " "time slices (parameter MINSIZE=%d).", status, block_end - block_start - 1, minsize ); } /* The next block starts at the first time slice following the previous block. */ block_start = block_end + 1; /* Find the time slice at which the corner bolometers have moved a critical distance (given by parameter ARCERROR) from their positions at the start of the block. Then back off some time slices to ensure that the block holds an integral number of half-waveplate rotations. This returns -1 if all time slices have been used. */ block_end = smf_block_end( data, block_start, ipolcrd, arcerror, maxsize, status ); } /* Free resources */ oprov = ndgFreeProv( oprov, status ); fc = astAnnul( fc ); } config = astAnnul( config ); /* Close the smfArray. */ smf_close_related( &concat, status ); } /* Annul the locators for the output container files. */ datAnnul( &locq, status ); datAnnul( &locu, status ); if( loci ) datAnnul( &loci, status ); /* The parameter system hangs onto a primary locator for each container file, so cancel the parameters to annul these locators. */ datCancl( "OUTQ", status ); datCancl( "OUTU", status ); datCancl( "OUTI", status ); } /* Free resources. */ smf_close_related( &darks, status ); smf_close_related( &flatramps, status ); if( igrp ) grpDelet( &igrp, status); if( sgrp ) grpDelet( &sgrp, status); if( bgrp ) grpDelet( &bgrp, status ); if( ogrp ) grpDelet( &ogrp, status ); if( sgroup ) smf_close_smfGroup( &sgroup, status ); if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status ); /* End the NDF and AST contexts. */ ndfEnd( status ); astEnd; /* Issue a status indication.*/ if( *status == SAI__OK ) { msgOutif( MSG__VERB, " ", "CALCQU succeeded.", status); } else { msgOutif( MSG__VERB, " ", "CALCQU failed.", status); } }
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; }
void smf_obsmap_fill( const smfData * data, AstKeyMap * obsmap, AstKeyMap * objmap, int * status ) { double dateobs = 0.0; /* MJD UTC of start of observation */ char object[SZFITSTR]; /* Object name */ char obsid[SZFITSTR]; /* Observation ID */ char instrume[SZFITSTR]; /* instrument name */ AstKeyMap * obsinfo = NULL; /* observation information */ if (*status != SAI__OK) return; /* if there is no hdr we can not index it in the reported summary */ if (data->hdr && data->hdr->fitshdr) { /* Get observation ID and see if we already have an entry in the map */ (void)smf_getobsidss( data->hdr->fitshdr, obsid, sizeof(obsid), NULL, 0, status ); /* As of 20080718 OBSID is not unique per obs so chop off date*/ obsid[22] = '\0'; if (!astMapHasKey(obsmap, obsid )) { int itemp; /* Create a sub keymap and populate it */ obsinfo = astKeyMap( " " ); /* fill with default value in case undefined */ one_strlcpy( object, "<undefined>", sizeof(object), status ); smf_getfitss( data->hdr, "OBJECT", object, sizeof(object), status ); astMapPut0C( obsinfo, "OBJECT", object, NULL ); astMapPut0I( obsinfo, "OBSMODE", data->hdr->obsmode, NULL ); astMapPut0I( obsinfo, "OBSTYPE", data->hdr->obstype, NULL ); astMapPut0I( obsinfo, "SWMODE", data->hdr->swmode, NULL ); astMapPut0I( obsinfo, "INBEAM", data->hdr->inbeam, NULL ); smf_fits_getI( data->hdr, "OBSNUM", &itemp, status ); astMapPut0I( obsinfo, "OBSNUM", itemp, NULL ); smf_fits_getI( data->hdr, "UTDATE", &itemp, status ); astMapPut0I( obsinfo, "UTDATE", itemp, NULL ); smf_fits_getL( data->hdr, "SIMULATE", &itemp, status ); astMapPut0I( obsinfo, "SIMULATE", itemp, NULL ); smf_getfitss( data->hdr, "INSTRUME", instrume, sizeof(instrume), status); astMapPut0C( obsinfo, "INSTRUME", instrume, NULL ); /* store the MJD of observation for sorting purposes */ smf_find_dateobs( data->hdr, &dateobs, NULL, status ); astMapPut0D( obsinfo, "MJD-OBS", dateobs, NULL ); /* store information in the global observation map and also track how many distinct objects we have */ astMapPut0A( obsmap, obsid, obsinfo, NULL ); astMapPut0I( objmap, object, 0, NULL ); obsinfo = astAnnul( obsinfo ); } else { int curbeam = 0; astMapGet0A( obsmap, obsid, &obsinfo ); /* INBEAM is interesting since technically each sequence can involve different hardware being in the beam so we have to retrieve the current value from the keymap and OR it with the current value from the data header. */ /* we know that the value has been filled in previously so no need to check */ astMapGet0I( obsinfo, "INBEAM", &curbeam ); curbeam |= data->hdr->inbeam; astMapPut0I( obsinfo, "INBEAM", curbeam, NULL ); obsinfo = astAnnul( obsinfo ); } } return; }
void smf_find_science(const Grp * ingrp, Grp **outgrp, int reverttodark, Grp **darkgrp, Grp **flatgrp, int reducedark, int calcflat, smf_dtype darktype, smfArray ** darks, smfArray **fflats, AstKeyMap ** heateffmap, double * meanstep, int * status ) { smfSortInfo *alldarks; /* array of sort structs for darks */ smfSortInfo *allfflats; /* array of fast flat info */ Grp * dgrp = NULL; /* Internal dark group */ double duration_darks = 0.0; /* total duration of all darks */ double duration_sci = 0.0; /* Duration of all science observations */ size_t dkcount = 0; /* Dark counter */ size_t ffcount = 0; /* Fast flat counter */ Grp * fgrp = NULL; /* Fast flat group */ size_t i; /* loop counter */ smfData *infile = NULL; /* input file */ size_t insize; /* number of input files */ size_t nsteps_dark = 0; /* Total number of steps for darks */ size_t nsteps_sci = 0; /* Total number of steps for science */ AstKeyMap * heatermap = NULL; /* Heater efficiency map */ AstKeyMap * obsmap = NULL; /* Info from all observations */ AstKeyMap * objmap = NULL; /* All the object names used */ AstKeyMap * scimap = NULL; /* All non-flat obs indexed by unique key */ Grp *ogrp = NULL; /* local copy of output group */ size_t sccount = 0; /* Number of accepted science files */ struct timeval tv1; /* Timer */ struct timeval tv2; /* Timer */ if (meanstep) *meanstep = VAL__BADD; if (outgrp) *outgrp = NULL; if (darkgrp) *darkgrp = NULL; if (darks) *darks = NULL; if (fflats) *fflats = NULL; if (heateffmap) *heateffmap = NULL; if (*status != SAI__OK) return; /* Sanity check to make sure we return some information */ if ( outgrp == NULL && darkgrp == NULL && darks == NULL && fflats == NULL) { *status = SAI__ERROR; errRep( " ", FUNC_NAME ": Must have some non-NULL arguments" " (possible programming error)", status); return; } /* Start a timer to see how long this takes */ smf_timerinit( &tv1, &tv2, status ); /* Create new group for output files */ ogrp = smf_grp_new( ingrp, "Science", status ); /* and a new group for darks */ dgrp = smf_grp_new( ingrp, "DarkFiles", status ); /* and for fast flats */ fgrp = smf_grp_new( ingrp, "FastFlats", status ); /* and also create a keymap for the observation description */ obsmap = astKeyMap( "KeyError=1" ); /* and an object map */ objmap = astKeyMap( "KeyError=1" ); /* This keymap contains the sequence counters for each related subarray/obsidss/heater/shutter combination and is used to decide if a bad flat is relevant */ scimap = astKeyMap( "KeyError=1,KeyCase=0" ); /* This keymap is used to contain relevant heater efficiency data */ heatermap = astKeyMap( "KeyError=1,KeyCase=0" ); /* Work out how many input files we have and allocate sufficient sorting space */ insize = grpGrpsz( ingrp, status ); alldarks = astCalloc( insize, sizeof(*alldarks) ); allfflats = astCalloc( insize, sizeof(*allfflats) ); /* check each file in turn */ for (i = 1; i <= insize; i++) { int seqcount = 0; char keystr[100]; /* Key for scimap entry */ /* open the file but just to get the header */ smf_open_file( ingrp, i, "READ", SMF__NOCREATE_DATA, &infile, status ); if (*status != SAI__OK) break; /* Fill in the keymap with observation details */ smf_obsmap_fill( infile, obsmap, objmap, status ); /* Find the heater efficiency map if required */ if (*status == SAI__OK && heateffmap) { char arrayidstr[32]; smf_fits_getS( infile->hdr, "ARRAYID", arrayidstr, sizeof(arrayidstr), status ); if (!astMapHasKey( heatermap, arrayidstr ) ) { smfData * heateff = NULL; dim_t nbolos = 0; smf_flat_params( infile, "RESIST", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &heateff, status ); smf_get_dims( heateff, NULL, NULL, &nbolos, NULL, NULL, NULL, NULL, status ); if (heateff) astMapPut0P( heatermap, arrayidstr, heateff, NULL ); } } /* Get the sequence counter for the file. We do not worry about duplicate sequence counters (at the moment) */ smf_find_seqcount( infile->hdr, &seqcount, status ); /* The key identifying this subarray/obsidss/heater/shutter combo */ smf__calc_flatobskey( infile->hdr, keystr, sizeof(keystr), status ); if (smf_isdark( infile, status )) { /* Store the sorting information */ dkcount = smf__addto_sortinfo( infile, alldarks, i, dkcount, "Dark", status ); smf__addto_durations( infile, &duration_darks, &nsteps_dark, status ); astMapPutElemI( scimap, keystr, -1, seqcount ); } else { /* compare sequence type with observation type and drop it (for now) if they differ */ if ( infile->hdr->obstype == infile->hdr->seqtype ) { /* Sanity check the header for corruption. Compare RTS_NUM with SEQSTART and SEQEND. The first RTS_NUM must either be SEQSTART or else between SEQSTART and SEQEND (if someone has giving us a section) */ int seqstart = 0; int seqend = 0; int firstnum = 0; JCMTState *tmpState = NULL; smf_getfitsi( infile->hdr, "SEQSTART", &seqstart, status ); smf_getfitsi( infile->hdr, "SEQEND", &seqend, status ); tmpState = infile->hdr->allState; if( tmpState ) { firstnum = (tmpState[0]).rts_num; smf_smfFile_msg( infile->file, "F", 1, "<unknown file>"); if ( firstnum >= seqstart && firstnum <= seqend ) { /* store the file in the output group */ ndgCpsup( ingrp, i, ogrp, status ); msgOutif(MSG__DEBUG, " ", "Non-dark file: ^F",status); smf__addto_durations( infile, &duration_sci, &nsteps_sci, status ); astMapPutElemI( scimap, keystr, -1, seqcount ); sccount++; } else { msgOutif( MSG__QUIET, "", "File ^F has a corrupt FITS header. Ignoring it.", status ); } } else { smf_smfFile_msg( infile->file, "F", 1, "<unknown file>"); /* store the file in the output group */ ndgCpsup( ingrp, i, ogrp, status ); msgOutif( MSG__DEBUG, " ", "File ^F lacks JCMTState: assuming it is non-dark",status); smf__addto_durations( infile, &duration_sci, &nsteps_sci, status ); astMapPutElemI( scimap, keystr, -1, seqcount ); sccount++; } } else if (infile->hdr->seqtype == SMF__TYP_FASTFLAT ) { ffcount = smf__addto_sortinfo( infile, allfflats, i, ffcount, "Fast flat", status ); } else { smf_smfFile_msg( infile->file, "F", 1, "<unknown file>"); msgOutif(MSG__DEBUG, " ", "Sequence type mismatch with observation type: ^F",status); } } /* close the file */ smf_close_file( &infile, status ); } /* Store output group in return variable or else free it */ if (outgrp) { *outgrp = ogrp; } else { grpDelet( &ogrp, status ); } /* process flatfields if necessary */ if (ffcount > 0 && fflats ) { smfArray * array = NULL; /* sort flats into order */ qsort( allfflats, ffcount, sizeof(*allfflats), smf_sort_bydouble); if (fflats) array = smf_create_smfArray( status ); /* now open the flats and store them if requested */ if (*status == SAI__OK && array && ffcount) { size_t start_ffcount = ffcount; AstKeyMap * flatmap = NULL; if (calcflat) { /* Use AgeUp so that we get the keys out in the sorted order that allfflats used */ flatmap = astKeyMap( "KeyCase=0,KeyError=1,SortBy=AgeDown" ); } /* Read each flatfield. Calculate a responsivity image and a flatfield solution. Store these in a keymap along with related information which is itself stored in a keymap indexed by a string made of OBSIDSS, reference heater value, shutter and subarray. */ for (i = 0; i < start_ffcount; i++ ) { size_t ori_index = (allfflats[i]).index; smfData * outfile = NULL; char keystr[100]; AstKeyMap * infomap = astKeyMap( "KeyError=1" ); int oplen = 0; char thisfile[MSG__SZMSG]; int seqcount = 0; /* read filename from group */ infile = NULL; smf_open_file( ingrp, ori_index, "READ", 0, &infile, status ); if ( *status != SAI__OK ) { /* This should not happen because we have already opened the file. If it does happen we abort with error. */ if (infile) smf_close_file( &infile, status ); break; } /* Calculate the key for this observation */ smf__calc_flatobskey( infile->hdr, keystr, sizeof(keystr), status ); /* Get the file name for error messages */ smf_smfFile_msg( infile->file, "F", 1, "<unknown file>" ); msgLoad( "", "^F", thisfile, sizeof(thisfile), &oplen, status ); /* And the sequence counter to link against science observations */ smf_find_seqcount( infile->hdr, &seqcount, status ); /* Prefill infomap */ astMapPut0C( infomap, "FILENAME", thisfile, ""); astMapPut0I( infomap, "SEQCOUNT", seqcount, ""); /* Collapse it */ if (*status == SAI__OK) { smf_flat_fastflat( infile, &outfile, status ); if (*status == SMF__BADFLAT) { errFlush( status ); if (calcflat) { /* Need to generate an outfile like smf_flat_fastflat and one heater setting will force smf_flat_calcflat to fail */ smf_flat_malloc( 1, infile, NULL, &outfile, status ); } else { if (outfile) smf_close_file( &outfile, status ); if (infile) smf_close_file( &infile, status ); infomap = astAnnul( infomap ); ffcount--; continue; } } } if (outfile && *status == SAI__OK) { smf_close_file( &infile, status ); infile = outfile; if (calcflat) { size_t ngood = 0; smfData * curresp = NULL; int utdate; if (*status == SAI__OK) { ngood = smf_flat_calcflat( MSG__VERB, NULL, "RESIST", "FLATMETH", "FLATORDER", NULL, "RESPMASK", "FLATSNR", NULL, infile, &curresp, status ); if (*status != SAI__OK) { /* if we failed to calculate a flatfield we continue but force the flatfield to be completely bad. This will force the science data associated with the flatfield to be correctly blanked. We do not annul though if we have a SUBPAR error telling us that we have failed to define our parameters properly. */ if (*status != SUBPAR__NOPAR) errAnnul(status); /* parameters of flatfield */ ngood = 0; /* Generate a blank flatfield and blank responsivity image */ smf_flat_badflat( infile, &curresp, status ); } /* Retrieve the UT date so we can decide whether to compare flatfields */ smf_getfitsi( infile->hdr, "UTDATE", &utdate, status ); /* Store the responsivity data for later on and the processed flatfield until we have vetted it */ astMapPut0P( infomap, "CALCFLAT", infile, "" ); astMapPut0P( infomap, "RESP", curresp, "" ); astMapPut0I( infomap, "UTDATE", utdate, "" ); astMapPut0I( infomap, "ISGOOD", 1, "" ); astMapPut0I( infomap, "NGOOD", ngood, "" ); astMapPut0I( infomap, "GRPINDEX", ori_index, "" ); astMapPut0I( infomap, "SMFTYP", infile->hdr->obstype, "" ); astMapPutElemA( flatmap, keystr, -1, infomap ); } } else { /* if (calcflat) */ /* Store the collapsed flatfield - the processed flat is not stored here yet */ smf_addto_smfArray( array, infile, status ); /* Copy the group info */ ndgCpsup( ingrp, ori_index, fgrp, status ); } } /* if (outfile) */ /* Annul the keymap (will be fine if it is has been stored in another keymap) */ infomap = astAnnul( infomap ); } /* End loop over flatfields */ /* Now we have to loop over the related flatfields to disable bolometers that are not good and also decide whether we need to set status to bad. */ if (*status == SAI__OK && calcflat ) { size_t nkeys = astMapSize( flatmap ); for (i = 0; i < nkeys; i++ ) { const char *key = astMapKey( flatmap, i ); int nf = 0; AstKeyMap ** kmaps = NULL; int nelem = astMapLength( flatmap, key ); kmaps = astMalloc( sizeof(*kmaps) * nelem ); astMapGet1A( flatmap, key, nelem, &nelem, kmaps ); for ( nf = 0; nf < nelem && *status == SAI__OK; nf++ ) { AstKeyMap * infomap = kmaps[nf]; int isgood = 0; astMapGet0I( infomap, "ISGOOD", &isgood ); if (isgood) { /* The flatfield worked */ size_t ngood = 0; int itemp; int utdate = 0; int ratioFlats = 0; /* Get the UT date - we do not compare flatfields after the time we enabled heater tracking at each sequence. */ astMapGet0I( infomap, "UTDATE", &utdate ); /* Get the number of good bolometers at this point */ astMapGet0I( infomap, "NGOOD", &itemp ); ngood = itemp; /* Decide if we want to do the ratio test. We default to not doing it between 20110901 and 20120827 which is the period when we did mini-heater tracks before each flat. ! indicates that we choose based on date. */ if (*status == SAI__OK) { parGet0l( "FLATUSENEXT", &ratioFlats, status ); if ( *status == PAR__NULL ) { errAnnul( status ); if (utdate >= 20110901 || utdate <= 20120827 ) { ratioFlats = 0; } else { ratioFlats = 1; } } } /* Can we compare with the next flatfield? */ if (ngood < SMF__MINSTATSAMP || !ratioFlats ) { /* no point doing all the ratio checking for this */ } else if ( nelem - nf >= 2 ) { AstKeyMap * nextmap = kmaps[nf+1]; const char *nextfname = NULL; const char *fname = NULL; smfData * curresp = NULL; smfData * nextresp = NULL; smfData * curflat = NULL; void *tmpvar = NULL; size_t bol = 0; smfData * ratio = NULL; double *in1 = NULL; double *in2 = NULL; double mean = VAL__BADD; size_t nbolo; double *out = NULL; double sigma = VAL__BADD; float clips[] = { 5.0, 5.0 }; /* 5.0 sigma iterative clip */ size_t ngoodz = 0; astMapGet0C( nextmap, "FILENAME", &nextfname ); astMapGet0C( infomap, "FILENAME", &fname ); /* Retrieve the responsivity images from the keymap */ astMapGet0P( infomap, "RESP", &tmpvar ); curresp = tmpvar; astMapGet0P( nextmap, "RESP", &tmpvar ); nextresp = tmpvar; astMapGet0P( infomap, "CALCFLAT", &tmpvar ); curflat = tmpvar; nbolo = (curresp->dims)[0] * (curresp->dims)[1]; /* get some memory for the ratio if we have not already. We could get some memory once assuming each flat has the same number of bolometers... */ ratio = smf_deepcopy_smfData( curresp, 0, 0, 0, 0, status ); if( *status == SAI__OK ) { /* divide: smf_divide_smfData ? */ in1 = (curresp->pntr)[0]; in2 = (nextresp->pntr)[0]; out = (ratio->pntr)[0]; for (bol=0; bol<nbolo;bol++) { if ( in1[bol] != VAL__BADD && in1[bol] != 0.0 && in2[bol] != VAL__BADD && in2[bol] != 0.0 ) { out[bol] = in1[bol] / in2[bol]; } else { out[bol] = VAL__BADD; } } } /* find some statistics */ smf_clipped_stats1D( out, 2, clips, 1, nbolo, NULL, 0, 0, &mean, &sigma, NULL, 0, &ngoodz, status ); if (*status == SMF__INSMP) { errAnnul(status); msgOutiff( MSG__QUIET, "", "Flatfield ramp ratio of %s with %s had too few bolometers (%zu < %d).", status, fname, nextfname, ngoodz, SMF__MINSTATSAMP ); ngood = ngoodz; /* Must be lower or equal to original ngood */ } else if (*status == SAI__OK && mean != VAL__BADD && sigma != VAL__BADD && curflat->da) { /* Now flag the flatfield as bad for bolometers that have changed more than n%. We expect the variation to be 1+/-a small bit */ const double pmrange = 0.10; double thrlo = 1.0 - pmrange; double thrhi = 1.0 + pmrange; size_t nmasked = 0; double *flatcal = curflat->da->flatcal; msgOutiff( MSG__DEBUG, "", "Flatfield fast ramp ratio mean = %g +/- %g (%zu bolometers)", status, mean, sigma, ngood); /* we can just set the first slice of the flatcal to bad. That should be enough to disable the entire bolometer. We have just read these data so they should be in ICD order. */ for (bol=0; bol<nbolo;bol++) { if ( out[bol] != VAL__BADD && (out[bol] < thrlo || out[bol] > thrhi ) ) { flatcal[bol] = VAL__BADD; nmasked++; } else if ( in1[bol] != VAL__BADD && in2[bol] == VAL__BADD ) { /* A bolometer is bad next time but good now so we must set it bad now */ flatcal[bol] = VAL__BADD; nmasked++; } } if ( nmasked > 0 ) { msgOutiff( MSG__NORM, "", "Masked %zu bolometers in %s from unstable flatfield", status, nmasked, fname ); /* update ngood to take into account the masking */ ngood -= nmasked; } } smf_close_file( &ratio, status ); } /* End of flatfield responsivity comparison */ /* if we only have a few bolometers left we now consider this a bad flat unless it is actually an engineering measurement where expect some configurations to give zero bolometers */ if (ngood < SMF__MINSTATSAMP) { const char *fname = NULL; void * tmpvar = NULL; int smftyp = 0; smfData * curflat = NULL; astMapGet0I( infomap, "SMFTYP", &smftyp ); astMapGet0C( infomap, "FILENAME", &fname ); if (smftyp != SMF__TYP_NEP) { msgOutiff( MSG__QUIET, "", "Flatfield %s has %zu good bolometer%s.%s", status, fname, ngood, (ngood == 1 ? "" : "s"), ( ngood == 0 ? "" : " Keeping none.") ); isgood = 0; /* Make sure that everything is blanked. */ if (ngood > 0) { astMapGet0P( infomap, "CALCFLAT", &tmpvar ); curflat = tmpvar; if (curflat && curflat->da) { size_t bol; size_t nbolo = (curflat->dims)[0] * (curflat->dims)[1]; double *flatcal = curflat->da->flatcal; for (bol=0; bol<nbolo; bol++) { /* Just need to set the first element to bad */ flatcal[bol] = VAL__BADD; } } } } else { msgOutiff( MSG__NORM, "", "Flatfield ramp file %s has %zu good bolometer%s. Eng mode.", status, fname, ngood, (ngood == 1 ? "" : "s") ); } } /* We do not need the responsivity image again */ { void *tmpvar = NULL; smfData * resp = NULL; astMapGet0P( infomap, "RESP", &tmpvar ); resp = tmpvar; if (resp) smf_close_file( &resp, status ); astMapRemove( infomap, "RESP" ); } } /* End of isgood comparison */ /* We are storing flats even if they failed. Let the downstream software worry about it */ { int ori_index; smfData * flatfile = NULL; void *tmpvar = NULL; /* Store in the output group */ astMapGet0I( infomap, "GRPINDEX", &ori_index ); ndgCpsup( ingrp, ori_index, fgrp, status ); /* And store in the smfArray */ astMapGet0P( infomap, "CALCFLAT", &tmpvar ); astMapRemove( infomap, "CALCFLAT" ); flatfile = tmpvar; smf_addto_smfArray( array, flatfile, status ); } /* Free the object as we go */ kmaps[nf] = astAnnul( kmaps[nf] ); } /* End of loop over this obsidss/subarray/heater */ kmaps = astFree( kmaps ); } } if (array->ndat) { if (fflats) *fflats = array; } else { smf_close_related(&array, status ); if (fflats) *fflats = NULL; } } } /* no need to do any more if neither darks nor darkgrp are defined or we might be wanting to revert to darks. */ if (dkcount > 0 && (darks || darkgrp || reverttodark ) ) { smfArray * array = NULL; /* sort darks into order */ qsort( alldarks, dkcount, sizeof(*alldarks), smf_sort_bydouble); if (darks) array = smf_create_smfArray( status ); /* now open the darks and store them if requested */ if (*status == SAI__OK) { for (i = 0; i < dkcount; i++ ) { size_t ori_index = (alldarks[i]).index; /* Store the entry in the output group */ ndgCpsup( ingrp, ori_index, dgrp, status ); if (darks) { /* read the value from the new group */ smf_open_file( dgrp, i+1, "READ", 0, &infile, status ); /* do we have to process these darks? */ if (reducedark) { smfData *outfile = NULL; smf_reduce_dark( infile, darktype, &outfile, status ); if (outfile) { smf_close_file( &infile, status ); infile = outfile; } } smf_addto_smfArray( array, infile, status ); } } if (darks) *darks = array; } } /* free memory */ alldarks = astFree( alldarks ); allfflats = astFree( allfflats ); if( reverttodark && outgrp && (grpGrpsz(*outgrp,status)==0) && (grpGrpsz(dgrp,status)>0) ) { /* If outgrp requested but no science observations were found, and dark observations were found, return darks in outgrp and set flatgrp and darkgrp to NULL. This is to handle cases where we want to process data taken in the dark like normal science data. To activate this behaviour set reverttodark */ msgOutiff( MSG__NORM, "", "Treating the dark%s as science data", status, ( dkcount > 1 ? "s" : "" ) ); *outgrp = dgrp; if( darkgrp ){ *darkgrp = NULL; } if( flatgrp ) { *flatgrp = NULL; } grpDelet( &ogrp, status); grpDelet( &fgrp, status); if (meanstep && nsteps_dark > 0) *meanstep = duration_darks / nsteps_dark; /* Have to clear the darks smfArray as well */ if (darks) smf_close_related( darks, status ); } else { /* Store the output groups in the return variable or free it */ if (darkgrp) { *darkgrp = dgrp; } else { grpDelet( &dgrp, status); } if (flatgrp) { *flatgrp = fgrp; } else { grpDelet( &fgrp, status); } if (meanstep && nsteps_sci > 0) *meanstep = duration_sci / nsteps_sci; } msgSeti( "ND", sccount ); msgSeti( "DK", dkcount ); msgSeti( "FF", ffcount ); msgSeti( "TOT", insize ); if ( insize == 1 ) { if (dkcount == 1) { msgOutif( MSG__VERB, " ", "Single input file was a dark", status); } else if (ffcount == 1) { msgOutif( MSG__VERB, " ", "Single input file was a fast flatfield", status); } else if (sccount == 1) { msgOutif( MSG__VERB, " ", "Single input file was accepted (observation type same as sequence type)", status); } else { msgOutif( MSG__VERB, " ", "Single input file was not accepted.", status); } } else { if (dkcount == 1) { msgSetc( "DKTXT", "was a dark"); } else { msgSetc( "DKTXT", "were darks"); } if (ffcount == 1) { msgSetc( "FFTXT", "was a fast flat"); } else { msgSetc( "FFTXT", "were fast flats"); } if (sccount == 1) { msgSetc( "NDTXT", "was science"); } else { msgSetc( "NDTXT", "were science"); } /* This might be a useful message */ msgOutif( MSG__NORM, " ", "Out of ^TOT input files, ^DK ^DKTXT, ^FF ^FFTXT " "and ^ND ^NDTXT", status ); } if (meanstep && *meanstep != VAL__BADD) { msgOutiff( MSG__VERB, "", "Mean step time for input files = %g sec", status, *meanstep ); } /* Store the heater efficiency map */ if (*status != SAI__OK) heatermap = smf_free_effmap( heatermap, status ); if (heateffmap) *heateffmap = heatermap; /* Now report the details of the observation */ smf_obsmap_report( MSG__NORM, obsmap, objmap, status ); obsmap = astAnnul( obsmap ); objmap = astAnnul( objmap ); scimap = astAnnul( scimap ); msgOutiff( SMF__TIMER_MSG, "", "Took %.3f s to find science observations", status, smf_timerupdate( &tv1, &tv2, status ) ); return; }
void smf_flat_params( const smfData * refdata, const char resistpar[], const char methpar[], const char orderpar[], const char snrminpar[], double * refohms, double **resistance, int * outrows, int * outcols, smf_flatmeth *flatmeth, int * order, double * snrmin, smfData ** heateff, int * status ) { dim_t datarows = 0; /* Number of rows in refdata */ dim_t datacols = 0; /* Number of columns in refdata */ size_t j = 0; /* Counter, index */ char method[SC2STORE_FLATLEN]; /* flatfield method string */ size_t nbols; /* Number of bolometers */ double refohmsval = 0.0; /* Internal version of refohms */ AstKeyMap * resmap = NULL; /* Resistor map */ AstKeyMap * subarrays = NULL; /* Subarray lookup table */ char thissub[32]; /* This sub-instrument string */ if (resistance) *resistance = NULL; if (*status != SAI__OK) return; if (!refdata) { *status = SAI__ERROR; errRep( "", "Must provide reference data file to calculate flatfield parameters" " (possible programming error)", status ); return; } /* Based on refdata we now need to calculate the default reference resistance and retrieve the correct heater efficiency file for each array. We need the unique subarray string so that we can set up a look up keymap. There is no code in SMURF to return all the known subarrays but we need to know all the options in order to use kpg1Config. */ subarrays = astKeyMap( " " ); astMapPut0I( subarrays, "CG450MK2_M0907D0501", 0, NULL ); astMapPut0I( subarrays, "CG850MK2_M0904D0503", 0, NULL ); astMapPut0I( subarrays, "SG850_M0906D1005", 0, NULL ); astMapPut0I( subarrays, "SG850_M1002D1006", 0, NULL ); astMapPut0I( subarrays, "SG850_M1005D1007", 0, NULL ); astMapPut0I( subarrays, "SG850_M1003D1004", 0, NULL ); astMapPut0I( subarrays, "SG450_M1004D1000", 0, NULL ); astMapPut0I( subarrays, "SG450_M1007D1002", 0, NULL ); astMapPut0I( subarrays, "SG450_M1006D1003", 0, NULL ); astMapPut0I( subarrays, "SG450_M1009D1008", 0, NULL ); /* and indicate which subarray we are interested in (uppercased) */ smf_fits_getS( refdata->hdr, "ARRAYID", thissub, sizeof(thissub), status ); { /* need to uppercase */ size_t l = strlen(thissub); for (j=0;j<l;j++) { thissub[j] = toupper(thissub[j]); } } astMapPut0I( subarrays, thissub, 1, NULL ); /* Read the config file */ resmap = kpg1Config( resistpar, "$SMURF_DIR/smurf_calcflat.def", subarrays, 1, status ); subarrays = astAnnul( subarrays ); if (*status != SAI__OK) goto CLEANUP; /* Read the reference resistance */ astMapGet0D( resmap, "REFRES", &refohmsval ); if (refohms && *status == SAI__OK) { *refohms = refohmsval; msgOutiff(MSG__VERB, "", "Read reference resistance for subarray %s of %g ohms\n", status, thissub, *refohms ); } /* We no longer want to read per-bolometer resistor values from the config file. To retain backwards compatibility with the current implementation of smf_flat_standardpow we simply fill the per-bol resistance array with the reference resistance which effectively disables smf_flat_standardpow */ smf_get_dims( refdata, &datarows, &datacols, NULL, NULL, NULL, NULL, NULL, status ); nbols = datacols * datarows; if (*status == SAI__OK && resistance ) { *resistance = astMalloc( nbols*sizeof(**resistance) ); for (j = 0; j < (size_t)nbols; j++) { (*resistance)[j] = refohmsval; } } /* Get the heater efficiency file */ if (heateff && astMapHasKey( resmap, "HEATEFF" ) ) { const char * heateffstr = NULL; if (astMapGet0C( resmap, "HEATEFF", &heateffstr )) { Grp * heateffgrp = NULL; smfData * heatefftmp = NULL; heateffgrp = grpNew( "heateff", status ); grpPut1( heateffgrp, heateffstr, 0, status ); smf_open_file( NULL, heateffgrp, 1, "READ", SMF__NOTTSERIES|SMF__NOFIX_METADATA, &heatefftmp, status ); /* Divorce the smfData from the underlying file. This file stays open for the entire duration of the data processing and can some times lead to issues when we attempt to close it an hour after we opened it (it's usually on an NFS disk) */ if (*status == SAI__OK) { *heateff = smf_deepcopy_smfData( NULL, heatefftmp, 0, SMF__NOCREATE_FILE | SMF__NOCREATE_FTS | SMF__NOCREATE_DA, 0, 0, status ); smf_close_file(NULL, &heatefftmp, status); } /* Check the dimensions */ if (*status == SAI__OK) { dim_t heatrows = 0; dim_t heatcols = 0; smf_get_dims( *heateff, &heatrows, &heatcols, NULL, NULL, NULL, NULL, NULL, status ); if (*status == SAI__OK) { if ( datarows != heatrows || datacols != heatcols ) { *status = SAI__ERROR; errRepf( "", "Dimensions of heater efficiency file %s are (%zu, %zu)" " but flatfield has dimensions (%zu, %zu)", status, heateffstr, (size_t)heatrows, (size_t)heatcols, (size_t)datarows, (size_t)datacols); } } if (*status == SAI__OK) { smf_dtype_check_fatal( *heateff, NULL, SMF__DOUBLE, status ); if (*status == SMF__BDTYP) { errRepf("", "Heater efficiency data in %s should be double precision", status, heateffstr); } } if (*status == SAI__OK) { char heateffarrid[32]; smf_fits_getS( refdata->hdr, "ARRAYID", heateffarrid, sizeof(heateffarrid), status ); if (*status != SAI__OK) errAnnul( status ); if (strcasecmp( thissub, heateffarrid ) != 0 ) { if (*status == SAI__OK) { *status = SAI__ERROR; errRepf("", "Subarray associated with heater efficiency image (%s)" " does not match that of the data to be flatfielded (%s)", status, heateffarrid, thissub ); } } } } if (heateffgrp) grpDelet( &heateffgrp, status ); } } if (methpar && flatmeth) { /* See if we want to use TABLE or POLYNOMIAL mode */ parChoic( methpar, "POLYNOMIAL", "POLYNOMIAL, TABLE", 1, method, sizeof(method), status ); *flatmeth = smf_flat_methcode( method, status ); if (*flatmeth == SMF__FLATMETH_POLY) { /* need an order for the polynomial */ if (order && orderpar) { parGdr0i( orderpar, 1, 1, 3, 1, order, status ); /* and if the order is 1 then we can ask for the snr min */ if (snrminpar && *order == 1) { parGet0d( snrminpar, snrmin, status ); } } } else { /* need an snr min for table mode responsivities */ if (snrminpar) parGet0d( snrminpar, snrmin, status ); } } if (outrows) *outrows = datarows; if (outcols) *outcols = datacols; CLEANUP: resmap = astAnnul( resmap ); if (*status != SAI__OK) { if (resistance && *resistance) *resistance = astFree( *resistance ); if (heateff && *heateff) smf_close_file( NULL, heateff, status ); } return; }
void smf_write_flagmap( ThrWorkForce *wf, smf_qual_t mask, smfArray *lut, smfArray *qua, smfDIMMData *dat, const Grp *flagrootgrp, size_t contchunk, const int *lbnd_out, const int *ubnd_out, AstFrameSet *outfset, int *status ) { AstFrameSet *tfset; /* Temporary FrameSet pointer */ Grp *mgrp=NULL; /* Temporary group for map names */ char *pname=NULL; /* Poiner to name */ char name[GRP__SZNAM+1]; /* Buffer for storing names */ char tempstr[20]; /* Temporary string */ char tmpname[GRP__SZNAM+1]; /* temp name buffer */ dim_t nbolo; /* Number of bolometers */ dim_t ntslice; /* Number of time slices */ double shift[ 1 ]; /* Shift from GRID to bit number */ int *flagmap=NULL; /* pointer to flagmap data */ int *lut_data=NULL; /* Pointer to DATA component of lut */ int ibit; /* Quality bit number */ int lbnd3d[3]; /* Lower bounds for 3D output */ int npix; /* Number of pixels per plane */ int target; /* Target value for incrementing pixel count */ int ubnd3d[3]; /* Upper bounds for 3D output */ size_t bstride; /* Bolometer stride */ size_t i; /* loop counter */ size_t idx=0; /* index within subgroup */ size_t ii; /* array offset index */ size_t j; /* loop counter */ size_t tstride; /* Time stride */ smfData *mapdata=NULL; /* smfData for new map */ smf_qual_t *qua_data=NULL; /* Pointer to DATA component of qua */ if( *status != SAI__OK ) return; if( !lut || !qua || !dat || !flagrootgrp || !lbnd_out || !ubnd_out || !outfset ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": NULL inputs supplied", status ); return; } /* Create a name for the flagmap, taking into account the chunk number. Only required if we are using a single output container. */ pname = tmpname; grpGet( flagrootgrp, 1, 1, &pname, sizeof(tmpname), status ); one_strlcpy( name, tmpname, sizeof(name), status ); one_strlcat( name, ".", sizeof(name), status ); sprintf(tempstr, "CH%02zd", contchunk); one_strlcat( name, tempstr, sizeof(name), status ); mgrp = grpNew( "flagmap", status ); grpPut1( mgrp, name, 0, status ); msgOutf( "", "*** Writing flagmap %s", status, name ); /* If a non-zero mask value wassupplied, the flagmap is 2-dimensional and each pixel value counts the number of samples flagged by any of the qualities included in the mask. */ if( mask ) { smf_open_newfile( wf, mgrp, 1, SMF__INTEGER, 2, lbnd_out, ubnd_out, 0, &mapdata, status); flagmap = mapdata->pntr[0]; /* Loop over subgroup index (subarray) */ for( idx=0; (idx<qua->ndat)&&(*status==SAI__OK); idx++ ) { smf_get_dims( qua->sdata[idx], NULL, NULL, &nbolo, &ntslice, NULL, &bstride, &tstride, status ); qua_data = (qua->sdata[idx]->pntr)[0]; lut_data = (lut->sdata[idx]->pntr)[0]; /* Loop over bolometer and time slice and create map */ for( i=0; i<nbolo; i++ ) { /* Skip bolometers only if SMF__Q_BADB is set both in the data and the mask */ if( !(qua_data[i*bstride] & mask & SMF__Q_BADB) ) { for( j=0; j<ntslice; j++ ) { ii = i*bstride + j*tstride; if( (qua_data[ii] & mask) && (lut_data[ii] != VAL__BADI) ) { flagmap[lut_data[ii]]++; } } } } } /* Write WCS */ smf_set_moving( (AstFrame *) outfset, NULL, status ); ndfPtwcs( outfset, mapdata->file->ndfid, status ); /* If the mask is zero, the flagmap is 3-dimensional and contains a plane for each quality bit, plus an additional plane (plane 1) containing the number of unflagged samples in each pixel. */ } else { lbnd3d[ 0 ] = lbnd_out[ 0 ]; lbnd3d[ 1 ] = lbnd_out[ 1 ]; lbnd3d[ 2 ] = -1; ubnd3d[ 0 ] = ubnd_out[ 0 ]; ubnd3d[ 1 ] = ubnd_out[ 1 ]; ubnd3d[ 2 ] = SMF__NQBITS_TSERIES - 1; smf_open_newfile( wf, mgrp, 1, SMF__INTEGER, 3, lbnd3d, ubnd3d, 0, &mapdata, status); flagmap = mapdata->pntr[0]; /* No. of pixels in one plane */ npix = ( ubnd3d[ 1 ] - lbnd3d[ 1 ] + 1 )*( ubnd3d[ 0 ] - lbnd3d[ 0 ] + 1 ); /* Loop over each quality bit (-1 == "no flags"). */ for( ibit = -1; ibit < SMF__NQBITS_TSERIES; ibit++ ) { /* The test of each sample is performed by checking if the sample's quality value ANDed with "mask" is equal to "target". This is requires since ibit==-1 (i.e. "count all samples that have no flags set") requires a different logic to the other ibit values. */ if( ibit == -1 ) { mask = SMF__Q_GOOD; target = 0; } else { mask = BIT_TO_VAL(ibit); target = mask; } /* Loop over subgroup index (subarray) */ for( idx=0; (idx<qua->ndat)&&(*status==SAI__OK); idx++ ) { smf_get_dims( qua->sdata[idx], NULL, NULL, &nbolo, &ntslice, NULL, &bstride, &tstride, status ); qua_data = (qua->sdata[idx]->pntr)[0]; lut_data = (lut->sdata[idx]->pntr)[0]; /* Loop over bolometer and time slice and create map */ for( i=0; i<nbolo; i++ ) { /* Skip bolometers only if SMF__Q_BADB is set both in the data and the mask */ for( j=0; j<ntslice; j++ ) { ii = i*bstride + j*tstride; if( ( (qua_data[ii] & mask) == target ) && (lut_data[ii] != VAL__BADI) ) { flagmap[lut_data[ii]]++; } } } } /* Move the pointer on to th enext plane. */ flagmap += npix; /* Next quality bit. */ } /* Take a copy of the supplied FrameSet so we do not modify it. */ tfset = astCopy( outfset ); /* Set atributes for moving target if necessary. */ smf_set_moving( (AstFrame *) tfset, NULL, status ); /* Modify the WCS FrameSet so that the base and current Frames are 3-dimensional. The current Frame is expanded by adding in a simple 1D Frame representing quality bit, and the base Frame is expanded by adding in a 3rd GRID axis. Other Frames are left unchanged. The quality bit Frame and the new GRID axis are connected using a ShiftMap that gives the right zero-based bit numbers (which also correspond to PIXEL indices). */ shift[ 0 ] = -2.0; atlAddWcsAxis( tfset, (AstMapping *) astShiftMap( 1, shift, " " ), astFrame( 1, "Label(1)=Quality bit,Domain=QUALITY" ), NULL, NULL, status ); /* Store the FrameSet in the 3D NDF. */ ndfPtwcs( tfset, mapdata->file->ndfid, status ); tfset = astAnnul( tfset ); } /* Clean up */ if( mgrp ) grpDelet( &mgrp, status ); smf_close_file( wf, &mapdata, status ); }
void smf_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; }
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 ); } }
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_rebincube_ast( ThrWorkForce *wf, smfData *data, int first, int last, int *ptime, dim_t nchan, dim_t ndet, dim_t nslice, dim_t nel, dim_t nxy, dim_t nout, dim_t dim[3], AstMapping *ssmap, AstSkyFrame *abskyfrm, AstMapping *oskymap, Grp *detgrp, int moving, int usewgt, int spread, const double params[], int genvar, double tfac, double fcon, float *data_array, float *var_array, double *wgt_array, float *texp_array, float *teff_array, int *good_tsys, int *nused, int *status ){ /* Local Variables */ AstCmpMap *detmap = NULL; /* Mapping from 1D det. index to 2D i/p "grid" coords */ AstMapping *dtotmap = NULL; /* 1D det index->o/p GRID Mapping */ AstMapping *fullmap = NULL; /* WCS->GRID LutMap from input WCS FrameSet */ AstMapping *lutmap = NULL; /* Mapping that identifies detectors to be used */ AstMapping *splut = NULL; /* Spatial LutMap */ AstMapping *sslut = NULL; /* Spectral LutMap */ AstMapping *totmap = NULL; /* WCS->GRID Mapping from input WCS FrameSet */ AstPermMap *pmap; /* Mapping to rearrange output axes */ const char *name = NULL; /* Pointer to current detector name */ const double *tsys = NULL; /* Pointer to Tsys value for first detector */ dim_t iv; /* Vector index into output 3D array */ double *detlut = NULL; /* Work space for detector mask */ double blk_bot[ 2*MAXTHREADS + 1 ]; /* First o/p channel no. in each block */ double con; /* Constant value */ double dtemp; /* Temporary value */ double tcon; /* Variance factor for whole time slice */ float *detwork = NULL; /* Work array for detector values */ float *tdata = NULL; /* Pointer to start of input time slice data */ float *varwork = NULL; /* Work array holding variances for 1 slice/channel */ float *vp = NULL; /* Pointer to next "varwork" element */ float invar; /* Input variance */ float rtsys; /* Tsys value */ float teff; /* Effective integration time */ float texp; /* Total time ( = ton + toff ) */ int *nexttime; /* Pointer to next time slice index to use */ int ast_flags; /* Basic flags to use with astRebinSeq */ int blk_size; /* Number of channels processed by a single thread */ int found; /* Was current detector name found in detgrp? */ int iblock; /* Index of current spectral block */ dim_t ichan; /* Index of current channel */ dim_t idet; /* detector index */ int ignore; /* Ignore this time slice? */ int inperm[ 3 ]; /* Input axis permutation array */ dim_t itime; /* Index of current time slice */ int junk; /* Unused parameter */ int lbnd_in[ 2 ]; /* Lower input bounds on receptor axis */ int ldim[ 3 ]; /* Output array lower GRID bounds */ int maxthreads; /* Max no. of threads to use when re-binning */ int nblock; /* Number of spectral blocks */ int nthreads; /* Number of threads to use when re-binning */ int outperm[ 3 ]; /* Output axis permutation array */ int timeslice_size; /* Number of elements in a time slice */ int ubnd_in[ 2 ]; /* Upper input bounds on receptor axis */ int uddim[ 1 ]; /* Detector array upper GRID bounds */ int udim[ 3 ]; /* Output array upper GRID bounds */ smfHead *hdr = NULL; /* Pointer to data header for this time slice */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Store a pointer to the input NDFs smfHead structure. */ hdr = data->hdr; /* Fill an array with the lower grid index bounds of the output. */ ldim[ 0 ] = 1; ldim[ 1 ] = 1; ldim[ 2 ] = 1; /* Integer upper grid index bounds of the output. */ udim[ 0 ] = dim[ 0 ]; udim[ 1 ] = dim[ 1 ]; udim[ 2 ] = dim[ 2 ]; /* Integer upper bounds of detector array. */ uddim[ 0 ] = ndet; /* Store the size of an input time slice. */ timeslice_size = nel/nslice; /* Create a LutMap that holds the output spectral axis GRID value at the centre of each input spectral axis pixel. LutMaps are faster to evaluate, and so astRebinSeq will go faster. We can use LutMaps without loosing accuracy since astRebinSeq only ever transforms the GRID values at input pixel centres (i.e. integer GRID values), and so the LutMap will always return a tabulated value rather than an interpolated value. */ atlTolut( (AstMapping *) ssmap, 1.0, (double) nchan, 1.0, "LutInterp=1", &sslut, status ); /* If this is the first pass through this file, initialise the arrays. */ if( first ) smf_rebincube_init( 0, nxy, nout, genvar, data_array, var_array, wgt_array, texp_array, teff_array, &junk, status ); /* Initialisation the flags for astRebinSeq (we do not include flag AST__REBININIT because the arrays have been initialised). */ ast_flags = AST__USEBAD; if( usewgt ) ast_flags = ast_flags | AST__VARWGT; if( genvar == 1 ) { ast_flags = ast_flags | AST__GENVAR; } else if( genvar == 2 ) { ast_flags = ast_flags | AST__USEVAR; } /* If required, allocate a work array to hold all the input variances for a single time slice. */ if( usewgt || genvar == 2 ) varwork = astMalloc( timeslice_size * sizeof( float ) ); /* Allocate a work array to hold the exposure time for each detector. */ detwork = astMalloc( ndet * sizeof( float ) ); /* If we are dealing with more than 1 detector, create a LutMap that holds the input GRID index of every detector to be included in the output, and AST__BAD for every detector that is not to be included in the output cube. First allocate the work space for the LUT. */ if( ndet > 1 ) { detlut = astMalloc( ndet*sizeof( double ) ); /* Initialise a string to point to the name of the first detector for which data is available */ name = hdr->detname; /* Loop round all detectors for which data is available. */ for( idet = 0; idet < ndet; idet++ ) { /* Store the input GRID coord of this detector. GRID coords start at 1, not 0. */ detlut[ idet ] = idet + 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 coord bad. This will cause astRebinSeq to ignore data from the detector. */ if( detgrp ) { found = grpIndex( name, detgrp, 1, status ); if( !found ) detlut[ idet ] = AST__BAD; } /* Move on to the next available detector name. */ name += strlen( name ) + 1; } /* Create the LutMap. */ lutmap = (AstMapping *) astLutMap( ndet, detlut, 1.0, 1.0, "LutInterp=1" ); /* If we only have 1 detector, use a UnitMap instead of a LutMap (lutMaps must have 2 or more table entries). */ } else { lutmap = (AstMapping *) astUnitMap( 1, " " ); } /* Combine the above LutMap with a 1-input, 2-output PermMap that copies its input to create its first output, and assigns a constant value of 1.0 to its second output. We need to do this because smf_tslice returns a 2D GRID system (even though the second GRID axis is not actually used). */ inperm[ 0 ] = 1; outperm[ 0 ] = 1; outperm[ 1 ] = -1; con = 1.0; detmap = astCmpMap( lutmap, astPermMap( 1, inperm, 2, outperm, &con, " " ), 1, " " ); /* Store the bounds of a single time slice grid. */ lbnd_in[ 0 ] = 1; ubnd_in[ 0 ] = nchan; lbnd_in[ 1 ] = 1; ubnd_in[ 1 ] = ndet; /* Create a PermMap that can be used to re-order the output axes so that channel number is axis 3. */ outperm[ 0 ] = 2; outperm[ 1 ] = 3; outperm[ 2 ] = 1; inperm[ 0 ] = 3; inperm[ 1 ] = 1; inperm[ 2 ] = 2; pmap = astPermMap( 3, inperm, 3, outperm, NULL, " " ); /* If we are using multiple threads to rebin spectral blocks in parallel, calculate the number of channels that are processed by each thread, and the number of threads to use. The whole output spectrum is divided up into blocks. The number of blocks is two times the number of threads, and each thread rebins two adjacent blocks. Alternate blocks are re-binned simultanously. First, the odd numbered blocks are re-binned (one by each thread). When all odd numbered blocks have been re-binned, the even numbered blocks are re-binned. We ensure that the number of threads used results in a block size that is larger than the spreading width produced by the requested spreading scheme. This means that no pair of simultanously executing threads will ever try to write to the same channel of the output spectrum. */ maxthreads = wf ? wf->nworker : 1; if( maxthreads > MAXTHREADS ) maxthreads = MAXTHREADS; if( maxthreads > 1 ) { /* Find the largest number of threads into which each output spectrum can be split. The limit is imposes by the requirement that each block is larger than the pixel spreading produced by the requested spreading scheme. */ nthreads = ( ( dim[ 2 ] + 1 )/2 )/smf_spreadwidth( spread, params, status ); /* If the spectral range is less than twice the spreading width, we cannot use multiple threads. */ if( nthreads > 1 ) { /* Restrict the number of threads to be no more than the number of workers available in the work force. */ if( nthreads > maxthreads ) nthreads = maxthreads; /* Find the number of output channels in each spectral block. */ blk_size = ( dim[ 2 ] - 1 )/( 2*nthreads ) + 1; /* Set up the first output channel number within each block. */ nblock = 2*nthreads; for( iblock = 0; iblock < nblock; iblock++ ) { blk_bot[ iblock ] = (double) ( iblock*blk_size + 1 ); } /* Add in the first channel number beyond the last block. */ blk_bot[ nblock ] = blk_bot[ nblock - 1 ] + blk_size; /* If the output spectrum is too short to guarantee that there are any independent blocks of output channels, we process the whole spectrum in a single thread. */ } else { nthreads = 1; nblock = 1; blk_bot[ 0 ] = 1.0; blk_bot[ 1 ] = (double) ( dim[ 2 ] + 1 ); } /* If multiple threads are not available, we process the whole spectrum in a single thread. */ } else { nthreads = 1; nblock = 1; blk_bot[ 0 ] = 1.0; blk_bot[ 1 ] = (double) ( dim[ 2 ] + 1 ); } /* Convert the block boundaries from output channel numbers into input channel numbers. */ astTran1( ssmap, nblock + 1, blk_bot, 0, blk_bot ); /* Ensure they are in increasing order, and are not outside the bounds of the input array. */ if( blk_bot[ 0 ] > blk_bot[ 1 ] ) { for( iblock = 0; iblock < ( nblock + 1 )/2; iblock++ ) { dtemp = blk_bot[ nblock - iblock ]; blk_bot[ nblock - iblock ] = blk_bot[ iblock ]; blk_bot[ iblock ] = dtemp; } } for( iblock = 0; iblock <= nblock; iblock++ ) { if( blk_bot[ iblock ] < 1 ) { blk_bot[ iblock ] = 1.0; } else if( blk_bot[ iblock ] > nchan ) { blk_bot[ iblock ] = nchan; } } /* Initialise a pointer to the next time slice index to be used. */ nexttime = ptime; /* Initialise the progress meter. */ smf_reportprogress( nslice, status ); /* Loop round all time slices in the input NDF. */ for( itime = 0; itime < nslice && *status == SAI__OK; itime++ ) { /* If this time slice is not being pasted into the output cube, pass on. */ if( nexttime ){ if( *nexttime != (int) itime ) continue; nexttime++; } /* Store a pointer to the first input data value in this time slice. */ tdata = ( (float *) (data->pntr)[ 0 ] ) + itime*timeslice_size; /* Begin an AST context. Having this context within the time slice loop helps keep the number of AST objects in use to a minimum. */ astBegin; /* Get a Mapping from the spatial GRID axes in the input the spatial GRID axes in the output for the current time slice. Note this has to be done first since it stores details of the current time slice in the "smfHead" structure inside "data", and this is needed by subsequent functions. */ totmap = smf_rebin_totmap( data, itime, abskyfrm, oskymap, moving, status ); if( !totmap ) break; /* Get the effective exposure time, the total exposure time, and the Tsys->Variance onversion factor for this time slice. Also get a pointer to the start of the Tsys array. */ tsys = smf_rebincube_tcon( hdr, itime, fcon, &texp, &teff, &tcon, status ); /* So "totmap" is a 2-input, 2-output Mapping that transforms the input spatial GRID coords into output spatial GRID coords. In order to speed up astRebinSeq we represent this by a pair of parallel LutMaps. To do this (using atlTolut) we need a Mapping which only has 1 input, so we preceed "totmap" with "detmap" (which also has the effect of exluding data from unrequired detectors). We then combine this Mapping in parallel with the spectral LutMap to get a 2-input (channel number, detector index) and 3-output (output grid coords) Mapping. We finally add a PermMap to re-arrange the output axes so that channel number is axis 3 in the output. */ dtotmap = (AstMapping *) astCmpMap( detmap, totmap, 1, " " ); if( ndet > 1 ) { atlTolut( dtotmap, 1.0, (double) ndet, 1.0, "LutInterp=1", &splut, status ); } else { splut = astClone( dtotmap ); } fullmap = astSimplify( astCmpMap( astCmpMap( sslut, splut, 0, " " ), pmap, 1, " " ) ); /* If required calculate the variance associated with each value in the current time slice. based on the input Tsys values. If they are needed, but not available, ignored the time slice. */ ignore = 0; if( varwork ) { ignore = 1; vp = varwork; for( idet = 0; idet < ndet; idet++ ) { invar = VAL__BADR; rtsys = tsys ? (float) tsys[ idet ] : VAL__BADR; if( rtsys <= 0.0 ) rtsys = VAL__BADR; if( rtsys != VAL__BADR ) { *good_tsys = 1; if( tcon != VAL__BADD ) { invar = tcon*rtsys*rtsys; ignore = 0; } } for( ichan = 0; ichan < nchan; ichan++ ) *(vp++) = invar; } } /* Unless we are ignoring this time slice, paste it into the 3D output cube. The smf_rebincube_seqf function is a wrapper for astRebinSeqF that splits the total job up between "nthreads" threads running in parallel. */ if( !ignore ) { smf_rebincube_seqf( wf, nthreads, blk_bot, fullmap, 0.0, 2, lbnd_in, ubnd_in, tdata, varwork, spread, params, ast_flags, 0.0, 50, VAL__BADR, 3, ldim, udim, lbnd_in, ubnd_in, data_array, var_array, wgt_array, nused, status ); /* Now we update the total exposure time array. Scale the exposure time of this time slice in order to reduce its influence on the output expsoure times if it does not have much spectral overlap with the output cube. then fill the 1D work array with this constant value and paste it into the 2D texp_array using the spatial mapping. Note we want the simple sum of the exposure times, with no normalisation. SO we use the AST__NONORM flag which means we do not need to supply a weights array. */ if( texp != VAL__BADR ) { texp *= tfac; for( iv = 0; iv < ndet; iv++ ) detwork[ iv ] = texp; astRebinSeqF( splut, 0.0, 1, ldim, uddim, detwork, NULL, spread, params, AST__NONORM, 0.0, 50, VAL__BADR, 2, ldim, udim, ldim, uddim, texp_array, NULL, NULL, NULL ); } /* Now do the same with the effective exposure time. */ if( teff != VAL__BADR ) { teff *= tfac; for( iv = 0; iv < ndet; iv++ ) detwork[ iv ] = teff; astRebinSeqF( splut, 0.0, 1, ldim, uddim, detwork, NULL, spread, params, AST__NONORM, 0.0, 50, VAL__BADR, 2, ldim, udim, ldim, uddim, teff_array, NULL, NULL, NULL ); } } /* Update the progress meter. */ smf_reportprogress( 0, status ); /* End the AST context. */ astEnd; } /* If this is the final pass through this function, normalise the returned data and variance values. */ if( last ) { /* Create a dummy mapping that can be used with astRebinSeq (it is not actually used for anything since we are not adding any more data into the output arrays). */ fullmap = (AstMapping *) astPermMap( 2, NULL, 3, NULL, NULL, " " ); /* Normalise the data values. We do not normalise the exposure time arrays. */ astRebinSeqF( fullmap, 0.0, 2, lbnd_in, ubnd_in, NULL, NULL, spread, params, AST__REBINEND | ast_flags, 0.0, 50, VAL__BADR, 3, ldim, udim, lbnd_in, ubnd_in, data_array, var_array, wgt_array, nused ); fullmap = astAnnul(fullmap); } /* Free resources. */ detlut = astFree( detlut ); detwork = astFree( detwork ); varwork = astFree( varwork ); }
void smf_close_file( smfData ** data, int * status ) { void *buf=NULL; /* Buffer pointer */ size_t buflen=0; /* Size of buffer */ smfDA * da; /* pointer to smfDA in smfData */ smfFts* fts; /* pointer to smfFts in smfData */ size_t datalen=0; /* Size of data buffer in bytes */ smfDream *dream = NULL; /* Pointer to smfDream in smfData */ smfFile * file; /* pointer to smfFile in smfData */ int freedata = 0; /* should the data arrays be freed? */ smfHead * hdr; /* pointer to smfHead in smfData */ size_t headlen=0; /* Size of header (mmap'd files) in bytes */ size_t i; /* loop counter */ smfDIMMHead *temphead=NULL; /* Pointer to DIMM header */ /* we need to be able to clean up even if input status is bad - this requires some defensive programming. */ if ( *data == NULL ) { if ( *status == SAI__OK ) { /* Status is good so we have a problem */ *status = SAI__ERROR; errRep( ERRFUNC, "Attempt to close file when smfData pointer is NULL (possible programming error)", status ); } /* null pointer so just return since there is nothing to free */ return; } /* Process reference count */ /* Only proceed if the decremented reference count is 0 */ (*data)->refcount--; if ((*data)->refcount > 0 ) return; /* Get the header and file, since we need them for checking */ hdr = (*data)->hdr; /* Before annulling close NDF try closing SCU2RED.MAPCOORD */ smf_close_mapcoord( *data, status ); /* Display any warnings generated by AST and stored in the FitsChan. */ file = (*data)->file; if( hdr && hdr->fitshdr ) { fts1Astwn( hdr->fitshdr, file ? file->ndfid : NDF__NOID, status ); } /* now file information */ if (file != NULL) { if ( file->isSc2store ) { /* Nothing to free here except for data */ freedata = 1; } else if ( file->ndfid != NDF__NOID ) { /* Handle quality as a special case */ if ( (*data)->qual) { (*data)->qual = smf_qual_unmap( file->ndfid, (*data)->qfamily, (*data)->qual, status ); } /* Annul the NDF (which will unmap things) */ ndfAnnul( &(file->ndfid), status ); } else if( file->fd ) { /* Array was mmap'd to a file, and must now be sync'd and unmapped, and the file descriptor closed */ /* Get the size of the header and data section */ smf_calc_mmapsize( sizeof(*temphead), *data, &headlen, &datalen, &buflen, status ); /* The header is in the same mapped array as the data array, but headlen bytes earlier. Point temphead to this location and update relevant header values before closing */ buf = ((char*)((*data)->pntr[0]) - headlen); temphead = (smfDIMMHead *) buf; temphead->data.isTordered = (*data)->isTordered; temphead->data.dtype = (*data)->dtype; temphead->data.ndims = (*data)->ndims; memcpy( temphead->data.dims, (*data)->dims, sizeof( temphead->data.dims)); if( msync( buf, buflen, MS_ASYNC ) == -1 ) { *status = SAI__ERROR; errRep( ERRFUNC, "Unable to synch model container file", status ); } else if( munmap( buf, buflen ) == -1 ) { *status = SAI__ERROR; errRep( ERRFUNC, "Unable to unmap model container file", status ); } else if( close( file->fd ) == -1 ) { *status = SAI__ERROR; errRep( ERRFUNC, "Unable to close model container file", status ); } else { file->fd = 0; } } else { /* No file so free the data */ freedata = 1; } (*data)->file = astFree( (*data)->file ); } else { /* no file - data is ours to free */ freedata = 1; } /* Tidy up the header */ if (hdr != NULL) { if (hdr->wcs != NULL) hdr->wcs = astAnnul( hdr->wcs ); if (hdr->tswcs != NULL) hdr->tswcs = astAnnul( hdr->tswcs ); if (hdr->fitshdr != NULL) hdr->fitshdr = astAnnul( hdr->fitshdr ); if( hdr->cache1 ) hdr->cache1 = sc2ast_createwcs2( SC2AST__NULLSUB, NULL, 0.0, NULL, NULL, NULL, hdr->cache1, status ); if( hdr->cache2 ) hdr->cache2 = smf_create_lutwcs( 1, NULL, NULL, 0, NULL, 0.0, NULL, NULL, NULL, hdr->cache2, status ); if( hdr->cache3 ) hdr->cache3 = smf_detpos_wcs( NULL, -1, 0.0, NULL, NULL, hdr->cache3, status ); if (!hdr->isCloned) { /* We are responsible for this memory - although what happens when we are cloned and the original is freed first? Need to think carefully about memory management. */ if (hdr->allState != NULL) { hdr->allState = astFree( hdr->allState ); } if (hdr->fplanex) hdr->fplanex = astFree( hdr->fplanex ); if (hdr->fplaney) hdr->fplaney = astFree( hdr->fplaney ); if (hdr->detpos) hdr->detpos = astFree( hdr->detpos ); if (hdr->tsys) hdr->tsys = astFree( hdr->tsys ); if (hdr->detname) hdr->detname = astFree( hdr->detname ); if (hdr->ocsconfig) hdr->ocsconfig = astFree( hdr->ocsconfig ); } hdr = astFree( hdr ); } /* Now the smfDA itself */ if ( (*data)->da != NULL ) { da = (*data)->da; da->flatcal = astFree( da->flatcal ); da->flatpar = astFree( da->flatpar ); if( da->dksquid) smf_close_file( &da->dksquid, status ); da->heatval = astFree( da->heatval ); da = astFree( da ); } /* Free smfFts */ if((*data)->fts != NULL) { fts = (*data)->fts; if(fts->zpd) { smf_close_file(&fts->zpd, status); } if(fts->fpm) { smf_close_file(&fts->fpm, status); } if(fts->sigma) { smf_close_file(&fts->sigma, status); } fts = astFree(fts); } /* Free smfDream */ if ( (*data)->dream != NULL ) { dream = (*data)->dream; if ( dream->gridwts != NULL) dream->gridwts = astFree( dream->gridwts ); if ( dream->invmatx != NULL) dream->invmatx = astFree( dream->invmatx ); dream= astFree( dream ); } /* Free up other pointers in the smfData: */ if ( (*data)->poly ) { (*data)->poly = astFree( (*data)->poly ); } if ( (*data)->lut ) { (*data)->lut = astFree( (*data)->lut ); } if( (*data)->theta ) { (*data)->theta = astFree( (*data)->theta ); } /* Free the data arrays if they are non-null (they should have been freed if they were mapped to a file but not if they were stored in a separate action as temp storage */ if (freedata) { for (i = 0; i < 2; i++ ) { if ( ((*data)->pntr)[i] != NULL ) ((*data)->pntr)[i] = astFree( ((*data)->pntr)[i] ); } if ( (*data)->qual ) { (*data)->qual = astFree( (*data)->qual ); } } /* finally free smfData */ *data = astFree( *data ); }
unsigned char *smf_get_mask( ThrWorkForce *wf, smf_modeltype mtype, AstKeyMap *config, smfDIMMData *dat, int flags, int *status ) { /* Local Variables: */ AstCircle *circle; /* AST Region used to mask a circular area */ AstKeyMap *akm; /* KeyMap holding AST config values */ AstKeyMap *subkm; /* KeyMap holding model config values */ char refparam[ DAT__SZNAM ];/* Name for reference NDF parameter */ char words[100]; /* Buffer for variable message words */ const char *cval; /* The ZERO_MASK string value */ const char *modname; /* The name of the model being masked */ const char *skyrefis; /* Pointer to SkyRefIs attribute value */ dim_t i; /* Pixel index */ double *pd; /* Pointer to next element of map data */ double *predef; /* Pointer to mask defined by previous run */ double *ptr; /* Pointer to NDF Data array */ double *pv; /* Pointer to next element of map variance */ double centre[ 2 ]; /* Coords of circle centre in radians */ double meanhits; /* Mean hits in the map */ double radius[ 1 ]; /* Radius of circle in radians */ double zero_circle[ 3 ]; /* LON/LAT/Radius of circular mask */ double zero_lowhits; /* Fraction of mean hits at which to threshold */ double zero_snr; /* Higher SNR at which to threshold */ double zero_snrlo; /* Lower SNR at which to threshold */ int *ph; /* Pointer to next hits value */ int have_mask; /* Did a mask already exist on entry? */ int imask; /* Index of next mask type */ int indf1; /* Id. for supplied reference NDF */ int indf2; /* Id. for used section of reference NDF */ int isstatic; /* Are all used masks static? */ int lbnd_grid[ 2 ]; /* Lower bounds of map in GRID coords */ int mask_types[ NTYPE ]; /* Identifier for the types of mask to use */ int munion; /* Use union of supplied masks */ int nel; /* Number of mapped NDF pixels */ int nmask; /* The number of masks to be combined */ int nsource; /* No. of source pixels in final mask */ int skip; /* No. of iters for which AST is not subtracted */ int thresh; /* Absolute threshold on hits */ int ubnd_grid[ 2 ]; /* Upper bounds of map in GRID coords */ int zero_c_n; /* Number of zero circle parameters read */ int zero_mask; /* Use the reference NDF as a mask? */ int zero_niter; /* Only mask for the first "niter" iterations. */ int zero_notlast; /* Don't zero on last iteration? */ size_t ngood; /* Number good samples for stats */ smf_qual_t *pq; /* Pinter to map quality */ unsigned char **mask; /* Address of model's mask pointer */ unsigned char *newmask; /* Individual mask work space */ unsigned char *pm; /* Pointer to next returned mask pixel */ unsigned char *pn; /* Pointer to next new mask pixel */ unsigned char *result; /* Returned mask pointer */ /* Initialise returned values */ result = NULL; /* Check inherited status. Also check that a map is being created. */ if( *status != SAI__OK || !dat || !dat->map ) return result; /* Begin an AST context. */ astBegin; /* Get the sub-keymap containing the configuration parameters for the requested model. Also get a pointer to the mask array to use (there is one for each maskable model)*/ if( mtype == SMF__COM ) { modname = "COM"; mask = &(dat->com_mask); } else if( mtype == SMF__AST ) { modname = "AST"; mask = &(dat->ast_mask); } else if( mtype == SMF__FLT ) { modname = "FLT"; mask = &(dat->flt_mask); } else { modname = NULL; mask = NULL; *status = SAI__ERROR; errRepf( " ", "smf_get_mask: Unsupported model type %d supplied - " "must be COM, FLT or AST.", status, mtype ); } subkm = NULL; astMapGet0A( config, modname, &subkm ); /* Get the "ast.skip" value - when considering "zero_niter" and "zero_freeze", we only count iterations for which the AST model is subtracted (i.e. the ones following the initial "ast.skip" iterations). */ astMapGet0A( config, "AST", &akm ); astMapGet0I( akm, "SKIP", &skip ); akm = astAnnul( akm ); /* Get the number of iterations over which the mask is to be applied. Zero means all. Return with no mask if this number of iterations has already been performed. */ zero_niter = 0; astMapGet0I( subkm, "ZERO_NITER", &zero_niter ); if( zero_niter == 0 || dat->iter < zero_niter + skip ) { /* Only return a mask if this is not the last iteration, or if ZERO_NOTLAST is unset. */ zero_notlast = 0; astMapGet0I( subkm, "ZERO_NOTLAST", &zero_notlast ); if( !( flags & SMF__DIMM_LASTITER ) || !zero_notlast ) { /* Create a list of the mask types to be combined to get the final mask by looking for non-default values for the corresponding configuration parameters in the supplied KeyMap. Static masks (predefined, circles or external NDFs) may be used on any iteration, but dynamic masks (lowhits, snr) will only be avialable once the map has been determined at the end of the first iteration. This means that when masking anything but the AST model (which is determined after the map), the dynamic masks cannot be used on the first iteration. Make a note if all masks being used are static. */ isstatic = 1; nmask = 0; zero_lowhits = 0.0; astMapGet0D( subkm, "ZERO_LOWHITS", &zero_lowhits ); if( zero_lowhits > 0.0 ) { if( mtype == SMF__AST || !( flags & SMF__DIMM_FIRSTITER ) ) { mask_types[ nmask++] = LOWHITS; isstatic = 0; } } else if( zero_lowhits < 0.0 && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "Bad value for config parameter %s.ZERO_LOWHITS (%g) - " "it must not be negative.", status, modname, zero_lowhits ); } if( astMapGet1D( subkm, "ZERO_CIRCLE", 3, &zero_c_n, zero_circle ) ) { if( zero_c_n == 1 || zero_c_n == 3 ) { mask_types[ nmask++] = CIRCLE; } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "Bad number of values (%d) for config parameter " "%s.ZERO_CIRCLE - must be 1 or 3.", status, zero_c_n, modname ); } } cval = NULL; astMapGet0C( subkm, "ZERO_MASK", &cval ); if( cval ) { if( !astChrMatch( cval, "REF" ) && !astChrMatch( cval, "MASK2" ) && !astChrMatch( cval, "MASK3" ) ) { astMapGet0I( subkm, "ZERO_MASK", &zero_mask ); cval = ( zero_mask > 0 ) ? "REF" : NULL; } if( cval ) { strcpy( refparam, cval ); astChrCase( NULL, refparam, 1, 0 ); mask_types[ nmask++] = REFNDF; } } zero_snr = 0.0; astMapGet0D( subkm, "ZERO_SNR", &zero_snr ); if( zero_snr > 0.0 ) { if( mtype == SMF__AST || !( flags & SMF__DIMM_FIRSTITER ) ) { mask_types[ nmask++] = SNR; isstatic = 0; } } else if( zero_snr < 0.0 && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "Bad value for config parameter %s.ZERO_SNR (%g) - " "it must not be negative.", status, modname, zero_snr ); } if( astMapHasKey( subkm, "ZERO_MASK_POINTER" ) ) { astMapGet0P( subkm, "ZERO_MASK_POINTER", (void **) &predef ); if( predef ) mask_types[ nmask++] = PREDEFINED; } /* No need to create a mask if no masking was requested or possible. */ if( nmask > 0 ) { /* Decide if we are using the union or intersection of the masks. */ astMapGet0I( subkm, "ZERO_UNION", &munion ); /* Note if a mask existed on entry. If not, create a mask now, and initialise it to hold the mask defined by the initial sky map. */ if( *mask == NULL ) { have_mask = 0; if( dat->initqual ) { *mask = astMalloc( dat->msize*sizeof( **mask ) ); if( *mask ) { pm = *mask; pq = dat->initqual; for( i = 0; i < dat->msize; i++ ) { *(pm++) = ( *(pq++) != 0 ); } } } else{ *mask = astCalloc( dat->msize, sizeof( **mask ) ); } } else { have_mask = 1; } /* If we are combining more than one mask, we need work space to hold an individual mask independently of the total mask. If we are using only one mask, then just use the main mask array. */ if( nmask > 1 ) { newmask = astMalloc( dat->msize*sizeof( *newmask ) ); } else { newmask = *mask; } /* Get the number of iterations after which the mask is to be frozen. Zero means "never freeze the mask". */ int zero_freeze = 0; astMapGet0I( subkm, "ZERO_FREEZE", &zero_freeze ); /* Loop round each type of mask to be used. */ for( imask = 0; imask < nmask && *status == SAI__OK; imask++ ){ /* If the mask is now frozen, we just return the existing mask. So leave the loop. */ if( zero_freeze != 0 && dat->iter > zero_freeze + skip ) { break; /* Low hits masking... */ } else if( mask_types[ imask ] == LOWHITS ) { /* Set hits pixels with 0 hits to VAL__BADI so that stats1 ignores them */ ph = dat->hitsmap; for( i = 0; i < dat->msize; i++,ph++ ) { if( *ph == 0 ) *ph = VAL__BADI; } /* Find the mean hits in the map */ smf_stats1I( dat->hitsmap, 1, dat->msize, NULL, 0, 0, &meanhits, NULL, NULL, &ngood, status ); msgOutiff( MSG__DEBUG, " ", "smf_get_mask: mean hits = %lf, ngood " "= %zd", status, meanhits, ngood ); /* Create the mask */ thresh = meanhits*zero_lowhits; ph = dat->hitsmap; pn = newmask; for( i = 0; i < dat->msize; i++,ph++ ) { *(pn++) = ( *ph != VAL__BADI && *ph < thresh ) ? 1 : 0; } /* Report masking info. */ msgOutiff( MSG__DEBUG, " ", "smf_get_mask: masking %s " "model at hits = %d.", status, modname, thresh ); /* Circle masking... */ } else if( mask_types[ imask ] == CIRCLE ) { /* If we had a mask on entry, then there is no need to create a new one since it will not have changed. But we need to recalculate the circle mask if are combining it with any non-static masks. */ if( ! have_mask || ! isstatic ) { /* If only one parameter supplied it is radius, assume reference LON/LAT from the frameset to get the centre. If the SkyFrame represents offsets from the reference position (i.e. the source is moving), assume the circle is to be centred on the origin. */ if( zero_c_n == 1 ) { zero_circle[ 2 ] = zero_circle[ 0 ]; skyrefis = astGetC( dat->outfset, "SkyRefIs" ); if( skyrefis && !strcmp( skyrefis, "Origin" ) ) { zero_circle[ 0 ] = 0.0; zero_circle[ 1 ] = 0.0; } else { zero_circle[ 0 ] = astGetD( dat->outfset, "SkyRef(1)" ); zero_circle[ 1 ] = astGetD( dat->outfset, "SkyRef(2)" ); } zero_circle[ 0 ] *= AST__DR2D; zero_circle[ 1 ] *= AST__DR2D; } /* The supplied bounds are for pixel coordinates... we need bounds for grid coordinates which have an offset */ lbnd_grid[ 0 ] = 1; lbnd_grid[ 1 ] = 1; ubnd_grid[ 0 ] = dat->ubnd_out[ 0 ] - dat->lbnd_out[ 0 ] + 1; ubnd_grid[ 1 ] = dat->ubnd_out[ 1 ] - dat->lbnd_out[ 1 ] + 1; /* Coordinates & radius of the circular region converted from degrees to radians */ centre[ 0 ] = zero_circle[ 0 ]*AST__DD2R; centre[ 1 ] = zero_circle[ 1 ]*AST__DD2R; radius[ 0 ] = zero_circle[ 2 ]*AST__DD2R; /* Create the Circle, defined in the current Frame of the FrameSet (i.e. the sky frame). */ circle = astCircle( astGetFrame( dat->outfset, AST__CURRENT), 1, centre, radius, NULL, " " ); /* Fill the mask with zeros. */ memset( newmask, 0, sizeof( *newmask )*dat->msize ); /* Get the mapping from the sky frame (current) to the grid frame (base), and then set the mask to 1 for all of the values outside this circle */ astMaskUB( circle, astGetMapping( dat->outfset, AST__CURRENT, AST__BASE ), 0, 2, lbnd_grid, ubnd_grid, newmask, 1 ); /* Report masking info. */ if( zero_niter == 0 ) { sprintf( words, "on each iteration" ); } else { sprintf( words, "for %d iterations", zero_niter ); } msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model will" " be masked %s using a circle of " "radius %g arc-secs, centred at %s=%s, %s=%s.", status, modname, words, radius[0]*AST__DR2D*3600, astGetC( dat->outfset, "Symbol(1)" ), astFormat( dat->outfset, 1, centre[ 0 ] ), astGetC( dat->outfset, "Symbol(2)" ), astFormat( dat->outfset, 2, centre[ 1 ] ) ); } /* Reference NDF masking... */ } else if( mask_types[ imask ] == REFNDF ) { /* If we had a mask on entry, then there is no need to create a new one since it will not have changed. But we need to recalculate the NDF mask if are combining it with any non-static masks. */ if( ! have_mask || ! isstatic ) { /* Begin an NDF context. */ ndfBegin(); /* Get an identifier for the NDF using the associated ADAM parameter. */ ndfAssoc( refparam, "READ", &indf1, status ); /* Get a section from this NDF that matches the bounds of the map. */ ndfSect( indf1, 2, dat->lbnd_out, dat->ubnd_out, &indf2, status ); /* Map the section. */ ndfMap( indf2, "DATA", "_DOUBLE", "READ", (void **) &ptr, &nel, status ); /* Check we can use the pointer safely. */ if( *status == SAI__OK ) { /* Find bad pixels in the NDF and set those pixels to 1 in the mask. */ pn = newmask; for( i = 0; i < dat->msize; i++ ) { *(pn++) = ( *(ptr++) == VAL__BADD ) ? 1 : 0; } /* Report masking info. */ ndfMsg( "N", indf2 ); msgSetc( "M", modname ); if( zero_niter == 0 ) { msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The ^M " "model will be masked on each iteration " "using the bad pixels in NDF '^N'.", status ); } else { msgSeti( "I", zero_niter ); msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The ^M " "model will be masked for ^I iterations " "using the bad pixels in NDF '^N'.", status ); } } /* End the NDF context. */ ndfEnd( status ); } /* SNR masking... */ } else if( mask_types[ imask ] == SNR ) { /* Get the lower SNR limit. */ zero_snrlo = 0.0; astMapGet0D( subkm, "ZERO_SNRLO", &zero_snrlo ); if( zero_snrlo <= 0.0 ) { zero_snrlo = zero_snr; } else if( zero_snrlo > zero_snr && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "Bad value for config parameter " "%s.ZERO_SNRLO (%g) - it must not be higher " "than %s.ZERO_SNR (%g).", status, modname, zero_snrlo, modname, zero_snr ); } /* If the higher and lower SNR limits are equal, just do a simple threshold on the SNR values to get the mask. */ if( zero_snr == zero_snrlo ) { pd = dat->map; pv = dat->mapvar; pn = newmask; for( i = 0; i < dat->msize; i++,pd++,pv++ ) { *(pn++) = ( *pd != VAL__BADD && *pv != VAL__BADD && *pv >= 0.0 && *pd < zero_snr*sqrt( *pv ) ) ? 1 : 0; } /* Report masking info. */ if( !have_mask ) { if( zero_niter == 0 ) { sprintf( words, "on each iteration" ); } else { sprintf( words, "for %d iterations", zero_niter ); } msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model " "will be masked %s using an SNR limit of %g.", status, modname, words, zero_snr ); } /* If the higher and lower SNR limits are different, create an initial mask by thresholding at the ZERO_SNR value, and then extend the source areas within the mask down to an SNR limit of ZERO_SNRLO. */ } else { smf_snrmask( wf, dat->map, dat->mapvar, dat->mdims, zero_snr, zero_snrlo, newmask, status ); /* Report masking info. */ if( !have_mask ) { if( zero_niter == 0 ) { sprintf( words, "on each iteration" ); } else { sprintf( words, "for %d iterations", zero_niter ); } msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model " "will be masked %s using an SNR limit of %g " "extended down to %g.", status, modname, words, zero_snr, zero_snrlo ); } } /* Predefined masking... */ } else if( mask_types[ imask ] == PREDEFINED ) { /* If we had a mask on entry, then there is no need to create a new one since it will not have changed. But we need to recalculate the mask if are combining it with any non-static masks. */ if( ! have_mask || ! isstatic ) { /* Find bad pixels in the predefined array and set those pixels to 1 in the mask. */ pn = newmask; for( i = 0; i < dat->msize; i++ ) { *(pn++) = ( *(predef++) == VAL__BADD ) ? 1 : 0; } /* Report masking info. */ if( zero_niter == 0 ) { sprintf( words, "on each iteration" ); } else { sprintf( words, "for %d iterations", zero_niter ); } msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model " "will be masked %s using a smoothed form of " "the final mask created with the previous map.", status, modname, words ); } } /* If required, add the new mask into the returned mask. If this is the first mask, we just copy the new mask to form the returned mask. Otherwise, we combine it with the existing returned mask. */ if( ! have_mask || ! isstatic ) { if( nmask > 1 ) { if( imask == 0 ) { memcpy( *mask, newmask, dat->msize*sizeof(*newmask)); } else { pm = *mask; pn = newmask; if( munion ) { for( i = 0; i < dat->msize; i++,pm++ ) { if( *(pn++) == 0 ) *pm = 0; } } else { for( i = 0; i < dat->msize; i++,pm++ ) { if( *(pn++) == 1 ) *pm = 1; } } } } } } /* Free the individual mask work array if it was used. */ if( nmask > 1 ) newmask = astFree( newmask ); /* Check that the mask has some source pixels (i.e. pixels that have non-bad data values - we do not also check variance values since they are not available until the second iteration). */ if( *status == SAI__OK ) { nsource = 0; pm = *mask; pd = dat->map; for( i = 0; i < dat->msize; i++,pd++,pv++,pm++ ) { if( *pd != VAL__BADD && *pm == 0 ) nsource++; } if( nsource < 5 && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "The %s mask being used has fewer than 5 " "source pixels.", status, modname ); if( zero_snr > 0.0 ) { errRepf( "", "Maybe your zero_snr value (%g) is too high?", status, zero_snr ); } } } /* Return the mask pointer if all has gone well. */ if( *status == SAI__OK ) result = *mask; } } } /* End the AST context, annulling all AST Objects created in the context. */ astEnd; /* Return the pointer to the boolean mask. */ return result; }
void smf_pread( Grp *igrp, const char *param, int *status ){ /* Local Variables: */ AstMapping *dlatmap; AstMapping *dlonmap; AstMapping *taimap; AstTable *table; char file[ GRP__SZNAM + 1 ]; char pbuf[ GRP__SZNAM + 1 ]; const char *system; void *p; /* Before we check the error status, see if we are annulling previously created Mappings. If so, get each formatted pointer from the group metadata, get an Object pointer form it, lock it for use by the current thread, and then annul it. Remove the metadata item from the group. */ if( !param ) { pbuf[ 0 ] = 0; smf_get_grp_metadata( igrp, "DLONMAP", pbuf, status ); if( pbuf[ 0 ] ) { sscanf( pbuf, "%p", &p ); dlonmap = (AstMapping *) p; astLock( dlonmap, 0 ); dlonmap = astAnnul( dlonmap ); smf_remove_grp_metadata( igrp, "DLONMAP", status ); } pbuf[ 0 ] = 0; smf_get_grp_metadata( igrp, "DLATMAP", pbuf, status ); if( pbuf[ 0 ] ) { sscanf( pbuf, "%p", &p ); dlatmap = (AstMapping *) p; astLock( dlatmap, 0 ); dlatmap = astAnnul( dlatmap ); smf_remove_grp_metadata( igrp, "DLATMAP", status ); } return; } /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Use the specified parameter to get the name of the text file containing the table of pointing corrections. */ parGet0c( param, file, sizeof( file ) - 1, status ); /* If no file was specified, annul the error. */ if( *status == PAR__NULL ) { errAnnul( status ); /* If a file was obtained sccuesfully, read it. */ } else if( *status == SAI__OK ) { /* Start an AST context. */ astBegin; /* Attempt to read an AST Table from the text file. */ table = atlReadTable( file, status ); /* Create a LutMap from each of the three columns. */ taimap = (AstMapping *) atlTablelutMap( table, "TAI", status ); dlonmap = (AstMapping *) atlTablelutMap( table, "DLON", status ); dlatmap = (AstMapping *) atlTablelutMap( table, "DLAT", status ); /* Create Mappings that transforms TAI into a DLON and DLAT. These use linear interpolation for non-tabulated TAI values. */ astInvert( taimap ); dlonmap = (AstMapping *) astCmpMap( taimap, dlonmap, 1, " " ); dlatmap = (AstMapping *) astCmpMap( taimap, dlatmap, 1, " " ); /* Format the pointers to these two Mappings and store them in the supplied group using names "DLONMAP" and "DLATMAP". */ sprintf( pbuf, "%p", (void *) dlonmap ); smf_add_grp_metadata( igrp, "DLONMAP", pbuf, status ); sprintf( pbuf, "%p", (void *) dlatmap ); smf_add_grp_metadata( igrp, "DLATMAP", pbuf, status ); /* See what system the DLON/DLAT values refer to (default to AZEL). Store it in the group. */ if( !astMapGet0C( table, "SYSTEM", &system ) ) system = "AZEL"; smf_add_grp_metadata( igrp, "PSYSTEM", system, status ); /* Unlock the pointers to the Mappings so that they can be used by a different thread. This also exempts the pointers from AST context handling (until they are re-locked) so the following call to astEnd will not annull them. */ astUnlock( dlonmap, 1 ); astUnlock( dlatmap, 1 ); /* End the AST context. This annuls all Objects created during the context, except for the unlocked Mappings. */ astEnd; /* Debug message. */ msgSetc( "F", file ); msgSetc( "S", system ); msgOutif( MSG__DEBUG, " ", "^S pointing corrections read from file ^F", status ); /* Issue a context message if anything went wrong. */ if( *status != SAI__OK ) { msgSetc( "F", file ); errRep( " ", "Failed to read pointing corrections from text file ^F.", 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 smurf_sc2clean( int *status ) { smfArray *array = NULL; /* Data to be cleaned */ Grp *basegrp=NULL; /* Grp containing first file each chunk */ size_t basesize; /* Number of files in base group */ smfArray *bbms = NULL; /* Bad bolometer masks */ smfArray *concat=NULL; /* Pointer to a smfArray */ size_t contchunk; /* Continuous chunk counter */ smfArray *darks = NULL; /* Dark data */ int ensureflat; /* Flag for flatfielding data */ smfArray *flatramps = NULL;/* Flatfield ramps */ AstKeyMap *heateffmap = NULL; /* Heater efficiency data */ smfData *odata = NULL; /* Pointer to output data struct */ Grp *fgrp = NULL; /* Filtered group, no darks */ size_t gcount=0; /* Grp index counter */ size_t idx; /* Subarray counter */ Grp *igrp = NULL; /* Input group of files */ smfGroup *igroup=NULL; /* smfGroup corresponding to igrp */ dim_t maxconcat=0; /* Longest continuous chunk length in samples */ double maxlen=0; /* Constrain maxconcat to this many seconds */ size_t ncontchunks=0; /* Number continuous chunks outside iter loop */ Grp *ogrp = NULL; /* Output group of files */ size_t osize; /* Total number of NDF names in the output group */ dim_t padStart=0; /* How many samples padding at start */ dim_t padEnd=0; /* How many samples padding at end */ size_t size; /* Number of files in input group */ int temp; /* Temporary signed integer */ int usedarks; /* flag for using darks */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ int writecom; /* Write COMmon mode to NDF if calculated? */ int writegai; /* Write GAIns to NDF if calculated? */ /* Main routine */ ndfBegin(); /* Find the number of cores/processors available and create a pool of threads of the same size. */ wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status ); /* Read the input file */ kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status ); /* Filter out darks */ smf_find_science( wf, igrp, &fgrp, 1, NULL, NULL, 1, 1, SMF__NULL, &darks, &flatramps, &heateffmap, NULL, status ); /* input group is now the filtered group so we can use that and free the old input group */ size = grpGrpsz( fgrp, status ); grpDelet( &igrp, status); igrp = fgrp; fgrp = NULL; if (size == 0) { msgOutif(MSG__NORM, " ","All supplied input frames were filtered," " nothing to do", status ); goto CLEANUP; } /* --- Parse ADAM parameters ---------------------------------------------- */ /* Maximum length of a continuous chunk */ parGdr0d( "MAXLEN", 0, 0, VAL__MAXD, 1, &maxlen, status ); /* Padding */ parGdr0i( "PADSTART", 0, 0, VAL__MAXI, 1, &temp, status ); padStart = (dim_t) temp; parGdr0i( "PADEND", 0, 0, VAL__MAXI, 1, &temp, status ); padEnd = (dim_t) temp; /* Are we using darks? */ parGet0l( "USEDARKS", &usedarks, status ); /* Are we flatfielding? */ parGet0l( "FLAT", &ensureflat, status ); /* Write COM/GAI to NDFs if calculated? */ parGet0l( "COM", &writecom, status ); parGet0l( "GAI", &writegai, status ); /* Get group of bolometer masks and read them into a smfArray */ smf_request_mask( wf, "BBM", &bbms, status ); /* Group the input files by subarray and continuity ----------------------- */ smf_grp_related( igrp, size, 1, 0, maxlen-padStart-padEnd, NULL, NULL, &maxconcat, NULL, &igroup, &basegrp, NULL, status ); /* Obtain the number of continuous chunks and subarrays */ if( *status == SAI__OK ) { ncontchunks = igroup->chunk[igroup->ngroups-1]+1; } basesize = grpGrpsz( basegrp, status ); /* Get output file(s) */ kpg1Wgndf( "OUT", basegrp, basesize, basesize, "More output files required...", &ogrp, &osize, status ); /* Loop over continuous chunks and clean -----------------------------------*/ gcount = 1; for( contchunk=0;(*status==SAI__OK)&&contchunk<ncontchunks; contchunk++ ) { AstKeyMap *keymap=NULL; int dkclean; AstKeyMap *sub_instruments=NULL; /* Place cleaning parameters into a keymap and set defaults. Do this inside the loop in case we are cleaning files with differing sub-instruments. Note that we use the map-maker defaults file here (which loads the sc2clean defaults) so that we populate the locked keymap with all the parameters that people may come across to allow them to load their map-maker config directly into sc2clean. */ sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE, NULL, igrp, igroup->subgroups[contchunk][0], status ); keymap = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_makemap.def", sub_instruments, 1, status ); if( sub_instruments ) sub_instruments = astAnnul( sub_instruments ); /* Now rerun smf_grp_related to figure out how long each downsampled chunk of data will be. */ if( basegrp ) grpDelet( &basegrp, status ); if( igroup ) smf_close_smfGroup( &igroup, status ); smf_grp_related( igrp, size, 1, 0, maxlen-padStart-padEnd, NULL, keymap, &maxconcat, NULL, &igroup, &basegrp, NULL, status ); /* Concatenate this continuous chunk */ smf_concat_smfGroup( wf, NULL, igroup, usedarks ? darks:NULL, bbms, flatramps, heateffmap, contchunk, ensureflat, 1, NULL, 0, NULL, NULL, NO_FTS, padStart, padEnd, 0, &concat, NULL, status ); if( *status == SAI__OK) { /* clean the dark squids now since we might need to use them to clean the bolometer data */ smf_get_cleanpar( keymap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dkclean, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, status ); for( idx=0; dkclean&&(*status==SAI__OK)&&idx<concat->ndat; idx++ ) { odata = concat->sdata[idx]; if( odata && odata->da && odata->da->dksquid ) { smfData *dksquid = odata->da->dksquid; AstKeyMap *kmap=NULL; msgOut("", TASK_NAME ": cleaning dark squids", status); /* fudge the header so that we can get at JCMTState */ dksquid->hdr = odata->hdr; /* clean darks using cleandk.* parameters */ astMapGet0A( keymap, "CLEANDK", &kmap ); array = smf_create_smfArray( status ); smf_addto_smfArray( array, dksquid, status ); smf_clean_smfArray( wf, array, NULL, NULL, NULL, kmap, status ); if( array ) { array->owndata = 0; smf_close_related( wf, &array, status ); } if( kmap ) kmap = astAnnul( kmap ); /* Unset hdr pointer so that we don't accidentally close it */ dksquid->hdr = NULL; } } /* Then the main data arrays */ if( *status == SAI__OK ) { smfArray *com = NULL; smfArray *gai = NULL; char filename[GRP__SZNAM+1]; msgOut("", TASK_NAME ": cleaning bolometer data", status ); smf_clean_smfArray( wf, concat, NULL, &com, &gai, keymap, status ); /* If ADAM parameters for COM or GAI were specified, and the common-mode was calculated, export to files here */ if( writecom && com ) { for( idx=0; (*status==SAI__OK)&&(idx<com->ndat); idx++ ) { smf_model_createHdr( com->sdata[idx], SMF__COM, concat->sdata[idx], status ); smf_stripsuffix( com->sdata[idx]->file->name, SMF__DIMM_SUFFIX, filename, status ); smf_dataOrder( wf, com->sdata[idx], 1, status ); smf_write_smfData( wf, com->sdata[idx], NULL, filename, NULL, 0, NDF__NOID, MSG__NORM, 0, NULL, NULL, status ); } } if( writegai && gai ) { for( idx=0; (*status==SAI__OK)&&(idx<gai->ndat); idx++ ) { smf_model_createHdr( gai->sdata[idx], SMF__GAI, concat->sdata[idx], status ); smf_stripsuffix( gai->sdata[idx]->file->name, SMF__DIMM_SUFFIX, filename, status ); smf_dataOrder( wf, gai->sdata[idx], 1, status ); smf_write_smfData( wf, gai->sdata[idx], NULL, filename, NULL, 0, NDF__NOID, MSG__NORM, 0, NULL, NULL, status ); } } /* Close com and gai */ if( com ) smf_close_related( wf, &com, status ); if( gai ) smf_close_related( wf, &gai, status ); } /* Report statistics (currently need a smfArray for that) */ if (*status == SAI__OK) { size_t last_qcount[SMF__NQBITS]; size_t last_nmap = 0; smf_qualstats_report( wf, MSG__VERB, SMF__QFAM_TSERIES, 1, concat, last_qcount, &last_nmap, 1, NULL, NULL, status ); } /* Clean up for contchunk loop */ if( keymap ) keymap = astAnnul( keymap ); } /* Export concatenated/cleaned data for each subarray to NDF file */ for( idx=0; (*status==SAI__OK)&&idx<concat->ndat; idx++ ) { odata = concat->sdata[idx]; /* Complete the history information in the output NDF so that it includes group parameters accessed since the default history information was written to the NDF (in smf_open_and_flatfield). */ smf_puthistory( odata, "SMURF:SC2CLEAN", status ); /* Ensure ICD data order */ smf_dataOrder( wf, odata, 1, status ); if( odata->file && odata->file->name ) { smf_write_smfData( wf, odata, NULL, NULL, ogrp, gcount, NDF__NOID, MSG__VERB, 0, NULL, NULL, status ); } else { *status = SAI__ERROR; errRep( FUNC_NAME, "Unable to determine file name for concatenated data.", status ); } /* Increment the group index counter */ gcount++; } /* Close the smfArray */ smf_close_related( wf, &concat, status ); } /* Write out the list of output NDF names, annulling the error if a null parameter value is supplied. */ if( *status == SAI__OK && ogrp ) { grpList( "OUTFILES", 0, 0, NULL, ogrp, status ); if( *status == PAR__NULL ) errAnnul( status ); } CLEANUP: /* Tidy up after ourselves: release the resources used by the grp routines */ if( darks ) smf_close_related( wf, &darks, status ); if( flatramps ) smf_close_related( wf, &flatramps, status ); if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status ); if( bbms ) smf_close_related( wf, &bbms, status ); if( igrp ) grpDelet( &igrp, status); if( ogrp ) grpDelet( &ogrp, status); if( basegrp ) grpDelet( &basegrp, status ); if( igroup ) smf_close_smfGroup( &igroup, status ); fftw_cleanup(); ndfEnd( status ); }
void smurf_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_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_calcmodel_noi( ThrWorkForce *wf, smfDIMMData *dat, int chunk, AstKeyMap *keymap, smfArray **allmodel, int flags, int *status) { /* Local Variables */ dim_t bolostep; /* Number of bolos per thread */ dim_t boxsize; /* No. of time slices in each noise box */ smfData *box = NULL; /* SmfData holding one box of input data */ size_t bstride; /* bolometer stride */ int calcfirst=0; /* Were bolo noises already measured? */ int dclimcorr; /* Min number of correlated steps */ int dcmaxsteps; /* Maximum allowed number of dc jumps */ dim_t dcfitbox; /* Width of box for DC step detection */ double dcthresh; /* Threshold for DC step detection */ dim_t dcsmooth; /* Width of median filter in DC step detection*/ double *din; /* Pointer to next input value */ double *dout; /* Pointer to next output value */ int fillgaps; /* If set perform gap filling */ dim_t i; /* Loop counter */ dim_t ibolo; /* Bolometer index */ int ibox; /* Index of current noise box */ dim_t itime; /* Time slice index */ dim_t idx=0; /* Index within subgroup */ JCMTState *instate=NULL; /* Pointer to input JCMTState */ int iw; /* Thread index */ dim_t j; /* Loop counter */ AstKeyMap *kmap=NULL; /* Local keymap */ size_t mbstride; /* model bolometer stride */ dim_t mntslice; /* Number of model time slices */ size_t mtstride; /* model time slice stride */ smfArray *model=NULL; /* Pointer to model at chunk */ double *model_data=NULL; /* Pointer to DATA component of model */ dim_t nbolo; /* Number of bolometers */ int nbox = 0; /* Number of noise boxes */ size_t nchisq; /* Number of data points in chisq calc */ dim_t nelbox; /* Number of data points in a noise box */ dim_t ndata; /* Total number of data points */ size_t nflag; /* Number of new flags */ int nleft; /* Number of samples not in a noise box */ dim_t ntslice; /* Number of time slices */ int nw; /* Number of worker threads */ size_t pend; /* Last non-PAD sample */ size_t pstart; /* First non-PAD sample */ smf_qual_t *qin; /* Pointer to next input quality value */ smf_qual_t *qout; /* Pointer to next output quality value */ smfArray *qua=NULL; /* Pointer to RES at chunk */ smf_qual_t *qua_data=NULL; /* Pointer to RES at chunk */ smfArray *res=NULL; /* Pointer to RES at chunk */ double *res_data=NULL; /* Pointer to DATA component of res */ dim_t spikebox=0; /* Box size for spike detection */ double spikethresh=0; /* Threshold for spike detection */ size_t tend; /* Last input sample to copy */ size_t tstart; /* First input sample to copy */ size_t tstride; /* time slice stride */ double *var=NULL; /* Sample variance */ size_t xbstride; /* Box bolometer stride */ int zeropad; /* Pad with zeros? */ /* Main routine */ if (*status != SAI__OK) return; /* Obtain pointer to sub-keymap containing NOI parameters */ astMapGet0A( keymap, "NOI", &kmap ); /* Assert bolo-ordered data */ smf_model_dataOrder( dat, allmodel, chunk, SMF__RES|SMF__QUA, 0, status ); /* Obtain pointers to relevant smfArrays for this chunk */ res = dat->res[chunk]; qua = dat->qua[chunk]; model = allmodel[chunk]; /* Obtain parameters for NOI */ /* Data-cleaning parameters */ smf_get_cleanpar( kmap, res->sdata[0], NULL, &dcfitbox, &dcmaxsteps, &dcthresh, &dcsmooth, &dclimcorr, NULL, &fillgaps, &zeropad, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &spikethresh, &spikebox, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, status ); /* Did we already calculate the noise on each detector? */ astMapGet0I( kmap, "CALCFIRST", &calcfirst ); /* Initialize chisquared */ dat->chisquared[chunk] = 0; nchisq = 0; /* Loop over index in subgrp (subarray) */ for( idx=0; idx<res->ndat; idx++ ) { /* Get pointers to DATA components */ res_data = (res->sdata[idx]->pntr)[0]; model_data = (model->sdata[idx]->pntr)[0]; qua_data = (qua->sdata[idx]->pntr)[0]; if( (res_data == NULL) || (model_data == NULL) || (qua_data == NULL) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": Null data in inputs", status); } else { /* Get the raw data dimensions */ smf_get_dims( res->sdata[idx], NULL, NULL, &nbolo, &ntslice, &ndata, &bstride, &tstride, status ); /* NOI model dimensions */ smf_get_dims( model->sdata[idx], NULL, NULL, NULL, &mntslice, NULL, &mbstride, &mtstride, status ); /* Only estimate the white noise level once at the beginning - the reason for this is to make measurements of the convergence easier. We either do it prior to the start of iterations (in which case the relative weights will be influeced by low-frequency noise, this is initialized in smf_model_create), or else we calculate the noise after the first iteration. */ if( (flags & SMF__DIMM_FIRSTITER) && (!calcfirst) ) { /* There are two forms for the NOI model: one constant noise value for each bolometer, or "ntslice" noise values for each bolometer. Handle the first case now. */ if( mntslice == 1 ) { var = astMalloc( nbolo*sizeof(*var) ); if (var) { /* Measure the noise from power spectra */ smf_bolonoise( wf, res->sdata[idx], 0, 0.5, SMF__F_WHITELO, SMF__F_WHITEHI, 0, zeropad ? SMF__MAXAPLEN : SMF__BADSZT, var, NULL, NULL, status ); for( i=0; i<nbolo; i++ ) if( !(qua_data[i*bstride]&SMF__Q_BADB) ) { /* Loop over time and store the variance for each sample */ for( j=0; j<mntslice; j++ ) { model_data[i*mbstride+(j%mntslice)*mtstride] = var[i]; } } var = astFree( var ); } /* If the NOI model is of the second form, the noise is estimated in boxes of samples lasting "NOI.BOX_SIZE" seconds, and then the noise level in the box is assigned to all samples in the box. */ } else if( mntslice == ntslice ) { /* If not already done, get NOI.BOX_SIZE and convert from seconds to samples. */ if( idx == 0 ) { boxsize = 0; smf_get_nsamp( kmap, "BOX_SIZE", res->sdata[0], &boxsize, status ); msgOutf( "", FUNC_NAME ": Calculating a NOI variance for each " "box of %d samples.", status, (int) boxsize ); /* Find the indices of the first and last non-PAD sample. */ smf_get_goodrange( qua_data, ntslice, tstride, SMF__Q_PAD, &pstart, &pend, status ); /* How many whole boxes fit into this range? */ nbox = ( pend - pstart + 1 ) / boxsize; if( nbox == 0 ) nbox = 1; /* How many samples would be left over at the end if we used this many boxes? */ nleft = ( pend - pstart + 1 ) - nbox*boxsize; /* Increase "boxsize" to reduce this number as far as possible. Any samples that are left over after this increase of boxsize will not be used when calculating the noise levels in each bolometer. */ boxsize += nleft/nbox; /* Create a smfData to hold one box-worth of input data. We do not need to copy jcmtstate information. */ if( res->sdata[idx]->hdr ) { instate = res->sdata[idx]->hdr->allState; res->sdata[idx]->hdr->allState = NULL; } box = smf_deepcopy_smfData( res->sdata[idx], 0, SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY, 0, 0, status ); if( instate ) res->sdata[idx]->hdr->allState = instate; /* Set the length of the time axis to the box size plus padding, and create empty data and quality arrays for it. */ if( *status == SAI__OK ) { box->dims[ box->isTordered?2:0 ] = boxsize + pstart + (ntslice - pend - 1); smf_get_dims( box, NULL, NULL, NULL, NULL, &nelbox, &xbstride, NULL, status ); box->pntr[0] = astMalloc( sizeof( double )*nelbox ); box->qual = astMalloc( sizeof( smf_qual_t )*nelbox ); /* For every bolometer, flag the start and end of the quality array as padding, and store zeros in the data array. */ for( ibolo = 0; ibolo < nbolo; ibolo++ ) { dout = ((double *) box->pntr[0]) + xbstride*ibolo; qout = box->qual + xbstride*ibolo; for( itime = 0; itime < pstart; itime++ ) { *(qout++) = SMF__Q_PAD; *(dout++) = 0.0; } dout = ((double *) box->pntr[0]) + xbstride*ibolo + pstart + boxsize;; qout = box->qual + xbstride*ibolo + pstart + boxsize; for( itime = pend + 1; itime < ntslice; itime++ ) { *(qout++) = SMF__Q_PAD; *(dout++) = 0.0; } } } } /* Work space to hold the variance for each bolometer in a box */ var = astMalloc( nbolo*sizeof(*var) ); if( *status == SAI__OK ) { /* Index of the first time slice within the input smfData that is included in the first box. */ tstart = pstart; /* Loop round each noise box */ for( ibox = 0; ibox < nbox; ibox++ ) { /* Copy the data and quality values for this box from the input smfData into "box", leaving room for padding at both ends of box. Note, data is bolo-ordered so we can assume that "tstride" is 1. */ din = ((double *)(res->sdata[idx]->pntr[0])) + tstart; dout = ((double *)(box->pntr[0])) + pstart; qin = qua_data + tstart; qout = box->qual + pstart; for( ibolo = 0; ibolo < nbolo; ibolo++ ) { memcpy( dout, din, boxsize*sizeof( *din ) ); memcpy( qout, qin, boxsize*sizeof( *qin ) ); din += bstride; dout += xbstride; qin += bstride; qout += xbstride; } /* Measure the noise from power spectra in the box. */ smf_bolonoise( wf, box, 0, 0.5, SMF__F_WHITELO, SMF__F_WHITEHI, 0, zeropad ? SMF__MAXAPLEN : SMF__BADSZT, var, NULL, NULL, status ); /* Loop over time and store the variance for each sample in the NOI model. On the last box, pick up any left over time slices. */ if( ibox < nbox - 1 ) { tend = tstart + boxsize - 1; } else { tend = pend; } for( ibolo = 0; ibolo < nbolo; ibolo++ ) { if( !( qua_data[ ibolo*bstride ] & SMF__Q_BADB ) ) { dout = model_data + ibolo*bstride + tstart; for( itime = tstart; itime <= tend; itime++ ) { *(dout++) = var[ ibolo ]; } } } /* Update the index of the first time slice within the input smfData that is included in the next box. */ tstart += boxsize; } var = astFree( var ); } /* Report an error if the number of samples for each bolometer in the NOI model is not 1 or "ntslice". */ } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", FUNC_NAME ": NOI model has %d samples - should be " "%d or 1.", status, (int) mntslice, (int) ntslice); } } if( kmap ) { /* Flag spikes in the residual after first iteration */ if( spikethresh && !(flags&SMF__DIMM_FIRSTITER) ) { /* Now re-flag */ smf_flag_spikes( wf, res->sdata[idx], SMF__Q_MOD, spikethresh, spikebox, &nflag, status ); msgOutiff(MSG__VERB," ", " flagged %zu new %lf-sig spikes", status, nflag, spikethresh ); } if( dcthresh && dcfitbox ) { smf_fix_steps( wf, res->sdata[idx], dcthresh, dcsmooth, dcfitbox, dcmaxsteps, dclimcorr, 1, &nflag, NULL, NULL, status ); msgOutiff(MSG__VERB, ""," detected %zu bolos with DC steps\n", status, nflag); } } /* Now calculate contribution to chi^2. This bit takes along time if there is a lot of data so share the work out amongst the available worker threads. How many threads do we get to play with */ nw = wf ? wf->nworker : 1; /* Find how many bolometers to process in each worker thread. */ bolostep = nbolo/nw; if( bolostep == 0 ) bolostep = 1; /* Allocate job data for threads, and store the range of bolos to be processed by each one. Ensure that the last thread picks up any left-over bolos. */ SmfCalcModelNoiData *job_data = astCalloc( nw, sizeof(*job_data) ); if( *status == SAI__OK ) { SmfCalcModelNoiData *pdata; for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->b1 = iw*bolostep; if( iw < nw - 1 ) { pdata->b2 = pdata->b1 + bolostep - 1; } else { pdata->b2 = nbolo - 1 ; } /* Store other values common to all jobs. */ pdata->ntslice = ntslice; pdata->mntslice = mntslice; pdata->qua_data = qua_data; pdata->model_data = model_data; pdata->res_data = res_data; pdata->bstride = bstride; pdata->tstride = tstride; pdata->mbstride = mbstride; pdata->mtstride = mtstride; /* Submit the job to the workforce. */ thrAddJob( wf, 0, pdata, smf1_calcmodel_noi, 0, NULL, status ); } /* Wait for all jobs to complete. */ thrWait( wf, status ); /* Accumulate the results from all the worker threads. */ for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; dat->chisquared[chunk] += pdata->chisquared; nchisq += pdata->nchisq; } /* Free the job data. */ job_data = astFree( job_data ); } } } /* Free resources */ if( box ) { box->pntr[0] = astFree( box->pntr[0] ); box->qual = astFree( box->qual ); smf_close_file( &box, status ); } /* Normalize chisquared for this chunk */ if( (*status == SAI__OK) && (nchisq >0) ) { dat->chisquared[chunk] /= (double) nchisq; } /* Clean Up */ if( kmap ) kmap = astAnnul( kmap ); }
/* Main entry point. */ void smf_calcmodel_smo( ThrWorkForce *wf, smfDIMMData *dat, int chunk, AstKeyMap *keymap, smfArray **allmodel, int flags __attribute__((unused)), int *status) { /* Local Variables */ size_t bstride; /* bolo stride */ dim_t boxcar = 0; /* size of boxcar smooth window */ smf_filt_t filter_type; /* The type of smoothing to perform */ size_t i; /* Loop counter */ dim_t idx=0; /* Index within subgroup */ int iworker; /* Owkrer index */ smfCalcmodelSmoJobData *job_data=NULL; /* Pointer to all job data structures */ AstKeyMap *kmap=NULL; /* Pointer to PLN-specific keys */ smfArray *model=NULL; /* Pointer to model at chunk */ double *model_data=NULL; /* Pointer to DATA component of model */ double *model_data_copy=NULL; /* Copy of model_data for one bolo */ dim_t nbolo=0; /* Number of bolometers */ dim_t ndata=0; /* Total number of data points */ int notfirst=0; /* flag for delaying until after 1st iter */ dim_t ntslice=0; /* Number of time slices */ int nworker; /* No. of worker threads in supplied Workforce */ smfCalcmodelSmoJobData *pdata=NULL; /* Pointer to current data structure */ smfArray *qua=NULL; /* Pointer to QUA at chunk */ smf_qual_t *qua_data=NULL; /* Pointer to quality data */ smfArray *res=NULL; /* Pointer to RES at chunk */ double *res_data=NULL; /* Pointer to DATA component of res */ int step; /* Number of bolometers per thread */ size_t tstride; /* Time slice stride in data array */ const char * typestr = NULL; /* smo.type value */ /* Main routine */ if (*status != SAI__OK) return; /* Obtain pointers to relevant smfArrays for this chunk */ res = dat->res[chunk]; qua = dat->qua[chunk]; /* Obtain pointer to sub-keymap containing PLN parameters. Something will always be available.*/ astMapGet0A( keymap, "SMO", &kmap ); /* Are we skipping the first iteration? */ astMapGet0I(kmap, "NOTFIRST", ¬first); if( notfirst && (flags & SMF__DIMM_FIRSTITER) ) { msgOutif( MSG__VERB, "", FUNC_NAME ": skipping SMO this iteration", status ); return; } /* Get the boxcar size */ if( kmap ) smf_get_nsamp( kmap, "BOXCAR", res->sdata[0], &boxcar, status ); /* Get the type of smoothing filter to use. Anthing that is not "MEDIAN" is mean */ filter_type = SMF__FILT_MEAN; if (astMapGet0C( kmap, "TYPE", &typestr ) ) { if (strncasecmp( typestr, "MED", 3 ) == 0 ) { filter_type = SMF__FILT_MEDIAN; } } /* Assert bolo-ordered data */ smf_model_dataOrder( wf, dat, allmodel, chunk, SMF__RES|SMF__QUA, 0, status ); smf_get_dims( res->sdata[0], NULL, NULL, NULL, &ntslice, &ndata, NULL, NULL, status); model = allmodel[chunk]; msgOutiff(MSG__VERB, "", " Calculating smoothed model using boxcar of width %" DIM_T_FMT " time slices", status, boxcar); /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); /* Loop over index in subgrp (subarray) and put the previous iteration of the filtered component back into the residual before calculating and removing the new filtered component */ for( idx=0; (*status==SAI__OK)&&(idx<res->ndat); idx++ ) { /* Obtain dimensions of the data */ smf_get_dims( res->sdata[idx], NULL, NULL, &nbolo, &ntslice, &ndata, &bstride, &tstride, status); /* Get pointers to data/quality/model */ res_data = (res->sdata[idx]->pntr)[0]; qua_data = (qua->sdata[idx]->pntr)[0]; model_data = (model->sdata[idx]->pntr)[0]; if( (res_data == NULL) || (model_data == NULL) || (qua_data == NULL) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": Null data in inputs", status); } else { /* Uncomment to aid debugging */ /* smf_write_smfData( res->sdata[idx], NULL, qua_data, "res_in", NULL, 0, 0, MSG__VERB, 0, status ); */ if( *status == SAI__OK ) { /* Place last iteration back into residual if this is a smoothable section of the time series */ for (i=0; i< ndata; i++) { if ( !(qua_data[i]&SMF__Q_FIT) && res_data[i] != VAL__BADD && model_data[i] != VAL__BADD ) { res_data[i] += model_data[i]; } } } /* Uncomment to aid debugging */ /* smf_write_smfData( model->sdata[idx], NULL, qua_data, "model_b4", NULL, 0, 0, MSG__VERB, 0, status ); smf_write_smfData( res->sdata[idx], NULL, qua_data, "res_b4", NULL, 0, 0, MSG__VERB, 0, status ); */ /* Determine which bolometers are to be processed by which threads. */ step = nbolo/nworker; if( step < 1 ) step = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->b1 = iworker*step; pdata->b2 = pdata->b1 + step - 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 apply the smoothing. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->boxcar = boxcar; pdata->bstride = bstride; pdata->bstride = bstride; pdata->filter_type = filter_type; pdata->model_data = model_data; pdata->nbolo = nbolo; pdata->nbolo = nbolo; pdata->ntslice = ntslice; pdata->ntslice = ntslice; pdata->qua_data = qua_data; pdata->qua_data = qua_data; pdata->res_data = res_data; pdata->res_data = res_data; pdata->tstride = tstride; pdata->tstride = tstride; thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calcmodel_smo_job, 0, NULL, status ); } thrWait( wf, status ); /* Uncomment to aid debugging */ /* smf_write_smfData( res->sdata[idx], NULL, qua_data, "res_af", NULL, 0, 0, MSG__VERB, 0, status ); smf_write_smfData( model->sdata[idx], NULL, qua_data, "model_af", NULL, 0, 0, MSG__VERB, 0, status ); */ } } /* Free work space (astFree returns without action if a NULL pointer is supplied). */ model_data_copy = astFree( model_data_copy ); job_data = astFree( job_data ); /* Annul AST Object pointers (astAnnul reports an error if a NULL pointer is supplied). */ if( kmap ) kmap = astAnnul( kmap ); }
void smurf_extinction( int * status ) { /* Local Variables */ smfArray *bbms = NULL; /* Bad bolometer masks */ smfArray *darks = NULL; /* Dark data */ AstKeyMap *extpars = NULL; /* Tau relation keymap */ Grp *fgrp = NULL; /* Filtered group, no darks */ smfArray *flatramps = NULL;/* Flatfield ramps */ int has_been_sky_removed = 0;/* Data are sky-removed */ AstKeyMap *heateffmap = NULL; /* Heater efficiency data */ size_t i; /* Loop counter */ Grp *igrp = NULL; /* Input group */ AstKeyMap *keymap=NULL; /* Keymap for storing parameters */ smf_tausrc tausrc; /* enum value of optical depth source */ smf_extmeth extmeth; /* Extinction correction method */ char tausource[LEN__METHOD]; /* String for optical depth source */ char method[LEN__METHOD]; /* String for extinction airmass method */ smfData *odata = NULL; /* Output data struct */ Grp *ogrp = NULL; /* Output group */ size_t outsize; /* Total number of NDF names in the output group */ size_t size; /* Number of files in input group */ double tau = 0.0; /* Zenith tau at this wavelength */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ if (*status != SAI__OK) return; /* Main routine */ ndfBegin(); /* Find the number of cores/processors available and create a pool of threads of the same size. */ wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status ); /* Read the input file */ kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status ); /* Filter out darks */ smf_find_science( igrp, &fgrp, 0, NULL, NULL, 1, 1, SMF__NULL, &darks, &flatramps, &heateffmap, NULL, status ); /* input group is now the filtered group so we can use that and free the old input group */ size = grpGrpsz( fgrp, status ); grpDelet( &igrp, status); igrp = fgrp; fgrp = NULL; if (size > 0) { /* Get output file(s) */ kpg1Wgndf( "OUT", igrp, size, size, "More output files required...", &ogrp, &outsize, status ); } else { msgOutif(MSG__NORM, " ","All supplied input frames were DARK," " nothing to extinction correct", status ); } /* Get group of pixel masks and read them into a smfArray */ smf_request_mask( "BBM", &bbms, status ); /* Read the tau relations from config file or group. We do not allow sub instrument overloading because these are all values based on filter name. */ keymap = kpg1Config( "TAUREL", "$SMURF_DIR/smurf_extinction.def", NULL, 1, status ); /* and we need to use the EXT entry */ astMapGet0A( keymap, "EXT", &extpars ); keymap = astAnnul( keymap ); /* Get tau source */ parChoic( "TAUSRC", "Auto", "Auto,CSOtau,CSOFit, Filtertau, WVMraw", 1, tausource, sizeof(tausource), status); /* Decide how the correction is to be applied - convert to flag */ parChoic( "METHOD", "ADAPTIVE", "Adaptive,Quick,Full,", 1, method, sizeof(method), status); /* Place parameters into a keymap and extract values */ if( *status == SAI__OK ) { keymap = astKeyMap( " " ); if( astOK ) { astMapPut0C( keymap, "TAUSRC", tausource, NULL ); astMapPut0C( keymap, "TAUMETHOD", method, NULL ); smf_get_extpar( keymap, &tausrc, &extmeth, NULL, status ); } } for (i=1; i<=size && ( *status == SAI__OK ); i++) { /* Flatfield - if necessary */ smf_open_and_flatfield( igrp, ogrp, i, darks, flatramps, heateffmap, &odata, status ); if (*status != SAI__OK) { /* Error flatfielding: tell the user which file it was */ msgSeti("I",i); errRep(TASK_NAME, "Unable to open the ^I th file", status); } /* Mask out bad pixels - mask data array not quality array */ smf_apply_mask( odata, bbms, SMF__BBM_DATA, 0, status ); /* Now check that the data are sky-subtracted */ if ( !smf_history_check( odata, "smf_subtract_plane", status ) ) { /* Should we override remsky check? */ parGet0l("HASSKYREM", &has_been_sky_removed, status); if ( !has_been_sky_removed && *status == SAI__OK ) { *status = SAI__ERROR; msgSeti("I",i); errRep("", "Input data from file ^I are not sky-subtracted", status); } } /* If status is OK, make decisions on source keywords the first time through. */ if ( *status == SAI__OK && i == 1 ) { if (tausrc == SMF__TAUSRC_CSOTAU || tausrc == SMF__TAUSRC_AUTO || tausrc == SMF__TAUSRC_TAU) { double deftau; const char * param = NULL; smfHead *ohdr = odata->hdr; /* get default CSO tau -- this could be calculated from CSO fits */ deftau = smf_calc_meantau( ohdr, status ); /* Now ask for desired CSO tau */ if ( tausrc == SMF__TAUSRC_CSOTAU || tausrc == SMF__TAUSRC_AUTO) { param = "CSOTAU"; } else if (tausrc == SMF__TAUSRC_TAU) { param = "FILTERTAU"; deftau = smf_cso2filt_tau( ohdr, deftau, extpars, status ); } parGdr0d( param, deftau, 0.0,1.0, 1, &tau, status ); } else if ( tausrc == SMF__TAUSRC_CSOFIT || tausrc == SMF__TAUSRC_WVMRAW ) { /* Defer a message until after extinction correction */ } else { *status = SAI__ERROR; errRep("", "Unsupported opacity source. Possible programming error.", status); } } /* Apply extinction correction - note that a check is made to determine whether the data have already been extinction corrected */ smf_correct_extinction( wf, odata, &tausrc, extmeth, extpars, tau, NULL, NULL, status ); if ( tausrc == SMF__TAUSRC_WVMRAW ) { msgOutif(MSG__VERB," ", "Used Raw WVM data for extinction correction", status); } else if ( tausrc == SMF__TAUSRC_CSOFIT ) { msgOutif(MSG__VERB," ", "Used fit to CSO data for extinction correction", status); } else if ( tausrc == SMF__TAUSRC_CSOTAU ) { msgOutif(MSG__VERB," ", "Used an explicit CSO tau value for extinction correction", status); } else if ( tausrc == SMF__TAUSRC_TAU ) { msgOutif(MSG__VERB," ", "Used an explicit filter tau value for extinction correction", status); } else { if (*status == SAI__OK) { const char * taustr = smf_tausrc_str( tausrc, status ); *status = SAI__ERROR; errRepf( "", "Unexpected opacity source used for extinction correction of %s." " Possible programming error.", status, taustr ); } } /* Set character labels */ smf_set_clabels( "Extinction corrected",NULL, NULL, odata->hdr, status); smf_write_clabels( odata, status ); /* Free resources for output data */ smf_close_file( &odata, status ); } /* Write out the list of output NDF names, annulling the error if a null parameter value is supplied. */ if( *status == SAI__OK && ogrp ) { grpList( "OUTFILES", 0, 0, NULL, ogrp, status ); if( *status == PAR__NULL ) errAnnul( status ); } /* Tidy up after ourselves: release the resources used by the grp routines */ if (darks) smf_close_related( &darks, status ); if (bbms) smf_close_related( &bbms, status ); if( flatramps ) smf_close_related( &flatramps, status ); if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status ); grpDelet( &igrp, status); grpDelet( &ogrp, status); if( keymap ) keymap = astAnnul( keymap ); if (extpars) extpars = astAnnul( extpars ); ndfEnd( status ); }