IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 6873


Ignore:
Timestamp:
Apr 17, 2006, 8:10:08 AM (20 years ago)
Author:
magnier
Message:

fixed up conflicts

Location:
trunk/psModules/src
Files:
21 edited

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/astrom/pmAstrometry.c

    r6872 r6873  
    1313* XXX: Should we implement non-linear cell->chip transforms?
    1414*
    15 *  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
    16 *  @date $Date: 2006-04-17 18:01:04 $
     15*  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
     16*  @date $Date: 2006-04-17 18:10:08 $
    1717*
    1818*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/astrom/pmAstrometry.h

    r6872 r6873  
    88*  @author GLG, MHPCC
    99*
    10 *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
    11 *  @date $Date: 2006-04-17 18:01:04 $
     10*  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
     11*  @date $Date: 2006-04-17 18:10:08 $
    1212*
    1313*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/astrom/pmAstrometryObjects.h

    r6872 r6873  
    88*  @author EAM, IfA
    99*
    10 *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
    11 *  @date $Date: 2006-04-17 18:01:04 $
     10*  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
     11*  @date $Date: 2006-04-17 18:10:08 $
    1212*
    1313*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/config/pmConfig.c

    r6418 r6873  
    33 *  @author PAP, IfA
    44 *
    5  *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
    6  *  @date $Date: 2006-02-10 02:43:19 $
     5 *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
     6 *  @date $Date: 2006-04-17 18:10:08 $
    77 *
    88 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
     
    1111#include <stdio.h>
    1212#include <string.h>
     13#include <unistd.h>
     14#include <assert.h>
     15#include <sys/types.h>
     16#include <sys/stat.h>
     17#include <glob.h>
    1318#include "pslib.h"
    1419#include "pmConfig.h"
    1520
    16 #define PS_SITE "PS_SITE"               // Name of the environment variable containing the site config file
    17 #define PS_DEFAULT_SITE "ipprc.config"  // Default site config file
     21#define PS_SITE "PS_SITE"         // Name of the environment variable containing the site config file
     22#define PS_DEFAULT_SITE ".ipprc"  // Default site config file
     23
     24static psArray *configPath = NULL;
     25
     26static void configFree(pmConfig *config)
     27{
     28    psFree(config->site);
     29    psFree(config->files);
     30    psFree(config->camera);
     31    psFree(config->recipes);
     32    psFree(config->arguments);
     33    psFree(config->database);
     34}
     35
     36pmConfig *pmConfigAlloc(void)
     37{
     38    pmConfig *config = psAlloc(sizeof(pmConfig));
     39    (void)psMemSetDeallocator(config, (psFreeFunc)configFree);
     40
     41    // Initialise
     42    config->site = NULL;
     43    config->camera = NULL;
     44    config->recipes = NULL;
     45    config->arguments = NULL;
     46    config->database = NULL;
     47
     48    // the file structure is used to carry pmFPAfiles
     49    config->files = psMetadataAlloc ();
     50    return config;
     51}
     52
     53void pmConfigDone(void)
     54{
     55    psFree(configPath);
     56}
     57
    1858
    1959/** readConfig
     
    2363 *
    2464 */
    25 static bool readConfig(
     65bool readConfig(
    2666    psMetadata **config,                // Config to output
    2767    const char *name,                   // Name of file
    2868    const char *description)            // Description of file
    2969{
     70    char *realName = NULL;
    3071    unsigned int numBadLines = 0;
     72    struct stat filestat;
    3173
    3274    psLogMsg(__func__, PS_LOG_INFO, "Loading %s configuration from file %s\n",
    3375             description, name);
    34     *config = psMetadataConfigParse(NULL, &numBadLines, name, true);
     76
     77    uid_t uid = getuid();
     78    gid_t gid = getgid();
     79
     80    // we try: name, path[0]/name, path[1]/name, ...
     81    // find the first existing entry in the path (starting with the bare name)
     82    realName = psStringCopy (name);
     83    psTrace (__func__, 5, "trying %s\n", realName);
     84
     85    int status = stat (realName, &filestat);
     86    if (status == 0) {
     87        if ((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR))
     88            goto found;
     89        if ((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP))
     90            goto found;
     91        if (filestat.st_mode & S_IROTH)
     92            goto found;
     93    }
     94    psFree (realName);
     95
     96    if (configPath == NULL) {
     97        psError(PS_ERR_IO, false, "Cannot find %s configuration file in path\n", description);
     98        return false;
     99    }
     100
     101    for (int i = 0; i < configPath->n; i++) {
     102        realName = psStringCopy (configPath->data[i]);
     103        psStringAppend (&realName, "/%s", name);
     104        psTrace (__func__, 5, "trying %s\n", realName);
     105
     106        status = stat (realName, &filestat);
     107        if (status == 0) {
     108            if ((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR))
     109                goto found;
     110            if ((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP))
     111                goto found;
     112            if (filestat.st_mode & S_IROTH)
     113                goto found;
     114        }
     115        psFree (realName);
     116    }
     117
     118    psError(PS_ERR_IO, false, "Cannot find %s configuration file in path\n", description);
     119    return false;
     120
     121found:
     122    *config = psMetadataConfigParse(NULL, &numBadLines, realName, true);
    35123    if (numBadLines > 0) {
    36124        psLogMsg(__func__, PS_LOG_WARN, "%d bad lines in %s configuration file (%s)\n",
    37                  description, name);
     125                 description, realName);
    38126    }
    39127    if (!*config) {
    40128        psError(PS_ERR_IO, false, "Unable to read %s configuration from %s\n",
    41                 description, name);
     129                description, realName);
     130        psFree (realName);
    42131        return false;
    43132    }
    44133
     134    psFree (realName);
    45135    return true;
    46136}
     
    57147 line.
    58148 *****************************************************************************/
    59 bool pmConfigRead(
    60     psMetadata **site,
    61     psMetadata **camera,
    62     psMetadata **recipe,
     149pmConfig *pmConfigRead(
    63150    int *argc,
    64     char **argv,
    65     const char *recipeName)
    66 {
    67     /* XXX: We need clarification on what is to be done if these arguments are
    68     NULL.
    69         PS_ASSERT_PTR_NON_NULL(site, false);
    70         PS_ASSERT_PTR_NON_NULL(*site, false);
    71         PS_ASSERT_PTR_NON_NULL(camera, false);
    72         PS_ASSERT_PTR_NON_NULL(*camera, false);
    73         PS_ASSERT_PTR_NON_NULL(recipe, false);
    74         PS_ASSERT_PTR_NON_NULL(*recipe, false);
    75         PS_ASSERT_INT_POSITIVE(*argc, false);
    76         PS_ASSERT_PTR_NON_NULL(argv, false);
    77     */
     151    char **argv)
     152{
     153    PS_ASSERT_INT_POSITIVE(*argc, false);
     154    PS_ASSERT_PTR_NON_NULL(argv, false);
     155
     156    pmConfig *config = pmConfigAlloc(); // The configuration, containing site, camera and recipes
     157
    78158    //
    79159    // The following section of code attempts to determine which file is
     
    97177                     "-site command-line switch provided without the required filename --- ignored.\n");
    98178        } else {
    99             siteName = argv[argNum];
     179            siteName = psStringCopy(argv[argNum]);
    100180            psArgumentRemove(argNum, argc, argv);
    101181        }
     
    106186    if (!siteName) {
    107187        siteName = getenv(PS_SITE);
     188        if (siteName) {
     189            siteName = psStringCopy (siteName);
     190        }
    108191    }
    109192
     
    111194    // Last chance is ~/.ipprc
    112195    //
    113     bool cleanupSiteName = false; // Do I have to psFree siteName?
    114196    if (!siteName) {
    115         siteName = psStringCopy(PS_DEFAULT_SITE);
    116         cleanupSiteName = true;
    117     }
    118 
    119     //
    120     // We have the connfiguration filename; now we read and parse the config
     197        char *home = getenv("HOME");
     198        siteName = psStringCopy(home);
     199        psStringAppend(&siteName, "/%s", PS_DEFAULT_SITE);
     200    }
     201
     202
     203    // We have the configuration filename; now we read and parse the config
    121204    // file and store in psMetadata struct site.
    122205    //
    123206
    124     if (!readConfig(site, siteName, "site")) {
    125         return false;
    126     }
    127     if (cleanupSiteName) {
    128         psFree(siteName);
    129     }
    130 
    131 
    132     //
    133     // Next, we do a similar thing for the recipe configuration file.  The
    134     // file is read and parsed into psMetadata struct "recipe".
    135     //
    136     //
    137     argNum = psArgumentGet(*argc, argv, "-recipe");
    138     if (argNum > 0) {
    139         psArgumentRemove(argNum, argc, argv);
    140         if (argNum >= *argc) {
    141             psLogMsg(__func__, PS_LOG_WARN,
    142                      "-recipe command-line switch provided without the required filename --- ignored.\n");
    143         } else {
    144             psArgumentRemove(argNum, argc, argv);
    145             readConfig(recipe, argv[argNum], "recipe");
    146         }
    147     }
    148     // Or, load the recipe from the camera file, if appropriate
    149     if (! *recipe && *camera && recipeName) {
    150         *recipe = pmConfigRecipeFromCamera(*camera, recipeName);
    151     }
    152 
    153 
    154     //
     207    if (!readConfig(&config->site, siteName, "site")) {
     208        psFree(config);
     209        return NULL;
     210    }
     211    psFree(siteName);
     212
     213    // define the config-file search path (configPath)
     214    if (configPath) {
     215        psFree(configPath);
     216        configPath = NULL;
     217    }
     218    char *path = psMetadataLookupStr(NULL, config->site, "PATH");
     219    if (path) {
     220        psList *list = psStringSplit(path, ":");
     221        configPath = psListToArray(list);
     222        psFree(list);
     223    }
     224
    155225    // Next, we do a similar thing for the camera configuration file.  The
    156226    // file is read and parsed into psMetadata struct "camera".
     
    164234        } else {
    165235            psArgumentRemove(argNum, argc, argv);
    166             readConfig(camera, argv[argNum], "camera");
    167         }
    168     } else {
    169         // XXX: Not sure is this is correct.
    170         *camera = NULL;
     236            readConfig(&config->camera, argv[argNum], "camera");
     237        }
     238    }
     239
     240    // Load the recipes from the camera file, if appropriate
     241    if (! config->recipes && config->camera) {
     242        pmConfigReadRecipes(config);
    171243    }
    172244
     
    183255    // with a call to psTimeInitialize.
    184256    //
    185     psString timeName = psMetadataLookupStr(&mdok, *site, "TIME");
     257    psString timeName = psMetadataLookupStr(&mdok, config->site, "TIME");
    186258    if (mdok && timeName) {
    187259        psTrace(__func__, 7, "Initialising psTime with file %s\n", timeName);
     
    195267    // with a call to psLogSetLevel().
    196268    //
    197     int logLevel = psMetadataLookupS32(&mdok, *site, "LOGLEVEL");
     269    int logLevel = psMetadataLookupS32(&mdok, config->site, "LOGLEVEL");
    198270    if (mdok && logLevel >= 0) {
    199271        psTrace(__func__, 7, "Setting log level to %d\n", logLevel);
     
    206278    // with a call to psLogSetFormat().
    207279    //
    208     psString logFormat = psMetadataLookupStr(&mdok, *site, "LOGFORMAT");
     280    psString logFormat = psMetadataLookupStr(&mdok, config->site, "LOGFORMAT");
    209281    if (mdok && logFormat) {
    210282        psTrace(__func__, 7, "Setting log format to %s\n", logFormat);
     
    218290    // XXX: This is not spec'ed in the SDRS.
    219291    //
    220     psString logDest = psMetadataLookupStr(&mdok, *site, "LOGDEST");
     292    psString logDest = psMetadataLookupStr(&mdok, config->site, "LOGDEST");
    221293    if (mdok && logDest) {
    222         // XXX: Only stdout is provided for now; this section should be
     294        // XXX: Only stdout and stderr are provided for now; this section should be
    223295        // expanded in the future to do files, and perhaps even sockets.
    224         if (strcasecmp(logDest, "STDOUT") != 0) {
    225             psLogMsg(__func__, PS_LOG_WARN, "Only STDOUT is currently supported as a log destination.\n");
     296        int logFD = STDIN_FILENO; // a known invalid value
     297        if (!strcasecmp(logDest, "STDOUT")) {
     298            logFD = STDOUT_FILENO;
     299        }
     300        if (!strcasecmp(logDest, "STDERR")) {
     301            logFD = STDERR_FILENO;
     302        }
     303        if (logFD == STDIN_FILENO) {
     304            psLogMsg(__func__, PS_LOG_WARN, "Only STDERR and STDOUT currently supported as a log destination.\n");
     305            logFD = STDERR_FILENO;
    226306        }
    227307        psTrace(__func__, 7, "Setting log destination to STDOUT.\n");
    228         // XXX: Use something other than "1"
    229         psLogSetDestination(1);
    230     }
    231 
     308        psLogSetDestination(logFD);
     309    }
    232310
    233311    //
     
    236314    // XXX: This is not spec'ed in the SDRS.
    237315    //
    238     psMetadata *trace = psMetadataLookupMD(&mdok, *site, "TRACE");
     316    psMetadata *trace = psMetadataLookupMD(&mdok, config->site, "TRACE");
    239317    if (mdok && trace) {
    240318        psMetadataIterator *traceIter = psMetadataIteratorAlloc(trace, PS_LIST_HEAD, NULL); // Iterator
     
    264342        psLogSetLevel(saveLogLevel);
    265343    }
    266     return(true);
    267 }
    268 
    269 bool pmConfigValidateCamera(
    270     const psMetadata *camera,
     344
     345    return config;
     346}
     347
     348
     349bool pmConfigValidateCameraFormat(
     350    const psMetadata *cameraFormat,
    271351    const psMetadata *header)
    272352{
    273     // Read the rule for that camera
     353    // Read the rule for that camera format
    274354    bool mdStatus = true;
    275     psMetadata *rule = psMetadataLookupMD(&mdStatus, camera, "RULE");
     355    psMetadata *rule = psMetadataLookupMD(&mdStatus, cameraFormat, "RULE");
    276356    if (! mdStatus || ! rule) {
    277357        psLogMsg(__func__, PS_LOG_WARN, "Unable to read rule for camera.\n");
     
    282362    psMetadataIterator *ruleIter = psMetadataIteratorAlloc(rule, PS_LIST_HEAD, NULL); // Rule iterator
    283363    psMetadataItem *ruleItem = NULL;    // Item from the metadata
    284     bool match = true;                  // Does it match?
    285     while ((ruleItem = psMetadataGetAndIncrement(ruleIter)) && match) {
     364    while ((ruleItem = psMetadataGetAndIncrement(ruleIter))) {
    286365        // Check for the existence of the rule
    287366        psMetadataItem *headerItem = psMetadataLookup((psMetadata*)header, ruleItem->name);
    288367        if (! headerItem || headerItem->type != ruleItem->type) {
    289             match = false;
     368            psFree(ruleIter);
     369            return false;
    290370        }
    291371
     
    297377            if (strncmp(ruleItem->data.V, headerItem->data.V,
    298378                        strlen(ruleItem->data.V)) != 0) {
    299                 match = false;
     379                psFree(ruleIter);
     380                return false;
    300381            }
    301382            break;
     
    305386                    ruleItem->data.S32, headerItem->data.S32);
    306387            if (ruleItem->data.S32 != headerItem->data.S32) {
    307                 match = false;
     388                psFree(ruleIter);
     389                return false;
    308390            }
    309391            break;
     
    312394                    ruleItem->data.F32, headerItem->data.F32);
    313395            if (ruleItem->data.F32 != headerItem->data.F32) {
    314                 match = false;
     396                psFree(ruleIter);
     397                return false;
    315398            }
    316399            break;
     
    319402                    ruleItem->data.F64, headerItem->data.F64);
    320403            if (ruleItem->data.F64 != headerItem->data.F64) {
    321                 match = false;
     404                psFree(ruleIter);
     405                return false;
    322406            }
    323407            break;
     
    329413
    330414    psFree(ruleIter);
    331 
    332     return match;
    333 }
    334 
    335 
    336 
    337 // Work out what camera we have, based on the FITS header and a set of
    338 // rules specified in the IPP configuration; return the camera configuration
    339 psMetadata *pmConfigCameraFromHeader(
    340     const psMetadata *ipprc,            // The IPP configuration
    341     const psMetadata *header)           // The FITS header
    342 {
    343     bool mdStatus = false;  // Metadata lookup status
    344     psMetadata *cameras = psMetadataLookupMD(&mdStatus, ipprc, "CAMERAS");
    345     if (! mdStatus) {
    346         psError(PS_ERR_IO, false, "Unable to find CAMERAS in the configuration.\n");
    347         return NULL;
    348     }
    349     psMetadata *winner = NULL;       // The camera configuration whose rule first matches
    350     //  the supplied header
    351     // Iterate over the cameras
    352     psMetadataIterator *iterator = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL);
    353     psMetadataItem *cameraItem = NULL; // Item from the metadata
    354 
    355     while ((cameraItem = psMetadataGetAndIncrement(iterator))) {
    356         // Open the camera information
    357         psTrace(__func__, 3, "Inspecting camera %s (%s)\n", cameraItem->name,
    358                 cameraItem->comment);
    359         psMetadata *camera = NULL; // The camera metadata
    360         if (cameraItem->type == PS_DATA_METADATA) {
    361             camera = psMemIncrRefCounter(cameraItem->data.md);
    362         } else if (cameraItem->type == PS_DATA_STRING) {
    363             psTrace(__func__, 5, "Reading camera configuration for %s...\n", cameraItem->name);
    364             unsigned int badLines = 0;  // Number of bad lines in reading camera configuration
    365             camera = psMetadataConfigParse(NULL, &badLines, cameraItem->data.V, true);
     415    return true;
     416}
     417
     418
     419static bool formatFromHeader(psMetadata **format, // Format to return
     420                             psMetadata *camera, // Camera configuration
     421                             const psMetadata *header, // FITS header
     422                             const char *cameraName // Name of camera
     423                            )
     424{
     425    psMetadata *testFormat;
     426
     427    assert(format);
     428    assert(camera);
     429    assert(header);
     430
     431    bool result = false;                // Did we find the first match?
     432
     433    // Read the list of formats
     434    bool mdok = true;                   // Status of MD lookup
     435    psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS"); // List of formats
     436    if (!mdok || !formats) {
     437        psLogMsg(__func__, PS_LOG_WARN, "Unable to find list of FORMATS in camera %s --- ignored\n",
     438                 cameraName);
     439        return false;
     440    }
     441    // Iterate over the formats
     442    psMetadataIterator *formatsIter = psMetadataIteratorAlloc(formats, PS_LIST_HEAD, NULL);
     443    psMetadataItem *formatsItem = NULL; // Item from formats
     444    while ((formatsItem = psMetadataGetAndIncrement(formatsIter))) {
     445        if (formatsItem->type != PS_DATA_STRING) {
     446            psLogMsg(__func__, PS_LOG_WARN, "In camera %s, camera format %s is not of type STR --- "
     447                     "ignored.\n", cameraName, formatsItem->name);
     448            continue;
     449        }
     450        psTrace(__func__, 5, "Reading camera format for %s...\n", formatsItem->name);
     451        // unsigned int badLines = 0;  // Number of bad lines in reading camera configuration
     452        // Format to test against what we've got
     453
     454        if (!readConfig(&testFormat, formatsItem->data.V, formatsItem->name)) {
     455            psLogMsg(__func__, PS_LOG_WARN, "trouble reading reading camera format %s\n", formatsItem->name);
     456            psFree(testFormat);
     457            continue;
     458        }
     459
     460        # if (0)
     461            psMetadata *testFormat = psMetadataConfigParse(NULL, &badLines, formatsItem->data.V, true);
     462        if (badLines > 0) {
     463            psLogMsg(__func__, PS_LOG_WARN, "%d bad lines encountered while reading camera"
     464                     "format %s\n", badLines, formatsItem->name);
     465        }
     466        # endif
     467
     468        if (pmConfigValidateCameraFormat(testFormat, header)) {
     469            if (!*format) {
     470                psLogMsg(__func__, PS_LOG_INFO, "Camera %s, format %s matches header.\n", cameraName,
     471                         formatsItem->name);
     472                *format = psMemIncrRefCounter(testFormat);
     473                result = true;
     474            } else {
     475                psLogMsg(__func__, PS_LOG_WARN, "Camera %s, format %s also matches header --- ignored.\n",
     476                         cameraName, formatsItem->name);
     477            }
     478        }
     479        psFree(testFormat);
     480    }
     481    psFree(formatsIter);
     482
     483    return result;
     484}
     485
     486// Work out what camera we have, based on the FITS header and a set of rules specified in the IPP
     487// configuration; return the camera configuration and format
     488psMetadata *pmConfigCameraFormatFromHeader(
     489    pmConfig *config,                   // The configuration
     490    const psMetadata *header           // The FITS header
     491)
     492{
     493    psMetadata *format = NULL;          // The winning format
     494    bool mdok = false;                  // Metadata lookup status
     495    psMetadata *testCamera = NULL;
     496
     497    // If we don't know what sort of camera we have, we try all that we know
     498    if (! config->camera) {
     499        psMetadata *cameras = psMetadataLookupMD(&mdok, config->site, "CAMERAS");
     500        if (! mdok) {
     501            psError(PS_ERR_IO, false, "Unable to find CAMERAS in the configuration.\n");
     502            return false;
     503        }
     504
     505        // Iterate over the cameras
     506        psMetadataIterator *camerasIter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL);
     507        psMetadataItem *camerasItem = NULL; // Item from the metadata
     508        while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
     509            // Open the camera information
     510            psTrace(__func__, 3, "Inspecting camera %s (%s)\n", camerasItem->name, camerasItem->comment);
     511            if (camerasItem->type != PS_DATA_STRING) {
     512                psLogMsg(__func__, PS_LOG_WARN, "Camera configuration for %s in CAMERAS is not of type STR "
     513                         "--- ignored.\n", camerasItem->name);
     514                continue;
     515            }
     516
     517            psTrace(__func__, 5, "Reading camera configuration for %s...\n", camerasItem->name);
     518            // unsigned int badLines = 0;  // Number of bad lines in reading camera configuration
     519            // Camera to test against what we've got:
     520
     521            if (!readConfig(&testCamera, camerasItem->data.V, camerasItem->name)) {
     522                psLogMsg(__func__, PS_LOG_WARN, "trouble reading reading camera configuration %s\n", camerasItem->name);
     523                psFree(testCamera);
     524                continue;
     525            }
     526
     527            # if (0)
     528                psMetadata *testCamera = psMetadataConfigParse(NULL, &badLines, camerasItem->data.V, true);
    366529            if (badLines > 0) {
    367530                psLogMsg(__func__, PS_LOG_WARN, "%d bad lines encountered while reading camera"
    368                          "configuration %s\n", badLines, cameraItem->name);
    369             }
    370         }
    371 
    372         if (! camera) {
    373             psLogMsg(__func__, PS_LOG_WARN, "Unable to interpret camera configuration for %s (%s)\n",
    374                      cameraItem->name, cameraItem->comment);
    375             continue;
    376         }
    377 
    378         if (pmConfigValidateCamera(camera, header)) {
    379             if (! winner) {
    380                 // This is the first match
    381                 winner = psMemIncrRefCounter(camera);
    382                 psLogMsg(__func__, PS_LOG_INFO, "FITS header matches camera %s\n",
    383                          cameraItem->name);
    384             } else {
    385                 // We have a duplicate match
    386                 psLogMsg(__func__, PS_LOG_WARN, "Additional camera found that matches the rules: %s\n",
    387                          cameraItem->name);
    388             }
    389         } // Done inspecting the camera
    390 
    391         psFree(camera);
    392 
    393     } // Done looking at all cameras
    394     if (! winner) {
    395         psError(PS_ERR_IO, true, "Unable to find an camera that matches input FITS header!\n");
    396     }
    397 
    398     psFree(iterator);
    399     return winner;
    400 }
    401 
    402 psMetadata *pmConfigRecipeFromCamera(
    403     const psMetadata *camera,
    404     const char *recipeName)
    405 {
    406     PS_ASSERT_PTR_NON_NULL(camera, false);
    407     PS_ASSERT_PTR_NON_NULL(recipeName, false);
    408 
    409     psMetadata *recipe = NULL;          // Recipe to read
     531                         "configuration %s\n", badLines, camerasItem->name);
     532            }
     533            # endif
     534
     535            if (! testCamera) {
     536                psLogMsg(__func__, PS_LOG_WARN, "Unable to interpret camera configuration for %s (%s) --- "
     537                         "ignored\n", camerasItem->name, camerasItem->comment);
     538                continue;
     539            }
     540
     541            if (formatFromHeader(&format, testCamera, header, camerasItem->name)) {
     542                config->camera = psMemIncrRefCounter(testCamera);
     543            }
     544            psFree(testCamera);
     545        } // Done looking at all cameras
     546        psFree(camerasIter);
     547
     548        if (! config->camera) {
     549            psError(PS_ERR_IO, true, "Unable to find a camera that matches input FITS header!\n");
     550            return NULL;
     551        }
     552
     553        // Now we have the camera, we can read the recipes
     554        if (!config->recipes) {
     555            pmConfigReadRecipes(config);
     556        }
     557
     558        return format;
     559    }
     560
     561    // Otherwise, try the specific camera
     562    if (! formatFromHeader(&format, config->camera, header, "specified camera")) {
     563        psError(PS_ERR_IO, true, "Unable to find a format with the specified camera that matches the "
     564                "given header.\n");
     565        return NULL;
     566    }
     567    return format;
     568}
     569
     570bool pmConfigReadRecipes(
     571    pmConfig *config
     572)
     573{
     574    PS_ASSERT_PTR_NON_NULL(config, false);
     575    PS_ASSERT_PTR_NON_NULL(config->camera, false);
     576
     577    if (!config->recipes) {
     578        config->recipes = psMetadataAlloc();
     579    }
     580
    410581    bool mdok = true;                   // Status of MD lookup
    411     psMetadata *recipes = psMetadataLookupMD(&mdok, camera, "RECIPES"); // The list of recipes
     582    psMetadata *recipes = psMetadataLookupMD(&mdok, config->camera, "RECIPES"); // The list of recipes
    412583    if (! mdok || ! recipes) {
    413584        psLogMsg(__func__, PS_LOG_WARN, "RECIPES in the camera configuration file is not of type METADATA\n");
    414     } else {
    415         psString recipeFileName = psMetadataLookupStr(&mdok, recipes, recipeName);
    416         (void)readConfig(&recipe, recipeFileName, "recipe");
    417     }
    418 
    419     return recipe;
     585        return false;
     586    }
     587    // Go through the recipes and load each one
     588    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
     589    psMetadataItem *recipesItem = NULL; // Item from iteration
     590    while ((recipesItem = psMetadataGetAndIncrement(recipesIter))) {
     591        if (recipesItem->type != PS_DATA_STRING) {
     592            psLogMsg(__func__, PS_LOG_WARN, "Filename for recipe %s isn't of type STR --- ignored.\n",
     593                     recipesItem->name);
     594            continue;
     595        }
     596
     597        psMetadata *recipe = NULL;      // Recipe from file
     598        if (readConfig(&recipe, recipesItem->data.V, "recipe")) {
     599            psMetadataAdd(config->recipes, PS_LIST_TAIL, recipesItem->name,
     600                          PS_DATA_METADATA | PS_META_REPLACE, recipesItem->comment, recipe);
     601        }
     602        psFree(recipe);                 // Drop reference
     603    }
     604    psFree(recipesIter);
     605
     606    return true;
    420607}
    421608
     
    423610pmConfigDB(*site)
    424611 
     612XXX: this should allow the option of having NO database server, if chosen by config
    425613XXX: What should we use for the Database namespace in the call to psDBInit()?
    426614This is currently NULL.
    427615 *****************************************************************************/
    428616
    429 #ifdef OMIT_PSDB
    430 psDB *pmConfigDB(psMetadata *site)
    431 {
    432     psError(PS_ERR_UNKNOWN, true, "pslib was built without psDB support");
    433     return NULL;
    434 }
    435 #else
    436617psDB *pmConfigDB(
    437618    psMetadata *site)
     
    442623    psBool mdStatus03 = false;
    443624
     625    // XXX leaky strings
    444626    psString dbServer = psMetadataLookupStr(&mdStatus01, site, "DBSERVER");
    445627    psString dbUsername = psMetadataLookupStr(&mdStatus02, site, "DBUSER");
    446628    psString dbPassword = psMetadataLookupStr(&mdStatus03, site, "DBPASSWORD");
    447629    psString dbName = psMetadataLookupStr(&mdStatus01, site, "DBNAME");
    448 
    449630    if (!(mdStatus01 & mdStatus02 & mdStatus03)) {
    450631        psLogMsg(__func__, PS_LOG_WARN, "Could not determine database server name, userID, and password from site metadata.\n");
    451         return NULL;
    452     }
    453 
    454 
    455 
    456     psDB *dbh = psDBInit(dbServer, dbUsername, dbPassword, dbName);
    457     psFree(dbServer);
    458     psFree(dbUsername);
    459     psFree(dbPassword);
    460     psFree(dbName);
    461 
    462     if (!dbh) {
    463         psError(PS_ERR_UNKNOWN, false, "database connection failed");
    464     }
    465 
    466     return dbh;
    467 }
    468 #endif
     632        return(NULL);
     633    }
     634
     635    return(psDBInit(dbServer, dbUsername, dbPassword, dbName));
     636}
     637
     638
     639bool pmConfigConformHeader(psMetadata *header, // Header to conform
     640                           const psMetadata *format // Camera format
     641                          )
     642{
     643    bool mdok = true;                   // Status of MD lookup
     644    psMetadata *rules = psMetadataLookupMD(&mdok, format, "RULE"); // How to identify this format
     645    if (!mdok || !rules) {
     646        psError(PS_ERR_IO, false, "Unable to find RULE in camer format.\n");
     647        return false;
     648    }
     649
     650    psMetadataIterator *rulesIter = psMetadataIteratorAlloc(rules, PS_LIST_HEAD, NULL); // Iterator for rules
     651    psMetadataItem *rulesItem = NULL;   // Item from iteration
     652    while ((rulesItem = psMetadataGetAndIncrement(rulesIter))) {
     653        psMetadataItem *newItem = psMetadataItemCopy(rulesItem); // Copy of item
     654        psMetadataAddItem(header, newItem, PS_LIST_TAIL, PS_META_REPLACE);
     655        psFree(newItem);                // Drop reference
     656    }
     657    psFree(rulesIter);
     658
     659    return true;
     660}
     661
     662// given the 'file' and 'list' words, find the arguments associated with these words
     663// and interpret them as lists of files.  return an array of the resulting filenames.
     664psArray *pmConfigFileSets (int *argc, char **argv, char *file, char *list)
     665{
     666
     667    int Narg;
     668
     669    // we load all input files onto a psArray, to be parsed later
     670    psArray *input = psArrayAlloc (16);
     671    input->n = 0;
     672
     673    // load the list of filenames the supplied file (may be a glob: "file*.fits")
     674    if ((Narg = psArgumentGet (*argc, argv, file))) {
     675        glob_t globList;
     676        psArgumentRemove (Narg, argc, argv);
     677        globList.gl_offs = 0;
     678        glob (argv[Narg], 0, NULL, &globList);
     679        for (int i = 0; i < globList.gl_pathc; i++) {
     680            char *filename = psStringCopy (globList.gl_pathv[i]);
     681            psArrayAdd (input, 16, filename);
     682            psFree (filename);
     683        }
     684        psArgumentRemove (Narg, argc, argv);
     685    }
     686
     687    // load the list from the supplied text file
     688    if ((Narg = psArgumentGet (*argc, argv, list))) {
     689        int nItems;
     690        char line[1024]; // XXX limits the list lines to 1024 chars
     691        char word[1024];
     692        char *filename;
     693
     694        psArgumentRemove (Narg, argc, argv);
     695        FILE *f = fopen (argv[Narg], "r");
     696        if (f == NULL) {
     697            psAbort ("psphot", "unable to open specified list file");
     698        }
     699        while (fgets (line, 1024, f) != NULL) {
     700            nItems = sscanf (line, "%s", word);
     701            switch (nItems) {
     702            case 0:
     703                break;
     704            case 1:
     705                filename = psStringCopy (word);
     706                psArrayAdd (input, 16, filename);
     707                psFree (filename);
     708                break;
     709            default:
     710                // rigid format, no comments allowed?
     711                psAbort ("pmConfig", "error parsing input list file");
     712                break;
     713            }
     714        }
     715        psArgumentRemove (Narg, argc, argv);
     716    }
     717
     718    return input;
     719}
     720
     721bool pmConfigFileSetsMD (psMetadata *metadata, int *argc, char **argv, char *name, char *file, char *list)
     722{
     723
     724    psArray *files = pmConfigFileSets (argc, argv, file, list);
     725    if (files->n == 0) {
     726        psFree (files);
     727        return false;
     728    }
     729
     730    psMetadataAddPtr (metadata, PS_LIST_TAIL, name,  PS_DATA_ARRAY, "", files);
     731    psFree (files);
     732    return true;
     733}
  • trunk/psModules/src/config/pmConfig.h

    r6872 r6873  
    33 *  @author PAP, IfA
    44 *
    5  *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
    6  *  @date $Date: 2006-04-17 18:01:05 $
     5 *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
     6 *  @date $Date: 2006-04-17 18:10:08 $
    77 *
    88 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/detrend/pmFlatField.c

    r6872 r6873  
    2424 *  @author Ross Harman, MHPCC
    2525 *
    26  *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
    27  *  @date $Date: 2006-04-17 18:01:05 $
     26 *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
     27 *  @date $Date: 2006-04-17 18:10:08 $
    2828 *
    2929 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/detrend/pmMaskBadPixels.c

    r6872 r6873  
    2424 *  @author Ross Harman, MHPCC
    2525 *
    26  *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
    27  *  @date $Date: 2006-04-17 18:01:05 $
     26 *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
     27 *  @date $Date: 2006-04-17 18:10:08 $
    2828 *
    2929 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/detrend/pmMaskBadPixelsErrors.h

    r6872 r6873  
    1212 *  @author Ross Harman, MHPCC
    1313 *
    14  *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
    15  *  @date $Date: 2006-04-17 18:01:05 $
     14 *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
     15 *  @date $Date: 2006-04-17 18:10:08 $
    1616 *
    1717 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/detrend/pmNonLinear.c

    r6872 r6873  
    1010 *  @author GLG, MHPCC
    1111 *
    12  *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
    13  *  @date $Date: 2006-04-17 18:01:05 $
     12 *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
     13 *  @date $Date: 2006-04-17 18:10:08 $
    1414 *
    1515 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/imcombine/pmReadoutCombine.c

    r6511 r6873  
    55 *  @author GLG, MHPCC
    66 *
    7  *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
    8  *  @date $Date: 2006-03-04 01:01:33 $
     7 *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
     8 *  @date $Date: 2006-04-17 18:10:08 $
    99 *
    1010 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
     
    3636}
    3737
     38static void pmCombineParamsFree (pmCombineParams *params)
     39{
     40
     41    if (params == NULL)
     42        return;
     43
     44    psFree (params->stats);
     45    return;
     46}
     47
     48pmCombineParams *pmCombineParamsAlloc (psStatsOptions statsOptions)
     49{
     50
     51    pmCombineParams *params = psAlloc (sizeof(pmCombineParams));
     52    psMemSetDeallocator(params, (psFreeFunc) pmCombineParamsFree);
     53
     54    params->stats = psStatsAlloc (statsOptions);
     55    params->maskVal = 0;
     56    params->fracHigh = 0.25;
     57    params->fracHigh = 0.25;
     58    params->nKeep = 3;
     59
     60    return (params);
     61}
     62
    3863/******************************************************************************
    3964XXX: Must add support for S16 and S32 types.  F32 currently supported.
    4065 *****************************************************************************/
    4166psImage *pmReadoutCombine(psImage *output,
    42                           const psList *inputs,
    43                           psCombineParams *params,
     67                          const psArray *inputs,
    4468                          const psVector *zero,
    4569                          const psVector *scale,
     70                          pmCombineParams *params,
    4671                          bool applyZeroScale,
    4772                          psF32 gain,
    4873                          psF32 readnoise)
     74{
     75    PS_ASSERT_PTR_NON_NULL(inputs, NULL);
     76    PS_ASSERT_PTR_NON_NULL(params, NULL);
     77    PS_ASSERT_PTR_NON_NULL(params->stats, NULL);
     78    if (zero != NULL) {
     79        PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL);
     80        //        PS_ASSERT_VECTOR_TYPE_S16_S32_F32(zero, NULL);
     81    }
     82    if (scale != NULL) {
     83        PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL);
     84        //        PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL);
     85    }
     86    if ((zero != NULL) && (scale != NULL)) {
     87        PS_ASSERT_VECTOR_TYPE_EQUAL(zero, scale, NULL);
     88        // PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL);
     89    }
     90
     91    psStats *stats = params->stats;
     92    psS32 maxInputCols = 0;
     93    psS32 maxInputRows = 0;
     94    psS32 minInputCols = PS_MAX_S32;
     95    psS32 minInputRows = PS_MAX_S32;
     96    pmReadout *tmpReadout = NULL;
     97    psS32 tmpI;
     98    psElemType outputType = PS_TYPE_F32;
     99
     100    if (DetermineNumBits(stats->options) != 1) {
     101        psError(PS_ERR_UNKNOWN, true,
     102                "Multiple statistical options have been requested.  Returning NULL.\n");
     103        return(NULL);
     104    }
     105
     106    // We step through each readout in the input image list.  If any readout is
     107    // NULL, empty, or has the wrong type, we generate an error and return
     108    // NULL.  We determine how big of an output image is needed to combine
     109    // these input images.  We do this by taking the
     110    //     max(readout->col0 + readout->numCols + image->col0 + image->numCols)
     111    //     max(readout->row0 + readout->numRows + image->row0 + image->numRows)
     112    //
     113    for (int i = 0; i < inputs->n; i++) {
     114        tmpReadout = inputs->data[i];
     115        PS_ASSERT_READOUT_NON_NULL(tmpReadout, output);
     116        PS_ASSERT_READOUT_NON_EMPTY(tmpReadout, output);
     117        PS_ASSERT_READOUT_TYPE(tmpReadout, PS_TYPE_F32, output);
     118
     119        minInputRows = PS_MIN(minInputRows, (tmpReadout->row0 + tmpReadout->image->row0));
     120        tmpI = tmpReadout->row0 +
     121               tmpReadout->image->row0 +
     122               tmpReadout->image->numRows;
     123        maxInputRows = PS_MAX(maxInputRows, tmpI);
     124
     125        minInputCols = PS_MIN(minInputCols, (tmpReadout->col0 + tmpReadout->image->col0));
     126        tmpI = tmpReadout->col0 +
     127               tmpReadout->image->col0 +
     128               tmpReadout->image->numCols;
     129        maxInputCols = PS_MAX(maxInputCols, tmpI);
     130    }
     131
     132    // We ensure that the zero vector is of the proper size.
     133    if (zero != NULL) {
     134        PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL);
     135        if (zero->n < inputs->n) {
     136            psError(PS_ERR_UNKNOWN, true, "zero vector has incorrect size (%d).  Returning NULL.\n", zero->n);
     137            return(NULL);
     138        } else if (zero->n > inputs->n) {
     139            // XXX EAM : abort on this condition? is probably an error
     140            psLogMsg(__func__, PS_LOG_WARN,
     141                     "WARNING: the zero vector too many elements (%d)\n", zero->n);
     142        }
     143    }
     144
     145    // We ensure that the scale vector is of the proper size.
     146    if (scale != NULL) {
     147        PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL);
     148        if (scale->n < inputs->n) {
     149            psError(PS_ERR_UNKNOWN, true, "scale vector has incorrect size (%d).  Returning NULL.\n", scale->n);
     150            return(NULL);
     151        } else if (scale->n > inputs->n) {
     152            // XXX EAM : abort on this condition? is probably an error
     153            psLogMsg(__func__, PS_LOG_WARN,
     154                     "WARNING: the scale vector has too many elements (%d)\n", scale->n);
     155        }
     156    }
     157
     158    // At this point, the following variables have been computed:
     159    // maxInputRows: the largest input row value, in output image space.
     160    // maxInputCols: the largest input column value, in output image space.
     161    // minInputRows: the smallest input row value, in output image space.
     162    // minInputCols: the smallest input column value, in output image space.
     163    //
     164    if (output == NULL) {
     165        output = psImageAlloc(maxInputCols-minInputCols, maxInputRows-minInputRows, outputType);
     166        *(psS32 *) &(output->col0) = minInputCols;
     167        *(psS32 *) &(output->row0) = minInputRows;
     168    } else {
     169
     170        // XXX EAM : recycle the existing output image?  why would we care about the existing pixels?
     171        PS_ASSERT_IMAGE_TYPE(output, PS_TYPE_F32, NULL);
     172        if (((output->col0 + output->numCols) < maxInputCols) ||
     173                ((output->row0 + output->numRows) < maxInputRows)) {
     174            psError(PS_ERR_UNKNOWN, true,
     175                    "Output image (%d, %d) is too small to hold combined images.  Returning NULL.\n",
     176                    output->row0 + output->numRows,
     177                    output->col0 + output->numCols);
     178            return(NULL);
     179        }
     180
     181        // reset output origin using logic of above
     182        *(psS32 *) &(output->col0) = minInputCols;
     183        *(psS32 *) &(output->row0) = minInputRows;
     184    }
     185
     186    psVector *tmpPixels     = psVectorAlloc(inputs->n, PS_TYPE_F32);
     187    psVector *tmpPixelsKeep = psVectorAlloc(inputs->n, PS_TYPE_F32);
     188    psVector *outRowLower   = psVectorAlloc(inputs->n, PS_TYPE_U32);
     189    psVector *outRowUpper   = psVectorAlloc(inputs->n, PS_TYPE_U32);
     190    psVector *outColLower   = psVectorAlloc(inputs->n, PS_TYPE_U32);
     191    psVector *outColUpper   = psVectorAlloc(inputs->n, PS_TYPE_U32);
     192
     193    // For each input readout, we store the min/max pixel indices for that readout, in detector coordinates,
     194    // in the psVectors (outRowLower, outColLower, outRowUpper, outColUpper).
     195    for (int i = 0; i < inputs->n; i++) {
     196        tmpReadout = (pmReadout *) inputs->data[i];
     197        outRowLower->data.U32[i] = tmpReadout->row0 + tmpReadout->image->row0;
     198        outColLower->data.U32[i] = tmpReadout->col0 + tmpReadout->image->col0;
     199        outRowUpper->data.U32[i] = tmpReadout->row0 +
     200                                   tmpReadout->image->row0 +
     201                                   tmpReadout->image->numRows;
     202        outColUpper->data.U32[i] = tmpReadout->col0 +
     203                                   tmpReadout->image->col0 +
     204                                   tmpReadout->image->numCols;
     205    }
     206
     207    // We loop through each pixel in the output image.  We loop through each
     208    // input readout.  We determine if that output pixel is contained in the
     209    // image from that readout.  If so, we save it in psVector tmpPixels.
     210    // If not, we set a mask for that element in tmpPixels.  Then, we mask off
     211    // pixels not between fracLow and fracHigh.  Then we call the vector
     212    // stats routine on those pixels/mask.  Then we set the output pixel value
     213    // to the result of the stats call.
     214
     215    int nx, ny;
     216    int nKeep, nMin;
     217    float keepFrac = 1.0 - params->fracLow - params->fracHigh;
     218    float value = 0;
     219    psF32 *saveVector = tmpPixelsKeep->data.F32;
     220
     221    for (int j = output->row0; j < (output->row0 + output->numRows) ; j++) {
     222        if (j % 10 == 0)
     223            fprintf (stderr, ".");
     224        for (int i = output->col0; i < (output->col0 + output->numCols) ; i++) {
     225            int nPix = 0;
     226            for (int r = 0; r < inputs->n; r++) {
     227                tmpReadout = (pmReadout *) inputs->data[r];
     228
     229                // psTrace (__func__, 6, "[%d][%d]: [%d][%d] to [%d][%d]\n", i, j, outColLower->data.U32[r], outRowLower->data.U32[r], outColUpper->data.U32[r], outRowUpper->data.U32[r]);
     230                if (i <  outColLower->data.U32[r])
     231                    continue;
     232                if (i >= outColUpper->data.U32[r])
     233                    continue;
     234                if (j <  outRowLower->data.U32[r])
     235                    continue;
     236                if (j >= outRowUpper->data.U32[r])
     237                    continue;
     238
     239                nx = i - (tmpReadout->col0 + tmpReadout->image->col0);
     240                ny = j - (tmpReadout->row0 + tmpReadout->image->row0);
     241
     242                if (tmpReadout->mask != NULL) {
     243                    if (tmpReadout->mask->data.U8[ny][nx] && params->maskVal)
     244                        continue;
     245                }
     246
     247                tmpPixels->data.F32[nPix] = tmpReadout->image->data.F32[ny][nx];
     248                // psTrace (__func__, 6, "readout[%d], image [%d][%d] is %f\n", r, i, j, tmpPixels->data.F32[nPix]);
     249                nPix ++;
     250            }
     251            tmpPixels->n = nPix;
     252
     253            // are there enough valid pixels to apply fracLow,fracHigh?
     254            nKeep = nPix * keepFrac;
     255            if (nKeep >= params->nKeep) {
     256                psVectorSort (tmpPixels, tmpPixels);
     257                nMin = nPix * params->fracLow;
     258                tmpPixelsKeep->data.F32 = &tmpPixels->data.F32[nMin];
     259                tmpPixelsKeep->n = nKeep;
     260            } else {
     261                tmpPixelsKeep->data.F32 = tmpPixels->data.F32;
     262                tmpPixelsKeep->n = nPix;
     263            }
     264
     265            // tmpPixelsKeep is already sorted.  sample mean and median are very easy
     266            if (stats->options & PS_STAT_SAMPLE_MEAN) {
     267                value = 0;
     268                for (int r = 0; r < tmpPixelsKeep->n; r++) {
     269                    value += tmpPixelsKeep->data.F32[r];
     270                }
     271                if (tmpPixelsKeep->n == 0) {
     272                    value = 0;
     273                } else {
     274                    value = value / tmpPixelsKeep->n;
     275                }
     276            }
     277            if (stats->options & PS_STAT_SAMPLE_MEDIAN) {
     278                int r = tmpPixelsKeep->n / 2;
     279                if (tmpPixelsKeep->n == 0) {
     280                    value = 0;
     281                    goto got_value;
     282                }
     283                if (tmpPixelsKeep->n % 2 == 1) {
     284                    int r = 0.5*tmpPixelsKeep->n;
     285                    value = tmpPixelsKeep->data.F32[r];
     286                    goto got_value;
     287                }
     288                if (tmpPixelsKeep->n % 2 == 0) {
     289                    value = 0.5*(tmpPixelsKeep->data.F32[r] +
     290                                 tmpPixelsKeep->data.F32[r-1]);
     291                    goto got_value;
     292                }
     293            }
     294got_value:
     295            output->data.F32[j-output->row0][i-output->col0] = value;
     296        }
     297    }
     298    tmpPixelsKeep->data.F32 = saveVector;
     299
     300    psFree(tmpPixels);
     301    psFree(tmpPixelsKeep);
     302    psFree(outRowLower);
     303    psFree(outRowUpper);
     304    psFree(outColLower);
     305    psFree(outColUpper);
     306
     307    return(output);
     308}
     309
     310/******************************************************************************
     311XXX: Must add support for S16 and S32 types.  F32 currently supported.
     312 *****************************************************************************/
     313psImage *pmReadoutCombine_OLD(psImage *output,
     314                              const psList *inputs,
     315                              pmCombineParams *params,
     316                              const psVector *zero,
     317                              const psVector *scale,
     318                              bool applyZeroScale,
     319                              psF32 gain,
     320                              psF32 readnoise)
    49321{
    50322    PS_ASSERT_PTR_NON_NULL(inputs, NULL);
     
    418690    psRegion minRegion;
    419691    psRegion maxRegion;
    420     psStats *minStats = psStatsAlloc(PS_STAT_FITTED_MEAN);
    421     psStats *maxStats = psStatsAlloc(PS_STAT_FITTED_MEAN);
     692    psStats *minStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
     693    psStats *maxStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
    422694    psStats *diffStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
    423695    psVector *diffs = psVectorAlloc(fringePoints->n, PS_TYPE_F32);
     
    454726        }
    455727
    456         fp->midValue = 0.5 * (maxStats->fittedMean + minStats->fittedMean);
    457         fp->delta = maxStats->fittedMean - minStats->fittedMean;
     728        fp->midValue = 0.5 * (maxStats->robustMedian + minStats->robustMedian);
     729        fp->delta = maxStats->robustMedian - minStats->robustMedian;
    458730        diffs->data.F32[i] = fp->delta;
    459731    }
     
    464736    psFree(diffs);
    465737    if (diffStats == NULL) {
    466         psError(PS_ERR_UNKNOWN, true, "Could not determine fitted median of the differences.\n");
     738        psError(PS_ERR_UNKNOWN, true, "Could not determine robust median of the differences.\n");
    467739        return(NULL);
    468740    }
    469741    return(diffStats);
    470742}
    471 
    472 
    473743
    474744/**
  • trunk/psModules/src/imcombine/pmReadoutCombine.h

    r6872 r6873  
    55 *  @author GLG, MHPCC
    66 *
    7  *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
    8  *  @date $Date: 2006-04-17 18:01:05 $
     7 *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
     8 *  @date $Date: 2006-04-17 18:10:08 $
    99 *
    1010 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/imsubtract/pmSubtractBias.c

    r6511 r6873  
     1//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     2// XXX WARNING: I have completely replaced this file with an OLD VERSION (that works) instead of the
     3// one that was being worked on.
     4//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     5
    16/** @file  pmSubtractBias.c
    27 *
     
    611 *  @author GLG, MHPCC
    712 *
    8  *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
    9  *  @date $Date: 2006-03-04 01:01:33 $
     13 *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
     14 *  @date $Date: 2006-04-17 18:10:08 $
    1015 *
    1116 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
    1217 *
    1318 */
    14 /*****************************************************************************/
    15 /* INCLUDE FILES                                                             */
    16 /*****************************************************************************/
    17 #include <stdio.h>
    18 #include <math.h>
    19 #include <string.h>
    20 #include "pslib.h"
     19
    2120#if HAVE_CONFIG_H
    2221#include <config.h>
    2322#endif
     23
     24#include <assert.h>
    2425#include "pmSubtractBias.h"
    2526
    26 /*****************************************************************************/
    27 /* DEFINE STATEMENTS                                                         */
    28 /*****************************************************************************/
     27#define PM_SUBTRACT_BIAS_POLYNOMIAL_ORDER 2
     28#define PM_SUBTRACT_BIAS_SPLINE_ORDER 3
     29
     30
     31#define MAX(a,b) ((a) > (b) ? (a) : (b))
     32#define MIN(a,b) ((a) < (b) ? (a) : (b))
     33
     34
    2935// XXX: put these in psConstants.h
    30 void PS_POLY1D_PRINT(
    31     psPolynomial1D *poly)
     36void PS_POLY1D_PRINT(psPolynomial1D *poly)
    3237{
    3338    printf("-------------- PS_POLY1D_PRINT() --------------\n");
     
    5762}\
    5863
    59 /*****************************************************************************/
    60 /* TYPE DEFINITIONS                                                          */
    61 /*****************************************************************************/
    62 
    63 /*****************************************************************************/
    64 /* GLOBAL VARIABLES                                                          */
    65 /*****************************************************************************/
    66 psS32 currentId = 0;                // XXX: remove
    67 psS32 memLeaks = 0;                 // XXX: remove
    68 //PRINT_MEMLEAKS(8); XXX
    69 /*****************************************************************************/
    70 /* FILE STATIC VARIABLES                                                     */
    71 /*****************************************************************************/
    72 
    73 /*****************************************************************************/
    74 /* FUNCTION IMPLEMENTATION - LOCAL                                           */
    75 /*****************************************************************************/
     64
     65void overscanOptionsFree(pmOverscanOptions *options)
     66{
     67    psFree(options->stat);
     68    psFree(options->poly);
     69    psFree(options->spline);
     70}
     71
     72pmOverscanOptions *pmOverscanOptionsAlloc(bool single, pmFit fitType, unsigned int order, psStats *stat)
     73{
     74    pmOverscanOptions *opts = psAlloc(sizeof(pmOverscanOptions));
     75    psMemSetDeallocator(opts, (psFreeFunc)overscanOptionsFree);
     76
     77    // Inputs
     78    opts->single = single;
     79    opts->fitType = fitType;
     80    opts->order = order;
     81    opts->stat = psMemIncrRefCounter(stat);
     82
     83    // Outputs
     84    opts->poly = NULL;
     85    opts->spline = NULL;
     86
     87    return opts;
     88}
     89
    7690
    7791/******************************************************************************
    78 psSubtractFrame(): this routine will take as input the pmReadout for the input
    79 image and a pmReadout for the bias image.  The bias image is subtracted in
    80 place from the input image.  We assume that sizes and types are checked
    81 elsewhere.
    82  
    83 XXX: Verify that the image and readout offsets are being used the right way.
    84  
    85 XXX: Ensure that it does the correct thing with image size.
     92psSubtractFrame(): this routine will take as input a readout for the input
     93image and a readout for the bias image.  The bias image is subtracted in
     94place from the input image.
    8695*****************************************************************************/
    87 static pmReadout *SubtractFrame(
    88     pmReadout *in,
    89     const pmReadout *bias)
    90 {
    91     // XXX: When did the ->row0 and ->col0 offsets get coded?
    92     for (psS32 i=0;i<in->image->numRows;i++) {
    93         for (psS32 j=0;j<in->image->numCols;j++) {
    94             in->image->data.F32[i][j]-=
    95                 bias->image->data.F32[i+in->row0-bias->row0][j+in->col0-bias->col0];
    96 
    97             if ((in->mask != NULL) && (bias->mask != NULL)) {
    98                 (in->mask->data.U8[i][j])|=
    99                     bias->mask->data.U8[i+in->row0-bias->row0][j+in->col0-bias->col0];
     96static bool SubtractFrame(pmReadout *in,// Input readout
     97                          const pmReadout *sub, // Readout to be subtracted from input
     98                          float scale   // Scale to apply before subtracting
     99                         )
     100{
     101    assert(in);
     102    assert(sub);
     103
     104    psImage *inImage  = in->image;      // The input image
     105    psImage *inMask   = in->mask;       // The input mask
     106    psImage *subImage = sub->image;     // The image to be subtracted
     107    psImage *subMask  = sub->mask;      // The mask for the subtraction image
     108
     109    // Offsets of the cells
     110    int x0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.X0");
     111    int y0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.Y0");
     112    int x0sub = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.X0");
     113    int y0sub = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.Y0");
     114
     115    if ((inImage->numCols + x0in - x0sub) > subImage->numCols) {
     116        psError(PS_ERR_UNKNOWN, true, "Image does not have enough columns for subtraction.\n");
     117        return false;
     118    }
     119    if ((inImage->numRows + y0in - y0sub) > subImage->numRows) {
     120        psError(PS_ERR_UNKNOWN, true, "Image does not have enough rows for subtraction.\n");
     121        return false;
     122    }
     123
     124    if (scale == 1.0) {
     125        for (int i = 0; i < inImage->numRows; i++) {
     126            for (int j = 0; j < inImage->numCols; j++) {
     127                inImage->data.F32[i][j] -= subImage->data.F32[i+y0in-y0sub][j+x0in-x0sub];
     128                if (inMask && subMask) {
     129                    inMask->data.U8[i][j] |= subMask->data.U8[i+y0in-y0sub][j+x0in-x0sub];
     130                }
    100131            }
    101132        }
    102     }
    103 
    104     return(in);
    105 }
    106 
    107 
    108 /******************************************************************************
    109 psSubtractDarkFrame(): this routine will take as input the pmReadout for the
    110 input image and a pmReadout for the dark image.  The dark image is scaled and
    111 subtracted in place from the input image.
    112  
    113 XXX: Verify that the image and readout offsets are being used the right way.
    114  
    115 XXX: Ensure that it does the correct thing with image size.
    116 *****************************************************************************/
    117 static pmReadout *SubtractDarkFrame(
    118     pmReadout *in,
    119     const pmReadout *dark,
    120     psF32 scale)
    121 {
    122     // XXX: When did the ->row0 and ->col0 offsets get coded?
    123     if (fabs(scale) > FLT_EPSILON) {
    124         for (psS32 i=0;i<in->image->numRows;i++) {
    125             for (psS32 j=0;j<in->image->numCols;j++) {
    126                 in->image->data.F32[i][j]-=
    127                     (scale * dark->image->data.F32[i+in->row0-dark->row0][j+in->col0-dark->col0]);
    128 
    129                 if ((in->mask != NULL) && (dark->mask != NULL)) {
    130                     (in->mask->data.U8[i][j])|=
    131                         dark->mask->data.U8[i+in->row0-dark->row0][j+in->col0-dark->col0];
     133    } else {
     134        for (int i = 0; i < inImage->numRows; i++) {
     135            for (int j = 0; j < inImage->numCols; j++) {
     136                inImage->data.F32[i][j] -= subImage->data.F32[i+y0in-y0sub][j+x0in-x0sub] * scale;
     137                if (inMask && subMask) {
     138                    inMask->data.U8[i][j] |= subMask->data.U8[i+y0in-y0sub][j+x0in-x0sub];
    132139                }
    133140            }
    134141        }
    135     } else {
    136         for (psS32 i=0;i<in->image->numRows;i++) {
    137             for (psS32 j=0;j<in->image->numCols;j++) {
    138                 in->image->data.F32[i][j]-=
    139                     dark->image->data.F32[i+in->row0-dark->row0][j+in->col0-dark->col0];
    140 
    141                 if ((in->mask != NULL) && (dark->mask != NULL)) {
    142                     (in->mask->data.U8[i][j])|=
    143                         dark->mask->data.U8[i+in->row0-dark->row0][j+in->col0-dark->col0];
    144                 }
    145             }
    146         }
    147     }
    148 
    149     return(in);
    150 }
    151 
     142    }
     143
     144    return true;
     145}
     146
     147
     148#if 0
    152149/******************************************************************************
    153150ImageSubtractScalar(): subtract a scalar from the input image.
    154151 
    155 XXX: Is there a psLib function for this?
     152XXX: Use a psLib function for this.
     153 
     154XXX: This should
    156155 *****************************************************************************/
    157 static psImage *ImageSubtractScalar(
    158     psImage *image,
    159     psF32 scalar)
     156static psImage *ImageSubtractScalar(psImage *image,
     157                                    psF32 scalar)
    160158{
    161159    for (psS32 i=0;i<image->numRows;i++) {
     
    166164    return(image);
    167165}
     166#endif
    168167
    169168/******************************************************************************
     
    179178    psStatsOptions opt = 0;
    180179
    181     /*
    182         if (stat->options & PS_STAT_ROBUST_MODE) {
    183             if (numOptions == 0) {
    184                 opt = PS_STAT_ROBUST_MODE;
    185             }
    186             numOptions++;
    187         }
    188     */
    189180    if (stat->options & PS_STAT_ROBUST_MEDIAN) {
    190181        if (numOptions == 0) {
     
    194185    }
    195186
    196     if (stat->options & PS_STAT_FITTED_MEAN) {
    197         if (numOptions == 0) {
    198             opt = PS_STAT_FITTED_MEAN;
    199         }
    200         numOptions++;
    201     }
    202 
    203187    if (stat->options & PS_STAT_CLIPPED_MEAN) {
    204188        if (numOptions == 0) {
     
    222206
    223207    if (numOptions == 0) {
    224         psError(PS_ERR_UNKNOWN,true, "No allowable statistics options have been specified.\n");
     208        psError(PS_ERR_UNKNOWN,true, "No statistics options have been specified.\n");
    225209    }
    226210    if (numOptions != 1) {
     
    231215}
    232216
    233 /******************************************************************************
    234 Polynomial1DCopy(): This private function copies the members of the existing
    235 psPolynomial1D "in" into the existing psPolynomial1D "out".  The previous
    236 members of the existing psPolynomial1D "out" are psFree'ed.
    237  *****************************************************************************/
    238 static psBool Polynomial1DCopy(
    239     psPolynomial1D *out,
    240     psPolynomial1D *in)
    241 {
    242     psFree(out->coeff);
    243     psFree(out->coeffErr);
    244     psFree(out->mask);
    245 
    246     out->type = in->type;
    247     out->nX = in->nX;
    248 
    249     out->coeff = (psF64 *) psAlloc((in->nX + 1) * sizeof(psF64));
    250     // XXX: use memcpy
    251     for (psS32 i = 0 ; i < (in->nX + 1) ; i++) {
    252         out->coeff[i] = in->coeff[i];
    253     }
    254 
    255     out->coeffErr = (psF64 *) psAlloc((in->nX + 1) * sizeof(psF64));
    256     // XXX: use memcpy
    257     for (psS32 i = 0 ; i < (in->nX + 1) ; i++) {
    258         out->coeffErr[i] = in->coeffErr[i];
    259     }
    260 
    261     out->mask = (psMaskType *) psAlloc((in->nX + 1) * sizeof(psMaskType));
    262     // XXX: use memcpy
    263     for (psS32 i = 0 ; i < (in->nX + 1) ; i++) {
    264         out->mask[i] = in->mask[i];
    265     }
    266 
    267     return(true);
    268 }
    269 
    270 /******************************************************************************
    271 Polynomial1DDup(): This private function duplicates and then returns the input
    272 psPolynomial1D "in".
    273  *****************************************************************************/
    274 static psPolynomial1D *Polynomial1DDup(
    275     psPolynomial1D *in)
    276 {
    277     psPolynomial1D *out = psPolynomial1DAlloc(in->type, in->nX);
    278     Polynomial1DCopy(out, in);
    279     return(out);
    280 }
    281 
    282 
    283 /******************************************************************************
    284 SplineCopy(): This private function copies the members of the existing
    285 psSpline in into the existing psSpline out.
    286  *****************************************************************************/
    287 static psBool SplineCopy(
    288     psSpline1D *out,
    289     psSpline1D *in)
    290 {
    291     PS_ASSERT_PTR_NON_NULL(out, false);
    292     PS_ASSERT_PTR_NON_NULL(in, false);
    293 
    294     for (psS32 i = 0 ; i < out->n ; i++) {
    295         psFree(out->spline[i]);
    296     }
    297     psFree(out->spline);
    298     psFree(out->knots);
    299     psFree(out->p_psDeriv2);
    300 
    301     out->n = in->n;
    302     out->spline = (psPolynomial1D **) psAlloc(in->n * sizeof(psPolynomial1D *));
    303     for (psS32 i = 0 ; i < in->n ; i++) {
    304         out->spline[i] = Polynomial1DDup(in->spline[i]);
    305     }
    306 
    307     // XXX: use psVectorCopy if they get it working.
    308     out->knots = psVectorAlloc(in->knots->n, in->knots->type.type);
    309     out->knots->n = out->knots->nalloc;
    310     for (psS32 i = 0 ; i < in->knots->n ; i++) {
    311         out->knots->data.F32[i] = in->knots->data.F32[i];
    312     }
    313     /*
    314         out->knots = psVectorCopy(out->knots, in->knots, in->knots->type.type);
    315     */
    316 
    317     out->p_psDeriv2 = (psF32 *) psAlloc((in->n + 1) * sizeof(psF32));
    318     // XXX: use memcpy
    319     for (psS32 i = 0 ; i < (in->n + 1) ; i++) {
    320         out->p_psDeriv2[i] = in->p_psDeriv2[i];
    321     }
    322 
    323     return(true);
    324 }
    325 
     217
     218
     219#if 0
    326220/******************************************************************************
    327221ScaleOverscanVector(): this routine takes as input an arbitrary vector,
     
    330224    PM_FIT_POLYNOMIAL: fit a polynomial to the entire input vector data.
    331225    PM_FIT_SPLINE: fit splines to the input vector data.
    332 The resulting spline or polynomial is set in the fitSpec argument.
     226XXX: Doesn't it make more sense to do polynomial interpolation on a few
     227elements of the input vector, rather than fit a polynomial to the entire
     228vector?
    333229 *****************************************************************************/
    334 static psVector *ScaleOverscanVector(
    335     psVector *overscanVector,
    336     psS32 n,
    337     void *fitSpec,
    338     pmFit fit)
     230static psVector *ScaleOverscanVector(psVector *overscanVector,
     231                                     psS32 n,
     232                                     void *fitSpec,
     233                                     pmFit fit)
    339234{
    340235    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
    341236            "---- ScaleOverscanVector() begin (%d -> %d) ----\n", overscanVector->n, n);
     237    //    PS_VECTOR_PRINT_F32(overscanVector);
    342238
    343239    if (NULL == overscanVector) {
     
    347243    // Allocate the new vector.
    348244    psVector *newVec = psVectorAlloc(n, PS_TYPE_F32);
    349     newVec->n = newVec->nalloc;
     245
    350246    //
    351247    // If the new vector is the same size as the old, simply copy the data.
    352248    //
    353249    if (n == overscanVector->n) {
    354         return(psVectorCopy(newVec, overscanVector, PS_TYPE_F32));
    355     }
     250        for (psS32 i = 0 ; i < n ; i++) {
     251            newVec->data.F32[i] = overscanVector->data.F32[i];
     252        }
     253        return(newVec);
     254    }
     255    psPolynomial1D *myPoly;
     256    psSpline1D *mySpline;
    356257    psF32 x;
    357 
     258    psS32 i;
    358259    if (fit == PM_FIT_POLYNOMIAL) {
    359260        // Fit a polynomial to the old overscan vector.
    360         psPolynomial1D *myPoly = (psPolynomial1D *) fitSpec;
     261        myPoly = (psPolynomial1D *) fitSpec;
    361262        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
    362         PS_ASSERT_POLY1D(myPoly, NULL);
    363263        myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
    364264        if (myPoly == NULL) {
    365             psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the psVector.\n");
     265            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(1): Could not fit a polynomial to the psVector.\n");
    366266            return(NULL);
    367267        }
     
    370270        // of the old vector, use the fitted polynomial to determine the
    371271        // interpolated value at that point, and set the new vector.
    372         for (psS32 i=0;i<n;i++) {
     272        for (i=0;i<n;i++) {
    373273            x = ((psF32) i) * ((psF32) overscanVector->n) / ((psF32) n);
    374274            newVec->data.F32[i] = psPolynomial1DEval(myPoly, x);
    375275        }
    376276    } else if (fit == PM_FIT_SPLINE) {
     277        psS32 mustFreeSpline = 0;
     278        // Fit a spline to the old overscan vector.
     279        mySpline = (psSpline1D *) fitSpec;
     280        // XXX: Does it make any sense to have a psSpline argument?
     281        if (mySpline == NULL) {
     282            mustFreeSpline = 1;
     283        }
     284
    377285        //
    378286        // NOTE: Since the X arg in the psVectorFitSpline1D() function is NULL,
     
    380288        // properly when doing the spline eval.
    381289        //
    382         psSpline1D *mySpline = psVectorFitSpline1D(NULL, overscanVector);
     290        //        mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL);
     291        mySpline = psVectorFitSpline1D(NULL, overscanVector);
    383292        if (mySpline == NULL) {
    384             psError(PS_ERR_UNKNOWN, false, "Could not fit a spline to the psVector.\n");
     293            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(2): Could not fit a spline to the psVector.\n");
    385294            return(NULL);
    386295        }
     296        //        PS_PRINT_SPLINE(mySpline);
    387297
    388298        // For each element of the new vector, convert the x-ordinate to that
    389         // of the old vector, use the fitted spline to determine the
     299        // of the old vector, use the fitted polynomial to determine the
    390300        // interpolated value at that point, and set the new vector.
    391         for (psS32 i=0;i<n;i++) {
     301        for (i=0;i<n;i++) {
    392302            // Scale to [0 : overscanVector->n - 1]
    393303            x = ((psF32) i) * ((psF32) (overscanVector->n-1)) / ((psF32) n);
    394304            newVec->data.F32[i] = psSpline1DEval(mySpline, x);
    395305        }
    396 
    397         psSpline1D *ptrSpline = (psSpline1D *) fitSpec;
    398         if (ptrSpline != NULL) {
    399             // Copy the resulting spline fit into ptrSpline.
    400             PS_ASSERT_SPLINE(ptrSpline, NULL);
    401             SplineCopy(ptrSpline, mySpline);
    402         }
    403         psFree(mySpline);
     306        if (mustFreeSpline ==1) {
     307            psFree(mySpline);
     308        }
     309        //        PS_VECTOR_PRINT_F32(newVec);
     310
     311
    404312    } else {
    405313        psError(PS_ERR_UNKNOWN, true, "unknown fit type.  Returning NULL.\n");
     
    413321}
    414322
     323#endif
     324
     325// Produce an overscan vector from an array of pixels
     326static psVector *overscanVector(pmOverscanOptions *overscanOpts, // Overscan options
     327                                const psArray *pixels, // Array of vectors containing the pixel values
     328                                psStats *myStats // Statistic to use in reducing the overscan
     329                               )
     330{
     331    // Reduce the overscans
     332    psVector *reduced = psVectorAlloc(pixels->n, PS_TYPE_F32); // Overscan for each row
     333    psVector *ordinate = psVectorAlloc(pixels->n, PS_TYPE_F32); // Ordinate
     334    psVector *mask = psVectorAlloc(pixels->n, PS_TYPE_U8); // Mask for fitting
     335    for (int i = 0; i < pixels->n; i++) {
     336        psVector *values = pixels->data[i]; // Vector with overscan values
     337        if (values->n > 0) {
     338            mask->data.U8[i] = 0;
     339            ordinate->data.F32[i] = 2.0*(float)i/(float)pixels->n - 1.0; // Scale to [-1,1]
     340            psVectorStats(myStats, values, NULL, NULL, 0);
     341            double reducedVal = NAN; // Result of statistics
     342            if (! p_psGetStatValue(myStats, &reducedVal)) {
     343                psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result "
     344                        "of statistics on row %d.\n", i);
     345                return NULL;
     346            }
     347            reduced->data.F32[i] = reducedVal;
     348        } else if (overscanOpts->fitType == PM_FIT_NONE) {
     349            psError(PS_ERR_UNKNOWN, true, "The overscan is not supplied for all points on the "
     350                    "image, and no fit is requested.\n");
     351            return NULL;
     352        } else {
     353            // We'll fit this one out
     354            mask->data.U8[i] = 1;
     355        }
     356    }
     357
     358    // Fit the overscan, if required
     359    switch (overscanOpts->fitType) {
     360    case PM_FIT_NONE:
     361        // No fitting --- that's easy.
     362        break;
     363    case PM_FIT_POLY_ORD:
     364        if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order ||
     365                                   overscanOpts->poly->type != PS_POLYNOMIAL_ORD)) {
     366            psFree(overscanOpts->poly);
     367            overscanOpts->poly = NULL;
     368        }
     369        if (! overscanOpts->poly) {
     370            overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, overscanOpts->order);
     371        }
     372        psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate);
     373        psFree(reduced);
     374        reduced = psPolynomial1DEvalVector(overscanOpts->poly, ordinate);
     375        break;
     376    case PM_FIT_POLY_CHEBY:
     377        if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order ||
     378                                   overscanOpts->poly->type != PS_POLYNOMIAL_CHEB)) {
     379            psFree(overscanOpts->poly);
     380            overscanOpts->poly = NULL;
     381        }
     382        if (! overscanOpts->poly) {
     383            overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, overscanOpts->order);
     384        }
     385        psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate);
     386        psFree(reduced);
     387        reduced = psPolynomial1DEvalVector(overscanOpts->poly, ordinate);
     388        break;
     389    case PM_FIT_SPLINE:
     390        // XXX I don't think psSpline1D is up to scratch yet --- it has no mask, and requires an
     391        // input spline
     392        overscanOpts->spline = psVectorFitSpline1D(reduced, ordinate);
     393        psFree(reduced);
     394        reduced = psSpline1DEvalVector(overscanOpts->spline, ordinate);
     395        break;
     396    default:
     397        psError(PS_ERR_UNKNOWN, true, "Unknown value for the fitting type: %d\n", overscanOpts->fitType);
     398        return NULL;
     399        break;
     400    }
     401
     402    psFree(ordinate);
     403    psFree(mask);
     404
     405    return reduced;
     406}
     407
     408
     409
    415410/******************************************************************************
     411XXX: The SDRS does not specify type support.  F32 is implemented here.
    416412 *****************************************************************************/
    417 static psS32 GetOverscanSize(
    418     psImage *inImg,
    419     pmOverscanAxis overScanAxis)
    420 {
    421     if (overScanAxis == PM_OVERSCAN_ROWS) {
    422         return(inImg->numCols);
    423     } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    424         return(inImg->numRows);
    425     } else if (overScanAxis == PM_OVERSCAN_ALL) {
    426         return(1);
    427     }
    428     return(0);
    429 }
    430 
    431 /******************************************************************************
    432 GetOverscanAxis(in) this private routine determines the appropiate overscan
    433 axis from the parent cell metadata.
    434  
    435 XXX: Verify the READDIR corresponds with my overscan axis.
    436  *****************************************************************************/
    437 static pmOverscanAxis GetOverscanAxis(pmReadout *in)
    438 {
    439     psBool rc;
    440     if ((in->parent != NULL) && (in->parent->concepts)) {
    441         psS32 dir = psMetadataLookupS32(&rc, in->parent->concepts, "CELL.READDIR");
    442         if (rc == true) {
    443             if (dir == 1) {
    444                 return(PM_OVERSCAN_ROWS);
    445             } else if (dir == 2) {
    446                 return(PM_OVERSCAN_COLUMNS);
    447             } else if (dir == 3) {
    448                 return(PM_OVERSCAN_ALL);
    449             }
    450         }
    451     }
    452 
    453     psLogMsg(__func__, PS_LOG_WARN,
    454              "WARNING: pmSubtractBias.(): could not determine CELL.READDIR from in->parent metadata.  Setting overscan axis to PM_OVERSCAN_NONE.\n");
    455     return(PM_OVERSCAN_NONE);
    456 }
    457 
    458 /******************************************************************************
    459 my_psListLength(list): determine the length of a psList.
    460  
    461 XXX: Put this elsewhere.
    462  
    463 XXX: psList.h now has a version of this function.  Use that instead.
    464  *****************************************************************************/
    465 
    466 static psS32 my_psListLength(
    467     psList *list)
    468 {
    469     psS32 length = 0;
    470     psListElem *tmpElem = (psListElem *) list->head;
    471     while (NULL != tmpElem) {
    472         tmpElem = tmpElem->next;
    473         length++;
    474     }
    475     return(length);
    476 }
    477 
    478 /******************************************************************************
    479 Note: this isn't needed anymore as of psModule SDRS 12-09.
    480  *****************************************************************************/
    481 static psBool OverscanReducePixel(
    482     psImage *in,
    483     psList *bias,
    484     psStats *myStats)
    485 {
    486     PS_ASSERT_PTR_NON_NULL(in, NULL);
    487     PS_ASSERT_PTR_NON_NULL(bias, NULL);
    488     PS_ASSERT_PTR_NON_NULL(bias->head, NULL);
    489     PS_ASSERT_PTR_NON_NULL(myStats, NULL);
    490 
    491     // Allocate a psVector with one element per overscan image.
    492     psS32 numOverscanImages = my_psListLength(bias);
    493     psVector *statsAll = psVectorAlloc(numOverscanImages, PS_TYPE_F32);
    494     statsAll->n = statsAll->nalloc;
    495     psListElem *tmpOverscan = (psListElem *) bias->head;
    496     psS32 i = 0;
    497     psF64 statValue;
    498     //
    499     // We loop through each overscan image, calculating the specified
    500     // statistic on that image.
    501     //
    502     while (NULL != tmpOverscan) {
    503         psImage *myOverscanImage = (psImage *) tmpOverscan->data;
    504 
    505         PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
    506         myStats = psImageStats(myStats, myOverscanImage, NULL, (psMaskType)0xffffffff);
    507         if (myStats == NULL) {
    508             psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
    509             psFree(statsAll);
    510             return(false);
    511         }
    512         if (false == p_psGetStatValue(myStats, &statValue)) {
    513             psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
    514             psFree(statsAll);
    515             return(false);
    516         }
    517         statsAll->data.F32[i] = statValue;
    518         i++;
    519         tmpOverscan = tmpOverscan->next;
    520     }
    521 
    522     //
    523     // We reduce the individual stats for each overscan image to
    524     // a single psF32.
    525     //
    526     myStats = psVectorStats(myStats, statsAll, NULL, NULL, 0);
    527     if (myStats == NULL) {
    528         psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
    529         psFree(statsAll);
    530         return(false);
    531     }
    532     if (false == p_psGetStatValue(myStats, &statValue)) {
    533         psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
    534         psFree(statsAll);
    535         return(false);
    536     }
    537 
    538     //
    539     // Subtract the result and return.
    540     //
    541     ImageSubtractScalar(in, statValue);
    542     psFree(statsAll);
    543     return(in);
    544 }
    545 
    546 /******************************************************************************
    547 ReduceOverscanImageToCol(overscanImage, myStats): This private routine reduces
    548 a single psImage to a column by combining all pixels from each row into a
    549 single pixel via requested statistic in myStats.
    550  *****************************************************************************/
    551 static psVector *ReduceOverscanImageToCol(
    552     psImage *overscanImage,
    553     psStats *myStats)
    554 {
    555     psF64 statValue;
    556     psVector *tmpRow = psVectorAlloc(overscanImage->numCols, PS_TYPE_F32);
    557     psVector *tmpCol = psVectorAlloc(overscanImage->numRows, PS_TYPE_F32);
    558     tmpRow->n = tmpRow->nalloc;
    559     tmpCol->n = tmpCol->nalloc;
    560 
    561     //
    562     // For each row, we store all pixels in that row into a temporary psVector,
    563     // then we run psVectorStats() on that vector.
    564     //
    565     for (psS32 i=0;i<overscanImage->numRows;i++) {
    566         for (psS32 j=0;j<overscanImage->numCols;j++) {
    567             tmpRow->data.F32[j] = overscanImage->data.F32[i][j];
    568         }
    569 
    570         psStats *rc = psVectorStats(myStats, tmpRow, NULL, NULL, 0);
    571         if (rc == NULL) {
    572             psError(PS_ERR_UNKNOWN, true, "psVectorStats() could not perform requested statistical operation.  Returning in image.\n");
    573             return(NULL);
    574         }
    575 
    576         if (false ==  p_psGetStatValue(rc, &statValue)) {
    577             psError(PS_ERR_UNKNOWN, true, "p_psGetStatValue() could not determine result from requested statistical operation.  Returning in image.\n");
    578             return(NULL);
    579         }
    580 
    581         tmpCol->data.F32[i] = (psF32) statValue;
    582     }
    583     psFree(tmpRow);
    584 
    585     return(tmpCol);
    586 }
    587 
    588 /******************************************************************************
    589 ReduceOverscanImageToCol(overscanImage, myStats): This private routine reduces
    590 a single psImage to a row by combining all pixels from each column into a
    591 single pixel via requested statistic in myStats.
    592  *****************************************************************************/
    593 static psVector *ReduceOverscanImageToRow(
    594     psImage *overscanImage,
    595     psStats *myStats)
    596 {
    597     psF64 statValue;
    598     psVector *tmpRow = psVectorAlloc(overscanImage->numCols, PS_TYPE_F32);
    599     psVector *tmpCol = psVectorAlloc(overscanImage->numRows, PS_TYPE_F32);
    600     tmpRow->n = tmpRow->nalloc;
    601     tmpCol->n = tmpCol->nalloc;
    602 
    603     //
    604     // For each column, we store all pixels in that column into a temporary psVector,
    605     // then we run psVectorStats() on that vector.
    606     //
    607     for (psS32 i=0;i<overscanImage->numCols;i++) {
    608         for (psS32 j=0;j<overscanImage->numRows;j++) {
    609             tmpCol->data.F32[j] = overscanImage->data.F32[j][i];
    610         }
    611 
    612         psStats *rc = psVectorStats(myStats, tmpCol, NULL, NULL, 0);
    613         if (rc == NULL) {
    614             psError(PS_ERR_UNKNOWN, true, "psVectorStats() could not perform requested statistical operation.  Returning in image.\n");
    615             return(NULL);
    616         }
    617 
    618         if (false ==  p_psGetStatValue(rc, &statValue)) {
    619             psError(PS_ERR_UNKNOWN, true, "p_psGetStatValue() could not determine result from requested statistical operation.  Returning in image.\n");
    620             return(NULL);
    621         }
    622 
    623         tmpRow->data.F32[i] = (psF32) statValue;
    624     }
    625     psFree(tmpCol);
    626 
    627     return(tmpRow);
    628 }
    629 
    630 /******************************************************************************
    631 OverscanReduce(vecSize, bias, myStats): This private routine takes a psList of
    632 overscan images (in bias) and reduces them to a single psVector via the
    633 specified psStats struct.  The vector is then scaled to the length or the
    634 row/column in inImg.
    635  *****************************************************************************/
    636 static psVector* OverscanReduce(
    637     psImage *inImg,
    638     pmOverscanAxis overScanAxis,
    639     psList *bias,
    640     void *fitSpec,
    641     pmFit fit,
    642     psStats *myStats)
    643 {
    644     if ((overScanAxis != PM_OVERSCAN_ROWS) && (overScanAxis != PM_OVERSCAN_COLUMNS)) {
    645         psError(PS_ERR_UNKNOWN, true, "overScanAxis must be PM_OVERSCAN_ROWS or PM_OVERSCAN_COLUMNS\n");
    646         return(NULL);
    647     }
    648     PS_ASSERT_PTR_NON_NULL(inImg, NULL);
    649     PS_ASSERT_PTR_NON_NULL(bias, NULL);
    650     PS_ASSERT_PTR_NON_NULL(bias->head, NULL);
    651     PS_ASSERT_PTR_NON_NULL(myStats, NULL);
    652     //
    653     // Allocate a psVector for the output of this routine.
    654     //
    655     psS32 vecSize = GetOverscanSize(inImg, overScanAxis);
    656     psVector *overscanVector = psVectorAlloc(vecSize, PS_TYPE_F32);
    657     overscanVector->n = overscanVector->nalloc;
    658 
    659     //
    660     // Allocate an array of psVectors with one psVector per element of the
    661     // final oversan column vector.  These psVectors will be used with
    662     // psStats to reduce the multiple elements from each overscan column
    663     // vector to a single final column vector.
    664     //
    665     psS32 numOverscanImages = my_psListLength(bias);
    666     psVector **overscanVectors = (psVector **) psAlloc(numOverscanImages * sizeof(psVector *));
    667     //    (*overscanVectors)->n = (*overscanVectors)->nalloc;
    668     for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    669         overscanVectors[i] = NULL;
    670     }
    671 
    672     //
    673     // We iterate through the list of overscan images.  For each image,
    674     // we reduce it to a single column or row.  Save the overscan vector
    675     // in overscanVectors[].
    676     //
    677     psListElem *tmpOverscan = (psListElem *) bias->head;
    678     psS32 overscanID = 0;
    679     while (tmpOverscan != NULL) {
    680         psImage *tmpOverscanImage = (psImage *) tmpOverscan->data;
    681         if (overScanAxis == PM_OVERSCAN_ROWS) {
    682             overscanVectors[overscanID] = ReduceOverscanImageToRow(tmpOverscanImage, myStats);
    683         } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    684             overscanVectors[overscanID] = ReduceOverscanImageToCol(tmpOverscanImage, myStats);
    685         }
    686 
    687         tmpOverscan = tmpOverscan->next;
    688         overscanID++;
    689     }
    690 
    691     //
    692     // For each overscan vector, if necessary, we scale that column or
    693     // row to vecSize.  Note: we should have already ensured that the
    694     // fit is poly or spline.
    695     //
    696     for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    697         psVector *tmpOverscanVector = overscanVectors[i];
    698 
    699         if (tmpOverscanVector->n != vecSize) {
    700             overscanVectors[i] = ScaleOverscanVector(tmpOverscanVector, vecSize, fitSpec, fit);
    701             if (overscanVectors[i] == NULL) {
    702                 psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.\n");
    703                 for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    704                     psFree(overscanVectors[i]);
    705                 }
    706                 psFree(overscanVectors);
    707                 psFree(tmpOverscanVector);
    708                 return(NULL);
    709             }
    710             psFree(tmpOverscanVector);
    711         }
    712     }
    713 
    714     //
    715     // We collect all elements in the overscan vectors for the various
    716     // overscan images into a single psVector (tmpVec).  Then we call
    717     // psStats on that vector to determine the final values for the
    718     // overscan vector.
    719     //
    720     psVector *tmpVec = psVectorAlloc(numOverscanImages, PS_TYPE_F32);
    721     tmpVec->n = tmpVec->nalloc;
    722     psF64 statValue;
    723     for (psS32 i = 0 ; i < vecSize ; i++) {
    724         // Collect the i-th elements from each overscan vector into a single vector.
    725         for (psS32 j = 0 ; j < numOverscanImages ; j++) {
    726             tmpVec->data.F32[j] = overscanVectors[j]->data.F32[i];
    727         }
    728 
    729         if (NULL == psVectorStats(myStats, tmpVec, NULL, NULL, 0)) {
    730             psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
    731             for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    732                 psFree(overscanVectors[i]);
    733             }
    734             psFree(overscanVectors);
    735             psFree(tmpVec);
    736             return(NULL);
    737         }
    738         if (false == p_psGetStatValue(myStats, &statValue)) {
    739             psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
    740             for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    741                 psFree(overscanVectors[i]);
    742             }
    743             psFree(overscanVectors);
    744             psFree(tmpVec);
    745             return(NULL);
    746         }
    747 
    748         overscanVector->data.F32[i] = (psF32) statValue;
    749     }
    750 
    751     //
    752     // We're done.  Free the intermediate overscan vectors.
    753     //
    754     psFree(tmpVec);
    755     for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    756         psFree(overscanVectors[i]);
    757     }
    758     psFree(overscanVectors);
    759 
    760     //
    761     // Return the computed overscanVector
    762     //
    763     return(overscanVector);
    764 }
    765 
    766 /******************************************************************************
    767 RebinOverscanVector(overscanVector, nBinOrig, myStats): this private routine
    768 takes groups of nBinOrig elements in the input vector, combines them into a
    769 single pixel via myStats and psVectorStats(), and then outputs a vector of
    770 those pixels.
    771  *****************************************************************************/
    772 static psS32 RebinOverscanVector(
    773     psVector *overscanVector,
    774     psS32 nBinOrig,
    775     psStats *myStats)
    776 {
    777     psF64 statValue;
    778     psS32 nBin;
    779     if ((nBinOrig > 1) && (nBinOrig < overscanVector->n)) {
    780         psS32 numBins = 1+((overscanVector->n)/nBinOrig);
    781         psVector *myBin = psVectorAlloc(numBins, PS_TYPE_F32);
    782         psVector *binVec = psVectorAlloc(nBinOrig, PS_TYPE_F32);
    783         myBin->n = myBin->nalloc;
    784         binVec->n = binVec->nalloc;
    785 
    786         for (psS32 i=0;i<numBins;i++) {
    787             for(psS32 j=0;j<nBinOrig;j++) {
    788                 if (overscanVector->n > ((i*nBinOrig)+j)) {
    789                     binVec->data.F32[j] = overscanVector->data.F32[(i*nBinOrig)+j];
    790                 } else {
    791                     // XXX: we get here if nBinOrig does not evenly divide
    792                     // the overscanVector vector.  This is the last bin.  Should
    793                     // we change the binVec->n to acknowledge that?
    794                     binVec->n = j;
    795                 }
    796             }
    797             psStats *rc = psVectorStats(myStats, binVec, NULL, NULL, 0);
    798             if (rc == NULL) {
    799                 psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
    800                 return(-1);
    801             }
    802             if (false ==  p_psGetStatValue(rc, &statValue)) {
    803                 psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
    804                 return(-1);
    805             }
    806             myBin->data.F32[i] = statValue;
    807         }
    808 
    809         // Change the effective size of overscanVector.
    810         overscanVector->n = numBins;
    811         for (psS32 i=0;i<numBins;i++) {
    812             overscanVector->data.F32[i] = myBin->data.F32[i];
    813         }
    814         psFree(binVec);
    815         psFree(myBin);
    816         nBin = nBinOrig;
    817     } else {
    818         nBin = 1;
    819     }
    820 
    821     return(nBin);
    822 }
    823 
    824 /******************************************************************************
    825 FitOverscanVectorAndUnbin(inImg, overscanVector, overScanAxis, fitSpec, fit,
    826 nBin):  this private routine fits a psPolynomial or psSpline to the overscan
    827 vector.  It then creates a new vector, with a size determined by the input
    828 image, evaluates the psPolynomial or psSpline at each element in that vector,
    829 then returns that vector.
    830  *****************************************************************************/
    831 static psVector *FitOverscanVectorAndUnbin(
    832     psImage *inImg,
    833     psVector *overscanVector,
    834     pmOverscanAxis overScanAxis,
    835     void *fitSpec,
    836     pmFit fit,
    837     psS32 nBin)
    838 {
    839     psPolynomial1D* myPoly = NULL;
    840     psSpline1D *mySpline = NULL;
    841     //
    842     // Fit a polynomial or spline to the overscan vector.
    843     //
    844     if (fit == PM_FIT_POLYNOMIAL) {
    845         myPoly = (psPolynomial1D *) fitSpec;
    846         PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
    847         PS_ASSERT_POLY1D(myPoly, NULL);
    848         myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
    849         if (myPoly == NULL) {
    850             psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to overscan vector.  Returning NULL.\n");
    851             return(NULL);
    852         }
    853     } else if (fit == PM_FIT_SPLINE) {
    854         mySpline = psVectorFitSpline1D(NULL, overscanVector);
    855         if (mySpline == NULL) {
    856             psError(PS_ERR_UNKNOWN, false, "Could not fit a spline to overscan vector.  Returning NULL.\n");
    857             return(NULL);
    858         }
    859         if (fitSpec != NULL) {
    860             // Copy the resulting spline fit into fitSpec.
    861             psSpline1D *ptrSpline = (psSpline1D *) fitSpec;
    862             PS_ASSERT_SPLINE(ptrSpline, NULL);
    863             SplineCopy(ptrSpline, mySpline);
    864         }
    865     }
    866 
    867     //
    868     // Evaluate the poly/spline at each pixel in the overscan row/column.
    869     //
    870     psS32 vecSize = GetOverscanSize(inImg, overScanAxis);
    871     psVector *newVec = psVectorAlloc(vecSize, PS_TYPE_F32);
    872     newVec->n = newVec->nalloc;
    873     if ((nBin > 1) && (nBin < overscanVector->n)) {
    874         for (psS32 i = 0 ; i < vecSize ; i++) {
    875             if (fit == PM_FIT_POLYNOMIAL) {
    876                 newVec->data.F32[i] = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin));
    877             } else if (fit == PM_FIT_SPLINE) {
    878                 newVec->data.F32[i] = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin));
    879             }
    880         }
    881     } else {
    882         for (psS32 i = 0 ; i < vecSize ; i++) {
    883             if (fit == PM_FIT_POLYNOMIAL) {
    884                 newVec->data.F32[i] = psPolynomial1DEval(myPoly, (psF32) i);
    885             } else if (fit == PM_FIT_SPLINE) {
    886                 newVec->data.F32[i] = psSpline1DEval(mySpline, (psF32) i);
    887             }
    888         }
    889     }
    890 
    891     psFree(mySpline);
    892     psFree(overscanVector);
    893     return(newVec);
    894 }
    895 
    896 
    897 
    898 /******************************************************************************
    899 UnbinOverscanVector(inImg, overscanVector, overScanAxis, nBin):  this private
    900 routine takes a psVector overscanVector that was previously binned by a factor
    901 of nBin, and then expands it to its original size, duplicated elements nBin
    902 times for each element in the input vector overscanVector.
    903  *****************************************************************************/
    904 static psVector *UnbinOverscanVector(
    905     psImage *inImg,
    906     psVector *overscanVector,
    907     pmOverscanAxis overScanAxis,
    908     psS32 nBin)
    909 {
    910     psS32 vecSize = 0;
    911 
    912     if (overScanAxis == PM_OVERSCAN_ROWS) {
    913         vecSize = inImg->numCols;
    914     } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    915         vecSize = inImg->numRows;
    916     }
    917 
    918     psVector *newVec = psVectorAlloc(vecSize, PS_TYPE_F32);
    919     newVec->n = newVec->nalloc;
    920     for (psS32 i = 0 ; i < vecSize ; i++) {
    921         newVec->data.F32[i] = overscanVector->data.F32[i/nBin];
    922     }
    923 
    924     psFree(overscanVector);
    925     return(newVec);
    926 }
    927 
    928 
    929 /******************************************************************************
    930 SubtractVectorFromImage(inImg, overscanVector, overScanAxis):  this private
    931 routine subtracts the overscanVector column-wise or row-wise from inImg.
    932  *****************************************************************************/
    933 static psImage *SubtractVectorFromImage(
    934     psImage *inImg,
    935     psVector *overscanVector,
    936     pmOverscanAxis overScanAxis)
    937 {
    938     //
    939     // Subtract overscan vector row-wise from the image.
    940     //
    941     if (overScanAxis == PM_OVERSCAN_ROWS) {
    942         for (psS32 i=0;i<inImg->numCols;i++) {
    943             for (psS32 j=0;j<inImg->numRows;j++) {
    944                 inImg->data.F32[j][i]-= overscanVector->data.F32[i];
    945             }
    946         }
    947     }
    948 
    949     //
    950     // Subtract overscan vector column-wise from the image.
    951     //
    952     if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    953         for (psS32 i=0;i<inImg->numRows;i++) {
    954             for (psS32 j=0;j<inImg->numCols;j++) {
    955                 inImg->data.F32[i][j]-= overscanVector->data.F32[i];
    956             }
    957         }
    958     }
    959 
    960     return(inImg);
    961 }
    962 
    963 
    964 
    965 typedef enum {
    966     PM_ERROR_NO_SUBTRACTION,
    967     PM_WARNING_NO_SUBTRACTION,
    968     PM_ERROR_NO_BIAS_SUBTRACT,
    969     PM_WARNING_NO_BIAS_SUBTRACT,
    970     PM_ERROR_NO_DARK_SUBTRACT,
    971     PM_WARNING_NO_DARK_SUBTRACT,
    972     PM_OKAY
    973 } pmSubtractBiasAssertStatus;
    974 /******************************************************************************
    975 AssertCodeOverscan(....) this private routine verifies that the various input
    976 parameters to pmSubtractBias() are correct for overscan subtraction.
    977  *****************************************************************************/
    978 pmSubtractBiasAssertStatus AssertCodeOverscan(
    979     pmReadout *in,
    980     void *fitSpec,
    981     pmFit fit,
    982     bool overscan,
    983     psStats *stat,
    984     int nBinOrig,
    985     const pmReadout *bias,
    986     const pmReadout *dark)
    987 {
    988 
    989     PS_ASSERT_READOUT_NON_NULL(in, PM_ERROR_NO_SUBTRACTION);
    990     PS_ASSERT_READOUT_NON_EMPTY(in, PM_ERROR_NO_SUBTRACTION);
    991     PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, PM_ERROR_NO_SUBTRACTION);
    992     PS_WARN_PTR_NON_NULL(in->parent);
    993     if (in->parent != NULL) {
    994         PS_WARN_PTR_NON_NULL(in->parent->concepts);
    995     }
    996 
    997     if (overscan == true) {
    998         pmOverscanAxis overScanAxis = GetOverscanAxis(in);
    999         PS_ASSERT_PTR_NON_NULL(stat, PM_ERROR_NO_SUBTRACTION);
    1000         PS_ASSERT_PTR_NON_NULL(in->bias, PM_ERROR_NO_SUBTRACTION);
    1001         PS_ASSERT_PTR_NON_NULL(in->bias->head, PM_ERROR_NO_SUBTRACTION);
    1002         //
    1003         // Check the type, size of each bias image.
    1004         //
    1005         psListElem *tmpOverscan = (psListElem *) in->bias->head;
    1006         psS32 numOverscans = 0;
    1007         while (NULL != tmpOverscan) {
    1008             numOverscans++;
    1009             psImage *myOverscanImage = (psImage *) tmpOverscan->data;
    1010             PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, PM_ERROR_NO_SUBTRACTION);
    1011             // XXX: Get this right with the rows and columns.
    1012             if (overScanAxis == PM_OVERSCAN_ROWS) {
    1013                 if (myOverscanImage->numRows != in->image->numRows) {
    1014                     psLogMsg(__func__, PS_LOG_WARN,
    1015                              "WARNING: pmSubtractBias.(): overscan image (# %d) has %d rows, input image has %d rows\n",
    1016                              numOverscans, myOverscanImage->numCols, in->image->numRows);
    1017                     if (fit == PM_FIT_NONE) {
    1018                         psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vectors.  Set fit to PM_FIT_POLYNOMIAL or PM_FIT_SPLINE.\n");
    1019                         return(PM_ERROR_NO_SUBTRACTION);
    1020                     }
    1021                 }
    1022             } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    1023                 if (myOverscanImage->numCols != in->image->numCols) {
    1024                     psLogMsg(__func__, PS_LOG_WARN,
    1025                              "WARNING: pmSubtractBias.(): overscan image (# %d) has %d columns, input image has %d columns\n",
    1026                              numOverscans, myOverscanImage->numCols, in->image->numCols);
    1027                     if (fit == PM_FIT_NONE) {
    1028                         psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vectors.  Set fit to PM_FIT_POLYNOMIAL or PM_FIT_SPLINE.\n");
    1029                         return(PM_ERROR_NO_SUBTRACTION);
    1030                     }
    1031                 }
    1032             } else if (overScanAxis != PM_OVERSCAN_ALL) {
    1033                 psError(PS_ERR_UNKNOWN, true, "Must specify and overscan axis.\n");
    1034                 return(PM_ERROR_NO_SUBTRACTION);
    1035             }
    1036             tmpOverscan = tmpOverscan->next;
    1037         }
    1038     } else {
    1039         if (fit != PM_FIT_NONE) {
    1040             psLogMsg(__func__, PS_LOG_WARN,
    1041                      "WARNING: pmSubtractBias.(): overscan is FALSE and fit is not PM_FIT_NONE.\n");
    1042             return(PM_WARNING_NO_SUBTRACTION);
    1043         }
    1044     }
    1045 
    1046     // XXX: I do not like the following spec since it's useless to specify
    1047     // a psSpline as the fitSpec.
    1048     if (0) {
    1049         if ((fitSpec == NULL) &&
    1050                 ((fit != PM_FIT_NONE) || (overscan == true))) {
    1051             psError(PS_ERR_UNKNOWN, true, "fitSpec is NULL and fit is not PM_FIT_NONE or overscan is TRUE.\n");
    1052             return(PM_ERROR_NO_SUBTRACTION);
    1053         }
    1054     }
    1055 
    1056     return(PM_OKAY);
    1057 }
    1058 
    1059 /******************************************************************************
    1060 AssertCodeBias(....) this private routine verifies that the various input
    1061 parameters to pmSubtractBias() are correct for bias subtraction.
    1062  *****************************************************************************/
    1063 static pmSubtractBiasAssertStatus AssertCodeBias(
    1064     pmReadout *in,
    1065     void *fitSpec,
    1066     pmFit fit,
    1067     bool overscan,
    1068     psStats *stat,
    1069     int nBinOrig,
    1070     const pmReadout *bias,
    1071     const pmReadout *dark)
    1072 {
    1073     if ((in->image->numRows + in->row0 - bias->row0) > bias->image->numRows) {
    1074         psError(PS_ERR_UNKNOWN,true, "bias image does not have enough rows.  Returning in image\n");
    1075         return(PM_ERROR_NO_BIAS_SUBTRACT);
    1076     }
    1077     if ((in->image->numCols + in->col0 - bias->col0) > bias->image->numCols) {
    1078         psError(PS_ERR_UNKNOWN,true, "bias image does not have enough columns.  Returning in image\n");
    1079         return(PM_ERROR_NO_BIAS_SUBTRACT);
    1080     }
    1081 
    1082     if (bias != NULL) {
    1083         PS_ASSERT_READOUT_NON_EMPTY(bias, PM_ERROR_NO_BIAS_SUBTRACT);
    1084         PS_ASSERT_READOUT_TYPE(bias, PS_TYPE_F32, PM_ERROR_NO_DARK_SUBTRACT);
    1085     }
    1086     return(PM_OKAY);
    1087 }
    1088 
    1089 /******************************************************************************
    1090 AssertCodeDark(....) this private routine verifies that the various input
    1091 parameters to pmSubtractBias() are correct for dark subtraction.
    1092  *****************************************************************************/
    1093 pmSubtractBiasAssertStatus AssertCodeDark(
    1094     pmReadout *in,
    1095     void *fitSpec,
    1096     pmFit fit,
    1097     bool overscan,
    1098     psStats *stat,
    1099     int nBinOrig,
    1100     const pmReadout *bias,
    1101     const pmReadout *dark)
    1102 {
    1103     if ((in->image->numRows + in->row0 - dark->row0) > dark->image->numRows) {
    1104         psError(PS_ERR_UNKNOWN, true, "dark image does not have enough rows.  Returning in image\n");
    1105         return(PM_ERROR_NO_DARK_SUBTRACT);
    1106     }
    1107     if ((in->image->numCols + in->col0 - dark->col0) > dark->image->numCols) {
    1108         psError(PS_ERR_UNKNOWN, true, "dark image does not have enough columns.  Returning in image\n");
    1109         return(PM_ERROR_NO_DARK_SUBTRACT);
    1110     }
    1111 
    1112     if (dark != NULL) {
    1113         PS_ASSERT_READOUT_NON_EMPTY(dark, PM_ERROR_NO_DARK_SUBTRACT);
    1114         PS_ASSERT_READOUT_TYPE(dark, PS_TYPE_F32, PM_ERROR_NO_DARK_SUBTRACT);
    1115     }
    1116     return(PM_OKAY);
    1117 }
    1118 
    1119 /******************************************************************************
    1120 p_psDetermineTrimmedImage(): global routine: determines the region of the
    1121 input pmReadout which will be operated on by the various detrend modules.  It
    1122 does a metadata fetch on "CELL.TRIMSEC" for the parent cell of the pmReadout.
    1123  
    1124 Use it this way:
    1125     PS_WARN_PTR_NON_NULL(in->parent);
    1126     if (in->parent != NULL) {
    1127         PS_WARN_PTR_NON_NULL(in->parent->concepts);
    1128     }
    1129     //
    1130     // Determine trimmed image from metadata.
    1131     //
    1132     psImage *trimmedImg = p_psDetermineTrimmedImage(in);
    1133  
    1134 XXX: Create a pmUtils.c file and put this routine there.
    1135  *****************************************************************************/
    1136 psImage *p_psDetermineTrimmedImage(pmReadout *in)
    1137 {
    1138     if ((in->parent == NULL) || (in->parent->concepts == NULL)) {
    1139         psLogMsg(__func__, PS_LOG_WARN,
    1140                  "WARNING: could not determine CELL.TRIMSEC from parent cell Metadata (NULL).\n");
    1141         return(in->image);
    1142     }
    1143 
    1144     psBool rc = false;
    1145     psImage *trimmedImg = NULL;
    1146     psRegion *trimRegion = psMetadataLookupPtr(&rc, in->parent->concepts,
    1147                            "CELL.TRIMSEC");
    1148     if (rc == false) {
    1149         psLogMsg(__func__, PS_LOG_WARN,
    1150                  "WARNING: could not determine CELL.TRIMSEC from parent cell Metadata.\n");
    1151         trimmedImg = in->image;
    1152     } else {
    1153         trimmedImg = psImageSubset(in->image, *trimRegion);
    1154     }
    1155 
    1156     return(trimmedImg);
    1157 }
    1158 
    1159 
    1160 /******************************************************************************
    1161 pmSubtractBias(....): see SDRS for complete specification.
    1162  
    1163 XXX: Code and assert type support: U16, S32, F32.
    1164 XXX: Add trace messages.
    1165  *****************************************************************************/
    1166 pmReadout *pmSubtractBias(
    1167     pmReadout *in,
    1168     void *fitSpec,
    1169     pmFit fit,
    1170     bool overscan,
    1171     psStats *stat,
    1172     int nBin,
    1173     const pmReadout *bias,
    1174     const pmReadout *dark)
     413pmReadout *pmSubtractBias(pmReadout *in, pmOverscanOptions *overscanOpts,
     414                          const pmReadout *bias, const pmReadout *dark)
    1175415{
    1176416    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
    1177417            "---- pmSubtractBias() begin ----\n");
    1178     //
    1179     // Check input parameters, generate warnings and errors.
    1180     //
    1181     if (PM_OKAY != AssertCodeOverscan(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) {
    1182         return(in);
    1183     }
    1184     //
    1185     // Determine trimmed image from metadata.
    1186     //
    1187     psImage *trimmedImg = p_psDetermineTrimmedImage(in);
    1188 
    1189     //
    1190     // Subtract overscan frames if necessary.
    1191     //
    1192     if (overscan == true) {
    1193         pmOverscanAxis overScanAxis = GetOverscanAxis(in);
    1194         //
    1195         //  Create a psStats data structure and determine the highest
    1196         //  priority stats option.
    1197         //
    1198         psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
    1199         if (stat != NULL) {
    1200             myStats->options = GenNewStatOptions(stat);
    1201         }
    1202 
    1203         //
    1204         // Reduce overscan images to a single pixel, then subtract.
    1205         // This code is no longer required as of SDRS 12-09.
    1206         //
    1207         if (overScanAxis == PM_OVERSCAN_ALL) {
    1208             if (false == OverscanReducePixel(trimmedImg, in->bias, myStats)) {
     418    PS_ASSERT_READOUT_NON_NULL(in, NULL);
     419    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
     420    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
     421
     422    psImage *image = in->image;         // The input image
     423
     424    // Overscan processing
     425    if (overscanOpts) {
     426        // Check for an unallowable pmFit.
     427        if (overscanOpts->fitType != PM_FIT_NONE && overscanOpts->fitType != PM_FIT_POLY_ORD &&
     428                overscanOpts->fitType != PM_FIT_POLY_CHEBY && overscanOpts->fitType != PM_FIT_SPLINE) {
     429            psError(PS_ERR_UNKNOWN, true, "Invalid fit type (%d).  Returning original image.\n", overscanOpts->fitType);
     430            return(in);
     431        }
     432
     433        psList *overscans = in->bias; // List of the overscan images
     434
     435        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // A new psStats, to avoid clobbering original
     436        myStats->options = GenNewStatOptions(overscanOpts->stat);
     437
     438        // Reduce all overscan pixels to a single value
     439        if (overscanOpts->single) {
     440            psVector *pixels = psVectorAlloc(0, PS_TYPE_F32);
     441            pixels->n = 0;
     442            psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
     443            psImage *overscan = NULL;   // Overscan image from iterator
     444            while ((overscan = psListGetAndIncrement(iter))) {
     445                int index = pixels->n;  // Index
     446                pixels = psVectorRealloc(pixels, pixels->n + overscan->numRows * overscan->numCols);
     447                // XXX Reimplement with memcpy
     448                for (int i = 0; i < overscan->numRows; i++) {
     449                    for (int j = 0; j < overscan->numCols; j++) {
     450                        pixels->data.F32[index++] = overscan->data.F32[i][j];
     451                    }
     452                }
     453
     454            }
     455            psFree(iter);
     456
     457            (void)psVectorStats(myStats, pixels, NULL, NULL, 0);
     458            double reduced = NAN;     // Result of statistics
     459            if (! p_psGetStatValue(myStats, &reduced)) {
     460                psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning input image.\n");
    1209461                return(in);
    1210462            }
    1211             psFree(myStats);
     463            (void)psBinaryOp(image, image, "-", psScalarAlloc((float)reduced, PS_TYPE_F32));
    1212464        } else {
    1213             //
    1214             // Reduce the overscan images to a single overscan vector.
    1215             //
    1216             psVector *overscanVector = OverscanReduce(in->image, overScanAxis,
    1217                                        in->bias, fitSpec,
    1218                                        fit, myStats);
    1219             if (overscanVector == NULL) {
    1220                 psError(PS_ERR_UNKNOWN, false, "Could not reduce overscan images to a single overscan vector.  Returning in image\n");
    1221                 psFree(myStats);
    1222                 return(in);
     465
     466            // We do the regular overscan subtraction
     467
     468            bool readRows = psMetadataLookupBool(NULL, in->parent->concepts, "CELL.READDIR");// Read direction
     469
     470            if (readRows) {
     471                // The read direction is rows
     472                psArray *pixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels
     473                for (int i = 0; i < pixels->n; i++) {
     474                    psVector *values = psVectorAlloc(0, PS_TYPE_F32);
     475                    values->n = 0;
     476                    pixels->data[i] = values;
     477                }
     478
     479                // Pull the pixels out into the vectors
     480                psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
     481                psImage *overscan = NULL; // Overscan image from iterator
     482                while ((overscan = psListGetAndIncrement(iter))) {
     483                    int diff = image->row0 - overscan->row0; // Offset between the two regions
     484                    for (int i = MAX(0,diff); i < MIN(image->numRows, overscan->numRows + diff); i++) {
     485                        // i is row on overscan
     486                        // XXX Reimplement with memcpy
     487                        psVector *values = pixels->data[i];
     488                        int index = values->n; // Index in the vector
     489                        values = psVectorRealloc(values, values->n + overscan->numCols);
     490                        for (int j = 0; j < overscan->numCols; j++) {
     491                            values->data.F32[index++] = overscan->data.F32[i][j];
     492                        }
     493                        values->n += overscan->numCols;
     494                        pixels->data[i] = values; // Update the pointer in case it's moved
     495                    }
     496                }
     497                psFree(iter);
     498
     499                // Reduce the overscans
     500                psVector *reduced = overscanVector(overscanOpts, pixels, myStats);
     501                psFree(pixels);
     502                if (! reduced) {
     503                    return in;
     504                }
     505
     506                // Subtract row by row
     507                for (int i = 0; i < image->numRows; i++) {
     508                    for (int j = 0; j < image->numCols; j++) {
     509                        image->data.F32[i][j] -= reduced->data.F32[i];
     510                    }
     511                }
     512                psFree(reduced);
     513
     514            } else {
     515                // The read direction is columns
     516                psArray *pixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels
     517                for (int i = 0; i < pixels->n; i++) {
     518                    psVector *values = psVectorAlloc(0, PS_TYPE_F32);
     519                    values->n = 0;
     520                    pixels->data[i] = values;
     521                }
     522
     523                // Pull the pixels out into the vectors
     524                psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
     525                psImage *overscan = NULL; // Overscan image from iterator
     526                while ((overscan = psListGetAndIncrement(iter))) {
     527                    int diff = image->col0 - overscan->col0; // Offset between the two regions
     528                    for (int i = MAX(0,diff); i < MIN(image->numCols, overscan->numCols + diff); i++) {
     529                        // i is column on overscan
     530                        // XXX Reimplement with memcpy
     531                        psVector *values = pixels->data[i];
     532                        int index = values->n; // Index in the vector
     533                        values = psVectorRealloc(values, values->n + overscan->numRows);
     534                        for (int j = 0; j < overscan->numRows; j++) {
     535                            values->data.F32[index++] = overscan->data.F32[i][j];
     536                        }
     537                        values->n += overscan->numRows;
     538                        pixels->data[i] = values; // Update the pointer in case it's moved
     539                    }
     540                }
     541                psFree(iter);
     542
     543                // Reduce the overscans
     544                psVector *reduced = overscanVector(overscanOpts, pixels, myStats);
     545                psFree(pixels);
     546                if (! reduced) {
     547                    return in;
     548                }
     549
     550                // Subtract column by column
     551                for (int i = 0; i < image->numCols; i++) {
     552                    for (int j = 0; j < image->numRows; j++) {
     553                        image->data.F32[j][i] -= reduced->data.F32[i];
     554                    }
     555                }
     556                psFree(reduced);
    1223557            }
    1224 
    1225             //
    1226             // Rebin the overscan vector if necessary.
    1227             //
    1228             psS32 newBin = RebinOverscanVector(overscanVector, nBin, myStats);
    1229             if (newBin < 0) {
    1230                 psError(PS_ERR_UNKNOWN, false, "Could rebin the overscan vector.  Returning in image\n");
    1231                 psFree(myStats);
    1232                 return(in);
    1233             }
    1234 
    1235             //
    1236             // If necessary, fit a psPolynomial or psSpline to the overscan vector.
    1237             // Then, unbin the overscan vector to appropriate length for the in image.
    1238             //
    1239             if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
    1240                 overscanVector = FitOverscanVectorAndUnbin(trimmedImg, overscanVector, overScanAxis, fitSpec, fit, newBin);
    1241                 if (overscanVector == NULL) {
    1242                     psError(PS_ERR_UNKNOWN, false, "Could not fit the polynomial or spline to the overscan vector.  Returning in image\n");
    1243                     psFree(myStats);
    1244                     return(in);
    1245                 }
    1246             } else {
    1247                 overscanVector = UnbinOverscanVector(trimmedImg, overscanVector, overScanAxis, newBin);
    1248             }
    1249 
    1250             //
    1251             // Subtract the overscan vector from the input image.
    1252             //
    1253             SubtractVectorFromImage(trimmedImg, overscanVector, overScanAxis);
    1254             psFree(myStats);
    1255             psFree(overscanVector);
    1256         }
    1257     }
    1258 
    1259     //
    1260     // Perform bias subtraction if necessary.
    1261     //
    1262     if (bias != NULL) {
    1263         if (PM_OKAY == AssertCodeBias(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) {
    1264             SubtractFrame(in, bias);
    1265         }
    1266     }
    1267 
    1268     //
    1269     // Perform dark subtraction if necessary.
    1270     //
    1271     if (dark != NULL) {
    1272         if (PM_OKAY == AssertCodeDark(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) {
    1273             psBool rc;
    1274             psF32 scale = 0.0;
    1275             if (in->parent != NULL) {
    1276                 scale = psMetadataLookupS32(&rc, in->parent->concepts, "CELL.DARKTIME");
    1277                 if (rc == false) {
    1278                     psLogMsg(__func__, PS_LOG_WARN,
    1279                              "WARNING: pmSubtractBias.(): could not determine CELL.FARKTIME from in->parent metadata.\n");
    1280                 }
    1281             }
    1282             SubtractDarkFrame(in, dark, scale);
    1283         }
    1284     }
    1285 
    1286     //
    1287     // All done.
    1288     //
    1289     psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
    1290             "---- pmSubtractBias() exit ----\n");
    1291     return(in);
    1292 }
    1293 
    1294 
     558        }
     559        psFree(myStats);
     560    } // End of overscan subtraction
     561
     562    // Bias frame subtraction
     563    if (bias) {
     564        SubtractFrame(in, bias, 1.0);
     565    }
     566
     567    if (dark) {
     568        // Get the scaling
     569        float inTime = psMetadataLookupF32(NULL, in->parent->concepts, "CELL.DARKTIME");
     570        float darkTime = psMetadataLookupF32(NULL, dark->parent->concepts, "CELL.DARKTIME");
     571        SubtractFrame(in, dark, inTime/darkTime);
     572    }
     573
     574    return in;
     575}
     576
     577
  • trunk/psModules/src/imsubtract/pmSubtractBias.h

    r6872 r6873  
    1111 *  @author GLG, MHPCC
    1212 *
    13  *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
    14  *  @date $Date: 2006-04-17 18:01:05 $
     13 *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
     14 *  @date $Date: 2006-04-17 18:10:08 $
    1515 *
    1616 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/imsubtract/pmSubtractSky.h

    r6872 r6873  
    66 *  @author GLG, MHPCC
    77 *
    8  *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
    9  *  @date $Date: 2006-04-17 18:01:05 $
     8 *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
     9 *  @date $Date: 2006-04-17 18:10:08 $
    1010 *
    1111 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/objects/pmModelGroup.c

    r6872 r6873  
    66 *  @author EAM, IfA
    77 *
    8  *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
    9  *  @date $Date: 2006-04-17 18:01:05 $
     8 *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
     9 *  @date $Date: 2006-04-17 18:10:08 $
    1010 *
    1111 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/objects/pmModelGroup.h

    r6872 r6873  
    99 *  @author EAM, IfA
    1010 *
    11  *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
    12  *  @date $Date: 2006-04-17 18:01:05 $
     11 *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
     12 *  @date $Date: 2006-04-17 18:10:08 $
    1313 *
    1414 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/objects/pmObjects.c

    r6511 r6873  
    66 *  @author EAM, IfA: significant modifications.
    77 *
    8  *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
    9  *  @date $Date: 2006-03-04 01:01:33 $
     8 *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
     9 *  @date $Date: 2006-04-17 18:10:08 $
    1010 *
    1111 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
     
    1919#include "pmObjects.h"
    2020#include "pmModelGroup.h"
    21 /******************************************************************************
    22 pmPeakAlloc(): Allocate the pmPeak data structure and set appropriate members.
    23 *****************************************************************************/
    24 pmPeak *pmPeakAlloc(psS32 x,
    25                     psS32 y,
    26                     psF32 counts,
    27                     pmPeakType class)
    28 {
    29     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    30     pmPeak *tmp = (pmPeak *) psAlloc(sizeof(pmPeak));
    31     tmp->x = x;
    32     tmp->y = y;
    33     tmp->counts = counts;
    34     tmp->class = class;
    3521
    36     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    37     return(tmp);
    38 }
    39 
    40 /******************************************************************************
    41 pmMomentsAlloc(): Allocate the pmMoments structure and initialize the members
    42 to zero.
    43 *****************************************************************************/
    44 pmMoments *pmMomentsAlloc()
    45 {
    46     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    47     pmMoments *tmp = (pmMoments *) psAlloc(sizeof(pmMoments));
    48     tmp->x = 0.0;
    49     tmp->y = 0.0;
    50     tmp->Sx = 0.0;
    51     tmp->Sy = 0.0;
    52     tmp->Sxy = 0.0;
    53     tmp->Sum = 0.0;
    54     tmp->Peak = 0.0;
    55     tmp->Sky = 0.0;
    56     tmp->nPixels = 0;
    57 
    58     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    59     return(tmp);
    60 }
    61 
    62 static void modelFree(pmModel *tmp)
    63 {
    64     psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
    65     psFree(tmp->params);
    66     psFree(tmp->dparams);
    67     psTrace(__func__, 4, "---- %s() end ----\n", __func__);
    68 }
    69 
    70 static void sourceFree(pmSource *tmp)
    71 {
    72     psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
    73     psFree(tmp->peak);
    74     psFree(tmp->pixels);
    75     psFree(tmp->weight);
    76     psFree(tmp->mask);
    77     psFree(tmp->moments);
    78     psFree(tmp->modelPSF);
    79     psFree(tmp->modelFLT);
    80     psTrace(__func__, 4, "---- %s() end ----\n", __func__);
    81 }
    82 
    83 /******************************************************************************
    84 getRowVectorFromImage(): a private function which simply returns a
    85 psVector containing the specified row of data from the psImage.
    86  
    87 XXX: Is there a better way to do this?
    88 XXX EAM: does this really need to alloc a new vector???
    89 *****************************************************************************/
    90 static psVector *getRowVectorFromImage(psImage *image,
    91                                        psU32 row)
    92 {
    93     psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
    94     PS_ASSERT_IMAGE_NON_NULL(image, NULL);
    95     PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
    96 
    97     psVector *tmpVector = psVectorAlloc(image->numCols, PS_TYPE_F32);
    98     tmpVector->n = tmpVector->nalloc;
    99     for (psU32 col = 0; col < image->numCols ; col++) {
    100         tmpVector->data.F32[col] = image->data.F32[row][col];
    101     }
    102     psTrace(__func__, 4, "---- %s() end ----\n", __func__);
    103     return(tmpVector);
    104 }
    105 
    106 /******************************************************************************
    107 myListAddPeak(): A private function which allocates a psArray, if the list
    108 argument is NULL, otherwise it adds the peak to that list.
    109 XXX EAM : changed the output to psArray
    110 XXX EAM : Switched row, col args
    111 XXX EAM : NOTE: this was changed in the call, so the new code is consistent
    112 *****************************************************************************/
    113 static psArray *myListAddPeak(psArray *list,
    114                               psS32 row,
    115                               psS32 col,
    116                               psF32 counts,
    117                               pmPeakType type)
    118 {
    119     psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
    120     pmPeak *tmpPeak = pmPeakAlloc(col, row, counts, type);
    121 
    122     if (list == NULL) {
    123         list = psArrayAlloc(100);
    124         list->n = 0;
    125     }
    126     psArrayAdd(list, 100, tmpPeak);
    127     psFree (tmpPeak);
    128     // XXX EAM : is this free appropriate?  (does psArrayAdd increment memory counter?)
    129 
    130     psTrace(__func__, 4, "---- %s() end ----\n", __func__);
    131     return(list);
    132 }
    133 
    134 
    135 /******************************************************************************
    136 bool checkRadius2(): private function which simply determines if the (x, y)
    137 point is within the radius of the specified peak.
    138  
    139 XXX: macro this for performance.
    140 XXX: this is rather inefficient - at least compute and compare against radius^2
    141 *****************************************************************************/
    142 static bool checkRadius2(psF32 xCenter,
    143                          psF32 yCenter,
    144                          psF32 radius,
    145                          psF32 x,
    146                          psF32 y)
    147 {
    148     psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
    149     /// XXX EAM should compare with hypot (x,y) for speed
    150     if ((PS_SQR(x - xCenter) + PS_SQR(y - yCenter)) < PS_SQR(radius)) {
    151         return(true);
    152     }
    153 
    154     psTrace(__func__, 4, "---- %s(false) end ----\n", __func__);
    155     return(false);
    156 }
    157 
    158 // XXX: Macro this.
    159 static bool isItInThisRegion(const psRegion valid,
    160                              psS32 x,
    161                              psS32 y)
    162 {
    163     psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
    164     if ((x >= valid.x0) &&
    165             (x <= valid.x1) &&
    166             (y >= valid.y0) &&
    167             (y <= valid.y1)) {
    168         psTrace(__func__, 4, "---- %s(true) end ----\n", __func__);
    169         return(true);
    170     }
    171     psTrace(__func__, 4, "---- %s(false) end ----\n", __func__);
    172     return(false);
    173 }
    174 
    175 /******************************************************************************
    176 findValue(source, level, row, col, dir): a private function which determines
    177 the column coordinate of the model function which has the value "level".  If
    178 dir equals 0, then you loop leftwards from the peak pixel, otherwise,
    179 rightwards.
    180  
    181 XXX: reverse order of row,col args?
    182  
    183 XXX: Input row/col are in image coords.
    184  
    185 XXX: The result is returned in image coords.
    186 *****************************************************************************/
    187 static psF32 findValue(pmSource *source,
    188                        psF32 level,
    189                        psU32 row,
    190                        psU32 col,
    191                        psU32 dir)
    192 {
    193     psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
    194     //
    195     // Convert coords to subImage space.
    196     //
    197     psU32 subRow = row - source->pixels->row0;
    198     psU32 subCol = col - source->pixels->col0;
    199 
    200     // Ensure that the starting column is allowable.
    201     if (!((0 <= subCol) && (subCol < source->pixels->numCols))) {
    202         psError(PS_ERR_UNKNOWN, true, "Starting column outside subImage range");
    203         psTrace(__func__, 4, "---- %s(NAN) end ----\n", __func__);
    204         return(NAN);
    205     }
    206     if (!((0 <= subRow) && (subRow < source->pixels->numRows))) {
    207         psTrace(__func__, 4, "---- %s(NAN) end ----\n", __func__);
    208         psError(PS_ERR_UNKNOWN, true, "Starting row outside subImage range");
    209         return(NAN);
    210     }
    211 
    212     // XXX EAM : i changed this to match pmModelEval above, but see
    213     // XXX EAM   the note below in pmSourceContour
    214     psF32 oldValue = pmModelEval(source->modelFLT, source->pixels, subCol, subRow);
    215     if (oldValue == level) {
    216         psTrace(__func__, 4, "---- %s() end ----\n", __func__);
    217         return(((psF32) (subCol + source->pixels->col0)));
    218     }
    219 
    220     //
    221     // We define variables incr and lastColumn so that we can use the same loop
    222     // whether we are stepping leftwards, or rightwards.
    223     //
    224     psS32 incr;
    225     psS32 lastColumn;
    226     if (dir == 0) {
    227         incr = -1;
    228         lastColumn = -1;
    229     } else {
    230         incr = 1;
    231         lastColumn = source->pixels->numCols;
    232     }
    233     subCol+=incr;
    234 
    235     while (subCol != lastColumn) {
    236         psF32 newValue = pmModelEval(source->modelFLT, source->pixels, subCol, subRow);
    237         if (oldValue == level) {
    238             psTrace(__func__, 4, "---- %s() end ----\n", __func__);
    239             return((psF32) (subCol + source->pixels->col0));
    240         }
    241 
    242         if ((newValue <= level) && (level <= oldValue)) {
    243             // This is simple linear interpolation.
    244             psTrace(__func__, 4, "---- %s() end ----\n", __func__);
    245             return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - newValue) / (oldValue - newValue)) );
    246         }
    247 
    248         if ((oldValue <= level) && (level <= newValue)) {
    249             // This is simple linear interpolation.
    250             psTrace(__func__, 4, "---- %s() end ----\n", __func__);
    251             return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - oldValue) / (newValue - oldValue)) );
    252         }
    253 
    254         subCol+=incr;
    255     }
    256 
    257     psTrace(__func__, 4, "---- %s(NAN) end ----\n", __func__);
    258     return(NAN);
    259 }
    260 
    261 /******************************************************************************
    262 pmModelAlloc(): Allocate the pmModel structure, along with its parameters,
    263 and initialize the type member.  Initialize the params to 0.0.
    264 XXX EAM: simplifying code with pmModelParameterCount
    265 *****************************************************************************/
    266 pmModel *pmModelAlloc(pmModelType type)
    267 {
    268     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    269     pmModel *tmp = (pmModel *) psAlloc(sizeof(pmModel));
    270 
    271     tmp->type = type;
    272     tmp->chisq = 0.0;
    273     tmp->nIter = 0;
    274     tmp->radius = 0;
    275     tmp->status = PM_MODEL_UNTRIED;
    276 
    277     psS32 Nparams = pmModelParameterCount(type);
    278     if (Nparams == 0) {
    279         psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
    280         return(NULL);
    281     }
    282 
    283     tmp->params  = psVectorAlloc(Nparams, PS_TYPE_F32);
    284     tmp->dparams = psVectorAlloc(Nparams, PS_TYPE_F32);
    285     tmp->params->n = tmp->params->nalloc;
    286     tmp->dparams->n = tmp->dparams->nalloc;
    287 
    288     for (psS32 i = 0; i < tmp->params->n; i++) {
    289         tmp->params->data.F32[i] = 0.0;
    290         tmp->dparams->data.F32[i] = 0.0;
    291     }
    292 
    293     psMemSetDeallocator(tmp, (psFreeFunc) modelFree);
    294     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    295     return(tmp);
    296 }
    297 
    298 /******************************************************************************
    299 XXX EAM : we can now free these pixels - memory ref is incremented now
    300 *****************************************************************************/
    301 
    302 /******************************************************************************
    303 pmSourceAlloc(): Allocate the pmSource structure and initialize its members
    304 to NULL.
    305 *****************************************************************************/
    306 pmSource *pmSourceAlloc()
    307 {
    308     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    309     pmSource *tmp = (pmSource *) psAlloc(sizeof(pmSource));
    310     tmp->peak = NULL;
    311     tmp->pixels = NULL;
    312     tmp->weight = NULL;
    313     tmp->mask = NULL;
    314     tmp->moments = NULL;
    315     tmp->modelPSF = NULL;
    316     tmp->modelFLT = NULL;
    317     tmp->type = 0;
    318     psMemSetDeallocator(tmp, (psFreeFunc) sourceFree);
    319 
    320     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    321     return(tmp);
    322 }
    323 
    324 /******************************************************************************
    325 pmFindVectorPeaks(vector, threshold): Find all local peaks in the given vector
    326 above the given threshold.  Returns a vector of type PS_TYPE_U32 containing
    327 the location (x value) of all peaks.
    328  
    329 XXX: What types should be supported?  Only F32 is implemented.
    330  
    331 XXX: We currently step through the input vector twice; once to determine the
    332 size of the output vector, then to set the values of the output vector.
    333 Depending upon actual use, this may need to be optimized.
    334 *****************************************************************************/
    335 psVector *pmFindVectorPeaks(const psVector *vector,
    336                             psF32 threshold)
    337 {
    338     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    339     PS_ASSERT_VECTOR_NON_NULL(vector, NULL);
    340     PS_ASSERT_VECTOR_NON_EMPTY(vector, NULL);
    341     PS_ASSERT_VECTOR_TYPE(vector, PS_TYPE_F32, NULL);
    342     int count = 0;
    343     int n = vector->n;
    344 
    345     //
    346     // Special case: the input vector has a single element.
    347     //
    348     if (n == 1) {
    349         psVector *tmpVector = NULL;
    350         ;
    351         if (vector->data.F32[0] > threshold) {
    352             tmpVector = psVectorAlloc(1, PS_TYPE_U32);
    353             tmpVector->n = 1;
    354             tmpVector->data.U32[0] = 0;
    355         } else {
    356             tmpVector = psVectorAlloc(0, PS_TYPE_U32);
    357         }
    358         psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    359         return(tmpVector);
    360     }
    361 
    362     //
    363     // Determine if first pixel is a peak
    364     //
    365     if ((vector->data.F32[0] > vector->data.F32[1]) &&
    366             (vector->data.F32[0] > threshold)) {
    367         count++;
    368     }
    369 
    370     //
    371     // Determine if interior pixels are peaks
    372     //
    373     for (psU32 i = 1; i < n-1 ; i++) {
    374         if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
    375                 (vector->data.F32[i] > vector->data.F32[i+1]) &&
    376                 (vector->data.F32[i] > threshold)) {
    377             count++;
    378         }
    379     }
    380 
    381     //
    382     // Determine if last pixel is a peak
    383     //
    384     if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
    385             (vector->data.F32[n-1] > threshold)) {
    386         count++;
    387     }
    388 
    389     //
    390     // We know how many peaks exist, so we now allocate a psVector to store
    391     // those peaks.
    392     //
    393     psVector *tmpVector = psVectorAlloc(count, PS_TYPE_U32);
    394     tmpVector->n = tmpVector->nalloc;
    395     count = 0;
    396 
    397     //
    398     // Determine if first pixel is a peak
    399     //
    400     if ((vector->data.F32[0] > vector->data.F32[1]) &&
    401             (vector->data.F32[0] > threshold)) {
    402         tmpVector->data.U32[count++] = 0;
    403     }
    404 
    405     //
    406     // Determine if interior pixels are peaks
    407     //
    408     for (psU32 i = 1; i < (n-1) ; i++) {
    409         if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
    410                 (vector->data.F32[i] > vector->data.F32[i+1]) &&
    411                 (vector->data.F32[i] > threshold)) {
    412             tmpVector->data.U32[count++] = i;
    413         }
    414     }
    415 
    416     //
    417     // Determine if last pixel is a peak
    418     //
    419     if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
    420             (vector->data.F32[n-1] > threshold)) {
    421         tmpVector->data.U32[count++] = n-1;
    422     }
    423 
    424     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    425     return(tmpVector);
    426 }
    427 
    428 
    429 /******************************************************************************
    430 pmFindImagePeaks(image, threshold): Find all local peaks in the given psImage
    431 above the given threshold.  Returns a psArray containing location (x/y value)
    432 of all peaks.
    433  
    434 XXX: I'm not convinced the peak type definition in the SDRS is mutually
    435 exclusive.  Some peaks can have multiple types.  Edges for sure.  Also, a
    436 digonal line with the same value at each point will have a peak for every
    437 point on that line.
    438  
    439 XXX: This does not work if image has either a single row, or a single column.
    440  
    441 XXX: In the output psArray elements, should we use the image row/column offsets?
    442      Currently, we do not.
    443  
    444 XXX: Merge with CVS 1.20.  This had the proper code for images with a single
    445 row or column.
    446 *****************************************************************************/
    447 psArray *pmFindImagePeaks(const psImage *image,
    448                           psF32 threshold)
    449 {
    450     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    451     PS_ASSERT_IMAGE_NON_NULL(image, NULL);
    452     PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
    453     if ((image->numRows == 1) || (image->numCols == 1)) {
    454         psError(PS_ERR_UNKNOWN, true, "Currently, input image must have at least 2 rows and 2 columns.");
    455         psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
    456         return(NULL);
    457     }
    458     psVector *tmpRow = NULL;
    459     psU32 col = 0;
    460     psU32 row = 0;
    461     psArray *list = NULL;
    462 
    463     //
    464     // Find peaks in row 0 only.
    465     //
    466     row = 0;
    467     tmpRow = getRowVectorFromImage((psImage *) image, row);
    468     psVector *row1 = pmFindVectorPeaks(tmpRow, threshold);
    469 
    470     for (psU32 i = 0 ; i < row1->n ; i++ ) {
    471         col = row1->data.U32[i];
    472         //
    473         // Determine if pixel (0,0) is a peak.
    474         //
    475         if (col == 0) {
    476             if ( (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
    477                     (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
    478                     (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
    479 
    480                 if (image->data.F32[row][col] > threshold) {
    481                     list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
    482                 }
    483             }
    484         } else if (col < (image->numCols - 1)) {
    485             if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
    486                     (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
    487                     (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
    488                     (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
    489                     (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
    490                 if (image->data.F32[row][col] > threshold) {
    491                     list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
    492                 }
    493             }
    494 
    495         } else if (col == (image->numCols - 1)) {
    496             if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
    497                     (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
    498                     (image->data.F32[row][col] >= image->data.F32[row+1][col-1])) {
    499                 if (image->data.F32[row][col] > threshold) {
    500                     list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
    501                 }
    502             }
    503 
    504         } else {
    505             psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
    506         }
    507     }
    508     psFree (tmpRow);
    509     psFree (row1);
    510 
    511     //
    512     // Exit if this image has a single row.
    513     //
    514     if (image->numRows == 1) {
    515         psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    516         return(list);
    517     }
    518 
    519     //
    520     // Find peaks in interior rows only.
    521     //
    522     for (row = 1 ; row < (image->numRows - 1) ; row++) {
    523         tmpRow = getRowVectorFromImage((psImage *) image, row);
    524         row1 = pmFindVectorPeaks(tmpRow, threshold);
    525 
    526         // Step through all local peaks in this row.
    527         for (psU32 i = 0 ; i < row1->n ; i++ ) {
    528             pmPeakType myType = PM_PEAK_UNDEF;
    529             col = row1->data.U32[i];
    530 
    531             if (col == 0) {
    532                 // If col==0, then we can not read col-1 pixels
    533                 if ((image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
    534                         (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
    535                         (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
    536                         (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
    537                         (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
    538                     myType = PM_PEAK_EDGE;
    539                     list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
    540                 }
    541             } else if (col < (image->numCols - 1)) {
    542                 // This is an interior pixel
    543                 if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
    544                         (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
    545                         (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
    546                         (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
    547                         (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
    548                         (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
    549                         (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
    550                         (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
    551                     if (image->data.F32[row][col] > threshold) {
    552                         if ((image->data.F32[row][col] > image->data.F32[row-1][col-1]) &&
    553                                 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
    554                                 (image->data.F32[row][col] > image->data.F32[row-1][col+1]) &&
    555                                 (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
    556                                 (image->data.F32[row][col] > image->data.F32[row][col+1]) &&
    557                                 (image->data.F32[row][col] > image->data.F32[row+1][col-1]) &&
    558                                 (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
    559                                 (image->data.F32[row][col] > image->data.F32[row+1][col+1])) {
    560                             myType = PM_PEAK_LONE;
    561                         }
    562 
    563                         if ((image->data.F32[row][col] == image->data.F32[row-1][col-1]) ||
    564                                 (image->data.F32[row][col] == image->data.F32[row-1][col]) ||
    565                                 (image->data.F32[row][col] == image->data.F32[row-1][col+1]) ||
    566                                 (image->data.F32[row][col] == image->data.F32[row][col-1]) ||
    567                                 (image->data.F32[row][col] == image->data.F32[row][col+1]) ||
    568                                 (image->data.F32[row][col] == image->data.F32[row+1][col-1]) ||
    569                                 (image->data.F32[row][col] == image->data.F32[row+1][col]) ||
    570                                 (image->data.F32[row][col] == image->data.F32[row+1][col+1])) {
    571                             myType = PM_PEAK_FLAT;
    572                         }
    573 
    574                         list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
    575                     }
    576                 }
    577             } else if (col == (image->numCols - 1)) {
    578                 // If col==numCols - 1, then we can not read col+1 pixels
    579                 if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
    580                         (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
    581                         (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
    582                         (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
    583                         (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
    584                         (image->data.F32[row][col] >= image->data.F32[row+1][col])) {
    585                     myType = PM_PEAK_EDGE;
    586                     list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
    587                 }
    588             } else {
    589                 psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
    590             }
    591 
    592         }
    593         psFree (tmpRow);
    594         psFree (row1);
    595     }
    596 
    597     //
    598     // Find peaks in the last row only.
    599     //
    600     row = image->numRows - 1;
    601     tmpRow = getRowVectorFromImage((psImage *) image, row);
    602     row1 = pmFindVectorPeaks(tmpRow, threshold);
    603     for (psU32 i = 0 ; i < row1->n ; i++ ) {
    604         col = row1->data.U32[i];
    605         if (col == 0) {
    606             if ( (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
    607                     (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
    608                     (image->data.F32[row][col] >  image->data.F32[row][col+1])) {
    609                 if (image->data.F32[row][col] > threshold) {
    610                     list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
    611                 }
    612             }
    613         } else if (col < (image->numCols - 1)) {
    614             if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
    615                     (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
    616                     (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
    617                     (image->data.F32[row][col] >  image->data.F32[row][col-1]) &&
    618                     (image->data.F32[row][col] >= image->data.F32[row][col+1])) {
    619                 if (image->data.F32[row][col] > threshold) {
    620                     list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
    621                 }
    622             }
    623 
    624         } else if (col == (image->numCols - 1)) {
    625             if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
    626                     (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
    627                     (image->data.F32[row][col] >  image->data.F32[row][col-1])) {
    628                 if (image->data.F32[row][col] > threshold) {
    629                     list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
    630                 }
    631             }
    632         } else {
    633             psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
    634         }
    635     }
    636     psFree (tmpRow);
    637     psFree (row1);
    638     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    639     return(list);
    640 }
    641 
    642 
    643 /******************************************************************************
    644 psCullPeaks(peaks, maxValue, valid): eliminate peaks from the psArray that have
    645 a peak value above the given maximum, or fall outside the valid region.
    646  
    647 XXX: Should the sky value be used when comparing the maximum?
    648  
    649 XXX: warning message if valid is NULL?
    650  
    651 XXX: changed API to create a NEW output psArray (should change name as well)
    652  
    653 XXX: Do we free the psList elements of those culled peaks?
    654  
    655 XXX EAM : do we still need pmCullPeaks, or only pmPeaksSubset?
    656 *****************************************************************************/
    657 psList *pmCullPeaks(psList *peaks,
    658                     psF32 maxValue,
    659                     const psRegion valid)
    660 {
    661     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    662     PS_ASSERT_PTR_NON_NULL(peaks, NULL);
    663 
    664     psListElem *tmpListElem = (psListElem *) peaks->head;
    665     psS32 indexNum = 0;
    666 
    667     //    printf("pmCullPeaks(): list size is %d\n", peaks->size);
    668     while (tmpListElem != NULL) {
    669         pmPeak *tmpPeak = (pmPeak *) tmpListElem->data;
    670         if ((tmpPeak->counts > maxValue) ||
    671                 (true == isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))) {
    672             psListRemoveData(peaks, (psPtr) tmpPeak);
    673         }
    674 
    675         indexNum++;
    676         tmpListElem = tmpListElem->next;
    677     }
    678 
    679     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    680     return(peaks);
    681 }
    682 
    683 // XXX EAM: I changed this to return a new, subset array
    684 //          rather than alter the existing one
    685 // XXX: Fix the *valid pointer.
    686 psArray *pmPeaksSubset(
    687     psArray *peaks,
    688     psF32 maxValue,
    689     const psRegion valid)
    690 {
    691     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    692     PS_ASSERT_PTR_NON_NULL(peaks, NULL);
    693 
    694     psArray *output = psArrayAlloc (200);
    695     output->n = 0;
    696 
    697     psTrace (".pmObjects.pmCullPeaks", 3, "list size is %d\n", peaks->n);
    698 
    699     for (int i = 0; i < peaks->n; i++) {
    700         pmPeak *tmpPeak = (pmPeak *) peaks->data[i];
    701         if (tmpPeak->counts > maxValue)
    702             continue;
    703         if (isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))
    704             continue;
    705         psArrayAdd (output, 200, tmpPeak);
    706     }
    707     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    708     return(output);
    709 }
    710 
    711 /******************************************************************************
    712 pmSource *pmSourceLocalSky(image, peak, innerRadius, outerRadius): this
    713 routine creates a new pmSource data structure and sets the following members:
    714     ->pmPeak
    715     ->pmMoments->sky
    716  
    717 The sky value is set from the pixels in the square annulus surrounding the
    718 peak pixel.
    719  
    720 We simply create a subSet image and mask the inner pixels, then call
    721 psImageStats on that subImage+mask.
    722  
    723 XXX: The subImage has width of 1+2*outerRadius.  Verify with IfA.
    724  
    725 XXX: Use static data structures for:
    726      subImage
    727      subImageMask
    728      myStats
    729  
    730 XXX: ensure that the inner and out radius fit in the actual image.  Should
    731      we generate an error, or warning?  Currently an error.
    732  
    733 XXX: Sync with IfA on whether the peak x/y coords are data structure coords,
    734      or they use the image row/column offsets.
    735  
    736 XXX: Should we simply set pmSource->peak = peak?  If so, should we increase
    737 the reference counter?  Or, should we copy the data structure?
    738  
    739 XXX: Currently the subimage always has an even number of rows/columns.  Is
    740      this correct?  Since there is a center pixel, maybe it should have an
    741      odd number of rows/columns.
    742  
    743 XXX: Use psTrace() for the print statements.
    744  
    745 XXX: Don't use separate structs for the subimage and mask.  Use the source->
    746      members.
    747 *****************************************************************************/
    748 
    749 bool pmSourceLocalSky(
    750     pmSource *source,
    751     psStatsOptions statsOptions,
    752     psF32 Radius)
    753 {
    754     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    755     PS_ASSERT_PTR_NON_NULL(source, false);
    756     PS_ASSERT_IMAGE_NON_NULL(source->pixels, false);
    757     PS_ASSERT_IMAGE_NON_NULL(source->mask, false);
    758     PS_ASSERT_PTR_NON_NULL(source->peak, false);
    759     PS_ASSERT_INT_POSITIVE(Radius, false);
    760     PS_ASSERT_INT_NONNEGATIVE(Radius, false);
    761 
    762     psImage *image = source->pixels;
    763     psImage *mask  = source->mask;
    764     pmPeak *peak  = source->peak;
    765     psRegion srcRegion;
    766 
    767     srcRegion = psRegionForSquare(peak->x, peak->y, Radius);
    768     srcRegion = psRegionForImage(mask, srcRegion);
    769 
    770     psImageMaskRegion(mask, srcRegion, "OR", PSPHOT_MASK_MARKED);
    771     psStats *myStats = psStatsAlloc(statsOptions);
    772     myStats = psImageStats(myStats, image, mask, 0xff);
    773     psImageMaskRegion(mask, srcRegion, "AND", ~PSPHOT_MASK_MARKED);
    774 
    775     psF64 tmpF64;
    776     p_psGetStatValue(myStats, &tmpF64);
    777     psFree(myStats);
    778 
    779     if (isnan(tmpF64)) {
    780         psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
    781         return(false);
    782     }
    783     source->moments = pmMomentsAlloc();
    784     source->moments->Sky = (psF32) tmpF64;
    785     psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);
    786     return (true);
    787 }
    788 
    789 /******************************************************************************
    790 pmSourceMoments(source, radius): this function takes a subImage defined in the
    791 pmSource data structure, along with the peak location, and determines the
    792 various moments associated with that peak.
    793  
    794 Requires the following to have been created:
    795     pmSource
    796     pmSource->peak
    797     pmSource->pixels
    798     pmSource->weight
    799     pmSource->mask
    800  
    801 XXX: The peak calculations are done in image coords, not subImage coords.
    802  
    803 XXX EAM : this version clips input pixels on S/N
    804 XXX EAM : this version returns false for several reasons
    805 *****************************************************************************/
    806 # define VALID_RADIUS(X,Y,RAD2) (((RAD2) >= (PS_SQR(X) + PS_SQR(Y))) ? 1 : 0)
    807 
    808 bool pmSourceMoments(pmSource *source,
    809                      psF32 radius)
    810 {
    811     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    812     PS_ASSERT_PTR_NON_NULL(source, false);
    813     PS_ASSERT_PTR_NON_NULL(source->peak, false);
    814     PS_ASSERT_PTR_NON_NULL(source->pixels, false);
    815     PS_ASSERT_PTR_NON_NULL(source->mask, false);
    816     PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
    817 
    818     //
    819     // XXX: Verify the setting for sky if source->moments == NULL.
    820     //
    821     psF32 sky = 0.0;
    822     if (source->moments == NULL) {
    823         source->moments = pmMomentsAlloc();
    824     } else {
    825         sky = source->moments->Sky;
    826     }
    827 
    828     //
    829     // Sum = SUM (z - sky)
    830     // X1  = SUM (x - xc)*(z - sky)
    831     // X2  = SUM (x - xc)^2 * (z - sky)
    832     // XY  = SUM (x - xc)*(y - yc)*(z - sky)
    833     //
    834     psF32 peakPixel = -PS_MAX_F32;
    835     psS32 numPixels = 0;
    836     psF32 Sum = 0.0;
    837     psF32 X1 = 0.0;
    838     psF32 Y1 = 0.0;
    839     psF32 X2 = 0.0;
    840     psF32 Y2 = 0.0;
    841     psF32 XY = 0.0;
    842     psF32 x  = 0;
    843     psF32 y  = 0;
    844     psF32 R2 = PS_SQR(radius);
    845 
    846     psF32 xPeak = source->peak->x;
    847     psF32 yPeak = source->peak->y;
    848 
    849     // XXX why do I get different results for these two methods of finding Sx?
    850     // XXX Sx, Sy would be better measured if we clip pixels close to sky
    851     // XXX Sx, Sy can still be imaginary, so we probably need to keep Sx^2?
    852     // We loop through all pixels in this subimage (source->pixels), and for each
    853     // pixel that is not masked, AND within the radius of the peak pixel, we
    854     // proceed with the moments calculation.  need to do two loops for a
    855     // numerically stable result.  first loop: get the sums.
    856     // XXX EAM : mask == 0 is valid
    857 
    858     for (psS32 row = 0; row < source->pixels->numRows ; row++) {
    859         for (psS32 col = 0; col < source->pixels->numCols ; col++) {
    860             if ((source->mask != NULL) && (source->mask->data.U8[row][col])) {
    861                 continue;
    862             }
    863 
    864             psF32 xDiff = col + source->pixels->col0 - xPeak;
    865             psF32 yDiff = row + source->pixels->row0 - yPeak;
    866 
    867             // XXX EAM : calculate xDiff, yDiff up front;
    868             //           radius is just a function of (xDiff, yDiff)
    869             if (!VALID_RADIUS(xDiff, yDiff, R2)) {
    870                 continue;
    871             }
    872 
    873             psF32 pDiff = source->pixels->data.F32[row][col] - sky;
    874 
    875             // XXX EAM : check for valid S/N in pixel
    876             // XXX EAM : should this limit be user-defined?
    877             if (pDiff / sqrt(source->weight->data.F32[row][col]) < 1) {
    878                 continue;
    879             }
    880 
    881             Sum += pDiff;
    882             X1  += xDiff * pDiff;
    883             Y1  += yDiff * pDiff;
    884             XY  += xDiff * yDiff * pDiff;
    885 
    886             X2  += PS_SQR(xDiff) * pDiff;
    887             Y2  += PS_SQR(yDiff) * pDiff;
    888 
    889             peakPixel = PS_MAX (source->pixels->data.F32[row][col], peakPixel);
    890             numPixels++;
    891         }
    892     }
    893     // XXX EAM - the limit is a bit arbitrary.  make it user defined?
    894     if ((numPixels < 3) || (Sum <= 0)) {
    895         psTrace (".psModules.pmSourceMoments", 5, "no valid pixels for source\n");
    896         psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
    897         return (false);
    898     }
    899 
    900     psTrace (".psModules.pmSourceMoments", 5,
    901              "sky: %f  Sum: %f  X1: %f  Y1: %f  X2: %f  Y2: %f  XY: %f  Npix: %d\n",
    902              sky, Sum, X1, Y1, X2, Y2, XY, numPixels);
    903 
    904     //
    905     // first moment X  = X1/Sum + xc
    906     // second moment X = sqrt (X2/Sum - (X1/Sum)^2)
    907     // Sxy             = XY / Sum
    908     //
    909     x = X1/Sum;
    910     y = Y1/Sum;
    911     if ((fabs(x) > radius) || (fabs(y) > radius)) {
    912         psTrace (".psModules.pmSourceMoments", 5,
    913                  "large centroid swing; invalid peak %d, %d\n",
    914                  source->peak->x, source->peak->y);
    915         psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
    916         return (false);
    917     }
    918 
    919     source->moments->x = x + xPeak;
    920     source->moments->y = y + yPeak;
    921 
    922     // XXX EAM : Sxy needs to have x*y subtracted
    923     source->moments->Sxy = XY/Sum - x*y;
    924     source->moments->Sum = Sum;
    925     source->moments->Peak = peakPixel;
    926     source->moments->nPixels = numPixels;
    927 
    928     // XXX EAM : these values can be negative, so we need to limit the range
    929     source->moments->Sx = sqrt(PS_MAX(X2/Sum - PS_SQR(x), 0));
    930     source->moments->Sy = sqrt(PS_MAX(Y2/Sum - PS_SQR(y), 0));
    931 
    932     psTrace (".psModules.pmSourceMoments", 4,
    933              "sky: %f  Sum: %f  x: %f  y: %f  Sx: %f  Sy: %f  Sxy: %f\n",
    934              sky, Sum, source->moments->x, source->moments->y,
    935              source->moments->Sx, source->moments->Sy, source->moments->Sxy);
    936 
    937     psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);
    938     return(true);
    939 }
    940 
    941 // XXX EAM : I used
    942 int pmComparePeakAscend (const void **a, const void **b)
    943 {
    944     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    945     pmPeak *A = *(pmPeak **)a;
    946     pmPeak *B = *(pmPeak **)b;
    947 
    948     psF32 diff;
    949 
    950     diff = A->counts - B->counts;
    951     if (diff < FLT_EPSILON) {
    952         psTrace(__func__, 3, "---- %s(-1) end ----\n", __func__);
    953         return (-1);
    954     } else if (diff > FLT_EPSILON) {
    955         psTrace(__func__, 3, "---- %s(+1) end ----\n", __func__);
    956         return (+1);
    957     }
    958     psTrace(__func__, 3, "---- %s(0) end ----\n", __func__);
    959     return (0);
    960 }
    961 
    962 int pmComparePeakDescend (const void **a, const void **b)
    963 {
    964     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    965     pmPeak *A = *(pmPeak **)a;
    966     pmPeak *B = *(pmPeak **)b;
    967 
    968     psF32 diff;
    969 
    970     diff = A->counts - B->counts;
    971     if (diff < FLT_EPSILON) {
    972         psTrace(__func__, 3, "---- %s(+1) end ----\n", __func__);
    973         return (+1);
    974     } else if (diff > FLT_EPSILON) {
    975         psTrace(__func__, 3, "---- %s(-1) end ----\n", __func__);
    976         return (-1);
    977     }
    978     psTrace(__func__, 3, "---- %s(0) end ----\n", __func__);
    979     return (0);
    980 }
    981 
    982 /******************************************************************************
    983 pmSourcePSFClump(source, metadata): Find the likely PSF clump in the
    984 sigma-x, sigma-y plane. return 0,0 clump in case of error.
    985 *****************************************************************************/
    986 
    987 // XXX EAM include a S/N cutoff in selecting the sources?
    988 pmPSFClump pmSourcePSFClump(psArray *sources, psMetadata *metadata)
    989 {
    990     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    991 
    992     # define NPIX 10
    993     # define SCALE 0.1
    994 
    995     psArray *peaks  = NULL;
    996     pmPSFClump emptyClump = {0.0, 0.0, 0.0, 0.0};
    997     pmPSFClump psfClump = emptyClump;
    998 
    999     PS_ASSERT_PTR_NON_NULL(sources, emptyClump);
    1000     PS_ASSERT_PTR_NON_NULL(metadata, emptyClump);
    1001 
    1002     // find the sigmaX, sigmaY clump
    1003     {
    1004         psStats *stats  = NULL;
    1005         psImage *splane = NULL;
    1006         int binX, binY;
    1007         bool status;
    1008 
    1009         psF32 SX_MAX = psMetadataLookupF32 (&status, metadata, "MOMENTS_SX_MAX");
    1010         if (!status)
    1011             SX_MAX = 10.0;
    1012         psF32 SY_MAX = psMetadataLookupF32 (&status, metadata, "MOMENTS_SY_MAX");
    1013         if (!status)
    1014             SY_MAX = 10.0;
    1015 
    1016         // construct a sigma-plane image
    1017         // psImageAlloc does zero the data
    1018         splane = psImageAlloc (SX_MAX/SCALE, SY_MAX/SCALE, PS_TYPE_F32);
    1019         for (int i = 0; i < splane->numRows; i++)
    1020         {
    1021             memset (splane->data.F32[i], 0, splane->numCols*sizeof(PS_TYPE_F32));
    1022         }
    1023 
    1024         // place the sources in the sigma-plane image (ignore 0,0 values?)
    1025         for (psS32 i = 0 ; i < sources->n ; i++)
    1026         {
    1027             pmSource *tmpSrc = (pmSource *) sources->data[i];
    1028             if (tmpSrc == NULL) {
    1029                 continue;
    1030             }
    1031             if (tmpSrc->moments == NULL) {
    1032                 continue;
    1033             }
    1034 
    1035             // Sx,Sy are limited at 0.  a peak at 0,0 is artificial
    1036             if ((fabs(tmpSrc->moments->Sx) < FLT_EPSILON) && (fabs(tmpSrc->moments->Sy) < FLT_EPSILON)) {
    1037                 continue;
    1038             }
    1039 
    1040             // for the moment, force splane dimensions to be 10x10 image pix
    1041             binX = tmpSrc->moments->Sx/SCALE;
    1042             if (binX < 0)
    1043                 continue;
    1044             if (binX >= splane->numCols)
    1045                 continue;
    1046 
    1047             binY = tmpSrc->moments->Sy/SCALE;
    1048             if (binY < 0)
    1049                 continue;
    1050             if (binY >= splane->numRows)
    1051                 continue;
    1052 
    1053             splane->data.F32[binY][binX] += 1.0;
    1054         }
    1055 
    1056         // find the peak in this image
    1057         stats = psStatsAlloc (PS_STAT_MAX);
    1058         stats = psImageStats (stats, splane, NULL, 0);
    1059         peaks = pmFindImagePeaks (splane, stats[0].max / 2);
    1060         psTrace (".pmObjects.pmSourceRoughClass", 2, "clump threshold is %f\n", stats[0].max/2);
    1061 
    1062         psFree (splane);
    1063         psFree (stats);
    1064 
    1065     }
    1066     // XXX EAM : possible errors:
    1067     //           1) no peak in splane
    1068     //           2) no significant peak in splane
    1069 
    1070     // measure statistics on Sx, Sy if Sx, Sy within range of clump
    1071     {
    1072         pmPeak *clump;
    1073         psF32 minSx, maxSx;
    1074         psF32 minSy, maxSy;
    1075         psVector *tmpSx = NULL;
    1076         psVector *tmpSy = NULL;
    1077         psStats *stats  = NULL;
    1078 
    1079         // XXX EAM : this lets us takes the single highest peak
    1080         psArraySort (peaks, pmComparePeakDescend);
    1081         clump = peaks->data[0];
    1082         psTrace (".pmObjects.pmSourceRoughClass", 2, "clump is at %d, %d (%f)\n", clump->x, clump->y, clump->counts);
    1083 
    1084         // define section window for clump
    1085         minSx = clump->x * SCALE - 0.2;
    1086         maxSx = clump->x * SCALE + 0.2;
    1087         minSy = clump->y * SCALE - 0.2;
    1088         maxSy = clump->y * SCALE + 0.2;
    1089 
    1090         tmpSx = psVectorAlloc (sources->n, PS_TYPE_F32);
    1091         tmpSy = psVectorAlloc (sources->n, PS_TYPE_F32);
    1092         tmpSx->n = 0;
    1093         tmpSy->n = 0;
    1094 
    1095         // XXX clip sources based on flux?
    1096         // create vectors with Sx, Sy values in window
    1097         for (psS32 i = 0 ; i < sources->n ; i++)
    1098         {
    1099             pmSource *tmpSrc = (pmSource *) sources->data[i];
    1100 
    1101             if (tmpSrc->moments->Sx < minSx)
    1102                 continue;
    1103             if (tmpSrc->moments->Sx > maxSx)
    1104                 continue;
    1105             if (tmpSrc->moments->Sy < minSy)
    1106                 continue;
    1107             if (tmpSrc->moments->Sy > maxSy)
    1108                 continue;
    1109             tmpSx->data.F32[tmpSx->n] = tmpSrc->moments->Sx;
    1110             tmpSy->data.F32[tmpSy->n] = tmpSrc->moments->Sy;
    1111             tmpSx->n++;
    1112             tmpSy->n++;
    1113             if (tmpSx->n == tmpSx->nalloc) {
    1114                 psVectorRealloc (tmpSx, tmpSx->nalloc + 100);
    1115                 psVectorRealloc (tmpSy, tmpSy->nalloc + 100);
    1116             }
    1117         }
    1118 
    1119         // measures stats of Sx, Sy
    1120         stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
    1121 
    1122         stats = psVectorStats (stats, tmpSx, NULL, NULL, 0);
    1123         psfClump.X  = stats->clippedMean;
    1124         psfClump.dX = stats->clippedStdev;
    1125 
    1126         stats = psVectorStats (stats, tmpSy, NULL, NULL, 0);
    1127         psfClump.Y  = stats->clippedMean;
    1128         psfClump.dY = stats->clippedStdev;
    1129 
    1130         psTrace (".pmObjects.pmSourceRoughClass", 2, "clump  X,  Y: %f, %f\n", psfClump.X, psfClump.Y);
    1131         psTrace (".pmObjects.pmSourceRoughClass", 2, "clump DX, DY: %f, %f\n", psfClump.dX, psfClump.dY);
    1132         // these values should be pushed on the metadata somewhere
    1133 
    1134         psFree (stats);
    1135         psFree (peaks);
    1136         psFree (tmpSx);
    1137         psFree (tmpSy);
    1138     }
    1139 
    1140     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    1141     return (psfClump);
    1142 }
    1143 
    1144 /******************************************************************************
    1145 pmSourceRoughClass(source, metadata): make a guess at the source
    1146 classification.
    1147  
    1148 XXX: push the clump info into the metadata?
    1149  
    1150 XXX: How can this function ever return FALSE?
    1151  
    1152 XXX EAM : add the saturated mask value to metadata
    1153 *****************************************************************************/
    1154 
    1155 bool pmSourceRoughClass(psArray *sources, psMetadata *metadata, pmPSFClump clump)
    1156 {
    1157     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1158 
    1159     psBool rc = true;
    1160 
    1161     int Nsat     = 0;
    1162     int Ngal     = 0;
    1163     int Nstar    = 0;
    1164     int Npsf     = 0;
    1165     int Ncr      = 0;
    1166     int Nsatstar = 0;
    1167     psRegion allArray = psRegionSet (0, 0, 0, 0);
    1168 
    1169     psVector *starsn = psVectorAlloc (sources->n, PS_TYPE_F32);
    1170     starsn->n = 0;
    1171 
    1172     // check return status value (do these exist?)
    1173     bool status;
    1174     psF32 RDNOISE    = psMetadataLookupF32 (&status, metadata, "RDNOISE");
    1175     psF32 GAIN       = psMetadataLookupF32 (&status, metadata, "GAIN");
    1176     psF32 PSF_SN_LIM = psMetadataLookupF32 (&status, metadata, "PSF_SN_LIM");
    1177     // psF32 SATURATE = psMetadataLookupF32 (&status, metadata, "SATURATE");
    1178 
    1179     // XXX allow clump size to be scaled relative to sigmas?
    1180     // make rough IDs based on clumpX,Y,DX,DY
    1181     for (psS32 i = 0 ; i < sources->n ; i++) {
    1182 
    1183         pmSource *tmpSrc = (pmSource *) sources->data[i];
    1184 
    1185         tmpSrc->peak->class = 0;
    1186 
    1187         psF32 sigX = tmpSrc->moments->Sx;
    1188         psF32 sigY = tmpSrc->moments->Sy;
    1189 
    1190         // calculate and save signal-to-noise estimates
    1191         psF32 S  = tmpSrc->moments->Sum;
    1192         psF32 A  = 4 * M_PI * sigX * sigY;
    1193         psF32 B  = tmpSrc->moments->Sky;
    1194         psF32 RT = sqrt(S + (A * B) + (A * PS_SQR(RDNOISE) / sqrt(GAIN)));
    1195         psF32 SN = (S * sqrt(GAIN) / RT);
    1196         tmpSrc->moments->SN = SN;
    1197 
    1198         // XXX EAM : can we use the value of SATURATE if mask is NULL?
    1199         int Nsatpix = psImageCountPixelMask (tmpSrc->mask, allArray, PSPHOT_MASK_SATURATED);
    1200 
    1201         // saturated star (size consistent with PSF or larger)
    1202         // Nsigma should be user-configured parameter
    1203         bool big = (sigX > (clump.X - clump.dX)) && (sigY > (clump.Y - clump.dY));
    1204         if ((Nsatpix > 1) && big) {
    1205             tmpSrc->type = PM_SOURCE_SATSTAR;
    1206             Nsatstar ++;
    1207             continue;
    1208         }
    1209 
    1210         // saturated object (not a star, eg bleed trails, hot pixels)
    1211         if (Nsatpix > 1) {
    1212             tmpSrc->type = PM_SOURCE_SATURATED;
    1213             Nsat ++;
    1214             continue;
    1215         }
    1216 
    1217         // likely defect (too small to be stellar) (push out to 3 sigma)
    1218         // low S/N objects which are small are probably stellar
    1219         // only set candidate defects if
    1220         if ((sigX < 0.05) || (sigY < 0.05)) {
    1221             tmpSrc->type = PM_SOURCE_DEFECT;
    1222             Ncr ++;
    1223             continue;
    1224         }
    1225 
    1226         // likely unsaturated galaxy (too large to be stellar)
    1227         if ((sigX > (clump.X + 3*clump.dX)) || (sigY > (clump.Y + 3*clump.dY))) {
    1228             tmpSrc->type = PM_SOURCE_GALAXY;
    1229             Ngal ++;
    1230             continue;
    1231         }
    1232 
    1233         // the rest are probable stellar objects
    1234         starsn->data.F32[starsn->n] = SN;
    1235         starsn->n ++;
    1236         Nstar ++;
    1237 
    1238         // PSF star (within 1.5 sigma of clump center
    1239         psF32 radius = hypot ((sigX-clump.X)/clump.dX, (sigY-clump.Y)/clump.dY);
    1240         if ((SN > PSF_SN_LIM) && (radius < 1.5)) {
    1241             tmpSrc->type = PM_SOURCE_PSFSTAR;
    1242             Npsf ++;
    1243             continue;
    1244         }
    1245 
    1246         // random type of star
    1247         tmpSrc->type = PM_SOURCE_OTHER;
    1248     }
    1249 
    1250     {
    1251         psStats *stats  = NULL;
    1252         stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
    1253         stats = psVectorStats (stats, starsn, NULL, NULL, 0);
    1254         psLogMsg ("pmObjects", 3, "SN range: %f - %f\n", stats[0].min, stats[0].max);
    1255         psFree (stats);
    1256         psFree (starsn);
    1257     }
    1258 
    1259     psTrace (".pmObjects.pmSourceRoughClass", 2, "Nstar:    %3d\n", Nstar);
    1260     psTrace (".pmObjects.pmSourceRoughClass", 2, "Npsf:     %3d\n", Npsf);
    1261     psTrace (".pmObjects.pmSourceRoughClass", 2, "Ngal:     %3d\n", Ngal);
    1262     psTrace (".pmObjects.pmSourceRoughClass", 2, "Nsatstar: %3d\n", Nsatstar);
    1263     psTrace (".pmObjects.pmSourceRoughClass", 2, "Nsat:     %3d\n", Nsat);
    1264     psTrace (".pmObjects.pmSourceRoughClass", 2, "Ncr:      %3d\n", Ncr);
    1265 
    1266     psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
    1267     return(rc);
    1268 }
    1269 
    1270 /** pmSourceDefinePixels()
    1271  *
    1272  * Define psImage subarrays for the source located at coordinates x,y on the
    1273  * image set defined by readout. The pixels defined by this operation consist of
    1274  * a square window (of full width 2Radius+1) centered on the pixel which contains
    1275  * the given coordinate, in the frame of the readout. The window is defined to
    1276  * have limits which are valid within the boundary of the readout image, thus if
    1277  * the radius would fall outside the image pixels, the subimage is truncated to
    1278  * only consist of valid pixels. If readout->mask or readout->weight are not
    1279  * NULL, matching subimages are defined for those images as well. This function
    1280  * fails if no valid pixels can be defined (x or y less than Radius, for
    1281  * example). This function should be used to define a region of interest around a
    1282  * source, including both source and sky pixels.
    1283  *
    1284  * XXX: must code this.
    1285  *
    1286  */
    1287 bool pmSourceDefinePixels(
    1288     pmSource *mySource,                 ///< Add comment.
    1289     pmReadout *readout,                 ///< Add comment.
    1290     psF32 x,                            ///< Add comment.
    1291     psF32 y,                            ///< Add comment.
    1292     psF32 Radius)                       ///< Add comment.
    1293 {
    1294     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1295     psLogMsg(__func__, PS_LOG_WARN, "WARNING: pmSourceDefinePixels() has not been implemented.  Returning FALSE.\n");
    1296     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    1297     return(false);
    1298 }
    1299 
    1300 /******************************************************************************
    1301 pmSourceSetPixelsCircle(source, image, radius)
    1302  
    1303 XXX: This was replaced by DefinePixels in SDRS.  Remove it.
    1304 *****************************************************************************/
    1305 bool pmSourceSetPixelsCircle(pmSource *source,
    1306                              const psImage *image,
    1307                              psF32 radius)
    1308 {
    1309     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1310     PS_ASSERT_IMAGE_NON_NULL(image, false);
    1311     PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
    1312     PS_ASSERT_PTR_NON_NULL(source, false);
    1313     PS_ASSERT_PTR_NON_NULL(source->moments, false);
    1314     PS_ASSERT_PTR_NON_NULL(source->peak, false);
    1315     PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(radius, 0.0, false);
    1316 
    1317     //
    1318     // We define variables for code readability.
    1319     //
    1320     // XXX: Since the peak->xy coords are in image, not subImage coords,
    1321     // these variables should be renamed for clarity (imageCenterRow, etc).
    1322     //
    1323     psS32 radiusS32 = (psS32) radius;
    1324     psS32 SubImageCenterRow = source->peak->y;
    1325     psS32 SubImageCenterCol = source->peak->x;
    1326     // XXX EAM : for the circle to stay on the image
    1327     // XXX EAM : EndRow is *exclusive* of pixel region (ie, last pixel + 1)
    1328     psS32 SubImageStartRow  = PS_MAX (0, SubImageCenterRow - radiusS32);
    1329     psS32 SubImageEndRow    = PS_MIN (image->numRows, SubImageCenterRow + radiusS32 + 1);
    1330     psS32 SubImageStartCol  = PS_MAX (0, SubImageCenterCol - radiusS32);
    1331     psS32 SubImageEndCol    = PS_MIN (image->numCols, SubImageCenterCol + radiusS32 + 1);
    1332 
    1333     // XXX: Must recycle image.
    1334     // XXX EAM: this message reflects a programming error we know about.
    1335     //          i am setting it to a trace message which we can take out
    1336     if (source->pixels != NULL) {
    1337         psTrace (".psModule.pmObjects.pmSourceSetPixelsCircle", 4,
    1338                  "WARNING: pmSourceSetPixelsCircle(): image->pixels not NULL.  Freeing and reallocating.\n");
    1339         psFree(source->pixels);
    1340     }
    1341     source->pixels = psImageSubset((psImage *) image, psRegionSet(SubImageStartCol,
    1342                                    SubImageStartRow,
    1343                                    SubImageEndCol,
    1344                                    SubImageEndRow));
    1345 
    1346     // XXX: Must recycle image.
    1347     if (source->mask != NULL) {
    1348         psFree(source->mask);
    1349     }
    1350     source->mask = psImageAlloc(source->pixels->numCols,
    1351                                 source->pixels->numRows,
    1352                                 PS_TYPE_U8); // XXX EAM : type was F32
    1353 
    1354     //
    1355     // Loop through the subimage mask, initialize mask to 0 or 1.
    1356     // XXX EAM: valid pixels should have 0, not 1
    1357     for (psS32 row = 0 ; row < source->mask->numRows; row++) {
    1358         for (psS32 col = 0 ; col < source->mask->numCols; col++) {
    1359 
    1360             if (checkRadius2((psF32) radiusS32,
    1361                              (psF32) radiusS32,
    1362                              radius,
    1363                              (psF32) col,
    1364                              (psF32) row)) {
    1365                 source->mask->data.U8[row][col] = 0;
    1366             } else {
    1367                 source->mask->data.U8[row][col] = 1;
    1368             }
    1369         }
    1370     }
    1371     psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);
    1372     return(true);
    1373 }
    1374 
    1375 /******************************************************************************
    1376 pmSourceModelGuess(source, model): This function allocates a new
    1377 pmModel structure based on the given modelType specified in the argument list.
    1378 The corresponding pmModelGuess function is returned, and used to
    1379 supply the values of the params array in the pmModel structure.
    1380  
    1381 XXX: Many parameters are based on the src->moments structure, which is in
    1382 image, not subImage coords.  Therefore, the calls to the model evaluation
    1383 functions will be in image, not subImage coords.  Remember this.
    1384 *****************************************************************************/
    1385 pmModel *pmSourceModelGuess(pmSource *source,
    1386                             pmModelType modelType)
    1387 {
    1388     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1389     PS_ASSERT_PTR_NON_NULL(source->moments, false);
    1390     PS_ASSERT_PTR_NON_NULL(source->peak, false);
    1391 
    1392     pmModel *model = pmModelAlloc(modelType);
    1393 
    1394     pmModelGuessFunc modelGuessFunc = pmModelGuessFunc_GetFunction(modelType);
    1395     modelGuessFunc(model, source);
    1396     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    1397     return(model);
    1398 }
    1399 
    1400 /******************************************************************************
    1401 evalModel(source, level, row): a private function which evaluates the
    1402 source->modelPSF function at the specified coords.  The coords are subImage, not
    1403 image coords.
    1404  
    1405 NOTE: The coords are in subImage source->pixel coords, not image coords.
    1406  
    1407 XXX: reverse order of row,col args?
    1408  
    1409 XXX: rename all coords in this file such that their name defines whether
    1410 the coords is in subImage or image space.
    1411  
    1412 XXX: This should probably be a public pmModules function.
    1413  
    1414 XXX: Use static vectors for x.
    1415  
    1416 XXX: Figure out if it's (row, col) or (col, row) for the model functions.
    1417  
    1418 XXX: For a while, the first psVectorAlloc() was generating a seg fault during
    1419 testing.  Try to reproduce that and debug.
    1420 *****************************************************************************/
    1421 
    1422 // XXX EAM : I have made this a public function
    1423 // XXX EAM : this now uses a pmModel as the input
    1424 // XXX EAM : it was using src->type to find the model, not model->type
    1425 psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row)
    1426 {
    1427     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1428     PS_ASSERT_PTR_NON_NULL(image, false);
    1429     PS_ASSERT_PTR_NON_NULL(model, false);
    1430     PS_ASSERT_PTR_NON_NULL(model->params, false);
    1431 
    1432     // Allocate the x coordinate structure and convert row/col to image space.
    1433     //
    1434     psVector *x = psVectorAlloc(2, PS_TYPE_F32);
    1435     x->n = x->nalloc;
    1436     x->data.F32[0] = (psF32) (col + image->col0);
    1437     x->data.F32[1] = (psF32) (row + image->row0);
    1438     psF32 tmpF;
    1439     pmModelFunc modelFunc;
    1440 
    1441     modelFunc = pmModelFunc_GetFunction (model->type);
    1442     tmpF = modelFunc (NULL, model->params, x);
    1443     psFree(x);
    1444     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    1445     return(tmpF);
    1446 }
    1447 
    1448 /******************************************************************************
    1449 pmSourceContour(src, img, level, mode): For an input subImage, and model, this
    1450 routine returns a psArray of coordinates that evaluate to the specified level.
    1451  
    1452 XXX: Probably should remove the "image" argument.
    1453 XXX: What type should the output coordinate vectors consist of?  col,row?
    1454 XXX: Why a pmArray output?
    1455 XXX: doex x,y correspond with col,row or row/col?
    1456 XXX: What is mode?
    1457 XXX: The top, bottom of the contour is not correctly determined.
    1458 XXX EAM : this function is using the model for the contour, but it should
    1459           be using only the image counts
    1460 *****************************************************************************/
    1461 psArray *pmSourceContour(pmSource *source,
    1462                          const psImage *image,
    1463                          psF32 level,
    1464                          pmContourType mode)
    1465 {
    1466     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1467     PS_ASSERT_PTR_NON_NULL(source, false);
    1468     PS_ASSERT_PTR_NON_NULL(image, false);
    1469     PS_ASSERT_PTR_NON_NULL(source->moments, false);
    1470     PS_ASSERT_PTR_NON_NULL(source->peak, false);
    1471     PS_ASSERT_PTR_NON_NULL(source->pixels, false);
    1472     PS_ASSERT_PTR_NON_NULL(source->modelFLT, false);
    1473     // XXX EAM : what is the purpose of modelPSF/modelFLT?
    1474 
    1475     //
    1476     // Allocate data for x/y pairs.
    1477     //
    1478     psVector *xVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
    1479     psVector *yVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
    1480     xVec->n = xVec->nalloc;
    1481     yVec->n = yVec->nalloc;
    1482     //
    1483     // Start at the row with peak pixel, then decrement.
    1484     //
    1485     psS32 col = source->peak->x;
    1486     for (psS32 row = source->peak->y; row>= 0 ; row--) {
    1487         // XXX: yVec contain no real information.  Do we really need it?
    1488         yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
    1489         yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
    1490 
    1491         // Starting at peak pixel, search leftwards for the column intercept.
    1492         psF32 leftIntercept = findValue(source, level, row, col, 0);
    1493         if (isnan(leftIntercept)) {
    1494             psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
    1495             psFree(xVec);
    1496             psFree(yVec);
    1497             psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
    1498             return(NULL);
    1499             //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
    1500         }
    1501         xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
    1502 
    1503         // Starting at peak pixel, search rightwards for the column intercept.
    1504 
    1505         psF32 rightIntercept = findValue(source, level, row, col, 1);
    1506         if (isnan(rightIntercept)) {
    1507             psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
    1508             psFree(xVec);
    1509             psFree(yVec);
    1510             psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
    1511             return(NULL);
    1512             //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
    1513         }
    1514         psTrace(__func__, 4, "The intercepts are (%.2f, %.2f)\n", leftIntercept, rightIntercept);
    1515         xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
    1516 
    1517         // Set starting column for next row
    1518         col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
    1519     }
    1520     //
    1521     // Start at the row (+1) with peak pixel, then increment.
    1522     //
    1523     col = source->peak->x;
    1524     for (psS32 row = 1 + source->peak->y; row < source->pixels->numRows ; row++) {
    1525         // XXX: yVec contain no real information.  Do we really need it?
    1526         yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
    1527         yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
    1528 
    1529         // Starting at peak pixel, search leftwards for the column intercept.
    1530         psF32 leftIntercept = findValue(source, level, row, col, 0);
    1531         if (isnan(leftIntercept)) {
    1532             psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
    1533             psFree(xVec);
    1534             psFree(yVec);
    1535             psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
    1536             return(NULL);
    1537             //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
    1538         }
    1539         xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
    1540 
    1541         // Starting at peak pixel, search rightwards for the column intercept.
    1542         psF32 rightIntercept = findValue(source, level, row, col, 1);
    1543         if (isnan(rightIntercept)) {
    1544             psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
    1545             psFree(xVec);
    1546             psFree(yVec);
    1547             psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
    1548             return(NULL);
    1549             //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
    1550         }
    1551         xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
    1552 
    1553         // Set starting column for next row
    1554         col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
    1555     }
    1556 
    1557     //
    1558     // Allocate an array for result, store coord vectors there.
    1559     //
    1560     psArray *tmpArray = psArrayAlloc(2);
    1561     tmpArray->n = 2;
    1562     tmpArray->data[0] = (psPtr *) yVec;
    1563     tmpArray->data[1] = (psPtr *) xVec;
    1564     psTrace(__func__, 3, "---- %s() end ----\n", __func__);
    1565     return(tmpArray);
    1566 }
    1567 
    1568 // XXX EAM : these are better starting values, but should be available from metadata?
    1569 #define PM_SOURCE_FIT_MODEL_NUM_ITERATIONS 15
    1570 #define PM_SOURCE_FIT_MODEL_TOLERANCE 0.1
    1571 /******************************************************************************
    1572 pmSourceFitModel(source, model): must create the appropiate arguments to the
    1573 LM minimization routines for the various p_pmMinLM_XXXXXX_Vec() functions.
    1574  
    1575 XXX: should there be a mask value?
    1576 XXX EAM : fit the specified model (not necessarily the one in source)
    1577 *****************************************************************************/
    1578 bool pmSourceFitModel_v5(pmSource *source,
    1579                          pmModel *model,
    1580                          const bool PSF)
    1581 {
    1582     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1583     PS_ASSERT_PTR_NON_NULL(source, false);
    1584     PS_ASSERT_PTR_NON_NULL(source->moments, false);
    1585     PS_ASSERT_PTR_NON_NULL(source->peak, false);
    1586     PS_ASSERT_PTR_NON_NULL(source->pixels, false);
    1587     PS_ASSERT_PTR_NON_NULL(source->mask, false);
    1588     PS_ASSERT_PTR_NON_NULL(source->weight, false);
    1589     psBool fitStatus = true;
    1590     psBool onPic     = true;
    1591     psBool rc        = true;
    1592 
    1593     // XXX EAM : is it necessary for the mask & weight to exist?  the
    1594     //           tests below could be conditions (!NULL)
    1595 
    1596     psVector *params = model->params;
    1597     psVector *dparams = model->dparams;
    1598     psVector *paramMask = NULL;
    1599 
    1600     pmModelFunc modelFunc = pmModelFunc_GetFunction (model->type);
    1601 
    1602     int nParams = PSF ? params->n - 4 : params->n;
    1603 
    1604     // find the number of valid pixels
    1605     // XXX EAM : this loop and the loop below could just be one pass
    1606     //           using the psArrayAdd and psVectorExtend functions
    1607     psS32 count = 0;
    1608     for (psS32 i = 0; i < source->pixels->numRows; i++) {
    1609         for (psS32 j = 0; j < source->pixels->numCols; j++) {
    1610             if (source->mask->data.U8[i][j] == 0) {
    1611                 count++;
    1612             }
    1613         }
    1614     }
    1615     if (count <  nParams + 1) {
    1616         psTrace (".pmObjects.pmSourceFitModel", 4, "insufficient valid pixels\n");
    1617         psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
    1618         model->status = PM_MODEL_BADARGS;
    1619         return(false);
    1620     }
    1621 
    1622     // construct the coordinate and value entries
    1623     psArray *x = psArrayAlloc(count);
    1624     psVector *y = psVectorAlloc(count, PS_TYPE_F32);
    1625     psVector *yErr = psVectorAlloc(count, PS_TYPE_F32);
    1626     x->n = x->nalloc;
    1627     y->n = y->nalloc;
    1628     yErr->n = yErr->nalloc;
    1629     psS32 tmpCnt = 0;
    1630     for (psS32 i = 0; i < source->pixels->numRows; i++) {
    1631         for (psS32 j = 0; j < source->pixels->numCols; j++) {
    1632             if (source->mask->data.U8[i][j] == 0) {
    1633                 psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
    1634                 coord->n = 2;
    1635                 // XXX: Convert i/j to image space:
    1636                 // XXX EAM: coord order is (x,y) == (col,row)
    1637                 coord->data.F32[0] = (psF32) (j + source->pixels->col0);
    1638                 coord->data.F32[1] = (psF32) (i + source->pixels->row0);
    1639                 x->data[tmpCnt] = (psPtr *) coord;
    1640                 y->data.F32[tmpCnt] = source->pixels->data.F32[i][j];
    1641                 yErr->data.F32[tmpCnt] = sqrt (source->weight->data.F32[i][j]);
    1642                 // XXX EAM : note the wasted effort: we carry dY^2, take sqrt(), then
    1643                 //           the minimization function calculates sq()
    1644                 tmpCnt++;
    1645             }
    1646         }
    1647     }
    1648 
    1649     psMinimization *myMin = psMinimizationAlloc(PM_SOURCE_FIT_MODEL_NUM_ITERATIONS,
    1650                             PM_SOURCE_FIT_MODEL_TOLERANCE);
    1651 
    1652     // PSF model only fits first 4 parameters, FLT model fits all
    1653     if (PSF) {
    1654         paramMask = psVectorAlloc (params->n, PS_TYPE_U8);
    1655         paramMask->n = paramMask->nalloc;
    1656         for (int i = 0; i < 4; i++) {
    1657             paramMask->data.U8[i] = 0;
    1658         }
    1659         for (int i = 4; i < paramMask->n; i++) {
    1660             paramMask->data.U8[i] = 1;
    1661         }
    1662     }
    1663 
    1664     // XXX EAM : covar must be F64?
    1665     psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F64);
    1666 
    1667     psTrace (".pmObjects.pmSourceFitModel", 5, "fitting function\n");
    1668 
    1669     psMinConstrain *constrain = psMinConstrainAlloc();
    1670     constrain->paramMask = paramMask;
    1671     fitStatus = psMinimizeLMChi2(myMin, covar, params, constrain,
    1672                                  x, y, yErr, modelFunc);
    1673     psFree(constrain);
    1674     for (int i = 0; i < dparams->n; i++) {
    1675         if ((paramMask != NULL) && paramMask->data.U8[i])
    1676             continue;
    1677         dparams->data.F32[i] = sqrt(covar->data.F64[i][i]);
    1678     }
    1679 
    1680     // save the resulting chisq, nDOF, nIter
    1681     model->chisq = myMin->value;
    1682     model->nIter = myMin->iter;
    1683     model->nDOF  = y->n - nParams;
    1684 
    1685     // get the Gauss-Newton distance for fixed model parameters
    1686     if (paramMask != NULL) {
    1687         psVector *delta = psVectorAlloc (params->n, PS_TYPE_F64);
    1688         delta->n = delta->nalloc;
    1689         psMinimizeGaussNewtonDelta (delta, params, NULL, x, y, yErr, modelFunc);
    1690         for (int i = 0; i < dparams->n; i++) {
    1691             if (!paramMask->data.U8[i])
    1692                 continue;
    1693             dparams->data.F32[i] = delta->data.F64[i];
    1694         }
    1695         psFree (delta);
    1696     }
    1697 
    1698     // set the model success or failure status
    1699     if (!fitStatus) {
    1700         model->status = PM_MODEL_NONCONVERGE;
    1701     } else {
    1702         model->status = PM_MODEL_SUCCESS;
    1703     }
    1704 
    1705     // models can go insane: reject these
    1706     onPic &= (params->data.F32[2] >= source->pixels->col0);
    1707     onPic &= (params->data.F32[2] <  source->pixels->col0 + source->pixels->numCols);
    1708     onPic &= (params->data.F32[3] >= source->pixels->row0);
    1709     onPic &= (params->data.F32[3] <  source->pixels->row0 + source->pixels->numRows);
    1710     if (!onPic) {
    1711         model->status = PM_MODEL_OFFIMAGE;
    1712     }
    1713 
    1714     psFree(x);
    1715     psFree(y);
    1716     psFree(yErr);
    1717     psFree(myMin);
    1718     psFree(covar);
    1719     psFree(paramMask);
    1720 
    1721     rc = (onPic && fitStatus);
    1722     psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
    1723     return(rc);
    1724 }
    1725 
    1726 // XXX EAM : new version with parameter range limits and weight enhancement
    1727 bool pmSourceFitModel (pmSource *source,
    1728                        pmModel *model,
    1729                        const bool PSF)
    1730 {
    1731     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1732     PS_ASSERT_PTR_NON_NULL(source, false);
    1733     PS_ASSERT_PTR_NON_NULL(source->moments, false);
    1734     PS_ASSERT_PTR_NON_NULL(source->peak, false);
    1735     PS_ASSERT_PTR_NON_NULL(source->pixels, false);
    1736     PS_ASSERT_PTR_NON_NULL(source->mask, false);
    1737     PS_ASSERT_PTR_NON_NULL(source->weight, false);
    1738 
    1739     // XXX EAM : is it necessary for the mask & weight to exist?  the
    1740     //           tests below could be conditions (!NULL)
    1741 
    1742     psBool fitStatus = true;
    1743     psBool onPic     = true;
    1744     psBool rc        = true;
    1745     psF32  Ro, ymodel;
    1746 
    1747     psVector *params = model->params;
    1748     psVector *dparams = model->dparams;
    1749     psVector *paramMask = NULL;
    1750 
    1751     pmModelFunc modelFunc = pmModelFunc_GetFunction (model->type);
    1752 
    1753     // XXX EAM : I need to use the sky value to constrain the weight model
    1754     int nParams = PSF ? params->n - 4 : params->n;
    1755     psF32 So = params->data.F32[0];
    1756 
    1757     // find the number of valid pixels
    1758     // XXX EAM : this loop and the loop below could just be one pass
    1759     //           using the psArrayAdd and psVectorExtend functions
    1760     psS32 count = 0;
    1761     for (psS32 i = 0; i < source->pixels->numRows; i++) {
    1762         for (psS32 j = 0; j < source->pixels->numCols; j++) {
    1763             if (source->mask->data.U8[i][j] == 0) {
    1764                 count++;
    1765             }
    1766         }
    1767     }
    1768     if (count <  nParams + 1) {
    1769         psTrace (".pmObjects.pmSourceFitModel", 4, "insufficient valid pixels\n");
    1770         psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
    1771         model->status = PM_MODEL_BADARGS;
    1772         return(false);
    1773     }
    1774 
    1775     // construct the coordinate and value entries
    1776     psArray *x = psArrayAlloc(count);
    1777     psVector *y = psVectorAlloc(count, PS_TYPE_F32);
    1778     psVector *yErr = psVectorAlloc(count, PS_TYPE_F32);
    1779     x->n = x->nalloc;
    1780     y->n = y->nalloc;
    1781     yErr->n = yErr->nalloc;
    1782     psS32 tmpCnt = 0;
    1783     for (psS32 i = 0; i < source->pixels->numRows; i++) {
    1784         for (psS32 j = 0; j < source->pixels->numCols; j++) {
    1785             if (source->mask->data.U8[i][j] == 0) {
    1786                 psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
    1787                 coord->n = 2;
    1788                 // XXX: Convert i/j to image space:
    1789                 // XXX EAM: coord order is (x,y) == (col,row)
    1790                 coord->data.F32[0] = (psF32) (j + source->pixels->col0);
    1791                 coord->data.F32[1] = (psF32) (i + source->pixels->row0);
    1792                 x->data[tmpCnt] = (psPtr *) coord;
    1793                 y->data.F32[tmpCnt] = source->pixels->data.F32[i][j];
    1794 
    1795                 // compare observed flux to model flux to adjust weight
    1796                 ymodel = modelFunc (NULL, model->params, coord);
    1797 
    1798                 // this test enhances the weight based on deviation from the model flux
    1799                 Ro = 1.0 + fabs (y->data.F32[tmpCnt] - ymodel) / sqrt(PS_SQR(ymodel - So)
    1800                         + PS_SQR(So));
    1801 
    1802                 // psMinimizeLMChi2_EAM takes wt = 1/dY^2
    1803                 if (source->weight->data.F32[i][j] == 0) {
    1804                     yErr->data.F32[tmpCnt] = 0.0;
    1805                 } else {
    1806                     yErr->data.F32[tmpCnt] = 1.0 / (source->weight->data.F32[i][j] * Ro);
    1807                 }
    1808                 tmpCnt++;
    1809             }
    1810         }
    1811     }
    1812 
    1813     psMinimization *myMin = psMinimizationAlloc(PM_SOURCE_FIT_MODEL_NUM_ITERATIONS,
    1814                             PM_SOURCE_FIT_MODEL_TOLERANCE);
    1815 
    1816     // PSF model only fits first 4 parameters, FLT model fits all
    1817     if (PSF) {
    1818         paramMask = psVectorAlloc (params->n, PS_TYPE_U8);
    1819         paramMask->n = paramMask->nalloc;
    1820         for (int i = 0; i < 4; i++) {
    1821             paramMask->data.U8[i] = 0;
    1822         }
    1823         for (int i = 4; i < paramMask->n; i++) {
    1824             paramMask->data.U8[i] = 1;
    1825         }
    1826     }
    1827 
    1828     // XXX EAM : I've added three types of parameter range checks
    1829     // XXX EAM : this requires my new psMinimization functions
    1830     pmModelLimits modelLimits = pmModelLimits_GetFunction (model->type);
    1831     psVector *beta_lim = NULL;
    1832     psVector *params_min = NULL;
    1833     psVector *params_max = NULL;
    1834 
    1835     // XXX EAM : in this implementation, I pass in the limits with the covar matrix.
    1836     //           in the SDRS, I define a new psMinimization which will take these in
    1837     psImage *covar = psImageAlloc (params->n, 3, PS_TYPE_F64);
    1838     modelLimits (&beta_lim, &params_min, &params_max);
    1839     for (int i = 0; i < params->n; i++) {
    1840         covar->data.F64[0][i] = beta_lim->data.F32[i];
    1841         covar->data.F64[1][i] = params_min->data.F32[i];
    1842         covar->data.F64[2][i] = params_max->data.F32[i];
    1843     }
    1844 
    1845     psTrace (".pmObjects.pmSourceFitModel", 5, "fitting function\n");
    1846     psMinConstrain *constrain = psMinConstrainAlloc();
    1847     constrain->paramMask = paramMask;
    1848     fitStatus = psMinimizeLMChi2(myMin, covar, params, constrain,
    1849                                  x, y, yErr, modelFunc);
    1850     psFree(constrain);
    1851     for (int i = 0; i < dparams->n; i++) {
    1852         if ((paramMask != NULL) && paramMask->data.U8[i])
    1853             continue;
    1854         dparams->data.F32[i] = sqrt(covar->data.F64[i][i]);
    1855     }
    1856 
    1857     // XXX EAM: we need to do something (give an error?) if rc is false
    1858     // XXX EAM: psMinimizeLMChi2 does not check convergence
    1859 
    1860     // XXX EAM: save the resulting chisq, nDOF, nIter
    1861     model->chisq = myMin->value;
    1862     model->nIter = myMin->iter;
    1863     model->nDOF  = y->n - nParams;
    1864 
    1865     // XXX EAM get the Gauss-Newton distance for fixed model parameters
    1866     if (paramMask != NULL) {
    1867         psVector *delta = psVectorAlloc (params->n, PS_TYPE_F64);
    1868         delta->n = delta->nalloc;
    1869         psMinimizeGaussNewtonDelta(delta, params, NULL, x, y, yErr, modelFunc);
    1870         for (int i = 0; i < dparams->n; i++) {
    1871             if (!paramMask->data.U8[i])
    1872                 continue;
    1873             dparams->data.F32[i] = delta->data.F64[i];
    1874         }
    1875     }
    1876 
    1877     // set the model success or failure status
    1878     if (!fitStatus) {
    1879         model->status = PM_MODEL_NONCONVERGE;
    1880     } else {
    1881         model->status = PM_MODEL_SUCCESS;
    1882     }
    1883 
    1884     // XXX models can go insane: reject these
    1885     onPic &= (params->data.F32[2] >= source->pixels->col0);
    1886     onPic &= (params->data.F32[2] <  source->pixels->col0 + source->pixels->numCols);
    1887     onPic &= (params->data.F32[3] >= source->pixels->row0);
    1888     onPic &= (params->data.F32[3] <  source->pixels->row0 + source->pixels->numRows);
    1889     if (!onPic) {
    1890         model->status = PM_MODEL_OFFIMAGE;
    1891     }
    1892 
    1893     psFree(x);
    1894     psFree(y);
    1895     psFree(yErr);
    1896     psFree(myMin);
    1897     psFree(covar);
    1898     psFree(paramMask);
    1899 
    1900     rc = (onPic && fitStatus);
    1901     psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
    1902     return(rc);
    1903 }
    1904 
    1905 bool p_pmSourceAddOrSubModel(psImage *image,
    1906                              psImage *mask,
    1907                              pmModel *model,
    1908                              bool center,
    1909                              bool sky,
    1910                              bool add
    1911                                 )
    1912 {
    1913     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1914 
    1915     PS_ASSERT_PTR_NON_NULL(model, false);
    1916     PS_ASSERT_IMAGE_NON_NULL(image, false);
    1917     PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
    1918 
    1919     psVector *x = psVectorAlloc(2, PS_TYPE_F32);
    1920     x->n = 2;
    1921     psVector *params = model->params;
    1922     pmModelFunc modelFunc = pmModelFunc_GetFunction (model->type);
    1923     psS32 imageCol;
    1924     psS32 imageRow;
    1925     psF32 skyValue = params->data.F32[0];
    1926     psF32 pixelValue;
    1927 
    1928     for (psS32 i = 0; i < image->numRows; i++) {
    1929         for (psS32 j = 0; j < image->numCols; j++) {
    1930             if ((mask != NULL) && mask->data.U8[i][j])
    1931                 continue;
    1932 
    1933             // XXX: Should you be adding the pixels for the entire subImage,
    1934             // or a radius of pixels around it?
    1935 
    1936             // Convert i/j to imace coord space:
    1937             // XXX: Make sure you have col/row order correct.
    1938             // XXX EAM : 'center' option changes this
    1939             // XXX EAM : i == numCols/2 -> x = model->params->data.F32[2]
    1940             if (center) {
    1941                 imageCol = j - 0.5*image->numCols + model->params->data.F32[2];
    1942                 imageRow = i - 0.5*image->numRows + model->params->data.F32[3];
    1943             } else {
    1944                 imageCol = j + image->col0;
    1945                 imageRow = i + image->row0;
    1946             }
    1947 
    1948             x->data.F32[0] = (float) imageCol;
    1949             x->data.F32[1] = (float) imageRow;
    1950 
    1951             // set the appropriate pixel value for this coordinate
    1952             if (sky) {
    1953                 pixelValue = modelFunc (NULL, params, x);
    1954             } else {
    1955                 pixelValue = modelFunc (NULL, params, x) - skyValue;
    1956             }
    1957 
    1958 
    1959             // add or subtract the value
    1960             if (add
    1961                ) {
    1962                 image->data.F32[i][j] += pixelValue;
    1963             }
    1964             else {
    1965                 image->data.F32[i][j] -= pixelValue;
    1966             }
    1967         }
    1968     }
    1969     psFree(x);
    1970     psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);
    1971     return(true);
    1972 }
    1973 
    1974 
    1975 
    1976 /******************************************************************************
    1977  *****************************************************************************/
    1978 bool pmSourceAddModel(psImage *image,
    1979                       psImage *mask,
    1980                       pmModel *model,
    1981                       bool center,
    1982                       bool sky)
    1983 {
    1984     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1985     psBool rc = p_pmSourceAddOrSubModel(image, mask, model, center, sky, true);
    1986     psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
    1987     return(rc);
    1988 }
    1989 
    1990 /******************************************************************************
    1991  *****************************************************************************/
    1992 bool pmSourceSubModel(psImage *image,
    1993                       psImage *mask,
    1994                       pmModel *model,
    1995                       bool center,
    1996                       bool sky)
    1997 {
    1998     psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
    1999     psBool rc = p_pmSourceAddOrSubModel(image, mask, model, center, sky, false);
    2000     psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
    2001     return(rc);
    2002 }
    2003 
    2004 bool pmSourcePhotometry (float *fitMag, float *obsMag, pmModel *model, psImage *image, psImage *mask)
    2005 {
    2006 
    2007     float obsSum = 0;
    2008     float fitSum = 0;
    2009     float sky = model->params->data.F32[0];
    2010 
    2011     pmModelFlux modelFluxFunc = pmModelFlux_GetFunction (model->type);
    2012     fitSum = modelFluxFunc (model->params);
    2013 
    2014     for (int ix = 0; ix < image->numCols; ix++) {
    2015         for (int iy = 0; iy < image->numRows; iy++) {
    2016             if (mask->data.U8[iy][ix])
    2017                 continue;
    2018             obsSum += image->data.F32[iy][ix] - sky;
    2019         }
    2020     }
    2021     if (obsSum <= 0)
    2022         return false;
    2023     if (fitSum <= 0)
    2024         return false;
    2025 
    2026     *fitMag = -2.5*log10(fitSum);
    2027     *obsMag = -2.5*log10(obsSum);
    2028     return (true);
    2029 }
    2030 
  • trunk/psModules/src/objects/pmPSF.c

    r6511 r6873  
    66 *  @author EAM, IfA
    77 *
    8  *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
    9  *  @date $Date: 2006-03-04 01:01:33 $
     8 *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
     9 *  @date $Date: 2006-04-17 18:10:08 $
    1010 *
    1111 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
     
    1818
    1919#include <pslib.h>
    20 #include "pmObjects.h"
     20#include "pmHDU.h"
     21#include "pmFPA.h"
     22#include "pmPeaks.h"
     23#include "pmMoments.h"
     24#include "pmModel.h"
     25#include "pmSource.h"
     26#include "pmGrowthCurve.h"
    2127#include "pmPSF.h"
    2228#include "pmModelGroup.h"
     
    5763        return;
    5864
     65    psFree (psf->ApTrend);
     66    psFree (psf->growth);
    5967    psFree (psf->params);
    6068    return;
     
    6977 X-center
    7078 Y-center
    71  ???: Sky background value?
    72  ???: Zo?
     79 Sky background value
     80 Object Normalization
    7381 *****************************************************************************/
    74 pmPSF *pmPSFAlloc (pmModelType type)
     82pmPSF *pmPSFAlloc (pmModelType type, bool poissonErrors)
    7583{
    7684    int Nparams;
     
    8391    psf->dApResid = 0.0;
    8492    psf->skyBias  = 0.0;
     93    psf->skySat   = 0.0;
     94    psf->poissonErrors = poissonErrors;
     95
     96    // the ApTrend components are (x, y, r2rflux, flux)
     97    psf->ApTrend = psPolynomial4DAlloc (PS_POLYNOMIAL_ORD, 2, 2, 1, 1);
     98    pmPSF_MaskApTrend (psf, PM_PSF_SKYBIAS);
     99
     100    if (psf->poissonErrors) {
     101        psf->ChiTrend = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 1);
     102    } else {
     103        psf->ChiTrend = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 2);
     104    }
     105
     106    // don't define a growth curve : user needs to choose radius bins
     107    psf->growth = NULL;
    85108
    86109    Nparams = pmModelParameterCount (type);
     
    94117    for (int i = 0; i < psf->params->n; i++) {
    95118        // XXX EAM : make this a user-defined value?
    96         psf->params->data[i] = psPolynomial2DAlloc(1, 1, PS_POLYNOMIAL_ORD);
     119        psf->params->data[i] = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 1, 1);
    97120    }
    98121
     
    174197
    175198/*****************************************************************************
    176 pmModelFromPSF (*modelFLT, *psf):  use the model position parameters to
     199pmModelFromPSF (*modelEXT, *psf):  use the model position parameters to
    177200construct a realization of the PSF model at the object coordinates
    178201 *****************************************************************************/
    179 pmModel *pmModelFromPSF (pmModel *modelFLT, pmPSF *psf)
    180 {
    181 
    182     // need to define the relationship between the modelFLT and modelPSF ?
     202pmModel *pmModelFromPSF (pmModel *modelEXT, pmPSF *psf)
     203{
     204
     205    // need to define the relationship between the modelEXT and modelPSF ?
    183206
    184207    // find function used to set the model parameters
     
    189212
    190213    // set model parameters for this source based on PSF information
    191     modelFromPSFFunc (modelPSF, modelFLT, psf);
     214    modelFromPSFFunc (modelPSF, modelEXT, psf);
    192215
    193216    return (modelPSF);
    194217}
     218
     219// zero and mask out all terms:
     220static bool maskAllTerms (psPolynomial4D *trend)
     221{
     222
     223    for (int i = 0; i < trend->nX + 1; i++) {
     224        for (int j = 0; j < trend->nY + 1; j++) {
     225            for (int k = 0; k < trend->nZ + 1; k++) {
     226                for (int m = 0; m < trend->nT + 1; m++) {
     227                    trend->mask[i][j][k][m] = 1;  // mask coeff
     228                    trend->coeff[i][j][k][m] = 0;  // zero coeff
     229                }
     230            }
     231        }
     232    }
     233    return true;
     234}
     235
     236/***********************************************
     237 * this function masks the psf.ApTrend polynomial
     238 * to enable the specific subset of the coefficients
     239 **********************************************/
     240bool pmPSF_MaskApTrend (pmPSF *psf, pmPSF_ApTrendOptions option)
     241{
     242
     243    switch (option) {
     244    case PM_PSF_NONE:
     245        maskAllTerms (psf->ApTrend);
     246        return true;
     247
     248    case PM_PSF_CONSTANT:
     249        maskAllTerms (psf->ApTrend);
     250        psf->ApTrend->mask[0][0][0][0] = 0;  // unmask constant
     251        return true;
     252
     253    case PM_PSF_SKYBIAS:
     254        maskAllTerms (psf->ApTrend);
     255        psf->ApTrend->mask[0][0][0][0] = 0;  // unmask constant
     256        psf->ApTrend->mask[0][0][1][0] = 0;  // unmask skybias
     257        return true;
     258
     259    case PM_PSF_SKYSAT:
     260        maskAllTerms (psf->ApTrend);
     261        psf->ApTrend->mask[0][0][0][0] = 0;  // unmask constant
     262        psf->ApTrend->mask[0][0][1][0] = 0;  // unmask skybias
     263        psf->ApTrend->mask[0][0][0][1] = 0;  // unmask skybias
     264        return true;
     265
     266    case PM_PSF_XY_LIN:
     267        maskAllTerms (psf->ApTrend);
     268        psf->ApTrend->mask[0][0][0][0] = 0;  // unmask constant
     269        psf->ApTrend->mask[1][0][0][0] = 0;  // unmask x
     270        psf->ApTrend->mask[0][1][0][0] = 0;  // unmask y
     271        return true;
     272
     273    case PM_PSF_XY_QUAD:
     274        maskAllTerms (psf->ApTrend);
     275        psf->ApTrend->mask[0][0][0][0] = 0;  // unmask constant
     276        psf->ApTrend->mask[1][0][0][0] = 0;  // unmask x
     277        psf->ApTrend->mask[2][0][0][0] = 0;  // unmask x^2
     278        psf->ApTrend->mask[1][1][0][0] = 0;  // unmask x y
     279        psf->ApTrend->mask[0][1][0][0] = 0;  // unmask y
     280        psf->ApTrend->mask[0][2][0][0] = 0;  // unmask y^2
     281        return true;
     282
     283    case PM_PSF_SKY_XY_LIN:
     284        maskAllTerms (psf->ApTrend);
     285        psf->ApTrend->mask[0][0][0][0] = 0;  // unmask constant
     286        psf->ApTrend->mask[1][0][0][0] = 0;  // unmask x
     287        psf->ApTrend->mask[0][1][0][0] = 0;  // unmask y
     288        psf->ApTrend->mask[0][0][1][0] = 0;  // unmask skybias
     289        return true;
     290
     291    case PM_PSF_SKYSAT_XY_LIN:
     292        maskAllTerms (psf->ApTrend);
     293        psf->ApTrend->mask[0][0][0][0] = 0;  // unmask constant
     294        psf->ApTrend->mask[1][0][0][0] = 0;  // unmask x
     295        psf->ApTrend->mask[0][1][0][0] = 0;  // unmask y
     296        psf->ApTrend->mask[0][0][1][0] = 0;  // unmask skybias
     297        psf->ApTrend->mask[0][0][0][1] = 0;  // unmask skysat
     298        return true;
     299
     300    case PM_PSF_ALL:
     301    default:
     302        maskAllTerms (psf->ApTrend);
     303        psf->ApTrend->mask[0][0][0][0] = 0;  // unmask constant
     304        psf->ApTrend->mask[0][0][1][0] = 0;  // unmask skybias
     305        psf->ApTrend->mask[0][0][0][1] = 0;  // unmask skysat
     306
     307        psf->ApTrend->mask[1][0][0][0] = 0;  // unmask x
     308        psf->ApTrend->mask[2][0][0][0] = 0;  // unmask x^2
     309        psf->ApTrend->mask[1][1][0][0] = 0;  // unmask x y
     310        psf->ApTrend->mask[0][1][0][0] = 0;  // unmask y
     311        psf->ApTrend->mask[0][2][0][0] = 0;  // unmask y^2
     312        return true;
     313    }
     314    return false;
     315}
  • trunk/psModules/src/objects/pmPSF.h

    r6872 r6873  
    66 *  @author EAM, IfA
    77 *
    8  *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
    9  *  @date $Date: 2006-04-17 18:01:05 $
    108 *
    119 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  • trunk/psModules/src/objects/pmPSFtry.c

    r6511 r6873  
    55 *  @author EAM, IfA
    66 *
    7  *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
    8  *  @date $Date: 2006-03-04 01:01:33 $
     7 *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
     8 *  @date $Date: 2006-04-17 18:10:08 $
    99 *
    1010 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
     
    1313
    1414# include <pslib.h>
    15 # include "pmObjects.h"
    16 # include "pmPSF.h"
    17 # include "pmPSFtry.h"
    18 # include "pmModelGroup.h"
     15#include "pmHDU.h"
     16#include "pmFPA.h"
     17#include "pmPeaks.h"
     18#include "pmMoments.h"
     19#include "pmModel.h"
     20#include "pmSource.h"
     21#include "pmGrowthCurve.h"
     22#include "pmPSF.h"
     23#include "pmPSFtry.h"
     24#include "pmModelGroup.h"
     25#include "pmSourceFitModel.h"
     26#include "pmSourcePhotometry.h"
    1927
    2028// ********  pmPSFtry functions  **************************************************
     
    3341    psFree (test->psf);
    3442    psFree (test->sources);
    35     psFree (test->modelFLT);
     43    psFree (test->modelEXT);
    3644    psFree (test->modelPSF);
    3745    psFree (test->metric);
     
    4250
    4351// allocate a pmPSFtry based on the desired sources and the model (identified by name)
    44 pmPSFtry *pmPSFtryAlloc (psArray *sources, char *modelName)
     52pmPSFtry *pmPSFtryAlloc (psArray *sources, char *modelName, bool poissonErrors)
    4553{
    4654
     
    5159    // XXX probably need to increment ref counter
    5260    type           = pmModelSetType (modelName);
    53     test->psf      = pmPSFAlloc (type);
     61    test->psf      = pmPSFAlloc (type, poissonErrors);
    5462    test->sources  = psMemIncrRefCounter(sources);
    55     test->modelFLT = psArrayAlloc (sources->n);
     63    test->modelEXT = psArrayAlloc (sources->n);
    5664    test->modelPSF = psArrayAlloc (sources->n);
    5765    test->metric   = psVectorAlloc (sources->n, PS_TYPE_F64);
     
    5967    test->mask     = psVectorAlloc (sources->n, PS_TYPE_U8);
    6068
    61     test->modelFLT->n = test->modelFLT->nalloc;
    62     test->modelPSF->n = test->modelPSF->nalloc;
    63     test->metric->n   = test->metric->nalloc;
    64     test->fitMag->n   = test->fitMag->nalloc;
    65     test->mask->n     = test->mask->nalloc;
    66 
    67     for (int i = 0; i < test->modelFLT->n; i++) {
     69    for (int i = 0; i < test->modelEXT->n; i++) {
    6870        test->mask->data.U8[i]  = 0;
    69         test->modelFLT->data[i] = NULL;
     71        test->modelEXT->data[i] = NULL;
    7072        test->modelPSF->data[i] = NULL;
    7173        test->metric->data.F64[i] = 0;
     
    8688// mask values indicate the reason the source was rejected:
    8789
    88 pmPSFtry *pmPSFtryModel (psArray *sources, char *modelName, float RADIUS)
     90pmPSFtry *pmPSFtryModel (psArray *sources, char *modelName, float RADIUS, bool poissonErrors)
    8991{
    9092    bool status;
     
    9395    float x;
    9496    float y;
    95     int Nflt = 0;
     97    int Next = 0;
    9698    int Npsf = 0;
    9799
    98     pmPSFtry *psfTry = pmPSFtryAlloc (sources, modelName);
     100    pmPSFtry *psfTry = pmPSFtryAlloc (sources, modelName, poissonErrors);
    99101
    100102    // stage 1:  fit an independent model (freeModel) to all sources
     
    108110
    109111        // set temporary object mask and fit object
    110         // fit model as FLT, not PSF
    111         psImageKeepCircle (source->mask, x, y, RADIUS, "OR", PSPHOT_MASK_MARKED);
    112         status = pmSourceFitModel (source, model, false);
    113         psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PSPHOT_MASK_MARKED);
     112        // fit model as EXT, not PSF
     113
     114        psImageKeepCircle (source->mask, x, y, RADIUS, "OR", PM_SOURCE_MASK_MARKED);
     115        status = pmSourceFitModel (source, model, PM_SOURCE_FIT_EXT);
     116        psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PM_SOURCE_MASK_MARKED);
    114117
    115118        // exclude the poor fits
    116119        if (!status) {
    117             psfTry->mask->data.U8[i] = PSFTRY_MASK_FLT_FAIL;
     120            psfTry->mask->data.U8[i] = PSFTRY_MASK_EXT_FAIL;
    118121            psFree (model);
    119122            continue;
    120123        }
    121         psfTry->modelFLT->data[i] = model;
    122         Nflt ++;
    123     }
    124     psLogMsg ("psphot.psftry", 4, "fit flt:   %f sec for %d sources\n", psTimerMark ("fit"), sources->n);
    125     psTrace ("psphot.psftry", 3, "keeping %d of %d PSF candidates (FLT)\n", Nflt, sources->n);
    126 
    127     // make this optional?
    128     // DumpModelFits (psfTry->modelFLT, "modelsFLT.dat");
     124        psfTry->modelEXT->data[i] = model;
     125        Next ++;
     126    }
     127    psLogMsg ("psphot.psftry", 4, "fit ext:   %f sec for %d sources\n", psTimerMark ("fit"), sources->n);
     128    psTrace ("psphot.psftry", 3, "keeping %d of %d PSF candidates (EXT)\n", Next, sources->n);
    129129
    130130    // stage 2: construct a psf (pmPSF) from this collection of model fits
    131     pmPSFFromModels (psfTry->psf, psfTry->modelFLT, psfTry->mask);
     131    pmPSFFromModels (psfTry->psf, psfTry->modelEXT, psfTry->mask);
    132132
    133133    // stage 3: refit with fixed shape parameters
     
    139139
    140140        pmSource *source = psfTry->sources->data[i];
    141         pmModel  *modelFLT = psfTry->modelFLT->data[i];
     141        pmModel  *modelEXT = psfTry->modelEXT->data[i];
    142142
    143143        // set shape for this model based on PSF
    144         pmModel *modelPSF = pmModelFromPSF (modelFLT, psfTry->psf);
     144        pmModel *modelPSF = pmModelFromPSF (modelEXT, psfTry->psf);
    145145        x = source->peak->x;
    146146        y = source->peak->y;
    147147
    148         psImageKeepCircle (source->mask, x, y, RADIUS, "OR", PSPHOT_MASK_MARKED);
    149         status = pmSourceFitModel (source, modelPSF, true);
     148        psImageKeepCircle (source->mask, x, y, RADIUS, "OR", PM_SOURCE_MASK_MARKED);
     149        status = pmSourceFitModel (source, modelPSF, PM_SOURCE_FIT_PSF);
    150150
    151151        // skip poor fits
     
    162162        // XXX : use a different estimator for the local sky?
    163163        // XXX : pass 'source' as input?
    164         if (!pmSourcePhotometry (&fitMag, &obsMag, modelPSF, source->pixels, source->mask)) {
     164        if (!pmSourcePhotometryModel (&fitMag, modelPSF)) {
     165            psfTry->mask->data.U8[i] = PSFTRY_MASK_BAD_PHOT;
     166            goto next_source;
     167        }
     168        if (!pmSourcePhotometryAper (&obsMag, modelPSF, source->pixels, source->mask)) {
    165169            psfTry->mask->data.U8[i] = PSFTRY_MASK_BAD_PHOT;
    166170            goto next_source;
     
    172176
    173177next_source:
    174         psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PSPHOT_MASK_MARKED);
    175 
    176     }
     178        psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PM_SOURCE_MASK_MARKED);
     179
     180    }
     181    psfTry->psf->nPSFstars = Npsf;
     182
    177183    psLogMsg ("psphot.psftry", 4, "fit psf:   %f sec for %d sources\n", psTimerMark ("fit"), sources->n);
    178184    psTrace ("psphot.psftry", 3, "keeping %d of %d PSF candidates (PSF)\n", Npsf, sources->n);
    179185
    180     // make this optional
    181     // DumpModelFits (psfTry->modelPSF, "modelsPSF.dat");
     186    // measure the chi-square trend as a function of flux (PAR[1])
     187    // this should be linear for Poisson errors and quadratic for constant sky errors
     188    psVector *flux  = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64);
     189    psVector *chisq = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64);
     190    psVector *mask  = psVectorAlloc (psfTry->sources->n, PS_TYPE_MASK);
     191
     192    // write sources with models first
     193    for (int i = 0; i < psfTry->sources->n; i++) {
     194        pmModel *model = psfTry->modelPSF->data[i];
     195        if (model == NULL) {
     196            flux->data.F64[i] = 0.0;
     197            chisq->data.F64[i] = 0.0;
     198            mask->data.U8[i] = 1;
     199        } else {
     200            flux->data.F64[i] = model->params->data.F32[1];
     201            chisq->data.F64[i] = model[0].chisq/model[0].nDOF;
     202            mask->data.U8[i] = 0;
     203        }
     204    }
     205    // use 3hi/3lo sigma clipping on the r2rflux vs metric fit
     206    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
     207    stats->clipSigma = 3.0;
     208    stats->clipIter = 3;
     209    // linear clipped fit of ApResid to r2rflux
     210    psVectorClipFitPolynomial1D (psfTry->psf->ChiTrend, stats, mask, 1, chisq, NULL, flux);
     211    for (int i = 0; i < psfTry->psf->ChiTrend->nX + 1; i++) {
     212        psLogMsg ("pmPSFtry", 4, "chisq vs flux fit term %d: %f +/- %f\n", i, psfTry->psf->ChiTrend->coeff[i]*pow(10000, i), psfTry->psf->ChiTrend->coeffErr[i]);
     213    }
     214    psLogMsg ("pmPSFtry", 4, "chisq vs flux fit: %f +/- %f\n", stats->sampleMedian, stats->sampleStdev);
     215    psFree (stats);
     216    psFree (flux);
     217    psFree (chisq);
    182218
    183219    // XXX this function wants aperture radius for pmSourcePhotometry
    184     pmPSFtryMetric_Alt (psfTry, RADIUS);
    185     psLogMsg ("psphot.pspsf", 3, "try model %s, ap-fit: %f +/- %f, sky bias: %f\n",
    186               modelName,
    187               psfTry->psf->ApResid,
    188               psfTry->psf->dApResid,
    189               psfTry->psf->skyBias);
     220    pmPSFtryMetric (psfTry, RADIUS);
     221    psLogMsg ("psphot.pspsf", 3, "try model %s, ap-fit: %f +/- %f : sky bias: %f\n",
     222              modelName, psfTry->psf->ApResid, psfTry->psf->dApResid, psfTry->psf->skyBias);
    190223
    191224    return (psfTry);
    192225}
    193226
    194 
    195227bool pmPSFtryMetric (pmPSFtry *psfTry, float RADIUS)
    196228{
    197 
    198     float dBin;
    199     int   nKeep, nSkip;
    200 
    201229    // the measured (aperture - fit) magnitudes (dA == psfTry->metric)
    202230    //   depend on both the true ap-fit (dAo) and the bias in the sky measurement:
     
    208236    //   we use an outlier rejection to avoid this bias
    209237
    210     // rflux = ten(0.4*fitMag);
    211     psVector *rflux = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64);
    212     rflux->n = rflux->nalloc;
     238    // r2rflux = radius^2 * ten(0.4*fitMag);
     239    psVector *r2rflux = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64);
    213240    for (int i = 0; i < psfTry->sources->n; i++) {
    214241        if (psfTry->mask->data.U8[i] & PSFTRY_MASK_ALL)
    215242            continue;
    216         rflux->data.F64[i] = pow(10.0, 0.4*psfTry->fitMag->data.F64[i]);
    217     }
    218 
    219     // find min and max of (1/flux):
    220     psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
    221     psVectorStats (stats, rflux, NULL, psfTry->mask, PSFTRY_MASK_ALL);
    222 
    223     // build binned versions of rflux, metric
    224     dBin = (stats->max - stats->min) / 10.0;
    225     psVector *rfBin = psVectorCreate(NULL, stats->min, stats->max, dBin, PS_TYPE_F64);
    226     psVector *daBin = psVectorAlloc (rfBin->n, PS_TYPE_F64);
    227     psVector *maskB = psVectorAlloc (rfBin->n, PS_TYPE_U8);
    228     daBin->n = daBin->nalloc;
    229     maskB->n = maskB->nalloc;
    230     psFree (stats);
    231 
    232     psTrace ("psphot.metricmodel", 3, "rflux max: %g, min: %g, delta: %g\n", stats->max, stats->min, dBin);
    233 
    234     // group data in daBin bins, measure lower 50% mean
    235     for (int i = 0; i < daBin->n; i++) {
    236 
    237         psVector *tmp = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64);
    238         tmp->n = 0;
    239 
    240         // accumulate data within bin range
    241         for (int j = 0; j < psfTry->sources->n; j++) {
    242             // masked for: bad model fit, outlier in parameters
    243             if (psfTry->mask->data.U8[j] & PSFTRY_MASK_ALL)
    244                 continue;
    245 
    246             // skip points with extreme dA values
    247             if (fabs(psfTry->metric->data.F64[j]) > 0.5)
    248                 continue;
    249 
    250             // skip points outside of this bin
    251             if (rflux->data.F64[j] < rfBin->data.F64[i] - 0.5*dBin)
    252                 continue;
    253             if (rflux->data.F64[j] > rfBin->data.F64[i] + 0.5*dBin)
    254                 continue;
    255 
    256             tmp->data.F64[tmp->n] = psfTry->metric->data.F64[j];
    257             tmp->n ++;
    258         }
    259 
    260         // is this a valid point?
    261         maskB->data.U8[i] = 0;
    262         if (tmp->n < 2) {
    263             maskB->data.U8[i] = 1;
    264             psFree (tmp);
    265             continue;
    266         }
    267 
    268         // dA values are contaminated with low outliers
    269         // measure statistics only on upper 50% of points
    270         // this would be easier if we could sort in reverse:
    271         //
    272         // psVectorSort (tmp, tmp);
    273         // tmp->n = 0.5*tmp->n;
    274         // stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
    275         // psVectorStats (stats, tmp, NULL, NULL, 0);
    276         // psTrace ("psphot.metricmodel", 4, "rfBin %d (%g): %d pts, %g\n", i, rfBin->data.F64[i], tmp->n, stats->sampleMedian);
    277 
    278         psVectorSort (tmp, tmp);
    279         nKeep = 0.5*tmp->n;
    280         nSkip = tmp->n - nKeep;
    281 
    282         psVector *tmp2 = psVectorAlloc (nKeep, PS_TYPE_F64);
    283         tmp2->n = tmp2->nalloc;
    284         for (int j = 0; j < tmp2->n; j++) {
    285             tmp2->data.F64[j] = tmp->data.F64[j + nSkip];
    286         }
    287 
    288         stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
    289         psVectorStats (stats, tmp2, NULL, NULL, 0);
    290         psTrace ("psphot.metricmodel", 4, "rfBin %d (%g): %d pts, %g\n", i, rfBin->data.F64[i], tmp->n, stats->sampleMedian);
    291 
    292         daBin->data.F64[i] = stats->sampleMedian;
    293 
    294         psFree (stats);
    295         psFree (tmp);
    296         psFree (tmp2);
    297     }
    298 
    299     // linear fit to rfBin, daBin
    300     psPolynomial1D *poly = psPolynomial1DAlloc(1, PS_POLYNOMIAL_ORD);
    301     psStats *fitstat = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
    302     poly = psVectorClipFitPolynomial1D (poly, fitstat, maskB, 1, daBin, NULL, rfBin);
    303 
    304     // XXX EAM : this is the intended API (cycle 7? cycle 8?)
    305     // poly = psVectorFitPolynomial1D(poly, maskB, 1, daBin, NULL, rfBin);
    306 
    307     // XXX EAM : replace this when the above version is implemented
    308     // poly = psVectorFitPolynomial1DOrd(poly, maskB, rfBin, daBin, NULL);
    309 
    310     psVector *daBinFit = psPolynomial1DEvalVector (poly, rfBin);
    311     psVector *daResid  = (psVector *) psBinaryOp (NULL, (void *) daBin, "-", (void *) daBinFit);
    312 
    313     stats = psStatsAlloc (PS_STAT_CLIPPED_STDEV);
    314     stats = psVectorStats (stats, daResid, NULL, maskB, 1);
    315 
    316     psfTry->psf->ApResid = poly->coeff[0];
    317     psfTry->psf->dApResid = stats->clippedStdev;
    318     psfTry->psf->skyBias = poly->coeff[1] / (M_PI * PS_SQR(RADIUS));
    319 
    320     psFree (rflux);
    321     psFree (rfBin);
    322     psFree (daBin);
    323     psFree (maskB);
    324     psFree (daBinFit);
    325     psFree (daResid);
    326     psFree (poly);
    327     psFree (stats);
    328     psFree (fitstat);
    329 
    330     return true;
    331 }
    332 
    333 bool pmPSFtryMetric_Alt (pmPSFtry *try
    334                          , float RADIUS)
    335 {
    336 
    337     // the measured (aperture - fit) magnitudes (dA == try->metric)
    338     //   depend on both the true ap-fit (dAo) and the bias in the sky measurement:
    339     //     dA = dAo + dsky/flux
    340     //   where flux is the flux of the star
    341     // we fit this trend to find the infinite flux aperture correction (dAo),
    342     //   the nominal sky bias (dsky), and the error on dAo
    343     // the values of dA are contaminated by stars with close neighbors in the aperture
    344     //   we use an outlier rejection to avoid this bias
    345 
    346     // rflux = ten(0.4*fitMag);
    347     psVector *rflux = psVectorAlloc (try
    348                                      ->sources->n, PS_TYPE_F64);
    349     rflux->n = rflux->nalloc;
    350     for (int i = 0; i < try
    351                 ->sources->n; i++) {
    352             if (try
    353                     ->mask->data.U8[i] & PSFTRY_MASK_ALL) continue;
    354             rflux->data.F64[i] = pow(10.0, 0.4*try
    355                                      ->fitMag->data.F64[i]);
    356         }
    357 
    358     // XXX EAM : try 3hi/1lo sigma clipping on the rflux vs metric fit
     243        r2rflux->data.F64[i] = PS_SQR(RADIUS) * pow(10.0, 0.4*psfTry->fitMag->data.F64[i]);
     244    }
     245
     246    // use 3hi/1lo sigma clipping on the r2rflux vs metric fit
    359247    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
    360 
    361     // XXX EAM
    362248    stats->min = 1.0;
    363249    stats->max = 3.0;
    364250    stats->clipIter = 3;
    365251
    366     // linear clipped fit to rfBin, daBin
    367     psPolynomial1D *poly = psPolynomial1DAlloc (1, PS_POLYNOMIAL_ORD);
    368     poly = psVectorClipFitPolynomial1D (poly, stats, try
    369                                             ->mask, PSFTRY_MASK_ALL, try
    370                                                 ->metric, NULL, rflux);
    371     fprintf (stderr, "fit stats: %f +/- %f\n", stats->sampleMedian, stats->sampleStdev);
    372 
    373     try
    374         ->psf->ApResid = poly->coeff[0];
    375     try
    376         ->psf->dApResid = stats->sampleStdev;
    377     try
    378         ->psf->skyBias = poly->coeff[1] / (M_PI * PS_SQR(RADIUS));
    379 
    380     fprintf (stderr, "*******************************************************************************\n");
    381 
    382     FILE *f;
    383     f = fopen ("apresid.dat", "w");
    384     if (f == NULL)
    385         psAbort ("pmPSFtry", "can't open output file");
    386 
    387     for (int i = 0; i < try
    388                 ->sources->n; i++) {
    389             fprintf (f, "%3d %8.4f %12.5e %8.4f %3d\n", i, try
    390                          ->fitMag->data.F64[i], rflux->data.F64[i], try
    391                              ->metric->data.F64[i], try
    392                                  ->mask->data.U8[i]);
    393         }
    394     fclose (f);
    395 
    396     psFree (rflux);
     252    // fit ApTrend only to r2rflux, ignore x,y,flux variations for now
     253    // linear clipped fit of ApResid to r2rflux
     254    psPolynomial1D *poly = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 1);
     255    poly = psVectorClipFitPolynomial1D (poly, stats, psfTry->mask, PSFTRY_MASK_ALL, psfTry->metric, NULL, r2rflux);
     256    psLogMsg ("pmPSFtryMetric", 4, "fit stats: %f +/- %f\n", stats->sampleMedian, stats->sampleStdev);
     257
     258    pmPSF_MaskApTrend (psfTry->psf, PM_PSF_SKYBIAS);
     259    psfTry->psf->ApTrend->coeff[0][0][0][0] = poly->coeff[0];
     260    psfTry->psf->ApTrend->coeff[0][0][1][0] = 0;
     261
     262    psfTry->psf->skyBias  = poly->coeff[1];
     263    psfTry->psf->ApResid  = poly->coeff[0];
     264    psfTry->psf->dApResid = stats->sampleStdev;
     265
     266    psFree (r2rflux);
    397267    psFree (poly);
    398268    psFree (stats);
    399269
    400     // psFree (daFit);
    401     // psFree (daResid);
    402 
    403270    return true;
    404271}
     272
     273/*
     274  (aprMag' - fitMag) = rflux*skyBias + ApTrend(x,y)
     275  (aprMag - rflux*skyBias) - fitMag = ApTrend(x,y)
     276  (aprMag - rflux*skyBias) = fitMag + ApTrend(x,y)
     277*/
     278
  • trunk/psModules/src/objects/pmPSFtry.h

    r6872 r6873  
    66 *  @author EAM, IfA
    77 *
    8  *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
    9  *  @date $Date: 2006-04-17 18:01:05 $
    108 *
    119 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
Note: See TracChangeset for help on using the changeset viewer.