int cupidRFillClumps( int *ipa, int *out, int nel, int ndim, int skip[ 3 ],
                      int dims[ 3 ], int peakval, int *status ){
/*
*+
*  Name:
*     cupidRFillClumps

*  Purpose:
*     Identify clumps by filling in the volume enclosed by the marked
*     edges.

*  Language:
*     Starlink C

*  Synopsis:
*     int cupidRFillClumps( int *ipa, int *out, int nel, int ndim,
*                           int skip[ 3 ], int dims[ 3 ], int peakval,
*                           int *status )

*  Description:
*     This function is supplied with an array in which pixels marking the
*     edges of clumps are flagged using the value CUPID__KEDGE, and
*     pixels marking the peak value within a clump are marked by the
*     value "peakval". It assigns a unique integer index to each peak
*     and then finds the extent of the clump surrounding the peak. In the
*     returned array, all pixels assigned to a peaks clump hold the integer
*     index associated with the clump. Pixels not in any clump have the
*     value -INT_MAX. Edge pixels are not considered to be part of any clump.
*
*     If more than one peak claims a pixel, the pixel is given to the
*     closest peak.
*
*     Note, the algorithm used for filling is not fool-proof and cannot
*     in general deal with clumps which are "S" shaped (for instance).

*  Parameters:
*     ipa
*        Pointer to an array which is the same shape and size as the data
*        array, and which holds a flag for every pixel. On entry, if the
*        pixel is an edge pixel this flag will be CUPID__KEDGE. If it is a
*        peak pixel it will have the value "peakval". Unchanged on exit.
*     out
*        Pointer to an array which is the same shape and size as the data
*        array, and which holds a flag for every pixel. On exit it holds
*        the pixel index (0 or more) at all pixels which are deemed to be
*        within a clump, and -INT_MAX everywhere else.
*     nel
*        The number of elements in "ipa".
*     ndim
*        The number of pixel axes in the data (this can be less than 3).
*     skip
*        The increment in 1D vector index required to move a distance of 1
*        pixel along each axis. This allows conversion between indexing
*        the array using a single 1D vector index and using nD coords. This
*        array should have 3 elements even if there are less than 3 pixel
*        axes, and the extra elements should be filled with zero's.
*     dims
*        The no. of pixels along each pixel axis. This array should have 3
*        elements even if there are less than 3 pixel axes, and the extra
*        elements should be filled with one's.
*     peakval
*        The "ipa" value used to flag peaks.
*     status
*        Pointer to the inherited status value.

*  Returned Value:
*     The largest integer clump identifier present in the "out" array.
*     The smallest identifier value is zero.

*  Copyright:
*     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
*     {enter_new_authors_here}

*  History:
*     24-JAN-2006 (DSB):
*        Original version.
*     {enter_further_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
*/

/* Local Variables: */

   int *pa;              /* Pointer to next "ipa" element */
   int gp[ 3 ];          /* Grid coords of peak position */
   int i;                /* Index of next "ipa" element */
   int ipeak;            /* Index of next clump */
   int ix;               /* The X Grid coord of the current pixel */
   int iy;               /* The Y Grid coord of the current pixel */
   int iz;               /* The Z Grid coord of the current pixel */
   int npeak;            /* Number of peaks being produced */
   int *gpeak[ 3 ];      /* Pointers to arrays of peak axis values */

/* Initialise */
   ipeak = -1;

/* Abort if an error has already occurred. */
   if( *status != SAI__OK ) return ipeak;

/* Fill the output array with -INT_MAX. */
   for( i = 0; i < nel; i++ ) out[ i ] = -INT_MAX;

/* So far we have no peaks */
   npeak = 0;
   gpeak[ 0 ] = astMalloc( sizeof( int )*30 );
   gpeak[ 1 ] = astMalloc( sizeof( int )*30 );
   gpeak[ 2 ] = astMalloc( sizeof( int )*30 );

/* Scan the ipa array looking for peaks. */
   pa = ipa;
   i = 0;

   for( iz = 1; iz <= dims[ 2 ]; iz++ ) {
      for( iy = 1; iy <= dims[ 1 ]; iy++ ) {
         for( ix = 1; ix <= dims[ 0 ]; ix++, i++, pa++ ) {
            if( *pa == peakval ) {

/* Find the integer identifier for this peak. */
               ipeak = npeak++;

/* Save thre grid coords of the peak in the gpeak array, extending it if
   necessary to make room. */
               gpeak[ 0 ] = astGrow( gpeak[ 0 ], npeak, sizeof( int ) );
               gpeak[ 1 ] = astGrow( gpeak[ 1 ], npeak, sizeof( int ) );
               gpeak[ 2 ] = astGrow( gpeak[ 2 ], npeak, sizeof( int ) );
               if( gpeak[ 2 ] ) {
                  gpeak[ 0 ][ ipeak ] = ix;
                  gpeak[ 1 ][ ipeak ] = iy;
                  gpeak[ 2 ][ ipeak ] = iz;
               }

/* Fill the volume between the edges marked in the "ipa" array by first
   moving out away from the peak along a 1D line parallel to the X axis
   until edge pixels are encountered. At each position along this line,
   store the "ipeak" value in the corresponding pixel of the "out" array,
   and then move out away from the position along a 1D line parallel to
   the Y axis until edge pixels are encountered. At each position along
   this line, store the "ipeak" value in the corresponding pixel of the
   "out" array, and then move out away from the position along a 1D line
   parallel to the Z axis until edge pixels are encountered. At each
   position along this line, store the "ipeak" value in the corresponding
   pixel of the "out" array. */
               gp[ 0 ] = ix;
               gp[ 1 ] = iy;
               gp[ 2 ] = iz;
               cupidRFillLine( ipa, out, nel, ndim, skip, dims, gp, i, 0,
                               ipeak, 1, gpeak, status );
            }
         }
      }
   }

/* If we are dealing with 2 or 3 d data, we do the whole process again
   (without re-initialising the "out" array), but this time scanning the
   axes in the order Y, Z, X (instead of X,Y,Z). */
   if( ndim > 1 ) {
      npeak = 0;
      pa = ipa;
      i = 0;

      for( iz = 1; iz <= dims[ 2 ]; iz++ ) {
         for( iy = 1; iy <= dims[ 1 ]; iy++ ) {
            for( ix = 1; ix <= dims[ 0 ]; ix++, i++, pa++ ) {
               if( *pa == peakval ) {
                  ipeak = npeak++;
                  gp[ 0 ] = ix;
                  gp[ 1 ] = iy;
                  gp[ 2 ] = iz;
                  cupidRFillLine( ipa, out, nel, ndim, skip, dims, gp, i, 1,
                                  ipeak, 1, gpeak, status );
               }
            }
         }
      }

/* If we are dealing with 3 d data, we do the whole process again (without
   re-initialising the "out" array), but this time scanning the axes in the
   order Z, X, Y. */
      if( ndim > 2 ) {
         npeak = 0;
         pa = ipa;
         i = 0;

         for( iz = 1; iz <= dims[ 2 ]; iz++ ) {
            for( iy = 1; iy <= dims[ 1 ]; iy++ ) {
               for( ix = 1; ix <= dims[ 0 ]; ix++, i++, pa++ ) {
                  if( *pa == peakval ) {
                     ipeak = npeak++;
                     gp[ 0 ] = ix;
                     gp[ 1 ] = iy;
                     gp[ 2 ] = iz;
                     cupidRFillLine( ipa, out, nel, ndim, skip, dims, gp, i, 2,
                                     ipeak, 1, gpeak, status );
                  }
               }
            }
         }
      }
   }

/* Free resources. */
   gpeak[ 0 ] = astFree( gpeak[ 0 ] );
   gpeak[ 1 ] = astFree( gpeak[ 1 ] );
   gpeak[ 2 ] = astFree( gpeak[ 2 ] );

/* Return the largest identifier present in "out". */
   return ipeak;

}
Exemplo n.º 2
0
/* Main entry */
void smf_jsadicer( int indf, const char *base, int trim, smf_inst_t instrument,
                   smf_jsaproj_t proj, size_t *ntile, Grp *grp, int *status ){

/* Local Variables: */
   AstBox *box;
   AstFitsChan *fc;
   AstFrame *specfrm = NULL;
   AstFrame *tile_frm = NULL;
   AstFrameSet *iwcs;
   AstFrameSet *tfs = NULL;
   AstFrameSet *tile_wcs;
   AstMapping *ndf_map = NULL;
   AstMapping *p2pmap = NULL;
   AstMapping *specmap = NULL;
   AstMapping *tile_map = NULL;
   AstRegion *region;
   Grp *grpt = NULL;
   char *path;
   char dtype[ NDF__SZFTP + 1 ];
   char jsatile_comment[45];
   char type[ NDF__SZTYP + 1 ];
   const char *dom = NULL;
   const char *keyword;
   const char *latsys = NULL;
   const char *lonsys = NULL;
   double *pd;
   double dlbnd[3];
   double dubnd[3];
   double gcen[3];
   double lbnd_in[3];
   double lbnd_out[3];
   double ubnd_in[3];
   double ubnd_out[3];
   float *pf;
   int *created_tiles = NULL;
   int *tiles;
   int axlat;
   int axlon;
   int axspec;
   int bbox[ 6 ];
   int i;
   int ifrm;
   int igrid;
   int indfo;
   int indfs;
   int indfx;
   int inperm[3];
   int ipixel;
   int ishpx;
   int isxph;
   int itile;
   int ix;
   int iy;
   int iz;
   int junk;
   int latax = -1;
   int lbnd[3];
   int lbnd_tile[ 3 ];
   int lbndx[ NDF__MXDIM ];
   int lonax = -1;
   int nbase;
   int ndim;
   int ndimx;
   int nfrm;
   int nsig;
   int ntiles;
   int olbnd[ 3 ];
   int oubnd[ 3 ];
   int outperm[ 3 ];
   int place;
   int qual;
   int tile_index;
   int tile_lbnd[2];
   int tile_ubnd[2];
   int ubnd[3];
   int ubnd_tile[ 3 ];
   int ubndx[ NDF__MXDIM ];
   int var;
   size_t iext;
   size_t size;
   smfJSATiling tiling;
   unsigned char *ipq = NULL;
   void *ipd = NULL;
   void *ipv = NULL;

/* Initialise */
   *ntile = 0;

/* Check inherited status */
   if( *status != SAI__OK ) return;

/* Begin an AST context. */
   astBegin;

/* Begin an NDF context. */
   ndfBegin();

/* Note the used length of the supplied base string. If it ends with
   ".sdf", reduce it by 4. */
   nbase = astChrLen( base );
   if( !strcmp( base + nbase - 4, ".sdf" ) ) nbase -= 4;

/* Allocate a buffer large enough to hold the full path for an output NDF. */
   path = astMalloc( nbase + 25 );

/* Get the WCS from the NDF. */
   kpg1Gtwcs( indf, &iwcs, status );

/* Note if the NDF projection is HPX or XPH. */
   ishpx = astChrMatch( astGetC( iwcs, "Projection" ), "HEALPix" );
   isxph = astChrMatch( astGetC( iwcs, "Projection" ), "polar HEALPix" );

/* Report an error if the NDFs projection is neither of these. */
   if( !ishpx && !isxph && *status == SAI__OK ) {
      ndfMsg( "N", indf );
      *status = SAI__ERROR;
      errRep( "", "The input NDF (^N) does not appear to be gridded "
              "on the JSA all-sky pixel grid.", status );
   }

/* Get the bounds of the NDF in pixel indices and the the corresponding
   double precision GRID bounds (reduce the size of the grid by a small
   amount to avoid problems with tiles that are on the edge of the valid sky
   regions - astMapRegion can report an error for such tiles). Also store
   the GRID coords of the centre. Also count the number of significant
   pixel axes. */
   ndfBound( indf, 3, lbnd, ubnd, &ndim, status );
   nsig = 0;
   for( i = 0; i < ndim; i++ ) {
      dlbnd[ i ] = 0.5 + 0.1;
      dubnd[ i ] = ubnd[ i ] - lbnd[ i ]  + 1.5 - 0.1;
      gcen[ i ] = 0.5*( dlbnd[ i ] + dubnd[ i ] );
      if( ubnd[ i ] > lbnd[ i ] ) nsig++;
   }

/* Find the one-based indices of the RA, Dec and spectral axes in the
   current Frame of the NDF. */
   axlon = 0;
   if( astGetI( iwcs, "IsLonAxis(1)" ) ) {
      axlon = 1;
      lonsys = astGetC( iwcs, "System(1)" );
   } else if( astGetI( iwcs, "IsLonAxis(2)" ) ) {
      axlon = 2;
      lonsys = astGetC( iwcs, "System(2)" );
   } else if( ndim == 3 && astGetI( iwcs, "IsLonAxis(3)" ) ) {
      axlon = 3;
      lonsys = astGetC( iwcs, "System(3)" );
   } else if( *status == SAI__OK ) {
      *status = SAI__ERROR;
      errRep( "", "smf_jsadicer: Cannot find the longitude axis in the "
              "input NDF.", status );
   }

   axlat = 0;
   if( astGetI( iwcs, "IsLatAxis(1)" ) ) {
      axlat = 1;
      latsys = astGetC( iwcs, "System(1)" );
   } else if( astGetI( iwcs, "IsLatAxis(2)" ) ) {
      axlat = 2;
      latsys = astGetC( iwcs, "System(2)" );
   } else if( ndim == 3 && astGetI( iwcs, "IsLatAxis(3)" ) ) {
      axlat = 3;
      latsys = astGetC( iwcs, "System(3)" );
   } else if( *status == SAI__OK ) {
      *status = SAI__ERROR;
      errRep( "", "smf_jsadicer: Cannot find the latitude axis in the "
              "input NDF.", status );
   }

   axspec = 6 - axlon - axlat;

/* Report an error if the spatial axes are not ICRS RA and Dec. */
   if( ( lonsys && strcmp( lonsys, "ICRS" ) ) ||
       ( latsys && strcmp( latsys, "ICRS" ) ) ) {
      if( *status == SAI__OK ) {
         *status = SAI__ERROR;
         ndfMsg( "N", indf );
         errRep( "", "smf_jsadicer: The spatial axes in '^N' are not "
                 "ICRS RA and Dec.", status );
      }
   }

/* Create a Box describing the region covered by the NDF pixel grid in
   GRID coords. */
   box = astBox( astGetFrame( iwcs, AST__BASE ), 1, dlbnd, dubnd,
                 AST__NULL, " " );

/* Map this Box into the current WCS Frame of the NDF. */
   region = astMapRegion( box, iwcs, iwcs );

/* If no instrument was specified, we will determine the instrument from
   the contexts of the FITS extension. Copy the NDF FITS extension to a
   FitsChan. Annul the error if the NDF no FITS extension. */
   if( instrument == SMF__INST_NONE && *status == SAI__OK ) {
      kpgGtfts( indf, &fc, status );
      if( *status == KPG__NOFTS ) {
         errAnnul( status );
         fc = NULL;
      }
   } else {
      fc = NULL;
   }

/* Get the parameters of the required tiling scheme. */
   smf_jsainstrument( NULL, fc, instrument, &tiling, status );

/* Get a list of the JSA tiles touched by the supplied NDF. */
   tiles = smf_jsatiles_region( region, &tiling, &ntiles, status );
   if( ntiles == 0 && *status == SAI__OK ) {
      *status = SAI__ERROR;
      errRep( "", "smf_jsadicer: No JSA tiles found touching supplied NDF "
              "(programming error).", status );
   }

/* Does the input NDF have a Variance component? */
   ndfState( indf, "Variance", &var, status );

/* Does the input NDF have a Quality component? */
   ndfState( indf, "Quality", &qual, status );

/* Decide on the data type to use: _REAL or _DOUBLE. */
   ndfMtype( "_REAL,_DOUBLE", indf, indf, "Data", type, sizeof(type), dtype,
             sizeof(dtype), status );

/* Tell the user what is happening. */
   msgBlank( status );
   msgOutf( "", "Dicing %s into JSA tiles:", status,
            ( nsig == 2 ) ? "map" : "cube" );

/* Loop round all tiles that overlap the supplied NDF. */
   for( itile = 0; itile < ntiles && *status == SAI__OK; itile++ ) {
      tile_index = tiles[ itile ];

/* Get the spatial pixel bounds of the current tile within the requested
   JSA all-sky projection. Also get the (2D) WCS FrameSet for the tile. */
      smf_jsatile( tile_index, &tiling, 0, proj, NULL, &tile_wcs, NULL,
                   tile_lbnd, tile_ubnd, status );

/* Extract the tile pixel->WCS mapping and WCS Frame. We know the indices
   of the required Frames because they are hard-wired in smf_jsatile. */
      tile_map = astGetMapping( tile_wcs, 3, 2 );
      tile_frm = astGetFrame( tile_wcs, 2 );

/* Find the indices of the grid and pixel frames in the input NDF. */
      ipixel = -1;
      igrid = astGetI( iwcs, "Base" );
      nfrm = astGetI( iwcs, "NFrame" );
      for( ifrm = 0; ifrm < nfrm; ifrm++ ) {
         dom = astGetC( astGetFrame( iwcs, ifrm + 1 ), "Domain" );
         if( astChrMatch( dom, "PIXEL" ) ) ipixel = ifrm + 1;
      }

/* If required, extract the pixel->spectral mapping and spectral frame in
   the input NDF, and add it in parallel with the above tile mapping. */
      if( ndim == 3 ) {
         astSetI( iwcs, "Base", ipixel );
         tfs = atlFrameSetSplit( iwcs, "DSBSPECTRUM SPECTRUM", NULL,
                                 NULL, status );
         astSetI( iwcs, "Base", igrid );
         if( tfs ) {
            specmap = astGetMapping( tfs, AST__BASE, AST__CURRENT );
            specfrm = astGetFrame( tfs, AST__CURRENT );
         } else if( *status == SAI__OK ) {
            *status = SAI__ERROR;
            ndfMsg( "N", indf );
            errRep( "", "smf_jsadicer: Cannot find the spectral axis "
                    "in '^N'.", status );
         }

         tile_map = (AstMapping *) astCmpMap( tile_map, specmap, 0, " " );
         tile_frm = (AstFrame *) astCmpFrame( tile_frm, specfrm, " " );
      }

/* Ensure the Epoch is inherited form the input NDF. */
      astSetD( tile_frm, "Epoch", astGetD( iwcs, "Epoch" ) );

/* Currently tile axis 1 is RA, axis 2 is Dec and axis 3 (if present) is
   spectral. Append a PermMap that re-orders these tile WCS axes to match
   those of the NDF. */
      outperm[ axlon - 1 ] = 1;
      outperm[ axlat - 1 ] = 2;
      outperm[ axspec - 1 ] = 3;
      inperm[ 0 ] = axlon;
      inperm[ 1 ] = axlat;
      inperm[ 2 ] = axspec;
      tile_map = (AstMapping *) astCmpMap( tile_map, astPermMap( ndim, inperm,
                                                                 ndim, outperm,
                                                                 NULL, " " ),
                                           1, " " );
      tile_map = astSimplify( tile_map );

/* Also re-order the WCS axes in the tile frame. */
      astPermAxes( tile_frm, outperm );

/* We want the zero-based indicies of the input pixel axes corresponding
   to ra, dec and spectral. So find the indicies of the pixel axes in the
   supplied NDF that are most closely aligned with each WCS axis. */
      atlPairAxes( iwcs, NULL, gcen, NULL, inperm, status );
      if( inperm[ 0 ] == axlon ) {
         lonax = 0;
      } else if( inperm[ 1 ] == axlon ) {
         lonax = 1;
      } else {
         lonax = 2;
      }
      if( inperm[ 0 ] == axlat ) {
         latax = 0;
      } else if( inperm[ 1 ] == axlat ) {
         latax = 1;
      } else {
         latax = 2;
      }

/* To get the mapping from pixel coords in the input NDF to pixel coords
   in the output NDF, we invert the above mapping so that it goes from WCS
   to pixel, and append it to the end of the NDF pixel->WCS mapping. */
      ndf_map = astGetMapping( iwcs, ipixel, AST__CURRENT );
      astInvert( tile_map );
      p2pmap = (AstMapping *) astCmpMap( ndf_map, tile_map, 1, " " );
      p2pmap = astSimplify( p2pmap );
      astInvert( tile_map );

/* Show the bounds of the tile within the input NDF. */
      msgOutiff( MSG__DEBUG, "", "   tile %d has bounds (%d:%d,%d:%d) "
                 "within the output NDF.", status, tile_index,
                 tile_lbnd[ 0 ], tile_ubnd[ 0 ], tile_lbnd[ 1 ],
                 tile_ubnd[ 1 ] );

/* Next job is to find the pixel bounds of the output NDF to create
   which will hold data for the current tile. First map the pixel bounds
   of the whole tile from output to input. */
      lbnd_in[ 0 ] = tile_lbnd[ 0 ] - 0.5;
      lbnd_in[ 1 ] = tile_lbnd[ 1 ] - 0.5;
      lbnd_in[ 2 ] = lbnd[ 2 ] - 0.5;
      ubnd_in[ 0 ] = tile_ubnd[ 0 ] - 0.5;
      ubnd_in[ 1 ] = tile_ubnd[ 1 ] - 0.5;
      ubnd_in[ 2 ] = ubnd[ 2 ] - 0.5;

      astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 1, lbnd_out + 0,
                 ubnd_out + 0, NULL, NULL );
      astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 2, lbnd_out + 1,
                 ubnd_out + 1, NULL, NULL );
      if( ndim == 3 ) astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 3,
                                 lbnd_out + 2, ubnd_out + 2, NULL,
                                 NULL );


      lbnd_tile[ 0 ] = floor( lbnd_out[ 0 ] ) + 1;
      lbnd_tile[ 1 ] = floor( lbnd_out[ 1 ] ) + 1;
      lbnd_tile[ 2 ] = floor( lbnd_out[ 2 ] ) + 1;
      ubnd_tile[ 0 ] = floor( ubnd_out[ 0 ] ) + 1;
      ubnd_tile[ 1 ] = floor( ubnd_out[ 1 ] ) + 1;
      ubnd_tile[ 2 ] = floor( ubnd_out[ 2 ] ) + 1;

