예제 #1
0
static void smf1_correct_extinction( void *job_data_ptr, int *status ) {
/*
*  Name:
*     smf1_correct_extinction

*  Purpose:
*     Executed in a worker thread to do various calculations for
*     smf_correct_extinction.

*  Invocation:
*     smf1_correct_extinction( void *job_data_ptr, int *status )

*  Arguments:
*     job_data_ptr = SmfCorrectExtinctionData * (Given)
*        Data structure describing the job to be performed by the worker
*        thread.
*     status = int * (Given and Returned)
*        Inherited status.

*/

  /* Local Variables: */
  SmfCorrectExtinctionData *pdata;
  double airmass;          /* Airmass */
  double state_airmass;    /* Airmass read from header */
  double state_az_ac2;     /* Elevation read from header */
  double amprev;           /* Previous airmass in loop */
  double *azel = NULL;     /* AZEL coordinates */
  size_t base;             /* Offset into 3d data array */
  double extcorr = 1.0;    /* Extinction correction factor */
  dim_t i;                 /* Loop counter */
  dim_t k;                 /* Loop counter */
  AstFrameSet *wcs = NULL; /* Pointer to AST WCS frameset */
  AstFrameSet *state_wcs = NULL; /* Pointer to copy of frameset from header */

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

  /* Get a pointer that can be used for accessing the required items in the
  supplied structure. */
  pdata = (SmfCorrectExtinctionData *) job_data_ptr;

  /* It is more efficient to call astTranGrid than astTran2
     Allocate memory in adaptive mode just in case. */
  if (pdata->method == SMF__EXTMETH_FULL ||
      pdata->method == SMF__EXTMETH_ADAPT ) {
    azel = astMalloc( (2*pdata->npts)*sizeof(*azel) );
  }

  amprev = pdata->amfirst;

  /* Assume we are using quick mode for all time slices. */
  pdata->allquick = 1;

  for ( k=pdata->f1; k<=pdata->f2 && (*status == SAI__OK) ; k++) {
    /* Flags to indicate which mode we are using for this time slice */
    int quick = 0;  /* use single airmass */
    int adaptive = 0; /* switch from quick to full if required */
    if (pdata->method == SMF__EXTMETH_SINGLE) {
      quick = 1;
    } else if (pdata->method == SMF__EXTMETH_ADAPT) {
      quick = 1;
      adaptive = 1;
    } else {
      pdata->allquick = 0;
    }

    /* Call tslice_ast to update the header for the particular
       timeslice. If we're in QUICK mode then we don't need the WCS. Use
       a mutex to prevent multiple threads writing to the header at the same
       time.  */
    thrMutexLock( &data_mutex, status );
    smf_lock_data( pdata->data, 1, status );
    smf_tslice_ast( pdata->data, k, !quick, NO_FTS, status );

    /* Copy the required bit of the header into thread-local storage. */
    if( *status == SAI__OK ) {
       state_airmass = pdata->hdr->state->tcs_airmass;
       state_az_ac2 = pdata->hdr->state->tcs_az_ac2;
       if( !quick && pdata->tau != VAL__BADD && pdata->hdr->wcs) state_wcs = astCopy( pdata->hdr->wcs );
    }

    /* Unlock the AST Objects in the smfData then unlock the local mutex. */
    smf_lock_data( pdata->data, 0, status );
    thrMutexUnlock( &data_mutex, status );

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

    /* Read the WVM tau value if required */
    if (pdata->tausrc == SMF__TAUSRC_WVMRAW) {
      pdata->tau = pdata->wvmtau[k];
    }

    /* in all modes we need to keep track of the previous airmass in case
       we need to gap fill bad telescope data */
    if ( quick && pdata->ndims == 2 ) {
      /* for 2-D we use the FITS header directly */
      /* This may change depending on exact FITS keyword */
      airmass = pdata->amstart;

      /* speed is not an issue for a 2d image */
      adaptive = 0;

    } else {
      /* Else use airmass value in state structure */
      airmass = state_airmass;

      /* if things have gone bad use the previous value else store
         this value. We also need to switch to quick mode and disable adaptive. */
      if (airmass == VAL__BADD || airmass == 0.0 ) {
        if ( state_az_ac2 != VAL__BADD ) {
          /* try the elevation */
          airmass = palAirmas( M_PI_2 - state_az_ac2 );
        } else {
          airmass = amprev;
          quick = 1;
          adaptive = 0;
        }
      } else {
        amprev = airmass;
      }
    }

    /* If we're using the FAST application method, we assume a single
       airmass and tau for the whole array but we have to consider adaptive mode.
       If the tau is bad the extinction correction must also be bad. */
    if( pdata->tau == VAL__BADD) {
      extcorr = VAL__BADD;
    } else if (quick) {
      /* we have an airmass, see if we need to provide per-pixel correction */
      if (adaptive) {
        if (is_large_delta_atau( airmass, pdata->hdr->state->tcs_az_ac2,
                                 pdata->tau, status) ) {
          /* we need WCS if we disable fast mode */
          quick = 0;
          pdata->allquick = 0;

          thrMutexLock( &data_mutex, status );
          smf_lock_data( pdata->data, 1, status );
          smf_tslice_ast( pdata->data, k, 1, NO_FTS, status );
          state_airmass = pdata->hdr->state->tcs_airmass;
          state_az_ac2 = pdata->hdr->state->tcs_az_ac2;
          if (pdata->hdr->wcs) state_wcs = astCopy( pdata->hdr->wcs );
          smf_lock_data( pdata->data, 0, status );
          thrMutexUnlock( &data_mutex, status );

        }
      }

      if (quick) extcorr = exp(airmass*pdata->tau);
    }

    /* The previous test may have forced quick off so we can not combine
       the tests in one if-then-else block */
    if (!quick && pdata->tau != VAL__BADD )  {
      /* Not using quick so retrieve WCS to obtain elevation info */
      wcs = state_wcs;
      /* Check current frame, store it and then select the AZEL
         coordinate system */
      if (wcs != NULL) {
        if (strcmp(astGetC(wcs,"SYSTEM"), "AZEL") != 0) {
          astSet( wcs, "SYSTEM=AZEL"  );
        }
        /* Transfrom from pixels to AZEL */
        astTranGrid( wcs, 2, pdata->lbnd, pdata->ubnd, 0.1, 1000000, 1, 2,
                     pdata->npts, azel );
      } else {
        /* this time slice may have bad telescope data so we trap for this and re-enable
           "quick" with a default value. We'll only get here if airmass was good but
           SMU was bad so we use the good airmass. The map-maker won't be using this
           data but we need to use something plausible so that we do not throw off the FFTs */
        quick = 1;
        extcorr = exp(airmass*pdata->tau);
      }
    }
    /* Loop over data in time slice. Start counting at 1 since this is
       the GRID coordinate frame */
    base = pdata->npts * k;  /* Offset into 3d data array (time-ordered) */

    for (i=0; i < pdata->npts && ( *status == SAI__OK ); i++ ) {
      /* calculate array indices - assumes that astTranGrid fills up
         azel[] array in same order as bolometer data are aligned */
      size_t index;
      if ( pdata->isTordered ) {
        index = base + i;
      } else {
        index = k + (pdata->nframes * i);
      }

      if (!quick) {
        if (pdata->tau != VAL__BADD) {
          double zd;
          zd = M_PI_2 - azel[pdata->npts+i];
          airmass = palAirmas( zd );
          extcorr = exp(airmass*pdata->tau);
        } else {
          extcorr = VAL__BADD;
        }
      }

      if( pdata->allextcorr ) {
        /* Store extinction correction factor */
        pdata->allextcorr[index] = extcorr;
      } else {
        /* Otherwise Correct the data */
        if (extcorr != VAL__BADD) {
          if( pdata->indata && (pdata->indata[index] != VAL__BADD) ) {
            pdata->indata[index] *= extcorr;
          }

          /* Correct the variance */
          if( pdata->vardata && (pdata->vardata[index] != VAL__BADD) ) {
            pdata->vardata[index] *= extcorr * extcorr;
          }
        } else {
          if (pdata->indata) pdata->indata[index] = VAL__BADD;
          if (pdata->vardata) pdata->vardata[index] = VAL__BADD;
        }
      }

    }

    /* Note that we do not need to free "wcs" or revert its SYSTEM
       since smf_tslice_ast will replace the object immediately. */
  } /* End loop over timeslice */

  azel = astFree( azel );

}
예제 #2
0
void smf_filter_execute( ThrWorkForce *wf, smfData *data, smfFilter *filt,
                         int complement, int whiten, int *status ) {

  /* Local Variables */
  size_t apod_length=0;           /* apodization length */
  fftw_iodim dims;                /* I/O dimensions for transformations */
  size_t first;                   /* First sample apodization at start */
  int i;                          /* Loop counter */
  smfFilterExecuteData *job_data=NULL;/* Array of job data for each thread */
  size_t last;                    /* Last sample apodization at end */
  dim_t nbolo=0;                  /* Number of bolometers */
  dim_t ndata=0;                  /* Total number of data points */
  int nw;                         /* Number of worker threads */
  dim_t ntslice=0;                /* Number of time slices */
  smf_qual_t *qua=NULL;           /* Pointer to quality flags */
  smfFilterExecuteData *pdata=NULL; /* Pointer to current job data */
  size_t step;                    /* step size for dividing up work */

  /* Main routine */
  if (*status != SAI__OK) return;

  /* Check for NULL pointers */
  if( !data ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": NULL smfData pointer", status );
    return;
  }

  if( !filt ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": NULL smfFilter pointer", status );
    return;
  }

  if( filt->ndims == 2 ) {
    smf_filter2d_execute( wf, data, filt, complement, status );
    return;
  }

  /* How many threads do we get to play with */
  nw = wf ? wf->nworker : 1;

  /* Ensure that the smfData is ordered correctly (bolo ordered) */
  smf_dataOrder( wf, data, 0, status );

  /* Obtain the dimensions of the array being filtered */
  smf_get_dims( data, NULL, NULL, &nbolo, &ntslice, &ndata, NULL, NULL, status);

  if( *status != SAI__OK ) return;

  /* Using complement of the filter? */
  if( complement ) smf_filter_complement( filt, status );

  /* Pointers to quality */
  qua = smf_select_qualpntr( data, NULL, status );

  /* Determine the first and last samples to apodize (after padding), if
     any. Assumed to be the same for all bolometers. */
  if( qua ) {
     smf_get_goodrange( qua, ntslice, 1, SMF__Q_PAD, &first, &last, status );
  } else {
     first = 0;
     last = ntslice - 1;
  }

  /* Can we apodize? */
  apod_length = filt->apod_length;
  if( *status == SAI__OK ) {
    if( apod_length == SMF__MAXAPLEN ) {
      apod_length = (last-first+1)/2;
      msgOutiff( MSG__DEBUG, "", FUNC_NAME
                 ": Using maximum apodization length, %zu samples.",
                 status, apod_length );
    } else if( (last-first+1) < (2*apod_length) && apod_length != SMF__BADSZT ){
      *status = SAI__ERROR;
      errRepf("", FUNC_NAME
              ": Can't apodize, not enough samples (%zu < %zu).", status,
              last-first+1, 2*apod_length);
    }
  }

  /* If apodising is switched off, fill gaps in the data and re-create
     the artifical data used for padding based on the current contents of
     the smfData. */
  if( apod_length == SMF__BADSZT ) {
    smf_fillgaps( wf, data, SMF__Q_PAD | SMF__Q_GAP, status );

  /* If apodising is switched on, fill the data (retaining the zero padding)
     and apodise the data. */
  } else {
    smf_fillgaps( wf, data, SMF__Q_GAP, status );
    if( apod_length > 0 ) smf_apodize( data, apod_length, 1, status );
  }

  /* Describe the input and output array dimensions for FFTW guru interface.
     - dims describes the length and stepsize of time slices within a bolometer
  */

  dims.n = ntslice;
  dims.is = 1;
  dims.os = 1;

  /* Set up the job data */

  if( nw > (int) nbolo ) {
    step = 1;
  } else {
    step = nbolo/nw;
  }

  job_data = astCalloc( nw, sizeof(*job_data) );
  for( i=0; (*status==SAI__OK)&&i<nw; i++ ) {
    pdata = job_data + i;

    pdata->b1 = i*step;
    pdata->b2 = (i+1)*step-1;

    /* Ensure that the last thread picks up any left-over bolometers */
    if( (i==(nw-1)) && (pdata->b1<(nbolo-1)) ) {
      pdata->b2=nbolo-1;
    }
    pdata->data = data;
    pdata->qua = qua;

    pdata->data_fft_r = astMalloc(filt->fdims[0]*sizeof(*pdata->data_fft_r));
    pdata->data_fft_i = astMalloc(filt->fdims[0]*sizeof(*pdata->data_fft_i));
    pdata->filt = filt;
    pdata->whiten = whiten;
    pdata->complement = complement;
    pdata->ijob = -1;   /* Flag job as ready to start */

    /* Setup forward FFT plan using guru interface. Requires protection
       with a mutex */
    thrMutexLock( &smf_filter_execute_mutex, status );

    if( *status == SAI__OK ) {
      /* Just use the data_fft_* arrays from the first chunk of job data since
         the guru interface allows you to use the same plans for multiple
         transforms. */
      pdata->plan_forward = fftw_plan_guru_split_dft_r2c( 1, &dims, 0, NULL,
                                                          data->pntr[0],
                                                          pdata->data_fft_r,
                                                          pdata->data_fft_i,
                                                          FFTW_ESTIMATE |
                                                          FFTW_UNALIGNED );
    }

    thrMutexUnlock( &smf_filter_execute_mutex, status );

    if( !pdata->plan_forward && (*status == SAI__OK) ) {
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME
              ": FFTW3 could not create plan for forward transformation",
              status);
    }

    /* Setup inverse FFT plan using guru interface */
    thrMutexLock( &smf_filter_execute_mutex, status );

    if( *status == SAI__OK ) {
      pdata->plan_inverse = fftw_plan_guru_split_dft_c2r( 1, &dims, 0, NULL,
                                                          pdata->data_fft_r,
                                                          pdata->data_fft_i,
                                                          data->pntr[0],
                                                          FFTW_ESTIMATE |
                                                          FFTW_UNALIGNED);
    }

    thrMutexUnlock( &smf_filter_execute_mutex, status );

    if( !pdata->plan_inverse && (*status==SAI__OK) ) {
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME
              ": FFTW3 could not create plan for inverse transformation",
              status);
    }

  }

  /* Execute the filter */
  for( i=0; (*status==SAI__OK)&&i<nw; i++ ) {
    pdata = job_data + i;
    pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata,
                               smfFilterExecuteParallel, 0,
                               NULL, status );
  }

  /* Wait until all of the submitted jobs have completed */
  thrWait( wf, status );

  /* Clean up the job data array */
  if( job_data ) {
    for( i=0; i<nw; i++ ) {
      pdata = job_data + i;
      if( pdata->data_fft_r ) pdata->data_fft_r = astFree( pdata->data_fft_r );
      if( pdata->data_fft_i ) pdata->data_fft_i = astFree( pdata->data_fft_i );

      /* Destroy the plans */
      thrMutexLock( &smf_filter_execute_mutex, status );
      fftw_destroy_plan( pdata->plan_forward );
      fftw_destroy_plan( pdata->plan_inverse );
      thrMutexUnlock( &smf_filter_execute_mutex, status );
    }
    job_data = astFree( job_data );
  }

  /* Return the filter to its original state if required */
  if( complement == -1 ) smf_filter_complement( filt, status );


  /* Remove the effects of the apodisation from the filtered data. */
  if( apod_length != SMF__BADSZT && apod_length > 0 ) {
     smf_apodize( data, apod_length, 0, status );
  }

}