示例#1
0
void smf_filter_edge( smfFilter *filt, double f, double w, int lowpass,
                      int *status ) {
  size_t base;          /* Index to start of memory to be zero'd */
  size_t i;             /* Channel index */
  size_t iedge;         /* Index corresponding to the edge frequency */
  size_t hw;            /* The half-width of transition zone in channels */
  size_t len;           /* Length of memory to be zero'd */

  if( *status != SAI__OK ) return;

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

  if( filt->ndims != 1 ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": function only generates filters for time-series",
            status );
    return;
  }

  /* If filt->real is NULL, create a real identity filter first */
  if( !filt->real ) {
    smf_filter_ident( filt, 0, status );
    if( *status != SAI__OK ) return;
  }

  /* Calculate offset of edge frequency in filter */
  iedge = smf_get_findex( f, filt->df[0], filt->fdims[0], status );

  /* Get half the width of the transition zone in channels. */
  hw = 0.5*w/filt->df[0];

  /* Hard-edged... */
  if( hw <= 0 ) {

    /* Since we're zero'ing a continuous piece of memory, just use memset */

    if( lowpass ) { /* Zero frequencies beyond edge */
      base = iedge;
      len = filt->fdims[0] - iedge;
    } else {        /* Zero frequencies from 0 to edge */
      base = 0;
      len = iedge + 1;
    }

    memset( ((unsigned char *) filt->real) + base*sizeof(*filt->real), 0,
            len*sizeof(*filt->real) );
    if( filt->isComplex ) {
      memset( ((unsigned char *) filt->imag) + base*sizeof(*filt->imag), 0,
              len*sizeof(*filt->imag) );
    }

  /* Soft-edged */
  } else {

    size_t ilo = ( iedge > hw ) ? iedge - hw : 0;
    size_t ihi = ( iedge + hw < filt->fdims[0] ) ? iedge + hw : filt->fdims[0];

    /* For low pass, we can skip the channels below the transisition
       zone, which will already hold 1.0 */
    if( lowpass ) {

       for( i = ilo; i < filt->fdims[0]; i++ ) {
          if( i > ihi ) {
             (filt->real)[ i ] = 0.0;
          } else {
             (filt->real)[ i ] = 0.5*( 1.0 - sin(AST__DPIBY2*( (float) i - (float) iedge )/hw));
          }
       }

    /* For high pass, we can skip the channels above the transisition
       zone, which will already hold 1.0 */
    } else {
       for( i = 0; i < ihi; i++ ) {
          if( i < ilo ) {
             (filt->real)[ i ] = 0.0;
          } else {
             (filt->real)[ i ] = 0.5*( 1.0 + sin(AST__DPIBY2*( (float) i - (float) iedge )/hw));
          }
       }
    }

    /* For complex filters, copy the real part into the imaginary part. */
    if( filt->isComplex ) {
      memcpy( filt->imag, filt->real, filt->fdims[0]*sizeof(*filt->imag) );
    }
  }
}
示例#2
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 );
        }
    }
}
示例#3
0
void smf_fit_pspec( const double *pspec, dim_t nf, size_t box, double df,
                    double minfreq, double whitefreq, double maxfreq,
                    double *a, double *b, double *w, int *status ) {

  double A=VAL__BADD;    /* Amplitude of 1/f component */
  double B=VAL__BADD;    /* Exponent of 1/f component */
  int converged;         /* Has white noise level calc converged? */
  double fit[2];         /* fit coefficients */
  size_t i;              /* Loop counter */
  size_t i_flo;          /* Index of lowest frequency for calculating 1/f  */
  size_t i_whi;          /* Index of high-freq. edge for white-noise level */
  size_t i_wlo;          /* Index of low-freq. edge for white-noise level */
  size_t nbad;           /* Number of outliers in white noise calc */
  smf_qual_t *qual=NULL; /* Quality to go with pspec */
  double sigma;          /* Uncertainty in white noise level */
  size_t thisnbad;       /* Outliers in white noise calc, this iteration */
  double white=VAL__BADD;/* White noise level */
  double *x=NULL;        /* Independent axis for 1/f fit */
  double *y=NULL;        /* Dependent axis for 1/f fit */

  if (*status != SAI__OK) return;

  if( (df<=0) || (nf<1) ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": invalid frequency information supplied.", status );
    return;
  }

  /* Convert frequencies to array indices (but only consider above DC) */
  i_flo = smf_get_findex( minfreq, df, nf, status );
  i_wlo = smf_get_findex( whitefreq, df, nf, status );

  if( !maxfreq ) {
    /* If maxfreq is 0 set to Nyquist */
    i_whi = nf-1;
  } else {
    i_whi = smf_get_findex( maxfreq, df, nf, status );
  }

  if( i_flo+box*2 > nf ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": box too large compared to number of frequencies",
            status );
    return;
  }

  /* Create a local quality array to go with pspec */
  qual = astCalloc( nf, sizeof(*qual) );

  if( i_flo == 0 ) i_flo = 1;

  if( (*status==SAI__OK) && ((i_flo > i_wlo) || (i_wlo > i_whi)) ) {
    *status = SAI__ERROR;
    errRepf( "", FUNC_NAME
            ": must have i_maxfreq (%zu) > i_whitefreq (%zu) > "
             "i_minfreq (%zu)", status, i_whi, i_wlo, i_flo );
  }

  /* Calculate the white noise level */
  converged = 0;
  nbad = 0;

  while( (!converged) && (*status==SAI__OK) ) {
    double thresh;

    smf_stats1D( pspec+i_wlo, 1, i_whi-i_wlo+1, qual, 1, SMF__Q_SPIKE, &white,
                 &sigma, NULL, NULL, status );

    if( *status==SAI__OK ) {

      thresh = white + SMF__FPSPEC_WHITESNR*sigma;
      thisnbad = 0;

      for( i=i_wlo; i<=i_whi; i++ ) {
        if( pspec[i] > thresh ) {
          qual[i] = SMF__Q_SPIKE;
          thisnbad++;
        }
      }

      if( thisnbad==nbad ) {
        converged = 1;
      }

      nbad = thisnbad;
    }
  }

  /* Now identify and fit the 1/f part of the power spectrum. We use a
     rolling box and increase the frequency of its centre until the fraction
     of samples below some threshold above the white level is exceeded.
     We then fit a straight line to the logarithm of the x- and y-axes. */

  if( *status == SAI__OK ) {
    size_t nfit;
    size_t ngood = 0;
    size_t nused;
    double thresh = white + SMF__FPSPEC_KNEESNR*sigma;

    /* Initialize ngood -- skip the DC term in the FFT */
    for( i=i_flo; i<(i_flo+box); i++ ) {
      if( pspec[i] > thresh ) {
        ngood++;
      }
    }

    /* Continuing from next element, and go until we hit the end or we reach
       the threshold number of samples below the threshold SNR above
       the white noise level. */

    for( i=i_flo+1; (i<(nf-(box-1))) && (ngood >= SMF__FPSPEC_KNEETHRESH*box);
         i++ ) {
      /* If the previous first element was good remove it */
      if( pspec[i-1] >= thresh ) {
        ngood--;
      }

      /* If the new last element is good add it */
      if( pspec[i+box-1] >= thresh ) {
        ngood++;
      }
    }

    /* We will fit the power-law from elements i_flo to nfit-1. We then
       evaluate the fitted power law as

                       y = A * x^B

       where x is the index in the frequency array
             A = exp( fit[0] )
             B = fit[1]                                           */

    nfit = i+box/2-i_flo;

    msgOutiff( MSG__DEBUG, "", FUNC_NAME
               ": i_flow=%zu nfit=%zu i_wlo=%zu i_whi=%zu\n", status,
               i_flo, nfit-1, i_wlo, i_whi);


    /* If we've entered the white-noise band in order to fit the 1/f
       noise give up */
    if( i >= i_wlo ) {
      *status = SMF__BADFIT;
      errRep( "", FUNC_NAME
              ": unable to fit 1/f spectrum with provided frequency ranges",
              status);
      goto CLEANUP;
    }

    /* Now fit a straight line to the log of the two axes */
    x = astMalloc( (nfit-1)*sizeof(*x) );
    y = astMalloc( (nfit-1)*sizeof(*y) );

    if( *status == SAI__OK ) {
      for( i=0; i<(nfit-1); i++ ) {
        x[i] = log((i+i_flo)*df);
        y[i] = log(pspec[i+i_flo]);
      }
    }

    smf_fit_poly1d( 1, nfit-1, 0, x, y, NULL, NULL, fit, NULL, NULL, &nused,
                    status );

    if( *status == SAI__OK ) {

      /* if the exponent is positive the fit is either garbage, or
         else there just isn't very much low frequency noise compared
         to the white noise level. Set B to 0 but generate a warning
         message */

      if( fit[1] >= 0 ) {
        *status = SMF__BADFIT;
        errRep( "", FUNC_NAME
                ": fit to 1/f component encountered rising spectrum!",
                status);
        goto CLEANUP;
      } else {
        B = fit[1];
      }

      A = (exp(fit[0]));
    }
  }

  /* Return fit values */
  if( a ) *a = A;
  if( b ) *b = B;
  if( w ) *w = white;

 CLEANUP:

  qual = astFree( qual );
  x = astFree( x );
  y = astFree( y );
}