/* Show the bounds of the tile within the input NDF. */
      msgOutiff( MSG__DEBUG, "", "   tile %d has bounds (%d:%d,%d:%d) "
                 "within the input NDF.", status, tile_index,
                 lbnd_tile[ 0 ], ubnd_tile[ 0 ], lbnd_tile[ 1 ],
                 ubnd_tile[ 1 ] );

/* If required, trim the bounds to the extent of the input NDF. */
      if( trim ) {
         if( lbnd_tile[ 0 ] < lbnd[ 0 ] ) lbnd_tile[ 0 ] = lbnd[ 0 ];
         if( lbnd_tile[ 1 ] < lbnd[ 1 ] ) lbnd_tile[ 1 ] = lbnd[ 1 ];
         if( lbnd_tile[ 2 ] < lbnd[ 2 ] ) lbnd_tile[ 2 ] = lbnd[ 2 ];
         if( ubnd_tile[ 0 ] > ubnd[ 0 ] ) ubnd_tile[ 0 ] = ubnd[ 0 ];
         if( ubnd_tile[ 1 ] > ubnd[ 1 ] ) ubnd_tile[ 1 ] = ubnd[ 1 ];
         if( ubnd_tile[ 2 ] > ubnd[ 2 ] ) ubnd_tile[ 2 ] = ubnd[ 2 ];
      }

/* Check there is some overlap. */
      if( lbnd_tile[ 0 ] <= ubnd_tile[ 0 ] &&
          lbnd_tile[ 1 ] <= ubnd_tile[ 1 ] &&
          lbnd_tile[ 2 ] <= ubnd_tile[ 2 ] ){

/* Now need to check if this section of the input NDF contains any good
   values. We also find the bounding box of the good values (within the
   input pixel coordinate system). So first obtain and map the required
   section of the input NDF. */
         ndfSect( indf, ndim, lbnd_tile, ubnd_tile, &indfs, status );
         ndfMap( indfs, "Data", type, "Read", &ipd, &junk, status );
         if( var ) ndfMap( indfs, "Variance", type, "Read", &ipv, &junk, status );
         if( qual ) ndfMap( indfs, "Quality", "_UBYTE", "Read", (void **) &ipq,
                            &junk, status );

/* Initialise the pixel bounds (within the input NDF) of the box holding
   good data values for the current tile. */
         bbox[ 0 ] = INT_MAX;
         bbox[ 1 ] = INT_MAX;
         bbox[ 2 ] = INT_MAX;
         bbox[ 3 ] = -INT_MAX;
         bbox[ 4 ] = -INT_MAX;
         bbox[ 5 ] = -INT_MAX;

/* Loop round all pixels in the section. */
         if( *status == SAI__OK ) {
            if( !strcmp( type, "_REAL" ) ) {
               pf = (float *) ipd;
               for( iz = lbnd_tile[ 2 ]; iz <= ubnd_tile[ 2 ]; iz++ ) {
                  for( iy = lbnd_tile[ 1 ]; iy <= ubnd_tile[ 1 ]; iy++ ) {
                     for( ix = lbnd_tile[ 0 ]; ix <= ubnd_tile[ 0 ]; ix++ ) {
                        if( *(pf++) != VAL__BADR ) {
                           if( ix < bbox[ 0 ] ) bbox[ 0 ] = ix;
                           if( iy < bbox[ 1 ] ) bbox[ 1 ] = iy;
                           if( iz < bbox[ 2 ] ) bbox[ 2 ] = iz;
                           if( ix > bbox[ 3 ] ) bbox[ 3 ] = ix;
                           if( iy > bbox[ 4 ] ) bbox[ 4 ] = iy;
                           if( iz > bbox[ 5 ] ) bbox[ 5 ] = iz;
                        }
                     }
                  }
               }
            } else {
               pd = (double *) ipd;
               for( iz = lbnd_tile[ 2 ]; iz <= ubnd_tile[ 2 ]; iz++ ) {
                  for( iy = lbnd_tile[ 1 ]; iy <= ubnd_tile[ 1 ]; iy++ ) {
                     for( ix = lbnd_tile[ 0 ]; ix <= ubnd_tile[ 0 ]; ix++ ) {
                        if( *(pd++) != VAL__BADD ) {
                           if( ix < bbox[ 0 ] ) bbox[ 0 ] = ix;
                           if( iy < bbox[ 1 ] ) bbox[ 1 ] = iy;
                           if( iz < bbox[ 2 ] ) bbox[ 2 ] = iz;
                           if( ix > bbox[ 3 ] ) bbox[ 3 ] = ix;
                           if( iy > bbox[ 4 ] ) bbox[ 4 ] = iy;
                           if( iz > bbox[ 5 ] ) bbox[ 5 ] = iz;
                        }
                     }
                  }
               }
            }

/* Skip empty tiles. */
            if( bbox[ 0 ] != INT_MAX ) {
               msgOutf( "", "   tile %d", status, tile_index );

/* If required, trim the bounds to the edges of the bounding box. */
               if( trim >= 2 ) {
                  olbnd[ 0 ] = bbox[ 0 ];
                  olbnd[ 1 ] = bbox[ 1 ];
                  olbnd[ 2 ] = bbox[ 2 ];
                  oubnd[ 0 ] = bbox[ 3 ];
                  oubnd[ 1 ] = bbox[ 4 ];
                  oubnd[ 2 ] = bbox[ 5 ];
               } else {
                  olbnd[ 0 ] = lbnd_tile[ 0 ];
                  olbnd[ 1 ] = lbnd_tile[ 1 ];
                  olbnd[ 2 ] = lbnd_tile[ 2 ];
                  oubnd[ 0 ] = ubnd_tile[ 0 ];
                  oubnd[ 1 ] = ubnd_tile[ 1 ];
                  oubnd[ 2 ] = ubnd_tile[ 2 ];
               }

/* Modify these pixel bounds so that they refer to the output NDF. */
               lbnd_in[ 0 ] = olbnd[ 0 ] - 0.5;
               lbnd_in[ 1 ] = olbnd[ 1 ] - 0.5;
               lbnd_in[ 2 ] = olbnd[ 2 ] - 0.5;
               ubnd_in[ 0 ] = oubnd[ 0 ] - 0.5;
               ubnd_in[ 1 ] = oubnd[ 1 ] - 0.5;
               ubnd_in[ 2 ] = oubnd[ 2 ] - 0.5;

               astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 1, lbnd_out + 0,
                          ubnd_out + 0, NULL, NULL );
               astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 2, lbnd_out + 1,
                          ubnd_out + 1, NULL, NULL );
               if( ndim == 3 ) astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 3,
                                          lbnd_out + 2, ubnd_out + 2, NULL,
                                          NULL );

               olbnd[ 0 ] = floor( lbnd_out[ 0 ] ) + 1;
               olbnd[ 1 ] = floor( lbnd_out[ 1 ] ) + 1;
               olbnd[ 2 ] = floor( lbnd_out[ 2 ] ) + 1;
               oubnd[ 0 ] = floor( ubnd_out[ 0 ] ) + 1;
               oubnd[ 1 ] = floor( ubnd_out[ 1 ] ) + 1;
               oubnd[ 2 ] = floor( ubnd_out[ 2 ] ) + 1;

