void atlKy2hd( AstKeyMap *keymap, HDSLoc *loc, int *status ) { /* * Name: * atlKy2hd * Purpose: * Copies values from an AST KeyMap to a primitive HDS object. * Language: * C. * Invocation: * void atlKy2hd( AstKeyMap *keymap, HDSLoc *loc, int *status ) * Description: * This routine copies the contents of an AST KeyMap into a supplied * HDS structure. * Arguments: * keymap * An AST pointer to the KeyMap. * loc * A locator for the HDS object into which the KeyMap contents * are to be copied. A new component is added to the HDS object for * each entry in the KeyMap. * status * The inherited status. * Copyright: * Copyright (C) 2008, 2010, 2012 Science & Technology Facilities Council. * All Rights Reserved. * Licence: * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be * useful,but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA * 02110-1301, USA * Authors: * DSB: David S. Berry * TIMJ: Tim Jenness (JAC, Hawaii) * {enter_new_authors_here} * History: * 29-APR-2008 (DSB): * Original version. * 2010-09-23 (TIMJ): * Fix arrays of strings. * 2010-09-30 (TIMJ): * Make sure that we are using the correct status pointer in AST. * 2010-10-01 (TIMJ): * Sort the keys when writing to HDS structured. * 2010-10-04 (TIMJ): * Support Short ints in keymap * 14-SEP-2012 (DSB): * Moved from kaplibs to atl. * 17-SEP-2012 (DSB): * Add support for undefined values. * {enter_further_changes_here} * Bugs: * {note_any_bugs_here} */ /* Local Varianles: */ AstObject **objArray = NULL; AstObject *obj = NULL; HDSLoc *cloc = NULL; HDSLoc *dloc = NULL; const char *cval = NULL; const char *key; double dval; float fval; int i; int ival; int j; int lenc; int nval; char *oldsortby; int *oldstat = NULL; int size; int type; int veclen; size_t el; void *pntr = NULL; /* Check inherited status */ if( *status != SAI__OK ) return; /* Make sure that we are checking AST status */ oldstat = astWatch( status ); /* If set, save the old SortBy value and then ensure alphabetical sorting. We need to take a copy of the original string since the buffer in which the string is stored may be re-used by subsequent incocations of astGetC. */ if( astTest( keymap, "SortBy" ) ) { int nc = 0; oldsortby = astAppendString( NULL, &nc, astGetC( keymap, "SortBy" ) ); } else { oldsortby = NULL; } astSet( keymap, "SortBy=KeyUp" ); /* Loop round each entry in the KeyMap. */ size = astMapSize( keymap ); for( i = 0; i < size; i++ ) { if (*status != SAI__OK) break; /* Get the key. the data type and the vector length for the current KeyMap entry. */ key = astMapKey( keymap, i ); type = astMapType( keymap, key ); veclen = astMapLength( keymap, key ); /* If the current entry holds one or more nested KeyMaps, then we call this function recursively to add them into a new HDS component. */ if( type == AST__OBJECTTYPE ) { /* First deal with scalar entries holding a single KeyMap. */ if( veclen == 1 ) { datNew( loc, key, "KEYMAP_ENTRY", 0, NULL, status ); datFind( loc, key, &cloc, status ); (void) astMapGet0A( keymap, key, &obj ); if( astIsAKeyMap( obj ) ) { atlKy2hd( (AstKeyMap *) obj, cloc, status ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "atlKy2hd: Supplied KeyMap contains unusable AST " "objects (programming error).", status ); } datAnnul( &cloc, status ); /* Now deal with vector entries holding multiple KeyMaps. */ } else { datNew( loc, key, "KEYMAP_ENTRY", 1, &veclen, status ); datFind( loc, key, &cloc, status ); objArray = astMalloc( sizeof( AstObject *) * (size_t)veclen ); if( objArray ) { (void) astMapGet1A( keymap, key, veclen, &nval, objArray ); for( j = 1; j <= veclen; j++ ) { datCell( cloc, 1, &j, &dloc, status ); if( astIsAKeyMap( objArray[ j - 1 ] ) ) { atlKy2hd( (AstKeyMap *) objArray[ j - 1 ], dloc, status ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "atlKy2hd: Supplied KeyMap contains unusable AST " "objects (programming error).", status ); } datAnnul( &dloc, status ); } objArray = astFree( objArray ); } datAnnul( &cloc, status ); } /* For primitive types... */ } else if( type == AST__INTTYPE ) { if( veclen == 1 ) { datNew0I( loc, key, status ); datFind( loc, key, &cloc, status ); (void) astMapGet0I( keymap, key, &ival ); datPut0I( cloc, ival, status ); datAnnul( &cloc, status ); } else { datNew1I( loc, key, veclen, status ); datFind( loc, key, &cloc, status ); datMapV( cloc, "_INTEGER", "WRITE", &pntr, &el, status ); (void) astMapGet1I( keymap, key, veclen, &nval, (int *) pntr ); datUnmap( cloc, status ); datAnnul( &cloc, status ); } } else if( type == AST__SINTTYPE ) { short sval = 0; if( veclen == 1 ) { datNew0W( loc, key, status ); datFind( loc, key, &cloc, status ); (void) astMapGet0S( keymap, key, &sval ); datPut0W( cloc, sval, status ); datAnnul( &cloc, status ); } else { datNew1W( loc, key, veclen, status ); datFind( loc, key, &cloc, status ); datMapV( cloc, "_WORD", "WRITE", &pntr, &el, status ); (void) astMapGet1S( keymap, key, veclen, &nval, (short *) pntr ); datUnmap( cloc, status ); datAnnul( &cloc, status ); } } else if( type == AST__DOUBLETYPE ) { if( veclen == 1 ) { datNew0D( loc, key, status ); datFind( loc, key, &cloc, status ); (void) astMapGet0D( keymap, key, &dval ); datPut0D( cloc, dval, status ); datAnnul( &cloc, status ); } else { datNew1D( loc, key, veclen, status ); datFind( loc, key, &cloc, status ); datMapV( cloc, "_DOUBLE", "WRITE", &pntr, &el, status ); (void) astMapGet1D( keymap, key, veclen, &nval, (double *) pntr ); datUnmap( cloc, status ); datAnnul( &cloc, status ); } } else if( type == AST__FLOATTYPE ) { if( veclen == 1 ) { datNew0R( loc, key, status ); datFind( loc, key, &cloc, status ); (void) astMapGet0F( keymap, key, &fval ); datPut0R( cloc, fval, status ); datAnnul( &cloc, status ); } else { datNew1R( loc, key, veclen, status ); datFind( loc, key, &cloc, status ); datMapV( cloc, "_REAL", "WRITE", &pntr, &el, status ); (void) astMapGet1F( keymap, key, veclen, &nval, (float *) pntr ); datUnmap( cloc, status ); datAnnul( &cloc, status ); } } else if( type == AST__STRINGTYPE ) { lenc = astMapLenC( keymap, key ); if( veclen == 1 ) { datNew0C( loc, key, lenc, status ); datFind( loc, key, &cloc, status ); (void) astMapGet0C( keymap, key, &cval ); datPut0C( cloc, cval, status ); datAnnul( &cloc, status ); } else { datNew1C( loc, key, lenc, veclen, status ); datFind( loc, key, &cloc, status ); datMapV( cloc, "_CHAR", "WRITE", &pntr, &el, status ); (void) atlMapGet1C( keymap, key, veclen*lenc, lenc, &nval, (char *) pntr, status ); datUnmap( cloc, status ); datAnnul( &cloc, status ); } /* KeyMap "UNDEF" values are always scalar and have no corresponding HDS data type. So arbitrarily use an "_INTEGER" primitive with no defined value to represent a KeyMap UNDEF value. */ } else if( type == AST__UNDEFTYPE ) { datNew0L( loc, key, status ); /* Unknown or unsupported data types. */ } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "T", type ); errRep( "", "atlKy2hd: Supplied KeyMap contains entries with " "unusable data type (^T) (programming error).", status ); } } /* If it was originally set, re-instate the old SortBy value in the KeyMap, then free the memory. Otherwise, clear the SortBy attribute. */ if( oldsortby ) { astSetC( keymap, "SortBy", oldsortby ); oldsortby = astFree( oldsortby ); } else { astClear( keymap, "SortBy" ); } /* Reset AST status */ astWatch( oldstat ); }
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; }
int *smf_jsatiles_data( Grp *igrp, size_t size, int *ntile, int *status ){ /* Local Variables */ AstFrame *frm = NULL; AstFrameSet *fs; JCMTState *state; const char *trsys; const char *trsys_last; dim_t *hits = NULL; dim_t *ph; dim_t iframe; double *gx = NULL; double *gy = NULL; double *p1; double *p2; double *px; double *py; double *trac1 = NULL; double *trac2 = NULL; double fov; double point1[ 2 ]; double point2[ 2 ]; double search; int *tiles = NULL; int dim[ 2 ]; int i; int ix; int iy; int lbnd[ 2 ]; int ubnd[ 2 ]; size_t ifile; smfData *data = NULL; smfHead *hdr = NULL; smfJSATiling skytiling; smf_inst_t inst = SMF__INST_NONE; smf_inst_t instrument; smf_subinst_t subinst; /* 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; /* Loop round all the input NDFs. */ trsys_last = ""; for( ifile = 1; ifile <= size && *status == SAI__OK; ifile++ ) { /* Obtain information about the current input NDF. */ smf_open_file( igrp, ifile, "READ", SMF__NOCREATE_DATA, &data, status ); /* Get a pointer to the header. */ hdr = data->hdr; /* Get the instrument. */ if( hdr->instrument == INST__SCUBA2 ) { subinst = smf_calc_subinst( hdr, status ); if( subinst == SMF__SUBINST_850 ) { inst = SMF__INST_SCUBA_2_850; } else { inst = SMF__INST_SCUBA_2_450; } } else if( hdr->instrument == INST__ACSIS ) { inst = SMF__INST_HARP; } else if( *status == SAI__OK ) { *status = SAI__ERROR; smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); errRep( "", "No tiles are yet defined for the instrument that " "created ^FILE.", status ); } /* If this is the first file, it defines the instrument in use. */ if( ifile == 1 ) { instrument = inst; /* Get the parameters that define the layout of sky tiles for the instrument. */ smf_jsatiling( instrument, &skytiling, status ); /* Create a FrameSet describing the whole sky in which each pixel corresponds to a single tile. 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( 0, &skytiling, 0, NULL, &fs, NULL, lbnd, ubnd, status ); /* Allocate an image with one pixel for each tile, and fill it with zeros. */ dim[ 0 ] = ubnd[ 0 ] - lbnd[ 0 ] + 1; dim[ 1 ] = ubnd[ 1 ] - lbnd[ 1 ] + 1; hits = astCalloc( dim[0]*dim[1], sizeof( *hits ) ); /* Get the radius of the field of view in radians. */ fov = 0.5*(skytiling.fov*AST__DD2R)/3600.0; /* If this is not the first file, report an error if the instrument has changed... */ } else if( instrument != inst && *status == SAI__OK ) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); errRep( "", "The file ^FILE was created by a different instrument " "to the previous files.", status ); } /* Re-map the current Frame of the hits map WCS FrameSet to be the tracking system used by the current file (if it has changed). */ trsys = sc2ast_convert_system( (hdr->allState)[0].tcs_tr_sys, status ); if( *status == SAI__OK && strcmp( trsys, trsys_last ) ) { astSetC( fs, "System", trsys ); trsys_last = trsys; frm = astGetFrame( fs, AST__CURRENT ); } /* Get the radius of the search circle. */ search = fov + sqrt( hdr->instap[ 0 ]*hdr->instap[ 0 ] + hdr->instap[ 1 ]*hdr->instap[ 1 ] ); /* Ensure our work arrays are big enough to hold the current file. */ trac1 = astGrow( trac1, 4*hdr->nframes, sizeof( *trac1 ) ); trac2 = astGrow( trac2, 4*hdr->nframes, sizeof( *trac2 ) ); gx = astGrow( gx, 4*hdr->nframes, sizeof( *gx ) ); gy = astGrow( gy, 4*hdr->nframes, sizeof( *gy ) ); /* Check pointers can be used safely. */ if( *status == SAI__OK ) { /* Loop round all time slices, getting the tracking coords at the corners of a box that enclose the field of view. */ p1 = trac1; p2 = trac2; state = hdr->allState; for( iframe = 0; iframe < hdr->nframes; iframe++,state++ ) { point1[ 0 ] = state->tcs_tr_ac1; point1[ 1 ] = state->tcs_tr_ac2; for( i = 0; i < 4; i++ ) { astOffset2( frm, point1, i*AST__DPIBY2, search, point2 ); *(p1++) = point2[ 0 ]; *(p2++) = point2[ 1 ]; } } /* Convert them to grid coords in the hits map. */ astTran2( fs, 4*hdr->nframes, trac1, trac2, 0, gx, gy ); /* Loop round them all again. Update the hits map to indicate how many points fall in each tile. */ px = gx; py = gy; for( iframe = 0; iframe < 4*hdr->nframes; iframe++,px++,py++ ) { ix = (int)( *px + 0.5 ) - 1; iy = (int)( *py + 0.5 ) - 1; hits[ ix + iy*dim[ 0 ] ]++; } } /* Close the current input data file. */ smf_close_file( &data, status); data = NULL; } /* Form a list of all the tiles that receive any data. */ ph = hits; for( iy = 0; iy < dim[ 1 ]; iy++ ) { for( ix = 0; ix < dim[ 0 ]; ix++,ph++ ) { if( *ph > 0 ) { tiles = astGrow( tiles, ++(*ntile), sizeof( *tiles ) ); if( *status == SAI__OK ) { tiles[ *ntile - 1 ] = smf_jsatilexy2i( ix, iy, &skytiling, status ); } } } } /* Free resources. */ hits = astFree( hits ); trac1 = astFree( trac1 ); trac2 = astFree( trac2 ); gx = astFree( gx ); gy = astFree( gy ); if( *status != SAI__OK ) { tiles = astFree( tiles ); *ntile = 0; } astEnd; return tiles; }
double smf_calc_mappa( smfHead *hdr, const char *system, AstFrame *sf, int *status ){ /* Local Variables */ AstFrameSet *fs = NULL; /* FrameSet joining tracking and requested systems */ AstFrame *sf2 = NULL; /* Frame in requested system */ const char *oldsys = NULL; /* Original System value for supplied Frame */ const char *trsys = NULL; /* AST tracking system */ const char *usesys = NULL; /* AST system for output cube */ double map_pa; /* MAP_PA in tracking system (degs) */ double p1[ 2 ]; /* Base pointing position */ double p2[ 2 ]; /* A point on the map vertical axis */ double p3[ 2 ]; /* A point north of the base pointing position */ double result; /* The returned angle */ double xin[ 2 ]; /* Longitude values in tracking system */ double xout[ 2 ]; /* Longitude values in requested system */ double yin[ 2 ]; /* Latitude values in tracking system */ double yout[ 2 ]; /* Latitude values in requested system */ /* Check inherited status */ if (*status != SAI__OK) return 0.0; /* Determine the tracking system, and choose the celestial coordinate system for the output cube. */ trsys = sc2ast_convert_system( hdr->state->tcs_tr_sys, status ); if( !strcmp( system, "TRACKING" ) ) { usesys = trsys; } else { usesys = system; } /* Save the System value of the supplied Frame, and set it to the tracking system. */ oldsys = astGetC( sf, "System" ); astSetC( sf, "System", trsys ); /* Get the base pointing position in the tracking system. */ p1[ 0 ] = hdr->state->tcs_tr_bc1; p1[ 1 ] = hdr->state->tcs_az_bc2; /* Move along the map "vertical" axis (as specified by the MAP_PA FITS header) for 1 arc-minute from the base pointing position. Set up a suitable default first in case the MAP_PA value is undefined. */ map_pa = 0.0; smf_getfitsd( hdr, "MAP_PA", &map_pa, status ); (void) astOffset2( sf, p1, map_pa*AST__DD2R, AST__DD2R/60.0, p2 ); /* Take a copy of the Frame and set its System value to the requested system. */ sf2 = astCopy( sf ); astSetC( sf2, "System", usesys ); /* Find a Mapping from the tracking system to the requested system. */ fs = astConvert( sf, sf2, "" ); /* Use this Mapping to transform the above two positions from tracking to the requested system. */ xin[ 0 ] = p1[ 0 ]; yin[ 0 ] = p1[ 1 ]; xin[ 1 ] = p2[ 0 ]; yin[ 1 ] = p2[ 1 ]; astTran2( fs, 2, xin, yin, 1, xout, yout ); p1[ 0 ] = xout[ 0 ]; p1[ 1 ] = yout[ 0 ]; p2[ 0 ] = xout[ 1 ]; p2[ 1 ] = yout[ 1 ]; /* Create a 3rd position which 1 arc-minute to the north of the base pointing position in the requested system. */ p3[ 0 ] = p1[ 0 ]; p3[ 1 ] = p1[ 1 ] + AST__DD2R/60.0; /* Find the angle subtended at p1 by p2 and p3. */ result = astAngle( sf2, p3, p1, p2 ); /* Re-instate the original System value in the supplied Frame. */ astSetC( sf, "System", oldsys ); /* Free resources. */ fs = astAnnul( fs ); sf2 = astAnnul( sf2 ); /* Return the required angle. */ return result; }
void smf_coords_lut( smfData *data, int tstep, dim_t itime_lo, dim_t itime_hi, AstSkyFrame *abskyfrm, AstMapping *oskymap, int moving, int olbnd[ 2 ], int oubnd[ 2 ], fts2Port fts_port, int *lut, double *angle, double *lon, double *lat, int *status ) { /* Local Variables */ AstCmpMap *bsmap = NULL; /* Tracking -> output grid Mapping */ AstFrame *trfrm = NULL; /* Tracking Frame */ AstFrameSet *fs = NULL; /* Tracking -> output sky FrameSet */ AstMapping *fullmap = NULL; /* Full Mapping from bolo GRID to output map GRID */ AstMapping *offmap = NULL; /* Mapping from absolute to offset sky coords */ AstMapping *tr2skyabs = NULL;/* Tracking -> output sky Mapping */ AstSkyFrame *offsky = NULL; /* Offset sky frame */ JCMTState *state; /* Pointer to telescope info for time slice */ dim_t ibolo; /* Vector index of bolometer */ dim_t idimx; /* Bolometers per row */ dim_t idimy; /* Bolometers per column */ dim_t ilut; /* Index of LUT element */ dim_t itime0; /* Time slice index at next full calculation */ dim_t itime; /* Time slice index */ dim_t nbolo; /* Total number of bolometers */ double *outmapcoord; /* Array holding output map GRID coords */ double *px; /* Pointer to next output map X GRID coord */ double *py; /* Pointer to next output map Y GRID coord */ double *pgx; /* Pointer to next X output grid coords */ double *pgy; /* Pointer to next Y output grid coords */ double *wgx = NULL; /* Work space to hold X output grid coords */ double *wgy = NULL; /* Work space to hold Y output grid coords */ double bsx0; /* Boresight output map GRID X at previous full calc */ double bsx; /* Boresight output map GRID X at current time slice */ double bsxlast; /* Boresight output map GRID X at previous time slice*/ double bsy0; /* Boresight output map GRID Y at previous full calc */ double bsy; /* Boresight output map GRID Y at current time slice */ double bsylast; /* Boresight output map GRID Y at previous time slice*/ double dx; /* Offset in GRID X from previous full calc */ double dxlast; /* Offset in GRID X from previous time slice */ double dy; /* Offset in GRID Y from previous full calc */ double dylast; /* Offset in GRID Y from previous time slice */ double shift[ 2 ]; /* Shift from PIXEL to GRID in output map */ double x; /* Output GRID X at current bolo in current row */ double xin[ 2 ]; /* Input X values */ double xout[ 2 ]; /* Output X values */ double y; /* Output GRID Y at current bolo in current row */ double yin[ 2 ]; /* Input Y values */ double yout[ 2 ]; /* Output Y values */ int lbnd_in[ 2 ]; /* Lower bounds of input array */ int np; /* Number of positions to transform */ int odimx; /* Output map X dimension in pixels */ int odimy; /* Output map Y dimension in pixels */ int ox; /* Output X GRID index (-1) containing current bolo */ int oy; /* Output Y GRID index (-1) containing current bolo */ int ubnd_in[ 2 ]; /* Upper bounds of input array */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Begin an AST context. */ astBegin; /* Get the dimensions of the output map. */ odimx = oubnd[ 0 ] - olbnd[ 0 ] + 1; odimy = oubnd[ 1 ] - olbnd[ 1 ] + 1; /* Get the dimensions of the bolometer array. */ idimx = (data->dims)[ 0 ]; idimy = (data->dims)[ 1 ]; /* Store integer bounds within the input bolometer GRID system. */ lbnd_in[ 0 ] = 1; lbnd_in[ 1 ] = 1; ubnd_in[ 0 ] = idimx; ubnd_in[ 1 ] = idimy; /* Determine the number of bolometers. */ nbolo = idimx*idimy; /* Initialise the index of the next LUT element to write. */ ilut = 0; /* Ensure tstep is at least one. */ if( tstep < 1 ) tstep = 1; /* Get the time slice index at which to do the next full calculation. */ itime0 = itime_lo; /* We only need the following AST objects if we will be approximating some caclulations. */ if( tstep > 1 ) { /* We need to find the first good TCS index in order to get the tracking system (Which for SCUBA-2 won't be changing in the sequence) */ itime0 = VAL__BADI; for (itime = itime_lo; itime <= itime_hi; itime++) { JCMTState * slice = &((data->hdr->allState)[itime]); if (!(slice->jos_drcontrol >= 0 && slice->jos_drcontrol & DRCNTRL__POSITION)) { itime0 = itime; break; } } if ((int)itime0 != VAL__BADI) { /* We need a Frame describing absolute tracking system coords. Take a copy of the supplied skyframe (to inherit obslat, obslon, epoch, etc), and then set its system to the tracking system. */ trfrm = astCopy( abskyfrm ); astSetC( trfrm, "System", sc2ast_convert_system( (data->hdr->allState)[itime0].tcs_tr_sys, status ) ); /* Get the Mapping from the tracking system to the output (absolute, since abskyfrm is absolute) sky system. */ fs = astConvert( trfrm, abskyfrm, " " ); if( !fs && *status == SAI__OK ) { *status = SAI__ERROR; errRep( " ", "smf_coords_lut: Failed to convert from " "tracking system to output WCS system.", status ); } tr2skyabs = astSimplify( astGetMapping( fs, AST__BASE, AST__CURRENT ) ); /* For moving targets, we also need a Frame describing offset sky coordinates in the output map, in which the reference point is the current telescope base position. This will involve changing the SkyRef attribute of the Frame for every time slice, so take a copy of the supplied SkyFrame to avoid changing it. */ if( moving ) { offsky = astCopy( abskyfrm ); astSet( offsky, "SkyRefIs=Origin" ); } /* Create the Mapping from offsets within the output map sky coordinate system output to map GRID coords to output map GRID coords. This uses a ShiftMap to convert from output PIXEL coords (produced by "oskymap") to output GRID coords. Note, if the target is moving, "oskymap" maps from sky *offsets* to output map PIXEL coords. */ shift[ 0 ] = 1.5 - olbnd[ 0 ]; shift[ 1 ] = 1.5 - olbnd[ 1 ]; bsmap = astCmpMap( oskymap, astShiftMap( 2, shift, " " ), 1, " " ); } else { /* We did not find any good TCS data so force us into tstep==1 case and end up with VAL__BADI for every element. */ tstep = 1; itime0 = itime_lo; msgOutiff(MSG__VERB, "", "All time slices from %zu -- %zu had bad TCS data.", status, (size_t)itime_lo, (size_t)itime_hi ); } } /* Allocate memory to hold the (x,y) output map grid coords at each bolo for a single time slice. */ outmapcoord = astMalloc( sizeof( *outmapcoord )*2*nbolo ); /* Initialise boresight position for the benefit of the tstep == 1 case. */ bsx = bsy = 0.0; bsx0 = bsy0 = AST__BAD; bsxlast = bsylast = AST__BAD; /* If lon and lat arrays are to be returned, allocate memory to hold the grid coords of every bolometer for a single sample. */ if( lon && lat ) { wgx = astMalloc( nbolo*sizeof( *wgx ) ); wgy = astMalloc( nbolo*sizeof( *wgy ) ); } /* Loop round each time slice. */ state = data->hdr->allState + itime_lo; for( itime = itime_lo; itime <= itime_hi && *status == SAI__OK; itime++,state++ ) { /* No need to get the boresight position if we are doing full calculations at every time slice. If this time slice has bad TCS data then the problem will be caught in smf_rebin_totmap. */ if( tstep > 1 ) { /* Transform the current boresight and base (if moving) positions from tracking coords to absolute output map sky coords. */ xin[ 0 ] = state->tcs_tr_ac1; yin[ 0 ] = state->tcs_tr_ac2; if( moving ) { xin[ 1 ] = state->tcs_tr_bc1; yin[ 1 ] = state->tcs_tr_bc2; np = 2; } else { np = 1; } astTran2( tr2skyabs, np, xin, yin, 1, xout, yout ); /* If the target is moving, find the offsets within the output map sky coordinate system, from base to boresight at the current time slice. These offsets become the new "boresight" position (in xin/yin). Guard against assigning bad values to the ref pos, which can cause AST to report errors. */ if( moving && xout[ 1 ] != AST__BAD && yout[ 1 ] != AST__BAD ) { /* Set the current telescope base position as the reference point in "offsky". Then get the Mapping from absolute to offset sky coords. */ astSetD( offsky, "SkyRef(1)", xout[ 1 ] ); astSetD( offsky, "SkyRef(2)", yout[ 1 ] ); offmap = astSkyOffsetMap( offsky ); /* Use this Mapping to convert the current boresight position from absolute sky coords to offsets from the current base position. */ astTran2( offmap, 1, xout, yout, 1, xin, yin ); /* Annul the Mapping to avoid keep the number of AST objects to a minimum. */ offmap = astAnnul( offmap ); /* If the target is stationary, we can just use the absolute boresight position as it is. */ } else { xin[ 0 ] = xout[ 0 ]; yin[ 0 ] = yout[ 0 ]; } /* Transform the above boresight position from output map sky coords to output map GRID coords. */ astTran2( bsmap, 1, xin, yin, 1, &bsx, &bsy ); } /* If we have reached the next full calculation... */ if( itime == itime0 ) { /* Calculate the full bolometer to map-pixel transformation for the current time slice */ fullmap = smf_rebin_totmap( data, itime, abskyfrm, oskymap, moving, fts_port, status ); /* If succesful, use it to transform every bolometer position from bolo GRID coords to output map GRID coords. */ if( fullmap ) { itime0 += tstep; astTranGrid( fullmap, 2, lbnd_in, ubnd_in, 0.1, 1000000, 1, 2, nbolo, outmapcoord ); fullmap = astAnnul( fullmap ); /* Record the boresight grid coords at this time slice. */ bsx0 = bsx; bsy0 = bsy; /* If we cannot determine a full Mapping for this time slice, move the node to the next time slice (otherwise we would loose all the data to the next node), and set the boresight position bad to indicate we have no mapping for this time slice. */ } else { itime0++; bsx0 = AST__BAD; bsy0 = AST__BAD; } } /* Get the offset from the boresight position at the previous full calculation and the current boresight position, in output map GRID coords. */ dx = ( bsx != AST__BAD && bsx0 != AST__BAD ) ? bsx - bsx0 : AST__BAD; dy = ( bsy != AST__BAD && bsy0 != AST__BAD ) ? bsy - bsy0 : AST__BAD; /* Work out the scan direction based on the GRID offsets between this and the previous time slice. Angles are calculated using atan2, with values ranging from -pi to +pi. */ if( angle ) { double theta = AST__BAD; dxlast = ( bsx != AST__BAD && bsxlast != AST__BAD ) ? bsx - bsxlast : AST__BAD; dylast = ( bsy != AST__BAD && bsylast != AST__BAD ) ? bsy - bsylast : AST__BAD; if( dxlast != AST__BAD && dylast != AST__BAD && !( !dxlast && !dylast ) ) { theta = atan2( dylast, dxlast ); } angle[itime-itime_lo] = theta; bsxlast = bsx; bsylast = bsy; } /* Initialise pointers to the place to store the final grid coords for each bolometer. */ pgx = wgx; pgy = wgy; /* Loop round all bolometers. */ px = outmapcoord; py = outmapcoord + nbolo; for( ibolo = 0; ibolo < nbolo; ibolo++ ){ /* If good, get the x and y output map GRID coords for this bolometer. */ if( dx != AST__BAD && dy != AST__BAD ) { x = *(px++) + dx; y = *(py++) + dy; /* If required, store them so that we can convert them into lon/lat values later. */ if( pgx && pgy ) { *(pgx++) = x; *(pgy++) = y; } /* Find the grid indices (minus one) of the output map pixel containing the mapped bolo grid coords. One is subtracted in order to simplify the subsequent calculation of the vector index. */ ox = (int) ( x - 0.5 ); oy = (int) ( y - 0.5 ); /* Check it is within the output map */ if( ox >= 0 && ox < odimx && oy >= 0 && oy < odimy ) { /* Find the 1-dimensional vector index into the output array for this pixel and store in the next element of the returned LUT. */ lut[ ilut++ ] = ox + oy*odimx; /* Store a bad value for points that are off the edge of the output map. */ } else { lut[ ilut++ ] = VAL__BADI; } /* If good, store a bad index in the LUT and move on to the next pixel. */ } else { lut[ ilut++ ] = VAL__BADI; px++; py++; if( pgx && pgy ) { *(pgx++) = AST__BAD; *(pgy++) = AST__BAD; } } } /* If required transform the grid coords into (lon,lat) coords and store in the relevant elements of the supplied "lon" and "lat" arrays. */ if( wgx && wgy ) { astTran2( oskymap, nbolo, wgx, wgy, 0, lon + itime*nbolo, lat + itime*nbolo ); /* Convert from rads to degs. */ pgx = lon + itime*nbolo; pgy = lat + itime*nbolo; for( ibolo = 0; ibolo < nbolo; ibolo++ ) { if( *pgx != AST__BAD && *pgy != AST__BAD ) { *(pgx++) *= AST__DR2D; *(pgy++) *= AST__DR2D; } else { pgx++; pgy++; } } } } /* To obtain a reasonable value for the first entry of angle, we simply duplicate the second value. */ if( angle && (itime_hi > itime_lo) ) { angle[0] = angle[1]; } /* Free remaining work space. */ outmapcoord = astFree( outmapcoord ); wgx = astFree( wgx ); wgy = astFree( wgy ); /* Export the WCS pointer in the data header since it will be annulled at a higher level. Note the FrameSet pointer may be null if the last full calculation was for a slice with bad telescope data. */ if( data->hdr->wcs ) astExport( data->hdr->wcs ); /* End the AST context. */ astEnd; }
void kpg1Kygp1( AstKeyMap *keymap, Grp **igrp, const char *prefix, int *status ){ /* *+ * Name: * kpg1Kygp1 * Purpose: * Creates a GRP group holding keyword/value pairs read from an AST KeyMap. * Language: * C. * Invocation: * void kpg1Kygp1( AstKeyMap *keymap, Grp **igrp, const char *prefix, * int *status ) * Description: * This function is the inverse of kpg1Kymp1. It extracts the values * from the supplied AST KeyMap and creates a set of "name=value" strings * which it appends to a supplied group (or creates a new group). If * the KeyMap contains nested KeyMaps, then the "name" associated with * each primitive value stored in the returned group is a hierarchical * list of component names separated by dots. * Arguments: * keymap * A pointer to the KeyMap. Numerical entries which have bad values * (VAL__BADI for integer entries or VAL__BADD for floating point * entries) are not copied into the group. * igrp * A location at which is stored a pointer to the Grp structure * to which the name=value strings are to be appended. A new group is * created and a pointer to it is returned if the supplied Grp * structure is not valid. * prefix * A string to append to the start of each key extracted from the * supplied KeyMap. If NULL, no prefix is used. * status * Pointer to the inherited status value. * Notes: * - This function provides a private implementation for the public * KPG1_KYGRP Fortran routine and kpg1Kygrp C function. * - Entries will be stored in the group in alphabetical order * Copyright: * Copyright (C) 2008,2010 Science & Technology Facilities Council. * Copyright (C) 2005 Particle Physics & Astronomy Research Council. * All Rights Reserved. * Licence: * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be * useful,but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA * 02110-1301, USA * Authors: * DSB: David S. Berry * TIMJ: Tim Jenness (JAC, Hawaii) * {enter_new_authors_here} * History: * 7-NOV-2005 (DSB): * Original version. * 15-JUL-2008 (TIMJ): * Tweak to GRP C API. * 2010-07-19 (TIMJ): * Handle vector keymap entries. * 2010-08-12 (TIMJ): * Store entries in group in alphabetical order. * 13-AUG-2010 (DSB): * Re-instate the original SortBy value before exiting. * {enter_further_changes_here} * Bugs: * {note_any_bugs_here} *- */ /* Local Variables: */ AstObject *obj; /* Pointer to nested AST Object */ char *oldsortby; /* The old value of the KeyMap's SortBy attribute */ char *text; /* Sum of concatenated strings */ const char *key; /* Key string for current entry in KeyMap */ const char *value; /* Value of current entry in KeyMap */ double dval; /* Double value */ int *old_status; /* Pointer to original status variable */ int bad; /* Is the numerical entry value bad? */ int i; /* Index into supplied KeyMap */ int ival; /* Integer value */ int n; /* Number of entries in the KeyMap */ int nc; /* Length of "text" excluding trailing null */ int type; /* Data type of current entry in KeyMap */ int valid; /* Is the supplied GRP structure valid? */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Make AST use the Fortran status variable. */ old_status = astWatch( status ); /* Create a new GRP group if required. */ valid = grpValid( *igrp, status ); if( !valid ) *igrp = grpNew( "Created by kpg1_Kygp1", status ); /* If set, save the old SortBy value and then ensure alphabetical sorting. We need to take a copy of the original string since the buffer in which the string is stored may be re-used by subsequent incocations of astGetC. */ if( astTest( keymap, "SortBy" ) ) { nc = 0; oldsortby = astAppendString( NULL, &nc, astGetC( keymap, "SortBy" ) ); } else { oldsortby = NULL; } astSet( keymap, "SortBy=KeyUp" ); /* Get the number of entries in the KeyMap. */ n = astMapSize( keymap ); /* Loop round all the entries in the KeyMap.*/ for( i = 0; i < n; i++ ) { /* Get the name and type of the current KeyMap entry. */ key = astMapKey( keymap, i ); type = astMapType( keymap, key ); /* If the entry is an AST Object, get a pointer to it.*/ if( type == AST__OBJECTTYPE && astMapGet0A( keymap, key, &obj ) ) { /* If it is a nested KeyMap, update the prefix and call this function recursively. We ignore other forms of AST Objects. */ if( astIsAKeyMap( obj ) ) { nc = 0; text = astAppendString( NULL, &nc, prefix ); text = astAppendString( text, &nc, key ); text = astAppendString( text, &nc, "." ); kpg1Kygp1( (AstKeyMap *) obj, igrp, text, status ); text = astFree( text ); } /* If it is a primitive, format it and add it to the group. */ } else { /* If it is a numerical type, see if it has a bad value. */ bad = 0; if( type == AST__INTTYPE && astMapGet0I( keymap, key, &ival ) ){ if( ival == VAL__BADI ) bad = 1; } else if( type == AST__DOUBLETYPE && astMapGet0D( keymap, key, &dval ) ){ if( dval == VAL__BADD ) bad = 1; } /* If it not bad, get its formatted value. We also make sure that astMapGet0C returns true because we intend to skip undefined values. */ if( !bad && astMapGet0C( keymap, key, &value ) ) { size_t length; /* Write the key and equals sign to a buffer */ nc = 0; text = astAppendString( NULL, &nc, prefix ); text = astAppendString( text, &nc, key ); text = astAppendString( text, &nc, "=" ); length = astMapLength( keymap, key ); if( length > 1 ) { /* Vector so we need to use (a,b,c) syntax */ char thiselem[GRP__SZNAM+1]; size_t l; text = astAppendString( text, &nc, "("); for ( l = 0; l < length; l++) { if( astMapGetElemC( keymap, key, sizeof(thiselem), l, thiselem ) ) { text = astAppendString( text, &nc, thiselem ); } /* always deal with the comma. Even if value was undef we need to put in the comma */ if( l < (length - 1) ) { text = astAppendString( text, &nc, "," ); } } text = astAppendString( text, &nc, ")"); } else { /* Scalar */ text = astAppendString( text, &nc, value ); } /* Put it in the group. */ grpPut1( *igrp, text, 0, status ); text = astFree( text ); } } } /* If it was originally set, re-instate the old SortBy value in the KeyMap, then free the memory. Otherwise, clear the SortBy attribute. */ if( oldsortby ) { astSetC( keymap, "SortBy", oldsortby ); oldsortby = astFree( oldsortby ); } else { astClear( keymap, "SortBy" ); } /* Make AST use its original status variable. */ astWatch( old_status ); }
/* Returns an array holding the angle (rad.s) from north to focal plane Y, measured positive in the sense of rotation from focal plane Y to focal plane X, for every bolometer sample in a smfData. The values are bolo ordered so that "bstride" is 1 and "tstsride" is nbolo. The returned array should be freed using astFre when no longer needed. */ static double *smf1_calcang( smfData *data, int *status ){ /* Local Variables: */ AstFrameSet *fpfset; AstFrameSet *wcs; AstMapping *g2s; AstMapping *s2f; const char *usesys; dim_t ibolo; dim_t itime; dim_t nbolo; dim_t ncol; dim_t ntslice; double *fx2; double *fx; double *fy2; double *fy; double *gx; double *gy; double *pr; double *result; double *sx; double *sy; int subsysnum; /* Check the inherited status. */ if( *status != SAI__OK ) return NULL; /* Get the number of bolometers and time slices, together with the strides between adjacent bolometers and adjacent time slices. */ smf_get_dims( data, NULL, &ncol, &nbolo, &ntslice, NULL, NULL, NULL, status ); /* Allocate the returned array. */ result = astMalloc( nbolo*ntslice*sizeof( *result ) ); /* Allocate arrays to hold the grid coords for every bolometer. */ gx = astMalloc( nbolo*sizeof( *gx ) ); gy = astMalloc( nbolo*sizeof( *gy ) ); /* Allocate arrays to hold the sky coords for every bolometer. */ sx = astMalloc( nbolo*sizeof( *sx ) ); sy = astMalloc( nbolo*sizeof( *sy ) ); /* Allocate arrays to hold the focal plane coords for every bolometer. */ fx = astMalloc( nbolo*sizeof( *fx ) ); fy = astMalloc( nbolo*sizeof( *fy ) ); /* Allocate arrays to hold the focal plane coords of a point slightly to the north of every bolometer. */ fx2 = astMalloc( nbolo*sizeof( *fx2 ) ); fy2 = astMalloc( nbolo*sizeof( *fy2 ) ); /* Get the AST code equivalent to the tracking system. */ usesys = sc2ast_convert_system( (data->hdr->allState)[0].tcs_tr_sys, status ); if( *status == SAI__OK ) { /* Initialise the arrays holding the grid coords for every bolometer. */ for( ibolo = 0; ibolo < nbolo; ibolo++ ) { gx[ ibolo ] = ibolo % ncol + 1; gy[ ibolo ] = ibolo / ncol + 1; } /* Get the GRID->focal plane FrameSet (the same for every time slice). */ smf_find_subarray( data->hdr, NULL, 0, &subsysnum, status ); sc2ast_createwcs( subsysnum, NULL, data->hdr->instap, data->hdr->telpos, NO_FTS, &fpfset, status); /* Use this to transform the bolometrer GRID coords to focal plane. */ astTran2( fpfset, nbolo, gx, gy, 1, fx, fy ); /* Loop over all time slices. */ pr = result; for( itime = 0; itime < ntslice; itime++ ) { /* Get the WCS FrameSet for the time slice, and set its current Frame to the tracking frame. */ smf_tslice_ast( data, itime, 1, NO_FTS, status ); wcs = data->hdr->wcs; if( wcs ) { astSetC( wcs, "System", usesys ); /* Get the mapping from GRID to SKY. */ astBegin; g2s = astSimplify( astGetMapping( wcs, AST__BASE, AST__CURRENT )); /* Get the mapping from SKY to focal plane (x,y) (the index of the FPLANE Frame is fixed at 3 by file sc2ast.c). */ s2f = astSimplify( astGetMapping( wcs, AST__CURRENT, 3 ) ); /* Transform the grid coords of all bolometers to SKY coordinates using the FrameSet. */ astTran2( g2s, nbolo, gx, gy, 1, sx, sy ); /* Increment the sky positions slightly to the north. */ for( ibolo = 0; ibolo < nbolo; ibolo++ ) sy[ ibolo ] += 1.0E-6; /* Transform these modified sky coordinates to focal plane. */ astTran2( s2f, nbolo, sx, sy, 1, fx2, fy2 ); astEnd; /* Loop round all bolometers. */ for( ibolo = 0; ibolo < nbolo; ibolo++ ) { /* Get the angle from north to focal plane Y, measured positive in the sense of rotation from focal plane Y to focal plane X. */ if( fx[ibolo] != VAL__BADD && fy[ibolo] != VAL__BADD && fx2[ibolo] != VAL__BADD && fy2[ibolo] != VAL__BADD ) { *(pr++) = atan2( fx[ibolo] - fx2[ibolo], fy2[ibolo] - fy[ibolo] ); } else { *(pr++) = VAL__BADD; } } } else { for( ibolo = 0; ibolo < nbolo; ibolo++ ) *(pr++) = VAL__BADD; } } } /* Free resources. */ fx = astFree( fx ); fy = astFree( fy ); fx2 = astFree( fx2 ); fy2 = astFree( fy2 ); sx = astFree( sx ); sy = astFree( sy ); gx = astFree( gx ); gy = astFree( gy ); /* Return the array of angle values. */ return result; }
void smf_calc_iqu( ThrWorkForce *wf, smfData *data, int block_start, int block_end, int ipolcrd, int qplace, int uplace, int iplace, NdgProvenance *oprov, AstFitsChan *fc, int pasign, double paoff, double angrot, int submean, int *status ){ /* Local Variables: */ AstFrameSet *wcs; /* WCS FrameSet for output NDFs */ AstWinMap *wm; /* Mapping to reverse the X GRID axis */ const JCMTState *state; /* JCMTState info for current time slice */ dim_t nbolo; /* No. of bolometers */ dim_t ncol; /* No. of columns of bolometers */ dim_t nrow; /* No. of rows of bolometers */ dim_t ntslice; /* Number of time-slices in data */ double *ipi; /* Pointer to output I array */ double *ipq; /* Pointer to output Q array */ double *ipu; /* Pointer to output U array */ double ina[ 2 ]; /* Bolometer coords at bottom left */ double inb[ 2 ]; /* Bolometer coords at top right */ double outa[ 2 ]; /* NDF GRID coords at bottom left */ double outb[ 2 ]; /* NDF GRID coords at top right */ int bstep; /* Bolometer step between threads */ int el; /* Number of mapped array elements */ int indfi; /* Identifier for NDF holding I values */ int indfq; /* Identifier for NDF holding Q values */ int indfu; /* Identifier for NDF holding Q values */ int itime; /* Time slice index */ int iworker; /* Index of a worker thread */ int lbnd[ 2 ]; /* Lower pixel bounds of output NDF */ int ntime; /* Time slices to check */ int nworker; /* No. of worker threads */ int old; /* Data has old-style POL_ANG values? */ int ubnd[ 2 ]; /* Upper pixel bounds of output NDF */ size_t bstride; /* Stride between adjacent bolometer values */ size_t tstride; /* Stride between adjacent time slice values */ smfCalcIQUJobData *job_data = NULL; /* Pointer to all job data */ smfCalcIQUJobData *pdata = NULL;/* Pointer to next job data */ smfHead *hdr; /* Pointer to data header this time slice */ double *mean; int tstep; /* Time slice step between threads */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Convenience pointers. */ hdr = data->hdr; /* Obtain number of time slices - will also check for 3d-ness. Also get the dimensions of the bolometer array and the strides between adjacent bolometer values. */ smf_get_dims( data, &nrow, &ncol, &nbolo, &ntslice, NULL, &bstride, &tstride, status ); /* Report an error if the block of time slices extends of either end. */ if( block_start < 0 || block_end >= (int) ntslice ) { if( *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "S", block_start ); msgSeti( "E", block_end ); msgSeti( "N", ntslice ); errRep( " ", "smf_calc_iqu: invalid block of time slices - ^S to " "^E (^N time slices are available).", status ); } } /* Create the output NDFs. Each one is a 2D array with dimensions equal to the bolometer array. */ lbnd[ 0 ] = 1; lbnd[ 1 ] = 1; ubnd[ 0 ] = ncol; ubnd[ 1 ] = nrow; ndfNew( "_DOUBLE", 2, lbnd, ubnd, &qplace, &indfq, status ); ndfNew( "_DOUBLE", 2, lbnd, ubnd, &uplace, &indfu, status ); if( iplace != NDF__NOPL ) { ndfNew( "_DOUBLE", 2, lbnd, ubnd, &iplace, &indfi, status ); } else { indfi = NDF__NOID; } /* Store any supplied provenance in all NDFs. */ if( oprov ) { ndgWriteProv( oprov, indfq, 1, status ); ndgWriteProv( oprov, indfu, 1, status ); if( indfi != NDF__NOID ) ndgWriteProv( oprov, indfi, 1, status ); } /* Store any supplied FITS headers in all NDFs.*/ if( fc && astGetI( fc, "NCard" ) > 0 ) { kpgPtfts( indfq, fc, status ); kpgPtfts( indfu, fc, status ); if( indfi != NDF__NOID ) kpgPtfts( indfi, fc, status ); } /* Store the WCS frameSet in all NDFs. First get the FrameSet for the central time slice in the block, and set its current Frame to the tracking frame. */ smf_tslice_ast( data, ( block_start + block_end )/2, 1, status); astSetC( hdr->wcs, "System", sc2ast_convert_system( (data->hdr->allState)[0].tcs_tr_sys, status ) ); /* Take a copy and then reverse the X axis of the GRID Frame by remaping the base Frame using a WinMap. This produces a pixel grid such as you would see by looking up at the sky from underneath the array, rather than looking down at the ground from above the array. */ wcs = astCopy( hdr->wcs ); ina[ 0 ] = 1.0; inb[ 0 ] = ncol; ina[ 1 ] = 1.0; inb[ 1 ] = nrow; outa[ 0 ] = ncol; outb[ 0 ] = 1.0; outa[ 1 ] = 1.0; outb[ 1 ] = nrow; wm = astWinMap( 2, ina, inb, outa, outb, " " ); astRemapFrame( wcs, AST__BASE, wm ); wm = astAnnul( wm ); /* Store the FrameSet in the output NDFs, then annull the copy. */ ndfPtwcs( wcs, indfq, status ); ndfPtwcs( wcs, indfu, status ); if( indfi != NDF__NOID ) ndfPtwcs( wcs, indfi, status ); wcs = astAnnul( wcs ); /* Map the Data array in each NDF. */ ndfMap( indfq, "Data", "_DOUBLE", "WRITE", (void **) &ipq, &el, status ); ndfMap( indfu, "Data", "_DOUBLE", "WRITE", (void **) &ipu, &el, status ); if( indfi != NDF__NOID ) { ndfMap( indfi, "Data", "_DOUBLE", "WRITE", (void **) &ipi, &el, status ); } else { ipi = NULL; } /* If required, allocate memory to hold the mean bolometer value at each time slice. */ mean = submean ? astMalloc( ntslice*sizeof( *mean ) ) : NULL; /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); /* Check the above pointers can be used safely. */ if( *status == SAI__OK ) { /* Go through the first thousand POL_ANG values to see if they are in units of radians (new data) or arbitrary encoder units (old data). They are assumed to be in radians if no POL_ANG value is larger than 20. */ old = 0; state = hdr->allState; ntime = ( ntslice > 1000 ) ? 1000 : ntslice; for( itime = 0; itime < ntime; itime++,state++ ) { if( state->pol_ang > 20 ) { old = 1; msgOutif( MSG__VERB, ""," POL2 data contains POL_ANG values " "in encoder units - converting to radians.", status ); break; } } /* If required, find the mean bolometer value at each time slice. */ if( submean ) { /* Determine which time-slices are to be processed by which threads. */ tstep = ntslice/nworker; if( tstep < 1 ) tstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->block_start = iworker*tstep; if( iworker < nworker - 1 ) { pdata->block_end = pdata->block_start + tstep - 1; } else { pdata->block_end = ntslice - 1; } } /* Store all the other info needed by the worker threads, and submit the jobs to calculate the Q and U values in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->bstride = bstride; pdata->dat = data->pntr[0]; pdata->nbolo = nbolo; pdata->qua = smf_select_qualpntr( data, NULL, status );; pdata->tstride = tstride; pdata->mean = mean; pdata->action = 1; /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calc_iqu_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); } /* Determine which bolometers are to be processed by which threads. */ bstep = nbolo/nworker; if( bstep < 1 ) bstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->b1 = iworker*bstep; pdata->b2 = pdata->b1 + bstep - 1; } /* Ensure that the last thread picks up any left-over bolometers */ pdata->b2 = nbolo - 1; /* Store all the other info needed by the worker threads, and submit the jobs to calculate the Q and U values in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->bstride = bstride; pdata->dat = data->pntr[0];; pdata->nbolo = nbolo; pdata->qua = smf_select_qualpntr( data, NULL, status );; pdata->tstride = tstride; pdata->allstates = hdr->allState; pdata->ipq = ipq; pdata->ipu = ipu; pdata->ipi = ipi; pdata->ipolcrd = ipolcrd; pdata->block_start = block_start; pdata->block_end = block_end; pdata->old = old; pdata->ncol = ncol; pdata->pasign = pasign ? +1: -1; pdata->paoff = paoff; pdata->angrot = angrot; pdata->action = 0; pdata->mean = mean; /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calc_iqu_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); } /* Add POLANAL Frames to the WCS FrameSet in each output NDF. This Frame is used by POLPACK to determine the reference direction of the Stokes vectors (focal plane Y in this case, i.e. zero-based axis 1 ). */ smf_polext( indfq, 0, 0.0, "FPLANE", 1, status ); smf_polext( indfu, 0, 0.0, "FPLANE", 1, status ); if( ipi ) smf_polext( indfi, 0, 0.0, "FPLANE", 1, status ); /* Free the two output NDFs. */ ndfAnnul( &indfq, status ); ndfAnnul( &indfu, status ); if( ipi ) ndfAnnul( &indfi, status ); /* Free other resources. */ job_data = astFree( job_data ); mean = astFree( mean ); }
void smurf_unmakemap( int *status ) { /* Local Variables */ AstFrameSet *wcsin = NULL; /* WCS Frameset for input cube */ AstMapping *skymap; /* GRID->SkyFrame Mapping from input WCS */ AstSkyFrame *abskyfrm; /* Input SkyFrame (always absolute) */ AstSkyFrame *skyfrm = NULL;/* SkyFrame from the input WCS Frameset */ Grp *igrp1 = NULL; /* Group of input sky files */ Grp *igrp2 = NULL; /* Group of input template files */ Grp *igrpc = NULL; /* Group of input COM files */ Grp *igrpg = NULL; /* Group of input GAI files */ Grp *igrpq = NULL; /* Group of input Q sky files */ Grp *igrpu = NULL; /* Group of input U sky files */ Grp *ogrp = NULL; /* Group containing output file */ HDSLoc *cloc = NULL; /* HDS locator for component ipdata structure */ HDSLoc *iploc = NULL; /* HDS locator for top level ipdata structure */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ char ipdata[ 200 ]; /* Text buffer for IPDATA value */ char pabuf[ 10 ]; /* Text buffer for parameter value */ char subarray[ 5 ]; /* Name of SCUBA-2 subarray (s8a,s8b,etc) */ dim_t iel; /* Index of next element */ dim_t ndata; /* Number of elements in array */ dim_t ntslice; /* Number of time slices in array */ double *ang_data = NULL; /* Pointer to the FP orientation angles */ double *angc_data = NULL; /* Pointer to the instrumental ANGC data */ double *c0_data = NULL; /* Pointer to the instrumental C0 data */ double *gai_data = NULL; /* Pointer to the input GAI map */ double *in_data = NULL; /* Pointer to the input I sky map */ double *inc_data = NULL; /* Pointer to the input COM data */ double *inq_data = NULL; /* Pointer to the input Q sky map */ double *inu_data = NULL; /* Pointer to the input U sky map */ double *outq_data = NULL; /* Pointer to the Q time series data */ double *outu_data = NULL; /* Pointer to the U time series data */ double *p0_data = NULL; /* Pointer to the instrumental P0 data */ double *p1_data = NULL; /* Pointer to the instrumental P1 data */ double *pd; /* Pointer to next element */ double *pq = NULL; /* Pointer to next Q time series value */ double *pu = NULL; /* Pointer to next U time series value */ double *qinst_data = NULL; /* Pointer to the instrumental Q data */ double *uinst_data = NULL; /* Pointer to the instrumental U data */ double amp16; /* Amplitude of 16 Hz signal */ double amp2; /* Amplitude of 2 Hz signal */ double amp4; /* Amplitude of 4 Hz signal */ double angrot; /* Angle from focal plane X axis to fixed analyser */ double paoff; /* WPLATE value corresponding to POL_ANG=0.0 */ double params[ 4 ]; /* astResample parameters */ double phase16; /* Phase of 16 Hz signal */ double phase2; /* Phase of 2 Hz signal */ double phase4; /* Phase of 4 Hz signal */ double sigma; /* Standard deviation of noise to add to output */ int alignsys; /* Align data in the map's system? */ int cdims[ 3 ]; /* Common-mode NDF dimensions */ int dims[ NDF__MXDIM ]; /* NDF dimensions */ int flag; /* Was the group expression flagged? */ int gdims[ 3 ]; /* GAI model NDF dimensions */ int harmonic; /* The requested harmonic */ int ifile; /* Input file index */ int indf; /* Input sky map NDF identifier */ int indfangc; /* IP ANGC values NDF identifier */ int indfc0; /* IP C0 values NDF identifier */ int indfc; /* Input COM NDF identifier */ int indfcs; /* NDF identifier for matching section of COM */ int indfg; /* Input GAI NDF identifier */ int indfin; /* Input template cube NDF identifier */ int indfiq; /* Input instrumental Q NDF */ int indfiu; /* Input instrumental U NDF */ int indfout; /* Output cube NDF identifier */ int indfp0; /* IP P0 values NDF identifier */ int indfp1; /* IP P1 values NDF identifier */ int indfq; /* Input Q map NDF identifier */ int indfu; /* Input U map NDF identifier */ int interp = 0; /* Pixel interpolation method */ int lbndc[ 3 ]; /* Array of lower bounds of COM NDF */ int moving; /* Is the telescope base position changing? */ int ndim; /* Number of pixel axes in NDF */ int ndimc; /* Number of pixel axes in common-mode NDF */ int ndimg; /* Number of pixel axes in GAI NDF */ int nel; /* Number of elements in array */ int nelc; /* Number of elements in COM array */ int nelg; /* Number of elements in GAI array */ int nelqu; /* Number of elements in Q or U array */ int ngood; /* No. of good values in putput cube */ int nparam = 0; /* No. of parameters required for interpolation scheme */ int pasign; /* Indicates sense of POL_ANG value */ int sdim[ 2 ]; /* Array of significant pixel axes */ int slbnd[ 2 ]; /* Array of lower bounds of input map */ int subnd[ 2 ]; /* Array of upper bounds of input map */ int ubndc[ 3 ]; /* Array of upper bounds of COM NDF */ size_t ncom; /* Number of com files */ size_t ngai; /* Number of gai files */ size_t nskymap; /* Number of supplied sky cubes */ size_t outsize; /* Number of files in output group */ size_t size; /* Number of files in input group */ smfData *odata = NULL; /* Pointer to output data struct */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context */ astBegin; /* Begin an NDF context. */ ndfBegin(); /* Find the number of cores/processors available and create a pool of threads of the same size. */ wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status ); /* Get an identifier for the input NDF. We use NDG (via kpg1Rgndf) instead of calling ndfAssoc directly since NDF/HDS has problems with file names containing spaces, which NDG does not have. */ kpg1Rgndf( "IN", 1, 1, "", &igrp1, &nskymap, status ); ndgNdfas( igrp1, 1, "READ", &indf, status ); /* Map the data array in the input sky map. */ ndfMap( indf, "DATA", "_DOUBLE", "READ", (void **) &in_data, &nel, status ); /* Get the WCS FrameSet from the sky map, together with its pixel index bounds. */ kpg1Asget( indf, 2, 0, 1, 1, sdim, slbnd, subnd, &wcsin, status ); /* Check the current Frame is a SKY frame. */ skyfrm = astGetFrame( wcsin, AST__CURRENT ); if( !astIsASkyFrame( skyfrm ) && *status == SAI__OK ) { ndfMsg( "N", indf ); *status = SAI__ERROR; errRep( " ", " Current Frame in ^N is not a SKY Frame.", status ); } /* Get a copy of the current frame that represents absolute coords rather than offsets. We assume the target is moving if the map represents offsets. */ moving = ( *status == SAI__OK && !strcmp( astGetC( skyfrm, "SkyRefIs" ), "Origin" ) ) ? 1 : 0; abskyfrm = astCopy( skyfrm ); astClear( abskyfrm, "SkyRefIs" ); /* If the ALIGNSYS parameter is TRUE then we align the raw data with the map in the current system of the map, rather than the default ICRS. */ parGet0l( "ALIGNSYS", &alignsys, status ); if( alignsys ) astSetC( abskyfrm, "AlignSystem", astGetC( abskyfrm, "System" ) ); /* Get the Mapping from the Sky Frame to grid axis in the iput map. */ skymap = astGetMapping( wcsin, AST__CURRENT, AST__BASE ); /* Get the pixel interpolation scheme to use. */ parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC," "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS", 1, pabuf, 10, status ); if( !strcmp( pabuf, "NEAREST" ) ) { interp = AST__NEAREST; nparam = 0; } else if( !strcmp( pabuf, "LINEAR" ) ) { interp = AST__LINEAR; nparam = 0; } else if( !strcmp( pabuf, "SINC" ) ) { interp = AST__SINC; nparam = 1; } else if( !strcmp( pabuf, "SINCSINC" ) ) { interp = AST__SINCSINC; nparam = 2; } else if( !strcmp( pabuf, "SINCCOS" ) ) { interp = AST__SINCCOS; nparam = 2; } else if( !strcmp( pabuf, "SINCGAUSS" ) ) { interp = AST__SINCGAUSS; nparam = 2; } else if( !strcmp( pabuf, "SOMB" ) ) { interp = AST__SOMB; nparam = 1; } else if( !strcmp( pabuf, "SOMBCOS" ) ) { interp = AST__SOMBCOS; nparam = 2; } else if( *status == SAI__OK ) { nparam = 0; *status = SAI__ERROR; msgSetc( "V", pabuf ); errRep( "", "Support not available for INTERP = ^V (programming " "error)", status ); } /* Get an additional parameter vector if required. */ if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status ); /* Get a group of reference time series files to use as templates for the output time series files.*/ ndgAssoc( "REF", 1, &igrp2, &size, &flag, status ); /* Get output file(s) */ kpg1Wgndf( "OUT", igrp2, size, size, "More output files required...", &ogrp, &outsize, status ); /* Get he noise level to add to the output data. */ parGet0d( "SIGMA", &sigma, status ); /* Get any Q and U input maps. */ if( *status == SAI__OK ) { kpg1Rgndf( "QIN", 1, 1, "", &igrpq, &nskymap, status ); ndgNdfas( igrpq, 1, "READ", &indfq, status ); ndfMap( indfq, "DATA", "_DOUBLE", "READ", (void **) &inq_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "Q", indfq ); *status = SAI__ERROR; errRep( "", "Q image '^Q' is not the same size as the I image.", status ); } kpg1Rgndf( "UIN", 1, 1, "", &igrpu, &nskymap, status ); ndgNdfas( igrpu, 1, "READ", &indfu, status ); ndfMap( indfu, "DATA", "_DOUBLE", "READ", (void **) &inu_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "U", indfu ); *status = SAI__ERROR; errRep( "", "U image '^U' is not the same size as the I image.", status ); } if( *status == PAR__NULL ) { ndfAnnul( &indfq, status ); ndfAnnul( &indfu, status ); inq_data = NULL; inu_data = NULL; errAnnul( status ); } else { parGet0d( "ANGROT", &angrot, status ); parGet0d( "PAOFF", &paoff, status ); parGet0l( "PASIGN", &pasign, status ); } } /* Get any common-mode files. */ if( *status == SAI__OK ) { kpg1Rgndf( "COM", size, size, "", &igrpc, &ncom, status ); if( *status == PAR__NULL ) { errAnnul( status ); ncom = 0; } } /* Get any GAI files. */ if( *status == SAI__OK ) { kpg1Rgndf( "GAI", size, size, "", &igrpg, &ngai, status ); if( *status == PAR__NULL ) { errAnnul( status ); ngai = 0; } } /* Get any instrumental polarisation files. */ if( *status == SAI__OK ) { /* First see if the user wants to use the "INSTQ/INSTU" scheme for specifying instrumental polarisation. */ ndfAssoc( "INSTQ", "Read", &indfiq, status ); ndfAssoc( "INSTU", "Read", &indfiu, status ); if( *status == PAR__NULL ) { ndfAnnul( &indfiq, status ); ndfAnnul( &indfiu, status ); errAnnul( status ); } else { msgOut( " ", "Using user-defined IP model", status ); ndfDim( indfiq, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfiq ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfiq, "DATA", "_DOUBLE", "READ", (void **) &qinst_data, &nel, status ); } ndfDim( indfiu, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfiu ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfiu, "DATA", "_DOUBLE", "READ", (void **) &uinst_data, &nel, status ); } } /* If not, see if the user wants to use the Johnstone/Kennedy instrumental polarisation model. The IPDATA parameter gives the path to an HDS container file contining NDFs holding the required IP data for all subarrays. */ if( !qinst_data ) { parGet0c( "IPDATA", ipdata, sizeof(ipdata), status ); if( *status == PAR__NULL ) { errAnnul( status ); } else { msgOutf( " ", "Using Johnstone/Kennedy IP model in %s", status, ipdata ); hdsOpen( ipdata, "READ", &iploc, status ); } } } /* Loop round all the template time series files. */ for( ifile = 1; ifile <= (int) size && *status == SAI__OK; ifile++ ) { /* Start a new NDF context. */ ndfBegin(); /* Create the output NDF by propagating everything from the input, except for quality and variance. */ ndgNdfas( igrp2, ifile, "READ", &indfin, status ); ndfMsg( "FILE", indfin ); msgSeti( "THISFILE", ifile ); msgSeti( "NUMFILES", size ); msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE", status ); ndgNdfpr( indfin, "DATA,HISTORY,LABEL,TITLE,WCS,UNITS,EXTENSION(*)", ogrp, ifile, &indfout, status ); ndfAnnul( &indfin, status ); ndfAnnul( &indfout, status ); /* We now re-open the output NDF and then modify its data values. */ smf_open_file( wf, ogrp, ifile, "UPDATE", 0, &odata, status ); /* Issue a suitable message and abort if anything went wrong. */ if( *status != SAI__OK ) { errRep( FUNC_NAME, "Could not open input template file.", status ); break; } else { if( odata->file == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfFile associated with smfData.", status ); break; } else if( odata->hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfHead associated with smfData.", status ); break; } } /* Check the reference time series contains double precision values. */ smf_dtype_check_fatal( odata, NULL, SMF__DOUBLE, status ); /* Get the total number of data elements, and the number of time slices. */ smf_get_dims( odata, NULL, NULL, NULL, &ntslice, &ndata, NULL, NULL, status ); /* Get the subarray name */ smf_fits_getS( odata->hdr, "SUBARRAY", subarray, sizeof(subarray), status ); /* If we are using the Johnstone/Kennedy IP model, open and map the relevant parameter NDFs within the IPDATA container file. */ if( iploc ) { datFind( iploc, subarray, &cloc, status ); ndfFind( cloc, "C0", &indfc0, status ); ndfDim( indfc0, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfc0 ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfc0, "DATA", "_DOUBLE", "READ", (void **) &c0_data, &nel, status ); } ndfFind( cloc, "P0", &indfp0, status ); ndfDim( indfp0, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfp0 ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfp0, "DATA", "_DOUBLE", "READ", (void **) &p0_data, &nel, status ); } ndfFind( cloc, "P1", &indfp1, status ); ndfDim( indfp1, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfp1 ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfp1, "DATA", "_DOUBLE", "READ", (void **) &p1_data, &nel, status ); } ndfFind( cloc, "ANGC", &indfangc, status ); ndfDim( indfangc, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfangc ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfangc, "DATA", "_DOUBLE", "READ", (void **) &angc_data, &nel, status ); } } /* Open any COM file. */ if( ncom ) { ndgNdfas( igrpc, ifile, "READ", &indfc, status ); ndfDim( indfc, 3, cdims, &ndimc, status ); /* Check its dimensions. */ if( *status == SAI__OK ) { if( ndimc == 1 ) { if( cdims[ 0 ] < (int) ntslice ) { *status = SAI__ERROR; ndfMsg( "C", indfc ); ndfMsg( "R", indfin ); msgSeti( "N", cdims[ 0 ] ); msgSeti( "M", ntslice ); errRep( " ", "Supplied COM file (^C) has ^N time-slices, but " "the reference NDF (^R) has ^M time-slices.", status ); } else { ndfBound( indfc, 3, lbndc, ubndc, &ndimc, status ); ubndc[ 0 ] = lbndc[ 0 ] + ntslice - 1; ndfSect( indfc, 1, lbndc, ubndc, &indfcs, status ); } } else if( ndimc == 3 ) { if( cdims[ 0 ] != 1 || cdims[ 1 ] != 1 ) { *status = SAI__ERROR; ndfMsg( "C", indfc ); errRep( " ", "Supplied 3D COM file (^C) has bad " "dimensions for axis 1 and/or 2 (should " "both be 1 pixel long).", status ); } else if( cdims[ 2 ] < (int) ntslice ) { *status = SAI__ERROR; ndfMsg( "C", indfc ); ndfMsg( "R", indfin ); msgSeti( "N", cdims[ 2 ] ); msgSeti( "M", ntslice ); errRep( " ", "Supplied COM file (^C) has ^N time-slices, but " "the reference NDF (^R) has ^M time-slices.", status ); } else { ndfBound( indfc, 3, lbndc, ubndc, &ndimc, status ); ubndc[ 2 ] = lbndc[ 2 ] + ntslice - 1; ndfSect( indfc, 3, lbndc, ubndc, &indfcs, status ); } } else { *status = SAI__ERROR; ndfMsg( "C", indfc ); msgSeti( "N", ndimc ); errRep( " ", "Supplied COM file (^C) has ^N dimensions - " "must be 3.", status ); } } ndfMap( indfcs, "DATA", "_DOUBLE", "READ", (void **) &inc_data, &nelc, status ); } else { indfcs = NDF__NOID; inc_data = NULL; } /* Open any GAI files. */ if( ngai ) { ndgNdfas( igrpg, ifile, "READ", &indfg, status ); ndfDim( indfg, 3, gdims, &ndimg, status ); /* Check its dimensions, and map it if OK. */ if( *status == SAI__OK ) { if( ndimg != 2 ) { *status = SAI__ERROR; ndfMsg( "C", indfg ); msgSeti( "N", ndimg ); errRep( " ", "Supplied GAI file (^C) has ^N dimensions - " "must be 2.", status ); } else if( gdims[ 0 ] != 32 || gdims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "C", indfg ); errRep( " ", "Supplied GAI file (^C) has has bad " "dimensions - should be 32x40.", status ); } } ndfMap( indfg, "DATA", "_DOUBLE", "READ", (void **) &gai_data, &nelg, status ); } else { indfg = NDF__NOID; gai_data = NULL; } /* Fill the output with bad values. */ if( *status == SAI__OK ) { pd = odata->pntr[ 0 ]; for( iel = 0; iel < ndata; iel++ ) *(pd++) = VAL__BADD; } /* Resample the sky map data into the output time series. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, in_data, odata->pntr[ 0 ], NULL, &ngood, status ); /* Add on any COM data. */ smf_addcom( wf, odata, inc_data, status ); /* Issue a wrning if there is no good data in the output cube. */ if( ngood == 0 ) msgOutif( MSG__NORM, " ", " Output contains no " "good data values.", status ); /* If Q and U maps have been given, allocate room to hold resampled Q and U values, and fill them with bad values. */ if( inq_data && inu_data ) { pq = outq_data = astMalloc( ndata*sizeof( *outq_data ) ); pu = outu_data = astMalloc( ndata*sizeof( *outu_data ) ); if( *status == SAI__OK ) { for( iel = 0; iel < ndata; iel++ ) { *(pu++) = VAL__BADD; *(pq++) = VAL__BADD; } } /* Determine the harmonic to use. */ parGet0i( "HARMONIC", &harmonic, status ); /* If producing the normal 8 Hz harmonic, get the amplitude and phase of a other signals to add onto the 8 Hz signal. */ if( harmonic == 4 ) { parGet0d( "AMP2", &2, status ); parGet0d( "PHASE2", &phase2, status ); parGet0d( "AMP4", &4, status ); parGet0d( "PHASE4", &phase4, status ); parGet0d( "AMP16", &16, status ); parGet0d( "PHASE16", &phase16, status ); } else { amp2 = 0.0; phase2 = 0.0; amp4 = 0.0; phase4 = 0.0; amp16 = 0.0; phase16 = 0.0; } /* Allocate room for an array to hold the angle from the Y pixel axis in the sky map to the focal plane Y axis, in radians, at each time slice. Positive rotation is in the same sense as rotation from focal plane X to focal plane Y. */ ang_data = astMalloc( ntslice*sizeof( *ang_data ) ); /* Resample them both into 3D time series. These Q/U values arw with respect to the sky image Y axis. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inq_data, outq_data, ang_data, &ngood, status ); smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inu_data, outu_data, NULL, &ngood, status ); /* Combine these time series with the main output time series so that the main output is analysed intensity. */ smf_uncalc_iqu( wf, odata, odata->pntr[ 0 ], outq_data, outu_data, ang_data, pasign, AST__DD2R*paoff, AST__DD2R*angrot, amp2, AST__DD2R*phase2, amp4, AST__DD2R*phase4, amp16, AST__DD2R*phase16, qinst_data, uinst_data, c0_data, p0_data, p1_data, angc_data, harmonic, status ); /* Release work space. */ outq_data = astFree( outq_data ); outu_data = astFree( outu_data ); ang_data = astFree( ang_data ); } /* Factor in any GAI data. */ smf_addgai( wf, odata, gai_data, status ); /* Close the output time series file. */ smf_close_file( wf, &odata, status ); /* Close the IP data container for the current subarray, if it is open. */ if( cloc ) datAnnul( &cloc, status ); /* End the NDF context. */ ndfEnd( status ); } /* Close any input data file that is still open due to an early exit from the above loop. */ if( odata != NULL ) { smf_close_file( wf, &odata, status ); odata = NULL; } /* Free remaining resources. */ if( igrp1 != NULL) grpDelet( &igrp1, status); if( igrp2 != NULL) grpDelet( &igrp2, status); if( igrpq != NULL) grpDelet( &igrpq, status); if( igrpu != NULL) grpDelet( &igrpu, status); if( igrpc != NULL) grpDelet( &igrpc, status); if( igrpg != NULL) grpDelet( &igrpg, status); if( ogrp != NULL) grpDelet( &ogrp, status); if( iploc ) datAnnul( &iploc, status ); /* End the NDF context. */ ndfEnd( status ); /* End the tile's AST context. */ astEnd; /* Issue a status indication.*/ if( *status == SAI__OK ) { msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status); } else { msgOutif(MSG__VERB," ",TASK_NAME " failed.", status); } }
void smf_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; }
smfDetposWcsCache *smf_detpos_wcs( smfHead *hdr, int index, double dut1, const double telpos[3], AstFrameSet **fset, smfDetposWcsCache *cache, int *status ) { /* Local Variables: */ AstCmpMap *cmap1 = NULL; /* Parallel CmpMap holding both LutMaps */ AstLutMap *latmap = NULL; /* LutMap holding latitude values */ AstLutMap *lonmap = NULL; /* LutMap holding longitude values */ AstMapping *map = NULL; /* GRID->SKY Mapping */ AstSkyFrame *csky = NULL; /* SkyFrame to put in returned FrameSet */ const double *p1; /* Pointer to next lon or lat value to copy */ double *p2; /* Pointer to next lon value */ double *p3; /* Pointer to next lat value */ int i; /* Index of current detector */ int nrec; /* Number of detectors */ int outperm[ 2 ]; /* Axis permutation */ smfDetposWcsCache *result; /* Pointer to returned cache structure */ /* If a negative index was supplied just free the allocated resources and return. */ if( index < 0 && cache ) { if( cache->latlut ) cache->latlut = astFree( cache->latlut ); if( cache->lonlut ) cache->lonlut = astFree( cache->lonlut ); if( cache->pmap ) cache->pmap = astAnnul( cache->pmap ); if( cache->grid ) cache->grid = astAnnul( cache->grid ); if( cache->sky ) cache->sky = astAnnul( cache->sky ); cache = astFree( cache ); return NULL; } /* Check inherited status */ result = cache; if( *status != SAI__OK) return result; /* If no cache structure was supplied, allocate and initialise one now. */ if( !cache ) { cache = astMalloc( sizeof( *cache ) ); if( cache ) { cache->latlut = NULL; cache->lonlut = NULL; cache->pmap = NULL; cache->grid = NULL; cache->sky = NULL; result = cache; } else { *status = SAI__ERROR; errRep( FUNC_NAME, FUNC_NAME": Can't allocate memory for cache.", status); return NULL; } } /* Get the number of detectors. */ nrec = hdr->ndet; /* Get a pointer to the start of the detpos values for the requested time slice. */ p1 = hdr->detpos + 2*nrec*index; /* Check there is more than 1 detector. */ if( nrec > 1 ) { /* It is possible that we have not allocated enough memory since this memory is allocated for the first file but subsequent files may have more receptors. So we use astGrow. */ /* If required, allocate memory to hold the individual look up tables for lon and lat vaues. */ cache->lonlut = astGrow( cache->lonlut, nrec, sizeof( double ) ); cache->latlut = astGrow( cache->latlut, nrec, sizeof( double ) ); /* Check the memory was allocated succesfully. */ if( cache->lonlut && cache->latlut ) { /* Copy the lon and lat values for the requested time slice from the smfHead structure to the local lut arrays. */ p2 = cache->lonlut; p3 = cache->latlut; for( i = 0; i < nrec; i++ ) { *(p2++) = *(p1++); *(p3++) = *(p1++); } /* Create the Mapping from GRID to SKY positions. This is a PermMap to duplicate the detector index, followed by 2 LutMaps in parallel to generate the lon and lat values. Set the LutInterpattribute in these LutMaps so that they use nearest neighbour interpolation. */ lonmap = astLutMap( nrec, cache->lonlut, 1.0, 1.0, "LutInterp=1" ); latmap = astLutMap( nrec, cache->latlut, 1.0, 1.0, "LutInterp=1" ); cmap1 = astCmpMap( lonmap, latmap, 0, " " ); latmap = astAnnul( latmap ); lonmap = astAnnul( lonmap ); if( !cache->pmap ) { outperm[ 0 ] = 1; outperm[ 1 ] = 1; cache->pmap = astPermMap( 2, NULL, 2, outperm, NULL, " " ); astExempt( cache->pmap ); } map = (AstMapping *) astCmpMap( cache->pmap, cmap1, 1, " " ); cache->pmap = astAnnul( cache->pmap ); cmap1 = astAnnul( cmap1 ); } /* If thre is only one detector, use a PermMap to describe this one position. rather than a LutMap (LutMaps cannot describe a single position). */ } else { outperm[ 0 ] = -1; outperm[ 1 ] = -2; map = (AstMapping *) astPermMap( 2, NULL, 2, outperm, p1, " " ); } /* Create two Frames to put in the FrameSet. */ if( !cache->grid ) { cache->grid = astFrame( 2, "Domain=GRID" ); astExempt( cache->grid ); } if( !cache->sky ) { cache->sky = astSkyFrame( "System=AzEl" ); astSetD( cache->sky, "ObsLon", -telpos[ 0 ] ); astSetD( cache->sky, "ObsLat", telpos[ 1 ] ); astExempt( cache->sky ); /* If the detpos positions are referred to the TRACKING frame, change the SkyFrame from AZEL to the AST equivalent of the TRACKING Frame. */ if( !hdr->dpazel ) { astSetC( cache->sky, "System", sc2ast_convert_system( hdr->state->tcs_tr_sys, status ) ); } } /* Take a copy of the skyframe, and then modify its Epoch attribute. We take a copy since otherwise all FrameSets returned by this function would share the same current Frame, and so the attribute change would affect them all. Always use TCS_TAI. smf_open_file corrects the JCMTState structure if TCS_TAI is missing. Remember to convert from TAI to TDB (as required by the Epoch attribute). */ csky = astClone( cache->sky ); astSet( csky, "Epoch=MJD %.*g, dut1=%.*g", DBL_DIG, hdr->state->tcs_tai + 32.184/SPD, DBL_DIG, dut1 ); /* Create the FrameSet */ *fset = astFrameSet( cache->grid, " " ); astAddFrame( *fset, AST__BASE, map, csky ); /* Free resources */ map =astAnnul( map ); csky =astAnnul( csky ); /* Exempt the FrameSet pointer from the AST context system rather because we do not know when, or in which context, it will be used. It will be annulled either in smf_tslice_ast or in smf_close_file. */ astExempt( *fset ); return result; }
void smf_calc_iqu( ThrWorkForce *wf, smfData *data, int block_start, int block_end, int ipolcrd, int qplace, int uplace, int iplace, NdgProvenance *oprov, AstFitsChan *fc, int pasign, double paoff, double angrot, int submean, int harmonic, int *status ){ /* Local Variables: */ AstCmpMap *cm1; AstCmpMap *cm2; AstFrameSet *wcs; /* WCS FrameSet for output NDFs */ AstMapping *fpmap1; AstMapping *fpmap2; AstMapping *oskymap; AstMapping *totmap; AstSkyFrame *oskyfrm; AstWinMap *wm; /* Mapping to reverse the X GRID axis */ const JCMTState *state; /* JCMTState info for current time slice */ const char *usesys; /* Used system string */ dim_t itime; /* Time slice index */ dim_t nbolo; /* No. of bolometers */ dim_t ncol; /* No. of columns of bolometers */ dim_t nrow; /* No. of rows of bolometers */ dim_t ntime; /* Time slices to check */ dim_t ntslice; /* Number of time-slices in data */ double *ipi; /* Pointer to output I array */ double *ipiv; /* Pointer to output I variance array */ double *ipq; /* Pointer to output Q array */ double *ipqv; /* Pointer to output Q variance array */ double *ipu; /* Pointer to output U array */ double *ipuv; /* Pointer to output U variance array */ double *mean; double ang_data[2]; double fox[2]; double foy[2]; double fpr0; double fprinc; double fx[2]; double fy[2]; double ina[ 2 ]; /* Bolometer coords at bottom left */ double inb[ 2 ]; /* Bolometer coords at top right */ double outa[ 2 ]; /* NDF GRID coords at bottom left */ double outb[ 2 ]; /* NDF GRID coords at top right */ int bstep; /* Bolometer step between threads */ int el; /* Number of mapped array elements */ int gotvar; /* Were any output variances created? */ int indfi; /* Identifier for NDF holding I values */ int indfq; /* Identifier for NDF holding Q values */ int indfu; /* Identifier for NDF holding Q values */ int iworker; /* Index of a worker thread */ int lbnd[ 2 ]; /* Lower pixel bounds of output NDF */ int moving; int nworker; /* No. of worker threads */ int old; /* Data has old-style POL_ANG values? */ int tstep; /* Time slice step between threads */ int ubnd[ 2 ]; /* Upper pixel bounds of output NDF */ size_t bstride; /* Stride between adjacent bolometer values */ size_t tstride; /* Stride between adjacent time slice values */ smfCalcIQUJobData *job_data = NULL; /* Pointer to all job data */ smfCalcIQUJobData *pdata = NULL;/* Pointer to next job data */ smfHead *hdr; /* Pointer to data header this time slice */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Convenience pointers. */ hdr = data->hdr; /* Obtain number of time slices - will also check for 3d-ness. Also get the dimensions of the bolometer array and the strides between adjacent bolometer values. */ smf_get_dims( data, &nrow, &ncol, &nbolo, &ntslice, NULL, &bstride, &tstride, status ); /* Report an error if the block of time slices extends of either end. */ if( block_start < 0 || block_end >= (int) ntslice ) { if( *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "S", block_start ); msgSeti( "E", block_end ); msgSeti( "N", ntslice ); errRep( " ", "smf_calc_iqu: invalid block of time slices - ^S to " "^E (^N time slices are available).", status ); } } /* Create the output NDFs. Each one is a 2D array with dimensions equal to the bolometer array. */ lbnd[ 0 ] = 1; lbnd[ 1 ] = 1; ubnd[ 0 ] = ncol; ubnd[ 1 ] = nrow; ndfNew( "_DOUBLE", 2, lbnd, ubnd, &qplace, &indfq, status ); ndfNew( "_DOUBLE", 2, lbnd, ubnd, &uplace, &indfu, status ); if( iplace != NDF__NOPL ) { ndfNew( "_DOUBLE", 2, lbnd, ubnd, &iplace, &indfi, status ); } else { indfi = NDF__NOID; } /* Store any supplied provenance in all NDFs. */ if( oprov ) { ndgWriteProv( oprov, indfq, 1, status ); ndgWriteProv( oprov, indfu, 1, status ); if( indfi != NDF__NOID ) ndgWriteProv( oprov, indfi, 1, status ); } /* Store any supplied FITS headers in all NDFs.*/ if( fc && astGetI( fc, "NCard" ) > 0 ) { kpgPtfts( indfq, fc, status ); kpgPtfts( indfu, fc, status ); if( indfi != NDF__NOID ) kpgPtfts( indfi, fc, status ); } /* Store the WCS frameSet in all NDFs. First get the FrameSet for the central time slice in the block, and set its current Frame to the tracking frame. */ smf_tslice_ast( data, ( block_start + block_end )/2, 1, NO_FTS, status); usesys = sc2ast_convert_system( (data->hdr->allState)[0].tcs_tr_sys, status ); astSetC( hdr->wcs, "System", usesys ); /* Get the Mapping from focal plane coords to bolometer grid coords. This is the same for all time slices. sc2ast ensures that frame 3 is FPLANE. */ fpmap1 = astGetMapping( hdr->wcs, 3, AST__BASE ); /* Take a copy and then reverse the X axis of the GRID Frame by remaping the base Frame using a WinMap. This produces a pixel grid such as you would see by looking up at the sky from underneath the array, rather than looking down at the ground from above the array. */ wcs = astCopy( hdr->wcs ); ina[ 0 ] = 1.0; inb[ 0 ] = ncol; ina[ 1 ] = 1.0; inb[ 1 ] = nrow; outa[ 0 ] = ncol; outb[ 0 ] = 1.0; outa[ 1 ] = 1.0; outb[ 1 ] = nrow; wm = astWinMap( 2, ina, inb, outa, outb, " " ); astRemapFrame( wcs, AST__BASE, wm ); wm = astAnnul( wm ); /* Get the Mapping from output grid coords to focal plane coords. */ fpmap2 = astGetMapping( wcs, AST__BASE, 3 ); /* If the target is moving (assumed to be the case if the tracking system is AZEL or GAPPT), make the FrameSet current Frame represent offsets from the reference position (i.e. the moving target), and indicate that the offset coord system should be used for alignment. */ if( !strcmp( usesys, "AZEL" ) || !strcmp( usesys, "GAPPT" ) ){ astSet( wcs, "SkyRefIs=Origin,AlignOffset=1" ); moving = 1; } else { moving = 0; } /* Store the FrameSet in the output NDFs. */ ndfPtwcs( wcs, indfq, status ); ndfPtwcs( wcs, indfu, status ); if( indfi != NDF__NOID ) ndfPtwcs( wcs, indfi, status ); /* Map the Data array in each NDF. */ ndfMap( indfq, "Data", "_DOUBLE", "WRITE", (void **) &ipq, &el, status ); ndfMap( indfu, "Data", "_DOUBLE", "WRITE", (void **) &ipu, &el, status ); if( indfi != NDF__NOID ) { ndfMap( indfi, "Data", "_DOUBLE", "WRITE", (void **) &ipi, &el, status ); } else { ipi = NULL; } /* Map the Variance array in each NDF. */ ndfMap( indfq, "Variance", "_DOUBLE", "WRITE", (void **) &ipqv, &el, status ); ndfMap( indfu, "Variance", "_DOUBLE", "WRITE", (void **) &ipuv, &el, status ); if( indfi != NDF__NOID ) { ndfMap( indfi, "Variance", "_DOUBLE", "WRITE", (void **) &ipiv, &el, status ); } else { ipiv = NULL; } /* If required, allocate memory to hold the mean bolometer value at each time slice. */ mean = submean ? astMalloc( ntslice*sizeof( *mean ) ) : NULL; /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); /* Check the above pointers can be used safely. */ if( *status == SAI__OK ) { /* Go through the first thousand POL_ANG values to see if they are in units of radians (new data) or arbitrary encoder units (old data). They are assumed to be in radians if no POL_ANG value is larger than 20. */ old = 0; state = hdr->allState; ntime = ( ntslice > 1000 ) ? 1000 : ntslice; for( itime = 0; itime < ntime; itime++,state++ ) { if( state->pol_ang > 20 ) { old = 1; msgOutif( MSG__VERB, ""," POL2 data contains POL_ANG values " "in encoder units - converting to radians.", status ); break; } } /* If required, find the mean bolometer value at each time slice. */ if( submean ) { /* Determine which time-slices are to be processed by which threads. */ tstep = ntslice/nworker; if( tstep < 1 ) tstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->block_start = iworker*tstep; if( iworker < nworker - 1 ) { pdata->block_end = pdata->block_start + tstep - 1; } else { pdata->block_end = ntslice - 1; } } /* Store all the other info needed by the worker threads, and submit the jobs to calculate the Q and U values in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->bstride = bstride; pdata->dat = data->pntr[0]; pdata->nbolo = nbolo; pdata->qua = smf_select_qualpntr( data, NULL, status );; pdata->tstride = tstride; pdata->mean = mean; pdata->action = 1; /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calc_iqu_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); } /* Get the Frame representing absolute sky coords in the output NDF, and the Mapping from sky to grid in the output NDF. */ oskyfrm = astCopy( astGetFrame( wcs, AST__CURRENT ) ); astSet( oskyfrm, "SkyRefIs=Ignored" ); oskymap = astGetMapping( wcs, AST__CURRENT, AST__BASE ); wcs = astAnnul( wcs ); /* Find the first and last time slices, calculate the angle between the focal pane Y axis at the time slice, and the focal plane Y axis in the output NDF. For intervening time-slices, the angle is found by linear interpolation between the extreme time slices. */ for( el = 0; el < 2; el++ ) { /* Get the mapping from GRID coords in the input time slice to GRID coords in the output. */ totmap = smf_rebin_totmap( data, el?ntslice-1:0, oskyfrm, oskymap, moving, NO_FTS, status ); /* Modify it to be the Mapping from focal plane coords in the input time slice to focal plane coords in the output. */ cm1 = astCmpMap( fpmap1, totmap, 1, " " ); cm2 = astCmpMap( cm1, fpmap2, 1, " " ); /* Use this Mapping to convert two points on the focal plane Y axis from the input to the output. */ fx[0] = 0.0; fy[0] = 0.0; fx[1] = 0.0; fy[1] = 4.0; astTran2( cm2, 2, fx, fy, 1, fox, foy ); /* The angle from the focal plane Y axis in the output to the focal plane Y axis in the input time slice, measured positive in sense of rotation from Fy to Fx. */ ang_data[ el ] = atan2( fox[1]-fox[0], foy[1]-foy[0] ); /* Free resources for this time slice. */ totmap = astAnnul( totmap ); cm1 = astAnnul( cm1 ); cm2 = astAnnul( cm2 ); } /* Annul objects. */ oskymap = astAnnul( oskymap ); oskyfrm = astAnnul( oskyfrm ); fpmap1 = astAnnul( fpmap1 ); fpmap2 = astAnnul( fpmap2 ); /* Get the constants of the linear relationship between focal plane rotation and time slice index "fpr = fpr0 + itime*fprinc". */ fpr0 = ang_data[ 0 ]; fprinc = ( ang_data[ 1 ] - fpr0 )/( ntslice - 1 ); /* Determine which bolometers are to be processed by which threads. */ bstep = nbolo/nworker; if( bstep < 1 ) bstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->b1 = iworker*bstep; pdata->b2 = pdata->b1 + bstep - 1; } /* Ensure that the last thread picks up any left-over bolometers */ pdata->b2 = nbolo - 1; /* Store all the other info needed by the worker threads, and submit the jobs to calculate the Q and U values in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->bstride = bstride; pdata->dat = data->pntr[0];; pdata->nbolo = nbolo; pdata->qua = smf_select_qualpntr( data, NULL, status );; pdata->tstride = tstride; pdata->allstates = hdr->allState; pdata->ipq = ipq; pdata->ipu = ipu; pdata->ipi = ipi; pdata->ipqv = ipqv; pdata->ipuv = ipuv; pdata->ipiv = ipiv; pdata->ipolcrd = ipolcrd; pdata->block_start = block_start; pdata->block_end = block_end; pdata->old = old; pdata->ncol = ncol; pdata->pasign = pasign ? +1: -1; pdata->paoff = paoff; pdata->angrot = angrot; pdata->fpr0 = fpr0; pdata->fprinc = fprinc; pdata->angfac = harmonic/4.0; pdata->action = 0; pdata->mean = mean; /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calc_iqu_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); /* See if any thread produced non-bad variance values. */ gotvar = 0; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; if( pdata->gotvar ) gotvar = 1; } /* If no variances were created, erase the Variance component and tell the user. */ ndfUnmap( indfq, "*", status ); ndfUnmap( indfu, "*", status ); if( ipi ) ndfUnmap( indfi, "*", status ); if( !gotvar ) { ndfReset( indfq, "Variance", status ); ndfReset( indfu, "Variance", status ); if( ipi ) ndfReset( indfi, "Variance", status ); msgOut( "", "Warning: Insufficient input data to produce variances", status ); } } /* Add POLANAL Frames to the WCS FrameSet in each output NDF. This Frame is used by POLPACK to determine the reference direction of the Stokes vectors (focal plane Y in this case, i.e. zero-based axis 1 ). */ smf_polext( indfq, 0, 0.0, "FPLANE", 1, status ); smf_polext( indfu, 0, 0.0, "FPLANE", 1, status ); if( ipi ) smf_polext( indfi, 0, 0.0, "FPLANE", 1, status ); /* Free the two output NDFs. */ ndfAnnul( &indfq, status ); ndfAnnul( &indfu, status ); if( ipi ) ndfAnnul( &indfi, status ); /* Free other resources. */ job_data = astFree( job_data ); mean = astFree( mean ); }
void smurf_unmakemap( int *status ) { /* Local Variables */ AstFrameSet *wcsin = NULL; /* WCS Frameset for input cube */ AstMapping *skymap; /* GRID->SkyFrame Mapping from input WCS */ AstSkyFrame *abskyfrm; /* Input SkyFrame (always absolute) */ AstSkyFrame *skyfrm = NULL;/* SkyFrame from the input WCS Frameset */ Grp *igrp1 = NULL; /* Group of input sky files */ Grp *igrp2 = NULL; /* Group of input template files */ Grp *igrpq = NULL; /* Group of input Q sky files */ Grp *igrpu = NULL; /* Group of input U sky files */ Grp *ogrp = NULL; /* Group containing output file */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ char pabuf[ 10 ]; /* Text buffer for parameter value */ dim_t iel; /* Index of next element */ dim_t ndata; /* Number of elements in array */ dim_t ntslice; /* Number of time slices in array */ double *ang_data = NULL; /* Pointer to the FP orientation angles */ double *in_data = NULL; /* Pointer to the input I sky map */ double *inq_data = NULL; /* Pointer to the input Q sky map */ double *inu_data = NULL; /* Pointer to the input U sky map */ double *outq_data = NULL; /* Pointer to the Q time series data */ double *outu_data = NULL; /* Pointer to the U time series data */ double *pd; /* Pointer to next element */ double *pq = NULL; /* Pointer to next Q time series value */ double *pu = NULL; /* Pointer to next U time series value */ double angrot; /* Angle from focal plane X axis to fixed analyser */ double paoff; /* WPLATE value corresponding to POL_ANG=0.0 */ double params[ 4 ]; /* astResample parameters */ double sigma; /* Standard deviation of noise to add to output */ int alignsys; /* Align data in the map's system? */ int flag; /* Was the group expression flagged? */ int harmonic; /* The requested harmonic */ int ifile; /* Input file index */ int indf; /* Input sky map NDF identifier */ int indfin; /* Input template cube NDF identifier */ int indfout; /* Output cube NDF identifier */ int indfq; /* Input Q map NDF identifier */ int indfu; /* Input U map NDF identifier */ int interp = 0; /* Pixel interpolation method */ int moving; /* Is the telescope base position changing? */ int nel; /* Number of elements in array */ int nelqu; /* Number of elements in Q or U array */ int ngood; /* No. of good values in putput cube */ int nparam = 0; /* No. of parameters required for interpolation scheme */ int pasign; /* Indicates sense of POL_ANG value */ int sdim[ 2 ]; /* Array of significant pixel axes */ int slbnd[ 2 ]; /* Array of lower bounds of input map */ int subnd[ 2 ]; /* Array of upper bounds of input map */ size_t nskymap; /* Number of supplied sky cubes */ size_t outsize; /* Number of files in output group */ size_t size; /* Number of files in input group */ smfData *odata = NULL; /* Pointer to output data struct */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context */ astBegin; /* Begin an NDF context. */ ndfBegin(); /* Find the number of cores/processors available and create a pool of threads of the same size. */ wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status ); /* Get an identifier for the input NDF. We use NDG (via kpg1Rgndf) instead of calling ndfAssoc directly since NDF/HDS has problems with file names containing spaces, which NDG does not have. */ kpg1Rgndf( "IN", 1, 1, "", &igrp1, &nskymap, status ); ndgNdfas( igrp1, 1, "READ", &indf, status ); /* Map the data array in the input sky map. */ ndfMap( indf, "DATA", "_DOUBLE", "READ", (void **) &in_data, &nel, status ); /* Get the WCS FrameSet from the sky map, together with its pixel index bounds. */ kpg1Asget( indf, 2, 0, 1, 1, sdim, slbnd, subnd, &wcsin, status ); /* Check the current Frame is a SKY frame. */ skyfrm = astGetFrame( wcsin, AST__CURRENT ); if( !astIsASkyFrame( skyfrm ) && *status == SAI__OK ) { ndfMsg( "N", indf ); *status = SAI__ERROR; errRep( " ", " Current Frame in ^N is not a SKY Frame.", status ); } /* Get a copy of the current frame that represents absolute coords rather than offsets. We assume the target is moving if the map represents offsets. */ moving = ( *status == SAI__OK && !strcmp( astGetC( skyfrm, "SkyRefIs" ), "Origin" ) ) ? 1 : 0; abskyfrm = astCopy( skyfrm ); astClear( abskyfrm, "SkyRefIs" ); /* If the ALIGNSYS parameter is TRUE then we align the raw data with the map in the current system of the map, rather than the default ICRS. */ parGet0l( "ALIGNSYS", &alignsys, status ); if( alignsys ) astSetC( abskyfrm, "AlignSystem", astGetC( abskyfrm, "System" ) ); /* Get the Mapping from the Sky Frame to grid axis in the iput map. */ skymap = astGetMapping( wcsin, AST__CURRENT, AST__BASE ); /* Get the pixel interpolation scheme to use. */ parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC," "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS", 1, pabuf, 10, status ); if( !strcmp( pabuf, "NEAREST" ) ) { interp = AST__NEAREST; nparam = 0; } else if( !strcmp( pabuf, "LINEAR" ) ) { interp = AST__LINEAR; nparam = 0; } else if( !strcmp( pabuf, "SINC" ) ) { interp = AST__SINC; nparam = 1; } else if( !strcmp( pabuf, "SINCSINC" ) ) { interp = AST__SINCSINC; nparam = 2; } else if( !strcmp( pabuf, "SINCCOS" ) ) { interp = AST__SINCCOS; nparam = 2; } else if( !strcmp( pabuf, "SINCGAUSS" ) ) { interp = AST__SINCGAUSS; nparam = 2; } else if( !strcmp( pabuf, "SOMB" ) ) { interp = AST__SOMB; nparam = 1; } else if( !strcmp( pabuf, "SOMBCOS" ) ) { interp = AST__SOMBCOS; nparam = 2; } else if( *status == SAI__OK ) { nparam = 0; *status = SAI__ERROR; msgSetc( "V", pabuf ); errRep( "", "Support not available for INTERP = ^V (programming " "error)", status ); } /* Get an additional parameter vector if required. */ if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status ); /* Get a group of reference time series files to use as templates for the output time series files.*/ ndgAssoc( "REF", 1, &igrp2, &size, &flag, status ); /* Get output file(s) */ kpg1Wgndf( "OUT", igrp2, size, size, "More output files required...", &ogrp, &outsize, status ); /* Get he noise level to add to the output data. */ parGet0d( "SIGMA", &sigma, status ); /* Get any Q and U input maps. */ if( *status == SAI__OK ) { kpg1Rgndf( "QIN", 1, 1, "", &igrpq, &nskymap, status ); ndgNdfas( igrpq, 1, "READ", &indfq, status ); ndfMap( indfq, "DATA", "_DOUBLE", "READ", (void **) &inq_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "Q", indfq ); *status = SAI__ERROR; errRep( "", "Q image '^Q' is not the same size as the I image.", status ); } kpg1Rgndf( "UIN", 1, 1, "", &igrpu, &nskymap, status ); ndgNdfas( igrpu, 1, "READ", &indfu, status ); ndfMap( indfu, "DATA", "_DOUBLE", "READ", (void **) &inu_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "U", indfu ); *status = SAI__ERROR; errRep( "", "U image '^U' is not the same size as the I image.", status ); } if( *status == PAR__NULL ) { ndfAnnul( &indfq, status ); ndfAnnul( &indfu, status ); inq_data = NULL; inu_data = NULL; errAnnul( status ); } else { parGet0d( "ANGROT", &angrot, status ); parGet0d( "PAOFF", &paoff, status ); parGet0l( "PASIGN", &pasign, status ); } } /* Loop round all the template time series files. */ for( ifile = 1; ifile <= (int) size && *status == SAI__OK; ifile++ ) { /* Start a new NDF context. */ ndfBegin(); /* Create the output NDF by propagating everything from the input, except for quality and variance. */ ndgNdfas( igrp2, ifile, "READ", &indfin, status ); ndfMsg( "FILE", indfin ); msgSeti( "THISFILE", ifile ); msgSeti( "NUMFILES", size ); msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE", status ); ndgNdfpr( indfin, "DATA,HISTORY,LABEL,TITLE,WCS,UNITS,EXTENSION(*)", ogrp, ifile, &indfout, status ); ndfAnnul( &indfin, status ); ndfAnnul( &indfout, status ); /* We now re-open the output NDF and then modify its data values. */ smf_open_file( wf, ogrp, ifile, "UPDATE", 0, &odata, status ); /* Issue a suitable message and abort if anything went wrong. */ if( *status != SAI__OK ) { errRep( FUNC_NAME, "Could not open input template file.", status ); break; } else { if( odata->file == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfFile associated with smfData.", status ); break; } else if( odata->hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfHead associated with smfData.", status ); break; } } /* Check the reference time series contains double precision values. */ smf_dtype_check_fatal( odata, NULL, SMF__DOUBLE, status ); /* Get the total number of data elements, and the number of time slices. */ smf_get_dims( odata, NULL, NULL, NULL, &ntslice, &ndata, NULL, NULL, status ); /* Fill the output with bad values. */ if( *status == SAI__OK ) { pd = odata->pntr[ 0 ]; for( iel = 0; iel < ndata; iel++ ) *(pd++) = VAL__BADD; } /* Resample the sky map data into the output time series. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, in_data, odata->pntr[ 0 ], NULL, &ngood, status ); /* Issue a wrning if there is no good data in the output cube. */ if( ngood == 0 ) msgOutif( MSG__NORM, " ", " Output contains no " "good data values.", status ); /* If Q and U maps have been given, allocate room to hold resampled Q and U values, and fill them with bad values. */ if( inq_data && inu_data ) { pq = outq_data = astMalloc( ndata*sizeof( *outq_data ) ); pu = outu_data = astMalloc( ndata*sizeof( *outu_data ) ); if( *status == SAI__OK ) { for( iel = 0; iel < ndata; iel++ ) { *(pu++) = VAL__BADD; *(pq++) = VAL__BADD; } } /* Determine the harmonic to use. */ parGet0i( "HARMONIC", &harmonic, status ); /* Allocate room for an array to hold the anti-clockwise angle from the focal plane Y axis to the Y pixel axis in the reference map, at each time slice. */ ang_data = astMalloc( ntslice*sizeof( *ang_data ) ); /* Resample them both into 3D time series. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inq_data, outq_data, ang_data, &ngood, status ); smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inu_data, outu_data, NULL, &ngood, status ); /* Combine these time series with the main output time series so that the main output is analysed intensity. */ smf_uncalc_iqu( wf, odata, odata->pntr[ 0 ], outq_data, outu_data, ang_data, pasign, AST__DD2R*paoff, AST__DD2R*angrot, harmonic, status ); /* Release work space. */ outq_data = astFree( outq_data ); outu_data = astFree( outu_data ); ang_data = astFree( ang_data ); } /* Close the output time series file. */ smf_close_file( wf, &odata, status ); /* End the NDF context. */ ndfEnd( status ); } /* Close any input data file that is still open due to an early exit from the above loop. */ if( odata != NULL ) { smf_close_file( wf, &odata, status ); odata = NULL; } /* Free remaining resources. */ if( igrp1 != NULL) grpDelet( &igrp1, status); if( igrp2 != NULL) grpDelet( &igrp2, status); if( igrpq != NULL) grpDelet( &igrpq, status); if( igrpu != NULL) grpDelet( &igrpu, status); if( ogrp != NULL) grpDelet( &ogrp, status); /* End the NDF context. */ ndfEnd( status ); /* End the tile's AST context. */ astEnd; /* Issue a status indication.*/ if( *status == SAI__OK ) { msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status); } else { msgOutif(MSG__VERB," ",TASK_NAME " failed.", status); } }
static void DisplayKeyMap( AstKeyMap *km, int sort, const char *prefix, AstKeyMap *refkm, int *status ){ /* * Name: * DisplayKeyMap * Purpose: * Display the contents of a keymap. * Synopsis: * void DisplayKeyMap( AstKeyMap *km, int sort, const char *prefix, * int *status ) * Arguments: * km * Pointer to the KeyMaps containing the values to display. * sort * If non-zero, sort the values alphabetically by their keys. * prefix * A string to prepend to eack key. * refkm * Reference key map (e.g. values from the supplied configuration * rather than the NDF history), or null if not required. * status * Inherited status pointer. * Description: * This function displays the contents of a supplied KeyMap as * a series of "key = value" strings, one per line. It calls itself * recursively if a nested KeyMap is found, adding a suitable * prefix to the nested keys. If a reference key map is supplied then * the output shows how the main key map differs from it. */ /* Local Variables: */ AstObject *avalue; AstObject *refavalue; char cbuffer[ 255 ]; char newpref[ 255 ]; const char *cvalue; const char *refcvalue; const char *key; int ikey; int ival; int nc; int nkey; int nval; /* Check the inherited status */ if( *status != SAI__OK ) return; if (refkm) astClear(refkm, "KeyError"); /* Sort the supplied KeyMap is required. */ if( sort ) astSetC( km, "SortBy", "KeyUp" ); /* Loop round all keys in the supplied KeyMap. */ nkey = astMapSize( km ); for( ikey = 0; ikey < nkey; ikey++ ) { key = astMapKey( km, ikey ); /* If the current entry is a nest KeyMap, get a pointer to it and call this function recurisvely to display it, modifying the prefix to add to each key so that it includes the key associated with the nest keymap. */ if( astMapType( km, key ) == AST__OBJECTTYPE ) { astMapGet0A( km, key, &avalue ); if (refkm) { if (! astMapGet0A(refkm, key, &refavalue)) { refavalue = (AstObject*) astKeyMap(""); } } else { refavalue = NULL; } sprintf( newpref, "%s%s.", prefix, key ); DisplayKeyMap( (AstKeyMap *) avalue, sort, newpref, (AstKeyMap *) refavalue, status ); avalue = astAnnul( avalue ); if (refavalue) refavalue = astAnnul(refavalue); /* If the current entry is not a nested keymap, we display it now. */ } else { /* Get the vector length of the entry. */ nval = astMapLength( km, key ); /* If it is a scalar, just get its value as a character string using the automatic type conversion provided by the KeyMap class, and display it, putting the supplied prefix at the start of the key. */ if( nval <= 1 ) { cvalue = "<undef>"; astMapGet0C( km, key, &cvalue ); if (refkm) { refcvalue = "<undef>"; if (astMapGet0C(refkm, key, &refcvalue) && ! strcmp(cvalue, refcvalue)) { msgOutf("", "- %s%s = %s", status, prefix, key, cvalue); } else { msgOutf("", "+ %s%s = %s", status, prefix, key, cvalue); } } else { msgOutf( "", "%s%s = %s", status, prefix, key, cvalue ); } /* If it is a vector, we construct a string containing a comma-separated list of elements, enclosed in parentheses. */ } else { nc = 0; cvalue = astAppendString( NULL, &nc, "(" ); for( ival = 0; ival < nval; ival++ ) { if( astMapGetElemC( km, key, sizeof( cbuffer) - 1, ival, cbuffer ) ) { cvalue = astAppendString( (char *) cvalue, &nc, cbuffer ); } if( ival < nval - 1 ) cvalue = astAppendString( (char *) cvalue, &nc, "," ); } cvalue = astAppendString( (char *) cvalue, &nc, ")" ); /* Do the same for the reference KeyMap. */ if (refkm && (nval = astMapLength(refkm, key))) { nc = 0; refcvalue = astAppendString(NULL, &nc, "("); for (ival = 0; ival < nval; ival++) { if (ival) { refcvalue = astAppendString((char*) refcvalue, &nc, "," ); } if (astMapGetElemC(refkm, key, sizeof(cbuffer) - 1, ival, cbuffer)) { refcvalue = astAppendString((char*) refcvalue, &nc, cbuffer); } } refcvalue = astAppendString((char*) refcvalue, &nc, ")"); } else { refcvalue = NULL; } /* Display the total string, with the current key prefix, and free the memory used to store it. */ if (refkm) { if (refcvalue && ! strcmp(cvalue, refcvalue)) { msgOutf("", "- %s%s = %s", status, prefix, key, cvalue); } else { msgOutf("", "+ %s%s = %s", status, prefix, key, cvalue); } } else { msgOutf( "", "%s%s = %s", status, prefix, key, cvalue ); } cvalue = astFree( (void *) cvalue ); if (refcvalue) refcvalue = astFree((void*) refcvalue); } } } }
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_fit_qui( ThrWorkForce *wf, smfData *idata, smfData **odataq, smfData **odatau, smfData **odatai, dim_t box, int ipolcrd, int pasign, double paoff, double angrot, int north, int *status ){ /* Local Variables: */ AstFrameSet *wcs; /* WCS FrameSet for current time slice */ JCMTState *instate=NULL; /* Pointer to input JCMTState */ JCMTState *outstate=NULL;/* Pointer to output JCMTState */ const char *usesys; /* Tracking system */ dim_t *box_starts; /* Array holding time slice at start of each box */ dim_t box_size; /* First time slice in box */ dim_t intslice; /* ntslice of idata */ dim_t istart; /* Input time index at start of fitting box */ dim_t itime; /* Time slice index */ dim_t nbolo; /* No. of bolometers */ dim_t ncol; /* No. of columns of bolometers in the array */ dim_t ntime; /* Time slices to check */ dim_t ondata; /* ndata of odata */ dim_t ontslice; /* ntslice of odata */ double scale; /* how much longer new samples are */ int bstep; /* Bolometer step between threads */ int iworker; /* Index of a worker thread */ int nworker; /* No. of worker threads */ size_t i; /* loop counter */ smfData *indksquid=NULL; /* Pointer to input dksquid data */ smfFitQUIJobData *job_data = NULL; /* Pointer to all job data */ smfFitQUIJobData *pdata = NULL;/* Pointer to next job data */ smfHead *hdr; /* Pointer to data header this time slice */ smf_qual_t *qua; /* Input quality pointer */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Check supplied arguments. */ if( !idata || !odataq || !odatau ) { *status = SAI__ERROR; errRep( "", "smf_fit_qui: NULL inputs supplied", status ); return; } if( idata->ndims != 3 ) { *status = SAI__ERROR; errRep( "", "smf_fit_qui: idata is not 3-dimensional", status ); return; } /* Ensure the supplied smfData is time-ordered. So "bstride" is 1 and "tstride" is nbolo. */ smf_dataOrder( wf, idata, 1, status ); /* Dimensions of input. */ smf_get_dims( idata, NULL, &ncol, &nbolo, &intslice, NULL, NULL, NULL, status ); /* Store a pointer to the quality array for the input smfData. */ qua = smf_select_qualpntr( idata, NULL, status );; /* Go through the first thousand POL_ANG values to see if they are in units of radians (new data) or arbitrary encoder units (old data). They are assumed to be in radians if no POL_ANG value is larger than 20. This function can only handle new data. */ hdr = idata->hdr; instate = hdr->allState; ntime = ( intslice > 1000 ) ? 1000 : intslice; for( itime = 0; itime < ntime; itime++,instate++ ) { if( instate->pol_ang > 20 ) { *status = SAI__ERROR; errRep( " "," POL2 data contains POL_ANG values in encoder " "units - connot fit to such old data.", status ); break; } } /* Find the input time slice at which each fitting box starts, and the length of the output time axis (in time-slices). */ smf1_find_boxes( intslice, hdr->allState, box, &ontslice, &box_starts, status ); /* Time axis scaling factor. */ scale = (double) intslice / (double) ontslice; /* First copy everything from input to output except for the data that needs to be downsampled */ /* We want to copy everything in the smfHead except for allState. So we make a copy of the allState pointer, and then set it to NULL in the header before the copy */ if( idata->hdr ) { instate = idata->hdr->allState; idata->hdr->allState = NULL; } /* Similarly, we want everything in the smfDa except for the dksquid. */ if( idata->da ) { indksquid = idata->da->dksquid; idata->da->dksquid = NULL; } /* Create copies, storing them in the supplied output smfData structures. Omit the header for U and I, as we will be copying the Q header into them. */ *odataq = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY, 0, 0, status ); *odatau = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY | SMF__NOCREATE_HEAD, 0, 0, status ); if( odatai ) { *odatai = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY | SMF__NOCREATE_HEAD, 0, 0, status ); } /* Restore values in idata now that we're done */ if( instate ) idata->hdr->allState = instate; if( indksquid ) idata->da->dksquid = indksquid; /* Store the required length for the output time axis. The time axis is axis two because the data is time-ordered. */ (*odataq)->dims[ 2 ] = ontslice; (*odatau)->dims[ 2 ] = ontslice; if( odatai) (*odatai)->dims[ 2 ] = ontslice; /* Get output dimensions - assumed to be the same for all three outputs. */ ondata = ontslice*idata->dims[0]*idata->dims[1]; /* Allocate the data arrays for the outputs. */ (*odataq)->pntr[0] = astCalloc( ondata, sizeof(double) ); (*odatau)->pntr[0] = astCalloc( ondata, sizeof(double) ); if( odatai ) (*odatai)->pntr[0] = astCalloc( ondata, sizeof(double) ); /* Allocate arrays for the output variances. */ (*odataq)->pntr[1] = astCalloc( ondata, sizeof(double) ); (*odatau)->pntr[1] = astCalloc( ondata, sizeof(double) ); if( odatai ) (*odatai)->pntr[1] = astCalloc( ondata, sizeof(double) ); /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); if( *status == SAI__OK ) { /* Determine which bolometers are to be processed by which threads. */ bstep = nbolo/nworker; if( bstep < 1 ) bstep = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->b1 = iworker*bstep; pdata->b2 = pdata->b1 + bstep - 1; } /* Ensure that the last thread picks up any left-over bolometers */ pdata->b2 = nbolo - 1; /* Loop round all output time slices. */ for( itime = 0; itime < ontslice; itime++ ) { /* Get the index of the first input time slice that contributes to the current output time slice. */ istart = box_starts[ itime ]; /* Get the number of input time slices that contribute to the output time slice. */ box_size = box_starts[ itime + 1 ] - istart; /* If we are using north as the reference direction, get the WCS FrameSet for the input time slice that is at the middle of the output time slice, and set its current Frame to the tracking frame. */ if( north ) { smf_tslice_ast( idata, istart + box_size/2, 1, NO_FTS, status ); wcs = idata->hdr->wcs; usesys = sc2ast_convert_system( (idata->hdr->allState)[0].tcs_tr_sys, status ); astSetC( wcs, "System", usesys ); } else { wcs = NULL; } /* Now enter the parellel code in which each thread calculates the values for a range of bolometers at the current output slice. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->dat = ((double *) idata->pntr[0] ) + istart*nbolo; pdata->qua = qua + istart*nbolo; pdata->allstates = hdr->allState + istart; pdata->ipi = odatai ? ( (double*) (*odatai)->pntr[0] ) + itime*nbolo : NULL; pdata->ipq = ( (double*) (*odataq)->pntr[0] ) + itime*nbolo; pdata->ipu = ( (double*) (*odatau)->pntr[0] ) + itime*nbolo; pdata->ipv = ( (double*) (*odataq)->pntr[1] ) + itime*nbolo; pdata->nbolo = nbolo; pdata->ncol = ncol; pdata->box_size = box_size; pdata->ipolcrd = ipolcrd; pdata->pasign = pasign ? +1: -1; pdata->paoff = paoff; pdata->angrot = angrot; if( wcs ) { pdata->wcs = astCopy( wcs ); astUnlock( pdata->wcs, 1 ); } else { pdata->wcs = NULL; } /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_fit_qui_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); /* Lock and annul the AST objects used by each thread. */ if( wcs ) { for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; astLock( pdata->wcs, 0 ); pdata->wcs = astAnnul( pdata->wcs ); } } } /* Down-sample the smfHead -------------------------------------------------*/ smfHead *hdr = (*odataq)->hdr; hdr->curframe = (dim_t) (((double) hdr->curframe + 0.5) / scale); hdr->nframes = ontslice; hdr->steptime *= scale; strcpy( hdr->dlabel, "Q" ); strncpy( hdr->title, "POL-2 Stokes parameter Q", SMF__CHARLABEL ); /* Down-sample all the JCMTState values using nearest neighbours */ instate = idata->hdr->allState; if( instate ) { hdr->allState = astCalloc( ontslice, sizeof(*instate) ); outstate = hdr->allState; if( *status == SAI__OK ) { size_t frame; /* index of nearest neighbour JCMTState */ for( i=0; i<ontslice; i++ ) { frame = (size_t) round(((double) i + 0.5)*scale); memcpy( outstate + i, instate + frame, sizeof(*instate) ); } /* Then go back and properly down-sample the more important fast-changing fields like pointing. Note that since there are approximate values there already we need to explicitly re-initialize to 0. */ RESAMPSTATE(instate, outstate, rts_end, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_az_jig_x, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_az_jig_y, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_az_chop_x, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_az_chop_y, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_tr_jig_x, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_tr_jig_y, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_tr_chop_x, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, smu_tr_chop_y, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_tai, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_airmass, intslice, ontslice, 0); /* Second coordinates (Dec, El etc) can not wrap 0 to 360 so we do not need to test for those cases */ RESAMPSTATE(instate, outstate, tcs_az_ang, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_az_ac1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_az_ac2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_az_dc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_az_dc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_az_bc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_az_bc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_tr_ang, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_tr_ac1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_tr_ac2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_tr_dc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_tr_dc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_tr_bc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_tr_bc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_en_dc1, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_en_dc2, intslice, ontslice, 0); RESAMPSTATE(instate, outstate, tcs_dm_abs, intslice, ontslice, 1); RESAMPSTATE(instate, outstate, tcs_dm_rel, intslice, ontslice, 0); /* Wait for all the above smf_downsamp1 jobs to finish. */ thrWait( wf, status ); } } /* Add a keyword to the Q header indicating the polarimetric reference direction. */ smf_fits_updateL( (*odataq)->hdr, "POLNORTH", north, north ? "Pol ref dir is tracking north" : "Pol ref dir is focal plane Y", status ); /* Copy the Q header to the other outputs. */ hdr = smf_deepcopy_smfHead( (*odataq)->hdr, status ); (*odatau)->hdr = hdr; if( *status == SAI__OK ) { strcpy( hdr->dlabel, "U" ); strncpy( hdr->title, "POL-2 Stokes parameter U", SMF__CHARLABEL ); } if( odatai ) { hdr = smf_deepcopy_smfHead( (*odataq)->hdr, status ); (*odatai)->hdr = hdr; if( *status == SAI__OK ) { strcpy( hdr->dlabel, "I" ); strncpy( hdr->title, "POL-2 Stokes parameter I", SMF__CHARLABEL ); } } } /* Copy the variances from the Q smfData into the U and (and I) smfData. */ if( *odataq && *status == SAI__OK ) { if( *odatau ) { memcpy( (*odatau)->pntr[1], (*odataq)->pntr[1], ondata*sizeof(double)); } if( odatai && *odatai ) { memcpy( (*odatai)->pntr[1], (*odataq)->pntr[1], ondata*sizeof(double)); } } /* Ensure all smfDatas are time-ordered. */ smf_dataOrder( wf, idata, 1, status ); if( odatai && *odatai ) smf_dataOrder( wf, *odatai, 1, status ); if( *odataq ) smf_dataOrder( wf, *odataq, 1, status ); if( *odatau ) smf_dataOrder( wf, *odatau, 1, status ); /* Free resources. */ job_data = astFree( job_data ); box_starts = astFree( box_starts ); }