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 main( int argc, char **argv ){ /* Local variables: */ AstBox *pixbox; AstFitsChan *fchan; AstFrame *pixfrm; AstFrame *wcsfrm; AstFrameSet *frameset; AstKeyMap *warnings; AstMapping *pix2wcs; AstObject *object; AstRegion *wcsbox; AstStcsChan *schan; FILE *fd; char key[ 15 ]; char keyword[ 9 ]; const char *message; double p1[ MAX_AXES ]; double p2[ MAX_AXES ]; int axis; int iwarn; int naxis; int status; /* Initialised the returned system status to indicate success. */ status = 0; /* Check a file was specified on the command line, and attempt to open it for read access. */ if( argc < 2 ) { printf( "Usage: stcschan-demo2 <header-file>\n" ); status = 1; } else { fd = fopen( argv[ 1 ], "r" ); if( !fd ) { printf("Failed to open input file '%s'.\n", argv[ 1 ] ); status = 1; } } /* If a disk file was opened successfully... */ if( !status ) { /* Start an AST object context. This means we do not need to annull each AST Object individually. Instead, all Objects created within this context will be annulled automatically by the corresponding invocation of astEnd. */ astBegin; /* Create a FitsChan. This is the object that converts external FITS headers into corresponding AST Objects. Tell it to use the "source" function for obtaining lines of text from the disk file. */ fchan = astFitsChan( source, NULL, " " ); /* Associate the descriptor for the input disk file with the StcsChan. This makes it available to the "source" function. Since this application is single threaded, we could instead have made "fd" a global variable, but the ChannelData facility is used here to illustrate how to pass data to a source or sink function safely in a multi-threaded application. */ astPutChannelData( fchan, fd ); /* Attempt to read the FITS heades and convert them into an AST FrameSet. */ object = astRead( fchan ); /* The astRead function is a generic function and so returns a generic AstObject pointer. Check an Object was created successfully. */ if( !object ) { printf( "Failed to read an AST Object from file '%s'.\n", argv[ 1 ] ); status = 1; /* Now check that the object read is actually an AST FrameSet, rather than some other class of AST Object. */ } else if( !astIsAFrameSet( object ) ) { printf( "Expected a FrameSet but read a %s from file '%s'.\n", astGetC( object, "Class" ), argv[ 1 ] ); status = 1; /* We now know we have a FrameSet so it is safe to use the pointer returned by astRead as a FrameSet pointer. Do the cast now to avoid repeated casting in future. */ } else { frameset = (AstFrameSet *) object; /* Get a pointer to the Frame that describes the attributes of the FITS world coordinate system. This is the current Frame in the FrameSet read from the FITS headers. */ wcsfrm = astGetFrame( frameset, AST__CURRENT ); /* Get a pointer to the Frame that describes the attributes of the FITS pixel coordinate system. This is the base Frame in the FrameSet read from the FITS headers. */ pixfrm = astGetFrame( frameset, AST__BASE ); /* Get the Mapping that transforms pixel positions into WCS positions. The is the Mapping from base to current Frame in the FrameSet read from the FITS headers. */ pix2wcs = astGetMapping( frameset, AST__BASE, AST__CURRENT ); /* Get the number of axes in ther pixel Frame. */ naxis = astGetI( pixfrm, "Naxes" ); /* For each pixel axis, form the name of the corresponding NAXISi keyword. */ for( axis = 0; axis < naxis; axis++ ) { sprintf( keyword, "NAXIS%d", axis + 1 ); /* Store the pixel coordinate on the current axis at the lower left corner of the first pixel. */ p1[ axis ] = 0.5; /* Get the NAXISi value for the current axis from the FITS header, and store it in array "p2". Report an error if NAXISi is not found. */ if( !astGetFitsF( fchan, keyword, p2 + axis ) ){ printf("Keyword '%s' not found in header\n", keyword ); status = 1; break; /* If it is found, modify "p2" so that it holds the pixel coordinate on the current axis at the upper right corner of the last pixel. */ } else { p2[ axis ] += 0.5; } } } /* If all has gone well, create an AST Region (a Box) describing the rectangular region of pixel coordinates covered by the pixel array. */ if( !status ) { pixbox = astBox( pixfrm, 1, p1, p2, NULL, " " ); /* Map this box into the FITS world coordinate system. The Mapping is specified by "pix2wcs", and the attributes of the resulting axes is described by "wcsfrm". */ wcsbox = astMapRegion( pixbox, pix2wcs, wcsfrm ); /* Create an StcsChan. This is the object that converts (either way) between external STC-S descriptions and their corresponding AST Objects. Tell it to use the "source" function for obtaining lines of text from the disk file. Also tell it to store all warnings generated by the conversion for later use. Other attributes of the StcsChan class retain their default values. */ schan = astStcsChan( NULL, NULL, "ReportLevel=3" ); /* Attempt to write out the Region describing the pixel array (in WCS) as an STC-S description. Report an error if this fails. */ if( ! astWrite( schan, wcsbox ) && astOK ) { printf( "Failed to convert the Region into an STC-S " "description.\n" ); } } /* We asked the StcsChan to record any warnings that were generated whilst converting the AST Region into a corresponding STC-S description. We now see if any such warnings were generated by the earlier call to astWrite. */ warnings = astWarnings( schan ); /* If any warnings were generated, and if no other error has occurred so far, display the warnings. */ if( warnings && !status && astOK ) { printf( "\nThe following warnings were issued:\n" ); /* The warnings are stored in an AST KeyMap (a sort of hashmap). Each warning message is associated with a key of the form "Warning_1", "Warning_2", etc. Loop round successive keys, obtaining a value for each key from the warnings KeyMap, and displaying it. */ iwarn = 1; while( astOK ) { sprintf( key, "Warning_%d", iwarn++ ); if( astMapGet0C( warnings, key, &message ) ) { printf( "\n- %s\n", message ); } else { break; } } } /* End the AST Object context. All Objects created since the corresponding invocation of astbegin will be annulled automatically. */ astEnd; /* Close the disk file. */ (void) fclose( fd ); } /* If an error occurred in the AST library, set the retiurns system status non-zero. */ if( !astOK ) status = 1; return status; }
void smf_mapbounds_approx( Grp *igrp, size_t index, char *system, int *lbnd_out, int *ubnd_out, AstFrameSet **outframeset, int *moving, int *status ) { /* Local variables */ smfData *data = NULL; /* pointer to SCUBA2 data struct */ int dxpix; /* Map X offset in pixels */ int dypix; /* Map Y offset in pixels */ smfFile *file = NULL; /* SCUBA2 data file information */ 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 */ double hghtbox; /* Map height in arcsec */ int hghtpix; /* RA-Dec map height in pixels */ int i; /* loop counter */ dim_t k; /* Loop counter */ double maphght = 0.0; /* Map height in radians */ double mappa = 0.0; /* Map position angle in radians */ double mapwdth = 0.0; /* Map width in radians */ double mapx; /* Map X offset in radians */ double mapy; /* Map Y offset in radians */ double par[7]; /* Projection parameters */ double pixsize = 0.0; /* Requested pixel size */ double shift[ 2 ]; /* Shifts from PIXEL to GRID coords */ AstMapping *sky2map = NULL; /* Mapping celestial->map coordinates */ AstSkyFrame *skyframe = NULL;/* Output SkyFrame */ AstFrame *skyin = NULL; /* Sky Frame in input FrameSet */ double skyref[ 2 ]; /* Values for output SkyFrame SkyRef attribute */ AstFrameSet *swcsin = NULL; /* FrameSet describing input WCS */ int temp; /* Temporary variable */ double wdthbox; /* Map width in arcsec */ int wdthpix; /* RA-Dec map width in pixels */ double x_array_corners[4]; /* X-Indices for corner bolos in array */ double y_array_corners[4]; /* Y-Indices for corner pixels in array */ /* Main routine */ if (*status != SAI__OK) return; /* Begin an AST context to ensure that all AST objects are annuled before returning to caller */ astBegin; /* Initialize output frameset pointer to NULL */ *outframeset = NULL; for( i = 0; i < 7; i++ ) par[ i ] = AST__BAD; /* Read data from the given input file in the group - note index should be 1 as we use the first file in the Grp to define the map bounds */ smf_open_file( igrp, index, "READ", SMF__NOCREATE_DATA, &data, status ); /* Simply abort if it is not a scan */ if (*status == SAI__OK && data->hdr->obsmode != SMF__OBS_SCAN) { *status = SAI__ERROR; errRep(" ", "Can not call smf_mapbounds_approx with non-scan observation" " (possible programming error)", status); goto CLEANUP; } /* Retrieve file name for use feedback */ file = data->file; smf_smfFile_msg( file, "FILE", 1, "<unknown>" ); if( *status == SAI__OK ) { msgOutif(MSG__VERB, " ", "SMF_MAPBOUNDS_APPROX: Processing ^FILE", status); } else { errRep( "smf_mapbounds_approx", "Couldn't open input file, ^FILE", status ); } /* Check that the data dimensions are 3 (for time ordered data) */ if( *status == SAI__OK ) { if( data->ndims != 3 ) { smf_smfFile_msg( file, "FILE", 1, "<unknown>" ); msgSeti("THEDIMS", data->ndims); *status = SAI__ERROR; errRep("smf_mapbounds_approx", "^FILE data has ^THEDIMS dimensions, should be 3.", status); } } /* Construct the WCS for the first time slice in this file */ smf_tslice_ast( data, 1, 1, NO_FTS, status); /* Retrieve header for later constructing output WCS */ if( *status == SAI__OK) { hdr = data->hdr; swcsin = hdr->wcs; /* Calculate default pixel size */ pixsize = smf_calc_telres( hdr->fitshdr, status ); /* Get the user defined pixel size - we trust that smf_get_projpar will also read PIXSIZE and get the same answer. We pre-fill par[] to allow PIXSIZE=! to accept the dynamic default in both places.*/ parGdr0d( "PIXSIZE", pixsize, 0, 60, 1, &pixsize, status ); par[4] = pixsize*AST__DD2R/3600.0; par[5] = par[4]; /* Retrieve input SkyFrame */ skyin = astGetFrame( swcsin, AST__CURRENT ); /* Retrieve map height and width from header - will be undef for non-scan so set up defaults first. */ mapwdth = 0.0; maphght = 0.0; smf_getfitsd( hdr, "MAP_WDTH", &mapwdth, status ); smf_getfitsd( hdr, "MAP_HGHT", &maphght, status ); /* Make an approximation if map height and width are not set - note that this should ONLY apply for non-scan mode data */ if ( !mapwdth || !maphght ) { if (*status == SAI__OK) { *status = SAI__ERROR; errRep(" ", "MAP_WDTH and MAP_HGHT must be > 0", status); goto CLEANUP; } } mapx = 0.0; /* Used if the FITS keyword values are undefed */ mapy = 0.0; smf_getfitsd( hdr, "MAP_X", &mapx, status ); smf_getfitsd( hdr, "MAP_Y", &mapy, status ); /* Convert map Position Angle to radians */ mappa = 0.0; smf_fits_getD( hdr, "MAP_PA", &mappa, status ); mappa *= AST__DD2R; /* Calculate size of output map in pixels */ /* Note: this works for the simulator... */ wdthbox = mapwdth*fabs(cos(mappa)) + maphght*fabs(sin(mappa)); hghtbox = maphght*fabs(cos(mappa)) + mapwdth*fabs(sin(mappa)); wdthpix = (int) ( wdthbox / pixsize); hghtpix = (int) ( wdthbox / pixsize); dxpix = (int) (mapx / pixsize); dypix = (int) (mapy / pixsize); /* Get the offsets for each corner of the array */ temp = (wdthpix - 1) / 2; x_array_corners[0] = dxpix - temp; x_array_corners[1] = dxpix - temp; x_array_corners[2] = dxpix + temp; x_array_corners[3] = dxpix + temp; temp = (hghtpix - 1) / 2; y_array_corners[0] = dypix - temp; y_array_corners[1] = dypix + temp; y_array_corners[2] = dypix - temp; y_array_corners[3] = dypix + temp; lbnd_out[0] = x_array_corners[0]; ubnd_out[0] = x_array_corners[0]; lbnd_out[1] = y_array_corners[0]; ubnd_out[1] = y_array_corners[0]; /* Update min/max */ for( k=0; k<4; k++ ) { if( x_array_corners[k] < lbnd_out[0] ) lbnd_out[0] = x_array_corners[k]; if( y_array_corners[k] < lbnd_out[1] ) lbnd_out[1] = y_array_corners[k]; if( x_array_corners[k] > ubnd_out[0] ) ubnd_out[0] = x_array_corners[k]; if( y_array_corners[k] > ubnd_out[1] ) ubnd_out[1] = y_array_corners[k]; } } else { goto CLEANUP; } /* Now create the output FrameSet. */ smf_calc_skyframe( skyin, system, hdr, 0, &skyframe, 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. */ mappa = smf_calc_mappa( hdr, system, skyin, status ); /* 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( skyframe, skyref, *moving, 0, 0, NULL, 0, mappa, par, NULL, NULL, status ); /* 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( skyframe, "System"), &(par[0]), &(par[2]), &(par[4]), par[6], fitschan, status ); astClear( fitschan, "Card" ); fs = astRead( fitschan ); /* Extract the output PIXEL->SKY Mapping - note this is will be inverted later to create the sk2map mapping */ sky2map = astGetMapping( fs, AST__BASE, AST__CURRENT ); /* Create the output FrameSet */ *outframeset = astFrameSet( astFrame(2, "Domain=GRID"), " " ); /* Now add the SkyFrame to it */ astAddFrame( *outframeset, AST__BASE, sky2map, skyframe ); /* Apply a ShiftMap to the output FrameSet to re-align the GRID coordinates */ shift[0] = -lbnd_out[0]; shift[1] = -lbnd_out[1]; astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) ); astExport( *outframeset ); /* 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 ); } /* Change the pixel bounds to be consistent with the new CRPIX */ ubnd_out[0] -= lbnd_out[0]-1; lbnd_out[0] = 1; ubnd_out[1] -= lbnd_out[1]-1; lbnd_out[1] = 1; /* Clean Up */ CLEANUP: if (*status != SAI__OK) { errRep(FUNC_NAME, "Unable to determine map bounds", status); } if( data != NULL ) smf_close_file( &data, status); astEnd; }
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; }
Hero * Hero::constructFromFitsFile(const QString &fname) { FitsParser parser; bool parsedOk = parser.loadFile( FitsFileLocation::fromLocal( fname)); if( ! parsedOk) { dbg(1) << "Parser failed to load " << fname; Hero * heroPtr = new Hero; heroPtr-> addError( "FitsParser failed to load the file"); return heroPtr; } // alias hdr auto & hdr = parser.getHeaderInfo().headerLines; Hero * heroPtr = new Hero; Hero & hero = * heroPtr; AstErrorGuard guard( heroPtr); AstGCGuard gcGuard; // set naxes in case AST fails to read this file hero.m_ast.naxes = parser.getHeaderInfo().naxis; // set up bunit { hero.m_bunit = parser.getHeaderInfo().bunit; } // and the nicer version of bunit { QString u = hero.m_bunit.simplified(); if( u.toLower() == "kelvin") { hero.m_bunitNiceHtml = "K"; } else { hero.m_bunitNiceHtml = u; } } // Create a FitsChan and feed it the fits header AstFitsChan *fitschan; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-zero-length" fitschan = astFitsChan( NULL, NULL, "" ); #pragma GCC diagnostic pop std::cout << "astOK = " << astOK << "\n"; // feed the header lines one by one and check for errors for( const QString & s : hdr) { std::string stdstr = s.toStdString(); astPutFits( fitschan, stdstr.c_str(), 1); if( ! astOK) { astClearStatus; QString ss = s.trimmed(); std::cout << "Skipping bad card: " << ss << "\n"; hero.addError( "Skipping card: " + ss); } } // reposition to the beginning of the channel (ast thing, it's required, hmmmkey) astClear( fitschan, "Card" ); std::cout << "astOK = " << astOK << "\n"; std::cout << "Here\n"; auto encoding = AstWrappers::getC( fitschan, "Encoding" ); std::cout << "Encoding = " << encoding << "\n"; // do we have warnings? AstKeyMap * warnings = static_cast<AstKeyMap *>( astWarnings( fitschan)); if( warnings && astOK ) { std::cout << "Warnings:\n"; int iwarn = 1; while( astOK ) { std::string key = QString("Warning_%1").arg( iwarn).toStdString(); const char * message = nullptr; if( astMapGet0C( warnings, key.c_str(), & message ) ) { printf( "\n- %s\n", message ); hero.addError( QString( "Warning: %1").arg( message)); } else { break; } } } else { std::cout << "No warnings\n"; } // create a frameset for this file AstFrameSet * wcsinfo = static_cast<AstFrameSet *> ( astRead( fitschan )); std::cout << "astOK = " << astOK << "\n"; if ( ! astOK ) { std::cout << "astOK is not ok\n"; hero.addError( "astRead failed"); astClearStatus; return heroPtr; } else if ( wcsinfo == AST__NULL ) { hero.addError( "No WCS found in the fits file"); std::cout << "No WCS found\n"; return heroPtr; } else if ( AstWrappers::getC( wcsinfo, "Class" ) != "FrameSet") { std::cout << "Some other weird error occured\n"; hero.addError( "AstLib returned non-frame-set"); return heroPtr; } // frame was read in OK, save it hero.m_ast.origWcsInfo = wcsinfo; astExempt( hero.m_ast.origWcsInfo); hero.m_ast.currWcsInfo = astClone( hero.m_ast.origWcsInfo); astExempt( hero.m_ast.currWcsInfo); astShow( wcsinfo); // extract the current sky system // TODO: this assumes axis1 is a skycs QString skysys = AstWrappers::getC( wcsinfo, "System(1)"); hero.m_currentSkyCs = string2skycs( skysys); hero.m_originalSkyCs = hero.m_currentSkyCs; // extract the labels/etc for axes hero.m_ast.naxes = AstWrappers::getI( wcsinfo, "Naxes" ); hero.parseAxesInfo(); return heroPtr; }