/* Get the full path to the output NDF for the current tile, and create an
   NDF placeholder for it. */
               sprintf( path, "%.*s_%d", nbase, base, tile_index );
               ndfPlace( NULL, path, &place, status );

/* Create a new output NDF by copying the meta-data from the input NDF
   section. */
               ndfScopy( indfs, "Units", &place, &indfo, status );

/* Set the pixel bounds of the output NDF to the values found above and copy
   the input data for the current tile into it. */
               smf1_jsadicer( indfo, olbnd, oubnd, tile_map, tile_frm, p2pmap,
                              ipd, ipv, ipq, status );

/* Add the name of this output NDF to the group holding the names of the
   output NDFs that have actually been created. */
               if( grp ) grpPut1( grp, path, 0, status );

/* Add a TILENUM header to the output FITS extension. */
               kpgGtfts( indfo, &fc, status );
               if( *status == KPG__NOFTS ) {
                  errAnnul( status );
                  fc = astFitsChan( NULL, NULL, " " );

/* If the last card is "END", remove it. */
               } else {
                  astSetI( fc, "Card", astGetI( fc, "NCARD" ) );
                  keyword = astGetC( fc, "CardName" );
                  if( keyword && !strcmp( keyword, "END" ) ) astDelFits( fc );
               }

               one_snprintf(jsatile_comment, 45, "JSA all-sky tile index (Nside=%i)",
                            status, tiling.ntpf);
               atlPtfti( fc, "TILENUM", tile_index, jsatile_comment, status );
               kpgPtfts( indfo, fc, status );
               fc = astAnnul( fc );

/* Now store an STC-S polygon that describes the shortest boundary
   enclosing the good data in the output NDF, and store it as an NDF extension. */
               kpgPutOutline( indfo, 0.5, 1, status );

/* We now reshape any extension NDFs contained within the output NDF to
   have the same spatial bounds as the main NDF (but only for extension
   NDFs that originally have the same spatial bounds as the supplied NDF).
   Get a group containing paths to all extension NDFs in the output NDF. */
               ndgMoreg( indfo, &grpt, &size, status );

/* Loop round each output extension NDF. */
               for( iext = 1; iext <= size && *status == SAI__OK; iext++ ) {
                  ndgNdfas( grpt, iext, "Update", &indfx, status );

/* Get its bounds. */
                  ndfBound( indfx, NDF__MXDIM, lbndx, ubndx, &ndimx, status );

/* See if this extension NDF has the same bounds on the spatial axes as
   the supplied NDF. */
                  if( ndimx > 1 && lbndx[ lonax ] == lbnd[ lonax ] &&
                                   lbndx[ latax ] == lbnd[ latax ] &&
                                   ubndx[ lonax ] == ubnd[ lonax ] &&
                                   ubndx[ latax ] == ubnd[ latax ] ) {

/* If so, change the bounds of the output extension NDF so that they are
   the same as the main NDF on the spatial axes, and map the original
   contents of the NDF onto the new pixel grid. */
                     smf1_jsadicer( indfx, olbnd, oubnd, tile_map, tile_frm, p2pmap,
                                    NULL, NULL, NULL, status );
                  }

/* Annul the extension NDF identifier. */
                  ndfAnnul( &indfx, status );
               }

/* Free resources associated with the current tile. */
               grpDelet( &grpt, status );
               ndfAnnul( &indfo, status );

/* Issue warnings about empty tiles. */
            } else {
               msgOutiff( MSG__VERB, "", "   tile %d is empty and so will not be "
                          "created", status, tile_index );
            }
         }

/* Free the section of the input NDF. */
         ndfAnnul( &indfs, status );

/* Append the index of this tile in the list of tiles to be created. */
         created_tiles = astGrow( created_tiles, ++(*ntile),
                                  sizeof( *created_tiles ) );
         if( *status == SAI__OK ) created_tiles[ *ntile - 1 ] = tile_index;

      } else {
         msgOutiff( MSG__DEBUG, "", "   Tile %d does not overlap the input "
                    "NDF after trimming.", status, tile_index );
      }
   }
   msgBlank( status );

/* Write the indicies of the created tiles out to a parameter. */
   if( *ntile ) parPut1i( "JSATILELIST", *ntile, created_tiles, status );

/* Free resources. */
   created_tiles = astFree( created_tiles );
   tiles = astFree( tiles );
   path = astFree( path );

/* End the NDF context. */
   ndfEnd( status );

