Beispiel #1
0
void smf_rebinmap1( ThrWorkForce *wf, smfData *data, smfData *variance, int *lut,
                    size_t tslice1, size_t tslice2, int trange,
                    int *whichmap, dim_t nmap, smf_qual_t mask, int sampvar,
                    int flags, double *map, double *mapweight,
                    double *mapweightsq, int *hitsmap,
                    double *mapvar, dim_t msize, double *scalevariance,
                    int *status ) {

  /* Local Variables */
  SmfRebinMap1Data *job_data = NULL;
  SmfRebinMap1Data *pdata;
  double *dat=NULL;          /* Pointer to data array */
  size_t dbstride;           /* bolo stride of data */
  size_t dtstride;           /* tstride of data */
  int iw;                    /* Thread index */
  dim_t mbufsize;            /* Size of full (multi-map) map buffers */
  dim_t nbolo;               /* number of bolos */
  dim_t ntslice;             /* number of time slices */
  int nw;                    /* Number of worker threads */
  size_t pixstep;            /* Number of map pixels per thread */
  dim_t bolostep;            /* Number of bolos per thread */
  smf_qual_t * qual = NULL;  /* Quality pointer */
  double scalevar;           /* variance scale factor */
  double scaleweight;        /* weights for calculating scalevar */
  size_t t1, t2;             /* range of time slices to re-grid */
  double *var=NULL;          /* Pointer to variance array */
  size_t vbstride;           /* bolo stride of variance */
  dim_t vnbolo;              /* number of bolos in variance */
  dim_t vntslice;            /* number of bolos in variance */
  size_t vtstride;           /* tstride of variance */

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

  /* Check inputs */
  if( !data || !map || !lut || !mapweight || !mapweightsq || !mapvar ||
      !hitsmap ) {
    *status = SAI__ERROR;
    errRep(" ", FUNC_NAME ": Null inputs", status );
    return;
  }

  if( !data->pntr[0] ) {
    *status = SAI__ERROR;
    errRep(" ", FUNC_NAME ": supplied data is empty", status );
    return;
  }

  dat = data->pntr[0];
  qual = smf_select_qualpntr( data, NULL, status );
  smf_get_dims( data, NULL, NULL, &nbolo, &ntslice, NULL, &dbstride,
                &dtstride, status );

  /* Size of full map buffers */
  if( whichmap ) {
    mbufsize = nmap * msize;
  } else {
    mbufsize = msize;
  }

  if( variance ) {
    var = variance->pntr[0];
    smf_get_dims( variance, NULL, NULL, &vnbolo, &vntslice, NULL, &vbstride,
                  &vtstride, status );

    /* Check that the variance dimensions are compatible with data */
    if( (*status==SAI__OK) &&
        ((vnbolo != nbolo) || ((vntslice>1)&&(vntslice!=ntslice))) ) {
      *status = SAI__ERROR;
      errRep(" ", FUNC_NAME ": variance dimensions incompatible with data",
             status );
      return;
    }
  }

  /* Range of time slices to regrid */
  if( trange ) {

    if( tslice2 >= ntslice ) {
      *status = SAI__ERROR;
      errRepf( "", FUNC_NAME ": tslice2 (%zu) can't be >= ntslice (%zu)",
               status, tslice2, ntslice );
      return;
    }

    if( tslice1 > tslice2  ) {
      *status = SAI__ERROR;
      errRepf( "", FUNC_NAME ": tslice1 (%zu) > tslice2 (%zu)",
               status, tslice1, tslice2 );
      return;
    }

    t1 = tslice1;
    t2 = tslice2;
  } else {
    t1 = 0;
    t2 = ntslice-1;
  }

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

  /* If this is the first data to be accumulated zero the arrays */
  if( flags & AST__REBININIT ) {
    memset( map, 0, nw*mbufsize*sizeof(*map) );
    memset( mapweight, 0, nw*mbufsize*sizeof(*mapweight) );
    memset( mapweightsq, 0, nw*mbufsize*sizeof(*mapweightsq) );
    memset( mapvar, 0, nw*mbufsize*sizeof(*mapvar) );
    memset( hitsmap, 0, nw*mbufsize*sizeof(*hitsmap) );
  }

  /* Find how many bolos to process in each worker thread. */
  bolostep = nbolo/nw;
  if ( bolostep == 0 ) bolostep = 1;

  /* Allocate job data for threads, and store the range of bolos to be
     processed by each one. Ensure that the last thread picks up any
     left-over bolos. */
  job_data = astCalloc( nw, sizeof(*job_data) );
  if( *status == SAI__OK ) {
    for( iw = 0; iw < nw; iw++ ) {
      pdata = job_data + iw;
      pdata->p1 = iw*bolostep;
      if( iw < nw - 1 ) {
        pdata->p2 = pdata->p1 + bolostep - 1;
      } else {
        pdata->p2 = nbolo - 1 ;
      }

/* Store other values common to all jobs. */
      pdata->msize = msize;
      pdata->nbolo = nbolo;
      pdata->t1 = t1;
      pdata->t2 = t2;
      pdata->vntslice = vntslice;
      pdata->dat = dat;
      pdata->map = map;
      pdata->mapvar = mapvar;
      pdata->mapweightsq = mapweightsq;
      pdata->mapweight = mapweight;
      pdata->var = var;
      pdata->hitsmap = hitsmap;
      pdata->lut = lut;
      pdata->whichmap = whichmap;
      pdata->dbstride = dbstride;
      pdata->dtstride = dtstride;
      pdata->vbstride = vbstride;
      pdata->vtstride = vtstride;
      pdata->mask = mask;
      pdata->qual = qual;
      pdata->mbufsize = mbufsize;
      pdata->nw = nw; /* used for final summing/rescaling */
      pdata->iw = iw; /* so the thread knows with chunk it's working on */
    }
  }

  if( var ) {
    /* Accumulate data and weights in the case that variances are given*/

    if( sampvar ) {

      /* Measure weighted sample variance for varmap */
      if( qual ) {       /* QUALITY checking version */

        /* Set up jobs to add the previous estimate of COM back on to the
           residuals, and then wait for the jobs to complete. These jobs also
           clear any SMF__Q_COM flags set by previous iterations. */
        for( iw = 0; iw < nw; iw++ ) {
           pdata = job_data + iw;
           pdata->operation = 1;
           thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
        }
        thrWait( wf, status );

      } else {           /* VAL__BADD checking version */
        for( iw = 0; iw < nw; iw++ ) {
           pdata = job_data + iw;
           pdata->operation = 2;
           thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
        }
        thrWait( wf, status );

      }

    } else {
      /* Otherwise use simple error propagation for varmap */

      if( qual ) {       /* QUALITY checking version */
        for( iw = 0; iw < nw; iw++ ) {
           pdata = job_data + iw;
           pdata->operation = 3;
           thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
        }
        thrWait( wf, status );

      } else {           /* VAL__BADD checking version */
        for( iw = 0; iw < nw; iw++ ) {
           pdata = job_data + iw;
           pdata->operation = 4;
           thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
        }
        thrWait( wf, status );

      }
    }

  } else {
    /* Accumulate data and weights when no variances are given. In this case
       the variance map is always estimated from the sample variance */

    if( qual ) {       /* QUALITY checking version */
      for( iw = 0; iw < nw; iw++ ) {
        pdata = job_data + iw;
        pdata->operation = 5;
        thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
      }
      thrWait( wf, status );

    } else {           /* VAL__BADD checking version */
      for( iw = 0; iw < nw; iw++ ) {
        pdata = job_data + iw;
        pdata->operation = 6;
        thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
      }
      thrWait( wf, status );
    }
  }

  /* If this is the last data to be accumulated re-normalize */
  if( flags & AST__REBINEND ) {

  /* Find how many buffer pixels to process in each worker thread. */
    pixstep = mbufsize/nw;
    if( pixstep == 0 ) pixstep = 1;

    for( iw = 0; iw < nw; iw++ ) {
      pdata = job_data + iw;
      pdata->p1 = iw*pixstep;
      if( iw < nw - 1 ) {
        pdata->p2 = pdata->p1 + pixstep - 1;
      } else {
        pdata->p2 = mbufsize - 1 ;
      }
    }

    if( sampvar || !var ) {

      for( iw = 0; iw < nw; iw++ ) {
        pdata = job_data + iw;
        pdata->operation = 7;
        thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
      }
      thrWait( wf, status );

      scaleweight=0;
      scalevar=0;
      for( iw = 0; iw < nw; iw++ ) {
        pdata = job_data + iw;
        scaleweight += pdata->scaleweight;
        scalevar += pdata->scalevar;
      }

      /* Re-normalize scalevar */
      if( scaleweight ) {
        scalevar /= scaleweight;

        if( scalevariance ) {
          *scalevariance = scalevar;
        }
      }

    } else {
      /* Re-normalization for error propagation case */

      for( iw = 0; iw < nw; iw++ ) {
        pdata = job_data + iw;
        pdata->operation = 8;
        thrAddJob( wf, 0, pdata, smf1_rebinmap1, 0, NULL, status );
      }
      thrWait( wf, status );

    }
  }

  job_data = astFree( job_data );

}
Beispiel #2
0
void smf_subip(  ThrWorkForce *wf, smfArray *res, smfArray *lut, int *lbnd_out,
                 int *ubnd_out, AstKeyMap *keymap, AstFrameSet *outfs, int *status ) {

/* Local Variables: */
   HDSLoc *loc = NULL;
   HDSLoc *sloc = NULL;
   SmfSubIPData *job_data = NULL;
   SmfSubIPData *pdata;
   char ipref[200];
   char subname[10];
   const char *ipdata;
   const char *qu;
   dim_t bolostep;
   dim_t nbolo;
   dim_t ntslice;
   double *angcdata;
   double *c0data;
   double *imapdata;
   double *ipang;
   double *p0data;
   double *p1data;
   int angcndf;
   int c0ndf;
   int imapndf;
   int iw;
   int nmap;
   int nw;
   int p0ndf;
   int p1ndf;
   size_t bstride;
   size_t idx;
   size_t tstride;
   smfData *data;
   smf_qual_t *qua_data;

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

/* Check if we have pol2 data, and see if it is Q or U. */
   qu = NULL;
   for( idx = 0; idx < res->ndat; idx++ ) {
      data = res->sdata[idx];

      if( !strcmp( data->hdr->dlabel, "Q" ) ){
         if( !qu ) {
            qu = "Q";
         } else if( strcmp( qu, "Q" ) ) {
            *status = SAI__ERROR;
            break;
         }

      } else if( !strcmp( data->hdr->dlabel, "U" ) ) {
         if( !qu ) {
            qu = "U";
         } else if( strcmp( qu, "U" ) ) {
            *status = SAI__ERROR;
            break;
         }

      } else if( qu ) {
         *status = SAI__ERROR;
         qu = NULL;
         break;
      }
   }

/* Report an error if there is a mix of pol2 and non-pol2, or a mix of Q
   and U. */
   if( *status != SAI__OK ) {
      if( qu ) {
         errRep( "", "smf_subip: Input data contains mix of Q and U "
                 "data", status );
      } else {
         errRep( "", "smf_subip: Input data contains mix of POL2 and "
                 "non-POL2 data", status );
      }

/* If we have pol2 data, get the path to the total intensity image that
   is to be used to define the level of IP correction required. If no
   value is supplied, annul the error and set "qu" NULL to indicate we should
   leave immediately. */
   } else if( qu && *status == SAI__OK ) {
      parGet0c( "IPREF", ipref, sizeof(ipref), status );
      if( *status == PAR__NULL ) {
         errAnnul( status );
         qu = NULL;
      }
   }

/* If we are applying IP correction... */
   if( qu && *status == SAI__OK ) {
      msgOutf( "", "smf_subip: applying instrumental polarisation %s "
               "correction based on total intensity map `%s'", status,
               qu, ipref );

/* Get an identifier for the IPREF NDF. */
      ndfFind( NULL, ipref, &imapndf, status );

/* Resample the NDFs data values onto the output map grid. */
      imapdata = smf_alignndf( imapndf, outfs, lbnd_out, ubnd_out,
                               status );

/* Annul the NDF identifier. */
      ndfAnnul( &imapndf, status );

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

/* Get the path to the container file holding the IP model parameters. */
      ipdata = "$STARLINK_DIR/share/smurf/ipdata.sdf";
      astMapGet0C( keymap, "IPDATA", &ipdata );

/* Open the container file. */
      hdsOpen( ipdata, "READ", &loc, status );

/* Do the IP correction for each subarray (s8a, s8b, etc) in turn. */
      for( idx = 0; idx < res->ndat && *status == SAI__OK; idx++ ) {
         data = res->sdata[idx];

/* Get an array holding the angle (rad.s) from north to focal plane Y,
   measured positive in the sense of rotation from focal plane Y to focal
   plane X, for every bolometer sample in the smfData. The values are bolo
   ordered so that "bstride" is 1 and "tstsride" is nbolo. */
         ipang = smf1_calcang( data, status );

/* Get the number of bolometers and time slices for the current subarray,
   together with the strides between adjacent bolometers and adjacent
   time slices. */
         smf_get_dims( data,  NULL, NULL, &nbolo, &ntslice, NULL, &bstride,
                       &tstride, status );

/* Get a locator for the structure holding the IP parameters for the
   current subarray */
         smf_find_subarray( data->hdr, subname, sizeof( subname ), NULL,
                            status );
         datFind( loc, subname, &sloc, status );

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

/* Get identifiers for the NDFs holding the individual parameters. Each
   NDF holds a parameter value for each bolometer. */
         ndfFind( sloc, "P0", &p0ndf, status );
         ndfFind( sloc, "P1", &p1ndf, status );
         ndfFind( sloc, "C0", &c0ndf, status );
         ndfFind( sloc, "ANGC", &angcndf, status );

/* Map them. Check each one has the expected number of elements. */
         ndfMap( p0ndf, "DATA", "_DOUBLE", "READ", (void **) &p0data,
                 &nmap, status );
         if( nmap != (int) nbolo && *status == SAI__OK ) {
            *status = SAI__ERROR;
            ndfMsg( "N", p0ndf );
            errRep( "", "smf_subip: Bad dimensions for ^N - should be 32x40.", status );
         }

         ndfMap( p1ndf, "DATA", "_DOUBLE", "READ", (void **) &p1data,
                 &nmap, status );
         if( nmap != (int) nbolo && *status == SAI__OK ) {
            *status = SAI__ERROR;
            ndfMsg( "N", p1ndf );
            errRep( "", "smf_subip: Bad dimensions for ^N - should be 32x40.", status );
         }

         ndfMap( c0ndf, "DATA", "_DOUBLE", "READ", (void **) &c0data,
                 &nmap, status );
         if( nmap != (int) nbolo && *status == SAI__OK ) {
            *status = SAI__ERROR;
            ndfMsg( "N", c0ndf );
            errRep( "", "smf_subip: Bad dimensions for ^N - should be 32x40.", status );
         }

         ndfMap( angcndf, "DATA", "_DOUBLE", "READ", (void **) &angcdata,
                 &nmap, status );
         if( nmap != (int) nbolo && *status == SAI__OK ) {
            *status = SAI__ERROR;
            ndfMsg( "N", angcndf );
            errRep( "", "smf_subip: Bad dimensions for ^N - should be 32x40.", status );
         }

/* Get a pointer to the quality array for the residuals. */
         qua_data = smf_select_qualpntr( data, NULL, status );

/* See how many bolometers to process in each thread. */
         bolostep = nbolo/nw;
         if( bolostep == 0 ) bolostep = 1;

/* Create jobs to apply the IP correction to a range of bolometers. */
         for( iw = 0; iw < nw; iw++ ) {
            pdata = job_data + iw;

/* Set the range of bolometers (b1 to b2) to be processed by the current
   job. */
            pdata->b1 = iw*bolostep;
            if( iw < nw - 1 ) {
               pdata->b2 = pdata->b1 + bolostep - 1;
            } else {
               pdata->b2 = nbolo - 1 ;
            }

/* Store the other info needed by the worker thread. */
            pdata->ntslice = ntslice;
            pdata->nbolo = nbolo;
            pdata->res_data = res->sdata[idx]->pntr[0];
            pdata->lut_data = lut->sdata[idx]->pntr[0];
            pdata->qua_data = qua_data;
            pdata->ipang = ipang;
            pdata->bstride = bstride;
            pdata->tstride = tstride;
            pdata->imapdata = imapdata;
            pdata->qu = qu;
            pdata->p0data = p0data;
            pdata->p1data = p1data;
            pdata->c0data = c0data;
            pdata->angcdata = angcdata;
            pdata->allstate = data->hdr->allState;

/* Submit the job for execution by the next available thread. */
            thrAddJob( wf, 0, pdata, smf1_subip, 0, NULL, status );
         }

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

/* End the NDF context, thus unmapping and freeing all NDF identifiers
   created since the context was started. */
         ndfEnd( status );

/* Free locator for subarray IP parameters. */
         datAnnul( &sloc, status );
         ipang = astFree( ipang );
      }

/* Free resources. */
      datAnnul( &loc, status );
      imapdata = astFree( imapdata );
      job_data = astFree( job_data );
   }
}
Beispiel #3
0
void smf_bolonoise( ThrWorkForce *wf, smfData *data, double gfrac,
                    size_t window, double f_low,
                    double f_white1, double f_white2,
                    int nep, size_t len, double *whitenoise, double *fratio,
                    smfData **fftpow,int *status ) {

    double *base=NULL;       /* Pointer to base coordinates of array */
    size_t bstride;          /* bolometer index stride */
    double df=1;             /* Frequency step size in Hz */
    size_t i;                /* Loop counter */
    size_t i_low;            /* Index in power spectrum to f_low */
    size_t i_w1;             /* Index in power spectrum to f_white1 */
    size_t i_w2;             /* Index in power spectrum to f_white2 */
    size_t j;                /* Loop counter */
    size_t mingood;          /* Min. required no. of good values in bolometer */
    dim_t nbolo;             /* Number of bolometers */
    dim_t ndata;             /* Number of data points */
    dim_t nf=0;              /* Number of frequencies */
    size_t ngood;            /* Number of good samples */
    dim_t ntslice;           /* Number of time slices */
    double p_low;            /* Power at f_low */
    double p_white;          /* Average power from f_white1 to f_white2 */
    smfData *pow=NULL;       /* Pointer to power spectrum data */
    smf_qual_t *qua=NULL; /* Pointer to quality component */
    double steptime=1;       /* Length of a sample in seconds */
    size_t tstride;          /* time index stride */

    if (*status != SAI__OK) return;

    /* Check inputs */
    if (!smf_dtype_check_fatal( data, NULL, SMF__DOUBLE, status )) return;

    if( !data->hdr ) {
        *status = SAI__ERROR;
        errRep( "", FUNC_NAME ": smfData has no header", status );
        return;
    }

    /* Obtain dimensions */
    smf_get_dims( data,  NULL, NULL, &nbolo, &ntslice, &ndata, &bstride, &tstride,
                  status );

    if( *status==SAI__OK ) {
        steptime = data->hdr->steptime;
        if( steptime < VAL__SMLD ) {
            *status = SAI__ERROR;
            errRep("",  FUNC_NAME ": FITS header error, STEPTIME must be > 0",
                   status);
        } else {
            /* Frequency steps in the FFT */
            df = 1. / (steptime * (double) ntslice );
        }
    }

    /* Initialize arrays */
    if( whitenoise ) for(i=0; i<nbolo; i++) whitenoise[i] = VAL__BADD;
    if( fratio ) for(i=0; i<nbolo; i++) fratio[i] = VAL__BADD;

    /* FFT the data and convert to polar power spectral density form */
    pow = smf_fft_data( wf, data, NULL, 0, len, status );
    smf_convert_bad( wf, pow, status );
    smf_fft_cart2pol( wf, pow, 0, 1, status );

    {
        dim_t fdims[2];
        smf_isfft( pow, NULL, NULL, fdims, NULL, NULL, status );
        if( *status == SAI__OK ) nf=fdims[0];
    }

    /* Check for reasonble frequencies, and integer offsets in the array */
    i_low = smf_get_findex( f_low, df, nf, status );
    i_w1 = smf_get_findex( f_white1, df, nf, status );
    i_w2 = smf_get_findex( f_white2, df, nf, status );

    /* Get the quality pointer from the smfData so that we can mask known
       bad bolometer. */
    qua = smf_select_qualpntr( data, NULL, status );

    /* The minimum required number of good values in a bolometer. */
    mingood = ( gfrac > 0.0 ) ? ntslice*gfrac : 0;

    /* Loop over detectors */
    for( i=0; (*status==SAI__OK)&&(i<nbolo); i++ )
        if( !qua || !(qua[i*bstride]&SMF__Q_BADB) ) {

            /* Pointer to start of power spectrum */
            base = pow->pntr[0];
            base += nf*i;

            /* Smooth the power spectrum */
            smf_boxcar1D( base, nf, 1, window, NULL, 0, 1, NULL, status );

            /* Measure the power */
            if( *status == SAI__OK ) {
                p_low = base[i_low];
                smf_stats1D( base+i_w1, 1, i_w2-i_w1+1, NULL, 0, 0, &p_white, NULL, NULL,
                             &ngood, status );

                /* It's OK if bad status was generated as long as a mean was calculated */
                if( *status==SMF__INSMP ) {
                    errAnnul( status );
                    /* if we had no good data there was probably a problem with SMF__Q_BADB
                       so we simply go to the next bolometer */
                    if (ngood == 0) continue;
                }

                /* Count the number of initially good values for the current
                   bolometer. */
                if( (*status==SAI__OK) && qua ) {
                    ngood = 0;
                    for( j=0; j<ntslice; j++ ) {
                        if( qua[i*bstride + j*tstride] == 0 ) ngood++;
                    }

                    /* Set bolometer to bad if no power detected, or the number of good
                       values is too low.  */
                    if( (p_low <= 0) || (p_white <= 0) || (ngood < mingood) ) {
                        for( j=0; j<ntslice; j++ ) {
                            qua[i*bstride + j*tstride] |= SMF__Q_BADB;
                        }
                    }
                }
            }

            if( (*status==SAI__OK) && (!qua || !(qua[i*bstride]&SMF__Q_BADB)) ) {

                /* Power ratio requested */
                if ( fratio ) {
                    fratio[i] = p_low/p_white;
                }

                /* Store values */
                if( whitenoise ) {
                    /* Integrate the PSD by multiplying the average white noise
                       level by total number of samples and the frequency spacing:
                       this calculates the time-domain variance (in 200 Hz SCUBA-2
                       samples for example) assuming this level holds at all
                       frequencies. */

                    whitenoise[i] = p_white * ntslice * df;

                    /* If NEP set, scale this to variance in a 1-second average by
                       dividing by the sampling frequency (equivalent to
                       multiplying by sample length). */

                    if( nep ) {
                        whitenoise[i] *= steptime;
                    }
                }
            }
        }

    /* Clean up if the caller does not want to take over the power spectrum */
    if( pow ) {
        if (fftpow) {
            *fftpow = pow;
        } else {
            smf_close_file( &pow, status );
        }
    }
}
Beispiel #4
0
void  smf_fillgaps( ThrWorkForce *wf, smfData *data,
                    smf_qual_t mask, int *status ) {

/* Local Variables */
  const gsl_rng_type *type;     /* GSL random number generator type */
  dim_t bpt;                    /* Number of bolos per thread */
  dim_t i;                      /* Bolometer index */
  dim_t nbolo;                  /* Number of bolos */
  dim_t ntslice;                /* Number of time slices */
  double *dat=NULL;             /* Pointer to bolo data */
  int fillpad;                  /* Fill PAD samples? */
  size_t bstride;               /* bolo stride */
  size_t pend;                  /* Last non-PAD sample */
  size_t pstart;                /* First non-PAD sample */
  size_t tstride;               /* time slice stride */
  smfFillGapsData *job_data;    /* Structures holding data for worker threads */
  smfFillGapsData *pdata;       /* Pointer to data for next worker thread */
  smf_qual_t *qua=NULL;         /* Pointer to quality array */

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

/* Check we have double precision data floating point data. */
  if (!smf_dtype_check_fatal( data, NULL, SMF__DOUBLE, status )) return;

/* Pointers to data and quality */
  dat = data->pntr[0];
  qua = smf_select_qualpntr( data, NULL, status );

  if( !qua ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": No valid QUALITY array was provided", status );
    return;
  }

  if( !dat ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": smfData does not contain a DATA component",status);
    return;
  }

 /* obtain data dimensions */
  smf_get_dims( data,  NULL, NULL, &nbolo, &ntslice, NULL, &bstride, &tstride,
                status );

  /* Determine how many bolometers to process in each thread, and create
     the structures used to pass data to the threads. */
  if( wf ) {
     bpt = nbolo/wf->nworker;
     if( wf->nworker*bpt < nbolo ) bpt++;
     job_data = astMalloc( sizeof( smfFillGapsData )*wf->nworker );
  } else {
     bpt = nbolo;
     job_data = astMalloc( sizeof( smfFillGapsData ) );
  }

  /* Find the indices of the first and last non-PAD sample. */
  smf_get_goodrange( qua, ntslice, tstride, SMF__Q_PAD, &pstart, &pend,
                     status );

  /* Report an error if it is too short. */
  if( pend - pstart <= 2*BOX && *status == SAI__OK ) {
    *status = SAI__ERROR;
    errRepf( "", FUNC_NAME ": length of data (%d samples) is too small "
             "to fill gaps. Must have at least %d samples per bolometer.",
             status, (int) ( pend - pstart ), 2*BOX + 1 );
  }

  /* If the supplied "mask" value includes SMF__Q_PAD, then we will be
  replacing the zero-padded region at the start and end of each time series
  with artificial noisey data that connects the first and last data values
  smoothly. Remove SMF__Q_PAD from the mask. */
  if( mask & SMF__Q_PAD ) {
     mask &= ~SMF__Q_PAD;
     fillpad = 1;
  } else {
     fillpad = 0;
  }

  /* Get the default GSL randim number generator type. A separate random
     number generator is used for each worker thread so that the gap filling
     process does not depend on the the order in which threads are
     executed. */
  type = gsl_rng_default;

  /* Begin a job context. */
  thrBeginJobContext( wf, status );

  /* Loop over bolometer in groups of "bpt". */
  pdata = job_data;
  for( i = 0; i < nbolo; i += bpt, pdata++ ) {

    /* Store information for this group in the  next smfFillGapsData
       structure. */
    pdata->ntslice = ntslice;
    pdata->dat = dat;
    pdata->r = gsl_rng_alloc( type );
    pdata->b1 = i;
    pdata->b2 = i + bpt - 1;
    pdata->pend = pend;
    pdata->fillpad = fillpad;
    pdata->pstart = pstart;
    if( pdata->b2 >= nbolo ) pdata->b2 = nbolo - 1;
    pdata->bstride = bstride;
    pdata->tstride = tstride;
    pdata->qua = qua;
    pdata->mask = mask;

    /* Submit a job to the workforce to process this group of bolometers. */
    (void) thrAddJob( wf, 0, pdata, smfFillGapsParallel, 0, NULL, status );
  }

  /* Wait until all jobs in the current job context have completed, and
     then end the job context. */
  thrWait( wf, status );
  thrEndJobContext( wf, status );

  /* Free resources. */
  if( job_data ) {
    pdata = job_data;
    for( i = 0; i < nbolo; i += bpt, pdata++ ) {
      if( pdata->r ) gsl_rng_free( pdata->r );
    }
    job_data = astFree( job_data );
  }
}
Beispiel #5
0
void smf_flag_spikes( ThrWorkForce *wf, smfData *data, smf_qual_t mask,
                      double thresh, size_t box, size_t *nflagged,
                      int *status ){

/* Local Variables */
   int i;                      /* Loop counter */
   smfFlagSpikesData *job_data=NULL;/* Array of job data for each thread */
   dim_t nbolo;                /* Number of bolometers */
   dim_t ntime;                /* Number of time-slices */
   double *dat = NULL;         /* Pointer to bolo data */
   smfFlagSpikesData *pdata=NULL;/* Pointer to job data */
   int nw;                     /* Number of worker threads */
   size_t bstride;             /* Vector stride between bolometer samples */
   size_t nflag;               /* Number of samples flagged */
   size_t tstride;             /* Vector stride between time samples */
   smf_qual_t *qua = NULL;     /* Pointer to quality flags */
   size_t step;                /* step size for dividing up work */
   int njobs=0;                /* Number of jobs to be processed */

/* Check inherited status. Also return immediately if no spike flagging
   is to be performed. */
   if( *status != SAI__OK || thresh == 0.0 ) return;

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

/* Check we have double precision data. */
   smf_dtype_check_fatal( data, NULL, SMF__DOUBLE, status );

/* Get a pointer to the quality array to use. */
   qua = smf_select_qualpntr( data, NULL, status );

/* Report an error if we have no quality array. */
   if( !qua && *status == SAI__OK ) {
     *status = SAI__ERROR;
     errRep( " ", FUNC_NAME ": No valid QUALITY array was provided", status );
   }

/* Get a pointer to the data array to use. Report an error if we have
   no data array. */
   dat = data->pntr[0];
   if( !dat && *status == SAI__OK ) {
     *status = SAI__ERROR;
     errRep( " ", FUNC_NAME ": smfData does not contain a DATA component",
             status);
   }

/* Check the supplied thresh value is valid. */
   if( thresh <= 0 && *status == SAI__OK ) {
      *status = SAI__ERROR;
      msgSetd( "THRESH", thresh );
      errRep( " ", FUNC_NAME ": Can't find spikes: thresh=^THRESH, must be > 0",
              status);
   }

/* Check the supplied box value is valid. */
   if( box <= 2 && *status == SAI__OK ) {
      *status = SAI__ERROR;
      msgSeti( "BOX", box );
      errRep( " ", FUNC_NAME ": Can't find spikes: box=^BOX, must be > 2",
              status);
   }

/* Obtain data dimensions, and the stride between adjacent elements on
   each axis (bolometer and time). Use the existing data order to avoid
   the cost of re-ordering. */
   smf_get_dims( data,  NULL, NULL, &nbolo, &ntime, NULL, &bstride, &tstride,
                 status );

/* Check we have room for at least 3 boxes along the time axis. */
   if( 3*box > ntime && *status == SAI__OK ) {
      *status = SAI__ERROR;
      msgSeti( "BOX", box );
      msgSeti( "MAX", ntime/3 - 1 );
      errRep( " ", FUNC_NAME ": Can't find spikes: box=^BOX is too large, "
              " must be < ^MAX.", status);
   }

/* Set up the job data */

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

   job_data = astCalloc( nw, sizeof(*job_data) );

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

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

/* if b1 is greater than the number of bolometers, we've run out of jobs... */
     if( pdata->b1 >= nbolo ) {
       break;
     }

/* increase the jobs counter */
     njobs++;

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

     pdata->ijob = -1;   /* Flag job as ready to start */
     pdata->box = box;
     pdata->bstride = bstride;
     pdata->dat = dat;
     pdata->mask = mask;
     pdata->thresh = thresh;
     pdata->nbolo = nbolo;
     pdata->ntime = ntime;
     pdata->qua = qua;
     pdata->tstride = tstride;
   }

