示例#1
0
void smurf_unmakecube( int *status ) {

/* Local Variables */
   AstFrame *tfrm = NULL;       /* Current Frame from input WCS */
   AstFrameSet *wcsin = NULL;   /* WCS Frameset for input cube */
   AstMapping *tmap = NULL;     /* Base->current Mapping from input WCS */
   AstSkyFrame *iskyfrm = NULL; /* SkyFrame from the input WCS Frameset */
   Grp *detgrp = NULL;        /* Group of detector names */
   Grp *igrp1 = NULL;         /* Group of input sky cube files */
   Grp *igrp2 = NULL;         /* Group of input template files */
   Grp *ogrp = NULL;          /* Group containing output file */
   NdgProvenance *oprov = NULL;/* Provenance for the output NDF */
   SkyCube *sky_cubes = NULL; /* Pointer to array of sky cube descriptions */
   SkyCube *skycube = NULL;   /* Pointer to next sky cube description */
   char pabuf[ 10 ];          /* Text buffer for parameter value */
   double params[ 4 ];        /* astResample parameters */
   int axes[ 2 ];             /* Indices of selected axes */
   int blank;                 /* Was a blank line just output? */
   int flag;                  /* Was the group expression flagged? */
   int ifile;                 /* Input file index */
   int interp = 0;            /* Pixel interpolation method */
   int iskycube;              /* Index of current sky cube */
   int nel;                   /* Number of elements in 3D array */
   int nparam = 0;            /* No. of parameters required for interpolation scheme */
   int ondf;                  /* Output time series NDF identifier */
   int outax[ 2 ];            /* Indices of corresponding output axes */
   int overlap;               /* Does time series overlap sky cube? */
   int sdim[3];               /* Array of significant pixel axes */
   int usedetpos;             /* Should the detpos array be used? */
   size_t ndet;               /* Number of detectors supplied for "DETECTORS" */
   size_t nskycube;           /* Number of supplied sky cubes */
   size_t outsize;            /* Number of files in output group */
   size_t size;               /* Number of files in input group */
   smfData *data = NULL;      /* Pointer to data struct */
   void *in_data = NULL;      /* Pointer to the input cube data array */
   void *out_data = NULL;     /* Pointer to the output cube data array */

#if defined(FPTRAP)
   feenableexcept(FE_DIVBYZERO|FE_INVALID|FE_OVERFLOW);
#endif

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

/* We have not yet displayed a blank line on stdout. */
   blank = 0;

/* Begin an AST context */
   astBegin;

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

/* Get a group holding the input sky cubes. */
   kpg1Rgndf( "IN", 0, 1, "", &igrp1, &nskycube, status );

/* Create an array of structures to hold information about each input sky
   cube. */
   sky_cubes = astMalloc( sizeof( SkyCube )*(size_t) nskycube );

/* Store a description of each sky cube. */
   if( sky_cubes ) {
      for( iskycube = 0; iskycube < nskycube; iskycube++ ) {
         skycube = sky_cubes + iskycube;

/* Get an NDF identifier for the next sky cube. */
         ndgNdfas( igrp1, iskycube + 1, "READ", &(skycube->indf), status );

/* Get the WCS FrameSet from the sky cube, together with its pixel index
   bounds. */
         kpg1Asget( skycube->indf, 3, 0, 1, 1, sdim, skycube->slbnd,
                    skycube->subnd, &wcsin, status );

/* Get the base->current Mapping from the input WCS FrameSet, and split it
   into two Mappings; one (iskymap) that maps the first 2 GRID axes into
   celestial sky coordinates, and one (ispecmap) that maps the third GRID
   axis into a spectral coordinate. Also extract the SpecFrame and
   SkyFrame from the current Frame. */
         tmap = astGetMapping( wcsin, AST__BASE, AST__CURRENT );
         tfrm = astGetFrame( wcsin, AST__CURRENT );

         axes[ 0 ] = 1;
         axes[ 1 ] = 2;
         astMapSplit( tmap, 2, axes, outax, &(skycube->iskymap) );
         iskyfrm = astPickAxes( tfrm, 2, outax, NULL );

         axes[ 0 ] = 3;
         astMapSplit( tmap, 1, axes, outax, &(skycube->ispecmap) );
         skycube->ispecfrm = astPickAxes( tfrm, 1, outax, NULL );

/* Create a copy of "iskyfrm" representing absolute coords rather than
   offsets. We assume the target is moving if the cube represents offsets. */
         skycube->abskyfrm = astCopy( iskyfrm );
         astClear( skycube->abskyfrm, "SkyRefIs" );
         skycube->moving = ( *status == SAI__OK &&
                             !strcmp( astGetC( iskyfrm, "SkyRefIs" ),
                                      "Origin" ) ) ? 1 : 0;

/* Invert the Mappings (for the convenience of smf_resamplecube), so
   that they go from current Frame to grid axis. */
         astInvert( skycube->ispecmap );
         astInvert( skycube->iskymap );

/* For efficiency, annul manually the unneeded AST objects created in
   this loop. */
         wcsin = astAnnul( wcsin );
         tmap = astAnnul( tmap );
         tfrm = astAnnul( tfrm );
         iskyfrm = astAnnul( iskyfrm );
      }
   }

/* See if the detector positions are to be read from the RECEPPOS array
   in the template NDFs. Otherwise, they are calculated on the basis of
   the FPLANEX/Y arrays. */
   parGet0l( "USEDETPOS", &usedetpos, status );

/* Get the detectors to use. If a null value is supplied, annull the
   error. Otherwise, make the group case insensitive. */
   detgrp = NULL;
   if( *status == SAI__OK ) {
      kpg1Gtgrp( "DETECTORS", &detgrp, &ndet, status );
      if( *status == PAR__NULL ) {
         errAnnul( status );
	 if (detgrp) {
	   grpDelet( &detgrp, status );
	 }
      } else {
         grpSetcs( detgrp, 0, status );
      }
   }

/* Get the pixel interpolation scheme to use. */
   parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC,"
             "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS",
             1, pabuf, 10, status );

   if( !strcmp( pabuf, "NEAREST" ) ) {
      interp = AST__NEAREST;
      nparam = 0;

   } else if( !strcmp( pabuf, "LINEAR" ) ) {
      interp = AST__LINEAR;
      nparam = 0;

   } else if( !strcmp( pabuf, "SINC" ) ) {
      interp = AST__SINC;
      nparam = 1;

   } else if( !strcmp( pabuf, "SINCSINC" ) ) {
      interp = AST__SINCSINC;
      nparam = 2;

   } else if( !strcmp( pabuf, "SINCCOS" ) ) {
      interp = AST__SINCCOS;
      nparam = 2;

   } else if( !strcmp( pabuf, "SINCGAUSS" ) ) {
      interp = AST__SINCGAUSS;
      nparam = 2;

   } else if( !strcmp( pabuf, "SOMB" ) ) {
      interp = AST__SOMB;
      nparam = 1;

   } else if( !strcmp( pabuf, "SOMBCOS" ) ) {
      interp = AST__SOMBCOS;
      nparam = 2;

   } else if( *status == SAI__OK ) {
      nparam = 0;
      *status = SAI__ERROR;
      msgSetc( "V", pabuf );
      errRep( "", "Support not available for INTERP = ^V (programming "
              "error)", status );
   }

/* Get an additional parameter vector if required. */
   if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status );

/* Get a group of reference time series files to use as templates for
   the output time series files.*/
   ndgAssoc( "REF", 1, &igrp2, &size, &flag, status );

/* Create a group holding the names of the corresponding output NDFs. */
   ndgCreat ( "OUT", igrp2, &ogrp, &outsize, &flag, status );
   if( outsize != size && *status == SAI__OK ) {
      *status = SAI__ERROR;
      msgSeti( "O", outsize );
      msgSeti( "I", size );
      errRep( "", "Numbers of input reference cubes (^I) and output "
              "cubes (^O) differ.", status );
   }

/* Loop round all the template time series files. */
   for( ifile = 1; ifile <= size && *status == SAI__OK; ifile++ ) {

/* Start a new NDF context. */
      ndfBegin();

/* Obtain information about the current template NDF, but do not map the
   arrays. */
      smf_open_file( igrp2, ifile, "READ", SMF__NOCREATE_DATA, &data, status );

/* Issue a suitable message and abort if anything went wrong. */
      if( *status != SAI__OK ) {
         errRep( FUNC_NAME, "Could not open input template file.", status );
         break;

      } else {
         if( data->file == NULL ) {
            *status = SAI__ERROR;
            errRep( FUNC_NAME, "No smfFile associated with smfData.",
                    status );
            break;

         } else if( data->hdr == NULL ) {
            *status = SAI__ERROR;
            errRep( FUNC_NAME, "No smfHead associated with smfData.",
                    status );
            break;

         }
      }

/* Report the name of the input template. */
      smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" );
      msgSeti( "THISFILE", ifile );
      msgSeti( "NUMFILES", size );
      msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE",
                status );

/* Create the output NDF by propagation from the input template NDF.
   Everything is copied except for the array components and any PROVENANCE
   extension. */
      ndgNdfpr( data->file->ndfid, "TITLE,LABEL,UNITS,AXIS,WCS,HISTORY,"
                "NOEXTENSION(PROVENANCE)", ogrp, ifile, &ondf, status );

/* Ensure the output NDF has a history component. */
      ndfHcre( ondf, status );

/* Get a pointer to the mapped output data array. Set all values bad. */
      ndfMap( ondf, "DATA", "_REAL", "WRITE/BAD", &out_data, &nel, status );

/* If the detector positions are to calculated on the basis of FPLANEX/Y
   rather than detpos, then free the detpos array in the templates smfHead
   structure. This will cause smf_tslice_ast to use the fplanex/y values. */
      if( !usedetpos && data->hdr->detpos ) {
         astFree( (double *) data->hdr->detpos );
         data->hdr->detpos = NULL;
      }

/* Get a pointer to a structure holding provenance information for the
   output time series. */
      oprov = ndgReadProv( ondf, "SMURF:UNMAKECUBE", status );

/* Record details of the template in the provenance structure for the
   output time series. */
      ndgPutProv( oprov, data->file->ndfid, NULL, 0, status );

/* Loop round all input sky cubes. */
      for( iskycube = 0; iskycube < nskycube; iskycube++ ) {
         skycube = sky_cubes + iskycube;

/* Record details of the input cube in the provenance extension of the
   output time series. */
         ndgPutProv( oprov, skycube->indf, NULL, 0, status );

/* See if the current time series overlaps the current sky cube. */
         smf_resampcube( data, skycube->abskyfrm,
                         skycube->iskymap, skycube->ispecfrm,
                         skycube->ispecmap, detgrp, skycube->moving,
                         skycube->slbnd, skycube->subnd, interp,
                         params, NULL, NULL, &overlap, status );

/* If not, pass on to the next sky cube. */
         if( overlap ) {

/* Report the name of the sky cube. */
            ndfMsg( "NDF", skycube->indf );
            msgOutif( MSG__NORM, " ", "   Re-sampling ^NDF", status );

/* Map the data array in the current sky cube. */
            ndfMap( skycube->indf, "DATA", "_REAL", "READ", &in_data, &nel,
                    status );

/* Resample the cube data into the output time series. */
            smf_resampcube( data, skycube->abskyfrm,
                            skycube->iskymap, skycube->ispecfrm,
                            skycube->ispecmap, detgrp, skycube->moving,
                            skycube->slbnd, skycube->subnd, interp,
                            params, in_data, out_data, &overlap, status );

/* Unmap the data array. */
            ndfUnmap( skycube->indf, "DATA", status );
         }
      }

/* Write the provenance structure to the output NDF, and then free it. */
      ndgWriteProv( oprov, ondf, 1, status );
      oprov =ndgFreeProv( oprov, status );

/* Close the input time series file. */
      if( data != NULL ) {
         smf_close_file( &data, status );
         data = NULL;
      }

/* End the NDF context. */
      ndfEnd( status );
   }

/* Close any input data file that is still open due to an early exit from
   the above loop. */
   if( data != NULL ) {
      smf_close_file( &data, status );
      data = NULL;
   }

/* Free remaining resources. */
   if( detgrp != NULL) grpDelet( &detgrp, status);
   if( igrp1 != NULL) grpDelet( &igrp1, status);
   if( igrp2 != NULL) grpDelet( &igrp2, status);
   if( ogrp != NULL) grpDelet( &ogrp, status);
   sky_cubes = astFree( sky_cubes );

/* End the NDF context. */
   ndfEnd( status );

/* End the tile's AST context. */
   astEnd;