/* End the AST context. */
   astEnd;

}
Exemplo n.º 3
0
int *smf_jsatiles_data( Grp *igrp, size_t size, int *ntile, int *status ){

/* Local Variables */
   AstFrame *frm = NULL;
   AstFrameSet *fs;
   JCMTState *state;
   const char *trsys;
   const char *trsys_last;
   dim_t *hits = NULL;
   dim_t *ph;
   dim_t iframe;
   double *gx = NULL;
   double *gy = NULL;
   double *p1;
   double *p2;
   double *px;
   double *py;
   double *trac1 = NULL;
   double *trac2 = NULL;
   double fov;
   double point1[ 2 ];
   double point2[ 2 ];
   double search;
   int *tiles = NULL;
   int dim[ 2 ];
   int i;
   int ix;
   int iy;
   int lbnd[ 2 ];
   int ubnd[ 2 ];
   size_t ifile;
   smfData *data = NULL;
   smfHead *hdr = NULL;
   smfJSATiling skytiling;
   smf_inst_t inst = SMF__INST_NONE;
   smf_inst_t instrument;
   smf_subinst_t subinst;

/* Initialise */
   *ntile = 0;

/* Check inherited status */
   if( *status != SAI__OK ) return tiles;

/* Start an AST context so that all AST objects created in this function
   are annulled automatically. */
   astBegin;

/* Loop round all the input NDFs. */
   trsys_last = "";
   for( ifile = 1; ifile <= size && *status == SAI__OK; ifile++ ) {

/* Obtain information about the current input NDF. */
      smf_open_file( igrp, ifile, "READ", SMF__NOCREATE_DATA, &data,
                     status );

/* Get a pointer to the header. */
      hdr = data->hdr;

/* Get the instrument. */
      if( hdr->instrument == INST__SCUBA2 ) {
         subinst = smf_calc_subinst( hdr, status );
         if( subinst == SMF__SUBINST_850 ) {
            inst = SMF__INST_SCUBA_2_850;
         } else {
            inst = SMF__INST_SCUBA_2_450;
         }

      } else if( hdr->instrument == INST__ACSIS ) {
         inst = SMF__INST_HARP;

      } else if( *status == SAI__OK ) {
         *status = SAI__ERROR;
         smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" );
         errRep( "", "No tiles are yet defined for the instrument that "
                 "created ^FILE.", status );
      }

/* If this is the first file, it defines the instrument in use. */
      if( ifile == 1 ) {
         instrument = inst;

/* Get the parameters that define the layout of sky tiles for the
   instrument. */
         smf_jsatiling( instrument, &skytiling, status );

/* Create a FrameSet describing the whole sky in which each pixel
   corresponds to a single tile. The current Frame is ICRS (RA,Dec) and
   the base Frame is grid coords in which each grid pixel corresponds to
   a single tile. */
         smf_jsatile( 0, &skytiling, 0, NULL, &fs, NULL, lbnd, ubnd, status );

/* Allocate an image with one pixel for each tile, and fill it with
   zeros. */
         dim[ 0 ] = ubnd[ 0 ] - lbnd[ 0 ] + 1;
         dim[ 1 ] = ubnd[ 1 ] - lbnd[ 1 ] + 1;
         hits = astCalloc( dim[0]*dim[1], sizeof( *hits ) );

/* Get the radius of the field of view in radians. */
         fov = 0.5*(skytiling.fov*AST__DD2R)/3600.0;

/* If this is not the first file, report an error if the instrument has
   changed... */
      } else if( instrument != inst && *status == SAI__OK ) {
         smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" );
         errRep( "", "The file ^FILE was created by a different instrument "
                 "to the previous files.", status );
      }

/* Re-map the current Frame of the hits map WCS FrameSet to be the tracking
   system used by the current file (if it has changed). */
      trsys = sc2ast_convert_system( (hdr->allState)[0].tcs_tr_sys,
                                      status );
      if( *status == SAI__OK && strcmp( trsys, trsys_last ) ) {
         astSetC( fs, "System", trsys );
         trsys_last = trsys;
         frm = astGetFrame( fs, AST__CURRENT );
      }

/* Get the radius of the search circle. */
      search = fov + sqrt( hdr->instap[ 0 ]*hdr->instap[ 0 ] +
                           hdr->instap[ 1 ]*hdr->instap[ 1 ] );

/* Ensure our work arrays are big enough to hold the current file. */
      trac1 = astGrow( trac1, 4*hdr->nframes, sizeof( *trac1 ) );
      trac2 = astGrow( trac2, 4*hdr->nframes, sizeof( *trac2 ) );
      gx = astGrow( gx, 4*hdr->nframes, sizeof( *gx ) );
      gy = astGrow( gy, 4*hdr->nframes, sizeof( *gy ) );

/* Check pointers can be used safely. */
      if( *status == SAI__OK ) {

/* Loop round all time slices, getting the tracking coords at the corners
   of a box that enclose the field of view. */
         p1 = trac1;
         p2 = trac2;
         state = hdr->allState;
         for( iframe = 0; iframe < hdr->nframes; iframe++,state++ ) {
            point1[ 0 ] = state->tcs_tr_ac1;
            point1[ 1 ] = state->tcs_tr_ac2;
            for( i = 0; i < 4; i++ ) {
               astOffset2( frm, point1, i*AST__DPIBY2, search, point2 );
               *(p1++) = point2[ 0 ];
               *(p2++) = point2[ 1 ];
            }
         }

/* Convert them to grid coords in the hits map. */
         astTran2( fs, 4*hdr->nframes, trac1, trac2, 0, gx, gy );

/* Loop round them all again. Update the hits map to indicate how many
   points fall in each tile. */
         px = gx;
         py = gy;
         for( iframe = 0; iframe < 4*hdr->nframes; iframe++,px++,py++ ) {
            ix = (int)( *px + 0.5 ) - 1;
            iy = (int)( *py + 0.5 ) - 1;
            hits[ ix + iy*dim[ 0 ] ]++;
         }
      }

/* Close the current input data file. */
      smf_close_file( &data, status);
      data = NULL;
   }

/* Form a list of all the tiles that receive any data. */
   ph = hits;
   for( iy = 0; iy < dim[ 1 ]; iy++ ) {
      for( ix = 0; ix < dim[ 0 ]; ix++,ph++ ) {
         if( *ph > 0 ) {
            tiles = astGrow( tiles, ++(*ntile), sizeof( *tiles ) );
            if( *status == SAI__OK ) {
               tiles[ *ntile - 1 ] = smf_jsatilexy2i( ix, iy, &skytiling,
                                                      status );
            }
         }
      }
   }

/* Free resources. */
   hits = astFree( hits );
   trac1 = astFree( trac1 );
   trac2 = astFree( trac2 );
   gx = astFree( gx );
   gy = astFree( gy );

   if( *status != SAI__OK ) {
      tiles = astFree( tiles );
      *ntile = 0;
   }

   astEnd;

   return tiles;
}
Exemplo n.º 4
0
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;

}
Exemplo n.º 5
0
void smf_calc_smoothedwvm ( ThrWorkForce *wf, const smfArray * alldata,
                            const smfData * adata, AstKeyMap* extpars, double **wvmtau,
                            size_t *nelems, size_t *ngoodvals, int * status ) {
  size_t i;
  size_t nrelated = 0;          /* Number of entries in smfArray */
  size_t nframes = 0;           /* Number of timeslices */
  size_t ngood = 0;             /* Number of elements with good tau */
  double *taudata = NULL;       /* Local version of WVM tau */
  const smfArray * thesedata = NULL;  /* Collection of smfDatas to analyse */
  smfArray * tmpthesedata = NULL; /* Local version of adata in a smfArray */

  if (*status != SAI__OK) return;

  if (alldata && adata) {
    *status = SAI__ERROR;
    errRep("", "smf_calc_smoothedwvm can not be given non-NULL alldata and non-NULL adata arguments"
           " (possible programming error)", status );
    return;
  }

  if (!alldata && !adata) {
    *status = SAI__ERROR;
    errRep("", "smf_calc_smoothedwvm: One of alldata or adata must be non-NULL",
           status);
    return;
  }

  if (!wvmtau) {
    *status = SAI__ERROR;
    errRep("", "Must supply a non-NULL pointer for wvmtau argument"
           " (possible programming error)", status );
    return;
  }

  /* if we have a single smfData put it in a smfArray */
  if (alldata) {
    if (alldata->ndat == 0 ) {
      *status = SAI__ERROR;
      errRep("", "No smfDatas present in supplied smfArray for WVM smoothing"
             " (possible programming error)", status );
      return;
    }
    thesedata = alldata;
  } else {
    tmpthesedata = smf_create_smfArray( status );
    if (tmpthesedata) {
      tmpthesedata->owndata = 0; /*not owned by the smfArray */

      /* we know that the smfData here will not be touched in this
         function so we do the BAD thing of casting const to non-const */
      smf_addto_smfArray( tmpthesedata, (smfData *)adata, status );
    }
    thesedata = tmpthesedata;
  }

  /* Check that we have headers and that the smfData are the same length */
  nrelated = thesedata->ndat;

  for (i = 0; i < nrelated; i++ ) {
    smfData * data = (thesedata->sdata)[i];
    smfHead * hdr = data->hdr;
    dim_t thisframes = 0;
    if ( !hdr) {
      *status = SAI__ERROR;
      errRepf( "", "smfData %zu has no header. Aborting WVM smoothing",
               status, i );
      return;
    }

    smf_get_dims( data, NULL, NULL, NULL, &thisframes, NULL, NULL, NULL, status );
    if (!nframes) nframes = thisframes;
    if (thisframes != nframes) {
      *status = SAI__ERROR;
      errRepf( "", "smfData %zu has different length. Aborting WVM smoothing",
               status, i );
      return;
    }
  }

  /* We will need the earliest and last airmass value in order
     to calculate a zenith tau */

  /* As a first step, just fill the time series with calculated WVM
     tau values even though we know there are about 240 fewer tau
     readings in reality. This initial approach will make it easier to
     use the smoothed data directly rather than having to interpolate
     from the 1.2 second data back into the 200 Hz data. */

  taudata = astCalloc( nframes, sizeof(*taudata) );

  if (*status == SAI__OK) {
    double amprev = VAL__BADD;
    double steptime;
    size_t maxgap;
    struct timeval tv1;
    struct timeval tv2;
    smfCalcWvmJobData *job_data = NULL;
    int nworker;

    /* We need to know the steptime so we can define the max good gap
       in seconds and convert it to steps*/

    steptime = (thesedata->sdata)[0]->hdr->steptime;
    maxgap = (size_t)( 5.0 / steptime );  /* 5 seconds is just larger than 2 WVM readings */

    /* Assume all files have the same airmass information */
    smf_find_airmass_interval( (thesedata->sdata)[0]->hdr, &amprev, NULL, NULL, NULL, status );

    smf_timerinit( &tv1, &tv2, status );


    /* Create structures used to pass information to the worker threads. */
    nworker = wf ? wf->nworker : 1;
     job_data = astMalloc( nworker*sizeof( *job_data ) );

    if (*status == SAI__OK) {
      dim_t tstep;
      int iworker;
      smfCalcWvmJobData *pdata = NULL;

      /* Get the number of time slices to process in each thread. */
      if( nworker > (int) nframes ) {
        tstep = 1;
      } else {
        tstep = nframes/nworker;
      }

      /* to return the same values for one thread and multiple threads
         we need to break the threads on wvm sample boundaries wherever
         possible. We make an initial estimate of the number of WVM measurements
         by assuming one every two seconds. */
      {
        smfData * curdata = NULL;
        size_t nwvm = 0;
        double prevtime = VAL__BADD;
        double curtime;
        size_t *boundaries = astGrow(NULL, nframes*(size_t)(steptime/2.0), sizeof(*boundaries));
        for (i=0; i<nframes; i++) {
          if (!curdata) {
            SELECT_DATA( thesedata, curdata, VAL__BADD, wvm_time, i );
          }

          if (curdata) smf_tslice_ast( curdata, i, 0, NO_FTS, status );

          if ( !curdata || curdata->hdr->state->wvm_time == VAL__BADD ) {
            /* Try the other datas */
            SELECT_DATA( thesedata, curdata, VAL__BADD, wvm_time, i );
          }
          if (*status != SAI__OK) break;

          if (!curdata) {
            curtime = VAL__BADD;
          } else {
            curtime = curdata->hdr->state->wvm_time;
          }

          if (curtime != prevtime || nwvm == 0 ) {
            /* Store the index in the boundaries array */
            nwvm++;
            boundaries = astGrow(boundaries, nwvm, sizeof(*boundaries));
            if (!boundaries) { /* this is serious */
              if (*status == SAI__OK) *status = SAI__ERROR;
              errRep("", "Error allocating temporary memory for WVM calculation\n",
                     status );
              break;
            }
            boundaries[nwvm-1] = i;
            prevtime = curtime;
          }
        }

        /* No point using too many threads */
        if (*status == SAI__OK) {
          if (nworker >= (int)nwvm) {
            nworker = nwvm;

            /* Allocate a measurement per thread */
            for( iworker = 0; iworker < nworker; iworker++ ) {
              pdata = job_data + iworker;
              pdata->t1 = boundaries[iworker];
              if (iworker+1 < nworker) pdata->t2 = boundaries[iworker+1]-1;
            }

            /* Ensure that the last thread picks up any left-over time slices */
            pdata->t2 = nframes - 1;

          } else {
            /* Allocate the workers to slices of approximate size tstep */
            size_t prevend = 0; /* End of previous slice */
            size_t prevbnd = 0; /* Index into previous boundaries[] array selection */
            for( iworker = 0; iworker < nworker; iworker++ ) {
              size_t belowidx = prevend+1;
              size_t aboveidx = nframes;
              size_t lbnd;
              size_t ubnd;
              size_t j;
              size_t guess;

              pdata = job_data + iworker;

              if (iworker == 0) { /* always start at the beginning */
                pdata->t1 = 0;
              } else { /* Start one after the previous block */
                pdata->t1 = prevend + 1;
              }

              /* Now we have to find the end of this slice */
              guess = (iworker*tstep) + tstep - 1;
              if (guess <= pdata->t1) guess = pdata->t1 + tstep;

              /* find nearest boundaries */
              for (j=prevbnd; j<nwvm; j++) {
                if ( boundaries[j] > guess ) {
                  aboveidx = boundaries[j];
                  ubnd = j;
                  if (j>0) {
                    belowidx = boundaries[j-1];
                    lbnd = j -1 ;
                  } else {
                    lbnd = 0;
                  }
                  break;
                }
              }

              /* Choose the closest, making sure that we are not choosing t1 */
              if ( (guess - belowidx < aboveidx - guess) && belowidx > pdata->t1 ) {
                pdata->t2 = belowidx - 1;
                prevbnd = lbnd;
              } else {
                pdata->t2 = aboveidx - 1;
                prevbnd = ubnd;
              }

              prevend = pdata->t2;

              if (prevend == nframes - 1 && iworker < nworker-1 ) {
                /* we have run out of slices so just use fewer workers */
                nworker = iworker + 1;
                break;
              }

            }

            /* Ensure that the last thread picks up any left-over time slices */
            pdata->t2 = nframes - 1;

          }

          /* Tidy up */
          boundaries = astFree( boundaries );
        }
      }

      /* Store all the other info needed by the worker threads, and submit the
         jobs to fix the steps in each bolo, and then wait for them to complete. */
      for( iworker = 0; iworker < nworker; iworker++ ) {
        smfArray *thrdata = NULL;
        pdata = job_data + iworker;

        pdata->nframes = nframes;
        pdata->airmass = amprev; /* really need to get it from the start of each chunk */
        pdata->taudata = taudata;
        pdata->maxgap = maxgap;

        /* Need to copy the smfDatas and create a new smfArray for each
           thread */
        thrdata = smf_create_smfArray( status );
        for (i=0;i<nrelated;i++) {
          smfData *tmpdata = NULL;
          tmpdata = smf_deepcopy_smfData( wf, (thesedata->sdata)[i], 0, SMF__NOCREATE_FILE |
                                          SMF__NOCREATE_DA |
                                          SMF__NOCREATE_FTS |
                                          SMF__NOCREATE_DATA |
                                          SMF__NOCREATE_VARIANCE |
                                          SMF__NOCREATE_QUALITY, 0, 0,
                                          status );
          smf_lock_data( tmpdata, 0, status );
          smf_addto_smfArray( thrdata, tmpdata, status );
        }
        pdata->thesedata = thrdata;

        /* Need to do a deep copy of ast data and unlock them */
        pdata->extpars = astCopy(extpars);
        astUnlock( pdata->extpars, 1 );

        /* Pass the job to the workforce for execution. */
        thrAddJob( wf, THR__REPORT_JOB, pdata, smf__calc_wvm_job, 0, NULL,
                   status );
      }

      /* Wait for the workforce to complete all jobs. */
      thrWait( wf, status );

      /* Now free the resources we allocated during job creation
         and calculate the number of good values */
      for( iworker = 0; iworker < nworker; iworker++ ) {
        smfArray * thrdata;
        pdata = job_data + iworker;
        astLock( pdata->extpars, 0 );
        pdata->extpars = astAnnul( pdata->extpars );
        thrdata = pdata->thesedata;
        for (i=0;i<thrdata->ndat;i++) {
          smf_lock_data( (thrdata->sdata)[i], 1, status );
        }
        smf_close_related( wf, &thrdata, status );
        ngood += pdata->ngood;
      }
    }
    job_data = astFree( job_data );

    msgOutiff( MSG__NORM, "", FUNC_NAME ": %f s to calculate unsmoothed WVM tau values",
               status, smf_timerupdate(&tv1,&tv2,status) );

  }




  if (*status == SAI__OK && extpars) {
    /* Read extpars to see if we need to smooth */
    double smoothtime = VAL__BADD;

    if (astMapGet0D( extpars, "SMOOTHWVM", &smoothtime ) ) {
      if (smoothtime != VAL__BADD && smoothtime > 0.0) {
        smfData * data = (thesedata->sdata)[0];
        double steptime = data->hdr->steptime;
        dim_t boxcar = (dim_t)( smoothtime / steptime );

        msgOutiff( MSG__VERB, "",
                   "Smoothing WVM data with %f s tophat function",
                   status, smoothtime );

        smf_tophat1D( taudata, nframes, boxcar, NULL, 0, 0.0, status );

        /* The tophat smoothing puts a bad value at the start and end of
           the time series so we replace that with the adjacent value since
           the step time is much smaller than WVM readout time. If more than
           one value is bad we do not try to find the good value. */
        taudata[0] = taudata[1];
        taudata[nframes-1] = taudata[nframes-2];
      }
    }
  }

  /* Use this to get the raw WVM output for debugging */
  /*
  if (*status == SAI__OK) {
    smfData *data = (thesedata->sdata)[0];
    smfHead *hdr = data->hdr;
    printf("# IDX TAU RTS_NUM RTS_END WVM_TIME\n");
    for (i=0; i<nframes;i++) {
      JCMTState * state;
      state = &(hdr->allState)[i];
      printf("%zu %.*g %d %.*g %.*g\n", i, DBL_DIG, taudata[i], state->rts_num,
             DBL_DIG, state->rts_end, DBL_DIG, state->wvm_time);
    }
  } */

  /* Free resources */
  if (tmpthesedata) smf_close_related( wf, &tmpthesedata, status );

  if (*status != SAI__OK) {
    if (taudata) taudata = astFree( taudata );
    *nelems = 0;
    *ngoodvals = 0;
  } else {
    *wvmtau = taudata;
    *nelems = nframes;
    *ngoodvals = ngood;
  }

}
Exemplo n.º 6
0
void smf_rebincube_nn( ThrWorkForce *wf, smfData *data, int first, int last,
                       int *ptime, dim_t nchan, dim_t ndet, dim_t nslice,
                       dim_t nxy, dim_t nout, dim_t dim[3],
                       int badmask, int is2d, AstMapping *ssmap,
                       AstSkyFrame *abskyfrm, AstMapping *oskymap,
                       Grp *detgrp, int moving, int usewgt, int genvar,
                       double tfac, double fcon, float *data_array,
                       float *var_array, double *wgt_array,
                       float *texp_array, float *teff_array, int *nused,
                       int *nreject, int *naccept, int *good_tsys,
                       int *status ){

/* Local Variables */
   AstMapping *totmap = NULL;  /* WCS->GRID Mapping from input WCS FrameSet */
   const char *name = NULL;    /* Pointer to current detector name */
   const double *tsys = NULL;  /* Pointer to Tsys value for first detector */
   dim_t gxout;                /* Output X grid index */
   dim_t gyout;                /* Output Y grid index */
   dim_t ichan;                /* Input channel index */
   dim_t idet;                 /* detector index */
   dim_t itime;                /* Index of current time slice */
   dim_t nchanout;             /* No of spectral channels in the output */
   dim_t timeslice_size;       /* No of detector values in one time slice */
   double *detxin = NULL;      /* Work space for input X grid coords */
   double *detxout = NULL;     /* Work space for output X grid coords */
   double *detyin = NULL;      /* Work space for input Y grid coords */
   double *detyout = NULL;     /* Work space for output Y grid coords */
   double invar;               /* Input variance */
   double tcon;                /* Variance factor for whole time slice */
   double wgt;                 /* Weight for input value */
   float *ddata = NULL;        /* Pointer to start of input detector data */
   float *tdata = NULL;        /* Pointer to start of input time slice data */
   float *work = NULL;         /* Pointer to start of work array */
   float rtsys;                /* Tsys value */
   float teff;                 /* Effective integration time */
   float texp;                 /* Total time ( = ton + toff ) */
   int *nexttime;              /* Pointer to next time slice index to use */
   int *specpop = NULL;        /* Input channels per output channel */
   int *spectab = NULL;        /* I/p->o/p channel number conversion table */
   int first_ts;               /* Is this the first time slice? */
   int found;                  /* Was current detector name found in detgrp? */
   int ignore;                 /* Ignore this time slice? */
   int init_detector_data;     /* Should detector_data be initialised? */
   int iv0;                    /* Offset for pixel in 1st o/p spectral channel */
   int jdet;                   /* Detector index */
   int naccept_old;            /* Previous number of accepted spectra */
   int ochan;                  /* Output channel index */
   int use_threads;            /* Use multiple threads? */
   smfHead *hdr = NULL;        /* Pointer to data header for this time slice */

   static smfRebincubeNNArgs1 *common_data = NULL; /* Holds data common to all detectors */
   static smfRebincubeNNArgs2 *detector_data = NULL; /* Holds data for each detector */
   static int *pop_array = NULL;/* I/p spectra pasted into each output spectrum */
   static dim_t ndet_max = 0;  /* Max number of detectors */

/* Check the inherited status. */
   if( *status != SAI__OK ) return;

/* Store a pointer to the input NDFs smfHead structure. */
   hdr = data->hdr;

/* Store the number of pixels in one time slice */
   timeslice_size = ndet*nchan;

/* Use this mapping to get the zero-based output channel number corresponding
   to each input channel number. */
   smf_rebincube_spectab( nchan, dim[ 2 ], ssmap, &spectab, status );
   if( !spectab ) goto L999;

/* The 2D weighting scheme assumes that each output channel receives
   contributions from one and only one input channel in each input file.
   Create an array with an element for each output channel, holding the
   number of input channels that contribute to the output channel. */
   nchanout = dim[ 2 ];
   if( is2d ) {
      specpop = astMalloc( nchanout*sizeof( int ) );
      memset( specpop, 0, nchanout*sizeof( int ) );
      for( ichan = 0; ichan < nchan; ichan++ ) {
         ochan = spectab[ ichan ];
         if( ochan != -1 ) specpop[ ochan ]++;
      }
   }

/* If this is the first pass through this file, initialise the arrays. */
   if( first ){
      smf_rebincube_init( is2d, nxy, nout, genvar, data_array, var_array,
                          wgt_array, texp_array, teff_array, nused, status );

/* Allocate an extra work array and initialise it to zero. This holds the
   total number of input spectra pasted into each output spectrum. It is
   not needed by the AST-based function and so has not been put into
   smf_rebincube_init. */
      if( is2d ) {
         pop_array = astMalloc( nxy*sizeof( int ) );
         memset( pop_array, 0, nxy*sizeof( int ) );
      }
   }

/* Allocate work arrays to hold the input and output grid coords for each
   detector. */
   detxin = astMalloc( ndet*sizeof( double ) );
   detyin = astMalloc( ndet*sizeof( double ) );
   detxout = astMalloc( ndet*sizeof( double ) );
   detyout = astMalloc( ndet*sizeof( double ) );

/* Initialise a string to point to the name of the first detector for which
   data is available */
   name = hdr->detname;

/* Fill the input arrays with the grid coords of each detector. */
   for( idet = 0; idet < ndet; idet++ ) {
      detxin[ idet ] = (double) idet + 1.0;
      detyin[ idet ] = 1.0;

/* If a group of detectors to be used was supplied, search the group for
   the name of the current detector. If not found, set the GRID coord
   bad. */
      if( detgrp ) {
         found = grpIndex( name, detgrp, 1, status );
         if( !found ) {
            detxin[ idet ] = AST__BAD;
            detyin[ idet ] = AST__BAD;
         }
      }

/* Move on to the next available detector name. */
      name += strlen( name ) + 1;
   }

/* Initialise a pointer to the ntex time slice index to be used. */
   nexttime = ptime;

/* Count the number of time slices to be processed. */
   if( ptime ) {
      itime = 0;
      while( ptime[ itime ] != VAL__MAXI ) itime++;
      if( data->file ) {
         msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Selecting %d time "
                    "slices from data file '%s'.", status, (int) itime,
                    data->file->name );
      }
   } else {
      itime = nslice;
      if( data->file ) {
         msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Using all %d time "
                    "slices from data file '%s'.", status, (int) itime,
                    data->file->name );
      }
   }