/* Submit jobs to find spikes in each block of bolos */
   thrBeginJobContext( wf, status );
   for( i=0; (*status==SAI__OK)&&i<njobs; i++ ) {
     pdata = job_data + i;
     pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata,
                                smfFlagSpikesPar, 0, NULL, status );
   }

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

/* Count flagged samples from all of the jobs and free resources */
   nflag=0;
   if( job_data ) {
     for( i=0; i<njobs; i++ ) {
       pdata = job_data + i;
       nflag += pdata->nflag;
     }
     job_data = astFree( job_data );
   }

/* Return the number of flagged samples, if requested */
   if( nflagged ) *nflagged = nflag;

}
Beispiel #6
0
void smf_clean_dksquid( smfData *indata, smf_qual_t mask, size_t window, smfData *model,
                        int calcdk, int nofit, int replacebad, int *status ) {

  dim_t b;                /* Bolometer index */
  size_t bstride;         /* Bolometer index stride */
  double corr;            /* Linear correlation coefficient */
  double *corrbuf=NULL;   /* Array of correlation coeffs all bolos this col */
  int needDA=0;           /* Do we need dksquids from the DA? */
  int *dkgood=NULL;       /* Flag for non-constant dark squid */
  double *dksquid=NULL;   /* Buffer for smoothed dark squid */
  double *dkav=NULL;      /* Buffer for average dark squid */
  double firstdk;         /* First value in dksquid signal */
  double gain;            /* Gain parameter from template fit */
  double *gainbuf=NULL;   /* Array of gains for all bolos in this col */
  size_t i;               /* Loop counter */
  size_t jt1;
  size_t jt2;
  size_t jf1;             /* Starting tslice that should be fit */
  size_t jf2;             /* Final tslice that should be fit */
  size_t j;               /* Loop counter */
  size_t k;               /* Loop counter */
  size_t nbad=0;          /* Number of new bad bolos due to bad dark squid */
  dim_t nbolo;            /* Number of bolometers */
  dim_t ncol;             /* Number of columns */
  dim_t ndata;            /* Number of data points */
  size_t nfit;            /* number of samples over good range to fit */
  size_t ngood=0;         /* number of good dark squids */
  dim_t nrow;             /* Number of rows */
  size_t ntot;
  dim_t ntslice;          /* Number of time slices */
  double offset;          /* Offset parameter from template fit */
  double *offsetbuf=NULL; /* Array of offsets for all bolos in this col */
  int pass;               /* two passes over data to get estimate of average */
  smf_qual_t *qua=NULL;/* Pointer to quality array */
  size_t tstride;         /* Time slice index stride */

  if (*status != SAI__OK) return;

  /* Check for NULL smfData pointer */
  if( !indata ) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME
            ": possible programming error, smfData pointer is NULL", status );
    return;
  }

  /* Decide if we need the DA extension or not */
  if( (!model) || (model && calcdk) ) {
    needDA = 1;
    if( !indata->da) {
      /* Check for NULL smfDA */
      *status = SAI__ERROR;
      errRep( " ", FUNC_NAME
              ": possible programming error, no smfDA struct in smfData",
              status);
      return;
    } else if( !indata->da->dksquid) {
      /* Check for NULL dksquid */
      *status = SAI__ERROR;
      errRep( " ", FUNC_NAME
              ": possible programming error, no dksquid array in smfData",
              status);
      return;
    }

    /* Assert the correct data order here */
    smf_dataOrder( indata->da->dksquid, 1, status );
  }

  /* Check for 3-d data and get dimensions */
  smf_get_dims( indata, &nrow, &ncol, &nbolo, &ntslice, &ndata, &bstride,
                &tstride, status );

  /* Identify the range of data that should be fit using SMF__Q_BOUND */
  if( qua ) {
    smf_get_goodrange( qua, ntslice, tstride, SMF__Q_BOUND, &jf1, &jf2,
                       status );
  } else {
    jf1 = 0;
    jf2 = ntslice-1;
  }
  nfit = jf2-jf1+1;

  /* Total total range only using SMF__Q_PAD */
  if( qua ) {
    smf_get_goodrange( qua, ntslice, tstride, SMF__Q_BOUND, &jt1, &jt2,
                       status );
  } else {
    jt1 = 0;
    jt2 = ntslice-1;
  }
  ntot = jt2-jt1+1;

  if( model ) {
    /* Check for valid model dimensions if supplied */
    if( model->dtype != SMF__DOUBLE ) {
      msgSetc("DT", smf_dtype_str(model->dtype, status) );
      *status = SAI__ERROR;
      errRep(" ", FUNC_NAME ": Data type ^DT for model not supported.",
             status );
      return;
    }

    if( (model->ndims != 2) ||
        (model->dims[0] != ntslice+nrow*3) ||
        (model->dims[1] != ncol) ) {
      *status = SAI__ERROR;
      errRep(" ", FUNC_NAME ": model has incorrect dimensions", status );
      return;
    }
  } else {
    /* Otherwise allocate space for local dksquid buffer */
    dksquid = astCalloc( ntslice, sizeof(*dksquid) );
  }

  /* Pointer to quality */
  qua = smf_select_qualpntr( indata, 0, status );

  /* Two passes: in the first we calculate an average dark squid to use as
     a surrogate for columns with dead dark squids. In the second we do the
     actual cleaning etc. */

  dkgood = astCalloc( ncol, sizeof(*dkgood) );
  dkav = astCalloc( ntslice, sizeof(*dkav) );

  for( pass=0; (*status==SAI__OK)&&(pass<2); pass++ ) {

    /* Loop over columns */
    for( i=0; (*status==SAI__OK)&&(i<ncol); i++ ) {

      /* Point dksquid, gainbuf, offsetbuf and corrbuf to the right
         place in model if supplied. */
      if( model ) {
        dksquid = model->pntr[0];
        dksquid += i*(ntslice+nrow*3);
        gainbuf = dksquid + ntslice;
        offsetbuf = gainbuf + nrow;
        corrbuf = offsetbuf + nrow;
      }

      /* First pass is just to copy the dark squid over to the model and replace
         dead dark squids with the average */
      if( pass==0 ) {

        /* Copy dark squids from the DA extension into dksquid */
        if( needDA && calcdk && model ) {
          double *ptr = indata->da->dksquid->pntr[0];
          for( j=0; j<ntslice; j++ ) {
            dksquid[j] = ptr[i+ncol*j];
          }
        }

        /* Check for a good dark squid by seeing if it ever changes */
        firstdk = VAL__BADD;
        for( j=jt1; j<=jt2; j++ ) {
          if(  dksquid[j] != VAL__BADD ) {
            if( firstdk == VAL__BADD ) {
              firstdk = dksquid[j];
            } else if( dksquid[j] != firstdk ) {
              dkgood[i] = 1;
              ngood++;

              /* Add good squid to average dksquid */
              for( k=jt1; k<=jt2; k++ ) {
                dkav[k] += dksquid[k];
              }
              break;
            }
          }
        }
      }

      /* Second pass actually do the fitting / replace with average dksquid if
         dksquid was dead */
      if( pass==1 ) {

        /* Do some dksquid initialization if requested  */
        if( (*status==SAI__OK) && needDA && calcdk && model && dkgood[i] ) {
          /* Smooth the dark squid template */
          smf_boxcar1D( &dksquid[jt1], ntot, window, NULL, 0, status );
        }

        /* Initialize fit coeffs to VAL__BADD */
        if( (*status == SAI__OK) && model ) {
          for( j=0; j<nrow; j++ ) {
            gainbuf[j] = VAL__BADD;
            offsetbuf[j] = VAL__BADD;
            corrbuf[j] = VAL__BADD;
          }
        }

        /* Loop over rows, removing the fitted dksquid template. */
        for( j=0; (!nofit) && (*status==SAI__OK) && (j<nrow); j++ ) {

          /* Calculate bolometer index from row/col counters */
          if( SC2STORE__COL_INDEX ) {
            b = i*nrow + j;
          } else {
            b = i + j*ncol;
          }

          /* If dark squid is bad, flag entire bolo as bad if it isn't already */
          if( !dkgood[i] && qua && !(qua[b*bstride]&SMF__Q_BADB) ) {
            nbad++;
            for( k=0; k<ntslice; k++ ) {
              qua[b*bstride+k*tstride] |= SMF__Q_BADB;
            }
          }

          /* Try to fit if we think we have a good dark squid and bolo, and only
             the goodrange of data (excluding padding etc.) */
          if((!qua && dkgood[i]) ||
             (qua && dkgood[i] && !(qua[b*bstride]&SMF__Q_BADB))) {
            double *d_d;
            int *d_i;

            switch( indata->dtype ) {
            case SMF__DOUBLE:
              d_d = (double *) indata->pntr[0];
              smf_templateFit1D( &d_d[b*bstride+jf1*tstride],
                                 &qua[b*bstride+jf1*tstride], NULL, NULL,
                                 mask, mask, nfit, tstride, &dksquid[jf1], 1,
                                 1, &gain, &offset, &corr, status );
              break;

            case SMF__INTEGER:
              d_i = (int *) indata->pntr[0];
              smf_templateFit1I( &d_i[b*bstride+jf1*tstride],
                                 &qua[b*bstride+jf1*tstride], NULL, NULL, mask,
                                 mask, nfit, tstride, &dksquid[jf1], 1, 1,
                                 &gain, &offset, &corr, status );
              break;

            default:
              msgSetc( "DT", smf_dtype_string( indata, status ));
              *status = SAI__ERROR;
              errRep( " ", FUNC_NAME
                      ": Unsupported data type for dksquid cleaning (^DT)",
                      status );
            }

            if( *status == SMF__INSMP || *status == SMF__DIVBZ ) {
              int wasinsmp = (*status == SMF__INSMP ? 1 : 0 );
              /* Annul SMF__INSMP as it was probably due to a bad bolometer */
              errAnnul( status );
              msgOutiff( MSG__DEBUG, "", FUNC_NAME
                         ": ROW,COL (%zu,%zu) %s", status, j, i,
                         (wasinsmp ? "insufficient good samples" : "division by zero" ));

              /* Flag entire bolo as bad if it isn't already */
              if( qua && !(qua[b*bstride]&SMF__Q_BADB) ) {
                for( k=0; k<ntslice; k++ ) {
                  qua[b*bstride+k*tstride] |= SMF__Q_BADB;
                }
              }
            } else {
              /* Store gain and offset in model */
              if( model ) {
                gainbuf[j] = gain;
                offsetbuf[j] = offset;
                corrbuf[j] = corr;
              }

              if( msgFlevok( MSG__DEBUG1, status ) ) {
                msgSeti( "COL", i );
                msgSeti( "ROW", j );
                msgSetd( "GAI", gain );
                msgSetd( "OFF", offset );
                msgSetd( "CORR", corr );
                msgOutif( MSG__DEBUG1, "", FUNC_NAME
                          ": ROW,COL (^ROW,^COL) GAIN,OFFSET,CORR "
                          "(^GAI,^OFF,^CORR)", status );
              }
            }
          }
        }
      }
    }

    /* Re-normalize the average dark-squid here at the end of pass 0 */
    if( (pass==0) && (ngood) && (*status==SAI__OK) ) {
      for( j=jt1; j<=jt2; j++ ) {
        dkav[j] /= ngood;
      }
    }

    /* Replace bad bolos in model with the average? */
    if( replacebad && calcdk && needDA && model && (*status==SAI__OK) ) {
      for( i=0; i<ncol; i++ ) {
        dksquid = model->pntr[0];
        dksquid += i*(ntslice+nrow*3);

        if( !dkgood[i] ) {
          memcpy( dksquid, dkav, sizeof(*dksquid)*ntslice );
          dkgood[i] = 1;
        }
      }
    }
  }

  /* Report number of new bad bolos that were flagged */
  if( !replacebad && nbad ) {
    msgOutiff( MSG__VERB, "", FUNC_NAME
               ": %zu new bolos flagged bad due to dead DKS", status, nbad );
  }

  /* Free dksquid only if it was a local buffer */
  if( !model && dksquid ) dksquid = astFree( dksquid );

  dkgood = astFree( dkgood );
  dkav = astFree( dkav );

}
Beispiel #7
0
void smf_clean_smfArray( ThrWorkForce *wf, smfArray *array,
                         smfArray **noisemaps, smfArray **com, smfArray **gai,
                         AstKeyMap *keymap, int *status ) {

  /* Local Variables */
  double badfrac;           /* Fraction of bad samples to flag bad bolo */
  smfData *data=NULL;       /* Pointer to individual smfData */
  int compreprocess;        /* COMmon-mode cleaning as pre-processing step */
  dim_t dcfitbox;           /* width of box for measuring DC steps */
  int dclimcorr;            /* Min number of correlated steps */
  int dcmaxsteps;           /* number of DC steps/min. to flag bolo bad */
  dim_t dcsmooth;           /* median filter width before finding DC steps */
  double dcthresh;          /* n-sigma threshold for primary DC steps */
  int dofft;                /* are we doing a freq.-domain filter? */
  int dkclean;              /* Flag for dark squid cleaning */
  smfFilter *filt=NULL;     /* Frequency domain filter */
  double flagfast;          /* Threshold for flagging slow slews */
  double flagslow;          /* Threshold for flagging slow slews */
  dim_t idx;                /* Index within subgroup */
  size_t nflag;             /* Number of elements flagged */
  double noisecliphigh = 0; /* Sigma clip high-noise outlier bolos */
  double noisecliplow = 0;  /* Sigma clip low-noise outlier bolos */
  int noiseclipprecom = 0;  /* Noise clipping before common-mode cleaning? */
  const char *opteff=NULL;  /* Pointer to optical efficiency NDF file name*/
  int opteffdiv;            /* Divide data by the optical efficiencies? */
  int order;                /* Order of polynomial for baseline fitting */
  char param[ 20 ];         /* Buffer for config parameter name */
  dim_t pcalen;             /* Chunk length for PCA cleaning */
  double pcathresh;         /* n-sigma threshold for PCA cleaning */
  double spikethresh;       /* Threshold for finding spikes */
  dim_t spikebox=0;         /* Box size for spike finder */
  struct timeval tv1, tv2;  /* Timers */
  int whiten;               /* Apply whitening filter? */
  int zeropad;              /* Pad with zeros? */

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

  /*** TIMER ***/
  smf_timerinit( &tv1, &tv2, status );

  /* Check for valid inputs */

  if( !array || (array->ndat < 1) ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": No data supplied", status );
  }

  if( array->sdata[0]->ndims != 3 ) {
    *status = SMF__WDIM;
    errRepf( "", FUNC_NAME ": Supplied smfData has %zu dims, needs 3", status,
             data->ndims );
    return;
  }

  if( !keymap ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": NULL AstKeyMap supplied", status );
    return;
  }

  /* Get cleaning parameters */
  smf_get_cleanpar( keymap, array->sdata[0], &badfrac, &dcfitbox, &dcmaxsteps,
                    &dcthresh, &dcsmooth, &dclimcorr, &dkclean,
                    NULL, &zeropad, NULL, NULL, NULL, NULL, NULL,
                    NULL, NULL, NULL, &flagslow, &flagfast, &order,
                    &spikethresh, &spikebox, &noisecliphigh, &noisecliplow,
                    NULL, &compreprocess, &pcalen, &pcathresh, NULL, NULL, NULL,
                    &noiseclipprecom, status );

  /* Loop over subarray */
  for( idx=0; (idx<array->ndat)&&(*status==SAI__OK); idx++ ) {
    data = array->sdata[idx];

    /* Update quality by synchronizing to the data array VAL__BADD values */
    msgOutif(MSG__VERB,"", FUNC_NAME ": update quality", status);
    smf_update_quality( data, 1, NULL, 0, badfrac, status );

    /*** TIMER ***/
    msgOutiff( SMF__TIMER_MSG, "", FUNC_NAME ":   ** %f s updating quality",
               status, smf_timerupdate(&tv1,&tv2,status) );

    /* Fix DC steps */
    if( dcthresh && dcfitbox ) {
      msgOutiff(MSG__VERB, "", FUNC_NAME
                ": Flagging bolos with %lf-sigma DC steps in %" DIM_T_FMT " "
                "samples as bad, using %" DIM_T_FMT
                "-sample median filter and max %d "
                "DC steps per min before flagging entire bolo bad...", status,
                dcthresh, dcfitbox, dcsmooth, dcmaxsteps);

      smf_fix_steps( wf, data, dcthresh, dcsmooth, dcfitbox, dcmaxsteps,
                     dclimcorr, 0, &nflag, NULL, NULL, status );

      msgOutiff(MSG__VERB, "", FUNC_NAME": ...%zd flagged\n", status, nflag);

      /*** TIMER ***/
      msgOutiff( SMF__TIMER_MSG, "", FUNC_NAME ":   ** %f s fixing DC steps",
                 status, smf_timerupdate(&tv1,&tv2,status) );
    }

    /* Flag Spikes */
    if( spikethresh ) {
      msgOutif(MSG__VERB," ", FUNC_NAME ": flag spikes...", status);
      smf_flag_spikes( wf, data, SMF__Q_FIT, spikethresh, spikebox,
                       &nflag, status );
      msgOutiff(MSG__VERB,"", FUNC_NAME ": ...found %zd", status, nflag );

      /*** TIMER ***/
      msgOutiff( SMF__TIMER_MSG, "", FUNC_NAME ":   ** %f s flagging spikes",
                 status, smf_timerupdate(&tv1,&tv2,status) );
    }

    /*  Flag periods of stationary pointing, and update scanspeed to more
        accurate value */
    if( flagslow || flagfast ) {
      if( data->hdr && data->hdr->allState ) {
        double scanvel=0;

        if( flagslow ) {
          msgOutiff( MSG__VERB, "", FUNC_NAME
                     ": Flagging regions with slew speeds < %.2lf arcsec/sec",
                     status, flagslow );
        }

        if( flagfast ) {
          msgOutiff( MSG__VERB, "", FUNC_NAME
                     ": Flagging regions with slew speeds > %.2lf arcsec/sec",
                     status, flagfast );


          /* Check to see if this was a sequence type that involved
             motion.  If not, skip this section */
          if( data && data->hdr && (
                                    (data->hdr->seqtype==SMF__TYP_SCIENCE) ||
                                    (data->hdr->seqtype==SMF__TYP_POINTING) ||
                                    (data->hdr->seqtype==SMF__TYP_FOCUS) ||
                                    (data->hdr->seqtype==SMF__TYP_SKYDIP))
                                 && (data->hdr->obsmode!=SMF__OBS_STARE) ) {

            smf_flag_slewspeed( data, flagslow, flagfast, &nflag, &scanvel,
                              status );
            msgOutiff( MSG__VERB,"", "%zu new time slices flagged", status,
                       nflag);

            if( msgIflev( NULL, status ) >= MSG__VERB ) {
              msgOutf( "", FUNC_NAME ": mean SCANVEL=%.2lf arcsec/sec"
                       " (was %.2lf)", status, scanvel, data->hdr->scanvel );
            }

            data->hdr->scanvel = scanvel;

            /*** TIMER ***/
            msgOutiff( SMF__TIMER_MSG, "", FUNC_NAME
                       ":   ** %f s flagging outlier slew speeds",
                       status, smf_timerupdate(&tv1,&tv2,status) );
          } else {
            msgOutif( MSG__VERB, "", FUNC_NAME
                      ": not a moving sequence or missing header, "
                      "skipping slew speed flagging", status );
          }
        }
      } else {
        msgOutif( MSG__DEBUG, "", FUNC_NAME
                  ": Skipping flagslow/flagfast because no header present",
                  status );
      }
    }

    /* Clean out the dark squid signal */
    if( dkclean ) {
      msgOutif(MSG__VERB, "", FUNC_NAME
               ": Cleaning dark squid signals from data.", status);
      smf_clean_dksquid( data, 0, 100, NULL, 0, 0, 0, status );

      /*** TIMER ***/
      msgOutiff( SMF__TIMER_MSG, "", FUNC_NAME ":   ** %f s DKSquid cleaning",
                 status, smf_timerupdate(&tv1,&tv2,status) );
    }

    /* Apply optical efficiency corrections. */
    one_strlcpy( param, "OPTEFF", sizeof(param), status );
    smf_find_subarray( data->hdr, param + strlen(param),
                       sizeof(param) - strlen(param), NULL, status );
    astChrCase( NULL, param, 1, 0 );
    if( astMapHasKey( keymap, param ) ) {
      astMapGet0I( keymap, "OPTEFFDIV", &opteffdiv );
      if ( astMapGet0C( keymap, param, &opteff ) ) {
        msgOutiff( MSG__VERB,"", FUNC_NAME ": %s bolometer values "
                   "by factors read from NDF %s", status,
                   opteffdiv ? "Dividing" : "Multiplying", opteff );
        smf_scale_bols( wf, data, NULL, opteff, param, opteffdiv, status );
      }
    }

    /* Remove baselines */
    if( order >= 0 ) {
      msgOutiff( MSG__VERB,"", FUNC_NAME
                 ": Fitting and removing %i-order polynomial baselines",
                 status, order );

      smf_fit_poly( wf, data, order, 1, NULL, status );

      /*** TIMER ***/
      msgOutiff( SMF__TIMER_MSG, "", FUNC_NAME
                 ":   ** %f s removing poly baseline",
                 status, smf_timerupdate(&tv1,&tv2,status) );
    }
  }

  /* Mask noisy bolos here if happening before common-mode cleaning */
  if( (*status == SAI__OK) && ((noisecliphigh>0.0) || (noisecliplow>0.0)) &&
      noiseclipprecom ) {

    smf__noisymask( wf, data, noisemaps, noisecliphigh, noisecliplow,
                    zeropad, &tv1, &tv2, status );
  }


  /* Optionally call smf_calcmodel_com to perform a subset of the following
     tasks as a pre-processing step:

       - remove the common-mode
       - flag outlier data using common-mode rejection
       - determine relative flatfields using amplitude of common-mode

     In order to do this we need to set up some temporary model container
     files so that the routine can be called properly. All of the same
     COMmon-mode and GAIn model parameters (e.g. com.* and gai.*) will be
     used here. However, in addition the "compreprocess" flag must be set
     for this operation to be performed. */

  if( compreprocess ) {
    smfArray *comdata = NULL;
    smfGroup *comgroup = NULL;
    smfDIMMData dat;
    smfArray *gaidata = NULL;
    smfGroup *gaigroup = NULL;
    smfArray *quadata = NULL;
    smfData *thisqua=NULL;

    msgOutif(MSG__VERB," ", FUNC_NAME ": Remove common-mode", status);

    /* Create model containers for COM, GAI */
    smf_model_create( wf, NULL, &array, NULL, NULL, NULL, NULL, NULL, 1, SMF__COM,
                      0, NULL, 0, NULL, NULL, &comgroup, &comdata, keymap,
                      status );

    smf_model_create( wf, NULL, &array, NULL, NULL, NULL, NULL, NULL, 1, SMF__GAI,
                      0, NULL, 0, NULL, NULL, &gaigroup, &gaidata, keymap,
                      status );

    /* Manually create quadata to share memory with the quality already
       stored in array */

    quadata = smf_create_smfArray( status );
    for( idx=0; (*status==SAI__OK) && (idx<array->ndat); idx++ ) {
      /* Create several new smfDatas, but they will all be freed
         properly when we close quadata */
      thisqua = smf_create_smfData( SMF__NOCREATE_DA | SMF__NOCREATE_HEAD |
                                    SMF__NOCREATE_FILE, status );

      /* Probably only need pntr->[0], but fill in the dimensionality
         information to be on the safe side */
      thisqua->dtype = SMF__QUALTYPE;
      thisqua->ndims = array->sdata[idx]->ndims;
      thisqua->isTordered = array->sdata[idx]->isTordered;
      memcpy( thisqua->dims, array->sdata[idx]->dims, sizeof(thisqua->dims) );
      memcpy( thisqua->lbnd, array->sdata[idx]->lbnd, sizeof(thisqua->lbnd) );
      thisqua->pntr[0] = smf_select_qualpntr( array->sdata[idx], NULL, status );

      smf_addto_smfArray( quadata, thisqua, status );
    }

    /* Set up the smfDIMMData and call smf_calcmodel_com */
    memset( &dat, 0, sizeof(dat) );
    dat.res = &array;
    dat.gai = &gaidata;
    dat.qua = &quadata;
    dat.noi = NULL;

    smf_calcmodel_com( wf, &dat, 0, keymap, &comdata, SMF__DIMM_FIRSTITER,
                       status );

    /*** TIMER ***/
    msgOutiff( SMF__TIMER_MSG, "", FUNC_NAME
               ":   ** %f s removing common-mode",
               status, smf_timerupdate(&tv1,&tv2,status) );

    /* Clean up and/or return values */
    if( com ) {
      *com = comdata;
    } else {
      if( comdata ) smf_close_related( &comdata, status );
    }

    if( gai ) {
      *gai = gaidata;
    } else {
      if( gaidata ) smf_close_related( &gaidata, status );
    }

    if( comgroup ) smf_close_smfGroup( &comgroup, status );
    if( gaigroup ) smf_close_smfGroup( &gaigroup, status );

    /* Before closing quadata unset all the pntr[0] since this is shared
       memory with the quality associated with array */
    if( quadata ) {
      for( idx=0; idx<quadata->ndat; idx++ ) {
        quadata->sdata[idx]->pntr[0] = NULL;
      }
      if( quadata ) smf_close_related( &quadata, status );
    }
  }

  /* PCA cleaning */
  if( pcathresh ) {
    /* Loop over subarray */
    for( idx=0; (idx<array->ndat)&&(*status==SAI__OK); idx++ ) {
      data = array->sdata[idx];

      smf_clean_pca_chunks( wf, data, pcalen, pcathresh, keymap, status );
    }

    /*** TIMER ***/
    msgOutiff( SMF__TIMER_MSG, "", FUNC_NAME ":   ** %f s PCA cleaning",
               status, smf_timerupdate(&tv1,&tv2,status) );
  }

  /* Allocate space for noisemaps if required */

  if( noisemaps ) {
    *noisemaps = smf_create_smfArray( status );
  }

  /* Loop over subarray */

  for( idx=0; (idx<array->ndat)&&(*status==SAI__OK); idx++ ) {
    data = array->sdata[idx];

    /* Filter the data. Note that we call smf_filter_execute to apply
       a per-bolometer whitening filter even if there is no
       explicitly requested smfFilter (in which case the
       smf_filter_fromkeymap call will leave the real/imaginary parts
       of the filter as NULL pointers and they will get ignored inside
       smf_filter_execute). */

    filt = smf_create_smfFilter( data, status );
    smf_filter_fromkeymap( filt, keymap, data->hdr, &dofft, &whiten, status );

    if( (*status == SAI__OK) && dofft ) {
      msgOutif( MSG__VERB, "", FUNC_NAME ": frequency domain filter", status );
      smf_filter_execute( wf, data, filt, 0, whiten, status );

      /*** TIMER ***/
      msgOutiff( SMF__TIMER_MSG, "", FUNC_NAME ":   ** %f s filtering data",
                 status, smf_timerupdate(&tv1,&tv2,status) );
    }
    filt = smf_free_smfFilter( filt, status );

    /* Mask noisy bolos here if happening after common-mode cleaning */
    if( (*status == SAI__OK) && ((noisecliphigh>0.0) || (noisecliplow>0.0)) &&
        !noiseclipprecom ) {

      smf__noisymask( wf, data, noisemaps, noisecliphigh, noisecliplow,
                      zeropad, &tv1, &tv2, status );
    }

  }
}
Beispiel #8
0
void smf_calc_iqu( ThrWorkForce *wf, smfData *data, int block_start,
                  int block_end, int ipolcrd, int qplace, int uplace,
                  int iplace, NdgProvenance *oprov, AstFitsChan *fc,
                  int pasign, double paoff, double angrot, int submean,
                  int harmonic, int *status ){

/* Local Variables: */
   AstCmpMap *cm1;
   AstCmpMap *cm2;
   AstFrameSet *wcs;          /* WCS FrameSet for output NDFs */
   AstMapping *fpmap1;
   AstMapping *fpmap2;
   AstMapping *oskymap;
   AstMapping *totmap;
   AstSkyFrame *oskyfrm;
   AstWinMap *wm;             /* Mapping to reverse the X GRID axis */
   const JCMTState *state;    /* JCMTState info for current time slice */
   const char *usesys;        /* Used system string */
   dim_t itime;               /* Time slice index */
   dim_t nbolo;               /* No. of bolometers */
   dim_t ncol;                /* No. of columns of bolometers */
   dim_t nrow;                /* No. of rows of bolometers */
   dim_t ntime;               /* Time slices to check */
   dim_t ntslice;             /* Number of time-slices in data */
   double *ipi;               /* Pointer to output I array */
   double *ipiv;              /* Pointer to output I variance array */
   double *ipq;               /* Pointer to output Q array */
   double *ipqv;              /* Pointer to output Q variance array */
   double *ipu;               /* Pointer to output U array */
   double *ipuv;              /* Pointer to output U variance array */
   double *mean;
   double ang_data[2];
   double fox[2];
   double foy[2];
   double fpr0;
   double fprinc;
   double fx[2];
   double fy[2];
   double ina[ 2 ];           /* Bolometer coords at bottom left */
   double inb[ 2 ];           /* Bolometer coords at top right */
   double outa[ 2 ];          /* NDF GRID coords at bottom left */
   double outb[ 2 ];          /* NDF GRID coords at top right */
   int bstep;                 /* Bolometer step between threads */
   int el;                    /* Number of mapped array elements */
   int gotvar;                /* Were any output variances created? */
   int indfi;                 /* Identifier for NDF holding I values */
   int indfq;                 /* Identifier for NDF holding Q values */
   int indfu;                 /* Identifier for NDF holding Q values */
   int iworker;               /* Index of a worker thread */
   int lbnd[ 2 ];             /* Lower pixel bounds of output NDF */
   int moving;
   int nworker;               /* No. of worker threads */
   int old;                   /* Data has old-style POL_ANG values? */
   int tstep;                 /* Time slice step between threads */
   int ubnd[ 2 ];             /* Upper pixel bounds of output NDF */
   size_t bstride;            /* Stride between adjacent bolometer values */
   size_t tstride;            /* Stride between adjacent time slice values */
   smfCalcIQUJobData *job_data = NULL; /* Pointer to all job data */
   smfCalcIQUJobData *pdata = NULL;/* Pointer to next job data */
   smfHead *hdr;              /* Pointer to data header this time slice */

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

/* Convenience pointers. */
   hdr = data->hdr;

/* Obtain number of time slices - will also check for 3d-ness. Also get
   the dimensions of the bolometer array and the strides between adjacent
   bolometer values. */
   smf_get_dims( data, &nrow, &ncol, &nbolo, &ntslice, NULL, &bstride,
                 &tstride, status );

/* Report an error if the block of time slices extends of either end. */
   if( block_start < 0 || block_end >= (int) ntslice ) {
      if( *status == SAI__OK ) {
         *status = SAI__ERROR;
         msgSeti( "S", block_start );
         msgSeti( "E", block_end );
         msgSeti( "N", ntslice );
         errRep( " ", "smf_calc_iqu: invalid block of time slices - ^S to "
                 "^E (^N time slices are available).", status );
      }
   }

/* Create the output NDFs. Each one is a 2D array with dimensions
   equal to the bolometer array. */
   lbnd[ 0 ] = 1;
   lbnd[ 1 ] = 1;
   ubnd[ 0 ] = ncol;
   ubnd[ 1 ] = nrow;
   ndfNew( "_DOUBLE", 2, lbnd, ubnd, &qplace, &indfq, status );
   ndfNew( "_DOUBLE", 2, lbnd, ubnd, &uplace, &indfu, status );
   if( iplace != NDF__NOPL ) {
      ndfNew( "_DOUBLE", 2, lbnd, ubnd, &iplace, &indfi, status );
   } else {
      indfi = NDF__NOID;
   }

/* Store any supplied provenance in all NDFs. */
   if( oprov ) {
      ndgWriteProv( oprov, indfq, 1, status );
      ndgWriteProv( oprov, indfu, 1, status );
      if( indfi != NDF__NOID ) ndgWriteProv( oprov, indfi, 1, status );
   }

/* Store any supplied FITS headers in all NDFs.*/
   if( fc && astGetI( fc, "NCard" ) > 0 ) {
      kpgPtfts( indfq, fc, status );
      kpgPtfts( indfu, fc, status );
      if( indfi != NDF__NOID )  kpgPtfts( indfi, fc, status );
   }

/* Store the WCS frameSet in all NDFs. First get the FrameSet for the
   central time slice in the block, and set its current Frame to the
   tracking frame. */
   smf_tslice_ast( data, ( block_start + block_end )/2, 1, NO_FTS, status);
   usesys = sc2ast_convert_system( (data->hdr->allState)[0].tcs_tr_sys,
                                    status );
   astSetC( hdr->wcs, "System", usesys );

/* Get the Mapping from focal plane coords to bolometer grid coords. This
   is the same for all time slices. sc2ast ensures that frame 3 is FPLANE. */
   fpmap1 = astGetMapping( hdr->wcs, 3, AST__BASE );

/* Take a copy and then reverse the X axis of the GRID Frame by remaping the
   base Frame using a WinMap. This produces a pixel grid such as you would
   see by looking up at the sky from underneath the array, rather than looking
   down at the ground from above the array. */
   wcs = astCopy( hdr->wcs );
   ina[ 0 ] = 1.0;
   inb[ 0 ] = ncol;
   ina[ 1 ] = 1.0;
   inb[ 1 ] = nrow;

   outa[ 0 ] = ncol;
   outb[ 0 ] = 1.0;
   outa[ 1 ] = 1.0;
   outb[ 1 ] = nrow;

   wm = astWinMap( 2, ina, inb, outa, outb, " " );
   astRemapFrame( wcs, AST__BASE, wm );
   wm = astAnnul( wm );

/* Get the Mapping from output grid coords to focal plane coords. */
   fpmap2 = astGetMapping( wcs, AST__BASE, 3 );

/* If the target is moving (assumed to be the case if the tracking
   system is AZEL or GAPPT), make the FrameSet current Frame represent
   offsets from the reference position (i.e. the moving target), and
   indicate that the offset coord system should be used for alignment. */
   if( !strcmp( usesys, "AZEL" ) || !strcmp( usesys, "GAPPT" ) ){
      astSet( wcs, "SkyRefIs=Origin,AlignOffset=1" );
      moving = 1;
   } else {
      moving = 0;
   }

/* Store the FrameSet in the output NDFs. */
   ndfPtwcs( wcs, indfq, status );
   ndfPtwcs( wcs, indfu, status );
   if( indfi != NDF__NOID ) ndfPtwcs( wcs, indfi, status );

/* Map the Data array in each NDF. */
   ndfMap( indfq, "Data", "_DOUBLE", "WRITE", (void **) &ipq, &el, status );
   ndfMap( indfu, "Data", "_DOUBLE", "WRITE", (void **) &ipu, &el, status );
   if( indfi != NDF__NOID ) {
      ndfMap( indfi, "Data", "_DOUBLE", "WRITE", (void **) &ipi, &el, status );
   } else {
      ipi = NULL;
   }

/* Map the Variance array in each NDF. */
   ndfMap( indfq, "Variance", "_DOUBLE", "WRITE", (void **) &ipqv, &el, status );
   ndfMap( indfu, "Variance", "_DOUBLE", "WRITE", (void **) &ipuv, &el, status );
   if( indfi != NDF__NOID ) {
      ndfMap( indfi, "Variance", "_DOUBLE", "WRITE", (void **) &ipiv, &el, status );
   } else {
      ipiv = NULL;
   }

/* If required, allocate memory to hold the mean bolometer value at each
   time slice. */
   mean = submean ? astMalloc( ntslice*sizeof( *mean ) ) : NULL;

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

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

/* Go through the first thousand POL_ANG values to see if they are in
   units of radians (new data) or arbitrary encoder units (old data).
   They are assumed to be in radians if no POL_ANG value is larger than
   20. */
      old = 0;
      state = hdr->allState;
      ntime = ( ntslice > 1000 ) ? 1000 : ntslice;
      for( itime = 0; itime < ntime; itime++,state++ ) {
         if( state->pol_ang > 20 ) {
            old = 1;
            msgOutif( MSG__VERB, "","   POL2 data contains POL_ANG values "
                      "in encoder units - converting to radians.", status );
            break;
         }
      }

/* If required, find the mean bolometer value at each time slice. */
      if( submean ) {

/* Determine which time-slices are to be processed by which threads. */
         tstep = ntslice/nworker;
         if( tstep < 1 ) tstep = 1;

         for( iworker = 0; iworker < nworker; iworker++ ) {
            pdata = job_data + iworker;
            pdata->block_start = iworker*tstep;
            if( iworker < nworker - 1 ) {
               pdata->block_end = pdata->block_start + tstep - 1;
            } else {
               pdata->block_end = ntslice - 1;
            }
         }

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

            pdata->bstride = bstride;
            pdata->dat = data->pntr[0];
            pdata->nbolo = nbolo;
            pdata->qua = smf_select_qualpntr( data, NULL, status );;
            pdata->tstride = tstride;
            pdata->mean = mean;
            pdata->action = 1;

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

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

      }

/* Get the Frame representing absolute sky coords in the output NDF,
   and the Mapping from sky to grid in the output NDF. */
      oskyfrm = astCopy( astGetFrame( wcs, AST__CURRENT ) );
      astSet( oskyfrm, "SkyRefIs=Ignored" );
      oskymap = astGetMapping( wcs, AST__CURRENT, AST__BASE );
      wcs = astAnnul( wcs );

/* Find the first and last time slices, calculate the angle between the
   focal pane Y axis at the time slice, and the focal plane Y axis in
   the output NDF. For intervening time-slices, the angle is found by
   linear interpolation between the extreme time slices. */
      for( el = 0; el < 2; el++ ) {

/* Get the mapping from GRID coords in the input time slice to GRID
   coords in the output. */
         totmap = smf_rebin_totmap( data, el?ntslice-1:0, oskyfrm, oskymap,
                                    moving, NO_FTS, status );

/* Modify it to be the Mapping from focal plane coords in the input time
   slice to focal plane coords in the output. */
         cm1 = astCmpMap( fpmap1, totmap, 1, " " );
         cm2 = astCmpMap( cm1, fpmap2, 1, " " );

/* Use this Mapping to convert two points on the focal plane Y axis from
   the input to the output. */
         fx[0] = 0.0;
         fy[0] = 0.0;
         fx[1] = 0.0;
         fy[1] = 4.0;
         astTran2( cm2, 2, fx, fy, 1, fox, foy );

/* The angle from the focal plane Y axis in the output to the focal plane
   Y axis in the input time slice, measured positive in sense of rotation
   from Fy to Fx. */
         ang_data[ el ] = atan2( fox[1]-fox[0], foy[1]-foy[0] );

/* Free resources for this time slice. */
         totmap = astAnnul( totmap );
         cm1 = astAnnul( cm1 );
         cm2 = astAnnul( cm2 );
      }

/* Annul objects. */
      oskymap = astAnnul( oskymap );
      oskyfrm = astAnnul( oskyfrm );
      fpmap1 = astAnnul( fpmap1 );
      fpmap2 = astAnnul( fpmap2 );

/* Get the constants of the linear relationship between focal plane
   rotation and time slice index "fpr = fpr0 + itime*fprinc". */
      fpr0 = ang_data[ 0 ];
      fprinc = ( ang_data[ 1 ] - fpr0 )/( ntslice - 1 );

/* Determine which bolometers are to be processed by which threads. */
      bstep = nbolo/nworker;
      if( bstep < 1 ) bstep = 1;

      for( iworker = 0; iworker < nworker; iworker++ ) {
         pdata = job_data + iworker;
         pdata->b1 = iworker*bstep;
         pdata->b2 = pdata->b1 + bstep - 1;
      }

/* Ensure that the last thread picks up any left-over bolometers */
      pdata->b2 = nbolo - 1;

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

         pdata->bstride = bstride;
         pdata->dat = data->pntr[0];;
         pdata->nbolo = nbolo;
         pdata->qua = smf_select_qualpntr( data, NULL, status );;
         pdata->tstride = tstride;
         pdata->allstates = hdr->allState;
         pdata->ipq = ipq;
         pdata->ipu = ipu;
         pdata->ipi = ipi;
         pdata->ipqv = ipqv;
         pdata->ipuv = ipuv;
         pdata->ipiv = ipiv;
         pdata->ipolcrd = ipolcrd;
         pdata->block_start = block_start;
         pdata->block_end = block_end;
         pdata->old = old;
         pdata->ncol = ncol;
         pdata->pasign = pasign ? +1: -1;
         pdata->paoff = paoff;
         pdata->angrot = angrot;
         pdata->fpr0 = fpr0;
         pdata->fprinc = fprinc;
         pdata->angfac = harmonic/4.0;
         pdata->action = 0;
         pdata->mean = mean;

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

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

/* See if any thread produced non-bad variance values. */
      gotvar = 0;
      for( iworker = 0; iworker < nworker; iworker++ ) {
         pdata = job_data + iworker;
         if( pdata->gotvar ) gotvar = 1;
      }

/* If no variances were created, erase the Variance component and tell
   the user. */
      ndfUnmap( indfq, "*", status );
      ndfUnmap( indfu, "*", status );
      if( ipi ) ndfUnmap( indfi, "*", status );

      if( !gotvar ) {
         ndfReset( indfq, "Variance", status );
         ndfReset( indfu, "Variance", status );
         if( ipi ) ndfReset( indfi, "Variance", status );
         msgOut( "", "Warning: Insufficient input data to produce variances",
                 status );
      }
   }