/* Issue a status indication.*/
   if( *status == SAI__OK ) {
      msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status);
   } else {
      msgOutif(MSG__VERB," ",TASK_NAME " failed.", status);
   }
}
示例#2
0
void smurf_extinction( int * status ) {

  /* Local Variables */
  smfArray *bbms = NULL;     /* Bad bolometer masks */
  smfArray *darks = NULL;    /* Dark data */
  AstKeyMap *extpars = NULL; /* Tau relation keymap */
  Grp *fgrp = NULL;          /* Filtered group, no darks */
  smfArray *flatramps = NULL;/* Flatfield ramps */
  int has_been_sky_removed = 0;/* Data are sky-removed */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  size_t i;                  /* Loop counter */
  Grp *igrp = NULL;          /* Input group */
  AstKeyMap *keymap=NULL;    /* Keymap for storing parameters */
  smf_tausrc tausrc;         /* enum value of optical depth source */
  smf_extmeth extmeth;       /* Extinction correction method */
  char tausource[LEN__METHOD];  /* String for optical depth source */
  char method[LEN__METHOD];  /* String for extinction airmass method */
  smfData *odata = NULL;     /* Output data struct */
  Grp *ogrp = NULL;          /* Output group */
  size_t outsize;            /* Total number of NDF names in the output group */
  size_t size;               /* Number of files in input group */
  double tau = 0.0;          /* Zenith tau at this wavelength */
  ThrWorkForce *wf = NULL;   /* Pointer to a pool of worker threads */

  if (*status != SAI__OK) return;

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Read the input file */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );

  /* Filter out darks */
  smf_find_science( igrp, &fgrp, 0, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  size = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  if (size > 0) {
    /* Get output file(s) */
    kpg1Wgndf( "OUT", igrp, size, size, "More output files required...",
               &ogrp, &outsize, status );
  } else {
    msgOutif(MSG__NORM, " ","All supplied input frames were DARK,"
             " nothing to extinction correct", status );
  }

  /* Get group of pixel masks and read them into a smfArray */
  smf_request_mask( "BBM", &bbms, status );

  /* Read the tau relations from config file or group. We do not
     allow sub instrument overloading because these are all values
     based on filter name. */
  keymap = kpg1Config( "TAUREL", "$SMURF_DIR/smurf_extinction.def", NULL,
                       1, status );

  /* and we need to use the EXT entry */
  astMapGet0A( keymap, "EXT", &extpars );
  keymap = astAnnul( keymap );

  /* Get tau source */
  parChoic( "TAUSRC", "Auto",
            "Auto,CSOtau,CSOFit, Filtertau, WVMraw", 1,
            tausource, sizeof(tausource), status);

  /* Decide how the correction is to be applied - convert to flag */
  parChoic( "METHOD", "ADAPTIVE",
            "Adaptive,Quick,Full,", 1, method, sizeof(method), status);

  /* Place parameters into a keymap and extract values */
  if( *status == SAI__OK ) {
    keymap = astKeyMap( " " );
    if( astOK ) {
      astMapPut0C( keymap, "TAUSRC", tausource, NULL );
      astMapPut0C( keymap, "TAUMETHOD", method, NULL );
      smf_get_extpar( keymap, &tausrc, &extmeth, NULL, status );
    }
  }

  for (i=1; i<=size && ( *status == SAI__OK ); i++) {

    /* Flatfield - if necessary */
    smf_open_and_flatfield( igrp, ogrp, i, darks, flatramps, heateffmap,
                            &odata, status );

    if (*status != SAI__OK) {
      /* Error flatfielding: tell the user which file it was */
      msgSeti("I",i);
      errRep(TASK_NAME, "Unable to open the ^I th file", status);
    }

    /* Mask out bad pixels - mask data array not quality array */
    smf_apply_mask( odata, bbms, SMF__BBM_DATA, 0, status );

    /* Now check that the data are sky-subtracted */
    if ( !smf_history_check( odata, "smf_subtract_plane", status ) ) {

      /* Should we override remsky check? */
      parGet0l("HASSKYREM", &has_been_sky_removed, status);

      if ( !has_been_sky_removed && *status == SAI__OK ) {
        *status = SAI__ERROR;
        msgSeti("I",i);
        errRep("", "Input data from file ^I are not sky-subtracted", status);
      }
    }

    /* If status is OK, make decisions on source keywords the first
       time through. */
    if ( *status == SAI__OK && i == 1 ) {
      if (tausrc == SMF__TAUSRC_CSOTAU ||
          tausrc == SMF__TAUSRC_AUTO ||
          tausrc == SMF__TAUSRC_TAU) {
        double deftau;
        const char * param = NULL;
        smfHead *ohdr = odata->hdr;

        /* get default CSO tau -- this could be calculated from CSO fits */
        deftau = smf_calc_meantau( ohdr, status );

        /* Now ask for desired CSO tau */
        if ( tausrc == SMF__TAUSRC_CSOTAU || tausrc == SMF__TAUSRC_AUTO) {
          param = "CSOTAU";
        } else if (tausrc == SMF__TAUSRC_TAU) {
          param = "FILTERTAU";
          deftau = smf_cso2filt_tau( ohdr, deftau, extpars, status );
        }
        parGdr0d( param, deftau, 0.0,1.0, 1, &tau, status );
      } else if ( tausrc == SMF__TAUSRC_CSOFIT || tausrc == SMF__TAUSRC_WVMRAW ) {
        /* Defer a message until after extinction correction */
      } else {
        *status = SAI__ERROR;
        errRep("", "Unsupported opacity source. Possible programming error.",
               status);
      }
    }

    /* Apply extinction correction - note that a check is made to
       determine whether the data have already been extinction
       corrected */
    smf_correct_extinction( wf, odata, &tausrc, extmeth, extpars, tau, NULL, NULL, status );

    if ( tausrc == SMF__TAUSRC_WVMRAW ) {
      msgOutif(MSG__VERB," ", "Used Raw WVM data for extinction correction", status);
    } else if ( tausrc == SMF__TAUSRC_CSOFIT ) {
      msgOutif(MSG__VERB," ", "Used fit to CSO data for extinction correction", status);
    } else if ( tausrc == SMF__TAUSRC_CSOTAU ) {
      msgOutif(MSG__VERB," ", "Used an explicit CSO tau value for extinction correction", status);
    } else if ( tausrc == SMF__TAUSRC_TAU ) {
      msgOutif(MSG__VERB," ", "Used an explicit filter tau value for extinction correction", status);
    } else {
      if (*status == SAI__OK) {
        const char * taustr = smf_tausrc_str( tausrc, status );
        *status = SAI__ERROR;
        errRepf( "", "Unexpected opacity source used for extinction correction of %s."
                 " Possible programming error.", status, taustr );
      }
    }

    /* Set character labels */
    smf_set_clabels( "Extinction corrected",NULL, NULL, odata->hdr, status);
    smf_write_clabels( odata, status );

    /* Free resources for output data */
    smf_close_file( &odata, status );
  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && ogrp ) {
    grpList( "OUTFILES", 0, 0, NULL, ogrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

  /* Tidy up after ourselves: release the resources used by the grp routines  */
  if (darks) smf_close_related( &darks, status );
  if (bbms) smf_close_related( &bbms, status );
  if( flatramps ) smf_close_related( &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  grpDelet( &igrp, status);
  grpDelet( &ogrp, status);
  if( keymap ) keymap = astAnnul( keymap );
  if (extpars) extpars = astAnnul( extpars );
  ndfEnd( status );
}
示例#3
0
static int smf1_check_steps( const char *param, int first, dim_t nx,
                             double sizetol, int nold, int nnew,
                             smfStepFix *oldsteps, smfStepFix *newsteps,
                             int *status ){
/*
*  Name:
*     smf1_check_steps

*  Purpose:
*     Compare two sets of steps, issuing a warning for each step that
*     has changed significantly.

*  Invocation:
*     int smf1_check_steps( const char *param, int first, dim_t nx,
*                           double sizetol, int nold, int nnew,
*                           smfStepFix *oldsteps, smfStepFix *newsteps,
*                           int *status )

*  Arguments:
*     param = const char * (Given)
*        Name of parameter to use when asking the user whether to
*        continue to look for further changes.
*     first = int (Given)
*        The index of the first change to report.
*     nx = dim_t (Given)
*        The length of the first axis of the bolometer array.
*     sizetol = double (Given)
*        The minimum significant relative error in step size.
*     nold = int (Given)
*        The number of steps in the "oldsteps" array.
*     nnew = int (Given)
*        The number of steps in the "newsteps" array.
*     oldsteps = smfStepFix * (Given and Returned)
*        A pointer to the first element of an array of smfStepFix structures
*        describing the steps fixed in a previous invocation of this program.
*        The array is sorted on exit.
*     newsteps = smfStepFix * (Given and Returned)
*        A pointer to the first element of an array of smfStepFix structures
*        describing the steps fixed in the current invocation of this program.
*        The array is sorted on exit.
*     status = int * (Given and Returned)
*        Pointer to global status.

*  Description:
*     Firstly, an attempt it made to associated each old step with a
*     corresponding new step (i.e. a new step that occurs at the same
*     time and in the same bolometer as the old step). Warning messages
*     are issued about each old step for which no corresponding new step
*     can be found, or for which the corresponding new step has a
*     significantly different height to the old step. Finally, warnings
*     messages are also issued for each new step that has not been
*     associated with an old step.

*  Returned Value:
*     Zero if no significant differences were found. Non-zero otherwise.

*/

/* Local Variables: */
   double abslim;
   double dsize;
   double dsize_min;
   int *fnew;
   int *new_flags;
   int cont;
   int inew;
   int iold;
   int jnew;
   int match;
   int result;
   smfStepFix *pnew;
   smfStepFix *pold;

/* Initialise the returned value. */
   result = 0;

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

/* Find the absolute minimum significant difference between step sizes.
   This is "sizetol" times the clipped RMS step size in the new steps. */
   abslim = sizetol*smf1_get_rmssize( nnew, newsteps, status );
   msgSetd( "T", abslim );
   msgOut( "", "Ignoring differences in step size smaller than ^T",
           status );
   msgBlank( status );

/* Allocate memory to hold an array with one element for each new step.
   Each element holds zero if the new step has not yet been associated
   with any old step. Otherwise, it holds the one-based index of the
   associated old step. Initialise it to hold zero at every element. */
   new_flags = astCalloc( nnew, sizeof( *new_flags ) );
   if( *status == SAI__OK ) {

/* Loop round each old step. */
      pold = oldsteps;
      for( iold = 0; iold < nold; iold++,pold++ ) {

/* Ignore old steps with bolometer indicies greater than 5000 */
         if( pold->ibolo > 5000 ) continue;

/* Indicate no new step has yet been associated with the old step. */
         jnew = -1;
         dsize_min = VAL__MAXD;
         match = 0;

/* Loop round all new steps. */
         pnew = newsteps;
         fnew = new_flags;
         for( inew = 0; inew < nnew; inew++,pnew++,fnew++ ) {

/* Ignore this new step if it has already been associated with a previous
   old step. */
            if( ! *fnew ) {

/* See if the current new and old steps occur in the same bolometer and
   have overlapping time spans. If so they are considered to be at the
   same time. */
               if( pold->ibolo == pnew->ibolo &&
                   pold->start <= pnew->end &&
                   pold->end >= pnew->start ) {

/* Get the difference in step size between the old and new steps. */
                  dsize = fabs( pold->size - pnew->size );

/* Note the index of the matching new step that is most similar in height
   to the old step. */
                  if( dsize < dsize_min ) {
                     jnew = inew;
                     dsize_min = dsize;
                  }

/* If the old and new step heights are about the same then we associate
   the new step with the old step. Store the (one based) index of the
   corresponding old step. We do not need to check any more new steps,
   so break out of the new step loop. */
                  if( dsize < abslim ||
                      dsize < sizetol*fabs( 0.5*( pold->size + pnew->size ) ) ) {
                     match = 1;
                     *fnew = iold + 1;
                     break;
                  }
               }
            }
         }

/* If a new step was found at the same time and place as the old step, and
   with the same height, pass on to the next old step. */
         if( ! match ) {

/* If no new step was found at the same time and place as the old step, an old
   step has dissappeared. */
            if( jnew == -1 ) {

/* If the old step was of significant height, tell the user. */
               if( fabs( pold->size ) > abslim ){
                  result++;

                  if( result >= first ) {
                     msgSeti( "N", result );
                     msgSeti( "I", pold->id );
                     msgSetc( "W", pold->corr ? "secondary" : "primary" );
                     msgOut( "", "^N: An old ^W step (index ^I) is no longer found:", status );

                     msgSeti( "B", pold->ibolo );
                     msgSeti( "X", pold->ibolo % nx );
                     msgSeti( "Y", pold->ibolo / nx );
                     msgOut( "", "   Bolometer = ^B (^X,^Y)", status );

                     msgSeti( "S", pold->start );
                     msgSeti( "E", pold->end );
                     msgOut( "", "   Time slice range = ^S:^E", status );

                     msgSetd( "H", pold->size );
                     msgOut( "", "   Height = ^H", status );


                     parGet0l( param, &cont, status );
                     parCancl( param, status );
                     if( !cont || *status != SAI__OK ) break;
                     msgBlank( status );
                  }
               }

/* If one or more new step were found at the same time and place as the
   old step (but with a significantly different height), tell the user
   about the change in height. */
            } else {
               pnew = newsteps + jnew;
               result++;

               if( result >= first ) {
                  msgSeti( "I", result );
                  msgSetd( "O", pold->size );
                  msgSetd( "N", pnew->size );
                  msgOut( "", "^I: Step size changed from ^O to ^N:", status );

                  msgSeti( "I", pnew->id );
                  msgSetc( "W", pnew->corr ? "secondary" : "primary" );
                  msgOut( "", "   New index = ^I (^W)", status );

                  msgSeti( "I", pold->id );
                  msgSetc( "W", pold->corr ? "secondary" : "primary" );
                  msgOut( "", "   Old index = ^I (^W)", status );

                  msgSeti( "B", pold->ibolo );
                  msgSeti( "X", pold->ibolo % nx );
                  msgSeti( "Y", pold->ibolo / nx );
                  msgOut( "", "   Bolometer = ^B (^X,^Y)", status );

                  msgSeti( "S", pold->start );
                  msgSeti( "E", pold->end );
                  msgOut( "", "   Old time slice range = ^S:^E", status );

                  msgSeti( "S", pnew->start );
                  msgSeti( "E", pnew->end );
                  msgOut( "", "   New time slice range = ^S:^E", status );

                  parGet0l( param, &cont, status );
                  parCancl( param, status );
                  if( !cont || *status != SAI__OK ) break;
                  msgBlank( status );
               }
            }
         }
      }

/* We have now checked all old steps for matching new steps. If no
   significant change has yet been found, look for new steps that
   have not been associated with an old step. */
      pnew = newsteps;
      fnew = new_flags;
      for( inew = 0; inew < nnew && cont; inew++,pnew++,fnew++ ) {
         if( ! *fnew ) {

/* If the new step is off significant height, tell the user. */
            if( fabs( pnew->size ) > abslim ){
               result++;

               if( result >= first ) {
                  msgSeti( "N", result );
                  msgSeti( "I", pnew->id );
                  msgSetc( "W", pnew->corr ? "secondary" : "primary" );
                  msgOut( "", "^N: A new ^W step (index ^I) was found:", status );

                  msgSeti( "B", pnew->ibolo );
                  msgSeti( "X", pnew->ibolo % nx );
                  msgSeti( "Y", pnew->ibolo / nx );
                  msgOut( "", "   Bolometer = ^B (^X,^Y)", status );

                  msgSeti( "S", pnew->start );
                  msgSeti( "E", pnew->end );
                  msgOut( "", "   Time slice range = ^S:^E", status );

                  msgSetd( "H", pnew->size );
                  msgOut( "", "   Height = ^H", status );

                  parGet0l( param, &cont, status );
                  parCancl( param, status );
                  if( !cont || *status != SAI__OK ) break;
                  msgBlank( status );
               }
            }
         }
      }
   }

/* Free resources. */
   new_flags = astFree( new_flags );

/* Return the result. */
   return result;
}
示例#4
0
void smf_find_science(const Grp * ingrp, Grp **outgrp, int reverttodark,
                      Grp **darkgrp, Grp **flatgrp, int reducedark,
                      int calcflat, smf_dtype darktype, smfArray ** darks,
                      smfArray **fflats, AstKeyMap ** heateffmap,
                      double * meanstep, int * status ) {

  smfSortInfo *alldarks; /* array of sort structs for darks */
  smfSortInfo *allfflats; /* array of fast flat info */
  Grp * dgrp = NULL;  /* Internal dark group */
  double duration_darks = 0.0; /* total duration of all darks */
  double duration_sci = 0.0;  /* Duration of all science observations */
  size_t dkcount = 0; /* Dark counter */
  size_t ffcount = 0; /* Fast flat counter */
  Grp * fgrp = NULL;  /* Fast flat group */
  size_t i;           /* loop counter */
  smfData *infile = NULL; /* input file */
  size_t insize;     /* number of input files */
  size_t nsteps_dark = 0;    /* Total number of steps for darks */
  size_t nsteps_sci = 0;     /* Total number of steps for science */
  AstKeyMap * heatermap = NULL; /* Heater efficiency map */
  AstKeyMap * obsmap = NULL; /* Info from all observations */
  AstKeyMap * objmap = NULL; /* All the object names used */
  AstKeyMap * scimap = NULL; /* All non-flat obs indexed by unique key */
  Grp *ogrp = NULL;   /* local copy of output group */
  size_t sccount = 0; /* Number of accepted science files */
  struct timeval tv1;  /* Timer */
  struct timeval tv2;  /* Timer */

  if (meanstep) *meanstep = VAL__BADD;
  if (outgrp) *outgrp = NULL;
  if (darkgrp) *darkgrp = NULL;
  if (darks) *darks = NULL;
  if (fflats) *fflats = NULL;
  if (heateffmap) *heateffmap = NULL;

  if (*status != SAI__OK) return;

  /* Sanity check to make sure we return some information */
  if ( outgrp == NULL && darkgrp == NULL && darks == NULL && fflats == NULL) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME ": Must have some non-NULL arguments"
            " (possible programming error)", status);
    return;
  }

  /* Start a timer to see how long this takes */
  smf_timerinit( &tv1, &tv2, status );

  /* Create new group for output files */
  ogrp = smf_grp_new( ingrp, "Science", status );

  /* and a new group for darks */
  dgrp =  smf_grp_new( ingrp, "DarkFiles", status );

  /* and for fast flats */
  fgrp =  smf_grp_new( ingrp, "FastFlats", status );

  /* and also create a keymap for the observation description */
  obsmap = astKeyMap( "KeyError=1" );

  /* and an object map */
  objmap = astKeyMap( "KeyError=1" );

  /* This keymap contains the sequence counters for each related
     subarray/obsidss/heater/shutter combination and is used to decide
     if a bad flat is relevant */
  scimap = astKeyMap( "KeyError=1,KeyCase=0" );

  /* This keymap is used to contain relevant heater efficiency data */
  heatermap = astKeyMap( "KeyError=1,KeyCase=0" );

  /* Work out how many input files we have and allocate sufficient sorting
     space */
  insize = grpGrpsz( ingrp, status );
  alldarks = astCalloc( insize, sizeof(*alldarks) );
  allfflats = astCalloc( insize, sizeof(*allfflats) );

  /* check each file in turn */
  for (i = 1; i <= insize; i++) {
    int seqcount = 0;
    char keystr[100];  /* Key for scimap entry */

    /* open the file but just to get the header */
    smf_open_file( ingrp, i, "READ", SMF__NOCREATE_DATA, &infile, status );
    if (*status != SAI__OK) break;

    /* Fill in the keymap with observation details */
    smf_obsmap_fill( infile, obsmap, objmap, status );

    /* Find the heater efficiency map if required */
    if (*status == SAI__OK && heateffmap) {
      char arrayidstr[32];
      smf_fits_getS( infile->hdr, "ARRAYID", arrayidstr, sizeof(arrayidstr),
                     status );
      if (!astMapHasKey( heatermap, arrayidstr ) ) {
        smfData * heateff = NULL;
        dim_t nbolos = 0;
        smf_flat_params( infile, "RESIST", NULL, NULL, NULL, NULL, NULL,
                         NULL, NULL, NULL, NULL, NULL, &heateff, status );
        smf_get_dims( heateff, NULL, NULL, &nbolos, NULL, NULL, NULL, NULL,
                      status );
        if (heateff) astMapPut0P( heatermap, arrayidstr, heateff, NULL );
      }
    }

    /* Get the sequence counter for the file. We do not worry about
       duplicate sequence counters (at the moment) */
    smf_find_seqcount( infile->hdr, &seqcount, status );

    /* The key identifying this subarray/obsidss/heater/shutter combo */
    smf__calc_flatobskey( infile->hdr, keystr, sizeof(keystr), status );

    if (smf_isdark( infile, status )) {
      /* Store the sorting information */
      dkcount = smf__addto_sortinfo( infile, alldarks, i, dkcount, "Dark", status );
      smf__addto_durations( infile, &duration_darks, &nsteps_dark, status );
      astMapPutElemI( scimap, keystr, -1, seqcount );
    } else {
      /* compare sequence type with observation type and drop it (for now)
         if they differ */
      if ( infile->hdr->obstype == infile->hdr->seqtype ) {
        /* Sanity check the header for corruption. Compare RTS_NUM with SEQSTART
           and SEQEND. The first RTS_NUM must either be SEQSTART or else between
           SEQSTART and SEQEND (if someone has giving us a section) */
        int seqstart = 0;
        int seqend = 0;
        int firstnum = 0;
        JCMTState *tmpState = NULL;
        smf_getfitsi( infile->hdr, "SEQSTART", &seqstart, status );
        smf_getfitsi( infile->hdr, "SEQEND", &seqend, status );
        tmpState = infile->hdr->allState;

        if( tmpState ) {
          firstnum = (tmpState[0]).rts_num;
          smf_smfFile_msg( infile->file, "F", 1, "<unknown file>");
          if ( firstnum >= seqstart && firstnum <= seqend ) {
            /* store the file in the output group */
            ndgCpsup( ingrp, i, ogrp, status );
            msgOutif(MSG__DEBUG, " ", "Non-dark file: ^F",status);
            smf__addto_durations( infile, &duration_sci, &nsteps_sci, status );
            astMapPutElemI( scimap, keystr, -1, seqcount );
            sccount++;
          } else {
            msgOutif( MSG__QUIET, "",
                      "File ^F has a corrupt FITS header. Ignoring it.",
                      status );
          }
        } else {
          smf_smfFile_msg( infile->file, "F", 1, "<unknown file>");
          /* store the file in the output group */
          ndgCpsup( ingrp, i, ogrp, status );
          msgOutif( MSG__DEBUG, " ",
                    "File ^F lacks JCMTState: assuming it is non-dark",status);
          smf__addto_durations( infile, &duration_sci, &nsteps_sci, status );
          astMapPutElemI( scimap, keystr, -1, seqcount );
          sccount++;
        }

      } else if (infile->hdr->seqtype == SMF__TYP_FASTFLAT ) {
        ffcount = smf__addto_sortinfo( infile, allfflats, i, ffcount, "Fast flat", status );
      } else {
        smf_smfFile_msg( infile->file, "F", 1, "<unknown file>");
        msgOutif(MSG__DEBUG, " ", "Sequence type mismatch with observation type: ^F",status);
      }
    }

    /* close the file */
    smf_close_file( &infile, status );
  }

  /* Store output group in return variable or else free it */
  if (outgrp) {
    *outgrp = ogrp;
  } else {
    grpDelet( &ogrp, status );
  }

  /* process flatfields if necessary */
  if (ffcount > 0 && fflats ) {
    smfArray * array = NULL;

    /* sort flats into order */
    qsort( allfflats, ffcount, sizeof(*allfflats), smf_sort_bydouble);

    if (fflats) array = smf_create_smfArray( status );

    /* now open the flats and store them if requested */
    if (*status == SAI__OK && array && ffcount) {
      size_t start_ffcount = ffcount;
      AstKeyMap * flatmap = NULL;

      if (calcflat) {
        /* Use AgeUp so that we get the keys out in the sorted order
           that allfflats used */
        flatmap = astKeyMap( "KeyCase=0,KeyError=1,SortBy=AgeDown" );
      }

      /* Read each flatfield. Calculate a responsivity image and a flatfield
         solution. Store these in a keymap along with related information
         which is itself stored in a keymap indexed by a string made of
         OBSIDSS, reference heater value, shutter and subarray.
      */

      for (i = 0; i < start_ffcount; i++ ) {
        size_t ori_index =  (allfflats[i]).index;
        smfData * outfile = NULL;
        char keystr[100];
        AstKeyMap * infomap = astKeyMap( "KeyError=1" );
        int oplen = 0;
        char thisfile[MSG__SZMSG];
        int seqcount = 0;

        /* read filename from group */
        infile = NULL;
        smf_open_file( ingrp, ori_index, "READ", 0, &infile, status );
        if ( *status != SAI__OK ) {
          /* This should not happen because we have already opened
             the file. If it does happen we abort with error. */
          if (infile) smf_close_file( &infile, status );
          break;
        }

        /* Calculate the key for this observation */
        smf__calc_flatobskey( infile->hdr, keystr, sizeof(keystr), status );

        /* Get the file name for error messages */
        smf_smfFile_msg( infile->file, "F", 1, "<unknown file>" );
        msgLoad( "", "^F", thisfile, sizeof(thisfile), &oplen, status );

        /* And the sequence counter to link against science observations */
        smf_find_seqcount( infile->hdr, &seqcount, status );

        /* Prefill infomap */
        astMapPut0C( infomap, "FILENAME", thisfile, "");
        astMapPut0I( infomap, "SEQCOUNT", seqcount, "");

        /* Collapse it */
        if (*status == SAI__OK) {
          smf_flat_fastflat( infile, &outfile, status );
          if (*status == SMF__BADFLAT) {
            errFlush( status );

            if (calcflat) {
              /* Need to generate an outfile like smf_flat_fastflat
                 and one heater setting will force smf_flat_calcflat to fail */
              smf_flat_malloc( 1, infile, NULL, &outfile, status );
            } else {
              if (outfile) smf_close_file( &outfile, status );
              if (infile) smf_close_file( &infile, status );
              infomap = astAnnul( infomap );
              ffcount--;
              continue;
            }
          }
        }

        if (outfile && *status == SAI__OK) {
          smf_close_file( &infile, status );
          infile = outfile;

          if (calcflat) {
            size_t ngood = 0;
            smfData * curresp = NULL;
            int utdate;

            if (*status == SAI__OK) {
              ngood = smf_flat_calcflat( MSG__VERB, NULL, "RESIST",
                                         "FLATMETH", "FLATORDER", NULL, "RESPMASK",
                                         "FLATSNR", NULL, infile, &curresp, status );
              if (*status != SAI__OK) {
                /* if we failed to calculate a flatfield we continue but force the
                   flatfield to be completely bad. This will force the science data associated
                   with the flatfield to be correctly blanked. We do not annul though
                   if we have a SUBPAR error telling us that we have failed to define
                   our parameters properly. */
                if (*status != SUBPAR__NOPAR) errAnnul(status);

                /* parameters of flatfield */
                ngood = 0;

                /* Generate a blank flatfield and blank responsivity image */
                smf_flat_badflat( infile, &curresp, status );
              }

              /* Retrieve the UT date so we can decide whether to compare
                 flatfields */
              smf_getfitsi( infile->hdr, "UTDATE", &utdate, status );

              /* Store the responsivity data for later on and the processed
                 flatfield until we have vetted it */
              astMapPut0P( infomap, "CALCFLAT", infile, "" );
              astMapPut0P( infomap, "RESP", curresp, "" );
              astMapPut0I( infomap, "UTDATE", utdate, "" );
              astMapPut0I( infomap, "ISGOOD", 1, "" );
              astMapPut0I( infomap, "NGOOD", ngood, "" );
              astMapPut0I( infomap, "GRPINDEX", ori_index, "" );
              astMapPut0I( infomap, "SMFTYP", infile->hdr->obstype, "" );
              astMapPutElemA( flatmap, keystr, -1, infomap );

            }

          } else { /* if (calcflat) */
            /* Store the collapsed flatfield  - the processed flat is not stored here yet */
            smf_addto_smfArray( array, infile, status );

            /* Copy the group info */
            ndgCpsup( ingrp, ori_index, fgrp, status );

          }

        } /* if (outfile) */

        /* Annul the keymap (will be fine if it is has been stored in another keymap) */
        infomap = astAnnul( infomap );

      } /* End loop over flatfields */

      /* Now we have to loop over the related flatfields to disable
         bolometers that are not good and also decide whether we
         need to set status to bad. */
      if (*status == SAI__OK && calcflat ) {
        size_t nkeys = astMapSize( flatmap );
        for (i = 0; i < nkeys; i++ ) {
          const char *key = astMapKey( flatmap, i );
          int nf = 0;
          AstKeyMap ** kmaps = NULL;
          int nelem = astMapLength( flatmap, key );
          kmaps = astMalloc( sizeof(*kmaps) * nelem );
          astMapGet1A( flatmap, key, nelem, &nelem, kmaps );

          for ( nf = 0; nf < nelem && *status == SAI__OK; nf++ ) {
            AstKeyMap * infomap = kmaps[nf];
            int isgood = 0;

            astMapGet0I( infomap, "ISGOOD", &isgood );

            if (isgood) {
              /* The flatfield worked */
              size_t ngood = 0;
              int itemp;
              int utdate = 0;
              int ratioFlats = 0;

              /* Get the UT date - we do not compare flatfields after
                 the time we enabled heater tracking at each sequence. */
              astMapGet0I( infomap, "UTDATE", &utdate );

              /* Get the number of good bolometers at this point */
              astMapGet0I( infomap, "NGOOD", &itemp );
              ngood = itemp;

              /* Decide if we want to do the ratio test. We default to
                 not doing it between 20110901 and 20120827 which is
                 the period when we did mini-heater tracks before each
                 flat. ! indicates that we choose based on date. */
              if (*status == SAI__OK) {
                parGet0l( "FLATUSENEXT", &ratioFlats, status );
                if ( *status == PAR__NULL ) {
                  errAnnul( status );
                  if (utdate >= 20110901 || utdate <= 20120827 ) {
                    ratioFlats = 0;
                  } else {
                    ratioFlats = 1;
                  }
                }
              }

              /* Can we compare with the next flatfield? */
              if (ngood < SMF__MINSTATSAMP || !ratioFlats ) {
                /* no point doing all the ratio checking for this */
              } else if ( nelem - nf >= 2 ) {
                AstKeyMap * nextmap = kmaps[nf+1];
                const char *nextfname = NULL;
                const char *fname = NULL;
                smfData * curresp = NULL;
                smfData * nextresp = NULL;
                smfData * curflat = NULL;
                void *tmpvar = NULL;
                size_t bol = 0;
                smfData * ratio = NULL;
                double *in1 = NULL;
                double *in2 = NULL;
                double mean = VAL__BADD;
                size_t nbolo;
                double *out = NULL;
                double sigma = VAL__BADD;
                float clips[] = { 5.0, 5.0 }; /* 5.0 sigma iterative clip */
                size_t ngoodz = 0;

                astMapGet0C( nextmap, "FILENAME", &nextfname );
                astMapGet0C( infomap, "FILENAME", &fname );

                /* Retrieve the responsivity images from the keymap */
                astMapGet0P( infomap, "RESP", &tmpvar );
                curresp = tmpvar;
                astMapGet0P( nextmap, "RESP", &tmpvar );
                nextresp = tmpvar;
                astMapGet0P( infomap, "CALCFLAT", &tmpvar );
                curflat = tmpvar;

                nbolo = (curresp->dims)[0] * (curresp->dims)[1];

                /* get some memory for the ratio if we have not already.
                   We could get some memory once assuming each flat has the
                   same number of bolometers... */
                ratio = smf_deepcopy_smfData( curresp, 0, 0, 0, 0, status );
                if( *status == SAI__OK ) {

                  /* divide: smf_divide_smfData ? */
                  in1 = (curresp->pntr)[0];
                  in2 = (nextresp->pntr)[0];
                  out = (ratio->pntr)[0];

                  for (bol=0; bol<nbolo;bol++) {
                    if ( in1[bol] != VAL__BADD && in1[bol] != 0.0 &&
                         in2[bol] != VAL__BADD && in2[bol] != 0.0 ) {
                      out[bol] = in1[bol] / in2[bol];
                    } else {
                      out[bol] = VAL__BADD;
                    }
                  }
                }

                /* find some statistics */
                smf_clipped_stats1D( out, 2, clips, 1, nbolo, NULL, 0, 0, &mean,
                                     &sigma, NULL, 0, &ngoodz, status );

                if (*status == SMF__INSMP) {
                  errAnnul(status);
                  msgOutiff( MSG__QUIET, "",
                            "Flatfield ramp ratio of %s with %s had too few bolometers (%zu < %d).",
                             status, fname, nextfname, ngoodz, SMF__MINSTATSAMP );
                  ngood = ngoodz; /* Must be lower or equal to original ngood */

                } else if (*status == SAI__OK && mean != VAL__BADD && sigma != VAL__BADD && curflat->da) {
                  /* Now flag the flatfield as bad for bolometers that have changed
                     more than n%. We expect the variation to be 1+/-a small bit */
                  const double pmrange = 0.10;
                  double thrlo = 1.0 - pmrange;
                  double thrhi = 1.0 + pmrange;
                  size_t nmasked = 0;
                  double *flatcal = curflat->da->flatcal;

                  msgOutiff( MSG__DEBUG, "", "Flatfield fast ramp ratio mean = %g +/- %g (%zu bolometers)",
                             status, mean, sigma, ngood);

                  /* we can just set the first slice of the flatcal to bad. That should
                     be enough to disable the entire bolometer. We have just read these
                     data so they should be in ICD order. */
                  for (bol=0; bol<nbolo;bol++) {
                    if ( out[bol] != VAL__BADD &&
                         (out[bol] < thrlo || out[bol] > thrhi ) ) {
                      flatcal[bol] = VAL__BADD;
                      nmasked++;
                    } else if ( in1[bol] != VAL__BADD && in2[bol] == VAL__BADD ) {
                      /* A bolometer is bad next time but good now so we must set it bad now */
                      flatcal[bol] = VAL__BADD;
                      nmasked++;
                    }
                  }

                  if ( nmasked > 0 ) {
                    msgOutiff( MSG__NORM, "", "Masked %zu bolometers in %s from unstable flatfield",
                               status, nmasked, fname );

                    /* update ngood to take into account the masking */
                    ngood -= nmasked;
                  }

                }

                smf_close_file( &ratio, status );

              } /* End of flatfield responsivity comparison */

              /* if we only have a few bolometers left we now consider this
                 a bad flat unless it is actually an engineering measurement
                 where expect some configurations to give zero bolometers */
              if (ngood < SMF__MINSTATSAMP) {
                const char *fname = NULL;
                void * tmpvar = NULL;
                int smftyp = 0;
                smfData * curflat = NULL;
                astMapGet0I( infomap, "SMFTYP", &smftyp );
                astMapGet0C( infomap, "FILENAME", &fname );
                if (smftyp != SMF__TYP_NEP) {
                  msgOutiff( MSG__QUIET, "",
                            "Flatfield %s has %zu good bolometer%s.%s",
                             status, fname, ngood, (ngood == 1 ? "" : "s"),
                             ( ngood == 0 ? "" : " Keeping none.") );
                  isgood = 0;

                  /* Make sure that everything is blanked. */
                  if (ngood > 0) {
                    astMapGet0P( infomap, "CALCFLAT", &tmpvar );
                    curflat = tmpvar;
                    if (curflat && curflat->da) {
                      size_t bol;
                      size_t nbolo = (curflat->dims)[0] * (curflat->dims)[1];
                      double *flatcal = curflat->da->flatcal;
                      for (bol=0; bol<nbolo; bol++) {
                        /* Just need to set the first element to bad */
                        flatcal[bol] = VAL__BADD;
                      }
                    }
                  }

                } else {
                  msgOutiff( MSG__NORM, "",
                            "Flatfield ramp file %s has %zu good bolometer%s. Eng mode.",
                             status, fname, ngood, (ngood == 1 ? "" : "s") );
                }
              }

              /* We do not need the responsivity image again */
              {
                void *tmpvar = NULL;
                smfData * resp = NULL;
                astMapGet0P( infomap, "RESP", &tmpvar );
                resp = tmpvar;
                if (resp) smf_close_file( &resp, status );
                astMapRemove( infomap, "RESP" );
              }

            } /* End of isgood comparison */

            /* We are storing flats even if they failed. Let the downstream
               software worry about it */
            {
              int ori_index;
              smfData * flatfile = NULL;
              void *tmpvar = NULL;

              /* Store in the output group */
              astMapGet0I( infomap, "GRPINDEX", &ori_index );
              ndgCpsup( ingrp, ori_index, fgrp, status );

              /* And store in the smfArray */
              astMapGet0P( infomap, "CALCFLAT", &tmpvar );
              astMapRemove( infomap, "CALCFLAT" );
              flatfile = tmpvar;
              smf_addto_smfArray( array, flatfile, status );
            }

            /* Free the object as we go */
            kmaps[nf] = astAnnul( kmaps[nf] );
          } /* End of loop over this obsidss/subarray/heater */

          kmaps = astFree( kmaps );

        }
      }

      if (array->ndat) {
        if (fflats) *fflats = array;
      } else {
        smf_close_related(&array, status );
        if (fflats) *fflats = NULL;
      }
    }
  }

  /* no need to do any more if neither darks nor darkgrp are defined or we might
     be wanting to revert to darks. */
  if (dkcount > 0 && (darks || darkgrp || reverttodark ) ) {
    smfArray * array = NULL;

    /* sort darks into order */
    qsort( alldarks, dkcount, sizeof(*alldarks), smf_sort_bydouble);

    if (darks) array = smf_create_smfArray( status );

    /* now open the darks and store them if requested */
    if (*status == SAI__OK) {
      for (i = 0; i < dkcount; i++ ) {
        size_t ori_index =  (alldarks[i]).index;

         /* Store the entry in the output group */
        ndgCpsup( ingrp, ori_index, dgrp, status );

        if (darks) {

          /* read the value from the new group */
          smf_open_file( dgrp, i+1, "READ", 0, &infile, status );

          /* do we have to process these darks? */
          if (reducedark) {
            smfData *outfile = NULL;
            smf_reduce_dark( infile, darktype, &outfile, status );
            if (outfile) {
              smf_close_file( &infile, status );
              infile = outfile;
            }
          }

          smf_addto_smfArray( array, infile, status );
        }
      }
      if (darks) *darks = array;
    }
  }

  /* free memory */
  alldarks = astFree( alldarks );
  allfflats = astFree( allfflats );

  if( reverttodark && outgrp && (grpGrpsz(*outgrp,status)==0) &&
      (grpGrpsz(dgrp,status)>0) ) {
    /* If outgrp requested but no science observations were found, and
       dark observations were found, return darks in outgrp and set
       flatgrp and darkgrp to NULL. This is to handle cases where we
       want to process data taken in the dark like normal science
       data. To activate this behaviour set reverttodark */

    msgOutiff( MSG__NORM, "", "Treating the dark%s as science data",
               status, ( dkcount > 1 ? "s" : "" ) );

    *outgrp = dgrp;

    if( darkgrp ){
      *darkgrp = NULL;
    }

    if( flatgrp ) {
      *flatgrp = NULL;
    }

    grpDelet( &ogrp, status);
    grpDelet( &fgrp, status);

    if (meanstep && nsteps_dark > 0) *meanstep = duration_darks / nsteps_dark;

    /* Have to clear the darks smfArray as well */
    if (darks) smf_close_related( darks, status );

  } else {
    /* Store the output groups in the return variable or free it */
    if (darkgrp) {
      *darkgrp = dgrp;
    } else {
      grpDelet( &dgrp, status);
    }
    if (flatgrp) {
      *flatgrp = fgrp;
    } else {
      grpDelet( &fgrp, status);
    }

    if (meanstep && nsteps_sci > 0) *meanstep = duration_sci / nsteps_sci;
  }

  msgSeti( "ND", sccount );
  msgSeti( "DK", dkcount );
  msgSeti( "FF", ffcount );
  msgSeti( "TOT", insize );
  if ( insize == 1 ) {
    if (dkcount == 1) {
      msgOutif( MSG__VERB, " ", "Single input file was a dark",
                status);
    } else if (ffcount == 1) {
      msgOutif( MSG__VERB, " ", "Single input file was a fast flatfield",
                status);
    } else if (sccount == 1) {
      msgOutif( MSG__VERB, " ", "Single input file was accepted (observation type same as sequence type)",
                status);
    } else {
      msgOutif( MSG__VERB, " ", "Single input file was not accepted.",
                status);
    }

  } else {
    if (dkcount == 1) {
      msgSetc( "DKTXT", "was a dark");
    } else {
      msgSetc( "DKTXT", "were darks");
    }
    if (ffcount == 1) {
      msgSetc( "FFTXT", "was a fast flat");
    } else {
      msgSetc( "FFTXT", "were fast flats");
    }
    if (sccount == 1) {
      msgSetc( "NDTXT", "was science");
    } else {
      msgSetc( "NDTXT", "were science");
    }

    /* This might be a useful message */
    msgOutif( MSG__NORM, " ", "Out of ^TOT input files, ^DK ^DKTXT, ^FF ^FFTXT "
              "and ^ND ^NDTXT", status );
  }

  if (meanstep && *meanstep != VAL__BADD) {
    msgOutiff( MSG__VERB, "", "Mean step time for input files = %g sec",
             status, *meanstep );
  }

  /* Store the heater efficiency map */
  if (*status != SAI__OK) heatermap = smf_free_effmap( heatermap, status );
  if (heateffmap) *heateffmap = heatermap;

  /* Now report the details of the observation */
  smf_obsmap_report( MSG__NORM, obsmap, objmap, status );

  obsmap = astAnnul( obsmap );
  objmap = astAnnul( objmap );
  scimap = astAnnul( scimap );

  msgOutiff( SMF__TIMER_MSG, "",
             "Took %.3f s to find science observations",
             status, smf_timerupdate( &tv1, &tv2, status ) );

  return;
}
示例#5
0
void smurf_sc2pca( int *status ) {

  smfData *amplitudes=NULL;  /* Amplitudes of each component */
  smfArray *bbms=NULL;       /* Bad bolometer masks */
  smfData *components=NULL;  /* Components */
  smfArray *darks=NULL ;     /* Dark data */
  int ensureflat;            /* Flag for flatfielding data */
  smfData *data=NULL;        /* Pointer to input smfData */
  Grp *fgrp=NULL;            /* Filtered group, no darks */
  smfArray *flatramps=NULL;  /* Flatfield ramps */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  size_t i=0;                /* Counter, index */
  Grp *igrp=NULL;            /* Input group of files */
  Grp *outampgrp=NULL;       /* Output amplitude group of files */
  Grp *outcompgrp=NULL;      /* Output component group of files */
  size_t outampsize;         /* Total number of NDF names in ocompgrp */
  size_t outcompsize;        /* Total number of NDF names in ocompgrp */
  size_t size;               /* Number of files in input group */
  ThrWorkForce *wf=NULL;     /* Pointer to a pool of worker threads */

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Get input file(s) */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );

  /* Are we flatfielding? */
  parGet0l( "FLAT", &ensureflat, status );

  /* Filter out useful data (revert to darks if no science data) */
  smf_find_science( wf, igrp, &fgrp, 1, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  size = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  if( size > 0 ) {
    /* Get output file(s) */
    kpg1Wgndf( "OUTAMP", igrp, size, size, "More output files required...",
               &outampgrp, &outampsize, status );

    kpg1Wgndf( "OUTCOMP", igrp, size, size, "More output files required...",
               &outcompgrp, &outcompsize, status );
  } else {
    msgOutif(MSG__NORM, " ","All supplied input frames were DARK,"
       " nothing to flatfield", status );
  }

  /* Get group of bolometer masks and read them into a smfArray */
  smf_request_mask( wf, "BBM", &bbms, status );

  for( i=1; i<=size; i++ ) {

    if( *status != SAI__OK ) break;

    /* Load data, flatfielding and/or opening raw as double as necessary */
    smf_open_asdouble( wf, igrp, i, darks, flatramps, heateffmap, ensureflat, &data, status );

    /* Mask out bad bolometers */
    smf_apply_mask( wf, data, bbms, SMF__BBM_DATA|SMF__BBM_QUAL, 0, status );

    /* Sync quality with bad values */
    smf_update_quality( wf, data, 1, NULL, 0, 0.05, status );

    /* Calculate the PCA */
    smf_clean_pca( wf, data, 0, 0, 0, &components, &amplitudes, 0, 1, NULL,
                   status );

    /* Write out to the new files */
    smf_write_smfData( wf, amplitudes, NULL, NULL, outampgrp, i, 0, MSG__VERB,
                       0, status );
    smf_write_smfData( wf, components, NULL, NULL, outcompgrp, i, 0, MSG__VERB,
                       0, status );

    /* Free resources for output data */
    smf_close_file( wf, &data, status );
    smf_close_file( wf, &amplitudes, status );
    smf_close_file( wf, &components, status );
  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && outampgrp ) {
    grpList( "OUTAMPFILES", 0, 0, NULL, outampgrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }
  if( *status == SAI__OK && outcompgrp ) {
    grpList( "OUTCOMPFILES", 0, 0, NULL, outcompgrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

  /* Tidy up after ourselves: release the resources used by the grp routines  */
  if( igrp ) grpDelet( &igrp, status);
  if( outampgrp ) grpDelet( &outampgrp, status);
  if( outcompgrp ) grpDelet( &outcompgrp, status);
  if( darks ) smf_close_related( wf, &darks, status );
  if( bbms ) smf_close_related( wf, &bbms, status );
  if( flatramps ) smf_close_related( wf, &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  ndfEnd( status );
}
示例#6
0
void clumpinfo( int *status ) {
    /*
    *+
    *  Name:
    *     CLUMPINFO

    *  Purpose:
    *     Obtain information about one or more previously identified clumps.

    *  Language:
    *     C

    *  Type of Module:
    *     ADAM A-task

    *  Description:
    *     This application returns various items of information about a
    *     single clump, or a collection of clumps, previously identified
    *     using FINDCLUMPS or EXTRACTCLUMPS.

    *  Usage:
    *     clumpinfo ndf clumps quiet

    *  ADAM Parameters:
    *     CLUMPS = LITERAL (Read)
    *        Specifies the indices of the clumps to be included in the
    *        returned information. It can take any of the following values:
    *
    *        - "ALL" or "*" --  All clumps.
    *
    *        - "xx,yy,zz" -- A list of clump indices.
    *
    *        - "xx:yy" --  Clump indices between xx and yy inclusively.  When
    *        xx is omitted the range begins from one; when yy is omitted the
    *        range ends with the final clump index.
    *
    *        - Any reasonable combination of above values separated by
    *        commas.
    *     FLBND( ) = _DOUBLE (Write)
    *          The lower bounds of the bounding box enclosing the selected
    *          clumps in the current WCS Frame of the input NDF. Celestial axis
    *          values are always in units of radians, but spectral axis units
    *          will be in the spectral units used by the current WCS Frame.
    *     FUBND( ) = _DOUBLE (Write)
    *          The upper bounds of the bounding box enclosing the selected
    *          clumps. See parameter FLBND for more details.
    *     LBOUND( ) = _INTEGER (Write)
    *          The lower pixel bounds of bounding box enclosing the selected
    *          clumps.
    *     NCLUMPS = _INTEGER (Write)
    *        The total number of clumps descrriptions stored within the supplied
    *        NDF.
    *     NDF = NDF (Read)
    *        The NDF defining the previously identified clumps. This
    *        should contain a CUPID extension describing all the identified
    *        clumps, in the format produced by FINDCLUMPS or EXTRACTCLUMPS.
    *     QUIET = _LOGICAL (Read)
    *        If TRUE, then no information is written out to the screen,
    *        although the output parameters are still assigned values. [FALSE]
    *     UBOUND( ) = _INTEGER (Write)
    *          The upper pixel bounds of bounding box enclosing the selected
    *          clumps.

    *  Notes:
    *     - It is hoped to extend the range of information reported by this
    *     application as new requirements arise.

    *  Synopsis:
    *     void clumpinfo( int *status );

    *  Copyright:
    *     Copyright (C) 2007 Particle Physics & Astronomy Research Council.
    *     All Rights Reserved.

    *  Licence:
    *     This program is free software; you can redistribute it and/or
    *     modify it under the terms of the GNU General Public License as
    *     published by the Free Software Foundation; either version 2 of
    *     the License, or (at your option) any later version.
    *
    *     This program is distributed in the hope that it will be
    *     useful, but WITHOUT ANY WARRANTY; without even the implied
    *     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
    *     PURPOSE. See the GNU General Public License for more details.
    *
    *     You should have received a copy of the GNU General Public License
    *     along with this program; if not, write to the Free Software
    *     Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA
    *     02110-1301, USA

    *  Authors:
    *     DSB: David S. Berry
    *     {enter_new_authors_here}

    *  History:
    *     22-MAR-2007 (DSB):
    *        Original version.
    *     {enter_further_changes_here}

    *  Bugs:
    *     {note_any_bugs_here}

    *-
    */

    /* Local Variables: */
    AstFrame *cfrm;      /* Pointer to current WCS Frame */
    AstMapping *cmap;    /* Pointer to PIXEL->current Frame Mapping */
    CupidClumpInfo info; /* Structure holding returned information */
    Grp *grp = NULL;     /* GRP group holding input NDF name */
    HDSLoc *aloc = NULL; /* Locator for CLUMPS array in CUPID extension */
    HDSLoc *cloc = NULL; /* Locator for a single CLUMP structure */
    HDSLoc *xloc = NULL; /* Locator for CUPID extension */
    char *p;             /* Pointer into tmpstr string */
    char tmpstr[ 100 ];  /* Buffer for temporary strings */
    const char *dom;     /* Pointer to axis Domain name */
    double flbnd[ NDF__MXDIM ]; /* Lower bounds of WCS bounding box */
    double fubnd[ NDF__MXDIM ]; /* Upper bounds of WCS bounding box */
    double plbnd[ NDF__MXDIM ]; /* Lower bounds of PIXEL bounding box */
    double pubnd[ NDF__MXDIM ]; /* Upper bounds of PIXEL bounding box */
    int *clump_flags = NULL;  /* Flags indicating if each clump is to be used */
    int *clump_indices = NULL;/* List of indices of clumps to be used */
    int i;               /* Loop count */
    int iclump;          /* One-based clump index */
    int indf;            /* NDF identifier for input NDF */
    int ipix;            /* Index of PIXEL Frame */
    size_t nclumps;      /* No. of clump descriptions within the supplied NDF */
    int nuse;            /* Number of clumps to be used */
    int primary;         /* Value for locator primary flag */
    int quiet;           /* Supress screen output? */
    size_t size;         /* Number of values in group "*grp" */
    int there;           /* Does the enquired object exist? */

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

    /* Begin an AST context */
    astBegin;

    /* Start an NDF context */
    ndfBegin();



    /* Obtain the input NDF and get a locator for the array of clump
       descriptions within it.
       -----------------------------------------------------------------  */

    /* Get an identifier for the input NDF. We use NDG (via kpg1_Rgndf)
       instead of calling ndfAssoc directly since NDF/HDS has problems with
       file names containing spaces, which NDG does not have. */
    kpg1Rgndf( "NDF", 1, 1, "", &grp, &size, status );
    ndgNdfas( grp, 1, "READ", &indf, status );
    grpDelet( &grp, status );

    /* Check the NDF has a suitable CUPID extension containing an array of
       clump cut-outs. Get an HDS locator for the array. */
    ndfXstat( indf, "CUPID", &there, status );
    if( !there ) {
        if( *status == SAI__OK ) {
            *status = SAI__ERROR;
            ndfMsg( "NDF", indf );
            errRep( "", "The NDF \"^NDF\" does not contain a CUPID extension "
                    "such as created by FINDCLUMPS or EXTRACTCLUMPS.", status );
        }

    } else {
        ndfXloc( indf, "CUPID", "READ", &xloc, status );
        datThere( xloc, "CLUMPS", &there, status );
        if( !there ) {
            if( *status == SAI__OK ) {
                *status = SAI__ERROR;
                ndfMsg( "NDF", indf );
                errRep( "", "The CUPID extension within NDF \"^NDF\" does not "
                        "contain an array of clumps such as created by "
                        "FINDCLUMPS or EXTRACTCLUMPS.", status );
            }

        } else {
            datFind( xloc, "CLUMPS", &aloc, status );
            primary = 1;
            datPrmry( 1, &aloc, &primary, status );

        }
        datAnnul( &xloc, status );
    }

    /* Abort if we have no clumps array locator, or if an error occurred. */
    if( !aloc || *status != SAI__OK ) goto L999;



    /* Calculate the required clump information, and store it in the "info"
       structure.
       -----------------------------------------------------------------  */

    /* Indicate that the structure holding the returned information has not
       yet been initialised. */
    info.init = 0;

    /* Get the WCS FrameSet from the input NDF, and store a pointer to it in
       the "info" structure. */
    kpg1Gtwcs( indf, &(info.iwcs), status );

    /* Get the number of clumps defined within the input NDF. */
    datSize( aloc, &nclumps, status );

    /* Get the list of clump indices to iclude in the returned information. */
    clump_flags = astMalloc( sizeof( int )*nclumps );
    clump_indices = astMalloc( sizeof( int )*nclumps );
    kpg1Gilst( 1, (int) nclumps, (int) nclumps, "CLUMPS", clump_flags, clump_indices,
               &nuse, status );

    /* Loop round all clumps that are to be used. */
    for( i = 0; i < nuse && *status == SAI__OK; i++ ) {
        iclump = clump_indices[ i ];

        /* Get a locator for this clump. */
        datCell( aloc, 1, &iclump, &cloc, status );

        /* Update the returned information to include this clump. */
        cupidClumpInfo1( cloc, &info, status );

        /* Annul the clump structure locator. */
        datAnnul( &cloc, status );

    }



    /* Write out the information to the screen and to appropriate output
       parameters.
       -----------------------------------------------------------------  */

    /* See if screen output is required. */
    parGet0l( "QUIET", &quiet, status );
    if( !quiet ) msgBlank( status );

    /* The number of clumps defined within the input NDF... */
    parPut0i( "NCLUMPS", (int) nclumps, status );
    if( ! quiet ) {
        msgSeti( "NCLUMPS", (int) nclumps );
        msgOut( "", "   Total no. of clumps: ^NCLUMPS", status );
    }

    /* Pixel index bounding box... */
    parPut1i( "LBOUND", info.npix, info.lbnd, status );
    parPut1i( "UBOUND", info.npix, info.ubnd, status );

    if( !quiet ) {
        p = tmpstr + sprintf( tmpstr, "( " );
        for( i = 0; i < info.npix; i++) {
            p += sprintf( p, "%d:%d", info.lbnd[ i ], info.ubnd[ i ] );
            if( i < info.npix - 1 ) p += sprintf( p, ", " );
        }
        p += sprintf( p, " )" );

        msgSetc( "SECTION", tmpstr );
        msgOut( "", "   Pixel index bounding box: ^SECTION", status );
    }

    /* WCS bounding box (first convert the pixel index bounding box into WCS
       coords)... */
    cfrm = astGetFrame( info.iwcs, AST__CURRENT );

    kpg1Asffr( info.iwcs, "PIXEL", &ipix, status );
    cmap = astGetMapping( info.iwcs, ipix, AST__CURRENT );

    for( i = 0; i < info.npix; i++ ) {
        plbnd[ i ] = info.lbnd[ i ] - 1.0;
        pubnd[ i ] = info.ubnd[ i ];
    }

    for( i = 0; i < info.nwcs; i++ ) {
        astMapBox( cmap, plbnd, pubnd, 1, i + 1, flbnd + i, fubnd + i,
                   NULL, NULL);
    }

    astNorm( cfrm, flbnd );
    astNorm( cfrm, fubnd );

    parPut1d( "FLBND", info.nwcs,  flbnd, status );
    parPut1d( "FUBND", info.nwcs,  fubnd, status );

    if( !quiet ) {
        msgOut( "", "   WCS bounding box:", status );

        for( i = 0; i < info.nwcs; i++) {
            msgSetc( "L", astFormat( cfrm, i + 1, flbnd[ i ] ) );
            msgSetc( "U", astFormat( cfrm, i + 1, fubnd[ i ] ) );

            sprintf( tmpstr, "Domain(%d)", i + 1 );
            dom = astGetC( cfrm, tmpstr );
            if( dom && strcmp( dom, "SKY" ) ) {
                sprintf( tmpstr, "Unit(%d)", i + 1 );
                msgSetc( "UNT", astGetC( cfrm, tmpstr ) );
            } else {
                msgSetc( "UNT", "" );
            }

            sprintf( tmpstr, "Label(%d)", i + 1 );
            msgSetc( "LAB", astGetC( cfrm, tmpstr ) );

            msgOut( "", "        ^LAB: ^L -> ^U ^UNT", status );
        }
    }

    if( !quiet ) msgBlank( status );



    /* Tidy up.
       --------      */
L999:
    ;

    /* Free resources. */
    clump_flags = astFree( clump_flags );
    clump_indices = astFree( clump_indices );
    if( aloc ) datAnnul( &aloc, status );

    /* End the NDF context */
    ndfEnd( status );

    /* End the AST context */
    astEnd;

    /* If an error has occurred, issue another error report identifying the
       program which has failed (i.e. this one). */
    if( *status != SAI__OK ) {
        errRep( "CLUMPINFO_ERR", "CLUMPINFO: Failed to obtain information "
                "about one or more previously identified clumps.", status );
    }

}
示例#7
0
void smurf_unmakemap( int *status ) {

/* Local Variables */
   AstFrameSet *wcsin = NULL; /* WCS Frameset for input cube */
   AstMapping *skymap;        /* GRID->SkyFrame Mapping from input WCS */
   AstSkyFrame *abskyfrm;     /* Input SkyFrame (always absolute) */
   AstSkyFrame *skyfrm = NULL;/* SkyFrame from the input WCS Frameset */
   Grp *igrp1 = NULL;         /* Group of input sky files */
   Grp *igrp2 = NULL;         /* Group of input template files */
   Grp *igrpc = NULL;         /* Group of input COM files */
   Grp *igrpg = NULL;         /* Group of input GAI files */
   Grp *igrpq = NULL;         /* Group of input Q  sky files */
   Grp *igrpu = NULL;         /* Group of input U sky files */
   Grp *ogrp = NULL;          /* Group containing output file */
   HDSLoc *cloc = NULL;       /* HDS locator for component ipdata structure */
   HDSLoc *iploc = NULL;      /* HDS locator for top level ipdata structure */
   ThrWorkForce *wf = NULL;   /* Pointer to a pool of worker threads */
   char ipdata[ 200 ];        /* Text buffer for IPDATA value */
   char pabuf[ 10 ];          /* Text buffer for parameter value */
   char subarray[ 5 ];        /* Name of SCUBA-2 subarray (s8a,s8b,etc) */
   dim_t iel;                 /* Index of next element */
   dim_t ndata;               /* Number of elements in array */
   dim_t ntslice;             /* Number of time slices in array */
   double *ang_data = NULL;   /* Pointer to the FP orientation angles */
   double *angc_data = NULL;  /* Pointer to the instrumental ANGC data */
   double *c0_data = NULL;    /* Pointer to the instrumental C0 data */
   double *gai_data = NULL;   /* Pointer to the input GAI map */
   double *in_data = NULL;    /* Pointer to the input I sky map */
   double *inc_data = NULL;   /* Pointer to the input COM data */
   double *inq_data = NULL;   /* Pointer to the input Q sky map */
   double *inu_data = NULL;   /* Pointer to the input U sky map */
   double *outq_data = NULL;  /* Pointer to the Q time series data */
   double *outu_data = NULL;  /* Pointer to the U time series data */
   double *p0_data = NULL;    /* Pointer to the instrumental P0 data */
   double *p1_data = NULL;    /* Pointer to the instrumental P1 data */
   double *pd;                /* Pointer to next element */
   double *pq = NULL;         /* Pointer to next Q time series value */
   double *pu = NULL;         /* Pointer to next U time series value */
   double *qinst_data = NULL; /* Pointer to the instrumental Q data */
   double *uinst_data = NULL; /* Pointer to the instrumental U data */
   double amp16;              /* Amplitude of 16 Hz signal */
   double amp2;               /* Amplitude of 2 Hz signal */
   double amp4;               /* Amplitude of 4 Hz signal */
   double angrot;             /* Angle from focal plane X axis to fixed analyser */
   double paoff;              /* WPLATE value corresponding to POL_ANG=0.0 */
   double params[ 4 ];        /* astResample parameters */
   double phase16;            /* Phase of 16 Hz signal */
   double phase2;             /* Phase of 2 Hz signal */
   double phase4;             /* Phase of 4 Hz signal */
   double sigma;              /* Standard deviation of noise to add to output */
   int alignsys;              /* Align data in the map's system? */
   int cdims[ 3 ];            /* Common-mode NDF dimensions */
   int dims[ NDF__MXDIM ];    /* NDF dimensions */
   int flag;                  /* Was the group expression flagged? */
   int gdims[ 3 ];            /* GAI model NDF dimensions */
   int harmonic;              /* The requested harmonic */
   int ifile;                 /* Input file index */
   int indf;                  /* Input sky map NDF identifier */
   int indfangc;              /* IP ANGC values NDF identifier */
   int indfc0;                /* IP C0 values NDF identifier */
   int indfc;                 /* Input COM NDF identifier */
   int indfcs;                /* NDF identifier for matching section of COM */
   int indfg;                 /* Input GAI NDF identifier */
   int indfin;                /* Input template cube NDF identifier */
   int indfiq;                /* Input instrumental Q NDF */
   int indfiu;                /* Input instrumental U NDF */
   int indfout;               /* Output cube NDF identifier */
   int indfp0;                /* IP P0 values NDF identifier */
   int indfp1;                /* IP P1 values NDF identifier */
   int indfq;                 /* Input Q map NDF identifier */
   int indfu;                 /* Input U map NDF identifier */
   int interp = 0;            /* Pixel interpolation method */
   int lbndc[ 3 ];            /* Array of lower bounds of COM NDF */
   int moving;                /* Is the telescope base position changing? */
   int ndim;                  /* Number of pixel axes in NDF */
   int ndimc;                 /* Number of pixel axes in common-mode NDF */
   int ndimg;                 /* Number of pixel axes in GAI NDF */
   int nel;                   /* Number of elements in array */
   int nelc;                  /* Number of elements in COM array */
   int nelg;                  /* Number of elements in GAI array */
   int nelqu;                 /* Number of elements in Q or U array */
   int ngood;                 /* No. of good values in putput cube */
   int nparam = 0;            /* No. of parameters required for interpolation scheme */
   int pasign;                /* Indicates sense of POL_ANG value */
   int sdim[ 2 ];             /* Array of significant pixel axes */
   int slbnd[ 2 ];            /* Array of lower bounds of input map */
   int subnd[ 2 ];            /* Array of upper bounds of input map */
   int ubndc[ 3 ];            /* Array of upper bounds of COM NDF */
   size_t ncom;               /* Number of com files */
   size_t ngai;               /* Number of gai files */
   size_t nskymap;            /* Number of supplied sky cubes */
   size_t outsize;            /* Number of files in output group */
   size_t size;               /* Number of files in input group */
   smfData *odata = NULL;     /* Pointer to output data struct */

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

/* Begin an AST context */
   astBegin;

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

/* Find the number of cores/processors available and create a pool of
   threads of the same size. */
   wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

/* Get an identifier for the input NDF. We use NDG (via kpg1Rgndf)
   instead of calling ndfAssoc directly since NDF/HDS has problems with
   file names containing spaces, which NDG does not have. */
   kpg1Rgndf( "IN", 1, 1, "", &igrp1, &nskymap, status );
   ndgNdfas( igrp1, 1, "READ", &indf, status );

/* Map the data array in the input sky map. */
   ndfMap( indf, "DATA", "_DOUBLE", "READ", (void **) &in_data, &nel,
           status );

/* Get the WCS FrameSet from the sky map, together with its pixel index
   bounds. */
   kpg1Asget( indf, 2, 0, 1, 1, sdim, slbnd, subnd, &wcsin, status );

/* Check the current Frame is a SKY frame. */
   skyfrm = astGetFrame( wcsin, AST__CURRENT );
   if( !astIsASkyFrame( skyfrm ) && *status == SAI__OK ) {
      ndfMsg( "N", indf );
      *status = SAI__ERROR;
      errRep( " ", " Current Frame in ^N is not a SKY Frame.", status );
   }

/* Get a copy of the current frame that represents absolute coords rather
   than offsets. We assume the target is moving if the map represents
   offsets. */
   moving = ( *status == SAI__OK &&
              !strcmp( astGetC( skyfrm, "SkyRefIs" ), "Origin" ) ) ? 1 : 0;
   abskyfrm = astCopy( skyfrm );
   astClear( abskyfrm, "SkyRefIs" );

/* If the ALIGNSYS parameter is TRUE then we align the raw data with the
   map in the current system of the map, rather than the default ICRS. */
   parGet0l( "ALIGNSYS", &alignsys, status );
   if( alignsys ) astSetC( abskyfrm, "AlignSystem", astGetC( abskyfrm,
                                                             "System" ) );

/* Get the Mapping from the Sky Frame to grid axis in the iput map. */
   skymap = astGetMapping( wcsin, AST__CURRENT, AST__BASE );

/* Get the pixel interpolation scheme to use. */
   parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC,"
             "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS",
             1, pabuf, 10, status );

   if( !strcmp( pabuf, "NEAREST" ) ) {
      interp = AST__NEAREST;
      nparam = 0;

   } else if( !strcmp( pabuf, "LINEAR" ) ) {
      interp = AST__LINEAR;
      nparam = 0;

   } else if( !strcmp( pabuf, "SINC" ) ) {
      interp = AST__SINC;
      nparam = 1;

   } else if( !strcmp( pabuf, "SINCSINC" ) ) {
      interp = AST__SINCSINC;
      nparam = 2;

   } else if( !strcmp( pabuf, "SINCCOS" ) ) {
      interp = AST__SINCCOS;
      nparam = 2;

   } else if( !strcmp( pabuf, "SINCGAUSS" ) ) {
      interp = AST__SINCGAUSS;
      nparam = 2;

   } else if( !strcmp( pabuf, "SOMB" ) ) {
      interp = AST__SOMB;
      nparam = 1;

   } else if( !strcmp( pabuf, "SOMBCOS" ) ) {
      interp = AST__SOMBCOS;
      nparam = 2;

   } else if( *status == SAI__OK ) {
      nparam = 0;
      *status = SAI__ERROR;
      msgSetc( "V", pabuf );
      errRep( "", "Support not available for INTERP = ^V (programming "
              "error)", status );
   }

/* Get an additional parameter vector if required. */
   if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status );

/* Get a group of reference time series files to use as templates for
   the output time series files.*/
   ndgAssoc( "REF", 1, &igrp2, &size, &flag, status );

/* Get output file(s) */
   kpg1Wgndf( "OUT", igrp2, size, size, "More output files required...",
              &ogrp, &outsize, status );

/* Get he noise level to add to the output data. */
   parGet0d( "SIGMA", &sigma, status );

/* Get any Q and U input maps. */
   if( *status == SAI__OK ) {

      kpg1Rgndf( "QIN", 1, 1, "", &igrpq, &nskymap, status );
      ndgNdfas( igrpq, 1, "READ", &indfq, status );
      ndfMap( indfq, "DATA", "_DOUBLE", "READ", (void **) &inq_data, &nelqu,
              status );
      if( nelqu != nel && *status == SAI__OK ) {
         ndfMsg( "Q", indfq );
         *status = SAI__ERROR;
         errRep( "", "Q image '^Q' is not the same size as the I image.",
                 status );
      }

      kpg1Rgndf( "UIN", 1, 1, "", &igrpu, &nskymap, status );
      ndgNdfas( igrpu, 1, "READ", &indfu, status );
      ndfMap( indfu, "DATA", "_DOUBLE", "READ", (void **) &inu_data, &nelqu,
              status );
      if( nelqu != nel && *status == SAI__OK ) {
         ndfMsg( "U", indfu );
         *status = SAI__ERROR;
         errRep( "", "U image '^U' is not the same size as the I image.",
                 status );
      }

      if( *status == PAR__NULL ) {
         ndfAnnul( &indfq, status );
         ndfAnnul( &indfu, status );
         inq_data = NULL;
         inu_data = NULL;
         errAnnul( status );
      } else {
         parGet0d( "ANGROT", &angrot, status );
         parGet0d( "PAOFF", &paoff, status );
         parGet0l( "PASIGN", &pasign, status );
      }
   }

/* Get any common-mode files. */
   if( *status == SAI__OK ) {
      kpg1Rgndf( "COM", size, size, "", &igrpc, &ncom, status );
      if( *status == PAR__NULL ) {
         errAnnul( status );
         ncom = 0;
      }
   }

/* Get any GAI files. */
   if( *status == SAI__OK ) {
      kpg1Rgndf( "GAI", size, size, "", &igrpg, &ngai, status );
      if( *status == PAR__NULL ) {
         errAnnul( status );
         ngai = 0;
      }
   }

/* Get any instrumental polarisation files. */
   if( *status == SAI__OK ) {

/* First see if the user wants to use the "INSTQ/INSTU" scheme for
   specifying instrumental polarisation. */
      ndfAssoc( "INSTQ", "Read", &indfiq, status );
      ndfAssoc( "INSTU", "Read", &indfiu, status );

      if( *status == PAR__NULL ) {
         ndfAnnul( &indfiq, status );
         ndfAnnul( &indfiu, status );
         errAnnul( status );

      } else {
         msgOut( " ", "Using user-defined IP model", status );

         ndfDim( indfiq, 2, dims, &ndim, status );
         if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) {
            *status = SAI__ERROR;
            ndfMsg( "N", indfiq );
            errRep( " ", "Instrumental polarisation file ^N has bad "
                    "dimensions - should be 32x40.", status );
         } else {
            ndfMap( indfiq, "DATA", "_DOUBLE", "READ", (void **) &qinst_data,
                    &nel, status );
         }

         ndfDim( indfiu, 2, dims, &ndim, status );
         if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) {
            *status = SAI__ERROR;
            ndfMsg( "N", indfiu );
            errRep( " ", "Instrumental polarisation file ^N has bad "
                    "dimensions - should be 32x40.", status );
         } else {
            ndfMap( indfiu, "DATA", "_DOUBLE", "READ", (void **) &uinst_data,
                    &nel, status );
         }
      }

/* If not, see if the user wants to use the Johnstone/Kennedy instrumental
   polarisation model. The IPDATA parameter gives the path to an HDS
   container file contining NDFs holding the required IP data for all
   subarrays. */
      if( !qinst_data ) {
         parGet0c( "IPDATA", ipdata, sizeof(ipdata), status );
         if( *status == PAR__NULL ) {
            errAnnul( status );
         } else {
            msgOutf( " ", "Using Johnstone/Kennedy IP model in %s",
                     status, ipdata );
            hdsOpen( ipdata, "READ", &iploc, status );
         }
      }
   }

/* Loop round all the template time series files. */
   for( ifile = 1; ifile <= (int) size && *status == SAI__OK; ifile++ ) {

/* Start a new NDF context. */
      ndfBegin();

/* Create the output NDF by propagating everything from the input, except
   for quality and variance. */
      ndgNdfas( igrp2, ifile, "READ", &indfin, status );

      ndfMsg( "FILE", indfin );
      msgSeti( "THISFILE", ifile );
      msgSeti( "NUMFILES", size );
      msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE",
                status );

      ndgNdfpr( indfin, "DATA,HISTORY,LABEL,TITLE,WCS,UNITS,EXTENSION(*)",
                ogrp, ifile, &indfout, status );
      ndfAnnul( &indfin, status );
      ndfAnnul( &indfout, status );

/* We now re-open the output NDF and then modify its data values. */
      smf_open_file( wf, ogrp, ifile, "UPDATE", 0, &odata, status );

/* Issue a suitable message and abort if anything went wrong. */
      if( *status != SAI__OK ) {
         errRep( FUNC_NAME, "Could not open input template file.", status );
         break;

      } else {
         if( odata->file == NULL ) {
            *status = SAI__ERROR;
            errRep( FUNC_NAME, "No smfFile associated with smfData.",
                    status );
            break;

         } else if( odata->hdr == NULL ) {
            *status = SAI__ERROR;
            errRep( FUNC_NAME, "No smfHead associated with smfData.",
                    status );
            break;
         }
      }

/* Check the reference time series contains double precision values. */
      smf_dtype_check_fatal( odata, NULL, SMF__DOUBLE, status );

/* Get the total number of data elements, and the number of time slices. */
      smf_get_dims( odata, NULL, NULL, NULL, &ntslice, &ndata, NULL,
                    NULL, status );

/* Get the subarray name */
      smf_fits_getS( odata->hdr, "SUBARRAY", subarray, sizeof(subarray),
                     status );

/* If we are using the Johnstone/Kennedy IP model, open and map the
   relevant parameter NDFs within the IPDATA container file. */
      if( iploc ) {
         datFind( iploc, subarray, &cloc, status );

         ndfFind( cloc, "C0", &indfc0, status );
         ndfDim( indfc0, 2, dims, &ndim, status );
         if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) {
            *status = SAI__ERROR;
            ndfMsg( "N", indfc0 );
            errRep( " ", "Instrumental polarisation file ^N has bad "
                    "dimensions - should be 32x40.", status );
         } else {
            ndfMap( indfc0, "DATA", "_DOUBLE", "READ", (void **) &c0_data,
                    &nel, status );
         }

         ndfFind( cloc, "P0", &indfp0, status );
         ndfDim( indfp0, 2, dims, &ndim, status );
         if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) {
            *status = SAI__ERROR;
            ndfMsg( "N", indfp0 );
            errRep( " ", "Instrumental polarisation file ^N has bad "
                    "dimensions - should be 32x40.", status );
         } else {
            ndfMap( indfp0, "DATA", "_DOUBLE", "READ", (void **) &p0_data,
                    &nel, status );
         }

         ndfFind( cloc, "P1", &indfp1, status );
         ndfDim( indfp1, 2, dims, &ndim, status );
         if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) {
            *status = SAI__ERROR;
            ndfMsg( "N", indfp1 );
            errRep( " ", "Instrumental polarisation file ^N has bad "
                    "dimensions - should be 32x40.", status );
         } else {
            ndfMap( indfp1, "DATA", "_DOUBLE", "READ", (void **) &p1_data,
                    &nel, status );
         }

         ndfFind( cloc, "ANGC", &indfangc, status );
         ndfDim( indfangc, 2, dims, &ndim, status );
         if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) {
            *status = SAI__ERROR;
            ndfMsg( "N", indfangc );
            errRep( " ", "Instrumental polarisation file ^N has bad "
                    "dimensions - should be 32x40.", status );
         } else {
            ndfMap( indfangc, "DATA", "_DOUBLE", "READ", (void **) &angc_data,
                    &nel, status );
         }
      }