/* Initialise the progress meter. */
   smf_reportprogress( itime, status );

/* Loop round all time slices in the input NDF. */
   use_threads = 0;
   first_ts = 1;
   for( itime = 0; itime < nslice && *status == SAI__OK; itime++ ) {

/* If this time slice is not being pasted into the output cube, pass on. */
      if( nexttime ){
         if( *nexttime != (int) itime ) continue;
         nexttime++;
      }

/* Store a pointer to the first input data value in this time slice. */
      tdata = ( (float *) (data->pntr)[ 0 ] ) + itime*timeslice_size;

/* Begin an AST context. Having this context within the time slice loop
   helps keep the number of AST objects in use to a minimum. */
      astBegin;

/* Get a Mapping from the spatial GRID axes in the input the spatial
   GRID axes in the output for the current time slice. Note this has
   to be done first since it stores details of the current time slice
   in the "smfHead" structure inside "data", and this is needed by
   subsequent functions. */
      totmap = smf_rebin_totmap( data, itime, abskyfrm, oskymap, moving,
				 status );
      if( !totmap ) {
         if( data->file ) {
            msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Cannot get "
                       "Mapping for slice %d from data file '%s'.", status,
                       (int) itime, data->file->name );
         }
         astEnd;
         break;
      }

/* Get the effective exposure time, the total exposure time, and the
   Tsys->Variance onversion factor for this time slice. Also get a
   pointer to the start of the Tsys array. */
      tsys = smf_rebincube_tcon( hdr, itime, fcon, &texp, &teff, &tcon,
                                 status );

/* Use this Mapping to get the output spatial grid coords for each input
   detector. */
      astTran2( totmap, ndet, detxin, detyin, 1, detxout, detyout );

/* If this is the first time slice from the current input file to be pasted
   into the output, see if any of the spectra will end up being pasted on
   top of each other in the output. If not we can use a separate thread to
   paste each spectrum. Otherwise, we cannot use multiple threads since they
   may end up trying to write to the same output pixel at the same time. */
      if( first_ts ) {
         first_ts = 0;
         use_threads = wf ? 1 : 0;
         for( idet = 0; idet < ndet - 1 && use_threads; idet++ ) {
            if( detxout[ idet ] != AST__BAD && detyout[ idet ] != AST__BAD ){

               gxout = floor( detxout[ idet ] + 0.5 );
               gyout = floor( detyout[ idet ] + 0.5 );

               if( gxout >= 1 && gxout <= dim[ 0 ] &&
                   gyout >= 1 && gyout <= dim[ 1 ] ) {

                  for( jdet = idet + 1; idet < ndet; idet++ ) {
                     if( detxout[ jdet ] != AST__BAD &&
                         detyout[ jdet ] != AST__BAD ){

                        if( floor( detxout[ jdet ] + 0.5 ) == gxout &&
                            floor( detyout[ jdet ] + 0.5 ) == gyout ) {
                           use_threads = 0;
                           break;
                        }
                     }
                  }

               }
            }
         }

/* If we will be using mutiple threads, do some preparation. */
         if( use_threads ) {
            if( data->file ) {
               msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Using multiple "
                          "threads to process data file '%s'.", status,
                          data->file->name );
            }

/* Ensure we have a structure holding information which is common to all
   detectors and time slices. */
            common_data = astGrow( common_data, sizeof( smfRebincubeNNArgs1 ), 1 );
            if( astOK ) {
               common_data->badmask = badmask;
               common_data->nchan = nchan;
               common_data->nchanout = nchanout;
               common_data->spectab = spectab;
               common_data->specpop = specpop;
               common_data->nxy = nxy;
               common_data->genvar = genvar;
               common_data->data_array = data_array;
               common_data->var_array = var_array;
               common_data->wgt_array = wgt_array;
               common_data->pop_array = pop_array;
               common_data->nout = nout;
               common_data->is2d = is2d;
            }

/* Ensure we have a structure for each detector to hold the detector-specific
   data, plus a pointer to the common data. */
            init_detector_data = ( detector_data == NULL );
            if( init_detector_data ) ndet_max = 0;
            detector_data = astGrow( detector_data, sizeof( smfRebincubeNNArgs2 ),
                                     ndet ) ;

/* Initialise pointers stored within any new elements added to the
   "detector_data" array. */
            if( ndet > ndet_max && astOK ) {
               for( idet = ndet_max; idet < ndet; idet++ ) {
                  detector_data[ idet ].common = NULL;
                  detector_data[ idet ].work = NULL;
                  detector_data[ idet ].ddata = NULL;
               }
               ndet_max = ndet;
            }

/* Allocate work space for each detector and store the common data
   pointer. */
            if( astOK ) {
               for( idet = 0; idet < ndet; idet++ ) {
                  detector_data[ idet ].common = common_data;
                  detector_data[ idet ].work = astGrow( detector_data[ idet ].work,
                                                        sizeof( float ), nchanout );
               }
            }

/* If we are using a single threads, do some alternative preparation. */
         } else {
            if( data->file ) {
               msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Using a single "
                          "thread to process data file '%s'.", status,
                          data->file->name );
            }

/* We need an extra work array for 2D weighting that can hold a single
   output spectrum. This is used as a staging post for each input
   spectrum prior to pasting it into the output cube. */
            work = astMalloc( nchanout*sizeof( float ) );

         }
      }

/* Loop round each detector, pasting its spectral values into the output
   cube. */
      for( idet = 0; idet < ndet; idet++ ) {

/* If multi-threaded, initialise a bad value for the detector's weight
   to indicate that it is not being used. */
         if( use_threads ) detector_data[ idet ].wgt = VAL__BADD;

/* See if any good tsys values are present. */
         rtsys = tsys ? (float) tsys[ idet ] : VAL__BADR;
         if( rtsys <= 0.0 ) rtsys = VAL__BADR;
         if( rtsys != VAL__BADR ) *good_tsys = 1;

/* Check the detector has a valid position in output grid coords */
         if( detxout[ idet ] != AST__BAD && detyout[ idet ] != AST__BAD ){

/* Find the closest output pixel and check it is within the bounds of the
   output cube. */
            gxout = floor( detxout[ idet ] + 0.5 );
            gyout = floor( detyout[ idet ] + 0.5 );
            if( gxout >= 1 && gxout <= dim[ 0 ] &&
                gyout >= 1 && gyout <= dim[ 1 ] ) {

/* Get the offset of the output array element that corresponds to this
   pixel in the first spectral channel. */
               iv0 = ( gyout - 1 )*dim[ 0 ] + ( gxout - 1 );

/* If required calculate the variance associated with this detector, based on
   the input Tsys values. */
               invar = VAL__BADR;
               if( usewgt || genvar == 2 ) {
                  if(  rtsys != VAL__BADR ) {
                     if( tcon != VAL__BADD ) invar = tcon*rtsys*rtsys;
                  }
               }

/* Calculate the weight for this detector. If we need the input variance,
   either to weight the input or to calculate output variances, but the
   input variance is not available, then ignore this detector. */
               ignore = 0;
               if( usewgt ) {
                  if( invar > 0.0 && invar != VAL__BADR ) {
                     wgt = 1.0/invar;
                  } else {
                     ignore = 1;
                  }

               } else if( genvar == 2 ) {
                  ignore = ( invar <= 0.0 || invar == VAL__BADR );
                  wgt = 1.0;

               } else {
                  wgt = 1.0;
               }

/* If we are not ignoring this input spectrum, get a pointer to the start
   of the input spectrum data and paste it into the output cube using
   either the 2D or 3D algorithm. */
               if( !ignore ) {
                  ddata = tdata + idet*nchan;

/* First deal with cases where we are using a single thread (the current
   thread). */
                  if( !use_threads ) {
                     naccept_old = *naccept;

                     if( is2d ) {
                        smf_rebincube_paste2d( badmask, nchan, nchanout, spectab,
                                               specpop, iv0, nxy, wgt, genvar,
                                               invar, ddata, data_array,
                                               var_array, wgt_array, pop_array,
                                               nused, nreject, naccept, work,
                                               status );
                     } else {
                        smf_rebincube_paste3d( nchan, nout, spectab, iv0, nxy,
                                               wgt, genvar, invar, ddata,
                                               data_array, var_array,
                                               wgt_array, nused, status );
                        (*naccept)++;
                     }

/* Now we update the total and effective exposure time arrays for the
   output spectrum that receives this input spectrum. Scale the exposure
   times of this time slice in order to reduce its influence on the
   output expsoure times if it does not have much spectral overlap with
   the output cube. Only update the exposure time arrays if the spectrum
   was used (as shown by an increase in the number of accepted spectra). */
                     if( texp != VAL__BADR && *naccept > naccept_old ) {
                        texp_array[ iv0 ] += texp*tfac;
                        teff_array[ iv0 ] += teff*tfac;
                     }

/* Now deal with cases where we are using several threads. */
                  } else {

/* Set up the detector specific data. */
                     detector_data[ idet ].iv0 = iv0;
                     detector_data[ idet ].wgt = wgt;
                     detector_data[ idet ].invar = invar;
                     detector_data[ idet ].ddata = ddata;
                     detector_data[ idet ].nused = 0;
                     detector_data[ idet ].nreject = 0;
                     detector_data[ idet ].naccept = 0;

/* Add a job to the workforce's job list. This job calls smf_rebincube_paste2d
   or smf_rebincube_paste3d to paste the detector input spectrum into the
   output cube. */
                     thrAddJob( wf, 0, detector_data + idet,
                                  smf_rebincube_paste_thread, 0, NULL, status );
                  }

               } else if( data->file ) {
                  msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Detector %d "
                             "is being ignored when processing data file '%s'.",
                             status, idet, data->file->name );
               }

            } else if( data->file ) {
               msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Detector %d "
                          "fell outside the output cube when processing "
                          "data file '%s'.", status, idet, data->file->name );
            }
         } else if( data->file ) {
            msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Detector %d has "
                       "an unknown position in the output cube when processing "
                       "data file '%s'.", status, idet, data->file->name );
         }
      }

