Beispiel #1
0
void smf_get_extpar( AstKeyMap *keymap, smf_tausrc *tausrc,
                     smf_extmeth *extmeth, int *import, int *status ) {

  const char *tempstr = NULL;


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

  /* Obtain parameters from keymap when non-NULL pointers given */

  if( import ) {
    if( ! astMapGet0I( keymap, "IMPORT", import ) ) {
       *import = 0;
    }
  }

  if( tausrc ) {
    if( astMapGet0C( keymap, "TAUSRC", &tempstr ) ) {

      switch( toupper(tempstr[0]) ) {
      case 'A': /* AUTO */
        *tausrc = SMF__TAUSRC_AUTO;
        break;
      case 'C': /* CSO* */
        /* Need to compare CSOTAU to CSOFIT */
        switch( toupper(tempstr[3]) ) {
        case 'T':
          *tausrc = SMF__TAUSRC_CSOTAU;
          break;
        case 'F':
          *tausrc = SMF__TAUSRC_CSOFIT;
          break;
        default:
          *tausrc = SMF__TAUSRC_NULL;
        }
        break;
      case 'F': /* FILTERTAU */
        *tausrc = SMF__TAUSRC_TAU;
        break;
      case 'W': /* WVM */
        /* Need to compare WVMRAW to WVMFIT */
        switch (toupper(tempstr[3])) {
        case 'R':
          *tausrc = SMF__TAUSRC_WVMRAW;
          break;
        case 'F':
          *tausrc = SMF__TAUSRC_WVMFIT;
          break;
        default:
          *tausrc = SMF__TAUSRC_NULL;
        }
        break;
      default:
        *tausrc = SMF__TAUSRC_NULL;
      }
    } else {
      *tausrc = SMF__TAUSRC_NULL;
    }
  }

  if( extmeth ) {
    if( astMapGet0C( keymap, "TAUMETHOD", &tempstr ) ) {
      switch( toupper(tempstr[0]) ) {
      case 'A':
        *extmeth = SMF__EXTMETH_ADAPT;
        break;
      case 'Q':
        *extmeth = SMF__EXTMETH_SINGLE;
        break;
      case 'F':
        *extmeth = SMF__EXTMETH_FULL;
        break;
      default:
        *extmeth = SMF__EXTMETH_NONE;
      }
    } else {
      *extmeth = SMF__EXTMETH_NONE;
    }
  }
}
Beispiel #2
0
Hero *  Hero::constructFromFitsFile(const QString &fname)
{
    FitsParser parser;
    bool parsedOk = parser.loadFile( FitsFileLocation::fromLocal( fname));
    if( ! parsedOk) {
        dbg(1) << "Parser failed to load " << fname;
        Hero * heroPtr = new Hero;
        heroPtr-> addError( "FitsParser failed to load the file");
        return heroPtr;
    }

    // alias hdr
    auto & hdr = parser.getHeaderInfo().headerLines;

    Hero * heroPtr = new Hero;
    Hero & hero = * heroPtr;
    AstErrorGuard guard( heroPtr);
    AstGCGuard gcGuard;

    // set naxes in case AST fails to read this file
    hero.m_ast.naxes = parser.getHeaderInfo().naxis;

    // set up bunit
    {
        hero.m_bunit = parser.getHeaderInfo().bunit;
    }
    // and the nicer version of bunit
    {
        QString u = hero.m_bunit.simplified();
        if( u.toLower() == "kelvin") {
            hero.m_bunitNiceHtml = "K";
        }
        else {
            hero.m_bunitNiceHtml = u;
        }
    }

    // Create a FitsChan and feed it the fits header
    AstFitsChan *fitschan;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-zero-length"
    fitschan = astFitsChan( NULL, NULL, "" );
#pragma GCC diagnostic pop

    std::cout << "astOK = " << astOK << "\n";

    // feed the header lines one by one and check for errors
    for( const QString & s : hdr) {
        std::string stdstr = s.toStdString();
        astPutFits( fitschan, stdstr.c_str(), 1);
        if( ! astOK) {
            astClearStatus;
            QString ss = s.trimmed();
            std::cout << "Skipping bad card: " << ss << "\n";
            hero.addError( "Skipping card: " + ss);
        }
    }
    // reposition to the beginning of the channel (ast thing, it's required, hmmmkey)
    astClear( fitschan, "Card" );

    std::cout << "astOK = " << astOK << "\n";

    std::cout << "Here\n";

    auto encoding = AstWrappers::getC( fitschan, "Encoding" );
    std::cout << "Encoding = " << encoding << "\n";

    // do we have warnings?
    AstKeyMap * warnings = static_cast<AstKeyMap *>( astWarnings( fitschan));

    if( warnings && astOK ) {
        std::cout << "Warnings:\n";

        int iwarn = 1;
        while( astOK ) {
            std::string key = QString("Warning_%1").arg( iwarn).toStdString();
            const char * message = nullptr;
            if( astMapGet0C( warnings, key.c_str(), & message ) ) {
                printf( "\n- %s\n", message );
                hero.addError( QString( "Warning: %1").arg( message));
            } else {
                break;
            }
        }
    }
    else {
        std::cout << "No warnings\n";
    }

    // create a frameset for this file
    AstFrameSet * wcsinfo = static_cast<AstFrameSet *> ( astRead( fitschan ));
    std::cout << "astOK = " << astOK << "\n";

    if ( ! astOK ) {
        std::cout << "astOK is not ok\n";
        hero.addError( "astRead failed");
        astClearStatus;
        return heroPtr;
    }
    else if ( wcsinfo == AST__NULL ) {
        hero.addError( "No WCS found in the fits file");
        std::cout << "No WCS found\n";
        return heroPtr;
    }
    else if ( AstWrappers::getC( wcsinfo, "Class" ) != "FrameSet") {
        std::cout << "Some other weird error occured\n";
        hero.addError( "AstLib returned non-frame-set");
        return heroPtr;
    }        

    // frame was read in OK, save it
    hero.m_ast.origWcsInfo = wcsinfo;
    astExempt( hero.m_ast.origWcsInfo);
    hero.m_ast.currWcsInfo = astClone( hero.m_ast.origWcsInfo);
    astExempt( hero.m_ast.currWcsInfo);

    astShow( wcsinfo);

    // extract the current sky system
    // TODO: this assumes axis1 is a skycs
    QString skysys = AstWrappers::getC( wcsinfo, "System(1)");
    hero.m_currentSkyCs = string2skycs( skysys);
    hero.m_originalSkyCs = hero.m_currentSkyCs;

    // extract the labels/etc for axes
    hero.m_ast.naxes = AstWrappers::getI( wcsinfo, "Naxes" );
    hero.parseAxesInfo();

    return heroPtr;
}
Beispiel #3
0
int main( int argc, char **argv ){

/* Local variables: */
   AstBox *pixbox;
   AstFitsChan *fchan;
   AstFrame *pixfrm;
   AstFrame *wcsfrm;
   AstFrameSet *frameset;
   AstKeyMap *warnings;
   AstMapping *pix2wcs;
   AstObject *object;
   AstRegion *wcsbox;
   AstStcsChan *schan;
   FILE *fd;
   char key[ 15 ];
   char keyword[ 9 ];
   const char *message;
   double p1[ MAX_AXES ];
   double p2[ MAX_AXES ];
   int axis;
   int iwarn;
   int naxis;
   int status;

/* Initialised the returned system status to indicate success. */
   status = 0;

/* Check a file was specified on the command line, and attempt to open it
   for read access. */
   if( argc < 2 ) {
      printf( "Usage: stcschan-demo2 <header-file>\n" );
      status = 1;
   } else {
      fd = fopen( argv[ 1 ], "r" );
      if( !fd ) {
         printf("Failed to open input file '%s'.\n", argv[ 1 ] );
         status = 1;
      }
   }

/* If a disk file was opened successfully... */
   if( !status ) {

/* Start an AST object context. This means we do not need to annull
   each AST Object individually. Instead, all Objects created within
   this context will be annulled automatically by the corresponding
   invocation of astEnd. */
      astBegin;

/* Create a FitsChan. This is the object that converts external FITS
   headers into corresponding AST Objects. Tell it to use the "source"
   function for obtaining lines of text from the disk file. */
      fchan = astFitsChan( source, NULL, " " );

/* Associate the descriptor for the input disk file with the StcsChan.
   This makes it available to the "source" function. Since this
   application is single threaded, we could instead have made "fd" a
   global variable, but the ChannelData facility is used here to illustrate
   how to pass data to a source or sink function safely in a multi-threaded
   application. */
      astPutChannelData( fchan, fd );

/* Attempt to read the FITS heades and convert them into an AST FrameSet. */
      object = astRead( fchan );

/* The astRead function is a generic function and so returns a generic
   AstObject pointer. Check an Object was created successfully. */
      if( !object ) {
         printf( "Failed to read an AST Object from file '%s'.\n",
                 argv[ 1 ] );
         status = 1;

/* Now check that the object read is actually an AST FrameSet, rather than
   some other class of AST Object. */
      } else if( !astIsAFrameSet( object ) ) {
         printf( "Expected a FrameSet but read a %s from file '%s'.\n",
                 astGetC( object, "Class" ), argv[ 1 ] );
         status = 1;

/* We now know we have a FrameSet so it is safe to use the pointer
   returned by astRead as a FrameSet pointer. Do the cast now to avoid
   repeated casting in future. */
      } else {
         frameset = (AstFrameSet *) object;

/* Get a pointer to the Frame that describes the attributes of the FITS
   world coordinate system. This is the current Frame in the FrameSet
   read from the FITS headers. */
         wcsfrm = astGetFrame( frameset, AST__CURRENT );

/* Get a pointer to the Frame that describes the attributes of the FITS
   pixel coordinate system. This is the base Frame in the FrameSet
   read from the FITS headers. */
         pixfrm = astGetFrame( frameset, AST__BASE );

/* Get the Mapping that transforms pixel positions into WCS positions.
   The is the Mapping from base to current Frame in the  FrameSet read
   from the FITS headers. */
         pix2wcs = astGetMapping( frameset, AST__BASE, AST__CURRENT );

/* Get the number of axes in ther pixel Frame. */
         naxis = astGetI( pixfrm, "Naxes" );

/* For each pixel axis, form the name of the corresponding NAXISi
   keyword. */
         for( axis = 0; axis < naxis; axis++ ) {
            sprintf( keyword, "NAXIS%d", axis + 1 );

/* Store the pixel coordinate on the current axis at the lower left corner
   of the first pixel. */
            p1[ axis ] = 0.5;

/* Get the NAXISi value for the current axis from the FITS header, and
   store it in array "p2". Report an error if NAXISi is not found. */
            if( !astGetFitsF( fchan, keyword, p2 + axis ) ){
               printf("Keyword '%s' not found in header\n", keyword );
               status = 1;
               break;

/* If it is found, modify "p2" so that it holds the pixel coordinate on
   the current axis at the upper right corner of the last pixel. */
            } else {
               p2[ axis ] += 0.5;
            }
         }
      }

/* If all has gone well, create an AST Region (a Box) describing the
   rectangular region of pixel coordinates covered by the pixel array. */
      if( !status ) {
         pixbox = astBox( pixfrm, 1, p1, p2, NULL, " " );

/* Map this box into the FITS world coordinate system. The Mapping is
   specified by "pix2wcs", and the attributes of the resulting axes is
   described by "wcsfrm". */
         wcsbox = astMapRegion( pixbox, pix2wcs, wcsfrm );

/* Create an StcsChan. This is the object that converts (either way)
   between external STC-S descriptions and their corresponding AST Objects.
   Tell it to use the "source" function for obtaining lines of text from
   the disk file. Also tell it to store all warnings generated by the
   conversion for later use. Other attributes of the StcsChan class retain
   their default values. */
         schan = astStcsChan( NULL, NULL, "ReportLevel=3" );

/* Attempt to write out the Region describing the pixel array (in WCS)
   as an STC-S description. Report an error if this fails. */
         if( ! astWrite( schan, wcsbox ) && astOK ) {
            printf( "Failed to convert the Region into an STC-S "
                    "description.\n" );
         }
      }

/* We asked the StcsChan to record any warnings that were generated
   whilst converting the AST Region into a corresponding STC-S description.
   We now see if any such warnings were generated by the earlier call to
   astWrite. */
      warnings = astWarnings( schan );

/* If any warnings were generated, and if no other error has occurred so
   far, display the warnings. */
      if( warnings && !status && astOK ) {
         printf( "\nThe following warnings were issued:\n" );

/* The warnings are stored in an AST KeyMap (a sort of hashmap). Each
   warning message is associated with a key of the form "Warning_1",
   "Warning_2", etc. Loop round successive keys, obtaining a value for
   each key from the warnings KeyMap, and displaying it. */
         iwarn = 1;
         while( astOK ) {
            sprintf( key, "Warning_%d", iwarn++ );
            if( astMapGet0C( warnings, key, &message ) ) {
               printf( "\n- %s\n", message );
            } else {
               break;
            }
         }
      }

/* End the AST Object context. All Objects created since the
   corresponding invocation of astbegin will be annulled automatically. */
      astEnd;

/* Close the disk file. */
      (void) fclose( fd );
   }

/* If an error occurred in the AST library, set the retiurns system
   status non-zero. */
   if( !astOK ) status = 1;
   return status;
}
Beispiel #4
0
void
smf_flat_params( const smfData * refdata, const char resistpar[],
                 const char methpar[], const char orderpar[], const char snrminpar[],
                 double * refohms, double **resistance, int * outrows,
                 int * outcols, smf_flatmeth  *flatmeth,
                 int * order, double * snrmin, smfData ** heateff,
                 int * status ) {

  dim_t datarows = 0;       /* Number of rows in refdata */
  dim_t datacols = 0;       /* Number of columns in refdata */
  size_t j = 0;             /* Counter, index */
  char method[SC2STORE_FLATLEN]; /* flatfield method string */
  size_t nbols;              /* Number of bolometers */
  double refohmsval = 0.0;   /* Internal version of refohms */
  AstKeyMap * resmap = NULL; /* Resistor map */
  AstKeyMap * subarrays = NULL; /* Subarray lookup table */
  char thissub[32];          /* This sub-instrument string */

  if (resistance) *resistance = NULL;

  if (*status != SAI__OK) return;

  if (!refdata) {
    *status = SAI__ERROR;
    errRep( "", "Must provide reference data file to calculate flatfield parameters"
            " (possible programming error)", status );
    return;
  }

  /* Based on refdata we now need to calculate the default reference
     resistance and retrieve the correct heater efficiency file for each array.
     We need the unique subarray string so that we can set up a look up keymap.
     There is no code in SMURF to return all the known subarrays but
     we need to know all the options in order to use kpg1Config. */
  subarrays = astKeyMap( " " );
  astMapPut0I( subarrays, "CG450MK2_M0907D0501", 0, NULL );
  astMapPut0I( subarrays, "CG850MK2_M0904D0503", 0, NULL );
  astMapPut0I( subarrays, "SG850_M0906D1005", 0, NULL );
  astMapPut0I( subarrays, "SG850_M1002D1006", 0, NULL );
  astMapPut0I( subarrays, "SG850_M1005D1007", 0, NULL );
  astMapPut0I( subarrays, "SG850_M1003D1004", 0, NULL );
  astMapPut0I( subarrays, "SG450_M1004D1000", 0, NULL );
  astMapPut0I( subarrays, "SG450_M1007D1002", 0, NULL );
  astMapPut0I( subarrays, "SG450_M1006D1003", 0, NULL );
  astMapPut0I( subarrays, "SG450_M1009D1008", 0, NULL );

  /* and indicate which subarray we are interested in (uppercased) */
  smf_fits_getS( refdata->hdr, "ARRAYID", thissub, sizeof(thissub), status );
  { /* need to uppercase */
    size_t l = strlen(thissub);
    for (j=0;j<l;j++) {
      thissub[j] = toupper(thissub[j]);
    }
  }
  astMapPut0I( subarrays, thissub, 1, NULL );

  /* Read the config file */
  resmap = kpg1Config( resistpar, "$SMURF_DIR/smurf_calcflat.def",
                       subarrays, 1, status );
  subarrays = astAnnul( subarrays );

  if (*status != SAI__OK) goto CLEANUP;

  /* Read the reference resistance */
  astMapGet0D( resmap, "REFRES", &refohmsval );

  if (refohms && *status == SAI__OK) {
    *refohms = refohmsval;
    msgOutiff(MSG__VERB, "",
              "Read reference resistance for subarray %s of %g ohms\n",
              status, thissub, *refohms );
  }

  /* We no longer want to read per-bolometer resistor values from the
     config file. To retain backwards compatibility with the current
     implementation of smf_flat_standardpow we simply fill the
     per-bol resistance array with the reference resistance which
     effectively disables smf_flat_standardpow */

  smf_get_dims( refdata, &datarows, &datacols, NULL, NULL, NULL, NULL, NULL, status );
  nbols = datacols * datarows;

  if (*status == SAI__OK && resistance ) {
    *resistance = astMalloc( nbols*sizeof(**resistance) );
    for (j = 0; j < (size_t)nbols; j++) {
      (*resistance)[j] = refohmsval;
    }
  }

  /* Get the heater efficiency file */
  if (heateff && astMapHasKey( resmap, "HEATEFF" ) ) {
    const char * heateffstr = NULL;
    if (astMapGet0C( resmap, "HEATEFF", &heateffstr )) {
      Grp * heateffgrp = NULL;
      smfData * heatefftmp = NULL;
      heateffgrp = grpNew( "heateff", status );
      grpPut1( heateffgrp, heateffstr, 0, status );
      smf_open_file( NULL, heateffgrp, 1, "READ", SMF__NOTTSERIES|SMF__NOFIX_METADATA, &heatefftmp, status );

      /* Divorce the smfData from the underlying file. This file stays open for the entire
         duration of the data processing and can some times lead to issues when we attempt
         to close it an hour after we opened it (it's usually on an NFS disk) */
      if (*status == SAI__OK) {
        *heateff = smf_deepcopy_smfData( NULL, heatefftmp, 0,
                                         SMF__NOCREATE_FILE | SMF__NOCREATE_FTS |
                                         SMF__NOCREATE_DA,
                                         0, 0, status );
        smf_close_file(NULL, &heatefftmp, status);
      }

      /* Check the dimensions */
      if (*status == SAI__OK) {
        dim_t heatrows = 0;
        dim_t heatcols = 0;
        smf_get_dims( *heateff, &heatrows, &heatcols, NULL, NULL, NULL, NULL, NULL, status );

        if (*status == SAI__OK) {
          if ( datarows != heatrows || datacols != heatcols ) {
            *status = SAI__ERROR;
            errRepf( "", "Dimensions of heater efficiency file %s are (%zu, %zu)"
                     " but flatfield has dimensions (%zu, %zu)",
                     status, heateffstr, (size_t)heatrows, (size_t)heatcols,
                     (size_t)datarows, (size_t)datacols);
          }
        }

        if (*status == SAI__OK) {
          smf_dtype_check_fatal( *heateff, NULL, SMF__DOUBLE, status );
          if (*status == SMF__BDTYP) {
            errRepf("", "Heater efficiency data in %s should be double precision",
                   status, heateffstr);
          }
        }

        if (*status == SAI__OK) {
          char heateffarrid[32];
          smf_fits_getS( refdata->hdr, "ARRAYID", heateffarrid, sizeof(heateffarrid), status );
          if (*status != SAI__OK) errAnnul( status );
          if (strcasecmp( thissub, heateffarrid ) != 0 ) {
            if (*status == SAI__OK) {
              *status = SAI__ERROR;
              errRepf("", "Subarray associated with heater efficiency image (%s)"
                     " does not match that of the data to be flatfielded (%s)",
                      status, heateffarrid, thissub );
            }
          }
        }
      }
      if (heateffgrp) grpDelet( &heateffgrp, status );
    }
  }

  if (methpar && flatmeth) {
    /* See if we want to use TABLE or POLYNOMIAL mode */
    parChoic( methpar, "POLYNOMIAL", "POLYNOMIAL, TABLE", 1,
              method, sizeof(method), status );

    *flatmeth = smf_flat_methcode( method, status );

    if (*flatmeth == SMF__FLATMETH_POLY) {
      /* need an order for the polynomial */
      if (order && orderpar) {
        parGdr0i( orderpar, 1, 1, 3, 1, order, status );

        /* and if the order is 1 then we can ask for the snr min */
        if (snrminpar && *order == 1) {
          parGet0d( snrminpar, snrmin, status );
        }
      }

    } else {
      /* need an snr min for table mode responsivities */
      if (snrminpar) parGet0d( snrminpar, snrmin, status );
    }
  }

  if (outrows) *outrows = datarows;
  if (outcols) *outcols = datacols;

 CLEANUP:
  resmap = astAnnul( resmap );
  if (*status != SAI__OK) {
    if (resistance && *resistance) *resistance = astFree( *resistance );
    if (heateff && *heateff) smf_close_file( NULL, heateff, status );
  }

  return;

}
Beispiel #5
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;
}
Beispiel #6
0
void atlKy2hd( AstKeyMap *keymap, HDSLoc *loc, int *status ) {
    /*
    *  Name:
    *     atlKy2hd

    *  Purpose:
    *     Copies values from an AST KeyMap to a primitive HDS object.

    *  Language:
    *     C.

    *  Invocation:
    *     void atlKy2hd( AstKeyMap *keymap, HDSLoc *loc, int *status )

    *  Description:
    *     This routine copies the contents of an AST KeyMap into a supplied
    *     HDS structure.

    *  Arguments:
    *     keymap
    *        An AST pointer to the KeyMap.
    *     loc
    *        A locator for the HDS object into which the KeyMap contents
    *        are to be copied. A new component is added to the HDS object for
    *        each entry in the KeyMap.
    *     status
    *        The inherited status.

    *  Copyright:
    *     Copyright (C) 2008, 2010, 2012 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
    *     TIMJ: Tim Jenness (JAC, Hawaii)
    *     {enter_new_authors_here}

    *  History:
    *     29-APR-2008 (DSB):
    *        Original version.
    *     2010-09-23 (TIMJ):
    *        Fix arrays of strings.
    *     2010-09-30 (TIMJ):
    *        Make sure that we are using the correct status pointer in AST.
    *     2010-10-01 (TIMJ):
    *        Sort the keys when writing to HDS structured.
    *     2010-10-04 (TIMJ):
    *        Support Short ints in keymap
    *     14-SEP-2012 (DSB):
    *        Moved from kaplibs to atl.
    *     17-SEP-2012 (DSB):
    *        Add support for undefined values.
    *     {enter_further_changes_here}

    *  Bugs:
    *     {note_any_bugs_here}
    */

    /* Local Varianles: */
    AstObject **objArray = NULL;
    AstObject *obj = NULL;
    HDSLoc *cloc = NULL;
    HDSLoc *dloc = NULL;
    const char *cval = NULL;
    const char *key;
    double dval;
    float fval;
    int i;
    int ival;
    int j;
    int lenc;
    int nval;
    char *oldsortby;
    int *oldstat = NULL;
    int size;
    int type;
    int veclen;
    size_t el;
    void *pntr = NULL;

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

    /* Make sure that we are checking AST status */
    oldstat = astWatch( status );

    /* If set, save the old SortBy value and then ensure alphabetical sorting.
       We need to take a copy of the original string since the buffer in which
       the string is stored may be re-used by subsequent incocations of astGetC.  */
    if( astTest( keymap, "SortBy" ) ) {
        int nc = 0;
        oldsortby = astAppendString( NULL, &nc, astGetC( keymap, "SortBy" ) );
    } else {
        oldsortby = NULL;
    }
    astSet( keymap, "SortBy=KeyUp" );

    /* Loop round each entry in the KeyMap. */
    size = astMapSize( keymap );
    for( i = 0; i < size; i++ ) {

        if (*status != SAI__OK) break;

        /* Get the key. the data type and the vector length for the current
           KeyMap entry. */
        key = astMapKey( keymap, i );
        type = astMapType( keymap, key );
        veclen = astMapLength( keymap, key );

        /* If the current entry holds one or more nested KeyMaps, then we call
           this function recursively to add them into a new HDS component. */
        if( type == AST__OBJECTTYPE ) {

            /* First deal with scalar entries holding a single KeyMap. */
            if( veclen == 1 ) {
                datNew( loc, key, "KEYMAP_ENTRY", 0, NULL, status );
                datFind( loc, key, &cloc, status );

                (void) astMapGet0A( keymap, key, &obj );

                if( astIsAKeyMap( obj ) ) {
                    atlKy2hd( (AstKeyMap *) obj, cloc, status );

                } else if( *status == SAI__OK ) {
                    *status = SAI__ERROR;
                    errRep( "", "atlKy2hd: Supplied KeyMap contains unusable AST "
                            "objects (programming error).", status );
                }

                datAnnul( &cloc, status );

                /* Now deal with vector entries holding multiple KeyMaps. */
            } else {
                datNew( loc, key, "KEYMAP_ENTRY", 1, &veclen, status );
                datFind( loc, key, &cloc, status );

                objArray = astMalloc( sizeof( AstObject *) * (size_t)veclen );
                if( objArray ) {
                    (void) astMapGet1A( keymap, key, veclen, &nval, objArray );

                    for( j = 1; j <= veclen; j++ ) {
                        datCell( cloc, 1, &j, &dloc, status );

                        if( astIsAKeyMap( objArray[ j - 1 ] ) ) {
                            atlKy2hd( (AstKeyMap *) objArray[ j - 1 ], dloc, status );

                        } else if( *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            errRep( "", "atlKy2hd: Supplied KeyMap contains unusable AST "
                                    "objects (programming error).", status );
                        }

                        datAnnul( &dloc, status );
                    }

                    objArray = astFree( objArray );
                }

                datAnnul( &cloc, status );
            }

            /* For primitive types... */
        } else if( type == AST__INTTYPE ) {
            if( veclen == 1 ) {
                datNew0I( loc, key, status );
                datFind( loc, key, &cloc, status );
                (void) astMapGet0I( keymap, key, &ival );
                datPut0I( cloc, ival, status );
                datAnnul( &cloc, status );

            } else {
                datNew1I( loc, key, veclen, status );
                datFind( loc, key, &cloc, status );
                datMapV( cloc, "_INTEGER", "WRITE", &pntr, &el, status );
                (void) astMapGet1I( keymap, key, veclen, &nval, (int *) pntr );
                datUnmap( cloc, status );
                datAnnul( &cloc, status );
            }

        } else if( type == AST__SINTTYPE ) {
            short sval = 0;
            if( veclen == 1 ) {
                datNew0W( loc, key, status );
                datFind( loc, key, &cloc, status );
                (void) astMapGet0S( keymap, key, &sval );
                datPut0W( cloc, sval, status );
                datAnnul( &cloc, status );

            } else {
                datNew1W( loc, key, veclen, status );
                datFind( loc, key, &cloc, status );
                datMapV( cloc, "_WORD", "WRITE", &pntr, &el, status );
                (void) astMapGet1S( keymap, key, veclen, &nval, (short *) pntr );
                datUnmap( cloc, status );
                datAnnul( &cloc, status );
            }

        } else if( type == AST__DOUBLETYPE ) {
            if( veclen == 1 ) {
                datNew0D( loc, key, status );
                datFind( loc, key, &cloc, status );
                (void) astMapGet0D( keymap, key, &dval );
                datPut0D( cloc, dval, status );
                datAnnul( &cloc, status );

            } else {
                datNew1D( loc, key, veclen, status );
                datFind( loc, key, &cloc, status );
                datMapV( cloc, "_DOUBLE", "WRITE", &pntr, &el, status );
                (void) astMapGet1D( keymap, key, veclen, &nval, (double *) pntr );
                datUnmap( cloc, status );
                datAnnul( &cloc, status );
            }

        } else if( type == AST__FLOATTYPE ) {
            if( veclen == 1 ) {
                datNew0R( loc, key, status );
                datFind( loc, key, &cloc, status );
                (void) astMapGet0F( keymap, key, &fval );
                datPut0R( cloc, fval, status );
                datAnnul( &cloc, status );

            } else {
                datNew1R( loc, key, veclen, status );
                datFind( loc, key, &cloc, status );
                datMapV( cloc, "_REAL", "WRITE", &pntr, &el, status );
                (void) astMapGet1F( keymap, key, veclen, &nval, (float *) pntr );
                datUnmap( cloc, status );
                datAnnul( &cloc, status );
            }

        } else if( type == AST__STRINGTYPE ) {
            lenc = astMapLenC( keymap, key );

            if( veclen == 1 ) {
                datNew0C( loc, key, lenc, status );
                datFind( loc, key, &cloc, status );
                (void) astMapGet0C( keymap, key, &cval );
                datPut0C( cloc, cval, status );
                datAnnul( &cloc, status );

            } else {
                datNew1C( loc, key, lenc, veclen, status );
                datFind( loc, key, &cloc, status );
                datMapV( cloc, "_CHAR", "WRITE", &pntr, &el, status );
                (void) atlMapGet1C( keymap, key, veclen*lenc, lenc, &nval,
                                    (char *) pntr, status );
                datUnmap( cloc, status );
                datAnnul( &cloc, status );
            }

            /* KeyMap "UNDEF" values are always scalar and have no corresponding HDS
               data type. So arbitrarily use an "_INTEGER" primitive with no defined
               value to represent a KeyMap UNDEF value. */
        } else if( type == AST__UNDEFTYPE ) {
            datNew0L( loc, key, status );

            /* Unknown or unsupported data types. */
        } else if( *status == SAI__OK ) {
            *status = SAI__ERROR;
            msgSeti( "T", type );
            errRep( "", "atlKy2hd: Supplied KeyMap contains entries with "
                    "unusable data type (^T) (programming error).", status );
        }
    }

    /* If it was originally set, re-instate the old SortBy value in the KeyMap,
       then free the memory. Otherwise, clear the SortBy attribute. */
    if( oldsortby ) {
        astSetC( keymap, "SortBy", oldsortby );
        oldsortby = astFree( oldsortby );
    } else {
        astClear( keymap, "SortBy" );
    }

    /* Reset AST status */
    astWatch( oldstat );

}
Beispiel #7
0
static void DisplayKeyMap( AstKeyMap *km, int sort, const char *prefix,
                           AstKeyMap *refkm, int *status ){
/*
*  Name:
*     DisplayKeyMap

*  Purpose:
*     Display the contents of a keymap.

*  Synopsis:
*     void DisplayKeyMap( AstKeyMap *km, int sort, const char *prefix,
*                         int *status )

*  Arguments:
*     km
*        Pointer to the KeyMaps containing the values to display.
*     sort
*        If non-zero, sort the values alphabetically by their keys.
*     prefix
*        A string to prepend to eack key.
*     refkm
*        Reference key map (e.g. values from the supplied configuration
*        rather than the NDF history), or null if not required.
*     status
*        Inherited status pointer.

*  Description:
*     This function displays the contents of a supplied KeyMap as
*     a series of "key = value" strings, one per line. It calls itself
*     recursively if a nested KeyMap is found, adding a suitable
*     prefix to the nested keys.  If a reference key map is supplied then
*     the output shows how the main key map differs from it.
*/

/* Local Variables: */
   AstObject *avalue;
   AstObject *refavalue;
   char cbuffer[ 255 ];
   char newpref[ 255 ];
   const char *cvalue;
   const char *refcvalue;
   const char *key;
   int ikey;
   int ival;
   int nc;
   int nkey;
   int nval;

/* Check the inherited status */
   if( *status != SAI__OK ) return;
   if (refkm) astClear(refkm, "KeyError");

/* Sort the supplied KeyMap is required. */
   if( sort ) astSetC( km, "SortBy", "KeyUp" );

/* Loop round all keys in the supplied KeyMap. */
   nkey = astMapSize( km );
   for( ikey = 0; ikey < nkey; ikey++ ) {
      key = astMapKey( km, ikey );

/* If the current entry is a nest KeyMap, get a pointer to it and call
   this function recurisvely to display it, modifying the prefix to add
   to each key so that it includes the key associated with the nest keymap. */
      if( astMapType( km, key ) == AST__OBJECTTYPE ) {
         astMapGet0A( km, key, &avalue );
         if (refkm) {
            if (! astMapGet0A(refkm, key, &refavalue)) {
               refavalue = (AstObject*) astKeyMap("");
            }
         }
         else {
            refavalue = NULL;
         }
         sprintf( newpref, "%s%s.", prefix, key );
         DisplayKeyMap( (AstKeyMap *) avalue, sort, newpref,
                        (AstKeyMap *) refavalue, status );
         avalue = astAnnul( avalue );
         if (refavalue) refavalue = astAnnul(refavalue);

/* If the current entry is not a nested keymap, we display it now. */
      } else {

/* Get the vector length of the entry. */
         nval = astMapLength( km, key );

/* If it is a scalar, just get its value as a character string using
   the automatic type conversion provided by the KeyMap class, and
   display it, putting the supplied prefix at the start of the key. */
         if( nval <= 1 ) {
            cvalue = "<undef>";
            astMapGet0C( km, key, &cvalue );
            if (refkm) {
               refcvalue = "<undef>";
               if (astMapGet0C(refkm, key, &refcvalue)
                     && ! strcmp(cvalue, refcvalue)) {
                  msgOutf("", "- %s%s = %s", status, prefix, key, cvalue);
               }
               else {
                  msgOutf("", "+ %s%s = %s", status, prefix, key, cvalue);
               }
            }
            else {
               msgOutf( "", "%s%s = %s", status, prefix, key, cvalue );
            }

/* If it is a vector, we construct a string containing a comma-separated
   list of elements, enclosed in parentheses. */
         } else {
            nc = 0;
            cvalue = astAppendString( NULL, &nc, "(" );
            for( ival = 0; ival < nval; ival++ ) {
               if( astMapGetElemC( km, key, sizeof( cbuffer) - 1, ival,
                                   cbuffer ) ) {
                  cvalue = astAppendString( (char *) cvalue, &nc, cbuffer );
               }
               if( ival < nval - 1 ) cvalue = astAppendString( (char *) cvalue,
                                                                &nc, "," );
            }
            cvalue = astAppendString( (char *) cvalue, &nc, ")" );

/* Do the same for the reference KeyMap. */
            if (refkm && (nval = astMapLength(refkm, key))) {
               nc = 0;
               refcvalue = astAppendString(NULL, &nc, "(");
               for (ival = 0; ival < nval; ival++) {
                  if (ival) {
                     refcvalue = astAppendString((char*) refcvalue, &nc, "," );
                  }

                  if (astMapGetElemC(refkm, key, sizeof(cbuffer) - 1, ival,
                                      cbuffer)) {
                     refcvalue = astAppendString((char*) refcvalue, &nc,
                                                 cbuffer);
                  }
               }
               refcvalue = astAppendString((char*) refcvalue, &nc, ")");
            }
            else {
               refcvalue = NULL;
            }

/* Display the total string, with the current key prefix, and free the memory
   used to store it. */
            if (refkm) {
               if (refcvalue && ! strcmp(cvalue, refcvalue)) {
                  msgOutf("", "- %s%s = %s", status, prefix, key, cvalue);
               }
               else {
                  msgOutf("", "+ %s%s = %s", status, prefix, key, cvalue);
               }
            }
            else {
               msgOutf( "", "%s%s = %s", status, prefix, key, cvalue );
            }
            cvalue = astFree( (void *) cvalue );
            if (refcvalue) refcvalue = astFree((void*) refcvalue);
         }
      }
   }
}
double cupidConfigD( AstKeyMap *config, const char *name, double def,
                     int *status ){
/*
*+
*  Name:
*     cupidConfigD

*  Purpose:
*     Get the value of a configuration parameter.

*  Language:
*     Starlink C

*  Synopsis:
*     double cupidConfigD( AstKeyMap *config, const char *name, double def,
*        int *status )

*  Description:
*     This function returns a named value from the supplied KeyMap. If
*     the KeyMap does not contain the named value, an attempt is made to
*     obtain a value from a secondary KeyMap which should be stored
*     within the supplied KeyMap, using a key equal to the constant
*     string CUPID__CONFIG. If the secondary KeyMap does not contain a
*     value, then the supplied default value is returned. In either case,
*     the returned value is stored in the KeyMap.

*  Parameters:
*     config
*        An AST KeyMap holding the configuration parameters. If NULL is
*        supplied, the default value is returned without error.
*     name
*        The name of the value to extract from the KeyMap.
*     def
*        The default value to use.
*     status
*        Pointer to the inherited status value.

*  Returned Value:
*     The required value. The supplied default value is returned if an
*     error occurs.

*  Copyright:
*     Copyright (C) 2005 Particle Physics & Astronomy Research Council.
*     Copyright (C) 2008 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
*     {enter_new_authors_here}

*  History:
*     5-OCT-2005 (DSB):
*        Original version.
*     26-MAR-2008 (DSB):
*        Re-report a more friendly message iof the supplied text could
*        not be interpreted as a numerical value.
*     {enter_further_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
*/

/* Local Variables: */
   AstObject *sconfig; /* Object pointer obtained from KeyMap */
   const char *text;   /* Pointer to uninterprted text value */
   double ret;         /* The returned value */

/* Initialise */
   ret = def;

/* Abort if an error has already occurred, or if no KeyMap was supplied. */
   if( *status != SAI__OK || !config ) return ret;

/* Attempt to extract the named value from the supplied KeyMap. */
   if( !astMapGet0D( config, name, &ret ) ) {

/* If the value was not found in the KeyMap, see if the KeyMap contains a
   secondary KeyMap. */
      if( astMapGet0A( config, CUPID__CONFIG, &sconfig ) ) {

/* If it does, see if the secondary KayMap contains a value for the named
   entry. If it does, remove the value from the KeyMap so it does not
   appear in the CUPID NDF extension. */
         if( astMapGet0D( (AstKeyMap *) sconfig, name, &ret ) ) {
            astMapRemove(  (AstKeyMap *) sconfig, name );

/* If the text supplied by the user could not be interpreted as a
   floating point value, re-report the error. */
         } else if( *status == AST__MPGER ) {
            ret = def;
            errAnnul( status );
            if( astMapGet0C( config, name, &text ) ) {
               *status = SAI__ERROR;
               msgSetc( "T", text );
               msgSetc( "N", name );
               errRep( "", "Illegal value \"^T\" supplied for configuration "
                       "parameter ^N.", status );
            }

/* If the value was not found in either KeyMap, return the default value. */
         } else {
            ret = def;
         }

/* Free the pointer to the secondary KeyMap. */
         sconfig = astAnnul( sconfig );

/* If no secondary KeyMap was found, return the default value. */
      } else {
         ret = def;
      }

/* Store the returned value in the supplied KeyMap if it is good. */
      if( ret != VAL__BADD ) astMapPut0D( config, name, ret, NULL );

/* If the text supplied by the user could not be interpreted as a
   floating point value, re-report the error. */
   } else if( *status == AST__MPGER ) {
      ret = def;
      errAnnul( status );
      if( astMapGet0C( config, name, &text ) ) {
         *status = SAI__ERROR;
         msgSetc( "T", text );
         msgSetc( "N", name );
         errRep( "", "Illegal value \"^T\" supplied for configuration "
                 "parameter ^N.", status );
      }
   }

/* Return the result. */
   return ret;
}
Beispiel #9
0
void kpg1Kygp1( AstKeyMap *keymap, Grp **igrp, const char *prefix,
                int *status ){
/*
*+
*  Name:
*     kpg1Kygp1

*  Purpose:
*     Creates a GRP group holding keyword/value pairs read from an AST KeyMap.

*  Language:
*     C.

*  Invocation:
*     void kpg1Kygp1( AstKeyMap *keymap, Grp **igrp, const char *prefix,
*                     int *status )

*  Description:
*     This function is the inverse of kpg1Kymp1. It extracts the values
*     from the supplied AST KeyMap and creates a set of "name=value" strings
*     which it appends to a supplied group (or creates a new group). If
*     the KeyMap contains nested KeyMaps, then the "name" associated with
*     each primitive value stored in the returned group is a hierarchical
*     list of component names separated by dots.

*  Arguments:
*     keymap
*        A pointer to the KeyMap. Numerical entries which have bad values
*        (VAL__BADI for integer entries or VAL__BADD for floating point
*        entries) are not copied into the group.
*     igrp
*        A location at which is stored a pointer to the Grp structure
*        to which the name=value strings are to be appended. A new group is
*        created and a pointer to it is returned if the supplied Grp
*        structure is not valid.
*     prefix
*        A string to append to the start of each key extracted from the
*        supplied KeyMap. If NULL, no prefix is used.
*     status
*        Pointer to the inherited status value.

*  Notes:
*     - This function provides a private implementation for the public
*     KPG1_KYGRP Fortran routine and kpg1Kygrp C function.
*     - Entries will be stored in the group in alphabetical order

*  Copyright:
*     Copyright (C) 2008,2010 Science & Technology Facilities Council.
*     Copyright (C) 2005 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:
*     7-NOV-2005 (DSB):
*        Original version.
*     15-JUL-2008 (TIMJ):
*        Tweak to GRP C API.
*     2010-07-19 (TIMJ):
*        Handle vector keymap entries.
*     2010-08-12 (TIMJ):
*        Store entries in group in alphabetical order.
*     13-AUG-2010 (DSB):
*        Re-instate the original SortBy value before exiting.
*     {enter_further_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
*/

/* Local Variables: */
   AstObject *obj;              /* Pointer to nested AST Object */
   char *oldsortby;             /* The old value of the KeyMap's SortBy attribute */
   char *text;                  /* Sum of concatenated strings */
   const char *key;             /* Key string for current entry in KeyMap */
   const char *value;           /* Value of current entry in KeyMap */
   double dval;                 /* Double value */
   int *old_status;             /* Pointer to original status variable */
   int bad;                     /* Is the numerical entry value bad? */
   int i;                       /* Index into supplied KeyMap */
   int ival;                    /* Integer value */
   int n;                       /* Number of entries in the KeyMap */
   int nc;                      /* Length of "text" excluding trailing null */
   int type;                    /* Data type of current entry in KeyMap */
   int valid;                   /* Is the supplied GRP structure valid? */

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

/* Make AST use the Fortran status variable. */
   old_status = astWatch( status );

/* Create a new GRP group if required. */
   valid = grpValid( *igrp, status );
   if( !valid ) *igrp = grpNew( "Created by kpg1_Kygp1", status );

/* If set, save the old SortBy value and then ensure alphabetical sorting.
   We need to take a copy of the original string since the buffer in which
   the string is stored may be re-used by subsequent incocations of astGetC.  */
   if( astTest( keymap, "SortBy" ) ) {
      nc = 0;
      oldsortby = astAppendString( NULL, &nc, astGetC( keymap, "SortBy" ) );
   } else {
      oldsortby = NULL;
   }
   astSet( keymap, "SortBy=KeyUp" );

/* Get the number of entries in the KeyMap. */
   n = astMapSize( keymap );

/* Loop round all the entries in the KeyMap.*/
   for( i = 0; i < n; i++ ) {

/* Get the name and type of the current KeyMap entry. */
      key = astMapKey( keymap, i );
      type = astMapType( keymap, key );

/* If the entry is an AST Object, get a pointer to it.*/
      if( type == AST__OBJECTTYPE && astMapGet0A( keymap, key, &obj ) ) {

/* If it is a nested KeyMap, update the prefix and call this function
   recursively. We ignore other forms of AST Objects. */
         if( astIsAKeyMap( obj ) ) {
            nc = 0;
            text = astAppendString( NULL, &nc, prefix );
            text = astAppendString( text, &nc, key );
            text = astAppendString( text, &nc, "." );
            kpg1Kygp1( (AstKeyMap *) obj, igrp, text, status );
            text = astFree( text );
         }

/* If it is a primitive, format it and add it to the group. */
      } else {

/* If it is a numerical type, see if it has a bad value. */
         bad = 0;
         if( type == AST__INTTYPE && astMapGet0I( keymap, key, &ival ) ){
            if( ival == VAL__BADI ) bad = 1;
         } else if( type == AST__DOUBLETYPE && astMapGet0D( keymap, key, &dval ) ){
            if( dval == VAL__BADD ) bad = 1;
         }

/* If it not bad, get its formatted value. We also make sure that astMapGet0C returns
   true because we intend to skip undefined values. */
         if( !bad && astMapGet0C( keymap, key, &value ) ) {
            size_t length;

/* Write the key and equals sign to a buffer */
            nc = 0;
            text = astAppendString( NULL, &nc, prefix );
            text = astAppendString( text, &nc, key );
            text = astAppendString( text, &nc, "=" );

            length = astMapLength( keymap, key );

            if( length > 1 ) {
/* Vector so we need to use  (a,b,c) syntax */
               char thiselem[GRP__SZNAM+1];
               size_t l;

               text = astAppendString( text, &nc, "(");
               for ( l = 0; l < length; l++) {
                  if( astMapGetElemC( keymap, key, sizeof(thiselem), l, thiselem ) ) {
                     text = astAppendString( text, &nc, thiselem );
                  }
/* always deal with the comma. Even if value was undef we need to put in the comma */
                  if( l < (length - 1) ) {
                     text = astAppendString( text, &nc, "," );
                  }
               }
               text = astAppendString( text, &nc, ")");

            } else {
/* Scalar */
               text = astAppendString( text, &nc, value );
            }
/* Put it in the group. */
            grpPut1( *igrp, text, 0, status );
            text = astFree( text );
         }
      }
   }

/* If it was originally set, re-instate the old SortBy value in the KeyMap,
   then free the memory. Otherwise, clear the SortBy attribute. */
   if( oldsortby ) {
      astSetC( keymap, "SortBy", oldsortby );
      oldsortby = astFree( oldsortby );
   } else {
      astClear( keymap, "SortBy" );
   }

/* Make AST use its original status variable. */
   astWatch( old_status );

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

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

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

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

  /* Check for valid inputs */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

            data->hdr->scanvel = scanvel;

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

      smf_addto_smfArray( quadata, thisqua, status );
    }

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

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

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

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

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

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

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

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

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

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

  /* Allocate space for noisemaps if required */

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

  /* Loop over subarray */

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

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

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

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

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

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

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

  }
}
Beispiel #11
0
void smf_updateprov( int ondf, const smfData *data, int indf,
                     const char *creator, NdgProvenance **modprov, int *status ){

/* Local Variables */
   AstFitsChan *fc = NULL;      /* AST FitsChan holding input FITS header */
   AstKeyMap *anc = NULL;       /* KeyMap holding ancestor info */
   AstKeyMap *tkm = NULL;       /* KeyMap holding contents of MORE */
   NdgProvenance *prov = NULL;  /* Pointer to input provenance structure */
   char *obsidss = NULL;        /* Pointer to OBSIDSS buffer */
   char obsidssbuf[SZFITSTR];   /* OBSIDSS value in input file */
   const char *vptr = NULL;     /* Pointer to OBSIDSS string */
   int found;                   /* Was OBSIDSS value found in an input ancestor? */
   int ianc;                    /* Ancestor index */
   int isroot;                  /* Ignore any ancestors in the input NDF? */

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

/* Get a FitsChan holding the contents of the input NDF FITS extension. */
   if( !data ) {
      kpgGtfts( indf, &fc, status );
   } else if (data->hdr) {
      fc = data->hdr->fitshdr;
   }

/* Get the input NDF identifier. */
   if( data && data->file &&
       data->file->ndfid != NDF__NOID) indf = data->file->ndfid;

/* Get a structure holding provenance information from indf. */
   prov = ndgReadProv( indf, " ", status );

/* Initially, assume that we should include details of ancestor NDFs. */
   isroot = 0;

/* Get the OBSIDSS keyword value from the input FITS header if we have a FITS
   header. There can be cases (eg in STACKFRAMES) where we do not have a
   FITS header so handle that gracefully. */
   if (fc) obsidss = smf_getobsidss( fc, NULL, 0, obsidssbuf,
                                     sizeof(obsidssbuf), status );
   if( obsidss ) {

/* Search through all the ancestors of the input NDF (including the input
   NDF itself) to see if any of them refer to the same OBSIDSS value. */
      found = 0;
      ianc = 0;
      anc = ndgGetProv( prov, ianc, status );
      while( anc ) {
         if( astMapGet0A( anc, "MORE", &tkm ) ) {
            if( astMapGet0C( tkm, "OBSIDSS", &vptr ) ) {
               found = !strcmp( obsidss, vptr );
            }
            tkm = astAnnul( tkm );
         }
         anc = astAnnul( anc );
         if( ! found ) anc = ndgGetProv( prov, ++ianc, status );
      }

/* If the OBSIDSS value was not found in any ancestor, then we add it
   now. So put the OBSIDSS keyword value in an AST KeyMap that will be
   stored with the output provenance information. */
      if( ! found ) {
         tkm = astKeyMap( " " );
         astMapPut0C( tkm, "OBSIDSS", obsidss, NULL );

/* Ignore ancestor NDFs if none of them referred to the correct OBSIDSS. */
         isroot = 1;
      }
   }

/* Update the provenance for the output NDF to include the input NDF as
   an ancestor. Indicate that each input NDF is a root NDF (i.e. has no
   parents). Do nothing if we have no provenance to propagate, unless
   we have root information and are propagating that. */
   if ( ndgCountProv( prov, status ) > 0 || tkm) {
     NdgProvenance *oprov = NULL;
     if (modprov && *modprov) {
       oprov = *modprov;
     } else {
       oprov = ndgReadProv( ondf, creator, status );
       if (modprov) *modprov = oprov;
     }
     ndgPutProv( oprov, indf, tkm, isroot, status );
     /* do not update the file or free provenance if we
        are using an external provenance struct or returning
        it to the caller */
     if (!modprov) {
       if( ondf != NDF__NOID ) {
         ndgWriteProv( oprov, ondf, 1, status );
         oprov = ndgFreeProv( oprov, status );
       } else if( *status == SAI__OK ){
         *status = SAI__ERROR;
         errRep( " ", "smf_updateprov: Provenance is to be stored in the "
                 "output NDF but no output NDF identifier was supplied "
                 "(programming error).", status );
       }
     }
   }

/* Free resources. */
   if( prov ) prov = ndgFreeProv( prov, status );
   if( tkm ) tkm = astAnnul( tkm );
   if( !data && fc ) fc = astAnnul( fc );
}
Beispiel #12
0
void smf_checkmem_dimm( dim_t maxlen, inst_t instrument, int nrelated,
			smf_modeltype *modeltyps, dim_t nmodels, dim_t msize,
                        AstKeyMap *keymap, size_t available, dim_t maxfilelen,
                        size_t *necessary, int *status ) {


  /* Local Variables */
  int dofft=0;                 /* flag if we need temp space for FFTs */
  dim_t i;                     /* Loop counter */
  dim_t gain_box;              /* Length of blocks for GAI/COM model */
  AstKeyMap *kmap=NULL;        /* Local keymap */
  dim_t nblock;                /* Number of blocks for GAI/COM model */
  size_t ncol;                 /* Number of columns */
  size_t ndet;                 /* Number of detectors each time step */
  size_t ndks;                 /* dksquid samples in a subarray, ncol*maxlen */
  size_t nrow;                 /* Number of rows */
  size_t nsamp;                /* bolo samples in a subarray, ndet*maxlen */
  const char *tempstr=NULL;    /* Temporary pointer to static char buffer */
  size_t total = 0;            /* Total bytes required */

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

  /* Check inputs */

  if( maxlen < 1 ) {
    *status = SAI__ERROR;
    errRep("", FUNC_NAME ": maxlen cannot be < 1", status);
    return;
  }

  if( (nrelated < 1) || (nrelated > SMF__MXSMF) ) {
    msgSeti("NREL",nrelated);
    msgSeti("MAXREL",SMF__MXSMF);
    *status = SAI__ERROR;
    errRep("", FUNC_NAME ": nrelated, ^NREL, must be in the range [1,^MAXREL]",
	   status);
    return;
  }

  if( modeltyps ) {
    if( nmodels < 1 ) {
    *status = SAI__ERROR;
    errRep("", FUNC_NAME ": modeltyps specified, mmodels cannot be < 1",
           status);
    return;
    }
  }

  if( *status == SAI__OK ) {

    /* Work out the data dimensions */
    switch( instrument ) {
    case INST__SCUBA2:
      /* Kludgey, but at least we check SC2STORE__COL_INDEX so this will help
         us catch possible future problems if order is changed */
      if( SC2STORE__COL_INDEX ) {
        ncol = 32;
        nrow = 40;
      } else {
        ncol = 40;
        nrow = 32;
      }
      ndet = ncol*nrow;
      break;
    default:
      *status = SAI__ERROR;
      errRep("", FUNC_NAME ": Invalid instrument given.", status);
    }
  }

  /* Number of samples in a full data cube for one subarray */
  nsamp = ndet*maxlen;

  /* Check to see if we need to do filtering as part of
     pre-processing. If so, we need an extra nsamp-sized buffer to
     store the FFT. */

  smf_get_cleanpar( keymap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                    &dofft, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                    status );

  /* Calculate memory usage of static model components: -------------------- */

  if( *status == SAI__OK ) {
    total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;   /* RES */
    total += nsamp*smf_dtype_sz(SMF__INTEGER,status)*nrelated;  /* LUT */
    total += nsamp*smf_dtype_sz(SMF__QUALTYPE,status)*nrelated; /* QUA */
  }

  /* Add on memory usage for the JCMTState (one per time slice per array) */
  if( *status == SAI__OK ) {
    total += maxlen*sizeof(JCMTState)*nrelated;
  }

  /* Add on space for dark squids */
  ndks = ncol*maxlen;
  total += ndks*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;

  /* Add on space for fakemap */
  astMapGet0C( keymap, "FAKEMAP", &tempstr );
  if( tempstr ) {
    total += msize*sizeof(double);
  }

  /* Apply fudge factor */
  total *= CHECKMEM_FUDGE;

  /* Calculate memory usage of dynamic model components: ------------------- */

  if( *status == SAI__OK ) {

    /* Most of these will have data arrays associated with each
       subarray (hence the multiplication by nrelated). An exception
       is SMF__COM for which a single-common mode is used across all
       subarrays. */

    if( modeltyps ) {
      for( i=0; i<nmodels; i++ ) {
	switch( modeltyps[i] ) {
	case SMF__NOI:
          /* SMF__NOI also estimates the noise in each detector from the
             power spectra, requiring a temporary buffer to store an FFT.
             Currently we just store one variance per detector */
          dofft = 1;
          total += ndet*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;
	  break;
	case SMF__COM:
          CHECK_MASK("COM")
	  total += maxlen*smf_dtype_sz(SMF__DOUBLE,status);
	  break;
	case SMF__EXT:
	  total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;
	  break;
        case SMF__DKS:
          total += (maxlen + nrow*3)*ncol*smf_dtype_sz(SMF__DOUBLE,status) *
            nrelated;
          break;
        case SMF__GAI:
          /* Every COM.GAIN_BOX samples there are 3 planes of data
             corresponding to each bolometer in the subarray. The
             conversion of COM.GAIN_BOX from seconds to samples within
             smf_get_nsamp assumes a sample rate of 200 Hz. Later
             downsampling may result in a lower sample rate, but at least
             we are erring on the conservative side by assuming 200 Hz. */
          if( astMapGet0A( keymap, "COM", &kmap ) ) {
             smf_get_nsamp( kmap, "GAIN_BOX", NULL, &gain_box, status );
             nblock = maxlen/gain_box;
             if( nblock == 0 ) nblock = 1;
             total += nblock*3*nrow*ncol*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;
             kmap = astAnnul( kmap );
          }
          break;
        case SMF__FLT:
          /* Presently the filter temporarily transforms the entire
             data cube into a second array. We therefore need to
             ensure enough memory to temporarily store the data cube
             twice. */
          CHECK_MASK("FLT")
          dofft = 1;
          total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;
          break;
        case SMF__PLN:
          total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;
          break;
        case SMF__SMO:
          total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;
          break;
        case SMF__TMP:
          /* An externally supplied template. The model just stores the gain,
             offset and correlation coefficient for each bolometer, similar
             to SMF__GAI except with only one chunk considered */
          total += 3*nrow*ncol*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;
          break;
        case SMF__TWO:
          /* two common-mode time series and coefficients for all the
             detectors */
          total += 2*(ndet+maxlen)*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;
          break;
        case SMF__AST:
          /* Mostly accounted for as static memory usage above, but add space
             for mask if required */
          CHECK_MASK("AST")
          break;
	default:
	  *status = SAI__ERROR;
	  errRep("", FUNC_NAME ": Invalid smf_modeltype given.", status);
	}

	/* Exit on bad status */
	if( *status != SAI__OK ) {
	  i = nmodels;
	}
      }
    }

    /* Calculate temporary space here -------------------------------------- */

    if( *status == SAI__OK ) {
      size_t temp=0;      /* current temp memory required */
      size_t maxtemp=0;   /* max temp memory required */

      /* Some temp space required when we initially read in the
         data. Normally smf_concat_smfGroup will
         smf_open_and_flatfield one file at a time before copying into
         the concatenated data array. If there are N time slices in a
         file, smf_open_and_flatfield will usually require space both
         for the raw, and double-precision flatfielded data
         simultaneously. This is an upper limit, since if previously
         flatfielded data are provided no extra space for the raw
         data will be required. We use the supplied maxfilelen to figure
         out this maximum temporary buffer size. Remember to account
         for bolo data, dark squids, and JCMTState. Also, the way
         smf_iteratemap is currently written, a concatenated memory-mapped
         pointing LUT is initially created, and then copied to a new
         malloc'd array, so we need extra temp space for the full LUT. */

      temp = (ndet + ncol)*( smf_dtype_sz(SMF__INTEGER,status)+
                             smf_dtype_sz(SMF__DOUBLE,status) )*maxfilelen +
        2*maxfilelen*sizeof(JCMTState);

      temp += nsamp*smf_dtype_sz(SMF__INTEGER,status)*nrelated;

      if( temp > maxtemp ) maxtemp = temp;

      /* If we are doing FFTs */
      if( dofft ) {
        temp = nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated;
        if( temp > maxtemp ) maxtemp = temp;
      }

      /* Data cleaning... seems to be a lot. Just putting in a rough
         estimate of 3 extra full sized arrays for the standard
         options. */
      temp = 3.*nsamp*smf_dtype_sz(SMF__DOUBLE,status);
      if( temp > maxtemp ) maxtemp = temp;

      /* Add on the maximum chunk of temporary memory that is required */
      total += maxtemp;
    }


    /* Set bad status if too big */
    if( (*status == SAI__OK) && (total > available) ) {
      *status = SMF__NOMEM;
      msgSeti("REQ",total/SMF__MIB);
      msgSeti("AVAIL",available/SMF__MIB);
      errRep("", FUNC_NAME
	     ": Requested memory ^REQ MiB for map exceeds available ^AVAIL MiB",
	     status);
    }

    /* Return the required space */
    if( necessary ) {
      *necessary = total;
    }
  }

}
Beispiel #13
0
void smf_grp_related( const Grp *igrp, const size_t grpsize,
                      const int grouping, const int checksubinst,
                      double maxlen_s, double *srate_maxlen,
                      AstKeyMap *keymap, dim_t *maxconcatlen,
                      dim_t *maxfilelen, smfGroup **group,
                      Grp **basegrp, dim_t *pad, int *status ) {

  /* Local variables */
  size_t *chunk=NULL;         /* Array of flags for continuous chunks */
  dim_t * chunklen = NULL;    /* Length of continuous chunk */
  size_t currentindex = 0;    /* Counter */
  char cwave[10];             /* String containing wavelength */
  smfData *data = NULL;       /* Current smfData */
  double downsampscale=0;     /* Angular scale downsampling size */
  double downsampfreq=0;      /* Target downsampling frequency */
  AstKeyMap * grouped = NULL; /* Primary AstKeyMap for grouping */
  size_t i;                   /* Loop counter for index into Grp */
  int isFFT=0;                /* Set if data are 4d FFT */
  size_t j;                   /* Loop counter */
  int *keepchunk=NULL;        /* Flag for chunks that will be kept */
  dim_t maxconcat=0;          /* Longest continuous chunk length */
  dim_t maxflen=0;            /* Max file length in time steps */
  dim_t maxlen=0;             /* Maximum concat length in samples */
  int maxlen_scaled=0;        /* Set once maxlen has been scaled, if needed */
  dim_t maxpad=0;             /* Maximum padding neeed for any input file */
  size_t maxrelated = 0;      /* Keep track of max number of related items */
  size_t *new_chunk=NULL;     /* keeper chunks associated with subgroups */
  dim_t *new_tlen=NULL;       /* tlens for new_subgroup */
  size_t ngroups = 0;         /* Counter for subgroups to be stored */
  size_t nkeep = 0;           /* Number of chunks to keep */
  dim_t * piecelen = NULL;    /* Length of single file */
  smf_subinst_t refsubinst;   /* Subinst of first file */
  size_t **subgroups = NULL;  /* Array containing index arrays to parent Grp */
  smf_subinst_t subinst;      /* Subinst of current file */

  if ( *status != SAI__OK ) return;

  if( maxlen_s < 0 ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": maxlen_s cannot be < 0!", status );
    return;
  }

  /* Get downsampling parameters */

  if( keymap ) {
    smf_get_cleanpar( keymap, 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, NULL, NULL, NULL, &downsampscale, &downsampfreq,
                      NULL, NULL, NULL, NULL, status );

    if( downsampscale && downsampfreq ) {
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME ": both downsampscale and downsampfreq are set",
              status );
      return;
    }
  }

  /* Initialize refcwave */
  refsubinst = SMF__SUBINST_NONE;

  /* Loop over files in input Grp: remember Grps are indexed from 1 */
  grouped = astKeyMap( "SortBy=KeyUp" );
  for (i=1; i<=grpsize; i++) {
    char newkey[128];
    char dateobs[81];
    char subarray[10];
    size_t nrelated = 0;
    AstKeyMap * filemap = NULL;
    AstKeyMap * indexmap = NULL;

    /* First step: open file and harvest metadata */
    smf_open_file( NULL, igrp, i, "READ", SMF__NOCREATE_DATA, &data, status );
    if (*status != SAI__OK) break;

    if( i==1 ) {
      isFFT = smf_isfft( data, NULL, NULL, NULL, NULL, NULL, status );
    } else if( smf_isfft(data, NULL, NULL, NULL, NULL, NULL, status) != isFFT ){
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME
              ": mixture of time-series and FFT data encountered!",
              status );
      break;
    }

    /* If maxlen has not been set, do it here */
    if( !maxlen && maxlen_s && data->hdr->steptime) {
      maxlen = (dim_t) (maxlen_s / data->hdr->steptime );
    }

    /* Return srate_maxlen if requested: may want to know this number
       even if maxlen_s is not set. Only calculate once, although it
       gets overwritten once later if down-sampling. */

    if( (i==1) && srate_maxlen && data->hdr->steptime ) {
      *srate_maxlen = 1. / (double) data->hdr->steptime;
    }


    /* If requested check to see if we are mixing wavelengths */
    if( checksubinst ) {
      if( refsubinst == SMF__SUBINST_NONE ) {
        refsubinst = smf_calc_subinst( data->hdr, status );
      }

      subinst = smf_calc_subinst( data->hdr, status );

      if( subinst != refsubinst ) {
        const char *refsubstr = smf_subinst_str( refsubinst, status );
        const char *substr = smf_subinst_str( subinst, status );

        *status = SAI__ERROR;
        smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" );
        msgSetc( "REFSUB", refsubstr );
        msgSetc( "SUB", substr );
        errRep( "", FUNC_NAME
                ": ^FILE uses sub-instrument ^SUB which doesn't match "
                "reference ^REFSUB", status );
      }
    }

    /* Want to form a key that will be unique for a particular subscan
       We know that DATE-OBS will be set for SCUBA-2 files and be the same
       for a single set. Prefix by wavelength if we are grouping by wavelength.
     */
    newkey[0] = '\0';

    smf_find_subarray( data->hdr, subarray, sizeof(subarray), NULL, status );

    if( grouping == 1 ) {
      /* Group different wavelengths separately */
      smf_fits_getS( data->hdr, "WAVELEN", cwave, sizeof(cwave), status);
      one_strlcat( newkey, cwave, sizeof(newkey), status );
      one_strlcat( newkey, "_", sizeof(newkey), status );
    }

    if( grouping == 2 ) {
      /* Group different subarrays separately */
      one_strlcat( newkey, subarray, sizeof(newkey), status );
    }

    smf_fits_getS( data->hdr, "DATE-OBS", dateobs, sizeof(dateobs), status );
    one_strlcat( newkey, dateobs, sizeof(newkey), status );

    /* Include the dimentionality of the time series in the primary key
       so that we do not end up doing something confusing like relating
       a truncated file with a full length file */
    if (*status == SAI__OK) {
      dim_t dims[3];
      char formatted[32];
      smf_get_dims( data, &dims[0], &dims[1], NULL, &dims[2], NULL, NULL, NULL,
                    status );
      sprintf(formatted, "_%" DIM_T_FMT "_%" DIM_T_FMT "_%" DIM_T_FMT, dims[0], dims[1], dims[2]);
      one_strlcat( newkey, formatted, sizeof(newkey), status );
    }

    /* May want to read the dimensionality of the file outside of loop
       so that we can compare values when storing in the keymap */

    /* Now we want to create a keymap based on this key */
    if (!astMapGet0A( grouped, newkey, &filemap ) ) {
      int itemp = 0;
      double steptime = data->hdr->steptime;
      dim_t ntslice = 0;
      dim_t thispad;              /* Padding neeed for current input file */

      filemap = astKeyMap( " " );
      astMapPut0A( grouped, newkey, filemap, NULL );

      /* Fill up filemap with general information on this file */
      smf_find_seqcount( data->hdr, &itemp, status );
      astMapPut0I( filemap, "SEQCOUNT", itemp, NULL );

      smf_fits_getI( data->hdr, "NSUBSCAN", &itemp, status );
      astMapPut0I( filemap, "NSUBSCAN", itemp, NULL );

      /* Number of time slices */
      smf_get_dims( data, NULL, NULL, NULL, &ntslice, NULL, NULL, NULL,
                    status );

      /* Find length of down-sampled data, new steptime and maxlen */
      if( (downsampscale || downsampfreq) && data->hdr && (*status==SAI__OK) ) {
        double scalelen;

        if( downsampscale ) {
          if( data->hdr->scanvel != VAL__BADD ) {
             double oldscale = steptime * data->hdr->scanvel;
             scalelen = oldscale / downsampscale;
          } else if( *status == SAI__OK ) {
             *status = SAI__ERROR;
            scalelen = VAL__BADD;
            smf_smfFile_msg( data->file, "FILE", 1, "" );
            errRep( "", FUNC_NAME ": can't resample ^FILE because it has "
                    "unknown scan velocity", status );
          }
        } else {
          if( steptime ) {
            double oldsampfreq = 1./steptime;
            scalelen = downsampfreq / oldsampfreq;
          } else {
            *status = SAI__ERROR;
            scalelen = VAL__BADD;
            smf_smfFile_msg( data->file, "FILE", 1, "" );
            errRep( "", FUNC_NAME ": can't resample ^FILE because it has "
                    "unknown sample rate", status );
          }
        }

        /* only down-sample if it will be a reasonable factor */
        if( (*status==SAI__OK) && (scalelen <= SMF__DOWNSAMPLIMIT) ) {
          smf_smfFile_msg(data->file, "FILE", 1, "" );
          msgOutiff( MSG__VERB, "", FUNC_NAME
                     ": will down-sample file ^FILE from %5.1lf Hz to "
                     "%5.1lf Hz", status, (1./steptime), (scalelen/steptime) );

          ntslice = round(ntslice * scalelen);

          /* If maxlen has been requested, and we have not already worked
             out a scaled version (just uses the sample rates for the first
             file... should be close enough -- the alternative is a 2-pass
             system). */

          if( !maxlen_scaled ) {
            maxlen = round(maxlen*scalelen);
            maxlen_scaled = 1;
            msgOutiff( MSG__VERB, "", FUNC_NAME
                       ": requested maxlen %g seconds = %" DIM_T_FMT " down-sampled "
                       "time-slices", status, maxlen_s, maxlen );

            /* Return updated srate_maxlen for down-sampling if requested */
            if( srate_maxlen ) {
              *srate_maxlen = scalelen/steptime;
            }
          }
        }
      }

      /* Check that an individual file is too long (we assume related
         files are all the same) */
      if( maxlen && (ntslice > maxlen) && *status == SAI__OK) {
        *status = SAI__ERROR;
        msgSeti("NTSLICE",ntslice);
        msgSeti("MAXLEN",maxlen);
        smf_smfFile_msg( data->file, "FILE", 1, "" );
        errRep(FUNC_NAME,
               "Number of time steps in file ^FILE time exceeds maximum "
               "(^NTSLICE>^MAXLEN)", status);
      }

      /* Scaled values of ntslice and maximum length */
      astMapPut0I( filemap, "NTSLICE", ntslice, NULL );

      /* Work out the padding needed for this file including downsampling. */
      if( keymap ) {
        thispad = smf_get_padding( keymap, 0, data->hdr, VAL__BADD, status );
        if( thispad > maxpad ) maxpad = thispad;
      } else {
        thispad = 0;
      }
      astMapPut0I( filemap, "PADDING", thispad, NULL );

      /* Update maxflen */
      if( ntslice > maxflen ) {
        maxflen = ntslice;
      }

      /* Store OBSID or OBSIDSS depending on whether we are grouping by wavelength */
      if (grouping) {
        astMapPut0C( filemap, "OBSID", data->hdr->obsidss, NULL );
      } else {
        char obsid[81];
        smf_getobsidss( data->hdr->fitshdr, obsid, sizeof(obsid), NULL, 0, status );
        astMapPut0C( filemap, "OBSID", obsid, NULL );
      }
    }

    /* Store the file index in another keymap indexed by subarray */
    if ( !astMapGet0A( filemap, "GRPINDICES", &indexmap ) ) {
      indexmap = astKeyMap( "SortBy=KeyUp" );
      astMapPut0A( filemap, "GRPINDICES", indexmap, NULL );
    }

    astMapPut0I( indexmap, subarray, i, NULL );

    /* Need to track the largest number of related subarrays in a single slot */
    nrelated = astMapSize( indexmap );
    if (nrelated > maxrelated) maxrelated = nrelated;

    /* Free resources */
    filemap = astAnnul( filemap );
    indexmap = astAnnul( indexmap );
    smf_close_file( NULL, &data, status );
  }

  /* We now know how many groups there are */
  ngroups = astMapSize( grouped );

  /* Sort out chunking. The items are sorted by date and then by wavelength.
     We define a continuous chunk if it has the same OBSID, the same SEQCOUNT
     and NSUBSCAN increments by one from the previous entry.

     Also count number of related items in each slot.
   */
  if (*status == SAI__OK) {
    typedef struct { /* somewhere to store the values easily */
      char obsid[81];
      char related[81];  /* for concatenated subarrays */
      int nsubscan;
      int seqcount;
    } smfCompareSeq;
    smfCompareSeq current;
    smfCompareSeq previous;
    dim_t totlen = 0;
    size_t thischunk;

    /* Get the chunk flags and also store the size of the chunk */
    chunk = astCalloc( ngroups, sizeof(*chunk) );
    chunklen = astCalloc( ngroups, sizeof(*chunklen) );
    piecelen = astCalloc( ngroups, sizeof(*piecelen) );

    thischunk = 0;  /* The current chunk */
    for (i=0; i<ngroups; i++) {
      AstKeyMap * thismap = NULL;
      AstKeyMap * grpindices = NULL;
      const char * tempstr = NULL;
      int thistlen = 0;
      size_t nsubarrays = 0;

      /* Get the keymap entry for this slot */
      astMapGet0A( grouped, astMapKey(grouped, i), &thismap );

      /* Get info for length limits */
      astMapGet0I( thismap, "NTSLICE", &thistlen );
      piecelen[i] = thistlen;

      if (isFFT) {
        /* Never concatenate FFT data */
        thismap = astAnnul(thismap);
        chunk[i] = i;
        chunklen[i] = thistlen;
        continue;
      }

      /* Get indices information and retrieve the sub-instrument names
         in sort order to concatenate for comparison. We only store in
         a continuous chunk if we have the same subarrays for the whole
         chunk. */
      astMapGet0A( thismap, "GRPINDICES", &grpindices );
      nsubarrays = astMapSize( grpindices );
      (current.related)[0] = '\0';
      for (j = 0; j < nsubarrays; j++ ) {
        one_strlcat( current.related, astMapKey(grpindices, j), sizeof(current.related), status );
      }
      grpindices = astAnnul( grpindices );

      /* Fill in the current struct */
      astMapGet0I( thismap, "SEQCOUNT", &(current.seqcount) );
      astMapGet0I( thismap, "NSUBSCAN", &(current.nsubscan) );
      astMapGet0C( thismap, "OBSID", &tempstr );
      one_strlcpy( current.obsid, tempstr, sizeof(current.obsid), status );

      /* First chunk is special, else compare */
      if (i == 0) {
        totlen = thistlen;
      } else {
        if (  ( current.seqcount == previous.seqcount  ) &&
              ( current.nsubscan - previous.nsubscan == 1 ) &&
              ( strcmp( current.obsid, previous.obsid ) == 0 ) &&
              ( strcmp( current.related, previous.related ) == 0 ) ) {
          /* continuous - check length */
          totlen += thistlen;
          if ( maxlen && totlen > maxlen ) {
            thischunk++;
            totlen = thistlen; /* reset length */
          } else {
            /* Continuous */
          }
        } else {
          /* discontinuity */
          thischunk++;
          totlen = thistlen;  /* Update length of current chunk */
        }
      }

      chunklen[thischunk] = totlen;
      chunk[i] = thischunk;
      memcpy( &previous, &current, sizeof(current) );

      thismap = astAnnul( thismap );
    }
  }

  /* Decide if we are keeping a chunk by looking at the length. */
  maxconcat = 0;
  nkeep = 0;
  keepchunk = astMalloc( ngroups*sizeof(*keepchunk) );
  for (i=0; i<ngroups; i++) {
    size_t thischunk;

    thischunk = chunk[i];
    if ( chunklen[thischunk] < SMF__MINCHUNKSAMP ) {
      /* Warning message */
      msgSeti("LEN",chunklen[thischunk]);
      msgSeti("MIN",SMF__MINCHUNKSAMP);
      msgOut( " ", "SMF_GRP_RELATED: ignoring short chunk (^LEN<^MIN)",
              status);
      keepchunk[i] = 0;
    } else {
      keepchunk[i] = 1;
      if (maxconcat < chunklen[thischunk]) maxconcat = chunklen[thischunk];
      nkeep++;
    }

  }

  /* If no useful chunks generate an error */
  if( (*status==SAI__OK) && (!nkeep) ) {
    *status = SAI__ERROR;
    errRep( "", FUNC_NAME ": No useful chunks.", status );
    goto CLEANUP;
  }

  /* Allocate a subgroup array of the right size and fill it. They keymap
     is sorted by date (and wavelength) so we can always index into it by using
     indices from the subgroup. */
  subgroups = astCalloc( nkeep, sizeof(*subgroups) );
  new_chunk = astCalloc( nkeep, sizeof(*new_chunk) );
  new_tlen  = astCalloc( nkeep, sizeof(*new_tlen) );

  currentindex = 0;
  for (i=0;i<ngroups;i++) {
    AstKeyMap * thismap = NULL;
    AstKeyMap * grpindices = NULL;
    size_t nsubarrays = 0;
    size_t *indices = astCalloc( maxrelated, sizeof(*indices) );

    /* skip if we are dropping this chunk */
    if (!keepchunk[i]) continue;

    /* Get the keymap entry for this slot */
    astMapGet0A( grouped, astMapKey(grouped, i), &thismap );

    /* Get the indices keymap */
    astMapGet0A( thismap, "GRPINDICES", &grpindices );
    nsubarrays = astMapSize( grpindices );
    for (j=0; j<nsubarrays; j++) {
      int myindex;
      astMapGet0I( grpindices, astMapKey(grpindices, j), &myindex );
      indices[j] = myindex;
    }
    grpindices = astAnnul( grpindices );
    thismap = astAnnul( thismap );

    subgroups[currentindex] = indices;
    new_chunk[currentindex] = chunk[i];
    new_tlen[currentindex]  = piecelen[i];
    currentindex++;

  }

  /* Create the smfGroup */
  *group = smf_construct_smfGroup( igrp, subgroups, new_chunk, new_tlen,
                                   nkeep, maxrelated, 0, status );

  /* Return maxfilelen if requested */
  if( maxfilelen ) {
    *maxfilelen = maxflen;
  }

  /* Return maxconcatlen if requested */
  if( maxconcatlen ) {
    *maxconcatlen = maxconcat;
  }

  /* Create a base group for output files if required */
  /* Create a base group of filenames */
  if (*status == SAI__OK && basegrp ) {
    *basegrp = smf_grp_new( (*group)->grp, "Base Group", status );

    /* Loop over time chunks */
    for( i=0; (*status==SAI__OK)&&(i<(*group)->ngroups); i++ ) {
      size_t idx;
      /* Check for new continuous chunk */
      if( i==0 || ( (*group)->chunk[i] != (*group)->chunk[i-1]) ) {
        /* Loop over subarray */
        for( idx=0; idx<(*group)->nrelated; idx++ ) {
          size_t grpindex = (*group)->subgroups[i][idx];
          if ( grpindex > 0 ) {
            ndgCpsup( (*group)->grp, grpindex, *basegrp, status );
          }
        }
      }
    }
  }

 CLEANUP:
  keepchunk = astFree( keepchunk );
  chunk = astFree( chunk );
  chunklen = astFree( chunklen );
  piecelen = astFree( piecelen );
  grouped = astAnnul( grouped );

  if( *status != SAI__OK ) {
    /* free the group */
    if (basegrp && *basegrp) grpDelet( basegrp, status );
    if (group && *group) {
      smf_close_smfGroup( group, status );
    } else {
      /* have to clean up manually */
      new_chunk = astFree( new_chunk );
      new_tlen = astFree( new_tlen );
      if( subgroups ) {
        size_t isub;
        for( isub=0; isub<nkeep; isub++ ) {
          subgroups[isub] = astFree( subgroups[isub] );
        }
        subgroups = astFree( subgroups );
      }
    }
  }

  /* Return the maximum padding if required. */
  if( pad ) *pad = maxpad;
}
Beispiel #14
0
int smf_initial_sky( ThrWorkForce *wf, AstKeyMap *keymap, smfDIMMData *dat,
                     int *iters, int *status ) {

/* Local Variables: */
   char refparam[ DAT__SZNAM ];/* Name for reference NDF parameter */
   const char *cval;          /* The IMPORTSKY string value */
   double *ptr;               /* Pointer to NDF Data array */
   double *vptr;              /* Pointer to NDF Variance array */
   int indf1;                 /* Id. for supplied reference NDF */
   int indf2;                 /* Id. for used section of reference NDF */
   int nel;                   /* Number of mapped NDF pixels */
   int result;                /* Returned flag */
   int there;                 /* Is there a smurf extension in the NDF? */
   int update;                /* Was NDF opened for UPDATE access? */
   size_t i;                  /* Loop count */
   size_t junk;               /* Unused value */

/* Initialise the returned value to indicate no sky has been subtractred. */
   result = 0;

/* Assume the sky map was not created by an interupted previous run of
   makemap. */
   *iters = -1;

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

/* Begin an AST context. */
   astBegin;

/* The IMPORTSKY config parameter should have the name of the ADAM
   parameter to use for acquiring the NDF that contains the initial sky
   estimate. If IMPORTSKY is "1", use REF. */
   cval = NULL;
   astMapGet0C( keymap, "IMPORTSKY", &cval );
   if( cval ) {
      if( !astChrMatch( cval, "REF" ) &&
          !astChrMatch( cval, "MASK2" ) &&
          !astChrMatch( cval, "MASK3" ) ) {
         astMapGet0I( keymap, "IMPORTSKY", &result );
         cval = ( result > 0 ) ? "REF" : NULL;
      }
      if( cval ) {
         result = 1;
         strcpy( refparam, cval );
         astChrCase( NULL, refparam, 1, 0 );
      }
   }

/* Do nothing more if we are not subtracting an initial sky from the data. */
   if( result && *status == SAI__OK ) {

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

/* Get an identifier for the NDF using the associated ADAM parameter.
   First try UPDATE access. If this fails try READ access. */
      ndfAssoc( refparam, "UPDATE", &indf1, status );
      if( *status != SAI__OK ) {
         errAnnul( status );
         ndfAssoc( refparam, "READ", &indf1, status );
         update = 0;
      } else {
         update = 1;
      }

/* Tell the user what we are doing. */
      ndfMsg( "N", indf1 );
      msgOut( "", "Using ^N as the initial guess at the sky", status );

/* Get a section from this NDF that matches the bounds of the map. */
      ndfSect( indf1, 2, dat->lbnd_out, dat->ubnd_out, &indf2, status );

/* Ensure masked values are not set bad in the mapped data array. */
      ndfSbb( 0, indf2, status );

/* Map the data array section, and copy it into the map buffer. */
      ndfMap( indf2, "DATA", "_DOUBLE", "READ", (void **) &ptr, &nel, status );
      if( *status == SAI__OK ) {
         memcpy( dat->map, ptr, dat->msize*sizeof(*ptr));
      }

/* Map the variance array section, and copy it into the map buffer. */
      ndfState( indf2, "VARIANCE", &there, status );
      if( there ) {
         ndfMap( indf2, "VARIANCE", "_DOUBLE", "READ", (void **) &vptr, &nel, status );
         if( *status == SAI__OK ) {
            memcpy( dat->mapvar, vptr, dat->msize*sizeof(*vptr));
         }
      }

/* If the NDF was created by a previous run of makemap that was interupted
   using control-C, it will contain a NUMITER item in the smurf extension,
   which gives the number of iterations that were completed before the
   map was created. Obtain and return this value, if it exists. */
      ndfXstat( indf1, SMURF__EXTNAME, &there, status );
      if( there ) ndfXgt0i( indf1, SMURF__EXTNAME, "NUMITER", iters,
                            status );

/* If the NDF has a Quality component, import it and create initial AST,
   FLT, PCA, SSN and COM masks from it. These will often be over-ridden by
   new masks calculated with smf_calcmodel_ast below, but will not be
   over-written if the masks have been frozen by xxx.zero_freeze. */
      ndfState( indf2, "Quality", &there, status );
      if( there && dat->mapqual ) {
         smf_qual_t *qarray = smf_qual_map( wf, indf2, "Read", NULL, &junk,
                                            status );
         if( *status == SAI__OK ) {
            smf_qual_t *pq = qarray;
            for( i = 0; i < dat->msize; i++,pq++ ) {
               if( *pq & SMF__MAPQ_AST ) {
                  if( !dat->ast_mask ) dat->ast_mask = astCalloc( dat->msize,
                                                  sizeof( *(dat->ast_mask) ) );
                  (dat->ast_mask)[ i ] = 1;
               }
               if( *pq & SMF__MAPQ_FLT ) {
                  if( !dat->flt_mask ) dat->flt_mask = astCalloc( dat->msize,
                                                  sizeof( *(dat->flt_mask) ) );
                  (dat->flt_mask)[ i ] = 1;
               }
               if( *pq & SMF__MAPQ_COM ) {
                  if( !dat->com_mask ) dat->com_mask = astCalloc( dat->msize,
                                                  sizeof( *(dat->com_mask) ) );
                  (dat->com_mask)[ i ] = 1;
               }
               if( *pq & SMF__MAPQ_SSN ) {
                  if( !dat->ssn_mask ) dat->ssn_mask = astCalloc( dat->msize,
                                                  sizeof( *(dat->ssn_mask) ) );
                  (dat->ssn_mask)[ i ] = 1;
               }
               if( *pq & SMF__MAPQ_PCA ) {
                  if( !dat->pca_mask ) dat->pca_mask = astCalloc( dat->msize,
                                                  sizeof( *(dat->pca_mask) ) );
                  (dat->pca_mask)[ i ] = 1;
               }
            }
         }
         qarray = astFree( qarray );
      }

/* Indicate the map arrays within the supplied smfDIMMData structure now
   contain usable values. We need to do this before calling
   smf_calcmodel_ast below so that the right mask gets used in
   smf_calcmodel_ast. */
      dat->mapok = 1;

/* Apply any existinction correction to the cleaned bolometer data. */
      if( dat->ext ) smf_calcmodel_ext( wf, dat, 0, keymap, dat->ext, 0,
                                        status);

/* Sample the above map at the position of each bolometer sample and
   subtract the sampled value from the cleaned bolometer value. */
      smf_calcmodel_ast( wf, dat, 0, keymap, NULL, SMF__DIMM_PREITER, status);

/* Remove any existinction correction to the modifed bolometer data. */
      if( dat->ext ) smf_calcmodel_ext( wf, dat, 0, keymap, dat->ext,
                                        SMF__DIMM_INVERT, status);

/* If the NDF was opened with UPDATE access, update the quality array in
   the NDF to reflect the AST mask created by smf_calcmodel_ast above. */
      if( update ) {
         smf_qual_t *qarray = astStore( NULL, dat->mapqual, dat->msize*sizeof(*qarray) );
         qarray = smf_qual_unmap( wf, indf2, SMF__QFAM_MAP, qarray, status );
      }

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

/* End the AST context. */
   astEnd;

/* Return the pointer to the boolean mask. */
   return result;
}
Beispiel #15
0
void smf_pread( Grp *igrp, const char *param, int *status ){

/* Local Variables: */
   AstMapping *dlatmap;
   AstMapping *dlonmap;
   AstMapping *taimap;
   AstTable *table;
   char file[ GRP__SZNAM + 1 ];
   char pbuf[ GRP__SZNAM + 1 ];
   const char *system;
   void *p;

/* Before we check the error status, see if we are annulling previously
   created Mappings. If so, get each formatted pointer from the group
   metadata, get an Object pointer form it, lock it for use by the
   current thread, and then annul it. Remove the metadata item from the
   group. */
   if( !param ) {

      pbuf[ 0 ] = 0;
      smf_get_grp_metadata( igrp, "DLONMAP", pbuf, status );
      if( pbuf[ 0 ] ) {
         sscanf( pbuf, "%p", &p );
         dlonmap = (AstMapping *) p;
         astLock( dlonmap, 0 );
         dlonmap = astAnnul( dlonmap );
         smf_remove_grp_metadata( igrp, "DLONMAP", status );
      }

      pbuf[ 0 ] = 0;
      smf_get_grp_metadata( igrp, "DLATMAP", pbuf, status );
      if( pbuf[ 0 ] ) {
         sscanf( pbuf, "%p", &p );
         dlatmap = (AstMapping *) p;
         astLock( dlatmap, 0 );
         dlatmap = astAnnul( dlatmap );
         smf_remove_grp_metadata( igrp, "DLATMAP", status );
      }

      return;
   }

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

/* Use the specified parameter to get the name of the text file containing
   the table of pointing corrections. */
   parGet0c( param, file, sizeof( file ) - 1, status );

/* If no file was specified, annul the error. */
   if( *status == PAR__NULL ) {
      errAnnul( status );

/* If a file was obtained sccuesfully, read it. */
   } else if( *status == SAI__OK ) {

/* Start an AST context. */
      astBegin;

/* Attempt to read an AST Table from the text file. */
      table = atlReadTable( file, status );

/* Create a LutMap from each of the three columns. */
      taimap = (AstMapping *) atlTablelutMap( table, "TAI", status );
      dlonmap = (AstMapping *) atlTablelutMap( table, "DLON", status );
      dlatmap = (AstMapping *) atlTablelutMap( table, "DLAT", status );

/* Create Mappings that transforms TAI into a DLON and DLAT. These use
   linear interpolation for non-tabulated TAI values. */
      astInvert( taimap );
      dlonmap = (AstMapping *) astCmpMap( taimap, dlonmap, 1, " " );
      dlatmap = (AstMapping *) astCmpMap( taimap, dlatmap, 1, " " );

/* Format the pointers to these two Mappings and store them in the
   supplied group using names "DLONMAP" and "DLATMAP". */
      sprintf( pbuf, "%p", (void *) dlonmap );
      smf_add_grp_metadata( igrp, "DLONMAP", pbuf, status );

      sprintf( pbuf, "%p", (void *) dlatmap );
      smf_add_grp_metadata( igrp, "DLATMAP", pbuf, status );

/* See what system the DLON/DLAT values refer to (default to AZEL). Store
   it in the group.  */
      if( !astMapGet0C( table, "SYSTEM", &system ) ) system = "AZEL";
      smf_add_grp_metadata( igrp, "PSYSTEM", system, status );

/* Unlock the pointers to the Mappings so that they can be used by a
   different thread. This also exempts the pointers from AST context
   handling (until they are re-locked) so the following call to astEnd
   will not annull them. */
      astUnlock( dlonmap, 1 );
      astUnlock( dlatmap, 1 );

/* End the AST context. This annuls all Objects created during the
   context, except for the unlocked Mappings. */
      astEnd;

/* Debug message. */
      msgSetc( "F", file );
      msgSetc( "S", system );
      msgOutif( MSG__DEBUG, " ", "^S pointing corrections read from file ^F",
                status );

/* Issue a context message if anything went wrong. */
      if( *status != SAI__OK ) {
         msgSetc( "F", file );
         errRep( " ", "Failed to read pointing corrections from text file ^F.",
                 status );
      }
   }
}
Beispiel #16
0
static AstTable *ReadNextTable( FILE *fd, const char *fname, int *iline,
                                int *status ) {
    /*
    *  Name:
    *     ReadOneTable

    *  Purpose:
    *     Reads a single Table from a text file.

    *  Description:
    *     This function reads text from the supplied file descriptor until it
    *     reaches the end of file or encounters an end-of-table marker (a line
    *     consisting just of two or more minus signs with no leading spaces).
    *     It creates an AstTable from the text and returns a pointer to it.

    *  Arguments:
    *     fd
    *        The file descriptor.
    *     fname
    *        The file name - used for error messages.
    *     iline
    *        Pointer to an int holding the number of lines read from the
    *        file so far. Updated on exit to include the lines read by the
    *        invocation of this function.
    *     status
    *        Pointer to the global status variable.

    *  Returned Value:
    *     A pointer to the Table read from the file, or NULL if an error occurs.

    */

    /* Local Variables: */
    AstTable *result;
    AstTable *subtable;
    char **cols;
    char **words;
    char *last_com;
    char *line;
    char *p;
    char *tname;
    char key[ 200 ];
    const char *cval;
    const char *oldname;
    const char *newname;
    double dval;
    int *types;
    int blank;
    int c;
    int com;
    int eot;
    int first;
    int icol;
    int irow;
    int ival;
    int iword;
    int line_len;
    int max_line_len;
    int more;
    int nc;
    int ncol;
    int nrow;
    int nword;
    int skip;
    size_t len;

    /* Initialise */
    result = NULL;

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

    /* Create an empty Table. */
    result = astTable( " " );

    /* Allocate a buffer for one one line of text. This will be increased in
       size as required. */
    max_line_len = 80;
    line = astMalloc( max_line_len*sizeof( *line ) );

    /* Read each line of text from the file. */
    eot = 0;
    skip = 1;
    line_len = 0;
    more = 1;
    last_com = NULL;
    cols = NULL;
    first = 1;
    irow = 0;
    types = NULL;

    while( more && *status == SAI__OK ) {
        (*iline)++;
        line_len = 0;

        /* Loop reading characters from the file until a newline or the end of file is
           reached. */
        while( ( c = fgetc( fd ) ) != EOF && c != '\n' ) {

            /* Increment the current line length, and double the size of the line buffer
               if it is full. */
            if( ++line_len >= max_line_len ) {
                max_line_len *= 2;
                line = astRealloc( line, max_line_len*sizeof( *line ) );
                if( *status != SAI__OK ) break;
            }

            /* Store the character. Ignore leading white space. */
            if( skip ) {
                if( ! isspace( c ) ) {
                    line[ line_len - 1 ] = c;
                    skip = 0;
                } else {
                    line_len--;
                }
            } else {
                line[ line_len - 1 ] = c;
            }

        }

        /* If the end-of-file was reached indicate that we should leave the main
           loop after processing the current line. */
        if( c == EOF ) more = 0;

        /* Terminate the line. */
        line[ line_len ] = 0;

        /* Terminate it again to exclude trailing white space. */
        line[ astChrLen( line ) ] = 0;

        /* Assume the line is a blank non-comment, and store a pointer to the first
           character to use. */
        blank = 1;
        com = 0;
        p = line;

        /* Skip blank lines. */
        if( line[ 0 ] ) {

            /* If the line starts with a comment character... */
            if( line[ 0 ] == '#' || line[ 0 ] == '!' ) {
                com = 1;

                /* Get a pointer to the first non-space/tab character after the comment
                   character. */
                p = line + 1;
                while( *p == ' ' || *p == '\t' ) p++;

                /* Note if it is blank. */
                if( *p ) blank = 0;

                /* If it is not a comment line, then the line is not blank. */
            } else {
                blank = 0;

                /* See if it is the end-of-table marker - a line containing just two or
                   more minus signs with no leading spaces. */
                eot = ( strspn( line, "-" ) > 1 );
            }
        }

        /* Skip blank lines, whether comment or not. */
        if( ! blank ) {

            /* First handle comment lines. */
            if( com ) {

                /* Does it look like a  parameter assignment... */
                words = astChrSplitRE( p, "^\\s*(\\w+)\\s*=\\s*(.*)$", &nword, NULL );
                if( words ) {

                    /* Add a parameter declaration to the Table. */
                    astAddParameter( result, words[ 0 ] );

                    /* Store the parameter value, using an appropriate data type. */
                    len = strlen( words[ 1 ] );
                    if( nc = 0, ( 1 == astSscanf( words[ 1 ], "%d%n", &ival, &nc ) )
                            && ( nc >= len ) ) {
                        astMapPut0I( result, words[ 0 ], ival, NULL );

                    } else if( nc = 0, ( 1 == astSscanf( words[ 1 ], "%lg%n", &dval, &nc ) )
                               && ( nc >= len ) ) {
                        astMapPut0D( result, words[ 0 ], dval, NULL );

                    } else {
                        astMapPut0C( result, words[ 0 ], words[ 1 ], NULL );

                    }

                    /* Free the words returned by astChrSplitRE. */
                    for( iword = 0; iword < nword; iword++ ) {
                        words[ iword ] = astFree( words[ iword ] );
                    }
                    words = astFree( words );

                    /* If it does not look like a parameter assignment... */
                } else {

                    /* Save a copy of it in case it turns out to be the last non-blank comment
                       line before the first row of data values (in which case it should
                       contain the column names). */
                    last_com = astStore( last_com, p, strlen( p ) + 1 );
                }

                /* If the line is not a comment see if it is an end of table marker. If so
                   indicate that we should leave the loop. */
            } else if( eot ) {
                more = 0;

                /* If the line is not a comment or an end of table marker ... */
            } else {

                /* Get the words from the row. */
                words = astChrSplit( p, &nword );

                /* If this is the first non-blank non-comment line, get the column names from
                   the previous non-blank comment line. */
                if( first ) {
                    if( last_com ) {
                        first = 0;
                        cols = astChrSplit( last_com, &ncol );

                        /* Create an array to hold the data type for each colum, and initialise
                           them to "integer". */
                        types = astMalloc( ncol*sizeof( int ) ) ;
                        for( iword = 0; iword < nword && astOK; iword++ ) {
                            if( iword < ncol ) {
                                types[ iword ] = AST__INTTYPE;

                                /* The columns are stored initially using interim names which have "T_"
                                   prepended to the names given in the file. */
                                tname = NULL;
                                nc = 0;
                                tname = astAppendString( tname, &nc, "T_" );
                                tname = astAppendString( tname, &nc, cols[ iword ] );
                                astFree( cols[ iword ] );
                                cols[ iword ] = tname;

                                /* Create the column definition within the returned Table. We store them
                                   initially as strings and then convert to the appropriate column data type
                                   later (once all rows have been read and the the data types are known). */
                                astAddColumn( result, cols[ iword ], AST__STRINGTYPE,
                                              0, NULL, " " );
                            }
                        }

                    } else if( *status == SAI__OK ) {
                        *status = SAI__ERROR;
                        msgSetc( "F", fname );
                        errRep( " ", "No column headers found in file ^F.", status );
                    }
                }

                /* Report an error if the line has the wrong number of values. */
                if( nword != ncol ) {
                    if( *status == SAI__OK ) {
                        *status = SAI__ERROR;
                        msgSeti( "N", nword );
                        msgSeti( "I", (*iline) );
                        msgSeti( "M", ncol );
                        msgSetc( "F", fname );
                        errRep( " ", "Wrong number of values (^N) at line ^I in "
                                "file ^F (should be ^M).", status );
                    }

                    /* Otherwise increment the number of rows read. */
                } else {
                    irow++;

                    /* Store each string value in the table, excluding "null" strings. Also check
                       the data type of each string an dupdate the column data types if necessary. */
                    for( iword = 0; iword < nword && *status == SAI__OK; iword++ ) {
                        if( strcmp( words[ iword ], "null" ) ) {
                            sprintf( key, "%s(%d)", cols[ iword ], irow );
                            astMapPut0C( result, key, words[ iword ], NULL );

                            /* If the column is currently thought to hold integers, check that the
                               current word looks like an integer. If not, down-grade the column type
                               to double. */
                            len = strlen( words[ iword ] );
                            if( types[ iword ] == AST__INTTYPE ) {
                                if( nc = 0, ( 1 != astSscanf( words[ iword ], "%d%n",
                                                              &ival, &nc ) ) ||
                                        ( nc < len ) ) {
                                    types[ iword ] = AST__DOUBLETYPE;
                                }
                            }

                            /* If the column is currently thought to hold doubles, check that the
                               current word looks like an doubler. If not, down-grade the column type
                               to string. */
                            if( types[ iword ] == AST__DOUBLETYPE ) {
                                if( nc = 0, ( 1 != astSscanf( words[ iword ], "%lg%n",
                                                              &dval, &nc ) ) ||
                                        ( nc < len ) ) {
                                    types[ iword ] = AST__STRINGTYPE;
                                }
                            }
                        }
                    }
                }

                /* Free the words returned by astChrSplit. */
                for( iword = 0; iword < nword; iword++ ) {
                    words[ iword ] = astFree( words[ iword ] );
                }
                words = astFree( words );

            }
        }
    }

    /* The entire file has now been read, and a Table created in which every
       column holds strings. We also have flags indicating whether the values
       in each column are all integers or doubles. Modify the type of the
       column within the Table to match these flags. */
    nrow = astGetI( result, "Nrow" );
    for( icol = 0; icol < ncol && *status == SAI__OK; icol++ ) {

        /* The column will be re-named from "T_<name>" to "<name>". */
        oldname = cols[ icol ];
        newname = oldname + 2;

        /* First convert string columns to integer columns if all the values in
           the column look like integers. */
        if( types[ icol ] == AST__INTTYPE ) {

            /* Create the new column */
            astAddColumn( result, newname, AST__INTTYPE, 0, NULL, " " );

            /* Copy each cell of the current column, converting from string to integer. */
            for( irow = 1; irow <= nrow; irow++ ) {
                sprintf( key, "%s(%d)", oldname, irow );
                if( astMapGet0I( result, key, &ival ) ) {
                    sprintf( key, "%s(%d)", newname, irow );
                    astMapPut0I( result, key, ival, NULL );
                }
            }

            /* Now do double columns in the same way. */
        } else if( types[ icol ] == AST__DOUBLETYPE ) {
            astAddColumn( result, newname, AST__DOUBLETYPE, 0, NULL, " " );
            for( irow = 1; irow <= nrow; irow++ ) {
                sprintf( key, "%s(%d)", oldname, irow );
                if( astMapGet0D( result, key, &dval ) ) {
                    sprintf( key, "%s(%d)", newname, irow );
                    astMapPut0D( result, key, dval, NULL );
                }
            }

            /* Copy string values without change. */
        } else {
            astAddColumn( result, newname, AST__STRINGTYPE, 0, NULL, " " );
            for( irow = 1; irow <= nrow; irow++ ) {
                sprintf( key, "%s(%d)", oldname, irow );
                if( astMapGet0C( result, key, &cval ) ) {
                    sprintf( key, "%s(%d)", newname, irow );
                    astMapPut0C( result, key, cval, NULL );
                }
            }
        }

        /* Remove the old column. */
        astRemoveColumn( result, oldname );

    }

    /* Free resources. */
    line = astFree( line );
    last_com = astFree( last_com );
    types = astFree( types );
    if( cols ) {
        for( icol = 0; icol < ncol; icol++ ) {
            cols[ icol ] = astFree( cols[ icol ] );
        }
        cols = astFree( cols );
    }

    /* If the table ended with an end-of-table marker, there may be another
       Table in the file. Call this function recursively to read it. */
    if( eot ) {
        subtable = ReadNextTable( fd, fname, iline, status );

        /* Store the subtable as a table parameter in the returned table. */
        if( subtable ) {
            astAddParameter( result, "SubTable" );
            astMapPut0A( result, "SubTable", subtable, NULL );

            /* The Table clones the pointer, so we must annull our local copy of it. */
            subtable = astAnnul( subtable );
        }
    }

    /* Return the Table pointer. */
    return result;
}
Beispiel #17
0
unsigned char *smf_get_mask( ThrWorkForce *wf, smf_modeltype mtype,
                             AstKeyMap *config, smfDIMMData *dat, int flags,
                             int *status ) {

/* Local Variables: */
   AstCircle *circle;         /* AST Region used to mask a circular area */
   AstKeyMap *akm;            /* KeyMap holding AST config values */
   AstKeyMap *subkm;          /* KeyMap holding model config values */
   char refparam[ DAT__SZNAM ];/* Name for reference NDF parameter */
   char words[100];           /* Buffer for variable message words */
   const char *cval;          /* The ZERO_MASK string value */
   const char *modname;       /* The name of the model  being masked */
   const char *skyrefis;      /* Pointer to SkyRefIs attribute value */
   dim_t i;                   /* Pixel index */
   double *pd;                /* Pointer to next element of map data */
   double *predef;            /* Pointer to mask defined by previous run */
   double *ptr;               /* Pointer to NDF  Data array */
   double *pv;                /* Pointer to next element of map variance */
   double centre[ 2 ];        /* Coords of circle centre in radians */
   double meanhits;           /* Mean hits in the map */
   double radius[ 1 ];        /* Radius of circle in radians */
   double zero_circle[ 3 ];   /* LON/LAT/Radius of circular mask */
   double zero_lowhits;       /* Fraction of mean hits at which to threshold */
   double zero_snr;           /* Higher SNR at which to threshold */
   double zero_snrlo;         /* Lower SNR at which to threshold */
   int *ph;                   /* Pointer to next hits value */
   int have_mask;             /* Did a mask already exist on entry? */
   int imask;                 /* Index of next mask type */
   int indf1;                 /* Id. for supplied reference NDF */
   int indf2;                 /* Id. for used section of reference NDF */
   int isstatic;              /* Are all used masks static? */
   int lbnd_grid[ 2 ];        /* Lower bounds of map in GRID coords */
   int mask_types[ NTYPE ];   /* Identifier for the types of mask to use */
   int munion;                /* Use union of supplied masks */
   int nel;                   /* Number of mapped NDF pixels */
   int nmask;                 /* The number of masks to be combined */
   int nsource;               /* No. of source pixels in final mask */
   int skip;                  /* No. of iters for which AST is not subtracted */
   int thresh;                /* Absolute threshold on hits */
   int ubnd_grid[ 2 ];        /* Upper bounds of map in GRID coords */
   int zero_c_n;              /* Number of zero circle parameters read */
   int zero_mask;             /* Use the reference NDF as a mask? */
   int zero_niter;            /* Only mask for the first "niter" iterations. */
   int zero_notlast;          /* Don't zero on last iteration? */
   size_t ngood;              /* Number good samples for stats */
   smf_qual_t *pq;            /* Pinter to map quality */
   unsigned char **mask;      /* Address of model's mask pointer */
   unsigned char *newmask;    /* Individual mask work space */
   unsigned char *pm;         /* Pointer to next returned mask pixel */
   unsigned char *pn;         /* Pointer to next new mask pixel */
   unsigned char *result;     /* Returned mask pointer */

/* Initialise returned values */
   result = NULL;

/* Check inherited status. Also check that a map is being created.  */
   if( *status != SAI__OK || !dat || !dat->map ) return result;

/* Begin an AST context. */
   astBegin;

/* Get the sub-keymap containing the configuration parameters for the
   requested model. Also get a pointer to the mask array to use (there is
   one for each maskable model)*/
   if( mtype == SMF__COM ) {
      modname = "COM";
      mask = &(dat->com_mask);
   } else if( mtype == SMF__AST ) {
      modname = "AST";
      mask = &(dat->ast_mask);
   } else if( mtype == SMF__FLT ) {
      modname = "FLT";
      mask = &(dat->flt_mask);
   } else {
      modname = NULL;
      mask = NULL;
      *status = SAI__ERROR;
      errRepf( " ", "smf_get_mask: Unsupported model type %d supplied - "
               "must be COM, FLT or AST.", status, mtype );
   }
   subkm = NULL;
   astMapGet0A( config, modname, &subkm );

/* Get the "ast.skip" value - when considering "zero_niter" and
   "zero_freeze", we only count iterations for which the AST model
   is subtracted (i.e. the ones following the initial "ast.skip"
   iterations). */
   astMapGet0A( config, "AST", &akm );
   astMapGet0I( akm, "SKIP", &skip );
   akm = astAnnul( akm );

/* Get the number of iterations over which the mask is to be applied. Zero
   means all. Return with no mask if this number of iterations has
   already been performed. */
   zero_niter = 0;
   astMapGet0I( subkm, "ZERO_NITER", &zero_niter );
   if( zero_niter == 0 || dat->iter < zero_niter + skip ) {

/* Only return a mask if this is not the last iteration, or if ZERO_NOTLAST
   is unset. */
      zero_notlast = 0;
      astMapGet0I( subkm, "ZERO_NOTLAST", &zero_notlast );
      if( !( flags & SMF__DIMM_LASTITER ) || !zero_notlast ) {

/* Create a list of the mask types to be combined to get the final mask by
   looking for non-default values for the corresponding configuration
   parameters in the supplied KeyMap. Static masks (predefined, circles
   or external NDFs) may be used on any iteration, but dynamic masks
   (lowhits, snr) will only be avialable once the map has been determined
   at the end of the first iteration. This means that when masking anything
   but the AST model (which is determined after the map), the dynamic masks
   cannot be used on the first iteration. Make a note if all masks being
   used are static. */

         isstatic = 1;
         nmask = 0;

         zero_lowhits = 0.0;
         astMapGet0D( subkm, "ZERO_LOWHITS", &zero_lowhits );
         if( zero_lowhits > 0.0 ) {
            if( mtype == SMF__AST || !( flags & SMF__DIMM_FIRSTITER ) ) {
               mask_types[ nmask++] = LOWHITS;
               isstatic = 0;
            }
         } else if( zero_lowhits <  0.0 && *status == SAI__OK ) {
            *status = SAI__ERROR;
            errRepf( " ", "Bad value for config parameter %s.ZERO_LOWHITS (%g) - "
                     "it must not be negative.", status, modname, zero_lowhits );
         }

         if( astMapGet1D( subkm, "ZERO_CIRCLE", 3, &zero_c_n, zero_circle ) ) {
            if( zero_c_n == 1 || zero_c_n == 3 ) {
               mask_types[ nmask++] = CIRCLE;
            } else if( *status == SAI__OK ) {
               *status = SAI__ERROR;
               errRepf( " ", "Bad number of values (%d) for config parameter "
                        "%s.ZERO_CIRCLE - must be 1 or 3.", status, zero_c_n,
                        modname );
            }
         }

         cval = NULL;
         astMapGet0C( subkm, "ZERO_MASK", &cval );
         if( cval ) {
            if( !astChrMatch( cval, "REF" ) &&
                !astChrMatch( cval, "MASK2" ) &&
                !astChrMatch( cval, "MASK3" ) ) {
               astMapGet0I( subkm, "ZERO_MASK", &zero_mask );
               cval = ( zero_mask > 0 ) ? "REF" : NULL;
            }
            if( cval ) {
               strcpy( refparam, cval );
               astChrCase( NULL, refparam, 1, 0 );
               mask_types[ nmask++] = REFNDF;
            }
         }

         zero_snr = 0.0;
         astMapGet0D( subkm, "ZERO_SNR", &zero_snr );
         if( zero_snr > 0.0 ) {
            if( mtype == SMF__AST || !( flags & SMF__DIMM_FIRSTITER ) ) {
               mask_types[ nmask++] = SNR;
               isstatic = 0;
            }
         } else if( zero_snr <  0.0 && *status == SAI__OK ) {
            *status = SAI__ERROR;
            errRepf( " ", "Bad value for config parameter %s.ZERO_SNR (%g) - "
                     "it must not be negative.", status, modname, zero_snr );
         }

         if( astMapHasKey( subkm, "ZERO_MASK_POINTER" ) ) {
            astMapGet0P( subkm, "ZERO_MASK_POINTER", (void **) &predef );
            if( predef ) mask_types[ nmask++] = PREDEFINED;
         }

/* No need to create a mask if no masking was requested or possible. */
         if( nmask > 0 ) {

/* Decide if we are using the union or intersection of the masks. */
            astMapGet0I( subkm, "ZERO_UNION", &munion );

/* Note if a mask existed on entry. If not, create a mask now, and
   initialise it to hold the mask defined by the initial sky map. */
            if( *mask == NULL ) {
               have_mask = 0;
               if( dat->initqual ) {
                  *mask = astMalloc( dat->msize*sizeof( **mask ) );
                  if( *mask ) {
                     pm = *mask;
                     pq = dat->initqual;
                     for( i = 0; i < dat->msize; i++ ) {
                        *(pm++) = ( *(pq++) != 0 );
                     }
                  }
               } else{
                  *mask = astCalloc( dat->msize, sizeof( **mask ) );
               }
            } else {
               have_mask = 1;
            }

/* If we are combining more than one mask, we need work space to hold
   an individual mask independently of the total mask. If we are using
   only one mask, then just use the main mask array. */
            if( nmask > 1 ) {
               newmask = astMalloc( dat->msize*sizeof( *newmask ) );
            } else {
               newmask = *mask;
            }

/* Get the number of iterations after which the mask is to be frozen.
   Zero means "never freeze the mask". */
            int zero_freeze = 0;
            astMapGet0I( subkm, "ZERO_FREEZE", &zero_freeze );

/* Loop round each type of mask to be used. */
            for( imask = 0; imask < nmask && *status == SAI__OK; imask++ ){

/* If the mask is now frozen, we just return the existing mask. So leave the
   loop. */
               if( zero_freeze != 0 && dat->iter > zero_freeze + skip ) {
                  break;

/* Low hits masking... */
               } else if( mask_types[ imask ] == LOWHITS ) {

/* Set hits pixels with 0 hits to VAL__BADI so that stats1 ignores them */
                  ph = dat->hitsmap;
                  for( i = 0; i < dat->msize; i++,ph++ ) {
                     if( *ph == 0 ) *ph = VAL__BADI;
                  }

/* Find the mean hits in the map */
                  smf_stats1I( dat->hitsmap, 1, dat->msize, NULL, 0, 0, &meanhits,
                               NULL, NULL, &ngood, status );
                  msgOutiff( MSG__DEBUG, " ", "smf_get_mask: mean hits = %lf, ngood "
                             "= %zd", status, meanhits, ngood );

/* Create the mask */
                  thresh = meanhits*zero_lowhits;
                  ph = dat->hitsmap;
                  pn = newmask;
                  for( i = 0; i < dat->msize; i++,ph++ ) {
                     *(pn++) = ( *ph != VAL__BADI && *ph < thresh ) ? 1 : 0;
                  }

/* Report masking info. */
                  msgOutiff( MSG__DEBUG, " ", "smf_get_mask: masking %s "
                             "model at hits = %d.", status, modname, thresh );

/* Circle masking... */
               } else if( mask_types[ imask ] == CIRCLE ) {

/* If we had a mask on entry, then there is no need to create a new one
   since it will not have changed. But we need to recalculate the circle
   mask if are combining it with any non-static masks. */
                  if( ! have_mask || ! isstatic ) {

/* If only one parameter supplied it is radius, assume reference
   LON/LAT from the frameset to get the centre. If the SkyFrame
   represents offsets from the reference position (i.e. the source is
   moving), assume the circle is to be centred on the origin.  */
                     if( zero_c_n == 1 ) {
                        zero_circle[ 2 ] = zero_circle[ 0 ];

                        skyrefis = astGetC( dat->outfset, "SkyRefIs" );
                        if( skyrefis && !strcmp( skyrefis, "Origin" ) ) {
                           zero_circle[ 0 ] = 0.0;
                           zero_circle[ 1 ] = 0.0;
                        } else {
                           zero_circle[ 0 ] = astGetD( dat->outfset, "SkyRef(1)" );
                           zero_circle[ 1 ] = astGetD( dat->outfset, "SkyRef(2)" );
                        }

                        zero_circle[ 0 ] *= AST__DR2D;
                        zero_circle[ 1 ] *= AST__DR2D;
                     }

/* The supplied bounds are for pixel coordinates... we need bounds for grid
    coordinates which have an offset */
                     lbnd_grid[ 0 ] = 1;
                     lbnd_grid[ 1 ] = 1;
                     ubnd_grid[ 0 ] = dat->ubnd_out[ 0 ] - dat->lbnd_out[ 0 ] + 1;
                     ubnd_grid[ 1 ] = dat->ubnd_out[ 1 ] - dat->lbnd_out[ 1 ] + 1;

/* Coordinates & radius of the circular region converted from degrees
   to radians */
                     centre[ 0 ] = zero_circle[ 0 ]*AST__DD2R;
                     centre[ 1 ] = zero_circle[ 1 ]*AST__DD2R;
                     radius[ 0 ] = zero_circle[ 2 ]*AST__DD2R;

/* Create the Circle, defined in the current Frame of the FrameSet (i.e.
   the sky frame). */
                     circle = astCircle( astGetFrame( dat->outfset, AST__CURRENT), 1,
                                         centre, radius, NULL, " " );

/* Fill the mask with zeros. */
                     memset( newmask, 0, sizeof( *newmask )*dat->msize );

/* Get the mapping from the sky frame (current) to the grid frame (base),
   and then set the mask to 1 for all of the values outside this circle */
                     astMaskUB( circle, astGetMapping( dat->outfset, AST__CURRENT,
                                                       AST__BASE ),
                                0, 2, lbnd_grid, ubnd_grid, newmask, 1 );

/* Report masking info. */
                     if( zero_niter == 0 ) {
                        sprintf( words, "on each iteration" );
                     } else {
                        sprintf( words, "for %d iterations", zero_niter );
                     }

                     msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model will"
                                " be masked %s using a circle of "
                                "radius %g arc-secs, centred at %s=%s, %s=%s.",
                                status, modname, words, radius[0]*AST__DR2D*3600,
                                astGetC( dat->outfset, "Symbol(1)" ),
                                astFormat( dat->outfset, 1, centre[ 0 ] ),
                                astGetC( dat->outfset, "Symbol(2)" ),
                                astFormat( dat->outfset, 2, centre[ 1 ] ) );
                  }

/* Reference NDF masking... */
               } else if( mask_types[ imask ] == REFNDF ) {

/* If we had a mask on entry, then there is no need to create a new one
   since it will not have changed. But we need to recalculate the NDF
   mask if are combining it with any non-static masks. */
                  if( ! have_mask || ! isstatic ) {

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

/* Get an identifier for the NDF using the associated ADAM parameter. */
                     ndfAssoc( refparam, "READ", &indf1, status );

/* Get a section from this NDF that matches the bounds of the map. */
                     ndfSect( indf1, 2, dat->lbnd_out, dat->ubnd_out, &indf2,
                              status );

/* Map the section. */
                     ndfMap( indf2, "DATA", "_DOUBLE", "READ", (void **) &ptr,
                             &nel, status );

/* Check we can use the pointer safely. */
                     if( *status == SAI__OK ) {

/* Find bad pixels in the NDF and set those pixels to 1 in the mask. */
                        pn = newmask;
                        for( i = 0; i < dat->msize; i++ ) {
                           *(pn++) = ( *(ptr++) == VAL__BADD ) ? 1 : 0;
                        }

/* Report masking info. */
                        ndfMsg( "N", indf2 );
                        msgSetc( "M", modname );
                        if( zero_niter == 0 ) {
                           msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The ^M "
                                      "model will be masked on each iteration "
                                      "using the bad pixels in NDF '^N'.",
                                      status );
                        } else {
                           msgSeti( "I", zero_niter );
                           msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The ^M "
                                      "model will be masked for ^I iterations "
                                      "using the bad pixels in NDF '^N'.",
                                      status );
                        }
                     }

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

/* SNR masking... */
               } else if( mask_types[ imask ] == SNR ) {

/* Get the lower SNR limit. */
                  zero_snrlo = 0.0;
                  astMapGet0D( subkm, "ZERO_SNRLO", &zero_snrlo );
                  if( zero_snrlo <= 0.0 ) {
                     zero_snrlo = zero_snr;
                  } else if( zero_snrlo > zero_snr && *status == SAI__OK ) {
                     *status = SAI__ERROR;
                     errRepf( " ", "Bad value for config parameter "
                              "%s.ZERO_SNRLO (%g) - it must not be higher "
                              "than %s.ZERO_SNR (%g).", status, modname,
                              zero_snrlo, modname, zero_snr );
                  }

/* If the higher and lower SNR limits are equal, just do a simple
   threshold on the SNR values to get the mask. */
                  if( zero_snr == zero_snrlo ) {
                     pd = dat->map;
                     pv = dat->mapvar;
                     pn = newmask;
                     for( i = 0; i < dat->msize; i++,pd++,pv++ ) {
                        *(pn++) = ( *pd != VAL__BADD && *pv != VAL__BADD &&
                                    *pv >= 0.0 && *pd < zero_snr*sqrt( *pv ) ) ? 1 : 0;
                     }

/* Report masking info. */
                     if( !have_mask ) {
                        if( zero_niter == 0 ) {
                           sprintf( words, "on each iteration" );
                        } else {
                           sprintf( words, "for %d iterations", zero_niter );
                        }
                        msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model "
                                   "will be masked %s using an SNR limit of %g.",
                                   status, modname, words, zero_snr );
                     }

/* If the higher and lower SNR limits are different, create an initial
   mask by thresholding at the ZERO_SNR value, and then extend the source
   areas within the mask down to an SNR limit of ZERO_SNRLO. */
                  } else {
                     smf_snrmask( wf, dat->map, dat->mapvar, dat->mdims,
                                  zero_snr, zero_snrlo, newmask, status );

/* Report masking info. */
                     if( !have_mask ) {
                        if( zero_niter == 0 ) {
                           sprintf( words, "on each iteration" );
                        } else {
                           sprintf( words, "for %d iterations", zero_niter );
                        }
                        msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model "
                                   "will be masked %s using an SNR limit of %g "
                                   "extended down to %g.", status, modname,
                                   words, zero_snr, zero_snrlo );
                     }
                  }

/* Predefined masking... */
               } else if( mask_types[ imask ] == PREDEFINED ) {

/* If we had a mask on entry, then there is no need to create a new one
   since it will not have changed. But we need to recalculate the
   mask if are combining it with any non-static masks. */
                  if( ! have_mask || ! isstatic ) {

/* Find bad pixels in the predefined array and set those pixels to 1 in
   the mask. */
                     pn = newmask;
                     for( i = 0; i < dat->msize; i++ ) {
                        *(pn++) = ( *(predef++) == VAL__BADD ) ? 1 : 0;
                     }

/* Report masking info. */
                     if( zero_niter == 0 ) {
                        sprintf( words, "on each iteration" );
                     } else {
                        sprintf( words, "for %d iterations", zero_niter );
                     }
                     msgOutiff( MSG__DEBUG, " ", "smf_get_mask: The %s model "
                                "will be masked %s using a smoothed form of "
                                "the final mask created with the previous map.",
                                status, modname, words );
                  }
               }

/* If required, add the new mask into the returned mask. If this is the
   first mask, we just copy the new mask to form the returned mask.
   Otherwise, we combine it with the existing returned mask. */
               if( ! have_mask || ! isstatic ) {
                  if( nmask > 1 ) {
                     if( imask == 0 ) {
                        memcpy( *mask, newmask, dat->msize*sizeof(*newmask));
                     } else {
                        pm = *mask;
                        pn = newmask;
                        if( munion ) {
                           for( i = 0; i < dat->msize; i++,pm++ ) {
                              if( *(pn++) == 0 ) *pm = 0;
                           }
                        } else {
                           for( i = 0; i < dat->msize; i++,pm++ ) {
                              if( *(pn++) == 1 ) *pm = 1;
                           }
                        }
                     }
                  }
               }
            }

/* Free the individual mask work array if it was used. */
            if( nmask > 1 ) newmask = astFree( newmask );

/* Check that the mask has some source pixels (i.e. pixels that have non-bad data values -
   we do not also check variance values since they are not available until the second
   iteration). */
            if( *status == SAI__OK ) {
               nsource = 0;
               pm = *mask;
               pd = dat->map;
               for( i = 0; i < dat->msize; i++,pd++,pv++,pm++ ) {
                  if( *pd != VAL__BADD && *pm == 0 ) nsource++;
               }
               if( nsource < 5 && *status == SAI__OK ) {
                  *status = SAI__ERROR;
                  errRepf( "", "The %s mask being used has fewer than 5 "
                           "source pixels.", status, modname );
                  if( zero_snr > 0.0 ) {
                     errRepf( "", "Maybe your zero_snr value (%g) is too high?",
                              status, zero_snr );
                  }
               }
            }

/* Return the mask pointer if all has gone well. */
            if( *status == SAI__OK ) result = *mask;
         }
      }
   }

