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 ); }
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 ); } }