/* If using multiple threads, wait until all spectra for this time slice
   have been pasted into the output cube. Then transfer the output values
   from the detector data structures to the returned variables. */
      if( use_threads ) {
         thrWait( wf, status );
         for( idet = 0; idet < ndet; idet++ ) {
            if( detector_data[ idet ].wgt != VAL__BADD ) {
               (*nused) += detector_data[ idet ].nused;
               (*nreject) += detector_data[ idet ].nreject;
               (*naccept) += detector_data[ idet ].naccept;

               if( texp != VAL__BADR && detector_data[ idet ].naccept > 0 ) {
                  texp_array[ detector_data[ idet ].iv0 ] += texp*tfac;
                  teff_array[ detector_data[ idet ].iv0 ] += teff*tfac;
               }
            }
         }
      }

/* Update the progress meter. */
      smf_reportprogress( 0, status );

/* End the AST context. */
      astEnd;
   }

/* If this is the final pass through this function, normalise the returned
   data and variance values, and release any static resources allocated
   within this function. */
   if( last ) {
      if( is2d ) {
         smf_rebincube_norm2d( nout, nxy, genvar, data_array,
                               var_array, wgt_array, pop_array, status );
      } else {
         smf_rebincube_norm3d( nout, genvar, *nused, data_array,
                               var_array, wgt_array, status );
      }

      pop_array = astFree( pop_array );

      if( use_threads ) {
         common_data = astFree( common_data );
         for( idet = 0; idet < ndet_max; idet++ ) {
            detector_data[ idet ].work = astFree( detector_data[ idet ].work );
         }
         detector_data = astFree( detector_data );
      }
   }

