void adamtest ( int * status ) { if (*status != SAI__OK) return; msgOut("MSG1", "This text should not appear for msgOut", status ); msgSeti("TEST", 5); msgOut(" ", " Testing ^^ %% % $ $$ %ET - $TESTOBJ ^TEST",status); msgOut( " ", " Testing $ %ET Again %%ET ^^$", status ); msgOut(" ", "$$DOLLAR ^^CARET %%PERCENT at start of string", status); /* Make up a bad status */ *status = MSG__SYNER; errRep( " ", "This is the error message ^STATUS embedded", status ); errRep( "MSG1", "This text should not appear", status ); errRep( " ", "Should be expanded: %ET as 'EXPOSURE_TIME'", status); errRep( " ", "Object $TESTOBJ %s %XX should be somewhere", status ); errRep( " ", "Multiple %ET and ^STATUS and %TESTOBJ and %BLAH", status ); errRep( " ", "Double up %% escape $$ characters", status ); msgSetc( "X1", STRING ); msgSetc( "X2", STRING); msgSetc( "X3", STRING); msgSetc( "X4", STRING); msgSetc( "X5", STRING); msgSetc( "X6", STRING); errRep( " ","Overflow: ^X1:^X2:^X3:^X4:^X5:^X6", status ); }
void smurf_unmakemap( int *status ) { /* Local Variables */ AstFrameSet *wcsin = NULL; /* WCS Frameset for input cube */ AstMapping *skymap; /* GRID->SkyFrame Mapping from input WCS */ AstSkyFrame *abskyfrm; /* Input SkyFrame (always absolute) */ AstSkyFrame *skyfrm = NULL;/* SkyFrame from the input WCS Frameset */ Grp *igrp1 = NULL; /* Group of input sky files */ Grp *igrp2 = NULL; /* Group of input template files */ Grp *igrpc = NULL; /* Group of input COM files */ Grp *igrpg = NULL; /* Group of input GAI files */ Grp *igrpq = NULL; /* Group of input Q sky files */ Grp *igrpu = NULL; /* Group of input U sky files */ Grp *ogrp = NULL; /* Group containing output file */ HDSLoc *cloc = NULL; /* HDS locator for component ipdata structure */ HDSLoc *iploc = NULL; /* HDS locator for top level ipdata structure */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ char ipdata[ 200 ]; /* Text buffer for IPDATA value */ char pabuf[ 10 ]; /* Text buffer for parameter value */ char subarray[ 5 ]; /* Name of SCUBA-2 subarray (s8a,s8b,etc) */ dim_t iel; /* Index of next element */ dim_t ndata; /* Number of elements in array */ dim_t ntslice; /* Number of time slices in array */ double *ang_data = NULL; /* Pointer to the FP orientation angles */ double *angc_data = NULL; /* Pointer to the instrumental ANGC data */ double *c0_data = NULL; /* Pointer to the instrumental C0 data */ double *gai_data = NULL; /* Pointer to the input GAI map */ double *in_data = NULL; /* Pointer to the input I sky map */ double *inc_data = NULL; /* Pointer to the input COM data */ double *inq_data = NULL; /* Pointer to the input Q sky map */ double *inu_data = NULL; /* Pointer to the input U sky map */ double *outq_data = NULL; /* Pointer to the Q time series data */ double *outu_data = NULL; /* Pointer to the U time series data */ double *p0_data = NULL; /* Pointer to the instrumental P0 data */ double *p1_data = NULL; /* Pointer to the instrumental P1 data */ double *pd; /* Pointer to next element */ double *pq = NULL; /* Pointer to next Q time series value */ double *pu = NULL; /* Pointer to next U time series value */ double *qinst_data = NULL; /* Pointer to the instrumental Q data */ double *uinst_data = NULL; /* Pointer to the instrumental U data */ double amp16; /* Amplitude of 16 Hz signal */ double amp2; /* Amplitude of 2 Hz signal */ double amp4; /* Amplitude of 4 Hz signal */ double angrot; /* Angle from focal plane X axis to fixed analyser */ double paoff; /* WPLATE value corresponding to POL_ANG=0.0 */ double params[ 4 ]; /* astResample parameters */ double phase16; /* Phase of 16 Hz signal */ double phase2; /* Phase of 2 Hz signal */ double phase4; /* Phase of 4 Hz signal */ double sigma; /* Standard deviation of noise to add to output */ int alignsys; /* Align data in the map's system? */ int cdims[ 3 ]; /* Common-mode NDF dimensions */ int dims[ NDF__MXDIM ]; /* NDF dimensions */ int flag; /* Was the group expression flagged? */ int gdims[ 3 ]; /* GAI model NDF dimensions */ int harmonic; /* The requested harmonic */ int ifile; /* Input file index */ int indf; /* Input sky map NDF identifier */ int indfangc; /* IP ANGC values NDF identifier */ int indfc0; /* IP C0 values NDF identifier */ int indfc; /* Input COM NDF identifier */ int indfcs; /* NDF identifier for matching section of COM */ int indfg; /* Input GAI NDF identifier */ int indfin; /* Input template cube NDF identifier */ int indfiq; /* Input instrumental Q NDF */ int indfiu; /* Input instrumental U NDF */ int indfout; /* Output cube NDF identifier */ int indfp0; /* IP P0 values NDF identifier */ int indfp1; /* IP P1 values NDF identifier */ int indfq; /* Input Q map NDF identifier */ int indfu; /* Input U map NDF identifier */ int interp = 0; /* Pixel interpolation method */ int lbndc[ 3 ]; /* Array of lower bounds of COM NDF */ int moving; /* Is the telescope base position changing? */ int ndim; /* Number of pixel axes in NDF */ int ndimc; /* Number of pixel axes in common-mode NDF */ int ndimg; /* Number of pixel axes in GAI NDF */ int nel; /* Number of elements in array */ int nelc; /* Number of elements in COM array */ int nelg; /* Number of elements in GAI array */ int nelqu; /* Number of elements in Q or U array */ int ngood; /* No. of good values in putput cube */ int nparam = 0; /* No. of parameters required for interpolation scheme */ int pasign; /* Indicates sense of POL_ANG value */ int sdim[ 2 ]; /* Array of significant pixel axes */ int slbnd[ 2 ]; /* Array of lower bounds of input map */ int subnd[ 2 ]; /* Array of upper bounds of input map */ int ubndc[ 3 ]; /* Array of upper bounds of COM NDF */ size_t ncom; /* Number of com files */ size_t ngai; /* Number of gai files */ size_t nskymap; /* Number of supplied sky cubes */ size_t outsize; /* Number of files in output group */ size_t size; /* Number of files in input group */ smfData *odata = NULL; /* Pointer to output data struct */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context */ astBegin; /* Begin an NDF context. */ ndfBegin(); /* Find the number of cores/processors available and create a pool of threads of the same size. */ wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status ); /* Get an identifier for the input NDF. We use NDG (via kpg1Rgndf) instead of calling ndfAssoc directly since NDF/HDS has problems with file names containing spaces, which NDG does not have. */ kpg1Rgndf( "IN", 1, 1, "", &igrp1, &nskymap, status ); ndgNdfas( igrp1, 1, "READ", &indf, status ); /* Map the data array in the input sky map. */ ndfMap( indf, "DATA", "_DOUBLE", "READ", (void **) &in_data, &nel, status ); /* Get the WCS FrameSet from the sky map, together with its pixel index bounds. */ kpg1Asget( indf, 2, 0, 1, 1, sdim, slbnd, subnd, &wcsin, status ); /* Check the current Frame is a SKY frame. */ skyfrm = astGetFrame( wcsin, AST__CURRENT ); if( !astIsASkyFrame( skyfrm ) && *status == SAI__OK ) { ndfMsg( "N", indf ); *status = SAI__ERROR; errRep( " ", " Current Frame in ^N is not a SKY Frame.", status ); } /* Get a copy of the current frame that represents absolute coords rather than offsets. We assume the target is moving if the map represents offsets. */ moving = ( *status == SAI__OK && !strcmp( astGetC( skyfrm, "SkyRefIs" ), "Origin" ) ) ? 1 : 0; abskyfrm = astCopy( skyfrm ); astClear( abskyfrm, "SkyRefIs" ); /* If the ALIGNSYS parameter is TRUE then we align the raw data with the map in the current system of the map, rather than the default ICRS. */ parGet0l( "ALIGNSYS", &alignsys, status ); if( alignsys ) astSetC( abskyfrm, "AlignSystem", astGetC( abskyfrm, "System" ) ); /* Get the Mapping from the Sky Frame to grid axis in the iput map. */ skymap = astGetMapping( wcsin, AST__CURRENT, AST__BASE ); /* Get the pixel interpolation scheme to use. */ parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC," "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS", 1, pabuf, 10, status ); if( !strcmp( pabuf, "NEAREST" ) ) { interp = AST__NEAREST; nparam = 0; } else if( !strcmp( pabuf, "LINEAR" ) ) { interp = AST__LINEAR; nparam = 0; } else if( !strcmp( pabuf, "SINC" ) ) { interp = AST__SINC; nparam = 1; } else if( !strcmp( pabuf, "SINCSINC" ) ) { interp = AST__SINCSINC; nparam = 2; } else if( !strcmp( pabuf, "SINCCOS" ) ) { interp = AST__SINCCOS; nparam = 2; } else if( !strcmp( pabuf, "SINCGAUSS" ) ) { interp = AST__SINCGAUSS; nparam = 2; } else if( !strcmp( pabuf, "SOMB" ) ) { interp = AST__SOMB; nparam = 1; } else if( !strcmp( pabuf, "SOMBCOS" ) ) { interp = AST__SOMBCOS; nparam = 2; } else if( *status == SAI__OK ) { nparam = 0; *status = SAI__ERROR; msgSetc( "V", pabuf ); errRep( "", "Support not available for INTERP = ^V (programming " "error)", status ); } /* Get an additional parameter vector if required. */ if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status ); /* Get a group of reference time series files to use as templates for the output time series files.*/ ndgAssoc( "REF", 1, &igrp2, &size, &flag, status ); /* Get output file(s) */ kpg1Wgndf( "OUT", igrp2, size, size, "More output files required...", &ogrp, &outsize, status ); /* Get he noise level to add to the output data. */ parGet0d( "SIGMA", &sigma, status ); /* Get any Q and U input maps. */ if( *status == SAI__OK ) { kpg1Rgndf( "QIN", 1, 1, "", &igrpq, &nskymap, status ); ndgNdfas( igrpq, 1, "READ", &indfq, status ); ndfMap( indfq, "DATA", "_DOUBLE", "READ", (void **) &inq_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "Q", indfq ); *status = SAI__ERROR; errRep( "", "Q image '^Q' is not the same size as the I image.", status ); } kpg1Rgndf( "UIN", 1, 1, "", &igrpu, &nskymap, status ); ndgNdfas( igrpu, 1, "READ", &indfu, status ); ndfMap( indfu, "DATA", "_DOUBLE", "READ", (void **) &inu_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "U", indfu ); *status = SAI__ERROR; errRep( "", "U image '^U' is not the same size as the I image.", status ); } if( *status == PAR__NULL ) { ndfAnnul( &indfq, status ); ndfAnnul( &indfu, status ); inq_data = NULL; inu_data = NULL; errAnnul( status ); } else { parGet0d( "ANGROT", &angrot, status ); parGet0d( "PAOFF", &paoff, status ); parGet0l( "PASIGN", &pasign, status ); } } /* Get any common-mode files. */ if( *status == SAI__OK ) { kpg1Rgndf( "COM", size, size, "", &igrpc, &ncom, status ); if( *status == PAR__NULL ) { errAnnul( status ); ncom = 0; } } /* Get any GAI files. */ if( *status == SAI__OK ) { kpg1Rgndf( "GAI", size, size, "", &igrpg, &ngai, status ); if( *status == PAR__NULL ) { errAnnul( status ); ngai = 0; } } /* Get any instrumental polarisation files. */ if( *status == SAI__OK ) { /* First see if the user wants to use the "INSTQ/INSTU" scheme for specifying instrumental polarisation. */ ndfAssoc( "INSTQ", "Read", &indfiq, status ); ndfAssoc( "INSTU", "Read", &indfiu, status ); if( *status == PAR__NULL ) { ndfAnnul( &indfiq, status ); ndfAnnul( &indfiu, status ); errAnnul( status ); } else { msgOut( " ", "Using user-defined IP model", status ); ndfDim( indfiq, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfiq ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfiq, "DATA", "_DOUBLE", "READ", (void **) &qinst_data, &nel, status ); } ndfDim( indfiu, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfiu ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfiu, "DATA", "_DOUBLE", "READ", (void **) &uinst_data, &nel, status ); } } /* If not, see if the user wants to use the Johnstone/Kennedy instrumental polarisation model. The IPDATA parameter gives the path to an HDS container file contining NDFs holding the required IP data for all subarrays. */ if( !qinst_data ) { parGet0c( "IPDATA", ipdata, sizeof(ipdata), status ); if( *status == PAR__NULL ) { errAnnul( status ); } else { msgOutf( " ", "Using Johnstone/Kennedy IP model in %s", status, ipdata ); hdsOpen( ipdata, "READ", &iploc, status ); } } } /* Loop round all the template time series files. */ for( ifile = 1; ifile <= (int) size && *status == SAI__OK; ifile++ ) { /* Start a new NDF context. */ ndfBegin(); /* Create the output NDF by propagating everything from the input, except for quality and variance. */ ndgNdfas( igrp2, ifile, "READ", &indfin, status ); ndfMsg( "FILE", indfin ); msgSeti( "THISFILE", ifile ); msgSeti( "NUMFILES", size ); msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE", status ); ndgNdfpr( indfin, "DATA,HISTORY,LABEL,TITLE,WCS,UNITS,EXTENSION(*)", ogrp, ifile, &indfout, status ); ndfAnnul( &indfin, status ); ndfAnnul( &indfout, status ); /* We now re-open the output NDF and then modify its data values. */ smf_open_file( wf, ogrp, ifile, "UPDATE", 0, &odata, status ); /* Issue a suitable message and abort if anything went wrong. */ if( *status != SAI__OK ) { errRep( FUNC_NAME, "Could not open input template file.", status ); break; } else { if( odata->file == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfFile associated with smfData.", status ); break; } else if( odata->hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfHead associated with smfData.", status ); break; } } /* Check the reference time series contains double precision values. */ smf_dtype_check_fatal( odata, NULL, SMF__DOUBLE, status ); /* Get the total number of data elements, and the number of time slices. */ smf_get_dims( odata, NULL, NULL, NULL, &ntslice, &ndata, NULL, NULL, status ); /* Get the subarray name */ smf_fits_getS( odata->hdr, "SUBARRAY", subarray, sizeof(subarray), status ); /* If we are using the Johnstone/Kennedy IP model, open and map the relevant parameter NDFs within the IPDATA container file. */ if( iploc ) { datFind( iploc, subarray, &cloc, status ); ndfFind( cloc, "C0", &indfc0, status ); ndfDim( indfc0, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfc0 ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfc0, "DATA", "_DOUBLE", "READ", (void **) &c0_data, &nel, status ); } ndfFind( cloc, "P0", &indfp0, status ); ndfDim( indfp0, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfp0 ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfp0, "DATA", "_DOUBLE", "READ", (void **) &p0_data, &nel, status ); } ndfFind( cloc, "P1", &indfp1, status ); ndfDim( indfp1, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfp1 ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfp1, "DATA", "_DOUBLE", "READ", (void **) &p1_data, &nel, status ); } ndfFind( cloc, "ANGC", &indfangc, status ); ndfDim( indfangc, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfangc ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfangc, "DATA", "_DOUBLE", "READ", (void **) &angc_data, &nel, status ); } } /* Open any COM file. */ if( ncom ) { ndgNdfas( igrpc, ifile, "READ", &indfc, status ); ndfDim( indfc, 3, cdims, &ndimc, status ); /* Check its dimensions. */ if( *status == SAI__OK ) { if( ndimc == 1 ) { if( cdims[ 0 ] < (int) ntslice ) { *status = SAI__ERROR; ndfMsg( "C", indfc ); ndfMsg( "R", indfin ); msgSeti( "N", cdims[ 0 ] ); msgSeti( "M", ntslice ); errRep( " ", "Supplied COM file (^C) has ^N time-slices, but " "the reference NDF (^R) has ^M time-slices.", status ); } else { ndfBound( indfc, 3, lbndc, ubndc, &ndimc, status ); ubndc[ 0 ] = lbndc[ 0 ] + ntslice - 1; ndfSect( indfc, 1, lbndc, ubndc, &indfcs, status ); } } else if( ndimc == 3 ) { if( cdims[ 0 ] != 1 || cdims[ 1 ] != 1 ) { *status = SAI__ERROR; ndfMsg( "C", indfc ); errRep( " ", "Supplied 3D COM file (^C) has bad " "dimensions for axis 1 and/or 2 (should " "both be 1 pixel long).", status ); } else if( cdims[ 2 ] < (int) ntslice ) { *status = SAI__ERROR; ndfMsg( "C", indfc ); ndfMsg( "R", indfin ); msgSeti( "N", cdims[ 2 ] ); msgSeti( "M", ntslice ); errRep( " ", "Supplied COM file (^C) has ^N time-slices, but " "the reference NDF (^R) has ^M time-slices.", status ); } else { ndfBound( indfc, 3, lbndc, ubndc, &ndimc, status ); ubndc[ 2 ] = lbndc[ 2 ] + ntslice - 1; ndfSect( indfc, 3, lbndc, ubndc, &indfcs, status ); } } else { *status = SAI__ERROR; ndfMsg( "C", indfc ); msgSeti( "N", ndimc ); errRep( " ", "Supplied COM file (^C) has ^N dimensions - " "must be 3.", status ); } } ndfMap( indfcs, "DATA", "_DOUBLE", "READ", (void **) &inc_data, &nelc, status ); } else { indfcs = NDF__NOID; inc_data = NULL; } /* Open any GAI files. */ if( ngai ) { ndgNdfas( igrpg, ifile, "READ", &indfg, status ); ndfDim( indfg, 3, gdims, &ndimg, status ); /* Check its dimensions, and map it if OK. */ if( *status == SAI__OK ) { if( ndimg != 2 ) { *status = SAI__ERROR; ndfMsg( "C", indfg ); msgSeti( "N", ndimg ); errRep( " ", "Supplied GAI file (^C) has ^N dimensions - " "must be 2.", status ); } else if( gdims[ 0 ] != 32 || gdims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "C", indfg ); errRep( " ", "Supplied GAI file (^C) has has bad " "dimensions - should be 32x40.", status ); } } ndfMap( indfg, "DATA", "_DOUBLE", "READ", (void **) &gai_data, &nelg, status ); } else { indfg = NDF__NOID; gai_data = NULL; } /* Fill the output with bad values. */ if( *status == SAI__OK ) { pd = odata->pntr[ 0 ]; for( iel = 0; iel < ndata; iel++ ) *(pd++) = VAL__BADD; } /* Resample the sky map data into the output time series. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, in_data, odata->pntr[ 0 ], NULL, &ngood, status ); /* Add on any COM data. */ smf_addcom( wf, odata, inc_data, status ); /* Issue a wrning if there is no good data in the output cube. */ if( ngood == 0 ) msgOutif( MSG__NORM, " ", " Output contains no " "good data values.", status ); /* If Q and U maps have been given, allocate room to hold resampled Q and U values, and fill them with bad values. */ if( inq_data && inu_data ) { pq = outq_data = astMalloc( ndata*sizeof( *outq_data ) ); pu = outu_data = astMalloc( ndata*sizeof( *outu_data ) ); if( *status == SAI__OK ) { for( iel = 0; iel < ndata; iel++ ) { *(pu++) = VAL__BADD; *(pq++) = VAL__BADD; } } /* Determine the harmonic to use. */ parGet0i( "HARMONIC", &harmonic, status ); /* If producing the normal 8 Hz harmonic, get the amplitude and phase of a other signals to add onto the 8 Hz signal. */ if( harmonic == 4 ) { parGet0d( "AMP2", &2, status ); parGet0d( "PHASE2", &phase2, status ); parGet0d( "AMP4", &4, status ); parGet0d( "PHASE4", &phase4, status ); parGet0d( "AMP16", &16, status ); parGet0d( "PHASE16", &phase16, status ); } else { amp2 = 0.0; phase2 = 0.0; amp4 = 0.0; phase4 = 0.0; amp16 = 0.0; phase16 = 0.0; } /* Allocate room for an array to hold the angle from the Y pixel axis in the sky map to the focal plane Y axis, in radians, at each time slice. Positive rotation is in the same sense as rotation from focal plane X to focal plane Y. */ ang_data = astMalloc( ntslice*sizeof( *ang_data ) ); /* Resample them both into 3D time series. These Q/U values arw with respect to the sky image Y axis. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inq_data, outq_data, ang_data, &ngood, status ); smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inu_data, outu_data, NULL, &ngood, status ); /* Combine these time series with the main output time series so that the main output is analysed intensity. */ smf_uncalc_iqu( wf, odata, odata->pntr[ 0 ], outq_data, outu_data, ang_data, pasign, AST__DD2R*paoff, AST__DD2R*angrot, amp2, AST__DD2R*phase2, amp4, AST__DD2R*phase4, amp16, AST__DD2R*phase16, qinst_data, uinst_data, c0_data, p0_data, p1_data, angc_data, harmonic, status ); /* Release work space. */ outq_data = astFree( outq_data ); outu_data = astFree( outu_data ); ang_data = astFree( ang_data ); } /* Factor in any GAI data. */ smf_addgai( wf, odata, gai_data, status ); /* Close the output time series file. */ smf_close_file( wf, &odata, status ); /* Close the IP data container for the current subarray, if it is open. */ if( cloc ) datAnnul( &cloc, status ); /* End the NDF context. */ ndfEnd( status ); } /* Close any input data file that is still open due to an early exit from the above loop. */ if( odata != NULL ) { smf_close_file( wf, &odata, status ); odata = NULL; } /* Free remaining resources. */ if( igrp1 != NULL) grpDelet( &igrp1, status); if( igrp2 != NULL) grpDelet( &igrp2, status); if( igrpq != NULL) grpDelet( &igrpq, status); if( igrpu != NULL) grpDelet( &igrpu, status); if( igrpc != NULL) grpDelet( &igrpc, status); if( igrpg != NULL) grpDelet( &igrpg, status); if( ogrp != NULL) grpDelet( &ogrp, status); if( iploc ) datAnnul( &iploc, status ); /* End the NDF context. */ ndfEnd( status ); /* End the tile's AST context. */ astEnd; /* Issue a status indication.*/ if( *status == SAI__OK ) { msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status); } else { msgOutif(MSG__VERB," ",TASK_NAME " failed.", status); } }
void smf_filter_fromkeymap( smfFilter *filt, AstKeyMap *keymap, const smfHead *hdr, int *dofilt, int *whiten, int *status ) { int dofft=0; /* Set if freq. domain filtering the data */ double f_edgelow; /* Freq. cutoff for low-pass edge filter */ double f_edgehigh; /* Freq. cutoff for high-pass edge filter */ double f_edgesmall; /* Select low-pass based on spatial scale */ double f_edgelarge; /* Select high-pass based on spatial scale */ double f_low; /* Lowest edge frequency */ double f_notchlow[SMF__MXNOTCH]; /* Array low-freq. edges of notch filters */ double f_notchhigh[SMF__MXNOTCH];/* Array high-freq. edges of notch filters */ int f_nnotch=0; /* Number of notch filters in array */ int i; /* Loop count */ int ival = 0; /* Dummy integer argument */ int whitening; /* Will we apply a whitening filter? */ /* Main routine */ if( *status != SAI__OK ) return; if( !filt ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": NULL smfFilter pointer", status ); return; } if( filt->ndims != 1 ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": can only create time-series filters at present", status ); return; } /* Search for filtering parameters in the keymap. None of these parameters represent a number of time clies, so we can set the smfData (the 2nd argument) to NULL. */ smf_get_cleanpar( keymap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &f_edgelow, &f_edgehigh, &f_edgesmall, &f_edgelarge, f_notchlow, f_notchhigh, &f_nnotch, &dofft, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &whitening, NULL, NULL, NULL, NULL, NULL, NULL, NULL, status ); /* Modify edge filters if spacial scales were requested */ smf_scale2freq( f_edgesmall, f_edgelarge, hdr, &f_edgelow, &f_edgehigh, status ); /* Return dofilt if requested */ if( dofilt ) { *dofilt = dofft; } if( f_edgelarge ) { } /* If filtering parameters given, create filter */ if( dofft ) { if( f_edgelow ) { smf_filter_edge( filt, f_edgelow, 1, status ); } if( f_edgehigh ) { smf_filter_edge( filt, f_edgehigh, 0, status ); } if( f_nnotch ) { smf_filter_notch( filt, f_notchlow, f_notchhigh, f_nnotch, status ); } if( ! astMapGet0D( keymap, "FILT_WLIM", &(filt->wlim) ) ) { filt->wlim = VAL__BADD; } /* If no apodisation length has been given, use a default of 1/(steptime*freq) where freq is the lowest edge or notch frequency. We only apodise if we are padding data with zeros. */ if( astMapGet0I( keymap, "ZEROPAD", &ival ) && ival ) { if( astMapGet0I( keymap, "APOD", &ival ) ) { filt->apod_length = ival; } else { f_low = ( f_edgehigh > 0.0 ) ? f_edgehigh : VAL__MAXD; if( f_edgelow > 0.0 && f_edgelow < f_low ) f_low = f_edgelow; for( i = 0; i < f_nnotch; i++ ) { if( f_notchlow[ i ] > 0.0 && f_notchlow[ i ] < f_low ) f_low = f_notchlow[ i ]; } if( f_low != VAL__MAXD ) { filt->apod_length = 0.5*filt->df[0]*filt->rdims[0]/f_low; } else { filt->apod_length = 0; } msgSeti( "P", (int) filt->apod_length ); msgOutif( MSG__VERB, "", "Apodising ^P samples at start and end of " "each time stream.", status ); } } else { filt->apod_length = SMF__BADSZT; msgOutif( MSG__VERB, "", " Data will not be apodised since time streams " "are being padded with artificial data rather than zeros.", status ); } } /* Return whiten if requested */ if( whiten ) *whiten = whitening; }
unsigned char *smf_get_mask( ThrWorkForce *wf, smf_modeltype mtype, AstKeyMap *config, smfDIMMData *dat, int flags, int *status ) { /* Local Variables: */ AstCircle *circle; /* AST Region used to mask a circular area */ AstKeyMap *akm; /* KeyMap holding AST config values */ AstKeyMap *subkm; /* KeyMap holding model config values */ char refparam[ DAT__SZNAM ];/* Name for reference NDF parameter */ char words[100]; /* Buffer for variable message words */ const char *cval; /* The ZERO_MASK string value */ const char *modname; /* The name of the model being masked */ const char *skyrefis; /* Pointer to SkyRefIs attribute value */ dim_t i; /* Pixel index */ double *pd; /* Pointer to next element of map data */ double *predef; /* Pointer to mask defined by previous run */ double *ptr; /* Pointer to NDF Data array */ double *pv; /* Pointer to next element of map variance */ double centre[ 2 ]; /* Coords of circle centre in radians */ double meanhits; /* Mean hits in the map */ double radius[ 1 ]; /* Radius of circle in radians */ double zero_circle[ 3 ]; /* LON/LAT/Radius of circular mask */ double zero_lowhits; /* Fraction of mean hits at which to threshold */ double zero_snr; /* Higher SNR at which to threshold */ double zero_snrlo; /* Lower SNR at which to threshold */ int *ph; /* Pointer to next hits value */ int have_mask; /* Did a mask already exist on entry? */ int imask; /* Index of next mask type */ int indf1; /* Id. for supplied reference NDF */ int indf2; /* Id. for used section of reference NDF */ int isstatic; /* Are all used masks static? */ int lbnd_grid[ 2 ]; /* Lower bounds of map in GRID coords */ int mask_types[ NTYPE ]; /* Identifier for the types of mask to use */ int munion; /* Use union of supplied masks */ int nel; /* Number of mapped NDF pixels */ int nmask; /* The number of masks to be combined */ int nsource; /* No. of source pixels in final mask */ int skip; /* No. of iters for which AST is not subtracted */ int thresh; /* Absolute threshold on hits */ int ubnd_grid[ 2 ]; /* Upper bounds of map in GRID coords */ int zero_c_n; /* Number of zero circle parameters read */ int zero_mask; /* Use the reference NDF as a mask? */ int zero_niter; /* Only mask for the first "niter" iterations. */ int zero_notlast; /* Don't zero on last iteration? */ size_t ngood; /* Number good samples for stats */ smf_qual_t *pq; /* Pinter to map quality */ unsigned char **mask; /* Address of model's mask pointer */ unsigned char *newmask; /* Individual mask work space */ unsigned char *pm; /* Pointer to next returned mask pixel */ unsigned char *pn; /* Pointer to next new mask pixel */ unsigned char *result; /* Returned mask pointer */ /* Initialise returned values */ result = NULL; /* Check inherited status. Also check that a map is being created. */ if( *status != SAI__OK || !dat || !dat->map ) return result; /* Begin an AST context. */ astBegin; /* Get the sub-keymap containing the configuration parameters for the requested model. Also get a pointer to the mask array to use (there is one for each maskable model)*/ if( mtype == SMF__COM ) { modname = "COM"; mask = &(dat->com_mask); } else if( mtype == SMF__AST ) { modname = "AST"; mask = &(dat->ast_mask); } else if( mtype == SMF__FLT ) { modname = "FLT"; mask = &(dat->flt_mask); } else { modname = NULL; mask = NULL; *status = SAI__ERROR; errRepf( " ", "smf_get_mask: Unsupported model type %d supplied - " "must be COM, FLT or AST.", status, mtype ); } subkm = NULL; astMapGet0A( config, modname, &subkm ); /* Get the "ast.skip" value - when considering "zero_niter" and "zero_freeze", we only count iterations for which the AST model is subtracted (i.e. the ones following the initial "ast.skip" iterations). */ astMapGet0A( config, "AST", &akm ); astMapGet0I( akm, "SKIP", &skip ); akm = astAnnul( akm ); /* Get the number of iterations over which the mask is to be applied. Zero means all. Return with no mask if this number of iterations has already been performed. */ zero_niter = 0; astMapGet0I( subkm, "ZERO_NITER", &zero_niter ); if( zero_niter == 0 || dat->iter < zero_niter + skip ) { /* Only return a mask if this is not the last iteration, or if ZERO_NOTLAST is unset. */ zero_notlast = 0; astMapGet0I( subkm, "ZERO_NOTLAST", &zero_notlast ); if( !( flags & SMF__DIMM_LASTITER ) || !zero_notlast ) { /* Create a list of the mask types to be combined to get the final mask by looking for non-default values for the corresponding configuration parameters in the supplied KeyMap. Static masks (predefined, circles or external NDFs) may be used on any iteration, but dynamic masks (lowhits, snr) will only be avialable once the map has been determined at the end of the first iteration. This means that when masking anything but the AST model (which is determined after the map), the dynamic masks cannot be used on the first iteration. Make a note if all masks being used are static. */ isstatic = 1; nmask = 0; zero_lowhits = 0.0; astMapGet0D( subkm, "ZERO_LOWHITS", &zero_lowhits ); if( zero_lowhits > 0.0 ) { if( mtype == SMF__AST || !( flags & SMF__DIMM_FIRSTITER ) ) { mask_types[ nmask++] = LOWHITS; isstatic = 0; } } else if( zero_lowhits < 0.0 && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "Bad value for config parameter %s.ZERO_LOWHITS (%g) - " "it must not be negative.", status, modname, zero_lowhits ); } if( astMapGet1D( subkm, "ZERO_CIRCLE", 3, &zero_c_n, zero_circle ) ) { if( zero_c_n == 1 || zero_c_n == 3 ) { mask_types[ nmask++] = CIRCLE; } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "Bad number of values (%d) for config parameter " "%s.ZERO_CIRCLE - must be 1 or 3.", status, zero_c_n, modname ); } } cval = NULL; astMapGet0C( subkm, "ZERO_MASK", &cval ); if( cval ) { if( !astChrMatch( cval, "REF" ) && !astChrMatch( cval, "MASK2" ) && !astChrMatch( cval, "MASK3" ) ) { astMapGet0I( subkm, "ZERO_MASK", &zero_mask ); cval = ( zero_mask > 0 ) ? "REF" : NULL; } if( cval ) { strcpy( refparam, cval ); astChrCase( NULL, refparam, 1, 0 ); mask_types[ nmask++] = REFNDF; } } zero_snr = 0.0; astMapGet0D( subkm, "ZERO_SNR", &zero_snr ); if( zero_snr > 0.0 ) { if( mtype == SMF__AST || !( flags & SMF__DIMM_FIRSTITER ) ) { mask_types[ nmask++] = SNR; isstatic = 0; } } else if( zero_snr < 0.0 && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "Bad value for config parameter %s.ZERO_SNR (%g) - " "it must not be negative.", status, modname, zero_snr ); } if( astMapHasKey( subkm, "ZERO_MASK_POINTER" ) ) { astMapGet0P( subkm, "ZERO_MASK_POINTER", (void **) &predef ); if( predef ) mask_types[ nmask++] = PREDEFINED; } /* No need to create a mask if no masking was requested or possible. */ if( nmask > 0 ) { /* Decide if we are using the union or intersection of the masks. */ astMapGet0I( subkm, "ZERO_UNION", &munion ); /* Note if a mask existed on entry. If not, create a mask now, and initialise it to hold the mask defined by the initial sky map. */ if( *mask == NULL ) { have_mask = 0; if( dat->initqual ) { *mask = astMalloc( dat->msize*sizeof( **mask ) ); if( *mask ) { pm = *mask; pq = dat->initqual; for( i = 0; i < dat->msize; i++ ) { *(pm++) = ( *(pq++) != 0 ); } } } else{ *mask = astCalloc( dat->msize, sizeof( **mask ) ); } } else { have_mask = 1; } /* If we are combining more than one mask, we need work space to hold an individual mask independently of the total mask. If we are using only one mask, then just use the main mask array. */ if( nmask > 1 ) { newmask = astMalloc( dat->msize*sizeof( *newmask ) ); } else { newmask = *mask; } /* Get the number of iterations after which the mask is to be frozen. Zero means "never freeze the mask". */ int zero_freeze = 0; astMapGet0I( subkm, "ZERO_FREEZE", &zero_freeze ); /* Loop round each type of mask to be used. */ for( imask = 0; imask < nmask && *status == SAI__OK; imask++ ){ /* If the mask is now frozen, we just return the existing mask. So leave the loop. */ if( zero_freeze != 0 && dat->iter > zero_freeze + skip ) { break; /* Low hits masking... */ } else if( mask_types[ imask ] == LOWHITS ) { /* Set hits pixels with 0 hits to VAL__BADI so that stats1 ignores them */ ph = dat->hitsmap; for( i = 0; i < dat->msize; i++,ph++ ) { if( *ph == 0 ) *ph = VAL__BADI; } /* Find the mean hits in the map */ smf_stats1I( dat->hitsmap, 1, dat->msize, NULL, 0, 0, &meanhits, NULL, NULL, &ngood, status ); msgOutiff( MSG__DEBUG, " ", "smf_get_mask: mean hits = %lf, ngood " "= %zd", status, meanhits, ngood ); /* Create the mask */ thresh = meanhits*zero_lowhits; ph = dat->hitsmap; pn = newmask; for( i = 0; i < dat->msize; i++,ph++ ) { *(pn++) = ( *ph != VAL__BADI && *ph < thresh ) ? 1 : 0; } /* Report masking info. */ msgOutiff( MSG__DEBUG, " ", "smf_get_mask: masking %s " "model at hits = %d.", status, modname, thresh ); /* Circle masking... */ } else if( mask_types[ imask ] == CIRCLE ) { /* If we had a mask on entry, then there is no need to create a new one since it will not have changed. But we need to recalculate the circle mask if are combining it with any non-static masks. */ if( ! have_mask || ! isstatic ) { /* If only one parameter supplied it is radius, assume reference LON/LAT from the frameset to get the centre. If the SkyFrame represents offsets from the reference position (i.e. the source is moving), assume the circle is to be centred on the origin. */ if( zero_c_n == 1 ) { zero_circle[ 2 ] = zero_circle[ 0 ]; skyrefis = astGetC( dat->outfset, "SkyRefIs" ); if( skyrefis && !strcmp( skyrefis, "Origin" ) ) { zero_circle[ 0 ] = 0.0; zero_circle[ 1 ] = 0.0; } else { zero_circle[ 0 ] = astGetD( dat->outfset, "SkyRef(1)" ); zero_circle[ 1 ] = astGetD( dat->outfset, "SkyRef(2)" ); } zero_circle[ 0 ] *= AST__DR2D; zero_circle[ 1 ] *= AST__DR2D; } /* The supplied bounds are for pixel coordinates... we need bounds for grid coordinates which have an offset */ lbnd_grid[ 0 ] = 1; lbnd_grid[ 1 ] = 1; ubnd_grid[ 0 ] = dat->ubnd_out[ 0 ] - dat->lbnd_out[ 0 ] + 1; ubnd_grid[ 1 ] = dat->ubnd_out[ 1 ] - dat->lbnd_out[ 1 ] + 1; /* Coordinates & radius of the circular region converted from degrees to radians */ centre[ 0 ] = zero_circle[ 0 ]*AST__DD2R; centre[ 1 ] = zero_circle[ 1 ]*AST__DD2R; radius[ 0 ] = zero_circle[ 2 ]*AST__DD2R; /* Create the Circle, defined in the current Frame of the FrameSet (i.e. the sky frame). */ circle = astCircle( astGetFrame( dat->outfset, AST__CURRENT), 1, centre, radius, NULL, " " ); /* Fill the mask with zeros. */ memset( newmask, 0, sizeof( *newmask )*dat->msize ); /* Get the mapping from the sky frame (current) to the grid frame (base), and then set the mask to 1 for all of the values outside this circle */ astMaskUB( circle, astGetMapping( dat->outfset, AST__CURRENT, AST__BASE ), 0, 2, lbnd_grid, ubnd_grid, newmask, 1 ); /* Report masking info. */ if( zero_niter == 0 ) { sprintf( words, "on each iteration" ); } else { sprintf( words, "for %d iterations", zero_niter ); } msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model will" " be masked %s using a circle of " "radius %g arc-secs, centred at %s=%s, %s=%s.", status, modname, words, radius[0]*AST__DR2D*3600, astGetC( dat->outfset, "Symbol(1)" ), astFormat( dat->outfset, 1, centre[ 0 ] ), astGetC( dat->outfset, "Symbol(2)" ), astFormat( dat->outfset, 2, centre[ 1 ] ) ); } /* Reference NDF masking... */ } else if( mask_types[ imask ] == REFNDF ) { /* If we had a mask on entry, then there is no need to create a new one since it will not have changed. But we need to recalculate the NDF mask if are combining it with any non-static masks. */ if( ! have_mask || ! isstatic ) { /* Begin an NDF context. */ ndfBegin(); /* Get an identifier for the NDF using the associated ADAM parameter. */ ndfAssoc( refparam, "READ", &indf1, status ); /* Get a section from this NDF that matches the bounds of the map. */ ndfSect( indf1, 2, dat->lbnd_out, dat->ubnd_out, &indf2, status ); /* Map the section. */ ndfMap( indf2, "DATA", "_DOUBLE", "READ", (void **) &ptr, &nel, status ); /* Check we can use the pointer safely. */ if( *status == SAI__OK ) { /* Find bad pixels in the NDF and set those pixels to 1 in the mask. */ pn = newmask; for( i = 0; i < dat->msize; i++ ) { *(pn++) = ( *(ptr++) == VAL__BADD ) ? 1 : 0; } /* Report masking info. */ ndfMsg( "N", indf2 ); msgSetc( "M", modname ); if( zero_niter == 0 ) { msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The ^M " "model will be masked on each iteration " "using the bad pixels in NDF '^N'.", status ); } else { msgSeti( "I", zero_niter ); msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The ^M " "model will be masked for ^I iterations " "using the bad pixels in NDF '^N'.", status ); } } /* End the NDF context. */ ndfEnd( status ); } /* SNR masking... */ } else if( mask_types[ imask ] == SNR ) { /* Get the lower SNR limit. */ zero_snrlo = 0.0; astMapGet0D( subkm, "ZERO_SNRLO", &zero_snrlo ); if( zero_snrlo <= 0.0 ) { zero_snrlo = zero_snr; } else if( zero_snrlo > zero_snr && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "Bad value for config parameter " "%s.ZERO_SNRLO (%g) - it must not be higher " "than %s.ZERO_SNR (%g).", status, modname, zero_snrlo, modname, zero_snr ); } /* If the higher and lower SNR limits are equal, just do a simple threshold on the SNR values to get the mask. */ if( zero_snr == zero_snrlo ) { pd = dat->map; pv = dat->mapvar; pn = newmask; for( i = 0; i < dat->msize; i++,pd++,pv++ ) { *(pn++) = ( *pd != VAL__BADD && *pv != VAL__BADD && *pv >= 0.0 && *pd < zero_snr*sqrt( *pv ) ) ? 1 : 0; } /* Report masking info. */ if( !have_mask ) { if( zero_niter == 0 ) { sprintf( words, "on each iteration" ); } else { sprintf( words, "for %d iterations", zero_niter ); } msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model " "will be masked %s using an SNR limit of %g.", status, modname, words, zero_snr ); } /* If the higher and lower SNR limits are different, create an initial mask by thresholding at the ZERO_SNR value, and then extend the source areas within the mask down to an SNR limit of ZERO_SNRLO. */ } else { smf_snrmask( wf, dat->map, dat->mapvar, dat->mdims, zero_snr, zero_snrlo, newmask, status ); /* Report masking info. */ if( !have_mask ) { if( zero_niter == 0 ) { sprintf( words, "on each iteration" ); } else { sprintf( words, "for %d iterations", zero_niter ); } msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model " "will be masked %s using an SNR limit of %g " "extended down to %g.", status, modname, words, zero_snr, zero_snrlo ); } } /* Predefined masking... */ } else if( mask_types[ imask ] == PREDEFINED ) { /* If we had a mask on entry, then there is no need to create a new one since it will not have changed. But we need to recalculate the mask if are combining it with any non-static masks. */ if( ! have_mask || ! isstatic ) { /* Find bad pixels in the predefined array and set those pixels to 1 in the mask. */ pn = newmask; for( i = 0; i < dat->msize; i++ ) { *(pn++) = ( *(predef++) == VAL__BADD ) ? 1 : 0; } /* Report masking info. */ if( zero_niter == 0 ) { sprintf( words, "on each iteration" ); } else { sprintf( words, "for %d iterations", zero_niter ); } msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model " "will be masked %s using a smoothed form of " "the final mask created with the previous map.", status, modname, words ); } } /* If required, add the new mask into the returned mask. If this is the first mask, we just copy the new mask to form the returned mask. Otherwise, we combine it with the existing returned mask. */ if( ! have_mask || ! isstatic ) { if( nmask > 1 ) { if( imask == 0 ) { memcpy( *mask, newmask, dat->msize*sizeof(*newmask)); } else { pm = *mask; pn = newmask; if( munion ) { for( i = 0; i < dat->msize; i++,pm++ ) { if( *(pn++) == 0 ) *pm = 0; } } else { for( i = 0; i < dat->msize; i++,pm++ ) { if( *(pn++) == 1 ) *pm = 1; } } } } } } /* Free the individual mask work array if it was used. */ if( nmask > 1 ) newmask = astFree( newmask ); /* Check that the mask has some source pixels (i.e. pixels that have non-bad data values - we do not also check variance values since they are not available until the second iteration). */ if( *status == SAI__OK ) { nsource = 0; pm = *mask; pd = dat->map; for( i = 0; i < dat->msize; i++,pd++,pv++,pm++ ) { if( *pd != VAL__BADD && *pm == 0 ) nsource++; } if( nsource < 5 && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "The %s mask being used has fewer than 5 " "source pixels.", status, modname ); if( zero_snr > 0.0 ) { errRepf( "", "Maybe your zero_snr value (%g) is too high?", status, zero_snr ); } } } /* Return the mask pointer if all has gone well. */ if( *status == SAI__OK ) result = *mask; } } } /* End the AST context, annulling all AST Objects created in the context. */ astEnd; /* Return the pointer to the boolean mask. */ return result; }
void smurf_extinction( int * status ) { /* Local Variables */ smfArray *bbms = NULL; /* Bad bolometer masks */ smfArray *darks = NULL; /* Dark data */ AstKeyMap *extpars = NULL; /* Tau relation keymap */ Grp *fgrp = NULL; /* Filtered group, no darks */ smfArray *flatramps = NULL;/* Flatfield ramps */ int has_been_sky_removed = 0;/* Data are sky-removed */ AstKeyMap *heateffmap = NULL; /* Heater efficiency data */ size_t i; /* Loop counter */ Grp *igrp = NULL; /* Input group */ AstKeyMap *keymap=NULL; /* Keymap for storing parameters */ smf_tausrc tausrc; /* enum value of optical depth source */ smf_extmeth extmeth; /* Extinction correction method */ char tausource[LEN__METHOD]; /* String for optical depth source */ char method[LEN__METHOD]; /* String for extinction airmass method */ smfData *odata = NULL; /* Output data struct */ Grp *ogrp = NULL; /* Output group */ size_t outsize; /* Total number of NDF names in the output group */ size_t size; /* Number of files in input group */ double tau = 0.0; /* Zenith tau at this wavelength */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ if (*status != SAI__OK) return; /* Main routine */ ndfBegin(); /* Find the number of cores/processors available and create a pool of threads of the same size. */ wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status ); /* Read the input file */ kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status ); /* Filter out darks */ smf_find_science( igrp, &fgrp, 0, NULL, NULL, 1, 1, SMF__NULL, &darks, &flatramps, &heateffmap, NULL, status ); /* input group is now the filtered group so we can use that and free the old input group */ size = grpGrpsz( fgrp, status ); grpDelet( &igrp, status); igrp = fgrp; fgrp = NULL; if (size > 0) { /* Get output file(s) */ kpg1Wgndf( "OUT", igrp, size, size, "More output files required...", &ogrp, &outsize, status ); } else { msgOutif(MSG__NORM, " ","All supplied input frames were DARK," " nothing to extinction correct", status ); } /* Get group of pixel masks and read them into a smfArray */ smf_request_mask( "BBM", &bbms, status ); /* Read the tau relations from config file or group. We do not allow sub instrument overloading because these are all values based on filter name. */ keymap = kpg1Config( "TAUREL", "$SMURF_DIR/smurf_extinction.def", NULL, 1, status ); /* and we need to use the EXT entry */ astMapGet0A( keymap, "EXT", &extpars ); keymap = astAnnul( keymap ); /* Get tau source */ parChoic( "TAUSRC", "Auto", "Auto,CSOtau,CSOFit, Filtertau, WVMraw", 1, tausource, sizeof(tausource), status); /* Decide how the correction is to be applied - convert to flag */ parChoic( "METHOD", "ADAPTIVE", "Adaptive,Quick,Full,", 1, method, sizeof(method), status); /* Place parameters into a keymap and extract values */ if( *status == SAI__OK ) { keymap = astKeyMap( " " ); if( astOK ) { astMapPut0C( keymap, "TAUSRC", tausource, NULL ); astMapPut0C( keymap, "TAUMETHOD", method, NULL ); smf_get_extpar( keymap, &tausrc, &extmeth, NULL, status ); } } for (i=1; i<=size && ( *status == SAI__OK ); i++) { /* Flatfield - if necessary */ smf_open_and_flatfield( igrp, ogrp, i, darks, flatramps, heateffmap, &odata, status ); if (*status != SAI__OK) { /* Error flatfielding: tell the user which file it was */ msgSeti("I",i); errRep(TASK_NAME, "Unable to open the ^I th file", status); } /* Mask out bad pixels - mask data array not quality array */ smf_apply_mask( odata, bbms, SMF__BBM_DATA, 0, status ); /* Now check that the data are sky-subtracted */ if ( !smf_history_check( odata, "smf_subtract_plane", status ) ) { /* Should we override remsky check? */ parGet0l("HASSKYREM", &has_been_sky_removed, status); if ( !has_been_sky_removed && *status == SAI__OK ) { *status = SAI__ERROR; msgSeti("I",i); errRep("", "Input data from file ^I are not sky-subtracted", status); } } /* If status is OK, make decisions on source keywords the first time through. */ if ( *status == SAI__OK && i == 1 ) { if (tausrc == SMF__TAUSRC_CSOTAU || tausrc == SMF__TAUSRC_AUTO || tausrc == SMF__TAUSRC_TAU) { double deftau; const char * param = NULL; smfHead *ohdr = odata->hdr; /* get default CSO tau -- this could be calculated from CSO fits */ deftau = smf_calc_meantau( ohdr, status ); /* Now ask for desired CSO tau */ if ( tausrc == SMF__TAUSRC_CSOTAU || tausrc == SMF__TAUSRC_AUTO) { param = "CSOTAU"; } else if (tausrc == SMF__TAUSRC_TAU) { param = "FILTERTAU"; deftau = smf_cso2filt_tau( ohdr, deftau, extpars, status ); } parGdr0d( param, deftau, 0.0,1.0, 1, &tau, status ); } else if ( tausrc == SMF__TAUSRC_CSOFIT || tausrc == SMF__TAUSRC_WVMRAW ) { /* Defer a message until after extinction correction */ } else { *status = SAI__ERROR; errRep("", "Unsupported opacity source. Possible programming error.", status); } } /* Apply extinction correction - note that a check is made to determine whether the data have already been extinction corrected */ smf_correct_extinction( wf, odata, &tausrc, extmeth, extpars, tau, NULL, NULL, status ); if ( tausrc == SMF__TAUSRC_WVMRAW ) { msgOutif(MSG__VERB," ", "Used Raw WVM data for extinction correction", status); } else if ( tausrc == SMF__TAUSRC_CSOFIT ) { msgOutif(MSG__VERB," ", "Used fit to CSO data for extinction correction", status); } else if ( tausrc == SMF__TAUSRC_CSOTAU ) { msgOutif(MSG__VERB," ", "Used an explicit CSO tau value for extinction correction", status); } else if ( tausrc == SMF__TAUSRC_TAU ) { msgOutif(MSG__VERB," ", "Used an explicit filter tau value for extinction correction", status); } else { if (*status == SAI__OK) { const char * taustr = smf_tausrc_str( tausrc, status ); *status = SAI__ERROR; errRepf( "", "Unexpected opacity source used for extinction correction of %s." " Possible programming error.", status, taustr ); } } /* Set character labels */ smf_set_clabels( "Extinction corrected",NULL, NULL, odata->hdr, status); smf_write_clabels( odata, status ); /* Free resources for output data */ smf_close_file( &odata, status ); } /* Write out the list of output NDF names, annulling the error if a null parameter value is supplied. */ if( *status == SAI__OK && ogrp ) { grpList( "OUTFILES", 0, 0, NULL, ogrp, status ); if( *status == PAR__NULL ) errAnnul( status ); } /* Tidy up after ourselves: release the resources used by the grp routines */ if (darks) smf_close_related( &darks, status ); if (bbms) smf_close_related( &bbms, status ); if( flatramps ) smf_close_related( &flatramps, status ); if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status ); grpDelet( &igrp, status); grpDelet( &ogrp, status); if( keymap ) keymap = astAnnul( keymap ); if (extpars) extpars = astAnnul( extpars ); ndfEnd( status ); }
void smurf_unmakecube( int *status ) { /* Local Variables */ AstFrame *tfrm = NULL; /* Current Frame from input WCS */ AstFrameSet *wcsin = NULL; /* WCS Frameset for input cube */ AstMapping *tmap = NULL; /* Base->current Mapping from input WCS */ AstSkyFrame *iskyfrm = NULL; /* SkyFrame from the input WCS Frameset */ Grp *detgrp = NULL; /* Group of detector names */ Grp *igrp1 = NULL; /* Group of input sky cube files */ Grp *igrp2 = NULL; /* Group of input template files */ Grp *ogrp = NULL; /* Group containing output file */ NdgProvenance *oprov = NULL;/* Provenance for the output NDF */ SkyCube *sky_cubes = NULL; /* Pointer to array of sky cube descriptions */ SkyCube *skycube = NULL; /* Pointer to next sky cube description */ char pabuf[ 10 ]; /* Text buffer for parameter value */ double params[ 4 ]; /* astResample parameters */ int axes[ 2 ]; /* Indices of selected axes */ int blank; /* Was a blank line just output? */ int flag; /* Was the group expression flagged? */ int ifile; /* Input file index */ int interp = 0; /* Pixel interpolation method */ int iskycube; /* Index of current sky cube */ int nel; /* Number of elements in 3D array */ int nparam = 0; /* No. of parameters required for interpolation scheme */ int ondf; /* Output time series NDF identifier */ int outax[ 2 ]; /* Indices of corresponding output axes */ int overlap; /* Does time series overlap sky cube? */ int sdim[3]; /* Array of significant pixel axes */ int usedetpos; /* Should the detpos array be used? */ size_t ndet; /* Number of detectors supplied for "DETECTORS" */ size_t nskycube; /* Number of supplied sky cubes */ size_t outsize; /* Number of files in output group */ size_t size; /* Number of files in input group */ smfData *data = NULL; /* Pointer to data struct */ void *in_data = NULL; /* Pointer to the input cube data array */ void *out_data = NULL; /* Pointer to the output cube data array */ #if defined(FPTRAP) feenableexcept(FE_DIVBYZERO|FE_INVALID|FE_OVERFLOW); #endif /* Check inherited status */ if( *status != SAI__OK ) return; /* We have not yet displayed a blank line on stdout. */ blank = 0; /* Begin an AST context */ astBegin; /* Begin an NDF context. */ ndfBegin(); /* Get a group holding the input sky cubes. */ kpg1Rgndf( "IN", 0, 1, "", &igrp1, &nskycube, status ); /* Create an array of structures to hold information about each input sky cube. */ sky_cubes = astMalloc( sizeof( SkyCube )*(size_t) nskycube ); /* Store a description of each sky cube. */ if( sky_cubes ) { for( iskycube = 0; iskycube < nskycube; iskycube++ ) { skycube = sky_cubes + iskycube; /* Get an NDF identifier for the next sky cube. */ ndgNdfas( igrp1, iskycube + 1, "READ", &(skycube->indf), status ); /* Get the WCS FrameSet from the sky cube, together with its pixel index bounds. */ kpg1Asget( skycube->indf, 3, 0, 1, 1, sdim, skycube->slbnd, skycube->subnd, &wcsin, status ); /* Get the base->current Mapping from the input WCS FrameSet, and split it into two Mappings; one (iskymap) that maps the first 2 GRID axes into celestial sky coordinates, and one (ispecmap) that maps the third GRID axis into a spectral coordinate. Also extract the SpecFrame and SkyFrame from the current Frame. */ tmap = astGetMapping( wcsin, AST__BASE, AST__CURRENT ); tfrm = astGetFrame( wcsin, AST__CURRENT ); axes[ 0 ] = 1; axes[ 1 ] = 2; astMapSplit( tmap, 2, axes, outax, &(skycube->iskymap) ); iskyfrm = astPickAxes( tfrm, 2, outax, NULL ); axes[ 0 ] = 3; astMapSplit( tmap, 1, axes, outax, &(skycube->ispecmap) ); skycube->ispecfrm = astPickAxes( tfrm, 1, outax, NULL ); /* Create a copy of "iskyfrm" representing absolute coords rather than offsets. We assume the target is moving if the cube represents offsets. */ skycube->abskyfrm = astCopy( iskyfrm ); astClear( skycube->abskyfrm, "SkyRefIs" ); skycube->moving = ( *status == SAI__OK && !strcmp( astGetC( iskyfrm, "SkyRefIs" ), "Origin" ) ) ? 1 : 0; /* Invert the Mappings (for the convenience of smf_resamplecube), so that they go from current Frame to grid axis. */ astInvert( skycube->ispecmap ); astInvert( skycube->iskymap ); /* For efficiency, annul manually the unneeded AST objects created in this loop. */ wcsin = astAnnul( wcsin ); tmap = astAnnul( tmap ); tfrm = astAnnul( tfrm ); iskyfrm = astAnnul( iskyfrm ); } } /* See if the detector positions are to be read from the RECEPPOS array in the template NDFs. Otherwise, they are calculated on the basis of the FPLANEX/Y arrays. */ parGet0l( "USEDETPOS", &usedetpos, status ); /* Get the detectors to use. If a null value is supplied, annull the error. Otherwise, make the group case insensitive. */ detgrp = NULL; if( *status == SAI__OK ) { kpg1Gtgrp( "DETECTORS", &detgrp, &ndet, status ); if( *status == PAR__NULL ) { errAnnul( status ); if (detgrp) { grpDelet( &detgrp, status ); } } else { grpSetcs( detgrp, 0, status ); } } /* Get the pixel interpolation scheme to use. */ parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC," "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS", 1, pabuf, 10, status ); if( !strcmp( pabuf, "NEAREST" ) ) { interp = AST__NEAREST; nparam = 0; } else if( !strcmp( pabuf, "LINEAR" ) ) { interp = AST__LINEAR; nparam = 0; } else if( !strcmp( pabuf, "SINC" ) ) { interp = AST__SINC; nparam = 1; } else if( !strcmp( pabuf, "SINCSINC" ) ) { interp = AST__SINCSINC; nparam = 2; } else if( !strcmp( pabuf, "SINCCOS" ) ) { interp = AST__SINCCOS; nparam = 2; } else if( !strcmp( pabuf, "SINCGAUSS" ) ) { interp = AST__SINCGAUSS; nparam = 2; } else if( !strcmp( pabuf, "SOMB" ) ) { interp = AST__SOMB; nparam = 1; } else if( !strcmp( pabuf, "SOMBCOS" ) ) { interp = AST__SOMBCOS; nparam = 2; } else if( *status == SAI__OK ) { nparam = 0; *status = SAI__ERROR; msgSetc( "V", pabuf ); errRep( "", "Support not available for INTERP = ^V (programming " "error)", status ); } /* Get an additional parameter vector if required. */ if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status ); /* Get a group of reference time series files to use as templates for the output time series files.*/ ndgAssoc( "REF", 1, &igrp2, &size, &flag, status ); /* Create a group holding the names of the corresponding output NDFs. */ ndgCreat ( "OUT", igrp2, &ogrp, &outsize, &flag, status ); if( outsize != size && *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "O", outsize ); msgSeti( "I", size ); errRep( "", "Numbers of input reference cubes (^I) and output " "cubes (^O) differ.", status ); } /* Loop round all the template time series files. */ for( ifile = 1; ifile <= size && *status == SAI__OK; ifile++ ) { /* Start a new NDF context. */ ndfBegin(); /* Obtain information about the current template NDF, but do not map the arrays. */ smf_open_file( igrp2, ifile, "READ", SMF__NOCREATE_DATA, &data, status ); /* Issue a suitable message and abort if anything went wrong. */ if( *status != SAI__OK ) { errRep( FUNC_NAME, "Could not open input template file.", status ); break; } else { if( data->file == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfFile associated with smfData.", status ); break; } else if( data->hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfHead associated with smfData.", status ); break; } } /* Report the name of the input template. */ smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); msgSeti( "THISFILE", ifile ); msgSeti( "NUMFILES", size ); msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE", status ); /* Create the output NDF by propagation from the input template NDF. Everything is copied except for the array components and any PROVENANCE extension. */ ndgNdfpr( data->file->ndfid, "TITLE,LABEL,UNITS,AXIS,WCS,HISTORY," "NOEXTENSION(PROVENANCE)", ogrp, ifile, &ondf, status ); /* Ensure the output NDF has a history component. */ ndfHcre( ondf, status ); /* Get a pointer to the mapped output data array. Set all values bad. */ ndfMap( ondf, "DATA", "_REAL", "WRITE/BAD", &out_data, &nel, status ); /* If the detector positions are to calculated on the basis of FPLANEX/Y rather than detpos, then free the detpos array in the templates smfHead structure. This will cause smf_tslice_ast to use the fplanex/y values. */ if( !usedetpos && data->hdr->detpos ) { astFree( (double *) data->hdr->detpos ); data->hdr->detpos = NULL; } /* Get a pointer to a structure holding provenance information for the output time series. */ oprov = ndgReadProv( ondf, "SMURF:UNMAKECUBE", status ); /* Record details of the template in the provenance structure for the output time series. */ ndgPutProv( oprov, data->file->ndfid, NULL, 0, status ); /* Loop round all input sky cubes. */ for( iskycube = 0; iskycube < nskycube; iskycube++ ) { skycube = sky_cubes + iskycube; /* Record details of the input cube in the provenance extension of the output time series. */ ndgPutProv( oprov, skycube->indf, NULL, 0, status ); /* See if the current time series overlaps the current sky cube. */ smf_resampcube( data, skycube->abskyfrm, skycube->iskymap, skycube->ispecfrm, skycube->ispecmap, detgrp, skycube->moving, skycube->slbnd, skycube->subnd, interp, params, NULL, NULL, &overlap, status ); /* If not, pass on to the next sky cube. */ if( overlap ) { /* Report the name of the sky cube. */ ndfMsg( "NDF", skycube->indf ); msgOutif( MSG__NORM, " ", " Re-sampling ^NDF", status ); /* Map the data array in the current sky cube. */ ndfMap( skycube->indf, "DATA", "_REAL", "READ", &in_data, &nel, status ); /* Resample the cube data into the output time series. */ smf_resampcube( data, skycube->abskyfrm, skycube->iskymap, skycube->ispecfrm, skycube->ispecmap, detgrp, skycube->moving, skycube->slbnd, skycube->subnd, interp, params, in_data, out_data, &overlap, status ); /* Unmap the data array. */ ndfUnmap( skycube->indf, "DATA", status ); } } /* Write the provenance structure to the output NDF, and then free it. */ ndgWriteProv( oprov, ondf, 1, status ); oprov =ndgFreeProv( oprov, status ); /* Close the input time series file. */ if( data != NULL ) { smf_close_file( &data, status ); data = NULL; } /* End the NDF context. */ ndfEnd( status ); } /* Close any input data file that is still open due to an early exit from the above loop. */ if( data != NULL ) { smf_close_file( &data, status ); data = NULL; } /* Free remaining resources. */ if( detgrp != NULL) grpDelet( &detgrp, status); if( igrp1 != NULL) grpDelet( &igrp1, status); if( igrp2 != NULL) grpDelet( &igrp2, status); if( ogrp != NULL) grpDelet( &ogrp, status); sky_cubes = astFree( sky_cubes ); /* End the NDF context. */ ndfEnd( status ); /* End the tile's AST context. */ astEnd; /* Issue a status indication.*/ if( *status == SAI__OK ) { msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status); } else { msgOutif(MSG__VERB," ",TASK_NAME " failed.", status); } }
void smf_find_science(const Grp * ingrp, Grp **outgrp, int reverttodark, Grp **darkgrp, Grp **flatgrp, int reducedark, int calcflat, smf_dtype darktype, smfArray ** darks, smfArray **fflats, AstKeyMap ** heateffmap, double * meanstep, int * status ) { smfSortInfo *alldarks; /* array of sort structs for darks */ smfSortInfo *allfflats; /* array of fast flat info */ Grp * dgrp = NULL; /* Internal dark group */ double duration_darks = 0.0; /* total duration of all darks */ double duration_sci = 0.0; /* Duration of all science observations */ size_t dkcount = 0; /* Dark counter */ size_t ffcount = 0; /* Fast flat counter */ Grp * fgrp = NULL; /* Fast flat group */ size_t i; /* loop counter */ smfData *infile = NULL; /* input file */ size_t insize; /* number of input files */ size_t nsteps_dark = 0; /* Total number of steps for darks */ size_t nsteps_sci = 0; /* Total number of steps for science */ AstKeyMap * heatermap = NULL; /* Heater efficiency map */ AstKeyMap * obsmap = NULL; /* Info from all observations */ AstKeyMap * objmap = NULL; /* All the object names used */ AstKeyMap * scimap = NULL; /* All non-flat obs indexed by unique key */ Grp *ogrp = NULL; /* local copy of output group */ size_t sccount = 0; /* Number of accepted science files */ struct timeval tv1; /* Timer */ struct timeval tv2; /* Timer */ if (meanstep) *meanstep = VAL__BADD; if (outgrp) *outgrp = NULL; if (darkgrp) *darkgrp = NULL; if (darks) *darks = NULL; if (fflats) *fflats = NULL; if (heateffmap) *heateffmap = NULL; if (*status != SAI__OK) return; /* Sanity check to make sure we return some information */ if ( outgrp == NULL && darkgrp == NULL && darks == NULL && fflats == NULL) { *status = SAI__ERROR; errRep( " ", FUNC_NAME ": Must have some non-NULL arguments" " (possible programming error)", status); return; } /* Start a timer to see how long this takes */ smf_timerinit( &tv1, &tv2, status ); /* Create new group for output files */ ogrp = smf_grp_new( ingrp, "Science", status ); /* and a new group for darks */ dgrp = smf_grp_new( ingrp, "DarkFiles", status ); /* and for fast flats */ fgrp = smf_grp_new( ingrp, "FastFlats", status ); /* and also create a keymap for the observation description */ obsmap = astKeyMap( "KeyError=1" ); /* and an object map */ objmap = astKeyMap( "KeyError=1" ); /* This keymap contains the sequence counters for each related subarray/obsidss/heater/shutter combination and is used to decide if a bad flat is relevant */ scimap = astKeyMap( "KeyError=1,KeyCase=0" ); /* This keymap is used to contain relevant heater efficiency data */ heatermap = astKeyMap( "KeyError=1,KeyCase=0" ); /* Work out how many input files we have and allocate sufficient sorting space */ insize = grpGrpsz( ingrp, status ); alldarks = astCalloc( insize, sizeof(*alldarks) ); allfflats = astCalloc( insize, sizeof(*allfflats) ); /* check each file in turn */ for (i = 1; i <= insize; i++) { int seqcount = 0; char keystr[100]; /* Key for scimap entry */ /* open the file but just to get the header */ smf_open_file( ingrp, i, "READ", SMF__NOCREATE_DATA, &infile, status ); if (*status != SAI__OK) break; /* Fill in the keymap with observation details */ smf_obsmap_fill( infile, obsmap, objmap, status ); /* Find the heater efficiency map if required */ if (*status == SAI__OK && heateffmap) { char arrayidstr[32]; smf_fits_getS( infile->hdr, "ARRAYID", arrayidstr, sizeof(arrayidstr), status ); if (!astMapHasKey( heatermap, arrayidstr ) ) { smfData * heateff = NULL; dim_t nbolos = 0; smf_flat_params( infile, "RESIST", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &heateff, status ); smf_get_dims( heateff, NULL, NULL, &nbolos, NULL, NULL, NULL, NULL, status ); if (heateff) astMapPut0P( heatermap, arrayidstr, heateff, NULL ); } } /* Get the sequence counter for the file. We do not worry about duplicate sequence counters (at the moment) */ smf_find_seqcount( infile->hdr, &seqcount, status ); /* The key identifying this subarray/obsidss/heater/shutter combo */ smf__calc_flatobskey( infile->hdr, keystr, sizeof(keystr), status ); if (smf_isdark( infile, status )) { /* Store the sorting information */ dkcount = smf__addto_sortinfo( infile, alldarks, i, dkcount, "Dark", status ); smf__addto_durations( infile, &duration_darks, &nsteps_dark, status ); astMapPutElemI( scimap, keystr, -1, seqcount ); } else { /* compare sequence type with observation type and drop it (for now) if they differ */ if ( infile->hdr->obstype == infile->hdr->seqtype ) { /* Sanity check the header for corruption. Compare RTS_NUM with SEQSTART and SEQEND. The first RTS_NUM must either be SEQSTART or else between SEQSTART and SEQEND (if someone has giving us a section) */ int seqstart = 0; int seqend = 0; int firstnum = 0; JCMTState *tmpState = NULL; smf_getfitsi( infile->hdr, "SEQSTART", &seqstart, status ); smf_getfitsi( infile->hdr, "SEQEND", &seqend, status ); tmpState = infile->hdr->allState; if( tmpState ) { firstnum = (tmpState[0]).rts_num; smf_smfFile_msg( infile->file, "F", 1, "<unknown file>"); if ( firstnum >= seqstart && firstnum <= seqend ) { /* store the file in the output group */ ndgCpsup( ingrp, i, ogrp, status ); msgOutif(MSG__DEBUG, " ", "Non-dark file: ^F",status); smf__addto_durations( infile, &duration_sci, &nsteps_sci, status ); astMapPutElemI( scimap, keystr, -1, seqcount ); sccount++; } else { msgOutif( MSG__QUIET, "", "File ^F has a corrupt FITS header. Ignoring it.", status ); } } else { smf_smfFile_msg( infile->file, "F", 1, "<unknown file>"); /* store the file in the output group */ ndgCpsup( ingrp, i, ogrp, status ); msgOutif( MSG__DEBUG, " ", "File ^F lacks JCMTState: assuming it is non-dark",status); smf__addto_durations( infile, &duration_sci, &nsteps_sci, status ); astMapPutElemI( scimap, keystr, -1, seqcount ); sccount++; } } else if (infile->hdr->seqtype == SMF__TYP_FASTFLAT ) { ffcount = smf__addto_sortinfo( infile, allfflats, i, ffcount, "Fast flat", status ); } else { smf_smfFile_msg( infile->file, "F", 1, "<unknown file>"); msgOutif(MSG__DEBUG, " ", "Sequence type mismatch with observation type: ^F",status); } } /* close the file */ smf_close_file( &infile, status ); } /* Store output group in return variable or else free it */ if (outgrp) { *outgrp = ogrp; } else { grpDelet( &ogrp, status ); } /* process flatfields if necessary */ if (ffcount > 0 && fflats ) { smfArray * array = NULL; /* sort flats into order */ qsort( allfflats, ffcount, sizeof(*allfflats), smf_sort_bydouble); if (fflats) array = smf_create_smfArray( status ); /* now open the flats and store them if requested */ if (*status == SAI__OK && array && ffcount) { size_t start_ffcount = ffcount; AstKeyMap * flatmap = NULL; if (calcflat) { /* Use AgeUp so that we get the keys out in the sorted order that allfflats used */ flatmap = astKeyMap( "KeyCase=0,KeyError=1,SortBy=AgeDown" ); } /* Read each flatfield. Calculate a responsivity image and a flatfield solution. Store these in a keymap along with related information which is itself stored in a keymap indexed by a string made of OBSIDSS, reference heater value, shutter and subarray. */ for (i = 0; i < start_ffcount; i++ ) { size_t ori_index = (allfflats[i]).index; smfData * outfile = NULL; char keystr[100]; AstKeyMap * infomap = astKeyMap( "KeyError=1" ); int oplen = 0; char thisfile[MSG__SZMSG]; int seqcount = 0; /* read filename from group */ infile = NULL; smf_open_file( ingrp, ori_index, "READ", 0, &infile, status ); if ( *status != SAI__OK ) { /* This should not happen because we have already opened the file. If it does happen we abort with error. */ if (infile) smf_close_file( &infile, status ); break; } /* Calculate the key for this observation */ smf__calc_flatobskey( infile->hdr, keystr, sizeof(keystr), status ); /* Get the file name for error messages */ smf_smfFile_msg( infile->file, "F", 1, "<unknown file>" ); msgLoad( "", "^F", thisfile, sizeof(thisfile), &oplen, status ); /* And the sequence counter to link against science observations */ smf_find_seqcount( infile->hdr, &seqcount, status ); /* Prefill infomap */ astMapPut0C( infomap, "FILENAME", thisfile, ""); astMapPut0I( infomap, "SEQCOUNT", seqcount, ""); /* Collapse it */ if (*status == SAI__OK) { smf_flat_fastflat( infile, &outfile, status ); if (*status == SMF__BADFLAT) { errFlush( status ); if (calcflat) { /* Need to generate an outfile like smf_flat_fastflat and one heater setting will force smf_flat_calcflat to fail */ smf_flat_malloc( 1, infile, NULL, &outfile, status ); } else { if (outfile) smf_close_file( &outfile, status ); if (infile) smf_close_file( &infile, status ); infomap = astAnnul( infomap ); ffcount--; continue; } } } if (outfile && *status == SAI__OK) { smf_close_file( &infile, status ); infile = outfile; if (calcflat) { size_t ngood = 0; smfData * curresp = NULL; int utdate; if (*status == SAI__OK) { ngood = smf_flat_calcflat( MSG__VERB, NULL, "RESIST", "FLATMETH", "FLATORDER", NULL, "RESPMASK", "FLATSNR", NULL, infile, &curresp, status ); if (*status != SAI__OK) { /* if we failed to calculate a flatfield we continue but force the flatfield to be completely bad. This will force the science data associated with the flatfield to be correctly blanked. We do not annul though if we have a SUBPAR error telling us that we have failed to define our parameters properly. */ if (*status != SUBPAR__NOPAR) errAnnul(status); /* parameters of flatfield */ ngood = 0; /* Generate a blank flatfield and blank responsivity image */ smf_flat_badflat( infile, &curresp, status ); } /* Retrieve the UT date so we can decide whether to compare flatfields */ smf_getfitsi( infile->hdr, "UTDATE", &utdate, status ); /* Store the responsivity data for later on and the processed flatfield until we have vetted it */ astMapPut0P( infomap, "CALCFLAT", infile, "" ); astMapPut0P( infomap, "RESP", curresp, "" ); astMapPut0I( infomap, "UTDATE", utdate, "" ); astMapPut0I( infomap, "ISGOOD", 1, "" ); astMapPut0I( infomap, "NGOOD", ngood, "" ); astMapPut0I( infomap, "GRPINDEX", ori_index, "" ); astMapPut0I( infomap, "SMFTYP", infile->hdr->obstype, "" ); astMapPutElemA( flatmap, keystr, -1, infomap ); } } else { /* if (calcflat) */ /* Store the collapsed flatfield - the processed flat is not stored here yet */ smf_addto_smfArray( array, infile, status ); /* Copy the group info */ ndgCpsup( ingrp, ori_index, fgrp, status ); } } /* if (outfile) */ /* Annul the keymap (will be fine if it is has been stored in another keymap) */ infomap = astAnnul( infomap ); } /* End loop over flatfields */ /* Now we have to loop over the related flatfields to disable bolometers that are not good and also decide whether we need to set status to bad. */ if (*status == SAI__OK && calcflat ) { size_t nkeys = astMapSize( flatmap ); for (i = 0; i < nkeys; i++ ) { const char *key = astMapKey( flatmap, i ); int nf = 0; AstKeyMap ** kmaps = NULL; int nelem = astMapLength( flatmap, key ); kmaps = astMalloc( sizeof(*kmaps) * nelem ); astMapGet1A( flatmap, key, nelem, &nelem, kmaps ); for ( nf = 0; nf < nelem && *status == SAI__OK; nf++ ) { AstKeyMap * infomap = kmaps[nf]; int isgood = 0; astMapGet0I( infomap, "ISGOOD", &isgood ); if (isgood) { /* The flatfield worked */ size_t ngood = 0; int itemp; int utdate = 0; int ratioFlats = 0; /* Get the UT date - we do not compare flatfields after the time we enabled heater tracking at each sequence. */ astMapGet0I( infomap, "UTDATE", &utdate ); /* Get the number of good bolometers at this point */ astMapGet0I( infomap, "NGOOD", &itemp ); ngood = itemp; /* Decide if we want to do the ratio test. We default to not doing it between 20110901 and 20120827 which is the period when we did mini-heater tracks before each flat. ! indicates that we choose based on date. */ if (*status == SAI__OK) { parGet0l( "FLATUSENEXT", &ratioFlats, status ); if ( *status == PAR__NULL ) { errAnnul( status ); if (utdate >= 20110901 || utdate <= 20120827 ) { ratioFlats = 0; } else { ratioFlats = 1; } } } /* Can we compare with the next flatfield? */ if (ngood < SMF__MINSTATSAMP || !ratioFlats ) { /* no point doing all the ratio checking for this */ } else if ( nelem - nf >= 2 ) { AstKeyMap * nextmap = kmaps[nf+1]; const char *nextfname = NULL; const char *fname = NULL; smfData * curresp = NULL; smfData * nextresp = NULL; smfData * curflat = NULL; void *tmpvar = NULL; size_t bol = 0; smfData * ratio = NULL; double *in1 = NULL; double *in2 = NULL; double mean = VAL__BADD; size_t nbolo; double *out = NULL; double sigma = VAL__BADD; float clips[] = { 5.0, 5.0 }; /* 5.0 sigma iterative clip */ size_t ngoodz = 0; astMapGet0C( nextmap, "FILENAME", &nextfname ); astMapGet0C( infomap, "FILENAME", &fname ); /* Retrieve the responsivity images from the keymap */ astMapGet0P( infomap, "RESP", &tmpvar ); curresp = tmpvar; astMapGet0P( nextmap, "RESP", &tmpvar ); nextresp = tmpvar; astMapGet0P( infomap, "CALCFLAT", &tmpvar ); curflat = tmpvar; nbolo = (curresp->dims)[0] * (curresp->dims)[1]; /* get some memory for the ratio if we have not already. We could get some memory once assuming each flat has the same number of bolometers... */ ratio = smf_deepcopy_smfData( curresp, 0, 0, 0, 0, status ); if( *status == SAI__OK ) { /* divide: smf_divide_smfData ? */ in1 = (curresp->pntr)[0]; in2 = (nextresp->pntr)[0]; out = (ratio->pntr)[0]; for (bol=0; bol<nbolo;bol++) { if ( in1[bol] != VAL__BADD && in1[bol] != 0.0 && in2[bol] != VAL__BADD && in2[bol] != 0.0 ) { out[bol] = in1[bol] / in2[bol]; } else { out[bol] = VAL__BADD; } } } /* find some statistics */ smf_clipped_stats1D( out, 2, clips, 1, nbolo, NULL, 0, 0, &mean, &sigma, NULL, 0, &ngoodz, status ); if (*status == SMF__INSMP) { errAnnul(status); msgOutiff( MSG__QUIET, "", "Flatfield ramp ratio of %s with %s had too few bolometers (%zu < %d).", status, fname, nextfname, ngoodz, SMF__MINSTATSAMP ); ngood = ngoodz; /* Must be lower or equal to original ngood */ } else if (*status == SAI__OK && mean != VAL__BADD && sigma != VAL__BADD && curflat->da) { /* Now flag the flatfield as bad for bolometers that have changed more than n%. We expect the variation to be 1+/-a small bit */ const double pmrange = 0.10; double thrlo = 1.0 - pmrange; double thrhi = 1.0 + pmrange; size_t nmasked = 0; double *flatcal = curflat->da->flatcal; msgOutiff( MSG__DEBUG, "", "Flatfield fast ramp ratio mean = %g +/- %g (%zu bolometers)", status, mean, sigma, ngood); /* we can just set the first slice of the flatcal to bad. That should be enough to disable the entire bolometer. We have just read these data so they should be in ICD order. */ for (bol=0; bol<nbolo;bol++) { if ( out[bol] != VAL__BADD && (out[bol] < thrlo || out[bol] > thrhi ) ) { flatcal[bol] = VAL__BADD; nmasked++; } else if ( in1[bol] != VAL__BADD && in2[bol] == VAL__BADD ) { /* A bolometer is bad next time but good now so we must set it bad now */ flatcal[bol] = VAL__BADD; nmasked++; } } if ( nmasked > 0 ) { msgOutiff( MSG__NORM, "", "Masked %zu bolometers in %s from unstable flatfield", status, nmasked, fname ); /* update ngood to take into account the masking */ ngood -= nmasked; } } smf_close_file( &ratio, status ); } /* End of flatfield responsivity comparison */ /* if we only have a few bolometers left we now consider this a bad flat unless it is actually an engineering measurement where expect some configurations to give zero bolometers */ if (ngood < SMF__MINSTATSAMP) { const char *fname = NULL; void * tmpvar = NULL; int smftyp = 0; smfData * curflat = NULL; astMapGet0I( infomap, "SMFTYP", &smftyp ); astMapGet0C( infomap, "FILENAME", &fname ); if (smftyp != SMF__TYP_NEP) { msgOutiff( MSG__QUIET, "", "Flatfield %s has %zu good bolometer%s.%s", status, fname, ngood, (ngood == 1 ? "" : "s"), ( ngood == 0 ? "" : " Keeping none.") ); isgood = 0; /* Make sure that everything is blanked. */ if (ngood > 0) { astMapGet0P( infomap, "CALCFLAT", &tmpvar ); curflat = tmpvar; if (curflat && curflat->da) { size_t bol; size_t nbolo = (curflat->dims)[0] * (curflat->dims)[1]; double *flatcal = curflat->da->flatcal; for (bol=0; bol<nbolo; bol++) { /* Just need to set the first element to bad */ flatcal[bol] = VAL__BADD; } } } } else { msgOutiff( MSG__NORM, "", "Flatfield ramp file %s has %zu good bolometer%s. Eng mode.", status, fname, ngood, (ngood == 1 ? "" : "s") ); } } /* We do not need the responsivity image again */ { void *tmpvar = NULL; smfData * resp = NULL; astMapGet0P( infomap, "RESP", &tmpvar ); resp = tmpvar; if (resp) smf_close_file( &resp, status ); astMapRemove( infomap, "RESP" ); } } /* End of isgood comparison */ /* We are storing flats even if they failed. Let the downstream software worry about it */ { int ori_index; smfData * flatfile = NULL; void *tmpvar = NULL; /* Store in the output group */ astMapGet0I( infomap, "GRPINDEX", &ori_index ); ndgCpsup( ingrp, ori_index, fgrp, status ); /* And store in the smfArray */ astMapGet0P( infomap, "CALCFLAT", &tmpvar ); astMapRemove( infomap, "CALCFLAT" ); flatfile = tmpvar; smf_addto_smfArray( array, flatfile, status ); } /* Free the object as we go */ kmaps[nf] = astAnnul( kmaps[nf] ); } /* End of loop over this obsidss/subarray/heater */ kmaps = astFree( kmaps ); } } if (array->ndat) { if (fflats) *fflats = array; } else { smf_close_related(&array, status ); if (fflats) *fflats = NULL; } } } /* no need to do any more if neither darks nor darkgrp are defined or we might be wanting to revert to darks. */ if (dkcount > 0 && (darks || darkgrp || reverttodark ) ) { smfArray * array = NULL; /* sort darks into order */ qsort( alldarks, dkcount, sizeof(*alldarks), smf_sort_bydouble); if (darks) array = smf_create_smfArray( status ); /* now open the darks and store them if requested */ if (*status == SAI__OK) { for (i = 0; i < dkcount; i++ ) { size_t ori_index = (alldarks[i]).index; /* Store the entry in the output group */ ndgCpsup( ingrp, ori_index, dgrp, status ); if (darks) { /* read the value from the new group */ smf_open_file( dgrp, i+1, "READ", 0, &infile, status ); /* do we have to process these darks? */ if (reducedark) { smfData *outfile = NULL; smf_reduce_dark( infile, darktype, &outfile, status ); if (outfile) { smf_close_file( &infile, status ); infile = outfile; } } smf_addto_smfArray( array, infile, status ); } } if (darks) *darks = array; } } /* free memory */ alldarks = astFree( alldarks ); allfflats = astFree( allfflats ); if( reverttodark && outgrp && (grpGrpsz(*outgrp,status)==0) && (grpGrpsz(dgrp,status)>0) ) { /* If outgrp requested but no science observations were found, and dark observations were found, return darks in outgrp and set flatgrp and darkgrp to NULL. This is to handle cases where we want to process data taken in the dark like normal science data. To activate this behaviour set reverttodark */ msgOutiff( MSG__NORM, "", "Treating the dark%s as science data", status, ( dkcount > 1 ? "s" : "" ) ); *outgrp = dgrp; if( darkgrp ){ *darkgrp = NULL; } if( flatgrp ) { *flatgrp = NULL; } grpDelet( &ogrp, status); grpDelet( &fgrp, status); if (meanstep && nsteps_dark > 0) *meanstep = duration_darks / nsteps_dark; /* Have to clear the darks smfArray as well */ if (darks) smf_close_related( darks, status ); } else { /* Store the output groups in the return variable or free it */ if (darkgrp) { *darkgrp = dgrp; } else { grpDelet( &dgrp, status); } if (flatgrp) { *flatgrp = fgrp; } else { grpDelet( &fgrp, status); } if (meanstep && nsteps_sci > 0) *meanstep = duration_sci / nsteps_sci; } msgSeti( "ND", sccount ); msgSeti( "DK", dkcount ); msgSeti( "FF", ffcount ); msgSeti( "TOT", insize ); if ( insize == 1 ) { if (dkcount == 1) { msgOutif( MSG__VERB, " ", "Single input file was a dark", status); } else if (ffcount == 1) { msgOutif( MSG__VERB, " ", "Single input file was a fast flatfield", status); } else if (sccount == 1) { msgOutif( MSG__VERB, " ", "Single input file was accepted (observation type same as sequence type)", status); } else { msgOutif( MSG__VERB, " ", "Single input file was not accepted.", status); } } else { if (dkcount == 1) { msgSetc( "DKTXT", "was a dark"); } else { msgSetc( "DKTXT", "were darks"); } if (ffcount == 1) { msgSetc( "FFTXT", "was a fast flat"); } else { msgSetc( "FFTXT", "were fast flats"); } if (sccount == 1) { msgSetc( "NDTXT", "was science"); } else { msgSetc( "NDTXT", "were science"); } /* This might be a useful message */ msgOutif( MSG__NORM, " ", "Out of ^TOT input files, ^DK ^DKTXT, ^FF ^FFTXT " "and ^ND ^NDTXT", status ); } if (meanstep && *meanstep != VAL__BADD) { msgOutiff( MSG__VERB, "", "Mean step time for input files = %g sec", status, *meanstep ); } /* Store the heater efficiency map */ if (*status != SAI__OK) heatermap = smf_free_effmap( heatermap, status ); if (heateffmap) *heateffmap = heatermap; /* Now report the details of the observation */ smf_obsmap_report( MSG__NORM, obsmap, objmap, status ); obsmap = astAnnul( obsmap ); objmap = astAnnul( objmap ); scimap = astAnnul( scimap ); msgOutiff( SMF__TIMER_MSG, "", "Took %.3f s to find science observations", status, smf_timerupdate( &tv1, &tv2, status ) ); return; }
void smf_check_pol2( Grp *igrp, int size, int raw, int *status ) { /* Local Variables */ smfData *data = NULL; /* pointer to SCUBA2 data struct */ int i; /* Loop counter */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Loop over all files in the Grp */ for( i = 1; i <= size && *status == SAI__OK; i++ ) { /* Read header info from the ith input file in the group */ smf_open_file( NULL, igrp, i, "READ", SMF__NOCREATE_DATA, &data, status ); /* Check there is a file. */ if( *status == SAI__OK && data->file == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfFile associated with smfData.", status ); /* Check there is a header. */ } else if( *status == SAI__OK && data->hdr == NULL ) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); *status = SAI__ERROR; errRep( FUNC_NAME, "No smfHead associated with ^FILE.", status ); } /* Check that there are 3 pixel axes. */ if( *status == SAI__OK && data->ndims != 3) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); msgSeti( "NDIMS", data->ndims ); *status = SAI__ERROR; errRep( FUNC_NAME, "^FILE has ^NDIMS pixel axes, should be 3.", status ); } /* Check that POL2 is in the beam. */ if( *status == SAI__OK && !(data->hdr->inbeam & SMF__INBEAM_POL) ) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); *status = SAI__ERROR; errRep( FUNC_NAME, "^FILE does not contain POL2 data.", status ); } /* For raw data check that the NDF Label component is "Q" or "U". */ if( *status == SAI__OK ) { if( raw ) { if( strcmp( data->hdr->dlabel, "Signal" ) ) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); msgSetc( "L", data->hdr->dlabel ); *status = SAI__ERROR; errRep( FUNC_NAME, "File ^FILE does not contain POL2 " "analysed intensity data (NDF label is '^L' " "but should be 'Signal').", status ); } /* For Q/U data check that the NDF Label component is "Q" or "U". */ } else { if( strcmp( data->hdr->dlabel, "Q" ) && strcmp( data->hdr->dlabel, "U" ) ) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); msgSetc( "L", data->hdr->dlabel ); *status = SAI__ERROR; errRep( FUNC_NAME, "File ^FILE does not contain POL2 " "Q or U data (NDF label is '^L' but should be " "'Q' or 'U').", status ); } } } /* Close the data file */ smf_close_file( NULL, &data, status); } }
static int smf1_check_steps( const char *param, int first, dim_t nx, double sizetol, int nold, int nnew, smfStepFix *oldsteps, smfStepFix *newsteps, int *status ){ /* * Name: * smf1_check_steps * Purpose: * Compare two sets of steps, issuing a warning for each step that * has changed significantly. * Invocation: * int smf1_check_steps( const char *param, int first, dim_t nx, * double sizetol, int nold, int nnew, * smfStepFix *oldsteps, smfStepFix *newsteps, * int *status ) * Arguments: * param = const char * (Given) * Name of parameter to use when asking the user whether to * continue to look for further changes. * first = int (Given) * The index of the first change to report. * nx = dim_t (Given) * The length of the first axis of the bolometer array. * sizetol = double (Given) * The minimum significant relative error in step size. * nold = int (Given) * The number of steps in the "oldsteps" array. * nnew = int (Given) * The number of steps in the "newsteps" array. * oldsteps = smfStepFix * (Given and Returned) * A pointer to the first element of an array of smfStepFix structures * describing the steps fixed in a previous invocation of this program. * The array is sorted on exit. * newsteps = smfStepFix * (Given and Returned) * A pointer to the first element of an array of smfStepFix structures * describing the steps fixed in the current invocation of this program. * The array is sorted on exit. * status = int * (Given and Returned) * Pointer to global status. * Description: * Firstly, an attempt it made to associated each old step with a * corresponding new step (i.e. a new step that occurs at the same * time and in the same bolometer as the old step). Warning messages * are issued about each old step for which no corresponding new step * can be found, or for which the corresponding new step has a * significantly different height to the old step. Finally, warnings * messages are also issued for each new step that has not been * associated with an old step. * Returned Value: * Zero if no significant differences were found. Non-zero otherwise. */ /* Local Variables: */ double abslim; double dsize; double dsize_min; int *fnew; int *new_flags; int cont; int inew; int iold; int jnew; int match; int result; smfStepFix *pnew; smfStepFix *pold; /* Initialise the returned value. */ result = 0; /* Check the inherited status. */ if( *status != SAI__OK ) return result; /* Find the absolute minimum significant difference between step sizes. This is "sizetol" times the clipped RMS step size in the new steps. */ abslim = sizetol*smf1_get_rmssize( nnew, newsteps, status ); msgSetd( "T", abslim ); msgOut( "", "Ignoring differences in step size smaller than ^T", status ); msgBlank( status ); /* Allocate memory to hold an array with one element for each new step. Each element holds zero if the new step has not yet been associated with any old step. Otherwise, it holds the one-based index of the associated old step. Initialise it to hold zero at every element. */ new_flags = astCalloc( nnew, sizeof( *new_flags ) ); if( *status == SAI__OK ) { /* Loop round each old step. */ pold = oldsteps; for( iold = 0; iold < nold; iold++,pold++ ) { /* Ignore old steps with bolometer indicies greater than 5000 */ if( pold->ibolo > 5000 ) continue; /* Indicate no new step has yet been associated with the old step. */ jnew = -1; dsize_min = VAL__MAXD; match = 0; /* Loop round all new steps. */ pnew = newsteps; fnew = new_flags; for( inew = 0; inew < nnew; inew++,pnew++,fnew++ ) { /* Ignore this new step if it has already been associated with a previous old step. */ if( ! *fnew ) { /* See if the current new and old steps occur in the same bolometer and have overlapping time spans. If so they are considered to be at the same time. */ if( pold->ibolo == pnew->ibolo && pold->start <= pnew->end && pold->end >= pnew->start ) { /* Get the difference in step size between the old and new steps. */ dsize = fabs( pold->size - pnew->size ); /* Note the index of the matching new step that is most similar in height to the old step. */ if( dsize < dsize_min ) { jnew = inew; dsize_min = dsize; } /* If the old and new step heights are about the same then we associate the new step with the old step. Store the (one based) index of the corresponding old step. We do not need to check any more new steps, so break out of the new step loop. */ if( dsize < abslim || dsize < sizetol*fabs( 0.5*( pold->size + pnew->size ) ) ) { match = 1; *fnew = iold + 1; break; } } } } /* If a new step was found at the same time and place as the old step, and with the same height, pass on to the next old step. */ if( ! match ) { /* If no new step was found at the same time and place as the old step, an old step has dissappeared. */ if( jnew == -1 ) { /* If the old step was of significant height, tell the user. */ if( fabs( pold->size ) > abslim ){ result++; if( result >= first ) { msgSeti( "N", result ); msgSeti( "I", pold->id ); msgSetc( "W", pold->corr ? "secondary" : "primary" ); msgOut( "", "^N: An old ^W step (index ^I) is no longer found:", status ); msgSeti( "B", pold->ibolo ); msgSeti( "X", pold->ibolo % nx ); msgSeti( "Y", pold->ibolo / nx ); msgOut( "", " Bolometer = ^B (^X,^Y)", status ); msgSeti( "S", pold->start ); msgSeti( "E", pold->end ); msgOut( "", " Time slice range = ^S:^E", status ); msgSetd( "H", pold->size ); msgOut( "", " Height = ^H", status ); parGet0l( param, &cont, status ); parCancl( param, status ); if( !cont || *status != SAI__OK ) break; msgBlank( status ); } } /* If one or more new step were found at the same time and place as the old step (but with a significantly different height), tell the user about the change in height. */ } else { pnew = newsteps + jnew; result++; if( result >= first ) { msgSeti( "I", result ); msgSetd( "O", pold->size ); msgSetd( "N", pnew->size ); msgOut( "", "^I: Step size changed from ^O to ^N:", status ); msgSeti( "I", pnew->id ); msgSetc( "W", pnew->corr ? "secondary" : "primary" ); msgOut( "", " New index = ^I (^W)", status ); msgSeti( "I", pold->id ); msgSetc( "W", pold->corr ? "secondary" : "primary" ); msgOut( "", " Old index = ^I (^W)", status ); msgSeti( "B", pold->ibolo ); msgSeti( "X", pold->ibolo % nx ); msgSeti( "Y", pold->ibolo / nx ); msgOut( "", " Bolometer = ^B (^X,^Y)", status ); msgSeti( "S", pold->start ); msgSeti( "E", pold->end ); msgOut( "", " Old time slice range = ^S:^E", status ); msgSeti( "S", pnew->start ); msgSeti( "E", pnew->end ); msgOut( "", " New time slice range = ^S:^E", status ); parGet0l( param, &cont, status ); parCancl( param, status ); if( !cont || *status != SAI__OK ) break; msgBlank( status ); } } } } /* We have now checked all old steps for matching new steps. If no significant change has yet been found, look for new steps that have not been associated with an old step. */ pnew = newsteps; fnew = new_flags; for( inew = 0; inew < nnew && cont; inew++,pnew++,fnew++ ) { if( ! *fnew ) { /* If the new step is off significant height, tell the user. */ if( fabs( pnew->size ) > abslim ){ result++; if( result >= first ) { msgSeti( "N", result ); msgSeti( "I", pnew->id ); msgSetc( "W", pnew->corr ? "secondary" : "primary" ); msgOut( "", "^N: A new ^W step (index ^I) was found:", status ); msgSeti( "B", pnew->ibolo ); msgSeti( "X", pnew->ibolo % nx ); msgSeti( "Y", pnew->ibolo / nx ); msgOut( "", " Bolometer = ^B (^X,^Y)", status ); msgSeti( "S", pnew->start ); msgSeti( "E", pnew->end ); msgOut( "", " Time slice range = ^S:^E", status ); msgSetd( "H", pnew->size ); msgOut( "", " Height = ^H", status ); parGet0l( param, &cont, status ); parCancl( param, status ); if( !cont || *status != SAI__OK ) break; msgBlank( status ); } } } } } /* Free resources. */ new_flags = astFree( new_flags ); /* Return the result. */ return result; }
static smfStepFix *smf1_read_steps( FILE *fd, double dcthresh0, dim_t dcsmooth0, dim_t dcfitbox0, int dcmaxsteps0, int dclimcorr0, size_t nrej0, int nstep0, int *nstep, int *status ) { /* * Name: * smf1_read_steps * Purpose: * Read step descriptions from a file, and check global values. * Invocation: * smfStepFix *smf1_read_steps( FILE *fd, double dcthresh0, * dim_t dcsmooth0, dim_t dcfitbox0, * int dcmaxsteps0, int dclimcorr0, * size_t nrej0, int nstep0, int *nstep, * int *status ) * Arguments: * fd = FILE * (Given) * A file descriptor from which to read the details of a set of * steps. * dcthresh0 = double (Given) * Expected value of DCTHRESH. * dcsmooth0 = dim_t (Given) * Expected value of DCSMOOTH. * dcfitbox0 = dim_t (Given) * Expected value of DCFITBOX. * dcmaxsteps0 = int (Given) * Expected value of DCMAXSTEPS. * dclimcorr = int (Given) * Expected value of DCLIMCORR. * nrej0 = size_t (Given) * The expected number of bolometers rejected. * nstep0 = int (Given) * The expected number of step fixes. * nstep = int * (Returned) * The number of steps fixes read from the file. * status = int* (Given and Returned) * Pointer to global status. * Description: * Reads information from the supplied file, returning an array of * step fixes. It also issues warnings if any of the global values * read from the file are not equal to the supplied expected values. * Returned Value: * A pointer to an array of smfStepFix structures describing the * steps fixes read form the file. The length of this array is equal * to "*nstep". The array should be freed using astFree when no longer * needed. */ /* Local Variables: */ smfStepFix *result; char buf[ 256 ]; char *c; double dval; double size; int bad; int corr; int end; int ibolo; int iline; int istep; int ival; int nc; int nold; int stage; int start; /* Initialise */ result = NULL; *nstep = 0; /* Check the inherited status. */ if( *status != SAI__OK ) return result; /* Indicate we have not yet reached the tabular data. */ stage = 0; /* Initialise the index of the next step */ istep = 0; /* Indicate we do not yet know how many steps are described in the text file. */ nold = -1; /* Indicate no bad lines found yet. */ bad = 0; /* Loop round reading lines of text from the supplied file until an illegal line is read or the end of file is reached. */ iline = 0; while( !bad && fgets( buf, sizeof(buf), fd ) && *status == SAI__OK ) { iline++; /* Remove trailing white space. */ c = buf + strlen( buf ) - 1; while( isspace( *c ) && c > buf ) c--; c[ 1 ] = 0; /* scanf indicates success if the strings do not fail to match before the end of the shorter of the two. So we need to check that sufficient characters are compared. Initialise the number of characters compared. */ nc = 0; /* If we are not yet in the tabular data, look for header lines, and issue a message if the old value is different to the new value. */ if( stage == 0 ) { if( sscanf( buf, "# Number of steps fixed = %d%n", &ival, &nc ) && nc > 26 ) { if( ival != nstep0 ) { msgSeti( "O", ival ); msgSeti( "N", nstep0 ); msgOut( "", "No. of steps fixed changed from ^O to ^N", status ); } nold = ival; } else if( sscanf( buf, "# Number of bolometers rejected = %d%n", &ival, &nc ) && nc > 34 ) { if( ival != (int) nrej0 ) { msgSeti( "O", ival ); msgSeti( "N", nrej0 ); msgOut( "", "No. of bolometers rejected changed from ^O to ^N", status ); } } else if( sscanf( buf, "# DCFITBOX = %d%n", &ival, &nc ) && nc > 13 ) { if( ival != (int) dcfitbox0 ) { msgSeti( "O", ival ); msgSeti( "N", dcfitbox0 ); msgOut( "", "Warning: DCFITBOX changed from ^O to ^N", status ); } } else if( sscanf( buf, "# DCMAXSTEPS = %d%n", &ival, &nc ) && nc > 14 ) { if( ival != dcmaxsteps0 ) { msgSeti( "O", ival ); msgSeti( "N", dcmaxsteps0 ); msgOut( "", "Warning: DCMAXSTEPS changed from ^O to ^N", status ); } } else if( sscanf( buf, "# DCLIMCORR = %d%n", &ival, &nc ) && nc > 14 ) { if( ival != dclimcorr0 ) { msgSeti( "O", ival ); msgSeti( "N", dclimcorr0 ); msgOut( "", "Warning: DCLIMCORR changed from ^O to ^N", status ); } } else if( sscanf( buf, "# DCSMOOTH = %d%n", &ival, &nc ) && nc > 18 ) { if( ival != (int) dcsmooth0 ) { msgSeti( "O", ival ); msgSeti( "N", dcsmooth0 ); msgOut( "", "Warning: DCSMOOTH changed from ^O to ^N", status ); } } else if( sscanf( buf, "# DCTHRESH = %lg%n", &dval, &nc ) && nc > 13 ) { if( fabs( dval - dcthresh0 ) > 1.0E-10 ) { msgSetd( "O", dval ); msgSetd( "N", dcthresh0 ); msgOut( "", "Warning: DCTHRESH changed from ^O to ^N", status ); } /* Look for the line that marks the start of the tabular data. */ } else if( !strcmp( buf, "# istep start end ibolo size corr" ) ) { stage = 1; msgBlank( status ); /* Allocate the returned array. */ result = astMalloc( nold*sizeof( *result ) ); *nstep = nold; /* Abort if an illegal header line is read. */ } else if( strcmp( buf, "#" ) && strncmp( buf, "# Steps fixed in '", 18 ) ) { bad = 1; } /* If we are now reading tabular data... */ } else { /* Extract the numerical values from the line of text. */ if( sscanf( buf, "%d %d %d %d %lg %d%n", &ival, &start, &end, &ibolo, &size, &corr, &nc ) == 6 && nc > 14 ) { /* Report an error if there is a jump in the step index (indicates lines missing from the supplied file). */ if( ival != istep ) { *status = SAI__ERROR; msgSeti( "I", istep ); errRep( "", "Step ^I data not found in old steps file:", status ); bad = 1; /* Otherwise, store the numerical values in the next element of the returned array. */ } else if( istep < nold ){ result[ istep ].id = ival; result[ istep ].ibolo = ibolo; result[ istep ].start = start; result[ istep ].end = end; result[ istep ].size = size; result[ istep ].corr = corr; } /* Increment the index of the next step to check. */ istep++; /* Abort if the numerical values cannot be read from the line of text. */ } else { bad = 1; } } } /* Report an error if the last line read was illegal. */ if( bad ) { *status = SAI__ERROR; msgSeti( "I", iline ); errRep( "", "Illegal line found in old steps file (line ^I):", status ); msgSetc( "L", buf ); errRep( "", "'^L'", status ); /* Report an error if the number of steps in the old file is still unknown */ } else if( nold == -1 ) { *status = SAI__ERROR; errRep( "", "Required line not found in old steps file:", status ); errRep( "", "'# Number of steps fixed = ...'", status ); /* Report an error if the number of lines of tabular data was wrong. */ } else if( istep != nold ) { *status = SAI__ERROR; errRep( "", "Incorrect number of step descriptions in old steps file.", status ); msgSeti( "I", istep ); msgSeti( "N", nold ); errRep( "", "Header says file contains ^N steps but data for ^I " "steps was found.", status ); } return result; }
/* Main entry */ void smurf_fixsteps( int *status ) { /* Local Variables */ AstKeyMap *keymap; /* Default config parameter values */ AstKeyMap *sub_instruments; /* Info about sub-instruments */ FILE *fd = NULL; /* File descriptor */ Grp *igrp = NULL; /* Input group of files */ Grp *ogrp = NULL; /* Output group of files */ dim_t dcfitbox; /* DCFITBOX config parameter */ dim_t dcsmooth; /* DCSMOOTH config parameter */ dim_t nx; /* Length of first pixel axis */ double dcthresh; /* DCTHRESH config parameter */ double sizetol; /* Tolerance allowed on step height */ int changed; /* Have any step fixes changed? */ int dclimcorr; /* DCLIMCORR config parameter */ int dcmaxsteps; /* DCMAXSTEPS config parameter */ int first; /* Index of first change to report */ int itemp; /* Intermediate value */ int meanshift; /* Use a mean shift filter? */ int nnew; /* Number of new step fixes */ int nold; /* Number of old step fixes */ size_t nrej; /* Number of rejected bolometers */ size_t outsize; /* Total number of NDF names in the output group */ size_t size; /* Number of files in input group */ smfData *data = NULL; /* Output smfData */ smfData *indata = NULL; /* Input smfData */ smfStepFix *newsteps = NULL; /* New step fix descriptions */ smfStepFix *oldsteps = NULL; /* Old step fix descriptions */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ /* Check inherited status */ if (*status != SAI__OK) return; /* begin an NDF context. */ ndfBegin(); /* Get the name of the input NDF. */ kpg1Rgndf( "IN", 1, 1, "", &igrp, &size, status ); /* Get output file(s) */ kpg1Wgndf( "OUT", igrp, size, 0, "More output files required...", &ogrp, &outsize, status ); /* Open the input data file, read-only. */ smf_open_file( igrp, 1, "Read", 0, &indata, status ); /* Since we will be modifying the data values, we need a deep copy. */ data = smf_deepcopy_smfData( indata, 0, 0, 0, 0, status ); /* Place cleaning parameters into a keymap and set defaults. Note that we use the map-maker defaults file here so that we populate the locked keymap with all the parameters that people may come across to allow them to load their map-maker config directly this application. */ sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE, data, NULL, 0, status ); keymap = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_makemap.def", sub_instruments, status ); sub_instruments = astAnnul( sub_instruments ); /* Set the default for each of the step fixing config parameters. */ astMapGet0I( keymap, "DCSMOOTH", &itemp ); parDef0i( "DCSMOOTH", itemp, status ); astMapGet0I( keymap, "DCFITBOX", &itemp ); parDef0i( "DCFITBOX", itemp, status ); astMapGet0I( keymap, "DCMAXSTEPS", &itemp ); parDef0i( "DCMAXSTEPS", itemp, status ); astMapGet0I( keymap, "DCLIMCORR", &itemp ); parDef0i( "DCLIMCORR", itemp, status ); astMapGet0D( keymap, "DCTHRESH", &dcthresh ); parDef0d( "DCTHRESH", dcthresh, status ); /* Get values for the config params */ parGet0i( "DCSMOOTH", &itemp, status ); dcsmooth = itemp; parGet0i( "DCFITBOX", &itemp, status ); dcfitbox = itemp; parGet0i( "DCMAXSTEPS", &itemp, status ); dcmaxsteps = itemp; parGet0i( "DCLIMCORR", &itemp, status ); dclimcorr = itemp; parGet0d( "DCTHRESH", &dcthresh, status ); parGet0l( "MEANSHIFT", &meanshift, status ); /* Find the number of cores/processors available and create a pool of threads of the same size. */ wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status ); /* Fix the steps. */ smf_fix_steps( wf, data, dcthresh, dcsmooth, dcfitbox, dcmaxsteps, dclimcorr, meanshift, &nrej, &newsteps, &nnew, status ); /* Display a summary of what was done by the step fixer. */ msgBlank( status ); if( nrej == 0 ) { msgOut( "", "No bolometers were rejected", status ); } else if( nrej == 1 ) { msgOut( "", "One bolometer was rejected", status ); } else { msgSeti( "NREJ", nrej ); msgOut( "", "^NREJ bolometers were rejected", status ); } parPut0i( "NREJECTED", nrej, status ); if( nnew == 0 ) { msgOut( "", "No steps were fixed", status ); } else if( nnew == 1 ) { msgOut( "", "One step was fixed", status ); } else { msgSeti( "NNEW", nnew ); msgOut( "", "^NNEW steps were fixed", status ); } parPut0i( "NFIXED", nnew, status ); /* If required, write out to a text file details of the steps that were fixed. */ fd = smf_open_textfile( "NEWSTEPS", "w", "<none>", status ); if( fd ) { smf1_write_steps( fd, indata, nnew, newsteps, dcthresh, dcsmooth, dcfitbox, dcmaxsteps, dclimcorr, nrej, status ); fclose( fd ); } /* If required, create the output NDF. */ if( outsize > 0 && indata && indata->file ) { smf_write_smfData( data, NULL, NULL, ogrp, 1, indata->file->ndfid, MSG__VERB, 0, status ); } /* Save the length of the first pixel axis. */ nx = data ? data->dims[ 0 ] : 0; /* Close the NDFs. */ smf_close_file( &data, status ); smf_close_file( &indata, status ); /* Attempt to open a file containing descriptions of steps fixed by a previous invocation of this program. */ fd = smf_open_textfile( "OLDSTEPS", "r", "<none>", status ); if( fd ) { /* Get SIZETOL - the minimum significant fractional error in step sizes. */ parGet0d( "SIZETOL", &sizetol, status ); /* Read the contents of the file, issuing a warning if the global properties read from the file (e.g. parameters used, no. of steps found, etc) differ from those of the current invocation. */ msgBlank( status ); oldsteps = smf1_read_steps( fd, dcthresh, dcsmooth, dcfitbox, dcmaxsteps, dclimcorr, nrej, nnew, &nold, status ); /* Get the index of the first change to report. */ parGet0i( "FIRST", &first, status ); /* Compare the new step fixes with the old step fixes, issuing a warning for the first step fix that has changed. */ changed = smf1_check_steps( "CONTINUE", first, nx, sizetol, nold, nnew, oldsteps, newsteps, status ); /* Store a flag indicating if any sstep fixes have chnaged. */ parPut0l( "CHANGED", changed, status ); /* Tell the user if nothing has changed. */ if( ! changed ) { msgOut( "", "There are no significant differences " "between old and new step fixes.", status ); } msgBlank( status ); /* Close the old steps file, and free the memory holding the old step descriptions. */ fclose( fd ); oldsteps = astFree( oldsteps ); } /* Free resources. */ newsteps = astFree( newsteps ); grpDelet( &igrp, status ); grpDelet( &ogrp, status ); /* End the NDF context. */ ndfEnd( status ); /* If anything went wrong issue a context message. */ if( *status != SAI__OK ) msgOutif( MSG__VERB, " ", "FIXSTEPS failed.", status ); }
int smf_dataOrder( ThrWorkForce *wf, smfData *data, int isTordered, int *status ) { /* Local Variables */ size_t bstr1; /* bolometer index stride input */ size_t bstr2; /* bolometer index stride output */ dim_t i; /* loop counter */ int inPlace=0; /* If set change array in-place */ dim_t nbolo; /* Number of bolometers */ dim_t ndata; /* Number of data points */ dim_t newdims[3]; /* Size of each dimension new buffer */ int newlbnd[3]; /* New pixel origin */ dim_t ntslice; /* Number of time slices */ size_t tstr1; /* time index stride input */ size_t tstr2; /* time index stride output */ int waschanged = 0; /* did we chagne the order? */ int writable; /* Is the NDF writable? */ int freeold; /* Free the old array if change is not in place? */ /* Main routine */ if (*status != SAI__OK) return waschanged; /* Check for valid isTordered */ if( (isTordered != 0) && (isTordered != 1) ) { *status = SAI__ERROR; msgSeti("ISTORDERED",isTordered); errRep( "", FUNC_NAME ": Invalid isTordered (0/1): ^ISTORDERED", status); return waschanged; } /* Check for a valid data */ if( !data ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": NULL data supplied", status); return waschanged; } /* If value of isTordered matches current value in smfData return */ if( data->isTordered == isTordered ) return waschanged; /* Make sure we're looking at 3-dimensions of bolo data */ if( data->ndims != 3 ) { *status = SMF__WDIM; msgSeti("NDIMS",data->ndims); errRep( "", FUNC_NAME ": Don't know how to handle ^NDIMS dimensions, should be 3.", status); return waschanged; } /* We shouldn't be trying to change the order of FFT'd data */ if( smf_isfft( data, NULL, NULL, NULL, NULL, NULL, status ) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": Possible programming error, attempting to re-order FFT of a " "2D map!", status ); return waschanged; } /* we are going to change */ waschanged = 1; /* inPlace=1 if smfData was mapped! Free the old array if it was not stored in a file. */ freeold = 1; if( data->file ){ if (data->file->fd ){ inPlace = 1; freeold = 0; /* Only change NDF values in-place if write access is abvaialble for the NDF. */ } else if( data->file->ndfid ) { freeold = 0; ndfIsacc( data->file->ndfid, "WRITE", &writable, status ); if( writable ) inPlace = 1; } } /* Calculate input data dimensions (before changing order) */ smf_get_dims( data, NULL, NULL, &nbolo, &ntslice, &ndata, &bstr1, &tstr1, status); /* What will the dimensions/strides be in the newly-ordered array? */ if( isTordered ) { newdims[0] = (data->dims)[1]; newdims[1] = (data->dims)[2]; newdims[2] = (data->dims)[0]; newlbnd[0] = (data->lbnd)[1]; newlbnd[1] = (data->lbnd)[2]; newlbnd[2] = (data->lbnd)[0]; bstr2 = 1; tstr2 = nbolo; } else { newdims[0] = (data->dims)[2]; newdims[1] = (data->dims)[0]; newdims[2] = (data->dims)[1]; newlbnd[0] = (data->lbnd)[2]; newlbnd[1] = (data->lbnd)[0]; newlbnd[2] = (data->lbnd)[1]; bstr2 = ntslice; tstr2 = 1; } /* Loop over elements of data->ptr and re-form arrays */ for( i=0; i<2; i++ ) { data->pntr[i] = smf_dataOrder_array( wf, data->pntr[i], data->dtype, data->dtype, ndata, ntslice, nbolo, tstr1, bstr1, tstr2, bstr2, inPlace, freeold, status ); } /* And Quality */ data->qual = smf_dataOrder_array( wf, data->qual, SMF__QUALTYPE, SMF__QUALTYPE, ndata, ntslice, nbolo, tstr1, bstr1, tstr2, bstr2, inPlace, freeold, status ); /* If NDF associated with data, modify dimensions of the data */ if( data->file && (data->file->ndfid != NDF__NOID) ) { msgOutif(MSG__DEBUG, " ", FUNC_NAME ": Warning - current implementation does not modify NDF " "dimensions to match re-ordered data array", status); } /* If there is a LUT re-order it here */ data->lut = smf_dataOrder_array( wf, data->lut, SMF__INTEGER, SMF__INTEGER, ndata, ntslice, nbolo, tstr1, bstr1, tstr2, bstr2, inPlace, 1, status ); /* Set the new dimensions in the smfData */ if( *status == SAI__OK ) { memcpy( data->dims, newdims, 3*sizeof(*newdims) ); memcpy( data->lbnd, newlbnd, 3*sizeof(*newlbnd) ); data->isTordered = isTordered; } /* Force any external quality to same ordering */ if (data->sidequal) { int qchanged = 0; qchanged = smf_dataOrder( wf, data->sidequal, isTordered, status ); /* and indicate if we changed anything (but not if we did not) */ if (qchanged) waschanged = qchanged; } /* Re-order the axes in the time-series WCS FrameSet */ if( data->hdr && data->hdr->tswcs ) { smf_tswcsOrder( &(data->hdr->tswcs), isTordered, status ); } /* If the re-ordering was not done in-place, then the new buffer must have been allocated here. Set a flag so that smf_close_file knows to deallocate the memory. */ if( ! inPlace ) data->isdyn = 1; return waschanged; }
void smurf_jsatileinfo( int *status ) { /* Local Variables */ AstCmpRegion *overlap; AstFitsChan *fc; AstFrameSet *fs; AstObject *obj; AstRegion *region; AstRegion *target; HDSLoc *cloc = NULL; HDSLoc *xloc = NULL; char *jcmt_tiles; char *tilendf = NULL; char text[ 200 ]; double dec[ 8 ]; double dist; double dlbnd[ 2 ]; double dubnd[ 2 ]; double gx[ 8 ]; double gy[ 8 ]; double maxdist; double norm_radec[2]; double point1[ 2 ]; double point2[ 2 ]; double ra[ 8 ]; double ra0; double dec0; int *ipntr; int axes[ 2 ]; int create; int dirlen; int el; int exists; int flag; int i; int indf1; int indf2; int indf3; int itile; int iv; int jtile; int lbnd[ 2 ]; int local_origin; int nc; int place; int tlbnd[ 2 ]; int tubnd[ 2 ]; int ubnd[ 2 ]; smf_jsaproj_t proj; int xt; int yt; smfJSATiling skytiling; void *pntr; /* Check inherited status */ if( *status != SAI__OK ) return; /* Start a new AST context. */ astBegin; /* Get the instrument to use abnd get the parameters describing the layout of its JSA tiles. */ smf_jsainstrument( "INSTRUMENT", NULL, SMF__INST_NONE, &skytiling, status ); /* Return the maximum tile index. */ parPut0i( "MAXTILE", skytiling.ntiles - 1, status ); /* Abort if an error has occurred. */ if( *status != SAI__OK ) goto L999; /* Decide what sort of projection to use. */ parChoic( "PROJ", "HPX", "HPX,HPX12,XPHN,XPHS", 1, text, sizeof(text), status ); proj = smf_jsaproj_fromstr( text, 1, status ); /* If required, create an all-sky NDF in which each pixel covers the area of a single tile, and holds the integer tile index. The NDF has an initial size of 1x1 pixels, but is expanded later to the required size. */ lbnd[ 0 ] = ubnd[ 0 ] = lbnd[ 1 ] = ubnd[ 1 ] = 1; ndfCreat( "ALLSKY", "_INTEGER", 2, lbnd, ubnd, &indf3, status ); /* If a null (!) value was supplied for parameter ALLSKY, annull the error and pass on. */ if( *status == PAR__NULL ) { errAnnul( status ); /* Otherwise, create a FrameSet describing the whole sky in which each pixel corresponds to a single tile. */ } else { smf_jsatile( -1, &skytiling, 0, proj, NULL, &fs, NULL, lbnd, ubnd, status ); /* Change the bounds of the output NDF. */ ndfSbnd( 2, lbnd, ubnd, indf3, status ); /* Store the FrameSet in the NDF. */ ndfPtwcs( fs, indf3, status ); /* Map the data array. */ ndfMap( indf3, "Data", "_INTEGER", "WRITE/BAD", (void **) &ipntr, &el, status ); /* Create all-sky map using XPH projection. */ if( *status == SAI__OK ) { /* Loop round every tile index. */ for( jtile = 0; jtile < skytiling.ntiles; jtile++ ) { /* Get the zero-based (x,y) indices of the tile within an HPX projection. This flips the bottom left half-facet up to the top right. */ smf_jsatilei2xy( jtile, &skytiling, &xt, &yt, NULL, status ); /* Convert these HPX indices to the corresponding indices within the required projection. Note, the lower left facet is split by the above call to smf_jsatilei2xy tile (i.e. (xt,yt) indices are *not* in the "raw" mode). For instance, (0,0) is not a valid tile. */ smf_jsatilexyconv( &skytiling, proj, xt, yt, 0, &xt, &yt, status ); /* Get the vector index of the corresponding element of the all-sky NDF. */ if( proj == SMF__JSA_HPX || proj == SMF__JSA_HPX12 ) { iv = xt + 5*skytiling.ntpf*yt; } else { iv = xt + 4*skytiling.ntpf*yt; } /* Report an error if this element has already been assigned a tile index. Otherwise, store the tile index. */ if( ipntr[ iv ] == VAL__BADI ) { ipntr[ iv ] = jtile; } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "%s projection assigns multiple tiles to " "pixel (%d,%d).", status, text, xt, yt ); break; } } } /* Store NDF title. */ sprintf( text, "JSA tile indices for %s data", skytiling.name ); ndfCput( text, indf3, "TITLE", status ); /* Store the instrument as a component in the SMURF extension. */ ndfXnew( indf3, "SMURF", "INSTRUMENT", 0, 0, &xloc, status ); ndfXpt0c( skytiling.name, indf3, "SMURF", "INSTRUMENT", status ); datAnnul( &xloc, status ); /* Close the NDF. */ ndfAnnul( &indf3, status ); } /* Abort if an error has occurred. */ if( *status != SAI__OK ) goto L999; /* Get the zero-based index of the required tile. If a null value is supplied, annull the error and skip to the end. */ parGdr0i( "ITILE", 0, 0, skytiling.ntiles - 1, 0, &itile, status ); if( *status == PAR__NULL ) { errAnnul( status ); goto L999; } /* See if the pixel origin is to be at the centre of the tile. */ parGet0l( "LOCAL", &local_origin, status ); /* Display the tile number. */ msgBlank( status ); msgSeti( "ITILE", itile ); msgSeti( "MAXTILE", skytiling.ntiles - 1); msgOut( " ", " Tile ^ITILE (from 0 to ^MAXTILE):", status ); /* Get the FITS header, FrameSet and Region defining the tile, and the tile bounds in pixel indices. */ smf_jsatile( itile, &skytiling, local_origin, proj, &fc, &fs, ®ion, lbnd, ubnd, status ); /* Write the FITS headers out to a file, annulling the error if the header is not required. */ if( *status == SAI__OK ) { atlDumpFits( "HEADER", fc, status ); if( *status == PAR__NULL ) errAnnul( status ); } /* If required, write the Region out to a text file. */ if( *status == SAI__OK ) { atlCreat( "REGION", (AstObject *) region, status ); if( *status == PAR__NULL ) errAnnul( status ); } /* Store the lower and upper pixel bounds of the tile. */ parPut1i( "LBND", 2, lbnd, status ); parPut1i( "UBND", 2, ubnd, status ); /* Display pixel bounds on the screen. */ msgSeti( "XL", lbnd[ 0 ] ); msgSeti( "XU", ubnd[ 0 ] ); msgSeti( "YL", lbnd[ 1 ] ); msgSeti( "YU", ubnd[ 1 ] ); msgOut( " ", " Pixel bounds: (^XL:^XU,^YL:^YU)", status ); /* Get the RA,Dec at the tile centre. */ if( astTest( fs, "SkyRef" ) ) { ra0 = astGetD( fs, "SkyRef(1)" ); dec0 = astGetD( fs, "SkyRef(2)" ); /* Format the central RA and Dec. and display. Call astNorm on the coordinates provided that the frame set has the correct number of axes (which it should as it comes from smf_jsatile). */ norm_radec[0] = ra0; norm_radec[1] = dec0; if (astGetI(fs, "Naxes") == 2) astNorm(fs, norm_radec); msgSetc( "RACEN", astFormat( fs, 1, norm_radec[ 0 ] )); msgSetc( "DECCEN", astFormat( fs, 2, norm_radec[ 1 ] )); msgOut( " ", " Centre (ICRS): RA=^RACEN DEC=^DECCEN", status ); /* Transform a collection of points on the edge of the region (corners and side mid-points) from GRID coords to RA,Dec. */ point1[ 0 ] = 0.5; point1[ 1 ] = 0.5; point2[ 0 ] = ubnd[ 0 ] - lbnd[ 0 ] + 1; point2[ 1 ] = ubnd[ 1 ] - lbnd[ 1 ] + 1; gx[ 0 ] = point1[ 0 ]; /* Bottom left */ gy[ 0 ] = point1[ 1 ]; gx[ 1 ] = point1[ 0 ]; /* Centre left */ gy[ 1 ] = gy[ 0 ]; gx[ 2 ] = point1[ 0 ]; /* Top left */ gy[ 2 ] = point2[ 1 ]; gx[ 3 ] = gx[ 0 ]; /* Top centre */ gy[ 3 ] = point2[ 1 ]; gx[ 4 ] = point2[ 0 ]; /* Top right */ gy[ 4 ] = point2[ 1 ]; gx[ 5 ] = point2[ 0 ]; /* Centre right */ gy[ 5 ] = gy[ 0 ]; gx[ 6 ] = point2[ 0 ]; /* Bottom right */ gy[ 6 ] = point1[ 1 ]; gx[ 7 ] = gx[ 0 ]; /* Bottom centre */ gy[ 7 ] = point1[ 1 ]; astTran2( fs, 8, gx, gy, 1, ra, dec ); /* Find the arc-distance from the centre to the furthest point from the centre. */ point1[ 0 ] = ra0; point1[ 1 ] = dec0; maxdist = -1.0; for( i = 1; i < 8; i++ ) { if( ra[ i ] != AST__BAD && dec[ i ] != AST__BAD ) { point2[ 0 ] = ra[ i ]; point2[ 1 ] = dec[ i ]; dist = astDistance( fs, point1, point2 ); if( dist > maxdist ) maxdist = dist; } } /* Convert from radius to diameter. */ maxdist *= 2.0; /* Format this size as a dec value (i.e. arc-distance) and display it. */ if( maxdist > 0.0 ) { msgSetc( "SIZE", astFormat( fs, 2, maxdist ) ); msgOut( " ", " Size: ^SIZE", status ); } else { maxdist = AST__BAD; msgOut( " ", " Size: <unknown>", status ); } /* If a discontinuity passes through the tile, the centre and size may be unknown. */ } else { ra0 = AST__BAD; dec0 = AST__BAD; maxdist = AST__BAD; msgOut( " ", " Centre (ICRS): RA=<unknown> DEC=<unknown>", status ); msgOut( " ", " Size: <unknown>", status ); } /* Write the tile centre ra and dec in radians to the output parameters. */ parPut0d( "RACEN", norm_radec[ 0 ], status ); parPut0d( "DECCEN", norm_radec[ 1 ], status ); /* Write the size to the output parameter as radians. */ parPut0d( "SIZE", maxdist, status ); /* Get the translation of the environment variable JSA_TILE_DIR. */ jcmt_tiles = getenv( "JSA_TILE_DIR" ); /* Initialise the path to the tile's NDF to hold the root directory. Use the current working directory if JSA_TILE_DIR is undefined. */ if( jcmt_tiles ) { nc = 0; tilendf = astAppendString( tilendf, &nc, jcmt_tiles ); } else { nc = 512; jcmt_tiles = astMalloc( nc ); while( !getcwd( jcmt_tiles, nc ) ) { nc *= 2; jcmt_tiles = astRealloc( jcmt_tiles, nc ); } nc = 0; tilendf = astAppendString( tilendf, &nc, jcmt_tiles ); jcmt_tiles = astFree( jcmt_tiles ); } /* Complete the path to the tile's NDF. */ tilendf = astAppendString( tilendf, &nc, "/" ); tilendf = astAppendString( tilendf, &nc, skytiling.subdir ); dirlen = nc; sprintf( text, "/tile_%d.sdf", itile ); tilendf = astAppendString( tilendf, &nc, text ); /* Write it to the output parameter. */ parPut0c( "TILENDF", tilendf, status ); /* See if the NDF exists, and store the flag in the output parameter. */ exists = access( tilendf, F_OK ) ? 0 : 1; parPut0l( "EXISTS", exists, status ); /* If the NDF does not exist, create it if required. */ parGet0l( "CREATE", &create, status ); if( !exists && create && *status == SAI__OK ) { /* Write the NDF info to the screen. */ msgSetc( "NDF", tilendf ); msgOutif( MSG__NORM, " ", " NDF: ^NDF (created)", status ); /* Temporarily terminate the NDF path at the end of the subdirectory. */ tilendf[ dirlen ] = 0; /* Create the required directory (does nothing if the directory already exists). It is given read/write/search permissions for owner and group, and read/search permissions for others. */ (void) mkdir( tilendf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ); /* Replace the character temporarily overwritten above. */ tilendf[ dirlen ] = '/'; /* Now create the tile's NDF. */ ndfPlace( NULL, tilendf, &place, status ); ndfNew( skytiling.type, 2, lbnd, ubnd, &place, &indf1, status ); /* Fill its data array with zeros. */ ndfMap( indf1, "Data", skytiling.type, "WRITE/ZERO", &pntr, &el, status ); /* Store the WCS FrameSet. */ ndfPtwcs( fs, indf1, status ); /* If the instrument jsatiles.have variance, fill the variance array with zeros. */ if( skytiling.var ) { ndfMap( indf1, "Variance", skytiling.type, "WRITE/ZERO", &pntr, &el, status ); } /* Create a SMURF extension. */ ndfXnew( indf1, SMURF__EXTNAME, SMURF__EXTTYPE, 0, NULL, &xloc, status ); /* Store the tile number and instrument name in the extension. */ datNew0I( xloc, "TILE", status ); datFind( xloc, "TILE", &cloc, status ); datPut0I( cloc, itile, status ); datAnnul( &cloc, status ); datNew0C( xloc, "INSTRUMENT", strlen( skytiling.name ), status ); datFind( xloc, "INSTRUMENT", &cloc, status ); datPut0C( cloc, skytiling.name, status ); datAnnul( &cloc, status ); /* Create a weights NDF within the SMURF extension, and fill its data array with zeros. */ ndfPlace( xloc, "WEIGHTS", &place, status ); ndfNew( skytiling.type, 2, lbnd, ubnd, &place, &indf2, status ); ndfMap( indf2, "Data", skytiling.type, "WRITE/ZERO", &pntr, &el, status ); ndfPtwcs( fs, indf2, status ); ndfAnnul( &indf2, status ); /* Annul the extension locator and the main NDF identifier. */ datAnnul( &xloc, status ); ndfAnnul( &indf1, status ); /* Write the NDF info to the screen. */ } else { msgSetc( "NDF", tilendf ); msgSetc( "E", exists ? "exists" : "does not exist" ); msgOut( " ", " NDF: ^NDF (^E)", status ); } /* Initialise TBND and TLBND to indicate no overlap. */ tlbnd[ 0 ] = 1; tlbnd[ 1 ] = 1; tubnd[ 0 ] = 0; tubnd[ 1 ] = 0; /* Attempt to to get an AST Region (assumed to be in some 2D sky coordinate system) using parameter "TARGET". */ if( *status == SAI__OK ) { kpg1Gtobj( "TARGET", "Region", (void (*)( void )) F77_EXTERNAL_NAME(ast_isaregion), &obj, status ); /* Annul the error if none was obtained. */ if( *status == PAR__NULL ) { errAnnul( status ); /* Otherwise, use the supplied object. */ } else { target = (AstRegion *) obj; /* If the target Region is 3-dimensional, remove the third axis, which is assumed to be a spectral axis. */ if( astGetI( target, "Naxes" ) == 3 ) { axes[ 0 ] = 1; axes[ 1 ] = 2; target = astPickAxes( target, 2, axes, NULL ); } /* See if there is any overlap between the target and the tile. */ overlap = NULL; flag = astOverlap( region, target ); if( flag == 0 ) { msgOut( "", " Cannot convert between the coordinate system of the " "supplied target and the tile.", status ); } else if( flag == 1 || flag == 6 ) { msgOut( "", " There is no overlap between the target and the tile.", status ); } else if( flag == 2 ) { msgOut( "", " The tile is contained within the target.", status ); tlbnd[ 0 ] = lbnd[ 0 ]; tlbnd[ 1 ] = lbnd[ 1 ]; tubnd[ 0 ] = ubnd[ 0 ]; tubnd[ 1 ] = ubnd[ 1 ]; } else if( flag == 3 ) { overlap = astCmpRegion( region, target, AST__AND, " " ); } else if( flag == 4 ) { overlap = astCmpRegion( region, target, AST__AND, " " ); } else if( flag == 5 ) { msgOut( "", " The target and tile are identical.", status ); tlbnd[ 0 ] = lbnd[ 0 ]; tlbnd[ 1 ] = lbnd[ 1 ]; tubnd[ 0 ] = ubnd[ 0 ]; tubnd[ 1 ] = ubnd[ 1 ]; } else if( *status == SAI__OK ) { *status = SAI__OK; errRepf( "", "Unexpected value %d returned by astOverlap " "(programming error).", status, flag ); } /* If a region containing the intersection of the tile and target was created above, map it into the grid coordinate system of the tile. */ if( overlap ) { overlap = astMapRegion( overlap, astGetMapping( fs, AST__CURRENT, AST__BASE ), astGetFrame( fs, AST__BASE ) ); /* Get its GRID bounds. */ astGetRegionBounds( overlap, dlbnd, dubnd ); /* Convert to integer. */ tlbnd[ 0 ] = ceil( dlbnd[ 0 ] - 0.5 ); tlbnd[ 1 ] = ceil( dlbnd[ 1 ] - 0.5 ); tubnd[ 0 ] = ceil( dubnd[ 0 ] - 0.5 ); tubnd[ 1 ] = ceil( dubnd[ 1 ] - 0.5 ); /* Convert to PIXEL indices within the tile. */ tlbnd[ 0 ] += lbnd[ 0 ] - 1; tlbnd[ 1 ] += lbnd[ 1 ] - 1; tubnd[ 0 ] += lbnd[ 0 ] - 1; tubnd[ 1 ] += lbnd[ 1 ] - 1; msgOutf( "", " The target overlaps section (%d:%d,%d:%d).", status, tlbnd[ 0 ], tubnd[ 0 ], tlbnd[ 1 ], tubnd[ 1 ] ); } } } /* Store the pixel index bounds of the tiles overlap with the target. */ parPut1i( "TLBND", 2, tlbnd, status ); parPut1i( "TUBND", 2, tubnd, status ); /* Arrive here if an error occurs. */ L999:; /* Free resources. */ tilendf = astFree( tilendf ); /* End the AST context. */ astEnd; /* Issue a status indication.*/ msgBlank( status ); if( *status == SAI__OK ) { msgOutif( MSG__VERB, "", "JSATILEINFO succeeded.", status); } else { msgOutif( MSG__VERB, "", "JSATILEINFO failed.", status); } }
void smf_jsatiling( smf_inst_t instrument, smfJSATiling *skytiling, int *status ){ /* Initialise the returned structure before checking the inherited status. */ skytiling->instrument = SMF__INST_NONE; skytiling->name = ""; skytiling->ntpf = 0; skytiling->ppt = 0; skytiling->ntiles = 0; skytiling->type = "_DOUBLE"; skytiling->var = 1; /* Check inherited status */ if( *status != SAI__OK ) return; /* SCUBA-2 850 - NTPF is chosen to be a power of 2 that gives a tile area close to 1 square degree (actually 0.8393 square degrees). PPT is chosen to give a pixel size under 4 arc-seconds (actually 3.22). */ if( instrument == SMF__INST_SCUBA_2_850 ) { skytiling->name = "SCUBA-2(850)"; skytiling->subdir = "scuba2-850"; skytiling->ntpf = 64; skytiling->ppt = 1024; skytiling->fov = 600.0; /* SCUBA-2 450 - NTPF is chosen to be a power of 2 that gives a tile area close to 1 square degree (actually 0.8393 square degrees). PPT is chosen to give a pixel size under 2 arc-seconds (actually 1.61). */ } else if( instrument == SMF__INST_SCUBA_2_450 ) { skytiling->name = "SCUBA-2(450)"; skytiling->subdir = "scuba2-450"; skytiling->ntpf = 64; skytiling->ppt = 2048; skytiling->fov = 600.0; /* HARP - NTPF is chosen to be a power of 2 that gives a tile area close to 1/16 square degree (actually 0.05 square degrees). PPT is chosen to give a pixel size close to 6 arc-seconds (actually 6.44). */ } else if( instrument == SMF__INST_HARP ) { skytiling->name = "HARP"; skytiling->subdir = "harp"; skytiling->type = "_REAL"; skytiling->ntpf = 256; skytiling->ppt = 128; skytiling->fov = 130.0; /* RxA */ } else if( instrument == SMF__INST_RXA ) { skytiling->name = "RxA"; skytiling->subdir = "rxa"; /* RxWD */ } else if( instrument == SMF__INST_RXWD ) { skytiling->name = "RxWD"; skytiling->subdir = "rxwd"; /* RxWB */ } else if( instrument == SMF__INST_RXWB ) { skytiling->name = "RxWB"; skytiling->subdir = "rxwb"; /* Unknown. */ } else { *status = SAI__ERROR; msgSeti( "I", instrument ); errRep( " ", "smf_jsatiling: Unknown instrument identifier (^I) " "supplied (programming error).", status ); } /* Report an error if the sky tiling for the instrument has not yet been defined. */ if( skytiling->ntpf == 0 ) { if( *status == SAI__OK ) { *status = SAI__ERROR; msgSetc( "I", skytiling->name ); errRep( " ", "Tiling for ^I has not yet been defined.", status ); } /* Otherwise, store the instrument identifier and the total number of tiles on the sky (this assumes that we are using 12 HEALPix facets to cover the whole sky). */ } else { skytiling->instrument = instrument; skytiling->ntiles = 12*skytiling->ntpf*skytiling->ntpf; } }
void findback( int *status ){ /* *+ * Name: * FINDBACK * Purpose: * Estimate the background in an NDF by removing small scale structure. * Language: * C * Type of Module: * ADAM A-task * Synopsis: * void findback( int *status ); * Description: * This application uses spatial filtering to remove structure with a * scale size less than a specified size from a 1, 2, or 3 dimensional * NDF, thus producing an estimate of the local background within the NDF. * * The algorithm proceeds as follows. A filtered form of the input data * is first produced by replacing every input pixel by the minimum of * the input values within a rectangular box centred on the pixel. * This filtered data is then filtered again, using a filter that * replaces every pixel value by the maximum value in a box centred on * the pixel. This produces an estimate of the lower envelope of the data, * but usually contains unacceptable sharp edges. In addition, this * filtered data has a tendency to hug the lower envelope of the * noise, thus under-estimating the true background of the noise-free * data. The first problem is minimised by smoothing the background * estimate using a filter that replaces every pixel value by the mean * of the values in a box centred on the pixel. The second problem * is minimised by estimating the difference between the input data * and the background estimate within regions well removed from any * bright areas. This difference is then extrapolated into the bright * source regions and used as a correction to the background estimate. * Specifically, the residuals between the input data and the initial * background estimate are first formed, and residuals which are more * than three times the RMS noise are set bad. The remaining residuals * are smoothed with a mean filter. This smoothing will replace a lot * of the bad values rejected above, but may not remove them all. Any * remaining bad values are estimated by linear interpolation between * the nearest good values along the first axis. The interpolated * residuals are then smoothed again using a mean filter, to get a * surface representing the bias in the initial background estimate. * This surface is finally added onto the initial background estimate * to obtain the output NDF. * Usage: * findback in out box * ADAM Parameters: * BOX() = _INTEGER (Read) * The dimensions of each of the filters, in pixels. Each value * should be odd (if an even value is supplied, the next higher odd * value will be used). The number of values supplied should not * exceed the number of significant (i.e. more than one element) * pixel axes in the input array. If any trailing values of 1 are * supplied, then each pixel value on the corresponding axes * will be fitted independently of its neighbours. For instance, * if the data array is 3-dimensional, and the third BOX value is 1, * then each x-y plane will be fitted independently of the neighbouring * planes. If the NDF has more than 1 pixel axis but only 1 value is * supplied, then the same value will be used for the both the first * and second pixel axes (a value of 1 will be assumed for the third * axis if the input array is 3-dimensional). * MSG_FILTER = _CHAR (Read) * Controls the amount of diagnostic information reported. This is the * standard messaging level. The default messaging level is NORM (2). * A value of NONE or 0 will suppress all screen output. VERB (3) will * indicate progress through the various stages of the algorithm. [NORM] * IN = NDF (Read) * The input NDF. * RMS = _DOUBLE (Read) * Specifies a value to use as the global RMS noise level in the * supplied data array. The suggested default value is the square root * of the mean of the values in the input NDF's Variance component. * If the NDF has no Variance component, the suggested default * is based on the differences between neighbouring pixel values, * measured over the entire input NDF. If multiple slices within the * NDF are to be processed independently (see parameter BOX), it * may be more appropriate for a separate default RMS to be calculated * for each slice. This will normally be the case if the noise could * be different in each of the slices. In such cases a null (!) can * be supplied for the RMS parameter, which forces a separate * default RMS value to be found and used for each slice. Any * pixel-to-pixel correlation in the noise can result in these * defaults being too low. * SUB = _LOGICAL (Read) * If a TRUE value is supplied, the output NDF will contain the * difference between the supplied input data and the estimated * background. If a FALSE value is supplied, the output NDF will * contain the estimated background itself. [FALSE] * OUT = NDF (Write) * The output NDF containing either the estimated background, or the * background-subtracted input data, as specified by parameter SUB. * Notes: * - Smoothing cubes in 3 dimensions can be very slow. * Copyright: * Copyright (C) 2009 Science and Technology Facilities Council. * Copyright (C) 2006, 2007 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: * 13-SEP-2006 (DSB): * Original version. * 19-MAR-2007 (DSB): * - Added parameters SUB and RMS. * - Fix bug that left the output NDF uninitialised if ILEVEL is set * non-zero. * - Use generic data type handling as in FINDCLUMPS. * 14-JAN-2009 (TIMJ): * Use MERS for message filtering. * 29-JUL-2009 (TIMJ): * Rename ILEVEL to MSG_FILTER * 17-MAY-2011 (DSB): * Use sqrt rather than sqrtf when calculating RMS. * 12-SEP-2011 (DSB): * Process slices in separate threads. * {enter_further_changes_here} *- */ /* Local Variables: */ CupidFindback0Data *job_data; /* Pointer to data for all jobs */ CupidFindback0Data *pdata; /* Pointer to data for current job */ Grp *grp; /* GRP identifier for configuration settings */ ThrWorkForce *wf = NULL; /* Pool of persistent worker threads */ char dtype[ 21 ]; /* HDS data type for output NDF */ char itype[ 21 ]; /* HDS data type to use when processing */ double *ipv; /* Pointer to Variance array */ double *pd1; /* Pointer to double precision input data */ double *pd2; /* Pointer to double precision output data */ double rms; /* Global rms error in data */ double sum; /* Sum of variances */ float *pf1; /* Pointer to single precision input data */ float *pf2; /* Pointer to single precision output data */ int *old_status; /* Pointer to original status value */ int box[ 3 ]; /* Dimensions of each cell in pixels */ int dim[ NDF__MXDIM ]; /* Dimensions of each NDF pixel axis */ int el; /* Number of elements mapped */ int i; /* Loop count */ int indf1; /* Identifier for input NDF */ int indf2; /* Identifier for output NDF */ int islice; /* Slice index */ int iystep; /* Index of slice in ydirection */ int izstep; /* Index of slice in z direction */ int lbnd[ NDF__MXDIM ]; /* Lower pixel bounds of slice */ int n; /* Number of values summed in "sum" */ int ndim; /* Total number of pixel axes in NDF */ int newalg; /* Use experimental algorithm variations? */ int nsdim; /* Number of significant pixel axes in NDF */ int nslice; /* Number of slices to process */ int nval; /* Number of values supplied */ int nystep; /* Number of independent y slices */ int nzstep; /* Number of slices in z direction */ int sdim[ 3 ]; /* Dimensions of each significant NDF axis */ int slice_dim[ 3 ]; /* Dimensions of each significant slice axis */ int slice_lbnd[ 3 ]; /* Lower bounds of each significant slice axis */ int slice_size; /* Number of pixels in each slice */ int state; /* Parameter state */ int sub; /* Output the background-subtracted input data? */ int type; /* Integer identifier for data type */ int ubnd[ NDF__MXDIM ]; /* Upper pixel bounds of slice */ int var; /* Does i/p NDF have a Variance component? */ size_t size; /* Size of GRP group */ void *ipd1; /* Pointer to input Data array */ void *ipd2; /* Pointer to output Data array */ void *ipdin; /* Pointer to input Data array */ void *ipdout; /* Pointer to output Data array */ /* Abort if an error has already occurred. */ if( *status != SAI__OK ) return; /* Start an NDF context */ ndfBegin(); /* Record the existing AST status pointer, and ensure AST uses the supplied status pointer instead. */ old_status = astWatch( status ); /* Get an identifier for the input NDF. We use NDG (via kpg1_Rgndf) instead of calling ndfAssoc directly since NDF/HDS has problems with file names containing spaces, which NDG does not have. */ kpg1Rgndf( "IN", 1, 1, "", &grp, &size, status ); ndgNdfas( grp, 1, "READ", &indf1, status ); grpDelet( &grp, status ); /* Get the pixel index bounds of the input NDF. */ ndfBound( indf1, NDF__MXDIM, lbnd, ubnd, &ndim, status ); /* Identify and count the number of significant axes (i.e. axes spanning more than 1 pixel). Also record their dimensions. */ nsdim = 0; for( i = 0; i < ndim; i++ ) { dim[ i ] = ubnd[ i ] - lbnd[ i ] + 1; if( dim[ i ] > 1 ) sdim[ nsdim++ ] = dim[ i ]; } /* If there are too many significant axes, report an error. */ if( nsdim > 3 && *status == SAI__OK ) { *status = SAI__ERROR; ndfMsg( "N", indf1 ); msgSeti( "NS", nsdim ); errRep( "", "The NDF '^N' has ^NS significant pixel axes, but this" "application requires 1, 2 or 3.", status ); } /* Ensure we have 3 values in sdim (pad with trailings 1's if required). */ if( nsdim < 3 ) sdim[ 2 ] = 1; if( nsdim < 2 ) sdim[ 1 ] = 1; /* See if the output is to contain the background-subtracted data, or the background estimate itself. */ parGet0l( "SUB", &sub, status ); /* Create the output by propagating everything except the Data and (if we are outputting the background itself) Variance arrays. */ if( sub ) { ndfProp( indf1, "UNITS,AXIS,WCS,QUALITY,VARIANCE", "OUT", &indf2, status ); } else { ndfProp( indf1, "UNITS,AXIS,WCS,QUALITY", "OUT", &indf2, status ); } msgBlankif( MSG__VERB, status ); /* Get the dimensions of each of the filters, in pixels. If only one value is supplied, duplicate it as the second value if the second axis is significant. If fewer than 3 values were supplied, use 1 for the 3rd value (whether or not it is significant). This results in each plane being fitted independently of the adjacent planes by default. */ parGet1i( "BOX", nsdim, box, &nval, status ); if( *status != SAI__OK ) goto L999; if( nval < 2 ) box[ 1 ] = ( nsdim > 1 ) ? box[ 0 ] : 1; if( nval < 3 ) box[ 2 ] = 1; /* Ensure box sizes are odd. */ box[ 0 ] = 2*( box[ 0 ] / 2 ) + 1; box[ 1 ] = 2*( box[ 1 ] / 2 ) + 1; box[ 2 ] = 2*( box[ 2 ] / 2 ) + 1; msgOutiff( MSG__VERB, "", "Using box sizes [%d,%d,%d].", status, box[0], box[1], box[2]); /* If any trailing axes have a cell size of 1, then we apply the algorithm independently to every pixel index on the trailing axes. First of all set things up assuming that there are no trailing axes with cell size of 1. */ nystep = 1; nzstep = 1; slice_dim[ 0 ] = sdim[ 0 ]; slice_dim[ 1 ] = sdim[ 1 ]; slice_dim[ 2 ] = sdim[ 2 ]; slice_lbnd[ 0 ] = lbnd[ 0 ]; slice_lbnd[ 1 ] = lbnd[ 1 ]; slice_lbnd[ 2 ] = lbnd[ 2 ]; /* If the 3rd pixel axis has a cell size of 1, arrange that each slice contains a single plane. */ if( box[ 2 ] == 1 ) { nzstep = sdim[ 2 ]; slice_dim[ 2 ] = 1; /* If the 2nd pixel axis also has a cell size of 1, arrange that each slice contains a single row. */ if( box[ 1 ] == 1 ) { nystep = sdim[ 1 ]; slice_dim[ 1 ] = 1; } } /* Determine the number of pixels in each independent slice. */ slice_size = slice_dim[ 0 ]*slice_dim[ 1 ]*slice_dim[ 2 ]; /* Decide what numeric data type to use, and set the output NDF data type. */ ndfMtype( "_REAL,_DOUBLE", indf1, indf1, "Data,Variance", itype, 20, dtype, 20, status ); if( !strcmp( itype, "_DOUBLE" ) ) { type = CUPID__DOUBLE; } else { type = CUPID__FLOAT; } ndfStype( dtype, indf2, "Data,Variance", status ); /* Map the input and output arrays. */ ndfMap( indf1, "Data", itype, "READ", &ipdin, &el, status ); ndfMap( indf2, "Data", itype, "WRITE", &ipdout, &el, status ); /* If the rms value is supplied on the command, there is no need to calculate a default value. */ parState( "RMS", &state, status ); if( state == PAR__GROUND ) { /* Calculate the default RMS value. If the NDF has a Variance component it is the square root of the mean Variance value. Otherwise, it is found by looking at differences between adjacent pixel values in the Data component. */ ndfState( indf1, "VARIANCE", &var, status ); if( *status == SAI__OK && var ) { ndfMap( indf1, "VARIANCE", "_DOUBLE", "READ", (void *) &ipv, &el, status ); sum = 0.0; n = 0; for( i = 0; i < el; i++ ) { if( ipv[ i ] != VAL__BADD ) { sum += ipv[ i ]; n++; } } if( n > 0 ) { rms = sqrt( sum/n ); } else { *status = SAI__ERROR; errRep( "", "The supplied data contains insufficient " "good Variance values to continue.", status ); } } else { ipv = NULL; rms = cupidRms( type, ipdin, el, sdim[ 0 ], status ); } /* Set the default RMS noise level. */ parDef0d( "RMS", rms, status ); } /* Abort if an error has occurred. */ if( *status != SAI__OK ) goto L999; /* Get the RMS noise level. */ parGet0d( "RMS", &rms, status ); /* Annul the error and use an RMS value of VAL__BAD if a null parameter value was supplied. This causes an independent default noise estimate to be used for each slice of the base NDF. */ if( *status == PAR__NULL ) { errAnnul( status ); rms = VAL__BADD; } /* See if any experimental algorithm variations are to be used. */ parGet0l( "NEWALG", &newalg, status ); /* Create a pool of worker threads. */ wf = thrCreateWorkforce( thrGetNThread( "CUPID_THREADS", status ), status ); /* Get memory to hold a description of each job passed to a worker. There is one job for each slice. */ nslice = nystep*nzstep; job_data = astMalloc( nslice*sizeof( *job_data ) ); if( *status == SAI__OK ) { /* Loop round all slices to be processed. */ ipd1 = ipdin; ipd2 = ipdout; islice = 0; pdata = job_data; for( izstep = 0; izstep < nzstep ; izstep++ ) { slice_lbnd[ 1 ] = lbnd[ 1 ]; for( iystep = 0; iystep < nystep; iystep++, islice++,pdata++ ) { /* Store the information needed by the function (cupidFindback0) that does the work in a thread. */ pdata->islice = islice; pdata->nslice = nslice; pdata->type = type; pdata->ndim = ndim; pdata->box[ 0 ] = box[ 0 ]; pdata->box[ 1 ] = box[ 1 ]; pdata->box[ 2 ] = box[ 2 ]; pdata->rms = rms; pdata->ipd1 = ipd1; pdata->ipd2 = ipd2; pdata->slice_dim[ 0 ] = slice_dim[ 0 ]; pdata->slice_lbnd[ 0 ] = slice_lbnd[ 0 ]; pdata->slice_dim[ 1 ] = slice_dim[ 1 ]; pdata->slice_lbnd[ 1 ] = slice_lbnd[ 1 ]; pdata->slice_dim[ 2 ] = slice_dim[ 2 ]; pdata->slice_lbnd[ 2 ] = slice_lbnd[ 2 ]; pdata->newalg = newalg; pdata->slice_size = slice_size; /* Submit a job to the workforce to process the current slice. */ thrAddJob( wf, 0, pdata, cupidFindback0, 0, NULL, status ); /* Update pointers to the start of the next slice in the input and output arrays. */ if( type == CUPID__FLOAT ) { ipd1 = ( (float *) ipd1 ) + slice_size; ipd2 = ( (float *) ipd2 ) + slice_size; } else { ipd1 = ( (double *) ipd1 ) + slice_size; ipd2 = ( (double *) ipd2 ) + slice_size; } /* Increment the lower bound on the 2nd pixel axis. */ slice_lbnd[ 1 ]++; } /* Increment the lower bound on the 3rd pixel axis. */ slice_lbnd[ 2 ]++; } /* Wait until all jobs have finished. */ thrWait( wf, status ); } /* The output currently holds the background estimate. If the user has requested that the output should hold the background-subtracted input data, then do the arithmetic now. */ if( sub && *status == SAI__OK ) { if( type == CUPID__FLOAT ) { pf1 = (float *) ipdin; pf2 = (float *) ipdout; for( i = 0; i < el; i++, pf1++, pf2++ ) { if( *pf1 != VAL__BADR && *pf2 != VAL__BADR ) { *pf2 = *pf1 - *pf2; } else { *pf2 = VAL__BADR; } } } else { pd1 = (double *) ipdin; pd2 = (double *) ipdout; for( i = 0; i < el; i++, pd1++, pd2++ ) { if( *pd1 != VAL__BADD && *pd2 != VAL__BADD ) { *pd2 = *pd1 - *pd2; } else { *pd2 = VAL__BADD; } } } } /* Tidy up */ L999:; msgBlankif( MSG__VERB, status ); /* Free workspace. */ job_data = astFree( job_data ); wf = thrDestroyWorkforce( wf ); /* Reinstate the original AST inherited status value. */ astWatch( old_status ); /* End the NDF context */ ndfEnd( status ); /* If an error has occurred, issue another error report identifying the program which has failed (i.e. this one). */ if( *status != SAI__OK ) { errRep( "FINDBACK_ERR", "FINDBACK: Failed to find the background " "of an NDF.", status ); } }
void cupid_mon( int *status ) { /* *+ * Name: * cupid_mon * Purpose: * Top-level CUPID function for A-task monolith on UNIX. * Language: * Starlink C * Type of Module: * ADAM A-task * Description: * This is the top-level A-task monolith function for the CUPID * suite of A-tasks. Each CUPID command is an alias to a softlink * that points to this monolith. The chosen command is obtained * from the ADAM routine TASK_GET_NAME. The command may be specified * from the shell or ICL. Given the command, the requested A-task * is called after a successful matching of the input string with a * valid task name. If there is no match, an error report is made. * Parameters: * status * Pointer to the global status variable used by the ADAM fixed part. * Synopsis: * void cupid_mon( int *status ); * Copyright: * Copyright (C) 2009 Science & Technology Facilities Council. * Copyright (C) 2005 Particle Physics & Astronomy Research Council. * All Rights Reserved. * Licence: * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA * 02110-1301, USA * Authors: * DSB: David S. Berry (STARLINK) * TIMJ: Tim Jenness (JAC, Hawaii) * {enter_new_authors_here} * History: * 28-SEP-2005 (DSB): * Original version. * 29-JUL-2009 (TIMJ): * Call taskGetName rather than Fortran. * Add CUPID and version number to NDF history. * 31-JUL-2009 (DSB): * Use ndgBegpv/Endpv to provide automatic provenance propagation. * 16-OCT-2009 (DSB): * Use ndgBeggh/ndgEndgh to record contents of group parameters in * the history component of output NDFs. * 2011-01-19 (TIMJ): * Add leak checking to CUPID monolith * {enter_further_changes_here} * Bugs: * {note_any_bugs_here} *- */ /* Local variables: */ char appname[NDF__SZAPP+1]; /* Application name for NDF History */ char buff[PAR__SZNAM+7]; /* Application name for provenance handling */ char filter[PAR__SZNAM+PAR__SZNAM+1]; char name[PAR__SZNAM+1]; /* C character variable to hold name */ int ast_caching; /* Initial value of AST MemoryCaching tuning parameter */ int emslev1; /* EMS level on entry */ int emslev2; /* EMS level on exit */ int ngrp0; /* Number of grp ids at start */ int ngrp1; /* Number of grp ids at end */ int nloc0; /* Number of active HDS Locators at start */ int nloc1; /* Number of active HDS Locators at end */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* For debugging, watch one of the leaked GRP identifiers listed by the call to grpWatch at the end of this routine (if any). */ /* grpWatch( 3129345, status ); */ /* Read the input error message stack level */ emsLevel( &emslev1 ); /* Obtain the command from the environment. This returns uppercase names. */ taskGetName( name, sizeof(name), status ); /* Update the application name in the NDF history recording to include the version number of the application */ snprintf( appname, NDF__SZAPP, "%-*s (%s V%s)", PAR__SZNAM, name, PACKAGE_UPCASE, PACKAGE_VERSION); ndfHappn( appname, status ); /* Make AST use the same variable for its inherited status. */ astWatch( status ); /* Tell AST to re-cycle memory when possible. */ ast_caching = astTune( "MemoryCaching", 1 ); /* Get the GRP and HDS status for leak checking - need the task name to mask out parameter names. Also need to mask out the monlith name */ one_strlcpy( filter, "!CUPID_MON,!", sizeof(filter), status); one_strlcat( filter, name, sizeof(filter), status ); grpInfoi( NULL, 0, "NGRP", &ngrp0, status ); hdsInfoI( NULL, "LOCATORS", filter, &nloc0, status ); /* Begin a provenance block. This causes event handlers to be registered with the NDF library so that a handler routine in NDG is called every time an NDF is opened. This handler routine keeps a record of all NDFs that are opened for input or output, until the block is closed by calling ndgEndpv. */ ndgBegpv( status ); /* Begin a GRP NDF history block. This causes the contents of GRP groups to be appended to default history text added to any NDFs during the block. */ ndgBeggh( status ); /* Check the string against valid A-task names---if matched then call the relevant A-task. */ /* Finds a low frequency background surface. */ if( !strcmp( name, "FINDBACK" ) ) { findback( status ); /* Identifies emission clumps within a 2- or 3D NDF. */ } else if( !strcmp( name, "FINDCLUMPS" ) ) { findclumps( status ); /* Give help on CUPID commands. */ } else if( !strcmp( name, "CUPIDHELP" ) ) { cupidhelp( status ); /* Create simulated data containing clumps and noise. */ } else if( !strcmp( name, "MAKECLUMPS" ) ) { makeclumps( status ); /* Extract clump parameters from another image */ } else if( !strcmp( name, "EXTRACTCLUMPS" ) ) { extractclumps( status ); /* Obtain information about one or more clumps. */ } else if( !strcmp( name, "CLUMPINFO" ) ) { clumpinfo( status ); /* Report an error if the command name is not recognised. */ } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "CUPID_MON_NOCOM", "CUPID: No such command ^CMD.", status ); } /* End the GRP NDF history block. */ ndgEndgh( status ); /* End the provenance block. This will result in every output NDF being given a provenance extension containing a record of the input NDFs that the application accessed in order to create the output NDF. Any output NDF that already contains a provenance extension is left unchanged (so individual application can override this automatic provenance handling by adding a provenance extension to the output NDF itself). */ sprintf( buff, "CUPID:%s", name ); ndgEndpv( buff, status ); /* Re-instate the original value of the AST ObjectCaching tuning parameter. */ astTune( "MemoryCaching", ast_caching ); /* Check for GRP leaks Do this in a new error reporting context so that we get the correct value even if an error has occurred. */ errBegin( status ); grpInfoi( NULL, 0, "NGRP", &ngrp1, status ); /* If there are more active groups now than there were on entry, there must be a problem (GRP identifiers are not being freed somewhere). So report it. */ if (*status == SAI__OK && ngrp1 > ngrp0) { msgBlank( status ); msgSetc( "NAME", name ); msgSeti( "NGRP0", ngrp0 ); msgSeti( "NGRP1", ngrp1 ); msgOut( " ", "WARNING: The number of active " "GRP identifiers increased from ^NGRP0 to ^NGRP1 " "during execution of ^NAME (" PACKAGE_UPCASE " programming " " error).", status); msgBlank(status); grpWatch( 0, status ); } errEnd( status ); /* Check for HDS leaks Do this in a new error reporting context so that we get the correct value even if an error has occurred. */ errBegin( status ); hdsInfoI( NULL, "LOCATORS", filter, &nloc1, status ); /* If there are more active locators now than there were on entry, there must be a problem (HDS locators are not being freed somewhere). So report it. */ if (*status == SAI__OK && nloc1 > nloc0) { msgBlank( status ); msgSetc( "NAME", name ); msgSeti( "NLOC0", nloc0 ); msgSeti( "NLOC1", nloc1 ); msgOut( " ", "WARNING: The number of active " "HDS Locators increased from ^NLOC0 to ^NLOC1 " "during execution of ^NAME (" PACKAGE_UPCASE " programming " " error).", status); msgBlank(status); hdsShow("LOCATORS", status); hdsShow("FILES", status); } errEnd( status ); /* Read the exitt error message stack level */ emsLevel( &emslev2 ); if (*status == SAI__OK && emslev1 != emslev2 ) { errMark(); msgBlank( status ); msgSetc( "NAME", name ); msgSeti( "LV1", emslev1); msgSeti( "LV2", emslev2); msgOut( " ", "WARNING: EMS Stack level went from ^LV1 to ^LV2" " during execution of ^NAME (" PACKAGE_UPCASE " programming" " error).", status ); msgBlank(status); errRlse(); } /* Make AST use its own internal variable for its inherited status. */ astWatch( NULL ); /* Clear out any remaining memory allocated by AST and report unintentional leaks. */ astFlushMemory( 1 ); }
void smf_fits_export2DA ( AstFitsChan *fitschan, size_t *ncards, char *fitsrec, int *status ) { /* Local variables */ char blank[SZFITSCARD+1];/* Reference blank card */ char card[SZFITSCARD+1];/* temporary buffer for current card */ int found; /* Boolean to indicate if a card was found */ size_t i; /* Loop counter */ size_t ncopied = 0; /* How many cards were copied */ size_t numcards = 0; /* How many cards are in the FitsChan */ char *outpos = NULL; /* current position in output buffer */ int prevblank = 0; /* Was this previously blank? */ char *tempfits = NULL; /* intermediate buffer for FITS cards */ *ncards = 0; /* Check status */ if (*status != SAI__OK) return; /* Fill the blank card */ for (i=0; i<SZFITSCARD;i++) { blank[i] = ' '; } blank[SZFITSCARD] = '\0'; /* Find the number of cards in this AstFitsChan and create a buffer for internal use. We do not yet worry about the allocated size of fitsrec because we might be compressing the array to get rid of multiple blank lines */ numcards = astGetI ( fitschan, "Ncard" ); tempfits = astMalloc( ( 1 + numcards * SZFITSCARD ) * sizeof(*tempfits) ); /* Rewind */ astClear ( fitschan, "Card"); if (*status == SAI__OK) { /* Retrieve all the FITS headers and store them in the character array. We compress consecutive blank cards. */ ncopied = 0; outpos = tempfits; prevblank = 0; for ( i = 0; i <= numcards; i++ ) { found = astFindFits ( fitschan, "%f", card, 1 ); if ( found ) { int isblank = 0; if (strncmp( card, blank, SZFITSCARD ) == 0 ) isblank = 1; /* skip if this is blank and before was */ if (isblank && prevblank) continue; prevblank = isblank; /* Now copy in the card and increment the pointer */ strncpy ( outpos, card, SZFITSCARD ); ncopied++; outpos += SZFITSCARD; } else { break; } } /* Guarantee to terminate the buffer */ *outpos = '\0'; /* make sure that it is no larger than the maximum allowed */ if ( ncopied > SC2STORE__MAXFITS ) { *status = SAI__ERROR; msgSeti("NC", (int)numcards); msgSeti("MC", SC2STORE__MAXFITS); errRep( FUNC_NAME, "Number of FITS cards ^NC exceeds maximum allowed (^MC)", status ); tempfits = astFree( tempfits ); return; } /* Copy into the output buffer */ one_strlcpy( fitsrec, tempfits, SZFITSCARD * SC2STORE__MAXFITS + 1, status ); } tempfits = astFree(tempfits); *ncards = ncopied; }
void smurf_mon( int * status ) { /* Local variables */ char taskname[PAR__SZNAM+1]; char appname[NDF__SZAPP+1]; char filter[PAR__SZNAM+PAR__SZNAM+1]; int ngrp0; /* Number of grp ids at start */ int ngrp1; /* Number of grp ids at end */ int nloc0; /* Number of active HDS Locators at start */ int nloc1; /* Number of active HDS Locators at end */ int memory_caching; /* Is AST current caching unused memory? */ int emslev1; /* EMS level on entry */ int emslev2; /* EMS level on exit */ if ( *status != SAI__OK ) return; /* Read the input error message stack level */ emsLevel( &emslev1 ); /* Initialise AST */ astBegin; memory_caching = astTune( "MemoryCaching", 1 ); /* Register our status variable with AST */ astWatch( status ); /* If we are watching a particular memory Id reported by astActiveMemory we set the watch point here. */ /* astWatchMemory( 29 ); */ /* For debugging, watch one of the leaked GRP identifiers listed by the call to grpWatch at the end of this routine (if any). */ /* grpWatch( 3129345, status ); */ /* Mark any currently active NDF parameters, so that they will not be cancelled by the call to ndfCancl at the end of this function. */ ndfCancl( "*", status ); /* Find out the task name and provenance name we were invoked with */ smf_get_taskname( taskname, NULL, status ); /* Get the GRP and HDS status for leak checking - need the task name to mask out parameter names. Also need to mask out the monlith name */ one_strlcpy( filter, "!SMURF_MON,!", sizeof(filter), status); one_strlcat( filter, taskname, sizeof(filter), status ); grpInfoi( NULL, 0, "NGRP", &ngrp0, status ); hdsInfoI( NULL, "LOCATORS", filter, &nloc0, status ); /* Update the application name in the NDF history recording to include the version number of the application */ snprintf( appname, NDF__SZAPP, "%-*s (%s V%s)", PAR__SZNAM, taskname, PACKAGE_UPCASE, PACKAGE_VERSION); ndfHappn( appname, status ); /* Begin a GRP NDF history block. This causes the contents of GRP groups to be appended to default history text added to any NDFs during the block. */ ndgBeggh( status ); /* Call the subroutine associated with the requested task */ if (strcmp( taskname, "BADBOLOS" ) == 0 ) { smurf_extinction( status ); } else if (strcmp( taskname, "CALCDARK" ) == 0 ) { smurf_calcdark( status ); } else if (strcmp( taskname, "CALCFLAT" ) == 0 ) { smurf_calcflat( status ); } else if (strcmp( taskname, "CALCNOISE" ) == 0 ) { smurf_calcnoise( status ); } else if (strcmp( taskname, "CALCQU" ) == 0 ) { smurf_calcqu( status ); } else if (strcmp( taskname, "CALCRESP" ) == 0 ) { smurf_calcresp( status ); } else if (strcmp( taskname, "COPYFLAT" ) == 0 ) { smurf_copyflat( status ); } else if (strcmp( taskname, "DREAMSOLVE" ) == 0 ) { smurf_dreamsolve( status ); } else if (strcmp( taskname, "DREAMWEIGHTS" ) == 0 ) { smurf_dreamweights( status ); } else if (strcmp( taskname, "DSUTILS" ) == 0 ) { smurf_dsutils( status ); } else if (strcmp( taskname, "EXTINCTION" ) == 0 ) { smurf_extinction( status ); } else if (strcmp( taskname, "FIT1D" ) == 0 ) { smurf_fit1d( status ); } else if (strcmp( taskname, "FIXSTEPS" ) == 0 ) { smurf_fixsteps( status ); } else if (strcmp( taskname, "FLATFIELD" ) == 0 ) { smurf_flatfield( status ); } else if (strcmp( taskname, "FTS2DEGLITCH" ) == 0 ) { smurf_fts2_deglitch( status ); } else if (strcmp( taskname, "FTS2FLATFIELD" ) == 0 ) { smurf_fts2_flatfield( status ); } else if (strcmp( taskname, "FTS2FREQCORR" ) == 0 ) { smurf_fts2_freqcorr( status ); } else if (strcmp( taskname, "FTS2SPLIT" ) == 0 ) { smurf_fts2_split( status ); } else if (strcmp( taskname, "FTS2INIT" ) == 0 ) { smurf_fts2_init( status ); } else if (strcmp( taskname, "FTS2MASKMAP" ) == 0 ) { smurf_fts2_maskmap( status ); } else if (strcmp( taskname, "FTS2OPCORR" ) == 0 ) { smurf_fts2_spatialwcs( status ); } else if (strcmp( taskname, "FTS2PHASECORR" ) == 0 ) { smurf_fts2_phasecorr( status ); } else if (strcmp( taskname, "FTS2PHASECORRDS" ) == 0 ) { smurf_fts2_phasecorrds( status ); } else if (strcmp( taskname, "FTS2PORTIMBAL" ) == 0 ) { smurf_fts2_portimbal( status ); } else if (strcmp( taskname, "FTS2REMOVEBSE" ) == 0 ) { smurf_fts2_removebse( status ); } else if (strcmp( taskname, "FTS2SPECTRUM" ) == 0 ) { smurf_fts2_spectrum( status ); } else if (strcmp( taskname, "FTS2TRANSCORR" ) == 0 ) { smurf_fts2_transcorr( status ); } else if (strcmp( taskname, "GSD2ACSIS" ) == 0 ) { smurf_gsd2acsis( status ); } else if (strcmp( taskname, "GSDSHOW" ) == 0 ) { smurf_gsdshow( status ); } else if (strcmp( taskname, "IMPAZTEC" ) == 0 ) { smurf_impaztec( status ); } else if (strcmp( taskname, "MAKECUBE" ) == 0 ) { smurf_makecube( status ); } else if (strcmp( taskname, "MAKEMAP" ) == 0 ) { smurf_makemap( status ); } else if (strcmp( taskname, "RAWFIXMETA" ) == 0 ) { smurf_rawfixmeta( status ); } else if (strcmp( taskname, "RAWPRESS" ) == 0 ) { smurf_rawpress( status ); } else if (strcmp( taskname, "RAWRECREATEWCS" ) == 0 ) { smurf_rawrecreatewcs( status ); } else if (strcmp( taskname, "RAWREWRTSC2WCS" ) == 0 ) { smurf_rawrewrtsc2wcs( status ); } else if (strcmp( taskname, "RAWUNPRESS" ) == 0 ) { smurf_rawunpress( status ); } else if (strcmp( taskname, "REMSKY" ) == 0 ) { smurf_remsky( status ); } else if (strcmp( taskname, "SC2CLEAN" ) == 0 ) { smurf_sc2clean( status ); } else if (strcmp( taskname, "SC2CONCAT" ) == 0 ) { smurf_sc2concat( status ); } else if (strcmp( taskname, "SC2EXPANDMODEL" ) == 0 ) { smurf_sc2expandmodel( status ); } else if (strcmp( taskname, "SC2FFT" ) == 0 ) { smurf_sc2fft( status ); } else if (strcmp( taskname, "SC2FILTERMAP" ) == 0 ) { smurf_sc2filtermap( status ); } else if (strcmp( taskname, "SC2MAPFFT" ) == 0 ) { smurf_sc2mapfft( status ); } else if (strcmp( taskname, "SC2PCA" ) == 0 ) { smurf_sc2pca( status ); } else if (strcmp( taskname, "SC2SIM" ) == 0 ) { smurf_sc2sim( status ); } else if (strcmp( taskname, "SC2THREADTEST" ) == 0 ) { smurf_sc2threadtest( status ); } else if (strcmp( taskname, "SKYNOISE" ) == 0 ) { smurf_skynoise( status ); } else if (strcmp( taskname, "SMURFCOPY" ) == 0 ) { smurf_smurfcopy( status ); } else if (strcmp( taskname, "SMURFHELP" ) == 0 ) { smurf_smurfhelp( status ); } else if (strcmp( taskname, "STACKFRAMES" ) == 0 ) { smurf_stackframes( status ); } else if (strcmp( taskname, "STARECALC" ) == 0 ) { smurf_starecalc( status ); } else if (strcmp( taskname, "TILEINFO" ) == 0 ) { smurf_tileinfo( status ); } else if (strcmp( taskname, "TILELIST" ) == 0 ) { smurf_tilelist( status ); } else if (strcmp( taskname, "TIMESORT" ) == 0 ) { smurf_timesort( status ); } else if (strcmp( taskname, "UNMAKECUBE" ) == 0 ) { smurf_unmakecube( status ); } else if (strcmp( taskname, "UNMAKEMAP" ) == 0 ) { smurf_unmakemap( status ); } else { *status = SAI__ERROR; msgSetc( "TASK", taskname ); errRep( "smurf_mon", "Unrecognized taskname: ^TASK", status); } /* End the GRP NDF history block. */ ndgEndgh( status ); /* Clear cached info from sc2ast_createwcs. */ sc2ast_createwcs(SC2AST__NULLSUB, NULL, NULL, NULL, NO_FTS, NULL, status); /* Clear WVM caches (one for each thread). */ smf_calc_wvm_clear( status ); /* Free AST resources */ astTune( "MemoryCaching", memory_caching ); astEnd; /* Check for GRP leaks Do this in a new error reporting context so * that we get the correct value even if an error has occurred. */ errBegin( status ); grpInfoi( NULL, 0, "NGRP", &ngrp1, status ); /* If there are more active groups now than there were on entry, * there must be a problem (GRP identifiers are not being freed * somewhere). So report it. */ if (*status == SAI__OK && ngrp1 > ngrp0) { msgBlank( status ); msgSetc( "NAME", taskname ); msgSeti( "NGRP0", ngrp0 ); msgSeti( "NGRP1", ngrp1 ); msgOut( " ", "WARNING: The number of active " "GRP identifiers increased from ^NGRP0 to ^NGRP1 " "during execution of ^NAME (" PACKAGE_UPCASE " programming " " error).", status); msgBlank(status); grpWatch( 0, status ); } errEnd( status ); /* The NDF library registers locators with SUBPAR for any NDFs that are opened directly using ndfAssoc or ndfExist. These locators are only annulled when the associated parameters are cancelled, but most smurf applications do not explicitly cancel their NDF parameters. This means that such locators are picked up by the following check for dangling HDS locators. In order to prevent this, we cancel any remaining NDF parameters now, excluding any that were marked by the call to ndfCancl at the start of this routine. */ ndfCancl( " ", status ); /* Check for HDS leaks Do this in a new error reporting context so * that we get the correct value even if an error has occurred. */ errBegin( status ); hdsInfoI( NULL, "LOCATORS", filter, &nloc1, status ); /* If there are more active locators now than there were on entry, * there must be a problem (HDS locators are not being freed * somewhere). So report it. */ if (*status == SAI__OK && nloc1 > nloc0) { msgBlank( status ); msgSetc( "NAME", taskname ); msgSeti( "NLOC0", nloc0 ); msgSeti( "NLOC1", nloc1 ); msgOut( " ", "WARNING: The number of active " "HDS Locators increased from ^NLOC0 to ^NLOC1 " "during execution of ^NAME (" PACKAGE_UPCASE " programming " " error).", status); msgBlank(status); hdsShow("LOCATORS", status); hdsShow("FILES", status); printf("filter - %s\n",filter); } errEnd( status ); /* Read the exitt error message stack level */ emsLevel( &emslev2 ); if (*status == SAI__OK && emslev1 != emslev2 ) { errMark(); msgBlank( status ); msgSetc( "NAME", taskname ); msgSeti( "LV1", emslev1); msgSeti( "LV2", emslev2); msgOut( " ", "WARNING: EMS Stack level went from ^LV1 to ^LV2" " during execution of ^NAME (" PACKAGE_UPCASE " programming" " error).", status ); msgBlank(status); errRlse(); } /* configure AST --with-memdebug, and uncomment the following lines to see how much memory usage SMURF hit at its peak */ /* { size_t memcurrent,mempeak; astMemoryStats( 0, &mempeak, &memcurrent ); msgOutf( "", "SMURF: === current /peak memory usage: %zu / %zu MiB ===", status, memcurrent/SMF__MIB, mempeak/SMF__MIB ); } */ /* The astCheckMemory function does nothing unless AST has been compiled * with the MEM_DEBUG flag. If this is the case, then it reports the number * of memory blocks that have not been freed (useful for identifying memory * leaks). Use astActiveMemory() below to list all active memory and * then use astWatchMemory() at the start of this routine to get reports * when a particular ID is used. Set a breakpoint in the debugger for * astMemoryAlarm_ */ astActiveMemory("Exit:"); astCheckMemory; }
void smf_ext2km( int indf, const char *xname, AstKeyMap *keymap, int mode, int *status ){ /* Local Variables */ HDSLoc *cloc = NULL; HDSLoc *sloc = NULL; HDSLoc *xloc = NULL; const char *key = NULL; hdsdim diml[NDF__MXDIM]; hdsdim dimu[NDF__MXDIM]; int dim[ NDF__MXDIM ]; int i; int idim; int lbnd[ NDF__MXDIM ]; int ncomp; int ndim; int nentry; int ntime = 0; int prim; int there; int ubnd[ NDF__MXDIM ]; /* Check the inherited status */ if( *status != SAI__OK ) return; /* Get the shape of the NDF. */ ndfBound( indf, 3, lbnd, ubnd, &ndim, status ); /* Get a locator to the named extension. */ ndfXloc( indf, xname, "READ", &xloc, status ); /* Select the name of the prototype component that determines the length of the arrays to be copied. */ if( !strcmp( xname, "JCMTSTATE" ) ) { key = "RTS_NUM"; } else if( !strcmp( xname, "ACSIS" ) ) { key = "TSYS"; } else { cloc = NULL; msgSetc( "X", xname ); errRep( "", "smf_ext2km: Unknown extension specified - '^X'. " "Must be 'JCMTSTATE' or 'ACSIS'.", status ); } /* Check the prorotype component is present in the extension. Report an error if not. */ datThere( xloc, key, &there, status ); if( !there && *status == SAI__OK ) { msgSetc( "C", key ); msgSetc( "X", xname ); errRep( "", "smf_ext2km: Component '^C' not found in extension " "^X", status ); /* If it is present, get the length of its last pixel axis. This is the length of the arrays to be copied. */ } else { datFind( xloc, key, &cloc, status ); datShape( cloc, NDF__MXDIM, dim, &ndim, status ); ntime = dim[ ndim - 1 ]; datAnnul( &cloc, status ); } /* First deal with mode 1... */ /* ========================= */ if( mode == 1 ) { /* Loop round every component in the extension. */ datNcomp( xloc, &ncomp, status ); for( i = 1; i <= ncomp; i++ ) { datIndex( xloc, i, &cloc, status ); /* Check the component has primitive values. */ datPrim( cloc, &prim, status ); if( prim ) { /* Get the shape of the component. */ datShape( cloc, NDF__MXDIM, dim, &ndim, status ); /* Skip over this component if the length of its final axis is not equal to the expected number of time slices. */ if( ndim > 0 && dim[ ndim - 1 ] == ntime ) { /* Also skip if we are dealing with the ACSIS extension and the array has only 1 (or zero) axes. */ if( ndim > 1 || strcmp( xname, "ACSIS" ) ) { /* Cut a section from the HDS array so that it matches the pixel bounds of the NDF . */ for( idim = 0; idim < ndim - 1; idim++ ) { diml[ idim ] = 1; dimu[ idim ] = dim[ idim ]; } diml[ idim ] = lbnd[ 2 ]; dimu[ idim ] = ubnd[ 2 ]; datSlice( cloc, ndim, diml, dimu, &sloc, status ); /* Store the values as a new entry in "keymap". */ kpg1Hdsky( sloc, keymap, 2, 1, status ); /* Annul the locator for the slice. */ datAnnul( &sloc, status ); } } } datAnnul( &cloc, status ); } /* Now deal with mode 2... */ /* ========================= */ } else if( mode == 2 ) { /* Loop round every entry in the KeyMap. */ nentry = astMapSize( keymap ); for( i = 0; i < nentry; i++ ) { key = astMapKey( keymap, i ); /* See if the supplied extension has a component with the same name. */ datThere( xloc, key, &there, status ); /* If it did, check the component is primitive. */ if( there && *status == SAI__OK ) { datFind( xloc, key, &cloc, status ); datPrim( cloc, &prim, status ); if( prim ) { /* Check the final axis of the primitive array has a length equal to the expected number of time slices. */ datShape( cloc, NDF__MXDIM, dim, &ndim, status ); if( ndim > 0 && dim[ ndim - 1 ] == ntime ) { /* Also skip if we are dealing with the ACSIS extension and the array has only 1 (or zero) axes. */ if( ndim > 1 || strcmp( xname, "ACSIS" ) ) { /* Cut a section from the HDS array so that it matches the pixel bounds of the NDF . */ for( idim = 0; idim < ndim - 1; idim++ ) { diml[ idim ] = 1; dimu[ idim ] = dim[ idim ]; } diml[ idim ] = lbnd[ 2 ]; dimu[ idim ] = ubnd[ 2 ]; datSlice( cloc, ndim, diml, dimu, &sloc, status ); /* Append the values to the end of the existing KeyMap entry. */ kpg1Hdsky( sloc, keymap, 1, 3, status ); /* Annul the locator for the slice. */ datAnnul( &sloc, status ); } } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSetc( "X", xname ); msgSetc( "K", key ); ndfMsg( "F", indf ); errRep( "", "The ^X.^K array has an unexpected shape in " "\"^F\".", status ); } } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSetc( "X", xname ); msgSetc( "K", key ); ndfMsg( "F", indf ); errRep( "", "The ^X.^K array has an unexpected data type in " "\"^F\".", status ); } datAnnul( &cloc, status ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSetc( "X", xname ); msgSetc( "K", key ); ndfMsg( "F", indf ); errRep( "", "The ^X.^K array is missing in \"^F\".", status ); } } /* Now tidy up. */ /* ============ */ /* Report an error if the "mode" value was illegal. */ } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "MODE", mode ); errRep( "", "smf_ext2km: Illegal value (^MODE) supplied for " "argument MODE (programming error).", status ); } /* Free resources. */ datAnnul( &xloc, status ); }
static AstTable *ReadNextTable( FILE *fd, const char *fname, int *iline, int *status ) { /* * Name: * ReadOneTable * Purpose: * Reads a single Table from a text file. * Description: * This function reads text from the supplied file descriptor until it * reaches the end of file or encounters an end-of-table marker (a line * consisting just of two or more minus signs with no leading spaces). * It creates an AstTable from the text and returns a pointer to it. * Arguments: * fd * The file descriptor. * fname * The file name - used for error messages. * iline * Pointer to an int holding the number of lines read from the * file so far. Updated on exit to include the lines read by the * invocation of this function. * status * Pointer to the global status variable. * Returned Value: * A pointer to the Table read from the file, or NULL if an error occurs. */ /* Local Variables: */ AstTable *result; AstTable *subtable; char **cols; char **words; char *last_com; char *line; char *p; char *tname; char key[ 200 ]; const char *cval; const char *oldname; const char *newname; double dval; int *types; int blank; int c; int com; int eot; int first; int icol; int irow; int ival; int iword; int line_len; int max_line_len; int more; int nc; int ncol; int nrow; int nword; int skip; size_t len; /* Initialise */ result = NULL; /* Check the inherited status. */ if( *status != SAI__OK ) return result; /* Create an empty Table. */ result = astTable( " " ); /* Allocate a buffer for one one line of text. This will be increased in size as required. */ max_line_len = 80; line = astMalloc( max_line_len*sizeof( *line ) ); /* Read each line of text from the file. */ eot = 0; skip = 1; line_len = 0; more = 1; last_com = NULL; cols = NULL; first = 1; irow = 0; types = NULL; while( more && *status == SAI__OK ) { (*iline)++; line_len = 0; /* Loop reading characters from the file until a newline or the end of file is reached. */ while( ( c = fgetc( fd ) ) != EOF && c != '\n' ) { /* Increment the current line length, and double the size of the line buffer if it is full. */ if( ++line_len >= max_line_len ) { max_line_len *= 2; line = astRealloc( line, max_line_len*sizeof( *line ) ); if( *status != SAI__OK ) break; } /* Store the character. Ignore leading white space. */ if( skip ) { if( ! isspace( c ) ) { line[ line_len - 1 ] = c; skip = 0; } else { line_len--; } } else { line[ line_len - 1 ] = c; } } /* If the end-of-file was reached indicate that we should leave the main loop after processing the current line. */ if( c == EOF ) more = 0; /* Terminate the line. */ line[ line_len ] = 0; /* Terminate it again to exclude trailing white space. */ line[ astChrLen( line ) ] = 0; /* Assume the line is a blank non-comment, and store a pointer to the first character to use. */ blank = 1; com = 0; p = line; /* Skip blank lines. */ if( line[ 0 ] ) { /* If the line starts with a comment character... */ if( line[ 0 ] == '#' || line[ 0 ] == '!' ) { com = 1; /* Get a pointer to the first non-space/tab character after the comment character. */ p = line + 1; while( *p == ' ' || *p == '\t' ) p++; /* Note if it is blank. */ if( *p ) blank = 0; /* If it is not a comment line, then the line is not blank. */ } else { blank = 0; /* See if it is the end-of-table marker - a line containing just two or more minus signs with no leading spaces. */ eot = ( strspn( line, "-" ) > 1 ); } } /* Skip blank lines, whether comment or not. */ if( ! blank ) { /* First handle comment lines. */ if( com ) { /* Does it look like a parameter assignment... */ words = astChrSplitRE( p, "^\\s*(\\w+)\\s*=\\s*(.*)$", &nword, NULL ); if( words ) { /* Add a parameter declaration to the Table. */ astAddParameter( result, words[ 0 ] ); /* Store the parameter value, using an appropriate data type. */ len = strlen( words[ 1 ] ); if( nc = 0, ( 1 == astSscanf( words[ 1 ], "%d%n", &ival, &nc ) ) && ( nc >= len ) ) { astMapPut0I( result, words[ 0 ], ival, NULL ); } else if( nc = 0, ( 1 == astSscanf( words[ 1 ], "%lg%n", &dval, &nc ) ) && ( nc >= len ) ) { astMapPut0D( result, words[ 0 ], dval, NULL ); } else { astMapPut0C( result, words[ 0 ], words[ 1 ], NULL ); } /* Free the words returned by astChrSplitRE. */ for( iword = 0; iword < nword; iword++ ) { words[ iword ] = astFree( words[ iword ] ); } words = astFree( words ); /* If it does not look like a parameter assignment... */ } else { /* Save a copy of it in case it turns out to be the last non-blank comment line before the first row of data values (in which case it should contain the column names). */ last_com = astStore( last_com, p, strlen( p ) + 1 ); } /* If the line is not a comment see if it is an end of table marker. If so indicate that we should leave the loop. */ } else if( eot ) { more = 0; /* If the line is not a comment or an end of table marker ... */ } else { /* Get the words from the row. */ words = astChrSplit( p, &nword ); /* If this is the first non-blank non-comment line, get the column names from the previous non-blank comment line. */ if( first ) { if( last_com ) { first = 0; cols = astChrSplit( last_com, &ncol ); /* Create an array to hold the data type for each colum, and initialise them to "integer". */ types = astMalloc( ncol*sizeof( int ) ) ; for( iword = 0; iword < nword && astOK; iword++ ) { if( iword < ncol ) { types[ iword ] = AST__INTTYPE; /* The columns are stored initially using interim names which have "T_" prepended to the names given in the file. */ tname = NULL; nc = 0; tname = astAppendString( tname, &nc, "T_" ); tname = astAppendString( tname, &nc, cols[ iword ] ); astFree( cols[ iword ] ); cols[ iword ] = tname; /* Create the column definition within the returned Table. We store them initially as strings and then convert to the appropriate column data type later (once all rows have been read and the the data types are known). */ astAddColumn( result, cols[ iword ], AST__STRINGTYPE, 0, NULL, " " ); } } } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSetc( "F", fname ); errRep( " ", "No column headers found in file ^F.", status ); } } /* Report an error if the line has the wrong number of values. */ if( nword != ncol ) { if( *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "N", nword ); msgSeti( "I", (*iline) ); msgSeti( "M", ncol ); msgSetc( "F", fname ); errRep( " ", "Wrong number of values (^N) at line ^I in " "file ^F (should be ^M).", status ); } /* Otherwise increment the number of rows read. */ } else { irow++; /* Store each string value in the table, excluding "null" strings. Also check the data type of each string an dupdate the column data types if necessary. */ for( iword = 0; iword < nword && *status == SAI__OK; iword++ ) { if( strcmp( words[ iword ], "null" ) ) { sprintf( key, "%s(%d)", cols[ iword ], irow ); astMapPut0C( result, key, words[ iword ], NULL ); /* If the column is currently thought to hold integers, check that the current word looks like an integer. If not, down-grade the column type to double. */ len = strlen( words[ iword ] ); if( types[ iword ] == AST__INTTYPE ) { if( nc = 0, ( 1 != astSscanf( words[ iword ], "%d%n", &ival, &nc ) ) || ( nc < len ) ) { types[ iword ] = AST__DOUBLETYPE; } } /* If the column is currently thought to hold doubles, check that the current word looks like an doubler. If not, down-grade the column type to string. */ if( types[ iword ] == AST__DOUBLETYPE ) { if( nc = 0, ( 1 != astSscanf( words[ iword ], "%lg%n", &dval, &nc ) ) || ( nc < len ) ) { types[ iword ] = AST__STRINGTYPE; } } } } } /* Free the words returned by astChrSplit. */ for( iword = 0; iword < nword; iword++ ) { words[ iword ] = astFree( words[ iword ] ); } words = astFree( words ); } } } /* The entire file has now been read, and a Table created in which every column holds strings. We also have flags indicating whether the values in each column are all integers or doubles. Modify the type of the column within the Table to match these flags. */ nrow = astGetI( result, "Nrow" ); for( icol = 0; icol < ncol && *status == SAI__OK; icol++ ) { /* The column will be re-named from "T_<name>" to "<name>". */ oldname = cols[ icol ]; newname = oldname + 2; /* First convert string columns to integer columns if all the values in the column look like integers. */ if( types[ icol ] == AST__INTTYPE ) { /* Create the new column */ astAddColumn( result, newname, AST__INTTYPE, 0, NULL, " " ); /* Copy each cell of the current column, converting from string to integer. */ for( irow = 1; irow <= nrow; irow++ ) { sprintf( key, "%s(%d)", oldname, irow ); if( astMapGet0I( result, key, &ival ) ) { sprintf( key, "%s(%d)", newname, irow ); astMapPut0I( result, key, ival, NULL ); } } /* Now do double columns in the same way. */ } else if( types[ icol ] == AST__DOUBLETYPE ) { astAddColumn( result, newname, AST__DOUBLETYPE, 0, NULL, " " ); for( irow = 1; irow <= nrow; irow++ ) { sprintf( key, "%s(%d)", oldname, irow ); if( astMapGet0D( result, key, &dval ) ) { sprintf( key, "%s(%d)", newname, irow ); astMapPut0D( result, key, dval, NULL ); } } /* Copy string values without change. */ } else { astAddColumn( result, newname, AST__STRINGTYPE, 0, NULL, " " ); for( irow = 1; irow <= nrow; irow++ ) { sprintf( key, "%s(%d)", oldname, irow ); if( astMapGet0C( result, key, &cval ) ) { sprintf( key, "%s(%d)", newname, irow ); astMapPut0C( result, key, cval, NULL ); } } } /* Remove the old column. */ astRemoveColumn( result, oldname ); } /* Free resources. */ line = astFree( line ); last_com = astFree( last_com ); types = astFree( types ); if( cols ) { for( icol = 0; icol < ncol; icol++ ) { cols[ icol ] = astFree( cols[ icol ] ); } cols = astFree( cols ); } /* If the table ended with an end-of-table marker, there may be another Table in the file. Call this function recursively to read it. */ if( eot ) { subtable = ReadNextTable( fd, fname, iline, status ); /* Store the subtable as a table parameter in the returned table. */ if( subtable ) { astAddParameter( result, "SubTable" ); astMapPut0A( result, "SubTable", subtable, NULL ); /* The Table clones the pointer, so we must annull our local copy of it. */ subtable = astAnnul( subtable ); } } /* Return the Table pointer. */ return result; }
double *cupidCFLevels( AstKeyMap *config, double maxd, double mind, double rms, int *nlevels, int *status ){ /* *+ * Name: * cupidCFLevels * Purpose: * Get the contour levels for use by the ClumpFind algorithm. * Language: * Starlink C * Synopsis: * double *cupidCFLevels( AstKeyMap *config, double maxd, double mind, * double rms, int *nlevels, int *status ) * Description: * This function obtains a series of contour levels at which the * ClumpFind algorithm will search for peaks. * Parameters: * config * An AST KeyMap holding tuning parameters for the algorithm. * maxd * The maximum data value in the data array. * mind * The minimum data value in the data array. * rms * The RMS noise level in the data. * nlevels * Pointer to an int to receive the number of contour levels. * status * Pointer to the inherited status value. * Returned Value: * A pointer to a dynamically allocated array containing "*nlevels" * floating point contour values. It should be freed using astFree * when no longer needed. * Copyright: * 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 * {enter_new_authors_here} * History: * 7-DEC-2005 (DSB): * Original version. * 18-SEP-2007 (DSB): * Correct calculation of number of contour levels based on TLOW * and TDELTA values.Original version. Previously, the number of * contours was too small by 1. * 3-MAR-2011 (DSB): * More error checking. * {enter_further_changes_here} * Bugs: * {note_any_bugs_here} *- */ /* Local Variables: */ char name[ 10 ]; /* Name of "LEVELn" value */ double *ret; /* Pointer to returned array of contour levels */ double cdelta; /* Increment between contour levels */ double clevel; /* Contour level */ double clow; /* Lowest contour level */ int i; /* Contour index */ int ihi; /* Index of last unsorted contour */ int more; /* Levels not yet sorted? */ /* Initialise */ ret = NULL; *nlevels = 0; /* Abort if an error has already occurred. */ if( *status != SAI__OK ) return ret; /* If the supplied KeyMap contains a set of "LEVELn" values, use them as the contour levels. The first value is called "LEVEL1". */ i = 0; while( 1 ) { i++; sprintf( name, "LEVEL%d", i ); clevel = cupidConfigRMS( config, name, rms, VAL__BADD, status ); if( clevel == AST__BAD ) { i--; break; } else { ret = astGrow( ret, i, sizeof( double ) ); if( ret ) { ret[ i - 1 ] = clevel; } else { break; } } } /* If a set of LEVELn values was found, sort them into decreasing order and return the number. */ if( ret ) { *nlevels = i; ihi = *nlevels - 1; more = 1; while( more ) { more = 0; for( i = 0; i < ihi; i++ ) { if( ret[ i ] < ret[ i + 1 ] ){ clevel = ret[ i ]; ret[ i ] = ret[ i + 1 ]; ret[ i + 1 ] = clevel; more = 1; } } ihi--; } /* Otherwise, use contour levels at increments of DELTAT, starting at TLOW. */ } else { /* Get the lowest contour level using twice the RMS as the default. */ clow = cupidConfigRMS( config, "TLOW", rms, 2.0*rms, status ); /* Report an error if the lowest contour level is below the minimum value in the data array. */ if( clow < mind && *status == SAI__OK ) { *status = SAI__ERROR; msgSetd( "TLOW", clow ); msgSetd( "MIND", mind ); errRep( "CUPIDCFLEVELS_ERR1", "The supplied lowest contour level " "(Tlow=^TLOW) is below the minimum value in the data " "array (^MIND).", status ); /* Report an error if the lowest contour level is above the maximum value in the data array. */ } else if( clow >= maxd && *status == SAI__OK ) { *status = SAI__ERROR; msgSetd( "TLOW", clow ); msgSetd( "MAXD", maxd ); errRep( "CUPIDCFLEVELS_ERR2", "The supplied lowest contour level " "(Tlow=^TLOW) is above the maximum value in the data " "array (^MAXD).", status ); /* Otherwise, use 2*RMS as the default. */ } else { cdelta = 2.0*rms; /* Get the contour interval using the above default. */ cdelta = cupidConfigRMS( config, "DELTAT", rms, cdelta, status ); /* Report an error if it is negative or zero. */ if( cdelta <= 0.0 && *status == SAI__OK ) { *status = SAI__ERROR; msgSetd( "CD", cdelta ); errRep( "CUPIDCFLEVELS_ERR3", "The supplied contour interval " "(DeltaT=^CD) is zero or negative.", status ); /* Otherwise, find the number of levels needed for this deltat. */ } else if( *status == SAI__OK ) { *nlevels = (int) ( ( maxd - clow )/cdelta ) + 1; /* Check the number of levels is reasonable. */ if( ( *nlevels < 2 || *nlevels > 10000000 ) ) { *status = CUPID__CFCNT; msgSetd( "T", clow ); msgSetd( "D", cdelta ); msgSetd( "R", rms ); msgSeti( "NL", *nlevels ); errRep( "CUPIDCFLEVELS_ERR4", "The supplied values for " "parameters TLOW (^T), DELTAT (^D) and RMS (^R) " "would result in an unusable number (^NL) of " "contours.", status ); /* If so, allocate the array and fill it with the appropriate contour levels. */ } else { ret = astMalloc( sizeof( double )*(*nlevels) ); if( ret ) { clevel = clow; for( i = *nlevels - 1; i >= 0; i-- ) { ret[ i ]= clevel; clevel += cdelta; } } } } } } /* Return no levels if an error occurred. */ if( *status != SAI__OK ) ret = astFree( ret ); if( !ret ) *nlevels = 0; /* Return the array of contour levels. */ return ret; }
void smf_flag_spikes( ThrWorkForce *wf, smfData *data, smf_qual_t mask, double thresh, size_t box, size_t *nflagged, int *status ){ /* Local Variables */ int i; /* Loop counter */ smfFlagSpikesData *job_data=NULL;/* Array of job data for each thread */ dim_t nbolo; /* Number of bolometers */ dim_t ntime; /* Number of time-slices */ double *dat = NULL; /* Pointer to bolo data */ smfFlagSpikesData *pdata=NULL;/* Pointer to job data */ int nw; /* Number of worker threads */ size_t bstride; /* Vector stride between bolometer samples */ size_t nflag; /* Number of samples flagged */ size_t tstride; /* Vector stride between time samples */ smf_qual_t *qua = NULL; /* Pointer to quality flags */ size_t step; /* step size for dividing up work */ int njobs=0; /* Number of jobs to be processed */ /* Check inherited status. Also return immediately if no spike flagging is to be performed. */ if( *status != SAI__OK || thresh == 0.0 ) return; /* How many threads do we get to play with */ nw = wf ? wf->nworker : 1; /* Check we have double precision data. */ smf_dtype_check_fatal( data, NULL, SMF__DOUBLE, status ); /* Get a pointer to the quality array to use. */ qua = smf_select_qualpntr( data, NULL, status ); /* Report an error if we have no quality array. */ if( !qua && *status == SAI__OK ) { *status = SAI__ERROR; errRep( " ", FUNC_NAME ": No valid QUALITY array was provided", status ); } /* Get a pointer to the data array to use. Report an error if we have no data array. */ dat = data->pntr[0]; if( !dat && *status == SAI__OK ) { *status = SAI__ERROR; errRep( " ", FUNC_NAME ": smfData does not contain a DATA component", status); } /* Check the supplied thresh value is valid. */ if( thresh <= 0 && *status == SAI__OK ) { *status = SAI__ERROR; msgSetd( "THRESH", thresh ); errRep( " ", FUNC_NAME ": Can't find spikes: thresh=^THRESH, must be > 0", status); } /* Check the supplied box value is valid. */ if( box <= 2 && *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "BOX", box ); errRep( " ", FUNC_NAME ": Can't find spikes: box=^BOX, must be > 2", status); } /* Obtain data dimensions, and the stride between adjacent elements on each axis (bolometer and time). Use the existing data order to avoid the cost of re-ordering. */ smf_get_dims( data, NULL, NULL, &nbolo, &ntime, NULL, &bstride, &tstride, status ); /* Check we have room for at least 3 boxes along the time axis. */ if( 3*box > ntime && *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "BOX", box ); msgSeti( "MAX", ntime/3 - 1 ); errRep( " ", FUNC_NAME ": Can't find spikes: box=^BOX is too large, " " must be < ^MAX.", status); } /* Set up the job data */ if( nw > (int) nbolo ) { step = 1; } else { step = nbolo/nw; if( !step ) { step = 1; } } job_data = astCalloc( nw, sizeof(*job_data) ); for( i=0; (*status==SAI__OK)&&i<nw; i++ ) { pdata = job_data + i; pdata->b1 = i*step; pdata->b2 = (i+1)*step-1; /* if b1 is greater than the number of bolometers, we've run out of jobs... */ if( pdata->b1 >= nbolo ) { break; } /* increase the jobs counter */ njobs++; /* Ensure that the last thread picks up any left-over bolometers */ if( (i==(nw-1)) && (pdata->b1<(nbolo-1)) ) { pdata->b2=nbolo-1; } pdata->ijob = -1; /* Flag job as ready to start */ pdata->box = box; pdata->bstride = bstride; pdata->dat = dat; pdata->mask = mask; pdata->thresh = thresh; pdata->nbolo = nbolo; pdata->ntime = ntime; pdata->qua = qua; pdata->tstride = tstride; } /* Submit jobs to find spikes in each block of bolos */ thrBeginJobContext( wf, status ); for( i=0; (*status==SAI__OK)&&i<njobs; i++ ) { pdata = job_data + i; pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata, smfFlagSpikesPar, 0, NULL, status ); } /* Wait until all of the submitted jobs have completed */ thrWait( wf, status ); thrEndJobContext( wf, status ); /* Count flagged samples from all of the jobs and free resources */ nflag=0; if( job_data ) { for( i=0; i<njobs; i++ ) { pdata = job_data + i; nflag += pdata->nflag; } job_data = astFree( job_data ); } /* Return the number of flagged samples, if requested */ if( nflagged ) *nflagged = nflag; }
void smf_mapbounds_approx( Grp *igrp, size_t index, char *system, int *lbnd_out, int *ubnd_out, AstFrameSet **outframeset, int *moving, int *status ) { /* Local variables */ smfData *data = NULL; /* pointer to SCUBA2 data struct */ int dxpix; /* Map X offset in pixels */ int dypix; /* Map Y offset in pixels */ smfFile *file = NULL; /* SCUBA2 data file information */ AstFitsChan *fitschan = NULL;/* Fits channels to construct WCS header */ AstFrameSet *fs = NULL; /* A general purpose FrameSet pointer */ smfHead *hdr = NULL; /* Pointer to data header this time slice */ double hghtbox; /* Map height in arcsec */ int hghtpix; /* RA-Dec map height in pixels */ int i; /* loop counter */ dim_t k; /* Loop counter */ double maphght = 0.0; /* Map height in radians */ double mappa = 0.0; /* Map position angle in radians */ double mapwdth = 0.0; /* Map width in radians */ double mapx; /* Map X offset in radians */ double mapy; /* Map Y offset in radians */ double par[7]; /* Projection parameters */ double pixsize = 0.0; /* Requested pixel size */ double shift[ 2 ]; /* Shifts from PIXEL to GRID coords */ AstMapping *sky2map = NULL; /* Mapping celestial->map coordinates */ AstSkyFrame *skyframe = NULL;/* Output SkyFrame */ AstFrame *skyin = NULL; /* Sky Frame in input FrameSet */ double skyref[ 2 ]; /* Values for output SkyFrame SkyRef attribute */ AstFrameSet *swcsin = NULL; /* FrameSet describing input WCS */ int temp; /* Temporary variable */ double wdthbox; /* Map width in arcsec */ int wdthpix; /* RA-Dec map width in pixels */ double x_array_corners[4]; /* X-Indices for corner bolos in array */ double y_array_corners[4]; /* Y-Indices for corner pixels in array */ /* Main routine */ if (*status != SAI__OK) return; /* Begin an AST context to ensure that all AST objects are annuled before returning to caller */ astBegin; /* Initialize output frameset pointer to NULL */ *outframeset = NULL; for( i = 0; i < 7; i++ ) par[ i ] = AST__BAD; /* Read data from the given input file in the group - note index should be 1 as we use the first file in the Grp to define the map bounds */ smf_open_file( igrp, index, "READ", SMF__NOCREATE_DATA, &data, status ); /* Simply abort if it is not a scan */ if (*status == SAI__OK && data->hdr->obsmode != SMF__OBS_SCAN) { *status = SAI__ERROR; errRep(" ", "Can not call smf_mapbounds_approx with non-scan observation" " (possible programming error)", status); goto CLEANUP; } /* Retrieve file name for use feedback */ file = data->file; smf_smfFile_msg( file, "FILE", 1, "<unknown>" ); if( *status == SAI__OK ) { msgOutif(MSG__VERB, " ", "SMF_MAPBOUNDS_APPROX: Processing ^FILE", status); } else { errRep( "smf_mapbounds_approx", "Couldn't open input file, ^FILE", status ); } /* Check that the data dimensions are 3 (for time ordered data) */ if( *status == SAI__OK ) { if( data->ndims != 3 ) { smf_smfFile_msg( file, "FILE", 1, "<unknown>" ); msgSeti("THEDIMS", data->ndims); *status = SAI__ERROR; errRep("smf_mapbounds_approx", "^FILE data has ^THEDIMS dimensions, should be 3.", status); } } /* Construct the WCS for the first time slice in this file */ smf_tslice_ast( data, 1, 1, NO_FTS, status); /* Retrieve header for later constructing output WCS */ if( *status == SAI__OK) { hdr = data->hdr; swcsin = hdr->wcs; /* Calculate default pixel size */ pixsize = smf_calc_telres( hdr->fitshdr, status ); /* Get the user defined pixel size - we trust that smf_get_projpar will also read PIXSIZE and get the same answer. We pre-fill par[] to allow PIXSIZE=! to accept the dynamic default in both places.*/ parGdr0d( "PIXSIZE", pixsize, 0, 60, 1, &pixsize, status ); par[4] = pixsize*AST__DD2R/3600.0; par[5] = par[4]; /* Retrieve input SkyFrame */ skyin = astGetFrame( swcsin, AST__CURRENT ); /* Retrieve map height and width from header - will be undef for non-scan so set up defaults first. */ mapwdth = 0.0; maphght = 0.0; smf_getfitsd( hdr, "MAP_WDTH", &mapwdth, status ); smf_getfitsd( hdr, "MAP_HGHT", &maphght, status ); /* Make an approximation if map height and width are not set - note that this should ONLY apply for non-scan mode data */ if ( !mapwdth || !maphght ) { if (*status == SAI__OK) { *status = SAI__ERROR; errRep(" ", "MAP_WDTH and MAP_HGHT must be > 0", status); goto CLEANUP; } } mapx = 0.0; /* Used if the FITS keyword values are undefed */ mapy = 0.0; smf_getfitsd( hdr, "MAP_X", &mapx, status ); smf_getfitsd( hdr, "MAP_Y", &mapy, status ); /* Convert map Position Angle to radians */ mappa = 0.0; smf_fits_getD( hdr, "MAP_PA", &mappa, status ); mappa *= AST__DD2R; /* Calculate size of output map in pixels */ /* Note: this works for the simulator... */ wdthbox = mapwdth*fabs(cos(mappa)) + maphght*fabs(sin(mappa)); hghtbox = maphght*fabs(cos(mappa)) + mapwdth*fabs(sin(mappa)); wdthpix = (int) ( wdthbox / pixsize); hghtpix = (int) ( wdthbox / pixsize); dxpix = (int) (mapx / pixsize); dypix = (int) (mapy / pixsize); /* Get the offsets for each corner of the array */ temp = (wdthpix - 1) / 2; x_array_corners[0] = dxpix - temp; x_array_corners[1] = dxpix - temp; x_array_corners[2] = dxpix + temp; x_array_corners[3] = dxpix + temp; temp = (hghtpix - 1) / 2; y_array_corners[0] = dypix - temp; y_array_corners[1] = dypix + temp; y_array_corners[2] = dypix - temp; y_array_corners[3] = dypix + temp; lbnd_out[0] = x_array_corners[0]; ubnd_out[0] = x_array_corners[0]; lbnd_out[1] = y_array_corners[0]; ubnd_out[1] = y_array_corners[0]; /* Update min/max */ for( k=0; k<4; k++ ) { if( x_array_corners[k] < lbnd_out[0] ) lbnd_out[0] = x_array_corners[k]; if( y_array_corners[k] < lbnd_out[1] ) lbnd_out[1] = y_array_corners[k]; if( x_array_corners[k] > ubnd_out[0] ) ubnd_out[0] = x_array_corners[k]; if( y_array_corners[k] > ubnd_out[1] ) ubnd_out[1] = y_array_corners[k]; } } else { goto CLEANUP; } /* Now create the output FrameSet. */ smf_calc_skyframe( skyin, system, hdr, 0, &skyframe, skyref, moving, status ); /* Get the orientation of the map vertical within the output celestial coordinate system. This is derived form the MAP_PA FITS header, which gives the orientation of the map vertical within the tracking system. */ mappa = smf_calc_mappa( hdr, system, skyin, status ); /* Calculate the projection parameters. We do not enable autogrid determination for SCUBA-2 so we do not need to obtain all the data before calculating projection parameters. */ smf_get_projpar( skyframe, skyref, *moving, 0, 0, NULL, 0, mappa, par, NULL, NULL, status ); /* Now populate a FitsChan with FITS-WCS headers describing the required tan plane projection. The longitude and latitude axis types are set to either (RA,Dec) or (AZ,EL) to get the correct handedness. */ fitschan = astFitsChan ( NULL, NULL, " " ); smf_makefitschan( astGetC( skyframe, "System"), &(par[0]), &(par[2]), &(par[4]), par[6], fitschan, status ); astClear( fitschan, "Card" ); fs = astRead( fitschan ); /* Extract the output PIXEL->SKY Mapping - note this is will be inverted later to create the sk2map mapping */ sky2map = astGetMapping( fs, AST__BASE, AST__CURRENT ); /* Create the output FrameSet */ *outframeset = astFrameSet( astFrame(2, "Domain=GRID"), " " ); /* Now add the SkyFrame to it */ astAddFrame( *outframeset, AST__BASE, sky2map, skyframe ); /* Apply a ShiftMap to the output FrameSet to re-align the GRID coordinates */ shift[0] = -lbnd_out[0]; shift[1] = -lbnd_out[1]; astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) ); astExport( *outframeset ); /* Report the pixel bounds of the cube. */ if( *status == SAI__OK ) { msgOutif( MSG__NORM, " ", " ", status ); msgSeti( "XL", lbnd_out[ 0 ] ); msgSeti( "YL", lbnd_out[ 1 ] ); msgSeti( "XU", ubnd_out[ 0 ] ); msgSeti( "YU", ubnd_out[ 1 ] ); msgOutif( MSG__NORM, " ", " Output map pixel bounds: ( ^XL:^XU, ^YL:^YU )", status ); } /* Change the pixel bounds to be consistent with the new CRPIX */ ubnd_out[0] -= lbnd_out[0]-1; lbnd_out[0] = 1; ubnd_out[1] -= lbnd_out[1]-1; lbnd_out[1] = 1; /* Clean Up */ CLEANUP: if (*status != SAI__OK) { errRep(FUNC_NAME, "Unable to determine map bounds", status); } if( data != NULL ) smf_close_file( &data, status); astEnd; }
HDSLoc *cupidReinhold( int type, int ndim, int *slbnd, int *subnd, void *ipd, double *ipv, double rms, AstKeyMap *config, int velax, double beamcorr[ 3 ], int *status ){ /* *+ * Name: * cupidReinhold * Purpose: * Identify clumps of emission within a 1, 2 or 3 dimensional NDF using * the REINHOLD algorithm. * Language: * Starlink C * Synopsis: * HDSLoc *cupidReinhold( int type, int ndim, int *slbnd, int *subnd, * void *ipd, double *ipv, double rms, * AstKeyMap *config, int velax, * double beamcorr[ 3 ], int *status ) * Description: * This function identifies clumps within a 1, 2 or 3 dimensional data * array using the REINHOLD algorithm, developed by Kim Reinhold at * JAC. This algorithm identifies the boundaries between clumps by * looking for minima in 1D sections through the data. No a priori clump * profile is assumed. In this algorithm, clumps never overlap. * Parameters: * type * An integer identifying the data type of the array values pointed to * by "ipd". Must be either CUPID__DOUBLE or CUPID__FLOAT (defined in * cupid.h). * ndim * The number of dimensions in the data array. Must be 2 or 3. * slbnd * Pointer to an array holding the lower pixel index bound of the * data array on each axis. * subnd * Pointer to an array holding the upper pixel index bound of the * data array on each axis. * ipd * Pointer to the data array. The elements should be stored in * Fortran order. The data type of this array is given by "itype". * ipv * Pointer to the input Variance array, or NULL if there is no Variance * array. The elements should be stored in Fortran order. The data * type of this array is "double". * rms * The default value for the global RMS error in the data array. * config * An AST KeyMap holding tuning parameters for the algorithm. * velax * The index of the velocity axis in the data array (if any). Only * used if "ndim" is 3. * beamcorr * An array in which is returned the FWHM (in pixels) describing the * instrumental smoothing along each pixel axis. The clump widths * stored in the output catalogue are reduced to correct for this * smoothing. * status * Pointer to the inherited status value. * Returned Value: * A locator for a new HDS object which is an array of NDF structures. * Each NDF will hold the data values associated with a single clump * and will be the smallest possible NDF that completely contains the * corresponding clump. Pixels not in the clump will be set bad. The * pixel origin is set to the same value as the supplied NDF. * Copyright: * Copyright (C) 2009 Science & Technology Facilities Council. * Copyright (C) 2006 Particle Physics & Astronomy Research Council. * All Rights Reserved. * Licence: * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA * 02110-1301, USA * Authors: * DSB: David S. Berry * TIMJ: Tim Jenness (JAC, Hawaii) * {enter_new_authors_here} * History: * 16-JAN-2006 (DSB): * Original version. * 14-JAN-2009 (TIMJ): * Use MERS for message filtering. * {enter_further_changes_here} * Bugs: * {note_any_bugs_here} *- */ /* Local Variables: */ AstKeyMap *rconfig; /* Configuration parameters for this algorithm */ HDSLoc *ret; /* Locator for the returned array of NDFs */ double *pd; /* Pointer to next element of data array */ double *peakvals; /* Pointer to array holding clump peak values */ double cathresh; /* Threshold for second cellular automata */ double flatslope; /* Minimum significant slope at edge of a peak */ double noise; /* Noise level */ double pv; /* Pixel value */ double thresh; /* Minimum peak value to be considered */ float *pf; /* Pointer to next element of data array */ int *clbnd; /* Array holding lower axis bounds of all clumps */ int *cubnd; /* Array holding upper axis bounds of all clumps */ int *igood; /* Pointer to array holding usable clump indices */ int *m1; /* Pointer to mask array */ int *m2; /* Pointer to mask array */ int *m3; /* Pointer to mask array */ int *mask2; /* Pointer to array marking out edge pixels */ int *mask; /* Pointer to array marking out edge pixels */ int *nrem; /* Pointer to array marking out edge pixels */ int *pa; /* Pointer to next element in the pixel assignment array */ int caiter; /* The number of CA iterations to perform */ int dims[3]; /* Pointer to array of array dimensions */ int el; /* Number of elements in array */ int fixiter; /* The number of CA iterations to perform */ int i; /* Loop count */ int ii; /* Loop count */ int ix; /* Grid index on 1st axis */ int iy; /* Grid index on 2nd axis */ int iz; /* Grid index on 3rd axis */ int j; /* Loop count */ int maxid; /* Largest id for any peak (smallest is zero) */ int minlen; /* Minimum size of a clump in pixels along one dimension*/ int minpix; /* Minimum total size of a clump in pixels */ int more; /* Continue looping?*/ int ngood; /* Number of good clumps */ int nsmall; /* Number of clumps that are too small */ int nthin; /* Number of clumps that span only a single pixel */ int peakval; /* Minimum value used to flag peaks */ int skip[3]; /* Pointer to array of axis skips */ /* Initialise */ ret = NULL; /* Abort if an error has already occurred. */ if( *status != SAI__OK ) return ret; /* Say which method is being used. */ msgBlankif( MSG__NORM, status ); msgOutif( MSG__NORM, "", "Reinhold:", status ); /* Get the AST KeyMap holding the configuration parameters for this algorithm. */ if( !astMapGet0A( config, "REINHOLD", (AstObject *) &rconfig ) ) { rconfig = astKeyMap( " " ); astMapPut0A( config, "REINHOLD", rconfig, " " ); } /* The configuration file can optionally omit the algorithm name. In this case the "config" KeyMap may contain values which should really be in the "rconfig" KeyMap. Add a copy of the "config" KeyMap into "rconfig" so that it can be searched for any value which cannot be found in the "rconfig" KeyMap. */ astMapPut0A( rconfig, CUPID__CONFIG, astCopy( config ), NULL ); /* Return the instrumental smoothing FWHMs */ beamcorr[ 0 ] = cupidConfigD( rconfig, "FWHMBEAM", 2.0, status ); beamcorr[ 1 ] = beamcorr[ 0 ]; if( ndim == 3 ) { beamcorr[ 2 ] = beamcorr[ 0 ]; beamcorr[ velax ]= cupidConfigD( rconfig, "VELORES", 2.0, status ); } /* Find the size of each dimension of the data array, and the total number of elements in the array, and the skip in 1D vector index needed to move by pixel along an axis. We use the memory management functions of the AST library since they provide greater security and functionality than direct use of malloc, etc. */ el = 1; for( i = 0; i < ndim; i++ ) { dims[ i ] = subnd[ i ] - slbnd[ i ] + 1; el *= dims[ i ]; skip[ i ] = ( i == 0 ) ? 1 : skip[ i - 1 ]*dims[ i - 1 ]; } for( ; i < 3; i++ ) { dims[ i ] = 1; skip[ i ] = 0; } /* Get various configuration parameters. */ rms = cupidConfigD( rconfig, "RMS", rms, status ); minlen = cupidConfigI( rconfig, "MINLEN", 4, status ); noise = cupidConfigRMS( rconfig, "NOISE", rms, 2*rms, status ); thresh = cupidConfigRMS( rconfig, "THRESH", rms, noise + 2*rms, status ); flatslope = cupidConfigRMS( rconfig, "FLATSLOPE", rms, rms, status ); cathresh = pow( 3, ndim ) - 1.0; cathresh = cupidConfigI( rconfig, "CATHRESH", (int) cathresh, status ); caiter = cupidConfigI( rconfig, "CAITERATIONS", 1, status ); fixiter = cupidConfigI( rconfig, "FIXCLUMPSITERATIONS", 1, status ); /* Get the minimum allowed number of pixels in a clump. */ minpix = cupidDefMinPix( ndim, beamcorr, noise, thresh, status ); minpix = cupidConfigI( rconfig, "MINPIX", minpix, status ); /* Convert CATHRESH from a number of pixels to a fraction. */ cathresh = cathresh/pow( 3, ndim ); if( cathresh > 0.98 ) cathresh = 0.98; if( cathresh < 0 ) cathresh = 0.0; cathresh += 0.01; /* Get a mask which is the same size and shape as the data array and which holds CUPID__KEDGE at every pixel thought to be on the edge of a clump. This is done by scanning the data cube using sets of parallel lines in different directions. Peaks are searched for in each line, and then the edges found by following the curve down from each peak until the gradient becomes zero or positive, or until the data value drops below a threshold value. Pixels which correspond to peaks in the data cube are flagged with the value greater than or equal to the returned "*peakval" value. All other pixels are set to some other value (which will usually be CUPID__KBACK but will be something else at positions of peaks which were not peaks in all scan directions). */ msgOutif( MSG__DEBUG, "", "Finding clump edges...", status ); mask = cupidRInitEdges( type, ipd, el, ndim, dims, skip, minlen, thresh, noise, rms, flatslope, &peakval, status ); /* Dilate the edge regions using a cellular automata. This creates a new mask array in which a pixel is marked as an edge pixel if any of its neighbours are marked as edge pixels in the mask array created above. */ msgOutif( MSG__DEBUG, "", "Dilating clump edges...", status ); mask2 = cupidRCA( mask, NULL, el, dims, skip, 0.0, peakval, CUPID__KEDGE, CUPID__KBACK, 0, status ); /* Erode the edge regions using a second cellular automata. This over-writes the original mask array so that a pixel is marked as an edge pixel if a fraction greater than "cathresh" of neighbouring pixels are marked as edge pixels in "mask2". We loop doing this "CAiteration" times. */ if( caiter > 0 ) msgOutif( MSG__DEBUG,"", "Eroding clump edges...", status ); m1 = mask; m2 = mask2; for( i = 0; i < caiter; i++ ) { m1 = cupidRCA( m2, m1, el, dims, skip, cathresh, peakval, CUPID__KEDGE, CUPID__KBACK, 0, status ); m3 = m1; m1 = m2; m2 = m3; } /* Fill the volume around each peak with integer values which indicate which peak they are close to. All the pixels around one peak form one clump. */ msgOutif( MSG__DEBUG, "", "Filling clumps...", status ); maxid = cupidRFillClumps( m2, m1, el, ndim, skip, dims, peakval, status ); /* Abort if no clumps found. */ if( maxid < 0 ) { msgOutif( MSG__NORM, "", "No usable clumps found.", status ); msgBlankif( MSG__NORM, status ); goto L10; } /* Smooth the boundaries between the clumps. This cellular automata replaces each output pixel by the most commonly occuring value within a 3x3x3 cube of input pixels centred on the output pixel. Put the smoothed results back into the supplied "m1" array. */ if( fixiter >0 ) msgOutif( MSG__DEBUG, "", "Smoothing clump boundaries...", status ); for( i = 0; i < fixiter; i++ ) { m2 = cupidRCA2( m1, m2, el, dims, skip, status ); m3 = m2; m2 = m1; m1 = m3; } /* Allocate an array used to store the number of pixels remaining in each clump. */ nrem = astMalloc( sizeof( int )*( maxid + 1 ) ); /* Allocate an array used to store the peak value in every clump. */ peakvals = astMalloc( sizeof( double )*( maxid + 1 ) ); /* Determine the bounding box of every clump. First allocate memory to hold the bounding boxes. */ clbnd = astMalloc( sizeof( int )*( maxid + 1 )*3 ); cubnd = astMalloc( sizeof( int )*( maxid + 1 )*3 ); igood = astMalloc( sizeof( int )*( maxid + 1 ) ); if( igood ) { /* Initialise a list to hold zero for every clump id. These values are used to count the number of pixels remaining in each clump. Also initialise the peak values to a very negative value. */ for( i = 0; i <= maxid; i++ ) { nrem[ i ] = 0; peakvals[ i ] = VAL__MIND; } /* Initialise the bounding boxes. */ for( i = 0; i < 3*( maxid + 1 ); i++ ) { clbnd[ i ] = VAL__MAXI; cubnd[ i ] = VAL__MINI; } /* Loop round every pixel in the final pixel assignment array. */ if( type == CUPID__DOUBLE ) { pd = (double *) ipd; pf = NULL; } else { pf = (float *) ipd; pd = NULL; } pa = m1; for( iz = 1; iz <= dims[ 2 ]; iz++ ){ for( iy = 1; iy <= dims[ 1 ]; iy++ ){ for( ix = 1; ix <= dims[ 0 ]; ix++, pa++ ){ /* Get the data value at this pixel */ if( type == CUPID__DOUBLE ) { pv = *(pd++); } else { pv = (double) *(pf++); } /* Skip pixels which are not in any clump. */ if( *pa >= 0 ) { /* Increment the number of pixels in this clump. */ ++( nrem[ *pa ] ); /* If this pixel value is larger than the current peak value for this clump, record it. */ if( pv > (double) peakvals[ *pa ] ) peakvals[ *pa ] = pv; /* Get the index within the clbnd and cubnd arrays of the current bounds on the x axis for this clump. */ i = 3*( *pa ); /* Update the bounds for the x axis, then increment to get the index of the y axis bounds. */ if( ix < clbnd[ i ] ) clbnd[ i ] = ix; if( ix > cubnd[ i ] ) cubnd[ i ] = ix; i++; /* Update the bounds for the y axis, then increment to get the index of the z axis bounds. */ if( iy < clbnd[ i ] ) clbnd[ i ] = iy; if( iy > cubnd[ i ] ) cubnd[ i ] = iy; i++; /* Update the bounds for the z axis. */ if( iz < clbnd[ i ] ) clbnd[ i ] = iz; if( iz > cubnd[ i ] ) cubnd[ i ] = iz; } } } } /* Loop round counting the clumps which are too small or too low. Put the indices of usable clumps into another array. */ nsmall = 0; ngood = 0; nthin = 0; for( i = 0; i <= maxid; i++ ) { j = 3*i; if( nrem[ i ] <= minpix ) { nsmall++; } else if( clbnd[ j ] == cubnd[ j ] || ( clbnd[ j + 1 ] == cubnd[ j + 1 ] && ndim > 1 ) || ( clbnd[ j + 2 ] == cubnd[ j + 2 ] && ndim > 2 ) ) { nthin++; } else { igood[ ngood++ ] = i; } } if( ngood == 0 ) msgOutif( MSG__NORM,"", "No usable clumps found.", status ); if( nsmall == 1 ){ msgOutif( MSG__NORM, "", "One clump rejected because it contains too few pixels.", status ); } else if( nsmall > 1 ) { msgSeti( "N", nsmall ); msgOutif( MSG__NORM, "", "^N clumps rejected because they contain too few pixels.", status ); } if( nthin == 1 ) { msgOutif( MSG__NORM, "", "1 clump rejected because it spans only a single " "pixel along one or more axes.", status ); } else if( nthin > 1 ) { msgSeti( "N", nthin ); msgOutif( MSG__NORM, "", "^N clumps rejected because they spans only a single " "pixel along one or more axes.", status ); } /* Sort the clump indices into descending order of peak value. */ j = ngood; more = 1; while( more ) { j--; more = 0; for( i = 0; i < j; i++ ) { if( peakvals[ igood[ i ] ] < peakvals[ igood[ i + 1 ] ] ) { ii = igood[ i + 1 ]; igood[ i + 1 ] = igood[ i ]; igood[ i ] = ii; more = 1; } } } /* Loop round creating an NDF describing each usable clump. */ for( j = 0; j < ngood; j++ ) { i = igood[ j ]; ret = cupidNdfClump( type, ipd, m1, el, ndim, dims, skip, slbnd, i, clbnd + 3*i, cubnd + 3*i, NULL, ret, cupidConfigD( rconfig, "MAXBAD", 0.05, status ), status ); } /* Free resources */ clbnd = astFree( clbnd ); cubnd = astFree( cubnd ); igood = astFree( igood ); } peakvals = astFree( peakvals ); nrem = astFree( nrem ); L10:; /* Remove the secondary KeyMap added to the KeyMap containing configuration parameters for this algorithm. This prevents the values in the secondary KeyMap being written out to the CUPID extension when cupidStoreConfig is called. */ astMapRemove( rconfig, CUPID__CONFIG ); /* Free resources */ rconfig = astAnnul( rconfig ); mask = astFree( mask ); mask2 = astFree( mask2 ); /* Return the list of clump NDFs. */ return ret; }
void smf_mapbounds( int fast, Grp *igrp, int size, const char *system, AstFrameSet *spacerefwcs, int alignsys, int *lbnd_out, int *ubnd_out, AstFrameSet **outframeset, int *moving, smfBox ** boxes, fts2Port fts_port, int *status ) { /* Local Variables */ AstSkyFrame *abskyframe = NULL; /* Output Absolute SkyFrame */ int actval; /* Number of parameter values supplied */ AstMapping *bolo2map = NULL; /* Combined mapping bolo->map coordinates, WCS->GRID Mapping from input WCS FrameSet */ smfBox *box = NULL; /* smfBox for current file */ smfData *data = NULL; /* pointer to SCUBA2 data struct */ double dlbnd[ 2 ]; /* Floating point lower bounds for output map */ drcntrl_bits drcntrl_mask = 0;/* Mask to use for DRCONTROL on this instrument */ double dubnd[ 2 ]; /* Floating point upper bounds for output map */ AstMapping *fast_map = NULL; /* Mapping from tracking to absolute map coords */ smfFile *file = NULL; /* SCUBA2 data file information */ int first; /* Is this the first good subscan ? */ AstFitsChan *fitschan = NULL;/* Fits channels to construct WCS header */ AstFrameSet *fs = NULL; /* A general purpose FrameSet pointer */ smfHead *hdr = NULL; /* Pointer to data header this time slice */ int i; /* Loop counter */ dim_t j; /* Loop counter */ AstSkyFrame *junksky = NULL; /* Unused SkyFrame argument */ dim_t k; /* Loop counter */ int lbnd0[ 2 ]; /* Defaults for LBND parameter */ double map_pa=0; /* Map PA in output coord system (rads) */ dim_t maxloop; /* Number of times to go round the time slice loop */ dim_t nbadt = 0; /* Number of bad time slices */ dim_t ngoodt = 0; /* Number of good time slices */ double par[7]; /* Projection parameters */ double shift[ 2 ]; /* Shifts from PIXEL to GRID coords */ AstMapping *oskymap = NULL; /* Mapping celestial->map coordinates, Sky <> PIXEL mapping in output FrameSet */ AstSkyFrame *oskyframe = NULL;/* Output SkyFrame */ char *refsys = NULL; /* Sky system from supplied reference FrameSet */ dim_t textreme[4]; /* Time index corresponding to minmax TCS posn */ AstFrame *skyin = NULL; /* Sky Frame in input FrameSet */ double skyref[ 2 ]; /* Values for output SkyFrame SkyRef attribute */ struct timeval tv1; /* Timer */ struct timeval tv2; /* Timer */ AstMapping *tmap; /* Temporary Mapping */ int trim; /* Trim borders of bad pixels from o/p image? */ int ubnd0[ 2 ]; /* Defaults for UBND parameter */ double x_array_corners[4]; /* X-Indices for corner bolos in array */ double x_map[4]; /* Projected X-coordinates of corner bolos */ double y_array_corners[4]; /* Y-Indices for corner pixels in array */ double y_map[4]; /* Projected X-coordinates of corner bolos */ /* Main routine */ if (*status != SAI__OK) return; /* Start a timer to see how long this takes */ smf_timerinit( &tv1, &tv2, status ); /* Initialize pointer to output FrameSet and moving-source flag */ *outframeset = NULL; *moving = 0; /* initialize double precision output bounds and the proj pars */ for( i = 0; i < 7; i++ ) par[ i ] = AST__BAD; dlbnd[ 0 ] = VAL__MAXD; dlbnd[ 1 ] = VAL__MAXD; dubnd[ 0 ] = VAL__MIND; dubnd[ 1 ] = VAL__MIND; /* If we have a supplied reference WCS we can use that directly without having to calculate it from the data. Replace the requested system with the system from the reference FrameSet (take a copy of the string since astGetC may re-use its buffer). */ if (spacerefwcs) { oskyframe = astGetFrame( spacerefwcs, AST__CURRENT ); int nc = 0; refsys = astAppendString( NULL, &nc, astGetC( oskyframe, "System" ) ); system = refsys; } /* Create array of returned smfBox structures and store a pointer to the next one to be initialised. */ *boxes = astMalloc( sizeof( smfBox ) * size ); box = *boxes; astBegin; /* Loop over all files in the Grp */ first = 1; for( i=1; i<=size; i++, box++ ) { /* Initialise the spatial bounds of section of the the output cube that is contributed to by the current ionput file. */ box->lbnd[ 0 ] = VAL__MAXD; box->lbnd[ 1 ] = VAL__MAXD; box->ubnd[ 0 ] = VAL__MIND; box->ubnd[ 1 ] = VAL__MIND; /* Read data from the ith input file in the group */ smf_open_file( NULL, igrp, i, "READ", SMF__NOCREATE_DATA, &data, status ); if (*status != SAI__OK) { msgSeti( "I", i ); errRep( "smf_mapbounds", "Could not open data file no ^I.", status ); break; } else { if( *status == SAI__OK ) { if( data->file == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfFile associated with smfData.", status ); break; } else if( data->hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfHead associated with smfData.", status ); break; } else if( data->hdr->fitshdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No FITS header associated with smfHead.", status ); break; } } } /* convenience pointers */ file = data->file; hdr = data->hdr; /* report name of the input file */ smf_smfFile_msg( file, "FILE", 1, "<unknown>" ); msgSeti("I", i); msgSeti("N", size); msgOutif(MSG__VERB, " ", "SMF_MAPBOUNDS: Processing ^I/^N ^FILE", status); /* Check that there are 3 pixel axes. */ if( data->ndims != 3 ) { smf_smfFile_msg( file, "FILE", 1, "<unknown>" ); msgSeti( "NDIMS", data->ndims ); *status = SAI__ERROR; errRep( FUNC_NAME, "^FILE has ^NDIMS pixel axes, should be 3.", status ); break; } /* Check that the data dimensions are 3 (for time ordered data) */ if( *status == SAI__OK ) { /* If OK Decide which detectors (GRID coord) to use for checking bounds, depending on the instrument in use. */ switch( hdr->instrument ) { case INST__SCUBA2: drcntrl_mask = DRCNTRL__POSITION; /* 4 corner bolometers of the subarray */ x_array_corners[0] = 1; x_array_corners[1] = 1; x_array_corners[2] = (data->dims)[0]; x_array_corners[3] = (data->dims)[0]; y_array_corners[0] = 1; y_array_corners[1] = (data->dims)[1]; y_array_corners[2] = 1; y_array_corners[3] = (data->dims)[1]; break; case INST__AZTEC: /* Rough guess for extreme bolometers around the edge */ x_array_corners[0] = 22; x_array_corners[1] = 65; x_array_corners[2] = 73; x_array_corners[3] = 98; y_array_corners[0] = 1; /* Always 1 for AzTEC */ y_array_corners[1] = 1; y_array_corners[2] = 1; y_array_corners[3] = 1; break; case INST__ACSIS: smf_find_acsis_corners( data, x_array_corners, y_array_corners, status); break; default: *status = SAI__ERROR; errRep(FUNC_NAME, "Don't know how to calculate mapbounds for data created with this instrument", status); } } if( *status == SAI__OK) { size_t goodidx = SMF__BADSZT; /* Need to build up a frameset based on good telescope position. We can not assume that we the first step will be a good TCS position so we look for one. If we can not find anything we skip to the next file. */ maxloop = (data->dims)[2]; for (j=0; j<maxloop; j++) { JCMTState state = (hdr->allState)[j]; if (state.jos_drcontrol >= 0 && state.jos_drcontrol & drcntrl_mask ) { /* bad TCS - so try again */ } else { /* Good tcs */ goodidx = j; break; } } if (goodidx == SMF__BADSZT) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>"); msgOutif( MSG__QUIET, "", "No good telescope positions found in file ^FILE. Ignoring", status ); smf_close_file( NULL, &data, status ); continue; } /* If we are dealing with the first good file, create the output SkyFrame. */ if( first ) { first = 0; /* Create output SkyFrame if it has not come from a reference */ if ( oskyframe == NULL ) { /* smf_tslice_ast only needs to get called once to set up framesets */ if( hdr->wcs == NULL ) { smf_tslice_ast( data, goodidx, 1, fts_port, status); } /* Retrieve input SkyFrame */ skyin = astGetFrame( hdr->wcs, AST__CURRENT ); smf_calc_skyframe( skyin, system, hdr, alignsys, &oskyframe, skyref, moving, status ); /* Get the orientation of the map vertical within the output celestial coordinate system. This is derived form the MAP_PA FITS header, which gives the orientation of the map vertical within the tracking system. */ map_pa = smf_calc_mappa( hdr, system, skyin, status ); /* Provide a sensible default for the pixel size based on wavelength */ par[4] = smf_calc_telres( hdr->fitshdr, status ); par[4] *= AST__DD2R/3600.0; par[5] = par[4]; /* Calculate the projection parameters. We do not enable autogrid determination for SCUBA-2 so we do not need to obtain all the data before calculating projection parameters. */ smf_get_projpar( oskyframe, skyref, *moving, 0, 0, NULL, 0, map_pa, par, NULL, NULL, status ); if (skyin) skyin = astAnnul( skyin ); /* If the output skyframe has been supplied, we still need to determine whether the source is moving or not, and set the reference position. */ } else { /* smf_tslice_ast only needs to get called once to set up framesets */ if( hdr->wcs == NULL ) { smf_tslice_ast( data, goodidx, 1, fts_port, status); } /* Retrieve input SkyFrame */ skyin = astGetFrame( hdr->wcs, AST__CURRENT ); smf_calc_skyframe( skyin, system, hdr, alignsys, &junksky, skyref, moving, status ); /* Store the sky reference position. If the target is moving, ensure the returned SkyFrame represents offsets from the reference position rather than absolute coords. */ astSetD( oskyframe, "SkyRef(1)", skyref[ 0 ] ); astSetD( oskyframe, "SkyRef(2)", skyref[ 1 ] ); if( *moving ) astSet( oskyframe, "SkyRefIs=Origin" ); /* Ensure the Epoch attribute in the map is set to the date of the first data in the map, rather than the date in supplied reference WCS. */ astSetD( oskyframe, "Epoch", astGetD( junksky, "Epoch" ) ); } if ( *outframeset == NULL && oskyframe != NULL && (*status == SAI__OK)){ /* Now created a spatial Mapping. Use the supplied reference frameset if supplied */ if (spacerefwcs) { oskymap = astGetMapping( spacerefwcs, AST__BASE, AST__CURRENT ); } else { /* Now populate a FitsChan with FITS-WCS headers describing the required tan plane projection. The longitude and latitude axis types are set to either (RA,Dec) or (AZ,EL) to get the correct handedness. */ fitschan = astFitsChan ( NULL, NULL, " " ); smf_makefitschan( astGetC( oskyframe, "System"), &(par[0]), &(par[2]), &(par[4]), par[6], fitschan, status ); astClear( fitschan, "Card" ); fs = astRead( fitschan ); /* Extract the output PIXEL->SKY Mapping. */ oskymap = astGetMapping( fs, AST__BASE, AST__CURRENT ); /* Tidy up */ fs = astAnnul( fs ); } /* Create the output FrameSet */ *outframeset = astFrameSet( astFrame(2, "Domain=GRID"), " " ); /* Now add the SkyFrame to it */ astAddFrame( *outframeset, AST__BASE, oskymap, oskyframe ); /* Now add a POLANAL Frame if required (i.e. if the input time series are POL-2 Q/U values). */ smf_addpolanal( *outframeset, hdr, status ); /* Invert the oskymap mapping */ astInvert( oskymap ); } /* End WCS FrameSet construction */ } /* Get a copy of the output SkyFrame and ensure it represents absolute coords rather than offset coords. */ abskyframe = astCopy( oskyframe ); astClear( abskyframe, "SkyRefIs" ); astClear( abskyframe, "AlignOffset" ); maxloop = (data->dims)[2]; if (fast) { /* For scan map we scan through looking for largest telescope moves. For dream/stare we just look at the start and end time slices to account for sky rotation. */ if (hdr->obsmode != SMF__OBS_SCAN) { textreme[0] = 0; textreme[1] = (data->dims)[2] - 1; maxloop = 2; } else { const char *tracksys; double *ac1list, *ac2list, *bc1list, *bc2list, *p1, *p2, *p3, *p4; double flbnd[4], fubnd[4]; JCMTState state; /* If the output and tracking systems are different, get a Mapping between them. */ tracksys = sc2ast_convert_system( (hdr->allState)[goodidx].tcs_tr_sys, status ); if( strcmp( system, tracksys ) ) { AstSkyFrame *tempsf = astCopy( abskyframe ); astSetC( tempsf, "System", tracksys ); AstFrameSet *tempfs = astConvert( tempsf, abskyframe, "" ); tmap = astGetMapping( tempfs, AST__BASE, AST__CURRENT ); fast_map = astSimplify( tmap ); tmap = astAnnul( tmap ); tempsf = astAnnul( tempsf ); tempfs = astAnnul( tempfs ); } else { fast_map = NULL; } /* Copy all ac1/2 positions into two array, and transform them from tracking to absolute output sky coords. */ ac1list = astMalloc( maxloop*sizeof( *ac1list ) ); ac2list = astMalloc( maxloop*sizeof( *ac2list ) ); if( *status == SAI__OK ) { p1 = ac1list; p2 = ac2list; for( j = 0; j < maxloop; j++ ) { state = (hdr->allState)[ j ]; *(p1++) = state.tcs_tr_ac1; *(p2++) = state.tcs_tr_ac2; } if( fast_map ) astTran2( fast_map, maxloop, ac1list, ac2list, 1, ac1list, ac2list ); } /* If the target is moving, we need to adjust these ac1/2 values to represent offsets from the base position. */ if( *moving ) { /* Copy all bc1/2 positions into two arrays. */ bc1list = astMalloc( maxloop*sizeof( *bc1list ) ); bc2list = astMalloc( maxloop*sizeof( *bc2list ) ); if( *status == SAI__OK ) { p1 = bc1list; p2 = bc2list; for( j = 0; j < maxloop; j++ ) { state = (hdr->allState)[ j ]; *(p1++) = state.tcs_tr_bc1; *(p2++) = state.tcs_tr_bc2; } /* Transform them from tracking to absolute output sky coords. */ if( fast_map ) astTran2( fast_map, maxloop, bc1list, bc2list, 1, bc1list, bc2list ); /* Replace each ac1/2 position with the offsets from the corresponding base position. */ p1 = bc1list; p2 = bc2list; p3 = ac1list; p4 = ac2list; for( j = 0; j < maxloop; j++ ) { smf_offsets( *(p1++), *(p2++), p3++, p4++, status ); } } /* We no longer need the base positions. */ bc1list = astFree( bc1list ); bc2list = astFree( bc2list ); } /* Transform the ac1/2 position from output sky coords to output pixel coords. */ astTran2( oskymap, maxloop, ac1list, ac2list, 1, ac1list, ac2list ); /* Find the bounding box containing these pixel coords and the time slices at which the boresight touches each edge of this box. */ flbnd[ 0 ] = VAL__MAXD; flbnd[ 1 ] = VAL__MAXD; fubnd[ 0 ] = VAL__MIND; fubnd[ 1 ] = VAL__MIND; for( j = 0; j < 4; j++ ) textreme[ j ] = (dim_t) VAL__BADI; if( *status == SAI__OK ) { p1 = ac1list; p2 = ac2list; for( j = 0; j < maxloop; j++,p1++,p2++ ) { if( *p1 != VAL__BADD && *p2 != VAL__BADD ){ if ( *p1 < flbnd[0] ) { flbnd[0] = *p1; textreme[0] = j; } if ( *p2 < flbnd[1] ) { flbnd[1] = *p2; textreme[1] = j; } if ( *p1 > fubnd[0] ) { fubnd[0] = *p1; textreme[2] = j; } if ( *p2 > fubnd[1] ) { fubnd[1] = *p2; textreme[3] = j; } } } } maxloop = 4; msgSetd("X1", textreme[0]); msgSetd("X2", textreme[1]); msgSetd("X3", textreme[2]); msgSetd("X4", textreme[3]); msgOutif( MSG__DEBUG, " ", "Extrema time slices are ^X1, ^X2, ^X3 and ^X4", status); ac1list = astFree( ac1list ); ac2list = astFree( ac2list ); } } /* Get the astrometry for all the relevant time slices in this data file */ for( j=0; j<maxloop; j++ ) { dim_t ts; /* Actual time slice to use */ /* if we are doing the fast loop, we need to read the time slice index from textreme. Else we just use the index */ if (fast) { /* get the index but make sure it is good */ ts = textreme[j]; if (ts == (dim_t)VAL__BADI) continue; } else { ts = j; } /* Calculate the bolo to map-pixel transformation for this tslice */ bolo2map = smf_rebin_totmap( data, ts, abskyframe, oskymap, *moving, fts_port, status ); if ( *status == SAI__OK ) { /* skip if we did not get a mapping this time round */ if (!bolo2map) continue; /* Check corner pixels in the array for their projected extent on the sky to set the pixel bounds */ astTran2( bolo2map, 4, x_array_corners, y_array_corners, 1, x_map, y_map ); /* Update min/max for this time slice */ for( k=0; k<4; k++ ) { if( x_map[k] != AST__BAD && y_map[k] != AST__BAD ) { if( x_map[k] < dlbnd[0] ) dlbnd[0] = x_map[k]; if( y_map[k] < dlbnd[1] ) dlbnd[1] = y_map[k]; if( x_map[k] > dubnd[0] ) dubnd[0] = x_map[k]; if( y_map[k] > dubnd[1] ) dubnd[1] = y_map[k]; if( x_map[k] < box->lbnd[0] ) box->lbnd[0] = x_map[k]; if( y_map[k] < box->lbnd[1] ) box->lbnd[1] = y_map[k]; if( x_map[k] > box->ubnd[0] ) box->ubnd[0] = x_map[k]; if( y_map[k] > box->ubnd[1] ) box->ubnd[1] = y_map[k]; } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( FUNC_NAME, "Extreme positions are bad.", status ); break; } } } /* Explicitly annul these mappings each time slice for reduced memory usage */ if (bolo2map) bolo2map = astAnnul( bolo2map ); if (fs) fs = astAnnul( fs ); /* Break out of loop over time slices if bad status */ if (*status != SAI__OK) goto CLEANUP; } /* Annul any remaining Ast objects before moving on to the next file */ if (fs) fs = astAnnul( fs ); if (bolo2map) bolo2map = astAnnul( bolo2map ); } /* Close the data file */ smf_close_file( NULL, &data, status); /* Break out of loop over data files if bad status */ if (*status != SAI__OK) goto CLEANUP; } /* make sure we got values - should not be possible with good status */ if (dlbnd[0] == VAL__MAXD || dlbnd[1] == VAL__MAXD) { if (*status == SAI__OK) { *status = SAI__ERROR; errRep( " ", "Unable to find any valid map bounds", status ); } } if (nbadt > 0) { msgOutf( "", " Processed %zu time slices to calculate bounds," " of which %zu had bad telescope data and were skipped", status, (size_t)(ngoodt+nbadt), (size_t)nbadt ); } /* If spatial reference wcs was supplied, store par values that result in no change to the pixel origin. */ if( spacerefwcs ){ par[ 0 ] = 0.5; par[ 1 ] = 0.5; } /* Need to re-align with the interim GRID coordinates */ lbnd_out[0] = ceil( dlbnd[0] - par[0] + 0.5 ); ubnd_out[0] = ceil( dubnd[0] - par[0] + 0.5 ); lbnd_out[1] = ceil( dlbnd[1] - par[1] + 0.5 ); ubnd_out[1] = ceil( dubnd[1] - par[1] + 0.5 ); /* Do the same with the individual input file bounding boxes */ box = *boxes; for (i = 1; i <= size; i++, box++) { box->lbnd[0] = ceil( box->lbnd[0] - par[0] + 0.5); box->ubnd[0] = ceil( box->ubnd[0] - par[0] + 0.5); box->lbnd[1] = ceil( box->lbnd[1] - par[1] + 0.5); box->ubnd[1] = ceil( box->ubnd[1] - par[1] + 0.5); } /* Apply a ShiftMap to the output FrameSet to re-align the GRID coordinates */ shift[0] = 2.0 - par[0] - lbnd_out[0]; shift[1] = 2.0 - par[1] - lbnd_out[1]; astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) ); /* Set the dynamic defaults for lbnd/ubnd */ lbnd0[ 0 ] = lbnd_out[ 0 ]; lbnd0[ 1 ] = lbnd_out[ 1 ]; parDef1i( "LBND", 2, lbnd0, status ); ubnd0[ 0 ] = ubnd_out[ 0 ]; ubnd0[ 1 ] = ubnd_out[ 1 ]; parDef1i( "UBND", 2, ubnd0, status ); parGet1i( "LBND", 2, lbnd_out, &actval, status ); if( actval == 1 ) lbnd_out[ 1 ] = lbnd_out[ 0 ]; parGet1i( "UBND", 2, ubnd_out, &actval, status ); if( actval == 1 ) ubnd_out[ 1 ] = ubnd_out[ 0 ]; /* Ensure the bounds are the right way round. */ if( lbnd_out[ 0 ] > ubnd_out[ 0 ] ) { int itmp = lbnd_out[ 0 ]; lbnd_out[ 0 ] = ubnd_out[ 0 ]; ubnd_out[ 0 ] = itmp; } if( lbnd_out[ 1 ] > ubnd_out[ 1 ] ) { int itmp = lbnd_out[ 1 ]; lbnd_out[ 1 ] = ubnd_out[ 1 ]; ubnd_out[ 1 ] = itmp; } /* If borders of bad pixels are being trimmed from the output image, then do not allow the user-specified bounds to extend outside the default bounding box (since we know that the default bounding box encloses all available data). */ parGet0l( "TRIM", &trim, status ); if( trim ) { if( lbnd_out[ 0 ] < lbnd0[ 0 ] ) lbnd_out[ 0 ] = lbnd0[ 0 ]; if( lbnd_out[ 1 ] < lbnd0[ 1 ] ) lbnd_out[ 1 ] = lbnd0[ 1 ]; if( ubnd_out[ 0 ] > ubnd0[ 0 ] ) ubnd_out[ 0 ] = ubnd0[ 0 ]; if( ubnd_out[ 1 ] > ubnd0[ 1 ] ) ubnd_out[ 1 ] = ubnd0[ 1 ]; } /* Modify the returned FrameSet to take account of the new pixel origin. */ shift[ 0 ] = lbnd0[ 0 ] - lbnd_out[ 0 ]; shift[ 1 ] = lbnd0[ 1 ] - lbnd_out[ 1 ]; if( shift[ 0 ] != 0.0 || shift[ 1 ] != 0.0 ) { astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) ); } /* Report the pixel bounds of the cube. */ if( *status == SAI__OK ) { msgOutif( MSG__NORM, " ", " ", status ); msgSeti( "XL", lbnd_out[ 0 ] ); msgSeti( "YL", lbnd_out[ 1 ] ); msgSeti( "XU", ubnd_out[ 0 ] ); msgSeti( "YU", ubnd_out[ 1 ] ); msgOutif( MSG__NORM, " ", " Output map pixel bounds: ( ^XL:^XU, ^YL:^YU )", status ); if( ( ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1 ) > MAX_DIM || ( ubnd_out[ 1 ] - lbnd_out[ 1 ] + 1 ) > MAX_DIM ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": The map is too big. Check your list of input " "data files does not include widely separated observations.", status ); } } /* If no error has occurred, export the returned FrameSet pointer from the current AST context so that it will not be annulled when the AST context is ended. Otherwise, ensure a null pointer is returned. */ if( *status == SAI__OK ) { astExport( *outframeset ); } else { *outframeset = astAnnul( *outframeset ); } msgOutiff( SMF__TIMER_MSG, "", "Took %.3f s to calculate map bounds", status, smf_timerupdate( &tv1, &tv2, status ) ); /* Clean Up */ CLEANUP: if (*status != SAI__OK) { errRep(FUNC_NAME, "Unable to determine map bounds", status); } if (oskymap) oskymap = astAnnul( oskymap ); if (bolo2map) bolo2map = astAnnul( bolo2map ); if (fitschan) fitschan = astAnnul( fitschan ); if( data != NULL ) smf_close_file( NULL, &data, status ); refsys = astFree( refsys ); astEnd; }
void smurf_dreamweights ( int *status ) { /* Local Variables */ Grp *confgrp = NULL; /* Group containing configuration file */ smfData *data = NULL; /* Input data */ const int defgridminmax[] = { -4, 4, -4, 4 }; /* Default extent xmin,xmax,ymin,ymax */ int gridminmax[4]; /* Extent of grid points array */ int gridpts[DREAM__MXGRID][2]; /* Array of points for reconstruction grid */ double gridstep; /* Size of reconstruction grid in arcsec */ size_t i; /* Loop counter */ Grp *igrp = NULL; /* Input group of NDFs */ size_t size; /* Size of input Grp of files */ AstKeyMap *keymap = NULL; /* Pointer to keymap of config settings */ size_t ksize; /* Size of group containing CONFIG file */ int ngrid; /* Number of points in reconstruction grid */ Grp *ogrp = NULL; /* Group of output weights files */ size_t outsize; /* Size of output Grp of files */ /* Main routine */ ndfBegin(); /* Get group of input raw data NDFs */ kpg1Rgndf( "NDF", 0, 1, "", &igrp, &size, status ); /* Get group of output files from user: assume 1 output file for every input file */ kpg1Wgndf( "OUT", igrp, size, size, "More output files required...", &ogrp, &outsize, status ); /* Read configuration settings into keymap */ if (*status == SAI__OK) { kpg1Gtgrp( "CONFIG", &confgrp, &ksize, status ); if (*status == PAR__NULL) { /* NULL value so provide defaults */ errAnnul( status ); msgOutif(MSG__VERB, " ", "No config file specified - assuming default configuration parameters", status); keymap = astKeyMap(" " ); astMapPut1I( keymap, "GRIDMINMAX", 4, (int*)defgridminmax, " " ); astMapPut0D( keymap, "GRIDSTEP", 6.28, " " ); } else { kpg1Kymap( confgrp, &keymap, status ); } if( confgrp ) grpDelet( &confgrp, status ); } /* Determine grid parameters from inputs given above */ smf_dream_getgrid( keymap, &gridstep, &ngrid, gridminmax, gridpts, status); /* Annul keymap immediately as it is no longer required */ if (keymap) keymap = astAnnul( keymap ); /* Loop over number of files */ for ( i=1; (i<= size) && (*status == SAI__OK); i++) { /* Open file */ smf_open_file( NULL, igrp, i, "READ", 0, &data, status ); /* Calculate weights based on this file */ smf_dream_calcweights( data, ogrp, i, gridstep, ngrid, gridminmax, &(gridpts[0]), status); /* Immediately check status on return and abort if an error occured */ if ( *status != SAI__OK ) { msgSeti("I",i); msgSeti("N",size); errRep(FUNC_NAME, "Unable to determine DREAM weights for file ^I of ^N", status); } smf_close_file( NULL, &data, status ); } /* Free up resources */ if ( ogrp != NULL ) { grpDelet( &ogrp, status); } if ( igrp != NULL ) { grpDelet( &igrp, status); } ndfEnd( status ); msgOutif(MSG__VERB," ", "DREAM weights calculation completed successfully", status); }
void smf_rebinsparse( smfData *data, int first, int *ptime, AstFrame *ospecfrm, AstMapping *ospecmap, AstSkyFrame *oskyframe, Grp *detgrp, int lbnd_out[ 3 ], int ubnd_out[ 3 ], int genvar, float *data_array, float *var_array, int *ispec, float *texp_array, float *teff_array, double *fcon, int *status ){ /* Local Variables */ AstCmpMap *fmap = NULL; /* Mapping from spectral grid to topo freq Hz */ AstCmpMap *ssmap = NULL; /* I/p GRID-> o/p PIXEL Mapping for spectral axis */ AstFitsChan *fc = NULL; /* Storage for FITS headers */ AstFrame *specframe = NULL; /* Spectral Frame in input FrameSet */ AstFrame *specframe2 = NULL; /* Temporary copy of SpecFrame in input WCS */ AstFrameSet *fs = NULL; /* A general purpose FrameSet pointer */ AstFrameSet *swcsin = NULL; /* FrameSet describing spatial input WCS */ AstMapping *fsmap = NULL; /* Base->Current Mapping extracted from a FrameSet */ AstMapping *specmap = NULL; /* PIXEL -> Spec mapping in input FrameSet */ char *fftwin = NULL; /* Name of FFT windowing function */ const char *name = NULL; /* Pointer to current detector name */ const double *tsys=NULL; /* Pointer to Tsys value for first detector */ dim_t timeslice_size; /* No of detector values in one time slice */ double *spectab = NULL;/* Workspace for spectral output grid positions */ double *xin = NULL; /* Workspace for detector input grid positions */ double *xout = NULL; /* Workspace for detector output pixel positions */ double *yin = NULL; /* Workspace for detector input grid positions */ double *yout = NULL; /* Workspace for detector output pixel positions */ double at; /* Frequency at which to take the gradient */ double dnew; /* Channel width in Hz */ double fcon2; /* Variance factor for whole file */ double k; /* Back-end degradation factor */ double tcon; /* Variance factor for whole time slice */ float *pdata = NULL; /* Pointer to next data sample */ float *qdata = NULL; /* Pointer to next data sample */ float rtsys; /* Tsys value */ float teff; /* Effective integration time, times 4 */ float texp; /* Total time ( = ton + toff ) */ float toff; /* Off time */ float ton; /* On time */ int *nexttime = NULL; /* Pointer to next time slice index to use */ int dim[ 3 ]; /* Output array dimensions */ int found; /* Was current detector name found in detgrp? */ int good; /* Are there any good detector samples? */ int ibasein; /* Index of base Frame in input FrameSet */ int ichan; /* Index of current channel */ int iv; /* Offset to next element */ int iz; /* Output grid index on axis 3 */ int nchan; /* Number of input spectral channels */ int pixax[ 3 ]; /* The output fed by each selected mapping input */ int specax; /* Index of spectral axis in input FrameSet */ size_t irec; /* Index of current input detector */ size_t itime; /* Index of current time slice */ smfHead *hdr = NULL; /* Pointer to data header for this time slice */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context.*/ astBegin; /* Store a pointer to the input NDFs smfHead structure. */ hdr = data->hdr; /* Store the dimensions of the output array. */ dim[ 0 ] = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1; dim[ 1 ] = ubnd_out[ 1 ] - lbnd_out[ 1 ] + 1; dim[ 2 ] = ubnd_out[ 2 ] - lbnd_out[ 2 ] + 1; /* Store the number of pixels in one time slice */ timeslice_size = (data->dims)[ 0 ]*(data->dims)[ 1 ]; /* We want a description of the spectral WCS axis in the input file. If the input file has a WCS FrameSet containing a SpecFrame, use it, otherwise we will obtain it from the FITS header later. NOTE, if we knew that all the input NDFs would have the same spectral axis calibration, then the spectral WCS need only be obtained from the first NDF. However, in the general case, I presume that data files may be combined that use different spectral axis calibrations, and so these differences need to be taken into account. */ if( hdr->tswcs ) { fs = astClone( hdr->tswcs ); /* The first axis should be a SpecFrame. See if this is so. If not annul the specframe pointer. */ specax = 1; specframe = astPickAxes( fs, 1, &specax, NULL ); if( !astIsASpecFrame( specframe ) ) specframe = astAnnul( specframe ); } /* If the above did not yield a SpecFrame, use the FITS-WCS headers in the FITS extension of the input NDF. Take a copy of the FITS header (so that the contents of the header are not changed), and then read a FrameSet out of it. */ if( !specframe ) { fc = astCopy( hdr->fitshdr ); astClear( fc, "Card" ); fs = astRead( fc ); /* Extract the SpecFrame that describes the spectral axis from the current Frame of this FrameSet. This is assumed to be the third WCS axis (NB the different axis number). */ specax = 3; specframe = astPickAxes( fs, 1, &specax, NULL ); } /* Split off the 1D Mapping for this single axis from the 3D Mapping for the whole WCS. This results in "specmap" holding the Mapping from SpecFrame value to GRID value. */ fsmap = astGetMapping( fs, AST__CURRENT, AST__BASE ); astMapSplit( fsmap, 1, &specax, pixax, &specmap ); /* Invert the Mapping for the spectral axis so that it goes from input GRID coord to spectral coord. */ astInvert( specmap ); /* Get a Mapping that converts values in the input spectral system to the corresponding values in the output spectral system. */ fs = astConvert( specframe, ospecfrm, "" ); /* Concatenate these Mappings with the supplied spectral Mapping to get a Mapping from the input spectral grid axis (pixel axis 1) to the output spectral grid axis (pixel axis 3). Simplify the Mapping. */ ssmap = astCmpMap( astCmpMap( specmap, astGetMapping( fs, AST__BASE, AST__CURRENT ), 1, " " ), ospecmap, 1, " " ); ssmap = astSimplify( ssmap ); /* Create a table with one element for each channel in the input array, holding the index of the nearest corresponding output channel. */ nchan = (data->dims)[ 0 ]; spectab = astMalloc( sizeof( *spectab )*nchan ); if( spectab ) { for( ichan = 0; ichan < nchan; ichan++ ) spectab[ ichan ] = ichan + 1; astTran1( ssmap, nchan, spectab, 1, spectab ); for( ichan = 0; ichan < nchan; ichan++ ) { if( spectab[ ichan ] != AST__BAD ) { iz = floor( spectab[ ichan ] + 0.5 ); if( iz >= 1 && iz <= dim[ 2 ] ) { spectab[ ichan ] = iz; } else { spectab[ ichan ] = 0; } } else { spectab[ ichan ] = 0; } } } /* Allocate work arrays big enough to hold the coords of all the detectors in the current input file.*/ xin = astMalloc( (data->dims)[ 1 ] * sizeof( *xin ) ); yin = astMalloc( (data->dims)[ 1 ] * sizeof( *yin ) ); xout = astMalloc( (data->dims)[ 1 ] * sizeof( *xout ) ); yout = astMalloc( (data->dims)[ 1 ] * sizeof( *yout ) ); /* Initialise a string to point to the name of the first detector for which data is available */ name = hdr->detname; /* Store input coords for the detectors. Axis 1 is the detector index, and axis 2 is a dummy axis that always has the value 1. */ for( irec = 0; irec < (data->dims)[ 1 ]; irec++ ) { xin[ irec ] = irec + 1.0; yin[ irec ] = 1.0; /* If a group of detectors to be used was supplied, search the group for the name of the current detector. If not found, set the GRID coords bad. */ if( detgrp ) { found = grpIndex( name, detgrp, 1, status ); if( !found ) { xin[ irec ] = AST__BAD; yin[ irec ] = AST__BAD; } } /* Move on to the next available detector name. */ name += strlen( name ) + 1; } /* Find the constant factor associated with the current input file. This is the squared backend degradation factor, divided by the noise bandwidth. Get the required FITS headers, checking they were found. */ if( astGetFitsF( hdr->fitshdr, "BEDEGFAC", &k ) && astGetFitsS( hdr->fitshdr, "FFT_WIN", &fftwin ) ){ /* Get a Mapping that converts values in the input spectral system to topocentric frequency in Hz, and concatenate this Mapping with the Mapping from input GRID coord to the input spectral system. The result is a Mapping from input GRID coord to topocentric frequency in Hz. */ specframe2 = astCopy( specframe ); astSet( specframe2, "system=freq,stdofrest=topo,unit=Hz" ); fmap = astCmpMap( specmap, astGetMapping( astConvert( specframe, specframe2, "" ), AST__BASE, AST__CURRENT ), 1, " " ); /* Differentiate this Mapping at the mid channel position to get the width of an input channel in Hz. */ at = 0.5*nchan; dnew = astRate( fmap, &at, 1, 1 ); /* Modify the channel width to take account of the effect of the FFT windowing function. Allow undef value because FFT_WIN for old data had a broken value in hybrid subband modes. */ if( dnew != AST__BAD ) { dnew = fabs( dnew ); if( !strcmp( fftwin, "truncate" ) ) { dnew *= 1.0; } else if( !strcmp( fftwin, "hanning" ) ) { dnew *= 1.5; } else if( !strcmp( fftwin, "<undefined>" ) ) { /* Deal with broken data - make an assumption */ dnew *= 1.0; } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSetc( "W", fftwin ); errRep( FUNC_NAME, "FITS header FFT_WIN has unknown value " "'^W' (programming error).", status ); } /* Form the required constant. */ fcon2 = k*k/dnew; } else { fcon2 = VAL__BADD; } } else { fcon2 = VAL__BADD; } /* Return the factor needed for calculating Tsys from the variance. */ if( first ) { *fcon = fcon2; } else if( fcon2 != *fcon ) { *fcon = VAL__BADD; } /* Initialise a pointer to the next time slice index to be used. */ nexttime = ptime; /* Loop round all the time slices in the input file. */ for( itime = 0; itime < (data->dims)[ 2 ] && *status == SAI__OK; itime++ ) { /* If this time slice is not being pasted into the output cube, pass on. */ if( nexttime ){ if( *nexttime != itime ) continue; nexttime++; } /* Store a pointer to the first input data value in this time slice. */ pdata = ( (float *) (data->pntr)[ 0 ] ) + itime*timeslice_size; /* Get a FrameSet describing the spatial coordinate systems associated with the current time slice of the current input data file. The base frame in the FrameSet will be a 2D Frame in which axis 1 is detector number and axis 2 is unused. The current Frame will be a SkyFrame (the SkyFrame System may be any of the JCMT supported systems). The Epoch will be set to the epoch of the time slice. */ smf_tslice_ast( data, itime, 1, NO_FTS, status ); swcsin = hdr->wcs; /* Note the total exposure time (texp) for all the input spectra produced by this time slice. */ ton = hdr->state->acs_exposure; if( ton == 0.0 ) ton = VAL__BADR; toff = hdr->state->acs_offexposure; if( toff == 0.0 ) toff = VAL__BADR; if( ton != VAL__BADR && toff != VAL__BADR ) { texp = ton + toff; teff = 4*ton*toff/( ton + toff ); } else { texp = VAL__BADR; teff = VAL__BADR; } /* If output variances are being calculated on the basis of Tsys values in the input, find the constant factor associated with the current time slice. */ tcon = AST__BAD; if( genvar == 2 && fcon2 != AST__BAD && texp != VAL__BADR ) { tcon = fcon2*( 1.0/ton + 1.0/toff ); /* Get a pointer to the start of the Tsys values for this time slice. */ tsys = hdr->tsys + hdr->ndet*itime; } /* We now create a Mapping from detector index to position in oskyframe. */ astInvert( swcsin ); ibasein = astGetI( swcsin, "Base" ); fs = astConvert( swcsin, oskyframe, "SKY" ); astSetI( swcsin, "Base", ibasein ); astInvert( swcsin ); if( fs == NULL ) { if( *status == SAI__OK ) { if (data->file) { smf_smfFile_msg(data->file, "FILE", 1, "<unknown>"); } else { msgSetc( "FILE", "<unknown>" ); } *status = SAI__ERROR; errRep( FUNC_NAME, "The spatial coordinate system in ^FILE " "is not compatible with the spatial coordinate " "system in the first input file.", status ); } break; } /* Transform the positions of the detectors from input GRID to oskyframe coords. */ astTran2( fs, (data->dims)[ 1 ], xin, yin, 1, xout, yout ); /* Loop round all detectors. */ for( irec = 0; irec < (data->dims)[ 1 ]; irec++ ) { /* If the detector has a valid position, see if it produced any good data values. */ if( xout[ irec ] != AST__BAD && yout[ irec ] != AST__BAD ) { qdata = pdata; good = 0; for( ichan = 0; ichan < nchan; ichan++ ){ if( *(qdata++) != VAL__BADR ) { good = 1; break; } } /* If it did, calculate the variance associated with each detector sample (if required), based on the input Tsys values, and copy the spectrum to the output NDF. */ if( good ) { if( *ispec < dim[ 0 ] ){ rtsys = tsys ? (float) tsys[ irec ] : VAL__BADR; if( rtsys <= 0.0 ) rtsys = VAL__BADR; if( tcon != AST__BAD && genvar == 2 && rtsys != VAL__BADR ) { var_array[ *ispec ] = tcon*rtsys*rtsys; } else if( var_array ) { var_array[ *ispec ] = VAL__BADR; } if( texp != VAL__BADR ) { texp_array[ *ispec ] = texp; teff_array[ *ispec ] = teff; } for( ichan = 0; ichan < nchan; ichan++, pdata++ ) { iz = spectab[ ichan ] - 1; if( iz >= 0 && iz < dim[ 2 ] ) { iv = *ispec + dim[ 0 ]*iz; data_array[ iv ] = *pdata; } } (*ispec)++; } else if( *status == SAI__OK ){ *status = SAI__ERROR; msgSeti( "DIM", dim[ 0 ] ); errRep( " ", "Too many spectra (more than ^DIM) for " "the output NDF (programming error).", status ); break; } /* If this detector does not have any valid data values, increment the data pointer to point at the first sample for the next detector. */ } else { pdata += nchan; } /* If this detector does not have a valid position, increment the data pointer to point at the first sample for the next detector. */ } else { pdata += nchan; } } /* For efficiency, explicitly annul the AST Objects created in this tight loop. */ fs = astAnnul( fs ); } /* Free resources */ spectab = astFree( spectab ); xin = astFree( xin ); yin = astFree( yin ); xout = astFree( xout ); yout = astFree( yout ); /* End the AST context. This will annul all AST objects created within the context (except for those that have been exported from the context). */ astEnd; }
HDSLoc *cupidFellWalker( int type, int ndim, int *slbnd, int *subnd, void *ipd, double *ipv, double rms, AstKeyMap *config, int velax, int perspectrum, double beamcorr[ 3 ], int *status ){ /* *+ * Name: * cupidFellWalker * Purpose: * Identify clumps of emission within a 1, 2 or 3 dimensional NDF using * the FELLWALKER algorithm. * Language: * Starlink C * Synopsis: * HDSLoc *cupidFellWalker( int type, int ndim, int *slbnd, int *subnd, * void *ipd, double *ipv, double rms, * AstKeyMap *config, int velax, * int perspectrum, double beamcorr[ 3 ], * int *status ) * Description: * This function identifies clumps within a 1, 2 or 3 dimensional data * array using the FELLWALKER algorithm. This algorithm loops over * every data pixel above the threshold which has not already been * assigned to a clump. For each such pixel, a route to the nearest * peak in the data value is found by moving from pixel to pixel along * the line of greatest gradient. When this route arrives at a peak, all * pixels within some small neighbourhood are checked to see if there * is a higher data value. If there is, the route recommences from the * highest pixel in the neighbourhood, again moving up the line of * greatest gradient. When a peak is reached which is the highest data * value within its neighbourhood, a check is made to see if this peak * pixel has already been assigned to a clump. If it has, then all * pixels which were traversed in following the route to this peak are * assigned to the same clump. If the peak has not yet been assigned to a * clump, then it, and all the traversed pixels, are assigned to a new * clump. If the route commences at "sea level" and starts with a low * gradient section (average gradient lower than a given value) then * the initial section of the route (up to the point where the gradient * exceeds the low gradient limit) is not assigned to the clump, but is * instead given a special value which prevents the pixels being re-used * as the start point for a new "walk". * * If the high data values in a clump form a plateau with slight * undulations, then the above algorithm may create a separate clump * for each undulation. This is probably inappropriate, especially if * the dips between the undulations are less than or are comparable to * the noise level in the data. This situation can arise for instance * if the pixel-to-pixel noise is correlated on a scale equal to or * larger than the value of the MaxJump configuration parameter. To * avoid this, adjoining clumps are merged together if the dip between * them is less than a specified value. Specifically, if two clumps * with peak values PEAK1 and PEAK2, where PEAK1 is less than PEAK2, * are adjacent to each other, and if the pixels along the interface * between the two clumps all have data values which are larger than * "PEAK1 - MinDip" (where MinDip is the value of the MinDip * configuration parameter), then the two clumps are merged together. * Parameters: * type * An integer identifying the data type of the array values pointed to * by "ipd". Must be either CUPID__DOUBLE or CUPID__FLOAT (defined in * cupid.h). * ndim * The number of dimensions in the data array. Must be 2 or 3. * slbnd * Pointer to an array holding the lower pixel index bound of the * data array on each axis. * subnd * Pointer to an array holding the upper pixel index bound of the * data array on each axis. * ipd * Pointer to the data array. The elements should be stored in * Fortran order. The data type of this array is given by "itype". * ipv * Pointer to the input Variance array, or NULL if there is no Variance * array. The elements should be stored in Fortran order. The data * type of this array is "double". * rms * The default value for the global RMS error in the data array. * config * An AST KeyMap holding tuning parameters for the algorithm. * velax * The index of the velocity axis in the data array (if any). Only * used if "ndim" is 3. * perspectrum * If non-zero, then each spectrum is processed independently of its * neighbours. A clump that extends across several spectra will be * split into multiple clumps, each restricted to a single spectrum. * Only used if "ndim" is 3. * beamcorr * An array in which is returned the FWHM (in pixels) describing the * instrumental smoothing along each pixel axis. The clump widths * stored in the output catalogue are reduced to correct for this * smoothing. * status * Pointer to the inherited status value. * Returned Value: * A locator for a new HDS object which is an array of NDF structures. * Each NDF will hold the data values associated with a single clump * and will be the smallest possible NDF that completely contains the * corresponding clump. Pixels not in the clump will be set bad. The * pixel origin is set to the same value as the supplied NDF. * Copyright: * Copyright (C) 2006 Particle Physics & Astronomy Research Council. * Copyright (C) 2007, 2009 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: * 16-JAN-2006 (DSB): * Original version. * 22-JUN-2007 (DSB): * Add rejection of clumps that touch the edge of an array. * 17-SEP-2007 (DSB): * Added "perspectrum" parameter. * 14-JAN-2009 (TIMJ): * Use MERS for message filtering. * 20-NOV-2013 (DSB): * Supplied config KeyMap now holds the method parameters directly, * rather than holding them in a sub-KeyMap. * 30-JUN-2014 (DSB): * Changed default for MinDip from 3*RMS to 2*RMS, and default for * MinHeight from "Noise+MinDip" to "Noise". These values seem to * do better at recovering artificial Gaussians. * {enter_further_changes_here} * Bugs: * {note_any_bugs_here} *- */ /* Local Variables: */ HDSLoc *ret; /* Locator for the returned array of NDFs */ double *pd; /* Pointer to next element of data array */ double *peakvals; /* Pointer to array holding clump peak values */ double mindip; /* Minimum dip between distinct peaks */ double minhgt; /* Min allowed height for a clump peak */ double noise; /* Background data value */ double pv; /* Pixel value */ float *pf; /* Pointer to next element of data array */ int *clbnd; /* Array holding lower axis bounds of all clumps */ int *cubnd; /* Array holding upper axis bounds of all clumps */ int *igood; /* Pointer to array holding usable clump indices */ int *ipa; /* Pointer to clump assignment array */ int *nrem; /* Pointer to array holding clump populations */ int *pa; /* Pointer to next element of the ipa array */ int allow_edge; /* Accept clumps that touch the edge of an array? */ int dims[3]; /* Pointer to array of array dimensions */ int el; /* Number of elements in array */ int i; /* Loop count */ int ii; /* Temp storage */ int ix; /* Grid index on 1st axis */ int iy; /* Grid index on 2nd axis */ int iz; /* Grid index on 3rd axis */ int j; /* Loop count */ int maxid; /* Largest id for any peak (smallest is zero) */ int minpix; /* Minimum total size of a clump in pixels */ int more; /* Continue looping? */ int ngood; /* Number of good clumps */ int nlow; /* Number of clumps with low peaks */ int nedge; /* Number of clumps that touch an edge of the array */ int nthin; /* Number of clumps that span only a single pixel */ int nsmall; /* Number of clumps with too few pixels */ int skip[3]; /* Pointer to array of axis skips */ /* Initialise */ ret = NULL; /* Abort if an error has already occurred. */ if( *status != SAI__OK ) return ret; /* Initialise things to avoid compiler warnings. */ peakvals = NULL; nrem = NULL; /* Say which method is being used. */ msgBlankif( MSG__NORM, status ); msgOutif( MSG__NORM, "", "FellWalker:", status ); msgBlankif( MSG__VERB, status ); /* Return the instrumental smoothing FWHMs */ if( ! perspectrum ) { beamcorr[ 0 ] = cupidConfigD( config, "FWHMBEAM", 2.0, status ); beamcorr[ 1 ] = beamcorr[ 0 ]; if( ndim == 3 ) { beamcorr[ 2 ] = beamcorr[ 0 ]; beamcorr[ velax ]= cupidConfigD( config, "VELORES", 2.0, status ); } } else { beamcorr[ 0 ] = 0.0; beamcorr[ 1 ] = 0.0; beamcorr[ 2 ] = 0.0; beamcorr[ velax ]= cupidConfigD( config, "VELORES", 2.0, status ); } /* Find the size of each dimension of the data array, and the total number of elements in the array, and the skip in 1D vector index needed to move by pixel along an axis. We use the memory management functions of the AST library since they provide greater security and functionality than direct use of malloc, etc. */ el = 1; for( i = 0; i < ndim; i++ ) { dims[ i ] = subnd[ i ] - slbnd[ i ] + 1; el *= dims[ i ]; skip[ i ] = ( i == 0 ) ? 1 : skip[ i - 1 ]*dims[ i - 1 ]; } for( ; i < 3; i++ ) { dims[ i ] = 1; skip[ i ] = 0; } /* Assign work array to hold the clump assignments. */ ipa = astMalloc( sizeof( int )*el ); /* Get the RMS noise level to use. */ rms = cupidConfigD( config, "RMS", rms, status ); /* Assign every data pixel to a clump and stores the clumps index in the corresponding pixel in "ipa". */ maxid = cupidFWMain( type, ipd, el, ndim, dims, skip, slbnd, rms, config, ipa, ( ndim > 2 && perspectrum ) ? velax + 1 : 0, status ); /* Abort if no clumps found. */ if( maxid < 0 ) { msgOutif( MSG__NORM, "", "No usable clumps found.", status ); msgBlankif( MSG__NORM, status ); goto L10; } /* Allocate an array used to store the number of pixels remaining in each clump. */ nrem = astMalloc( sizeof( int )*( maxid + 1 ) ); /* Allocate an array used to store the peak value in every clump. */ peakvals = astMalloc( sizeof( double )*( maxid + 1 ) ); /* Determine the bounding box of every clump. First allocate memory to hold the bounding boxes, etc. */ clbnd = astMalloc( sizeof( int )*( maxid + 1 )*3 ); cubnd = astMalloc( sizeof( int )*( maxid + 1 )*3 ); igood = astMalloc( sizeof( int )*( maxid + 1 ) ); if( cubnd ) { /* Get the lowest data value to be considered. */ noise = cupidConfigRMS( config, "NOISE", rms, 2.0*rms, status ); /* Get the minimum dip between two adjoining peaks necessary for the two peaks to be considered distinct. */ mindip = cupidConfigRMS( config, "MINDIP", rms, 2.0*rms, status ); /* Get the lowest allowed clump peak value. */ minhgt = cupidConfigRMS( config, "MINHEIGHT", rms, noise, status ); /* See if clumps are allowed to touch an edge of the data array. */ allow_edge = cupidConfigI( config, "ALLOWEDGE", 1, status ); /* Get the minimum allowed number of pixels in a clump. */ if( ! perspectrum ) { minpix = cupidDefMinPix( ndim, beamcorr, noise, minhgt, status ); } else { minpix = 3; } minpix = cupidConfigI( config, "MINPIX", minpix, status ); /* Initialise a list to hold zero for every clump id. These values are used to count the number of pixels remaining in each clump. Also initialise the peak values to a very negative value. */ for( i = 0; i <= maxid; i++ ) { nrem[ i ] = 0; peakvals[ i ] = VAL__MIND; } /* Initialise the bounding boxes. */ for( i = 0; i < 3*( maxid + 1 ); i++ ) { clbnd[ i ] = VAL__MAXI; cubnd[ i ] = VAL__MINI; } /* Loop round every pixel in the final pixel assignment array. */ if( type == CUPID__DOUBLE ) { pd = (double *) ipd; pf = NULL; } else { pf = (float *) ipd; pd = NULL; } pa = ipa; for( iz = 1; iz <= dims[ 2 ]; iz++ ){ for( iy = 1; iy <= dims[ 1 ]; iy++ ){ for( ix = 1; ix <= dims[ 0 ]; ix++, pa++ ){ /* Get the data value at this pixel */ if( type == CUPID__DOUBLE ) { pv = *(pd++); } else { pv = (double) *(pf++); } /* Skip pixels which are not in any clump. */ if( *pa >= 0 ) { /* Increment the number of pixels in this clump. */ ++( nrem[ *pa ] ); /* If this pixel value is larger than the current peak value for this clump, record it. */ if( pv > (double) peakvals[ *pa ] ) peakvals[ *pa ] = pv; /* Get the index within the clbnd and cubnd arrays of the current bounds on the x axis for this clump. */ i = 3*( *pa ); /* Update the bounds for the x axis, then increment to get the index of the y axis bounds. */ if( ix < clbnd[ i ] ) clbnd[ i ] = ix; if( ix > cubnd[ i ] ) cubnd[ i ] = ix; i++; /* Update the bounds for the y axis, then increment to get the index of the z axis bounds. */ if( iy < clbnd[ i ] ) clbnd[ i ] = iy; if( iy > cubnd[ i ] ) cubnd[ i ] = iy; i++; /* Update the bounds for the z axis. */ if( iz < clbnd[ i ] ) clbnd[ i ] = iz; if( iz > cubnd[ i ] ) cubnd[ i ] = iz; } } } } /* Loop round counting the clumps which are too small or too low. Put the indices of usable clumps into another array. */ nsmall = 0; nlow = 0; ngood = 0; nthin = 0; nedge = 0; for( i = 0; i <= maxid; i++ ) { j = 3*i; if( nrem[ i ] <= minpix ) { nsmall++; } else if( peakvals[ i ] < minhgt ) { nlow++; } else if( ( ndim < 3 || !perspectrum ) && ( clbnd[ j ] == cubnd[ j ] || ( clbnd[ j + 1 ] == cubnd[ j + 1 ] && ndim > 1 ) || ( clbnd[ j + 2 ] == cubnd[ j + 2 ] && ndim > 2 ) ) ) { nthin++; } else if ( !allow_edge && ( clbnd[ j ] < 3 || cubnd[ j ] > dims[ 0 ] - 1 || ( ( clbnd[ j + 1 ] < 3 || cubnd[ j + 1 ] > dims[ 1 ] - 1 ) && ndim > 1 ) || ( ( clbnd[ j + 2 ] < 3 || cubnd[ j + 2 ] > dims[ 2 ] - 1 ) && ndim > 2 ) ) ){ nedge++; } else { igood[ ngood++ ] = i; } } if( ngood == 0 ) msgOutif( MSG__NORM, "", "No usable clumps found.", status ); if( nsmall == 1 ){ msgOutif( MSG__NORM, "", "One clump rejected because it contains too few pixels.", status ); } else if( nsmall > 0 ){ msgSeti( "N", nsmall ); msgOutif( MSG__NORM, "", "^N clumps rejected because they contain too few pixels.", status ); } if( nlow == 1 ){ msgOutif( MSG__NORM, "", "One clump rejected because its peak is too low.", status ); } else if( nlow > 0 ){ msgSeti( "N", nlow ); msgOutif( MSG__NORM, "", "^N clumps rejected because the peaks are too low.", status ); } if( nthin == 1 ) { msgOutif( MSG__NORM, "", "1 clump rejected because it spans only a single " "pixel along one or more axes.", status ); } else if( nthin > 1 ) { msgSeti( "N", nthin ); msgOutif( MSG__NORM, "", "^N clumps rejected because they spans only a single " "pixel along one or more axes.", status ); } if( nedge == 1 ) { msgOutif( MSG__NORM, "", "1 clump rejected because it touches an edge " "of the array.", status ); } else if( nedge > 1 ) { msgSeti( "N", nedge ); msgOutif( MSG__NORM, "", "^N clumps rejected because they touch an edge " "of the array.", status ); } /* Sort the clump indices into descending order of peak value. */ j = ngood; more = 1; while( more ) { j--; more = 0; for( i = 0; i < j; i++ ) { if( peakvals[ igood[ i ] ] < peakvals[ igood[ i + 1 ] ] ) { ii = igood[ i + 1 ]; igood[ i + 1 ] = igood[ i ]; igood[ i ] = ii; more = 1; } } } /* Loop round creating an NDF describing each usable clump. */ for( j = 0; j < ngood; j++ ) { i = igood[ j ]; ret = cupidNdfClump( type, ipd, ipa, el, ndim, dims, skip, slbnd, i, clbnd + 3*i, cubnd + 3*i, NULL, ret, cupidConfigD( config, "MAXBAD", 0.05, status ), status ); } /* Free resources */ clbnd = astFree( clbnd ); cubnd = astFree( cubnd ); igood = astFree( igood ); } L10:; /* Free resources */ ipa = astFree( ipa ); nrem = astFree( nrem ); peakvals = astFree( peakvals ); /* Return the list of clump NDFs. */ return ret; }
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 smurf_sc2threadtest( int *status ) { /* Local Variables */ smfArray **res=NULL; /* array of smfArrays of test data */ smfData *data=NULL; /* Pointer to SCUBA2 data struct */ dim_t datalen; /* Number of data points */ smfFilter *filt=NULL; /* Frequency domain filter */ size_t i; /* Loop counter */ size_t j; /* Loop counter */ smfTimeChunkData *job_data=NULL; /* Array of pointers for job data */ size_t joblen; /* Number of chunks per job */ size_t k; /* Loop counter */ size_t nchunks; /* Number of chunks */ size_t nsub; /* Number of subarrays */ int nthread; /* Number of threads */ smfTimeChunkData *pdata=NULL; /* Pointer to data for single job */ int temp; /* Temporary integer */ size_t tsteps; /* How many time steps in chunk */ struct timeval tv1, tv2; /* Timers */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ double *dat=NULL; dim_t nbolo; dim_t ntslice; dim_t ndata; size_t bstride; size_t tstride; dim_t offset; if (*status != SAI__OK) return; /* Get input parameters */ parGdr0i( "NTHREAD", 1, 1, NUM__MAXI, 1, &nthread, status ); parGdr0i( "TSTEPS", 6000, 0, NUM__MAXI, 1, &temp, status ); tsteps = (size_t) temp; parGdr0i( "NCHUNKS", 1, 1, NUM__MAXI, 1, &temp, status ); nchunks = (size_t) temp; parGdr0i( "NSUB", 1, 1, 4, 1, &temp, status ); nsub = (size_t) temp; msgSeti("N",nthread); msgOut( "", TASK_NAME ": Running test with ^N threads", status ); /*** TIMER ***/ smf_timerinit( &tv1, &tv2, status ); /* Create some fake test data in the form of an array of smfArrays */ msgSeti("T",tsteps); msgSeti("C",nchunks); msgSeti("NS",nsub); msgOut( "", TASK_NAME ": Creating ^NS subarrays of data with ^C chunks * ^T samples", status ); res = astCalloc( nchunks, sizeof(*res) ); for( k=0; (*status==SAI__OK)&&(k<nchunks); k++ ) { res[k] = smf_create_smfArray( status ); for( i=0; (*status==SAI__OK)&&(i<nsub); i++ ) { /* Create individual smfDatas and add to array */ data = smf_create_smfData( SMF__NOCREATE_FILE | SMF__NOCREATE_DA | SMF__NOCREATE_FTS, status ); if( *status==SAI__OK ) { data->dtype=SMF__DOUBLE; data->ndims=3; data->dims[0]=40; data->dims[1]=32; data->dims[2]=(dim_t) tsteps; datalen=1; data->isFFT=-1; for( j=0; j<data->ndims; j++ ) datalen *= data->dims[j]; data->hdr->steptime = 0.005; data->pntr[0] = astCalloc( datalen, smf_dtype_sz(data->dtype,status) ); data->qual = astCalloc( datalen, sizeof(*data->qual) ); } smf_addto_smfArray( res[k], data, status ); } } /*** TIMER ***/ msgOutf( "", "** %f seconds generating data", status, smf_timerupdate(&tv1,&tv2,status) ); msgOut( "", TASK_NAME ": Starting test 1 __parallel time: dataOrder__", status ); /* Create a pool of threads. */ wf = thrGetWorkforce( nthread, status ); /* Work out number of chunks per thread */ joblen = nchunks/nthread; if( joblen == 0 ) joblen = 1; /* At least one chunk per thread */ /* The first test will process separate time chunks of data in parallel, re-ordering each to bolo-ordered format. All subarrays and an integer number of input file chunks all go into a single thread. Start by allocating and initializing a number of smfTimeChunkData's that hold the information required for each thread */ job_data = astCalloc( nthread, sizeof(*job_data) ); for( i=0; (i<(size_t)nthread) && (*status==SAI__OK); i++ ) { pdata = job_data + i; pdata->type = 0; /* Start with a data re-order */ pdata->data = res; /* Pointer to main data array */ pdata->chunk1 = i*joblen; /* Index of first chunk for job */ pdata->nchunks = nchunks; /* Total number of time chunks in data */ pdata->ijob = -1; /* Flag job as available to do work */ /* The last thread has to pick up the remainder of chunks */ if( i==(size_t)(nthread-1) ) pdata->chunk2=nchunks-1; else pdata->chunk2 = (i+1)*joblen-1; /* Index of last chunk for job */ /* Ensure a valid chunk range, or set to a length that we know to ignore */ if( pdata->chunk1 >= nchunks ) { pdata->chunk1 = nchunks; pdata->chunk2 = nchunks; } else if( pdata->chunk2 >= nchunks ) { pdata->chunk2 = nchunks-1; } if( pdata->chunk1 >= nchunks ) { /* Nothing for this thread to do */ msgSeti( "W", i+1); msgOutif( MSG__DEBUG, "", "-- parallel time: skipping thread ^W, nothing to do", status); } else { /* Since we know there is one job_data per thread, just submit jobs immediately */ pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata, smfParallelTime, 0, NULL, status ); } } /* Wait until all of the submitted jobs have completed */ thrWait( wf, status ); /* Annul the bad status that we set in smfParallelTime */ if( *status == SMF__INSMP ) { errAnnul( status ); msgOut( "", " *** Annulled SMF__INSMP set in smfParallelTime *** ", status ); } else { msgOut( "", " *** Flushing good status *** ", status ); errFlush( status ); } /*** TIMER ***/ msgOutf( "", "** %f seconds to complete test", status, smf_timerupdate(&tv1,&tv2,status) ); /* The second test will boxcar smooth bolometers from time chunks in parallel */ msgOut( "", TASK_NAME ": Starting test 2 __parallel time: boxcar smooth__", status ); for( i=0; (i<(size_t)nthread) && (*status==SAI__OK); i++ ) { pdata = job_data + i; pdata->type = 1; /* Boxcar smooth */ if( pdata->chunk1 >= nchunks ) { /* Nothing for this thread to do */ msgSeti( "W", i+1); msgOutif( MSG__DEBUG, "", "-- parallel time: skipping thread ^W, nothing to do", status); } else { /* Since we know there is one job_data per thread, just submit jobs immediately */ pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata, smfParallelTime, 0, NULL, status ); } } /* Wait until all of the submitted jobs have completed */ thrWait( wf, status ); /*** TIMER ***/ msgOutf( "", "** %f seconds to complete test", status, smf_timerupdate(&tv1,&tv2,status) ); msgOut( "", TASK_NAME ": *** Next 2 tests will be done twice due to FFTW planning *****", status ); for( k=0; k<2; k++ ) { /* The third test will FFT filter bolometers from time chunks in parallel */ msgOut( "", TASK_NAME ": Starting test 3 __parallel time: FFT filter__", status ); for( i=0; (i<(size_t)nthread) && (*status==SAI__OK); i++ ) { pdata = job_data + i; pdata->type = 2; /* FFT filter */ if( pdata->chunk1 >= nchunks ) { /* Nothing for this thread to do */ msgSeti( "W", i+1); msgOutif( MSG__DEBUG, "", "-- parallel time: skipping thread ^W, nothing to do", status); } else { /* Since we know there is one job_data per thread, just submit jobs immediately */ pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata, smfParallelTime, 0, NULL, status ); } } /* Wait until all of the submitted jobs have completed */ thrWait( wf, status ); /*** TIMER ***/ msgOutf( "", "** %f seconds to complete test", status, smf_timerupdate(&tv1,&tv2,status) ); msgOut( "", TASK_NAME ": Starting test 4 __FFTW filter using internal threading__", status ); for( i=0; (*status==SAI__OK)&&(i<nchunks); i++ ) { filt = smf_create_smfFilter( res[i]->sdata[0], status ); smf_filter_ident( filt, 1, status ); for( j=0; (*status==SAI__OK)&&(j<nsub); j++ ) { msgOutiff( MSG__DEBUG, "", " filter chunk %zu/%zu, bolo %zu/%zu", status, i+1, nchunks, j+1, nsub ); smf_filter_execute( wf, res[i]->sdata[j], filt, 0, 0, status ); } if( filt ) filt = smf_free_smfFilter( filt, status ); } /*** TIMER ***/ msgOutf( "", "** %f seconds to complete test", status, smf_timerupdate(&tv1,&tv2,status) ); } msgOut( "", TASK_NAME ": **************************************************************", status ); /* Series of short single-thread array index tests */ data = res[0]->sdata[0]; dat = data->pntr[0]; smf_get_dims( data, NULL, NULL, &nbolo, &ntslice, &ndata, &bstride, &tstride, status ); msgOut("","Array index test #1: two multiplies in inner loop",status); smf_timerinit( &tv1, &tv2, status ); for( i=0; i<nbolo; i++ ) { for( j=0; j<ntslice; j++ ) { dat[i*bstride + j*tstride] += 5; } } msgOutf( "", "** %f seconds to complete test", status, smf_timerupdate(&tv1,&tv2,status) ); msgOut("","Array index test #2: only index increments",status); smf_timerinit( &tv1, &tv2, status ); for( i=0; i<nbolo*bstride; i+=bstride ) { for( j=i; j<(i+ntslice*tstride); j+=tstride ) { dat[j] += 5; } } msgOutf( "", "** %f seconds to complete test", status, smf_timerupdate(&tv1,&tv2,status) ); msgOut("","Array index test #3: one multiply in outer loop",status); smf_timerinit( &tv1, &tv2, status ); offset = 0; for( i=0; i<nbolo; i++ ) { offset = i*bstride; for( j=0; j<ntslice; j++ ) { dat[offset] += 5; offset += tstride; } } msgOutf( "", "** %f seconds to complete test", status, smf_timerupdate(&tv1,&tv2,status) ); /* Clean up */ if( res ) { for( i=0; i<nchunks; i++ ) { if( res[i] ) { smf_close_related( &res[i], status ); } } res = astFree( res ); } job_data = astFree( job_data ); /* Ensure that FFTW doesn't have any used memory kicking around */ fftw_cleanup(); }