/* 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_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; }
int *smf_jsatiles_region( AstRegion *region, smfJSATiling *skytiling, int *ntile, int *status ){ /* Local Variables */ AstFrameSet *fs; AstKeyMap *km; AstRegion *region2; AstRegion *space_region; AstRegion *tregion; AstSkyFrame *skyframe; char text[ 200 ]; const char *key; double *mesh = NULL; double *xmesh; double *ymesh; int *tiles = NULL; int axes[ 2 ]; int i; int ineb; int itile2; int itile; int ix; int iy; int key_index; int lbnd[ 2 ]; int mapsize; int npoint; int old_sv; int overlap; int ubnd[ 2 ]; int value; int xoff[ 4 ] = { -1, 0, 1, 0 }; int xt; int yoff[ 4 ] = { 0, 1, 0, -1 }; int yt; /* Initialise */ *ntile = 0; /* Check inherited status */ if( *status != SAI__OK ) return tiles; /* Start an AST context so that all AST objects created in this function are annulled automatically. */ astBegin; /* Identify the celestial axes in the Region. */ atlFindSky( (AstFrame *) region, &skyframe, axes + 1, axes, status ); /* Report an error if no celestial axes were found. */ if( !skyframe && *status == SAI__OK ) { space_region = NULL; *status = SAI__ERROR; errRep( "", "The current WCS Frame in the supplied Region or " "NDF does not include celestial longitude and latitude axes.", status ); /* Otherwise, if the Region itself is 2-dimensional, it does not contain any other axes, so just use it as is. */ } else if( astGetI( region, "Naxes" ) == 2 ) { space_region = astClone( region ); /* Otherwise, create a new Region by picking the celestial axes from the supplied Region. Report an error if a Region cannot be created in this way. */ } else { space_region = astPickAxes( region, 2, axes, NULL ); if( !astIsARegion( space_region ) && *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "The celestial longitude and latitude axes in the " "supplied Region or NDF are not independent of the other " "axes.", status ); } } /* Create a FrameSet describing the whole sky in which each pixel corresponds to a single tile in SMF__JSA_HPX projection. The current Frame is ICRS (RA,Dec) and the base Frame is grid coords in which each grid pixel corresponds to a single tile. */ smf_jsatile( -1, skytiling, 0, SMF__JSA_HPX, NULL, &fs, NULL, lbnd, ubnd, status ); /* Map the Region using the FrameSet obtained above so that the new Region describes offsets in tiles from the lower left tile. If "space_region" is a Polygon, ensure that the SimpVertices attribute is set so that the simplify method will take non-linearities into account (such as the region being split by the RA=12h meridian). */ astInvert( fs ); fs = astConvert( space_region, fs, "SKY" ); if( !fs && *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "Cannot convert the supplied Region to ICRS.", status ); goto L999; } old_sv = -999; if( astIsAPolygon( space_region ) ){ if( astTest( space_region, "SimpVertices" ) ) { old_sv = astGetI( space_region, "SimpVertices" ); } astSetI( space_region, "SimpVertices", 0 ); } region2 = astMapRegion( space_region, fs, fs ); if( astIsAPolygon( space_region ) ){ if( old_sv == -999 ) { astClear( space_region, "SimpVertices" ); } else { astSetI( space_region, "SimpVertices", old_sv ); } } /* Get a mesh of all-sky "grid" positions (actually tile X and Y indices) covering the region. Since the mesh positions are limited in number and placed arbitrarily within the Region, the mesh will identify some, but potentially not all, of the tiles that overlap the Region. */ astGetRegionMesh( region2, 0, 0, 2, &npoint, NULL ); mesh = astMalloc( 2*npoint*sizeof( *mesh ) ); astGetRegionMesh( region2, 0, npoint, 2, &npoint, mesh ); /* Find the index of the tile containing each mesh position, and store them in a KeyMap using the tile index as the key and "1" (indicating the tile overlaps the region) as the value. The KeyMap is sorted by age of entry. Neighbouring tiles will be added to this KeyMap later. If an entry has a value of zero, it means the tile does not overlap the supplied Region. If the value is positive, it means the tile does overlap the supplied Region. If the value is negative, it means the tile has not yet been tested to see if it overlaps the supplied Region. */ km = astKeyMap( "SortBy=KeyAgeDown" ); xmesh = mesh; ymesh = mesh + npoint; for( i = 0; i < npoint && *status == SAI__OK; i++ ) { ix = (int)( *(xmesh++) + 0.5 ) - 1; iy = (int)( *(ymesh++) + 0.5 ) - 1; itile = smf_jsatilexy2i( ix, iy, skytiling, status ); if (itile != VAL__BADI) { sprintf( text, "%d", itile ); astMapPut0I( km, text, 1, NULL ); } } /* Starting with the oldest entry in the KeyMap, loop round checking all entries, in the order they were added, until all have been checked. Checking an entry may cause further entries to be added to the end of the KeyMap. */ key_index = 0; mapsize = astMapSize( km ); while( key_index < mapsize && *status == SAI__OK ) { key = astMapKey( km, key_index++ ); /* Convert the key string to an integer tile index. */ itile = atoi( key ); /* Get the integer value associated with the tile. */ astMapGet0I( km, key, &value ); /* If the tile associated with the current KeyMap entry has not yet been tested for overlap with the requested Region (as shown by the entry value being -1), test it now. */ if( value == -1 ) { /* Get a Region covering the tile. */ smf_jsatile( itile, skytiling, 0, SMF__JSA_HPX, NULL, NULL, &tregion, lbnd, ubnd, status ); /* See if this Region overlaps the user supplied region. Set the value of the KeyMap entry to +1 or 0 accordingly. */ overlap = astOverlap( tregion, space_region ); if( overlap == 0 ) { if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "Cannot align supplied Region with the sky " "tile coordinate system (programming error).", status ); } } else if( overlap == 1 || overlap == 6 ) { value = 0; } else { value = 1; } astMapPut0I( km, key, value, NULL ); } /* Skip the current KeyMap entry if the corresponding tile does not overlap the requested Region (as shown by the entry value being zero). */ if( value == 1 ) { /* The current tile overlaps the supplied Region, so add the tile index to the returned list of tile indices. */ tiles = astGrow( tiles, ++(*ntile), sizeof( *tiles ) ); if( *status == SAI__OK ) { tiles[ *ntile - 1 ] = itile; /* Add the adjoining tiles to the end of the KeyMap so that they will be tested in their turn, giving them a value of -1 to indicate that they have not yet been tested to see if they overlap the supplied Region. Ignore adjoining tiles that are already in the keyMap. */ smf_jsatilei2xy( itile, skytiling, &xt, &yt, NULL, status ); for( ineb = 0; ineb < 4; ineb++ ) { itile2 = smf_jsatilexy2i( xt + xoff[ ineb ], yt + yoff[ ineb ], skytiling, status ); if( itile2 != VAL__BADI ) { sprintf( text, "%d", itile2 ); if( !astMapHasKey( km, text ) ) { astMapPut0I( km, text, -1, NULL ); mapsize++; } } } } } } /* Arrive here if an error occurs. */ L999:; /* Free resources. */ mesh = astFree( mesh ); if( *status != SAI__OK ) { tiles = astFree( tiles ); *ntile = 0; } astEnd; return tiles; }
void smf_flat_write( smf_flatmeth flatmeth, const char * flatname, double refres, const smfData * bolval, const smfData * powref, const smfData * bolref, const smfData * polyfit, const Grp * prvgrp, int * status ) { size_t colsize; /* number of columns */ double *dbuf = NULL; /* input double buffer for mean data */ double *dvar = NULL; /* input double buffer for variance of data */ char fitsrec[SC2STORE__MAXFITS*SZFITSCARD+1]; /* Store for FITS records */ int *ibuf = NULL; /* int buffer for mean data */ int indf = NDF__NOID; /* NDF identifier for output file */ size_t ncards; /* number of fits cards */ size_t numbols; /* number of bolometers */ double *outvar = NULL; /* buffer for variance of data */ int place = NDF__NOPL; /* Dummy placeholder for NDF */ size_t rowsize; /* number of rows */ JCMTState *state = NULL; /* State for this flatfield */ sc2ast_subarray_t subnum; /* subarray number */ AstFrameSet *result, *spacefset; AstLutMap *heatmap; AstFrame *heatfrm; int *dksquid; /* pointer to dummy dark SQUID data */ size_t j; /* loop counter */ int jig_vert[1][2]; /* dummy jiggle vertices */ double jig_path[1][2]; /* dummy jiggle path */ size_t nframes = 0; /* Number of frames in bolval */ int npath = 0; /* size of jiggle path */ int nvert = 0; /* number of jiggle vertices */ char *xmlfile = NULL; /* dummy xmlfile name */ if (*status != SAI__OK) return; if (!bolval->da) { *status = SAI__ERROR; errRep( "", "No flatfield solution provided for writing", status ); return; } if (!bolval->da->heatval) { *status = SAI__ERROR; errRep( "", "Must provide heater values in DA struct to smf_flat_write" " (possible programming error)", status ); return; } /* note that colsize is the number of rows and rowsize is the number of columns */ colsize = (bolval->dims)[SC2STORE__ROW_INDEX]; rowsize = (bolval->dims)[SC2STORE__COL_INDEX]; numbols = colsize * rowsize; nframes = (bolval->dims)[2]; /* Make sure we have a FLAT header that reflects this file as the flatfield solution */ smf_fits_updateS( bolval->hdr, "FLAT", flatname, "Name of flat-field file", status ); /* Create a FITS header for DA */ smf_fits_export2DA( bolval->hdr->fitshdr, &ncards, fitsrec, status ); /* Copy the data as integers so it can be written to data file. To prevent overflow in the variance we store that as doubles */ ibuf = astMalloc( (numbols * nframes)*sizeof(*ibuf) ); outvar = astMalloc( (numbols * nframes)*sizeof(*outvar) ); dbuf = (bolval->pntr)[0]; dvar = (bolval->pntr)[1]; if (*status == SAI__OK) { for (j = 0; j < (nframes * numbols); j++) { /* These started off as integers so the mean value must fit in an integer */ if ( dbuf[j] == VAL__BADD) { ibuf[j] = VAL__BADI; } else { ibuf[j] = (int)dbuf[j]; } /* Same data type so no need to convert bad values */ if (dvar) { outvar[j] = dvar[j]; } else { outvar[j] = VAL__BADD; } } } /* get subarray number */ smf_find_subarray( bolval->hdr, NULL, 0, &subnum, status ); /* Create dummy components for output file */ dksquid = astCalloc ( rowsize* nframes, sizeof(*dksquid) ); jig_vert[0][0] = 0; jig_vert[0][1] = 0; jig_path[0][0] = 0.0; jig_path[0][1] = 0.0; sc2store_setcompflag ( SC2STORE__NONE, status ); sc2store_wrtstream ( flatname, subnum, ncards, fitsrec, colsize, rowsize, nframes, (bolref->dims)[2], refres, 0, smf_flat_methstring( flatmeth, status ), bolval->hdr->allState, NULL, ibuf, dksquid, (bolref->pntr)[0], (powref->pntr)[0], "FLATCAL", NULL, NULL, jig_vert, nvert, jig_path, npath, xmlfile, status ); sc2store_free ( status ); /* To copy in the variance and modify fix up the WCS we need to reopen the file */ ndfOpen( NULL, flatname, "UPDATE", "OLD", &indf, &place, status ); /* make sure that history is not written twice */ ndfHsmod( "SKIP", indf, status ); if (outvar) { void *pntr[3]; int el; ndfStype( "_DOUBLE", indf, "VARIANCE", status ); ndfMap( indf, "VARIANCE", "_DOUBLE", "WRITE", pntr, &el, status ); if (*status == SAI__OK) { memcpy( pntr[0], outvar, sizeof(*outvar)*el ); } } /* For the WCS a time frame is less relevant than heater settings */ astBegin; /* Create frame for focal plane coordinates */ sc2ast_createwcs( subnum, NULL, NULL, NULL, NO_FTS, &spacefset, status ); /* Copy it to make sure we do not mess with the cache */ result = astCopy( spacefset ); /* and switch to BOLO frame which is best for bolometer analysis */ { int frnum = AST__NOFRAME; kpg1Asffr( result, "BOLO", &frnum, status ); if (frnum != AST__NOFRAME) astSetI( result, "CURRENT", frnum ); } /* Create a simple frame for heater settings */ heatfrm = astFrame( 1, "Domain=HEATER,Label(1)=Heater Setting" ); heatmap = astLutMap( nframes, bolval->da->heatval, 1.0, 1.0, " " ); /* Append the heater axis to the spatial frameset */ atlAddWcsAxis( result, (AstMapping *)heatmap, (AstFrame *) heatfrm, NULL, NULL, status ); /* write it to the NDF */ ndfPtwcs( result, indf, status ); /* Write provenance information */ if (prvgrp) { size_t size = grpGrpsz( prvgrp, status ); char prvname[ 2 * PAR__SZNAM + 1]; smf_get_taskname( NULL, prvname, status ); for (j=1; j<=size; j++) { smf_accumulate_prov( NULL, prvgrp, j, indf, prvname, NULL, status ); } } /* Write the polynomial expansion into an extension */ if (polyfit) { char fitfile[GRP__SZNAM+1]; int fndf = NDF__NOID; place = NDF__NOPL; one_strlcpy( fitfile, flatname, sizeof(fitfile), status ); one_strlcat( fitfile, ".MORE.SMURF.FLATFIT", sizeof(fitfile), status ); /* create the file */ smf_write_smfData( polyfit, NULL, fitfile, NULL, 0, NDF__NOID, MSG__VERB, 0, status ); /* Same WCS as the main file */ ndfOpen( NULL, fitfile, "UPDATE", "OLD", &fndf, &place, status ); ndfPtwcs( result, fndf, status ); ndfAnnul( &fndf, status ); } astEnd; ndfAnnul( &indf, status); if (ibuf) ibuf = astFree( ibuf ); if (outvar) outvar = astFree( outvar ); if (dksquid) dksquid = astFree( dksquid ); if (state) state = astFree( state ); }
void smf_addpolanal( AstFrameSet *fset, smfHead *hdr, AstKeyMap *config, int *status ){ /* Local Variables */ AstCmpMap *tmap; AstFrame *cfrm; AstFrame *pfrm; AstFrame *tfrm; AstFrameSet *tfs; AstPermMap *pm; char *polnorth = NULL; const char *cursys; const char *trsys; int aloff; int icurr; int inperm[2]; int outperm[2]; int pol2fp; /* Check inherited status, and also check the supplied angle is not bad. */ if( *status != SAI__OK ) return; /* Begin an AST object context. */ astBegin; /* Get the value of the POLNORTH FITS keyword from the supplied header. The rest only happens if the keyword is found. */ if( astGetFitsS( hdr->fitshdr, "POLNORTH", &polnorth ) ) { /* Normally, we do not allow maps to be made from Q/U time streams that use focal plane Y as the reference direction (because of the problems of sky rotation). Therefore we report an error. However, we do need to make such maps as part of the process of determining the parameters of the Instrumental Polarisation (IP) model. So only report the error if "pol2fp" config parameter is non-zero. */ if( !strcmp( polnorth, "FPLANE" ) ) { astMapGet0I( config, "POL2FP", &pol2fp ); if( pol2fp ) { msgBlank( status ); msgOut( "", "WARNING: The input NDFs hold POL-2 Q/U data specified " "with respect to focal plane Y.",status ); msgOut( "", "Maps should normally be made from POL-2 data specified " "with respect to celestial north.",status ); msgOut( "", "The output map will not contain a POLANAL Frame and " "so will be unusable by POLPACK applications.",status ); msgBlank( status ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "The input NDFs hold POL-2 Q/U data specified with " "respect to focal plane Y.",status ); errRep( "", "Maps can only be made from POL-2 data specified with " "respect to celestial north.",status ); } /* If the ref. direction is celestial north, create a suitable Frame and Mapping and add them into the supplied FrameSet. */ } else { /* Check the current Frame is a SkyFrame. */ cfrm = astGetFrame( fset, AST__CURRENT ); if( astIsASkyFrame( cfrm ) ) { /* Create a POLANAL Frame. */ pfrm = astFrame( 2, "Domain=POLANAL" ); astSet( pfrm, "Title=Polarimetry reference frame" ); astSet( pfrm, "Label(1)=Polarimetry reference direction" ); astSet( pfrm, "Label(2)=" ); /* Create a PermMap that ensures that axis 1 of the POLANAL Frame is parallel to the latitude axis (i.e. north) of the curent Frame (the current Frame axes may have been swapped). */ outperm[ 0 ] = astGetI( cfrm, "LatAxis" ); outperm[ 1 ] = astGetI( cfrm, "LonAxis" ); inperm[ outperm[ 0 ] - 1 ] = 1; inperm[ outperm[ 1 ] - 1 ] = 2; pm = astPermMap( 2, inperm, 2, outperm, NULL, " " ); /* Record the index of the original current Frame. */ icurr = astGetI( fset, "Current" ); /* Determine the system to use. */ if( !strcmp( polnorth, "TRACKING" ) ) { trsys = sc2ast_convert_system( hdr->state->tcs_tr_sys, status ); } else { trsys = polnorth; } /* If the current Frame in the supplied FrameSet has this system. Then we use the above PermMap to connect the POLANAL Frame directly to the current Frame. */ cursys = astGetC( cfrm, "System" ); if( trsys && cursys && !strcmp( cursys, trsys ) ) { astAddFrame( fset, AST__CURRENT, pm, pfrm ); /* Otherwise we need to get a Mapping from the current Frame to the required frame. */ } else { /* Take a copy of the current Frame (in order to pick up epoch, observatory position, etc), and set its System to the required system. */ tfrm = astCopy( cfrm ); astSetC( tfrm, "System", trsys ); /* Get the Mapping from the original current Frame to this modified copy. Ensure alignment happens in absolute coords (alignment in offset coords is always a unit mapping and so no rotation occurs). */ aloff = astGetI( cfrm, "AlignOffset" ); if( aloff ) { astSetI( cfrm, "AlignOffset", 0 ); astSetI( tfrm, "AlignOffset", 0 ); } tfs = astConvert( cfrm, tfrm, "SKY" ); if( aloff ) { astSetI( cfrm, "AlignOffset", 1 ); astSetI( tfrm, "AlignOffset", 1 ); } if( tfs ) { /* Use it, in series with with the above PermMap, to connect the POLANAL frame to the current Frame. */ tmap = astCmpMap( astGetMapping( tfs, AST__BASE, AST__CURRENT ), pm, 1, " " ); astAddFrame( fset, AST__CURRENT, astSimplify( tmap ), pfrm ); /* Report an error if the mapping from current to required system could not be found. */ } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "smf_addpolanal: Could not convert Frame " "from %s to %s (prgramming error).", status, cursys, trsys ); } } /* Re-instate the original current Frame. */ astSetI( fset, "Current", icurr ); /* Report an error if the current Frame is not a SkyFrame. */ } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_addpolanal: The current Frame in the " "supplied FrameSet is not a SkyFrame (prgramming " "error).", status ); } } } /* End the AST object context. */ astEnd; }
void smf_getrefwcs( const char *param, Grp *igrp, AstFrameSet **specwcs, AstFrameSet **spacewcs, int *isjsa, int *status ){ /* Local Variables */ AstFrame *frm = NULL; AstFrameSet *refwcs = NULL; /* The WCS FrameSet from the reference NDF */ AstRegion *circle; char text[ 255 ]; /* Parameter value */ int *tiles; int i; int jsatiles; int lbnd[2]; /* Lower pixel index bounds of mid tile */ int ntile; int perm[ 2 ]; int refndf; /* NDF identifier for the refence NDF */ int ubnd[2]; /* Upper pixel index bounds of mid tile */ size_t code; smfData *data = NULL; /* Structure describing 1st input file */ smfJSATiling skytiling; smf_inst_t inst = SMF__INST_NONE; smf_jsaproj_t proj; /* Specific JSA projection to use */ smf_subinst_t subinst; /* Initialise the returned values. */ *specwcs = NULL; *spacewcs = NULL; *isjsa = 0; /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context. */ astBegin; /* If the JSAILES parameter is TRUE, then we use the JSA all-sky pixel grid regardless of the setting of REF. */ parGet0l( "JSATILES", &jsatiles, status ); if( jsatiles ) { strcpy( text, "JSA" ); *isjsa = 1; /* Otherwise, first get the parameter value as a string. Use subpar to avoid problem caused by interpretion of the text within the parameter system. */ } else { subParFindpar( param, &code, status ); subParGetname( code, text, sizeof(text), status ); } /* If no value was supplied, annul the error and do nothing more. */ if( *status == PAR__NULL ) { errAnnul( status ); /* If it is "JSA", or one of the JSA projection codes, we return WCS that describes one of the the JSA all-sky pixel grids. */ } else if( *status == SAI__OK ) { proj = smf_jsaproj_fromstr( text, 0, status ); if( astChrMatch( text, "JSA" ) || proj != SMF__JSA_NULL ) { *isjsa = 1; /* Report an error if the instrument cannot be determined. */ if( !igrp ) { *status = SAI__ERROR; errRep( "", "smf_getrefwcs: Cannot use the JSA all-sky pixel " "grid since no input group has been supplied (possibly " "programming error).", status ); } else { /* Open the first input file. */ smf_open_file( NULL, igrp, 1, "READ", SMF__NOCREATE_DATA, &data, status ); if( *status == SAI__OK ) { /* Get the instrument. */ if( data->hdr->instrument == INST__SCUBA2 ) { subinst = smf_calc_subinst( data->hdr, status ); if( subinst == SMF__SUBINST_850 ) { inst = SMF__INST_SCUBA_2_850; } else { inst = SMF__INST_SCUBA_2_450; } } else if( data->hdr->instrument == INST__ACSIS ) { inst = SMF__INST_ACSIS; } else if( *status == SAI__OK ) { *status = SAI__ERROR; if( data->file ) { smf_smfFile_msg( data->file, "FILE", 1, "one or more of " "the input data files" ); } else { msgSetc( "FILE", "one or more of the input data files" ); } errRep( "", "No tiles are yet defined for the instrument that " "created ^FILE.", status ); } /* Get the parameters that define the layout of sky tiles for the instrument. */ smf_jsatiling( inst, &skytiling, status ); /* For "JSA" - choose the best projection. */ if( astChrMatch( text, "JSA" ) ) { /* Use the FITS headers in the first raw data file to create an AST Circle describing the approximate area of the observation within the tracking system. */ circle = smf_mapregion_approx( igrp, status ); /* Convert the circle to ICRS (as used by the JSA all-sky grid). */ astSetC( circle, "System", "ICRS" ); /* Get a list of the tiles that touch this circle. */ tiles = smf_jsatiles_region( circle, &skytiling, &ntile, status ); /* Choose the best projection (i.e. the projection that puts the circle furthest away from any singularities). */ proj = smf_jsaproj( ntile, tiles, &skytiling, status); /* Free resources. */ tiles = astFree( tiles ); circle = astAnnul( circle ); /* If a good projection was specified, use it. Otherwise report an error. */ } else if( proj == SMF__JSA_NULL && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "Bad value '%s' supplied for parameter %s.", status, text, param ); } /* Report the projection type. */ msgOutf( " ", "The %s will be created on the JSA %s " "pixel grid.", status, (data->hdr->instrument==INST__ACSIS)?"cube":"map", smf_jsaproj_tostr( proj ) ); /* All tiles within the same JSA projection use the same WCS, so we get the WCS FrameSet for an arbitrary central tile, and use it for the full map. The exception is that tiles within the HPX facet that is split between bottom-left and top-right, use a different WCS (they have different reference points). But our choice of projection should mean that the map never falls in that facet. The base Frame will be GRID coords within the tile, and the current Frame will be ICRS (RA,Dec). */ smf_jsatile( ((skytiling.ntpf * skytiling.ntpf - 1) * 2) / 3, &skytiling, 0, proj, NULL, spacewcs, NULL, lbnd, ubnd, status ); /* Change the base Frame to be PIXEL. */ for( i = 1; i <= astGetI( *spacewcs, "NFrame" ); i++ ) { frm = astGetFrame( *spacewcs, i ); if( astChrMatch( astGetC( frm, "Domain" ), "PIXEL" ) ) { astSetI( *spacewcs, "Base", i ); } frm = astAnnul( frm ); } } /* Close the current input data file. */ smf_close_file( NULL, &data, status); } /* Otherwise get the parameter value as an NDF. */ } else { ndfAssoc( param, "READ", &refndf, status ); /* Get the WCS FrameSet from the reference NDF. */ ndfGtwcs( refndf, &refwcs, status ); /* Attempt to extract a new FrameSet from this WCS FrameSet, in which the current Frame is a SkyFrame, and the base Frame is a 2D PIXEL Frame. Since the NDF library sets the GRID Frame to be the Base Frame, we need to make the PIXEL Frame the base Frame first. The NDF library ensures that the pixel Frame is Frame 2. */ astSetI( refwcs, "Base", 2 ); *spacewcs = atlFrameSetSplit( refwcs, "SKY", NULL, NULL, status ); if( !(*spacewcs) ) { if( *status == SAI__OK ) { ndfMsg( "N", refndf ); *status = SAI__ERROR; errRep( "", "The supplied reference NDF (^N) either has no " "celestial WCS axes, or the celestial axes cannot " "be separated from the non-celestial axes.", status ); } /* The rest of makemap assumes that the sky frame axes are in the default order (lon,lat). If this is not the case, permute them. */ } else if( astGetI( *spacewcs, "IsLatAxis(1)" ) ) { perm[ 0 ] = 2; perm[ 1 ] = 1; astPermAxes( *spacewcs, perm ); } /* Now look for the spectral WCS (described by a DSBSpecFrame). */ smf_getspectralwcs( refwcs, 1, specwcs, status ); /* We no longer need the NDF so annul it. */ ndfAnnul( &refndf, status ); } } /* If no error has occurred, export any returned FrameSet pointers 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 ) { if( *spacewcs ) astExport( *spacewcs ); if( *specwcs ) astExport( *specwcs ); } else { if( *spacewcs ) *spacewcs = astAnnul( *spacewcs ); if( *specwcs ) *specwcs = astAnnul( *specwcs ); } /* 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_get_projpar( AstSkyFrame *skyframe, const double skyref[2], int moving, int autogrid, int nallpos, const double * allpos, float telres, double map_pa, double par[7], int * issparse,int *usedefs, int *status ) { /* Local Variables */ char reflat[ 41 ]; /* Reference latitude string */ char reflon[ 41 ]; /* Reference longitude string */ char usesys[ 41 ]; /* Output skyframe system */ const char *deflat; /* Default for REFLAT */ const char *deflon; /* Default for REFLON */ const double fbpixsize = 6.0; /* Fallback pixel size if we have no other information */ double autorot; /* Autogrid default for CROTA parameter */ double defsize[ 2 ]; /* Default pixel sizes in arc-seconds */ double pixsize[ 2 ]; /* Pixel sizes in arc-seconds */ double refpix[ 2 ]; /* New REFPIX values */ double rdiam; /* Diameter of bounding circle, in rads */ int coin; /* Are all points effectively co-incident? */ int i; int nval; /* Number of values supplied */ int refine_crpix; /* Should the pixel ref position be updated? */ int sparse = 0; /* Local definition of sparseness */ int udefs = 0; /* Flag for defaults used or not */ /* Check inherited status. */ if( *status != SAI__OK ) return; /* If the number of supplied positions is 0 or null pointer, disable autogrid */ if( nallpos == 0 || !allpos ) autogrid = 0; /* Get the output system */ one_strlcpy( usesys, astGetC( skyframe, "SYSTEM"), sizeof(usesys), status ); /* Ensure the reference position in the returned SkyFrame is set to the first telescope base pointing position. */ astSetD( skyframe, "SkyRef(1)", skyref[ 0 ] ); astSetD( skyframe, "SkyRef(2)", skyref[ 1 ] ); /* If the target is moving, ensure the returned SkyFrame represents offsets from the first telescope base pointing position rather than absolute coords. */ if( moving ) smf_set_moving( (AstFrame *) skyframe, NULL, status ); /* Set a flag indicating if all the points are co-incident. */ coin = 0; /* Set the sky axis values at the tangent point. If the target is moving, the tangent point is at (0,0) (i.e. it is at the origin of the offset coordinate system). If the target is not moving, the tangent point is at the position held in "skyref". */ if( par ) { if( moving ){ par[ 2 ] = 0.0; par[ 3 ] = 0.0; } else { par[ 2 ] = skyref[ 0 ]; par[ 3 ] = skyref[ 1 ]; } /* If required, calculate the optimal projection parameters. If the target is moving, these refer to the offset coordinate system centred on the first time slice base pointing position, with north defined by the requested output coordinate system. The values found here are used as dynamic defaults for the environment parameter */ if( autogrid ) { kpg1Opgrd( nallpos, allpos, strcmp( usesys, "AZEL" ), par, &rdiam, status ); /* See if all the points are effectively co-incident (i.e. within an Airy disk). If so, we use default grid parameters that result in a grid of 1x1 spatial pixels. The grid pixel sizes (par[4] and par[5]) are made larger than the area covered by the points in order to avoid points spanning two pixels. */ if( rdiam < telres || nallpos < 3 ) { if( rdiam < 0.1*AST__DD2R/3600.0 ) rdiam = 0.1*AST__DD2R/3600.0; par[ 0 ] = 0.0; par[ 1 ] = 0.0; par[ 4 ] = -rdiam*4; par[ 5 ] = -par[ 4 ]; par[ 6 ] = 0.0; coin = 1; /* If the sky positions are not co-incident, and the automatic grid determination failed, we cannot use a grid, so warn the user. */ } else if( par[ 0 ] == AST__BAD ) { msgOutif( MSG__NORM, " ", " Automatic grid determination " "failed: the detector samples do not form a " "regular grid.", status ); } } /* If autogrid values were not found, use the following fixed default values. Do not override extenal defaults for pixel size. */ if( !autogrid || ( autogrid && par[ 0 ] == AST__BAD ) ) { par[ 0 ] = 0.0; par[ 1 ] = 0.0; if (par[4] == AST__BAD || par[5] == AST__BAD ) { par[ 4 ] = (fbpixsize/3600.0)*AST__DD2R; par[ 5 ] = (fbpixsize/3600.0)*AST__DD2R; } par[ 6 ] = map_pa; } /* Ensure the default pixel sizes have the correct signs. */ if( par[ 4 ] != AST__BAD ) { if( !strcmp( usesys, "AZEL" ) ) { par[ 4 ] = fabs( par[ 4 ] ); } else { par[ 4 ] = -fabs( par[ 4 ] ); } par[ 5 ] = fabs( par[ 5 ] ); } /* See if the output cube is to include a spatial projection, or a sparse list of spectra. Disabled if the sparse pointer is NULL. */ if (issparse) { parDef0l( "SPARSE", ( par[ 0 ] == AST__BAD ), status ); parGet0l( "SPARSE", &sparse, status ); } /* If we are producing an output cube with the XY plane being a spatial projection, then get the parameters describing the projection, using the defaults calculated above. */ if( !sparse && *status == SAI__OK ) { const int ndigits = 8; /* Number of digits for deflat/deflon precision */ /* If the target is moving, display the tracking centre coordinates for the first time slice. */ if( moving ) { astClear( skyframe, "SkyRefIs" ); msgBlank( status ); msgSetc( "S1", astGetC( skyframe, "Symbol(1)" ) ); msgSetc( "S2", astGetC( skyframe, "Symbol(2)" ) ); msgOutif( MSG__NORM, " ", " Output sky coordinates are " "(^S1,^S2) offsets from the (moving)", status ); msgSetc( "S1", astGetC( skyframe, "Symbol(1)" ) ); msgSetc( "S2", astGetC( skyframe, "Symbol(2)" ) ); msgSetc( "SREF", astGetC( skyframe, "SkyRef" ) ); msgOutif( MSG__NORM, " ", " telescope base position, which " "started at (^S1,^S2) = (^SREF).", status ); astSet( skyframe, "SkyRefIs=Origin" ); } /* Set up a flag indicating that the default values calculated by autogrid are being used. */ udefs = 1; /* Ensure we have usable CRPIX1/2 values */ if( par[ 0 ] == AST__BAD ) par[ 0 ] = 1.0; if( par[ 1 ] == AST__BAD ) par[ 1 ] = 1.0; /* Get the crpix1/2 (in the interim GRID frame) to use. Note if the user specifies any values. These parameters have vpath=default (which is null) and ppath=dynamic. */ refine_crpix = 0; parDef0d( "REFPIX1", par[ 0 ], status ); parDef0d( "REFPIX2", par[ 1 ], status ); if( *status == SAI__OK ) { parGet0d( "REFPIX1", refpix + 0, status ); parGet0d( "REFPIX2", refpix + 1, status ); if( *status == PAR__NULL ) { errAnnul( status ); refine_crpix = 1; } else { par[ 0 ] = refpix[ 0 ]; par[ 1 ] = refpix[ 1 ]; } } /* Get the sky coords reference position strings. Use the returned SkyFrame to format and unformat them. */ if( par[ 2 ] != AST__BAD ) { int curdigits; curdigits = astGetI( skyframe, "digits(1)" ); astSetI( skyframe, "digits(1)", ndigits ); deflon = astFormat( skyframe, 1, par[ 2 ] ); astSetI( skyframe, "digits(1)", curdigits ); parDef0c( "REFLON", deflon, status ); } else { deflon = NULL; } if( par[ 3 ] != AST__BAD ) { int curdigits; curdigits = astGetI( skyframe, "digits(2)" ); astSetI( skyframe, "digits(2)", ndigits ); deflat = astFormat( skyframe, 2, par[ 3 ] ); astSetI( skyframe, "digits(2)", curdigits ); parDef0c( "REFLAT", deflat, status ); } else { deflat = NULL; } parGet0c( "REFLON", reflon, 40, status ); parGet0c( "REFLAT", reflat, 40, status ); if( *status == SAI__OK ) { if( ( deflat && strcmp( deflat, reflat ) ) || ( deflon && strcmp( deflon, reflon ) ) ) udefs = 0; if( astUnformat( skyframe, 1, reflon, par + 2 ) == 0 && *status == SAI__OK ) { msgSetc( "REFLON", reflon ); errRep( "", "Bad value supplied for REFLON: '^REFLON'", status ); } if( astUnformat( skyframe, 2, reflat, par + 3 ) == 0 && *status == SAI__OK ) { msgSetc( "REFLAT", reflat ); errRep( "", "Bad value supplied for REFLAT: '^REFLAT'", status ); } /* Ensure the reference position in the returned SkyFrame is set to the supplied position (which defaults to the first telescope base pointing position). */ if( !moving ){ astSetD( skyframe, "SkyRef(1)", par[ 2 ] ); astSetD( skyframe, "SkyRef(2)", par[ 3 ] ); } } /* Get the user defined spatial pixel size in arcsec (the calibration for the spectral axis is fixed by the first input data file - see smf_cubebounds.c). First convert the autogrid values form rads to arcsec and establish them as the dynamic default for "PIXSIZE". */ nval = 0; if( par[ 4 ] != AST__BAD || par[ 5 ] != AST__BAD ) { for ( i = 4; i <= 5; i++ ) { if ( par[ i ] != AST__BAD ) { defsize[ nval ] = 0.1*NINT( fabs( par[ i ] )*AST__DR2D*36000.0 ); nval++; } } /* set the dynamic default, handling case where both dimensions have same default. */ if (nval == 1) { defsize[1] = defsize[0]; } else if (nval == 2 && defsize[0] == defsize[1]) { nval = 1; } parDef1d( "PIXSIZE", nval, defsize, status ); } else { /* pick a default in case something odd happens and we have no other values*/ defsize[ 0 ] = fbpixsize; defsize[ 1 ] = defsize[ 0 ]; nval = 2; } if (*status == SAI__OK) { pixsize[0] = AST__BAD; pixsize[1] = AST__BAD; parGet1d( "PIXSIZE", 2, pixsize, &nval, status ); if (*status == PAR__NULL) { /* Null just defaults to what we had before */ errAnnul( status ); pixsize[0] = defsize[0]; pixsize[1] = defsize[1]; nval = 2; } } /* If OK, duplicate the first value if only one value was supplied. */ if( *status == SAI__OK ) { if( nval < 2 ) pixsize[ 1 ] = pixsize[ 0 ]; if( defsize[ 0 ] != pixsize[ 0 ] || defsize[ 1 ] != pixsize[ 1 ] ) udefs = 0; /* Check the values are OK. */ if( pixsize[ 0 ] <= 0 || pixsize[ 1 ] <= 0 ) { msgSetd( "P1", pixsize[ 0 ] ); msgSetd( "P2", pixsize[ 1 ] ); *status = SAI__ERROR; errRep( FUNC_NAME, "Invalid pixel sizes (^P1,^P2).", status); } /* Convert to rads, and set the correct signs. */ if( par[ 4 ] == AST__BAD || par[ 4 ] < 0.0 ) { par[ 4 ] = -pixsize[ 0 ]*AST__DD2R/3600.0; } else { par[ 4 ] = pixsize[ 0 ]*AST__DD2R/3600.0; } if( par[ 5 ] == AST__BAD || par[ 5 ] < 0.0 ) { par[ 5 ] = -pixsize[ 1 ]*AST__DD2R/3600.0; } else { par[ 5 ] = pixsize[ 1 ]*AST__DD2R/3600.0; } } /* Convert the autogrid CROTA value from rads to degs and set as the dynamic default for parameter CROTA (the position angle of the output Y axis, in degrees). The get the CROTA value and convert to rads. */ if( par[ 6 ] != AST__BAD ) { autorot = par[ 6 ]*AST__DR2D; parDef0d( "CROTA", autorot, status ); } else { parDef0d( "CROTA", map_pa*AST__DR2D, status ); autorot = AST__BAD; } parGet0d( "CROTA", par + 6, status ); if( par[ 6 ] != autorot ) udefs = 0; par[ 6 ] *= AST__DD2R; /* If any parameter were given explicit values which differ from the autogrid default values, then we need to re-calculate the optimal CRPIX1/2 values. We also do this if all the points are effectively co-incident. */ if( ( coin || !udefs ) && autogrid && refine_crpix ) { par[ 0 ] = AST__BAD; par[ 1 ] = AST__BAD; kpg1Opgrd( nallpos, allpos, strcmp( usesys, "AZEL" ), par, &rdiam, status ); } /* Display the projection parameters being used. */ smf_display_projpars( skyframe, par, status ); /* Write out the reference grid coords to output parameter PIXREF. */ parPut1d( "PIXREF", 2, par, status ); /* If no grid was found, indicate that no spatial projection will be used. */ } else { msgBlank( status ); msgOutif( MSG__NORM, " ", " The output will be a sparse array " "containing a list of spectra.", status ); } /* If we have a pre-defined spatial projection, indicate that the output array need not be sparse. */ } else { sparse = 0; } /* Return usedefs if requested */ if( usedefs ) { *usedefs = udefs; } /* Set sparse if requested */ if( issparse ) *issparse = sparse; }