/* Free non-static resources. */
L999:;
   work = astFree( work );
   spectab = astFree( spectab );
   specpop = astFree( specpop );
   detxin = astFree( detxin );
   detyin = astFree( detyin );
   detxout = astFree( detxout );
   detyout = astFree( detyout );

}
Exemplo n.º 7
0
int *smf_jsatiles_region( AstRegion *region, smfJSATiling *skytiling,
                          int *ntile, int *status ){

/* Local Variables */
   AstFrameSet *fs;
   AstKeyMap *km;
   AstRegion *region2;
   AstRegion *space_region;
   AstRegion *tregion;
   AstSkyFrame *skyframe;
   char text[ 200 ];
   const char *key;
   double *mesh = NULL;
   double *xmesh;
   double *ymesh;
   int *tiles = NULL;
   int axes[ 2 ];
   int i;
   int ineb;
   int itile2;
   int itile;
   int ix;
   int iy;
   int key_index;
   int lbnd[ 2 ];
   int mapsize;
   int npoint;
   int old_sv;
   int overlap;
   int ubnd[ 2 ];
   int value;
   int xoff[ 4 ] = { -1, 0, 1, 0 };
   int xt;
   int yoff[ 4 ] = { 0, 1, 0, -1 };
   int yt;

/* Initialise */
   *ntile = 0;

/* Check inherited status */
   if( *status != SAI__OK ) return tiles;

/* Start an AST context so that all AST objects created in this function
   are annulled automatically. */
   astBegin;

/* Identify the celestial axes in the Region. */
   atlFindSky( (AstFrame *) region, &skyframe, axes + 1, axes, status );

/* Report an error if no celestial axes were found. */
   if( !skyframe && *status == SAI__OK ) {
      space_region = NULL;
      *status = SAI__ERROR;
      errRep( "", "The current WCS Frame in the supplied Region or "
              "NDF does not include celestial longitude and latitude axes.",
              status );

/* Otherwise, if the Region itself is 2-dimensional, it does not contain
   any other axes, so just use it as is. */
   } else if( astGetI( region, "Naxes" ) == 2 ) {
      space_region = astClone( region );

/* Otherwise, create a new Region by picking the celestial axes from the
   supplied Region. Report an error if a Region cannot be created in this
   way. */
   } else {
      space_region = astPickAxes( region, 2, axes, NULL );
      if( !astIsARegion( space_region ) && *status == SAI__OK ) {
         *status = SAI__ERROR;
         errRep( "", "The  celestial longitude and latitude axes in the "
                 "supplied Region or NDF are not independent of the other "
                 "axes.", status );
      }
   }

/* Create a FrameSet describing the whole sky in which each pixel
   corresponds to a single tile in SMF__JSA_HPX projection. The current
   Frame is ICRS (RA,Dec) and the base Frame is grid coords in which each
   grid pixel corresponds to a single tile. */
   smf_jsatile( -1, skytiling, 0, SMF__JSA_HPX, NULL, &fs, NULL, lbnd, ubnd,
                status );

/* Map the Region using the FrameSet obtained above so that the new Region
   describes offsets in tiles from the lower left tile. If "space_region"
   is a Polygon, ensure that the SimpVertices attribute is set so that the
   simplify method will take non-linearities into account (such as the
   region being split by the RA=12h meridian). */
   astInvert( fs );
   fs = astConvert( space_region, fs, "SKY" );
   if( !fs && *status == SAI__OK ) {
      *status = SAI__ERROR;
      errRep( "", "Cannot convert the supplied Region to ICRS.", status );
      goto L999;
   }

   old_sv = -999;
   if( astIsAPolygon( space_region ) ){
      if( astTest( space_region, "SimpVertices" ) ) {
         old_sv = astGetI( space_region, "SimpVertices" );
      }
      astSetI( space_region, "SimpVertices", 0 );
   }

   region2 = astMapRegion( space_region, fs, fs );

   if( astIsAPolygon( space_region ) ){
      if( old_sv == -999 ) {
         astClear( space_region, "SimpVertices" );
      } else {
         astSetI( space_region, "SimpVertices", old_sv );
      }
   }

/* Get a mesh of all-sky "grid" positions (actually tile X and Y indices)
   covering the region. Since the mesh positions are limited in number
   and placed arbitrarily within the Region, the mesh will identify some,
   but potentially not all, of the tiles that overlap the Region. */
   astGetRegionMesh( region2, 0, 0, 2, &npoint, NULL );
   mesh = astMalloc( 2*npoint*sizeof( *mesh ) );
   astGetRegionMesh( region2, 0, npoint, 2, &npoint, mesh );

/* Find the index of the tile containing each mesh position, and store
   them in a KeyMap using the tile index as the key and "1" (indicating
   the tile overlaps the region) as the value. The KeyMap is sorted by
   age of entry. Neighbouring tiles will be added to this KeyMap later.
   If an entry has a value of zero, it means the tile does not overlap
   the supplied Region. If the value is positive, it means the tile
   does overlap the supplied Region. If the value is negative, it means
   the tile has not yet been tested to see if it overlaps the supplied
   Region. */
   km = astKeyMap( "SortBy=KeyAgeDown" );
   xmesh = mesh;
   ymesh = mesh + npoint;
   for( i = 0; i < npoint && *status == SAI__OK; i++ ) {
      ix = (int)( *(xmesh++) + 0.5 ) - 1;
      iy = (int)( *(ymesh++) + 0.5 ) - 1;
      itile = smf_jsatilexy2i( ix, iy, skytiling, status );
      if (itile != VAL__BADI) {
         sprintf( text, "%d", itile );
         astMapPut0I( km, text, 1, NULL );
      }
   }

/* Starting with the oldest entry in the KeyMap, loop round checking all
   entries, in the order they were added, until all have been checked.
   Checking an entry may cause further entries to be added to the end of
   the KeyMap. */
   key_index = 0;
   mapsize = astMapSize( km );
   while( key_index < mapsize && *status == SAI__OK ) {
      key = astMapKey( km, key_index++ );

/* Convert the key string to an integer tile index. */
      itile = atoi( key );

/* Get the integer value associated with the tile. */
      astMapGet0I( km, key, &value );

/* If the tile associated with the current KeyMap entry has not yet been
   tested for overlap with the requested Region (as shown by the entry
   value being -1), test it now. */
      if( value == -1 ) {

/* Get a Region covering the tile. */
         smf_jsatile( itile, skytiling, 0, SMF__JSA_HPX, NULL, NULL, &tregion,
                      lbnd, ubnd, status );

/* See if this Region overlaps the user supplied region. Set the value of
   the KeyMap entry to +1 or 0 accordingly. */
         overlap = astOverlap( tregion, space_region );
         if( overlap == 0 ) {
            if( *status == SAI__OK ) {
               *status = SAI__ERROR;
               errRep( "", "Cannot align supplied Region with the sky "
                       "tile coordinate system (programming error).",
                       status );
            }
         } else if( overlap == 1 || overlap == 6 ) {
            value = 0;
         } else {
            value = 1;
         }
         astMapPut0I( km, key, value, NULL );
      }

/* Skip the current KeyMap entry if the corresponding tile does not
   overlap the requested Region (as shown by the entry value being zero). */
      if( value == 1 ) {

/* The current tile overlaps the supplied Region, so add the tile index to
   the returned list of tile indices. */
         tiles = astGrow( tiles, ++(*ntile), sizeof( *tiles ) );
         if( *status == SAI__OK ) {
            tiles[ *ntile - 1 ] = itile;

/* Add the adjoining tiles to the end of the KeyMap so that they will be
   tested in their turn, giving them a value of -1 to indicate that they
   have not yet been tested to see if they overlap the supplied Region.
   Ignore adjoining tiles that are already in the keyMap. */
            smf_jsatilei2xy( itile, skytiling, &xt, &yt, NULL, status );
            for( ineb = 0; ineb < 4; ineb++ ) {
               itile2 = smf_jsatilexy2i( xt + xoff[ ineb ], yt + yoff[ ineb ],
                                         skytiling, status );
               if( itile2 != VAL__BADI ) {
                  sprintf( text, "%d", itile2 );
                  if( !astMapHasKey( km, text ) ) {
                     astMapPut0I( km, text, -1, NULL );
                     mapsize++;
                  }
               }
            }
         }
      }
   }

/* Arrive here if an error occurs. */
   L999:;

/* Free resources. */
   mesh = astFree( mesh );

   if( *status != SAI__OK ) {
      tiles = astFree( tiles );
      *ntile = 0;
   }

   astEnd;

   return tiles;
}
Exemplo n.º 8
0
int *smf_find_bad_dets( Grp *igrp,  int size, int *nbaddet, int *status ){

/* Local Variables */
   float *p = NULL;
   int *result;
   int dims[ 3 ];
   int el;
   int i;
   int ifile;
   int indf;
   int j;
   int k;
   int ndim;
   int outlen;

/* Initialise returned values. */
   *nbaddet = 0;
   result = NULL;
   outlen = 0;

/* Check inherited status */
   if( *status != SAI__OK ) return result;

/* Loop round all the input NDFs. */
   for( ifile = 1; ifile <= size && *status == SAI__OK; ifile++ ) {

/* Get an NDF identifier for the input NDF. */
      ndgNdfas( igrp, ifile, "READ", &indf, status );

/* Get the dimensions of the NDF. */
      ndfDim( indf, 3, dims, &ndim, status );

/* If the returned array is shorter than the number of detectors in this
   NDF, extend the returned array and initialise the new elements to
   zero. */
      if( outlen < dims[ 1 ] ) {
         result = astGrow( result, dims[ 1 ], sizeof( int ) );
         if( astOK ) {
            for( i = outlen; i < dims[ 1 ]; i++ ) result[ i ] = 0;
            outlen = dims[ 1 ];
         }
      }

/* Map the data array of the NDF. */
      ndfMap( indf, "Data", "_REAL", "READ", (void *) &p, &el, status );

/* Loop round all elements of the NDF. */
      for( k = 0; k < dims[ 2 ]; k++ ) {
         for( j = 0; j < dims[ 1 ]; j++ ) {
            for( i = 0; i < dims[ 0 ]; i++ ) {

/* If this data value is good, indicate that the detector has some good
   values, and break out of the "i" loop to move on to the next spectrum. */
               if( *p != VAL__BADR ) {
                  result[ j ] = 1;
                  p += dims[ 0 ] - i;
                  break;
               } else {
                  p++;
               }
            }
         }
      }

/* Annul the input NDF identifier. */
      ndfAnnul( &indf, status );
   }

/* Count the number of bad detectors. */
   for( i = 0; i < outlen; i++ ) {
      if( result[ i ] == 0 ) (*nbaddet)++;
   }

/* Return the result. */
   return result;
}
Exemplo n.º 9
0
smfDetposWcsCache *smf_detpos_wcs( smfHead *hdr, int index, double dut1,
                                   const double telpos[3],
                                   AstFrameSet **fset, smfDetposWcsCache *cache,
                                   int *status ) {

/* Local Variables: */
   AstCmpMap *cmap1 = NULL;    /* Parallel CmpMap holding both LutMaps */
   AstLutMap *latmap = NULL;   /* LutMap holding latitude values */
   AstLutMap *lonmap = NULL;   /* LutMap holding longitude values */
   AstMapping *map = NULL;     /* GRID->SKY Mapping */
   AstSkyFrame *csky = NULL;   /* SkyFrame to put in returned FrameSet */
   const double *p1;           /* Pointer to next lon or lat value to copy */
   double *p2;                 /* Pointer to next lon value */
   double *p3;                 /* Pointer to next lat value */
   int i;                      /* Index of current detector */
   int nrec;                   /* Number of detectors */
   int outperm[ 2 ];           /* Axis permutation */
   smfDetposWcsCache *result;  /* Pointer to returned cache structure */

/* If a negative index was supplied just free the allocated resources and
   return. */
   if( index < 0 && cache ) {
      if( cache->latlut ) cache->latlut = astFree( cache->latlut );
      if( cache->lonlut ) cache->lonlut = astFree( cache->lonlut );
      if( cache->pmap ) cache->pmap = astAnnul( cache->pmap );
      if( cache->grid ) cache->grid = astAnnul( cache->grid );
      if( cache->sky ) cache->sky = astAnnul( cache->sky );
      cache = astFree( cache );
      return NULL;
   }

/* Check inherited status */
   result = cache;
   if( *status != SAI__OK) return result;

/* If no cache structure was supplied, allocate and initialise one now. */
   if( !cache ) {
      cache = astMalloc( sizeof( *cache ) );
      if( cache ) {
         cache->latlut = NULL;
         cache->lonlut = NULL;
         cache->pmap = NULL;
         cache->grid = NULL;
         cache->sky = NULL;
         result = cache;

      } else {
         *status = SAI__ERROR;
         errRep( FUNC_NAME, FUNC_NAME": Can't allocate memory for cache.",
                 status);
         return NULL;
      }
   }

/* Get the number of detectors. */
   nrec = hdr->ndet;

/* Get a pointer to the start of the detpos values for the requested
   time slice. */
   p1 = hdr->detpos + 2*nrec*index;

/* Check there is more than 1 detector. */
   if( nrec > 1 ) {

/* It is possible that we have not allocated enough memory since
   this memory is allocated for the first file but subsequent files
   may have more receptors. So we use astGrow. */

/* If required, allocate memory to hold the individual look up tables for
   lon and lat vaues. */
      cache->lonlut = astGrow( cache->lonlut, nrec, sizeof( double ) );
      cache->latlut = astGrow( cache->latlut, nrec, sizeof( double ) );

/* Check the memory was allocated succesfully. */
      if( cache->lonlut && cache->latlut ) {

/* Copy the lon and lat values for the requested time slice from the
   smfHead structure to the local lut arrays. */
         p2 = cache->lonlut;
         p3 = cache->latlut;
         for( i = 0; i < nrec; i++ ) {
            *(p2++) = *(p1++);
            *(p3++) = *(p1++);
         }

/* Create the Mapping from GRID to SKY positions. This is a PermMap to
   duplicate the detector index, followed by 2 LutMaps in parallel to
   generate the lon and lat values. Set the LutInterpattribute in these
   LutMaps so that they use nearest neighbour interpolation. */
         lonmap = astLutMap( nrec, cache->lonlut, 1.0, 1.0, "LutInterp=1" );
         latmap = astLutMap( nrec, cache->latlut, 1.0, 1.0, "LutInterp=1" );
         cmap1 = astCmpMap( lonmap, latmap, 0, " " );

         latmap = astAnnul( latmap );
         lonmap = astAnnul( lonmap );

         if( !cache->pmap ) {
            outperm[ 0 ] = 1;
            outperm[ 1 ] = 1;
            cache->pmap = astPermMap( 2, NULL, 2, outperm, NULL, " " );
            astExempt( cache->pmap );
         }
         map = (AstMapping *) astCmpMap( cache->pmap, cmap1, 1, " " );

         cache->pmap = astAnnul( cache->pmap );
         cmap1 = astAnnul( cmap1 );
      }

/* If thre is only one detector, use a PermMap to describe this one
   position. rather than a LutMap (LutMaps cannot describe a single
   position). */
   } else {
      outperm[ 0 ] = -1;
      outperm[ 1 ] = -2;
      map = (AstMapping *) astPermMap( 2, NULL, 2, outperm, p1, " " );
   }

/* Create two Frames to put in the FrameSet. */
   if( !cache->grid ) {
      cache->grid = astFrame( 2, "Domain=GRID" );
      astExempt( cache->grid );
   }

   if( !cache->sky ) {
      cache->sky = astSkyFrame( "System=AzEl" );
      astSetD( cache->sky, "ObsLon", -telpos[ 0 ] );
      astSetD( cache->sky, "ObsLat", telpos[ 1 ] );
      astExempt( cache->sky );

/* If the detpos positions are referred to the TRACKING frame, change
   the SkyFrame from AZEL to the AST equivalent of the TRACKING Frame. */
      if( !hdr->dpazel ) {
         astSetC( cache->sky, "System", sc2ast_convert_system( hdr->state->tcs_tr_sys,
                                                     status ) );
      }
   }

/* Take a copy of the skyframe, and then modify its Epoch attribute. We take a
   copy since otherwise all FrameSets returned by this function would share
   the same current Frame, and so the attribute change would affect them all.
   Always use TCS_TAI. smf_open_file corrects the JCMTState structure
   if TCS_TAI is missing. Remember to convert from TAI to TDB (as required by
   the Epoch attribute). */
   csky = astClone( cache->sky );
   astSet( csky, "Epoch=MJD %.*g, dut1=%.*g",
           DBL_DIG, hdr->state->tcs_tai + 32.184/SPD,
           DBL_DIG, dut1 );

/* Create the FrameSet */
   *fset = astFrameSet( cache->grid, " " );
   astAddFrame( *fset, AST__BASE, map, csky );

/* Free resources */
   map =astAnnul( map );
   csky =astAnnul( csky );

/* Exempt the FrameSet pointer from the AST context system rather because
   we do not know when, or in which context, it will be used. It will be
   annulled either in smf_tslice_ast or in smf_close_file. */
   astExempt( *fset );

   return result;
}
CupidPixelSet *cupidCFFreePS( CupidPixelSet *ps, int *ipa, int nel,
         int *status ){
/*
*+
*  Name:
*     cupidCFFreePS

*  Purpose:
*     Free a CupidPixelSet structure.

*  Language:
*     Starlink C

*  Synopsis:
*     CupidPixelSet *cupidCFFreePS( CupidPixelSet *ps, int *ipa, int nel,
*        int *status )

*  Description:
*     This function releases the resources used by a CupidPixelSet
*     structure, returning the PixelSet structure itself to a cache of
*     unused structures.

*  Parameters:
*     ps
*        Pointer to the CupidPixelSet structure to be freed.
*     ipa
*        Pointer to pixel assignment array. If this is not NULL, the
*        entire array is checked to see any pixels are still assigned to
*        the PixelSet which is being freed. An error is reported if any
*        such pixels are found. No check is performed if the pointer is
*        NULL.
*     nel
*        The length of the "ipa" array, if supplied.
*     status
*        Pointer to the inherited status value.

*  Returned Value:
*     A NULL pointer.

*  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:
*     26-NOV-2005 (DSB):
*        Original version.
*     {enter_further_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
*/

/* Local Variables: */

   int i;              /* Index of neighbouring PixelSet */

   if( !ps ) return NULL;

/* If required, check that no pixels are still assigned to the PixelSet
   which is being freed. */
   if( ipa && *status == SAI__OK ) {
      int n = 0;
      for( i = 0; i < nel; i++ ) {
         if( ipa[ i ] == ps->index ) n++;
      }

      if( n ) {
         *status = SAI__ERROR;
         msgSeti( "I", ps->index );
         if( n > 1 ) {
            msgSeti( "N", n );
            errRep( "CUPIDCFFREEPS_ERR1", "Attempt made to free PixelSet ^I "
                    "whilst ^N pixels are still assigned to it (internal "
                    "CUPID programming error).", status );
         } else {
            errRep( "CUPIDCFFREEPS_ERR1", "Attempt made to free PixelSet ^I "
                    "whilst 1 pixel is still assigned to it (internal "
                    "CUPID programming error).", status );
         }
      }
   }


/* Put all scalar fields back to their initial values in prepreation for
   the PixelSet pointer being re-issued by cupidMakePS. */
   ps->nebs = astFree( ps->nebs );
   ps->cols = astFree( ps->cols );
   ps->nneb = 0;
   ps->pop = 0;
   ps->edge = 0;
   ps->vpeak = -DBL_MAX;
   ps->index = CUPID__CFNULL;
   ps->lneb = -1;
   ps->lnebi = -1;

/* Move the supplied PixelSet structure to the end of the cache so that
   it can be re-used. */
   cupid_ps_cache = astGrow( cupid_ps_cache, cupid_ps_cache_size + 1,
                             sizeof( CupidPixelSet * ) );
   if( astOK ) cupid_ps_cache[ cupid_ps_cache_size++ ] = ps;

/* Return a NULL pointer. */
   return NULL;

}
Exemplo n.º 11
0
void smf_snrmask( ThrWorkForce *wf, int abssnr, unsigned char *oldmask,
                  const double *map, const double *mapvar,
                  const dim_t *dims, double snr_hi, double snr_lo,
                  unsigned char *mask, int *status ){

/* Local Variables: */
   const double *pm = NULL;
   const double *pv = NULL;
   dim_t i;
   dim_t j;
   double snr;
   int *cindex = NULL;
   int *ps = NULL;
   int *psn = NULL;
   int *table = NULL;
   int iass;
   int iclean;
   int iclump;
   int ineb;
   int itemp;
   int itop1;
   int itop2;
   int iworker;
   int neb_offset[ 4 ];
   int nworker;
   int ok;
   int rowstep;
   int top;
   smfSnrMaskJobData *job_data = NULL;
   smfSnrMaskJobData *pdata = NULL;
   unsigned char *maskold = NULL;

/* Check inherited status */
   if( *status != SAI__OK ) return;

/* Save a copy of the old mask, if supplied. Doing it now, means that the
   old and new mask pointers can be the same. */
   if( oldmask ) maskold = astStore( NULL, oldmask,
                                     sizeof(*oldmask)*dims[0]*dims[1] );

/* Allocate an array to hold a clump index for every map pixel. Initialise
   it to hold zeros. */
   cindex = astCalloc( dims[ 0 ]*dims[ 1 ], sizeof( *cindex ) );

/* Initialise the index to assign to the next clump of pixels found above
   the lower SNR limit. Note, no clump is given an index of zero. */
   top = 1;

/* Initialise the pointer to the table holding associated clump indices.
   The first element is unused, so set it to a safe value of zero (i.e.
   "no clump"). */
   table = astCalloc( top, sizeof( *table ) );

/* Set up the vector offsets to the three neighbouring pixels in the lower
   row, and the left hand neighbour in the current row. */
   neb_offset[ 0 ] = -1;              /* Left neighbour in current row */
   neb_offset[ 1 ] = -dims[ 0 ] - 1;  /* Left neighbour in lower row */
   neb_offset[ 2 ] = -dims[ 0 ];      /* Central neighbour in lower row */
   neb_offset[ 3 ] = -dims[ 0 ] + 1;  /* Right neighbour in lower row */

/* Loop round the map, looking for pixels that are above the lower SNR
   limit. Within this loop we store a positive clump index for each pixel that
   is above the lower SNR limit. Each clump of contiguous pixel above the limit
   has a separate clump index. If two clumps touch each other, we associate
   their indices together using a table to indicate that they are part of the
   same physical clump. */
   pm = map;
   pv = mapvar;
   ps = cindex;
   for( j = 0; j < dims[ 1 ] && *status == SAI__OK; j++ ) {
      for( i = 0; i < dims[ 0 ]; i++, pm++, pv++, ps++ ) {

/* Get the SNR value. */
         if( mapvar ) {
            if( *pm != VAL__BADD && *pv != VAL__BADD && *pv > 0.0 ){
               snr = *pm / sqrt( *pv );
            } else {
               snr = VAL__BADD;
            }
         } else {
            snr = *pm;
         }

/* If source can be negative as well as positive, use the absolute SNR in
   the following check. */
         if( abssnr && snr != VAL__BADD ) snr = fabs( snr );

/* Check the SNR is good and above the lower limit. */
         if( snr != VAL__BADD && snr > snr_lo ){

/* The three neighbouring pixels on row (j-1), and the left hand
   neighbouring pixel on row j, have already been checked on earlier
   passes round this loop. Check each of these four pixels in turn to see
   if they were flagged as being above the lower SNR limit. */
            itop1 = 0;
            for( ineb = 0; ineb < 4; ineb++ ) {

/* Get a pointer to the neighbouring clump index value, checking it is not off
   the edge of the array. */
               if( ineb == 0 ) {
                  ok = ( i > 0 );
               } else if( ineb == 1 ) {
                  ok = ( i > 0 && j > 0 );
               } else if( ineb == 2 ) {
                  ok = ( j > 0 );
               } else {
                  ok = ( i < dims[ 0 ] - 1 && j > 0 );
               }
               if( ok ) {
                  psn = ps + neb_offset[ ineb ];

/* If this neighbour is flagged as above the lower SNR limit (i.e. has a
   positive clump index), and the current pixel has not yet been assigned to
   an existing clump, assign the neighbour's clump index to the current pixel. */
                  if( *psn > 0 ) {
                     if( *ps == 0 ) {
                        *ps = *psn;

/* Find the clump index at the top of the tree containing the neighbouring pixel. */
                        itop1 = *psn;
                        while( table[ itop1 ] ) itop1 = table[ itop1 ];

/* If this neighbour is flagged as above the lower SNR limit, but the
   current pixel has already been assigned to an existing clump, the current
   pixel is adjacent to both clumps and so joins them into one. So record that
   this neighbours clump index should be associated with the clump index of
   the current pixel. */
                     } else {

/* We need to check first that the two clump indices are not already part
   of the same tree of associated clumps. Without this we could produce
   loops in the tree. Find the clump indices at the top of the tree
   containing the neighbouring pixel. */
                        itop2 = *psn;
                        while( table[ itop2 ] ) itop2 = table[ itop2 ];

/* If the two clumps are not in the same tree, indicate that the pixel
   index at the top of the tree for the neighbouring pixels clump index is
   associated with the central pixel's clump index. */
                        if( itop1 != itop2 ) table[ itop2 ] = *ps;
                     }
                  }
               }
            }

/* If the current pixel has no neighbours that are above the lower SNR
   limit, we start a new clump for the current pixel. */
            if( *ps == 0 ) {

/* Assign the next clump index to the current pixel, and then increment
   the next clump index. Report an error if we have reached the max
   allowable clump index value. */
               if( top == INT_MAX ) {
                  *status = SAI__ERROR;
                  errRep( "", "smf_snrmask: Too many low-SNR clumps found.",
                          status );
                  break;
               }
               *ps = top++;

/* Extend the table that holds the associations between clumps. This
   table has one element for each clump index (plus an unused element at the
   start for the unused clump index "0"). The value stored in this table
   for a given clump index is the index of another clump with which the
   first clump should be associated. If two clumps are associated it
   indicates that they are part of the same physical clump. Associations
   form a tree structure. A value of zero in this table indicates that
   the clump is either unassociated with any other clump, or is at the head
   of a tree of associated clumps. */
               table = astGrow( table, top, sizeof( *table ) );
               if( *status != SAI__OK ) break;
               table[ *ps ] = 0;
            }
         }
      }
   }

/* We now loop round the map again, this time looking for pixels that are
   above the higher SNR limit. */
   pm = map;
   pv = mapvar;
   ps = cindex;
   for( j = 0; j < dims[ 1 ]; j++ ) {
      for( i = 0; i < dims[ 0 ]; i++, pm++, pv++, ps++ ) {

/* Get the SNR value. */
         if( mapvar ) {
            if( *pm != VAL__BADD && *pv != VAL__BADD && *pv > 0.0 ){
               snr = *pm / sqrt( *pv );
            } else {
               snr = VAL__BADD;
            }
         } else {
            snr = *pm;
         }

/* If source can be negative as well as positive, use the absolute SNR. */
         if( abssnr && snr != VAL__BADD ) snr = fabs( snr );

/* Check the SNR is good and above the upper limit. */
         if( snr != VAL__BADD && snr > snr_hi ){

/* Since this pixel is above the higher SNR limit, it must also be above
   the lower SNR Limit, and so will have a non-zero clump index. We flag that
   this clump contains "source" pixels by storing a value of -1 for it in the
   clump association table. First record the original value for later use. */
            iass = table[ *ps ];
            table[ *ps ] = -1;

/* If this clump index is associated with another clump (i.e. had a non-zero
   value in the clump association table), the two clumps adjoins each other.
   So indicate that the second clump also contains "source" pixels by
   changing its table value to -1. Enter a loop to do this all the way up
   to the top of the association tree. Note, this is not necessarily all
   adjoining clumps, since we have only gone "up" the tree - there may be
   other adjoining clumps lower down the tree. */
            while( iass > 0 ) {
               itemp =  table[ iass ];
               table[ iass ] = -1;
               iass = itemp;
            }
         }
      }
   }

/* Now check all cumps to see if they adjoin a "source" clump. Note, no
   clumps are given the index zero, so we skip the first element of the
   table. */
   for( iclump = 1; iclump < top; iclump++ ) {
      iass = table[ iclump ];

/* Work up the tree of neighbouring clumps until we find a clump that has
   an index of 0 or -1. If 0, it means that we have reached the top of
   the tree without finding a "source" clump. If -1 it means we have
   reached a source clump. */
      while( iass > 0 ) {
         iass =  table[ iass ];
      }

/* If we have found a source clump, then all clumps above it in the tree
   should already be set to -1. We now walk up the tree from the current
   clump until we reach the source clump, marking all intermediate clumps
   as source clumps by setting them to -1 in the table. */
      if( iass < 0 ) {
         iass = iclump;
         while( iass > 0 ) {
            itemp =  table[ iass ];
            table[ iass ] = -1;
            iass = itemp;
         }

/* If no source clump was found, mark all intermediate clumps as
   non-source by setting theem to zero in the table. This may give us a
   little extra speed (maybe) since subsequent walks will terminate
   sooner. */
      } else {
         iass = iclump;
         while( iass > 0 ) {
            itemp =  table[ iass ];
            table[ iass ] = 0;
            iass = itemp;
         }
      }
   }

/* One last pass, to store the final mask values. We can multi-thread
   this bit. Create structures used to pass information to the worker
   threads. If we have more threads than rows, we will process one row
   in each thread and so we can reduce the number of threads used to
   equal the number of rows. */
   nworker = wf ? wf->nworker : 1;
   if( nworker > (int) dims[ 1 ] ) nworker = dims[ 1 ];
   job_data = astMalloc( nworker*sizeof( *job_data ) );

/* Check we can de-reference the job data pointer safely. */
   if( *status == SAI__OK ) {

/* Decide how many rows to process in each thread. */
      rowstep = dims[ 1 ]/nworker;
      if( rowstep == 0 ) rowstep = 1;

/* Set up the information needed by each thread, */
      for( iworker = 0; iworker < nworker; iworker++ ) {
         pdata = job_data + iworker;
         pdata->operation = 1;
         pdata->cindex = cindex;
         pdata->jlo = iworker*rowstep;
         if( iworker == nworker - 1 ) {
            pdata->jhi = dims[ 1 ] - 1;
         } else {
            pdata->jhi = pdata->jlo + rowstep - 1;
         }
         pdata->rowlen = dims[ 0 ];
         pdata->mask = mask;
         pdata->table = table;

/* Pass the job to the workforce for execution. */
         thrAddJob( wf, 0, pdata, smf1_snrmask_job, 0, NULL, status );
      }

/* Wait for the workforce to complete all jobs. */
      thrWait( wf, status );

/* Now clean up the very crinkly edges of the mask. Also, the mask may
   contain small holes which need to be cleaned. Clean it NCLEAN times. */
      for( iclean = 0; iclean < NCLEAN; iclean++ ) {

/* Clean the mask, putting the cleaned mask into "cindex" array. We
   exclude pixels in the first and last rows since they do not have a
   complete set of neighbours (each worker thread also ignores the first
   and last pixel in each row for the same reason). Decide how many rows
   to process in each thread. */
         rowstep = ( dims[ 1 ] - 2 )/nworker;
         if( rowstep == 0 ) rowstep = 1;

/* Modify the information needed by each thread, */
         for( iworker = 0; iworker < nworker; iworker++ ) {
            pdata = job_data + iworker;
            pdata->operation = 2;
            pdata->jlo = iworker*rowstep + 1;
            if( iworker == nworker - 1 ) {
               pdata->jhi = dims[ 1 ] - 2;
            } else {
               pdata->jhi = pdata->jlo + rowstep - 1;
            }

/* Pass the job to the workforce for execution. */
            thrAddJob( wf, 0, pdata, smf1_snrmask_job, 0, NULL, status );
         }

/* Wait for the workforce to complete all jobs. */
         thrWait( wf, status );

/* Transfer the new mask from the "cindex" array back to the "mask" array.
   Add in any source pixels from the old mask if required. */
         for( iworker = 0; iworker < nworker; iworker++ ) {
            pdata = job_data + iworker;
            pdata->maskold = maskold;
            pdata->operation = 3;
            thrAddJob( wf, 0, pdata, smf1_snrmask_job, 0, NULL, status );
         }
         thrWait( wf, status );

/* If an old mask was supplied, ensure any source pixels in the old mask
   are also source pixels in the new mask. */
         if( oldmask ) {
            for( iworker = 0; iworker < nworker; iworker++ ) {
               pdata = job_data + iworker;
               pdata->maskold = maskold;
               pdata->operation = 4;
               thrAddJob( wf, 0, pdata, smf1_snrmask_job, 0, NULL, status );
            }
            thrWait( wf, status );
         }
      }
   }

/* Free resources. */
   job_data = astFree( job_data );
   maskold = astFree( maskold );
   table = astFree( table );
   cindex = astFree( cindex );
}
Exemplo n.º 12
0
static void smf1_find_boxes( dim_t intslice, const JCMTState *allstates, dim_t box,
                             dim_t *ontslice, dim_t **box_starts, int *status ) {
/*
*  Name:
*     smf1_find_boxes

*  Purpose:
*     Find the length of the output time axis, and find the input time slices
*     at the start and end of each fitting box.

*  Language:
*     Starlink ANSI C

*  Type of Module:
*     SMURF subroutine

*  Invocation:
*     smf1_find_boxes( dim_t intslice, const JCMTState *allstates, dim_t box,
*                      dim_t *ontslice, dim_t **box_starts, int *status )

*  Arguments:
*     intslice = dim_t (Given)
*        Number of time slices in input.
*     allstates = const JCMTState * (Given)
*        Pointer to the JCMT state information for all input time slices.
*     box = dim_t (Given)
*        Number of complete HWP rotations per fitting box.
*     ontslice = dim_t * (Returned)
*        Number of time slices in output. There is one output time slice
*        for each fitting box.
*     box_starts = dim_t ** (Returned)
*        Pointer to a newly allocated array holding the input time slice
*        index at which each fitting box starts. This array has length
*        equal to (*ontslice) + 1  - the extra last element is the index
*        of the first input time slice beyond the end of the last fitting
*        box.
*     status = int* (Given and Returned)
*        Pointer to global status.

*  Description:
*     This routine determines the start and end of each box over which
*     to perform a least squares fit. It is needed because the
*     half-waveplate (HWP) angles (the POL_ANG values in the JCMT state
*     array) are not completely regular.

*/

/* Local Variables: */
   char more;
   const JCMTState *state;
   dim_t iel;
   dim_t itime;
   dim_t nrot;
   double ang0 = 0.5*AST__DPI;

/* Initialise returned values. */
   *ontslice = 0;
   *box_starts = NULL;

/* Check inherited status. */
   if( *status != SAI__OK ) return;

/* If the HWP angle is above PI, move on until the HWP angle wraps back to
   zero. */
   nrot = 0;
   itime = 0;
   state = allstates;
   more = 1;
   while( more && state->pol_ang > ang0 ) {
      if( ++itime == intslice ) more = 0;
      state++;
   }

/* HWP angles vary between 0 and 2*PI in a roughly linear manner, but
   with some sudden steps. When the HWP angle reaches 2*PI it wraps
   back round to zero. Find the first time slice for which the HWP
   angle is greater than PI (a safe value in the middle of the range). */
   while( more && state->pol_ang <= ang0 ) {
      if( ++itime == intslice ) more = 0;
      state++;
   }

/* This is the start of the first box. */
   if( more ) {
      iel = (*ontslice)++;
      *box_starts = astGrow( *box_starts, *ontslice, sizeof( **box_starts ) );
      if( *status == SAI__OK ) (*box_starts)[ iel ] = itime;
   }

/* Loop over all rotations. */
   while( 1 ) {

/* Move on until the HWP angle wraps round back to zero. */
      while( more && state->pol_ang > ang0 ) {
         if( ++itime == intslice ) more = 0;
         state++;
      }

/* Move on until the HWP angle again exceeds PI. */
      while( more && state->pol_ang <= ang0 ) {
         if( ++itime == intslice ) more = 0;
         state++;
      }

/* Break if we have reached the end of the time line. */
      if( !more ) break;

/* Increment the number of HWP rotations covered so far. */
      nrot++;

/* If the box now encompasses the correct number of rotations, mark the
   current time slice as the start of the next box. */
      if( nrot == box ) {
         nrot = 0;
         iel = (*ontslice)++;
         *box_starts = astGrow( *box_starts, *ontslice, sizeof( **box_starts ) );
         if( *status == SAI__OK ) (*box_starts)[ iel ] = itime;
      }
   }

/* The number of output time slices will be one less than the number of
   values in "box_Starts", because the last value in "box_starts" is the end
   of the last box. */
   (*ontslice)--;
}