/* Open any COM file. */
      if( ncom ) {
         ndgNdfas( igrpc, ifile, "READ", &indfc, status );
         ndfDim( indfc, 3, cdims, &ndimc, status );

/* Check its dimensions. */
         if( *status == SAI__OK ) {
            if( ndimc == 1 ) {
               if( cdims[ 0 ] < (int) ntslice ) {
                  *status = SAI__ERROR;
                  ndfMsg( "C", indfc );
                  ndfMsg( "R", indfin );
                  msgSeti( "N", cdims[ 0 ] );
                  msgSeti( "M", ntslice );
                  errRep( " ", "Supplied COM file (^C) has ^N time-slices, but "
                          "the reference NDF (^R) has ^M time-slices.", status );
               } else {
                  ndfBound( indfc, 3, lbndc, ubndc, &ndimc, status );
                  ubndc[ 0 ] = lbndc[ 0 ] + ntslice - 1;
                  ndfSect( indfc, 1, lbndc, ubndc, &indfcs, status );
               }
            } else if( ndimc == 3 ) {
               if( cdims[ 0 ] != 1 || cdims[ 1 ] != 1 ) {
                  *status = SAI__ERROR;
                  ndfMsg( "C", indfc );
                  errRep( " ", "Supplied 3D COM file (^C) has bad "
                          "dimensions for axis 1 and/or 2 (should "
                          "both be 1 pixel long).", status );
               } else if( cdims[ 2 ] < (int) ntslice ) {
                  *status = SAI__ERROR;
                  ndfMsg( "C", indfc );
                  ndfMsg( "R", indfin );
                  msgSeti( "N", cdims[ 2 ] );
                  msgSeti( "M", ntslice );
                  errRep( " ", "Supplied COM file (^C) has ^N time-slices, but "
                          "the reference NDF (^R) has ^M time-slices.", status );
               } else {
                  ndfBound( indfc, 3, lbndc, ubndc, &ndimc, status );
                  ubndc[ 2 ] = lbndc[ 2 ] + ntslice - 1;
                  ndfSect( indfc, 3, lbndc, ubndc, &indfcs, status );
               }
            } else {
               *status = SAI__ERROR;
               ndfMsg( "C", indfc );
               msgSeti( "N", ndimc );
               errRep( " ", "Supplied COM file (^C) has ^N dimensions - "
                       "must be 3.", status );
            }
         }

         ndfMap( indfcs, "DATA", "_DOUBLE", "READ", (void **) &inc_data,
                 &nelc, status );

      } else {
         indfcs = NDF__NOID;
         inc_data = NULL;
      }

/* Open any GAI files. */
      if( ngai ) {
         ndgNdfas( igrpg, ifile, "READ", &indfg, status );
         ndfDim( indfg, 3, gdims, &ndimg, status );

/* Check its dimensions, and map it if OK. */
         if( *status == SAI__OK ) {
            if( ndimg != 2 ) {
               *status = SAI__ERROR;
               ndfMsg( "C", indfg );
               msgSeti( "N", ndimg );
               errRep( " ", "Supplied GAI file (^C) has ^N dimensions - "
                       "must be 2.", status );
            } else if( gdims[ 0 ] != 32 || gdims[ 1 ] != 40 ) {
               *status = SAI__ERROR;
               ndfMsg( "C", indfg );
               errRep( " ", "Supplied GAI file (^C) has has bad "
                       "dimensions - should be 32x40.", status );
            }
         }
         ndfMap( indfg, "DATA", "_DOUBLE", "READ", (void **) &gai_data,
                 &nelg, status );

      } else {
         indfg = NDF__NOID;
         gai_data = NULL;
      }

/* Fill the output with bad values. */
      if( *status == SAI__OK ) {
         pd = odata->pntr[ 0 ];
         for( iel = 0; iel < ndata; iel++ ) *(pd++) = VAL__BADD;
      }

/* Resample the sky map data into the output time series. */
      smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd,
                     interp, params, sigma, in_data, odata->pntr[ 0 ],
                     NULL, &ngood, status );

/* Add on any COM data. */
      smf_addcom( wf, odata, inc_data, status );

/* Issue a wrning if there is no good data in the output cube. */
      if( ngood == 0 ) msgOutif( MSG__NORM, " ", "   Output contains no "
                                 "good data values.", status );

/* If Q and U maps have been given, allocate room to hold resampled Q and
   U values, and fill them with bad values. */
      if( inq_data && inu_data ) {
         pq = outq_data = astMalloc( ndata*sizeof( *outq_data ) );
         pu = outu_data = astMalloc( ndata*sizeof( *outu_data ) );
         if( *status == SAI__OK ) {
            for( iel = 0; iel < ndata; iel++ ) {
               *(pu++) = VAL__BADD;
               *(pq++) = VAL__BADD;
            }
         }

/* Determine the harmonic to use. */
         parGet0i( "HARMONIC", &harmonic, status );

/* If producing the normal 8 Hz harmonic, get the amplitude and phase of a
   other signals to add onto the 8 Hz signal. */
         if( harmonic == 4 ) {
            parGet0d( "AMP2", &amp2, status );
            parGet0d( "PHASE2", &phase2, status );
            parGet0d( "AMP4", &amp4, status );
            parGet0d( "PHASE4", &phase4, status );
            parGet0d( "AMP16", &amp16, status );
            parGet0d( "PHASE16", &phase16, status );
         } else {
            amp2 = 0.0;
            phase2 = 0.0;
            amp4 = 0.0;
            phase4 = 0.0;
            amp16 = 0.0;
            phase16 = 0.0;
         }

/* Allocate room for an array to hold the angle from the Y pixel axis
   in the sky map to the focal plane Y axis, in radians, at each time
   slice. Positive rotation is in the same sense as rotation from
   focal plane X to focal plane Y. */
         ang_data = astMalloc( ntslice*sizeof( *ang_data ) );

/* Resample them both into 3D time series. These Q/U values arw with
  respect to the sky image Y axis. */
         smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd,
                        interp, params, sigma, inq_data, outq_data,
                        ang_data, &ngood, status );
         smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd,
                        interp, params, sigma, inu_data, outu_data,
                        NULL, &ngood, status );

/* Combine these time series with the main output time series so that the
   main output is analysed intensity. */
         smf_uncalc_iqu( wf, odata, odata->pntr[ 0 ], outq_data, outu_data,
                         ang_data, pasign, AST__DD2R*paoff, AST__DD2R*angrot,
                         amp2, AST__DD2R*phase2, amp4, AST__DD2R*phase4,
                         amp16, AST__DD2R*phase16, qinst_data, uinst_data,
                         c0_data, p0_data, p1_data, angc_data, harmonic,
                         status );

/* Release work space. */
         outq_data = astFree( outq_data );
         outu_data = astFree( outu_data );
         ang_data = astFree( ang_data );
      }

/* Factor in any GAI data. */
      smf_addgai( wf, odata, gai_data, status );

/* Close the output time series file. */
      smf_close_file( wf, &odata, status );

/* Close the IP data container for the current subarray, if it is open. */
      if( cloc ) datAnnul( &cloc, status );

/* End the NDF context. */
      ndfEnd( status );
   }

/* Close any input data file that is still open due to an early exit from
   the above loop. */
   if( odata != NULL ) {
      smf_close_file( wf, &odata, status );
      odata = NULL;
   }

/* Free remaining resources. */
   if( igrp1 != NULL) grpDelet( &igrp1, status);
   if( igrp2 != NULL) grpDelet( &igrp2, status);
   if( igrpq != NULL) grpDelet( &igrpq, status);
   if( igrpu != NULL) grpDelet( &igrpu, status);
   if( igrpc != NULL) grpDelet( &igrpc, status);
   if( igrpg != NULL) grpDelet( &igrpg, status);
   if( ogrp != NULL) grpDelet( &ogrp, status);
   if( iploc ) datAnnul( &iploc, status );

/* End the NDF context. */
   ndfEnd( status );

/* End the tile's AST context. */
   astEnd;