/* Add POLANAL Frames to the WCS FrameSet in each output NDF. This Frame
   is used by POLPACK to determine the reference direction of the Stokes
   vectors (focal plane Y in this case, i.e. zero-based axis 1 ). */
   smf_polext( indfq, 0, 0.0, "FPLANE", 1, status );
   smf_polext( indfu, 0, 0.0, "FPLANE", 1, status );
   if( ipi ) smf_polext( indfi, 0, 0.0, "FPLANE", 1, status );

/* Free the two output NDFs. */
   ndfAnnul( &indfq, status );
   ndfAnnul( &indfu, status );
   if( ipi ) ndfAnnul( &indfi, status );

/* Free other resources. */
   job_data = astFree( job_data );
   mean = astFree( mean );
}
Beispiel #9
0
void smf_calc_iqu( ThrWorkForce *wf, smfData *data, int block_start,
                  int block_end, int ipolcrd, int qplace, int uplace,
                  int iplace, NdgProvenance *oprov, AstFitsChan *fc,
                  int pasign, double paoff, double angrot, int submean,
                  int *status ){

/* Local Variables: */
   AstFrameSet *wcs;          /* WCS FrameSet for output NDFs */
   AstWinMap *wm;             /* Mapping to reverse the X GRID axis */
   const JCMTState *state;    /* JCMTState info for current time slice */
   dim_t nbolo;               /* No. of bolometers */
   dim_t ncol;                /* No. of columns of bolometers */
   dim_t nrow;                /* No. of rows of bolometers */
   dim_t ntslice;             /* Number of time-slices in data */
   double *ipi;               /* Pointer to output I array */
   double *ipq;               /* Pointer to output Q array */
   double *ipu;               /* Pointer to output U array */
   double ina[ 2 ];           /* Bolometer coords at bottom left */
   double inb[ 2 ];           /* Bolometer coords at top right */
   double outa[ 2 ];          /* NDF GRID coords at bottom left */
   double outb[ 2 ];          /* NDF GRID coords at top right */
   int bstep;                 /* Bolometer step between threads */
   int el;                    /* Number of mapped array elements */
   int indfi;                 /* Identifier for NDF holding I values */
   int indfq;                 /* Identifier for NDF holding Q values */
   int indfu;                 /* Identifier for NDF holding Q values */
   int itime;                 /* Time slice index */
   int iworker;               /* Index of a worker thread */
   int lbnd[ 2 ];             /* Lower pixel bounds of output NDF */
   int ntime;                 /* Time slices to check */
   int nworker;               /* No. of worker threads */
   int old;                   /* Data has old-style POL_ANG values? */
   int ubnd[ 2 ];             /* Upper pixel bounds of output NDF */
   size_t bstride;            /* Stride between adjacent bolometer values */
   size_t tstride;            /* Stride between adjacent time slice values */
   smfCalcIQUJobData *job_data = NULL; /* Pointer to all job data */
   smfCalcIQUJobData *pdata = NULL;/* Pointer to next job data */
   smfHead *hdr;              /* Pointer to data header this time slice */
   double *mean;
   int tstep;                 /* Time slice step between threads */

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

/* Convenience pointers. */
   hdr = data->hdr;

/* Obtain number of time slices - will also check for 3d-ness. Also get
   the dimensions of the bolometer array and the strides between adjacent
   bolometer values. */
   smf_get_dims( data, &nrow, &ncol, &nbolo, &ntslice, NULL, &bstride,
                 &tstride, status );

/* Report an error if the block of time slices extends of either end. */
   if( block_start < 0 || block_end >= (int) ntslice ) {
      if( *status == SAI__OK ) {
         *status = SAI__ERROR;
         msgSeti( "S", block_start );
         msgSeti( "E", block_end );
         msgSeti( "N", ntslice );
         errRep( " ", "smf_calc_iqu: invalid block of time slices - ^S to "
                 "^E (^N time slices are available).", status );
      }
   }

/* Create the output NDFs. Each one is a 2D array with dimensions
   equal to the bolometer array. */
   lbnd[ 0 ] = 1;
   lbnd[ 1 ] = 1;
   ubnd[ 0 ] = ncol;
   ubnd[ 1 ] = nrow;
   ndfNew( "_DOUBLE", 2, lbnd, ubnd, &qplace, &indfq, status );
   ndfNew( "_DOUBLE", 2, lbnd, ubnd, &uplace, &indfu, status );
   if( iplace != NDF__NOPL ) {
      ndfNew( "_DOUBLE", 2, lbnd, ubnd, &iplace, &indfi, status );
   } else {
      indfi = NDF__NOID;
   }

/* Store any supplied provenance in all NDFs. */
   if( oprov ) {
      ndgWriteProv( oprov, indfq, 1, status );
      ndgWriteProv( oprov, indfu, 1, status );
      if( indfi != NDF__NOID ) ndgWriteProv( oprov, indfi, 1, status );
   }

/* Store any supplied FITS headers in all NDFs.*/
   if( fc && astGetI( fc, "NCard" ) > 0 ) {
      kpgPtfts( indfq, fc, status );
      kpgPtfts( indfu, fc, status );
      if( indfi != NDF__NOID )  kpgPtfts( indfi, fc, status );
   }

/* Store the WCS frameSet in all NDFs. First get the FrameSet for the
   central time slice in the block, and set its current Frame to the
   tracking frame. */
   smf_tslice_ast( data, ( block_start + block_end )/2, 1, status);
   astSetC( hdr->wcs, "System",
            sc2ast_convert_system( (data->hdr->allState)[0].tcs_tr_sys,
                                    status ) );

/* Take a copy and then reverse the X axis of the GRID Frame by remaping the
   base Frame using a WinMap. This produces a pixel grid such as you would
   see by looking up at the sky from underneath the array, rather than looking
   down at the ground from above the array. */
   wcs = astCopy( hdr->wcs );
   ina[ 0 ] = 1.0;
   inb[ 0 ] = ncol;
   ina[ 1 ] = 1.0;
   inb[ 1 ] = nrow;

   outa[ 0 ] = ncol;
   outb[ 0 ] = 1.0;
   outa[ 1 ] = 1.0;
   outb[ 1 ] = nrow;

   wm = astWinMap( 2, ina, inb, outa, outb, " " );
   astRemapFrame( wcs, AST__BASE, wm );
   wm = astAnnul( wm );

/* Store the FrameSet in the output NDFs, then annull the copy. */
   ndfPtwcs( wcs, indfq, status );
   ndfPtwcs( wcs, indfu, status );
   if( indfi != NDF__NOID ) ndfPtwcs( wcs, indfi, status );
   wcs = astAnnul( wcs );

/* Map the Data array in each NDF. */
   ndfMap( indfq, "Data", "_DOUBLE", "WRITE", (void **) &ipq, &el, status );
   ndfMap( indfu, "Data", "_DOUBLE", "WRITE", (void **) &ipu, &el, status );
   if( indfi != NDF__NOID ) {
      ndfMap( indfi, "Data", "_DOUBLE", "WRITE", (void **) &ipi, &el, status );
   } else {
      ipi = NULL;
   }


/* If required, allocate memory to hold the mean bolometer value at each
   time slice. */
   mean = submean ? astMalloc( ntslice*sizeof( *mean ) ) : NULL;

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

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

/* Go through the first thousand POL_ANG values to see if they are in
   units of radians (new data) or arbitrary encoder units (old data).
   They are assumed to be in radians if no POL_ANG value is larger than
   20. */
      old = 0;
      state = hdr->allState;
      ntime = ( ntslice > 1000 ) ? 1000 : ntslice;
      for( itime = 0; itime < ntime; itime++,state++ ) {
         if( state->pol_ang > 20 ) {
            old = 1;
            msgOutif( MSG__VERB, "","   POL2 data contains POL_ANG values "
                      "in encoder units - converting to radians.", status );
            break;
         }
      }

/* If required, find the mean bolometer value at each time slice. */
      if( submean ) {

/* Determine which time-slices are to be processed by which threads. */
         tstep = ntslice/nworker;
         if( tstep < 1 ) tstep = 1;

         for( iworker = 0; iworker < nworker; iworker++ ) {
            pdata = job_data + iworker;
            pdata->block_start = iworker*tstep;
            if( iworker < nworker - 1 ) {
               pdata->block_end = pdata->block_start + tstep - 1;
            } else {
               pdata->block_end = ntslice - 1;
            }
         }

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

            pdata->bstride = bstride;
            pdata->dat = data->pntr[0];
            pdata->nbolo = nbolo;
            pdata->qua = smf_select_qualpntr( data, NULL, status );;
            pdata->tstride = tstride;
            pdata->mean = mean;
            pdata->action = 1;

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

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

      }

/* Determine which bolometers are to be processed by which threads. */
      bstep = nbolo/nworker;
      if( bstep < 1 ) bstep = 1;

      for( iworker = 0; iworker < nworker; iworker++ ) {
         pdata = job_data + iworker;
         pdata->b1 = iworker*bstep;
         pdata->b2 = pdata->b1 + bstep - 1;
      }

/* Ensure that the last thread picks up any left-over bolometers */
      pdata->b2 = nbolo - 1;

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

         pdata->bstride = bstride;
         pdata->dat = data->pntr[0];;
         pdata->nbolo = nbolo;
         pdata->qua = smf_select_qualpntr( data, NULL, status );;
         pdata->tstride = tstride;
         pdata->allstates = hdr->allState;
         pdata->ipq = ipq;
         pdata->ipu = ipu;
         pdata->ipi = ipi;
         pdata->ipolcrd = ipolcrd;
         pdata->block_start = block_start;
         pdata->block_end = block_end;
         pdata->old = old;
         pdata->ncol = ncol;
         pdata->pasign = pasign ? +1: -1;
         pdata->paoff = paoff;
         pdata->angrot = angrot;
         pdata->action = 0;
         pdata->mean = mean;

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

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

/* Add POLANAL Frames to the WCS FrameSet in each output NDF. This Frame
   is used by POLPACK to determine the reference direction of the Stokes
   vectors (focal plane Y in this case, i.e. zero-based axis 1 ). */
   smf_polext( indfq, 0, 0.0, "FPLANE", 1, status );
   smf_polext( indfu, 0, 0.0, "FPLANE", 1, status );
   if( ipi ) smf_polext( indfi, 0, 0.0, "FPLANE", 1, status );

/* Free the two output NDFs. */
   ndfAnnul( &indfq, status );
   ndfAnnul( &indfu, status );
   if( ipi ) ndfAnnul( &indfi, status );

/* Free other resources. */
   job_data = astFree( job_data );
   mean = astFree( mean );
}
Beispiel #10
0
void smf_update_quality( ThrWorkForce *wf, smfData *data, int syncbad,
			 const int *badmask, smf_qual_t addqual, double badfrac,
			 int *status ) {

  dim_t nbolo;                  /* Number of bolometers */
  dim_t ndata;                  /* Number of data points */
  dim_t ntslice;                /* Number of time slices */
  size_t bstride;               /* bol stride */
  size_t tstride;               /* time slice stride */
  smf_qual_t *qual=NULL;        /* Pointer to the QUALITY array */
  SmfUpdateQualityData *job_data = NULL;
  SmfUpdateQualityData *pdata;
  int nw;
  size_t istep;
  size_t bstep;
  int iw;

  if ( *status != SAI__OK ) return;

  /* Check for QUALITY */
  qual = smf_select_qualpntr( data, NULL, status );
  if (!qual) {
    if (*status == SAI__OK) {
      *status = SAI__ERROR;
      errRep( FUNC_NAME, "smfData does not contain a QUALITY component",
              status);
    }
    return;
  }

  /* Check for DATA */
  if( !data->pntr[0] ) {
    *status = SAI__ERROR;
    errRep( FUNC_NAME, "smfData does not contain a DATA component", status );
    return;
  }

  /* Check for valid badfrac */
  if( (badfrac < 0) || (badfrac > 1) ) {
    msgSeti( "BADFRAC", badfrac );
    errRep(FUNC_NAME,
           "Invalid badfrac: ^BADFRAC. Must be in range (0 -- 1).", status);
  }

  /* Calculate data dimensions */
  smf_get_dims( data,  NULL, NULL, &nbolo, &ntslice, &ndata, &bstride,
                &tstride, status );

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

  /* Find how many elements to process in each worker thread. */
  istep = ndata/nw;
  if( istep == 0 ) istep = 1;

  /* Find how many bolometers to process in each worker thread. */
  bstep = nbolo/nw;
  if( bstep == 0 ) bstep = 1;

  /* Allocate job data for threads, and store common values. Ensure that the
     last thread picks up any left-over elements or bolometers.  */
  job_data = astCalloc( nw, sizeof(*job_data) );
  if( *status == SAI__OK ) {
    for( iw = 0; iw < nw; iw++ ) {
      pdata = job_data + iw;
      pdata->i1 = iw*istep;
      pdata->b1 = iw*bstep;
      if( iw < nw - 1 ) {
        pdata->i2 = pdata->i1 + istep - 1;
        pdata->b2 = pdata->b1 + bstep - 1;
      } else {
        pdata->i2 = ndata - 1;
        pdata->b2 = nbolo - 1;
      }
      pdata->qual = qual;
    }
  }

  if( *status == SAI__OK ) {
    /* some pointers to the data array if needed */
    double * ddata = NULL;
    int * idata = NULL;

    /* we will need the data array if we are checking it for bad values
       or looking for bad fraction */
    if (syncbad || badfrac) {
      smf_select_pntr( data->pntr, data->dtype, &ddata, NULL,
                       &idata, NULL, status);
    }

    /* Synchronize SMF__Q_BADDA quality and VAL__BADD in data array */
    if( syncbad ) {
      if (data->dtype == SMF__DOUBLE) {
        for( iw = 0; iw < nw; iw++ ) {
            pdata = job_data + iw;
            pdata->operation = 1;
            pdata->ddata = ddata;
            thrAddJob( wf, 0, pdata, smf1_update_quality, 0, NULL, status );
        }
        thrWait( wf, status );

      } else if (data->dtype == SMF__INTEGER) {
        for( iw = 0; iw < nw; iw++ ) {
            pdata = job_data + iw;
            pdata->operation = 2;
            pdata->idata = idata;
            thrAddJob( wf, 0, pdata, smf1_update_quality, 0, NULL, status );
        }
        thrWait( wf, status );

      } else {
        msgSetc( "TYP", smf_dtype_string( data, status ));
        *status = SAI__ERROR;
        errRep( "",FUNC_NAME " data is of unsupported type (^TYP)",
                status);
        return;
      }
    }

    /* Apply badmask if available */
    if( badmask || badfrac ) {

      /* calculate the badfraction threshold in terms of number of bad
         found so that we do not have to continually divide to calculate
         the current fraction */
      dim_t badthresh = ntslice;
      /* special case 0 */
      if (badfrac) badthresh = badfrac * (double)ntslice;

      /* Submit the jobs and wait for them all to finish. */
      for( iw = 0; iw < nw; iw++ ) {
          pdata = job_data + iw;
          pdata->operation = 3;
          pdata->badthresh = badthresh;
          pdata->addqual = addqual;
          pdata->ntslice = ntslice;
          pdata->bstride = bstride;
          pdata->tstride = tstride;
          pdata->badmask = badmask;
          pdata->syncbad = syncbad;
          pdata->idata = idata;
          pdata->ddata = ddata;
          thrAddJob( wf, 0, pdata, smf1_update_quality, 0, NULL, status );
      }
      thrWait( wf, status );
    }
  }

  job_data = astFree( job_data );
}
Beispiel #11
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 );
  }

}
Beispiel #12
0
size_t smf_clean_pca( ThrWorkForce *wf, smfData *data, size_t t_first,
                      size_t t_last, double thresh, size_t ncomp,
                      smfData **components, smfData **amplitudes,
                      int flagbad, int sub, AstKeyMap *keymap,
                      smf_qual_t mask, int *status ){

  double *amp=NULL;       /* matrix of components amplitudes for each bolo */
  size_t abstride;        /* bolo stride in amp array */
  size_t acompstride;     /* component stride in amp array */
  size_t bstride;         /* bolo stride */
  double *comp=NULL;      /* data cube of components */
  size_t ccompstride;     /* component stride in comp array */
  size_t ctstride;        /* time stride in comp array */
  gsl_matrix *cov=NULL;   /* bolo-bolo covariance matrix */
  size_t i;               /* Loop counter */
  int ii;                 /* Loop counter */
  size_t j;               /* Loop counter */
  smfPCAData *job_data=NULL;/* job data */
  size_t k;               /* Loop counter */
  size_t *goodbolo=NULL;  /* Indices of the good bolometers for analysis */
  dim_t nbolo;            /* number of bolos */
  dim_t ndata;            /* number of samples in data */
  size_t ngoodbolo;       /* number good bolos = number principal components */
  dim_t ntslice;          /* number of time slices */
  int nw;                 /* total available worker threads */
  smfPCAData *pdata=NULL; /* Pointer to job data */
  smf_qual_t *qua=NULL;   /* Pointer to quality array */
  gsl_vector *s=NULL;     /* singular values for SVD */
  size_t bstep;           /* Bolo step size for job division */
  size_t step;            /* step size for job division */
  size_t tlen;            /* Length of the time-series used for PCA */
  size_t tstride;         /* time slice stride */
  gsl_vector *work=NULL;  /* workspace for SVD */

  if (*status != SAI__OK) return 0;

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

  /* Check for NULL smfData pointer */
  if( !data || !data->pntr[0]) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME
            ": possible programming error, NULL data supplied", status );
    return 0;
  }

  smf_get_dims( data, NULL, NULL, &nbolo, &ntslice, &ndata, &bstride, &tstride,
                status );

  if( data->ndims != 3 ) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME
            ": possible programming error, smfData should be 3-dimensional",
            status );
    return 0;
  }

  if( data->dtype != SMF__DOUBLE ) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME
            ": possible programming error, smfData should be double precision",
            status );
    return 0;
  }

  if( ntslice <= 2 ) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME ": fewer than 2 time slices!", status );
    goto CLEANUP;
  }

  /* If the range of time slices has not been specified, us the total
     range excluding padding and apodizing. */
  qua = smf_select_qualpntr( data, 0, status );
  if( !t_last ) {
     if( qua ) {
        smf_get_goodrange( qua, ntslice, tstride, (SMF__Q_PAD | SMF__Q_APOD),
                           &t_first, &t_last, status );
     } else {
        t_last = ntslice-1;
     }
  }

  if( t_last > (ntslice-1) ) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME ": t_last is set past the last time slice!",
            status );
    goto CLEANUP;
  }

  if( (t_last < t_first) || ( (t_last - t_first) < 1 ) ) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME ": t_last - t_first must be > 1", status );
    goto CLEANUP;
  }

  tlen = t_last - t_first + 1;

  if( flagbad && (tlen != ntslice ) ) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME
            ": flagbad unsupported if t_first/last do not span full data",
            status );
    goto CLEANUP;
  }

  if( qua ) {
    /* If quality supplied, identify good bolometers */
    ngoodbolo = 0;
    for( i=0; i<nbolo; i++ ) {
      if( !(qua[i*bstride]&SMF__Q_BADB) ) {
        ngoodbolo++;
      }
    }

    /* Now remember which were the good bolometers */
    goodbolo = astCalloc( ngoodbolo, sizeof(*goodbolo) );
    ngoodbolo = 0;
    for( i=0; i<nbolo; i++ ) {
      if( !(qua[i*bstride]&SMF__Q_BADB) ) {
        goodbolo[ngoodbolo] = i;
        ngoodbolo++;
      }
    }

  } else {
    /* Otherwise assume all bolometers are good */
    ngoodbolo = nbolo;
    goodbolo = astCalloc( ngoodbolo, sizeof(*goodbolo) );
    for( i=0; i<ngoodbolo; i++ ) {
      goodbolo[i] = i;
    }
  }

  if( ngoodbolo <= 2 ) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME ": fewer than 2 working bolometers!", status );
    goto CLEANUP;
  }

  /* Fill bad values and values flagged via "mask" (except entirely bad
     bolometers) with interpolated data values. */
  mask &= ~SMF__Q_BADB;
  smf_fillgaps( wf, data, mask, status );

  /* Allocate arrays */
  amp = astCalloc( nbolo*ngoodbolo, sizeof(*amp) );
  comp = astCalloc( ngoodbolo*tlen, sizeof(*comp) );
  cov = gsl_matrix_alloc( ngoodbolo, ngoodbolo );
  s = gsl_vector_alloc( ngoodbolo );
  work = gsl_vector_alloc( ngoodbolo );

  /* These strides will make comp time-ordered */
  ccompstride = 1;
  ctstride = ngoodbolo;

  /* These strides will also make amp look time-ordered (sort-of: the time
     axis is now the component number */
  abstride = 1;
  acompstride = nbolo;

  /* Allocate job data for threads */
  job_data = astCalloc( nw, sizeof(*job_data) );

  /* Set up the division of labour for threads: independent blocks of time */

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

  if( nw > (int) ngoodbolo ) {
    bstep = 1;
  } else {
    bstep = ngoodbolo/nw;
  }

  for( ii=0; (*status==SAI__OK)&&(ii<nw); ii++ ) {
    pdata = job_data + ii;

    /* Blocks of time slices */
    pdata->t1 = ii*step + t_first;
    pdata->t2 = (ii+1)*step + t_first - 1;

    /* Blocks of bolometers. */
    pdata->b1 = ii*bstep;
    pdata->b2 = (ii+1)*bstep - 1;

    /* Ensure that the last thread picks up any left-over tslices */
    if( (ii==(nw-1)) ) {
       pdata->t2 = t_first + tlen - 1;
       pdata->b2 = ngoodbolo - 1;
    }

    /* initialize work data */
    pdata->amp = NULL;
    pdata->abstride = abstride;
    pdata->acompstride = acompstride;
    pdata->bstride = bstride;
    pdata->comp = comp;
    pdata->cov = NULL;
    pdata->covwork = NULL;
    pdata->ccompstride = ccompstride;
    pdata->ctstride = ctstride;
    pdata->data = data;
    pdata->goodbolo = NULL;
    pdata->ijob = -1;
    pdata->nbolo = nbolo;
    pdata->ngoodbolo = ngoodbolo;
    pdata->t_first = t_first;
    pdata->t_last = t_last;
    pdata->tlen = tlen;
    pdata->operation = 0;
    pdata->tstride = tstride;

    /* Each thread will accumulate the projection of its own portion of
       the time-series. We'll add them to the master amp at the end */
    pdata->amp = astCalloc( nbolo*ngoodbolo, sizeof(*(pdata->amp)) );

    /* Each thread will accumulate sums of x, y, and x*y for each bolo when
       calculating the covariance matrix */
    pdata->covwork = astCalloc( ngoodbolo*ngoodbolo,
                                sizeof(*(pdata->covwork)) );

    /* each thread gets its own copy of the goodbolo lookup table */
    pdata->goodbolo = astCalloc( ngoodbolo, sizeof(*(pdata->goodbolo)) );
    if( *status == SAI__OK ) {
      memcpy( pdata->goodbolo, goodbolo,
              ngoodbolo*sizeof(*(pdata->goodbolo)) );
    }

  }

  if( *status == SAI__OK ) {

    /* Remove the mean from each gap-filled bolometer time stream ---------------------*/

    msgOutif( MSG__VERB, "", FUNC_NAME ": removing bolometer means...",
              status );

    for( ii=0; ii<nw; ii++ ) {
      pdata = job_data + ii;
      pdata->operation = -1;
      thrAddJob( wf, 0, pdata, smfPCAParallel, 0, NULL, status );
    }

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



    /* Measure the covariance matrix using parallel code ---------------------*/

    msgOutif( MSG__VERB, "", FUNC_NAME
              ": measuring bolo-bolo covariance matrix...", status );

    /* Set up the jobs to calculate sums for each time block and submit */
    for( ii=0; ii<nw; ii++ ) {
      pdata = job_data + ii;
      pdata->operation = 0;
      pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata, smfPCAParallel,
                                 0, NULL, status );
    }

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

    /* We now have to add together all of the sums from each thread and
       normalize */
    if( *status == SAI__OK ) {
      for( i=0; i<ngoodbolo; i++ ) {
        for( j=i; j<ngoodbolo; j++ ) {
          double c;
          double *covwork=NULL;
          double sum_xy;

          sum_xy = 0;

          for( ii=0; ii<nw; ii++ ) {
            pdata = job_data + ii;
            covwork = pdata->covwork;

            sum_xy += covwork[ i + j*ngoodbolo ];
          }

          c = sum_xy / ((double)tlen-1);

          gsl_matrix_set( cov, i, j, c );
          gsl_matrix_set( cov, j, i, c );
        }
      }
    }
  }

  /* Factor cov = u s v^T, noting that the SVD routine calculates v^T in
     in-place of cov. --------------------------------------------------------*/

  msgOutif( MSG__VERB, "", FUNC_NAME
            ": perfoming singular value decomposition...", status );

  smf_svd( wf, ngoodbolo, cov->data, s->data, NULL, 10*VAL__EPSD,
           1, status );
  if( CHECK ) {
    double check=0;

    for( i=0; i<ngoodbolo; i++ ) {
      for( j=0; j<ngoodbolo; j++ ) {
        check += gsl_matrix_get( cov, j, i );
      }
    }

    printf("--- check inverted: %lf\n", check);
  }

  /* Calculate normalized eigenvectors with parallel code --------------------*/

  msgOutif( MSG__VERB, "", FUNC_NAME
            ": calculating statistically-independent components...", status );

  /* The above calculation tells us what linear combinations of the original
     bolometer time series will give us the statistically independent new
     set of basis vectors (components), which we then normalize by their RMS. */

  /* Set up the jobs to calculate sums for each time block and submit */
  if( *status == SAI__OK ) {
    for( ii=0; ii<nw; ii++ ) {
      pdata = job_data + ii;
      pdata->cov = cov;
      pdata->operation = 1;
      pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata, smfPCAParallel,
                                 0, NULL, status );
    }
  }

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

  /* Then normalize. Some of the components may have zero amplitude and
     so cannot be used (i.e. we are trying to use more components than
     there is evidence for in the data). So we check for zero sigma. In
     fact, we check for silly small sigma, not just zero sigma. Any
     component for which the sigma is less than 1E-10 of the log-mean
     sigma is excluded. */
  {
    double *sigmas = astMalloc( ngoodbolo*sizeof( *sigmas ) );
    double check = 0;
    double s1 = 0.0;
    int s2 = 0;
    int nlow = 0;

    for( i=0; (*status==SAI__OK)&&(i<ngoodbolo); i++ ) {
      double sigma;

      smf_stats1D( comp + i*ccompstride, ctstride, tlen, NULL, 0,
                   0, NULL, &sigma, NULL, NULL, status );

      /* Apparently we need this to get the normalization right */
      sigma *= sqrt((double) tlen);

      if( *status == SAI__OK ) {
        if( sigma > 0.0 ) {
           for( k=0; k<tlen; k++ ) {
             comp[i*ccompstride + k*ctstride] /= sigma;
             sigmas[ i ] = sigma;
             s1 += log10( sigma );
             s2++;
           }
        } else {
           for( k=0; k<tlen; k++ ) {
             comp[i*ccompstride + k*ctstride] = VAL__BADD;
             sigmas[ i ] = VAL__BADD;
           }
           nlow++;
        }
      }
    }

    /* Exclude any components that have a silly small standard deviation
       (less that 1E-10 of the logmean of all components). Any with zero
       standard deviation will already have been excluded. */
    if( s2 > 0 ) {
       double logmean = s1/s2;
       for( i=0; i<ngoodbolo; i++ ) {
          if( sigmas[ i ] != VAL__BADD && sigmas[ i ] < 1E-10*logmean ) {
             for( k=0; k<tlen; k++ ) {
                comp[i*ccompstride + k*ctstride] = VAL__BADD;
                nlow++;
             }
          }
       }
    }

    msgOutiff( MSG__DEBUG, "", FUNC_NAME ": rejecting %d (out of %zu) components"
               " because they are too weak to normalise", status, nlow, ngoodbolo );

    for( i=0; i<ngoodbolo*tlen; i++ ) {
      if( comp[i] != VAL__BADD ) check += comp[i];
    }

    sigmas = astFree( sigmas );

    //printf("--- check component: %lf\n", check);
  }
  /* Now project the data along each of these normalized basis vectors
     to figure out the amplitudes of the components in each bolometer
     time series. ------------------------------------------------------------*/

  msgOutif( MSG__VERB, "", FUNC_NAME
              ": calculating component amplitudes in each bolo...", status );

  /* Set up the jobs  */
  if( *status == SAI__OK ) {
    for( ii=0; ii<nw; ii++ ) {
      pdata = job_data + ii;
      pdata->operation = 2;
      pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata, smfPCAParallel,
                                 0, NULL, status );
    }
  }

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

  /* Add all of the amp arrays together from the threads */
  if( *status == SAI__OK ) {
    size_t index;

    for( ii=0; ii<nw; ii++ ) {
      pdata = job_data + ii;

      for( i=0; i<ngoodbolo; i++ ) {        /* Loop over good bolo */
        for( j=0; j<ngoodbolo; j++ ) {      /* Loop over component */
          index = goodbolo[i]*abstride + j*acompstride;
          amp[index] += pdata->amp[index];
        }
      }
    }
  }

  if( CHECK ){
    double check=0;

    for( i=0; i<nbolo*ngoodbolo; i++ ) {
      check += amp[i];
    }
    printf("--- check combined amp: %lf\n", check);
  }

  if( CHECK ){
    double check=0;
    for( i=0; i<ngoodbolo*tlen; i++ ) {
      if( comp[i] != VAL__BADD ) check += comp[i];
    }

    printf("--- check component A: %lf\n", check);
  }

  /* Check to see if the amplitudes are mostly negative or positive. If
     mostly negative, flip the sign of both the component and amplitudes */
  if( *status == SAI__OK ) {
    double total;
    for( j=0; j<ngoodbolo; j++ ) {    /* loop over component */
      total = 0;
      for( i=0; i<ngoodbolo; i++ ) {  /* loop over bolometer */
        total += amp[goodbolo[i]*abstride + j*acompstride];
      }

      /* Are most amplitudes negative for this component? */
      if( total < 0 ) {
        /* Flip sign of the amplitude */
        for( i=0; i<ngoodbolo; i++ ) { /* loop over bolometer */
          amp[goodbolo[i]*abstride + j*acompstride] =
            -amp[goodbolo[i]*abstride + j*acompstride];
        }

        /* Flip sign of the component */
        for( k=0; k<tlen; k++ ) {
           if(  comp[j*ccompstride + k*ctstride] != VAL__BADD ) {
              comp[j*ccompstride + k*ctstride] *= -1;
           }
        }
      }
    }
  }

  /* Finally, copy the master amp array back into the workspace for
     each thread */
  if( *status == SAI__OK ) {
    for( ii=0; ii<nw; ii++ ) {
      pdata = job_data + ii;
      memcpy( pdata->amp, amp, sizeof(*(pdata->amp))*nbolo*ngoodbolo );
    }
  }

  if( CHECK ){
    double check=0;
    for( i=0; i<ngoodbolo*tlen; i++ ) {
      if( comp[i] != VAL__BADD ) check += comp[i];
    }

    printf("--- check component B: %lf\n", check);
  }

  /* Flag outlier bolometers if requested ------------------------------------*/

  if( (*status==SAI__OK) && flagbad ) {
    smfArray *data_array=NULL;
    smfArray *gain_array=NULL;
    smfGroup *gain_group=NULL;
    AstKeyMap *kmap=NULL;         /* Local keymap */
    AstObject *obj=NULL;          /* Used to avoid compiler warnings */
    double *template=NULL;
Beispiel #13
0
size_t smf_check_quality( ThrWorkForce *wf, smfData *data, int showbad,
                          int *status ) {

  double *d=NULL;               /* Pointer to data array */
  size_t nbad=0;                /* inconsistency counter */
  size_t nnan = 0;              /* Number of nan values found */
  size_t ninf = 0;              /* Number of inf values found */
  size_t nqualincon = 0;        /* Number of inconsistent bad/qual */
  dim_t ndata;                  /* Number of data points */
  size_t bstride;               /* bol stride */
  size_t tstride;               /* time slice stride */
  smf_qual_t *qual=NULL;        /* Pointer to the QUALITY array */
  int nw;                       /* Number of worker threads */
  int iw;                       /* Thread index */
  SmfCheckQualityData *job_data = NULL;  /* Array of job descriptions */
  SmfCheckQualityData *pdata;   /* Pointer to next job description */
  size_t sampstep;              /* Number of samples per thread */

  if ( *status != SAI__OK ) return 0;

  /* Check for DATA */
  if( !data ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": NULL data supplied", status );
    return 0;
  }

  if( !data->pntr[0] ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": smfData does not contain a DATA component",
            status );
    return 0;
  }

  if( data->dtype != SMF__DOUBLE ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": smfData does not have type SMF__DOUBLE", status );
    return 0;
  }

  d = (double *) data->pntr[0];

  /* Check for QUALITY */
  qual = smf_select_qualpntr( data, NULL, status );

  if( !qual ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": NULL quality supplied", status);
    return 0;
  }


  /* Calculate data dimensions */
  smf_get_dims( data,  NULL, NULL, NULL, NULL, &ndata, &bstride,
                &tstride, status );

  if( *status == SAI__OK ) {

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

    /* Find how many samples to process in each worker thread. */
    sampstep = ndata/nw;
    if( sampstep == 0 ) sampstep = 1;

    /* Allocate job data for threads, and store the range of samples to be
       processed by each one. Ensure that the last thread picks up any
       left-over samples. */
    job_data = astCalloc( nw, sizeof(*job_data) );
    if( *status == SAI__OK ) {
      for( iw = 0; iw < nw; iw++ ) {
        pdata = job_data + iw;
        pdata->d1 = iw*sampstep;
        if( iw < nw - 1 ) {
          pdata->d2 = pdata->d1 + sampstep - 1;
        } else {
          pdata->d2 = ndata - 1 ;
        }

        /* Store other values common to all jobs. */
        pdata->qual = qual;
        pdata->d = d;
        pdata->showbad = showbad;
        pdata->bstride = bstride;
        pdata->tstride = tstride;

        /* Submit the job to the workforce. */
        thrAddJob( wf, 0, pdata, smf1_check_quality, 0, NULL, status );
      }

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

      /* Accumulate the results from all the worker threads. */
      for( iw = 0; iw < nw; iw++ ) {
        pdata = job_data + iw;
        nbad += pdata->nbad;
        nnan += pdata->nnan;
        ninf += pdata->ninf;
        nqualincon += pdata->nqualincon;
      }

      /* Free the job data. */
      job_data = astFree( job_data );
    }
  }

  if (nbad > 0) {
    msgOutiff( MSG__VERB, "", "Quality inconsistency found: %zu DATA/QUAL, %zu NaN, %zu Inf",
               status, nqualincon, nnan, ninf );
  }

  return nbad;
}
Beispiel #14
0
void smf_fit_qui( ThrWorkForce *wf, smfData *idata, smfData **odataq,
                  smfData **odatau, smfData **odatai, dim_t box, int ipolcrd,
                  int pasign, double paoff, double angrot, int north,
                  int *status ){

/* Local Variables: */
   AstFrameSet *wcs;        /* WCS FrameSet for current time slice */
   JCMTState *instate=NULL; /* Pointer to input JCMTState */
   JCMTState *outstate=NULL;/* Pointer to output JCMTState */
   const char *usesys;      /* Tracking system */
   dim_t *box_starts;       /* Array holding time slice at start of each box */
   dim_t box_size;          /* First time slice in box */
   dim_t intslice;          /* ntslice of idata */
   dim_t istart;            /* Input time index at start of fitting box */
   dim_t itime;             /* Time slice index */
   dim_t nbolo;             /* No. of bolometers */
   dim_t ncol;              /* No. of columns of bolometers in the array */
   dim_t ntime;             /* Time slices to check */
   dim_t ondata;            /* ndata of odata */
   dim_t ontslice;          /* ntslice of odata */
   double scale;            /* how much longer new samples are */
   int bstep;               /* Bolometer step between threads */
   int iworker;             /* Index of a worker thread */
   int nworker;             /* No. of worker threads */
   size_t i;                /* loop counter */
   smfData *indksquid=NULL; /* Pointer to input dksquid data */
   smfFitQUIJobData *job_data = NULL; /* Pointer to all job data */
   smfFitQUIJobData *pdata = NULL;/* Pointer to next job data */
   smfHead *hdr;            /* Pointer to data header this time slice */
   smf_qual_t *qua;         /* Input quality pointer */

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

/* Check supplied arguments. */
   if( !idata || !odataq || !odatau ) {
      *status = SAI__ERROR;
      errRep( "", "smf_fit_qui: NULL inputs supplied", status );
      return;
   }

   if( idata->ndims != 3 ) {
      *status = SAI__ERROR;
      errRep( "", "smf_fit_qui: idata is not 3-dimensional", status );
      return;
   }

/* Ensure the supplied smfData is time-ordered. So "bstride" is 1 and "tstride"
   is nbolo. */
   smf_dataOrder( wf, idata, 1, status );

/* Dimensions of input. */
   smf_get_dims( idata, NULL, &ncol, &nbolo, &intslice, NULL, NULL, NULL,
                 status );

/* Store a pointer to the quality array for the input smfData. */
   qua = smf_select_qualpntr( idata, NULL, status );;

/* Go through the first thousand POL_ANG values to see if they are in
   units of radians (new data) or arbitrary encoder units (old data).
   They are assumed to be in radians if no POL_ANG value is larger than
   20. This function can only handle new data. */
   hdr = idata->hdr;
   instate = hdr->allState;
   ntime = ( intslice > 1000 ) ? 1000 : intslice;
   for( itime = 0; itime < ntime; itime++,instate++ ) {
      if( instate->pol_ang > 20 ) {
         *status = SAI__ERROR;
         errRep( " ","   POL2 data contains POL_ANG values in encoder "
                 "units - connot fit to such old data.", status );
         break;
      }
   }

/* Find the input time slice at which each fitting box starts, and the
   length of the output time axis (in time-slices). */
   smf1_find_boxes( intslice, hdr->allState, box, &ontslice, &box_starts,
                    status );

/* Time axis scaling factor. */
   scale = (double) intslice / (double) ontslice;

/* First copy everything from input to output except for the data that needs
   to be downsampled */

/* We want to copy everything in the smfHead except for allState. So we
   make a copy of the allState pointer, and then set it to NULL in the
   header before the copy */
   if( idata->hdr ) {
     instate = idata->hdr->allState;
     idata->hdr->allState = NULL;
   }

/* Similarly, we want everything in the smfDa except for the dksquid. */
   if( idata->da ) {
     indksquid = idata->da->dksquid;
     idata->da->dksquid = NULL;
   }

/* Create copies, storing them in the supplied  output smfData
   structures. Omit the header for U and I, as we will be copying the Q
   header into them.  */
   *odataq = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA |
                                SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY,
                                0, 0, status );

   *odatau = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA |
                                SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY |
                                SMF__NOCREATE_HEAD, 0, 0, status );

   if( odatai ) {
      *odatai = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA |
                                SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY |
                                SMF__NOCREATE_HEAD, 0, 0, status );
   }

