Beispiel #1
0
void smf_rebinsparse( smfData *data, int first, int *ptime, AstFrame *ospecfrm,
                      AstMapping *ospecmap, AstSkyFrame *oskyframe,
                      Grp *detgrp, int lbnd_out[ 3 ], int ubnd_out[ 3 ],
                      int genvar, float *data_array, float *var_array,
                      int *ispec, float *texp_array, float *teff_array,
                      double *fcon, int *status ){

/* Local Variables */
   AstCmpMap *fmap = NULL;      /* Mapping from spectral grid to topo freq Hz */
   AstCmpMap *ssmap = NULL;     /* I/p GRID-> o/p PIXEL Mapping for spectral axis */
   AstFitsChan *fc = NULL;      /* Storage for FITS headers */
   AstFrame *specframe = NULL;  /* Spectral Frame in input FrameSet */
   AstFrame *specframe2 = NULL; /* Temporary copy of SpecFrame in input WCS */
   AstFrameSet *fs = NULL;      /* A general purpose FrameSet pointer */
   AstFrameSet *swcsin = NULL;  /* FrameSet describing spatial input WCS */
   AstMapping *fsmap = NULL;    /* Base->Current Mapping extracted from a FrameSet */
   AstMapping *specmap = NULL;  /* PIXEL -> Spec mapping in input FrameSet */
   char *fftwin = NULL;  /* Name of FFT windowing function */
   const char *name = NULL; /* Pointer to current detector name */
   const double *tsys=NULL; /* Pointer to Tsys value for first detector */
   dim_t timeslice_size; /* No of detector values in one time slice */
   double *spectab = NULL;/* Workspace for spectral output grid positions */
   double *xin = NULL;   /* Workspace for detector input grid positions */
   double *xout = NULL;  /* Workspace for detector output pixel positions */
   double *yin = NULL;   /* Workspace for detector input grid positions */
   double *yout = NULL;  /* Workspace for detector output pixel positions */
   double at;            /* Frequency at which to take the gradient */
   double dnew;          /* Channel width in Hz */
   double fcon2;         /* Variance factor for whole file */
   double k;             /* Back-end degradation factor */
   double tcon;          /* Variance factor for whole time slice */
   float *pdata = NULL;  /* Pointer to next data sample */
   float *qdata = NULL;  /* Pointer to next data sample */
   float rtsys;          /* Tsys value */
   float teff;           /* Effective integration time, times 4 */
   float texp;           /* Total time ( = ton + toff ) */
   float toff;           /* Off time */
   float ton;            /* On time */
   int *nexttime = NULL; /* Pointer to next time slice index to use */
   int dim[ 3 ];         /* Output array dimensions */
   int found;            /* Was current detector name found in detgrp? */
   int good;             /* Are there any good detector samples? */
   int ibasein;          /* Index of base Frame in input FrameSet */
   int ichan;            /* Index of current channel */
   int iv;               /* Offset to next element */
   int iz;               /* Output grid index on axis 3 */
   int nchan;            /* Number of input spectral channels */
   int pixax[ 3 ];       /* The output fed by each selected mapping input */
   int specax;           /* Index of spectral axis in input FrameSet */
   size_t irec;          /* Index of current input detector */
   size_t itime;         /* Index of current time slice */
   smfHead *hdr = NULL;  /* Pointer to data header for this time slice */

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

/* Begin an AST context.*/
   astBegin;

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

/* Store the dimensions of the output array. */
   dim[ 0 ] = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1;
   dim[ 1 ] = ubnd_out[ 1 ] - lbnd_out[ 1 ] + 1;
   dim[ 2 ] = ubnd_out[ 2 ] - lbnd_out[ 2 ] + 1;

/* Store the number of pixels in one time slice */
   timeslice_size = (data->dims)[ 0 ]*(data->dims)[ 1 ];

/* We want a description of the spectral WCS axis in the input file. If
   the input file has a WCS FrameSet containing a SpecFrame, use it,
   otherwise we will obtain it from the FITS header later. NOTE, if we knew
   that all the input NDFs would have the same spectral axis calibration,
   then the spectral WCS need only be obtained from the first NDF. However,
   in the general case, I presume that data files may be combined that use
   different spectral axis calibrations, and so these differences need to
   be taken into account. */
   if( hdr->tswcs ) {
      fs = astClone( hdr->tswcs );

/* The first axis should be a SpecFrame. See if this is so. If not annul
   the specframe pointer. */
      specax = 1;
      specframe = astPickAxes( fs, 1, &specax, NULL );
      if( !astIsASpecFrame( specframe ) ) specframe = astAnnul( specframe );
   }

/* If the above did not yield a SpecFrame, use the FITS-WCS headers in the
   FITS extension of the input NDF. Take a copy of the FITS header (so that
   the contents of the header are not changed), and then read a FrameSet
   out of it. */
   if( !specframe ) {
      fc = astCopy( hdr->fitshdr );
      astClear( fc, "Card" );
      fs = astRead( fc );

/* Extract the SpecFrame that describes the spectral axis from the current
   Frame of this FrameSet. This is assumed to be the third WCS axis (NB
   the different axis number). */
      specax = 3;
      specframe = astPickAxes( fs, 1, &specax, NULL );
   }

/* Split off the 1D Mapping for this single axis from the 3D Mapping for
   the whole WCS. This results in "specmap" holding the Mapping from
   SpecFrame value to GRID value. */
   fsmap = astGetMapping( fs, AST__CURRENT, AST__BASE );
   astMapSplit( fsmap, 1, &specax, pixax, &specmap );

/* Invert the Mapping for the spectral axis so that it goes from input GRID
   coord to spectral coord. */
   astInvert( specmap );

/* Get a Mapping that converts values in the input spectral system to the
   corresponding values in the output spectral system. */
   fs = astConvert( specframe, ospecfrm, "" );

/* Concatenate these Mappings with the supplied spectral Mapping to get
   a Mapping from the input spectral grid axis (pixel axis 1) to the
   output spectral grid axis (pixel axis 3). Simplify the Mapping. */
   ssmap = astCmpMap( astCmpMap( specmap, astGetMapping( fs, AST__BASE,
                                                         AST__CURRENT ),
                                 1, " " ),
                      ospecmap, 1, " " );
   ssmap = astSimplify( ssmap );

/* Create a table with one element for each channel in the input array,
   holding the index of the nearest corresponding output channel. */
   nchan = (data->dims)[ 0 ];
   spectab = astMalloc( sizeof( *spectab )*nchan );
   if( spectab ) {
      for( ichan = 0; ichan < nchan; ichan++ ) spectab[ ichan ] = ichan + 1;
      astTran1( ssmap, nchan, spectab, 1, spectab );
      for( ichan = 0; ichan < nchan; ichan++ ) {
         if( spectab[ ichan ] != AST__BAD ) {
            iz = floor( spectab[ ichan ] + 0.5 );
            if( iz >= 1 && iz <= dim[ 2 ] ) {
               spectab[ ichan ] = iz;
            } else {
               spectab[ ichan ] = 0;
            }
         } else {
            spectab[ ichan ] = 0;
         }
      }
   }

/* Allocate work arrays big enough to hold the coords of all the
   detectors in the current input file.*/
   xin = astMalloc( (data->dims)[ 1 ] * sizeof( *xin ) );
   yin = astMalloc( (data->dims)[ 1 ] * sizeof( *yin ) );
   xout = astMalloc( (data->dims)[ 1 ] * sizeof( *xout ) );
   yout = astMalloc( (data->dims)[ 1 ] * sizeof( *yout ) );

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

/* Store input coords for the detectors. Axis 1 is the detector index, and
   axis 2 is a dummy axis that always has the value 1. */
   for( irec = 0; irec < (data->dims)[ 1 ]; irec++ ) {
      xin[ irec ] = irec + 1.0;
      yin[ irec ] = 1.0;

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

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

/* Find the constant factor associated with the current input file. This
   is the squared backend degradation factor, divided by the noise bandwidth.
   Get the required FITS headers, checking they were found. */
   if( astGetFitsF( hdr->fitshdr, "BEDEGFAC", &k ) &&
       astGetFitsS( hdr->fitshdr, "FFT_WIN", &fftwin ) ){

/* Get a Mapping that converts values in the input spectral system to
   topocentric frequency in Hz, and concatenate this Mapping with the
   Mapping from input GRID coord to the input spectral system. The result
   is a Mapping from input GRID coord to topocentric frequency in Hz. */
      specframe2 = astCopy( specframe );
      astSet( specframe2, "system=freq,stdofrest=topo,unit=Hz" );
      fmap = astCmpMap( specmap, astGetMapping( astConvert( specframe,
                                                            specframe2,
                                                            "" ),
                                                AST__BASE, AST__CURRENT ),
                        1, " " );

/* Differentiate this Mapping at the mid channel position to get the width
   of an input channel in Hz. */
      at = 0.5*nchan;
      dnew = astRate( fmap, &at, 1, 1 );

/* Modify the channel width to take account of the effect of the FFT windowing
   function. Allow undef value because FFT_WIN for old data had a broken value
   in hybrid subband modes. */
      if( dnew != AST__BAD ) {
         dnew = fabs( dnew );

         if( !strcmp( fftwin, "truncate" ) ) {
            dnew *= 1.0;

         } else if( !strcmp( fftwin, "hanning" ) ) {
            dnew *= 1.5;

	    } else if( !strcmp( fftwin, "<undefined>" ) ) {
	      /* Deal with broken data - make an assumption */
	       dnew *= 1.0;

         } else if( *status == SAI__OK ) {
            *status = SAI__ERROR;
            msgSetc( "W", fftwin );
            errRep( FUNC_NAME, "FITS header FFT_WIN has unknown value "
                    "'^W' (programming error).", status );
         }

/* Form the required constant. */
         fcon2 = k*k/dnew;

      } else {
         fcon2 = VAL__BADD;
      }

   } else {
      fcon2 = VAL__BADD;
   }

/* Return the factor needed for calculating Tsys from the variance. */
   if( first ) {
      *fcon = fcon2;
   } else if( fcon2 != *fcon ) {
      *fcon = VAL__BADD;
   }

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

/* Loop round all the time slices in the input file. */
   for( itime = 0; itime < (data->dims)[ 2 ] && *status == SAI__OK; itime++ ) {

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

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

/* Get a FrameSet describing the spatial coordinate systems associated with
   the current time slice of the current input data file. The base frame in
   the FrameSet will be a 2D Frame in which axis 1 is detector number and
   axis 2 is unused. The current Frame will be a SkyFrame (the SkyFrame
   System may be any of the JCMT supported systems). The Epoch will be
   set to the epoch of the time slice. */
      smf_tslice_ast( data, itime, 1, NO_FTS, status );
      swcsin = hdr->wcs;

/* Note the total exposure time (texp) for all the input spectra produced by
   this time slice. */
      ton = hdr->state->acs_exposure;
      if( ton == 0.0 ) ton = VAL__BADR;

      toff = hdr->state->acs_offexposure;
      if( toff == 0.0 ) toff = VAL__BADR;

      if( ton != VAL__BADR && toff != VAL__BADR ) {
         texp = ton + toff;
         teff = 4*ton*toff/( ton + toff );
      } else {
         texp = VAL__BADR;
         teff = VAL__BADR;
      }

/* If output variances are being calculated on the basis of Tsys values
   in the input, find the constant factor associated with the current
   time slice. */
      tcon = AST__BAD;
      if( genvar == 2 && fcon2 != AST__BAD && texp != VAL__BADR ) {
         tcon = fcon2*( 1.0/ton + 1.0/toff );

/* Get a pointer to the start of the Tsys values for this time slice. */
         tsys = hdr->tsys + hdr->ndet*itime;
      }

/* We now create a Mapping from detector index to position in oskyframe. */
      astInvert( swcsin );
      ibasein = astGetI( swcsin, "Base" );
      fs = astConvert( swcsin, oskyframe, "SKY" );
      astSetI( swcsin, "Base", ibasein );
      astInvert( swcsin );

      if( fs == NULL ) {
         if( *status == SAI__OK ) {
            if (data->file) {
               smf_smfFile_msg(data->file, "FILE", 1, "<unknown>");
            } else {
               msgSetc( "FILE", "<unknown>" );
            }
            *status = SAI__ERROR;
            errRep( FUNC_NAME, "The spatial coordinate system in ^FILE "
                    "is not compatible with the spatial coordinate "
                    "system in the first input file.", status );
         }
         break;
      }

/* Transform the positions of the detectors from input GRID to oskyframe
   coords. */
      astTran2( fs, (data->dims)[ 1 ], xin, yin, 1, xout, yout );

/* Loop round all detectors. */
      for( irec = 0; irec < (data->dims)[ 1 ]; irec++ ) {

/* If the detector has a valid position, see if it produced any good
   data values. */
         if( xout[ irec ] != AST__BAD && yout[ irec ] != AST__BAD ) {
            qdata = pdata;
            good = 0;
            for( ichan = 0; ichan < nchan; ichan++ ){
               if( *(qdata++) != VAL__BADR ) {
                  good = 1;
                  break;
               }
            }

/* If it did, calculate the variance associated with each detector
   sample (if required), based on the input Tsys values, and copy the
   spectrum to the output NDF. */
            if( good ) {
               if( *ispec < dim[ 0 ] ){
                  rtsys = tsys ? (float) tsys[ irec ] : VAL__BADR;
                  if( rtsys <= 0.0 ) rtsys = VAL__BADR;
                  if( tcon != AST__BAD && genvar == 2 && rtsys != VAL__BADR ) {
                     var_array[ *ispec ] = tcon*rtsys*rtsys;
                  } else if( var_array ) {
                     var_array[ *ispec ] = VAL__BADR;
                  }

                  if( texp != VAL__BADR ) {
                     texp_array[ *ispec ] = texp;
                     teff_array[ *ispec ] = teff;
                  }

                  for( ichan = 0; ichan < nchan; ichan++, pdata++ ) {
                     iz = spectab[ ichan ] - 1;
                     if( iz >= 0 && iz < dim[ 2 ] ) {
                        iv = *ispec + dim[ 0 ]*iz;
                        data_array[ iv ] = *pdata;
                     }
                  }

                  (*ispec)++;

               } else if( *status == SAI__OK ){
                  *status = SAI__ERROR;
                  msgSeti( "DIM", dim[ 0 ] );
                  errRep( " ", "Too many spectra (more than ^DIM) for "
                          "the output NDF (programming error).", status );
                  break;
               }

/* If this detector does not have any valid data values, increment the data
   pointer to point at the first sample for the next detector. */
            } else {
               pdata += nchan;
            }

/* If this detector does not have a valid position, increment the data
   pointer to point at the first sample for the next detector. */
         } else {
            pdata += nchan;
         }
      }

/* For efficiency, explicitly annul the AST Objects created in this tight
   loop. */
      fs = astAnnul( fs );
   }

/* Free resources */
   spectab = astFree( spectab );
   xin = astFree( xin );
   yin = astFree( yin );
   xout = astFree( xout );
   yout = astFree( yout );

/* End the AST context. This will annul all AST objects created within the
   context (except for those that have been exported from the context). */
   astEnd;
}
Beispiel #2
0
void smf_resampcube_nn( smfData *data, dim_t nchan,
                        dim_t ndet, dim_t nslice, dim_t nxy,
                        dim_t dim[3], AstMapping *ssmap,
                        AstSkyFrame *abskyfrm, AstMapping *iskymap,
                        Grp *detgrp, int moving, float *in_data,
                        float *out_data, int *overlap, int *status ){

/* Local Variables */
   AstMapping *totmap = NULL;  /* WCS->GRID Mapping from template WCS FrameSet */
   const char *name = NULL;    /* Pointer to current detector name */
   dim_t timeslice_size;       /* No of detector values in one time slice */
   double *detxtemplt = NULL;  /* Work space for template X grid coords */
   double *detxskycube = NULL; /* Work space for sky cube X grid coords */
   double *detytemplt = NULL;  /* Work space for template Y grid coords */
   double *detyskycube = NULL; /* Work space for sky cube Y grid coords */
   float *ddata = NULL;        /* Pointer to start of output detector data */
   float *tdata = NULL;        /* Pointer to start of sky cube time slice data */
   int *spectab = NULL;        /* Template->sky cube channel number conversion table */
   int found;                  /* Was current detector name found in detgrp? */
   dim_t gxsky;                /* Sky cube X grid index */
   dim_t gysky;                /* Sky cube Y grid index */
   dim_t idet;                 /* Detector index */
   dim_t itime;                /* Index of current time slice */
   int iv0;                    /* Offset for pixel in 1st sky cube spectral channel */
   smfHead *hdr = NULL;        /* Pointer to data header for this time slice */

/* Initialise */
   *overlap = 0;

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

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

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

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

/* Allocate work arrays to hold the template and sky cube grid coords for each
   detector. */
   detxtemplt = astMalloc( ndet*sizeof( double ) );
   detytemplt = astMalloc( ndet*sizeof( double ) );
   detxskycube = astMalloc( ndet*sizeof( double ) );
   detyskycube = astMalloc( ndet*sizeof( double ) );

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

/* Fill the arrays with the grid coords of each detector. */
   for( idet = 0; idet < ndet; idet++ ) {
      detxtemplt[ idet ] = (double) idet + 1.0;
      detytemplt[ 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 ) {
            detxtemplt[ idet ] = AST__BAD;
            detytemplt[ idet ] = AST__BAD;
         }
      }

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

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

/* Store a pointer to the first output data value in this time slice. */
      tdata = in_data ? ( out_data + itime*timeslice_size ) : NULL;

/* 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 template to the spatial
   GRID axes in the sky cube 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, iskymap, moving,
				 status );
      if( !totmap ) {
         astEnd;
         break;
      }

/* Use this Mapping to get the sky cube spatial grid coords for each
   template detector. */
      astTran2( totmap, ndet, detxtemplt, detytemplt, 1, detxskycube,
                detyskycube );

/* Loop round each detector, obtaining its output value from the sky cube. */
      for( idet = 0; idet < ndet; idet++ ) {

/* Get a pointer to the start of the output spectrum data. */
         ddata = tdata + idet*nchan;

/* Check the detector has a valid position in sky cube grid coords */
         if( detxskycube[ idet ] != AST__BAD && detyskycube[ idet ] != AST__BAD ){

/* Find the closest sky cube pixel and check it is within the bounds of the
   sky cube. */
            gxsky = floor( detxskycube[ idet ] + 0.5 );
            gysky = floor( detyskycube[ idet ] + 0.5 );
            if( gxsky >= 1 && gxsky <= dim[ 0 ] &&
                gysky >= 1 && gysky <= dim[ 1 ] ) {

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

/* Copy the sky cube spectrum into the output time series cube. */
               *overlap = 1;
               if( in_data ) {
                  smf_resampcube_copy( nchan, spectab, iv0, nxy,
                                       ddata, in_data, status );
               } else {
                  break;
               }
            }
         }
      }

/* End the AST context. */
      astEnd;

/* If no input data was supplied, and we have found at least one input
   spectrum that overlaps the sky cube, we can finish early. */
      if( !in_data && *overlap ) break;
   }

/* Free non-static resources. */
L999:;
   spectab = astFree( spectab );
   detxtemplt = astFree( detxtemplt );
   detytemplt = astFree( detytemplt );
   detxskycube = astFree( detxskycube );
   detyskycube = astFree( detyskycube );

}
Beispiel #3
0
void smf_rebincube_ast( ThrWorkForce *wf, smfData *data, int first, int last,
                      int *ptime, dim_t nchan, dim_t ndet, dim_t nslice,
                      dim_t nel, dim_t nxy, dim_t nout, dim_t dim[3],
                      AstMapping *ssmap, AstSkyFrame *abskyfrm,
                      AstMapping *oskymap, Grp *detgrp, int moving,
                      int usewgt, int spread, const double params[],
                      int genvar, double tfac, double fcon,
                      float *data_array, float *var_array,
                      double *wgt_array, float *texp_array,
                      float *teff_array, int *good_tsys, int *nused,
                      int *status ){

/* Local Variables */
   AstCmpMap *detmap = NULL;   /* Mapping from 1D det. index to 2D i/p "grid" coords */
   AstMapping *dtotmap = NULL; /* 1D det index->o/p GRID Mapping */
   AstMapping *fullmap = NULL; /* WCS->GRID LutMap from input WCS FrameSet */
   AstMapping *lutmap = NULL;  /* Mapping that identifies detectors to be used */
   AstMapping *splut = NULL;   /* Spatial LutMap */
   AstMapping *sslut = NULL;   /* Spectral LutMap */
   AstMapping *totmap = NULL;  /* WCS->GRID Mapping from input WCS FrameSet */
   AstPermMap *pmap;           /* Mapping to rearrange output axes */
   const char *name = NULL;    /* Pointer to current detector name */
   const double *tsys = NULL;  /* Pointer to Tsys value for first detector */
   dim_t iv;                   /* Vector index into output 3D array */
   double *detlut = NULL;      /* Work space for detector mask */
   double blk_bot[ 2*MAXTHREADS + 1 ]; /* First o/p channel no. in each block */
   double con;                 /* Constant value */
   double dtemp;               /* Temporary value */
   double tcon;                /* Variance factor for whole time slice */
   float *detwork = NULL;      /* Work array for detector values */
   float *tdata = NULL;        /* Pointer to start of input time slice data */
   float *varwork = NULL;      /* Work array holding variances for 1 slice/channel */
   float *vp = NULL;           /* Pointer to next "varwork" element */
   float invar;                /* Input variance */
   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 ast_flags;              /* Basic flags to use with astRebinSeq */
   int blk_size;               /* Number of channels processed by a single thread */
   int found;                  /* Was current detector name found in detgrp? */
   int iblock;                 /* Index of current spectral block */
   dim_t ichan;                /* Index of current channel */
   dim_t idet;                 /* detector index */
   int ignore;                 /* Ignore this time slice? */
   int inperm[ 3 ];            /* Input axis permutation array */
   dim_t itime;                /* Index of current time slice */
   int junk;                   /* Unused parameter */
   int lbnd_in[ 2 ];           /* Lower input bounds on receptor axis */
   int ldim[ 3 ];              /* Output array lower GRID bounds */
   int maxthreads;             /* Max no. of threads to use when re-binning */
   int nblock;                 /* Number of spectral blocks */
   int nthreads;               /* Number of threads to use when re-binning */
   int outperm[ 3 ];           /* Output axis permutation array */
   int timeslice_size;         /* Number of elements in a time slice */
   int ubnd_in[ 2 ];           /* Upper input bounds on receptor axis */
   int uddim[ 1 ];             /* Detector array upper GRID bounds */
   int udim[ 3 ];              /* Output array upper GRID bounds */
   smfHead *hdr = NULL;        /* Pointer to data header for this time slice */

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

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

/* Fill an array with the lower grid index bounds of the output. */
   ldim[ 0 ] = 1;
   ldim[ 1 ] = 1;
   ldim[ 2 ] = 1;

/* Integer upper grid index bounds of the output. */
   udim[ 0 ] = dim[ 0 ];
   udim[ 1 ] = dim[ 1 ];
   udim[ 2 ] = dim[ 2 ];

/* Integer upper bounds of detector array. */
   uddim[ 0 ] = ndet;

/* Store the size of an input time slice. */
   timeslice_size = nel/nslice;

/* Create a LutMap that holds the output spectral axis GRID value at
   the centre of each input spectral axis pixel. LutMaps are faster to
   evaluate, and so astRebinSeq will go faster. We can use LutMaps without
   loosing accuracy since astRebinSeq only ever transforms the GRID
   values at input pixel centres (i.e. integer GRID values), and so the
   LutMap will always return a tabulated value rather than an
   interpolated value. */
   atlTolut( (AstMapping *) ssmap, 1.0, (double) nchan, 1.0, "LutInterp=1",
              &sslut, status );

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

/* Initialisation the flags for astRebinSeq (we do not include flag
   AST__REBININIT because the arrays have been initialised). */
   ast_flags = AST__USEBAD;
   if( usewgt ) ast_flags = ast_flags | AST__VARWGT;

   if( genvar == 1 ) {
      ast_flags = ast_flags | AST__GENVAR;
   } else if( genvar == 2 ) {
      ast_flags = ast_flags | AST__USEVAR;
   }

/* If required, allocate a work array to hold all the input variances for a
   single time slice. */
   if( usewgt || genvar == 2 ) varwork = astMalloc( timeslice_size * sizeof( float ) );

/* Allocate a work array to hold the exposure time for each detector. */
   detwork = astMalloc( ndet * sizeof( float ) );

/* If we are dealing with more than 1 detector, create a LutMap that holds
   the input GRID index of every detector to be included in the output, and
   AST__BAD for every detector that is not to be included in the output cube.
   First allocate the work space for the LUT. */
   if( ndet > 1 ) {
      detlut = astMalloc( ndet*sizeof( double ) );

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

/* Loop round all detectors for which data is available. */
      for( idet = 0; idet < ndet; idet++ ) {

/* Store the input GRID coord of this detector. GRID coords start at 1,
   not 0. */
         detlut[ idet ] = 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.
   This will cause astRebinSeq to ignore data from the detector. */
         if( detgrp ) {
            found = grpIndex( name, detgrp, 1, status );
            if( !found ) detlut[ idet ] = AST__BAD;
         }

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

/* Create the LutMap. */
      lutmap = (AstMapping *) astLutMap( ndet, detlut, 1.0, 1.0,
                                         "LutInterp=1" );

/* If we only have 1 detector, use a UnitMap instead of a LutMap (lutMaps
   must have 2 or more table entries). */
   } else {
      lutmap = (AstMapping *) astUnitMap( 1, " " );
   }

/* Combine the above LutMap with a 1-input, 2-output PermMap that copies its
   input to create its first output, and assigns a constant value of 1.0 to
   its second output. We need to do this because smf_tslice returns a 2D
   GRID system (even though the second GRID axis is not actually used). */
   inperm[ 0 ] = 1;
   outperm[ 0 ] = 1;
   outperm[ 1 ] = -1;
   con = 1.0;
   detmap = astCmpMap( lutmap, astPermMap( 1, inperm, 2, outperm, &con, " " ),
                       1, " " );

/* Store the bounds of a single time slice grid. */
   lbnd_in[ 0 ] = 1;
   ubnd_in[ 0 ] = nchan;
   lbnd_in[ 1 ] = 1;
   ubnd_in[ 1 ] = ndet;

/* Create a PermMap that can be used to re-order the output axes so that
   channel number is axis 3. */
   outperm[ 0 ] = 2;
   outperm[ 1 ] = 3;
   outperm[ 2 ] = 1;
   inperm[ 0 ] = 3;
   inperm[ 1 ] = 1;
   inperm[ 2 ] = 2;
   pmap = astPermMap( 3, inperm, 3, outperm, NULL, " " );

/* If we are using multiple threads to rebin spectral blocks in parallel,
   calculate the number of channels that are processed by each thread,
   and the number of threads to use. The whole output spectrum is divided
   up into blocks. The number of blocks is two times the number of
   threads, and each thread rebins two adjacent blocks. Alternate blocks
   are re-binned simultanously. First, the odd numbered blocks are re-binned
   (one by each thread). When all odd numbered blocks have been re-binned,
   the even numbered blocks are re-binned. We ensure that the number of
   threads used results in a block size that is larger than the spreading
   width produced by the requested spreading scheme. This means that no
   pair of simultanously executing threads will ever try to write to the
   same channel of the output spectrum. */
   maxthreads = wf ? wf->nworker : 1;
   if( maxthreads > MAXTHREADS ) maxthreads = MAXTHREADS;
   if( maxthreads > 1 ) {

/* Find the largest number of threads into which each output spectrum can
   be split. The limit is imposes by the requirement that each block is
   larger than the pixel spreading produced by the requested spreading
   scheme. */
      nthreads = ( ( dim[ 2 ] + 1 )/2 )/smf_spreadwidth( spread, params,
                                                         status );

/* If the spectral range is less than twice the spreading width, we
   cannot use multiple threads. */
      if( nthreads > 1 ) {

/* Restrict the number of threads to be no more than the number of workers
   available in the work force. */
         if( nthreads > maxthreads ) nthreads = maxthreads;

/* Find the number of output channels in each spectral block. */
         blk_size = ( dim[ 2 ] - 1 )/( 2*nthreads ) + 1;

/* Set up the first output channel number within each block. */
         nblock = 2*nthreads;
         for( iblock = 0; iblock < nblock; iblock++ ) {
            blk_bot[ iblock ] = (double) ( iblock*blk_size + 1 );
         }

/* Add in the first channel number beyond the last block. */
         blk_bot[ nblock ] = blk_bot[ nblock - 1 ] + blk_size;

/* If the output spectrum is too short to guarantee that there are any
   independent blocks of output channels, we process the whole spectrum
   in a single thread. */
      } else {
         nthreads = 1;
         nblock = 1;
         blk_bot[ 0 ] = 1.0;
         blk_bot[ 1 ] = (double) ( dim[ 2 ] + 1 );
      }

/* If multiple threads are not available, we process the whole spectrum
   in a single thread. */
   } else {
      nthreads = 1;
      nblock = 1;
      blk_bot[ 0 ] = 1.0;
      blk_bot[ 1 ] = (double) ( dim[ 2 ] + 1 );
   }

/* Convert the block boundaries from output channel numbers into input
   channel numbers. */
   astTran1( ssmap, nblock + 1, blk_bot, 0, blk_bot );

/* Ensure they are in increasing order, and are not outside the bounds of
   the input array. */
   if( blk_bot[ 0 ] > blk_bot[ 1 ] ) {
      for( iblock = 0; iblock < ( nblock + 1 )/2; iblock++ ) {
         dtemp = blk_bot[ nblock - iblock ];
         blk_bot[ nblock - iblock ] = blk_bot[ iblock ];
         blk_bot[ iblock ] = dtemp;
      }
   }

   for( iblock = 0; iblock <= nblock; iblock++ ) {
      if( blk_bot[ iblock ] < 1 ) {
         blk_bot[ iblock ] = 1.0;
      } else if( blk_bot[ iblock ] > nchan ) {
         blk_bot[ iblock ] = nchan;
      }
   }

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

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

/* Loop round all time slices in the input NDF. */
   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 ) 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 );