/* Issue a status indication.*/
   if( *status == SAI__OK ) {
      msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status);
   } else {
      msgOutif(MSG__VERB," ",TASK_NAME " failed.", status);
   }
}
示例#8
0
void smurf_calcqu( int *status ) {

    /* Local Variables: */
    AstFitsChan *fc;           /* Holds FITS headers for output NDFs */
    AstKeyMap *config;         /* Holds all cleaning parameters */
    AstKeyMap *dkpars;         /* Holds dark squid cleaning parameters */
    AstKeyMap *heateffmap = NULL; /* Heater efficiency data */
    AstKeyMap *sub_instruments;/* Indicates which instrument is being used */
    Grp *bgrp = NULL;          /* Group of base names for each chunk */
    Grp *igrp = NULL;          /* Group of input files */
    Grp *ogrp = NULL;          /* Group of output files  */
    Grp *sgrp = NULL;          /* Group of science files */
    HDSLoc *loci = NULL;       /* Locator for output I container file */
    HDSLoc *locq = NULL;       /* Locator for output Q container file */
    HDSLoc *locu = NULL;       /* Locator for output U container file */
    NdgProvenance *oprov;      /* Provenance to store in each output NDF */
    ThrWorkForce *wf;          /* Pointer to a pool of worker threads */
    char headval[ 81 ];        /* FITS header value */
    char ndfname[ 30 ];        /* Name of output Q or U NDF */
    char polcrd[ 81 ];         /* FITS 'POL_CRD' header value */
    char subarray[ 10 ];       /* Subarray name (e.g. "s4a", etc) */
    double angrot;             /* Angle from focal plane X axis to fixed analyser */
    double paoff;              /* WPLATE value corresponding to POL_ANG=0.0 */
    float arcerror;            /* Max acceptable error (arcsec) in one block */
    int block_end;             /* Index of last time slice in block */
    int block_start;           /* Index of first time slice in block */
    int dkclean;               /* Clean dark squids? */
    int fix;                   /* Fix the POL-2 triggering issue? */
    int iblock;                /* Index of current block */
    int iplace;                /* NDF placeholder for current block's I image */
    int ipolcrd;               /* Reference direction for waveplate angles */
    int maxsize;               /* Max no. of time slices in a block */
    int minsize;               /* Min no. of time slices in a block */
    int nc;                    /* Number of characters written to a string */
    int pasign;                /* +1 or -1 indicating sense of POL_ANG value */
    int qplace;                /* NDF placeholder for current block's Q image */
    int submean;               /* Subtract mean value from each time slice? */
    int uplace;                /* NDF placeholder for current block's U image */
    size_t ichunk;             /* Continuous chunk counter */
    size_t idx;                /* Subarray counter */
    size_t igroup;             /* Index for group of related input NDFs */
    size_t inidx;              /* Index into group of science input NDFs */
    size_t nchunk;             /* Number continuous chunks outside iter loop */
    size_t ssize;              /* Number of science files in input group */
    smfArray *concat = NULL;   /* Pointer to smfArray holding bolometer data */
    smfArray *darks = NULL;    /* dark frames */
    smfArray *dkarray = NULL;  /* Pointer to smfArray holding dark squid data */
    smfArray *flatramps = NULL;/* Flatfield ramps */
    smfData *data = NULL;      /* Concatenated data for one subarray */
    smfData *dkdata = NULL;    /* Concatenated dark squid data for one subarray */
    smfGroup *sgroup = NULL;   /* smfGroup corresponding to sgrp */

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

    /* Start new AST and NDF contexts. */
    astBegin;
    ndfBegin();

    /* Find the number of cores/processors available and create a work force
       holding the same number of threads. */
    wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

    /* Get a group of input files */
    kpg1Rgndf( "IN", 0, 1, "  Give more NDFs...", &igrp, &ssize, status );

    /* Get a group containing just the files holding science data. */
    smf_find_science( igrp, &sgrp, 0, NULL, NULL, 1, 1, SMF__NULL, &darks,
                      &flatramps, &heateffmap, NULL, status );

    /* Check we have at least once science file. */
    ssize = grpGrpsz( sgrp, status );
    if( ssize == 0 ) {
        msgOutif( MSG__NORM, " ", "All supplied input frames were DARK.",
                  status );
    } else {

        /* See if a correction should be made for the POL2 triggering issue. */
        parGet0l( "FIX", &fix, status );

        /* Create HDS container files to hold the output NDFs. */
        datCreat( "OUTQ", "CALCQU", 0, 0, status );
        datCreat( "OUTU", "CALCQU", 0, 0, status );

        /* Associate the locators with the structures. */
        datAssoc( "OUTQ", "WRITE", &locq, status );
        datAssoc( "OUTU", "WRITE", &locu, status );

        /* The I images are optional. */
        if( *status == SAI__OK ) {
            datCreat( "OUTI", "CALCQU", 0, 0, status );
            datAssoc( "OUTI", "WRITE", &loci, status );
            if( *status == PAR__NULL ) {
                errAnnul( status );
                loci = NULL;
            }
        }

        /* Group the input files so that all files within a single group have the
           same wavelength and belong to the same subscan of the same observation.
           Also identify chunks of data that are contiguous in time, and
           determine to which such chunk each group belongs. All this information
           is returned in a smfGroup structure ("*sgroup"). */
        smf_grp_related( sgrp, ssize, 1, 1, 0, NULL, NULL, NULL,
                         NULL, &sgroup, &bgrp, NULL, status );

        /* Obtain the number of contiguous chunks. */
        if( *status == SAI__OK ) {
            nchunk = sgroup->chunk[ sgroup->ngroups - 1 ] + 1;
        } else {
            nchunk = 0;
        }

        /* Indicate we have not yet found a value for the ARCERROR parameter. */
        arcerror = 0.0;

        /* Loop over all contiguous chunks */
        for( ichunk = 0; ichunk < nchunk && *status == SAI__OK; ichunk++ ) {

            /* Display the chunk number. */
            if( nchunk > 1 ) {
                msgOutiff( MSG__VERB, "", "   Doing chunk %d of %d.",
                           status, (int) ichunk + 1, (int) nchunk );
            }

            /* Concatenate the data within this contiguous chunk. This produces a
               smfArray ("concat") containing a smfData for each subarray present in
               the chunk. Each smfData holds the concatenated data for a single
               subarray. */
            smf_concat_smfGroup( wf, NULL, sgroup, darks, NULL, flatramps,
                                 heateffmap, ichunk, 1, 1, NULL, 0, NULL, NULL,
                                 0, 0, 0, &concat, NULL, status );

            /* Get a KeyMap holding values for the configuration parameters. Since we
               sorted by wavelength when calling smf_grp_related, we know that all
               smfDatas in the current smfArray (i.e. chunk) will relate to the same
               wavelength. Therefore we can use the same parameters for all smfDatas in
               the current smfArray. */
            sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE,
                                                  concat->sdata[ 0 ], NULL,
                                                  0, status );
            config = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_calcqu.def",
                                 sub_instruments, status );
            sub_instruments = astAnnul( sub_instruments );


            /* Get the CALCQU specific parameters. */
            if( !astMapGet0I( config, "PASIGN", &pasign ) ) pasign = 1;
            msgOutiff( MSG__VERB, "", "PASIGN=%d", status, pasign );
            if( !astMapGet0D( config, "PAOFF", &paoff ) ) paoff = 0.0;
            msgOutiff( MSG__VERB, "", "PAOFF=%g", status, paoff );
            if( !astMapGet0D( config, "ANGROT", &angrot ) ) angrot = 90.0;
            msgOutiff( MSG__VERB, "", "ANGROT=%g", status, angrot );
            if( !astMapGet0I( config, "SUBMEAN", &submean ) ) submean = 0;
            msgOutiff( MSG__VERB, "", "SUBMEAN=%d", status, submean );

            /* See if the dark squids should be cleaned. */
            if( !astMapGet0I( config, "DKCLEAN", &dkclean ) ) dkclean = 0;

            /* If required, clean the dark squids now since we might need to use them to
               clean the bolometer data. */
            if( dkclean ) {

                /* Create a smfArray containing the dark squid data. For each one, store
                   a pointer to the main header so that smf_clean_smfArray can get at the
                   JCMTState information. */
                dkarray = smf_create_smfArray( status );
                for( idx = 0; idx < concat->ndat && *status == SAI__OK; idx++ ) {
                    data = concat->sdata[ idx ];
                    if( data && data->da && data->da->dksquid ) {
                        dkdata = data->da->dksquid;
                        dkdata->hdr = data->hdr;
                        smf_addto_smfArray( dkarray, dkdata, status );
                    }
                }

                /* Clean the smfArray containing the dark squid data. Use the "CLEANDK.*"
                   parameters. */
                (void) astMapGet0A( config, "CLEANDK", &dkpars );
                smf_clean_smfArray( wf, dkarray, NULL, NULL, NULL, dkpars, status );
                dkpars = astAnnul( dkpars );

                /* Nullify the header pointers so that we don't accidentally close any. */
                if( dkarray ) {
                    for( idx = 0; idx < dkarray->ndat; idx++ ) {
                        dkdata = dkarray->sdata[ idx ];
                        dkdata->hdr = NULL;
                    }

                    /* Free the smfArray holding the dark squid data, but do not free the
                       individual smfDatas within it. */
                    dkarray->owndata = 0;
                    smf_close_related( &dkarray, status );
                }
            }

            /* Now clean the bolometer data */
            smf_clean_smfArray( wf, concat, NULL, NULL, NULL, config, status );

            /* If required correct for the POL2 triggering issue. */
            if( fix ) smf_fix_pol2( wf, concat, status );

            /* Loop round each sub-array in the current contiguous chunk of data. */
            for( idx = 0; idx < concat->ndat && *status == SAI__OK; idx++ ) {
                data = concat->sdata[ idx ];

                /* Find the name of the subarray that generated the data. */
                smf_find_subarray( data->hdr, subarray, sizeof(subarray), NULL,
                                   status );

                /* Display the sub-array. */
                if( concat->ndat > 1 ) {
                    msgOutiff( MSG__VERB, "", "   Doing sub-array %s.",
                               status, subarray );
                }

                /* Create an empty provenance structure. Each input NDF that contributes
                   to the current chunk and array will be added as an ancestor to this
                   structure, which will later be stored in each output NDF created for
                   this chunk and array. */
                oprov = ndgReadProv( NDF__NOID, "SMURF:CALCQU", status );

                /* Indicate we do not yet have any FITS headers for the output NDFs */
                fc = NULL;

                /* Indicate we do not yet know the coordinate reference frame for the
                   half-waveplate angle. */
                polcrd[ 0 ] = 0;
                ipolcrd = 0;

                /* Go through the smfGroup looking for groups of related input NDFs that
                   contribute to the current chunk. */
                for( igroup = 0; igroup < sgroup->ngroups; igroup++ ) {
                    if( sgroup->chunk[ igroup ] == ichunk ) {

                        /* Get the integer index into the GRP group (sgrp) that holds the input NDFs.
                           This index identifies the input NDF that provides the data for the current
                           chunk and subarray. This assumes that the order in which smf_concat_smfGroup
                           stores arrays in the "concat" smfArray matches the order in which
                           smf_grp_related stores arrays within the sgroup->subgroups. */
                        inidx = sgroup->subgroups[ igroup ][ idx ];

                        /* Add this input NDF as an ancestor into the output provenance structure. */
                        smf_accumulate_prov( NULL, sgrp, inidx, NDF__NOID,
                                             "SMURF:CALCQU", &oprov, status );

                        /* Merge the FITS headers from the current input NDF into the FitsChan
                           that holds headers for the output NDFs. The merging retains only those
                           headers which have the same value in all input NDFs. */
                        smf_fits_outhdr( data->hdr->fitshdr, &fc, status );

                        /* Get the polarimetry related FITS headers and check that all input NDFs
                           have usabie values. */
                        headval[ 0 ] = 0;
                        smf_getfitss( data->hdr, "POL_MODE", headval,
                                      sizeof(headval), status );
                        if( strcmp( headval, "CONSTANT" ) && *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            grpMsg( "N", sgrp, inidx );
                            errRep( " ", "Input NDF ^N does not contain "
                                    "polarimetry data obtained with a spinning "
                                    "half-waveplate.", status );
                        }

                        headval[ 0 ] = 0;
                        smf_getfitss( data->hdr, "POLWAVIN", headval,
                                      sizeof(headval), status );
                        if( strcmp( headval, "Y" ) && *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            grpMsg( "N", sgrp, inidx );
                            errRep( " ", "Half-waveplate was not in the beam for "
                                    "input NDF ^N.", status );
                        }

                        headval[ 0 ] = 0;
                        smf_getfitss( data->hdr, "POLANLIN", headval,
                                      sizeof(headval), status );
                        if( strcmp( headval, "Y" ) && *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            grpMsg( "N", sgrp, inidx );
                            errRep( " ", "Analyser was not in the beam for input "
                                    "NDF ^N.", status );
                        }

                        if( polcrd[ 0 ] ) {
                            headval[ 0 ] = 0;
                            smf_getfitss( data->hdr, "POL_CRD", headval,
                                          sizeof(headval), status );
                            if( strcmp( headval, polcrd ) && *status == SAI__OK ) {
                                *status = SAI__ERROR;
                                errRep( " ", "Input NDFs have differing values for "
                                        "FITS header 'POL_CRD'.", status );
                            }

                        } else {
                            smf_getfitss( data->hdr, "POL_CRD", polcrd,
                                          sizeof(polcrd), status );
                            if( !strcmp( polcrd, "FPLANE" ) ) {
                                ipolcrd = 0;
                            } else if( !strcmp( polcrd, "AZEL" ) ) {
                                ipolcrd = 1;
                            } else if( !strcmp( polcrd, "TRACKING" ) ) {
                                ipolcrd = 2;
                            } else if( *status == SAI__OK ) {
                                *status = SAI__ERROR;
                                msgSetc( "N", data->file->name );
                                msgSetc( "V", polcrd );
                                errRep( " ", "Input NDF ^N contains unknown value "
                                        "'^V' for FITS header 'POL_CRD'.", status );
                            }
                        }
                    }
                }

                /* If not already done, get the maximum spatial drift (in arc-seconds) that
                   can be tolerated whilst creating a single I/Q/U image. The default value is
                   half the makemap default pixel size. Also get limits on the number of
                   time slices in any block. */
                if( arcerror == 0.0 ) {
                    parDef0d( "ARCERROR", 0.5*smf_calc_telres( data->hdr->fitshdr,
                              status ), status );
                    parGet0r( "ARCERROR", &arcerror, status );

                    parGet0i( "MAXSIZE", &maxsize, status );
                    parGet0i( "MINSIZE", &minsize, status );
                    if( maxsize > 0 && maxsize < minsize && *status == SAI__OK ) {
                        *status = SAI__ERROR;
                        errRepf( "", "Value of parameter MAXSIZE (%d) is less "
                                 "than value of parameter MINSIZE (%d)", status,
                                 maxsize, minsize );
                    }
                }

                /* The algorithm that calculates I, Q and U assumes that all samples for a
                   single bolometer measure flux from the same point on the sky. Due to
                   sky rotation, this will not be the case - each bolometer will drift
                   slowly across the sky. However, since the drift is (or should be)
                   slow we can apply the I/Q/U algorithm to blocks of contiguous data over
                   which the bolometers do not move significantly. We produce a separate
                   I, Q and U image for each such block. The first block starts at the first
                   time slice in the smfData. */
                block_start = 0;

                /* Find the time slice at which the corner bolometers have moved
                   a critical distance (given by parameter ARCERROR) from their
                   positions at the start of the block. Then back off some time slices
                   to ensure that the block holds an integral number of half-waveplate
                   rotations. */
                block_end = smf_block_end( data, block_start, ipolcrd, arcerror,
                                           maxsize, status );

                /* Loop round creating I/Q/U images for each block. Count them. */
                iblock = 0;
                while( block_end >= 0 && *status == SAI__OK ) {

                    /* Skip very short blocks. */
                    if( block_end - block_start > minsize ) {

                        /* Display the start and end of the block. */
                        msgOutiff( MSG__VERB, "", "   Doing time slice block %d "
                                   "-> %d", status, (int) block_start,
                                   (int) block_end );

                        /* Get the name for the Q NDF for this block. Start of with "Q" followed by
                           the block index. */
                        iblock++;
                        nc = sprintf( ndfname, "Q%d", iblock );

                        /* Append the subarray name to the NDF name. */
                        nc += sprintf( ndfname + nc, "_%s", subarray );

                        /* Append the chunk index to the NDF name. */
                        nc += sprintf( ndfname + nc, "_%d", (int) ichunk );

                        /* Get NDF placeholder for the Q NDF. The NDFs are created inside the
                           output container file. */
                        ndfPlace( locq, ndfname, &qplace, status );

                        /* The name of the U NDF is the same except the initial "Q" is changed to
                           "U". */
                        ndfname[ 0 ] = 'U';
                        ndfPlace( locu, ndfname, &uplace, status );

                        /* The name of the I NDF is the same except the initial "Q" is changed to
                           "I". */
                        if( loci ) {
                            ndfname[ 0 ] = 'I';
                            ndfPlace( loci, ndfname, &iplace, status );
                        } else {
                            iplace = NDF__NOPL;
                        }

                        /* Store the chunk and block numbers as FITS headers. */
                        atlPtfti( fc, "POLCHUNK", (int) ichunk, "Chunk index used by CALCQU", status );
                        atlPtfti( fc, "POLBLOCK", iblock, "Block index used by CALCQU", status );

                        /* Create the Q and U images for the current block of time slices from
                           the subarray given by "idx", storing them in the output container
                           file. */
                        smf_calc_iqu( wf, data, block_start, block_end, ipolcrd,
                                      qplace, uplace, iplace, oprov, fc,
                                      pasign, AST__DD2R*paoff, AST__DD2R*angrot,
                                      submean, status );

                        /* Warn about short blocks. */
                    } else {
                        msgOutiff( MSG__VERB, "", "   Skipping short block of %d "
                                   "time slices (parameter MINSIZE=%d).", status,
                                   block_end - block_start - 1, minsize );
                    }

                    /* The next block starts at the first time slice following the previous
                       block. */
                    block_start = block_end + 1;

                    /* Find the time slice at which the corner bolometers have moved
                       a critical distance (given by parameter ARCERROR) from their
                       positions at the start of the block. Then back off some time slices
                       to ensure that the block holds an integral number of half-waveplate
                       rotations. This returns -1 if all time slices have been used. */
                    block_end = smf_block_end( data, block_start, ipolcrd,
                                               arcerror, maxsize, status );
                }

                /* Free resources */
                oprov = ndgFreeProv( oprov, status );
                fc = astAnnul( fc );
            }
            config = astAnnul( config );

            /* Close the smfArray. */
            smf_close_related( &concat, status );
        }

        /* Annul the locators for the output container files. */
        datAnnul( &locq, status );
        datAnnul( &locu, status );
        if( loci ) datAnnul( &loci, status );

        /* The parameter system hangs onto a primary locator for each container
           file, so cancel the parameters to annul these locators. */
        datCancl( "OUTQ", status );
        datCancl( "OUTU", status );
        datCancl( "OUTI", status );
    }

    /* Free resources. */
    smf_close_related( &darks, status );
    smf_close_related( &flatramps, status );

    if( igrp ) grpDelet( &igrp, status);
    if( sgrp ) grpDelet( &sgrp, status);
    if( bgrp ) grpDelet( &bgrp, status );
    if( ogrp ) grpDelet( &ogrp, status );
    if( sgroup ) smf_close_smfGroup( &sgroup, status );
    if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );

    /* End the NDF and AST contexts. */
    ndfEnd( status );
    astEnd;

    /* Issue a status indication.*/
    if( *status == SAI__OK ) {
        msgOutif( MSG__VERB, " ", "CALCQU succeeded.", status);
    } else {
        msgOutif( MSG__VERB, " ", "CALCQU failed.", status);
    }
}
示例#9
0
void smurf_unmakemap( int *status ) {

/* Local Variables */
   AstFrameSet *wcsin = NULL; /* WCS Frameset for input cube */
   AstMapping *skymap;        /* GRID->SkyFrame Mapping from input WCS */
   AstSkyFrame *abskyfrm;     /* Input SkyFrame (always absolute) */
   AstSkyFrame *skyfrm = NULL;/* SkyFrame from the input WCS Frameset */
   Grp *igrp1 = NULL;         /* Group of input sky files */
   Grp *igrp2 = NULL;         /* Group of input template files */
   Grp *igrpq = NULL;         /* Group of input Q  sky files */
   Grp *igrpu = NULL;         /* Group of input U sky files */
   Grp *ogrp = NULL;          /* Group containing output file */
   ThrWorkForce *wf = NULL;   /* Pointer to a pool of worker threads */
   char pabuf[ 10 ];          /* Text buffer for parameter value */
   dim_t iel;                 /* Index of next element */
   dim_t ndata;               /* Number of elements in array */
   dim_t ntslice;             /* Number of time slices in array */
   double *ang_data = NULL;   /* Pointer to the FP orientation angles */
   double *in_data = NULL;    /* Pointer to the input I sky map */
   double *inq_data = NULL;   /* Pointer to the input Q sky map */
   double *inu_data = NULL;   /* Pointer to the input U sky map */
   double *outq_data = NULL;  /* Pointer to the Q time series data */
   double *outu_data = NULL;  /* Pointer to the U time series data */
   double *pd;                /* Pointer to next element */
   double *pq = NULL;         /* Pointer to next Q time series value */
   double *pu = NULL;         /* Pointer to next U time series value */
   double angrot;             /* Angle from focal plane X axis to fixed analyser */
   double paoff;              /* WPLATE value corresponding to POL_ANG=0.0 */
   double params[ 4 ];        /* astResample parameters */
   double sigma;              /* Standard deviation of noise to add to output */
   int alignsys;              /* Align data in the map's system? */
   int flag;                  /* Was the group expression flagged? */
   int harmonic;              /* The requested harmonic */
   int ifile;                 /* Input file index */
   int indf;                  /* Input sky map NDF identifier */
   int indfin;                /* Input template cube NDF identifier */
   int indfout;               /* Output cube NDF identifier */
   int indfq;                 /* Input Q map NDF identifier */
   int indfu;                 /* Input U map NDF identifier */
   int interp = 0;            /* Pixel interpolation method */
   int moving;                /* Is the telescope base position changing? */
   int nel;                   /* Number of elements in array */
   int nelqu;                 /* Number of elements in Q or U array */
   int ngood;                 /* No. of good values in putput cube */
   int nparam = 0;            /* No. of parameters required for interpolation scheme */
   int pasign;                /* Indicates sense of POL_ANG value */
   int sdim[ 2 ];             /* Array of significant pixel axes */
   int slbnd[ 2 ];            /* Array of lower bounds of input map */
   int subnd[ 2 ];            /* Array of upper bounds of input map */
   size_t nskymap;            /* Number of supplied sky cubes */
   size_t outsize;            /* Number of files in output group */
   size_t size;               /* Number of files in input group */
   smfData *odata = NULL;     /* Pointer to output data struct */

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

/* Begin an AST context */
   astBegin;

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

/* Find the number of cores/processors available and create a pool of
   threads of the same size. */
   wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

/* Get an identifier for the input NDF. We use NDG (via kpg1Rgndf)
   instead of calling ndfAssoc directly since NDF/HDS has problems with
   file names containing spaces, which NDG does not have. */
   kpg1Rgndf( "IN", 1, 1, "", &igrp1, &nskymap, status );
   ndgNdfas( igrp1, 1, "READ", &indf, status );

/* Map the data array in the input sky map. */
   ndfMap( indf, "DATA", "_DOUBLE", "READ", (void **) &in_data, &nel,
           status );

/* Get the WCS FrameSet from the sky map, together with its pixel index
   bounds. */
   kpg1Asget( indf, 2, 0, 1, 1, sdim, slbnd, subnd, &wcsin, status );

/* Check the current Frame is a SKY frame. */
   skyfrm = astGetFrame( wcsin, AST__CURRENT );
   if( !astIsASkyFrame( skyfrm ) && *status == SAI__OK ) {
      ndfMsg( "N", indf );
      *status = SAI__ERROR;
      errRep( " ", " Current Frame in ^N is not a SKY Frame.", status );
   }

/* Get a copy of the current frame that represents absolute coords rather
   than offsets. We assume the target is moving if the map represents
   offsets. */
   moving = ( *status == SAI__OK &&
              !strcmp( astGetC( skyfrm, "SkyRefIs" ), "Origin" ) ) ? 1 : 0;
   abskyfrm = astCopy( skyfrm );
   astClear( abskyfrm, "SkyRefIs" );

/* If the ALIGNSYS parameter is TRUE then we align the raw data with the
   map in the current system of the map, rather than the default ICRS. */
   parGet0l( "ALIGNSYS", &alignsys, status );
   if( alignsys ) astSetC( abskyfrm, "AlignSystem", astGetC( abskyfrm,
                                                             "System" ) );

/* Get the Mapping from the Sky Frame to grid axis in the iput map. */
   skymap = astGetMapping( wcsin, AST__CURRENT, AST__BASE );

/* Get the pixel interpolation scheme to use. */
   parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC,"
             "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS",
             1, pabuf, 10, status );

   if( !strcmp( pabuf, "NEAREST" ) ) {
      interp = AST__NEAREST;
      nparam = 0;

   } else if( !strcmp( pabuf, "LINEAR" ) ) {
      interp = AST__LINEAR;
      nparam = 0;

   } else if( !strcmp( pabuf, "SINC" ) ) {
      interp = AST__SINC;
      nparam = 1;

   } else if( !strcmp( pabuf, "SINCSINC" ) ) {
      interp = AST__SINCSINC;
      nparam = 2;

   } else if( !strcmp( pabuf, "SINCCOS" ) ) {
      interp = AST__SINCCOS;
      nparam = 2;

   } else if( !strcmp( pabuf, "SINCGAUSS" ) ) {
      interp = AST__SINCGAUSS;
      nparam = 2;

   } else if( !strcmp( pabuf, "SOMB" ) ) {
      interp = AST__SOMB;
      nparam = 1;

   } else if( !strcmp( pabuf, "SOMBCOS" ) ) {
      interp = AST__SOMBCOS;
      nparam = 2;

   } else if( *status == SAI__OK ) {
      nparam = 0;
      *status = SAI__ERROR;
      msgSetc( "V", pabuf );
      errRep( "", "Support not available for INTERP = ^V (programming "
              "error)", status );
   }

/* Get an additional parameter vector if required. */
   if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status );

/* Get a group of reference time series files to use as templates for
   the output time series files.*/
   ndgAssoc( "REF", 1, &igrp2, &size, &flag, status );

/* Get output file(s) */
   kpg1Wgndf( "OUT", igrp2, size, size, "More output files required...",
              &ogrp, &outsize, status );

/* Get he noise level to add to the output data. */
   parGet0d( "SIGMA", &sigma, status );

/* Get any Q and U input maps. */
   if( *status == SAI__OK ) {

      kpg1Rgndf( "QIN", 1, 1, "", &igrpq, &nskymap, status );
      ndgNdfas( igrpq, 1, "READ", &indfq, status );
      ndfMap( indfq, "DATA", "_DOUBLE", "READ", (void **) &inq_data, &nelqu,
              status );
      if( nelqu != nel && *status == SAI__OK ) {
         ndfMsg( "Q", indfq );
         *status = SAI__ERROR;
         errRep( "", "Q image '^Q' is not the same size as the I image.",
                 status );
      }

      kpg1Rgndf( "UIN", 1, 1, "", &igrpu, &nskymap, status );
      ndgNdfas( igrpu, 1, "READ", &indfu, status );
      ndfMap( indfu, "DATA", "_DOUBLE", "READ", (void **) &inu_data, &nelqu,
              status );
      if( nelqu != nel && *status == SAI__OK ) {
         ndfMsg( "U", indfu );
         *status = SAI__ERROR;
         errRep( "", "U image '^U' is not the same size as the I image.",
                 status );
      }

      if( *status == PAR__NULL ) {
         ndfAnnul( &indfq, status );
         ndfAnnul( &indfu, status );
         inq_data = NULL;
         inu_data = NULL;
         errAnnul( status );
      } else {
         parGet0d( "ANGROT", &angrot, status );
         parGet0d( "PAOFF", &paoff, status );
         parGet0l( "PASIGN", &pasign, status );
      }
   }

/* Loop round all the template time series files. */
   for( ifile = 1; ifile <= (int) size && *status == SAI__OK; ifile++ ) {

/* Start a new NDF context. */
      ndfBegin();

/* Create the output NDF by propagating everything from the input, except
   for quality and variance. */
      ndgNdfas( igrp2, ifile, "READ", &indfin, status );

      ndfMsg( "FILE", indfin );
      msgSeti( "THISFILE", ifile );
      msgSeti( "NUMFILES", size );
      msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE",
                status );

      ndgNdfpr( indfin, "DATA,HISTORY,LABEL,TITLE,WCS,UNITS,EXTENSION(*)",
                ogrp, ifile, &indfout, status );
      ndfAnnul( &indfin, status );
      ndfAnnul( &indfout, status );

/* We now re-open the output NDF and then modify its data values. */
      smf_open_file( wf, ogrp, ifile, "UPDATE", 0, &odata, status );

/* Issue a suitable message and abort if anything went wrong. */
      if( *status != SAI__OK ) {
         errRep( FUNC_NAME, "Could not open input template file.", status );
         break;

      } else {
         if( odata->file == NULL ) {
            *status = SAI__ERROR;
            errRep( FUNC_NAME, "No smfFile associated with smfData.",
                    status );
            break;

         } else if( odata->hdr == NULL ) {
            *status = SAI__ERROR;
            errRep( FUNC_NAME, "No smfHead associated with smfData.",
                    status );
            break;
         }
      }

/* Check the reference time series contains double precision values. */
     smf_dtype_check_fatal( odata, NULL, SMF__DOUBLE, status );

/* Get the total number of data elements, and the number of time slices. */
     smf_get_dims( odata, NULL, NULL, NULL, &ntslice, &ndata, NULL,
                   NULL, status );

/* Fill the output with bad values. */
      if( *status == SAI__OK ) {
         pd = odata->pntr[ 0 ];
         for( iel = 0; iel < ndata; iel++ ) *(pd++) = VAL__BADD;
      }

/* Resample the sky map data into the output time series. */
      smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd,
                     interp, params, sigma, in_data, odata->pntr[ 0 ],
                     NULL, &ngood, status );

/* Issue a wrning if there is no good data in the output cube. */
      if( ngood == 0 ) msgOutif( MSG__NORM, " ", "   Output contains no "
                                 "good data values.", status );

/* If Q and U maps have been given, allocate room to hold resampled Q and
   U values, and fill them with bad values. */
      if( inq_data && inu_data ) {
         pq = outq_data = astMalloc( ndata*sizeof( *outq_data ) );
         pu = outu_data = astMalloc( ndata*sizeof( *outu_data ) );
         if( *status == SAI__OK ) {
            for( iel = 0; iel < ndata; iel++ ) {
               *(pu++) = VAL__BADD;
               *(pq++) = VAL__BADD;
            }
         }

/* Determine the harmonic to use. */
         parGet0i( "HARMONIC", &harmonic, status );

/* Allocate room for an array to hold the anti-clockwise angle from the
   focal plane Y axis to the Y pixel axis in the reference map, at each
   time slice. */
         ang_data = astMalloc( ntslice*sizeof( *ang_data ) );

/* Resample them both into 3D time series. */
         smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd,
                        interp, params, sigma, inq_data, outq_data,
                        ang_data, &ngood, status );
         smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd,
                        interp, params, sigma, inu_data, outu_data,
                        NULL, &ngood, status );

/* Combine these time series with the main output time series so that the
   main output is analysed intensity. */
         smf_uncalc_iqu( wf, odata, odata->pntr[ 0 ], outq_data, outu_data,
                         ang_data, pasign, AST__DD2R*paoff, AST__DD2R*angrot,
                         harmonic, status );

/* Release work space. */
         outq_data = astFree( outq_data );
         outu_data = astFree( outu_data );
         ang_data = astFree( ang_data );
      }

/* Close the output time series file. */
      smf_close_file( wf, &odata, status );

/* End the NDF context. */
      ndfEnd( status );
   }

/* Close any input data file that is still open due to an early exit from
   the above loop. */
   if( odata != NULL ) {
      smf_close_file( wf, &odata, status );
      odata = NULL;
   }

/* Free remaining resources. */
   if( igrp1 != NULL) grpDelet( &igrp1, status);
   if( igrp2 != NULL) grpDelet( &igrp2, status);
   if( igrpq != NULL) grpDelet( &igrpq, status);
   if( igrpu != NULL) grpDelet( &igrpu, status);
   if( ogrp != NULL) grpDelet( &ogrp, status);

/* End the NDF context. */
   ndfEnd( status );

/* End the tile's AST context. */
   astEnd;