/* Restore values in idata now that we're done */
   if( instate ) idata->hdr->allState = instate;
   if( indksquid ) idata->da->dksquid = indksquid;

/* Store the required length for the output time axis. The time axis is
   axis two because the data is time-ordered. */
   (*odataq)->dims[ 2 ] = ontslice;
   (*odatau)->dims[ 2 ] = ontslice;
   if( odatai) (*odatai)->dims[ 2 ] = ontslice;

/* Get output dimensions - assumed to be the same for all three outputs. */
   ondata = ontslice*idata->dims[0]*idata->dims[1];

/* Allocate the data arrays for the outputs. */
   (*odataq)->pntr[0] = astCalloc( ondata, sizeof(double) );
   (*odatau)->pntr[0] = astCalloc( ondata, sizeof(double) );
   if( odatai ) (*odatai)->pntr[0] = astCalloc( ondata, sizeof(double) );

/* Allocate arrays for the output variances. */
   (*odataq)->pntr[1] = astCalloc( ondata, sizeof(double) );
   (*odatau)->pntr[1] = astCalloc( ondata, sizeof(double) );
   if( odatai ) (*odatai)->pntr[1] = astCalloc( ondata, sizeof(double) );

/* 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 ) {

/* Determine which bolometers are to be processed by which threads. */
      bstep = nbolo/nworker;
      if( bstep < 1 ) bstep = 1;

      for( iworker = 0; iworker < nworker; iworker++ ) {
         pdata = job_data + iworker;
         pdata->b1 = iworker*bstep;
         pdata->b2 = pdata->b1 + bstep - 1;
      }

