Exemple #1
0
void starFree( void * ptr ) {
#if USE_AST_MALLOC
  int ast_status = 0;
  int *old_ast_status = NULL;
#endif

#if STARMEM_DEBUG
  if (STARMEM_PRINT_MALLOC)
    printf(__FILE__": Free pointer %p\n", ptr );
#endif

  switch ( STARMEM_MALLOC ) {

  case STARMEM__SYSTEM:
    free( ptr );
    break;

  case STARMEM__AST:
#if USE_AST_MALLOC
    old_ast_status = astWatch( &ast_status );
    astFree( ptr );
    astWatch( old_ast_status );
#else
    starMemFatalAST;
#endif
    break;

  case STARMEM__DL:
    dlfree( ptr );
    break;

  case STARMEM__GC:
    /* Nothing to do if garbage collector selected */
    break;

  default:
    starMemFatalNone;
  }

  return;
}
Exemple #2
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 );

}
Exemple #3
0
void atlGetParam( const char *param, AstKeyMap *keymap, int *status ){
/*
*+
*  Name:
*     atlGetParam

*  Purpose:
*     Create a vector character string entry in an AST KeyMap from a list
*     of fixed length strings.

*  Language:
*     C.

*  Invocation:
*     void atlGetParam( const char *param, AstKeyMap *keymap, int *status )

*  Description:
*     This function obtains a value for a named environment parameter and
*     stores it in the supplied KeyMap, using the parameter name as the
*     key.

*  Arguments:
*     param
*        The parameter name.
*     keymap
*        A pointer to an existing KeyMap.
*     status
*        Pointer to the global status variable.

*  Notes:
*     - An error will be reported if the parameter value obtained from
*     the environment is a vector with more than 100 elements.
*     - An error will be reported if any individual element in the parameter
*     value obtained from the environment requires more than 255 when
*     represented as a string.

*  Copyright:
*     Copyright (C) 2009 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  (JAC, HAwaii)
*     {enter_new_authors_here}

*  History:
*     16-APR-2009 (DSB):
*        Original version.
*     {enter_further_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*+
*/

/* Local Variables: */
   char *ptrs[ MAXVAL ];          /* Pointers to formatted parameter values */
   char buff[ MAXVAL ][ MAXLEN ]; /* Array of formatted parameter values */
   int *old_status;               /* Original AST status pointer */
   int actval;                    /* Number of parameter values supplied */
   int i;                         /* Element index */

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

/* Ensure AST uses the supplied status pointer. */
   old_status = astWatch( status );

/* Store a set of pointers to the buffers used to store the formatted
   parameter values. */
   for( i = 0; i < MAXVAL; i++ ) ptrs[ i ] = buff[ i ];

/* Get the parameter values as an array of strings. */
   parGet1c( param, MAXVAL, ptrs, MAXLEN, &actval, status );

/* If only one value was supplied, store it as a scalar entry in the
   KeyMap, using the parameter name as the key. */
   if( actval == 1 ) {
      astMapPut0C( keymap, param, ptrs[ 0 ], NULL );

/* If more than one value was supplied, store them as a vector entry in the
   KeyMap, using the parameter name as the key. */
   } else {
      astMapPut1C( keymap, param, actval, (const char **) ptrs, NULL );
   }

/* Revert to using the old AST status pointer. */
   astWatch( old_status );
}
Exemple #4
0
void cupid_mon( int *status ) {
/*
*+
*  Name:
*     cupid_mon

*  Purpose:
*     Top-level CUPID function for A-task monolith on UNIX.

*  Language:
*     Starlink C

*  Type of Module:
*     ADAM A-task

*  Description:
*     This is the top-level A-task monolith function for the CUPID
*     suite of A-tasks.  Each CUPID command is an alias to a softlink
*     that points to this monolith.  The chosen command is obtained
*     from the ADAM routine TASK_GET_NAME.  The command may be specified
*     from the shell or ICL.  Given the command, the requested A-task
*     is called after a successful matching of the input string with a
*     valid task name.  If there is no match, an error report is made.

*  Parameters:
*     status
*        Pointer to the global status variable used by the ADAM fixed part.

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

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

*  History:
*     28-SEP-2005 (DSB):
*        Original version.
*     29-JUL-2009 (TIMJ):
*        Call taskGetName rather than Fortran.
*        Add CUPID and version number to NDF history.
*     31-JUL-2009 (DSB):
*        Use ndgBegpv/Endpv to provide automatic provenance propagation.
*     16-OCT-2009 (DSB):
*        Use ndgBeggh/ndgEndgh to record contents of group parameters in
*        the history component of output NDFs.
*     2011-01-19 (TIMJ):
*        Add leak checking to CUPID monolith
*     {enter_further_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
*/

/* Local variables: */
   char appname[NDF__SZAPP+1];    /* Application name for NDF History */
   char buff[PAR__SZNAM+7];       /* Application name for provenance handling */
   char filter[PAR__SZNAM+PAR__SZNAM+1];
   char name[PAR__SZNAM+1];       /* C character variable to hold name */
   int ast_caching;               /* Initial value of AST MemoryCaching tuning parameter */
   int emslev1;                   /* EMS level on entry */
   int emslev2;                   /* EMS level on exit */
   int ngrp0;                     /* Number of grp ids at start */
   int ngrp1;                     /* Number of grp ids at end */
   int nloc0;                     /* Number of active HDS Locators at start */
   int nloc1;                     /* Number of active HDS Locators at end */

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

/* For debugging, watch one of the leaked GRP identifiers listed by the
   call to grpWatch at the end of this routine (if any). */
   /* grpWatch( 3129345, status ); */

/* Read the input error message stack level */
   emsLevel( &emslev1 );

/* Obtain the command from the environment.  This returns uppercase names. */
   taskGetName( name, sizeof(name), status );

/* Update the application name in the NDF history recording
   to include the version number of the application */
   snprintf( appname, NDF__SZAPP, "%-*s (%s V%s)", PAR__SZNAM,
             name, PACKAGE_UPCASE, PACKAGE_VERSION);
   ndfHappn( appname, status );

/* Make AST use the same variable for its inherited status. */
   astWatch( status );

/* Tell AST to re-cycle memory when possible. */
   ast_caching = astTune( "MemoryCaching", 1 );

/* Get the GRP and HDS status for leak checking - need the task name
   to mask out parameter names. Also need to mask out the monlith name */
   one_strlcpy( filter, "!CUPID_MON,!", sizeof(filter), status);
   one_strlcat( filter, name, sizeof(filter), status );
   grpInfoi( NULL, 0, "NGRP", &ngrp0, status );
   hdsInfoI( NULL, "LOCATORS", filter, &nloc0, status );

/* Begin a provenance block. This causes event handlers to be registered
   with the NDF library so that a handler routine in NDG is called every
   time an NDF is opened. This handler routine keeps a record of all
   NDFs that are opened for input or output, until the block is closed
   by calling ndgEndpv. */
   ndgBegpv( status );

/* Begin a GRP NDF history block. This causes the contents of GRP groups
   to be appended to default history text added to any NDFs during the
   block. */
   ndgBeggh( status );

/* Check the string against valid A-task names---if matched then call
   the relevant A-task. */

/* Finds a low frequency background surface. */
   if( !strcmp( name, "FINDBACK" ) ) {
      findback( status );

/* Identifies emission clumps within a 2- or 3D NDF. */
   } else if( !strcmp( name, "FINDCLUMPS" ) ) {
      findclumps( status );

/* Give help on CUPID commands. */
   } else if( !strcmp( name, "CUPIDHELP" ) ) {
      cupidhelp( status );

/* Create simulated data containing clumps and noise. */
   } else if( !strcmp( name, "MAKECLUMPS" ) ) {
      makeclumps( status );

/* Extract clump parameters from another image */
   } else if( !strcmp( name, "EXTRACTCLUMPS" ) ) {
      extractclumps( status );

/* Obtain information about one or more clumps. */
   } else if( !strcmp( name, "CLUMPINFO" ) ) {
      clumpinfo( status );

/* Report an error if the command name is not recognised. */
   } else if( *status == SAI__OK ) {
      *status = SAI__ERROR;
      errRep( "CUPID_MON_NOCOM", "CUPID: No such command ^CMD.", status );
   }

/* End the GRP NDF history block. */
   ndgEndgh( status );

/* End the provenance block. This will result in every output NDF being
   given a provenance extension containing a record of the input NDFs
   that the application accessed in order to create the output NDF. Any
   output NDF that already contains a provenance extension is left
   unchanged (so individual application can override this automatic
   provenance handling by adding a provenance extension to the output
   NDF itself). */
   sprintf( buff, "CUPID:%s", name );
   ndgEndpv( buff, status );

/* Re-instate the original value of the AST ObjectCaching tuning
   parameter. */
   astTune( "MemoryCaching", ast_caching );

/* Check for GRP leaks Do this in a new error reporting context so
   that we get the correct value even if an error has occurred. */
   errBegin( status );
   grpInfoi( NULL, 0, "NGRP", &ngrp1, status );

/* If there are more active groups now than there were on entry,
   there must be a problem (GRP identifiers are not being freed
   somewhere). So report it. */
   if (*status == SAI__OK && ngrp1 > ngrp0) {
     msgBlank( status );
     msgSetc( "NAME", name );
     msgSeti( "NGRP0", ngrp0 );
     msgSeti( "NGRP1", ngrp1 );
     msgOut( " ", "WARNING: The number of active "
             "GRP identifiers increased from ^NGRP0 to ^NGRP1 "
             "during execution of ^NAME (" PACKAGE_UPCASE " programming "
             " error).", status);
     msgBlank(status);
     grpWatch( 0, status );
   }
   errEnd( status );

/* Check for HDS leaks Do this in a new error reporting context so
   that we get the correct value even if an error has occurred. */
   errBegin( status );
   hdsInfoI( NULL, "LOCATORS", filter, &nloc1, status );

/* If there are more active locators now than there were on entry,
   there must be a problem (HDS locators are not being freed
   somewhere). So report it. */
   if (*status == SAI__OK && nloc1 > nloc0) {
     msgBlank( status );
     msgSetc( "NAME", name );
     msgSeti( "NLOC0", nloc0 );
     msgSeti( "NLOC1", nloc1 );
     msgOut( " ", "WARNING: The number of active "
             "HDS Locators increased from ^NLOC0 to ^NLOC1 "
             "during execution of ^NAME (" PACKAGE_UPCASE " programming "
             " error).", status);
     msgBlank(status);
     hdsShow("LOCATORS", status);
     hdsShow("FILES", status);
   }
   errEnd( status );

/* Read the exitt error message stack level */
   emsLevel( &emslev2 );

   if (*status == SAI__OK && emslev1 != emslev2 ) {
     errMark();
     msgBlank( status );
     msgSetc( "NAME", name );
     msgSeti( "LV1", emslev1);
     msgSeti( "LV2", emslev2);
     msgOut( " ", "WARNING: EMS Stack level went from ^LV1 to ^LV2"
             " during execution of ^NAME (" PACKAGE_UPCASE " programming"
             " error).", status );
     msgBlank(status);
     errRlse();
   }

/* Make AST use its own internal variable for its inherited status. */
   astWatch( NULL );

/* Clear out any remaining memory allocated by AST and report
   unintentional leaks. */
   astFlushMemory( 1 );

}
Exemple #5
0
AstTable *atlReadTable( const char *fname, int *status ) {
    /*
    *+
    *  Name:
    *     atlReadTable

    *  Purpose:
    *     Create an AST Table from a text file.

    *  Language:
    *     C.

    *  Invocation:
    *     AstTable *atlReadTable( const char *fname, int *status )

    *  Description:
    *     This function creates an AST Table by reading the contents of a
    *     given text file. The text file format matches that of TOPCAT's
    *     "ASCII" format, except that any comment lines before the first row
    *     of column values that are of the form "# name = value" or
    *     "! name = value" are used to create Table parameters. Here "name"
    *     is a contiguous block of alphanumeric characters, and "value"
    *     represents all characters following the equals sign, up to the end
    *     of the line, excluding leading and trailing white space. If value
    *     is a scalar integer or double, it is stored as such in the Table.
    *     Otherwise, it is stored as a string.
    *
    *     In addition, any row that consists just of two or more minus signs,
    *     with no leading spaces, is taken to mark the end of the catalogue.
    *     Any subsequent lines in the file are assumed to form a whole new
    *     table that is read in exactly the same way as the first. This
    *     second Table is stored as a parameter of the first Table using the
    *     key "SubTable".

    *  Arguments:
    *     fname
    *        The file name.
    *     status
    *        Pointer to the global status variable.

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

    *  Notes:
    *     - All columns in the returned Table will hold scalar values.

    *  Copyright:
    *     Copyright (C) 2016 East Asian Observatory.
    *     Copyright (C) 2011 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:
    *     13-MAY-2011 (DSB):
    *        Original version.
    *     16-MAR-2016 (DSB):
    *        Added the SubTable facility.
    *     22-NOV-2016 (DSB):
    *        Report error if file has no column headers. Previously this
    *        caused a seg fault.
    *     {enter_further_changes_here}

    *  Bugs:
    *     {note_any_bugs_here}

    *+
    */

    /* Local Variables: */
    AstTable *result;
    FILE *fd;
    int iline;
    int *old_status;

    /* Initialise */
    result = NULL;

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

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

    /* Open the file */
    fd = fopen( fname, "r" );

    /* Report an error if the file could not be opened. */
    if( !fd ) {
        *status = SAI__ERROR;
        msgSetc( "F", fname );
        errRep( "", "atlReadTable: Failed to open input text file: \"^F\".",
                status );

        /* Otherwise... */
    } else {

        /* Create the Table by reading text from the start of the file. */
        iline = 0;
        result = ReadNextTable( fd, fname, &iline, status );

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

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

    /* Return the Table pointer. */
    return result;
}
Exemple #6
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 );

}
Exemple #7
0
void smurf_mon( int * status ) {

  /* Local variables */
  char taskname[PAR__SZNAM+1];
  char appname[NDF__SZAPP+1];
  char filter[PAR__SZNAM+PAR__SZNAM+1];
  int ngrp0;                   /* Number of grp ids at start */
  int ngrp1;                   /* Number of grp ids at end */
  int nloc0;                   /* Number of active HDS Locators at start */
  int nloc1;                   /* Number of active HDS Locators at end */
  int memory_caching;          /* Is AST current caching unused memory? */
  int emslev1;                 /* EMS level on entry */
  int emslev2;                 /* EMS level on exit */

  if ( *status != SAI__OK ) return;

  /* Read the input error message stack level */
  emsLevel( &emslev1 );

  /* Initialise AST */
  astBegin;
  memory_caching = astTune( "MemoryCaching", 1 );

  /* Register our status variable with AST */
  astWatch( status );

  /* If we are watching a particular memory Id reported by astActiveMemory
     we set the watch point here. */
  /* astWatchMemory( 29 ); */

  /* For debugging, watch one of the leaked GRP identifiers listed by the
     call to grpWatch at the end of this routine (if any). */
  /* grpWatch( 3129345, status ); */

  /* Mark any currently active NDF parameters, so that they will
     not be cancelled by the call to ndfCancl at the end of this
     function. */
  ndfCancl( "*", status );

  /* Find out the task name and provenance name we were invoked with */
  smf_get_taskname( taskname, NULL, status );

  /* Get the GRP and HDS status for leak checking - need the task name
     to mask out parameter names. Also need to mask out the monlith name */
  one_strlcpy( filter, "!SMURF_MON,!", sizeof(filter), status);
  one_strlcat( filter, taskname, sizeof(filter), status );
  grpInfoi( NULL, 0, "NGRP", &ngrp0, status );
  hdsInfoI( NULL, "LOCATORS", filter, &nloc0, status );


  /* Update the application name in the NDF history recording
     to include the version number of the application */
  snprintf( appname, NDF__SZAPP, "%-*s (%s V%s)", PAR__SZNAM,
            taskname, PACKAGE_UPCASE, PACKAGE_VERSION);
  ndfHappn( appname, status );

  /* Begin a GRP NDF history block. This causes the contents of GRP
     groups to be appended to default history text added to any NDFs
     during the block. */
  ndgBeggh( status );



  /* Call the subroutine associated with the requested task */
  if (strcmp( taskname, "BADBOLOS" ) == 0 ) {
    smurf_extinction( status );
  } else if (strcmp( taskname, "CALCDARK" ) == 0 ) {
    smurf_calcdark( status );
  } else if (strcmp( taskname, "CALCFLAT" ) == 0 ) {
    smurf_calcflat( status );
  } else if (strcmp( taskname, "CALCNOISE" ) == 0 ) {
    smurf_calcnoise( status );
  } else if (strcmp( taskname, "CALCQU" ) == 0 ) {
    smurf_calcqu( status );
  } else if (strcmp( taskname, "CALCRESP" ) == 0 ) {
    smurf_calcresp( status );
  } else if (strcmp( taskname, "COPYFLAT" ) == 0 ) {
    smurf_copyflat( status );
  } else if (strcmp( taskname, "DREAMSOLVE" ) == 0 ) {
    smurf_dreamsolve( status );
  } else if (strcmp( taskname, "DREAMWEIGHTS" ) == 0 ) {
    smurf_dreamweights( status );
  } else if (strcmp( taskname, "DSUTILS" ) == 0 ) {
    smurf_dsutils( status );
  } else if (strcmp( taskname, "EXTINCTION" ) == 0 ) {
    smurf_extinction( status );
  } else if (strcmp( taskname, "FIT1D" ) == 0 ) {
    smurf_fit1d( status );
  } else if (strcmp( taskname, "FIXSTEPS" ) == 0 ) {
    smurf_fixsteps( status );
  } else if (strcmp( taskname, "FLATFIELD" ) == 0 ) {
    smurf_flatfield( status );
  } else if (strcmp( taskname, "FTS2DEGLITCH" ) == 0 ) {
    smurf_fts2_deglitch( status );
  } else if (strcmp( taskname, "FTS2FLATFIELD" ) == 0 ) {
    smurf_fts2_flatfield( status );
  } else if (strcmp( taskname, "FTS2FREQCORR" ) == 0 ) {
    smurf_fts2_freqcorr( status );
  } else if (strcmp( taskname, "FTS2SPLIT" ) == 0 ) {
    smurf_fts2_split( status );
  } else if (strcmp( taskname, "FTS2INIT" ) == 0 ) {
    smurf_fts2_init( status );
  } else if (strcmp( taskname, "FTS2MASKMAP" ) == 0 ) {
    smurf_fts2_maskmap( status );
  } else if (strcmp( taskname, "FTS2OPCORR" ) == 0 ) {
    smurf_fts2_spatialwcs( status );
  } else if (strcmp( taskname, "FTS2PHASECORR" ) == 0 ) {
    smurf_fts2_phasecorr( status );
  } else if (strcmp( taskname, "FTS2PHASECORRDS" ) == 0 ) {
    smurf_fts2_phasecorrds( status );
  } else if (strcmp( taskname, "FTS2PORTIMBAL" ) == 0 ) {
    smurf_fts2_portimbal( status );
  } else if (strcmp( taskname, "FTS2REMOVEBSE" ) == 0 ) {
    smurf_fts2_removebse( status );
  } else if (strcmp( taskname, "FTS2SPECTRUM" ) == 0 ) {
    smurf_fts2_spectrum( status );
  } else if (strcmp( taskname, "FTS2TRANSCORR" ) == 0 ) {
    smurf_fts2_transcorr( status );
  } else if (strcmp( taskname, "GSD2ACSIS" ) == 0 ) {
    smurf_gsd2acsis( status );
  } else if (strcmp( taskname, "GSDSHOW" ) == 0 ) {
    smurf_gsdshow( status );
  } else if (strcmp( taskname, "IMPAZTEC" ) == 0 ) {
    smurf_impaztec( status );
  } else if (strcmp( taskname, "MAKECUBE" ) == 0 ) {
    smurf_makecube( status );
  } else if (strcmp( taskname, "MAKEMAP" ) == 0 ) {
    smurf_makemap( status );
  } else if (strcmp( taskname, "RAWFIXMETA" ) == 0 ) {
    smurf_rawfixmeta( status );
  } else if (strcmp( taskname, "RAWPRESS" ) == 0 ) {
    smurf_rawpress( status );
  } else if (strcmp( taskname, "RAWRECREATEWCS" ) == 0 ) {
    smurf_rawrecreatewcs( status );
  } else if (strcmp( taskname, "RAWREWRTSC2WCS" ) == 0 ) {
    smurf_rawrewrtsc2wcs( status );
  } else if (strcmp( taskname, "RAWUNPRESS" ) == 0 ) {
    smurf_rawunpress( status );
  } else if (strcmp( taskname, "REMSKY" ) == 0 ) {
    smurf_remsky( status );
  } else if (strcmp( taskname, "SC2CLEAN" ) == 0 ) {
    smurf_sc2clean( status );
  } else if (strcmp( taskname, "SC2CONCAT" ) == 0 ) {
    smurf_sc2concat( status );
  } else if (strcmp( taskname, "SC2EXPANDMODEL" ) == 0 ) {
    smurf_sc2expandmodel( status );
  } else if (strcmp( taskname, "SC2FFT" ) == 0 ) {
    smurf_sc2fft( status );
  } else if (strcmp( taskname, "SC2FILTERMAP" ) == 0 ) {
    smurf_sc2filtermap( status );
  } else if (strcmp( taskname, "SC2MAPFFT" ) == 0 ) {
    smurf_sc2mapfft( status );
  } else if (strcmp( taskname, "SC2PCA" ) == 0 ) {
    smurf_sc2pca( status );
  } else if (strcmp( taskname, "SC2SIM" ) == 0 ) {
    smurf_sc2sim( status );
  } else if (strcmp( taskname, "SC2THREADTEST" ) == 0 ) {
    smurf_sc2threadtest( status );
  } else if (strcmp( taskname, "SKYNOISE" ) == 0 ) {
    smurf_skynoise( status );
  } else if (strcmp( taskname, "SMURFCOPY" ) == 0 ) {
    smurf_smurfcopy( status );
  } else if (strcmp( taskname, "SMURFHELP" ) == 0 ) {
    smurf_smurfhelp( status );
  } else if (strcmp( taskname, "STACKFRAMES" ) == 0 ) {
    smurf_stackframes( status );
  } else if (strcmp( taskname, "STARECALC" ) == 0 ) {
    smurf_starecalc( status );
  } else if (strcmp( taskname, "TILEINFO" ) == 0 ) {
    smurf_tileinfo( status );
  } else if (strcmp( taskname, "TILELIST" ) == 0 ) {
    smurf_tilelist( status );
  } else if (strcmp( taskname, "TIMESORT" ) == 0 ) {
    smurf_timesort( status );
  } else if (strcmp( taskname, "UNMAKECUBE" ) == 0 ) {
    smurf_unmakecube( status );
  } else if (strcmp( taskname, "UNMAKEMAP" ) == 0 ) {
    smurf_unmakemap( status );
  } else {
    *status = SAI__ERROR;
    msgSetc( "TASK", taskname );
    errRep( "smurf_mon", "Unrecognized taskname: ^TASK", status);
  }


  /* End the GRP NDF history block. */
  ndgEndgh( status );

  /* Clear cached info from sc2ast_createwcs. */
  sc2ast_createwcs(SC2AST__NULLSUB, NULL, NULL, NULL, NO_FTS, NULL, status);

  /* Clear WVM caches (one for each thread). */
  smf_calc_wvm_clear( status );

  /* Free AST resources */
  astTune( "MemoryCaching", memory_caching );
  astEnd;

  /* Check for GRP leaks Do this in a new error reporting context so
   * that we get the correct value even if an error has occurred. */
  errBegin( status );
  grpInfoi( NULL, 0, "NGRP", &ngrp1, status );

  /* If there are more active groups now than there were on entry,
   * there must be a problem (GRP identifiers are not being freed
   * somewhere). So report it. */
  if (*status == SAI__OK && ngrp1 > ngrp0) {
    msgBlank( status );
    msgSetc( "NAME", taskname );
    msgSeti( "NGRP0", ngrp0 );
    msgSeti( "NGRP1", ngrp1 );
    msgOut( " ", "WARNING: The number of active "
            "GRP identifiers increased from ^NGRP0 to ^NGRP1 "
            "during execution of ^NAME (" PACKAGE_UPCASE " programming "
            " error).", status);
    msgBlank(status);
    grpWatch( 0, status );
  }
  errEnd( status );

  /* The NDF library registers locators with SUBPAR for any NDFs that
     are opened directly using ndfAssoc or ndfExist. These locators are
     only annulled when the associated parameters are cancelled, but most
     smurf applications do not explicitly cancel their NDF parameters.
     This means that such locators are picked up by the following check
     for dangling HDS locators. In order to prevent this, we cancel any
     remaining NDF parameters now, excluding any that were marked by the
     call to ndfCancl at the start of this routine. */
  ndfCancl( " ", status );

  /* Check for HDS leaks Do this in a new error reporting context so
   * that we get the correct value even if an error has occurred. */
  errBegin( status );
  hdsInfoI( NULL, "LOCATORS", filter, &nloc1, status );

  /* If there are more active locators now than there were on entry,
   * there must be a problem (HDS locators are not being freed
   * somewhere). So report it. */
  if (*status == SAI__OK && nloc1 > nloc0) {
    msgBlank( status );
    msgSetc( "NAME", taskname );
    msgSeti( "NLOC0", nloc0 );
    msgSeti( "NLOC1", nloc1 );
    msgOut( " ", "WARNING: The number of active "
            "HDS Locators increased from ^NLOC0 to ^NLOC1 "
            "during execution of ^NAME (" PACKAGE_UPCASE " programming "
            " error).", status);
    msgBlank(status);
    hdsShow("LOCATORS", status);
    hdsShow("FILES", status);
    printf("filter - %s\n",filter);
  }
  errEnd( status );

  /* Read the exitt error message stack level */
  emsLevel( &emslev2 );

  if (*status == SAI__OK && emslev1 != emslev2 ) {
    errMark();
    msgBlank( status );
    msgSetc( "NAME", taskname );
    msgSeti( "LV1", emslev1);
    msgSeti( "LV2", emslev2);
    msgOut( " ", "WARNING: EMS Stack level went from ^LV1 to ^LV2"
            " during execution of ^NAME (" PACKAGE_UPCASE " programming"
            " error).", status );
    msgBlank(status);
    errRlse();
  }

  /* configure AST --with-memdebug, and uncomment the following lines
     to see how much memory usage SMURF hit at its peak */
  /*
  {
    size_t memcurrent,mempeak;
    astMemoryStats( 0, &mempeak, &memcurrent );
    msgOutf( "", "SMURF: === current /peak memory usage: %zu / %zu MiB ===",
             status, memcurrent/SMF__MIB, mempeak/SMF__MIB );
  }
  */

  /* The astCheckMemory function does nothing unless AST has been compiled
   * with the MEM_DEBUG flag. If this is the case, then it reports the number
   * of memory blocks that have not been freed (useful for identifying memory
   * leaks). Use astActiveMemory() below to list all active memory and
   * then use astWatchMemory() at the start of this routine to get reports
   * when a particular ID is used. Set a breakpoint in the debugger for
   * astMemoryAlarm_
   */
  astActiveMemory("Exit:");
  astCheckMemory;
}
Exemple #8
0
void findback( int *status ){
/*
*+
*  Name:
*     FINDBACK

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

*  Language:
*     C

*  Type of Module:
*     ADAM A-task

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

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

*  Usage:
*     findback in out box

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

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

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

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

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

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

*-
*/

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

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

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

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

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

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

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

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

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

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

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

   msgBlankif( MSG__VERB, status );

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

         slice_lbnd[ 1 ] = lbnd[ 1 ];

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

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

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

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

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

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

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

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

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

      }
   }

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

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

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

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

/* If an error has occurred, issue another error report identifying the
   program which has failed (i.e. this one). */
   if( *status != SAI__OK ) {
      errRep( "FINDBACK_ERR", "FINDBACK: Failed to find the background "
              "of an NDF.", status );
   }
}