/* So "totmap" is a 2-input, 2-output Mapping that transforms the input
   spatial GRID coords into output spatial GRID coords. In order to speed
   up astRebinSeq we represent this by a pair of parallel LutMaps. To do
   this (using atlTolut) we need a Mapping which only has 1 input, so we
   preceed "totmap" with "detmap" (which also has the effect of exluding
   data from unrequired detectors). We then combine this Mapping in
   parallel with the spectral LutMap to get a 2-input (channel number,
   detector index) and 3-output (output grid coords) Mapping. We finally
   add a PermMap to re-arrange the output axes so that channel number is
   axis 3 in the output. */
      dtotmap = (AstMapping *) astCmpMap( detmap, totmap, 1, " " );
      if( ndet > 1 ) {
         atlTolut( dtotmap, 1.0, (double) ndet, 1.0, "LutInterp=1", &splut,
                   status );
      } else {
         splut = astClone( dtotmap );
      }

      fullmap = astSimplify( astCmpMap( astCmpMap( sslut, splut, 0, " " ),
                                        pmap, 1, " " ) );

/* If required calculate the variance associated with each value in the
   current time slice. based on the input Tsys values. If they are
   needed, but not available, ignored the time slice. */
      ignore = 0;
      if( varwork ) {
         ignore = 1;
         vp = varwork;
         for( idet = 0; idet < ndet; idet++ ) {
            invar = VAL__BADR;
            rtsys = tsys ? (float) tsys[ idet ] : VAL__BADR;
            if( rtsys <= 0.0 ) rtsys = VAL__BADR;
            if( rtsys != VAL__BADR ) {
               *good_tsys = 1;
               if( tcon != VAL__BADD ) {
                  invar = tcon*rtsys*rtsys;
                  ignore = 0;
               }
            }
            for( ichan = 0; ichan < nchan; ichan++ ) *(vp++) = invar;
         }
      }