/* Ensure that the last thread picks up any left-over bolometers */
      pdata->b2 = nbolo - 1;

/* Loop round all output time slices. */
      for( itime = 0; itime < ontslice; itime++ ) {

/* Get the index of the first input time slice that contributes to the
   current output time slice. */
         istart = box_starts[ itime ];

/* Get the number of input time slices that contribute to the output time
   slice. */
         box_size = box_starts[ itime + 1 ] - istart;

/* If we are using north as the reference direction, get the WCS FrameSet
   for the input time slice that is at the middle of the output time
   slice, and set its current Frame to the tracking frame. */
         if( north ) {
            smf_tslice_ast( idata, istart + box_size/2, 1, NO_FTS, status );
            wcs = idata->hdr->wcs;
            usesys = sc2ast_convert_system( (idata->hdr->allState)[0].tcs_tr_sys,
                                            status );
            astSetC( wcs, "System", usesys );
         } else {
            wcs = NULL;
         }

/* Now enter the parellel code in which each thread calculates the values
   for a range of bolometers at the current output slice. */
         for( iworker = 0; iworker < nworker; iworker++ ) {
            pdata = job_data + iworker;

            pdata->dat = ((double *) idata->pntr[0] ) + istart*nbolo;
            pdata->qua = qua + istart*nbolo;
            pdata->allstates = hdr->allState + istart;

            pdata->ipi = odatai ? ( (double*) (*odatai)->pntr[0] ) + itime*nbolo : NULL;
            pdata->ipq = ( (double*) (*odataq)->pntr[0] ) + itime*nbolo;
            pdata->ipu = ( (double*) (*odatau)->pntr[0] ) + itime*nbolo;
            pdata->ipv = ( (double*) (*odataq)->pntr[1] ) + itime*nbolo;

            pdata->nbolo = nbolo;
            pdata->ncol = ncol;
            pdata->box_size = box_size;
            pdata->ipolcrd = ipolcrd;
            pdata->pasign = pasign ? +1: -1;
            pdata->paoff = paoff;
            pdata->angrot = angrot;
            if( wcs ) {
               pdata->wcs = astCopy( wcs );
               astUnlock( pdata->wcs, 1 );
            } else {
               pdata->wcs = NULL;
            }

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

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

/* Lock and annul the AST objects used by each thread. */
         if( wcs ) {
            for( iworker = 0; iworker < nworker; iworker++ ) {
               pdata = job_data + iworker;
               astLock( pdata->wcs, 0 );
               pdata->wcs = astAnnul( pdata->wcs );
            }
         }
      }

/* Down-sample the smfHead -------------------------------------------------*/
      smfHead *hdr = (*odataq)->hdr;

      hdr->curframe = (dim_t) (((double) hdr->curframe + 0.5) / scale);
      hdr->nframes = ontslice;
      hdr->steptime *= scale;
      strcpy( hdr->dlabel, "Q" );
      strncpy( hdr->title, "POL-2 Stokes parameter Q", SMF__CHARLABEL );

/* Down-sample all the JCMTState values using nearest neighbours */
      instate = idata->hdr->allState;
      if( instate ) {

         hdr->allState = astCalloc( ontslice, sizeof(*instate) );
         outstate = hdr->allState;

         if( *status == SAI__OK ) {
            size_t frame;  /* index of nearest neighbour JCMTState */

            for( i=0; i<ontslice; i++ ) {
               frame = (size_t) round(((double) i + 0.5)*scale);
               memcpy( outstate + i, instate + frame, sizeof(*instate) );
            }

/* Then go back and properly down-sample the more important fast-changing
   fields like pointing. Note that since there are approximate values there
   already we need to explicitly re-initialize to 0. */

            RESAMPSTATE(instate, outstate, rts_end, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, smu_az_jig_x, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_az_jig_y, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_az_chop_x, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_az_chop_y, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_tr_jig_x, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_tr_jig_y, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_tr_chop_x, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_tr_chop_y, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, tcs_tai, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_airmass, intslice, ontslice, 0);

/* Second coordinates (Dec, El etc) can not wrap 0 to 360 so we do not need
   to test for those cases */
            RESAMPSTATE(instate, outstate, tcs_az_ang, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_az_ac1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_az_ac2, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_az_dc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_az_dc2, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_az_bc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_az_bc2, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, tcs_tr_ang, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_tr_ac1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_tr_ac2, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_tr_dc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_tr_dc2, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_tr_bc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_tr_bc2, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, tcs_en_dc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_en_dc2, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, tcs_dm_abs, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_dm_rel, intslice, ontslice, 0);

/* Wait for all the above smf_downsamp1 jobs to finish. */
            thrWait( wf, status );

         }
      }

/* Add a keyword to the Q header indicating the polarimetric reference
   direction. */
      smf_fits_updateL( (*odataq)->hdr, "POLNORTH", north,
                        north ? "Pol ref dir is tracking north" :
                                "Pol ref dir is focal plane Y", status );

/* Copy the Q header to the other outputs. */
      hdr = smf_deepcopy_smfHead( (*odataq)->hdr, status );
      (*odatau)->hdr = hdr;
      if( *status == SAI__OK ) {
         strcpy( hdr->dlabel, "U" );
         strncpy( hdr->title, "POL-2 Stokes parameter U", SMF__CHARLABEL );
      }

      if( odatai ) {
         hdr = smf_deepcopy_smfHead( (*odataq)->hdr, status );
         (*odatai)->hdr = hdr;
         if( *status == SAI__OK ) {
            strcpy( hdr->dlabel, "I" );
            strncpy( hdr->title, "POL-2 Stokes parameter I", SMF__CHARLABEL );
         }
      }
   }

/* Copy the variances from the Q smfData into the U and (and I) smfData. */
   if( *odataq && *status == SAI__OK ) {
      if( *odatau ) {
         memcpy( (*odatau)->pntr[1], (*odataq)->pntr[1], ondata*sizeof(double));
      }
      if( odatai && *odatai ) {
         memcpy( (*odatai)->pntr[1], (*odataq)->pntr[1], ondata*sizeof(double));
      }
   }

/* Ensure all smfDatas are time-ordered. */
   smf_dataOrder( wf, idata, 1, status );
   if( odatai && *odatai ) smf_dataOrder( wf, *odatai, 1, status );
   if( *odataq ) smf_dataOrder( wf, *odataq, 1, status );
   if( *odatau ) smf_dataOrder( wf, *odatau, 1, status );

/* Free resources. */
   job_data = astFree( job_data );
   box_starts = astFree( box_starts );
}