/* End the AST context, annulling all AST Objects created in the context. */
   astEnd;

/* Return the pointer to the boolean mask. */
   return result;
}
Beispiel #18
0
void smf_subip(  ThrWorkForce *wf, smfArray *res, smfArray *lut, int *lbnd_out,
                 int *ubnd_out, AstKeyMap *keymap, AstFrameSet *outfs, int *status ) {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/* Free resources. */
      datAnnul( &loc, status );
      imapdata = astFree( imapdata );
      job_data = astFree( job_data );
   }
}
Beispiel #19
0
/* Main entry point. */
void smf_calcmodel_smo( ThrWorkForce *wf, smfDIMMData *dat, int chunk,
                        AstKeyMap *keymap, smfArray **allmodel,
                        int flags __attribute__((unused)),
                        int *status) {

  /* Local Variables */
  size_t bstride;               /* bolo stride */
  dim_t boxcar = 0;             /* size of boxcar smooth window */
  smf_filt_t filter_type;       /* The type of smoothing to perform */
  size_t i;                     /* Loop counter */
  dim_t idx=0;                  /* Index within subgroup */
  int iworker;                  /* Owkrer index */
  smfCalcmodelSmoJobData *job_data=NULL; /* Pointer to all job data structures */
  AstKeyMap *kmap=NULL;         /* Pointer to PLN-specific keys */
  smfArray *model=NULL;         /* Pointer to model at chunk */
  double *model_data=NULL;      /* Pointer to DATA component of model */
  double *model_data_copy=NULL; /* Copy of model_data for one bolo */
  dim_t nbolo=0;                /* Number of bolometers */
  dim_t ndata=0;                /* Total number of data points */
  int notfirst=0;               /* flag for delaying until after 1st iter */
  dim_t ntslice=0;              /* Number of time slices */
  int nworker;                  /* No. of worker threads in supplied Workforce */
  smfCalcmodelSmoJobData *pdata=NULL; /* Pointer to current data structure */
  smfArray *qua=NULL;           /* Pointer to QUA at chunk */
  smf_qual_t *qua_data=NULL; /* Pointer to quality data */
  smfArray *res=NULL;           /* Pointer to RES at chunk */
  double *res_data=NULL;        /* Pointer to DATA component of res */
  int step;                     /* Number of bolometers per thread */
  size_t tstride;               /* Time slice stride in data array */
  const char * typestr = NULL;  /* smo.type value */

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

  /* Obtain pointers to relevant smfArrays for this chunk */
  res = dat->res[chunk];
  qua = dat->qua[chunk];

  /* Obtain pointer to sub-keymap containing PLN parameters. Something will
     always be available.*/
  astMapGet0A( keymap, "SMO", &kmap );

  /* Are we skipping the first iteration? */
  astMapGet0I(kmap, "NOTFIRST", &notfirst);

  if( notfirst && (flags & SMF__DIMM_FIRSTITER) ) {
    msgOutif( MSG__VERB, "", FUNC_NAME
              ": skipping SMO this iteration", status );
    return;
  }

  /* Get the boxcar size */
  if( kmap ) smf_get_nsamp( kmap, "BOXCAR", res->sdata[0], &boxcar, status );

  /* Get the type of smoothing filter to use. Anthing that is not "MEDIAN" is mean */
  filter_type = SMF__FILT_MEAN;
  if (astMapGet0C( kmap, "TYPE", &typestr ) ) {
    if (strncasecmp( typestr, "MED", 3 ) == 0 ) {
      filter_type = SMF__FILT_MEDIAN;
    }
  }

  /* Assert bolo-ordered data */
  smf_model_dataOrder( wf, dat, allmodel, chunk, SMF__RES|SMF__QUA,
                       0, status );

  smf_get_dims( res->sdata[0],  NULL, NULL, NULL, &ntslice,
                &ndata, NULL, NULL, status);

  model = allmodel[chunk];

  msgOutiff(MSG__VERB, "",
            "    Calculating smoothed model using boxcar of width %" DIM_T_FMT " time slices",
            status, boxcar);

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

  /* Loop over index in subgrp (subarray) and put the previous iteration
     of the filtered component back into the residual before calculating
     and removing the new filtered component */
  for( idx=0; (*status==SAI__OK)&&(idx<res->ndat); idx++ ) {
    /* Obtain dimensions of the data */

    smf_get_dims( res->sdata[idx],  NULL, NULL, &nbolo, &ntslice,
                  &ndata, &bstride, &tstride, status);

    /* Get pointers to data/quality/model */
    res_data = (res->sdata[idx]->pntr)[0];
    qua_data = (qua->sdata[idx]->pntr)[0];
    model_data = (model->sdata[idx]->pntr)[0];

    if( (res_data == NULL) || (model_data == NULL) || (qua_data == NULL) ) {
      *status = SAI__ERROR;
      errRep( "", FUNC_NAME ": Null data in inputs", status);
    } else {

      /* Uncomment to aid debugging */
      /*
      smf_write_smfData( res->sdata[idx], NULL, qua_data, "res_in",
                         NULL, 0, 0, MSG__VERB, 0, status );
      */

      if( *status == SAI__OK ) {
        /* Place last iteration back into residual if this is a smoothable section of the time series */
        for (i=0; i< ndata; i++) {
          if ( !(qua_data[i]&SMF__Q_FIT)  && res_data[i] != VAL__BADD && model_data[i] != VAL__BADD ) {
            res_data[i] += model_data[i];
          }
        }
      }

      /* Uncomment to aid debugging */
      /*
      smf_write_smfData( model->sdata[idx], NULL, qua_data, "model_b4",
                         NULL, 0, 0, MSG__VERB, 0, status );

      smf_write_smfData( res->sdata[idx], NULL, qua_data, "res_b4",
                         NULL, 0, 0, MSG__VERB, 0, status );
      */

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

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

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

      /* Store all the other info needed by the worker threads, and submit the
         jobs to apply the smoothing. */
      for( iworker = 0; iworker < nworker; iworker++ ) {
         pdata = job_data + iworker;

         pdata->boxcar = boxcar;
         pdata->bstride = bstride;
         pdata->bstride = bstride;
         pdata->filter_type = filter_type;
         pdata->model_data = model_data;
         pdata->nbolo = nbolo;
         pdata->nbolo = nbolo;
         pdata->ntslice = ntslice;
         pdata->ntslice = ntslice;
         pdata->qua_data = qua_data;
         pdata->qua_data = qua_data;
         pdata->res_data = res_data;
         pdata->res_data = res_data;
         pdata->tstride = tstride;
         pdata->tstride = tstride;

         thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calcmodel_smo_job,
                      0, NULL, status );
      }
      thrWait( wf, status );

      /* Uncomment to aid debugging */
      /*
      smf_write_smfData( res->sdata[idx], NULL, qua_data, "res_af",
                         NULL, 0, 0, MSG__VERB, 0, status );
      smf_write_smfData( model->sdata[idx], NULL, qua_data, "model_af",
                         NULL, 0, 0, MSG__VERB, 0, status );
      */

    }
  }

  /* Free work space (astFree returns without action if a NULL pointer is
     supplied). */
  model_data_copy = astFree( model_data_copy );
  job_data = astFree( job_data );

  /* Annul AST Object pointers (astAnnul reports an error if a NULL pointer
     is supplied). */
  if( kmap ) kmap = astAnnul( kmap );
}
Beispiel #20
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 );
   }

}