/* Unless we are ignoring this time slice, paste it into the 3D output
   cube. The smf_rebincube_seqf function is a wrapper for astRebinSeqF
   that splits the total job up between "nthreads" threads running in
   parallel. */
      if( !ignore ) {
         smf_rebincube_seqf( wf, nthreads, blk_bot, fullmap, 0.0, 2, lbnd_in,
                             ubnd_in, tdata, varwork, spread, params,
                             ast_flags, 0.0, 50, VAL__BADR, 3, ldim, udim,
                             lbnd_in, ubnd_in, data_array, var_array,
                             wgt_array, nused, status );

/* Now we update the total exposure time array. Scale the exposure time
   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. then fill the 1D work array with this constant value and
   paste it into the 2D texp_array using the spatial mapping. Note we
   want the simple sum of the exposure times, with no normalisation. SO
   we use the AST__NONORM flag which means we do not need to supply a
   weights array.  */
         if( texp != VAL__BADR ) {
            texp *= tfac;
            for( iv = 0; iv < ndet; iv++ ) detwork[ iv ] = texp;
            astRebinSeqF( splut, 0.0, 1, ldim, uddim, detwork, NULL,
                          spread, params, AST__NONORM, 0.0, 50,
                          VAL__BADR, 2, ldim, udim, ldim, uddim, texp_array,
                          NULL, NULL, NULL );
         }

/* Now do the same with the effective exposure time. */
         if( teff != VAL__BADR ) {
            teff *= tfac;
            for( iv = 0; iv < ndet; iv++ ) detwork[ iv ] = teff;
            astRebinSeqF( splut, 0.0, 1, ldim, uddim, detwork, NULL,
                          spread, params, AST__NONORM, 0.0, 50, VAL__BADR, 2,
                          ldim, udim, ldim, uddim, teff_array, NULL, NULL,
                          NULL );
         }
      }

/* 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. */
   if( last ) {

/* Create a dummy mapping that can be used with astRebinSeq (it is not
   actually used for anything since we are not adding any more data into the
   output arrays). */
      fullmap = (AstMapping *) astPermMap( 2, NULL, 3, NULL, NULL, " " );

/* Normalise the data values. We do not normalise the exposure time arrays. */
      astRebinSeqF( fullmap, 0.0, 2, lbnd_in,
                    ubnd_in, NULL, NULL, spread, params,
                    AST__REBINEND | ast_flags, 0.0, 50, VAL__BADR, 3,
                    ldim, udim, lbnd_in, ubnd_in, data_array, var_array,
                    wgt_array, nused );
      fullmap = astAnnul(fullmap);
   }

/* Free resources. */
   detlut = astFree( detlut );
   detwork = astFree( detwork );
   varwork = astFree( varwork );
}
Beispiel #4
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 );

}