/* Issue a status indication.*/
   if( *status == SAI__OK ) {
      msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status);
   } else {
      msgOutif(MSG__VERB," ",TASK_NAME " failed.", status);
   }
}
示例#10
0
void smurf_fts2_transcorr(int* status)
{
  if( *status != SAI__OK ) { return; }

  char filename[GRP__SZNAM+1]; /* Filename */
  char *pname        = NULL; /* Pointer to filename */
  int bolCount       = 0;    /* Number of bolometers */
  int bolIndex       = 0;    /* Bolometer index */
  int count;
  int dims[NDF__MXDIM];
  int debug          = 0;    /* If not debug, include dry component */
  int ftsExists      = 0;
  int index          = 0;
  int indf;                  /* NDF identifier for TAU file */
  int KERNELLENGTH   = 101;
  int nbolX          = 0;    /* Width of the source subarray */
  int nbolY          = 0;    /* Height of the source subarray */
  int ndfTau;
  int ndim;
  int nPWV           = 0;
  int nWN            = 0;
  int N              = 0;    /* Sample count */
  int i              = 0;    /* Index */
  int j              = 0;    /* Index */
  int k              = 0;    /* Index */
  int place;
  double AM          = 0.0;  /* Airmass at ZPD */
  double DELTAPWV    = 0.0;
  double PWV0        = 0.0;
  double PWV         = 0.0;  /* PWV at ZPD */
  double wnFact      = 0.0;  /* Wave number factor */
  double* inPntr     = NULL; /* Pointer to the input data */
  double* outPntr    = NULL; /* Pointer to the output data */
  double* wnScan     = NULL;
  double* wnTau      = NULL;
  double* TAtm       = NULL;
  double* TAtmNew    = NULL;
  double* GAUSSIANKERNEL = NULL;
  double* PWVARR     = NULL;
  double* PWVNEW     = NULL;
  double* TAUNEW     = NULL;
  double* TAUWET     = NULL;
  double* TMPARR     = NULL;
  Grp* inGrp         = NULL; /* Input group */
  Grp* outGrp        = NULL; /* Output group */
  Grp* tauGrp        = NULL; /* TAU WET group */
  HDSLoc* loc        = NULL; /* HDS location */
  size_t fIndex      = 0;    /* File loop counter */
  size_t inSize      = 0;    /* Size of the input group */
  size_t outSize     = 0;    /* Size of the output group */
  size_t tauSize     = 0;    /* Size of the tau group */
  smfData* inData    = NULL; /* Pointer to input data */
  smfData* outData   = NULL; /* Pointer to output data */
  smfData* tauData   = NULL; /* Pointer to tau dry data */
  void* TAU[]        = {NULL, NULL}; /* {dry, wet} */

  const double SQRT2PI  = 2.50662827463100050242;
  const double SQRT2LN2 = 1.17741002251547469101;

  // GET INPUT GROUP
  kpg1Rgndf("IN", 0, 1, "", &inGrp, &inSize, status);
  // GET OUTPUT GROUP
  kpg1Wgndf("OUT", outGrp, inSize, inSize,
            "Equal number of input and output files expected!",
            &outGrp, &outSize, status);
  // GET TAU GROUP
  kpg1Gtgrp("TAU", &tauGrp, &tauSize, status);

  parGet0l("DEBUG", &debug, status);

  ndfBegin();

  // ===========================================================================
  // GET TAU INFORMATION
  // ===========================================================================
  int dryOK = 0;
  int wetOK = 0;
  pname = filename;
  grpGet(tauGrp, 1, 1, &pname, sizeof(filename), status);
  ndgNdfas(tauGrp, 1, "READ", &indf, status );
  if (indf == NDF__NOID) {
    *status = SAI__ERROR;
    msgSetc("FILE", filename);
    errRep("", FUNC_NAME ": Could not locate file ^FILE", status);
    return;
  }
  ndfXstat(indf, "FTS2", &ftsExists, status);
  if(*status == SAI__OK && ftsExists) {
    ndfXloc(indf, "FTS2", "READ", &loc, status);
    if(*status == SAI__OK && loc != NULL) {
      // DRY COMPONENT
      ndfOpen(loc, "DRY", "READ", "UNKNOWN", &ndfTau, &place, status);
      if(*status == SAI__OK && ndfTau != NDF__NOID) {
        ndfDim(ndfTau, NDF__MXDIM, dims, &ndim, status);
        if(*status == SAI__OK && ndim == 1) {
          ndfMap(ndfTau, "DATA", "_DOUBLE", "READ", &TAU[0], &count, status);
          dryOK = 1;
        }
      }
      // WET COMPONENT
      ndfOpen(loc, "WET", "READ", "UNKNOWN", &ndfTau, &place, status);
      if(*status == SAI__OK && ndfTau != NDF__NOID) {
        ndfDim(ndfTau, NDF__MXDIM, dims, &ndim, status);
        if(*status == SAI__OK && ndim == 2) {
          ndfMap(ndfTau, "DATA", "_DOUBLE", "READ", &TAU[1], &count, status);
          wetOK = 1;
        }
      }
    }
  }
  if(loc) { datAnnul(&loc, status); }
  if(!(dryOK && wetOK)) {
    *status = SAI__ERROR;
    errRep("", FUNC_NAME ": Unable to obtain TAU values!", status);
    return;
  }

  smf_open_file(NULL, tauGrp, 1, "READ", 0, &tauData, status);
  smf_fits_getD(tauData->hdr, "PWV0", &PWV0, status);
  smf_fits_getD(tauData->hdr, "DELTAPWV", &DELTAPWV, status);
  if(*status != SAI__OK) {
    *status = SAI__ERROR;
    errRep("", FUNC_NAME ": Unable to obtain PWV value(s)!", status);
    return;
  }

  nWN  = dims[0];
  nPWV = dims[1];
  PWVARR = astMalloc(nPWV * sizeof(*PWVARR));
  for(i = 0; i < nPWV; i++) {
    PWVARR[i] = PWV0 + i * DELTAPWV;
  }
  PWVNEW = astMalloc(1 * sizeof(*PWVNEW));
  TAUNEW = astMalloc(1 * sizeof(*TAUNEW));

  // ===========================================================================
  // LOOP THROUGH EACH NDF FILE IN THE INPUT GROUP
  // ===========================================================================
  for(fIndex = 1; fIndex <= inSize; fIndex++) {
    // OPEN INPUT FILE
    smf_open_file(NULL, inGrp, fIndex, "READ", 0, &inData, status);
    if(*status != SAI__OK) {
      *status = SAI__ERROR;
      errRep(FUNC_NAME, "Unable to open source file!", status);
      break;
    }

    outData = smf_deepcopy_smfData(NULL, inData, 0, SMF__NOCREATE_DATA, 0, 0, status);
    if(*status == SAI__OK) {
      inPntr   = inData->pntr[0];
      nbolX    = inData->dims[0];
      nbolY    = inData->dims[1];
      N        = inData->dims[2];
      bolCount = nbolX * nbolY;

      outData->dtype   = SMF__DOUBLE;
      outData->ndims   = 3;
      outData->dims[0] = inData->dims[0];
      outData->dims[1] = inData->dims[1];
      outData->dims[2] = inData->dims[2];
      outData->lbnd[0] = outData->lbnd[0];
      outData->lbnd[1] = outData->lbnd[1];
      outData->lbnd[2] = outData->lbnd[2];
      outData->pntr[0] = (double*) astMalloc( (N * bolCount)*sizeof(double) );
      outPntr          = outData->pntr[0];

      // DETERMINE WAVENUMBER FACTOR FROM FITS
      smf_fits_getD(inData->hdr, "WNFACT", &wnFact, status);
      if(*status != SAI__OK) {
        errRep(FUNC_NAME, "Unable to find wave number factor!", status);
        smf_close_file( NULL,&inData, status);
        break;
      }

      // TODO
      // DETERMINE AIRMASS AT ZPD

      // TODO
      // DETERMINE PWV AT ZPD
      PWVNEW[0] = PWV;

      // GET TAU WET FOR CORRESPONDING PWV
      TAUWET = astMalloc(nWN * sizeof(*TAUWET));
      TMPARR = astMalloc(nWN * sizeof(*TMPARR));
      for(k = 0; k < nWN; k++) {
        for(j = 0; j < nPWV; j++) {
          TMPARR[j] = *((double*) TAU[1] + j);
        }
        fts2_naturalcubicsplineinterpolator(PWVARR, TMPARR, nPWV, PWVNEW, TAUNEW, 1);
        TAUWET[k] = TAUNEW[0];
      }
      astFree(TMPARR);

      // COMPUTE ATMOSPHERIC TRANSMISSION
      // TATM = EXP(-AIRMASS * (PWV * TAUWET + TAUDRY))
      TAtm = astMalloc(nWN * sizeof(*TAtm));
      if(!debug) {
        for(i = 0; i < nWN; i++) {
          TAtm[i] = exp(-AM * (PWV * TAUWET[i] + (*((double*) TAU[0] + i))));
        }
      } else {
        for(i = 0; i < nWN; i++) {
          TAtm[i] = exp(-AM * PWV * TAUWET[i]);
        }
      }

      // SMOOTH ATMOSPHERIC TRANSMISSION VIA GAUSSIAN CONVOLUTION
      // NEED TO TRIM FROM BOTH ENDS BY HALF OF (KERNELLENGTH - 1)
      double OPDMAX = 1.0;
      double FWHM   = 1.0 / (2.0 * OPDMAX);   // FWHM = 1 / (2 x OPDMAX)
      double SDEV   = 0.5 * FWHM / SQRT2LN2;  // FWHM = 2 x SQRT(2ln2) x SDEV
      double VAR    = SDEV * SDEV;
      double VAR2   = 2.0 * VAR;
      double NORM   = 1.0 / (SDEV * SQRT2PI);
      double XMIN   = -6 * SDEV;
      double XMAX   =  6 * SDEV;
      double DX = (XMAX - XMIN) / (KERNELLENGTH - 1);
      double X = XMIN;
      for(i = 0; i < KERNELLENGTH; i++) {
        X = XMIN + i * DX;
        GAUSSIANKERNEL[i] = NORM * exp(-(X * X) / VAR2);
      }
      int M = nWN + KERNELLENGTH - 1;
      TMPARR = astMalloc(M * sizeof(*TMPARR));
      for(i = 0; i < nWN; i++) {
        for(j = 0; j < KERNELLENGTH; j++) {
          TMPARR[i + j] += (TAtm[i] * GAUSSIANKERNEL[j]);
        }
      }
      int OFFSET = (KERNELLENGTH - 1) >> 1;
      for(i = 0; i < nWN; i++) {
        TAtm[i] = TMPARR[i + OFFSET];
      }
      astFree(TMPARR);

      // INTERPOLATE ATMOSPHERIC TRANSMISSION ONTO SCAN RESOLUTION
      wnTau = astMalloc(nWN * sizeof(*wnTau));
      wnScan = astMalloc(N * sizeof(*wnScan));
      TAtmNew = astMalloc(N * sizeof(*TAtmNew));
      for(i = 0; i < N; i++) {
        wnScan[i] = i * wnFact;
      }
      for(i = 0; i < nWN; i++) {
        wnTau[i] = i;
      }
      fts2_naturalcubicsplineinterpolator(wnTau, TAtm, nWN, wnScan, TAtmNew, N);

      // TSOURCE = TOBS / TATM
      for(i = 0; i < nbolY; i++) {
        for(j = 0; j < nbolX; j++) {
          bolIndex = i + j * nbolY;
          for(k = 0; k < N; k++) {
            index = bolIndex + bolCount * k;
            outPntr[index] = inPntr[index] / TAtmNew[k];
          }
        }
      }
      astFree(wnTau);
      astFree(wnScan);
      astFree(TAtm);
      astFree(TAtmNew);

      smf_write_smfData(NULL, outData, NULL, NULL, outGrp, fIndex, 0, MSG__VERB,
                        0, status);
      smf_close_file( NULL,&outData, status);
      smf_close_file( NULL,&inData, status);
    } else {
示例#11
0
void smurf_sc2filtermap( int *status ) {

  Grp *fgrp = NULL;         /* Output filter group */
  smfFilter *filt=NULL;     /* Filter */
  double filt_edgehigh=0;   /* High-pass filter */
  double filt_edgelow=0;    /* Low-pass filter */
  size_t fsize;             /* Number of files in fgrp */
  size_t i;                 /* Loop (grp) counter */
  smfData *idata;           /* Pointer to input smfData */
  Grp *igrp = NULL;         /* Input group of files */
  int isfft=0;              /* Are data fft or real space? */
  int *mask=NULL;           /* Mask indicating where bad data are */
  size_t ndata=0;           /* Number of pixels in the map */
  size_t ndims;             /* Number of real space dimensions */
  smfData *odata=NULL;      /* Pointer to output smfData to be exported */
  Grp *ogrp = NULL;         /* Output group of files */
  size_t outsize;           /* Number of files in output group */
  size_t size;              /* Number of files in input group */
  ThrWorkForce *wf = NULL;  /* Pointer to a pool of worker threads */
  smfData *wrefmap=NULL;    /* Whitening reference map */
  int whiten;               /* Applying whitening filter? */
  Grp *wgrp = NULL;         /* Whitening reference map group */
  size_t wsize;             /* Size of wgrp */
  int zerobad;              /* Zero VAL__BADD before taking FFT? */

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Get input file(s) */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );
  size = grpGrpsz( igrp, status );

  if (size > 0) {
    int parstate=0;           /* ADAM parameter state */

    /* Get output file(s) */
    kpg1Wgndf( "OUT", igrp, size, size, "More output files required...",
               &ogrp, &outsize, status );

    /* Write out the filter? */
    parState( "OUTFILTER", &parstate, status );
    if( parstate != PAR__GROUND ) {
      kpg1Wgndf( "OUTFILTER", igrp, size, size,
                 "More output filter files required...",
                 &fgrp, &fsize, status );
    }

  }

  /* Are we going to zero bad values first? */
  parGet0l( "ZEROBAD", &zerobad, status );

  /* High/low-pass filters? */
  parGet0d( "FILT_EDGEHIGH", &filt_edgehigh, status );
  parGet0d( "FILT_EDGELOW", &filt_edgelow, status );

  /* Are we applying a spatial whitening filter? */
  parGet0l( "WHITEN", &whiten, status );

  if( whiten ) {
    /* We also need the reference map to measure the whitening filter. We
       make a deep copy of it so that we can set bad values to 0 etc. */

    smfData *tempdata=NULL;

    kpg1Rgndf( "whiterefmap", 0, 1, "", &wgrp, &wsize, status );
    if( (*status == SAI__OK) && (wsize != 1) ) {
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME ": WHITEREFMAP must be a single reference map",
              status );
    }

    smf_open_file( wgrp, 1, "READ", SMF__NOTTSERIES, &tempdata, status );
    wrefmap = smf_deepcopy_smfData( tempdata, 0, 0, 0, 0, status );
    smf_close_file( &tempdata, status );

    /* Set VAL__BADD to zero if requested */
    if( (*status==SAI__OK) && zerobad ) {
      double *d=NULL;
      size_t j;

      ndata=1;
      for( j=0; j<wrefmap->ndims; j++ ) ndata *= wrefmap->dims[j];

      d = wrefmap->pntr[0];

      if( d ) {
        for( j=0; j<ndata; j++ ) {
          if( d[j] == VAL__BADD ) {
            d[j] = 0;
          }
        }
      }
    }

  }

  for( i=1;(*status==SAI__OK)&&i<=size; i++ ) {
    smf_open_file( igrp, i, "READ", SMF__NOTTSERIES, &idata, status );
    isfft = smf_isfft( idata, NULL, NULL, NULL, NULL, &ndims, status);

    if( (*status==SAI__OK) && isfft ) {
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME ": Input data are FFT, not real-space!\n",
              status );
      break;
    }

    if( (*status==SAI__OK) && (ndims != 2) ) {
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME ": Input data not a 2D map!\n",
              status );
      break;
    }

    /* smf_filter_execute operates in-place, so first create the output
       data as a copy of the input */

    odata = smf_deepcopy_smfData( idata, 0, 0, 0, 0, status );

    /* Set VAL__BADD to zero if requested */
    if( (*status==SAI__OK) && zerobad ) {
      double *d=NULL;
      size_t j, k;

      ndata=1;
      for( j=0; j<odata->ndims; j++ ) ndata *= odata->dims[j];

      mask = astCalloc( ndata, sizeof(*mask) );

      /* Do both DATA and VARIANCE */
      if( *status == SAI__OK ) {
        for( k=0; k<2; k++ ) {
          d = odata->pntr[k];

          if( d ) {
            for( j=0; j<ndata; j++ ) {
              if( d[j] == VAL__BADD ) {
                d[j] = 0;
                mask[j] = 1;
              }
            }
          }
        }
      }

    }

    /* Measure and apply the whitening filter. We need to do this
       every time because the dimensions of filt need to match idata
       (not the wrefmap) and they might be different each time. We
       could try to be more clever in the future if this is too slow. */

    filt = smf_create_smfFilter( idata, status );
    /* Set to the identity in case no whitening is applied */
    msgOut( "", TASK_NAME ": initializing filter", status );
    smf_filter_ident( filt, 0, status );

    if( whiten ) {
      msgOut( "", TASK_NAME ": whitening the filter", status );
      smf_filter2d_whiten( wf, filt, wrefmap, 0, 0, 3, status );
    }

    if( filt_edgelow ) {
      msgOutf( "", TASK_NAME ": applying low-pass at < %lg 1/arcsec", status,
               filt_edgelow );
      smf_filter2d_edge( filt, filt_edgelow, 1, status );
    }

    if( filt_edgehigh ) {
      msgOutf( "", TASK_NAME ": applying high-pass at >= %lg 1/arcsec", status,
               filt_edgehigh );
      smf_filter2d_edge( filt, filt_edgehigh, 0, status );
    }

    smf_filter_execute( wf, odata, filt, 0, 0, status );

    /* Set bad values from the mask */
    if( mask ) {
      double *d=NULL;
      size_t j, k;

      /* Do both DATA and VARIANCE */
      for( k=0; k<2; k++ ) {
        d = odata->pntr[k];

        if( d ) {
          for( j=0; j<ndata; j++ ) {
            if( mask[j] ) {
              d[j] = VAL__BADD;
            }
          }
        }
      }
    }

    /* Export the data to a new file */
    smf_write_smfData( odata, NULL, NULL, ogrp, i, 0, MSG__NORM, status );

    /* Write out filters? */
    if( fgrp ) smf_write_smfFilter( filt, NULL, fgrp, i, status );
    if( filt ) smf_free_smfFilter( filt, status );

  }

  /* Tidy up after ourselves */

  if( fgrp ) grpDelet( &fgrp, status);
  if( igrp ) grpDelet( &igrp, status);
  if( ogrp ) grpDelet( &ogrp, status);
  if( wgrp ) grpDelet( &wgrp, status );

  if( odata ) smf_close_file( &odata, status );
  if( wrefmap ) smf_close_file( &wrefmap, status );

  if( mask ) mask = astFree( mask );

  ndfEnd( status );

  /* Ensure that FFTW doesn't have any used memory kicking around */
  fftw_cleanup();
}
示例#12
0
F77_SUBROUTINE(configecho)( INTEGER(STATUS) ){
/*
*+
*  Name:
*     CONFIGECHO

*  Purpose:
*     Displays one or more configuration parameters.

*  Language:
*     C (designed to be called from Fortran)

*  Type of Module:
*     ADAM A-task

*  Invocation:
*     CALL CONFIGECHO( STATUS )

*  Arguments:
*     STATUS = INTEGER (Given and Returned)
*        The global status.

*  Description:
*     This application displays the name and value of one or all
*     configuration parameters, specified using Parameters CONFIG or
*     NDF. If a single parameter is displayed, its value is also
*     written to an output parameter. If the parameter value is not
*     specified by the CONFIG, NDF or DEFAULTS parameter, then the
*     value supplied for DEFVAL is displayed.
*
*     If an input NDF is supplied then configuration parameters
*     are read from its history (see Parameters NDF and APPLICATION).
*
*     If values are supplied for both CONFIG and NDF, then the
*     differences between the two sets of configuration parameters
*     are displayed (see Parameter NDF).

*  Usage:
*     configecho name config [defaults] [select] [defval]

*  ADAM Parameters:
*     APPLICATION = LITERAL (Read)
*        When reading configuration parameters from the history
*        of an NDF, this parameter specifies the name of the application
*        to find in the history. There must be a history component
*        corresponding to the value of this parameter, and it must
*        include a CONFIG group. [current value]
*     CONFIG = GROUP (Read)
*        Specifies values for the configuration parameters. If the string
*        "def" (case-insensitive) or a null (!) value is supplied, the
*        configuration parameters are obtained using Parameter NDF. If
*        a null value is also supplied for NDF, a set of default
*        configuration parameter values will be used, as specified by
*        Parameter DEFAULTS.
*
*        The supplied value should be either a comma-separated list of
*        strings or the name of a text file preceded by an up-arrow
*        character "^", containing one or more comma-separated lists of
*        strings. Each string is either a "keyword=value" setting, or the
*        name of a text file preceded by an up-arrow character "^". Such
*        text files should contain further comma-separated lists which
*        will be read and interpreted in the same manner (any blank lines
*        or lines beginning with "#" are ignored). Within a text file,
*        newlines can be used as delimiters, as well as commas. Settings
*        are applied in the order in which they occur within the list,
*        with later settings overriding any earlier settings given for
*        the same keyword.
*
*        Each individual setting should be of the form "<keyword>=<value>".
*        If a non-null value is supplied for Parameter DEFAULTS, an error
*        will be reported if CONFIG includes values for any parameters
*        that are not included in DEFAULTS.
*     DEFAULTS = LITERAL (Read)
*        The path to a file containing the default value for every
*        allowed configuration parameter. If null (!) is supplied, no
*        defaults will be supplied for parameters that are not specified
*        by CONFIG, and no tests will be performed on the validity of
*        paramter names supplied by CONFIG. [!]
*     DEFVAL = LITERAL (Read)
*        The value to return if no value can be obtained for the named
*        parameter, or if the value is "<undef>".  [<***>]
*     NAME = LITERAL (Read)
*        The name of the configuration parameter to display.  If set to
*        null (!), then all parameters defined in the configuration are
*        displayed.
*     NDF = NDF (Read)
*        An NDF file containing history entries which include
*        configuration parameters. If not null (!) the history
*        of the NDF will be searched for a component corresponding
*        to the Parameter APPLICATION.  The Parameter CONFIG
*        is then optional, but if it too is not null (!) then
*        the output will show the differences between the configuration
*        stored in the NDF history and the given configuration:
*        new parameters and those different from the reference
*        configuration (given by Parameter CONFIG) are prefixed
*        with "+" and those which are the same as the reference
*        configuration are prefixed with "-". [!]
*     SELECT = GROUP (Read)
*        A group that specifies any alternative prefixes that can be
*        included at the start of any parameter name. For instance, if
*        this group contains the two entries "450=1" and "850=0", then
*        either CONFIG or DEFAULTS can specify two values for any single
*        parameter -- one for the parameter prefixed by "450." and another
*        for the parameter prefixed by "850.". Thus, for instance, if
*        DEFAULTS defines a parameter called "filter", it could include
*        "450.filter=300" and "850.filter=600". The CONFIG parameter could
*        then either set the filter parameter for a specific prefix (as
*        in "450.filter=234"); or it could leave the prefix unspecified,
*        in which case the prefix used is the first one with a
*        non-zero value in SELECT (450 in the case of this example - 850
*        has a value zero in SELECT). Thus the names of the items in
*        SELECT define the set of allowed alternative prefixes, and the
*        values indicate which one of these alternatives is to be used
*        (the first one with non-zero value). [!]
*     SORT = _LOGICAL (Read)
*        If TRUE then sort the listed parameters in to alphabetical order.
*        Otherwise, retain the order they have in the supplied
*        configuration. Only used if a null (!) value is supplied for
*        Parameter NAME. [FALSE]
*     VALUE = LITERAL (Write)
*        The value of the configuration parameter, or "<***>" if the
*        parameter has no value in CONFIG and DEFAULTS.

*  Examples:
*     configecho m81 ^myconf
*        Report the value of configuration parameter "m81" defined within
*        the file "myconf". If the file does not contain a value for
*        "m81", then "<***>" is displayed.
*     configecho type ^myconf select="m57=0,m31=1,m103=0"
*        Report the value of configuration parameter "type" defined within
*        the file "myconf". If the file does not contain a value for
*        "type", then the value of "m31.type" will be reported instead. If
*        neither is present, then "<***>" is displayed.
*     configecho flt.filt_edge_largescale \
*                config=^/star/share/smurf/dimmconfig.lis \
*                defaults=/star/bin/smurf/smurf_makemap.def \
*                select="450=1,850=0"
*        Report the value of configuration parameter "flt.filt_edge_largescale"
*        defined within the file "/star/share/smurf/dimmconfig.lis", using
*        defaults from the file "/star/bin/smurf/smurf_makemap.def". If
*        dimmconfig.lis does not contain a value for "flt.filt_edge_largescale"
*        then it is searched for "450.flt.filt_edge_largescale" instead. An
*        error is reported if dimmconfig.lis contains values for any
*        items that are not defined in smurf_makemap.def.
*     configecho ndf=omc1 config=^/star/share/smurf/dimmconfig.lis \
*                defaults=/star/bin/smurf/smurf_makemap.def \
*                application=makemap name=! sort select="450=0,850=1"
*        Show how the configuration used to generate the 850um map
*        of OMC1 differs from the basic dimmconfig.lis file.

*  Copyright:
*     Copyright (C) 2012-3 Science & Technology Facilities Council.
*     All Rights Reserved.

*  Licence:
*     This program is free software; you can redistribute it and/or
*     modify it under the terms of the GNU General Public License as
*     published by the Free Software Foundation; either Version 2 of
*     the License, or (at your option) any later version.
*
*     This program is distributed in the hope that it will be
*     useful, but WITHOUT ANY WARRANTY; without even the implied
*     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
*     PURPOSE. See the GNU General Public License for more details.
*
*     You should have received a copy of the GNU General Public License
*     along with this program; if not, write to the Free Software
*     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
*     02110-1301, USA.

*  Authors:
*     DSB: David S. Berry
*     GSB: Graham S. Bell
*     {enter_new_authors_here}

*  History:
*     10-DEC-2012 (DSB):
*        Original version.
*     6-FEB-2013 (DSB):
*        Added parameter DEFVAL.
*     11-FEB-2013 (DSB):
*        Added parameter SORT and allow all parameters to be listed by
*        providing a null value for NAME.
*     11-FEB-2013 (GSB):
*        Added ability to read configuration from history entries.
*     13-FEB-2013 (DSB):
*        Nullify AST object pointers when the objects are annulled,
*        to avoid re-use of dead pointers.
*     14-FEB-2013 (DSB):
*        Allow the SELECT feature to be used even if no DEFAULTS file is
*        supplied (see the new entry in the "Examples:" section).
*     15-FEB-2013 (DSB):
*        Expand the prologue docs, and use NULL in place of zero for pointers.
*     22-FEB-2013 (DSB):
*        Guard against seg fault in HistoryKeymap when the NDF does 
*        not contain the required CONFIG entry in the History 
*        component.
*     {enter_further_changes_here}

*-
*/

   GENPTR_INTEGER(STATUS)

/* Local Variables: */
   AstKeyMap *keymap2;
   AstKeyMap *keymap;
   Grp *grp = NULL;
   char *dot;
   char *pname;
   char defs[250];
   char defval[250];
   char name[250];
   const char *value;
   const char *historyValue = NULL;
   int showall;
   int sort;
   size_t size;
   int indf = 0;
   int nrec;
   int i;
   char application[NDF__SZAPP];
   char applicationi[NDF__SZAPP];

/* Abort if an error has already occurred. */
   if( *STATUS != SAI__OK ) return;

/* Begin an AST context */
   astBegin;

/* Get the value to return if no value can be obtained for the named
   parameter, of it it has a value of <undef>. */
   parGet0c( "DEFVAL", defval, sizeof(defval), STATUS );

/* Get any defaults file, annuling the error if null (!) is supplied. */
   if( *STATUS == SAI__OK ) {
      parGet0c( "DEFAULTS", defs, sizeof(defs), STATUS );
      if( *STATUS == PAR__NULL ) {
         errAnnul( STATUS );
         defs[0] = 0;
      }
   }

/* Get the NDF identifier if requested. */
   ndfBegin();
   if (*STATUS == SAI__OK) {
      ndfAssoc("NDF", "READ", &indf, STATUS);
      if (*STATUS == PAR__NULL) {
         errAnnul(STATUS);
         indf = 0;
      }
      else {
         parGet0c("APPLICATION", application, sizeof(application), STATUS);
         /* Check now for error because the block below allowing an undefined
          * CONFIG clears this status otherwise. */
         if (*STATUS != SAI__OK) goto L999;
      }
   }

/* See if any alternate keyword prefixes are allowed, and if so determine
   which of the alternatices is to be displayed. */
   kpg1Gtgrp( "SELECT", &grp, &size, STATUS );
   if( *STATUS == PAR__NULL ) {
      grpDelet( &grp, STATUS );
      errAnnul( STATUS );
      keymap2 = NULL;
   } else {
      kpg1Kymap( grp, &keymap2, STATUS );
      grpDelet( &grp, STATUS );
   }

/* Create a KeyMap holding the selected alternative for each keyword, and
   also supply defaults for any missing values (if a defaults file was
   supplied by the user). */
   keymap = kpg1Config( "CONFIG", defs[0]?defs:NULL, keymap2, 0, STATUS );

/* Allow it to be NULL if we're reading an NDF because we'll replace
   keymap with historyConfig later if necessary. */
   if( indf && *STATUS == PAR__NULL ) {
      errAnnul(STATUS);
      keymap = NULL;
   }

/* Abort if an error has occurred. */
   if( *STATUS != SAI__OK ) goto L999;

/* Get the name of the required parameter, and convert to upper case (if
   supplied). If not supplied, set a flag indicating that all parameters
   should be displayed. */
   parGet0c( "NAME", name, sizeof(name), STATUS );
   if( *STATUS == PAR__NULL ) {
      errAnnul( STATUS );
      showall = 1;
   } else {
      showall = 0;
      astChrCase( NULL, name, 1, 0 );
   }

/* Attempt to find the NDF's corresponding history record. */
   if (indf && *STATUS == SAI__OK) {
      ndfHnrec(indf, &nrec, STATUS);
      for (i = 0; i < nrec; i ++) {
         ndfHinfo(indf, "APPLICATION", i + 1, applicationi,
                  sizeof(applicationi), STATUS);
         if (! strncasecmp(application, applicationi, strlen(application))) {
            ndfHout(indf, i + 1, HistoryKeyMap, STATUS);
            break;
         }
      }

      if (*STATUS == SAI__OK && ! historyConfig) {
         *STATUS = SAI__ERROR;

         errRepf("CONFIGECHO_ERR", "CONFIGECHO: Failed to find %s "
                 "configuration in NDF history.", STATUS, application);
      }
      else if (! keymap) {
         keymap = historyConfig;
         historyConfig = NULL;
      }
   }

   if( *STATUS == SAI__OK ) {

/* First deal with cases where we are displaying a single parameter
   value. */
      if( !showall ) {

/* Loop round each section of the name that ends with a dot. */
         value = defval;
         pname = name;

         dot = strchr( pname, '.' );
         while( dot && keymap ) {

/* Get a nested keymap with the name that occurs prior to the dot. If
   found, use it in place of the parent keymap. */
            pname[ dot - pname ] = 0;
            if( astMapGet0A( keymap, pname, &keymap2 ) ) {
               astAnnul( keymap );
               keymap = keymap2;
            } else {
               keymap = astAnnul( keymap );
            }

/* If historyConfig exists, do the same there. */
            if (historyConfig) {
               if (astMapGet0A(historyConfig, pname, &keymap2)) {
                  astAnnul(historyConfig);
                  historyConfig = keymap2;
               }
               else {
                  historyConfig = astAnnul(historyConfig);
               }
            }

/* Re-instate the original dot, and move on to find the next dot. */
            pname[ dot - pname ] = '.';
            pname = dot + 1;
            dot = strchr( pname, '.' );
         }

/* Ensure no error is reported if the parameter is not found in the
   KeyMap. */
         if( keymap ) {
            astClear( keymap, "KeyError" );

/* Get the parameter value as a string. */
            astMapGet0C( keymap, pname, &value );
         }

         if (historyConfig) {
            astClear(historyConfig, "KeyError");
            astMapGet0C(historyConfig, pname, &historyValue);

/* In NDF history mode we only want to return a value if it
   was found in the configuration from the history. */

            if (historyValue) {
               if (strcmp(value, historyValue)) {
                  msgOutf("", "+ %s", STATUS, historyValue);
               }
               else {
                  msgOutf("", "- %s", STATUS, historyValue);
               }
               parPut0c("VALUE", historyValue, STATUS);
            }
         }
         else {
/* Display it. */
            msgOut( "", value, STATUS );

/* Write it to the output parameter. */
            parPut0c( "VALUE", value, STATUS );
         }

/* Now deal with cases were we are displaying all parameter values. */
      } else {

/* See if the values should be sorted. */
         parGet0l( "SORT", &sort, STATUS );

/* Display them. */
         if (historyConfig) {
            DisplayKeyMap( historyConfig , sort, "", keymap, STATUS );
         }
         else {
            DisplayKeyMap( keymap, sort, "", NULL, STATUS );
         }
      }
   }

/* Tidy up. */
L999:;

/* End the AST context */
   astEnd;

/* Close the NDF if open. */
   ndfEnd(STATUS);

/* If an error has occurred, issue another error report identifying the
   program which has failed (i.e. this one). */
   if( *STATUS != SAI__OK ) {
      errRep( "CONFIGECHO_ERR", "CONFIGECHO: Failed to echo configuration "
              "parameters.", STATUS );
   }

}
示例#13
0
void smf_getrefwcs( const char *param, Grp *igrp, AstFrameSet **specwcs,
                    AstFrameSet **spacewcs, int *isjsa, int *status ){

/* Local Variables */
   AstFrame *frm = NULL;
   AstFrameSet *refwcs = NULL;  /* The WCS FrameSet from the reference NDF */
   AstRegion *circle;
   char text[ 255 ];            /* Parameter value */
   int *tiles;
   int i;
   int jsatiles;
   int lbnd[2];                 /* Lower pixel index bounds of mid tile */
   int ntile;
   int perm[ 2 ];
   int refndf;                  /* NDF identifier for the refence NDF */
   int ubnd[2];                 /* Upper pixel index bounds of mid tile */
   size_t code;
   smfData *data = NULL;        /* Structure describing 1st input file */
   smfJSATiling skytiling;
   smf_inst_t inst = SMF__INST_NONE;
   smf_jsaproj_t proj;          /* Specific JSA projection to use */
   smf_subinst_t subinst;

/* Initialise the returned values. */
   *specwcs = NULL;
   *spacewcs = NULL;
   *isjsa = 0;

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

/* Begin an AST context. */
   astBegin;

/* If the JSAILES parameter is TRUE, then we use the JSA all-sky pixel
   grid regardless of the setting of REF. */
   parGet0l( "JSATILES", &jsatiles, status );
   if( jsatiles ) {
      strcpy( text, "JSA" );
      *isjsa = 1;

/* Otherwise, first get the parameter value as a string. Use subpar to avoid problem
   caused by interpretion of the text within the parameter system. */
   } else {
      subParFindpar( param, &code, status );
      subParGetname( code, text, sizeof(text), status );
   }

/* If no value was supplied, annul the error and do nothing more. */
   if( *status == PAR__NULL ) {
      errAnnul( status );

/* If it is "JSA", or one of the JSA projection codes, we return WCS that
   describes one of the the JSA all-sky pixel grids. */
   } else if( *status == SAI__OK ) {
      proj = smf_jsaproj_fromstr( text, 0, status );
      if( astChrMatch( text, "JSA" ) || proj != SMF__JSA_NULL ) {
         *isjsa = 1;

/* Report an error if the instrument cannot be determined. */
         if( !igrp ) {
            *status = SAI__ERROR;
            errRep( "", "smf_getrefwcs: Cannot use the JSA all-sky pixel "
                    "grid since no input group has been supplied (possibly "
                    "programming error).", status );
         } else {

/* Open the first input file. */
            smf_open_file( NULL, igrp, 1, "READ", SMF__NOCREATE_DATA, &data,
                           status );
            if( *status == SAI__OK ) {

/* Get the instrument. */
               if( data->hdr->instrument == INST__SCUBA2 ) {
                  subinst = smf_calc_subinst( data->hdr, status );
                  if( subinst == SMF__SUBINST_850 ) {
                     inst = SMF__INST_SCUBA_2_850;
                  } else {
                     inst = SMF__INST_SCUBA_2_450;
                  }

               } else if( data->hdr->instrument == INST__ACSIS ) {
                  inst = SMF__INST_ACSIS;

               } else if( *status == SAI__OK ) {
                  *status = SAI__ERROR;
                  if( data->file ) {
                     smf_smfFile_msg( data->file, "FILE", 1, "one or more of "
                                      "the input data files" );
                  } else {
                     msgSetc( "FILE", "one or more of the input data files" );
                  }
                  errRep( "", "No tiles are yet defined for the instrument that "
                          "created ^FILE.", status );
               }

/* Get the parameters that define the layout of sky tiles for the
   instrument. */
               smf_jsatiling( inst, &skytiling, status );

/* For "JSA" - choose the best projection. */
               if( astChrMatch( text, "JSA" ) ) {

/* Use the FITS headers in the first raw data file to create an AST Circle
   describing the approximate area of the observation within the tracking
   system. */
                  circle = smf_mapregion_approx( igrp, status );

/* Convert the circle to ICRS (as used by the JSA all-sky grid). */
                  astSetC( circle, "System", "ICRS" );

/* Get a list of the tiles that touch this circle. */
                  tiles = smf_jsatiles_region( circle, &skytiling,
                                               &ntile, status );

/* Choose the best projection (i.e. the projection that puts the circle
   furthest away from any singularities). */
                  proj = smf_jsaproj( ntile, tiles, &skytiling, status);

/* Free resources. */
                  tiles = astFree( tiles );
                  circle = astAnnul( circle );

/* If a good projection was specified, use it. Otherwise report an error. */
               } else if( proj == SMF__JSA_NULL && *status == SAI__OK ) {
                  *status = SAI__ERROR;
                  errRepf( "", "Bad value '%s' supplied for parameter %s.",
                           status, text, param );
               }

/* Report the projection type. */
               msgOutf( " ", "The %s will be created on the JSA %s "
                        "pixel grid.", status,
                        (data->hdr->instrument==INST__ACSIS)?"cube":"map",
                        smf_jsaproj_tostr( proj ) );

/* All tiles within the same JSA projection use the same WCS, so we get
   the WCS FrameSet for an arbitrary central tile, and use it for the
   full map. The exception is that tiles within the HPX facet that is
   split between bottom-left and top-right, use a different WCS (they
   have different reference points). But our choice of projection should
   mean that the map never falls in that facet. The base Frame will be
   GRID coords within the tile, and the current Frame will be ICRS
   (RA,Dec). */
               smf_jsatile( ((skytiling.ntpf * skytiling.ntpf - 1) * 2) / 3,
                            &skytiling, 0, proj, NULL, spacewcs, NULL, lbnd,
                            ubnd, status );

/* Change the base Frame to be PIXEL. */
               for( i = 1; i <= astGetI( *spacewcs, "NFrame" ); i++ ) {
                  frm = astGetFrame( *spacewcs, i );
                  if( astChrMatch( astGetC( frm, "Domain" ), "PIXEL" ) ) {
                     astSetI( *spacewcs, "Base", i );
                  }
                  frm = astAnnul( frm );
               }
            }

/* Close the current input data file. */
            smf_close_file( NULL, &data, status);
         }

/* Otherwise get the parameter value as an NDF. */
      } else {
         ndfAssoc( param, "READ", &refndf, status );

/* Get the WCS FrameSet from the reference NDF. */
         ndfGtwcs( refndf, &refwcs, status );

/* Attempt to extract a new FrameSet from this WCS FrameSet, in which the
   current Frame is a SkyFrame, and the base Frame is a 2D PIXEL Frame.
   Since the NDF library sets the GRID Frame to be the Base Frame, we need
   to make the PIXEL Frame the base Frame first. The NDF library ensures
   that the pixel Frame is Frame 2. */
         astSetI( refwcs, "Base", 2 );
         *spacewcs = atlFrameSetSplit( refwcs, "SKY", NULL, NULL, status );
         if( !(*spacewcs) ) {
            if( *status == SAI__OK ) {
               ndfMsg( "N", refndf );
               *status = SAI__ERROR;
               errRep( "", "The supplied reference NDF (^N) either has no "
                       "celestial WCS axes, or the celestial axes cannot "
                       "be separated from the non-celestial axes.", status );
            }

/* The rest of makemap assumes that the sky frame axes are in the default
   order (lon,lat). If this is not the case, permute them. */
         } else if( astGetI( *spacewcs, "IsLatAxis(1)" ) ) {
            perm[ 0 ] = 2;
            perm[ 1 ] = 1;
            astPermAxes( *spacewcs, perm );
         }

/* Now look for the spectral WCS (described by a DSBSpecFrame). */
         smf_getspectralwcs( refwcs, 1, specwcs, status );

/* We no longer need the NDF so annul it. */
         ndfAnnul( &refndf, status );
      }
   }

/* If no error has occurred, export any returned FrameSet pointers from
   the current AST context so that it will not be annulled when the AST
   context is ended. Otherwise, ensure a null pointer is returned. */
   if( *status == SAI__OK ) {
      if( *spacewcs ) astExport( *spacewcs );
      if( *specwcs ) astExport( *specwcs );
   } else {
      if( *spacewcs ) *spacewcs = astAnnul( *spacewcs );
      if( *specwcs ) *specwcs = astAnnul( *specwcs );
   }

/* End the AST context. This will annul all AST objects created within the
   context (except for those that have been exported from the context). */
   astEnd;

}
示例#14
0
void smurf_sc2concat( int *status ) {

  /* Local Variables */
  Grp *basegrp=NULL;         /* Grp containing first file each chunk */
  size_t basesize;           /* Number of files in base group */
  smfArray *concat=NULL;     /* Pointer to a smfArray */
  size_t contchunk;          /* Continuous chunk counter */
  smfArray *darks = NULL;    /* dark frames */
  int ensureflat;            /* Flag for flatfielding data */
  Grp *fgrp = NULL;          /* Filtered group, no darks */
  smfArray * flatramps = NULL; /* Flatfield ramps */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  size_t gcount=0;           /* Grp index counter */
  size_t idx;                /* Subarray counter */
  int usedarks;              /* flag for using darks */
  Grp *igrp = NULL;          /* Group of input files */
  smfGroup *igroup=NULL;     /* smfGroup corresponding to igrp */
  size_t isize;              /* Number of files in input group */
  dim_t maxconcat=0;         /* Longest continuous chunk length in samples */
  double maxlen;             /* Constrain maxconcat to this many seconds */
  size_t ncontchunks=0;      /* Number continuous chunks outside iter loop */
  Grp *ogrp = NULL;          /* Output files  */
  size_t osize;              /* Number of files in input group */
  dim_t padStart=0;          /* How many samples padding at start */
  dim_t padEnd=0;            /* How many samples padding at end */
  int temp;                  /* Temporary signed integer */
  ThrWorkForce *wf = NULL;   /* Pointer to a pool of worker threads */

  if (*status != SAI__OK) return;

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Read the input file */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &isize, status );

  /* Filter out darks */
  smf_find_science( igrp, &fgrp, 1, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  isize = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  if (isize == 0) {
    msgOutif(MSG__NORM, " ","All supplied input frames were filtered,"
       " nothing to do", status );
    goto CLEANUP;
  }

  /* --- Parse ADAM parameters ------------------------ */

  /* Maximum length of a continuous chunk */
  parGdr0d( "MAXLEN", 0, 0, VAL__MAXD, 1, &maxlen, status );

  /* Padding */
  parGdr0i( "PADSTART", 0, 0, VAL__MAXI, 1, &temp, status );
  padStart = (dim_t) temp;

  parGdr0i( "PADEND", 0, 0, VAL__MAXI, 1, &temp, status );
  padEnd = (dim_t) temp;

  /* Are we using darks? */
  parGet0l( "USEDARKS", &usedarks, status );

  /* Are we flatfielding? */
  parGet0l( "FLAT", &ensureflat, status );

  /* Group the input files by subarray and continuity */
  smf_grp_related( igrp, isize, 1, 0, maxlen-padStart-padEnd, NULL, NULL,
                   &maxconcat, NULL, &igroup, &basegrp, NULL, status );

  /* Obtain the number of continuous chunks and subarrays */
  if( *status == SAI__OK ) {
    ncontchunks = igroup->chunk[igroup->ngroups-1]+1;
  }

  basesize = grpGrpsz( basegrp, status );

  /* Get output file(s) */
  kpg1Wgndf( "OUT", basegrp, basesize, basesize,
             "More output files required...",
             &ogrp, &osize, status );

  /* Loop over continuous chunks */
  gcount = 1;
  for( contchunk=0;(*status==SAI__OK)&&contchunk<ncontchunks; contchunk++ ) {

    /* Concatenate this continuous chunk */
    smf_concat_smfGroup( wf, NULL, igroup, usedarks ? darks:NULL, NULL, flatramps,
                         heateffmap, contchunk, ensureflat, 1, NULL, 0, NULL, NULL,
                         NO_FTS, padStart, padEnd, 0, &concat, NULL, status );

    /* Export concatenated data for each subarray to NDF file */
    for( idx=0; (*status==SAI__OK)&&idx<concat->ndat; idx++ ) {
      if( concat->sdata[idx]->file && concat->sdata[idx]->file->name ) {
        smf_write_smfData( concat->sdata[idx], NULL, NULL, ogrp, gcount,
                           NDF__NOID, MSG__VERB, 0, status );
      } else {
        *status = SAI__ERROR;
        errRep( FUNC_NAME,
                "Unable to determine file name for concatenated data.",
                status );
      }

      /* Increment the group index counter */
      gcount++;
    }

    /* Close the smfArray */
    smf_close_related( &concat, status );

  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && ogrp ) {
    grpList( "OUTFILES", 0, 0, NULL, ogrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

 CLEANUP:
  if( darks ) smf_close_related( &darks, status );
  if( flatramps ) smf_close_related( &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  if( igrp ) grpDelet( &igrp, status);
  if( basegrp ) grpDelet( &basegrp, status );
  if( ogrp ) grpDelet( &ogrp, status );
  if( igroup ) smf_close_smfGroup( &igroup, status );

  ndfEnd( status );

  if( *status == SAI__OK ) {
    msgOutif(MSG__VERB," ","SC2CONCAT succeeded.", status);
  } else {
    msgOutif(MSG__VERB," ","SC2CONCAT failed.", status);
  }

}
示例#15
0
void findback( int *status ){
/*
*+
*  Name:
*     FINDBACK

*  Purpose:
*     Estimate the background in an NDF by removing small scale structure.

*  Language:
*     C

*  Type of Module:
*     ADAM A-task

*  Synopsis:
*     void findback( int *status );

*  Description:
*     This application uses spatial filtering to remove structure with a
*     scale size less than a specified size from a 1, 2, or 3 dimensional
*     NDF, thus producing an estimate of the local background within the NDF.
*
*     The algorithm proceeds as follows. A filtered form of the input data
*     is first produced by replacing every input pixel by the minimum of
*     the input values within a rectangular box centred on the pixel.
*     This filtered data is then filtered again, using a filter that
*     replaces every pixel value by the maximum value in a box centred on
*     the pixel. This produces an estimate of the lower envelope of the data,
*     but usually contains unacceptable sharp edges. In addition, this
*     filtered data has a tendency to hug the lower envelope of the
*     noise, thus under-estimating the true background of the noise-free
*     data. The first problem is minimised by smoothing the background
*     estimate using a filter that replaces every pixel value by the mean
*     of the values in a box centred on the pixel. The second problem
*     is minimised by estimating the difference between the input data
*     and the background estimate within regions well removed from any
*     bright areas. This difference is then extrapolated into the bright
*     source regions and used as a correction to the background estimate.
*     Specifically, the residuals between the input data and the initial
*     background estimate are first formed, and residuals which are more
*     than three times the RMS noise are set bad. The remaining residuals
*     are smoothed with a mean filter. This smoothing will replace a lot
*     of the bad values rejected above, but may not remove them all. Any
*     remaining bad values are estimated by linear interpolation between
*     the nearest good values along the first axis. The interpolated
*     residuals are then smoothed again using a mean filter, to get a
*     surface representing the bias in the initial background estimate.
*     This surface is finally added onto the initial background estimate
*     to obtain the output NDF.

*  Usage:
*     findback in out box

*  ADAM Parameters:
*     BOX() = _INTEGER (Read)
*        The dimensions of each of the filters, in pixels. Each value
*        should be odd (if an even value is supplied, the next higher odd
*        value will be used). The number of values supplied should not
*        exceed the number of significant (i.e. more than one element)
*        pixel axes in the input array. If any trailing values of 1 are
*        supplied, then each pixel value on the corresponding axes
*        will be fitted independently of its neighbours. For instance,
*        if the data array is 3-dimensional, and the third BOX value is 1,
*        then each x-y plane will be fitted independently of the neighbouring
*        planes. If the NDF has more than 1 pixel axis but only 1 value is
*        supplied, then the same value will be used for the both the first
*        and second pixel axes (a value of 1 will be assumed for the third
*        axis if the input array is 3-dimensional).
*     MSG_FILTER = _CHAR (Read)
*        Controls the amount of diagnostic information reported. This is the
*        standard messaging level. The default messaging level is NORM (2).
*        A value of NONE or 0 will suppress all screen output. VERB (3) will
*        indicate progress through the various stages of the algorithm. [NORM]
*     IN = NDF (Read)
*        The input NDF.
*     RMS = _DOUBLE (Read)
*        Specifies a value to use as the global RMS noise level in the
*        supplied data array. The suggested default value is the square root
*        of the mean of the values in the input NDF's Variance component.
*        If the NDF has no Variance component, the suggested default
*        is based on the differences between neighbouring pixel values,
*        measured over the entire input NDF. If multiple slices within the
*        NDF are to be processed independently (see parameter BOX), it
*        may be more appropriate for a separate default RMS to be calculated
*        for each slice. This will normally be the case if the noise could
*        be different in each of the slices. In such cases a null (!) can
*        be supplied for the RMS parameter, which forces a separate
*        default RMS value to be found and used for each slice. Any
*        pixel-to-pixel correlation in the noise can result in these
*        defaults being too low.
*     SUB = _LOGICAL (Read)
*        If a TRUE value is supplied, the output NDF will contain the
*        difference between the supplied input data and the estimated
*        background. If a FALSE value is supplied, the output NDF will
*        contain the estimated background itself. [FALSE]
*     OUT = NDF (Write)
*        The output NDF containing either the estimated background, or the
*        background-subtracted input data, as specified by parameter SUB.

*  Notes:
*     - Smoothing cubes in 3 dimensions can be very slow.

*  Copyright:
*     Copyright (C) 2009 Science and Technology Facilities Council.
*     Copyright (C) 2006, 2007 Particle Physics & Astronomy Research Council.
*     All Rights Reserved.

*  Licence:
*     This program is free software; you can redistribute it and/or
*     modify it under the terms of the GNU General Public License as
*     published by the Free Software Foundation; either version 2 of
*     the License, or (at your option) any later version.
*
*     This program is distributed in the hope that it will be
*     useful, but WITHOUT ANY WARRANTY; without even the implied
*     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
*     PURPOSE. See the GNU General Public License for more details.
*
*     You should have received a copy of the GNU General Public License
*     along with this program; if not, write to the Free Software
*     Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA
*     02110-1301, USA

*  Authors:
*     DSB: David S. Berry
*     TIMJ: Tim Jenness (JAC, Hawaii)
*     {enter_new_authors_here}

*  History:
*     13-SEP-2006 (DSB):
*        Original version.
*     19-MAR-2007 (DSB):
*        - Added parameters SUB and RMS.
*        - Fix bug that left the output NDF uninitialised if ILEVEL is set
*        non-zero.
*        - Use generic data type handling as in FINDCLUMPS.
*     14-JAN-2009 (TIMJ):
*        Use MERS for message filtering.
*     29-JUL-2009 (TIMJ):
*        Rename ILEVEL to MSG_FILTER
*     17-MAY-2011 (DSB):
*        Use sqrt rather than sqrtf when calculating RMS.
*     12-SEP-2011 (DSB):
*        Process slices in separate threads.
*     {enter_further_changes_here}

*-
*/

/* Local Variables: */
   CupidFindback0Data *job_data; /* Pointer to data for all jobs */
   CupidFindback0Data *pdata; /* Pointer to data for current job */
   Grp *grp;                 /* GRP identifier for configuration settings */
   ThrWorkForce *wf = NULL;  /* Pool of persistent worker threads */
   char dtype[ 21 ];         /* HDS data type for output NDF */
   char itype[ 21 ];         /* HDS data type to use when processing */
   double *ipv;              /* Pointer to Variance array */
   double *pd1;              /* Pointer to double precision input data */
   double *pd2;              /* Pointer to double precision output data */
   double rms;               /* Global rms error in data */
   double sum;               /* Sum of variances */
   float *pf1;               /* Pointer to single precision input data */
   float *pf2;               /* Pointer to single precision output data */
   int *old_status;          /* Pointer to original status value */
   int box[ 3 ];             /* Dimensions of each cell in pixels */
   int dim[ NDF__MXDIM ];    /* Dimensions of each NDF pixel axis */
   int el;                   /* Number of elements mapped */
   int i;                    /* Loop count */
   int indf1;                /* Identifier for input NDF */
   int indf2;                /* Identifier for output NDF */
   int islice;               /* Slice index */
   int iystep;               /* Index of slice in ydirection */
   int izstep;               /* Index of slice in z direction */
   int lbnd[ NDF__MXDIM ];   /* Lower pixel bounds of slice */
   int n;                    /* Number of values summed in "sum" */
   int ndim;                 /* Total number of pixel axes in NDF */
   int newalg;               /* Use experimental algorithm variations? */
   int nsdim;                /* Number of significant pixel axes in NDF */
   int nslice;               /* Number of slices to process */
   int nval;                 /* Number of values supplied */
   int nystep;               /* Number of independent y slices */
   int nzstep;               /* Number of slices in z direction */
   int sdim[ 3 ];            /* Dimensions of each significant NDF axis */
   int slice_dim[ 3 ];       /* Dimensions of each significant slice axis */
   int slice_lbnd[ 3 ];      /* Lower bounds of each significant slice axis */
   int slice_size;           /* Number of pixels in each slice */
   int state;                /* Parameter state */
   int sub;                  /* Output the background-subtracted input data? */
   int type;                 /* Integer identifier for data type */
   int ubnd[ NDF__MXDIM ];   /* Upper pixel bounds of slice */
   int var;                  /* Does i/p NDF have a Variance component? */
   size_t size;              /* Size of GRP group */
   void *ipd1;               /* Pointer to input Data array */
   void *ipd2;               /* Pointer to output Data array */
   void *ipdin;              /* Pointer to input Data array */
   void *ipdout;             /* Pointer to output Data array */

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

/* Start an NDF context */
   ndfBegin();

/* Record the existing AST status pointer, and ensure AST uses the supplied
   status pointer instead. */
   old_status = astWatch( status );

/* Get an identifier for the input NDF. We use NDG (via kpg1_Rgndf)
   instead of calling ndfAssoc directly since NDF/HDS has problems with
   file names containing spaces, which NDG does not have. */
   kpg1Rgndf( "IN", 1, 1, "", &grp, &size, status );
   ndgNdfas( grp, 1, "READ", &indf1, status );
   grpDelet( &grp, status );

/* Get the pixel index bounds of the input NDF. */
   ndfBound( indf1, NDF__MXDIM, lbnd, ubnd, &ndim, status );

/* Identify and count the number of significant axes (i.e. axes spanning
   more than 1 pixel). Also record their dimensions. */
   nsdim = 0;
   for( i = 0; i < ndim; i++ ) {
      dim[ i ] = ubnd[ i ] - lbnd[ i ] + 1;
      if( dim[ i ] > 1 ) sdim[ nsdim++ ] = dim[ i ];
   }

/* If there are too many significant axes, report an error. */
   if( nsdim > 3 && *status == SAI__OK ) {
       *status = SAI__ERROR;
       ndfMsg( "N", indf1 );
       msgSeti( "NS", nsdim );
       errRep( "", "The NDF '^N' has ^NS significant pixel axes, but this"
               "application requires 1, 2 or 3.", status );
   }

/* Ensure we have 3 values in sdim (pad with trailings 1's if required). */
   if( nsdim < 3 ) sdim[ 2 ] = 1;
   if( nsdim < 2 ) sdim[ 1 ] = 1;

/* See if the output is to contain the background-subtracted data, or the
   background estimate itself. */
   parGet0l( "SUB", &sub, status );

/* Create the output by propagating everything except the Data and
   (if we are outputting the background itself) Variance arrays. */
   if( sub ) {
      ndfProp( indf1, "UNITS,AXIS,WCS,QUALITY,VARIANCE", "OUT", &indf2,
               status );
   } else {
      ndfProp( indf1, "UNITS,AXIS,WCS,QUALITY", "OUT", &indf2, status );
   }

   msgBlankif( MSG__VERB, status );

/* Get the dimensions of each of the filters, in pixels. If only one
   value is supplied, duplicate it as the second value if the second axis
   is significant. If fewer than 3 values were supplied, use 1 for the 3rd
   value (whether or not it is significant). This results in each plane
   being fitted independently of the adjacent planes by default. */
   parGet1i( "BOX", nsdim, box, &nval, status );
   if( *status != SAI__OK ) goto L999;
   if( nval < 2 ) box[ 1 ] = ( nsdim > 1 ) ? box[ 0 ] : 1;
   if( nval < 3 ) box[ 2 ] = 1;

/* Ensure box sizes are odd. */
   box[ 0 ] = 2*( box[ 0 ] / 2 ) + 1;
   box[ 1 ] = 2*( box[ 1 ] / 2 ) + 1;
   box[ 2 ] = 2*( box[ 2 ] / 2 ) + 1;

   msgOutiff( MSG__VERB, "", "Using box sizes [%d,%d,%d].", status,
              box[0], box[1], box[2]);

/* If any trailing axes have a cell size of 1, then we apply the algorithm
   independently to every pixel index on the trailing axes. First of all
   set things up assuming that there are no trailing axes with cell size
   of 1. */
   nystep = 1;
   nzstep = 1;
   slice_dim[ 0 ] = sdim[ 0 ];
   slice_dim[ 1 ] = sdim[ 1 ];
   slice_dim[ 2 ] = sdim[ 2 ];
   slice_lbnd[ 0 ] = lbnd[ 0 ];
   slice_lbnd[ 1 ] = lbnd[ 1 ];
   slice_lbnd[ 2 ] = lbnd[ 2 ];

/* If the 3rd pixel axis has a cell size of 1, arrange that each slice
   contains a single plane. */
   if( box[ 2 ] == 1 ) {
      nzstep = sdim[ 2 ];
      slice_dim[ 2 ] = 1;

/* If the 2nd pixel axis also has a cell size of 1, arrange that each slice
   contains a single row. */
      if( box[ 1 ] == 1 ) {
         nystep = sdim[ 1 ];
         slice_dim[ 1 ] = 1;
      }
   }

/* Determine the number of pixels in each independent slice. */
   slice_size = slice_dim[ 0 ]*slice_dim[ 1 ]*slice_dim[ 2 ];

/* Decide what numeric data type to use, and set the output NDF data type. */
   ndfMtype( "_REAL,_DOUBLE", indf1, indf1, "Data,Variance", itype,
             20, dtype, 20, status );
   if( !strcmp( itype, "_DOUBLE" ) ) {
      type = CUPID__DOUBLE;
   } else {
      type = CUPID__FLOAT;
   }

   ndfStype( dtype, indf2, "Data,Variance", status );

/* Map the input and output arrays. */
   ndfMap( indf1, "Data", itype, "READ", &ipdin, &el, status );
   ndfMap( indf2, "Data", itype, "WRITE", &ipdout, &el, status );

/* If the rms value is supplied on the command, there is no need to
   calculate a default value. */
   parState( "RMS", &state, status );
   if( state == PAR__GROUND ) {

/* Calculate the default RMS value. If the NDF has a Variance component
   it is the square root of the mean Variance value. Otherwise, it is found
   by looking at differences between adjacent pixel values in the Data
   component. */
      ndfState( indf1, "VARIANCE", &var, status );
      if( *status == SAI__OK && var ) {
         ndfMap( indf1, "VARIANCE", "_DOUBLE", "READ", (void *) &ipv, &el, status );

         sum = 0.0;
         n = 0;
         for( i = 0; i < el; i++ ) {
            if( ipv[ i ] != VAL__BADD ) {
               sum += ipv[ i ];
               n++;
            }
         }

         if( n > 0 ) {
            rms = sqrt( sum/n );

         } else {
            *status = SAI__ERROR;
            errRep( "", "The supplied data contains insufficient "
                    "good Variance values to continue.", status );
         }

      } else {
         ipv = NULL;
         rms = cupidRms( type, ipdin, el, sdim[ 0 ], status );
      }

/* Set the default RMS noise level. */
      parDef0d( "RMS", rms, status );
   }

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

/* Get the RMS noise level. */
   parGet0d( "RMS", &rms, status );

/* Annul the error and use an RMS value of VAL__BAD if a null parameter
   value was supplied. This causes an independent default noise estimate to
   be used for each slice of the base NDF. */
   if( *status == PAR__NULL ) {
      errAnnul( status );
      rms = VAL__BADD;
   }

/* See if any experimental algorithm variations are to be used. */
   parGet0l( "NEWALG", &newalg, status );

/* Create a pool of worker threads. */
   wf = thrCreateWorkforce( thrGetNThread( "CUPID_THREADS", status ), status );

/* Get memory to hold a description of each job passed to a worker. There
   is one job for each slice. */
   nslice = nystep*nzstep;
   job_data = astMalloc( nslice*sizeof( *job_data ) );
   if( *status == SAI__OK ) {

/* Loop round all slices to be processed. */
      ipd1 = ipdin;
      ipd2 = ipdout;
      islice = 0;
      pdata = job_data;

      for( izstep = 0; izstep < nzstep ; izstep++ ) {

         slice_lbnd[ 1 ] = lbnd[ 1 ];

         for( iystep = 0; iystep < nystep; iystep++, islice++,pdata++ ) {

/* Store the information needed by the function (cupidFindback0) that
   does the work in a thread. */
            pdata->islice = islice;
            pdata->nslice = nslice;
            pdata->type = type;
            pdata->ndim = ndim;
            pdata->box[ 0 ] = box[ 0 ];
            pdata->box[ 1 ] = box[ 1 ];
            pdata->box[ 2 ] = box[ 2 ];
            pdata->rms = rms;
            pdata->ipd1 = ipd1;
            pdata->ipd2 = ipd2;
            pdata->slice_dim[ 0 ] = slice_dim[ 0 ];
            pdata->slice_lbnd[ 0 ] = slice_lbnd[ 0 ];
            pdata->slice_dim[ 1 ] = slice_dim[ 1 ];
            pdata->slice_lbnd[ 1 ] = slice_lbnd[ 1 ];
            pdata->slice_dim[ 2 ] = slice_dim[ 2 ];
            pdata->slice_lbnd[ 2 ] = slice_lbnd[ 2 ];
            pdata->newalg = newalg;
            pdata->slice_size = slice_size;

/* Submit a job to the workforce to process the current slice. */
            thrAddJob( wf, 0, pdata, cupidFindback0, 0, NULL, status );

/* Update pointers to the start of the next slice in the input and output
   arrays. */
            if( type == CUPID__FLOAT ) {
               ipd1 = ( (float *) ipd1 ) + slice_size;
               ipd2 = ( (float *) ipd2 ) + slice_size;
            } else {
               ipd1 = ( (double *) ipd1 ) + slice_size;
               ipd2 = ( (double *) ipd2 ) + slice_size;
            }

/* Increment the lower bound on the 2nd pixel axis. */
            slice_lbnd[ 1 ]++;
         }

/* Increment the lower bound on the 3rd pixel axis. */
         slice_lbnd[ 2 ]++;
      }

/* Wait until all jobs have finished. */
      thrWait( wf, status );
   }

/* The output currently holds the background estimate. If the user has
   requested that the output should hold the background-subtracted input
   data, then do the arithmetic now. */
   if( sub && *status == SAI__OK ) {
      if( type == CUPID__FLOAT ) {
         pf1 = (float *) ipdin;
         pf2 = (float *) ipdout;
         for( i = 0; i < el; i++, pf1++, pf2++ ) {
            if( *pf1 != VAL__BADR && *pf2 != VAL__BADR ) {
               *pf2 = *pf1 - *pf2;
            } else {
               *pf2 = VAL__BADR;
            }
         }

      } else {
         pd1 = (double *) ipdin;
         pd2 = (double *) ipdout;
         for( i = 0; i < el; i++, pd1++, pd2++ ) {
            if( *pd1 != VAL__BADD && *pd2 != VAL__BADD ) {
               *pd2 = *pd1 - *pd2;
            } else {
               *pd2 = VAL__BADD;
            }
         }

      }
   }

/* Tidy up */
L999:;
   msgBlankif( MSG__VERB, status );

/* Free workspace. */
   job_data = astFree( job_data );
   wf = thrDestroyWorkforce( wf );

/* Reinstate the original AST inherited status value. */
   astWatch( old_status );

/* End the NDF context */
   ndfEnd( status );

/* If an error has occurred, issue another error report identifying the
   program which has failed (i.e. this one). */
   if( *status != SAI__OK ) {
      errRep( "FINDBACK_ERR", "FINDBACK: Failed to find the background "
              "of an NDF.", status );
   }
}
示例#16
0
void smf_mapbounds( int fast, Grp *igrp,  int size, const char *system,
                    AstFrameSet *spacerefwcs, int alignsys, int *lbnd_out,
                    int *ubnd_out, AstFrameSet **outframeset, int *moving,
                    smfBox ** boxes, fts2Port fts_port, int *status ) {

  /* Local Variables */
  AstSkyFrame *abskyframe = NULL; /* Output Absolute SkyFrame */
  int actval;           /* Number of parameter values supplied */
  AstMapping *bolo2map = NULL; /* Combined mapping bolo->map
                                  coordinates, WCS->GRID Mapping from
                                  input WCS FrameSet */
  smfBox *box = NULL;          /* smfBox for current file */
  smfData *data = NULL;        /* pointer to  SCUBA2 data struct */
  double dlbnd[ 2 ];    /* Floating point lower bounds for output map */
  drcntrl_bits drcntrl_mask = 0;/* Mask to use for DRCONTROL on this instrument */
  double dubnd[ 2 ];    /* Floating point upper bounds for output map */
  AstMapping *fast_map = NULL; /* Mapping from tracking to absolute map coords */
  smfFile *file = NULL;        /* SCUBA2 data file information */
  int first;                   /* Is this the first good subscan ? */
  AstFitsChan *fitschan = NULL;/* Fits channels to construct WCS header */
  AstFrameSet *fs = NULL;      /* A general purpose FrameSet pointer */
  smfHead *hdr = NULL;         /* Pointer to data header this time slice */
  int i;                       /* Loop counter */
  dim_t j;                     /* Loop counter */
  AstSkyFrame *junksky = NULL; /* Unused SkyFrame argument */
  dim_t k;                     /* Loop counter */
  int lbnd0[ 2 ];              /* Defaults for LBND parameter */
  double map_pa=0;             /* Map PA in output coord system (rads) */
  dim_t maxloop;               /* Number of times to go round the time slice loop */
  dim_t nbadt  = 0;            /* Number of bad time slices */
  dim_t ngoodt = 0;            /* Number of good time slices */
  double par[7];               /* Projection parameters */
  double shift[ 2 ];           /* Shifts from PIXEL to GRID coords */
  AstMapping *oskymap = NULL;  /* Mapping celestial->map coordinates,
                                  Sky <> PIXEL mapping in output
                                  FrameSet */
  AstSkyFrame *oskyframe = NULL;/* Output SkyFrame */
  char *refsys = NULL;         /* Sky system from supplied reference FrameSet */
  dim_t textreme[4];           /* Time index corresponding to minmax TCS posn */
  AstFrame *skyin = NULL;      /* Sky Frame in input FrameSet */
  double skyref[ 2 ];          /* Values for output SkyFrame SkyRef attribute */
  struct timeval tv1;          /* Timer */
  struct timeval tv2;          /* Timer */
  AstMapping *tmap;            /* Temporary Mapping */
  int trim;                    /* Trim borders of bad pixels from o/p image? */
  int ubnd0[ 2 ];              /* Defaults for UBND parameter */
  double x_array_corners[4];   /* X-Indices for corner bolos in array */
  double x_map[4];             /* Projected X-coordinates of corner bolos */
  double y_array_corners[4];   /* Y-Indices for corner pixels in array */
  double y_map[4];             /* Projected X-coordinates of corner bolos */

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

  /* Start a timer to see how long this takes */
  smf_timerinit( &tv1, &tv2, status );

  /* Initialize pointer to output FrameSet and moving-source flag */
  *outframeset = NULL;
  *moving = 0;

  /* initialize double precision output bounds and the proj pars */
  for( i = 0; i < 7; i++ ) par[ i ] = AST__BAD;
  dlbnd[ 0 ] = VAL__MAXD;
  dlbnd[ 1 ] = VAL__MAXD;
  dubnd[ 0 ] = VAL__MIND;
  dubnd[ 1 ] = VAL__MIND;

  /* If we have a supplied reference WCS we can use that directly
     without having to calculate it from the data. Replace the requested
     system with the system from the reference FrameSet (take a copy of the
     string since astGetC may re-use its buffer). */
  if (spacerefwcs) {
     oskyframe = astGetFrame( spacerefwcs, AST__CURRENT );
     int nc = 0;
     refsys = astAppendString( NULL, &nc, astGetC( oskyframe, "System" ) );
     system = refsys;
  }

  /* Create array of returned smfBox structures and store a pointer
     to the next one to be initialised. */
  *boxes = astMalloc( sizeof( smfBox ) * size );
  box = *boxes;

  astBegin;

  /* Loop over all files in the Grp */
  first = 1;
  for( i=1; i<=size; i++, box++ ) {

    /* Initialise the spatial bounds of section of the the output cube that is
       contributed to by the current ionput file. */
    box->lbnd[ 0 ] = VAL__MAXD;
    box->lbnd[ 1 ] = VAL__MAXD;
    box->ubnd[ 0 ] = VAL__MIND;
    box->ubnd[ 1 ] = VAL__MIND;

    /* Read data from the ith input file in the group */
    smf_open_file( NULL, igrp, i, "READ", SMF__NOCREATE_DATA, &data, status );

    if (*status != SAI__OK) {
      msgSeti( "I", i );
      errRep( "smf_mapbounds", "Could not open data file no ^I.", status );
      break;
    } else {
      if( *status == SAI__OK ) {
        if( data->file == NULL ) {
          *status = SAI__ERROR;
          errRep( FUNC_NAME, "No smfFile associated with smfData.",
                  status );
          break;

        } else if( data->hdr == NULL ) {
          *status = SAI__ERROR;
          errRep( FUNC_NAME, "No smfHead associated with smfData.",
                  status );
          break;

        } else if( data->hdr->fitshdr == NULL ) {
          *status = SAI__ERROR;
          errRep( FUNC_NAME, "No FITS header associated with smfHead.",
                  status );
          break;

        }
      }
    }

    /* convenience pointers */
    file = data->file;
    hdr = data->hdr;

    /* report name of the input file */
    smf_smfFile_msg( file, "FILE", 1, "<unknown>" );
    msgSeti("I", i);
    msgSeti("N", size);
    msgOutif(MSG__VERB, " ",
             "SMF_MAPBOUNDS: Processing ^I/^N ^FILE",
             status);

/* Check that there are 3 pixel axes. */
    if( data->ndims != 3 ) {
      smf_smfFile_msg( file, "FILE", 1, "<unknown>" );
      msgSeti( "NDIMS", data->ndims );
      *status = SAI__ERROR;
      errRep( FUNC_NAME, "^FILE has ^NDIMS pixel axes, should be 3.",
              status );
      break;
    }

    /* Check that the data dimensions are 3 (for time ordered data) */
    if( *status == SAI__OK ) {

      /* If OK Decide which detectors (GRID coord) to use for
         checking bounds, depending on the instrument in use. */

      switch( hdr->instrument ) {

      case INST__SCUBA2:
        drcntrl_mask = DRCNTRL__POSITION;
        /* 4 corner bolometers of the subarray */
        x_array_corners[0] = 1;
        x_array_corners[1] = 1;
        x_array_corners[2] = (data->dims)[0];
        x_array_corners[3] = (data->dims)[0];

        y_array_corners[0] = 1;
        y_array_corners[1] = (data->dims)[1];
        y_array_corners[2] = 1;
        y_array_corners[3] = (data->dims)[1];
        break;

      case INST__AZTEC:
        /* Rough guess for extreme bolometers around the edge */
        x_array_corners[0] = 22;
        x_array_corners[1] = 65;
        x_array_corners[2] = 73;
        x_array_corners[3] = 98;

        y_array_corners[0] = 1; /* Always 1 for AzTEC */
        y_array_corners[1] = 1;
        y_array_corners[2] = 1;
        y_array_corners[3] = 1;
        break;

      case INST__ACSIS:
        smf_find_acsis_corners( data, x_array_corners, y_array_corners,
                                status);
        break;

      default:
        *status = SAI__ERROR;
        errRep(FUNC_NAME, "Don't know how to calculate mapbounds for data created with this instrument", status);
      }
    }

    if( *status == SAI__OK) {
      size_t goodidx = SMF__BADSZT;

      /* Need to build up a frameset based on good telescope position.
         We can not assume that we the first step will be a good TCS position
         so we look for one. If we can not find anything we skip to the
         next file. */
      maxloop = (data->dims)[2];
      for (j=0; j<maxloop; j++) {
        JCMTState state = (hdr->allState)[j];
        if (state.jos_drcontrol >= 0 && state.jos_drcontrol & drcntrl_mask ) {
          /* bad TCS - so try again */
        } else {
          /* Good tcs */
          goodidx = j;
          break;
        }
      }

      if (goodidx == SMF__BADSZT) {
        smf_smfFile_msg( data->file, "FILE", 1, "<unknown>");
        msgOutif( MSG__QUIET, "", "No good telescope positions found in file ^FILE. Ignoring",
                  status );
        smf_close_file( NULL, &data, status );
        continue;
      }

      /* If we are dealing with the first good file, create the output
         SkyFrame. */
      if( first ) {
        first = 0;

        /* Create output SkyFrame if it has not come from a reference */
        if ( oskyframe == NULL ) {

          /* smf_tslice_ast only needs to get called once to set up framesets */
          if( hdr->wcs == NULL ) {
            smf_tslice_ast( data, goodidx, 1, fts_port, status);
          }

          /* Retrieve input SkyFrame */
          skyin = astGetFrame( hdr->wcs, AST__CURRENT );

          smf_calc_skyframe( skyin, system, hdr, alignsys, &oskyframe, skyref,
                             moving, status );

          /* Get the orientation of the map vertical within the output celestial
             coordinate system. This is derived form the MAP_PA FITS header, which
             gives the orientation of the map vertical within the tracking system. */
          map_pa = smf_calc_mappa( hdr, system, skyin, status );

          /* Provide a sensible default for the pixel size based on wavelength */
          par[4] = smf_calc_telres( hdr->fitshdr, status );
          par[4] *= AST__DD2R/3600.0;
          par[5] = par[4];

          /* Calculate the projection parameters. We do not enable autogrid determination
             for SCUBA-2 so we do not need to obtain all the data before calculating
             projection parameters. */
          smf_get_projpar( oskyframe, skyref, *moving, 0, 0, NULL, 0,
                           map_pa, par, NULL, NULL, status );

          if (skyin) skyin = astAnnul( skyin );

        /* If the output skyframe has been supplied, we still need to
           determine whether the source is moving or not, and set the
           reference position. */
        } else {

          /* smf_tslice_ast only needs to get called once to set up framesets */
          if( hdr->wcs == NULL ) {
            smf_tslice_ast( data, goodidx, 1, fts_port, status);
          }

          /* Retrieve input SkyFrame */
          skyin = astGetFrame( hdr->wcs, AST__CURRENT );
          smf_calc_skyframe( skyin, system, hdr, alignsys, &junksky, skyref,
                             moving, status );

          /* Store the sky reference position. If the target is moving,
             ensure the returned SkyFrame represents offsets from the
             reference position rather than absolute coords. */
          astSetD( oskyframe, "SkyRef(1)", skyref[ 0 ] );
          astSetD( oskyframe, "SkyRef(2)", skyref[ 1 ] );
          if( *moving ) astSet( oskyframe, "SkyRefIs=Origin" );

          /* Ensure the Epoch attribute in the map is set to the date of
             the first data in the map, rather than the date in supplied
             reference WCS. */
          astSetD( oskyframe, "Epoch", astGetD( junksky, "Epoch" ) );
        }

        if ( *outframeset == NULL && oskyframe != NULL && (*status == SAI__OK)){
          /* Now created a spatial Mapping. Use the supplied reference frameset
             if supplied */
          if (spacerefwcs) {
            oskymap = astGetMapping( spacerefwcs, AST__BASE, AST__CURRENT );
          } else {
            /* Now populate a FitsChan with FITS-WCS headers describing
               the required tan plane projection. The longitude and
               latitude axis types are set to either (RA,Dec) or (AZ,EL)
               to get the correct handedness. */
            fitschan = astFitsChan ( NULL, NULL, " " );
            smf_makefitschan( astGetC( oskyframe, "System"), &(par[0]),
                              &(par[2]), &(par[4]), par[6], fitschan, status );
            astClear( fitschan, "Card" );
            fs = astRead( fitschan );

            /* Extract the output PIXEL->SKY Mapping. */
            oskymap = astGetMapping( fs, AST__BASE, AST__CURRENT );

            /* Tidy up */
            fs = astAnnul( fs );
          }

          /* Create the output FrameSet */
          *outframeset = astFrameSet( astFrame(2, "Domain=GRID"), " " );

          /* Now add the SkyFrame to it */
          astAddFrame( *outframeset, AST__BASE, oskymap, oskyframe );

          /* Now add a POLANAL Frame if required (i.e. if the input time
             series are POL-2 Q/U values). */
          smf_addpolanal( *outframeset, hdr, status );

          /* Invert the oskymap mapping */
          astInvert( oskymap );

        } /* End WCS FrameSet construction */
      }

      /* Get a copy of the output SkyFrame and ensure it represents
         absolute coords rather than offset coords. */
      abskyframe = astCopy( oskyframe );
      astClear( abskyframe, "SkyRefIs" );
      astClear( abskyframe, "AlignOffset" );

      maxloop = (data->dims)[2];
      if (fast) {
        /* For scan map we scan through looking for largest telescope moves.
           For dream/stare we just look at the start and end time slices to
           account for sky rotation. */

        if (hdr->obsmode != SMF__OBS_SCAN) {
          textreme[0] = 0;
          textreme[1] = (data->dims)[2] - 1;
          maxloop = 2;

        } else {
          const char *tracksys;
          double *ac1list, *ac2list, *bc1list, *bc2list, *p1, *p2, *p3, *p4;
          double flbnd[4], fubnd[4];
          JCMTState state;

          /* If the output and tracking systems are different, get a
             Mapping between them. */
          tracksys = sc2ast_convert_system( (hdr->allState)[goodidx].tcs_tr_sys,
                                            status );
          if( strcmp( system, tracksys ) ) {
             AstSkyFrame *tempsf = astCopy( abskyframe );
             astSetC( tempsf, "System", tracksys );
             AstFrameSet *tempfs = astConvert( tempsf, abskyframe, "" );
             tmap = astGetMapping( tempfs, AST__BASE, AST__CURRENT );
             fast_map = astSimplify( tmap );
             tmap = astAnnul( tmap );
             tempsf = astAnnul( tempsf );
             tempfs = astAnnul( tempfs );
          } else {
             fast_map = NULL;
          }

          /* Copy all ac1/2 positions into two array, and transform them
             from tracking to absolute output sky coords. */
          ac1list = astMalloc( maxloop*sizeof( *ac1list ) );
          ac2list = astMalloc( maxloop*sizeof( *ac2list ) );
          if( *status == SAI__OK ) {
             p1 = ac1list;
             p2 = ac2list;
             for( j = 0; j < maxloop; j++ ) {
                state = (hdr->allState)[ j ];
                *(p1++) = state.tcs_tr_ac1;
                *(p2++) = state.tcs_tr_ac2;
             }
             if( fast_map ) astTran2( fast_map, maxloop, ac1list, ac2list, 1,
                                      ac1list, ac2list );
          }

          /* If the target is moving, we need to adjust these ac1/2 values
             to represent offsets from the base position. */
          if( *moving ) {

          /* Copy all bc1/2 positions into two arrays. */
             bc1list = astMalloc( maxloop*sizeof( *bc1list ) );
             bc2list = astMalloc( maxloop*sizeof( *bc2list ) );
             if( *status == SAI__OK ) {
                p1 = bc1list;
                p2 = bc2list;

                for( j = 0; j < maxloop; j++ ) {
                   state = (hdr->allState)[ j ];
                   *(p1++) = state.tcs_tr_bc1;
                   *(p2++) = state.tcs_tr_bc2;
                }

                /* Transform them from tracking to absolute output sky coords. */
                if( fast_map ) astTran2( fast_map, maxloop, bc1list, bc2list,
                                         1, bc1list, bc2list );

                /* Replace each ac1/2 position with the offsets from the
                   corresponding base position. */
                p1 = bc1list;
                p2 = bc2list;
                p3 = ac1list;
                p4 = ac2list;
                for( j = 0; j < maxloop; j++ ) {
                  smf_offsets( *(p1++), *(p2++), p3++, p4++, status );
                }
             }

             /* We no longer need the base positions. */
             bc1list = astFree( bc1list );
             bc2list = astFree( bc2list );
          }

          /* Transform the ac1/2 position from output sky coords to
             output pixel coords. */
          astTran2( oskymap, maxloop, ac1list, ac2list, 1, ac1list, ac2list );

          /* Find the bounding box containing these pixel coords and the
             time slices at which the boresight touches each edge of this
             box. */
          flbnd[ 0 ] = VAL__MAXD;
          flbnd[ 1 ] = VAL__MAXD;
          fubnd[ 0 ] = VAL__MIND;
          fubnd[ 1 ] = VAL__MIND;
          for( j = 0; j < 4; j++ ) textreme[ j ] = (dim_t) VAL__BADI;

          if( *status == SAI__OK ) {
             p1 = ac1list;
             p2 = ac2list;
             for( j = 0; j < maxloop; j++,p1++,p2++ ) {
                if( *p1 != VAL__BADD && *p2 != VAL__BADD ){

                   if ( *p1 < flbnd[0] ) { flbnd[0] = *p1; textreme[0] = j; }
                   if ( *p2 < flbnd[1] ) { flbnd[1] = *p2; textreme[1] = j; }
                   if ( *p1 > fubnd[0] ) { fubnd[0] = *p1; textreme[2] = j; }
                   if ( *p2 > fubnd[1] ) { fubnd[1] = *p2; textreme[3] = j; }
                }
             }
          }

          maxloop = 4;
          msgSetd("X1", textreme[0]);
          msgSetd("X2", textreme[1]);
          msgSetd("X3", textreme[2]);
          msgSetd("X4", textreme[3]);
          msgOutif( MSG__DEBUG, " ",
                    "Extrema time slices are ^X1, ^X2, ^X3 and ^X4",
                    status);

          ac1list = astFree( ac1list );
          ac2list = astFree( ac2list );

        }
      }

      /* Get the astrometry for all the relevant time slices in this data file */
      for( j=0; j<maxloop; j++ ) {
        dim_t ts;  /* Actual time slice to use */

        /* if we are doing the fast loop, we need to read the time slice
           index from textreme. Else we just use the index */
        if (fast) {
          /* get the index but make sure it is good */
          ts = textreme[j];
          if (ts == (dim_t)VAL__BADI) continue;
        } else {
          ts = j;
        }
        /* Calculate the bolo to map-pixel transformation for this tslice */
        bolo2map = smf_rebin_totmap( data, ts, abskyframe, oskymap,
                                     *moving, fts_port, status );

        if ( *status == SAI__OK ) {
          /* skip if we did not get a mapping this time round */
          if (!bolo2map) continue;

          /* Check corner pixels in the array for their projected extent
             on the sky to set the pixel bounds */
          astTran2( bolo2map, 4, x_array_corners, y_array_corners, 1,
                    x_map, y_map );

          /* Update min/max for this time slice */
          for( k=0; k<4; k++ ) {

            if( x_map[k] != AST__BAD && y_map[k] != AST__BAD ) {
              if( x_map[k] < dlbnd[0] ) dlbnd[0] = x_map[k];
              if( y_map[k] < dlbnd[1] ) dlbnd[1] = y_map[k];
              if( x_map[k] > dubnd[0] ) dubnd[0] = x_map[k];
              if( y_map[k] > dubnd[1] ) dubnd[1] = y_map[k];

              if( x_map[k] < box->lbnd[0] ) box->lbnd[0] = x_map[k];
              if( y_map[k] < box->lbnd[1] ) box->lbnd[1] = y_map[k];
              if( x_map[k] > box->ubnd[0] ) box->ubnd[0] = x_map[k];
              if( y_map[k] > box->ubnd[1] ) box->ubnd[1] = y_map[k];

            } else if( *status == SAI__OK ) {
              *status = SAI__ERROR;
              errRep( FUNC_NAME, "Extreme positions are bad.", status );
              break;
            }
          }
        }
        /* Explicitly annul these mappings each time slice for reduced
           memory usage */
        if (bolo2map) bolo2map = astAnnul( bolo2map );
        if (fs) fs = astAnnul( fs );

        /* Break out of loop over time slices if bad status */
        if (*status != SAI__OK) goto CLEANUP;
      }

      /* Annul any remaining Ast objects before moving on to the next file */
      if (fs) fs = astAnnul( fs );
      if (bolo2map) bolo2map = astAnnul( bolo2map );
    }

    /* Close the data file */
    smf_close_file( NULL, &data, status);

    /* Break out of loop over data files if bad status */
    if (*status != SAI__OK) goto CLEANUP;
  }

  /* make sure we got values - should not be possible with good status */
  if (dlbnd[0] == VAL__MAXD || dlbnd[1] == VAL__MAXD) {
    if (*status == SAI__OK) {
      *status = SAI__ERROR;
      errRep( " ", "Unable to find any valid map bounds", status );
    }
  }

  if (nbadt > 0) {
    msgOutf( "", "   Processed %zu time slices to calculate bounds,"
             " of which %zu had bad telescope data and were skipped",
             status, (size_t)(ngoodt+nbadt), (size_t)nbadt );
  }

  /* If spatial reference wcs was supplied, store par values that result in
     no change to the pixel origin. */
  if( spacerefwcs ){
    par[ 0 ] = 0.5;
    par[ 1 ] = 0.5;
  }

  /* Need to re-align with the interim GRID coordinates */
  lbnd_out[0] = ceil( dlbnd[0] - par[0] + 0.5 );
  ubnd_out[0] = ceil( dubnd[0] - par[0] + 0.5 );
  lbnd_out[1] = ceil( dlbnd[1] - par[1] + 0.5 );
  ubnd_out[1] = ceil( dubnd[1] - par[1] + 0.5 );

  /* Do the same with the individual input file bounding boxes */
  box = *boxes;
  for (i = 1; i <= size; i++, box++) {
    box->lbnd[0] = ceil( box->lbnd[0] - par[0] + 0.5);
    box->ubnd[0] = ceil( box->ubnd[0] - par[0] + 0.5);
    box->lbnd[1] = ceil( box->lbnd[1] - par[1] + 0.5);
    box->ubnd[1] = ceil( box->ubnd[1] - par[1] + 0.5);
  }

  /* Apply a ShiftMap to the output FrameSet to re-align the GRID
     coordinates */
  shift[0] = 2.0 - par[0] - lbnd_out[0];
  shift[1] = 2.0 - par[1] - lbnd_out[1];
  astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) );

  /* Set the dynamic defaults for lbnd/ubnd */
  lbnd0[ 0 ] = lbnd_out[ 0 ];
  lbnd0[ 1 ] = lbnd_out[ 1 ];
  parDef1i( "LBND", 2, lbnd0, status );

  ubnd0[ 0 ] = ubnd_out[ 0 ];
  ubnd0[ 1 ] = ubnd_out[ 1 ];
  parDef1i( "UBND", 2, ubnd0, status );

  parGet1i( "LBND", 2, lbnd_out, &actval, status );
  if( actval == 1 ) lbnd_out[ 1 ] = lbnd_out[ 0 ];

  parGet1i( "UBND", 2, ubnd_out, &actval, status );
  if( actval == 1 ) ubnd_out[ 1 ] = ubnd_out[ 0 ];

  /* Ensure the bounds are the right way round. */
  if( lbnd_out[ 0 ] > ubnd_out[ 0 ] ) {
    int itmp = lbnd_out[ 0 ];
    lbnd_out[ 0 ] = ubnd_out[ 0 ];
    ubnd_out[ 0 ] = itmp;
  }

  if( lbnd_out[ 1 ] > ubnd_out[ 1 ] ) {
    int itmp = lbnd_out[ 1 ];
    lbnd_out[ 1 ] = ubnd_out[ 1 ];
    ubnd_out[ 1 ] = itmp;
  }

  /* If borders of bad pixels are being trimmed from the output image,
     then do not allow the user-specified bounds to extend outside the
     default bounding box (since we know that the default bounding box
     encloses all available data). */
  parGet0l( "TRIM", &trim, status );
  if( trim ) {
     if( lbnd_out[ 0 ] < lbnd0[ 0 ] ) lbnd_out[ 0 ] = lbnd0[ 0 ];
     if( lbnd_out[ 1 ] < lbnd0[ 1 ] ) lbnd_out[ 1 ] = lbnd0[ 1 ];
     if( ubnd_out[ 0 ] > ubnd0[ 0 ] ) ubnd_out[ 0 ] = ubnd0[ 0 ];
     if( ubnd_out[ 1 ] > ubnd0[ 1 ] ) ubnd_out[ 1 ] = ubnd0[ 1 ];
  }

  /* Modify the returned FrameSet to take account of the new pixel origin. */
  shift[ 0 ] = lbnd0[ 0 ] - lbnd_out[ 0 ];
  shift[ 1 ] = lbnd0[ 1 ] - lbnd_out[ 1 ];
  if( shift[ 0 ] != 0.0 || shift[ 1 ] != 0.0 ) {
    astRemapFrame( *outframeset, AST__BASE, astShiftMap( 2, shift, " " ) );
  }

/* Report the pixel bounds of the cube. */
  if( *status == SAI__OK ) {
    msgOutif( MSG__NORM, " ", " ", status );
    msgSeti( "XL", lbnd_out[ 0 ] );
    msgSeti( "YL", lbnd_out[ 1 ] );
    msgSeti( "XU", ubnd_out[ 0 ] );
    msgSeti( "YU", ubnd_out[ 1 ] );
    msgOutif( MSG__NORM, " ", "   Output map pixel bounds: ( ^XL:^XU, ^YL:^YU )",
              status );

    if( ( ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1 ) > MAX_DIM ||
        ( ubnd_out[ 1 ] - lbnd_out[ 1 ] + 1 ) > MAX_DIM ) {
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME ": The map is too big. Check your list of input "
              "data files does not include widely separated observations.",
              status );
    }
  }

  /* If no error has occurred, export the returned FrameSet pointer from the
     current AST context so that it will not be annulled when the AST
     context is ended. Otherwise, ensure a null pointer is returned. */
  if( *status == SAI__OK ) {
    astExport( *outframeset );
  } else {
    *outframeset = astAnnul( *outframeset );
  }

  msgOutiff( SMF__TIMER_MSG, "",
             "Took %.3f s to calculate map bounds",
             status, smf_timerupdate( &tv1, &tv2, status ) );

  /* Clean Up */
 CLEANUP:
  if (*status != SAI__OK) {
    errRep(FUNC_NAME, "Unable to determine map bounds", status);
  }
  if (oskymap) oskymap  = astAnnul( oskymap );
  if (bolo2map) bolo2map = astAnnul( bolo2map );
  if (fitschan) fitschan = astAnnul( fitschan );

  if( data != NULL )
    smf_close_file( NULL, &data, status );

  refsys = astFree( refsys );

  astEnd;

}
示例#17
0
void smurf_jsatileinfo( int *status ) {

/* Local Variables */
   AstCmpRegion *overlap;
   AstFitsChan *fc;
   AstFrameSet *fs;
   AstObject *obj;
   AstRegion *region;
   AstRegion *target;
   HDSLoc *cloc = NULL;
   HDSLoc *xloc = NULL;
   char *jcmt_tiles;
   char *tilendf = NULL;
   char text[ 200 ];
   double dec[ 8 ];
   double dist;
   double dlbnd[ 2 ];
   double dubnd[ 2 ];
   double gx[ 8 ];
   double gy[ 8 ];
   double maxdist;
   double norm_radec[2];
   double point1[ 2 ];
   double point2[ 2 ];
   double ra[ 8 ];
   double ra0;
   double dec0;
   int *ipntr;
   int axes[ 2 ];
   int create;
   int dirlen;
   int el;
   int exists;
   int flag;
   int i;
   int indf1;
   int indf2;
   int indf3;
   int itile;
   int iv;
   int jtile;
   int lbnd[ 2 ];
   int local_origin;
   int nc;
   int place;
   int tlbnd[ 2 ];
   int tubnd[ 2 ];
   int ubnd[ 2 ];
   smf_jsaproj_t proj;
   int xt;
   int yt;
   smfJSATiling skytiling;
   void *pntr;

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

/* Start a new AST context. */
   astBegin;

/* Get the instrument to use abnd get the parameters describing the
   layout of its JSA tiles. */
   smf_jsainstrument( "INSTRUMENT", NULL, SMF__INST_NONE, &skytiling,
                      status );

/* Return the maximum tile index. */
   parPut0i( "MAXTILE", skytiling.ntiles - 1, status );

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

/* Decide what sort of projection to use. */
   parChoic( "PROJ", "HPX", "HPX,HPX12,XPHN,XPHS", 1, text, sizeof(text),
             status );
   proj = smf_jsaproj_fromstr( text, 1, status );

/* If required, create an all-sky NDF in which each pixel covers the area
   of a single tile, and holds the integer tile index. The NDF has an
   initial size of 1x1 pixels, but is expanded later to the required size. */
   lbnd[ 0 ] = ubnd[ 0 ] = lbnd[ 1 ] = ubnd[ 1 ] = 1;
   ndfCreat( "ALLSKY", "_INTEGER", 2, lbnd, ubnd, &indf3, status );

/* If a null (!) value was supplied for parameter ALLSKY, annull the
   error and pass on. */
   if( *status == PAR__NULL ) {
      errAnnul( status );

/* Otherwise, create a FrameSet describing the whole sky in which each
   pixel corresponds to a single tile. */
   } else {
      smf_jsatile( -1, &skytiling, 0, proj, NULL, &fs, NULL, lbnd, ubnd,
                   status );

/* Change the bounds of the output NDF. */
      ndfSbnd( 2, lbnd, ubnd, indf3, status );

/* Store the FrameSet in the NDF. */
      ndfPtwcs( fs, indf3, status );

/* Map the data array. */
      ndfMap( indf3, "Data", "_INTEGER", "WRITE/BAD", (void **) &ipntr, &el,
              status );

/* Create all-sky map using XPH projection. */
      if( *status == SAI__OK ) {

/* Loop round every tile index. */
         for( jtile = 0; jtile < skytiling.ntiles; jtile++ ) {

/* Get the zero-based (x,y) indices of the tile within an HPX projection.
   This flips the bottom left half-facet up to the top right. */
            smf_jsatilei2xy( jtile, &skytiling, &xt, &yt, NULL, status );

/* Convert these HPX indices to the corresponding indices within the
   required projection. Note, the lower left facet is split by the above
   call to smf_jsatilei2xy tile (i.e. (xt,yt) indices are *not* in the
   "raw" mode). For instance, (0,0) is not a valid tile. */
            smf_jsatilexyconv( &skytiling, proj, xt, yt, 0, &xt, &yt, status );

/* Get the vector index of the corresponding element of the all-sky NDF. */
            if( proj == SMF__JSA_HPX || proj == SMF__JSA_HPX12 ) {
               iv = xt + 5*skytiling.ntpf*yt;
            } else {
               iv = xt + 4*skytiling.ntpf*yt;
            }

/* Report an error if this element has already been assigned a tile
   index. Otherwise, store the tile index. */
            if( ipntr[ iv ] == VAL__BADI ) {
               ipntr[ iv ] = jtile;
            } else if( *status == SAI__OK ) {
               *status = SAI__ERROR;
               errRepf( "", "%s projection assigns multiple tiles to "
                        "pixel (%d,%d).", status, text, xt, yt );
               break;
            }
         }
      }

/* Store NDF title. */
      sprintf( text, "JSA tile indices for %s data", skytiling.name );
      ndfCput( text, indf3, "TITLE", status );

/* Store the instrument as a component in the SMURF extension. */
      ndfXnew( indf3, "SMURF", "INSTRUMENT", 0, 0, &xloc, status );
      ndfXpt0c( skytiling.name, indf3, "SMURF", "INSTRUMENT", status );
      datAnnul( &xloc, status );

/* Close the NDF. */
      ndfAnnul( &indf3, status );
   }

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

/* Get the zero-based index of the required tile. If a null value is
   supplied, annull the error and skip to the end. */
   parGdr0i( "ITILE", 0, 0, skytiling.ntiles - 1, 0, &itile, status );
   if( *status == PAR__NULL ) {
       errAnnul( status );
       goto L999;
   }

/* See if the pixel origin is to be at the centre of the tile. */
   parGet0l( "LOCAL", &local_origin, status );

/* Display the tile number. */
   msgBlank( status );
   msgSeti( "ITILE", itile );
   msgSeti( "MAXTILE", skytiling.ntiles - 1);
   msgOut( " ", "   Tile ^ITILE (from 0 to ^MAXTILE):", status );

/* Get the FITS header, FrameSet and Region defining the tile, and the tile
   bounds in pixel indices. */
   smf_jsatile( itile, &skytiling, local_origin,  proj, &fc, &fs, &region,
                lbnd, ubnd, status );

/* Write the FITS headers out to a file, annulling the error if the
   header is not required. */
   if( *status == SAI__OK ) {
      atlDumpFits( "HEADER", fc, status );
      if( *status == PAR__NULL ) errAnnul( status );
   }

/* If required, write the Region out to a text file. */
   if( *status == SAI__OK ) {
      atlCreat( "REGION", (AstObject *) region, status );
      if( *status == PAR__NULL ) errAnnul( status );
   }

/* Store the lower and upper pixel bounds of the tile. */
   parPut1i( "LBND", 2, lbnd, status );
   parPut1i( "UBND", 2, ubnd, status );

/* Display pixel bounds on the screen. */
   msgSeti( "XL", lbnd[ 0 ] );
   msgSeti( "XU", ubnd[ 0 ] );
   msgSeti( "YL", lbnd[ 1 ] );
   msgSeti( "YU", ubnd[ 1 ] );
   msgOut( " ", "      Pixel bounds: (^XL:^XU,^YL:^YU)", status );

/* Get the RA,Dec at the tile centre. */
   if( astTest( fs, "SkyRef" ) ) {
      ra0 = astGetD( fs, "SkyRef(1)" );
      dec0 = astGetD( fs, "SkyRef(2)" );

/* Format the central RA and Dec. and display. Call astNorm on the
   coordinates provided that the frame set has the correct number of
   axes (which it should as it comes from smf_jsatile). */
      norm_radec[0] = ra0;
      norm_radec[1] = dec0;
      if (astGetI(fs, "Naxes") == 2) astNorm(fs, norm_radec);
      msgSetc( "RACEN",  astFormat( fs, 1, norm_radec[ 0 ] ));
      msgSetc( "DECCEN",  astFormat( fs, 2, norm_radec[ 1 ] ));
      msgOut( " ", "      Centre (ICRS): RA=^RACEN DEC=^DECCEN", status );

/* Transform a collection of points on the edge of the region (corners and
   side mid-points) from GRID coords to RA,Dec. */
      point1[ 0 ] = 0.5;
      point1[ 1 ] = 0.5;
      point2[ 0 ] = ubnd[ 0 ] - lbnd[ 0 ] + 1;
      point2[ 1 ] = ubnd[ 1 ] - lbnd[ 1 ] + 1;

      gx[ 0 ] = point1[ 0 ];         /* Bottom left */
      gy[ 0 ] = point1[ 1 ];

      gx[ 1 ] = point1[ 0 ];         /* Centre left */
      gy[ 1 ] = gy[ 0 ];

      gx[ 2 ] = point1[ 0 ];         /* Top left */
      gy[ 2 ] = point2[ 1 ];

      gx[ 3 ] = gx[ 0 ];             /* Top centre */
      gy[ 3 ] = point2[ 1 ];

      gx[ 4 ] = point2[ 0 ];         /* Top right */
      gy[ 4 ] = point2[ 1 ];

      gx[ 5 ] = point2[ 0 ];         /* Centre right */
      gy[ 5 ] = gy[ 0 ];

      gx[ 6 ] = point2[ 0 ];         /* Bottom right */
      gy[ 6 ] = point1[ 1 ];

      gx[ 7 ] = gx[ 0 ];             /* Bottom centre */
      gy[ 7 ] = point1[ 1 ];

      astTran2( fs, 8, gx, gy, 1, ra, dec );

/* Find the arc-distance from the centre to the furthest point from the
   centre. */
      point1[ 0 ] = ra0;
      point1[ 1 ] = dec0;
      maxdist = -1.0;

      for( i = 1; i < 8; i++ ) {
         if( ra[ i ] != AST__BAD && dec[ i ] != AST__BAD ) {
            point2[ 0 ] = ra[ i ];
            point2[ 1 ] = dec[ i ];
            dist = astDistance( fs, point1, point2 );
            if( dist > maxdist ) maxdist = dist;
         }
      }

/* Convert from radius to diameter. */
      maxdist *= 2.0;

/* Format this size as a dec value (i.e. arc-distance) and display it. */
      if( maxdist > 0.0 ) {
         msgSetc( "SIZE",  astFormat( fs, 2, maxdist ) );
         msgOut( " ", "      Size: ^SIZE", status );
      } else {
         maxdist = AST__BAD;
         msgOut( " ", "      Size: <unknown>", status );
      }

/* If a discontinuity passes through the tile, the centre and size may be
   unknown. */
   } else {
      ra0 = AST__BAD;
      dec0 = AST__BAD;
      maxdist = AST__BAD;
      msgOut( " ", "      Centre (ICRS): RA=<unknown> DEC=<unknown>", status );
      msgOut( " ", "      Size: <unknown>", status );
   }

/* Write the tile centre ra and dec in radians to the output parameters. */
   parPut0d( "RACEN", norm_radec[ 0 ], status );
   parPut0d( "DECCEN", norm_radec[ 1 ], status );

/* Write the size to the output parameter as radians. */
   parPut0d( "SIZE", maxdist, status );

/* Get the translation of the environment variable JSA_TILE_DIR. */
   jcmt_tiles = getenv( "JSA_TILE_DIR" );

/* Initialise the path to the tile's NDF to hold the root directory.
   Use the current working directory if JSA_TILE_DIR is undefined. */
   if( jcmt_tiles ) {
      nc = 0;
      tilendf = astAppendString( tilendf, &nc, jcmt_tiles );

   } else {

      nc = 512;
      jcmt_tiles = astMalloc( nc );

      while( !getcwd( jcmt_tiles, nc ) ) {
         nc *= 2;
         jcmt_tiles = astRealloc( jcmt_tiles, nc );
      }

      nc = 0;
      tilendf = astAppendString( tilendf, &nc, jcmt_tiles );
      jcmt_tiles = astFree( jcmt_tiles );
   }

/* Complete the path to the tile's NDF. */
   tilendf = astAppendString( tilendf, &nc, "/" );
   tilendf = astAppendString( tilendf, &nc, skytiling.subdir );
   dirlen = nc;
   sprintf( text, "/tile_%d.sdf", itile );
   tilendf = astAppendString( tilendf, &nc, text );

/* Write it to the output parameter. */
   parPut0c( "TILENDF", tilendf, status );

/* See if the NDF exists, and store the flag in the output parameter. */
   exists = access( tilendf, F_OK ) ? 0 : 1;
   parPut0l( "EXISTS", exists, status );

/* If the NDF does not exist, create it if required. */
   parGet0l( "CREATE", &create, status );
   if( !exists && create && *status == SAI__OK ) {

/* Write the NDF info to the screen. */
      msgSetc( "NDF",  tilendf );
      msgOutif( MSG__NORM, " ", "      NDF: ^NDF (created)", status );

/* Temporarily terminate the NDF path at the end of the subdirectory. */
      tilendf[ dirlen ] = 0;

/* Create the required directory (does nothing if the directory
   already exists).  It is given read/write/search permissions for owner
   and group, and read/search permissions for others. */
      (void) mkdir( tilendf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );

/* Replace the character temporarily overwritten above. */
      tilendf[ dirlen ] = '/';

/* Now create the tile's NDF. */
      ndfPlace( NULL, tilendf, &place, status );
      ndfNew( skytiling.type, 2, lbnd, ubnd, &place, &indf1, status );

/* Fill its data array with zeros. */
      ndfMap( indf1, "Data", skytiling.type, "WRITE/ZERO", &pntr, &el,
              status );

/* Store the WCS FrameSet. */
      ndfPtwcs( fs, indf1, status );

/* If the instrument jsatiles.have variance, fill the variance array with zeros. */
      if( skytiling.var ) {
         ndfMap( indf1, "Variance", skytiling.type, "WRITE/ZERO", &pntr,
                 &el, status );
      }

/* Create a SMURF extension. */
      ndfXnew( indf1, SMURF__EXTNAME, SMURF__EXTTYPE, 0, NULL, &xloc, status );

/* Store the tile number and instrument name in the extension. */
      datNew0I( xloc, "TILE", status );
      datFind( xloc, "TILE", &cloc, status );
      datPut0I( cloc, itile, status );
      datAnnul( &cloc, status );

      datNew0C( xloc, "INSTRUMENT", strlen( skytiling.name ), status );
      datFind( xloc, "INSTRUMENT", &cloc, status );
      datPut0C( cloc, skytiling.name, status );
      datAnnul( &cloc, status );

/* Create a weights NDF within the SMURF extension, and fill its data
   array with zeros. */
      ndfPlace( xloc, "WEIGHTS", &place, status );
      ndfNew( skytiling.type, 2, lbnd, ubnd, &place, &indf2, status );
      ndfMap( indf2, "Data", skytiling.type, "WRITE/ZERO", &pntr, &el,
              status );
      ndfPtwcs( fs, indf2, status );
      ndfAnnul( &indf2, status );

/* Annul the extension locator and the main NDF identifier. */
      datAnnul( &xloc, status );
      ndfAnnul( &indf1, status );

/* Write the NDF info to the screen. */
   } else {
      msgSetc( "NDF",  tilendf );
      msgSetc( "E",  exists ? "exists" : "does not exist" );
      msgOut( " ", "      NDF: ^NDF (^E)", status );
   }

/* Initialise TBND and TLBND to indicate no overlap. */
   tlbnd[ 0 ] = 1;
   tlbnd[ 1 ] = 1;
   tubnd[ 0 ] = 0;
   tubnd[ 1 ] = 0;

/* Attempt to to get an AST Region (assumed to be in some 2D sky coordinate
   system) using parameter "TARGET". */
   if( *status == SAI__OK ) {
      kpg1Gtobj( "TARGET", "Region",
                 (void (*)( void )) F77_EXTERNAL_NAME(ast_isaregion),
                 &obj, status );

/* Annul the error if none was obtained. */
      if( *status == PAR__NULL ) {
         errAnnul( status );

/* Otherwise, use the supplied object. */
      } else {
         target = (AstRegion *) obj;

/* If the target Region is 3-dimensional, remove the third axis, which
   is assumed to be a spectral axis. */
         if( astGetI( target, "Naxes" ) == 3 ) {
            axes[ 0 ] = 1;
            axes[ 1 ] = 2;
            target = astPickAxes( target, 2, axes, NULL );
         }

/* See if there is any overlap between the target and the tile. */
         overlap = NULL;
         flag = astOverlap( region, target );

         if( flag == 0 ) {
            msgOut( "", "      Cannot convert between the coordinate system of the "
                    "supplied target and the tile.", status );

         } else if( flag == 1 || flag == 6 ) {
            msgOut( "", "      There is no overlap between the target and the tile.",
                    status );

         } else if( flag == 2 ) {
            msgOut( "", "      The tile is contained within the target.",
                    status );
            tlbnd[ 0 ] = lbnd[ 0 ];
            tlbnd[ 1 ] = lbnd[ 1 ];
            tubnd[ 0 ] = ubnd[ 0 ];
            tubnd[ 1 ] = ubnd[ 1 ];

         } else if( flag == 3 ) {
            overlap = astCmpRegion( region, target, AST__AND, " " );

         } else if( flag == 4 ) {
            overlap = astCmpRegion( region, target, AST__AND, " " );

         } else if( flag == 5 ) {
            msgOut( "", "      The target and tile are identical.",
                    status );
            tlbnd[ 0 ] = lbnd[ 0 ];
            tlbnd[ 1 ] = lbnd[ 1 ];
            tubnd[ 0 ] = ubnd[ 0 ];
            tubnd[ 1 ] = ubnd[ 1 ];

         } else if( *status == SAI__OK ) {
            *status = SAI__OK;
            errRepf( "", "Unexpected value %d returned by astOverlap "
                     "(programming error).", status, flag );
         }

/* If a region containing the intersection of the tile and target was
   created above, map it into the grid coordinate system of the tile. */
         if( overlap ) {
            overlap = astMapRegion( overlap, astGetMapping( fs, AST__CURRENT,
                                                            AST__BASE ),
                                    astGetFrame( fs, AST__BASE ) );

/* Get its GRID bounds. */
            astGetRegionBounds( overlap, dlbnd, dubnd );

/* Convert to integer. */
            tlbnd[ 0 ] = ceil( dlbnd[ 0 ] - 0.5 );
            tlbnd[ 1 ] = ceil( dlbnd[ 1 ] - 0.5 );
            tubnd[ 0 ] = ceil( dubnd[ 0 ] - 0.5 );
            tubnd[ 1 ] = ceil( dubnd[ 1 ] - 0.5 );

/* Convert to PIXEL indices within the tile. */
            tlbnd[ 0 ] += lbnd[ 0 ] - 1;
            tlbnd[ 1 ] += lbnd[ 1 ] - 1;
            tubnd[ 0 ] += lbnd[ 0 ] - 1;
            tubnd[ 1 ] += lbnd[ 1 ] - 1;

            msgOutf( "", "      The target overlaps section (%d:%d,%d:%d).",
                     status, tlbnd[ 0 ], tubnd[ 0 ], tlbnd[ 1 ], tubnd[ 1 ] );
         }
      }
   }

/* Store the pixel index bounds of the tiles overlap with the target. */
   parPut1i( "TLBND", 2, tlbnd, status );
   parPut1i( "TUBND", 2, tubnd, status );

/* Arrive here if an error occurs. */
   L999:;

/* Free resources. */
   tilendf = astFree( tilendf );

/* End the AST context. */
   astEnd;

/* Issue a status indication.*/
   msgBlank( status );
   if( *status == SAI__OK ) {
      msgOutif( MSG__VERB, "", "JSATILEINFO succeeded.", status);
   } else {
      msgOutif( MSG__VERB, "", "JSATILEINFO failed.", status);
   }
}
示例#18
0
void smurf_sc2clean( int *status ) {
  smfArray *array = NULL;    /* Data to be cleaned */
  Grp *basegrp=NULL;         /* Grp containing first file each chunk */
  size_t basesize;           /* Number of files in base group */
  smfArray *bbms = NULL;     /* Bad bolometer masks */
  smfArray *concat=NULL;     /* Pointer to a smfArray */
  size_t contchunk;          /* Continuous chunk counter */
  smfArray *darks = NULL;    /* Dark data */
  int ensureflat;            /* Flag for flatfielding data */
  smfArray *flatramps = NULL;/* Flatfield ramps */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  smfData *odata = NULL;     /* Pointer to output data struct */
  Grp *fgrp = NULL;          /* Filtered group, no darks */
  size_t gcount=0;           /* Grp index counter */
  size_t idx;                /* Subarray counter */
  Grp *igrp = NULL;          /* Input group of files */
  smfGroup *igroup=NULL;     /* smfGroup corresponding to igrp */
  dim_t maxconcat=0;         /* Longest continuous chunk length in samples */
  double maxlen=0;           /* Constrain maxconcat to this many seconds */
  size_t ncontchunks=0;      /* Number continuous chunks outside iter loop */
  Grp *ogrp = NULL;          /* Output group of files */
  size_t osize;              /* Total number of NDF names in the output group */
  dim_t padStart=0;          /* How many samples padding at start */
  dim_t padEnd=0;            /* How many samples padding at end */
  size_t size;               /* Number of files in input group */
  int temp;                  /* Temporary signed integer */
  int usedarks;              /* flag for using darks */
  ThrWorkForce *wf = NULL;   /* Pointer to a pool of worker threads */
  int writecom;              /* Write COMmon mode to NDF if calculated? */
  int writegai;              /* Write GAIns to NDF if calculated? */

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Read the input file */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );

  /* Filter out darks */
  smf_find_science( wf, igrp, &fgrp, 1, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  size = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  if (size == 0) {
    msgOutif(MSG__NORM, " ","All supplied input frames were filtered,"
       " nothing to do", status );
    goto CLEANUP;
  }

  /* --- Parse ADAM parameters ---------------------------------------------- */

  /* Maximum length of a continuous chunk */
  parGdr0d( "MAXLEN", 0, 0, VAL__MAXD, 1, &maxlen, status );

  /* Padding */
  parGdr0i( "PADSTART", 0, 0, VAL__MAXI, 1, &temp, status );
  padStart = (dim_t) temp;

  parGdr0i( "PADEND", 0, 0, VAL__MAXI, 1, &temp, status );
  padEnd = (dim_t) temp;

  /* Are we using darks? */
  parGet0l( "USEDARKS", &usedarks, status );

  /* Are we flatfielding? */
  parGet0l( "FLAT", &ensureflat, status );

  /* Write COM/GAI to NDFs if calculated? */
  parGet0l( "COM", &writecom, status );
  parGet0l( "GAI", &writegai, status );

  /* Get group of bolometer masks and read them into a smfArray */
  smf_request_mask( wf, "BBM", &bbms, status );

  /* Group the input files by subarray and continuity ----------------------- */
  smf_grp_related( igrp, size, 1, 0, maxlen-padStart-padEnd, NULL, NULL,
                   &maxconcat, NULL, &igroup, &basegrp, NULL, status );

  /* Obtain the number of continuous chunks and subarrays */
  if( *status == SAI__OK ) {
    ncontchunks = igroup->chunk[igroup->ngroups-1]+1;
  }

  basesize = grpGrpsz( basegrp, status );

  /* Get output file(s) */
  kpg1Wgndf( "OUT", basegrp, basesize, basesize,
             "More output files required...",
             &ogrp, &osize, status );

  /* Loop over continuous chunks and clean -----------------------------------*/
  gcount = 1;
  for( contchunk=0;(*status==SAI__OK)&&contchunk<ncontchunks; contchunk++ ) {
    AstKeyMap *keymap=NULL;
    int dkclean;
    AstKeyMap *sub_instruments=NULL;

    /* Place cleaning parameters into a keymap and set defaults. Do
       this inside the loop in case we are cleaning files with
       differing sub-instruments.  Note that we use the map-maker
       defaults file here (which loads the sc2clean defaults) so that
       we populate the locked keymap with all the parameters that
       people may come across to allow them to load their map-maker
       config directly into sc2clean.
    */

    sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE,
                                          NULL, igrp,
                                          igroup->subgroups[contchunk][0],
                                          status );

    keymap = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_makemap.def",
                         sub_instruments, 1, status );
    if( sub_instruments ) sub_instruments = astAnnul( sub_instruments );

    /* Now rerun smf_grp_related to figure out how long each downsampled
       chunk of data will be. */

    if( basegrp ) grpDelet( &basegrp, status );
    if( igroup ) smf_close_smfGroup( &igroup, status );

    smf_grp_related( igrp, size, 1, 0, maxlen-padStart-padEnd, NULL, keymap,
                     &maxconcat, NULL, &igroup, &basegrp, NULL, status );

    /* Concatenate this continuous chunk */
    smf_concat_smfGroup( wf, NULL, igroup, usedarks ? darks:NULL, bbms, flatramps,
                         heateffmap, contchunk, ensureflat, 1, NULL, 0, NULL,
                         NULL, NO_FTS, padStart, padEnd, 0, &concat, NULL, status );

    if( *status == SAI__OK) {
      /* clean the dark squids now since we might need to use them
         to clean the bolometer data */

      smf_get_cleanpar( keymap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        &dkclean, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        NULL, NULL, NULL, status );

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

        if( odata && odata->da && odata->da->dksquid ) {
          smfData *dksquid = odata->da->dksquid;
          AstKeyMap *kmap=NULL;

          msgOut("", TASK_NAME ": cleaning dark squids", status);

          /* fudge the header so that we can get at JCMTState */
          dksquid->hdr = odata->hdr;

          /* clean darks using cleandk.* parameters */
          astMapGet0A( keymap, "CLEANDK", &kmap );
          array = smf_create_smfArray( status );
          smf_addto_smfArray( array, dksquid, status );
          smf_clean_smfArray( wf, array, NULL, NULL, NULL, kmap, status );
          if( array ) {
            array->owndata = 0;
            smf_close_related( wf, &array, status );
          }
          if( kmap ) kmap = astAnnul( kmap );

          /* Unset hdr pointer so that we don't accidentally close it */
          dksquid->hdr = NULL;
        }
      }

      /* Then the main data arrays */
      if( *status == SAI__OK ) {
        smfArray *com = NULL;
        smfArray *gai = NULL;
        char filename[GRP__SZNAM+1];

        msgOut("", TASK_NAME ": cleaning bolometer data", status );
        smf_clean_smfArray( wf, concat, NULL, &com, &gai, keymap, status );

        /* If ADAM parameters for COM or GAI were specified, and the
           common-mode was calculated, export to files here */

        if( writecom && com ) {
          for( idx=0; (*status==SAI__OK)&&(idx<com->ndat); idx++ ) {
            smf_model_createHdr( com->sdata[idx], SMF__COM, concat->sdata[idx],
                                 status );
            smf_stripsuffix( com->sdata[idx]->file->name,
                             SMF__DIMM_SUFFIX, filename, status );

            smf_dataOrder( wf, com->sdata[idx], 1, status );

            smf_write_smfData( wf, com->sdata[idx], NULL, filename, NULL, 0,
                               NDF__NOID, MSG__NORM, 0, NULL, NULL, status );
          }
        }

        if( writegai && gai ) {
          for( idx=0; (*status==SAI__OK)&&(idx<gai->ndat); idx++ ) {
            smf_model_createHdr( gai->sdata[idx], SMF__GAI, concat->sdata[idx],
                                 status );
            smf_stripsuffix( gai->sdata[idx]->file->name,
                             SMF__DIMM_SUFFIX, filename, status );

            smf_dataOrder( wf, gai->sdata[idx], 1, status );
            smf_write_smfData( wf, gai->sdata[idx], NULL, filename, NULL, 0,
                               NDF__NOID, MSG__NORM, 0, NULL, NULL, status );
          }
        }

        /* Close com and gai */
        if( com ) smf_close_related( wf, &com, status );
        if( gai ) smf_close_related( wf, &gai, status );

      }

      /* Report statistics (currently need a smfArray for that) */
      if (*status == SAI__OK) {
        size_t last_qcount[SMF__NQBITS];
        size_t last_nmap = 0;
        smf_qualstats_report( wf, MSG__VERB, SMF__QFAM_TSERIES, 1, concat,
                              last_qcount, &last_nmap, 1, NULL, NULL, status );
      }

      /* Clean up for contchunk loop */
      if( keymap ) keymap = astAnnul( keymap );
    }

    /* Export concatenated/cleaned data for each subarray to NDF file */
    for( idx=0; (*status==SAI__OK)&&idx<concat->ndat; idx++ ) {
      odata = concat->sdata[idx];

      /* Complete the history information in the output NDF so that it
         includes group parameters accessed since the default history
         information was written to the NDF (in smf_open_and_flatfield). */
      smf_puthistory( odata, "SMURF:SC2CLEAN", status );

      /* Ensure ICD data order */
      smf_dataOrder( wf, odata, 1, status );

      if( odata->file && odata->file->name ) {
        smf_write_smfData( wf, odata, NULL, NULL, ogrp, gcount, NDF__NOID,
                           MSG__VERB, 0, NULL, NULL, status );
      } else {
        *status = SAI__ERROR;
        errRep( FUNC_NAME,
                "Unable to determine file name for concatenated data.",
                status );
      }

      /* Increment the group index counter */
      gcount++;
    }

    /* Close the smfArray */
    smf_close_related( wf, &concat, status );
  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && ogrp ) {
    grpList( "OUTFILES", 0, 0, NULL, ogrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

 CLEANUP:

  /* Tidy up after ourselves: release the resources used by the grp routines */
  if( darks ) smf_close_related( wf, &darks, status );
  if( flatramps ) smf_close_related( wf, &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  if( bbms ) smf_close_related( wf, &bbms, status );
  if( igrp ) grpDelet( &igrp, status);
  if( ogrp ) grpDelet( &ogrp, status);
  if( basegrp ) grpDelet( &basegrp, status );
  if( igroup ) smf_close_smfGroup( &igroup, status );
  fftw_cleanup();
  ndfEnd( status );
}
示例#19
0
/* Main entry */
void smurf_fixsteps( int *status ) {

/* Local Variables */
   AstKeyMap *keymap;        /* Default config parameter values */
   AstKeyMap *sub_instruments; /* Info about sub-instruments */
   FILE *fd = NULL;          /* File descriptor */
   Grp *igrp = NULL;         /* Input group of files */
   Grp *ogrp = NULL;         /* Output group of files */
   dim_t dcfitbox;           /* DCFITBOX config parameter */
   dim_t dcsmooth;           /* DCSMOOTH config parameter */
   dim_t nx;                 /* Length of first pixel axis */
   double dcthresh;          /* DCTHRESH config parameter */
   double sizetol;           /* Tolerance allowed on step height */
   int changed;              /* Have any step fixes changed? */
   int dclimcorr;            /* DCLIMCORR config parameter */
   int dcmaxsteps;           /* DCMAXSTEPS config parameter */
   int first;                /* Index of first change to report */
   int itemp;                /* Intermediate value */
   int meanshift;            /* Use a mean shift filter? */
   int nnew;                 /* Number of new step fixes */
   int nold;                 /* Number of old step fixes */
   size_t nrej;              /* Number of rejected bolometers */
   size_t outsize;           /* Total number of NDF names in the output group */
   size_t size;              /* Number of files in input group */
   smfData *data = NULL;     /* Output smfData */
   smfData *indata = NULL;   /* Input smfData */
   smfStepFix *newsteps = NULL; /* New step fix descriptions */
   smfStepFix *oldsteps = NULL; /* Old step fix descriptions */
   ThrWorkForce *wf = NULL;  /* Pointer to a pool of worker threads */

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

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

/* Get the name of the input NDF. */
   kpg1Rgndf( "IN", 1, 1, "", &igrp, &size, status );

/* Get output file(s) */
   kpg1Wgndf( "OUT", igrp, size, 0, "More output files required...",
               &ogrp, &outsize, status );

/* Open the input data file, read-only. */
   smf_open_file( igrp, 1, "Read", 0, &indata, status );

/* Since we will be modifying the data values, we need a deep copy. */
   data = smf_deepcopy_smfData( indata, 0, 0, 0, 0, status );

/* Place cleaning parameters into a keymap and set defaults. Note that we
   use the map-maker defaults file here so that we populate the locked
   keymap with all the parameters that people may come across to allow
   them to load their map-maker config directly this application. */
   sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE, data, NULL, 0,
                                         status );
   keymap = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_makemap.def",
                        sub_instruments, status );
   sub_instruments = astAnnul( sub_instruments );

/* Set the default for each of the step fixing config parameters. */
   astMapGet0I( keymap, "DCSMOOTH", &itemp );
   parDef0i( "DCSMOOTH", itemp, status );

   astMapGet0I( keymap, "DCFITBOX", &itemp );
   parDef0i( "DCFITBOX", itemp, status );

   astMapGet0I( keymap, "DCMAXSTEPS", &itemp );
   parDef0i( "DCMAXSTEPS", itemp, status );

   astMapGet0I( keymap, "DCLIMCORR", &itemp );
   parDef0i( "DCLIMCORR", itemp, status );

   astMapGet0D( keymap, "DCTHRESH", &dcthresh );
   parDef0d( "DCTHRESH", dcthresh, status );

/* Get values for the config params */
   parGet0i( "DCSMOOTH", &itemp, status );
   dcsmooth = itemp;

   parGet0i( "DCFITBOX", &itemp, status );
   dcfitbox = itemp;

   parGet0i( "DCMAXSTEPS", &itemp, status );
   dcmaxsteps = itemp;

   parGet0i( "DCLIMCORR", &itemp, status );
   dclimcorr = itemp;

   parGet0d( "DCTHRESH", &dcthresh, status );

   parGet0l( "MEANSHIFT", &meanshift, status );

/* Find the number of cores/processors available and create a pool of
   threads of the same size. */
   wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

/* Fix the steps. */
   smf_fix_steps( wf, data, dcthresh, dcsmooth, dcfitbox, dcmaxsteps,
                  dclimcorr, meanshift, &nrej, &newsteps, &nnew, status );

/* Display a summary of what was done by the step fixer. */
   msgBlank( status );
   if( nrej == 0 ) {
      msgOut( "", "No bolometers were rejected", status );
   } else if( nrej == 1 ) {
      msgOut( "", "One bolometer was rejected", status );
   } else {
      msgSeti( "NREJ", nrej );
      msgOut( "", "^NREJ bolometers were rejected", status );
   }
   parPut0i( "NREJECTED", nrej, status );

   if( nnew == 0 ) {
      msgOut( "", "No steps were fixed", status );
   } else if( nnew == 1 ) {
      msgOut( "", "One step was fixed", status );
   } else {
      msgSeti( "NNEW", nnew );
      msgOut( "", "^NNEW steps were fixed", status );
   }
   parPut0i( "NFIXED", nnew, status );

/* If required, write out to a text file details of the steps that were
   fixed. */
   fd = smf_open_textfile( "NEWSTEPS", "w", "<none>", status );
   if( fd ) {
      smf1_write_steps( fd, indata, nnew, newsteps, dcthresh, dcsmooth,
                        dcfitbox, dcmaxsteps, dclimcorr, nrej, status );
      fclose( fd );
   }

/* If required, create the output NDF. */
   if( outsize > 0 && indata && indata->file ) {
      smf_write_smfData( data, NULL, NULL, ogrp, 1,
                         indata->file->ndfid, MSG__VERB, 0, status );
   }

/* Save the length of the first pixel axis. */
   nx = data ? data->dims[ 0 ] : 0;

/* Close the NDFs. */
   smf_close_file( &data, status );
   smf_close_file( &indata, status );

/* Attempt to open a file containing descriptions of steps fixed by a
   previous invocation of this program. */
   fd = smf_open_textfile( "OLDSTEPS", "r", "<none>", status );
   if( fd ) {

/* Get SIZETOL - the minimum significant fractional error in step sizes. */
      parGet0d( "SIZETOL", &sizetol, status );

/* Read the contents of the file, issuing a warning if the global
   properties read from the file (e.g. parameters used, no. of steps
   found, etc) differ from those of the current invocation. */
      msgBlank( status );
      oldsteps = smf1_read_steps( fd, dcthresh, dcsmooth,
                                  dcfitbox, dcmaxsteps, dclimcorr,
                                  nrej, nnew, &nold, status );

/* Get the index of the first change to report. */
      parGet0i( "FIRST", &first, status );

/* Compare the new step fixes with the old step fixes, issuing a warning
   for the first step fix that has changed. */
      changed = smf1_check_steps( "CONTINUE", first, nx, sizetol,
                                  nold, nnew, oldsteps, newsteps, status );

/* Store a flag indicating if any sstep fixes have chnaged. */
      parPut0l( "CHANGED", changed, status );

/* Tell the user if nothing has changed. */
      if( ! changed ) {
         msgOut( "", "There are no significant differences "
                 "between old and new step fixes.", status );
      }
      msgBlank( status );

/* Close the old steps file, and free the memory holding the old step
   descriptions. */
      fclose( fd );
      oldsteps = astFree( oldsteps );
   }

/* Free resources. */
   newsteps = astFree( newsteps );
   grpDelet( &igrp, status );
   grpDelet( &ogrp, status );

/* End the NDF context. */
   ndfEnd( status );

/* If anything went wrong issue a context message. */
   if( *status != SAI__OK ) msgOutif( MSG__VERB, " ", "FIXSTEPS failed.",
                                      status );
}
示例#20
0
void smurf_sc2fft( int *status ) {

  int avpspec=0;            /* Flag for doing average power spectrum */
  double avpspecthresh=0;   /* Threshold noise for detectors in avpspec */
  Grp * basegrp = NULL;     /* Basis group for output filenames */
  smfArray *bbms = NULL;    /* Bad bolometer masks */
  smfArray *concat=NULL;    /* Pointer to a smfArray */
  size_t contchunk;         /* Continuous chunk counter */
  smfArray *darks = NULL;   /* dark frames */
  int ensureflat;           /* Flag for flatfielding data */
  Grp *fgrp = NULL;         /* Filtered group, no darks */
  smfArray *flatramps = NULL;/* Flatfield ramps */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  size_t gcount=0;          /* Grp index counter */
  size_t i;                 /* Loop counter */
  smfGroup *igroup=NULL;    /* smfGroup corresponding to igrp */
  Grp *igrp = NULL;         /* Input group of files */
  int inverse=0;            /* If set perform inverse transform */
  int isfft=0;              /* Are data fft or real space? */
  dim_t maxconcat=0;        /* Longest continuous chunk length in samples */
  size_t ncontchunks=0;     /* Number continuous chunks outside iter loop */
  smfData *odata=NULL;      /* Pointer to output smfData to be exported */
  Grp *ogrp = NULL;         /* Output group of files */
  size_t outsize;           /* Total number of NDF names in the output group */
  int polar=0;              /* Flag for FFT in polar coordinates */
  int power=0;              /* Flag for squaring amplitude coeffs */
  size_t size;              /* Number of files in input group */
  smfData *tempdata=NULL;   /* Temporary smfData pointer */
  int weightavpspec=0;      /* Flag for 1/noise^2 weighting */
  ThrWorkForce *wf = NULL;  /* Pointer to a pool of worker threads */
  int zerobad;              /* Zero VAL__BADD before taking FFT? */

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Get input file(s) */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );

  /* Filter out darks */
  smf_find_science( igrp, &fgrp, 1, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  size = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  /* We now need to combine files from the same subarray and same sequence
     to form a continuous time series */
  smf_grp_related( igrp, size, 1, 0, 0, NULL, NULL, &maxconcat, NULL, &igroup,
                   &basegrp, NULL, status );

  /* Get output file(s) */
  size = grpGrpsz( basegrp, status );
  if( size > 0 ) {
    kpg1Wgndf( "OUT", basegrp, size, size, "More output files required...",
               &ogrp, &outsize, status );
  } else {
    msgOutif(MSG__NORM, " ", TASK_NAME ": All supplied input frames were DARK,"
             " nothing to do", status );
  }

  /* Get group of bolometer masks and read them into a smfArray */
  smf_request_mask( "BBM", &bbms, status );

  /* Obtain the number of continuous chunks and subarrays */
  if( *status == SAI__OK ) {
    ncontchunks = igroup->chunk[igroup->ngroups-1]+1;
  }
  msgOutiff( MSG__NORM, "", "Found %zu continuous chunk%s", status, ncontchunks,
             (ncontchunks > 1 ? "s" : "") );

  /* Are we flatfielding? */
  parGet0l( "FLAT", &ensureflat, status );

  /* Are we doing an inverse transform? */
  parGet0l( "INVERSE", &inverse, status );

  /* Are we using polar coordinates instead of cartesian for the FFT? */
  parGet0l( "POLAR", &polar, status );

  /* Are we going to assume amplitudes are squared? */
  parGet0l( "POWER", &power, status );

  /* Are we going to zero bad values first? */
  parGet0l( "ZEROBAD", &zerobad, status );

  /* Are we calculating the average power spectrum? */
  parGet0l( "AVPSPEC", &avpspec, status );

  if( avpspec ) {
    power = 1;
    parGet0d( "AVPSPECTHRESH", &avpspecthresh, status );

    parGet0l( "WEIGHTAVPSPEC", &weightavpspec, status );
  }

  /* If power is true, we must be in polar form */
  if( power && !polar) {
    msgOutif( MSG__NORM, " ", TASK_NAME
              ": power spectrum requested so setting POLAR=TRUE", status );
    polar = 1;
  }

  gcount = 1;
  for( contchunk=0;(*status==SAI__OK)&&contchunk<ncontchunks; contchunk++ ) {
    size_t idx;

    /* Concatenate this continuous chunk but forcing a raw data read.
       We will need quality. */
    smf_concat_smfGroup( wf, NULL, igroup, darks, NULL, flatramps, heateffmap,
                         contchunk, ensureflat, 1, NULL, 0, NULL, NULL, 0, 0, 0,
                         &concat, NULL, status );

    /* Now loop over each subarray */
    /* Export concatenated data for each subarray to NDF file */
    for( idx=0; (*status==SAI__OK)&&idx<concat->ndat; idx++ ) {
      if( concat->sdata[idx] ) {
        smfData * idata = concat->sdata[idx];
        int provid = NDF__NOID;
        dim_t nbolo;                /* Number of detectors  */
        dim_t ndata;                /* Number of data points */

        /* Apply a mask to the quality array and data array */
        smf_apply_mask( idata, bbms, SMF__BBM_QUAL|SMF__BBM_DATA, 0, status );

        smf_get_dims( idata,  NULL, NULL, &nbolo, NULL, &ndata, NULL, NULL,
                      status );


        /* Check for double precision data */
        if( idata->dtype != SMF__DOUBLE ) {
          *status = SAI__ERROR;
          errRep( "", FUNC_NAME ": data are not double precision.", status );
        }

        /* Are we zeroing VAL__BADD? */
        if( (*status==SAI__OK) && zerobad ) {
          double *data= (double *) idata->pntr[0];

          for( i=0; i<ndata; i++ ) {
            if( data[i] == VAL__BADD ) {
              data[i] = 0;
            }
          }
        }

        /* Check whether we need to transform the data at all */
        isfft = smf_isfft(idata,NULL,NULL,NULL,NULL,NULL,status);

        if( isfft && avpspec && (*status == SAI__OK) ) {
          *status = SAI__ERROR;
          errRep( "", FUNC_NAME
                  ": to calculate average power spectrum input data cannot "
                  "be FFT", status );
        }

        if( (*status == SAI__OK) && (isfft == inverse) ) {

          if( avpspec ) {
            /* If calculating average power spectrum do the transforms with
               smf_bolonoise so that we can also measure the noise of
               each detector */

            double *whitenoise=NULL;
            smf_qual_t *bolomask=NULL;
            double mean, sig, freqlo;
            size_t ngood, newgood;

            whitenoise = astCalloc( nbolo, sizeof(*whitenoise) );
            bolomask = astCalloc( nbolo, sizeof(*bolomask) );

	    freqlo = 1. / (idata->hdr->steptime * idata->hdr->nframes);

            smf_bolonoise( wf, idata, 1, freqlo, SMF__F_WHITELO,
                           SMF__F_WHITEHI, 1, 0, whitenoise, NULL, &odata,
                           status );

            /* Initialize quality */
            for( i=0; i<nbolo; i++ ) {
              if( whitenoise[i] == VAL__BADD ) {
                bolomask[i] = SMF__Q_BADB;
              } else {
                /* smf_bolonoise returns a variance, so take sqrt */
                whitenoise[i] = sqrt(whitenoise[i]);
              }
            }

            ngood=-1;
            newgood=0;

            /* Iteratively cut n-sigma noisy outlier detectors */
            while( ngood != newgood ) {
              ngood = newgood;
              smf_stats1D( whitenoise, 1, nbolo, bolomask, 1, SMF__Q_BADB,
                           &mean, &sig, NULL, NULL, status );
              msgOutiff( MSG__DEBUG, "", TASK_NAME
                         ": mean=%lf sig=%lf ngood=%li\n", status,
                         mean, sig, ngood);

              newgood=0;
              for( i=0; i<nbolo; i++ ) {
                if( whitenoise[i] != VAL__BADD ){
                  if( (whitenoise[i] - mean) > avpspecthresh *sig ) {
                    whitenoise[i] = VAL__BADD;
                    bolomask[i] = SMF__Q_BADB;
                  } else {
                    newgood++;
                  }
                }
              }
            }

            msgOutf( "", TASK_NAME
                     ": Calculating average power spectrum of best %li "
                     " bolometers.", status, newgood);

            /* If using 1/noise^2 weights, calculate 1/whitenoise^2 in-place
               to avoid allocating another array */
            if( weightavpspec ) {
              msgOutif( MSG__VERB, "", TASK_NAME ": using 1/noise^2 weights",
                        status );

              for( i=0; i<nbolo; i++ ) {
                if( whitenoise[i] && (whitenoise[i] != VAL__BADD) ) {
                  whitenoise[i] = 1/(whitenoise[i]*whitenoise[i]);
                }
              }
            }

            /* Calculate the average power spectrum of good detectors */
            tempdata = smf_fft_avpspec( odata, bolomask, 1, SMF__Q_BADB,
                                        weightavpspec ? whitenoise : NULL,
                                        status );
            smf_close_file( &odata, status );
            whitenoise = astFree( whitenoise );
            bolomask = astFree( bolomask );
            odata = tempdata;
            tempdata = NULL;
	    /* Store the number of good bolometers */
	    parPut0i( "NGOOD", newgood, status );
          } else {
            /* Otherwise do forward/inverse transforms here as needed */

            /* If inverse transform convert to cartesian representation first */
            if( inverse && polar ) {
              smf_fft_cart2pol( wf, idata, 1, power, status );
            }

            /* Tranform the data */
            odata = smf_fft_data( wf, idata, NULL, inverse, 0, status );
            smf_convert_bad( wf, odata, status );

            if( inverse ) {
              /* If output is time-domain, ensure that it is ICD bolo-ordered */
              smf_dataOrder( odata, 1, status );
            } else if( polar ) {
              /* Store FFT of data in polar form */
              smf_fft_cart2pol( wf, odata, 0, power, status );
            }
          }

          /* open a reference input file for provenance propagation */
          ndgNdfas( basegrp, gcount, "READ", &provid, status );

          /* Export the data to a new file */
          smf_write_smfData( odata, NULL, NULL, ogrp, gcount, provid,
                             MSG__VERB, 0, status );

          /* Free resources */
          ndfAnnul( &provid, status );
          smf_close_file( &odata, status );
        } else {
          msgOutif( MSG__NORM, " ",
                    "Data are already transformed. No output will be produced",
                    status );
        }
      }

      /* Update index into group */
      gcount++;
    }

    /* Close the smfArray */
    smf_close_related( &concat, status );
  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK ) {
    grpList( "OUTFILES", 0, 0, NULL, ogrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

  /* Tidy up after ourselves: release the resources used by the grp routines */
  grpDelet( &igrp, status);
  grpDelet( &ogrp, status);
  if (basegrp) grpDelet( &basegrp, status );
  if( igroup ) smf_close_smfGroup( &igroup, status );
  if( flatramps ) smf_close_related( &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  if (bbms) smf_close_related( &bbms, status );

  ndfEnd( status );

  /* Ensure that FFTW doesn't have any used memory kicking around */
  fftw_cleanup();
}
示例#21
0
void smf_get_projpar( AstSkyFrame *skyframe, const double skyref[2],
                      int moving, int autogrid, int nallpos,
                      const double * allpos, float telres, double map_pa,
                      double par[7], int * issparse,int *usedefs, int *status ) {

/* Local Variables */
   char reflat[ 41 ];    /* Reference latitude string */
   char reflon[ 41 ];    /* Reference longitude string */
   char usesys[ 41 ];    /* Output skyframe system */
   const char *deflat;   /* Default for REFLAT */
   const char *deflon;   /* Default for REFLON */
   const double fbpixsize = 6.0; /* Fallback pixel size if we have no other information */
   double autorot;       /* Autogrid default for CROTA parameter */
   double defsize[ 2 ];  /* Default pixel sizes in arc-seconds */
   double pixsize[ 2 ];  /* Pixel sizes in arc-seconds */
   double refpix[ 2 ];   /* New REFPIX values */
   double rdiam;         /* Diameter of bounding circle, in rads */
   int coin;             /* Are all points effectively co-incident? */
   int i;
   int nval;             /* Number of values supplied */
   int refine_crpix;     /* Should the pixel ref position be updated? */
   int sparse = 0;       /* Local definition of sparseness */
   int udefs = 0;        /* Flag for defaults used or not */

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

/* If the number of supplied positions is 0 or null pointer,
   disable autogrid */
   if( nallpos == 0 || !allpos ) autogrid = 0;

/* Get the output system */
   one_strlcpy( usesys, astGetC( skyframe, "SYSTEM"), sizeof(usesys),
                status );

/* Ensure the reference position in the returned SkyFrame is set to the
   first telescope base pointing position. */
   astSetD( skyframe, "SkyRef(1)", skyref[ 0 ] );
   astSetD( skyframe, "SkyRef(2)", skyref[ 1 ] );

/* If the target is moving, ensure the returned SkyFrame represents
   offsets from the first telescope base pointing position rather than
   absolute coords. */
   if( moving ) smf_set_moving( (AstFrame *) skyframe, NULL, status );

/* Set a flag indicating if all the points are co-incident. */
   coin = 0;

/* Set the sky axis values at the tangent point. If the target is moving,
   the tangent point is at (0,0) (i.e. it is at the origin of the offset
   coordinate system). If the target is not moving, the tangent point is
   at the position held in "skyref". */
   if( par ) {
      if( moving ){
         par[ 2 ] = 0.0;
         par[ 3 ] = 0.0;
      } else {
         par[ 2 ] = skyref[ 0 ];
         par[ 3 ] = skyref[ 1 ];
      }

/* If required, calculate the optimal projection parameters. If the target
   is moving, these refer to the offset coordinate system centred on the
   first time slice base pointing position, with north defined by the
   requested output coordinate system. The values found here are used as
   dynamic defaults for the environment parameter */
      if( autogrid ) {
         kpg1Opgrd( nallpos, allpos, strcmp( usesys, "AZEL" ), par, &rdiam,
                          status );

/* See if all the points are effectively co-incident (i.e. within an Airy
   disk). If so, we use default grid parameters that result in a grid of
   1x1 spatial pixels. The grid pixel sizes (par[4] and par[5]) are made
   larger than the area covered by the points in order to avoid points
   spanning two pixels. */
         if( rdiam < telres || nallpos < 3 ) {
            if( rdiam < 0.1*AST__DD2R/3600.0 ) rdiam = 0.1*AST__DD2R/3600.0;
            par[ 0 ] = 0.0;
            par[ 1 ] = 0.0;
            par[ 4 ] = -rdiam*4;
            par[ 5 ] = -par[ 4 ];
            par[ 6 ] = 0.0;

            coin = 1;

/* If the sky positions are not co-incident, and the automatic grid
   determination failed, we cannot use a grid, so warn the user. */
         } else if( par[ 0 ] == AST__BAD ) {
            msgOutif( MSG__NORM, " ", "   Automatic grid determination "
                           "failed: the detector samples do not form a "
                           "regular grid.", status );
         }
      }

/* If autogrid values were not found, use the following fixed default
   values. Do not override extenal defaults for pixel size. */
      if( !autogrid || ( autogrid && par[ 0 ] == AST__BAD ) ) {
         par[ 0 ] = 0.0;
         par[ 1 ] = 0.0;
         if (par[4] == AST__BAD || par[5] == AST__BAD ) {
           par[ 4 ] = (fbpixsize/3600.0)*AST__DD2R;
           par[ 5 ] = (fbpixsize/3600.0)*AST__DD2R;
         }
         par[ 6 ] = map_pa;
      }

/* Ensure the default pixel sizes have the correct signs. */
      if( par[ 4 ] != AST__BAD ) {
         if( !strcmp( usesys, "AZEL" ) ) {
            par[ 4 ] = fabs( par[ 4 ] );
         } else {
            par[ 4 ] = -fabs( par[ 4 ] );
         }
         par[ 5 ] = fabs( par[ 5 ] );
      }

/* See if the output cube is to include a spatial projection, or a sparse
   list of spectra. Disabled if the sparse pointer is NULL. */
      if (issparse) {
        parDef0l( "SPARSE", ( par[ 0 ] == AST__BAD ), status );
        parGet0l( "SPARSE",  &sparse, status );

      }

/* If we are producing an output cube with the XY plane being a spatial
   projection, then get the parameters describing the projection, using the
   defaults calculated above. */
      if( !sparse && *status == SAI__OK ) {
         const int ndigits = 8; /* Number of digits for deflat/deflon precision */

/* If the target is moving, display the tracking centre coordinates for
   the first time slice. */
         if( moving ) {
            astClear( skyframe, "SkyRefIs" );
            msgBlank( status );
            msgSetc( "S1", astGetC( skyframe, "Symbol(1)" ) );
            msgSetc( "S2", astGetC( skyframe, "Symbol(2)" ) );
            msgOutif( MSG__NORM, " ", "   Output sky coordinates are "
                           "(^S1,^S2) offsets from the (moving)", status );
            msgSetc( "S1", astGetC( skyframe, "Symbol(1)" ) );
            msgSetc( "S2", astGetC( skyframe, "Symbol(2)" ) );
            msgSetc( "SREF", astGetC( skyframe, "SkyRef" ) );
            msgOutif( MSG__NORM, " ", "   telescope base position, which "
                           "started at (^S1,^S2) = (^SREF).", status );
            astSet( skyframe, "SkyRefIs=Origin" );
         }

/* Set up a flag indicating that the default values calculated by autogrid
   are being used. */
         udefs = 1;

/* Ensure we have usable CRPIX1/2 values */
         if( par[ 0 ] == AST__BAD ) par[ 0 ] = 1.0;
         if( par[ 1 ] == AST__BAD ) par[ 1 ] = 1.0;

/* Get the crpix1/2 (in the interim GRID frame) to use. Note if the user
   specifies any values. These parameters have vpath=default (which is null)
   and ppath=dynamic. */
         refine_crpix = 0;
         parDef0d( "REFPIX1", par[ 0 ], status );
         parDef0d( "REFPIX2", par[ 1 ], status );
         if( *status == SAI__OK ) {
            parGet0d( "REFPIX1", refpix + 0, status );
            parGet0d( "REFPIX2", refpix + 1, status );
            if( *status == PAR__NULL ) {
               errAnnul( status );
               refine_crpix = 1;
            } else {
               par[ 0 ] = refpix[ 0 ];
               par[ 1 ] = refpix[ 1 ];
            }
         }

/* Get the sky coords reference position strings. Use the returned SkyFrame
   to format and unformat them. */
         if( par[ 2 ] != AST__BAD ) {
            int curdigits;
            curdigits = astGetI( skyframe, "digits(1)" );
            astSetI( skyframe, "digits(1)", ndigits );
            deflon = astFormat( skyframe, 1, par[ 2 ] );
            astSetI( skyframe, "digits(1)", curdigits );
            parDef0c( "REFLON", deflon, status );
         } else {
            deflon = NULL;
         }

         if( par[ 3 ] != AST__BAD ) {
            int curdigits;
            curdigits = astGetI( skyframe, "digits(2)" );
            astSetI( skyframe, "digits(2)", ndigits );
            deflat = astFormat( skyframe, 2, par[ 3 ] );
            astSetI( skyframe, "digits(2)", curdigits );
            parDef0c( "REFLAT", deflat, status );
         } else {
            deflat = NULL;
         }

         parGet0c( "REFLON", reflon, 40, status );
         parGet0c( "REFLAT", reflat, 40, status );

         if( *status == SAI__OK ) {

            if( ( deflat && strcmp( deflat, reflat ) ) ||
                  ( deflon && strcmp( deflon, reflon ) ) ) udefs = 0;

            if( astUnformat( skyframe, 1, reflon, par + 2 ) == 0 && *status == SAI__OK ) {
               msgSetc( "REFLON", reflon );
               errRep( "", "Bad value supplied for REFLON: '^REFLON'", status );
            }

            if( astUnformat( skyframe, 2, reflat, par + 3 ) == 0 && *status == SAI__OK ) {
               msgSetc( "REFLAT", reflat );
               errRep( "", "Bad value supplied for REFLAT: '^REFLAT'", status );
            }

/* Ensure the reference position in the returned SkyFrame is set to the
   supplied position (which defaults to the first telescope base pointing
   position). */
            if( !moving ){
               astSetD( skyframe, "SkyRef(1)", par[ 2 ] );
               astSetD( skyframe, "SkyRef(2)", par[ 3 ] );
            }
         }

/* Get the user defined spatial pixel size in arcsec (the calibration for
   the spectral axis is fixed by the first input data file - see
   smf_cubebounds.c). First convert the autogrid values form rads to arcsec
   and establish them as the dynamic default for "PIXSIZE". */
         nval = 0;
         if( par[ 4 ] != AST__BAD || par[ 5 ] != AST__BAD ) {
           for ( i = 4; i <= 5; i++ ) {
             if ( par[ i ] != AST__BAD ) {
               defsize[ nval ] = 0.1*NINT( fabs( par[ i ] )*AST__DR2D*36000.0 );
               nval++;
             }
           }
           /* set the dynamic default, handling case where both dimensions
              have same default. */
           if (nval == 1) {
             defsize[1] = defsize[0];
           } else if (nval == 2 && defsize[0] == defsize[1]) {
             nval = 1;
           }
           parDef1d( "PIXSIZE", nval, defsize, status );

         } else {
           /* pick a default in case something odd happens and we have
              no other values*/
           defsize[ 0 ] = fbpixsize;
           defsize[ 1 ] = defsize[ 0 ];
           nval = 2;
         }
         if (*status == SAI__OK) {
           pixsize[0] = AST__BAD;
           pixsize[1] = AST__BAD;
           parGet1d( "PIXSIZE", 2, pixsize, &nval, status );
           if (*status == PAR__NULL) {
             /* Null just defaults to what we had before */
             errAnnul( status );
             pixsize[0] = defsize[0];
             pixsize[1] = defsize[1];
             nval = 2;
           }
         }

/* If OK, duplicate the first value if only one value was supplied. */
         if( *status == SAI__OK ) {
            if( nval < 2 ) pixsize[ 1 ] = pixsize[ 0 ];

            if( defsize[ 0 ] != pixsize[ 0 ] ||
                  defsize[ 1 ] != pixsize[ 1 ] ) udefs = 0;

/* Check the values are OK. */
            if( pixsize[ 0 ] <= 0 || pixsize[ 1 ] <= 0 ) {
               msgSetd( "P1", pixsize[ 0 ] );
               msgSetd( "P2", pixsize[ 1 ] );
               *status = SAI__ERROR;
               errRep( FUNC_NAME, "Invalid pixel sizes (^P1,^P2).", status);
            }

/* Convert to rads, and set the correct signs. */
            if( par[ 4 ] == AST__BAD || par[ 4 ] < 0.0 ) {
               par[ 4 ] = -pixsize[ 0 ]*AST__DD2R/3600.0;
            } else {
               par[ 4 ] = pixsize[ 0 ]*AST__DD2R/3600.0;
            }

            if( par[ 5 ] == AST__BAD || par[ 5 ] < 0.0 ) {
               par[ 5 ] = -pixsize[ 1 ]*AST__DD2R/3600.0;
            } else {
               par[ 5 ] = pixsize[ 1 ]*AST__DD2R/3600.0;
            }

         }

/* Convert the autogrid CROTA value from rads to degs and set as the
   dynamic default for parameter CROTA (the position angle of the output
   Y axis, in degrees). The get the CROTA value and convert to rads. */
         if( par[ 6 ] != AST__BAD ) {
            autorot = par[ 6 ]*AST__DR2D;
            parDef0d( "CROTA", autorot, status );

         } else {
            parDef0d( "CROTA", map_pa*AST__DR2D, status );
            autorot = AST__BAD;
         }

         parGet0d( "CROTA", par + 6, status );
         if( par[ 6 ] != autorot ) udefs = 0;
         par[ 6 ] *= AST__DD2R;

/* If any parameter were given explicit values which differ from the
   autogrid default values, then we need to re-calculate the optimal CRPIX1/2
   values. We also do this if all the points are effectively co-incident. */
         if( ( coin || !udefs ) && autogrid && refine_crpix ) {
            par[ 0 ] = AST__BAD;
            par[ 1 ] = AST__BAD;
            kpg1Opgrd( nallpos, allpos, strcmp( usesys, "AZEL" ), par,
                       &rdiam, status );
         }

/* Display the projection parameters being used. */
         smf_display_projpars( skyframe, par, status );

/* Write out the reference grid coords to output parameter PIXREF. */
         parPut1d( "PIXREF", 2, par, status );

/* If no grid was found, indicate that no spatial projection will be used. */
      } else {
         msgBlank( status );
         msgOutif( MSG__NORM, " ", "   The output will be a sparse array "
                        "containing a list of spectra.", status );
      }

/* If we have a pre-defined spatial projection, indicate that the output
   array need not be sparse. */
   } else {
      sparse = 0;
   }

/* Return usedefs if requested */
   if( usedefs ) {
     *usedefs = udefs;
   }

/* Set sparse if requested */
   if( issparse ) *issparse = sparse;

}