IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
May 3, 2010, 8:50:52 AM (16 years ago)
Author:
eugene
Message:

updates from trunk

Location:
branches/simtest_nebulous_branches
Files:
8 deleted
136 edited
33 copied

Legend:

Unmodified
Added
Removed
  • branches/simtest_nebulous_branches

  • branches/simtest_nebulous_branches/psModules

  • branches/simtest_nebulous_branches/psModules/src/astrom/pmAstrometryModel.c

    r19595 r27840  
    725725    double X = Xo + RX*cos(POS - To)*cos(Po) + RY*sin(POS - To)*sin(Po);
    726726    double Y = Yo + RY*sin(POS - To)*cos(Po) - RX*cos(POS - To)*sin(Po);
    727     psLogMsg ("psModules.astrom", 4, "Boresite coords on reference chip: %f, %f\n", X, Y);
     727    psLogMsg ("psModules.astrom", 4, "Boresite coords on reference chip: %f, %f pix = %f, %f sky\n", X, Y, PM_DEG_RAD*RA, PM_DEG_RAD*DEC);
    728728
    729729    // get reference chip from name
  • branches/simtest_nebulous_branches/psModules/src/astrom/pmAstrometryObjects.c

    r24034 r27840  
    3838#include "pmAstrometryVisual.h"
    3939
     40// XXX this is defined in pmPSFtry.h, which makes no sense
     41float psVectorSystematicError (psVector *residuals, psVector *errors, float clipFraction);
     42
    4043#define PM_ASTROMETRYOBJECTS_DEBUG 1
    4144
     
    105108        }
    106109
    107         if (found1->data.S8[i]) {
    108             i++;
    109             continue;
    110         }
    111         if (found2->data.S8[j]) {
    112             j++;
    113             continue;
    114         }
     110        if (found1->data.S8[i]) {
     111            i++;
     112            continue;
     113        }
     114        if (found2->data.S8[j]) {
     115            j++;
     116            continue;
     117        }
    115118
    116119        jStart = j;
     
    125128                continue;
    126129            }
    127             if (found2->data.S8[j]) {
    128                 j++;
    129                 continue;
    130             }
     130            if (found2->data.S8[j]) {
     131                j++;
     132                continue;
     133            }
    131134
    132135            // got a match; add to output list
     
    135138            psFree (match);
    136139
    137             found1->data.S8[i] = 1;
    138             found2->data.S8[j] = 1;
     140            found1->data.S8[i] = 1;
     141            found2->data.S8[j] = 1;
    139142
    140143            j++;
     
    193196    psArray *matches = match_lists(x1, y1, x2, y2, sorted1, sorted2, RADIUS); \
    194197    \
    195     psFree((void *)sorted1); \
    196     psFree((void *)sorted2); \
     198    psFree(sorted1); \
     199    psFree(sorted2); \
    197200    psFree(x1); \
    198201    psFree(y1); \
     
    283286            return results;
    284287        }
    285         psTrace ("psModules.astrom", 3, "x resid: %f +/- %f (%ld of %ld)\n", results->xStats->clippedMean, results->xStats->clippedStdev, results->xStats->clippedNvalues, x->n);
     288        // psTrace ("psModules.astrom", 3, "x resid: %f +/- %f (%ld of %ld)\n", results->xStats->clippedMean, results->xStats->clippedStdev, results->xStats->clippedNvalues, x->n);
     289        psTrace ("psModules.astrom", 3, "x resid: %f +/- %f (%ld of %ld)\n", results->xStats->robustMedian, results->xStats->robustStdev, results->xStats->clippedNvalues, x->n);
    286290
    287291        if (!psVectorClipFitPolynomial2D (map->y, results->yStats, mask, 0xff, y, wt, X, Y)) {
     
    296300            return results;
    297301        }
    298         psTrace ("psModules.astrom", 3, "y resid: %f +/- %f (%ld of %ld)\n", results->yStats->clippedMean, results->yStats->clippedStdev, results->yStats->clippedNvalues, y->n);
     302        // psTrace ("psModules.astrom", 3, "y resid: %f +/- %f (%ld of %ld)\n", results->yStats->clippedMean, results->yStats->clippedStdev, results->yStats->clippedNvalues, y->n);
     303        psTrace ("psModules.astrom", 3, "y resid: %f +/- %f (%ld of %ld)\n", results->yStats->robustMedian, results->yStats->robustStdev, results->yStats->clippedNvalues, y->n);
    299304    }
    300305    results->xStats->clipIter = stats->clipIter;
    301306    results->yStats->clipIter = stats->clipIter;
     307
     308    // *** calculate the 90%-ile and the systematic scatter for each direction.
     309
     310    // generate the X residual vector
     311    psVector *xFit = psPolynomial2DEvalVector (map->x, X, Y);
     312    if (!xFit) abort();
     313    psVector *xRes = (psVector *) psBinaryOp (NULL, x, "-", xFit);
     314    if (!xRes) abort();
     315    psFree (xFit);
     316
     317    psVector *yFit = psPolynomial2DEvalVector (map->y, X, Y);
     318    if (!yFit) abort();
     319    psVector *yRes = (psVector *) psBinaryOp (NULL, y, "-", yFit);
     320    if (!yRes) abort();
     321    psFree (yFit);
     322
     323    // extract a high-quality subset (unmasked, S/N > XXX) and position errors
     324    // XXX for now, generate a position error based on the magnitude error
     325    psVector *xErr     = psVectorAllocEmpty (match->n, PS_TYPE_F32);
     326    psVector *yErr     = psVectorAllocEmpty (match->n, PS_TYPE_F32);
     327    psVector *xResGood = psVectorAllocEmpty (match->n, PS_TYPE_F32);
     328    psVector *yResGood = psVectorAllocEmpty (match->n, PS_TYPE_F32);
     329
     330    for (int i = 0; i < match->n; i++) {
     331        if (mask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
     332        pmAstromMatch *pair = match->data[i];
     333        pmAstromObj *rawStar = raw->data[pair->raw];
     334        if (!isfinite(rawStar->dMag)) continue;
     335        if (rawStar->dMag > 0.02) continue;
     336
     337        // two likely failure values: NAN or 0.0 --> use dMag in this case
     338        float xErrValue, yErrValue;
     339        if (isfinite(rawStar->chip->xErr) && (rawStar->chip->xErr > 0.0)) {
     340            xErrValue = rawStar->chip->xErr;
     341        } else {
     342            xErrValue = PS_MAX(0.005, rawStar->dMag);
     343        }
     344        if (isfinite(rawStar->chip->yErr) && (rawStar->chip->yErr > 0.0)) {
     345            yErrValue = rawStar->chip->yErr;
     346        } else {
     347            yErrValue = PS_MAX(0.005, rawStar->dMag);
     348        }
     349
     350        psVectorAppend (xErr,     xErrValue);
     351        psVectorAppend (yErr,     yErrValue);
     352        psVectorAppend (xResGood, xRes->data.F32[i]);
     353        psVectorAppend (yResGood, yRes->data.F32[i]);
     354    }
     355
     356    results->dXsys = psVectorSystematicError (xResGood, xErr, 0.05);
     357    results->dYsys = psVectorSystematicError (yResGood, yErr, 0.05);
     358
     359    results->dXrange = pmAstromVectorRange (xResGood, 0.1, 0.9, results->xStats->clippedStdev);
     360    results->dYrange = pmAstromVectorRange (yResGood, 0.1, 0.9, results->yStats->clippedStdev);
     361
     362    psTrace ("psModules.astrom", 3, "dXsys: %f, dXrange: %f\n", results->dXsys, results->dXrange);
     363    psTrace ("psModules.astrom", 3, "dYsys: %f, dYrange: %f\n", results->dYsys, results->dYrange);
     364
     365    psFree (xErr);
     366    psFree (yErr);
     367    psFree (xRes);
     368    psFree (yRes);
     369    psFree (xResGood);
     370    psFree (yResGood);
    302371
    303372    psFree (x);
     
    311380}
    312381
     382// set the bin closest to the corresponding value.  if USE_END is +/- 1,
     383// out-of-range saturates on lower/upper bin REGARDLESS of actual value
     384#define PS_BIN_FOR_VALUE(RESULT, VECTOR, VALUE, USE_END) { \
     385        psVectorBinaryDisectResult result; \
     386        psScalar tmpScalar; \
     387        tmpScalar.type.type = PS_TYPE_F32; \
     388        tmpScalar.data.F32 = (VALUE); \
     389        RESULT = psVectorBinaryDisect (&result, VECTOR, &tmpScalar); \
     390        switch (result) { \
     391          case PS_BINARY_DISECT_PASS: \
     392            break; \
     393          case PS_BINARY_DISECT_OUTSIDE_RANGE: \
     394            psTrace("psModules.astrom", 6, "selected bin outside range"); \
     395            if (USE_END == -1) { RESULT = 0; } \
     396            if (USE_END == +1) { RESULT = VECTOR->n - 1; } \
     397            break; \
     398          case PS_BINARY_DISECT_INVALID_INPUT: \
     399          case PS_BINARY_DISECT_INVALID_TYPE: \
     400            psAbort ("programming error"); \
     401            break; \
     402        } }
     403
     404# define PS_BIN_INTERPOLATE(RESULT, VECTOR, BOUNDS, BIN, VALUE) { \
     405        float dX, dY, Xo, Yo, Xt; \
     406        if (BIN == BOUNDS->n - 1) { \
     407            dX = 0.5*(BOUNDS->data.F32[BIN+1] - BOUNDS->data.F32[BIN-1]); \
     408            dY = VECTOR->data.F32[BIN] - VECTOR->data.F32[BIN-1]; \
     409            Xo = 0.5*(BOUNDS->data.F32[BIN+1] + BOUNDS->data.F32[BIN]); \
     410            Yo = VECTOR->data.F32[BIN]; \
     411        } else { \
     412            dX = 0.5*(BOUNDS->data.F32[BIN+2] - BOUNDS->data.F32[BIN]); \
     413            dY = VECTOR->data.F32[BIN+1] - VECTOR->data.F32[BIN]; \
     414            Xo = 0.5*(BOUNDS->data.F32[BIN+1] + BOUNDS->data.F32[BIN]); \
     415            Yo = VECTOR->data.F32[BIN]; \
     416        } \
     417        if (dY != 0) { \
     418            Xt = (VALUE - Yo)*dX/dY + Xo; \
     419        } else { \
     420            Xt = Xo; \
     421        } \
     422        Xt = PS_MIN (BOUNDS->data.F32[BIN+1], PS_MAX(BOUNDS->data.F32[BIN], Xt)); \
     423        psTrace("psModules.astrom", 6, "(Xo, Yo, dX, dY, Xt, Yt) is (%.2f %.2f %.2f %.2f %.2f %.2f)\n", \
     424                Xo, Yo, dX, dY, Xt, VALUE); \
     425        RESULT = Xt; }
     426
     427float pmAstromVectorRange (psVector *myVector, float minFrac, float maxFrac, float stdevGuess) {
     428
     429    psStats *stats = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
     430    psHistogram *histogram = NULL;      // Histogram of the data
     431    psHistogram *cumulative = NULL;     // Cumulative histogram of the data
     432    float min = NAN, max = NAN;         // Mimimum and maximum values
     433
     434    // Get the minimum and maximum values
     435    if (!psVectorStats(stats, myVector, NULL, NULL, 0)) {
     436        psFree(stats);
     437        return NAN;
     438    }
     439    min = stats->min;
     440    max = stats->max;
     441    if (isnan(min) || isnan(max)) {
     442        psFree(stats);
     443        return NAN;
     444    }
     445
     446    psTrace("psModules.astrom", 5, "Data min/max is (%.2f, %.2f)\n", min, max);
     447
     448    // If all data points have the same value, then we set the appropriate members of stats and return.
     449    if (fabs(max - min) <= FLT_EPSILON) {
     450        psFree (stats);
     451        return 0.0;
     452    }
     453
     454    // Define the histogram bin size.
     455    float binSize = 0.001;
     456    long numBins = PS_MAX(PS_MIN(100000, (max - min) / binSize), 2); // Number of bins
     457    psTrace("psModules.astrom", 5, "Numbins is %ld\n", numBins);
     458    psTrace("psModules.astrom", 5, "Creating a robust histogram from data range (%.2f - %.2f)\n", min, max);
     459
     460    // allocate the histogram containers
     461    histogram = psHistogramAlloc(min, max, numBins);
     462    cumulative = psHistogramAlloc(min, max, numBins);
     463
     464    if (!psVectorHistogram(histogram, myVector, NULL, NULL, 0)) {
     465        // if psVectorHistogram returns false, we have a programming error
     466        psAbort ("Unable to generate histogram");
     467    }
     468    if (psTraceGetLevel("psModules.astrom") >= 8) {
     469        PS_VECTOR_PRINT_F32(histogram->bounds);
     470        PS_VECTOR_PRINT_F32(histogram->nums);
     471    }
     472
     473    // Convert the specific histogram to a cumulative histogram
     474    // The cumulative histogram data points correspond to the UPPER bound value (N < Bin[i+1])
     475    cumulative->nums->data.F32[0] = histogram->nums->data.F32[0];
     476    for (long i = 1; i < histogram->nums->n; i++) {
     477        cumulative->nums->data.F32[i] = cumulative->nums->data.F32[i-1] + histogram->nums->data.F32[i];
     478        cumulative->bounds->data.F32[i-1] = histogram->bounds->data.F32[i];
     479    }
     480    if (psTraceGetLevel("psModules.astrom") >= 8) {
     481        PS_VECTOR_PRINT_F32(cumulative->bounds);
     482        PS_VECTOR_PRINT_F32(cumulative->nums);
     483    }
     484
     485    // Find the bin which contains the first data point above the limit
     486    long totalDataPoints = cumulative->nums->data.F32[numBins - 1];
     487    psTrace("psModules.astrom", 6, "Total data points is %ld\n", totalDataPoints);
     488
     489    // find bin which is the lower bound of the limit value (value[bin] < f < value[bin+1]
     490    long binMin;
     491    PS_BIN_FOR_VALUE(binMin, cumulative->nums, minFrac * totalDataPoints, 0);
     492    psTrace("psModules.astrom", 6, "The bin is %ld (%.4f to %.4f)\n", binMin, cumulative->bounds->data.F32[binMin], cumulative->bounds->data.F32[binMin+1]);
     493
     494    // Linear interpolation to the limit value in bin units
     495    float valueMin;
     496    PS_BIN_INTERPOLATE (valueMin, cumulative->nums, cumulative->bounds, binMin, totalDataPoints * minFrac);
     497    psTrace("psModules.astrom", 6, "limit value is %f\n", valueMin);
     498
     499    // find bin which is the lower bound of the limit value (value[bin] < f < value[bin+1]
     500    long binMax;
     501    PS_BIN_FOR_VALUE(binMax, cumulative->nums, maxFrac * totalDataPoints, 0);
     502    psTrace("psModules.astrom", 6, "The bin is %ld (%.4f to %.4f)\n", binMax, cumulative->bounds->data.F32[binMax], cumulative->bounds->data.F32[binMax+1]);
     503
     504    // Linear interpolation to the limit value in bin units
     505    float valueMax;
     506    PS_BIN_INTERPOLATE (valueMax, cumulative->nums, cumulative->bounds, binMax, totalDataPoints * maxFrac);
     507    psTrace("psModules.astrom", 6, "limit value is %f\n", valueMax);
     508
     509    // Clean up
     510    psFree(histogram);
     511    psFree(cumulative);
     512    psFree(stats);
     513
     514    return (valueMax - valueMin);
     515}
    313516
    314517/******************************************************************************
     
    634837        }
    635838
    636 # if 0
    637         char line[16];
    638         psFits *fits = psFitsOpen ("grid.image.fits", "w");
    639         psFitsWriteImage (fits, NULL, gridNP, 0, NULL);
    640         psFitsClose (fits);
    641         fprintf (stderr, "wrote grid image, press return to continue\n");
    642         fgets (line, 15, stdin);
    643 # endif
     839        if (psTraceGetLevel("psModules.astrom") >= 5) {
     840            char line[16];
     841            psFits *fits = psFitsOpen ("grid.image.fits", "w");
     842            psFitsWriteImage (fits, NULL, gridNP, 0, NULL);
     843            psFitsClose (fits);
     844            fprintf (stderr, "wrote grid image, press return to continue\n");
     845            if (!fgets (line, 15, stdin)) {
     846                fprintf(stderr, "Error waiting for RETURN.");
     847            }
     848        }
    644849
    645850        // only check bins with at least 1/2 of max bin
    646851        // XXX requiring at least 3 matches in bin
    647852        int minNpts = PS_MAX (0.5*imStats->max, 5);
    648         psTrace("psModule.astrom", 5, "minNpts: %d, min: %d, max: %d, median: %f, stdev: %f", minNpts, (int)(imStats->min), (int)(imStats->max), imStats->sampleMedian, imStats->sampleStdev);
     853        psTrace("psModule.astrom", 4, "minNpts: %d, min: %d, max: %d, median: %f, stdev: %f", minNpts, (int)(imStats->min), (int)(imStats->max), imStats->sampleMedian, imStats->sampleStdev);
    649854
    650855        // find the 'best' bin
     
    687892
    688893        // XXX this function is crashing
    689         // pmAstromVisualPlotGridMatch(raw, ref, gridNP, stats->offset.x, stats->offset.y, maxOffpix, Scale, Offset);
     894        pmAstromVisualPlotGridMatch(raw, ref, gridNP, stats->offset.x, stats->offset.y, maxOffpix, Scale, Offset);
     895        pmAstromVisualPlotGridMatchOverlay(raw, ref);
    690896
    691897        psFree (imStats);
     
    9621168*/
    9631169
     1170/*****************************************************************************/
     1171static void pmAstromMatchInfoFree (pmAstromMatchInfo *info)
     1172{
     1173    if (info == NULL) return;
     1174    return;
     1175}
     1176
     1177
     1178/*****************************************************************************/
     1179pmAstromMatchInfo *pmAstromMatchInfoAlloc()
     1180{
     1181    pmAstromMatchInfo *info = psAlloc (sizeof(pmAstromMatchInfo));
     1182    psMemSetDeallocator(info, (psFreeFunc) pmAstromMatchInfoFree);
     1183
     1184    info->match = NULL;
     1185    info->radius = NAN;
     1186
     1187    return (info);
     1188}
     1189
     1190// generate a unique set of matches (choose closest match)
     1191psArray *pmAstromRadiusMatchUniq (psArray *rawstars, psArray *refstars, psArray *matches) {
     1192
     1193    // I have the matches between the refstars and the rawstars.
     1194    // For each refstar, find the single match which has the smallest radius and reject
     1195    // all others.
     1196
     1197    // create an array of refstars->n arrays, each containing all of the matches for the
     1198    // given refstar.
     1199
     1200    psArray *refstarMatches = psArrayAlloc (refstars->n);
     1201
     1202    for (int i = 0; i < matches->n; i++) {
     1203
     1204        pmAstromMatch *match = matches->data[i];
     1205
     1206        // accumulate this refstar match on the array for this refstar (create if needed)
     1207        psArray *refSet = refstarMatches->data[match->ref];
     1208        if (!refSet) {
     1209            refstarMatches->data[match->ref] = psArrayAllocEmpty (8);
     1210            refSet = refstarMatches->data[match->ref];
     1211        }
     1212
     1213        pmAstromMatchInfo *matchInfo = pmAstromMatchInfoAlloc();
     1214
     1215        pmAstromObj *refStar = refstars->data[match->ref];
     1216        pmAstromObj *rawStar = rawstars->data[match->raw];
     1217
     1218        matchInfo->match = match; // reference to the match of interest
     1219        matchInfo->radius = hypot (refStar->FP->x - rawStar->FP->x, refStar->FP->y - rawStar->FP->y);
     1220
     1221        psArrayAdd (refSet, 8, matchInfo); // matchInfo->match is just a reference
     1222        psFree (matchInfo);
     1223    }
     1224
     1225    // we now have a set of matches for each refstar and their distances; create a new set
     1226    // keeping only the closest entry for each match
     1227
     1228    psArray *unique = psArrayAllocEmpty (PS_MAX(16, matches->n / 2));
     1229    for (int i = 0; i < refstars->n; i++) {
     1230
     1231        psArray *refSet = refstarMatches->data[i];
     1232        if (!refSet) continue;
     1233        if (refSet->n == 0) continue; // not certain how this can happen...
     1234
     1235        if (refSet->n == 1) {
     1236            pmAstromMatchInfo *matchInfo = refSet->data[0];
     1237            psArrayAdd (unique, 32, matchInfo->match);
     1238            continue;
     1239        }
     1240
     1241        pmAstromMatchInfo *matchInfo = refSet->data[0];
     1242        float minRadius = matchInfo->radius;
     1243        pmAstromMatch *minMatch = matchInfo->match;
     1244        for (int j = 1; j < refSet->n; j++) {
     1245            pmAstromMatchInfo *matchInfo = refSet->data[j];
     1246            if (minRadius < matchInfo->radius) continue;
     1247            minMatch = matchInfo->match;
     1248            minRadius = matchInfo->radius;
     1249        }
     1250
     1251        psArrayAdd (unique, 32, minMatch); // minMatch is just a reference to a match on matches,
     1252    }
     1253
     1254    psLogMsg ("psModules.astrom", 3, "generate unique matches to reference stars: %ld matches -> %ld matches\n", matches->n, unique->n);
     1255    psFree (refstarMatches);
     1256
     1257    return unique;
     1258}
  • branches/simtest_nebulous_branches/psModules/src/astrom/pmAstrometryObjects.h

    r24021 r27840  
    5454typedef struct
    5555{
    56     int raw;                             ///< What is this?
    57     int ref;                             ///< What is this?
     56    int raw;                             ///< reference to the rawstar entry
     57    int ref;                             ///< reference to the refstar entry
    5858}
    5959pmAstromMatch;
     60
     61
     62/*
     63 * The pmAstromMatchInfo structure is used to generate a unique set of matches
     64 */
     65typedef struct
     66{
     67    pmAstromMatch *match;               ///< reference to the match
     68    float radius;                       ///< distance between the object
     69}
     70pmAstromMatchInfo;
    6071
    6172
     
    8596    int     nMatch;                     ///<
    8697    double  nSigma;                     ///<
     98    double  dXsys;                      ///< systematic error in X
     99    double  dYsys;                      ///< systematic error in Y
     100    double  dXrange;                    ///< 10% - 90% range X residuals (unmasked, high S/N)
     101    double  dYrange;                    ///< 10% - 90% range Y residuals (unmasked, high S/N)
    87102}
    88103pmAstromFitResults;
     
    121136);
    122137
     138psArray *pmAstromRadiusMatchUniq (psArray *refstars, psArray *rawstars, psArray *matches);
    123139
    124140pmAstromStats *pmAstromStatsAlloc(void);
     
    343359);
    344360
     361float pmAstromVectorRange (psVector *myVector, float minFrac, float maxFrac, float stdevGuess);
     362
    345363/// @}
    346364#endif // PM_ASTROMETRY_OBJECTS_H
  • branches/simtest_nebulous_branches/psModules/src/astrom/pmAstrometryVisual.c

    r24818 r27840  
    450450
    451451    graphdata.color = KapaColorByName ("red");
    452     graphdata.style = 1;
     452    graphdata.style = 0;
    453453
    454454    //overplot clumpy regions excluded from analysis
     
    905905    KapaPlotVector (kapa, gridNP->numCols, horizontalIndices, "x");
    906906    KapaPlotVector (kapa, gridNP->numCols, horizHistSlice, "y");
     907
    907908    float xslice[2] = {offsetX - Scale / 2., offsetX - Scale / 2.};
    908909    float yslice[2] = {-5, 100};
     910    graphdata.style = 0;
    909911    graphdata.color = KapaColorByName("red");
    910912    KapaPrepPlot(kapa, 2, &graphdata);
     
    927929    KapaPlotVector (kapa, gridNP->numRows, vertHistSlice, "x");
    928930    KapaPlotVector (kapa, gridNP->numRows, verticalIndices, "y");
     931
    929932    yslice[0] = yslice[1] = offsetY - Scale / 2.;
    930933    xslice[0] = -5; xslice[1] = 100;
     934    graphdata.style = 0;
    931935    graphdata.color = KapaColorByName("red");
    932936    KapaPrepPlot(kapa, 2, &graphdata);
     
    940944} // end of pmAstromVisualPlotGridMatch
    941945
     946
     947bool pmAstromVisualPlotGridMatchOverlay (const psArray *raw,
     948                                         const psArray *ref)
     949{
     950    //make sure we want to plot this
     951    if (!pmVisualIsVisual() || !plotGridMatch) return true;
     952    if (!pmVisualInitWindow(&kapa2, "psastro:plots")){
     953        return false;
     954    }
     955
     956    Graphdata graphdata;
     957    psVector *xPlot = psVectorAlloc (PS_MAX(raw->n, ref->n), PS_TYPE_F32); // x data points
     958    psVector *yPlot = psVectorAlloc (PS_MAX(raw->n, ref->n), PS_TYPE_F32); // y data points
     959    psVector *zPlot = psVectorAlloc (PS_MAX(raw->n, ref->n), PS_TYPE_F32); // y data points
     960
     961    // set up plot information
     962    KapaClearPlots(kapa2);
     963    KapaInitGraph(&graphdata);
     964
     965    KapaSetFont(kapa2, "helvetica", 14);
     966    KapaBox(kapa2, &graphdata);
     967    KapaSendLabel (kapa2, "X (FP)", KAPA_LABEL_XM);
     968    KapaSendLabel (kapa2, "Y (FP)", KAPA_LABEL_YM);
     969    KapaSendLabel (kapa2, "pmAstromGridAngle residuals. Box: Correlation Peak.", KAPA_LABEL_XP);
     970
     971    // plot the REF data.  (also calculate the plot ranges, accumulate the plot vectors)
     972    graphdata.xmin = +INT_MAX;
     973    graphdata.xmax = -INT_MAX;
     974    graphdata.ymin = +INT_MAX;
     975    graphdata.ymax = -INT_MAX;
     976    for (int i = 0; i < ref->n; i++) {
     977        pmAstromObj *obj = ref->data[i];
     978        graphdata.xmin = PS_MIN(graphdata.xmin, obj->FP->x);
     979        graphdata.xmax = PS_MAX(graphdata.xmax, obj->FP->x);
     980        graphdata.ymin = PS_MIN(graphdata.ymin, obj->FP->y);
     981        graphdata.ymax = PS_MAX(graphdata.ymax, obj->FP->y);
     982        xPlot->data.F32[i] = obj->FP->x;
     983        yPlot->data.F32[i] = obj->FP->y;
     984        zPlot->data.F32[i] = obj->Mag;
     985    }
     986    xPlot->n = yPlot->n = zPlot->n = ref->n;
     987    KapaSetLimits(kapa2, &graphdata);
     988
     989    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
     990    psVectorStats (stats, zPlot, NULL, NULL, 0);
     991    float zero = stats->sampleMedian + 3.0;
     992    float range = 6.0;
     993
     994    for (int i = 0; i < zPlot->n; i++) {
     995        float value = (zero - zPlot->data.F32[i]) / range;
     996        zPlot->data.F32[i] = PS_MAX(0.0, PS_MIN(1.0, value));
     997    }
     998
     999    // the point size will be scaled from the z vector
     1000    graphdata.style = 2;
     1001    graphdata.ptype = 7;
     1002    graphdata.size = -1;
     1003    graphdata.color = KapaColorByName ("black");
     1004
     1005    KapaPrepPlot   (kapa2, xPlot->n, &graphdata);
     1006    KapaPlotVector (kapa2, xPlot->n, xPlot->data.F32, "x");
     1007    KapaPlotVector (kapa2, yPlot->n, yPlot->data.F32, "y");
     1008    KapaPlotVector (kapa2, zPlot->n, zPlot->data.F32, "z");
     1009
     1010    // plot the RAW data (keep previous limits)
     1011    for (int i = 0; i < raw->n; i++) {
     1012        pmAstromObj *obj = raw->data[i];
     1013        xPlot->data.F32[i] = obj->FP->x;
     1014        yPlot->data.F32[i] = obj->FP->y;
     1015        zPlot->data.F32[i] = obj->Mag;
     1016    }
     1017    xPlot->n = yPlot->n = zPlot->n = raw->n;
     1018
     1019    psStatsInit(stats);
     1020    psVectorStats (stats, zPlot, NULL, NULL, 0);
     1021    zero = stats->sampleMedian + 3.0;
     1022    range = 6.0;
     1023
     1024    for (int i = 0; i < zPlot->n; i++) {
     1025        float value = (zero - zPlot->data.F32[i]) / range;
     1026        zPlot->data.F32[i] = PS_MAX(0.0, PS_MIN(1.0, value));
     1027    }
     1028
     1029    // the point size will be scaled from the z vector
     1030    graphdata.style = 2;
     1031    graphdata.ptype = 7;
     1032    graphdata.size = -1;
     1033    graphdata.color = KapaColorByName ("red");
     1034
     1035    KapaPrepPlot   (kapa2, xPlot->n, &graphdata);
     1036    KapaPlotVector (kapa2, xPlot->n, xPlot->data.F32, "x");
     1037    KapaPlotVector (kapa2, yPlot->n, yPlot->data.F32, "y");
     1038    KapaPlotVector (kapa2, zPlot->n, zPlot->data.F32, "z");
     1039
     1040    pmVisualAskUser(&plotGridMatch);
     1041    psFree(xPlot);
     1042    psFree(yPlot);
     1043    psFree(zPlot);
     1044    psFree(stats);
     1045    return true;
     1046}
    9421047
    9431048bool pmAstromVisualPlotTweak (psVector *xHist, // Smoothed Horizontal cut through the histogram
     
    12091314    KapaClearPlots (kapa2);
    12101315
    1211     graphdata.color = KapaColorByName ("black");
    1212     graphdata.ptype = 2;
     1316    KapaSendLabel (kapa2, "X", KAPA_LABEL_XM);
     1317    KapaSendLabel (kapa2, "Y", KAPA_LABEL_YM);
     1318    KapaSendLabel (kapa2, "Chip Coordinates. Black = Raw Stars. Red = Ref Stars. Blue = Matched Stars", KAPA_LABEL_XP);
     1319
     1320    // X vs Y by mag (ref)
     1321    graphdata.color = KapaColorByName ("red");
     1322    graphdata.ptype = 7;
    12131323    graphdata.style = 2;
    12141324
     
    12171327    psFree (zVec);
    12181328
    1219     xVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
    1220     yVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
    1221     zVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
    1222 
    1223     // X vs Y by mag (raw)
    1224     n = 0;
    1225     for (int i = 0; i < rawstars->n; i++) {
    1226         pmAstromObj *raw = rawstars->data[i];
    1227         if (!isfinite(raw->Mag)) continue;
    1228         if (raw->Mag < iMagMin) continue;
    1229         if (raw->Mag > iMagMax) continue;
    1230 
    1231         xVec->data.F32[n] = raw->chip->x;
    1232         yVec->data.F32[n] = raw->chip->y;
    1233         zVec->data.F32[n] = raw->Mag;
    1234         n++;
    1235     }
    1236     xVec->n = yVec->n = zVec->n = n;
    1237 
    1238     KapaSendLabel (kapa2, "X", KAPA_LABEL_XM);
    1239     KapaSendLabel (kapa2, "Y", KAPA_LABEL_YM);
    1240     KapaSendLabel (kapa2,
    1241                    "Chip Coordinates. Black = Raw Stars. Red = Ref Stars. Blue = Matched Stars"
    1242                    , KAPA_LABEL_XP);
    1243     pmVisualTriplePlot (kapa2, &graphdata, xVec, yVec, zVec, false);
    1244 
    1245     // X vs Y by mag (ref)
    1246     psFree (xVec);
    1247     psFree (yVec);
    1248     psFree (zVec);
    1249 
    12501329    xVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
    12511330    yVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
    12521331    zVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
    1253 
    1254     graphdata.color = KapaColorByName ("red");
    1255     graphdata.ptype = 7;
    1256     graphdata.style = 2;
    12571332
    12581333    n = 0;
     
    12691344    }
    12701345    xVec->n = yVec->n = zVec->n = n;
    1271     pmVisualTripleOverplot (kapa2, &graphdata, xVec, yVec, zVec, false);
     1346    pmVisualTriplePlot (kapa2, &graphdata, xVec, yVec, zVec, false);
    12721347
    12731348    //rescale the graph to include all points
     
    12821357    graphdata.ymax = PS_MAX(ymax, graphdata.ymax);
    12831358    KapaSetLimits (kapa2, &graphdata);
     1359
     1360    bool plotTweak;
     1361    pmVisualAskUser(&plotTweak);
     1362
     1363    // X vs Y by mag (raw)
     1364    graphdata.color = KapaColorByName ("black");
     1365    graphdata.ptype = 2;
     1366    graphdata.style = 2;
     1367
     1368    psFree (xVec);
     1369    psFree (yVec);
     1370    psFree (zVec);
     1371
     1372    xVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
     1373    yVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
     1374    zVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
     1375
     1376    n = 0;
     1377    for (int i = 0; i < rawstars->n; i++) {
     1378        pmAstromObj *raw = rawstars->data[i];
     1379        if (!isfinite(raw->Mag)) continue;
     1380        if (raw->Mag < iMagMin) continue;
     1381        if (raw->Mag > iMagMax) continue;
     1382
     1383        xVec->data.F32[n] = raw->chip->x;
     1384        yVec->data.F32[n] = raw->chip->y;
     1385        zVec->data.F32[n] = raw->Mag;
     1386        n++;
     1387    }
     1388    xVec->n = yVec->n = zVec->n = n;
     1389    pmVisualTripleOverplot (kapa2, &graphdata, xVec, yVec, zVec, false);
    12841390
    12851391    //overplot matched stars in blue
  • branches/simtest_nebulous_branches/psModules/src/astrom/pmAstrometryVisual.h

    r23487 r27840  
    4545                                  );
    4646
     47
     48bool pmAstromVisualPlotGridMatchOverlay (const psArray *raw,
     49                                         const psArray *ref);
    4750
    4851/**
  • branches/simtest_nebulous_branches/psModules/src/astrom/pmAstrometryWCS.c

    r24052 r27840  
    289289    // test the CDELTi varient
    290290    if (pcKeys) {
     291        wcs->wcsCDkeys = 0;
    291292        wcs->cdelt1 = psMetadataLookupF64 (&status, header, "CDELT1");
    292293        wcs->cdelt2 = psMetadataLookupF64 (&status, header, "CDELT2");
     
    334335    // test the CDi_j varient
    335336    if (cdKeys) {
     337        wcs->wcsCDkeys = 1;
     338
    336339        wcs->trans->x->coeff[1][0] = psMetadataLookupF64 (&status, header, "CD1_1"); // == PC1_1
    337340        wcs->trans->x->coeff[0][1] = psMetadataLookupF64 (&status, header, "CD1_2"); // == PC1_2
     
    375378    // XXX make it optional to write out CDi_j terms, or other versions
    376379    // apply CDELT1,2 (degrees / pixel) to yield PCi,j terms of order unity
    377     double cdelt1 = wcs->cdelt1;
    378     double cdelt2 = wcs->cdelt2;
    379     psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT1", PS_META_REPLACE, "", cdelt1);
    380     psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT2", PS_META_REPLACE, "", cdelt2);
    381 
    382     // test the PC00i00j varient:
    383     psMetadataAddF64 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", wcs->trans->x->coeff[1][0] / cdelt1); // == PC1_1
    384     psMetadataAddF64 (header, PS_LIST_TAIL, "PC001002", PS_META_REPLACE, "", wcs->trans->x->coeff[0][1] / cdelt2); // == PC1_2
    385     psMetadataAddF64 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", wcs->trans->y->coeff[1][0] / cdelt1); // == PC2_1
    386     psMetadataAddF64 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", wcs->trans->y->coeff[0][1] / cdelt2); // == PC2_2
    387 
    388     // Elixir-style polynomial terms
    389     // XXX currently, Elixir/DVO cannot accept mixed orders
    390     // XXX need to respect the masks
    391     // XXX is wcs->cdelt1,2 always consistent?
    392     int fitOrder = wcs->trans->x->nX;
    393     if (fitOrder > 1) {
     380    if (!wcs->wcsCDkeys) {
     381
     382      double cdelt1 = wcs->cdelt1;
     383      double cdelt2 = wcs->cdelt2;
     384      psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT1", PS_META_REPLACE, "", cdelt1);
     385      psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT2", PS_META_REPLACE, "", cdelt2);
     386     
     387      // test the PC00i00j varient:
     388      psMetadataAddF64 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", wcs->trans->x->coeff[1][0] / cdelt1); // == PC1_1
     389      psMetadataAddF64 (header, PS_LIST_TAIL, "PC001002", PS_META_REPLACE, "", wcs->trans->x->coeff[0][1] / cdelt2); // == PC1_2
     390      psMetadataAddF64 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", wcs->trans->y->coeff[1][0] / cdelt1); // == PC2_1
     391      psMetadataAddF64 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", wcs->trans->y->coeff[0][1] / cdelt2); // == PC2_2
     392     
     393      // Elixir-style polynomial terms
     394      // XXX currently, Elixir/DVO cannot accept mixed orders
     395      // XXX need to respect the masks
     396      // XXX is wcs->cdelt1,2 always consistent?
     397      int fitOrder = wcs->trans->x->nX;
     398      if (fitOrder > 1) {
    394399        for (int i = 0; i <= fitOrder; i++) {
    395             for (int j = 0; j <= fitOrder; j++) {
    396                 if (i + j < 2)
    397                     continue;
    398                 if (i + j > fitOrder)
    399                     continue;
    400                 sprintf (name, "PCA1X%1dY%1d", i, j);
    401                 psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->x->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
    402                 sprintf (name, "PCA2X%1dY%1d", i, j);
    403                 psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->y->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
    404             }
     400          for (int j = 0; j <= fitOrder; j++) {
     401            if (i + j < 2)
     402              continue;
     403            if (i + j > fitOrder)
     404              continue;
     405            sprintf (name, "PCA1X%1dY%1d", i, j);
     406            psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->x->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
     407            sprintf (name, "PCA2X%1dY%1d", i, j);
     408            psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->y->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
     409          }
    405410        }
    406411        psMetadataAddS32 (header, PS_LIST_TAIL, "NPLYTERM", PS_META_REPLACE, "", fitOrder);
    407     }
    408 
    409     // remove any existing 'CDi_j style' wcs keywords
    410     if (psMetadataLookup(header, "CD1_1")) {
     412      }
     413     
     414      // remove any existing 'CDi_j style' wcs keywords
     415      if (psMetadataLookup(header, "CD1_1")) {
    411416        psMetadataRemoveKey(header, "CD1_1");
    412417        psMetadataRemoveKey(header, "CD1_2");
    413418        psMetadataRemoveKey(header, "CD2_1");
    414419        psMetadataRemoveKey(header, "CD2_2");
     420      }
     421    } else {
     422
     423      psMetadataAddF64 (header, PS_LIST_TAIL, "CD1_1", PS_META_REPLACE, "", wcs->trans->x->coeff[1][0]);
     424      psMetadataAddF64 (header, PS_LIST_TAIL, "CD1_2", PS_META_REPLACE, "", wcs->trans->x->coeff[0][1]);
     425      psMetadataAddF64 (header, PS_LIST_TAIL, "CD2_1", PS_META_REPLACE, "", wcs->trans->y->coeff[1][0]);
     426      psMetadataAddF64 (header, PS_LIST_TAIL, "CD2_2", PS_META_REPLACE, "", wcs->trans->y->coeff[0][1]);
     427
     428      if (psMetadataLookup(header, "PC001001")) {
     429        psMetadataRemoveKey(header, "PC001001");
     430        psMetadataRemoveKey(header, "PC001002");
     431        psMetadataRemoveKey(header, "PC002001");
     432        psMetadataRemoveKey(header, "PC002002");
     433      }
    415434    }
    416435
     
    535554        fpa->toSky->R -= 2.0*M_PI;
    536555
     556    fpa->wcsCDkeys = wcs->wcsCDkeys;
     557
    537558    psTrace ("psastro", 5, "toFPA: %f %f  (%f,%f),(%f,%f)\n",
    538559             chip->toFPA->x->coeff[0][0], chip->toFPA->y->coeff[0][0],
     
    648669    wcs->crval2 = fpa->toSky->D*PS_DEG_RAD;
    649670
     671    // generate a transform that has 0.0 rotation:
     672    // get the current posangle of the ref chip
     673    // XXX average angles for x and y...
     674    float angle = atan2 (toTPA->y->coeff[1][0], toTPA->x->coeff[1][0]);
     675    // fprintf (stderr, "angle: %f\n", angle*PS_DEG_RAD);
     676    psPlaneTransform *tpa1 = psPlaneTransformRotate (NULL, toTPA, angle);
     677
    650678    // given transformation, solve for coordinates which yields output coordinates of 0,0
    651     psPlane *center = psPlaneTransformGetCenter (toTPA, tol);
     679    psPlane *center = psPlaneTransformGetCenter (tpa1, tol);
    652680    if (!center) {
    653681        psError(PS_ERR_UNKNOWN, false, "Unable to solve for TPA center.");
     
    657685    }
    658686
     687    // generate transform with the original orientation (does this rotate about 'center'?)
     688    psPlaneTransform *tpa2 = psPlaneTransformRotate (NULL, tpa1, -1.0*angle);
     689
     690    // prove that the center coordinates give 0,0:
     691    // float Xo = psPolynomial2DEval (tpa1->x, center->x, center->y);
     692    // float Yo = psPolynomial2DEval (tpa1->x, center->x, center->y);
     693    // fprintf (stderr, "tpa1: Xo, Yo: %f, %f\n", Xo, Yo);
     694
     695    // prove that the center coordinates give 0,0:
     696    // Xo = psPolynomial2DEval (tpa2->x, center->x, center->y);
     697    // Yo = psPolynomial2DEval (tpa2->x, center->x, center->y);
     698    // fprintf (stderr, "tpa2: Xo, Yo: %f, %f\n", Xo, Yo);
     699
    659700    // create wcs transform from toFPA, resulting transformation has units of microns/pixel
    660701    // adjust wcs transform to use center as reference coordinate
    661     psPlaneTransformSetCenter (wcs->trans, toTPA, center->x, center->y);
     702    psPlaneTransformSetCenter (wcs->trans, tpa2, center->x, center->y);
    662703
    663704    // calculated center is crpix1,2
     
    665706    wcs->crpix2 = center->y;
    666707    psFree (center);
     708    psFree (tpa1);
     709    psFree (tpa2);
    667710
    668711    // pdelt1,2 has units of degrees/micron
     
    682725    wcs->cdelt2 = hypot (wcs->trans->y->coeff[1][0], wcs->trans->y->coeff[0][1]);
    683726
     727    wcs->wcsCDkeys = fpa->wcsCDkeys;
    684728    psFree (toTPA);
    685729
     
    800844    int k=0;
    801845    for (int j=0; j<nSamples; j++) {
    802         double y = j * deltaY / nSamples;
     846        double y = bounds->y0 + (j * deltaY / nSamples);
    803847        for (int i=0; i<nSamples; i++) {
    804848            psPlane *s = psPlaneAlloc();
    805             s->x = i * deltaX / nSamples;
     849            s->x = bounds->x0 + (i * deltaX / nSamples);
    806850            s->y = y;
    807851            psArraySet(src, k, s);
    808852            psPlane *d = psPlaneTransformApply(NULL, trans, s);
    809853            psArraySet(dst, k, d);
    810             psFree(s);
     854            psFree(s);  // drop our refs to s and d
    811855            psFree(d);
    812856            ++k;
     
    821865    }
    822866
     867#define noCOMPARE_TRANS
    823868#ifdef COMPARE_TRANS
    824869    // compare the computed coordintes from this transform with the original
    825870    psPlane *new = psPlaneAlloc();
     871    printf("   i     chip_x  tpa_x     tpa_x_fit     dx         chip_y    tpa_y     tpa_y_fit     dy     dx > 0.5 || dy > 0.5\n");
    826872    for (int i=0; i<psArrayLength(dst); i++) {
    827873        psPlane *d = (psPlane *) psArrayGet(dst, i);
     
    830876        new = psPlaneTransformApply(new, newTrans, s);
    831877
    832         printf("%4d %f %f\n", i, 100.*(new->x - d->x)/d->x, 100.*(new->y - d->y)/d->y);
     878        double xerr = new->x - d->x;
     879        double yerr = new->y - d->y;
     880        bool bigerr = (fabs(xerr) > .5) || (fabs(yerr) > .5);
     881        printf("%4d %9.2f %9.2f %9.2f %9.4f     %9.2f %9.2f %9.2f %9.4f   %s\n"
     882        , i, s->x, new->x, d->x, xerr, s->y, new->y, d->y, yerr, bigerr ? "BIGERR" : "");
    833883    }
    834884    psFree(new);
     
    842892}
    843893
    844 bool pmAstromLinearizeTransforms(pmFPA *fpa, pmChip *chip)
    845 {
    846     psRegion    *chipBounds = pmChipPixels(chip);
    847 
    848     psPlaneTransform *newToFPA = linearFitToTransform(chip->toFPA, chipBounds);
    849     if (!newToFPA) {
    850         psFree(chipBounds);
    851         psError(PS_ERR_UNKNOWN, false, "linear fit for toFPA failed");
    852         return false;
    853     }
    854 
    855     psFree(chip->toFPA);
    856     chip->toFPA = newToFPA;
    857 
    858     psFree(chip->fromFPA);
    859     chip->fromFPA = psPlaneTransformInvert(NULL, chip->toFPA, *chipBounds, 50);
    860     if (!chip->fromFPA) {
    861         psError(PS_ERR_UNKNOWN, false, "failed to invert linear fit for toFPA");
    862         return false;
    863     }
    864 
    865     psPlane *chip0 = psPlaneAlloc();
    866     chip0->x = 0;
    867     chip0->y = 0;
    868     psPlane *chip1 = psPlaneAlloc();
    869     chip1->x = chipBounds->x1;
    870     chip1->y = chipBounds->y1;
    871 
    872     // compute bounding region for fpa
    873     psPlane *fpa0 = psPlaneTransformApply(NULL, newToFPA, chip0);
    874     psPlane *fpa1 = psPlaneTransformApply(NULL, newToFPA, chip1);
    875 
    876     psRegion *fpaBounds = psRegionAlloc(fpa0->x, fpa1->x, fpa0->y, fpa1->y);
    877     psFree(chip0);
    878     psFree(chip1);
    879     psFree(fpa0);
    880     psFree(fpa1);
    881 
    882     psPlaneTransform *newToTPA = linearFitToTransform(fpa->toTPA, fpaBounds);
    883     if (!newToTPA) {
    884         psError(PS_ERR_UNKNOWN, false, "failed to perform linear fit to toTPA");
    885         psFree(fpaBounds);
    886         return false;
    887     }
    888     psFree(fpa->toTPA);
    889     fpa->toTPA = newToTPA;
    890 
    891     // XXX: is this region ok?
    892     psFree(fpa->fromTPA);
    893     fpa->fromTPA = psPlaneTransformInvert(NULL, fpa->toTPA, *fpaBounds, 50);
    894     if (!fpa->fromTPA) {
    895         psError(PS_ERR_UNKNOWN, false, "failed to invert linear fit to toTPA");
    896         return false;
    897     }
    898 
    899     fpa->toSky->type = PS_PROJ_TAN;
    900 
    901     psFree(chipBounds);
    902     psFree(fpaBounds);
     894bool pmAstromLinearizeTransforms(pmFPA *inFPA, pmChip *inChip, pmFPA *outFPA, pmChip *outChip, psRegion *outputBounds, double offset_x, double offset_y)
     895{
     896    PS_ASSERT_PTR_NON_NULL(inFPA, NULL);
     897    PS_ASSERT_PTR_NON_NULL(inChip, NULL);
     898
     899    if (outFPA == NULL) {
     900        outFPA = inFPA;
     901    }
     902    if (outChip == NULL) {
     903        outChip = inChip;
     904    }
     905    if (outputBounds == NULL) {
     906        outputBounds = pmChipPixels(outChip);
     907    }
     908
     909    // First combine the "chip to FPA" and "FPA to TPA" into a single transformation
     910    psPlaneTransform *chipToTPA = psPlaneTransformCombine(NULL, inChip->toFPA, inFPA->toTPA, *outputBounds, 50);
     911    if (!chipToTPA) {
     912        psError(PS_ERR_UNKNOWN, false, "failed to create chipToTPA");
     913        return false;
     914    }
     915
     916    // Next do the linear fit within the output boundary pixels
     917    psPlaneTransform *chipToFPA = linearFitToTransform(chipToTPA, outputBounds);
     918    psFree(chipToTPA);
     919    if (!chipToFPA) {
     920        psError(PS_ERR_UNKNOWN, false, "linear fit of chip to TPA transform failed");
     921        return false;
     922    }
     923
     924    // if requested,  change the center
     925    psPlaneTransform *outToFPA;
     926    if (offset_x != 0. && offset_y != 0.) {
     927        outToFPA = psPlaneTransformSetCenter(NULL, chipToFPA, offset_x, offset_y);
     928        psFree(chipToFPA);
     929    } else {
     930        outToFPA = chipToFPA;
     931    }
     932
     933    psPlaneTransform *outFromFPA = psPlaneTransformInvert(NULL, outToFPA, *outputBounds, 50);
     934    if (!outFromFPA) {
     935        psFree(outToFPA);
     936        psError(PS_ERR_UNKNOWN, false, "inversion of fit of output chip toFPA failed");
     937        return false;
     938    }
     939
     940    // Success. Now set the fpa's toTPA and fromTPA to identity and replace the chip's transforms.
     941
     942    psFree(outFPA->toTPA);
     943    outFPA->toTPA =  psPlaneTransformIdentity(1);
     944
     945    psFree(outFPA->fromTPA);
     946    outFPA->fromTPA = psPlaneTransformIdentity(1);
     947
     948    psFree(outChip->toFPA);
     949    outChip->toFPA = outToFPA;
     950
     951    psFree(outChip->fromFPA);
     952    outChip->fromFPA = outFromFPA;
     953
     954    // Finally, change the type for the projection.
     955    outFPA->toSky->type = PS_PROJ_TAN;
     956
     957    return true;
     958}
     959
     960bool pmAstromLinearizeToSky(pmFPA *inFPA, pmChip *inChip, pmFPA *outFPA, pmChip *outChip, psRegion *bounds)
     961{
     962    PS_ASSERT_PTR_NON_NULL(inFPA, NULL);
     963    PS_ASSERT_PTR_NON_NULL(inChip, NULL);
     964    PS_ASSERT_PTR_NON_NULL(outFPA, NULL);
     965    PS_ASSERT_PTR_NON_NULL(outChip, NULL);
     966    PS_ASSERT_PTR_NON_NULL(bounds, NULL);
     967
     968    // outFPA projection must be defined as the goal
     969   
     970    // the output transformations are:
     971    // chip -> FPA : standard linear trans with needed rotation, etc
     972    // FPA  -> TPA : identidy
     973
     974    int nSamples = 10;  // 10 samples in each dimension
     975
     976    double deltaX = (bounds->x1 - bounds->x0);
     977    double deltaY = (bounds->y1 - bounds->y0);
     978
     979    psArray *src = psArrayAllocEmpty(nSamples * nSamples);
     980    psArray *dst = psArrayAllocEmpty(nSamples * nSamples);
     981
     982    psPlane srcFP, srcTP;
     983
     984    for (int j = 0; j < nSamples; j++) {
     985        double y = bounds->y0 + (j * deltaY / nSamples);
     986        for (int i =  0; i < nSamples; i++) {
     987
     988            psSphere srcSky;
     989            psPlane *srcChip = psPlaneAlloc();
     990            psPlane *dstTP = psPlaneAlloc();
     991
     992            srcChip->x = bounds->x0 + (i * deltaX / nSamples);
     993            srcChip->y = y;
     994
     995            psPlaneTransformApply (&srcFP, inChip->toFPA, srcChip);
     996            psPlaneTransformApply (&srcTP, inFPA->toTPA, &srcFP);
     997            psDeproject (&srcSky, &srcTP, inFPA->toSky);
     998           
     999            // fprintf (stderr, "%f %f | %f %f | %f %f | %f %f\n", srcChip->x, srcChip->y, srcFP.x, srcFP.y, srcTP.x, srcTP.y, srcSky.r*PS_DEG_RAD, srcSky.d*PS_DEG_RAD);
     1000
     1001            psProject (dstTP, &srcSky, outFPA->toSky);
     1002
     1003            srcChip->x -= bounds->x0;
     1004            srcChip->y -= bounds->y0;
     1005            psArrayAdd (src, 100, srcChip);
     1006            psArrayAdd (dst, 100, dstTP);
     1007
     1008            psFree(srcChip);  // drop our refs to s and d
     1009            psFree(dstTP);
     1010        }
     1011    }
     1012
     1013    psPlaneTransform *newToFPA = psPlaneTransformAlloc(1, 1);
     1014    newToFPA->x->coeffMask[1][1] = 1;
     1015    newToFPA->y->coeffMask[1][1] = 1;
     1016
     1017    if (!psPlaneTransformFit(newToFPA, src, dst, 0, 0)) {
     1018        psError(PS_ERR_UNKNOWN, false, "linear fit to transform failed");
     1019        psFree(src);
     1020        psFree(dst);
     1021        return NULL;
     1022    }
     1023   
     1024# if (0)
     1025    for (int i = 0; i < src->n; i++) {
     1026       
     1027        psSphere srcSky, dstSky;
     1028        psPlane *srcChip = src->data[i];
     1029        psPlane *dstTP   = dst->data[i];
     1030
     1031        psPlaneTransformApply (&srcFP, newToFPA, srcChip);
     1032        psDeproject (&srcSky, &srcFP, outFPA->toSky);
     1033        psDeproject (&dstSky, dstTP, outFPA->toSky);
     1034
     1035        double dX = (srcSky.r*PS_DEG_RAD - dstSky.r*PS_DEG_RAD)*3600.0;
     1036        double dY = (srcSky.d*PS_DEG_RAD - dstSky.d*PS_DEG_RAD)*3600.0;
     1037        fprintf (stderr, "%f %f | %f %f | %f %f | %f %f | %f %f | %f %f\n", dX, dY, srcChip->x, srcChip->y, srcFP.x, srcFP.y, dstTP->x, dstTP->y, srcSky.r*PS_DEG_RAD, srcSky.d*PS_DEG_RAD, dstSky.r*PS_DEG_RAD, dstSky.d*PS_DEG_RAD);
     1038
     1039    }
     1040# endif
     1041
     1042    psFree(src);
     1043    psFree(dst);
     1044
     1045    // this is a linear transformation
     1046    psPlaneTransform *newFromFPA = psPlaneTransformInvert(NULL, newToFPA, *bounds, 1);
     1047    if (!newFromFPA) {
     1048        psFree(newToFPA);
     1049        psError(PS_ERR_UNKNOWN, false, "inversion of fit of output chip toFPA failed");
     1050        return false;
     1051    }
     1052
     1053    // Success. Now set the fpa's toTPA and fromTPA to identity and replace the chip's transforms.
     1054    psFree(outChip->toFPA);
     1055    outChip->toFPA = newToFPA;
     1056
     1057    psFree(outChip->fromFPA);
     1058    outChip->fromFPA = newFromFPA;
     1059
     1060    psFree(outFPA->toTPA);
     1061    outFPA->toTPA =  psPlaneTransformIdentity(1);
     1062
     1063    psFree(outFPA->fromTPA);
     1064    outFPA->fromTPA = psPlaneTransformIdentity(1);
    9031065
    9041066    return true;
     
    9221084    wcs->trans = psPlaneTransformAlloc (nXorder, nYorder);
    9231085    wcs->toSky = NULL;
     1086    wcs->wcsCDkeys = 0;
    9241087
    9251088    memset (wcs->ctype1, 0, PM_ASTROM_WCS_TYPE_SIZE);
  • branches/simtest_nebulous_branches/psModules/src/astrom/pmAstrometryWCS.h

    r24766 r27840  
    2323    double crpix1, crpix2;
    2424    double cdelt1, cdelt2;
     25    bool wcsCDkeys;
    2526    psProjection *toSky;
    2627    psPlaneTransform *trans;
     
    6263bool pmAstromWriteBilevelMosaic (psMetadata *header, const pmFPA *fpa, double tol);
    6364
    64 bool pmAstromLinearizeTransforms(pmFPA *fpa, pmChip *);
     65bool pmAstromLinearizeTransforms(pmFPA *inFPA, pmChip *inChip, pmFPA *outFPA, pmChip *outChip, psRegion *outputBounds, double offset_x, double offset_y);
     66bool pmAstromLinearizeToSky(pmFPA *inFPA, pmChip *inChip, pmFPA *outFPA, pmChip *outChip, psRegion *bounds);
    6567
    6668// move to pslib
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPA.c

    r23740 r27840  
    105105    psFree(fpa->concepts);
    106106    psFree(fpa->analysis);
    107     psFree((void *)fpa->camera);
     107    psFree(fpa->camera);
    108108    psFree(fpa->hdu);
    109109
     
    378378    tmpFPA->toTPA = NULL;
    379379    tmpFPA->toSky = NULL;
     380    tmpFPA->wcsCDkeys = false;
    380381
    381382    tmpFPA->analysis = psMetadataAlloc();
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPA.h

    r21363 r27840  
    4848    psPlaneTransform *toTPA;  ///< Transformation from focal plane to tangent plane, or NULL
    4949    psProjection *toSky;         ///< Projection from tangent plane to sky, or NULL
     50    bool wcsCDkeys;
    5051    // Information
    5152    psMetadata *concepts;               ///< FPA-level concepts
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAMaskWeight.c

    r24767 r27840  
    111111        // psError(PS_ERR_IO, true, "CELL.SATURATION is not set --- unable to set mask.\n");
    112112        // return false;
    113         psWarning("CELL.SATURATION is not set --- completely masking cell.\n");
    114         saturation = NAN;
     113        psWarning("CELL.SATURATION is not set --- completely masking cell.\n");
     114        saturation = NAN;
    115115    }
    116116    float bad = psMetadataLookupF32(&mdok, cell->concepts, "CELL.BAD"); // Bad level
     
    118118        // psError(PS_ERR_IO, true, "CELL.BAD is not set --- unable to set mask.\n");
    119119        // return false;
    120         psWarning("CELL.BAD is not set --- completely masking cell.\n");
    121         bad = NAN;
     120        psWarning("CELL.BAD is not set --- completely masking cell.\n");
     121        bad = NAN;
    122122    }
    123123    psTrace("psModules.camera", 5, "Saturation: %f, bad: %f\n", saturation, bad);
    124124
    125     // if CELL.GAIN or CELL.READNOISE are not set, then the variance will be set to NAN; 
     125    // if CELL.GAIN or CELL.READNOISE are not set, then the variance will be set to NAN;
    126126    // in this case, we have to set the mask as well
    127127    float gain = psMetadataLookupF32(&mdok, cell->concepts, "CELL.GAIN"); // Cell gain
     
    140140    // completely mask if SATURATION or BAD are invalid
    141141    if (isnan(saturation) || isnan(bad) || isnan(gain) || isnan(readnoise)) {
    142         psImageInit(mask, badMask);
    143         return true;
     142        psImageInit(mask, badMask);
     143        return true;
    144144    }
    145145
     
    230230        // return false;
    231231        psWarning("CELL.GAIN is not set --- setting variance to NAN\n");
    232         gain = NAN;
     232        gain = NAN;
    233233    }
    234234    float readnoise = psMetadataLookupF32(&mdok, cell->concepts, "CELL.READNOISE"); // Cell read noise
     
    237237        // return false;
    238238        psWarning("CELL.READNOISE is not set --- setting variance to NAN\n");
    239         readnoise = NAN;
     239        readnoise = NAN;
    240240    }
    241241    // if we have a non-NAN readnoise, then we need to ensure it has been updated (not necessary if NAN)
     
    248248    if (isnan(gain) || isnan(readnoise)) {
    249249        if (!readout->variance) {
    250             // generate the image if needed
     250            // generate the image if needed
    251251            readout->variance = psImageAlloc(readout->image->numCols, readout->image->numRows, PS_TYPE_F32);
    252252        }
    253         // XXX need to set the mask, if defined
     253        // XXX need to set the mask, if defined
    254254        psImageInit(readout->variance, NAN);
    255         return true;
     255        return true;
    256256    }
    257257
     
    262262
    263263        // a negative variance is non-sensical. if the image value drops below 1, the variance must be 1.
    264         // XXX this calculation is wrong: limit is 1 e-, but this is in DN
     264        // XXX this calculation is wrong: limit is 1 e-, but this is in DN
    265265        readout->variance = (psImage*)psUnaryOp(readout->variance, readout->variance, "abs");
    266266        readout->variance = (psImage*)psBinaryOp(readout->variance, readout->variance, "max",
     
    276276    // apply a supplied readnoise map (NOTE: in DN, not electrons):
    277277    if (noiseMap) {
    278         psImage *rdVar = (psImage*)psBinaryOp(NULL, (const psPtr) noiseMap, "*", (const psPtr) noiseMap);
    279         readout->variance = (psImage*)psBinaryOp(readout->variance, readout->variance, "+", rdVar);
    280         psFree (rdVar);
     278        psImage *rdVar = (psImage*)psBinaryOp(NULL, (const psPtr) noiseMap, "*", (const psPtr) noiseMap);
     279        readout->variance = (psImage*)psBinaryOp(readout->variance, readout->variance, "+", rdVar);
     280        psFree (rdVar);
    281281    } else {
    282         readout->variance = (psImage*)psBinaryOp(readout->variance, readout->variance, "+", psScalarAlloc(readnoise*readnoise/gain/gain, PS_TYPE_F32));
     282        readout->variance = (psImage*)psBinaryOp(readout->variance, readout->variance, "+", psScalarAlloc(readnoise*readnoise/gain/gain, PS_TYPE_F32));
    283283    }
    284284
     
    362362
    363363
    364 bool pmReadoutVarianceRenormPixels(const pmReadout *readout, psImageMaskType maskVal,
    365                                  psStatsOptions meanStat, psStatsOptions stdevStat, psRandom *rng)
     364bool pmReadoutVarianceRenormalise(const pmReadout *readout, psImageMaskType maskVal,
     365                                  int sample, float minValid, float maxValid)
    366366{
    367367    PM_ASSERT_READOUT_NON_NULL(readout, false);
     
    370370
    371371    psImage *image = readout->image, *mask = readout->mask, *variance = readout->variance; // Readout parts
    372 
    373     if (!psMemIncrRefCounter(rng)) {
    374         rng = psRandomAlloc(PS_RANDOM_TAUS);
    375     }
    376 
    377     psStats *varianceStats = psStatsAlloc(meanStat);// Statistics for mean
    378     if (!psImageBackground(varianceStats, NULL, variance, mask, maskVal, rng)) {
    379         psError(PS_ERR_UNKNOWN, false, "Unable to measure mean variance for image");
    380         psFree(varianceStats);
    381         psFree(rng);
    382         return false;
    383     }
    384     float meanVariance = varianceStats->robustMedian; // Mean variance
    385     psFree(varianceStats);
    386 
    387     psStats *imageStats = psStatsAlloc(stdevStat);// Statistics for mean
    388     if (!psImageBackground(imageStats, NULL, image, mask, maskVal, rng)) {
    389         psError(PS_ERR_UNKNOWN, false, "Unable to measure stdev of image");
    390         psFree(imageStats);
    391         psFree(rng);
    392         return false;
    393     }
    394     float stdevImage = imageStats->robustStdev; // Standard deviation of image
    395     psFree(imageStats);
    396     psFree(rng);
    397 
    398     float correction = PS_SQR(stdevImage) / meanVariance; // Correction to take variance to what it should be
    399     psLogMsg("psModules.camera", PS_LOG_INFO, "Renormalising variance map by %f", correction);
    400     psBinaryOp(variance, variance, "*", psScalarAlloc(correction, PS_TYPE_F32));
    401 
    402     return true;
    403 }
    404 
    405 
    406 bool pmReadoutVarianceRenormPhot(const pmReadout *readout, psImageMaskType maskVal, int num, float width,
    407                                psStatsOptions meanStat, psStatsOptions stdevStat, psRandom *rng)
    408 {
    409     PM_ASSERT_READOUT_NON_NULL(readout, false);
    410     PM_ASSERT_READOUT_IMAGE(readout, false);
    411     PM_ASSERT_READOUT_VARIANCE(readout, false);
    412 
    413     if (!psMemIncrRefCounter(rng)) {
    414         rng = psRandomAlloc(PS_RANDOM_TAUS);
    415     }
    416 
    417     psImage *image = readout->image, *mask = readout->mask, *variance = readout->variance; // Readout images
    418 
    419     // Measure background
    420     psStats *bgStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);// Statistics for background
    421     if (!psImageBackground(bgStats, NULL, image, mask, maskVal, rng)) {
    422         psError(PS_ERR_UNKNOWN, false, "Unable to measure background for image");
    423         psFree(bgStats);
    424         psFree(rng);
    425         return false;
    426     }
    427     float bgMean = bgStats->robustMedian; // Background level
    428     float bgNoise = bgStats->robustStdev; // Background standard deviation
    429     psFree(bgStats);
    430     psTrace("psModules.camera", 5, "Background is %f +/- %f\n", bgMean, bgNoise);
    431 
    432 
    433     // Construct kernels for flux measurement
    434     // We use N(0,width) and N(0,width/sqrt(2)) kernels, following psphotSignificanceImage.
    435     float sigFactor = 4.0 * M_PI * PS_SQR(width); // Factor for conversion from im/wt ratio to significance
    436     int size = RENORM_NUM_SIGMA, fullSize = 2 * size + 1; // Half-size and full size of Gaussian
    437     psVector *gauss = psVectorAlloc(fullSize, PS_TYPE_F32); // Gaussian for weighting
    438     psVector *gauss2 = psVectorAlloc(fullSize, PS_TYPE_F32); // Gaussian squared
    439     for (int i = 0, x = -size; i < fullSize; i++, x++) {
    440         gauss->data.F32[i] = expf(-0.5 * PS_SQR(x) / PS_SQR(width));
    441         gauss2->data.F32[i] = expf(-PS_SQR(x) / PS_SQR(width));
    442     }
    443 
    444     // Size of image
    445     int numCols = image->numCols, numRows = image->numRows; // Size of images
    446     int xSize = numCols - fullSize, ySize = numRows - fullSize; // Size of consideration
    447     int xOffset = size, yOffset = size;       // Offset to region of consideration
    448 
    449     // Measure fluxes
    450     float peakFlux = RENORM_PEAK * bgNoise;     // Peak flux for fake sources
    451     psVector *noise = psVectorAlloc(num, PS_TYPE_F32); // Measurements of the noise
    452     psVector *source = psVectorAlloc(num, PS_TYPE_F32); // Measurements of fake sources
    453     psVector *guess = psVectorAlloc(num, PS_TYPE_F32); // Guess at significance
    454     psVector *photMask = psVectorAlloc(num, PS_TYPE_VECTOR_MASK); // Mask for fluxes
    455     for (int i = 0; i < num; i++) {
    456         // Coordinates of interest
    457         int xPix = psRandomUniform(rng) * xSize + xOffset + 0.5;
    458         int yPix = psRandomUniform(rng) * ySize + yOffset + 0.5;
    459         psAssert(xPix - size >= 0 && xPix + size < numCols &&
    460                  yPix - size >= 0 && yPix + size < numRows,
    461                  "Bad pixel position: %d,%d", xPix, yPix);
    462 
    463         // Weighted aperture photometry
    464         // This has the same effect as smoothing the image by the window function
    465         float sumNoise = 0.0;       // Sum for noise measurement
    466         float sumSource = 0.0;      // Sum for source measurement
    467         float sumVariance = 0.0;      // Sum for variance measurement
    468         float sumGauss = 0.0, sumGauss2 = 0.0; // Sums of Gaussian kernels
    469         for (int v = 0, y = yPix - size; v < fullSize; v++, y++) {
    470             float xSumNoise = 0.0;  // Sum for noise measurement in x
    471             float xSumSource = 0.0; // Sum for source measurement in x
    472             float xSumVariance = 0.0; // Sum for variance measurement in x
    473             float xSumGauss = 0.0, xSumGauss2 = 0.0; // Sums of Gaussian kernels in x
    474             float yGauss = gauss->data.F32[v]; // Value of Gaussian in y
    475             for (int u = 0, x = xPix - size; u < fullSize; u++, x++) {
    476                 if (mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) {
     372    int numCols = image->numCols, numRows = image->numRows; // Size of image
     373
     374    int xMin, xMax, yMin, yMax;         // Bounds of image
     375    if (mask) {
     376        xMin = numCols;
     377        xMax = 0;
     378        yMin = numRows;
     379        yMax = 0;
     380        for (int y = 0; y < numRows; y++) {
     381            for (int x = 0; x < numCols; x++) {
     382                if (mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) {
    477383                    continue;
    478384                }
    479                 float value = image->data.F32[y][x] - bgMean; // Value of image
    480                 float xGauss = gauss->data.F32[u]; // Value of Gaussian in x
    481                 float xGauss2 = gauss2->data.F32[u]; // Value of Gaussian^2 in x
    482                 xSumNoise += value * xGauss;
    483                 xSumSource += (value + peakFlux * xGauss * yGauss) * xGauss;
    484                 xSumVariance += variance->data.F32[y][x] * xGauss2;
    485                 xSumGauss += xGauss;
    486                 xSumGauss2 += xGauss2;
    487             }
    488             float yGauss2 = gauss2->data.F32[v]; // Value of Gaussian^2 in y
    489             sumNoise += xSumNoise * yGauss;
    490             sumSource += xSumSource * yGauss;
    491             sumVariance += xSumVariance * yGauss2;
    492             sumGauss += xSumGauss * yGauss;
    493             sumGauss2 += xSumGauss2 * yGauss2;
    494         }
    495 
    496         photMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = ((isfinite(sumNoise) && isfinite(sumSource) &&
    497                                                 isfinite(sumVariance) && sumGauss > 0 && sumGauss2 > 0) ?
    498                                                0 : 0xFF);
    499 
    500         float smoothImageNoise = sumNoise / sumGauss; // Value of smoothed image pixel for noise
    501         float smoothImageSource = sumSource / sumGauss; // Value of smoothed image pixel for source
    502         float smoothVariance = sumVariance / sumGauss2; // Value of smoothed variance pixel
    503 
    504         noise->data.F32[i] = smoothImageNoise;
    505         source->data.F32[i] = smoothImageSource;
    506         guess->data.F32[i] = (sumSource > 0) ? sigFactor * PS_SQR(smoothImageSource) / smoothVariance : 0.0;
    507         psTrace("psModules.camera", 10, "Flux %d (%d,%d): %f, %f, %f\n",
    508                 i, xPix, yPix, smoothImageNoise, smoothImageSource, smoothVariance);
    509     }
    510     psFree(gauss);
    511     psFree(gauss2);
    512     psFree(rng);
    513 
    514     // Standard deviation of fluxes gives us the real significance
    515     psStats *stdevStats = psStatsAlloc(stdevStat); // Statistics
    516     if (!psVectorStats(stdevStats, noise, NULL, photMask, 0xFF)) {
    517         psError(PS_ERR_UNKNOWN, false, "Unable to measure standard deviation of fluxes");
    518         psFree(stdevStats);
    519         psFree(noise);
    520         psFree(source);
    521         psFree(guess);
    522         psFree(photMask);
     385                xMin = PS_MIN(xMin, x);
     386                xMax = PS_MAX(xMax, x);
     387                yMin = PS_MIN(yMin, y);
     388                yMax = PS_MAX(yMax, y);
     389            }
     390        }
     391    } else {
     392        xMin = 0;
     393        xMax = numCols;
     394        yMin = 0;
     395        yMax = numRows;
     396    }
     397
     398    int xNum = xMax - xMin, yNum = yMax - yMin; // Number of pixels
     399
     400    int numPix = xNum * yNum;                                  // Number of pixels
     401    int num = PS_MIN(sample, numPix);                          // Number we care about
     402    psVector *signoise = psVectorAllocEmpty(num, PS_TYPE_F32);   // Signal-to-noise values
     403
     404    if (num >= numPix) {
     405        // We have an image smaller than Nsubset, so just loop over the image pixels
     406        int index = 0;                  // Index for vector
     407        for (int y = yMin; y < yMax; y++) {
     408            for (int x = xMin; x < xMax; x++) {
     409                if ((mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) ||
     410                    !isfinite(image->data.F32[y][x]) || !isfinite(variance->data.F32[y][x])) {
     411                    continue;
     412                }
     413
     414                signoise->data.F32[index] = image->data.F32[y][x] / sqrtf(variance->data.F32[y][x]);
     415                index++;
     416            }
     417        }
     418        signoise->n = index;
     419    } else {
     420        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
     421        int index = 0;                  // Index for vector
     422        for (long i = 0; i < num; i++) {
     423            // Pixel coordinates
     424            int pixel = numPix * psRandomUniform(rng);
     425            int x = xMin + pixel % xNum;
     426            int y = yMin + pixel / xNum;
     427
     428            if ((mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) ||
     429                !isfinite(image->data.F32[y][x]) || !isfinite(variance->data.F32[y][x])) {
     430                continue;
     431            }
     432
     433            signoise->data.F32[index] = image->data.F32[y][x] / sqrtf(variance->data.F32[y][x]);
     434            index++;
     435        }
     436        signoise->n = index;
     437        psFree(rng);
     438    }
     439
     440    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_STDEV); // Statistics
     441
     442    if (!psVectorStats(stats, signoise, NULL, NULL, 0)) {
     443        psError(PS_ERR_UNKNOWN, false, "Unable to measure statistics on S/N image");
     444        psFree(signoise);
    523445        return false;
    524446    }
    525     float stdev = psStatsGetValue(stdevStats, stdevStat); // Standard deviation of fluxes
    526     psFree(stdevStats);
    527     psFree(noise);
    528     psTrace("psModules.camera", 5, "Standard deviation of fluxes is %f\n", stdev);
    529 
    530     // Ratio of measured significance to guessed significance
    531     psVector *ratio = psVectorAlloc(num, PS_TYPE_F32); // Ratio of measured to guess
    532     for (int i = 0; i < num; i++) {
    533         float measuredSig = PS_SQR(source->data.F32[i] / stdev); // Measured significance
    534         ratio->data.F32[i] = measuredSig / guess->data.F32[i];
    535         if (guess->data.F32[i] <= 0.0 || source->data.F32[i] <= 0.0 || !isfinite(ratio->data.F32[i])) {
    536             photMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0xFF;
    537         }
    538         psTrace("psModules.camera", 9, "Ratio %d: %f, %f, %f\n",
    539                 i, guess->data.F32[i], measuredSig, ratio->data.F32[i]);
    540     }
    541     psFree(source);
    542     psFree(guess);
    543 
    544     psStats *meanStats = psStatsAlloc(meanStat | stdevStat); // Statistics
    545     if (!psVectorStats(meanStats, ratio, NULL, photMask, 0xFF)) {
    546         psError(PS_ERR_UNKNOWN, false, "Unable to measure mean ratio");
    547         psFree(meanStats);
    548         psFree(ratio);
    549         psFree(photMask);
     447    psFree(signoise);
     448
     449    float covar = sqrtf(psImageCovarianceFactor(readout->covariance)); // Covariance factor
     450    float correction = stats->robustStdev / covar; // Correction factor
     451    psFree(stats);
     452    psLogMsg("psModules.camera", PS_LOG_DETAIL, "Variance renormalisation factor is %f", correction);
     453
     454    // Check valid range of correction factor
     455    if ((isfinite(minValid) && correction < minValid) || (isfinite(maxValid) && correction > maxValid)) {
     456        psError(PS_ERR_UNKNOWN, true, "Variance renormalisation is outside valid range: %f vs %f:%f --- no correction made", correction, minValid, maxValid);
     457        psMetadataAddF32(readout->analysis, PS_LIST_TAIL, PM_READOUT_ANALYSIS_RENORM, 0, "Renormalisation of variance", PS_SQR(correction));
    550458        return false;
    551459    }
    552     float meanRatio = psStatsGetValue(meanStats, meanStat); // Mean ratio
    553     psTrace("psModules.camera", 5, "Mean significance ratio is %f +/- %f\n",
    554             meanRatio, psStatsGetValue(meanStats, stdevStat));
    555     psFree(meanStats);
    556     psFree(ratio);
    557     psFree(photMask);
    558 
    559     psLogMsg("psModules.camera", PS_LOG_INFO, "Renormalising variance map by %f", meanRatio);
    560     psBinaryOp(variance, variance, "*", psScalarAlloc(meanRatio, PS_TYPE_F32));
    561 
    562     return true;
    563 }
    564 
    565 
    566 bool pmReadoutVarianceRenorm(const pmReadout *readout, psImageMaskType maskVal, psStatsOptions meanStat,
    567                            psStatsOptions stdevStat, int width, psRandom *rng)
    568 {
    569     PM_ASSERT_READOUT_NON_NULL(readout, false);
    570     PM_ASSERT_READOUT_IMAGE(readout, false);
    571     PM_ASSERT_READOUT_VARIANCE(readout, false);
    572     PS_ASSERT_INT_POSITIVE(width, false);
    573 
    574     if (!psMemIncrRefCounter(rng)) {
    575         rng = psRandomAlloc(PS_RANDOM_TAUS);
    576     }
    577 
    578     psImage *image = readout->image, *mask = readout->mask, *variance = readout->variance; // Readout images
    579     int numCols = image->numCols, numRows = image->numRows; // Size of images
    580     int xNum = numCols / width + 1, yNum = numRows / width + 1; // Number of renormalisation regions
    581     float xSize = numCols / (float)xNum, ySize = numRows / (float)yNum; // Size of renormalisation regions
    582 
    583     psStats *meanStats = psStatsAlloc(meanStat), *stdevStats = psStatsAlloc(stdevStat); // Statistics
    584     psVector *buffer = NULL;
    585 
    586     for (int j = 0; j < yNum; j++) {
    587         // Bounds in y
    588         int yMin = j * ySize;
    589         int yMax = (j + 1) * ySize;
    590         for (int i = 0; i < xNum; i++) {
    591             // Bounds in x
    592             int xMin = i * xSize;
    593             int xMax = (i + 1) * xSize;
    594 
    595             psRegion region = psRegionSet(xMin, xMax, yMin, yMax); // Region of interest
    596             psImage *subImage = psImageSubset(image, region); // Sub-image of the image pixels
    597             psImage *subVariance = psImageSubset(variance, region); // Sub image of the variance pixels
    598             psImage *subMask = mask ? psImageSubset(mask, region) : NULL; // Sub-image of the mask pixels
    599 
    600             if (!psImageBackground(stdevStats, &buffer, subImage, subMask, maskVal, rng) ||
    601                 !psImageBackground(meanStats, &buffer, subVariance, subMask, maskVal, rng)) {
    602                 // Nothing we can do about it, but don't want to keel over and die, so do our best to flag it.
    603                 psString regionStr = psRegionToString(region); // String with region
    604                 psWarning("Unable to measure statistics over %s", regionStr);
    605                 psFree(regionStr);
    606                 psErrorClear();
    607                 psImageInit(subVariance, NAN);
    608                 if (subMask) {
    609                     psImageInit(subMask, maskVal);
    610                 }
    611             } else {
    612                 double meanVar = psStatsGetValue(meanStats, meanStat); // Mean of variance map
    613                 double stdev = psStatsGetValue(stdevStats, stdevStat); // Standard deviation of image
    614                 psTrace("psModules.camera", 3,
    615                         "Region [%d:%d,%d:%d] has variance %lf, but mean of variance map is %lf\n",
    616                         xMin, xMax, yMin, yMax, PS_SQR(stdev), meanVar);
    617                 psBinaryOp(subVariance, subVariance, "*", psScalarAlloc(PS_SQR(stdev) / meanVar, PS_TYPE_F32));
    618             }
    619 
    620             psFree(subImage);
    621             psFree(subVariance);
    622             psFree(subMask);
    623         }
    624     }
    625     psFree(meanStats);
    626     psFree(stdevStats);
    627     psFree(rng);
    628     psFree(buffer);
    629 
    630     return true;
    631 }
     460
     461    psImage *subImage = psImageSubset(variance, psRegionSet(xMin, xMax, yMin, yMax)); // Smaller image
     462    psBinaryOp(subImage, subImage, "*", psScalarAlloc(PS_SQR(correction), PS_TYPE_F32));
     463    psFree(subImage);
     464
     465    pmHDU *hdu = pmHDUFromReadout(readout); // HDU for readout
     466    if (hdu)  {
     467        psString history = NULL;
     468        psStringAppend(&history, "Rescaled variance by %6.4f (stdev by %6.4f)",
     469                       PS_SQR(correction), correction);
     470        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
     471        psFree(history);
     472    }
     473
     474    return psMetadataAddF32(readout->analysis, PS_LIST_TAIL, PM_READOUT_ANALYSIS_RENORM, 0,
     475                            "Renormalisation of variance", PS_SQR(correction));
     476}
     477
    632478
    633479
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAMaskWeight.h

    r24483 r27840  
    1616/// @{
    1717
    18 #if 0
    19 /// Pixel mask values
    20 typedef enum {
    21     PM_MASK_CLEAR    = 0x00,            ///< The pixel is not masked
    22     PM_MASK_BLANK    = 0x01,            ///< The pixel is blank or has no (valid) data
    23     PM_MASK_FLAT     = 0x02,            ///< The pixel is non-positive in the flat-field
    24     PM_MASK_DETECTOR = 0x02,            ///< The detector pixel is bad (e.g., bad column, charge trap)
    25     PM_MASK_SAT      = 0x04,            ///< The pixel is saturated in the image of interest
    26     PM_MASK_BAD      = 0x04,            ///< The pixel is low in the image of interest
    27     PM_MASK_RANGE    = 0x04,            ///< The pixel is out of range in the image of interest
    28     PM_MASK_CR       = 0x08,            ///< The pixel is probably a CR
    29     PM_MASK_SPARE1   = 0x10,            ///< Spare mask value
    30     PM_MASK_SPARE2   = 0x20,            ///< Spare mask value
    31     PM_MASK_SUSPECT  = 0x40,            ///< The pixel is suspected of being bad, but may not be
    32     PM_MASK_MARK     = 0x80,            ///< The pixel is marked as temporarily ignored
    33 } pmMaskValue;
    34 #define PM_MASK_MARK 0x80            ///< The pixel is marked as temporarily ignored
    35 #define PM_MASK_SAT  0x04            ///< The pixel is saturated in the image of interest
    36 #endif
     18#define PM_READOUT_ANALYSIS_RENORM "READOUT.RENORM" // Name on analysis metadata for renormalisation
    3719
    3820/// Set a temporary readout mask using CELL.SATURATION and CELL.BAD
     
    5436/// can't be generated.
    5537bool pmReadoutSetVariance(pmReadout *readout, ///< Readout for which to set variance
    56                           const psImage *noiseMap, ///< 2D image of the read noise in DN
     38                          const psImage *noiseMap, ///< 2D image of the read noise in DN
    5739                          bool poisson    ///< Include poisson variance (in addition to read noise)?
    5840    );
     
    7355/// with HDU entry).  This is intended for most operations.
    7456bool pmReadoutGenerateVariance(pmReadout *readout, ///< Readout for which to generate variance
    75                           const psImage *noiseMap, ///< 2D image of the read noise in DN
     57                          const psImage *noiseMap, ///< 2D image of the read noise in DN
    7658                               bool poisson    ///< Include poisson variance (in addition to read noise)?
    7759    );
     
    8365                                   psImageMaskType sat, ///< Mask value to give saturated pixels
    8466                                   psImageMaskType bad, ///< Mask value to give bad (low) pixels
    85                                    const psImage *noiseMap, ///< 2D image of the read noise in DN
     67                                   const psImage *noiseMap, ///< 2D image of the read noise in DN
    8668                                   bool poisson ///< Include poisson variance (in addition to read noise)?
    8769    );
     
    9375                                psImageMaskType sat, ///< Mask value to give saturated pixels
    9476                                psImageMaskType bad, ///< Mask value to give bad (low) pixels
    95                                 const psImage *noiseMap, ///< 2D image of the read noise in DN
     77                                const psImage *noiseMap, ///< 2D image of the read noise in DN
    9678                                bool poisson ///< Include poisson variance (in addition to read noise)?
    9779    );
     
    10082///
    10183/// The variance map is adjusted so that the mean matches the actual pixel variance in the image
    102 bool pmReadoutVarianceRenormPixels(
    103     const pmReadout *readout,           ///< Readout to normalise
    104     psImageMaskType maskVal,                 ///< Value to mask
    105     psStatsOptions meanStat,            ///< Statistic to measure the mean (of the variance map)
    106     psStatsOptions stdevStat,           ///< Statistic to measure the stdev (of the image)
    107     psRandom *rng                       ///< Random number generator
    108     );
    109 
    110 /// Renormalise the variance map to match the actual photometry variance
    111 ///
    112 /// The variance map is adjusted so that the actual significance of fake sources matches the
    113 /// guestimated significance
    114 bool pmReadoutVarianceRenormPhot(
     84bool pmReadoutVarianceRenormalise(
    11585    const pmReadout *readout,           ///< Readout to normalise
    11686    psImageMaskType maskVal,            ///< Value to mask
    117     int num,                            ///< Number of instances to measure over the image
    118     float width,                        ///< Photometry width
    119     psStatsOptions meanStat,            ///< Statistic to measure the mean
    120     psStatsOptions stdevStat,           ///< Statistic to measure the stdev
    121     psRandom *rng                       ///< Random number generator
    122     );
    123 
    124 /// Renormalise the variance map to match the actual variance
    125 ///
    126 /// The variance in the image is measured in patches, and the variance map is adjusted so that the mean for
    127 /// that patch corresponds.
    128 bool pmReadoutVarianceRenorm(const pmReadout *readout, // Readout to normalise
    129                              psImageMaskType maskVal, // Value to mask
    130                              psStatsOptions meanStat, // Statistic to measure the mean (of the variance map)
    131                              psStatsOptions stdevStat, // Statistic to measure the stdev (of the image)
    132                              int width,   // Width of patch (pixels)
    133                              psRandom *rng // Random number generator (for sub-sampling images)
     87    int sample,                         ///< Sample size
     88    float minValid,                     ///< Minimum valid renormalisation, or NAN
     89    float maxValid                      ///< Maximum valid renormalisation, or NAN
    13490    );
    13591
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAMosaic.c

    r21363 r27840  
    626626    bool good = true;                   // Is everything good?
    627627
     628    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
     629    const char *chipName = psMetadataLookupStr(NULL, cell->parent->concepts, "CHIP.NAME"); // Name of chip
     630
    628631    // Offset of the cell on the chip
    629632    int x0Cell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0");
    630633    if (!mdok) {
    631         psError(PS_ERR_UNKNOWN, true, "CELL.X0 for cell is not set.\n");
     634        psError(PS_ERR_UNKNOWN, true, "CELL.X0 for cell %s,%s is not set.\n", chipName, cellName);
    632635        good = false;
    633636    }
    634637    int y0Cell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0");
    635638    if (!mdok) {
    636         psError(PS_ERR_UNKNOWN, true, "CELL.Y0 for cell is not set.\n");
     639        psError(PS_ERR_UNKNOWN, true, "CELL.Y0 for cell %s,%s is not set.\n", chipName, cellName);
    637640        good = false;
    638641    }
    639     psTrace("psModules.camera", 5, "Cell %ld: x0=%d y0=%d\n", index, x0Cell, y0Cell);
     642    psTrace("psModules.camera", 5, "Cell %s,%s (%ld): x0=%d y0=%d\n",
     643            chipName, cellName, index, x0Cell, y0Cell);
    640644
    641645    // Offset of the chip on the FPA
     
    649653        x0Chip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.X0");
    650654        if (!mdok) {
    651             psError(PS_ERR_UNKNOWN, true, "CHIP.X0 for chip is not set.\n");
     655            psError(PS_ERR_UNKNOWN, true, "CHIP.X0 for chip %s is not set.\n", chipName);
    652656            good = false;
    653657        }
    654658        y0Chip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.Y0");
    655659        if (!mdok) {
    656             psError(PS_ERR_UNKNOWN, true, "CHIP.Y0 for chip is not set.\n");
     660            psError(PS_ERR_UNKNOWN, true, "CHIP.Y0 for chip %s is not set.\n", chipName);
    657661            good = false;
    658662        }
     
    662666    xBin->data.S32[index] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XBIN");
    663667    if (!mdok || xBin->data.S32[index] == 0) {
    664         psError(PS_ERR_UNKNOWN, true, "CELL.XBIN for cell is not set.\n");
     668        psError(PS_ERR_UNKNOWN, true, "CELL.XBIN for cell %s,%s is not set.\n", chipName, cellName);
    665669        return false;
    666670    } else if (xBin->data.S32[index] < *xBinMin) {
     
    669673    yBin->data.S32[index] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YBIN");
    670674    if (!mdok || yBin->data.S32[index] == 0) {
    671         psError(PS_ERR_UNKNOWN, true, "CELL.YBIN for cell is not set.\n");
     675        psError(PS_ERR_UNKNOWN, true, "CELL.YBIN for cell %s,%s is not set.\n", chipName, cellName);
    672676        return false;
    673677    } else if (yBin->data.S32[index] < *yBinMin) {
     
    678682    int xParityCell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XPARITY");
    679683    if (!mdok || (xParityCell != 1 && xParityCell != -1)) {
    680         psError(PS_ERR_UNKNOWN, true, "CELL.XPARITY for cell is not set.\n");
     684        psError(PS_ERR_UNKNOWN, true, "CELL.XPARITY for cell %s,%s is not set.\n", chipName, cellName);
    681685        return false;
    682686    }
    683687    int yParityCell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YPARITY");
    684688    if (!mdok || (yParityCell != 1 && yParityCell != -1)) {
    685         psError(PS_ERR_UNKNOWN, true, "CELL.YPARITY for cell is not set.\n");
     689        psError(PS_ERR_UNKNOWN, true, "CELL.YPARITY for cell %s,%s is not set.\n", chipName, cellName);
    686690        return false;
    687691    }
     
    693697        xParityChip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.XPARITY");
    694698        if (!mdok || (xParityChip != 1 && xParityChip != -1)) {
    695             psError(PS_ERR_UNKNOWN, true, "CHIP.XPARITY for chip is not set.\n");
     699            psError(PS_ERR_UNKNOWN, true, "CHIP.XPARITY for chip %s is not set.\n", chipName);
    696700            return false;
    697701        }
    698702        yParityChip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.YPARITY");
    699703        if (!mdok || (yParityChip != 1 && yParityChip != -1)) {
    700             psError(PS_ERR_UNKNOWN, true, "CHIP.YPARITY for chip is not set.\n");
     704            psError(PS_ERR_UNKNOWN, true, "CHIP.YPARITY for chip %s is not set.\n", chipName);
    701705            return false;
    702706        }
     
    740744static bool chipMosaic(psImage **mosaicImage, // The mosaic image, to be returned
    741745                       psImage **mosaicMask, // The mosaic mask, to be returned
    742                        psImage **mosaicVariances, // The mosaic variances, to be returned
     746                       psImage **mosaicVariance, // The mosaic variance, to be returned
    743747                       int *xBinChip, int *yBinChip, // The binning in x and y, to be returned
    744748                       const pmChip *chip, // Chip to mosaic
     
    749753    assert(mosaicImage);
    750754    assert(mosaicMask);
    751     assert(mosaicVariances);
     755    assert(mosaicVariance);
    752756    assert(xBinChip);
    753757    assert(yBinChip);
     
    826830    if (allGood) {
    827831        *mosaicImage = imageMosaic(images, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0, BLANK_VALUE);
    828         *mosaicVariances = imageMosaic(variances, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0, BLANK_VALUE);
     832        *mosaicVariance = imageMosaic(variances, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0, BLANK_VALUE);
    829833        *mosaicMask = imageMosaic(masks, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0, blank);
    830834    }
     
    847851static bool fpaMosaic(psImage **mosaicImage, // The mosaic image, to be returned
    848852                      psImage **mosaicMask, // The mosaic mask, to be returned
    849                       psImage **mosaicVariances, // The mosaic variances, to be returned
     853                      psImage **mosaicVariance, // The mosaic variance, to be returned
    850854                      int *xBinFPA, int *yBinFPA, // The binning in x and y, to be returned
    851855                      const pmFPA *fpa,  // FPA to mosaic
     
    857861    assert(mosaicImage);
    858862    assert(mosaicMask);
    859     assert(mosaicVariances);
     863    assert(mosaicVariance);
    860864    assert(xBinFPA);
    861865    assert(yBinFPA);
     
    960964    if (allGood) {
    961965        *mosaicImage = imageMosaic(images, xFlip, yFlip, xBin, yBin, *xBinFPA, *yBinFPA, x0, y0, BLANK_VALUE);
    962         *mosaicVariances = imageMosaic(variances, xFlip, yFlip, xBin, yBin, *xBinFPA, *yBinFPA, x0, y0, BLANK_VALUE);
     966        *mosaicVariance = imageMosaic(variances, xFlip, yFlip, xBin, yBin, *xBinFPA, *yBinFPA, x0, y0, BLANK_VALUE);
    963967        *mosaicMask = imageMosaic(masks, xFlip, yFlip, xBin, yBin, *xBinFPA, *yBinFPA, x0, y0, blank);
    964968    }
     
    10251029    psImage *mosaicImage   = NULL;      // The mosaic image
    10261030    psImage *mosaicMask    = NULL;      // The mosaic mask
    1027     psImage *mosaicVariances = NULL;      // The mosaic variances
     1031    psImage *mosaicVariance = NULL;      // The mosaic variances
    10281032
    10291033    // Find the HDU
     
    10521056        }
    10531057        if (hdu->variances) {
    1054             mosaicVariances = psImageSubset(hdu->variances->data[0], bounds);
    1055             if (!mosaicVariances) {
     1058            mosaicVariance = psImageSubset(hdu->variances->data[0], bounds);
     1059            if (!mosaicVariance) {
    10561060                psError(PS_ERR_UNKNOWN, false, "Unable to select variance pixels.\n");
    10571061                return false;
     
    10611065        // Case 2 --- we need to mosaic by cut and paste
    10621066        psTrace("psModules.camera", 1, "Case 2 mosaicking: cut and paste.\n");
    1063         if (!chipMosaic(&mosaicImage, &mosaicMask, &mosaicVariances, &xBin, &yBin, source, targetCell, blank)) {
     1067        if (!chipMosaic(&mosaicImage, &mosaicMask, &mosaicVariance, &xBin, &yBin, source, targetCell, blank)) {
    10641068            psError(PS_ERR_UNKNOWN, false, "Unable to mosaic cells.\n");
    10651069            return false;
     
    10691073    }
    10701074    psTrace("psModules.camera", 1, "xBin,yBin: %d,%d\n", xBin, yBin);
     1075
    10711076
    10721077    // Set the concepts for the target cell
     
    10901095    target->parent->concepts = psMetadataCopy(target->parent->concepts, source->parent->concepts); // FPA lvl
    10911096
     1097    // Average the covariances
     1098    psList *covariances = psListAlloc(NULL); // Input covariance matrices
     1099    for (int i = 0; i < source->cells->n; i++) {
     1100        pmCell *cell = source->cells->data[i]; // Cell of interest
     1101        if (!cell || !cell->data_exists) {
     1102            continue;
     1103        }
     1104        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
     1105        if (!ro || !ro->covariance) {
     1106            continue;
     1107        }
     1108        psListAdd(covariances, PS_LIST_TAIL, ro->covariance);
     1109    }
     1110    psKernel *mosaicCovariance = NULL;  // Covariance for mosaic
     1111    if (psListLength(covariances) > 0) {
     1112        psArray *covarArray = psListToArray(covariances); // Array with covariances
     1113        mosaicCovariance = psImageCovarianceAverage(covarArray);
     1114        psFree(covarArray);
     1115    }
     1116    psFree(covariances);
     1117
    10921118    // Now make a new readout to go in the target cell
    10931119    pmReadout *newReadout = pmReadoutAlloc(targetCell); // New readout
    10941120    newReadout->image  = mosaicImage;
    10951121    newReadout->mask   = mosaicMask;
    1096     newReadout->variance = mosaicVariances;
     1122    newReadout->variance = mosaicVariance;
     1123    newReadout->covariance = mosaicCovariance;
    10971124    psFree(newReadout);                 // Drop reference
    10981125
     
    13341361    target->concepts = psMetadataCopy(target->concepts, source->concepts);
    13351362
     1363    // Average the covariances
     1364    psList *covariances = psListAlloc(NULL); // Input covariance matrices
     1365    for (int i = 0; i < covariances->n; i++) {
     1366        pmChip *chip = chips->data[i];  // Chip of interest
     1367        if (!chip || !chip->data_exists) {
     1368            continue;
     1369        }
     1370        psArray *cells = chip->cells;   // Cells in chip
     1371        for (long j = 0; j < cells->n; j++) {
     1372            pmCell *cell = cells->data[i]; // Cell of interest
     1373            if (!cell || !cell->data_exists) {
     1374                continue;
     1375            }
     1376            pmReadout *ro = cell->readouts->data[0]; // Readout of interest
     1377            if (!ro || !ro->covariance) {
     1378                continue;
     1379            }
     1380            psListAdd(covariances, PS_LIST_TAIL, ro->covariance);
     1381        }
     1382    }
     1383    psKernel *mosaicCovariances = NULL; // Covariance for mosaic
     1384    if (psListLength(covariances) > 0) {
     1385        psArray *covarArray = psListToArray(covariances); // Array with covariances
     1386        mosaicCovariances = psImageCovarianceAverage(covarArray);
     1387        psFree(covarArray);
     1388    }
     1389    psFree(covariances);
     1390
    13361391    // Now make a new readout to go in the new cell
    13371392    pmReadout *newReadout = pmReadoutAlloc(targetCell); // New readout
     
    13391394    newReadout->mask   = mosaicMask;
    13401395    newReadout->variance = mosaicVariances;
     1396    newReadout->covariance = mosaicCovariances;
    13411397    psFree(newReadout);                 // Drop reference
    13421398
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPARead.c

    r24792 r27840  
    296296                        psFits *fits,    // FITS file
    297297                        int z,          // Plane number to read
    298                         int *zMax,      // Max plane number in this cell
     298                        int *zMax,      // Max plane number in this cell
    299299                        int numScans,   // Number of scans to read at a time
    300300                        fpaReadType type, // Type of image
     
    314314        return true;
    315315      } else {
    316         return false;
     316        return false;
    317317      }
    318318    }
     
    387387                psFree(regionString);
    388388                psFree(readout);
     389                psFree(iter);
    389390                return false;
    390391            }
     
    485486                             psFits *fits, // FITS file
    486487                             int z,     // Desired image plane
    487                              int *zMax, // Max plane number in this cell
     488                             int *zMax, // Max plane number in this cell
    488489                             int numScans, // Number of scans (row or col depends on CELL.READDIR); 0 for all
    489490                             int overlap, // Number of scans (row/col) to overlap between scans
     
    627628    // Blow away existing data.
    628629    // Do this before returning, so that we're not returning data from a previous read
    629     psFree(*image);
    630     *image = NULL;
     630//    psFree(*image);
     631//    *image = NULL;
    631632    *image = readoutReadComponent(*image, fits, trimsec, readdir, thisScan, lastScan, z, bad, pixelTypes[type]);
    632633
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAWrite.c

    r24854 r27840  
    307307
    308308        if (writeBlank || writeImage) {
    309             if (!pmConceptsWriteFPA(fpa, true, config)) {
     309            if (!pmConceptsWriteFPA(fpa, true, config)) {
    310310                psError(PS_ERR_IO, false, "Unable to write concepts for FPA.\n");
    311311                return false;
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAfile.c

    r23746 r27840  
    2222static int fileNum = 0;                 // Number of file
    2323
     24static bool fpaFileFreeStrict = true;   // Strict checking when file has been freed?
     25
     26bool pmFPAfileFreeSetStrict(bool new)
     27{
     28    bool old = fpaFileFreeStrict;       // Old value, to return
     29    fpaFileFreeStrict = new;
     30    return old;
     31}
     32
    2433static void pmFPAfileFree(pmFPAfile *file)
    2534{
     
    2736        return;
    2837    }
     38
     39    psAssert(!fpaFileFreeStrict || file->fits == NULL, "File %s wasn't closed.", file->name);
    2940
    3041    psTrace ("pmFPAfileFree", 5, "freeing %s\n", file->name);
     
    4051    psFree (file->name);
    4152
    42     if (file->fits != NULL) {
    43         psFitsClose (file->fits);
    44     }
    4553    psFree(file->compression);
    4654    psFree(file->options);
     
    103111    file->save = false;
    104112
    105     file->index = fileNum++;
     113    file->fileIndex = fileNum++;
     114    file->fileID = 0;
    106115
    107116    file->imageId = 0;
     
    364373        // Number of the file in list
    365374        psString num = NULL;            // Number to use
    366         psStringAppend(&num, "%d", file->index);
     375        psStringAppend(&num, "%d", file->fileIndex);
    367376        psStringSubstitute(&newRule, num, "{FILE.INDEX}");
     377        psFree(num);
     378    }
     379
     380    if (strstr(newRule, "{FILE.ID}")) {
     381        // Number of the file in list
     382        psString num = NULL;            // Number to use
     383        psStringAppend(&num, "%03d", file->fileID);
     384        psStringSubstitute(&newRule, num, "{FILE.ID}");
    368385        psFree(num);
    369386    }
     
    519536    if (!strcasecmp(type, "SUBKERNEL"))     {
    520537        return PM_FPA_FILE_SUBKERNEL;
     538    }
     539    if (!strcasecmp(type, "PATTERN")) {
     540        return PM_FPA_FILE_PATTERN;
    521541    }
    522542
     
    563583      case PM_FPA_FILE_SUBKERNEL:
    564584        return ("SUBKERNEL");
     585      case PM_FPA_FILE_PATTERN:
     586        return "PATTERN";
    565587      default:
    566588        return ("NONE");
     
    625647    psFree(iter);
    626648
    627     psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find instance %d of file %s", num, name);
     649    psLogMsg("psModules.camera", PS_LOG_MINUTIA, "Unable to find instance %d of file %s", num, name);
    628650    return NULL;
    629651}
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAfile.h

    r23487 r27840  
    4848    PM_FPA_FILE_ASTROM_REFSTARS,
    4949    PM_FPA_FILE_SUBKERNEL,
     50    PM_FPA_FILE_SRCTEXT,
     51    PM_FPA_FILE_PATTERN,
    5052} pmFPAfileType;
    5153
     
    109111    psString formatName;                // name of the camera format
    110112
    111     int index;                          // Index of file
     113    int fileIndex;                      // Index of file
     114    int fileID;                         // internal sequence number
    112115
    113116    psS64 imageId, sourceId;            // Image and source identifiers
     
    160163    );
    161164
     165/// Set strict checking when freeing file
     166///
     167/// If true (default), freeing a pmFPAfile involves asserting that the FITS filehandle has been closed.
     168bool pmFPAfileFreeSetStrict(bool new    // New state
     169    );
    162170
    163171
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAfileDefine.c

    r23370 r27840  
    5757        return NAN;
    5858    }
    59     int value = psMetadataItemParseF32(item); // Value of interst
     59    float value = psMetadataItemParseF32(item); // Value of interst
    6060    return value;
    6161}
     
    7272        return NAN;
    7373    }
    74     int value = psMetadataItemParseF64(item); // Value of interst
     74    double value = psMetadataItemParseF64(item); // Value of interst
    7575    return value;
    7676}
     
    9191    psMetadata *data = pmConfigFileRule(config, camera, name); // File rule
    9292    if (!data) {
    93         psError(PS_ERR_IO, true, "Can't find file rule %s!", name);
     93        psError(psErrorCodeLast(), false, "Can't find file rule %s!", name);
    9494        return NULL;
    9595    }
     
    105105    file->type = pmFPAfileTypeFromString(type);
    106106    if (file->type == PM_FPA_FILE_NONE) {
    107         psError(PS_ERR_IO, true, "FILE.TYPE is not defined for %s\n", name);
     107        psError(PM_ERR_CONFIG, true, "FILE.TYPE is not defined for %s\n", name);
    108108        psFree(file);
    109109        return NULL;
     
    115115    file->dataLevel = pmFPALevelFromName(psMetadataLookupStr (&status, data, "DATA.LEVEL"));
    116116    if (file->dataLevel == PM_FPA_LEVEL_NONE) {
    117         psError(PS_ERR_IO, true, "DATA.LEVEL is not set for %s\n", name);
     117        psError(PM_ERR_CONFIG, true, "DATA.LEVEL is not set for %s\n", name);
    118118        psFree(file);
    119119        return NULL;
     
    137137    if (!psMetadataAddPtr(config->files, PS_LIST_TAIL, name,
    138138                          PS_DATA_UNKNOWN | PS_META_DUPLICATE_OK, "", file)) {
    139         psError(PS_ERR_IO, false, "could not add %s to config files", name);
     139        psError(PM_ERR_CONFIG, false, "could not add %s to config files", name);
    140140        return NULL;
    141141    }
     
    167167        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->system, "CAMERAS"); // Known cameras
    168168        if (!mdok || !cameras) {
    169             psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the system configuration.\n");
     169            psError(PM_ERR_CONFIG, true, "Unable to find CAMERAS in the system configuration.\n");
    170170            return NULL;
    171171        }
    172172        camera = psMetadataLookupMetadata(&mdok, cameras, cameraName); // Camera configuration of interest
    173173        if (!mdok || !camera) {
    174             psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find automatically generated "
    175                     "camera configuration %s in system configuration.\n", cameraName);
     174            psError(PM_ERR_CONFIG, true,
     175                    "Unable to find automatically generated camera configuration %s in system configuration.",
     176                    cameraName);
    176177            return NULL;
    177178        }
     
    184185    psMetadata *filerule = pmConfigFileRule(config, camera, name); // File rule
    185186    if (!filerule) {
    186         psError(PS_ERR_IO, true, "Can't find file rule %s!", name);
     187        psError(psErrorCodeLast(), false, "Can't find file rule %s!", name);
    187188        return NULL;
    188189    }
     
    199200    file->type = pmFPAfileTypeFromString(type);
    200201    if (file->type == PM_FPA_FILE_NONE) {
    201         psError(PS_ERR_IO, true, "FILE.TYPE is not defined for %s\n", name);
     202        psError(PM_ERR_CONFIG, true, "FILE.TYPE is not defined for %s\n", name);
    202203        psFree(file);
    203204        return NULL;
     
    230231    psMetadata *format = psMetadataLookupMetadata(&status, formats, formatName); // Camera format to use
    231232    if (!format) {
    232         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find format %s for file %s.\n",
     233        psError(PM_ERR_CONFIG, true, "Unable to find format %s for file %s.\n",
    233234                formatName, file->name);
    234235        psFree(file);
     
    287288                options->stdevNum = parseOptionFloat(scheme, "STDEV.NUM", source); // Padding to edge
    288289                if (!isfinite(options->stdevNum)) {
    289                     psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Bad value for STDEV.NUM for %s", source);
     290                    psError(PM_ERR_CONFIG, true, "Bad value for STDEV.NUM for %s", source);
    290291                    psFree(source);
    291292                    psFree(file);
     
    296297                options->stdevBits = parseOptionInt(scheme, "STDEV.BITS", source, 0); // Bits for stdev
    297298                if (options->stdevBits <= 0) {
    298                     psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Bad value for STDEV.BITS (%d) for %s",
     299                    psError(PM_ERR_CONFIG, true, "Bad value for STDEV.BITS (%d) for %s",
    299300                            options->stdevBits, source);
    300301                    psFree(source);
     
    310311                psAbort("Should never get here.");
    311312            }
     313        }
     314
     315        psMetadataItem *fuzz = psMetadataLookup(scheme, "FUZZ"); // Quantisation fuzz?
     316        if (fuzz) {
     317            if (fuzz->type != PS_TYPE_BOOL) {
     318                psWarning("FUZZ in compression scheme %s isn't boolean.", fitsType);
     319                goto FITS_OPTIONS_DONE;
     320            }
     321            options->fuzz = fuzz->data.B;
    312322        }
    313323
     
    337347    file->fileLevel = pmFPAPHULevel(format);
    338348    if (file->fileLevel == PM_FPA_LEVEL_NONE) {
    339         psError(PS_ERR_IO, true, "Unable to determine file level for %s\n", name);
     349        psError(PM_ERR_CONFIG, true, "Unable to determine file level for %s\n", name);
    340350        psFree(file);
    341351        return NULL;
     
    344354    file->dataLevel = pmFPALevelFromName(psMetadataLookupStr(&status, filerule, "DATA.LEVEL"));
    345355    if (file->dataLevel == PM_FPA_LEVEL_NONE) {
    346         psError(PS_ERR_IO, true, "DATA.LEVEL is not set for %s\n", name);
     356        psError(PM_ERR_CONFIG, true, "DATA.LEVEL is not set for %s\n", name);
    347357        psFree(file);
    348358        return NULL;
     
    443453        psString realName = pmConfigConvertFilename(filenames->data[0], config, false, false);
    444454        if (!realName) {
    445             psError(PS_ERR_IO, false, "Failed to convert file name %s", (char *)filenames->data[0]);
     455            psError(psErrorCodeLast(), false, "Failed to convert file name %s", (char *)filenames->data[0]);
    446456            return NULL;
    447457        }
     
    451461        psFits *fits = psFitsOpen(realName, "r"); // FITS file
    452462        if (!fits) {
    453             psError(PS_ERR_IO, false, "Failed to open file %s", realName);
     463            psError(psErrorCodeLast(), false, "Failed to open file %s", realName);
    454464            psFree(realName);
    455465            return NULL;
     
    457467        phu = psFitsReadHeader (NULL, fits); // Primary header
    458468        if (!phu) {
    459             psError(PS_ERR_IO, false, "Failed to read file header %s", realName);
     469            psError(psErrorCodeLast(), false, "Failed to read file header %s", realName);
    460470            psFree(realName);
    461471            return NULL;
    462472        }
    463         psFitsClose(fits);
     473        if (!psFitsClose(fits)) {
     474            psError(psErrorCodeLast(), false, "Failed to close file %s", realName);
     475            psFree(realName);
     476            return NULL;
     477        }
    464478
    465479        // Determine the current format from the header; determine camera if not specified already.
     
    467481        format = pmConfigCameraFormatFromHeader(&camera, &cameraName, &formatName, config, phu, true);
    468482        if (!format) {
    469             psError(PS_ERR_IO, false, "Failed to determine camera format for %s", realName);
     483            psError(psErrorCodeLast(), false, "Failed to determine camera format for %s", realName);
    470484            psFree(camera);
    471485            psFree(formatName);
     
    477491        fileLevel = pmFPAPHULevel(format);
    478492        if (fileLevel == PM_FPA_LEVEL_NONE) {
    479             psError(PS_ERR_IO, true, "Unable to determine file level for %s", realName);
     493            psError(PM_ERR_CONFIG, true, "Unable to determine file level for %s", realName);
    480494            psFree(camera);
    481495            psFree(formatName);
     
    490504        psFree(camera);
    491505        if (!fpa) {
    492             psError(PS_ERR_IO, false, "Failed to construct FPA from %s", realName);
     506            psError(psErrorCodeLast(), false, "Failed to construct FPA from %s", realName);
    493507            psFree(formatName);
    494508            psFree(realName);
     
    504518    pmFPAfile *file = pmFPAfileDefineInput(config, fpa, cameraName, name); // File, to return
    505519    if (!file) {
    506         psError(PS_ERR_IO, false, "File %s not defined", name);
     520        psError(psErrorCodeLast(), false, "File %s not defined", name);
    507521        psFree(formatName);
    508522        psFree(format);
     
    531545            psString realName = pmConfigConvertFilename(filenames->data[i], config, false, false);
    532546            if (!realName) {
    533                 psError(PS_ERR_IO, false, "Failed to convert file name %s", (char*)filenames->data[i]);
     547                psError(psErrorCodeLast(), false, "Failed to convert file name %s", (char*)filenames->data[i]);
    534548                return NULL;
    535549            }
    536550            psFits *fits = psFitsOpen(realName, "r"); // FITS file
    537551            if (!fits) {
    538                 psError(PS_ERR_IO, false, "Failed to open file %s", realName);
     552                psError(psErrorCodeLast(), false, "Failed to open file %s", realName);
    539553                psFree(realName);
    540554                return NULL;
    541555            }
    542556            phu = psFitsReadHeader(NULL, fits);
    543             psFitsClose(fits);
     557            if (!psFitsClose(fits)) {
     558                psError(psErrorCodeLast(), false, "Failed to close file %s", realName);
     559                psFree(realName);
     560                return NULL;
     561            }
    544562            if (!phu) {
    545                 psError(PS_ERR_IO, false, "Failed to read file header %s", realName);
     563                psError(psErrorCodeLast(), false, "Failed to read file header %s", realName);
    546564                psFree(realName);
    547565                return NULL;
     
    552570        if (i == 0 && file->type == PM_FPA_FILE_MASK) {
    553571            if (!pmConfigMaskReadHeader(config, phu)) {
    554                 psError(PS_ERR_IO, false, "Error reading mask bits");
     572                psError(psErrorCodeLast(), false, "Error reading mask bits");
    555573                psFree(phu);
    556574                return NULL;
     
    562580                bool valid = false;
    563581                if (!pmConfigValidateCameraFormat(&valid, format, phu)) {
    564                     psError(PS_ERR_UNKNOWN, false, "Error in config scripts\n");
     582                    psError(psErrorCodeLast(), false, "Error in config scripts\n");
    565583                    psFree(phu);
    566584                    return NULL;
    567585                }
    568586                if (!valid) {
    569                     psError(PS_ERR_IO, false, "File %s is not from the required camera",
     587                    psError(psErrorCodeLast(), false, "File %s is not from the required camera",
    570588                            (char*)filenames->data[i]);
    571589                    psFree(phu);
     
    575593                format = pmConfigCameraFormatFromHeader(NULL, NULL, NULL, config, phu, true);
    576594                if (!format) {
    577                     psError(PS_ERR_IO, false, "Failed to determine camera format from %s",
     595                    psError(psErrorCodeLast(), false, "Failed to determine camera format from %s",
    578596                            (char*)filenames->data[i]);
    579597                    psFree(phu);
     
    601619        phu = NULL;
    602620        if (!view) {
    603             psError(PS_ERR_IO, false, "Unable to determine source for %s", name);
     621            psError(PM_ERR_CONFIG, true, "Unable to determine source for %s", name);
    604622            return NULL;
    605623        }
     
    633651    }
    634652    if (filenames->n == 0) {
    635         psError(PS_ERR_IO, false, "No files in array in %s in arguments", argname);
     653        psError(PM_ERR_CONFIG, true, "No files in array in %s in arguments", argname);
    636654        if (success) {
    637655            *success = false;
     
    667685    }
    668686    if (filenames->n == 0) {
    669         psError(PS_ERR_IO, false, "No files in array in %s in arguments", argname);
     687        psError(PM_ERR_CONFIG, true, "No files in array in %s in arguments", argname);
    670688        if (success) {
    671689            *success = false;
     
    700718    }
    701719    if (filenames->n <= entry) {
    702         psError(PS_ERR_IO, false, "Insufficient files (%ld) in array in %s in arguments",
     720        psError(PM_ERR_CONFIG, true, "Insufficient files (%ld) in array in %s in arguments",
    703721                filenames->n, argname);
    704722        if (success) {
     
    760778    }
    761779    if (bind && files->n != bind->n) {
    762         psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     780        psError(PM_ERR_CONFIG, true,
    763781                "Length of filenames (%ld) and bind files (%ld) does not match.",
    764782                files->n, bind->n);
     
    774792        files->data[i] = psMemIncrRefCounter(fpaFileDefineFromArray(config, bindFile, filename, dummy));
    775793        if (!files->data[i]) {
    776             psError(PS_ERR_UNKNOWN, false, "Unable to define file %s %d", filename, i);
     794            psError(psErrorCodeLast(), false, "Unable to define file %s %d", filename, i);
    777795            psFree(dummy);
    778796            psFree(files);
     
    802820    // a camera config is needed (as source of file rule)
    803821    if (config->camera == NULL) {
    804         psError(PS_ERR_IO, true, "camera is not defined");
     822        psError(PM_ERR_PROG, true, "camera is not defined");
    805823        return NULL;
    806824    }
     
    809827    pmFPA *fpa = pmFPAConstruct(config->camera, config->cameraName);
    810828    if (!fpa) {
    811         psError(PS_ERR_IO, false, "Failed to construct FPA for %s", filename);
     829        psError(psErrorCodeLast(), false, "Failed to construct FPA for %s", filename);
    812830        return NULL;
    813831    }
     
    818836    psFree (fpa);
    819837    if (!file) {
    820         psError(PS_ERR_IO, false, "file %s not defined\n", filename);
     838        psError(psErrorCodeLast(), false, "file %s not defined\n", filename);
    821839        return NULL;
    822840    }
     
    824842    // image names may not come from file->names
    825843    if (!strcasecmp(file->filerule, "@FILES")) {
    826         psError(PS_ERR_IO, true, "supplied filerule uses illegal value @FILES");
     844        psError(PM_ERR_CONFIG, true, "supplied filerule uses illegal value @FILES");
    827845        // XXX remove the file from config->files
    828846        return NULL;
     
    881899    // a camera config is needed (as source of file rule)
    882900    if (config->camera == NULL) {
    883         psError(PS_ERR_IO, true, "camera is not defined");
     901        psError(PM_ERR_PROG, true, "camera is not defined");
    884902        return NULL;
    885903    }
     
    895913        fpa = pmFPAConstruct(config->camera, config->cameraName);
    896914        if (!fpa) {
    897             psError(PS_ERR_IO, false, "Failed to construct FPA for %s", filename);
     915            psError(psErrorCodeLast(), false, "Failed to construct FPA for %s", filename);
    898916            return NULL;
    899917        }
     
    902920        file = pmFPAfileDefineInput (config, fpa, NULL, filename);
    903921        if (!file) {
    904             psError(PS_ERR_IO, false, "file %s not defined\n", filename);
     922            psError(psErrorCodeLast(), false, "file %s not defined\n", filename);
    905923            psFree(fpa);
    906924            return NULL;
     
    934952    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, "PPIMAGE");
    935953    if (!status) {
    936         psError(PS_ERR_UNEXPECTED_NULL, true, "PPIMAGE recipe not found.");
     954        psError(PM_ERR_CONFIG, true, "PPIMAGE recipe not found.");
    937955        psFree(options);
    938956        psFree(fpa);
     
    10121030        pmDetrendSelectResults *results = pmDetrendSelect (options, config);
    10131031        if (!results) {
    1014             psError (PS_ERR_IO, false, "no matching detrend data");
     1032            psError (psErrorCodeLast(), false, "no matching detrend data");
    10151033            return NULL;
    10161034        }
     
    10181036        file->fileLevel = pmFPALevelFromName(results->level);
    10191037        if (file->fileLevel == PM_FPA_LEVEL_NONE) {
    1020             psError (PS_ERR_IO, false, "invalid file level for selected detrend data");
     1038            psError (PM_ERR_CONFIG, false, "invalid file level for selected detrend data");
    10211039            return NULL;
    10221040        }
     
    10431061    pmFPAfile *file = pmFPAfileDefineOutput (config, fpa, filename);
    10441062    if (!file) {
    1045         psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
     1063        psError(psErrorCodeLast(), false, "file %s not defined\n", filename);
    10461064        return NULL;
    10471065    }
     
    10621080    pmFPAfile *file = pmFPAfileDefineOutputForFormat(config, NULL, filename, src->cameraName, src->formatName);
    10631081    if (!file) {
    1064         psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
     1082        psError(psErrorCodeLast(), false, "file %s not defined\n", filename);
    10651083        return NULL;
    10661084    }
     
    10841102    pmFPAfile *file = pmFPAfileDefineOutput (config, NULL, filename);
    10851103    if (!file) {
    1086         psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
     1104        psError(psErrorCodeLast(), false, "file %s not defined\n", filename);
    10871105        return NULL;
    10881106    }
    10891107    if (!file->camera) {
    1090         psError(PS_ERR_UNEXPECTED_NULL, false, "file %s does not define a new camera\n", filename);
     1108        psError(PM_ERR_CONFIG, false, "file %s does not define a new camera\n", filename);
    10911109        return NULL;
    10921110    }
     
    11271145    }
    11281146    if (!file) {
    1129         psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
     1147        psError(PM_ERR_CONFIG, true, "file %s not defined\n", filename);
    11301148        return NULL;
    11311149    }
     
    11701188    }
    11711189    if (!file) {
    1172         psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
     1190        psError(PM_ERR_CONFIG, true, "file %s not defined\n", filename);
    11731191        return NULL;
    11741192    }
     
    11771195    if (src) {
    11781196        if (!pmConceptsCopyFPA(file->fpa, src, true, false)) {
    1179             psError(PS_ERR_UNKNOWN, false, "Unable to copy concepts from source to new FPA");
     1197            psError(psErrorCodeLast(), false, "Unable to copy concepts from source to new FPA");
    11801198            return NULL;
    11811199        }
     
    12211239    }
    12221240    if (!file) {
    1223         psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
     1241        psError(PM_ERR_CONFIG, true, "file %s not defined\n", filename);
    12241242        return NULL;
    12251243    }
     
    12281246    if (src) {
    12291247        if (!pmConceptsCopyFPA(file->fpa, src, false, false)) {
    1230             psError(PS_ERR_UNKNOWN, false, "Unable to copy concepts from source to new FPA");
     1248            psError(psErrorCodeLast(), false, "Unable to copy concepts from source to new FPA");
    12311249            return NULL;
    12321250        }
     
    12531271    file->name = psStringCopy (name);
    12541272
     1273    // free a previously existing readout
     1274    psFree(file->readout);
    12551275    file->readout = readout;
    1256     psMetadataAddPtr(files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "", file);
     1276
     1277    // allow for multiple entries
     1278    // XXX handle replace vs multiple?
     1279    psMetadataAddPtr(files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN | PS_META_DUPLICATE_OK, "", file);
    12571280    psFree(file);
    12581281    // we free this copy of file, but 'files' still has a copy
     
    12741297    }
    12751298    if (file == NULL) {
    1276         psError(PS_ERR_IO, true, "file %s is NULL", name);
     1299        psError(PM_ERR_CONFIG, true, "file %s is NULL", name);
    12771300        return false;
    12781301    }
     
    12951318                                const char *name, // name of internal/external file
    12961319                                const pmFPA *fpa, // use this fpa to generate
    1297                                 const psImageBinning *binning) {
     1320                                const psImageBinning *binning,
     1321                                int index) {
    12981322  pmReadout *readout = NULL;
    12991323
    1300   bool status = true;
    1301   pmFPAfile *file = psMetadataLookupPtr(&status, config->files, name);
     1324  pmFPAfile *file = pmFPAfileSelectSingle(config->files, name, index);
    13021325
    13031326  // if the file does not exist, it is not being used as an I/O file: define an internal version
    13041327  if (file == NULL) {
    1305     readout = pmFPAfileDefineInternal (config->files, name, binning->nXruff, binning->nYruff, PS_TYPE_F32);
    1306     return readout;
     1328      // XXX currently, we do not guarantee that the defined file lands on entry 'index'
     1329      psAssert (binning, "internal files must be supplied a psImageBinning for the output images size");
     1330      readout = pmFPAfileDefineInternal (config->files, name, binning->nXruff, binning->nYruff, PS_TYPE_F32);
     1331      return readout;
    13071332  }
    13081333
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAfileDefine.h

    r23354 r27840  
    172172                                const char *name, // name of internal/external file
    173173                                const pmFPA *fpa, // use this fpa to generate
    174                                 const psImageBinning *binning);
     174                                const psImageBinning *binning,
     175                                int index
     176    );
    175177
    176178/// @}
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAfileFitsIO.c

    r23428 r27840  
    162162                  hdu->header = psMetadataAlloc();
    163163              }
     164
    164165              pmConfigConformHeader(hdu->header, file->format);
    165166
     
    178179          pmChip *chip = pmFPAviewThisChip(view, fpa); // Chip of interest, or NULL
    179180          pmCell *cell = pmFPAviewThisCell(view, fpa); // Cell of interest, or NULL
    180           if (!pmFPAUpdateNames(fpa, chip, cell, file->imageId, file->sourceId)) {
    181               psError(PS_ERR_UNKNOWN, false, "Unable to update names in header.");
    182               return false;
    183           }
    184 
    185181          if (cell) {
    186182              if (!pmConceptsWriteCell(cell, true, config)) {
     
    195191          } else if (!pmConceptsWriteFPA(fpa, true, config)) {
    196192              psError(PS_ERR_IO, false, "Unable to write concepts for FPA.\n");
     193              return false;
     194          }
     195
     196          if (!pmFPAUpdateNames(fpa, chip, cell, file->imageId, file->sourceId)) {
     197              psError(PS_ERR_UNKNOWN, false, "Unable to update names in header.");
    197198              return false;
    198199          }
  • branches/simtest_nebulous_branches/psModules/src/camera/pmFPAfileIO.c

    r24836 r27840  
    4343#include "pmFPAConstruct.h"
    4444#include "pmSubtractionIO.h"
     45#include "pmPatternIO.h"
    4546#include "pmConcepts.h"
    4647#include "pmConfigRun.h"
     
    6667
    6768        switch (place) {
    68         case PM_FPA_BEFORE:
     69          case PM_FPA_BEFORE:
    6970            if (!pmFPAfileRead (file, view, config)) {
    7071                psError(PS_ERR_IO, false, "failed READ in FPA_BEFORE block for %s", file->name);
     
    7677            }
    7778            break;
    78         case PM_FPA_AFTER:
     79          case PM_FPA_AFTER:
    7980            if (!pmFPAfileWrite (file, view, config)) {
    8081                psError(PS_ERR_IO, false, "failed WRITE in FPA_AFTER block for %s", file->name);
     
    8687            }
    8788            break;
    88         default:
     89          default:
    8990            psAbort("You can't get here");
    9091        }
     
    202203        status = pmSubtractionReadKernels(view, file, config);
    203204        break;
     205      case PM_FPA_FILE_PATTERN:
     206        status = pmPatternRead(view, file, config);
     207        break;
    204208      case PM_FPA_FILE_SX:
    205209      case PM_FPA_FILE_RAW:
     
    208212      case PM_FPA_FILE_CMF:
    209213      case PM_FPA_FILE_WCS:
     214      case PM_FPA_FILE_SRCTEXT:
    210215        status = pmFPAviewReadObjects (view, file, config);
    211216        break;
     
    272277      case PM_FPA_FILE_VARIANCE:
    273278      case PM_FPA_FILE_FRINGE:
    274       case PM_FPA_FILE_DARK: {
    275           // create FPA structure component based on view
    276           psMetadata *format = file->format; // Camera format configuration
    277           if (!format) {
    278               format = config->format;
    279           }
    280 
    281           pmFPA *nameSource = file->src; // Source of FPA.OBS
    282           if (!nameSource) {
    283               nameSource = file->fpa;
    284           }
    285           bool mdok;                  // Status of MD lookup
    286           const char *fpaObs = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.OBS"); // Obs. id
    287 
    288           pmFPAAddSourceFromView(file->fpa, fpaObs, view, format);
    289           psTrace ("psModules.camera", 5, "created fpa data elements for %s (%s) (%d:%d:%d)\n",
    290                    file->name, file->name, view->chip, view->cell, view->readout);
    291           break;
    292       }
     279      case PM_FPA_FILE_DARK:
     280      case PM_FPA_FILE_PATTERN:
     281        {
     282            // create FPA structure component based on view
     283            psMetadata *format = file->format; // Camera format configuration
     284            if (!format) {
     285                format = config->format;
     286            }
     287
     288            pmFPA *nameSource = file->src; // Source of FPA.OBS
     289            if (!nameSource) {
     290                nameSource = file->fpa;
     291            }
     292            bool mdok;                  // Status of MD lookup
     293            const char *fpaObs = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.OBS"); // Obs. id
     294
     295            pmFPAAddSourceFromView(file->fpa, fpaObs, view, format);
     296            psTrace ("psModules.camera", 5, "created fpa data elements for %s (%s) (%d:%d:%d)\n",
     297                     file->name, file->name, view->chip, view->cell, view->readout);
     298            break;
     299        }
    293300      case PM_FPA_FILE_HEADER:
    294301        psAbort ("Create not defined for HEADER");
     
    461468        status = pmSubtractionWriteKernels(view, file, config);
    462469        break;
     470      case PM_FPA_FILE_PATTERN:
     471        status = pmPatternWrite(view, file, config);
     472        break;
    463473      case PM_FPA_FILE_SX:
    464474      case PM_FPA_FILE_RAW:
     
    542552      case PM_FPA_FILE_DARK:
    543553      case PM_FPA_FILE_SUBKERNEL:
     554      case PM_FPA_FILE_PATTERN:
    544555      case PM_FPA_FILE_CMF:
    545556      case PM_FPA_FILE_WCS:
     
    609620        break;
    610621      case PM_FPA_FILE_SUBKERNEL:
     622      case PM_FPA_FILE_PATTERN:
    611623      case PM_FPA_FILE_SX:
    612624      case PM_FPA_FILE_RAW:
     
    770782      case PM_FPA_FILE_DARK:
    771783      case PM_FPA_FILE_SUBKERNEL:
     784      case PM_FPA_FILE_PATTERN:
    772785      case PM_FPA_FILE_CMF:
    773786      case PM_FPA_FILE_WCS:
     
    961974      case PM_FPA_FILE_SUBKERNEL:
    962975        status = pmSubtractionWritePHU(view, file, config);
     976        break;
     977      case PM_FPA_FILE_PATTERN:
     978        status = pmPatternWritePHU(view, file, config);
     979        break;
    963980      case PM_FPA_FILE_CMF:
    964981        status = pmSource_CMF_WritePHU (view, file, config);
  • branches/simtest_nebulous_branches/psModules/src/camera/pmHDUGenerate.c

    r24769 r27840  
    611611            if (cells->n == 0) {
    612612                // Nothing to do
     613                psFree (cells);
    613614                return true;
    614615            }
     
    660661            if (cells->n == 0) {
    661662                // Nothing to do
     663                psFree (cells);
    662664                return true;
    663665            }
     
    707709            if (cells->n == 0) {
    708710                // Nothing to do
     711                psFree(cells);
    709712                return true;
    710713            }
    711 
    712             return generateHDU(hdu, cells);
     714            bool status = generateHDU(hdu, cells);
     715            psFree(cells);
     716            return status;
    713717        }
    714718    default:
  • branches/simtest_nebulous_branches/psModules/src/camera/pmReadoutFake.c

    r23960 r27840  
    2323#include "pmSourceUtils.h"
    2424#include "pmModelUtils.h"
     25#include "pmSourceGroups.h"
    2526
    2627#include "pmReadoutFake.h"
    2728
    28 #define MODEL_TYPE "PS_MODEL_RGAUSS"    // Type of model to use
    2929#define MAX_AXIS_RATIO 20.0             // Maximum axis ratio for PSF model
    30 #define SOURCE_MASK (PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_CR_LIMIT) // Mask to apply to input sources
    3130#define MODEL_MASK (PM_MODEL_STATUS_NONCONVERGE | PM_MODEL_STATUS_OFFIMAGE | \
    3231                    PM_MODEL_STATUS_BADARGS | PM_MODEL_STATUS_LIMITS) // Mask to apply to models
     32
     33
     34static bool threaded = false;           // Running threaded?
     35
     36
    3337
    3438
     
    5054}
    5155
    52 bool pmReadoutFakeFromSources(pmReadout *readout, int numCols, int numRows, const psArray *sources,
    53                               const psVector *xOffset, const psVector *yOffset, const pmPSF *psf,
    54                               float minFlux, int radius, bool circularise, bool normalisePeak)
     56/// Generate fake sources on a readout
     57static bool readoutFake(pmReadout *readout, // Readout of interest
     58                        const pmSourceGroups *groups, // Source groups
     59                        const psVector *x,        // x coordinates
     60                        const psVector *y,        // y coordinates
     61                        const psVector *mag,      // Magnitudes
     62                        const psVector *xOffset,  // Offsets in x
     63                        const psVector *yOffset,  // Offsets in y
     64                        const pmPSF *psf,         // PSF
     65                        float minFlux,            // Minimum flux
     66                        float radius,             // Minimum radius
     67                        bool circularise,         // Circularise PSF?
     68                        bool normalisePeak,       // Normalise sources for peak?
     69                        int groupIndex,           // Group index
     70                        int cellIndex             // Cell index
     71                        )
     72{
     73    psArray *cells = groups->groups->data[groupIndex]; // Cells in group
     74    psVector *cellSources = cells->data[cellIndex];    // Sources in cell
     75
     76    for (int i = 0; i < cellSources->n; i++) {
     77        int index = cellSources->data.S32[i];                       // Index for source of interest
     78        float flux = powf(10.0, -0.4 * mag->data.F32[index]);       // Flux of source
     79        float xSrc = x->data.F32[index], ySrc = y->data.F32[index]; // Coordinates of source
     80
     81        if (normalisePeak) {
     82            // Normalise flux
     83            pmModel *normModel = pmModelFromPSFforXY(psf, xSrc, ySrc, 1.0); // Model for normalisation
     84            if (!normModel || (normModel->flags & MODEL_MASK)) {
     85                psFree(normModel);
     86                continue;
     87            }
     88            // check that all params are valid:
     89            bool validParams = true;
     90            for (int j = 0; validParams && (j < normModel->params->n); j++) {
     91                switch (j) {
     92                  case PM_PAR_SKY:
     93                  case PM_PAR_I0:
     94                  case PM_PAR_XPOS:
     95                  case PM_PAR_YPOS:
     96                    continue;
     97                  default:
     98                    if (!isfinite(normModel->params->data.F32[j])) {
     99                        validParams = false;
     100                    }
     101                }
     102            }
     103            if (!validParams) {
     104                psFree(normModel);
     105                continue;
     106            }
     107            if (circularise && !circulariseModel(normModel)) {
     108                psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
     109                psFree(normModel);
     110                return false;
     111            }
     112
     113            flux /= normModel->modelFlux(normModel->params);
     114            psFree(normModel);
     115        }
     116
     117        pmModel *fakeModel = pmModelFromPSFforXY(psf, xSrc, ySrc, flux);
     118        if (!fakeModel || (fakeModel->flags & MODEL_MASK)) {
     119            psFree(fakeModel);
     120            continue;
     121        }
     122        // check that all params are valid:
     123        bool validParams = true;
     124        for (int j = 0; validParams && (j < fakeModel->params->n); j++) {
     125            switch (j) {
     126              case PM_PAR_SKY:
     127              case PM_PAR_I0:
     128              case PM_PAR_XPOS:
     129              case PM_PAR_YPOS:
     130                continue;
     131              default:
     132                if (!isfinite(fakeModel->params->data.F32[j])) {
     133                    validParams = false;
     134                }
     135            }
     136        }
     137        if (!validParams) {
     138            psFree(fakeModel);
     139            continue;
     140        }
     141        if (circularise && !circulariseModel(fakeModel)) {
     142            psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
     143            psFree(fakeModel);
     144            return false;
     145        }
     146
     147        psTrace("psModules.camera", 10, "Adding source at %f,%f with flux %f\n",
     148                fakeModel->params->data.F32[PM_PAR_XPOS], fakeModel->params->data.F32[PM_PAR_YPOS],
     149                fakeModel->params->data.F32[PM_PAR_I0]);
     150
     151        pmSource *fakeSource = pmSourceAlloc(); // Fake source to generate
     152        fakeSource->peak = pmPeakAlloc(xSrc, ySrc, fakeModel->params->data.F32[PM_PAR_I0], PM_PEAK_LONE);
     153        float fakeRadius = 1.0;         // Radius of fake source
     154        if (isfinite(minFlux)) {
     155            fakeRadius = PS_MAX(fakeRadius, fakeModel->modelRadius(fakeModel->params, minFlux));
     156        }
     157        if (radius > 0) {
     158            fakeRadius = PS_MAX(fakeRadius, radius);
     159        }
     160
     161        if (xOffset) {
     162            if (!pmSourceDefinePixels(fakeSource, readout, xSrc + xOffset->data.S32[index],
     163                                      ySrc + yOffset->data.S32[index], fakeRadius)) {
     164                psErrorClear();
     165                continue;
     166            }
     167            if (!pmModelAddWithOffset(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0,
     168                                      xOffset->data.S32[index], yOffset->data.S32[index])) {
     169                psErrorClear();
     170                continue;
     171            }
     172        } else {
     173            if (!pmSourceDefinePixels(fakeSource, readout, xSrc, ySrc, fakeRadius)) {
     174                psErrorClear();
     175                continue;
     176            }
     177            if (!pmModelAdd(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0)) {
     178                psErrorClear();
     179                continue;
     180            }
     181        }
     182        psFree(fakeSource);
     183        psFree(fakeModel);
     184    }
     185
     186    return true;
     187}
     188
     189/// Thread job for readoutFake()
     190static bool readoutFakeThread(psThreadJob *job)
     191{
     192    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
     193
     194    psArray *args = job->args;          // Arguments
     195
     196    pmReadout *readout = args->data[0];     // Readout of interest
     197    const pmSourceGroups *groups = args->data[1]; // Source groups
     198    const psVector *x = args->data[2];        // x coordinates
     199    const psVector *y = args->data[3];        // y coordinates
     200    const psVector *mag = args->data[4];      // Magnitudes
     201    const psVector *xOffset = args->data[5];  // Offsets in x
     202    const psVector *yOffset = args->data[6];  // Offsets in y
     203    const pmPSF *psf = args->data[7];         // PSF
     204    float minFlux = PS_SCALAR_VALUE(args->data[8], F32); // Minimum flux
     205    float radius = PS_SCALAR_VALUE(args->data[9], F32);  // Minimum radius
     206    bool circularise = PS_SCALAR_VALUE(args->data[10], U8); // Circularise PSF?
     207    bool normalisePeak = PS_SCALAR_VALUE(args->data[11], U8); // Normalise for peak?
     208    int groupIndex = PS_SCALAR_VALUE(args->data[12], S32); // Group index
     209    int cellIndex = PS_SCALAR_VALUE(args->data[13], S32);  // Cell index
     210
     211    return readoutFake(readout, groups, x, y, mag, xOffset, yOffset, psf, minFlux, radius, circularise,
     212                       normalisePeak, groupIndex, cellIndex);
     213}
     214
     215
     216bool pmReadoutFakeThreads(bool new)
     217{
     218    bool old = threaded;                // Old status, to return
     219
     220    if (!old && new) {
     221        threaded = true;
     222
     223        {
     224            psThreadTask *task = psThreadTaskAlloc("PSMODULES_READOUT_FAKE", 14);
     225            task->function = &readoutFakeThread;
     226            psThreadTaskAdd(task);
     227            psFree(task);
     228        }
     229
     230    } else if (old && !new) {
     231        threaded = false;
     232        psThreadTaskRemove("PSMODULES_READOUT_FAKE");
     233    }
     234
     235    return old;
     236}
     237
     238
     239bool pmReadoutFakeFromVectors(pmReadout *readout, int numCols, int numRows,
     240                              const psVector *x, const psVector *y, const psVector *mag,
     241                              const psVector *xOffset, const psVector *yOffset,
     242                              const pmPSF *psf, float minFlux, int radius,
     243                              bool circularise, bool normalisePeak)
    55244{
    56245    PS_ASSERT_PTR_NON_NULL(readout, false);
    57246    PS_ASSERT_INT_LARGER_THAN(numCols, 0, false);
    58247    PS_ASSERT_INT_LARGER_THAN(numRows, 0, false);
    59     PS_ASSERT_ARRAY_NON_NULL(sources, false);
    60 
     248    PS_ASSERT_VECTOR_NON_NULL(x, false);
     249    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F32, false);
     250    PS_ASSERT_VECTOR_NON_NULL(y, false);
     251    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F32, false);
     252    PS_ASSERT_VECTORS_SIZE_EQUAL(y, x, false);
     253    PS_ASSERT_VECTOR_NON_NULL(mag, false);
     254    PS_ASSERT_VECTOR_TYPE(mag, PS_TYPE_F32, false);
     255    PS_ASSERT_VECTORS_SIZE_EQUAL(mag, x, false);
     256    long numSources = x->n;              // Number of sources
    61257    if (xOffset || yOffset) {
    62258        PS_ASSERT_VECTOR_NON_NULL(xOffset, false);
     
    64260        PS_ASSERT_VECTORS_SIZE_EQUAL(xOffset, yOffset, false);
    65261        PS_ASSERT_VECTOR_TYPE(xOffset, PS_TYPE_S32, false);
    66         PS_ASSERT_VECTOR_TYPE_EQUAL(xOffset, yOffset, false);
    67         if (xOffset->n != sources->n) {
     262        PS_ASSERT_VECTOR_TYPE(yOffset, PS_TYPE_S32, false);
     263        if (xOffset->n != numSources) {
    68264            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
    69265                    "Number of offset vectors (%ld) and sources (%ld) doesn't match",
    70                     xOffset->n, sources->n);
     266                    xOffset->n, numSources);
    71267            return false;
    72268        }
    73269    }
    74270    PS_ASSERT_PTR_NON_NULL(psf, false);
    75     if (radius > 0 && isfinite(minFlux) && minFlux > 0.0) {
    76         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot define both minimum flux and fixed radius.");
    77         return false;
    78     }
    79271
    80272    readout->image = psImageRecycle(readout->image, numCols, numRows, PS_TYPE_F32);
    81273    psImageInit(readout->image, 0);
    82274
     275    int numThreads = threaded ? psThreadPoolSize() : 0; // Number of threads
     276    pmSourceGroups *groups = pmSourceGroupsFromVectors(readout, x, y, numThreads); // Groups of sources
     277    if (!groups) {
     278        psError(PS_ERR_UNKNOWN, false, "Unable to generate source groups");
     279        return false;
     280    }
     281
     282    if (threaded) {
     283        for (int i = 0; i < groups->groups->n; i++) {
     284            psArray *cells = groups->groups->data[i]; // Cell with sources
     285            for (int j = 0; j < cells->n; j++) {
     286                psThreadJob *job = psThreadJobAlloc("PSMODULES_READOUT_FAKE");
     287                psArray *args = job->args;
     288                psArrayAdd(args, 1, readout);
     289                psArrayAdd(args, 1, groups);
     290                // Casting away const to add to array
     291                psArrayAdd(args, 1, (psVector*)x);
     292                psArrayAdd(args, 1, (psVector*)y);
     293                psArrayAdd(args, 1, (psVector*)mag);
     294                psArrayAdd(args, 1, (psVector*)xOffset);
     295                psArrayAdd(args, 1, (psVector*)yOffset);
     296                psArrayAdd(args, 1, (pmPSF*)psf);
     297                PS_ARRAY_ADD_SCALAR(args, minFlux, PS_TYPE_F32);
     298                PS_ARRAY_ADD_SCALAR(args, radius, PS_TYPE_S32);
     299                PS_ARRAY_ADD_SCALAR(args, circularise, PS_TYPE_U8);
     300                PS_ARRAY_ADD_SCALAR(args, normalisePeak, PS_TYPE_U8);
     301                PS_ARRAY_ADD_SCALAR(args, i, PS_TYPE_S32);
     302                PS_ARRAY_ADD_SCALAR(args, j, PS_TYPE_S32);
     303
     304                if (!psThreadJobAddPending(job)) {
     305                    psFree(job);
     306                    psFree(groups);
     307                    return false;
     308                }
     309                psFree(job);
     310            }
     311            if (!psThreadPoolWait(true)) {
     312                psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
     313                psFree(groups);
     314                return false;
     315            }
     316        }
     317    } else if (!readoutFake(readout, groups, x, y, mag, xOffset, yOffset, psf, minFlux, radius, circularise,
     318                            normalisePeak, 0, 0)) {
     319        psError(PS_ERR_UNKNOWN, false, "Unable to generate fake sources on readout");
     320        psFree(groups);
     321        return false;
     322    }
     323
     324    psFree(groups);
     325
     326// Set a concept value
     327#define CONCEPT_SET_S32(CONCEPTS, NAME, OLD, NEW) { \
     328        psMetadataItem *item = psMetadataLookup(CONCEPTS, NAME); \
     329        psAssert(item->type == PS_TYPE_S32, "Incorrect type: %x", item->type); \
     330        if (item->data.S32 == OLD) { \
     331            item->data.S32 = NEW; \
     332        } \
     333    }
     334
     335    if (readout->parent) {
     336        CONCEPT_SET_S32(readout->parent->concepts, "CELL.XPARITY", 0, 1);
     337        CONCEPT_SET_S32(readout->parent->concepts, "CELL.YPARITY", 0, 1);
     338        CONCEPT_SET_S32(readout->parent->concepts, "CELL.XBIN", 0, 1);
     339        CONCEPT_SET_S32(readout->parent->concepts, "CELL.YBIN", 0, 1);
     340    }
     341
     342    return true;
     343
     344}
     345
     346
     347bool pmReadoutFakeFromSources(pmReadout *readout, int numCols, int numRows, const psArray *sources,
     348                              pmSourceMode sourceMask, const psVector *xOffset, const psVector *yOffset,
     349                              const pmPSF *psf, float minFlux, int radius,
     350                              bool circularise, bool normalisePeak)
     351{
     352    PS_ASSERT_ARRAY_NON_NULL(sources, false);
     353
    83354    int numSources = sources->n;          // Number of stars
     355    psVector *x = psVectorAllocEmpty(numSources, PS_TYPE_F32);
     356    psVector *y = psVectorAllocEmpty(numSources, PS_TYPE_F32);
     357    psVector *mag = psVectorAllocEmpty(numSources, PS_TYPE_F32);
     358
     359    int numGood = 0;                    // Number of good sources
    84360    for (int i = 0; i < numSources; i++) {
    85361        pmSource *source = sources->data[i]; // Source of interest
     
    87363            continue;
    88364        }
    89         if (source->mode & SOURCE_MASK) {
     365        if (source->mode & sourceMask) {
    90366            continue;
    91367        }
     
    93369            continue;
    94370        }
    95         float x, y;                     // Coordinates of source
     371        float xSrc, ySrc;                     // Coordinates of source
    96372        if (source->modelPSF) {
    97             x = source->modelPSF->params->data.F32[PM_PAR_XPOS];
    98             y = source->modelPSF->params->data.F32[PM_PAR_YPOS];
     373            xSrc = source->modelPSF->params->data.F32[PM_PAR_XPOS];
     374            ySrc = source->modelPSF->params->data.F32[PM_PAR_YPOS];
    99375        } else {
    100             x = source->peak->xf;
    101             y = source->peak->yf;
    102         }
    103 
    104         float flux = powf(10.0, -0.4 * source->psfMag); // Flux of source
    105 
    106         if (normalisePeak) {
    107             // Normalise flux
    108             pmModel *normModel = pmModelFromPSFforXY(psf, x, y, 1.0); // Model for normalisation
    109             if (!normModel || (normModel->flags & MODEL_MASK)) {
    110                 psFree(normModel);
    111                 continue;
    112             }
    113             if (circularise && !circulariseModel(normModel)) {
    114                 psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
    115                 psFree(normModel);
    116                 return false;
    117             }
    118 
    119             flux /= normModel->modelFlux(normModel->params);
    120             psFree(normModel);
    121         }
    122 
    123         pmModel *fakeModel = pmModelFromPSFforXY(psf, x, y, flux);
    124         if (!fakeModel || (fakeModel->flags & MODEL_MASK)) {
    125             psFree(fakeModel);
    126             continue;
    127         }
    128         if (circularise && !circulariseModel(fakeModel)) {
    129             psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
    130             psFree(fakeModel);
    131             return false;
    132         }
    133 
    134         psTrace("psModules.camera", 10, "Adding source at %f,%f with flux %f\n",
    135                 fakeModel->params->data.F32[PM_PAR_XPOS], fakeModel->params->data.F32[PM_PAR_YPOS],
    136                 fakeModel->params->data.F32[PM_PAR_I0]);
    137 
    138         pmSource *fakeSource = pmSourceAlloc(); // Fake source to generate
    139         fakeSource->peak = pmPeakAlloc(x, y, fakeModel->params->data.F32[PM_PAR_I0], PM_PEAK_LONE);
    140         float fakeRadius = radius > 0 ? radius :
    141             PS_MAX(1.0, fakeModel->modelRadius(fakeModel->params, minFlux)); // Radius of fake source
    142 
    143         if (xOffset) {
    144             if (!pmSourceDefinePixels(fakeSource, readout, x + xOffset->data.S32[i],
    145                                       y + yOffset->data.S32[i], fakeRadius)) {
    146                 psErrorClear();
    147                 continue;
    148             }
    149             if (!pmModelAddWithOffset(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0,
    150                                       xOffset->data.S32[i], yOffset->data.S32[i])) {
    151                 psErrorClear();
    152                 continue;
    153             }
    154         } else {
    155             if (!pmSourceDefinePixels(fakeSource, readout, x, y, fakeRadius)) {
    156                 psErrorClear();
    157                 continue;
    158             }
    159             if (!pmModelAdd(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0)) {
    160                 psErrorClear();
    161                 continue;
    162             }
    163         }
    164         psFree(fakeSource);
    165         psFree(fakeModel);
    166     }
    167 
    168     return true;
    169 }
     376            xSrc = source->peak->xf;
     377            ySrc = source->peak->yf;
     378        }
     379
     380        x->data.F32[numGood] = xSrc;
     381        y->data.F32[numGood] = ySrc;
     382        mag->data.F32[numGood] = source->psfMag;
     383        numGood++;
     384    }
     385    x->n = numGood;
     386    y->n = numGood;
     387    mag->n = numGood;
     388
     389    bool status = pmReadoutFakeFromVectors(readout, numCols, numRows, x, y, mag, xOffset, yOffset, psf,
     390                                           minFlux, radius, circularise, normalisePeak);
     391    psFree(x);
     392    psFree(y);
     393    psFree(mag);
     394
     395    return status;
     396}
  • branches/simtest_nebulous_branches/psModules/src/camera/pmReadoutFake.h

    r20999 r27840  
    1111#include <pmTrend2D.h>
    1212#include <pmPSF.h>
     13#include <pmSourceMasks.h>
     14
     15/// Set threading
     16///
     17/// Returns old threading state
     18bool pmReadoutFakeThreads(
     19    bool new                            // New threading state
     20    );
     21
     22/// Generate a fake readout from vectors
     23bool pmReadoutFakeFromVectors(pmReadout *readout, ///< Output readout
     24                              int numCols, int numRows, ///< Dimension of image
     25                              const psVector *x, const psVector *y, ///< Source coordinates
     26                              const psVector *mag, ///< Source magnitudes
     27                              const psVector *xOffset, ///< x offsets for sources (source -> img), or NULL
     28                              const psVector *yOffset, ///< y offsets for sources (source -> img), or NULL
     29                              const pmPSF *psf, ///< PSF for sources
     30                              float minFlux, ///< Minimum flux to bother about; for setting source radius
     31                              int radius, ///< Fixed radius for sources
     32                              bool circularise, ///< Circularise PSF model?
     33                              bool normalisePeak ///< Normalise the peak value?
     34    );
    1335
    1436/// Generate a fake readout from an array of sources
    15 bool pmReadoutFakeFromSources(pmReadout *readout, ///< Output readout, or NULL
     37bool pmReadoutFakeFromSources(pmReadout *readout, ///< Output readout
    1638                              int numCols, int numRows, ///< Dimension of image
    1739                              const psArray *sources, ///< Array of pmSource
     40                              pmSourceMode sourceMask, ///< Mask for sources
    1841                              const psVector *xOffset, ///< x offsets for sources (source -> img), or NULL
    1942                              const psVector *yOffset, ///< y offsets for sources (source -> img), or NULL
  • branches/simtest_nebulous_branches/psModules/src/concepts/pmConcepts.c

    r25046 r27840  
    249249CONCEPT_REGISTER_FUNCTION(S32, Enum, -1); // For enums: set default to -1
    250250CONCEPT_REGISTER_FUNCTION(S32, S32, 0); // For values: set default to 0
     251//CONCEPT_REGISTER_FUNCTION(Bool, Bool, NULL); // For values: set default to 0
    251252
    252253static void conceptRegisterTime(const char *name, /* Name of concept */ \
     
    291292        conceptRegisterF32("FPA.FOCUS", "Telescope focus", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
    292293        conceptRegisterF32("FPA.AIRMASS", "Airmass at boresight", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     294        // XXX p_pmConceptParse_FPA_FILTER -> p_pmConceptParse_FPA_FILTERID (and Format as well)?
    293295        conceptRegisterStr("FPA.FILTERID", "Filter used (parsed, abstract name)", p_pmConceptParse_FPA_FILTER, p_pmConceptFormat_FPA_FILTER, NULL, false, PM_FPA_LEVEL_FPA);
    294296        conceptRegisterStr("FPA.FILTER", "Filter used (instrument name)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     
    300302        conceptRegisterF64("FPA.LONGITUDE", "West longitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
    301303        conceptRegisterF64("FPA.LATITUDE", "Latitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
    302         conceptRegisterF32("FPA.ELEVATION", "Elevation of observatory (metres)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
    303         conceptRegisterStr("FPA.OBSTYPE", "Type of observation", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     304        conceptRegisterF32("FPA.ELEVATION", "Elevation of observatory (meters)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     305
     306        // conceptRegisterStr("FPA.OBSTYPE", "Type of observation", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     307        conceptRegisterStr("FPA.OBSTYPE", "Type of observation", p_pmConceptParse_FPA_OBSTYPE, p_pmConceptFormat_FPA_OBSTYPE, NULL, false, PM_FPA_LEVEL_FPA);
     308
    304309        conceptRegisterStr("FPA.OBJECT", "Object of observation", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
    305310        conceptRegisterF64("FPA.ALT", "Altitude of boresight", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     
    329334        conceptRegisterF32("FPA.TELTEMP.EXTRA", "Telescope Temperatures: extra", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
    330335        conceptRegisterF32("FPA.PON.TIME", "Power On Time", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     336        conceptRegisterS32("FPA.BURNTOOL.APPLIED", "[T=applied] Burn streaks applied to image data", p_pmConceptParse_BTOOLAPP,p_pmConceptFormat_BTOOLAPP,NULL,false,PM_FPA_LEVEL_FPA);
    331337        conceptRegisterF32("FPA.EXPOSURE", "Exposure time (sec)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     338        conceptRegisterF32("FPA.ZP", "Magnitude zero point", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
    332339    }
    333340    if (!conceptsChip) {
     
    344351        conceptRegisterF32("CHIP.TEMP", "Temperature of chip", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
    345352        conceptRegisterStr("CHIP.ID", "Chip identifier", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
     353        conceptRegisterF32("CHIP.SEEING", "Seeing FWHM (pixels)", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
    346354    }
    347355
  • branches/simtest_nebulous_branches/psModules/src/concepts/pmConceptsAverage.c

    r22706 r27840  
    3636
    3737    double time      = 0.0;             // Time of observation
     38    double zp        = 0.0;             // Zero point
    3839    psTimeType timeSys = 0;             // Time system
    3940    char *filter     = NULL;            // Filter
     
    6364        psTimeConvert(fpaTime, PS_TIME_TAI);
    6465        time       += psTimeToMJD(fpaTime);
     66
     67        zp += psMetadataLookupF32(NULL, fpa->concepts, "FPA.ZP");
     68
    6569        if (num == 1) {
    6670            timeSys = psMetadataLookupS32(NULL, fpa->concepts, "FPA.TIMESYS");
     
    8589
    8690    time /= (double)num;
     91    zp /= (double)num;
    8792
    8893    MD_UPDATE(target->concepts, "FPA.TIMESYS", S32, timeSys);
     
    9297    MD_UPDATE_STR(target->concepts, "FPA.INSTRUMENT", instrument);
    9398    MD_UPDATE_STR(target->concepts, "FPA.DETECTOR", detector);
     99    MD_UPDATE(target->concepts, "FPA.ZP", F32, zp);
    94100
    95101    // FPA.TIME needs special care
     
    278284
    279285    float temp = 0.0;                   // Temperature
     286    float seeing = 0.0;                 // Seeing FWHM
    280287    int x0 = 0, y0 = 0;                 // Offset
    281288    int xParity = 0, yParity = 0;       // Parity
     
    291298        }
    292299        temp += psMetadataLookupF32(NULL, chip->concepts, "CHIP.TEMP");
     300        seeing += psMetadataLookupF32(NULL, chip->concepts, "CHIP.SEEING");
    293301        if (nChips == 0) {
    294302            xSize = psMetadataLookupS32(NULL, chip->concepts, "CHIP.XSIZE");
     
    335343
    336344    temp /= (float)nChips;
     345    seeing /= (float)nChips;
    337346
    338347    MD_UPDATE(target->concepts, "CHIP.TEMP", F32, temp);
     348    MD_UPDATE(target->concepts, "CHIP.SEEING", F32, seeing);
    339349    if (same) {
    340350        MD_UPDATE(target->concepts, "CHIP.X0", S32, x0);
  • branches/simtest_nebulous_branches/psModules/src/concepts/pmConceptsRead.c

    r24824 r27840  
    4747    case PS_DATA_F64:
    4848        return psMetadataItemAllocF64(pattern->name, pattern->comment, psMetadataItemParseF64(concept));
     49    case PS_DATA_BOOL:
     50      return psMetadataItemAllocBool(pattern->name, pattern->comment, psMetadataItemParseBool(concept));
    4951    default:
    5052        psWarning("Concept %s (%s) is not of a standard type (%x)\n",
     
    7375        return false;
    7476    }
    75 
    7677    psTrace ("psModules.concepts", 3, "parsing concept: %s\n", spec->blank->name);
    7778    if (!strcmp (spec->blank->name, "CELL.XPARITY")) {
     
    275276        psMetadataItem *headerItem = NULL; // The value of the concept from the header
    276277
     278
    277279        psTrace ("psModules.concepts", 3, "reading concept: %s\n", name);
    278280        if (!strcmp (name, "CELL.XPARITY")) {
     
    307309            }
    308310        }
    309 
    310311        if (!headerItem) {
    311312            psMetadataItem *formatItem = psMetadataLookup(transSpec, name); // Item with keyword
     
    328329            }
    329330            psString keywords = formatItem->data.str; // The FITS keywords
    330 
    331331            // In case there are multiple headers
    332332            psList *keys = psStringSplit(keywords, " ,;", true); // List of keywords
  • branches/simtest_nebulous_branches/psModules/src/concepts/pmConceptsStandard.c

    r24419 r27840  
    44
    55#include <stdio.h>
     6#include <ctype.h>                      // for tolower()
    67#include <string.h>
    7 #include <strings.h>            /* for strn?casecmp */
     8#include <strings.h>                    // for strn?casecmp
    89#include <assert.h>
    910#include <pslib.h>
     
    160161    assert(concept);
    161162    assert(pattern);
    162 
    163163    double value = NAN;
    164164    switch (concept->type) {
     
    318318            "Unable to find %s in FILTER.ID in camera configuration.\n", key);
    319319    return NULL;
     320}
     321
     322// FPA.OBSTYPE
     323// convert concept->data.str to new value
     324psMetadataItem *p_pmConceptParse_FPA_OBSTYPE(const psMetadataItem *concept,
     325                                            const psMetadataItem *pattern,
     326                                            pmConceptSource source,
     327                                            const psMetadata *cameraFormat,
     328                                            const pmFPA *fpa,
     329                                            const pmChip *chip,
     330                                            const pmCell *cell)
     331{
     332    assert(concept);
     333    assert(pattern);
     334    assert(fpa);
     335    assert(fpa->camera);
     336
     337    if (concept->type != PS_DATA_STRING) {
     338        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not STR\n",
     339                pattern->name, concept->type);
     340        return NULL;
     341    }
     342    if (!concept->data.str || strlen(concept->data.str) == 0) {
     343        return psMetadataItemAllocStr(pattern->name, pattern->comment, "");
     344    }
     345
     346    bool mdok;                          // Status of MD lookup
     347    psMetadata *table = psMetadataLookupMetadata(&mdok, fpa->camera, "OBSTYPE.TABLE");
     348    if (!mdok || !table) {
     349        // if the table is not defined, pass the supplied value unmodified
     350        return psMetadataItemAllocStr(pattern->name, pattern->comment, concept->data.str);
     351    }
     352
     353    // the metadata is in the format (external) STR (internal)
     354    // do a lookup to get the internal name
     355    char *extname = psStringCopy (concept->data.str);
     356    for (int i = 0; i < strlen(extname); i++) {
     357        extname[i] = tolower(extname[i]);
     358    }
     359    char *name = psMetadataLookupStr (&mdok, table, extname);
     360    psFree(extname);
     361    if (!name) {
     362        // if the entry is not defined, pass the supplied value unmodified
     363        return psMetadataItemAllocStr(pattern->name, pattern->comment, concept->data.str);
     364    }
     365
     366    return psMetadataItemAllocStr(pattern->name, pattern->comment, name);
     367}
     368
     369// convert concept->data.str to new value
     370psMetadataItem *p_pmConceptFormat_FPA_OBSTYPE(const psMetadataItem *concept,
     371                                             pmConceptSource source,
     372                                             const psMetadata *cameraFormat,
     373                                             const pmFPA *fpa,
     374                                             const pmChip *chip,
     375                                             const pmCell *cell)
     376{
     377    assert(concept);
     378    assert(fpa);
     379    assert(fpa->camera);
     380
     381    if (concept->type != PS_DATA_STRING) {
     382        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not STR\n",
     383                concept->name, concept->type);
     384        return NULL;
     385    }
     386    if (!concept->data.str || strlen(concept->data.str) == 0) {
     387        return psMetadataItemAllocStr(concept->name, concept->comment, "");
     388    }
     389
     390    bool mdok;                          // Status of MD lookup
     391    psMetadata *table = psMetadataLookupMetadata(&mdok, fpa->camera, "OBSTYPE.TABLE");
     392    if (!mdok || !table) {
     393        // if the table is not defined, pass the supplied value unmodified
     394        return psMetadataItemAllocStr(concept->name, concept->comment, concept->data.str);
     395    }
     396
     397    const char *key = concept->data.str;        // The name to look up
     398    if (!key || strlen(key) == 0) {
     399        return psMetadataItemAllocStr(concept->name, concept->comment, NULL);
     400    }
     401
     402    // the metadata is in the format (internal) STR (external)
     403    // do a reverse lookup to get the internal name
     404    psMetadataIterator *iter = psMetadataIteratorAlloc(table, PS_LIST_HEAD, NULL); // Iterator for filters
     405    psMetadataItem *item;               // Item from iteration
     406    char *name = NULL;                  // The winning name
     407    while ((item = psMetadataGetAndIncrement(iter))) {
     408        if (item->type != PS_DATA_STRING) {
     409            psWarning("Type for %s (%x) in OBSTYPE.TABLE in camera configuration is not STR\n", item->name, item->type);
     410            continue;
     411        }
     412        if (strcmp(item->data.str, key) == 0) {
     413            name = item->name;
     414            break;
     415        }
     416    }
     417    psFree(iter);
     418
     419    if (!name) {
     420        return psMetadataItemAllocStr(concept->name, concept->comment, key);
     421    }
     422    return psMetadataItemAllocStr(concept->name, concept->comment, name);
    320423}
    321424
     
    429532        }
    430533
    431         psString ra = psMetadataLookupStr(&mdok, formats, "FPA.RA"); // Format for RA
    432         psString dec = psMetadataLookupStr(&mdok, formats, "FPA.DEC"); // Format for Dec
    433         if (ra && strcasecmp(ra, "HOURS") == 0 && dec && strcasecmp(dec, "DEGREES") == 0) {
    434             sexagesimal = true;
     534        if (strcmp(concept->name, "FPA.RA") == 0 || strcmp(concept->name, "FPA.DEC") == 0) {
     535            psString ra = psMetadataLookupStr(&mdok, formats, "FPA.RA"); // Format for RA
     536            psString dec = psMetadataLookupStr(&mdok, formats, "FPA.DEC"); // Format for Dec
     537            if (ra && strcasecmp(ra, "HOURS") == 0 && dec && strcasecmp(dec, "DEGREES") == 0) {
     538                sexagesimal = true;
     539            }
    435540        }
    436541    } else {
     
    450555        small = 3600.0 * coords;
    451556        small = (float)((int)(small * 1000.0)) / 1000.0;
    452         if (negative) {
    453             big *= -1;
    454         }
    455557        psString coordString = NULL;        // String with the coordinates in sexagesimal format
    456         psStringAppend(&coordString, "%d:%02d:%06.3f", big, medium, small);
     558        psStringAppend(&coordString, "%s%02d:%02d:%06.3f",
     559                       negative ? "-" : (strcmp(concept->name, "FPA.DEC") == 0 ? "+" : ""),
     560                       big, medium, small);
    457561        coordItem = psMetadataItemAllocStr(concept->name, concept->comment, coordString);
    458562        psFree(coordString);
     
    631735    return psMetadataItemAllocS32(pattern->name, pattern->comment, binning);
    632736}
     737
     738// BTOOLAPP
     739psMetadataItem *p_pmConceptParse_BTOOLAPP(const psMetadataItem *concept,
     740                                          const psMetadataItem *pattern,
     741                                          pmConceptSource source,
     742                                          const psMetadata *cameraFormat,
     743                                          const pmFPA *fpa,
     744                                          const pmChip *chip,
     745                                          const pmCell *cell)
     746{
     747  assert(concept);
     748  assert(pattern);
     749
     750  int bt_status = 0;
     751
     752  if (concept->type != PS_DATA_BOOL) {
     753    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not BOOL\n",
     754            concept->name, concept->type);
     755    if (concept->type != PS_DATA_S32) {
     756      psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Wasn't the type I'd guessed either.\n");
     757      return NULL;
     758    }
     759    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Looks like an S32 value? (%d)\n",
     760            concept->data.S32);
     761
     762    if (concept->data.S32 == 0) {
     763      bt_status = 1;
     764    }
     765    else if (concept->data.S32 == 1) {
     766      bt_status = -2;
     767    }
     768  }
     769
     770  if (concept->data.B == true) {
     771    bt_status = -2;
     772  }
     773  else if (concept->data.B == false) {
     774    bt_status = 1 ;
     775  }
     776 
     777  return psMetadataItemAllocS32(concept->name, concept->comment, bt_status);
     778
     779psMetadataItem *p_pmConceptFormat_BTOOLAPP(const psMetadataItem *concept,
     780                                           pmConceptSource source,
     781                                           const psMetadata *cameraFormat,
     782                                           const pmFPA *fpa,
     783                                           const pmChip *chip,
     784                                           const pmCell *cell)
     785{
     786  assert(concept);
     787
     788  if (concept->type != PS_DATA_S32) {
     789    return NULL;
     790  }
     791
     792  if (concept->data.S32 == 0) {
     793    return NULL;
     794  }
     795  else if (concept->data.S32 == -2) {
     796    return psMetadataItemAllocBool(concept->name,concept->comment,true);
     797  }
     798  else if (concept->data.S32 == 1) {
     799    return psMetadataItemAllocBool(concept->name,concept->comment,false);
     800  }
     801  else {
     802    return NULL;
     803  }
     804
     805
     806
    633807
    634808// Get the current value of a concept
  • branches/simtest_nebulous_branches/psModules/src/concepts/pmConceptsStandard.h

    r22699 r27840  
    7272    );
    7373
     74/// Parse the FPA.OBSTYPE concept to apply a lookup table
     75psMetadataItem *p_pmConceptParse_FPA_OBSTYPE(
     76    const psMetadataItem *concept, ///< Concept to parse
     77    const psMetadataItem *pattern, ///< Pattern to use in parsing
     78    pmConceptSource source, ///< Source for concept
     79    const psMetadata *cameraFormat, ///< Camera format definition
     80    const pmFPA *fpa, ///< FPA for concept, or NULL
     81    const pmChip *chip, ///< Chip for concept, or NULL
     82    const pmCell *cell ///< Cell for concept, or NULL
     83    );
     84
     85/// Format the FPA.OBSTYPE concept to (reverse-)apply a lookup table
     86psMetadataItem *p_pmConceptFormat_FPA_OBSTYPE(
     87    const psMetadataItem *concept, ///< Concept to format
     88    pmConceptSource source, ///< Source for concept
     89    const psMetadata *cameraFormat, ///< Camera format definition
     90    const pmFPA *fpa, ///< FPA for concept, or NULL
     91    const pmChip *chip, ///< Chip for concept, or NULL
     92    const pmCell *cell ///< Cell for concept, or NULL
     93    );
     94
    7495/// Parse the coordinates concepts: FPA.RA and FPA.DEC
    7596psMetadataItem *p_pmConceptParse_FPA_Coords(
     
    114135    const pmCell *cell ///< Cell for concept, or NULL
    115136    );
     137
     138/// Format for the BTOOLAPP conceptn
     139psMetadataItem *p_pmConceptParse_BTOOLAPP(
     140    const psMetadataItem *concept, ///< Concept to format
     141    const psMetadataItem *pattern,
     142    pmConceptSource source, ///< Source for concept
     143    const psMetadata *cameraFormat, ///< Camera format definition
     144    const pmFPA *fpa, ///< FPA for concept, or NULL
     145    const pmChip *chip, ///< Chip for concept, or NULL
     146    const pmCell *cell ///< Cell for concept, or NULL
     147    );
     148psMetadataItem *p_pmConceptFormat_BTOOLAPP(
     149    const psMetadataItem *concept, ///< Concept to format
     150    pmConceptSource source, ///< Source for concept
     151    const psMetadata *cameraFormat, ///< Camera format definition
     152    const pmFPA *fpa, ///< FPA for concept, or NULL
     153    const pmChip *chip, ///< Chip for concept, or NULL
     154    const pmCell *cell ///< Cell for concept, or NULL
     155    );
     156
    116157
    117158/// Parse the cell binning concepts: CELL.XBIN, CELL.YBIN
  • branches/simtest_nebulous_branches/psModules/src/concepts/pmConceptsWrite.c

    r23623 r27840  
    157157    }
    158158    switch (item->type) {
    159     case PS_DATA_STRING:
     159      case PS_DATA_BOOL:
     160        psTrace("psModules.concepts", 9, "Writing header %s: %d\n", keyword, item->data.B);
     161        return psMetadataAddBool(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
     162                                 item->data.B);
     163      case PS_DATA_STRING:
    160164        psTrace("psModules.concepts", 9, "Writing header %s: %s\n", keyword, item->data.str);
    161165        return psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
    162166                                item->data.V);
    163     case PS_DATA_S32:
     167      case PS_DATA_S32:
    164168        psTrace("psModules.concepts", 9, "Writing header %s: %d\n", keyword, item->data.S32);
    165169        return psMetadataAddS32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
    166170                                item->data.S32);
    167     case PS_DATA_F32:
     171      case PS_DATA_F32:
    168172        psTrace("psModules.concepts", 9, "Writing header %s: %f\n", keyword, item->data.F32);
    169173        return psMetadataAddF32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
    170174                                item->data.F32);
    171     case PS_DATA_F64:
     175      case PS_DATA_F64:
    172176        psTrace("psModules.concepts", 9, "Writing header %s: %f\n", keyword, item->data.F64);
    173177        return psMetadataAddF64(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
    174178                                item->data.F64);
    175     case PS_DATA_REGION: {
    176             psString region = psRegionToString(*(psRegion*)item->data.V);
    177             psTrace("psModules.concepts", 9, "Writing header %s: %s\n", keyword, region);
    178             bool result = psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
    179                                            region);
    180             psFree(region);
    181             return result;
    182         }
    183     default:
    184       psWarning("Type of %s is not suitable for a FITS header --- not added.\n",
    185                  item->name);
     179      case PS_DATA_REGION: {
     180          psString region = psRegionToString(*(psRegion*)item->data.V);
     181          psTrace("psModules.concepts", 9, "Writing header %s: %s\n", keyword, region);
     182          bool result = psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
     183                                         region);
     184          psFree(region);
     185          return result;
     186      }
     187      default:
     188        psWarning("Type of %s is not suitable for a FITS header --- not added.\n",
     189                  item->name);
    186190        return false;
    187191    }
  • branches/simtest_nebulous_branches/psModules/src/config/Makefile.am

    r23794 r27840  
    3232        pmConfigDump.c \
    3333        pmConfigRun.c \
     34        pmConfigRecipeValue.c \
    3435        pmVersion.c \
    3536        pmErrorCodes.c
     
    4344        pmConfigDump.h \
    4445        pmConfigRun.h \
     46        pmConfigRecipeValue.h \
    4547        pmVersion.h \
    4648        pmErrorCodes.h
  • branches/simtest_nebulous_branches/psModules/src/config/pmConfig.c

    r24496 r27840  
    4343#define DEFAULT_TRACE STDERR_FILENO     // Default file descriptor for trace messages
    4444
     45#define CHECK_FILE_RETRY 5              // Number of retries when checking a file
     46#define CHECK_FILE_WAIT 250000          // Wait between retries (usec) when checking a file
     47
    4548static bool readCameraConfig = true;    // Read the camera config on startup (with pmConfigRead)?
    4649static psArray *configPath = NULL;      // Search path for configuration files
    4750
    4851static bool checkPath(const char *filename, bool create, bool trunc);
     52static psString resolveConfigFile(const char *name);
    4953
    5054bool pmConfigReadParamsSet(bool newReadCameraConfig)
     
    174178        char *envName = envStart + 1;   // Start of the environment variable name
    175179        if (envName[0] == '\0') {
    176             psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Path %s contains a bad environment variable.\n", dir);
     180            psError(PM_ERR_CONFIG, true, "Path %s contains a bad environment variable.\n", dir);
    177181            return NULL;
    178182        }
     
    180184            envName++;
    181185            if (envName[0] == '\0') {
    182                 psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     186                psError(PM_ERR_CONFIG, true,
    183187                        "Path %s contains a bad environment variable.\n", dir);
    184188                return NULL;
     
    272276
    273277    if (configPath == NULL) {
    274         psError(PS_ERR_IO, true, "Cannot find %s configuration file (%s) in path\n", description, name);
     278        psError(PM_ERR_CONFIG, true, "Cannot find %s configuration file (%s) in path\n", description, name);
    275279        return false;
    276280    }
     
    296300    }
    297301
    298     psError(PS_ERR_IO, true, "Cannot find %s configuration file %s in path\n", description, name);
     302    psError(PM_ERR_CONFIG, true, "Cannot find %s configuration file %s in path\n", description, name);
    299303    return false;
    300304
     
    302306    *config = psMetadataConfigRead(NULL, &numBadLines, realName, true);
    303307    if (numBadLines > 0) {
    304         psError(PS_ERR_IO, false, "%d bad lines in %s configuration file (%s)",
     308        psError(PM_ERR_CONFIG, false, "%d bad lines in %s configuration file (%s)",
    305309                numBadLines, description, realName);
    306310        psFree (realName);
     
    309313    }
    310314    if (!*config) {
    311         psError(PS_ERR_IO, true, "Unable to read %s configuration from %s",
     315        psError(PM_ERR_CONFIG, true, "Unable to read %s configuration from %s",
    312316                description, realName);
    313317        psFree (realName);
     
    328332    }
    329333    if (item->type != PS_DATA_STRING) {
    330         psTrace("config", 2, "Element %s in %s metadata is not of type STR.\n",
     334        psError(PM_ERR_CONFIG, true, "Element %s in %s metadata is not of type STR.\n",
    331335                item->name, description);
    332336        return false;
     
    336340    psMetadata *new = NULL;         // New metadata
    337341    if (!pmConfigFileRead(&new, item->data.str, item->name)) {
    338         psError(PM_ERR_CONFIG, false, "Trouble reading reading %s %s.\n",
     342        psError(psErrorCodeLast(), false, "Trouble reading reading %s %s.\n",
    339343                description, item->name);
    340344        psFree(new);
     
    361365    while ((item = psMetadataGetAndIncrement(iter))) {
    362366        if (!pmConfigFileIngest(item, description)) {
    363             psError(PM_ERR_CONFIG, false, "Unable to read %s %s.", description, item->name);
     367            psError(psErrorCodeLast(), false, "Unable to read %s %s.", description, item->name);
    364368            psFree(iter);
    365369            return false;
     
    382386    psMetadata *formats = psMetadataLookupMetadata(&mdok, camera, "FORMATS"); // Formats
    383387    if (!mdok || !formats) {
    384         psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find FORMATS in camera configuration %s.\n", name);
     388        psError(PM_ERR_CONFIG, true, "Unable to find FORMATS in camera configuration %s.\n", name);
    385389        return false;
    386390    }
    387391    if (!metadataReadFiles(formats, "camera format")) {
    388         psError(PS_ERR_UNKNOWN, false, "Unable to read formats within camera configuration %s.\n", name);
     392        psError(psErrorCodeLast(), false, "Unable to read formats within camera configuration %s.\n", name);
    389393        return false;
    390394    }
     
    447451            psWarning("-ipprc command-line switch provided without the required filename --- ignored.\n");
    448452        } else {
    449             configFile = psStringCopy(argv[argNum]);
     453            configFile = resolveConfigFile(argv[argNum]);
    450454            psArgumentRemove(argNum, argc, argv);
    451455        }
     
    486490    psMetadataItem *siteItem = psMetadataLookup(config->user, "SITE");
    487491    if (!siteItem) {
    488         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find SITE in user configuration.");
     492        psError(PM_ERR_CONFIG, true, "Unable to find SITE in user configuration.");
    489493        psFree(config);
    490494        return NULL;
    491495    }
    492496    if (!pmConfigFileIngest(siteItem, "site configuration")) {
    493         psError(PS_ERR_UNKNOWN, false, "Unable to read site configuration");
     497        psError(psErrorCodeLast(), false, "Unable to read site configuration");
    494498        psFree(config);
    495499        return NULL;
     
    500504    psMetadataItem *systemItem = psMetadataLookup(config->user, "SYSTEM");
    501505    if (!systemItem) {
    502         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find SYSTEM in user configuration.");
     506        psError(PM_ERR_CONFIG, true, "Unable to find SYSTEM in user configuration.");
    503507        psFree(config);
    504508        return NULL;
    505509    }
    506510    if (!pmConfigFileIngest(systemItem, "system configuration")) {
    507         psError(PS_ERR_UNKNOWN, false, "Unable to read system configuration");
     511        psError(psErrorCodeLast(), false, "Unable to read system configuration");
    508512        psFree(config);
    509513        return NULL;
     
    551555            psString resolved = pmConfigConvertFilename(logDest, config, true, false); // Resolved filename
    552556            if (!resolved || strlen(resolved) == 0) {
    553                 psWarning("Unable to resolve log destination: %s --- ignored", logDest);
    554             } else {
    555                 pmConfigRunFilenameAddWrite(config, "LOG", logDest);
    556                 config->logFD = psMessageDestination(resolved);
    557             }
     557                psError(psErrorCodeLast(), false, "Unable to resolve log destination: %s", logDest);
     558                psFree(logDest);
     559                return NULL;
     560            }
     561            pmConfigRunFilenameAddWrite(config, "LOG", logDest);
     562            config->logFD = psMessageDestination(resolved);
    558563            psFree(resolved);
    559564            psFree(logDest);
     
    610615            psString resolved = pmConfigConvertFilename(traceDest, config, true, false); // Resolved filename
    611616            if (!resolved || strlen(resolved) == 0) {
    612                 psWarning("Unable to resolve trace destination: %s --- ignored", traceDest);
    613             } else {
    614                 pmConfigRunFilenameAddWrite(config, "TRACE", traceDest);
    615                 config->traceFD = psMessageDestination(resolved);
    616             }
     617                psError(psErrorCodeLast(), false, "Unable to resolve trace destination: %s", traceDest);
     618                psFree(traceDest);
     619                return NULL;
     620            }
     621            pmConfigRunFilenameAddWrite(config, "TRACE", traceDest);
     622            config->traceFD = psMessageDestination(resolved);
    617623            psFree(resolved);
    618624            psFree(traceDest);
     
    659665                seed = strtoll(argv[argNum], &end, 0);
    660666                if (strlen(end) > 0) {
    661                     psError(PS_ERR_IO, true, "Unable to read random number generator seed: %s", argv[argNum]);
     667                    psError(PM_ERR_CONFIG, true, "Unable to read random number generator seed: %s",
     668                            argv[argNum]);
    662669                    psFree(config);
    663670                    return NULL;
     
    684691            psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->system, "CAMERAS");
    685692            if (!cameras) {
    686                 psError(PS_ERR_IO, false, "Unable to find CAMERAS in site configuration.\n");
     693                psError(PM_ERR_CONFIG, false, "Unable to find CAMERAS in site configuration.\n");
    687694                psFree(config);
    688695                return NULL;
     
    692699            char *cameraFile = psMetadataLookupStr(&mdok, cameras, cameraName); // The filename
    693700            if (!cameraFile) {
    694                 psError(PS_ERR_IO, false, "%s is not listed in the site CAMERAS list\n", cameraName);
     701                psError(PM_ERR_CONFIG, false, "%s is not listed in the site CAMERAS list\n", cameraName);
    695702                psFree(config);
    696703                return NULL;
     
    699706            // load this camera's configuration informatoin
    700707            if (!pmConfigFileRead(&config->camera, cameraFile, "camera")) {
    701                 psError(PM_ERR_CONFIG, false, "Problem reading %s", cameraName);
     708                psError(psErrorCodeLast(), false, "Problem reading %s", cameraName);
    702709                psFree(config);
    703710                return NULL;
     
    710717            // Read in the formats
    711718            if (!cameraReadFormats(config->camera, cameraFile)) {
    712                 psError(PS_ERR_UNKNOWN, false, "Unable to read formats within camera configuration %s.\n",
     719                psError(psErrorCodeLast(), false, "Unable to read formats within camera configuration %s.\n",
    713720                        cameraFile);
    714721                psFree(config);
     
    718725            // Read in any camera-specific calibrations
    719726            if (!cameraReadCalibrations(config->camera, cameraName)) {
    720                 psError(PS_ERR_UNKNOWN, false,
     727                psError(psErrorCodeLast(), false,
    721728                        "Unable to read calibrations within camera configuration %s.\n",
    722729                        cameraName);
     
    729736
    730737            if (!pmConfigCameraSkycellVersion(config->system, cameraName)) {
    731                 psError(PS_ERR_UNKNOWN, false,
     738                psError(psErrorCodeLast(), false,
    732739                        "Unable to generate skycell versions of specified camera %s.\n",
    733740                        cameraName);
     
    737744
    738745            if (!pmConfigCameraMosaickedVersions(config->system, cameraName)) {
    739                 psError(PS_ERR_UNKNOWN, false,
     746                psError(psErrorCodeLast(), false,
    740747                        "Unable to generate mosaicked versions of specified camera %s.\n",
    741748                        cameraName);
     
    751758        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->system, "CAMERAS"); // List of cameras
    752759        if (!mdok || !cameras) {
    753             psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the system configuration.\n");
     760            psError(PM_ERR_CONFIG, true, "Unable to find CAMERAS in the system configuration.\n");
    754761            return false;
    755762        }
    756763
    757764        if (!metadataReadFiles(cameras, "camera configuration")) {
    758             psError(PS_ERR_UNKNOWN, false, "Unable to read cameras within system configuration.\n");
     765            psError(psErrorCodeLast(), false, "Unable to read cameras within system configuration.\n");
    759766            psFree(config);
    760767            return NULL;
     
    784791
    785792        if (!pmConfigCameraSkycellVersionsAll(config->system)) {
    786             psError(PS_ERR_UNKNOWN, false, "Unable to generate skycell versions of cameras.\n");
     793            psError(psErrorCodeLast(), false, "Unable to generate skycell versions of cameras.\n");
    787794            psFree(config);
    788795            return NULL;
    789796        }
    790797        if (!pmConfigCameraMosaickedVersionsAll(config->system)) {
    791             psError(PS_ERR_UNKNOWN, false, "Unable to generate mosaicked versions of cameras.\n");
     798            psError(psErrorCodeLast(), false, "Unable to generate mosaicked versions of cameras.\n");
    792799            psFree(config);
    793800            return NULL;
     
    797804    // Load the recipes from the camera file, if appropriate
    798805    if(!pmConfigReadRecipes(config, PM_RECIPE_SOURCE_SYSTEM | PM_RECIPE_SOURCE_CAMERA)) {
    799         psError(PS_ERR_IO, false, "Failed to read recipes from camera file");
     806        psError(psErrorCodeLast(), false, "Failed to read recipes from camera file");
    800807        psFree(config);
    801808        return NULL;
     
    812819
    813820    if (!pmConfigReadRecipes(config, PM_RECIPE_SOURCE_CL)) {
    814         psError(PS_ERR_IO, false, "Failed to read recipes from command-line");
     821        psError(psErrorCodeLast(), false, "Failed to read recipes from command-line");
    815822        psFree(config);
    816823        return NULL;
     
    821828        psArgumentRemove(argNum, argc, argv);
    822829        if (argNum + 1 >= *argc) {
    823             psError(PS_ERR_BAD_PARAMETER_SIZE, true,
     830            psError(PM_ERR_CONFIG, true,
    824831                    "Filerule switch (-F) provided without old and new filerule.");
    825832            psFree(config);
     
    834841        psMetadata *cameras = psMetadataLookupMetadata(NULL, config->system, "CAMERAS"); // List of cameras
    835842        if (!cameras) {
    836             psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find CAMERAS in the site configuration.\n");
     843            psError(PM_ERR_CONFIG, false, "Unable to find CAMERAS in the site configuration.\n");
    837844            return false;
    838845        }
     
    9951002    psMetadata *rule = psMetadataLookupMetadata(&mdStatus, cameraFormat, "RULE");
    9961003    if (! mdStatus || ! rule) {
    997         psError(PS_ERR_UNKNOWN, false, "Unable to read rule for camera.");
     1004        psError(PM_ERR_CONFIG, false, "Unable to read rule for camera.");
    9981005        *valid = false;
    9991006        return false;
     
    10041011    psArray *keys = psListToArray (keyList);
    10051012    if (! keys) {
    1006         psError(PS_ERR_UNKNOWN, false, "Unable to read rule for camera.");
     1013        psError(PM_ERR_CONFIG, false, "Unable to read rule for camera.");
    10071014        *valid = false;
    10081015        return false;
     
    10531060        }
    10541061
    1055         psError(PS_ERR_UNKNOWN, false, "Invalid type for RULE %s.", ruleItem->name);
     1062        psError(PM_ERR_CONFIG, false, "Invalid type for RULE %s.", ruleItem->name);
    10561063        *valid = false;
    10571064        psFree (keyList);
     
    10881095    psMetadata *formats = psMetadataLookupMetadata(&mdok, camera, "FORMATS"); // List of formats
    10891096    if (!mdok || !formats) {
    1090         psError(PS_ERR_UNKNOWN, false, "Unable to find list of FORMATS in camera %s", cameraName);
     1097        psError(PM_ERR_CONFIG, false, "Unable to find list of FORMATS in camera %s", cameraName);
    10911098        *status = false;
    10921099        return false;
     
    10941101
    10951102    if (!metadataReadFiles(formats, "camera format")) {
    1096         psError(PS_ERR_UNKNOWN, false, "Unable to read cameras formats within camera configuration.\n");
     1103        psError(psErrorCodeLast(), false, "Unable to read cameras formats within camera configuration.\n");
    10971104        *status = false;
    10981105        return false;
     
    11101117        bool valid = false;
    11111118        if (!pmConfigValidateCameraFormat(&valid, testFormat, header)) {
    1112             psError (PS_ERR_UNKNOWN, false, "Error in config scripts for camera %s, format %s\n",
     1119            psError (psErrorCodeLast(), false, "Error in config scripts for camera %s, format %s\n",
    11131120                     cameraName, formatsItem->name);
    11141121            *status = false;
     
    11561163        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->system, "CAMERAS");
    11571164        if (! mdok || !cameras) {
    1158             psError(PS_ERR_IO, true, "Unable to find CAMERAS in the configuration.");
     1165            psError(PM_ERR_CONFIG, true, "Unable to find CAMERAS in the configuration.");
    11591166            return NULL;
    11601167        }
    11611168
    11621169        if (!metadataReadFiles(cameras, "camera configuration")) {
    1163             psError(PS_ERR_UNKNOWN, false, "Unable to read cameras within site configuration.\n");
     1170            psError(psErrorCodeLast(), false, "Unable to read cameras within site configuration.\n");
    11641171            return NULL;
    11651172        }
     
    11901197            } else {
    11911198                if (!status) {
    1192                     psError(PS_ERR_IO, false, "Error reading camera config data for %s", camerasItem->name);
     1199                    psError(psErrorCodeLast(), false, "Error reading camera config data for %s",
     1200                            camerasItem->name);
    11931201                    return NULL;
    11941202                }
     
    11991207        // Done looking at all cameras
    12001208        if (!config->camera) {
    1201             psError(PS_ERR_IO, false, "Unable to find a camera that matches input FITS header!");
     1209            psError(PM_ERR_CONFIG, true, "Unable to find a camera that matches input FITS header!");
    12021210            return NULL;
    12031211        }
     
    12051213        // Now we have the camera, we can read the recipes
    12061214        if (readRecipes && !pmConfigReadRecipes(config, PM_RECIPE_SOURCE_CAMERA | PM_RECIPE_SOURCE_CL)) {
    1207             psError(PS_ERR_IO, false, "Error reading recipes from camera config for %s", config->cameraName);
     1215            psError(psErrorCodeLast(), false, "Error reading recipes from camera config for %s",
     1216                    config->cameraName);
    12081217            return NULL;
    12091218        }
     
    12811290
    12821291    if (!found) {
    1283         psError(PS_ERR_IO, true, "Unable to find a format with the specified camera (%s) that matches the "
    1284                 "given header.\n", baseName);
     1292        psError(PM_ERR_CONFIG, true,
     1293                "Unable to find a format with the specified camera (%s) that matches the given header.",
     1294                baseName);
    12851295        psFree (baseName);
    12861296        return NULL;
     
    13131323    psMetadata *cameras = psMetadataLookupMetadata(NULL, config->system, "CAMERAS");
    13141324    if (!cameras) {
    1315         psError(PS_ERR_IO, true, "Unable to find CAMERAS in the configuration.");
     1325        psError(PM_ERR_CONFIG, true, "Unable to find CAMERAS in the configuration.");
    13161326        return NULL;
    13171327    }
     
    13191329    psMetadataItem *item = psMetadataLookup(cameras, cameraName); // Item with camera of interest
    13201330    if (!pmConfigFileIngest(item, "camera configuration")) {
    1321         psError(PS_ERR_UNKNOWN, false, "Unable to ingest camera configuration.");
     1331        psError(psErrorCodeLast(), false, "Unable to ingest camera configuration.");
    13221332        return NULL;
    13231333    }
     
    13351345        item = psMetadataLookup(config->site, name);
    13361346        if (!item) {
    1337             psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     1347            psError(PM_ERR_CONFIG, true,
    13381348                    "Unable to find %s in user or site configuration", name);
    13391349            return NULL;
     
    13411351    }
    13421352    if (item->type != type) {
    1343         psError(PS_ERR_BAD_PARAMETER_TYPE, true,
     1353        psError(PM_ERR_CONFIG, true,
    13441354                "Type of %s (%x) in user/site configuration does not match expected (%x)",
    13451355                name, item->type, type);
     
    13581368#ifndef HAVE_PSDB
    13591369
    1360     psError(PS_ERR_UNKNOWN, false,
     1370    psError(PM_ERR_PROG, false,
    13611371            "Cannot configure database: psModules was compiled without database support.");
    13621372    return NULL;
     
    14071417    psMetadata *rules = psMetadataLookupMetadata(&mdok, format, "RULE"); // How to identify this format
    14081418    if (!mdok || !rules) {
    1409         psError(PS_ERR_IO, true, "Unable to find RULE in camera format.\n");
     1419        psError(PM_ERR_CONFIG, true, "Unable to find RULE in camera format.\n");
    14101420        return false;
    14111421    }
     
    14151425    while ((rulesItem = psMetadataGetAndIncrement(rulesIter))) {
    14161426        if (!PS_DATA_IS_PRIMITIVE(rulesItem->type) && rulesItem->type != PS_DATA_STRING) {
    1417             psError(PS_ERR_UNKNOWN, false, "Invalid type for RULE %s.", rulesItem->name);
     1427            psError(PM_ERR_CONFIG, false, "Invalid type for RULE %s.", rulesItem->name);
    14181428            return false;
    14191429        }
     
    14471457          case PS_METADATA_ITEM_COMPARE_OP_NE:
    14481458            // It's not at all obvious what the value should be, so return an error.
    1449             psError(PS_ERR_IO, true,
     1459            psError(PM_ERR_CONFIG, true,
    14501460                    "RULE %s (defined by an OPeration) is not present or not consistent in output header",
    14511461                    rulesItem->name);
     
    15301540              default:
    15311541                // rigid format, no comments allowed?
    1532                 psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to parse file list: spaces detected.");
     1542                psError(PM_ERR_CONFIG, true, "Unable to parse file list: spaces detected.");
    15331543                psFree(input);
    15341544                fclose(f);
     
    15631573    if (!files) {
    15641574        psAbort("error parsing argument list");
    1565         psError(PS_ERR_IO, false, "error parsing argument list");
     1575        psError(psErrorCodeLast(), false, "error parsing argument list");
    15661576        psFree (files);
    15671577        return false;
     
    15901600
    15911601        char *point = newName + strlen("file:");
    1592         while (*point == '/')
     1602        while (*point == '/') {
    15931603            point ++;
     1604        }
    15941605        char *tmpName = NULL;
    15951606        psStringAppend (&tmpName, "/%s", point);
     
    15991610        if (!checkPath(newName, create, trunc)) {
    16001611            // let checkPath()'s psError() call float up
    1601             psError(PS_ERR_UNKNOWN, false, "error from checkPath for file:// (%s)", newName);
     1612            psError(psErrorCodeLast(), false, "error from checkPath for file:// (%s)", newName);
    16021613            psFree (newName);
    16031614            return NULL;
     
    16171628        psMetadata *datapath = psMetadataLookupPtr (NULL, config->site, "DATAPATH");
    16181629        if (datapath == NULL) {
    1619             psError(PS_ERR_UNKNOWN, true, "DATAPATH is not defined in config.site");
     1630            psError(PM_ERR_CONFIG, true, "DATAPATH is not defined in config.site");
    16201631            psFree (newName);
    16211632            return NULL;
     
    16251636        char *mark = strchr (point, '/');
    16261637        if (mark == NULL) {
    1627             psError(PS_ERR_UNKNOWN, true, "syntax error in PATH-style name %s", newName);
     1638            psError(PM_ERR_CONFIG, true, "syntax error in PATH-style name %s", newName);
    16281639            psFree (newName);
    16291640            return false;
     
    16331644        char *realpath = psMetadataLookupStr (NULL, datapath, path);
    16341645        if (realpath == NULL) {
    1635             psError(PS_ERR_UNKNOWN, true,
     1646            psError(PM_ERR_CONFIG, true,
    16361647                    "path (%s) not defined in config.site:DATAPATH for PATH-style name %s",
    16371648                    path, newName);
     
    16491660        if (!checkPath(newName, create, trunc)) {
    16501661            // let checkPath()'s psError() call float up
    1651             psError(PS_ERR_UNKNOWN, false, "error from checkPath for path:// (%s)", newName);
     1662            psError(psErrorCodeLast(), false, "error from checkPath for path:// (%s)", newName);
    16521663            psFree (newName);
    16531664            return NULL;
     
    17111722        nebServerFree(server);
    17121723
    1713         if (trunc) {
    1714             if(truncate(path, 0) != 0) {
    1715                 psError(PS_ERR_IO, true, "Failed to truncate Nebulous file %s (real name %s)\n",
    1716                         filename, path);
    1717                 return NULL;
    1718             }
     1724        // Check to ensure it's there.  Will create the file if Nebulous failed to do so.
     1725        if (!checkPath(path, create, trunc)) {
     1726            psError(psErrorCodeLast(), false, "Cannot find file %s", path);
     1727            psFree(path);
     1728            return NULL;
    17191729        }
    17201730
     
    17411751    psMetadataItem *item = psMetadataLookup(camera, "FILERULES"); // Item with the file rule of interest
    17421752    if (!item) {
    1743         psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find FILERULES in the camera configuration.");
     1753        psError(PM_ERR_CONFIG, false, "Unable to find FILERULES in the camera configuration.");
    17441754        return NULL;
    17451755    }
     
    17741784    psMetadataItem *item = psMetadataLookup(camera, "FITSTYPES"); // Item with the file rule of interest
    17751785    if (!item) {
    1776         psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find FITSTYPES in the camera configuration.");
     1786        psError(PM_ERR_CONFIG, false, "Unable to find FITSTYPES in the camera configuration.");
    17771787        return NULL;
    17781788    }
     
    18011811
    18021812    // re-try access up to 5 times (1.25sec) to reduce NFS lurches
    1803     for (int i = 0; i < 5; i++) {
     1813    for (int i = 0; i < CHECK_FILE_RETRY; i++) {
    18041814        if (access(filename, R_OK) == 0) {
    18051815            // file already exists
     
    18261836            return true;
    18271837        }
    1828         usleep (250000);
     1838        usleep(CHECK_FILE_WAIT);
    18291839    }
    18301840
     
    18341844    return false;
    18351845}
     1846
     1847static psString resolveConfigFile(const char *nameArg)
     1848{
     1849    // if config file name is nebulous path resolve it
     1850    // otherwise just return a copy of the argument
     1851    if (strncasecmp(nameArg, "neb://", strlen("neb://"))) {
     1852        return psStringCopy(nameArg);
     1853    }
     1854
     1855#ifdef HAVE_NEBCLIENT
     1856    char *neb_server = getenv("NEB_SERVER");
     1857
     1858    // if env isn't set, check the config system
     1859    if (!neb_server) {
     1860        psError(PM_ERR_CONFIG, true, "NEB_SERVER environment variable must be set in order to resolve config file.");
     1861            return NULL;
     1862    }
     1863
     1864    nebServer *server = nebServerAlloc(neb_server);
     1865    if (!server) {
     1866        psError(PM_ERR_SYS, true, "failed to create a nebServer object.");
     1867        return NULL;
     1868    }
     1869
     1870    char *nebfile = nebFind(server, nameArg);
     1871    nebServerFree(server);
     1872    if (!nebfile) {
     1873        // object does not exist
     1874        psError(PM_ERR_SYS, true, "failed to resolve nebulous path: %s.", nameArg);
     1875        return NULL;
     1876    }
     1877    // XXX: do I need to free nebfile?
     1878
     1879    return psStringCopy(nebfile);
     1880#else
     1881    psError(PM_ERR_PROG, true, "psModules was compiled without nebulous support.");
     1882    return NULL;
     1883#endif // ifdef HAVE_NEBCLIENT
     1884}
  • branches/simtest_nebulous_branches/psModules/src/config/pmConfigCamera.c

    r23452 r27840  
    149149    camerasIter = psMetadataIteratorAlloc(new, PS_LIST_HEAD, NULL); // Iterator
    150150    while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
    151         psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, 0);
     151        psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, PS_META_REPLACE);
    152152    }
    153153    psFree(camerasIter);
     
    210210
    211211    // See if the new one is already there
    212     psString newName = NULL;       // Name of skycelled camera
    213     psStringAppend(&newName, "_%s-SKYCELL", name);
    214     if (psMetadataLookup(oldCameras, newName)) {
     212    psString newName = pmConfigCameraSkycellName(name); // Name of skycelled camera
     213    bool mdok;                       // Status of MD lookup
     214    psMetadata *oldCam = psMetadataLookupMetadata(&mdok, oldCameras, newName); // Existing camera configuration
     215    if (mdok && oldCam) {
     216        // Ensure new camera goes to the head of the metadata, so that it will be recognised first
     217        // The old camera doesn't contain the PSMOSAIC header, so it will match anything!
     218        psTrace("psModules.config", 6, "Camera configuration for %s exists, so moving to the front.", newName);
     219        psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE, NULL, oldCam);
    215220        psFree(newName);
    216221        return true;
     
    366371    // New camera MUST go to the head of the metadata, so that it will be recognised first
    367372    // The old camera doesn't contain the PSCAMERA and PSFORMAT headers, so it will match anything!
     373    psTrace("psModules.config", 6, "Generated new camera configuration for %s.", newName);
    368374    psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE,
    369375                          "Automatically generated", new);
     
    436442    camerasIter = psMetadataIteratorAlloc(new, PS_LIST_HEAD, NULL); // Iterator
    437443    while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
    438         psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, 0);
     444        psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, PS_META_REPLACE);
    439445    }
    440446    psFree(camerasIter);
     
    469475
    470476    // See if the new one is already there
    471     psString newName = NULL;       // Name of mosaicked camera
    472     psStringAppend(&newName, "_%s-%s", name, mosaicLevel == PM_FPA_LEVEL_CHIP ? "CHIP" : "FPA");
    473     if (psMetadataLookup(oldCameras, newName)) {
     477    psString newName = mosaicLevel == PM_FPA_LEVEL_CHIP ? pmConfigCameraChipName(name) :
     478        pmConfigCameraFPAName(name); // Name of mosaicked camera
     479    bool mdok;                       // Status of MD lookup
     480    psMetadata *oldCam = psMetadataLookupMetadata(&mdok, oldCameras, newName); // Existing camera configuration
     481    if (mdok && oldCam) {
     482        // Ensure new camera goes to the head of the metadata, so that it will be recognised first
     483        // The old camera doesn't contain the PSMOSAIC header, so it will match anything!
     484        psTrace("psModules.config", 6, "Camera configuration for %s exists, so moving to the front.", newName);
     485        psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE, NULL, oldCam);
    474486        psFree(newName);
    475487        return true;
     
    477489
    478490    psMetadata *new = psMetadataCopy(NULL, camera); // Copy of the camera description
    479     bool mdok;                          // Status of MD lookups
    480491
    481492    // ** Fix up the contents of the FPA description to match the mosaicked camera **
     
    849860    // New camera MUST go to the head of the metadata, so that it will be recognised first
    850861    // The old camera doesn't contain the PSMOSAIC header, so it will match anything!
     862    psTrace("psModules.config", 6, "Generated new camera configuration for %s.", newName);
    851863    psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE,
    852864                          "Automatically generated", new);
  • branches/simtest_nebulous_branches/psModules/src/config/pmConfigDump.c

    r23753 r27840  
    1414#include "pmFPAfile.h"
    1515#include "pmConfigCamera.h"
     16#include "pmErrorCodes.h"
    1617
    1718#include "pmConfigDump.h"
     
    5556
    5657    if (!configCull(config->recipes, keep)) {
    57         psError(PS_ERR_UNKNOWN, false, "Unable to cull system recipes.");
     58        psError(psErrorCodeLast(), false, "Unable to cull system recipes.");
    5859        psFree(keep);
    5960        return false;
     
    6465    psMetadata *cameras = psMetadataLookupMetadata(NULL, config->system, "CAMERAS"); // Known cameras
    6566    if (!cameras) {
    66         psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find CAMERAS in the system configuration.\n");
     67        psError(PM_ERR_CONFIG, false, "Unable to find CAMERAS in the system configuration.\n");
    6768        return false;
    6869    }
     
    8081        }
    8182        if (!configCull(recipes, keep)) {
    82             psError(PS_ERR_UNKNOWN, false, "Unable to cull recipes for camera %s", item->name);
     83            psError(psErrorCodeLast(), false, "Unable to cull recipes for camera %s", item->name);
    8384            psFree(iter);
    8485            psFree(keep);
     
    9899      psMetadata *cameras = psMetadataLookupMetadata(NULL, config->system, "CAMERAS"); // Known cameras
    99100      if (!cameras) {
    100           psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find CAMERAS in the system configuration.\n");
     101          psError(PM_ERR_CONFIG, false, "Unable to find CAMERAS in the system configuration.\n");
    101102          return NULL;
    102103      }
     
    144145
    145146    psString resolved = pmConfigConvertFilename(filename, config, true, false); // Resolved filename
     147    if (!resolved) {
     148        psError(psErrorCodeLast(), false, "Unable to create file for configuration dump: %s", filename);
     149        return false;
     150    }
    146151
    147152    if (!psMetadataConfigWrite(config->user, resolved)) {
    148         psError(PS_ERR_IO, false, "Unable to dump configuration to %s", filename);
     153        psError(psErrorCodeLast(), false, "Unable to dump configuration to %s", filename);
    149154        psFree(resolved);
    150155        return false;
  • branches/simtest_nebulous_branches/psModules/src/config/pmConfigMask.c

    r23498 r27840  
    1616    { "BLANK",     "DETECTOR", 0x01, true  }, // Pixel doesn't contain valid data
    1717    { "CTE",       "DETECTOR", 0x01, true  }, // Pixel has poor CTE
     18    { "BURNTOOL",  NULL,       0x04, false }, // Pixel has been touched by burntool
    1819    // Invalid signal ranges
    1920    { "SAT",       NULL,       0x02, true  }, // Pixel is saturated or non-linear
     
    2223    // Non-astronomical structures
    2324    { "CR",        NULL,       0x08, true  }, // Pixel contains a cosmic ray
    24     { "SPIKE",     NULL,       0x08, true  }, // Pixel contains a diffraction spike
    25     { "GHOST",     NULL,       0x08, true  }, // Pixel contains an optical ghost
    26     { "STREAK",    NULL,       0x08, true  }, // Pixel contains streak data
    27     { "STARCORE",  NULL,       0x08, true  }, // Pixel contains a bright star core
     25    { "SPIKE",     NULL,       0x08, false  }, // Pixel contains a diffraction spike
     26    { "GHOST",     NULL,       0x08, false  }, // Pixel contains an optical ghost
     27    { "STREAK",    NULL,       0x08, false  }, // Pixel contains streak data
     28    { "STARCORE",  NULL,       0x08, false  }, // Pixel contains a bright star core
    2829    // Effects of convolution and interpolation
    2930    { "CONV.BAD",  NULL,       0x02, true  }, // Pixel is bad after convolution with a bad pixel
  • branches/simtest_nebulous_branches/psModules/src/config/pmConfigRun.c

    r23748 r27840  
    160160    psArray *files = configRunFileGet(config, name, "FILES.INPUT"); // Files from RUN metadata
    161161    if (!files) {
    162         configRunFileGet(config, name, "FILES.OUTPUT");
     162        files = configRunFileGet(config, name, "FILES.OUTPUT");
    163163    }
    164164
  • branches/simtest_nebulous_branches/psModules/src/detrend/Makefile.am

    r24891 r27840  
    1717        pmDark.c \
    1818        pmRemnance.c \
    19         pmPattern.c
     19        pmPattern.c \
     20        pmPatternIO.c
    2021
    2122#       pmSkySubtract.c
     
    3536        pmDark.h \
    3637        pmRemnance.h \
    37         pmPattern.h
     38        pmPattern.h \
     39        pmPatternIO.h
    3840
    3941#       pmSkySubtract.h
  • branches/simtest_nebulous_branches/psModules/src/detrend/pmDark.c

    r24869 r27840  
    3838    if (!item) {
    3939        pmChip *chip = cell->parent; // Parent chip
    40         psAssert(chip, "cell is missing chip \n");
     40        psAssert(chip, "cell is missing chip \n");
    4141
    4242        item = psMetadataLookup(chip->concepts, name);
    4343        if (!item) {
    4444            pmFPA *fpa = chip->parent; // Parent FPA
    45             psAssert(fpa, "chip is missing fpa \n");
     45            psAssert(fpa, "chip is missing fpa \n");
    4646
    4747            item = psMetadataLookup(fpa->concepts, name);
     
    6868
    6969    psArray *words = psStringSplitArray(rule, " ", false);
    70    
     70
    7171    // we should have a rule of the form (concept) OP (concept) OP (concept) ...
    7272    // for now, the only allowed OP is * (eventually, we can steal code from opihi for a better
     
    7474
    7575    if (words->n % 2 == 0) {
    76         psError(PM_ERR_CONFIG, true, "syntax error in DARK.ORDINATE %s rule %s\n", name, rule);
    77         psFree(words);
    78         return false;
     76        psError(PM_ERR_CONFIG, true, "syntax error in DARK.ORDINATE %s rule %s\n", name, rule);
     77        psFree(words);
     78        return false;
    7979    }
    8080
    8181    for (int i = 1; i < words->n; i+=2) {
    82         if (strcmp((char *)words->data[i], "*")) {
    83             psError(PM_ERR_CONFIG, true, "syntax error in DARK.ORDINATE %s rule %s\n", name, rule);
    84             psFree(words);
    85             return false;
    86         }
    87     }
    88    
     82        if (strcmp((char *)words->data[i], "*")) {
     83            psError(PM_ERR_CONFIG, true, "syntax error in DARK.ORDINATE %s rule %s\n", name, rule);
     84            psFree(words);
     85            return false;
     86        }
     87    }
     88
    8989    if (!ordinateParseConcept(value, readout, words->data[0])) {
    90         psError(PM_ERR_CONFIG, false, "syntax error in DARK.ORDINATE %s rule %s\n", name, rule);
    91         psFree(words);
    92         return false;
     90        psError(PM_ERR_CONFIG, false, "syntax error in DARK.ORDINATE %s rule %s\n", name, rule);
     91        psFree(words);
     92        return false;
    9393    }
    9494
    9595    double value2 = 0.0;
    9696    for (int i = 2; i < words->n; i+=2) {
    97         if (!ordinateParseConcept(&value2, readout, words->data[i])) {
    98             psError(PM_ERR_CONFIG, false, "syntax error in DARK.ORDINATE %s rule %s\n", name, rule);
    99             psFree(words);
    100             return false;
    101         }
    102         *value *= value2;
     97        if (!ordinateParseConcept(&value2, readout, words->data[i])) {
     98            psError(PM_ERR_CONFIG, false, "syntax error in DARK.ORDINATE %s rule %s\n", name, rule);
     99            psFree(words);
     100            return false;
     101        }
     102        *value *= value2;
    103103    }
    104104    psFree(words);
     
    123123
    124124    if (rule) {
    125         if (!ordinateParseRule(value, readout, name, rule)) {
    126             psError(PM_ERR_CONFIG, false, "trouble parsing rule %s for DARK.ORDINATE %s", rule, name);
    127             return false;
    128         }
     125        if (!ordinateParseRule(value, readout, name, rule)) {
     126            psError(PM_ERR_CONFIG, false, "trouble parsing rule %s for DARK.ORDINATE %s", rule, name);
     127            return false;
     128        }
    129129    } else {
    130         if (!ordinateParseConcept(value, readout, name)) {
    131             psError(PM_ERR_CONFIG, false, "trouble parsing rule %s for DARK.ORDINATE %s", rule, name);
    132             return false;
    133         }
     130        if (!ordinateParseConcept(value, readout, name)) {
     131            psError(PM_ERR_CONFIG, false, "trouble parsing rule %s for DARK.ORDINATE %s", rule, name);
     132            return false;
     133        }
    134134    }
    135135
     
    188188        double normValue;            // Normalisation value
    189189        if (!ordinateLookup(&normValue, &inRange, normConcept, NULL, false, NAN, NAN, readout)) {
    190             psError(PM_ERR_CONFIG, false, "problem finding concept %s for DARK.NORM", normConcept);
    191             return false;
    192         }
    193         if (!isfinite(normValue)) {
     190            psError(PM_ERR_CONFIG, false, "problem finding concept %s for DARK.NORM", normConcept);
     191            return false;
     192        }
     193        if (!isfinite(normValue)) {
    194194            psWarning("Unable to find acceptable value of %s for readout %d", normConcept, i);
    195195            roMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0xff;
     
    231231            double value = NAN;          // Value of ordinate
    232232            if (!ordinateLookup(&value, &inRange, ord->name, ord->rule, ord->scale, ord->min, ord->max, readout)) {
    233                 psError(PM_ERR_CONFIG, false, "problem finding rule for DARK.ORDINATE %s", ord->name);
    234                 return false;
    235             }
    236             if (!isfinite(value)) {
    237                 psWarning("Unable to find acceptable value of DARK.ORDINATE %s for readout %d", ord->name, i);
     233                psError(PM_ERR_CONFIG, false, "problem finding rule for DARK.ORDINATE %s", ord->name);
     234                return false;
     235            }
     236            if (!isfinite(value)) {
     237                psWarning("Unable to find acceptable value of DARK.ORDINATE %s for readout %d", ord->name, i);
    238238                roMask->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xff;
    239239                val->data.F32[i] = NAN;
     
    242242            }
    243243            if (!inRange) {
    244                 psWarning("Value of DARK.ORDINATE %s for readout %d is out of range", ord->name, i);
     244                psWarning("Value of DARK.ORDINATE %s for readout %d is out of range", ord->name, i);
    245245                roMask->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xff;
    246246                val->data.F32[i] = NAN;
     
    349349    PS_ASSERT_INT_NONNEGATIVE(iter, false);
    350350    PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
    351 
    352     pthread_t id = pthread_self();
    353     char name[64];
    354     sprintf (name, "%x", (unsigned int) id);
    355     psTimerStart (name);
    356351
    357352    bool mdok = false;
     
    433428            }
    434429
    435             pmDarkVisualPixelFit(pixels, mask);
    436             pmDarkVisualPixelModel(poly, values);
     430            pmDarkVisualPixelFit(pixels, mask);
     431            pmDarkVisualPixelModel(poly, values);
    437432
    438433            for (int k = 0; k < poly->coeff->n; k++) {
     
    537532            return false;
    538533        }
    539         if (!isfinite(value)) {
     534        if (!isfinite(value)) {
    540535            psError(PS_ERR_UNKNOWN, true, "Value for DARK.ORDINATE %s is NAN", ord->name);
    541536            psFree(values);
    542537            return false;
    543         }
     538        }
    544539        values->data.F32[i] = value;
    545540    }
     
    792787            psMetadataAddStr(row, PS_LIST_TAIL, PM_DARK_FITS_NAME, 0, "DARK.ORDINATE name", ord->name);
    793788
    794             // XXX write a dummy value if ord->rule == NULL? (eg, NONE)
    795             if (ord->rule) {
    796                 psMetadataAddStr(row, PS_LIST_TAIL, PM_DARK_FITS_RULE, 0, "DARK.ORDINATE rule", ord->rule);
    797             } else {
    798                 psMetadataAddStr(row, PS_LIST_TAIL, PM_DARK_FITS_RULE, 0, "DARK.ORDINATE rule", "NONE");
    799             }
     789            // XXX write a dummy value if ord->rule == NULL? (eg, NONE)
     790            if (ord->rule) {
     791                psMetadataAddStr(row, PS_LIST_TAIL, PM_DARK_FITS_RULE, 0, "DARK.ORDINATE rule", ord->rule);
     792            } else {
     793                psMetadataAddStr(row, PS_LIST_TAIL, PM_DARK_FITS_RULE, 0, "DARK.ORDINATE rule", "NONE");
     794            }
    800795
    801796            psMetadataAddS32(row, PS_LIST_TAIL, PM_DARK_FITS_ORDER, 0, "Polynomial order", ord->order);
     
    975970              ord->max = psMetadataLookupF32(NULL, row, PM_DARK_FITS_MAX);
    976971
    977               // load the ordinate rule; it is not an error if this field is missing or NULL
    978               // a NULL rule means 'use the name as the single concept'
     972              // load the ordinate rule; it is not an error if this field is missing or NULL
     973              // a NULL rule means 'use the name as the single concept'
    979974              const char *rule = psMetadataLookupStr(&mdok, row, PM_DARK_FITS_RULE);
    980               if (!rule || !strcasecmp(rule, "NONE")) {
    981                   ord->rule = NULL;
    982               } else {
    983                   ord->rule = psStringCopy(rule);
    984               }
     975              if (!rule || !strcasecmp(rule, "NONE")) {
     976                  ord->rule = NULL;
     977              } else {
     978                  ord->rule = psStringCopy(rule);
     979              }
    985980              ordinates->data[i] = ord;
    986981          }
     
    10091004// this init function only gets the ordinates for the first readout...
    10101005bool pmDarkVisualInit(psArray *values) {
    1011    
     1006
    10121007    if (!pmVisualIsVisual()) return true;
    10131008
     
    10181013    int nOrders = 0;
    10191014    for (int i = 0; i < values->n; i++) {
    1020         psVector *vect = values->data[i];
    1021         if (!nOrders) {
    1022             nOrders = vect->n;
    1023         } else {
    1024             psAssert (nOrders == vect->n, "mismatch in order vector lengths");
    1025         }
     1015        psVector *vect = values->data[i];
     1016        if (!nOrders) {
     1017            nOrders = vect->n;
     1018        } else {
     1019            psAssert (nOrders == vect->n, "mismatch in order vector lengths");
     1020        }
    10261021    }
    10271022    xVectors = psArrayAlloc(nOrders);
    10281023    for (int i = 0; i < nOrders; i++) {
    1029         xVectors->data[i] = psVectorAlloc(values->n, PS_TYPE_F32);
     1024        xVectors->data[i] = psVectorAlloc(values->n, PS_TYPE_F32);
    10301025    }
    10311026
    10321027    for (int i = 0; i < values->n; i++) {
    1033         psVector *vect = values->data[i];
    1034         for (int j = 0; j < vect->n; j++) {
    1035             psVector *xVec = xVectors->data[j];
    1036             xVec->data.F32[i] = vect->data.F32[j];
    1037         }
     1028        psVector *vect = values->data[i];
     1029        for (int j = 0; j < vect->n; j++) {
     1030            psVector *xVec = xVectors->data[j];
     1031            xVec->data.F32[i] = vect->data.F32[j];
     1032        }
    10381033    }
    10391034
     
    10411036
    10421037    kapa = psAlloc(nKapa*sizeof(int));
    1043    
     1038
    10441039    for (int i = 0; i < nKapa; i++) {
    1045         kapa[i] = -1;
    1046         pmVisualInitWindow(&kapa[i], "ppmerge");
     1040        kapa[i] = -1;
     1041        pmVisualInitWindow(&kapa[i], "ppmerge");
    10471042    }
    10481043    return true;
     
    10631058
    10641059    for (int i = 0; i < xVectors->n; i++) {
    1065         psVector *x = xVectors->data[i];
    1066 
    1067         // generate vectors of the unmasked values
    1068         int nSub = 0;
    1069         for (int j = 0; j < pixels->n; j++) {
    1070             if (mask && mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) continue;
    1071             xSub->data.F32[nSub] = x->data.F32[j];
    1072             ySub->data.F32[nSub] = pixels->data.F32[j];
    1073             nSub ++;
    1074         }
    1075         xSub->n = ySub->n = nSub;
    1076        
    1077         // plot the unmasked values
    1078         pmVisualScaleGraphdata (&graphdata, xSub, ySub, false);
    1079         KapaSetGraphData(kapa[i], &graphdata);
    1080         KapaSetLimits(kapa[i], &graphdata);
    1081         KapaClearPlots (kapa[i]);
    1082 
    1083         KapaSetFont (kapa[i], "courier", 14);
    1084         KapaBox (kapa[i], &graphdata);
    1085         KapaSendLabel (kapa[i], "ordinate", KAPA_LABEL_XM);
    1086         KapaSendLabel (kapa[i], "pixel values", KAPA_LABEL_YM);
    1087 
    1088         graphdata.color = KapaColorByName("black");
    1089         graphdata.style = 2;
    1090         graphdata.ptype = 2;
    1091         KapaPrepPlot  (kapa[i], xSub->n, &graphdata);
    1092         KapaPlotVector(kapa[i], xSub->n, xSub->data.F32, "x");
    1093         KapaPlotVector(kapa[i], xSub->n, ySub->data.F32, "y");
     1060        psVector *x = xVectors->data[i];
     1061
     1062        // generate vectors of the unmasked values
     1063        int nSub = 0;
     1064        for (int j = 0; j < pixels->n; j++) {
     1065            if (mask && mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) continue;
     1066            xSub->data.F32[nSub] = x->data.F32[j];
     1067            ySub->data.F32[nSub] = pixels->data.F32[j];
     1068            nSub ++;
     1069        }
     1070        xSub->n = ySub->n = nSub;
     1071
     1072        // plot the unmasked values
     1073        pmVisualScaleGraphdata (&graphdata, xSub, ySub, false);
     1074        KapaSetGraphData(kapa[i], &graphdata);
     1075        KapaSetLimits(kapa[i], &graphdata);
     1076        KapaClearPlots (kapa[i]);
     1077
     1078        KapaSetFont (kapa[i], "courier", 14);
     1079        KapaBox (kapa[i], &graphdata);
     1080        KapaSendLabel (kapa[i], "ordinate", KAPA_LABEL_XM);
     1081        KapaSendLabel (kapa[i], "pixel values", KAPA_LABEL_YM);
     1082
     1083        graphdata.color = KapaColorByName("black");
     1084        graphdata.style = 2;
     1085        graphdata.ptype = 2;
     1086        KapaPrepPlot  (kapa[i], xSub->n, &graphdata);
     1087        KapaPlotVector(kapa[i], xSub->n, xSub->data.F32, "x");
     1088        KapaPlotVector(kapa[i], xSub->n, ySub->data.F32, "y");
    10941089    }
    10951090    pmVisualAskUser (&plotFlag);
     
    11101105
    11111106    for (int i = 0; i < values->n; i++) {
    1112         psVector *coord = values->data[i];
    1113         yFit->data.F32[i] = psPolynomialMDEval (poly, coord);
     1107        psVector *coord = values->data[i];
     1108        yFit->data.F32[i] = psPolynomialMDEval (poly, coord);
    11141109    }
    11151110
    11161111    for (int i = 0; i < xVectors->n; i++) {
    1117         psVector *xFit = xVectors->data[i];
    1118 
    1119         KapaGetGraphData(kapa[i], &graphdata);
    1120         graphdata.color = KapaColorByName("red");
    1121         graphdata.style = 2;
    1122         graphdata.ptype = 7;
    1123         KapaPrepPlot  (kapa[i], xFit->n, &graphdata);
    1124         KapaPlotVector(kapa[i], xFit->n, xFit->data.F32, "x");
    1125         KapaPlotVector(kapa[i], xFit->n, yFit->data.F32, "y");
     1112        psVector *xFit = xVectors->data[i];
     1113
     1114        KapaGetGraphData(kapa[i], &graphdata);
     1115        graphdata.color = KapaColorByName("red");
     1116        graphdata.style = 2;
     1117        graphdata.ptype = 7;
     1118        KapaPrepPlot  (kapa[i], xFit->n, &graphdata);
     1119        KapaPlotVector(kapa[i], xFit->n, xFit->data.F32, "x");
     1120        KapaPlotVector(kapa[i], xFit->n, yFit->data.F32, "y");
    11261121    }
    11271122    pmVisualAskUser (&plotFlag);
     
    11321127
    11331128    for (int i = 0; i < nKapa; i++) {
    1134         KapaClose(kapa[i]);
     1129        KapaClose(kapa[i]);
    11351130    }
    11361131    psFree (kapa);
  • branches/simtest_nebulous_branches/psModules/src/detrend/pmFringeStats.c

    r24912 r27840  
    1111#include "pmFPA.h"
    1212#include "pmFringeStats.h"
     13
     14#include "psPolynomialMD.h"
     15#include "psMinimizePolyFit.h"
     16#include "psVector.h"
     17
    1318
    1419// Future optimisations for speed:
     
    331336        dfPt[i] = 1.0 / medianSd->sampleStdev;
    332337
    333         psTrace("psModules.detrend", 7, "[%d:%d,%d:%d]: %f %f\n", (int)region.x0, (int)region.x1,
    334                 (int)region.y0, (int)region.y1, fPt[i], dfPt[i]);
     338        psTrace("psModules.detrend", 7, "[%d:%d,%d:%d]: %f %f : %s\n", (int)region.x0, (int)region.x1,
     339                (int)region.y0, (int)region.y1, fPt[i], dfPt[i], readout->parent->hdu->extname);
    335340    }
    336341    psFree(sky);
     
    840845            }
    841846        }
    842         B->data.F64[i] = vector;
     847        B->data.F64[i] = vector;
    843848    }
    844849
     
    966971            }
    967972        }
     973       
    968974    }
    969975
     
    988994                psTrace("psModules.detrend", 9, "Masking region %d because not finite in fringe %d.\n", j, i);
    989995            }
    990         }
    991     }
    992 
     996            else if (fabs(fringe->f->data.F32[j]) > 0.1) {
     997              mask->data.PS_TYPE_VECTOR_MASK_DATA[j] = 1;
     998              psTrace("psModules.detrend", 9, "Masking region %d because too large fringe %d.\n", j, i);
     999            }
     1000            // Mask bad points in the science data as well.
     1001            if ((i == 0) && (!isfinite(science->f->data.F32[j]))) {
     1002                mask->data.PS_TYPE_VECTOR_MASK_DATA[j] = 1;
     1003                psTrace("psModules.detrend", 9, "Masking region %d because not finite in science fringe %d.\n", j, i);
     1004            }         
     1005            psTrace("psModules.detrend", 7, "F %f %f %f %d\n",
     1006                    fringe->f->data.F32[j], science->f->data.F32[j],
     1007                    1 / science->df->data.F32[j],(int) mask->data.PS_TYPE_VECTOR_MASK_DATA[j]);
     1008        }
     1009    }
     1010   
     1011/*     // Begin switch from old outlier removal and fitting code. */
     1012
     1013    psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
     1014
     1015    pmFringeStats *fringe = fringes->data[0];
     1016    psVector *errors = psVectorAlloc(science->df->n,PS_TYPE_F32);
     1017    for (int j = 0; j < errors->n; j++) {
     1018      errors->data.F32[j] = 1 / science->df->data.F32[j];
     1019    }
     1020    psVectorFitPolynomial1D(poly,mask,0xff,science->f,errors,fringe->f);
     1021
     1022    for (int i = 0; i <= poly->nX; i++) {
     1023      scale->coeff->data.F32[i] = poly->coeff[i];
     1024      psTrace("psModules.detrend",7,"COEFFS: %d %g %g %g\n",i,scale->coeff->data.F32[i],poly->coeff[i],poly->coeffErr[i]);
     1025    }
     1026
     1027    psFree(poly);
     1028    //    psFree(fringe);
     1029    psFree(errors);
     1030
     1031    psFree(median);
     1032    psFree(diff);
     1033    return scale;
     1034    // End switch from old code.
     1035   
     1036
     1037   
    9931038# if (0)
    9941039    // Write fringe data to file for a test
     
    10191064        }
    10201065        scale->coeff->data.F32[0] -= median->sampleMedian;
    1021         scale->coeff->data.F32[i] = 0.0;
     1066        if (i != 0) {
     1067          scale->coeff->data.F32[i] = 0.0;
     1068        }
    10221069    }
    10231070    psFree(median);
     
    10511098
    10521099    return scale;
     1100    //# endif
    10531101}
    10541102
  • branches/simtest_nebulous_branches/psModules/src/detrend/pmMaskBadPixels.c

    r24113 r27840  
    266266            }
    267267        }
     268        psFree(histo);
    268269
    269270        // Since the mode is most likely zero, we add one to get something realistic.  Then "thresh" is
  • branches/simtest_nebulous_branches/psModules/src/detrend/pmPattern.c

    r24905 r27840  
    11#include <stdio.h>
     2#include <string.h>
    23#include <pslib.h>
    34
     
    910// Mask a row as bad
    1011static void patternMaskRow(pmReadout *ro, // Readout to mask
    11                            int y,         // Row to mask
     12                           int y,       // Row to mask
    1213                           psImageMaskType bad // Mask value to give
    1314                           )
     
    1718    psAssert(y < image->numRows, "Row not in image");
    1819
    19     int numCols = image->numCols;       // Size of image
     20    int numCols = image->numCols;       // Number of columns
    2021    for (int x = 0; x < numCols; x++) {
    2122        image->data.F32[y][x] = NAN;
     
    2930    return;
    3031}
     32
     33//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     34// Measurement and application
     35//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    3136
    3237bool pmPatternRow(pmReadout *ro, int order, int iter, float rej, float thresh,
     
    6368    float lower = stats->robustMedian - thresh * stats->robustStdev; // Lower bound for data
    6469    float upper = stats->robustMedian + thresh * stats->robustStdev; // Upper bound for data
     70    float background = stats->robustMedian;
    6571    psFree(stats);
    6672    psFree(rng);
     
    7985    psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, order); // Polynomial to fit
    8086    psVector *data = psVectorAlloc(numCols, PS_TYPE_F32); // Data to fit
     87
     88    psImage *corr = psImageAlloc(order + 1, numRows, PS_TYPE_F64); // Corrections applied
     89    psImageInit(corr, NAN);
    8190
    8291    for (int y = 0; y < numRows; y++) {
     
    104113            continue;
    105114        }
     115
     116        poly->coeff[0] -= background;
     117        memcpy(corr->data.F64[y], poly->coeff, (order + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    106118        psVector *solution = psPolynomial1DEvalVector(poly, indices); // Solution vector
    107119        if (!solution) {
     
    117129        psFree(solution);
    118130    }
     131
     132    psMetadataAddImage(ro->analysis, PS_LIST_TAIL, PM_PATTERN_ROW_CORRECTION, PS_META_REPLACE,
     133                       "Pattern row correction", corr);
     134    psFree(corr);
    119135
    120136    psFree(indices);
     
    126142    return true;
    127143}
     144
     145bool pmPatternRowApply(pmReadout *ro, psImageMaskType maskBad)
     146{
     147    PM_ASSERT_READOUT_NON_NULL(ro, false);
     148    PM_ASSERT_READOUT_IMAGE(ro, false);
     149
     150    bool mdok;                          // Status of MD lookup
     151    psImage *corr = psMetadataLookupPtr(&mdok, ro->analysis, PM_PATTERN_ROW_CORRECTION); // Correction
     152    if (!mdok) {
     153        // No correction to apply
     154        return true;
     155    }
     156
     157    psImage *image = ro->image; // Image of interest
     158    int numCols = image->numCols, numRows = image->numRows; // Size of image
     159
     160    if (corr->numRows != numRows) {
     161        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
     162                "Number of rows of image (%d) does not match number of rows of pattern correction (%d)\n",
     163                numRows, corr->numRows);
     164        return false;
     165    }
     166
     167    int order = corr->numCols - 1;                                        // Polynomial order
     168    psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, order); // Polynomial to apply
     169    psVector *indices = psVectorAlloc(numCols, PS_TYPE_F32); // Indices for polynomial
     170    float norm = 2.0 / (float)numCols;  // Normalisation for indices
     171    for (int x = 0; x < numCols; x++) {
     172        indices->data.F32[x] = x * norm - 1.0;
     173    }
     174
     175    for (int y = 0; y < numRows; y++) {
     176        memcpy(poly->coeff, corr->data.F64[y], (order + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
     177        psVector *solution = psPolynomial1DEvalVector(poly, indices); // Solution vector
     178        if (!solution) {
     179            psWarning("Unable to evaluate polynomial for row %d", y);
     180            psErrorClear();
     181            patternMaskRow(ro, y, maskBad);
     182            continue;
     183        }
     184
     185        for (int x = 0; x < numCols; x++) {
     186            image->data.F32[y][x] -= solution->data.F32[x];
     187        }
     188        psFree(solution);
     189    }
     190
     191    psFree(poly);
     192    psFree(indices);
     193
     194    return true;
     195}
     196
     197
     198bool pmPatternCell(pmChip *chip, const psVector *tweak, psStatsOptions bgStat, psStatsOptions cellStat,
     199                   psImageMaskType maskVal, psImageMaskType maskBad)
     200{
     201    PS_ASSERT_PTR_NON_NULL(chip, false);
     202    PS_ASSERT_VECTOR_NON_NULL(tweak, false);
     203    PS_ASSERT_VECTOR_SIZE(tweak, chip->cells->n, false);
     204    PS_ASSERT_VECTOR_TYPE(tweak, PS_TYPE_U8, false);
     205
     206    int numCells = tweak->n;            // Number of cells
     207
     208    psVector *mean = psVectorAlloc(numCells, PS_TYPE_F32); // Mean for each cell
     209    psVector *meanMask = psVectorAlloc(numCells, PS_TYPE_VECTOR_MASK); // Mask for means
     210    psVectorInit(mean, NAN);
     211    psVectorInit(meanMask, 0);
     212
     213    // Mask bits
     214    enum {
     215        PM_PATTERN_IGNORE = 0x01,       // Ignore this cell
     216        PM_PATTERN_TWEAK  = 0x02,       // Tweak this cell
     217        PM_PATTERN_ERROR  = 0x04,       // Error in calculating background
     218        PM_PATTERN_ALL    = 0xFF,       // All causes
     219    };
     220
     221    // Count number of cells to tweak
     222    int numTweak = 0;                   // Number of cells to tweak
     223    int numIgnore = 0;                  // Number of cells to ignore
     224    for (int i = 0; i < numCells; i++) {
     225        pmCell *cell = chip->cells->data[i]; // Cell of interest
     226        if (!cell || !cell->data_exists || !cell->process ||
     227            cell->readouts->n == 0 || cell->readouts->n > 1 || !cell->readouts->data[0]) {
     228            numIgnore++;
     229            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PM_PATTERN_IGNORE;
     230            continue;
     231        }
     232        if (tweak->data.U8[i]) {
     233            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PM_PATTERN_TWEAK;
     234            numTweak++;
     235        }
     236    }
     237    if (numTweak == 0) {
     238        // Nothing to do
     239        psFree(mean);
     240        psFree(meanMask);
     241        return true;
     242    }
     243    if (numTweak >= numCells - numIgnore) {
     244        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot pattern-correct all cells within a chip.");
     245        psFree(mean);
     246        psFree(meanMask);
     247        return false;
     248    }
     249
     250    // Measure mean of each cell
     251    // This is not really the perfect thing to do, which would be to take a common mean for the set of cells
     252    // which aren't being tweaked (because some cells will be heavily masked, so shouldn't be weighted the
     253    // same as pure cells), but it's simple and fast.
     254    psStats *bgStats = psStatsAlloc(bgStat); // Statistics on background
     255    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
     256    for (int i = 0; i < numCells; i++) {
     257        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
     258            continue;
     259        }
     260        pmCell *cell = chip->cells->data[i]; // Cell of interest
     261        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
     262
     263        psStatsInit(bgStats);
     264#if 1
     265        if (!psImageBackground(bgStats, NULL, ro->image, ro->mask, maskVal, rng)) {
     266            psWarning("Unable to measure background for cell %d\n", i);
     267            psErrorClear();
     268            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PM_PATTERN_ERROR;
     269            continue;
     270        }
     271#else
     272        if (!psImageStats(bgStats, ro->image, ro->mask, maskVal)) {
     273            psWarning("Unable to measure background for cell %d\n", i);
     274            psErrorClear();
     275            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PM_PATTERN_ERROR;
     276            continue;
     277        }
     278#endif
     279        mean->data.F32[i] = psStatsGetValue(bgStats, bgStat);
     280        if (!isfinite(mean->data.F32[i])) {
     281            psWarning("Non-finite background for cell %d\n", i);
     282            psErrorClear();
     283            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PM_PATTERN_ERROR;
     284            continue;
     285        }
     286    }
     287    psFree(bgStats);
     288    psFree(rng);
     289
     290    psStats *cellStats = psStatsAlloc(cellStat); // Statistics on cells
     291    if (!psVectorStats(cellStats, mean, NULL, meanMask, PM_PATTERN_ALL)) {
     292        // an error in psVectorStats implies a programming error
     293        psError(PS_ERR_UNKNOWN, false, "Unable to calculate mean cell background.");
     294        psFree(mean);
     295        psFree(meanMask);
     296        psFree(cellStats);
     297        return false;
     298    }
     299
     300    float background = psStatsGetValue(cellStats, cellStat); // Background value for chip
     301    psFree(cellStats);
     302    if (!isfinite(background)) {
     303        // this can happen if all data in the image is bad -- in this case, do not correct, but
     304        // do not treat this as an error (other functions are responsible for check data validity)
     305        psLogMsg("psModules.detrend", PS_LOG_DETAIL, "Non-finite mean cell background -- skipping correction (data probabaly bad).");
     306        psFree(mean);
     307        psFree(meanMask);
     308        return true;
     309    }
     310
     311    psLogMsg("psModules.detrend", PS_LOG_DETAIL, "Mean chip background is %f", background);
     312
     313    for (int i = 0; i < numCells; i++) {
     314        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
     315            continue;
     316        }
     317        if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_TWEAK)) {
     318            continue;
     319        }
     320        pmCell *cell = chip->cells->data[i]; // Cell of interest
     321        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
     322        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_ERROR) {
     323            psImageInit(ro->image, NAN);
     324            psBinaryOp(ro->mask, ro->mask, "|", psScalarAlloc(maskBad, PS_TYPE_IMAGE_MASK));
     325            psMetadataAddF32(ro->analysis, PS_LIST_TAIL, PM_PATTERN_CELL_CORRECTION, PS_META_REPLACE,
     326                             "Pattern cell correction solution", NAN);
     327            continue;
     328        }
     329        float correction = background - mean->data.F32[i]; // Correction to apply
     330        const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
     331        psLogMsg("psModules.detrend", PS_LOG_DETAIL, "Correcting background of cell %s by %f",
     332                 cellName, correction);
     333        psBinaryOp(ro->image, ro->image, "+", psScalarAlloc(correction, PS_TYPE_F32));
     334        psMetadataAddF32(ro->analysis, PS_LIST_TAIL, PM_PATTERN_CELL_CORRECTION, PS_META_REPLACE,
     335                         "Pattern cell correction solution", correction);
     336    }
     337
     338    psFree(mean);
     339    psFree(meanMask);
     340
     341    return true;
     342}
     343
     344bool pmPatternCellApply(pmReadout *ro, psImageMaskType maskBad)
     345{
     346    PM_ASSERT_READOUT_NON_NULL(ro, false);
     347    PM_ASSERT_READOUT_IMAGE(ro, false);
     348
     349    bool mdok;                          // Status of MD lookup
     350    float corr = psMetadataLookupF32(&mdok, ro->analysis, PM_PATTERN_CELL_CORRECTION); // Correction to apply
     351    if (!mdok) {
     352        // No correction to apply
     353        return true;
     354    }
     355
     356    psImage *image = ro->image, *mask = ro->mask; // Image and mask of interest
     357    int numCols = image->numCols, numRows = image->numRows; // Size of image
     358
     359    if (!isfinite(corr)) {
     360        for (int y = 0; y < numRows; y++) {
     361            for (int x = 0; x < numCols; x++) {
     362                image->data.F32[y][x] = NAN;
     363            }
     364        }
     365        if (mask) {
     366            for (int y = 0; y < numRows; y++) {
     367                for (int x = 0; x < numCols; x++) {
     368                    mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= maskBad;
     369                }
     370            }
     371        }
     372    } else {
     373        for (int y = 0; y < numRows; y++) {
     374            for (int x = 0; x < numCols; x++) {
     375                image->data.F32[y][x] += corr;
     376            }
     377        }
     378    }
     379
     380    return true;
     381}
     382
     383
  • branches/simtest_nebulous_branches/psModules/src/detrend/pmPattern.h

    r24903 r27840  
    1818/// @{
    1919
     20#define PM_PATTERN_ROW_CORRECTION "PATTERN.ROW.CORRECTION" // Pattern row correction on analysis metadata
     21#define PM_PATTERN_CELL_CORRECTION "PATTERN.CELL.CORRECTION" // Pattern cell correction on analysis metadata
     22
    2023/// Fit and remove pattern noise over rows
    2124bool pmPatternRow(
     
    3134    );
    3235
     36/// Apply previously measured row pattern correction
     37bool pmPatternRowApply(pmReadout *ro,   ///< Readout to correct
     38                       psImageMaskType maskBad ///< Mask value to give bad pixels
     39                       );
     40
     41/// Fix the background on cells known to be troublesome
     42bool pmPatternCell(
     43    pmChip *chip,                       ///< Chip to correct
     44    const psVector *tweak,              ///< U8 vector indicating whether to tweak the corresponding cell
     45    psStatsOptions bgStat,              ///< Statistic to use for background measurement
     46    psStatsOptions cellStat,            ///< Statistic to use for combination of cell background measurements
     47    psImageMaskType maskVal,            ///< Mask value to use
     48    psImageMaskType maskBad             ///< Mask value to give bad pixels
     49    );
     50
     51/// Apply previously measured cell pattern correction
     52bool pmPatternCellApply(pmReadout *ro,          ///< Readout to correct
     53                        psImageMaskType maskBad ///< Mask value to give bad pixels
     54                        );
     55
     56
    3357/// @}
    3458#endif
  • branches/simtest_nebulous_branches/psModules/src/detrend/pmShutterCorrection.c

    r23989 r27840  
    10151015
    10161016        pmShutterCorrection *corr = pmShutterCorrectionFullFit(newtimes, newcounts, newerrors, guess); // The actual correction
    1017         psTrace("psModules.detrend", 5, "Shutter correction fit: scale: %f, offset: %f, offref: %f\n", corr->scale, corr->offset, corr->offref);
    1018 
    1019         if (corr && isfinite(corr->offref) && corr->valid) {
    1020             psTrace("psModules.detrend", 5, "Sample reference value: %f\n", corr->offref);
    1021             meanRef += corr->offref;
    1022             numGood++;
    1023         }
     1017
     1018        if (corr) {
     1019            psTrace("psModules.detrend", 5, "Shutter correction fit: scale: %f, offset: %f, offref: %f\n", corr->scale, corr->offset, corr->offref);
     1020            if (isfinite(corr->offref) && corr->valid) {
     1021                psTrace("psModules.detrend", 5, "Sample reference value: %f\n", corr->offref);
     1022                meanRef += corr->offref;
     1023                numGood++;
     1024            }
     1025        } else {
     1026            psTrace("psModules.detrend", 5, "failed Shutter correction fit\n");
     1027        }
    10241028
    10251029        psFree(corr);
  • branches/simtest_nebulous_branches/psModules/src/extras/Makefile.am

    r24880 r27840  
    1717        pmKapaPlots.h \
    1818        pmVisual.h \
     19        ippDiffMode.h \
    1920        ippStages.h
    2021
  • branches/simtest_nebulous_branches/psModules/src/extras/ippStages.h

    r24880 r27840  
    1 /* @file psVisual.h
     1/* @file ippStages.h
    22 * @brief some macro defintions for the stages of the pipeline
    33 * @author Bill Sweeney, IfA
  • branches/simtest_nebulous_branches/psModules/src/extras/pmVisual.c

    r23989 r27840  
    2222#include "pmSubtractionStamps.h"
    2323#include "pmTrend2D.h"
     24#include "pmPSF.h"
     25#include "pmPSFtry.h"
     26#include "pmSource.h"
    2427#include "pmFPAExtent.h"
    2528
     
    8689{
    8790    char key[10];
    88     fprintf (stdout, "[c]ontinue? [s]kip the rest of these plots? [a]bort all visual plots?");
     91    if (plotFlag) {
     92        fprintf (stdout, "[c]ontinue? [s]kip the rest of these plots? [a]bort all visual plots? (c) ");
     93    } else {
     94        fprintf (stdout, "[c]ontinue? [a]bort all visual plots? (c) ");
     95    }
    8996    if (!fgets(key, 8, stdin)) {
    9097        psWarning("Unable to read option");
    9198    }
    92     if (key[0] == 's') {
     99    if (plotFlag && (key[0] == 's')) {
    93100        *plotFlag = false;
    94101    }
  • branches/simtest_nebulous_branches/psModules/src/imcombine/Makefile.am

    r23242 r27840  
    1313        pmSubtractionIO.c       \
    1414        pmSubtractionKernels.c  \
     15        pmSubtractionHermitian.c        \
     16        pmSubtractionDeconvolve.c       \
    1517        pmSubtractionMask.c     \
    1618        pmSubtractionMatch.c    \
     
    3234        pmSubtractionIO.h       \
    3335        pmSubtractionKernels.h  \
     36        pmSubtractionHermitian.h        \
     37        pmSubtractionDeconvolve.h       \
    3438        pmSubtractionMask.h     \
    3539        pmSubtractionMatch.h    \
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmImageCombine.c

    r23989 r27840  
    118118            psImage *mask = masks->data[i]; // Mask of interest
    119119            pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[i] = (mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal);
    120         }       
    121         // Set the pixel error data, if necessary
     120        }
     121        // Set the pixel error data, if necessary
    122122        if (errors) {
    123123            psImage *error = errors->data[i]; // Error image of interest
     
    132132        // Combine all the pixels, using the specified stat.
    133133        if (!psVectorStats(stats, pixelData, pixelErrors, pixelMasks, 0xff)) {
    134             psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
    135             return false;
    136         }
    137         if (isnan(stats->sampleMean)) {
     134            psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
     135            return false;
     136        }
     137        if (isnan(stats->sampleMean)) {
    138138            combine->data.F32[y][x] = NAN;
    139139            psFree(buffer);
     
    141141        }
    142142        float combinedPixel = stats->sampleMean; // Value of the combination
    143        
     143
    144144        if (iter == 0) {
    145145            // Save the value produced with no rejection, since it may be useful later
     
    369369    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
    370370    if (!psVectorStats(stats, pixels, NULL, mask, 1)) {
    371         psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
     371        psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
    372372    }
    373373    float median = stats->sampleMedian;
     
    640640                                              (psPlaneTransform * )outToIn->data[otherImg],
    641641                                              outCoords);
    642                         psS32 xPix = (int)(inCoords->x + 0.5);
    643                         psS32 yPix = (int)(inCoords->y + 0.5);
     642                        psS32 xPix = (int)(inCoords->x - 0.5);
     643                        psS32 yPix = (int)(inCoords->y - 0.5);
    644644                        if ((xPix >= 0) && (xPix <= ((psImage*)(images->data[otherImg]))->numCols - 1) &&
    645645                                (yPix >= 0) && (yPix <= ((psImage*)(images->data[otherImg]))->numRows - 1)) {
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmPSFEnvelope.c

    r24622 r27840  
    3434
    3535// #define TESTING                         // Enable test output
     36// #define PEAK_NORM                       // Normalise peaks?
    3637#define PEAK_FLUX 1.0e4                 // Peak flux for each source
    3738#define SKY_VALUE 0.0e0                 // Sky value for fake image
     
    6465                     int radius,        // Radius of each PSF
    6566                     const char *modelName,// Name of PSF model to use
    66                      int xOrder, int yOrder // Order for PSF variation fit
     67                     int xOrder, int yOrder, // Order for PSF variation fit
     68                     psImageMaskType maskVal
    6769                     )
    6870{
     
    122124            continue;
    123125        }
     126
     127        if (psTraceGetLevel("psModules.imcombine") >= 1) {
     128            psString string = NULL;     // String with values
     129            psStringAppend(&string, "PSF %d: ", i);
     130            float x = numCols / 2.0, y = numRows / 2.0; // Coordinates of interest
     131            for (int j = 4; j < psf->params->n; j++) {
     132                pmTrend2D *trend = psf->params->data[j]; // Trend of interest
     133                double val = pmTrend2DEval(trend, x, y);
     134                double err;
     135                switch (trend->mode) {
     136                  case PM_TREND_POLY_ORD:
     137                  case PM_TREND_POLY_CHEB:
     138                    err = NAN;
     139                    break;
     140                  case PM_TREND_MAP:
     141                    err = psImageUnbinPixel(x, y, trend->map->error, trend->map->binning);
     142                    break;
     143                  default:
     144                    psAbort("Unsupported mode: %x", trend->mode);
     145                }
     146                psStringAppend(&string, "%lf %lf   ", val, err);
     147            }
     148            psTrace("psModules.imcombine", 1, "%s\n", string);
     149            psFree(string);
     150        }
     151
     152        // Test PSF
     153        {
     154            bool goodPSF = false;       // Is there a PSF that we can use?
     155            int xNum = PS_MAX(psf->trendNx, 1), yNum = PS_MAX(psf->trendNy, 1); // Number of positions to check
     156            for (int j = 0; j < yNum && !goodPSF; j++) {
     157                float y = ((float)j + 0.5) / (float)yNum * numRows; // Position on image
     158                for (int i = 0; i < xNum && !goodPSF; i++) {
     159                    float x = ((float)i + 0.5) / (float)xNum * numCols; // Position on image
     160                    pmModelClassSetLimits(PM_MODEL_LIMITS_IGNORE);
     161                    pmModel *model = pmModelFromPSFforXY(psf, x, y, PEAK_FLUX); // Test model
     162                    if (!model) {
     163                        continue;
     164                    }
     165                    model->modelSetLimits(PM_MODEL_LIMITS_MODERATE);
     166                    bool limits = true; // Model within limits?
     167                    for (int j = 0; j < model->params->n && limits; j++) {
     168                        if (!model->modelLimits(PS_MINIMIZE_PARAM_MIN, j, model->params->data.F32, NULL) ||
     169                            !model->modelLimits(PS_MINIMIZE_PARAM_MAX, j, model->params->data.F32, NULL)) {
     170                            limits = false;
     171                        }
     172                    }
     173                    psFree(model);
     174                    if (limits) {
     175                        goodPSF = true;
     176                    }
     177                }
     178            }
     179            if (!goodPSF) {
     180                psWarning("PSF %d is completely bad --- not including in envelope calculation.", i);
     181                continue;
     182            }
     183        }
     184
    124185        pmResiduals *resid = psf->residuals;// PSF residuals
    125186        psf->residuals = NULL;
    126         if (!pmReadoutFakeFromSources(fakeRO, fakeSize, fakeSize, fakes, xOffset, yOffset, psf,
    127                                       NAN, radius, true, true)) {
     187        pmModelClassSetLimits(PM_MODEL_LIMITS_MODERATE);
     188        if (!pmReadoutFakeFromSources(fakeRO, fakeSize, fakeSize, fakes, 0, xOffset, yOffset, psf,
     189                                      NAN, radius, true, false)) {
    128190            psError(PS_ERR_UNKNOWN, false, "Unable to generate fake readout.");
    129191            psFree(envelope);
     
    144206            float y = source->peak->yf + yOffset->data.S32[j]; // y coordinate of source
    145207
    146             double flux = fakeRO->image->data.F32[(int)y][(int)x];
     208#ifdef PEAK_NORM
     209            // Perhaps I'm being paranoid, but specify a range to check
     210            int uMax = PS_MIN(x + radius, numCols - 1), uMin = PS_MAX(x - radius, 0);
     211            int vMax = PS_MIN(y + radius, numRows - 1), vMin = PS_MAX(y - radius, 0);
     212
     213            double flux = -INFINITY;    // Peak flux
     214            for (int v = vMin; v <= vMax; v++) {
     215                for (int u = uMin; u <= uMax; u++) {
     216                    if (fakeRO->image->data.F32[v][u] > flux) {
     217                        flux = fakeRO->image->data.F32[v][u];
     218                    }
     219                }
     220            }
    147221            if (!isfinite(flux) || flux < 0) {
    148222                continue;
    149223            }
    150224            float norm = PEAK_FLUX / flux; // Normalisation for source
     225#endif
    151226            psRegion region = psRegionSet(x - radius, x + radius, y - radius, y + radius); // PSF region
    152227            psImage *subImage = psImageSubset(fakeRO->image, region); // Subimage of fake PSF
    153228            psImage *subEnv = psImageSubset(envelope, region); // Subimage of envelope
     229#ifdef PEAK_NORM
    154230            psBinaryOp(subImage, subImage, "*", psScalarAlloc(norm, PS_TYPE_F32));
     231#endif
    155232            psBinaryOp(subEnv, subEnv, "MAX", subImage);
    156233            psFree(subImage);
     
    163240            }
    164241            float srcRadius = model->modelRadius(model->params, PS_SQR(VARIANCE_VAL)); // Radius for source
     242            psFree(model);
    165243            if (srcRadius == 0) {
    166244                continue;
     
    298376        }
    299377
    300         // measure the source moments: tophat windowing, no pixel S/N cutoff
    301         if (!pmSourceMoments(source, maxRadius, 0.0, 1.0)) {
     378        // measure the source moments: tophat windowing, no pixel S/N cutoff
     379        // XXX probably should be passing the maskVal to this function so we can pass it along here...
     380        if (!pmSourceMoments(source, maxRadius, 0.0, 1.0, maskVal)) {
    302381            // Can't do anything about it; limp along as best we can
    303382            psErrorClear();
     
    320399    options->poissonErrorsParams = true;
    321400    options->stats = psStatsAlloc(PSF_STATS);
    322     options->radius = maxRadius;
     401    options->fitRadius = maxRadius;
     402    options->apRadius = maxRadius; // XXX need to decide if aperture mags need a different radius
    323403    options->psfTrendMode = PM_TREND_MAP;
    324404    options->psfTrendNx = xOrder;
     
    331411
    332412    pmSourceFitModelInit(SOURCE_FIT_ITERATIONS, 0.01, VARIANCE_VAL, true);
     413    pmModelClassSetLimits(PM_MODEL_LIMITS_STRICT); // Important for getting a good stack target PSF
    333414
    334415    pmPSFtry *try = pmPSFtryModel(fakes, modelName, options, 0, 0xff);
     
    343424    pmPSF *psf = psMemIncrRefCounter(try->psf); // Output PSF
    344425    psFree(try);
     426
     427    if (psTraceGetLevel("psModules.imcombine") >= 1) {
     428        psString string = NULL;     // String with values
     429        psStringAppend(&string, "Envelope PSF: ");
     430        float x = numCols / 2.0, y = numRows / 2.0; // Coordinates of interest
     431        for (int j = 4; j < psf->params->n; j++) {
     432            pmTrend2D *trend = psf->params->data[j]; // Trend of interest
     433            double val = pmTrend2DEval(trend, x, y);
     434            double err;
     435            switch (trend->mode) {
     436              case PM_TREND_POLY_ORD:
     437              case PM_TREND_POLY_CHEB:
     438                err = NAN;
     439                break;
     440              case PM_TREND_MAP:
     441                err = psImageUnbinPixel(x, y, trend->map->error, trend->map->binning);
     442                break;
     443              default:
     444                psAbort("Unsupported mode: %x", trend->mode);
     445            }
     446            psStringAppend(&string, "%lf %lf   ", val, err);
     447        }
     448        psTrace("psModules.imcombine", 1, "%s\n", string);
     449        psFree(string);
     450    }
    345451
    346452#ifdef TESTING
     
    357463
    358464        pmReadout *generated = pmReadoutAlloc(NULL); // Generated image
    359         pmReadoutFakeFromSources(generated, numCols, numRows, fakes, NULL, NULL, psf, NAN, radius,
     465        pmReadoutFakeFromSources(generated, numCols, numRows, fakes, 0, NULL, NULL, psf, NAN, radius,
    360466                                 false, true);
    361467        {
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmPSFEnvelope.h

    r15837 r27840  
    1717                     int radius,        // Radius of each PSF
    1818                     const char *modelName, // Name of PSF model to use
    19                      int xOrder, int yOrder // Order for PSF variation
     19                     int xOrder, int yOrder, // Order for PSF variation
     20                     psImageMaskType maskVal
    2021    );
    2122
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmStack.c

    r23775 r27840  
    3030#define PIXEL_LIST_BUFFER 100           // Number of entries to add to pixel list at a time
    3131#define PIXEL_MAP_BUFFER 2              // Number of entries to add to pixel map at a time
    32 #define ADD_VARIANCE                    // Allow additional variance (besides variance factor)?
     32//#define ADD_VARIANCE                    // Allow additional variance (besides variance factor)?
    3333#define NUM_DIRECT_STDEV 5              // For less than this number of values, measure stdev directly
    3434
     35
    3536//#define TESTING                         // Enable test output
    36 //#define TEST_X 1085                     // x coordinate to examine
    37 //#define TEST_Y 3371                     // y coordinate to examine
     37//#define TEST_X 843-1                     // x coordinate to examine
     38//#define TEST_Y 813-1                     // y coordinate to examine
     39//#define TEST_RADIUS 0                    // Radius to examine
    3840
    3941
     
    4244typedef struct {
    4345    psVector *pixels;                   // Pixel values
    44     psVector *masks;                    // Pixel masks
    4546    psVector *variances;                // Pixel variances
    4647    psVector *weights;                  // Pixel weightings
     48    psVector *exps;                     // Pixel exposures
    4749    psVector *sources;                  // Pixel sources (which image did they come from?)
    4850    psVector *limits;                   // Rejection limits
     51    psVector *suspects;                 // Pixel is suspect?
    4952    psVector *sort;                     // Buffer for sorting (to get a robust estimator of the standard dev)
    5053} combineBuffer;
     
    5356{
    5457    psFree(buffer->pixels);
    55     psFree(buffer->masks);
    5658    psFree(buffer->variances);
    5759    psFree(buffer->weights);
     60    psFree(buffer->exps);
    5861    psFree(buffer->sources);
    5962    psFree(buffer->limits);
     63    psFree(buffer->suspects);
    6064    psFree(buffer->sort);
    6165    return;
     
    6973
    7074    buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32);
    71     buffer->masks = psVectorAlloc(numImages, PS_TYPE_VECTOR_MASK);
    7275    buffer->variances = psVectorAlloc(numImages, PS_TYPE_F32);
    7376    buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32);
     77    buffer->exps = psVectorAlloc(numImages, PS_TYPE_F32);
    7478    buffer->sources = psVectorAlloc(numImages, PS_TYPE_U16);
    7579    buffer->limits = psVectorAlloc(numImages, PS_TYPE_F32);
     80    buffer->suspects = psVectorAlloc(numImages, PS_TYPE_U8);
    7681    buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32);
    7782    return buffer;
     
    9398static bool combinationMeanVariance(float *mean, // Mean value, to return
    9499                                    float *var, // Variance value, to return
     100                                    float *exp, // Exposure time, to return
     101                                    float *expWeight,          // Weighted exposure time, to return
    95102                                    const psVector *values, // Values to combine
    96103                                    const psVector *variances, // Pixel variances to combine
     104                                    const psVector *exps,      // Exposure times to combine
    97105                                    const psVector *weights // Weights to apply
    98106                                    )
     
    119127    float sumVarianceWeight = 0.0;     // Sum of the pixel variances multiplied by the global weights
    120128    float sumWeight = 0.0;              // Sum of the image weights
     129    float sumExp = 0.0;                 // Sum of the exposure time
     130    float sumExpWeight = 0.0;           // Sum of the exposure time multiplied by the global weights
     131    int numGood = 0;                    // Number of good exposures
    121132    for (int i = 0; i < values->n; i++) {
    122133        sumValueWeight += values->data.F32[i] * weights->data.F32[i];
     
    125136            sumVarianceWeight += variances->data.F32[i] * PS_SQR(weights->data.F32[i]);
    126137        }
     138        if (exps) {
     139            sumExp += exps->data.F32[i];
     140            sumExpWeight += exps->data.F32[i] * weights->data.F32[i];
     141            numGood++;
     142        }
    127143    }
    128144
     
    134150    if (var) {
    135151        *var = sumVarianceWeight / PS_SQR(sumWeight);
     152    }
     153    if (exp) {
     154        *exp = sumExp;
     155    }
     156    if (expWeight) {
     157        *expWeight = sumExpWeight;
    136158    }
    137159    return true;
     
    143165                                   float *stdev, // Standard deviation value, to return
    144166                                   const psVector *values, // Values to combine
    145                                    const psVector *masks, // Mask to apply
    146167                                   psVector *sortBuffer // Buffer for sorting
    147168                                   )
    148169{
    149170    assert(values);
    150     assert(!masks || values->n == masks->n);
    151171    assert(values->type.type == PS_TYPE_F32);
    152     assert(!masks || masks->type.type == PS_TYPE_VECTOR_MASK);
    153172    assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32);
    154173
    155     // Need to filter out clipped values
    156     int num = 0;            // Number of valid values
    157     for (int i = 0; i < values->n; i++) {
    158         if (!masks || !masks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    159             sortBuffer->data.F32[num++] = values->data.F32[i];
    160         }
    161     }
    162     sortBuffer->n = num;
    163     if (!psVectorSortInPlace(sortBuffer)) {
     174    int num = values->n;                // Number of values
     175    sortBuffer = psVectorSortIndex(sortBuffer, values);
     176    if (!sortBuffer) {
     177        *median = NAN;
     178        *stdev = NAN;
    164179        return false;
    165180    }
     
    167182    if (num == 3) {
    168183        // Attempt to measure standard deviation with only three values (and one of those possibly corrupted)
    169         *median = sortBuffer->data.F32[1];
     184        *median = values->data.F32[sortBuffer->data.S32[1]];
    170185        if (stdev) {
    171             float diff1 = sortBuffer->data.F32[0] - *median;
    172             float diff2 = sortBuffer->data.F32[2] - *median;
     186            float diff1 = values->data.F32[sortBuffer->data.S32[0]] - *median;
     187            float diff2 = values->data.F32[sortBuffer->data.S32[2]] - *median;
    173188            // This factor of sqrt(2) might not be exact, but it's about right
    174189            *stdev = M_SQRT2 * PS_MIN(fabsf(diff1), fabsf(diff2));
    175190        }
    176191    } else {
    177         *median = num % 2 ? sortBuffer->data.F32[num / 2] :
    178             (sortBuffer->data.F32[num / 2 - 1] + sortBuffer->data.F32[num / 2]) / 2.0;
     192        *median = num % 2 ? values->data.F32[sortBuffer->data.S32[num / 2]] :
     193            (values->data.F32[sortBuffer->data.S32[num / 2 - 1]] +
     194             values->data.F32[sortBuffer->data.S32[num / 2]]) / 2.0;
    179195        if (stdev) {
    180196            if (num <= NUM_DIRECT_STDEV) {
     
    182198                double sum = 0.0;
    183199                for (int i = 0; i < num; i++) {
    184                     sum += PS_SQR(sortBuffer->data.F32[i] - *median);
     200                    sum += PS_SQR(values->data.F32[sortBuffer->data.S32[i]] - *median);
    185201                }
    186202                *stdev = sqrt(sum / (double)(num - 1));
    187203            } else {
    188204                // Standard deviation from the interquartile range
    189                 *stdev = 0.74 * (sortBuffer->data.F32[(int)(0.75 * num)] -
    190                                  sortBuffer->data.F32[(int)(0.25 * num)]);
     205                *stdev = 0.74 * (values->data.F32[sortBuffer->data.S32[(int)(0.75 * num)]] -
     206                                 values->data.F32[sortBuffer->data.S32[(int)(0.25 * num)]]);
    191207            }
    192208        }
     
    195211    return true;
    196212}
    197 
    198 #if 0
    199 // Return the weighted median for the pixels
    200 // This does not appear to produce as clean images as the weighted Olympic mean
    201 static float combinationWeightedMedian(const psVector *values, // Values to combine
    202                                        const psVector *weights, // Weights to combine
    203                                        const psVector *masks, // Mask to apply
    204                                        psVector *sortBuffer // Buffer for sorting
    205                                        )
    206 {
    207     double sumWeight = 0.0;             // Sum of weights
    208     for (int j = 0; j < values->n; j++) {
    209         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
    210             continue;
    211         }
    212         sumWeight += weights->data.F32[j];
    213     }
    214 
    215     sortBuffer = psVectorSortIndex(sortBuffer, values);
    216     double target = sumWeight / 2.0;    // Target weight
    217 
    218     int dominant = -1;                  // Index of dominant value, if any
    219     double cumulativeWeight = 0.0;      // Sum of weights
    220     for (int j = 0; j < values->n; j++) {
    221         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
    222             continue;
    223         }
    224         int index = sortBuffer->data.S32[j];  // Index of value of interest
    225         float weight = weights->data.F32[index]; // Weight for value of interest
    226         if (weight >= target) {
    227             // Get the weighted median of the rest
    228             dominant = index;
    229             sumWeight -= weight;
    230             target = sumWeight / 2.0;
    231             continue;
    232         }
    233         cumulativeWeight += weight;
    234         if (cumulativeWeight >= target) {
    235             float median = values->data.F32[index]; // Weighted median median
    236             if (dominant != -1) {
    237                 // In the case that a single value contains a disproportionate weight compared to the rest,
    238                 // we use a weighted mean between that dominant value and the weighted median of the rest.
    239                 return (values->data.F32[dominant] * weights->data.F32[dominant] + median * sumWeight) /
    240                     (weights->data.F32[dominant] + sumWeight);
    241             }
    242             return median;
    243         }
    244     }
    245 
    246     return NAN;
    247 }
    248 #endif
    249213
    250214// Return the weighted Olympic mean for the pixels
    251215static float combinationWeightedOlympic(const psVector *values, // Values to combine
    252216                                        const psVector *weights, // Weights to combine
    253                                         const psVector *masks, // Mask to apply
    254217                                        float frac, // Fraction to discard
    255218                                        psVector *sortBuffer // Buffer for sorting
    256219                                        )
    257220{
    258     int numGood = 0;                    // Number of good values
    259     for (int i = 0; i < values->n; i++) {
    260         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    261             continue;
    262         }
    263         numGood++;
    264     }
     221    int numGood = values->n;            // Number of good values
    265222
    266223    int numBad = frac * numGood + 0.5;  // Number of bad values
     
    272229    for (int i = 0, j = 0; i < values->n; i++) {
    273230        int index = sortBuffer->data.S32[i]; // Index of interest
    274         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[index]) {
    275             continue;
    276         }
    277231        j++;
    278232        if (j > high) {
     
    290244
    291245// Mark a pixel for inspection
    292 static inline void combineInspect(const psArray *inputs, // Stack data
    293                                   int x, int y, // Pixel
    294                                   int source // Source image index
    295                                   )
    296 {
     246// Value in pixel doesn't seem to agree with the stack, so need to look closer
     247static inline void combineMarkInspect(const psArray *inputs, // Stack data
     248                                      int x, int y, // Pixel
     249                                      int source // Source image index
     250                                      )
     251{
     252#ifdef TESTING
     253    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     254        fprintf(stderr, "Marking image %d, pixel %d,%d for inspection\n", source, x, y);
     255    }
     256#endif
    297257    pmStackData *data = inputs->data[source]; // Stack data of interest
    298258    if (!data) {
     
    304264}
    305265
    306 // Given a stack of images, combine with optional rejection.
    307 // Pixels in the stack that are rejected are marked for subsequent inspection
    308 static void combinePixels(psImage *image, // Combined image, for output
    309                           psImage *mask, // Combined mask, for output
    310                           psImage *variance, // Combined variance map, for output
    311                           const psArray *inputs, // Stack data
    312                           const psVector *weights, // Global (single value) weights for data, or NULL
    313                           const psVector *addVariance, // Additional variance for data
    314                           const psVector *reject, // Indices of pixels to reject, or NULL
    315                           int x, int y, // Coordinates of interest; frame of output image
    316                           psImageMaskType maskVal, // Value to mask
    317                           psImageMaskType bad, // Value to give bad pixels
    318                           int numIter, // Number of rejection iterations
    319                           float rej, // Number of standard deviations at which to reject
    320                           float sys,    // Relative systematic error
    321                           float discard,// Fraction of values to discard (Olympic weighted mean)
    322                           bool useVariance, // Use variance for rejection when combining?
    323                           bool safe,    // Combine safely?
    324                           bool rejectInspect, // Reject values marked for inspection from combination?
    325                           combineBuffer *buffer // Buffer for combination; to avoid multiple allocations
    326                          )
     266// Mark a pixel for rejection
     267// Cannot possibly inspect this pixel and confirm that it's good.
     268// e.g., Only a single input
     269static inline void combineMarkReject(const psArray *inputs, // Stack data
     270                                     int x, int y, // Pixel
     271                                     int source // Source image index
     272                                     )
     273{
     274#ifdef TESTING
     275    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     276        fprintf(stderr, "Marking pixel image %d, pixel %d,%d for rejection\n", source, x, y);
     277    }
     278#endif
     279    pmStackData *data = inputs->data[source]; // Stack data of interest
     280    if (!data) {
     281        psWarning("Can't find input data for source %d", source);
     282        return;
     283    }
     284    data->reject = psPixelsAdd(data->reject, data->reject->nalloc, x, y);
     285    return;
     286}
     287
     288
     289// Extract vectors for simple combination/rejection operations
     290static void combineExtract(int *num,                        // Number of good pixels
     291                           bool *suspect,                   // Any suspect pixels?
     292                           combineBuffer *buffer, // Buffer with vectors
     293                           psImage *image, // Combined image, for output
     294                           psImage *mask, // Combined mask, for output
     295                           psImage *variance, // Combined variance map, for output
     296                           const psArray *inputs, // Stack data
     297                           const psVector *weights, // Global (single value) weights for data, or NULL
     298                           const psVector *exps,    // Exposures for data, or NULL
     299                           const psVector *addVariance, // Additional variance for data
     300                           const psVector *reject, // Indices of pixels to reject, or NULL
     301                           int x, int y, // Coordinates of interest; frame of output image
     302                           psImageMaskType maskVal, // Value to mask
     303                           psImageMaskType maskSuspect // Value to suspect
     304                           )
    327305{
    328306    // Rudimentary error checking
     307    assert(buffer);
    329308    assert(image);
    330309    assert(mask);
    331310    assert(inputs);
    332     assert(numIter >= 0);
    333     assert(buffer);
    334     assert(addVariance);
    335     assert((useVariance && variance) || !useVariance);
    336311
    337312    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
    338     psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest
    339313    psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest
    340314    psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
     315    psVector *pixelExps = buffer->exps;       // Exposure times
    341316    psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
    342317    psVector *pixelLimits = buffer->limits; // Limits for the pixel of interest
    343     psVector *sort = buffer->sort;      // Sort buffer
     318    psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect?
     319
     320    if (suspect) {
     321        *suspect = false;
     322    }
    344323
    345324    // Extract the pixel and mask data
    346     int num = 0;                        // Number of good images
     325    int numGood = 0;                    // Number of good pixels
    347326    for (int i = 0, j = 0; i < inputs->n; i++) {
    348327        // Check if this pixel has been rejected.  Assumes that the rejection pixel list is sorted --- it
     
    364343        }
    365344
     345        pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskSuspect ?
     346            true : false;
     347
    366348        psImage *image = data->readout->image; // Image of interest
    367349        psImage *variance = data->readout->variance; // Variance map of interest
    368         pixelData->data.F32[num] = image->data.F32[yIn][xIn];
     350        pixelData->data.F32[numGood] = image->data.F32[yIn][xIn];
    369351        if (variance) {
    370             pixelVariances->data.F32[num] = variance->data.F32[yIn][xIn] * addVariance->data.F32[i];
    371         }
    372         pixelWeights->data.F32[num] = data->weight;
    373         pixelSources->data.U16[num] = i;
    374         num++;
    375     }
    376     pixelData->n = num;
     352            pixelVariances->data.F32[numGood] = variance->data.F32[yIn][xIn] * addVariance->data.F32[i];
     353        }
     354        pixelWeights->data.F32[numGood] = data->weight;
     355        pixelExps->data.F32[numGood] = data->exp;
     356        pixelSources->data.U16[numGood] = i;
     357        numGood++;
     358    }
     359    pixelData->n = numGood;
    377360    if (variance) {
    378         pixelVariances->n = num;
    379     }
    380     pixelWeights->n = num;
    381     pixelSources->n = num;
    382     pixelLimits->n = num;
    383 
    384 #ifdef TESTING
    385     if (x == TEST_X && y == TEST_Y) {
    386         for (int i = 0; i < num; i++) {
    387             fprintf(stderr, "Input %d (%" PRIu16 "): %f %f %f\n",
    388                     i, pixelSources->data.U16[i], pixelData->data.F32[i], pixelVariances->data.F32[i],
    389                     pixelWeights->data.F32[i]);
    390         }
    391     }
    392 #endif
    393 
    394     // The sensible thing to do varies according to how many good pixels there are.
     361        pixelVariances->n = numGood;
     362    }
     363    pixelWeights->n = numGood;
     364    pixelSources->n = numGood;
     365    pixelLimits->n = numGood;
     366    pixelSuspects->n = numGood;
     367    *num = numGood;
     368
     369#ifdef TESTING
     370    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     371        for (int i = 0; i < numGood; i++) {
     372            fprintf(stderr, "Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f %d\n",
     373                    i, x, y, pixelSources->data.U16[i], pixelData->data.F32[i], pixelVariances->data.F32[i],
     374                    addVariance->data.F32[i], pixelWeights->data.F32[i], pixelExps->data.F32[i],
     375                    pixelSuspects->data.U8[i]);
     376        }
     377    }
     378#endif
     379
     380    return;
     381}
     382
     383
     384// Combine pixels
     385static void combinePixels(psImage *image, // Combined image, for output
     386                          psImage *mask, // Combined mask, for output
     387                          psImage *variance, // Combined variance map, for output
     388                          psImage *exp,   // Exposure map (time), for output
     389                          psImage *expnum,       // Exposure map (number) for output
     390                          psImage *expweight,    // Exposure map (weighted time) for output
     391                          int num,      // Number of good pixels
     392                          combineBuffer *buffer, // Buffer with vectors
     393                          int x, int y, // Coordinates of interest; frame of output image
     394                          psImageMaskType bad, // Value for bad pixels
     395                          bool safe,           // Safe combination?
     396                          float invTotalWeight    // Inverse of total weight for all inputs
     397                          )
     398{
     399    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     400    psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest
     401    psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
     402    psVector *pixelExps = buffer->exps;       // Exposure times
     403
    395404    // Default option is that the pixel is bad
    396405    float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map
    397406    psImageMaskType maskValue = bad;    // Value for combined mask
     407    float expValue = 0.0, expWeightValue = NAN; // Exposure value (straight, and weighted)
     408
    398409    switch (num) {
    399       case 0:
    400         // Nothing to combine: it's bad
    401 #ifdef TESTING
    402     if (x == TEST_X && y == TEST_Y) {
    403         fprintf(stderr, "No inputs to combine, pixel is bad.\n");
    404     }
    405 #endif
    406         break;
     410      case 0: {
     411          // Nothing to combine: it's bad
     412#ifdef TESTING
     413          if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     414              fprintf(stderr, "No inputs to combine, pixel %d,%d is bad.\n", x, y);
     415          }
     416#endif
     417          break;
     418      }
    407419      case 1: {
    408420          // Accept the single pixel unless we have to be safe
    409421          if (!safe) {
    410 #ifdef TESTING
    411               if (x == TEST_X && y == TEST_Y) {
    412                   fprintf(stderr, "Single input to combine, safety off.\n");
    413               }
    414 #endif
    415422              imageValue = pixelData->data.F32[0];
    416423              if (variance) {
    417424                  varianceValue = pixelVariances->data.F32[0];
    418425              }
     426              if (exp) {
     427                  expValue = pixelExps->data.F32[0];
     428                  expWeightValue = pixelExps->data.F32[0];
     429              }
    419430              maskValue = 0;
     431#ifdef TESTING
     432              if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     433                  fprintf(stderr, "Single input to combine, safety off, pixel %d,%d --> %f\n",
     434                          x, y, imageValue);
     435              }
     436#endif
    420437          }
    421438#ifdef TESTING
    422           else if (x == TEST_X && y == TEST_Y) {
    423               fprintf(stderr, "Single input to combine, safety on, pixel is bad.\n");
     439          else if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     440              fprintf(stderr, "Single input to combine, safety on, pixel %d,%d is bad.\n", x, y);
    424441          }
    425442#endif
     
    427444      }
    428445      case 2: {
    429           // Accept the mean of the pixels only if we're going to reject based on the variance, or we're not
    430           // playing safe
    431           if (useVariance || !safe) {
    432               float mean, var;   // Mean and variance from combination
    433               if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
    434                   imageValue = mean;
    435                   varianceValue = var;
     446          // Automatically accept the mean of the pixels only if we're not playing safe
     447          if (!safe) {
     448              if (combinationMeanVariance(&imageValue, &varianceValue, &expValue, &expWeightValue,
     449                                          pixelData, pixelVariances, pixelExps, pixelWeights)) {
    436450                  maskValue = 0;
    437451#ifdef TESTING
    438                   if (x == TEST_X && y == TEST_Y) {
    439                       fprintf(stderr, "Two inputs to combine using variance/unsafe --> %f %f\n",
    440                               mean, var);
     452                  if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     453                      fprintf(stderr, "Two inputs to combine using unsafe, pixel %d,%d --> %f %f\n",
     454                              x, y, imageValue, varianceValue);
    441455                  }
    442456#endif
    443457              }
    444458          }
    445           if (useVariance && numIter > 0) {
    446               // Use variance to check that the two are consistent
    447               float diff = 0.5 * (pixelData->data.F32[0] - pixelData->data.F32[1]); // Mean flux difference
    448               float var1 = pixelVariances->data.F32[0]; // Variance of first
    449               float var2 = pixelVariances->data.F32[1]; // Variance of second
    450               // Systematic error contributes to the rejection level
    451               var1 += PS_SQR(sys * pixelData->data.F32[0]);
    452               var2 += PS_SQR(sys * pixelData->data.F32[1]);
    453 
    454               float sigma2 = var1 + var2; // Combined variance
    455               if (PS_SQR(diff) > PS_SQR(rej) * sigma2) {
    456                   // Not consistent: mark both for inspection
    457                   if (rejectInspect) {
    458                       imageValue = NAN;
    459                       varianceValue = NAN;
    460                       maskValue = bad;
    461                   } else {
    462                       combineInspect(inputs, x, y, pixelSources->data.U16[0]);
    463                       combineInspect(inputs, x, y, pixelSources->data.U16[1]);
    464                   }
    465 #ifdef TESTING
    466                   if (x == TEST_X && y == TEST_Y) {
    467                       fprintf(stderr, "Both pixels marked for inspection (%f > %f x %f\n)",
    468                               diff, rej, sqrtf(sigma2));
    469                   }
    470 #endif
     459#ifdef TESTING
     460          else {
     461              if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     462                  fprintf(stderr, "Two inputs to combine, safety on, pixel %d,%d is bad\n", x, y);
    471463              }
    472464          }
     465#endif
    473466          break;
    474467      }
    475468      default: {
    476           // Record the value derived with no clipping, because pixels rejected using the harsh clipping
    477           // applied in the first pass might later be accepted.
    478           float mean, var;           // Mean and variance of the combination
    479           if (!combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
     469          // Can combine without too much worrying
     470          if (!combinationMeanVariance(&imageValue, &varianceValue, &expValue, &expWeightValue,
     471                                       pixelData, pixelVariances, pixelExps, pixelWeights)) {
    480472              break;
    481473          }
    482           imageValue = mean;
    483           varianceValue = var;
    484474          maskValue = 0;
    485475#ifdef TESTING
    486           if (x == TEST_X && y == TEST_Y) {
    487               fprintf(stderr, "Combined inputs: %f %f\n", mean, var);
     476          if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     477              fprintf(stderr, "Combined inputs, pixel %d,%d --> %f %f\n", x, y, imageValue, varianceValue);
    488478          }
    489479#endif
    490 
    491           // Prepare for clipping iteration
    492           if (numIter > 0) {
    493               pixelMasks->n = num;
    494               psVectorInit(pixelMasks, 0);
    495               if (useVariance) {
    496                   // Convert to rejection limits --- saves doing it later.
    497                   // Using squared rejection limit because it's cheaper than sqrts
    498                   float rej2 = PS_SQR(rej); // Rejection level squared
    499                   double sumWeights = 0.0;
    500                   for (int i = 0; i < num; i++) {
    501                       sumWeights += pixelWeights->data.F32[i];
    502                   }
    503                   for (int i = 0; i < num; i++) {
    504                       // Systematic error contributes to the rejection level
    505                       float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
    506 #ifdef TESTING
    507                       // Correct variance for comparison against weighted mean including itself
    508                       float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights;
    509                       if (x == TEST_X && y == TEST_Y) {
    510                           fprintf(stderr, "Variance %d (%d): %f %f %f\n", i, pixelSources->data.U16[i],
    511                                   pixelVariances->data.F32[i], sysVar, compare);
    512                       }
    513 #endif
    514 
    515                       pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
    516                   }
    517               }
    518           }
    519 
    520           // The clipping that follows is solely to identify suspect pixels.
    521           // These suspect pixels will be inspected in more detail by other functions.
    522           int numClipped = INT_MAX;     // Number of pixels clipped per iteration
    523           int totalClipped = 0;         // Total number of pixels clipped
    524           for (int i = 0; i < numIter && numClipped > 0 && num - totalClipped > 2; i++) {
    525               numClipped = 0;
    526               float median = NAN;       // Middle of distribution
    527               float limit = NAN;        // Rejection limit
    528               if (!useVariance) {
    529                   float stdev;  // Median and stdev of the combination, for rejection
    530                   if (!combinationMedianStdev(&median, useVariance ? NULL : &stdev,
    531                                               pixelData, pixelMasks, sort)) {
    532                       psWarning("Bad median/stdev at %d,%d", x, y);
    533                       break;
    534                   }
    535                   limit = rej * stdev;
    536 #ifdef TESTING
    537                   if (x == TEST_X && y == TEST_Y) {
    538                       fprintf(stderr, "Rejecting without variance; rejection limit: %f\n", limit);
    539                   }
    540 #endif
    541               } else {
    542 #ifdef TESTING
    543                   if (x == TEST_X && y == TEST_Y) {
    544                       fprintf(stderr, "Rejecting with variance...\n");
    545                   }
    546 #endif
    547                   median = combinationWeightedOlympic(pixelData, pixelWeights, pixelMasks, discard, sort);
    548               }
    549 
    550 #ifdef TESTING
    551               if (x == TEST_X && y == TEST_Y) {
    552                   fprintf(stderr, "Median: %f\n", median);
    553               }
    554 #endif
    555 
    556 
    557 // Mask a pixel for inspection
    558 #define MASK_PIXEL_FOR_INSPECTION() \
    559     pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xff; \
    560     if (!rejectInspect) { \
    561         combineInspect(inputs, x, y, pixelSources->data.U16[j]); \
    562     } \
    563     numClipped++; \
    564     totalClipped++;
    565 
    566               for (int j = 0; j < num; j++) {
    567                   if (pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
    568                       continue;
    569                   }
    570                   float diff = pixelData->data.F32[j] - median; // Difference from expected
    571                   if (useVariance) {
    572                       // Comparing squares --- cheaper than lots of sqrts
    573                       // pixelVariances includes the rejection limit, from above
    574                       if (PS_SQR(diff) > pixelLimits->data.F32[j]) {
    575                           MASK_PIXEL_FOR_INSPECTION();
    576 #ifdef TESTING
    577                           if (x == TEST_X && y == TEST_Y) {
    578                               fprintf(stderr, "Rejecting input %d based on variance: %f > %f\n",
    579                                       j, diff, sqrtf(pixelLimits->data.F32[j]));
    580                           }
    581 #endif
    582                       }
    583                   } else if (fabsf(diff) > limit) {
    584                       MASK_PIXEL_FOR_INSPECTION();
    585 #ifdef TESTING
    586                       if (x == TEST_X && y == TEST_Y) {
    587                           fprintf(stderr, "Rejecting input %d based on distribution: %f > %f\n",
    588                                   j, diff, limit);
    589                       }
    590 #endif
    591                   }
    592               }
    593           }
    594 
    595           if (rejectInspect && totalClipped > 0) {
    596               // Get rid of the masked values
    597               // The alternative to this is to make combinationMeanVariance() accept a mask
    598               int good = 0;            // Index of good value
    599               for (int i = 0; i < num; i++) {
    600                   if (pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    601                       continue;
    602                   }
    603                   if (i != good) {
    604                       pixelData->data.F32[good] = pixelData->data.F32[i];
    605                       pixelVariances->data.F32[good] = pixelVariances->data.F32[i];
    606                       pixelWeights->data.F32[good] = pixelWeights->data.F32[i];
    607                       pixelData->data.F32[good] = pixelData->data.F32[i];
    608                   }
    609                   good++;
    610               }
    611               pixelData->n = good;
    612               pixelVariances->n = good;
    613               pixelWeights->n = good;
    614               if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
    615                   imageValue = mean;
    616                   varianceValue = var;
    617                   maskValue = 0;
    618               } else {
    619                   imageValue = NAN;
    620                   varianceValue = NAN;
    621                   maskValue = bad;
    622               }
    623           }
    624 
    625480          break;
    626481      }
     
    632487        variance->data.F32[y][x] = varianceValue;
    633488    }
     489    if (exp) {
     490        exp->data.F32[y][x] = expValue;
     491    }
     492    if (expnum) {
     493        expnum->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = num;
     494    }
     495    if (expweight) {
     496        expweight->data.F32[y][x] = expWeightValue * invTotalWeight;
     497    }
    634498
    635499    return;
     500}
     501
     502
     503// Test pixels to be combined
     504// Returns false to repeat without suspect pixels
     505static bool combineTest(int num,      // Number of good pixels
     506                        bool suspect, // Does the stack contain suspect pixels?
     507                        psArray *inputs,       // Original inputs (for flagging)
     508                        combineBuffer *buffer, // Buffer with vectors
     509                        int x, int y, // Coordinates of interest; frame of output image
     510                        float iter, // Number of rejection iterations per input
     511                        float rej, // Number of standard deviations at which to reject
     512                        float sys,    // Relative systematic error
     513                        float olympic,// Fraction of values to discard (Olympic weighted mean)
     514                        bool useVariance, // Use variance for rejection when combining?
     515                        bool safe    // Combine safely?
     516                        )
     517{
     518    if (iter <= 0) {
     519        return true;
     520    }
     521
     522    int numIter = PS_MAX(iter * num, 1); // Number of iterations
     523
     524#ifdef TESTING
     525    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     526        fprintf(stderr, "Testing pixel %d,%d: %d %f %f %f %d %d\n",
     527                x, y, numIter, rej, sys, olympic, useVariance, safe);
     528    }
     529#endif
     530
     531    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     532    psVector *pixelWeights = buffer->weights; // Is the pixel suspect?
     533    psVector *pixelVariances = buffer->variances; // Variances for the pixel of interest
     534    psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
     535    psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect?
     536    psVector *pixelLimits = buffer->limits; // Is the pixel suspect?
     537
     538    // Set up rejection limits
     539    float rej2 = PS_SQR(rej); // Rejection level squared
     540    if (num > 2 && useVariance) {
     541        // Convert rejection limits --- saves doing it later multiple times
     542        // Using squared rejection limit because it's cheaper than sqrts
     543        double sumWeights = 0.0;
     544        for (int i = 0; i < num; i++) {
     545            sumWeights += pixelWeights->data.F32[i];
     546        }
     547        for (int i = 0; i < num; i++) {
     548            // Systematic error contributes to the rejection level
     549            float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
     550#ifdef TESTING
     551            // Correct variance for comparison against weighted mean including itself
     552            float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights;
     553            if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     554                fprintf(stderr, "Variance %d (%d), pixel %d,%d: %f %f %f\n", i, pixelSources->data.U16[i],
     555                        x, y, pixelVariances->data.F32[i], sysVar, compare);
     556            }
     557#endif
     558            pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
     559        }
     560    }
     561
     562    int maskIndex = 0;                  // Index of pixel to mask
     563    int totalClipped = 0;               // Total number of pixels clipped
     564    for (int i = 0; i < numIter && maskIndex >= 0; i++) {
     565        maskIndex = -1;                 // Nothing to reject
     566
     567        switch (num) {
     568          case 0:
     569            break;
     570          case 1:
     571            if (i == 0 && safe) {
     572                combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     573            }
     574            break;
     575          case 2: {
     576              if (useVariance) {
     577                  // Use variance to check that the two are consistent
     578                  float diff = 0.5 * (pixelData->data.F32[0] - pixelData->data.F32[1]); // Mean flux difference
     579                  float var1 = pixelVariances->data.F32[0]; // Variance of first
     580                  float var2 = pixelVariances->data.F32[1]; // Variance of second
     581                  // Systematic error contributes to the rejection level
     582                  var1 += PS_SQR(sys * pixelData->data.F32[0]);
     583                  var2 += PS_SQR(sys * pixelData->data.F32[1]);
     584
     585                  float sigma2 = var1 + var2; // Combined variance
     586                  if (PS_SQR(diff) > rej2 * sigma2) {
     587                      // Not consistent: don't believe either!
     588                      if (i == 0 && suspect) {
     589                          combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     590                          combineMarkReject(inputs, x, y, pixelSources->data.U16[1]);
     591                      } else {
     592                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     593                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     594                      }
     595#ifdef TESTING
     596                      if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     597                          fprintf(stderr, "Flagged both inputs for pixel %d,%d (%f > %f x %f\n)",
     598                                  x, y, diff, rej, sqrtf(sigma2));
     599                      }
     600#endif
     601                  }
     602              } else if (i == 0 && safe) {
     603                  // Can't test them, and we want to be safe, so reject
     604                  combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     605                  combineMarkReject(inputs, x, y, pixelSources->data.U16[1]);
     606              }
     607              break;
     608          }
     609#if 0
     610          case 3: {
     611              // Want to be a bit careful on the rejection than for a larger number of inputs
     612              if (!useVariance) {
     613                  return combineTestGeneral(num, suspect, inputs, buffer, x, y, numIter, rej, sys,
     614                                            olympic, useVariance, safe, allowSuspect);
     615              }
     616
     617              // Differences between pixel values
     618              float diff01 = pixelData->data.F32[0] - pixelData->data.F32[1];
     619              float diff12 = pixelData->data.F32[1] - pixelData->data.F32[2];
     620              float diff20 = pixelData->data.F32[2] - pixelData->data.F32[0];
     621              // Variance for each pixel
     622              float var0 = pixelVariances->data.F32[0] + PS_SQR(sys * pixelData->data.F32[0]);
     623              float var1 = pixelVariances->data.F32[1] + PS_SQR(sys * pixelData->data.F32[1]);
     624              float var2 = pixelVariances->data.F32[2] + PS_SQR(sys * pixelData->data.F32[2]);
     625              // Errors in pixel differences
     626              float err01 = var0 + var1;
     627              float err12 = var1 + var2;
     628              float err20 = var2 + var0;
     629
     630#ifdef TESTING
     631              if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     632                  fprintf(stderr, "Diff 0-1: %f %f\n", diff01, err01);
     633                  fprintf(stderr, "Diff 1-2: %f %f\n", diff12, err12);
     634                  fprintf(stderr, "Diff 2-0: %f %f\n", diff20, err20);
     635              }
     636#endif
     637
     638              int badPairs = 0;         // Number of bad pairs
     639              bool bad01 = false, bad12 = false, bad20 = false; // Pair is bad?
     640              if (PS_SQR(diff01) > rej2 * err01) {
     641                  bad01 = true;
     642                  badPairs++;
     643              }
     644              if (PS_SQR(diff12) > rej2 * err12) {
     645                  bad12 = true;
     646                  badPairs++;
     647              }
     648              if (PS_SQR(diff20) > rej2 * err20) {
     649                  bad20 = true;
     650                  badPairs++;
     651              }
     652
     653              if (badPairs > 0 && allowSuspect && suspect) {
     654                  return false;
     655              }
     656
     657              switch (badPairs) {
     658                case 0:
     659                  // Nothing to worry about!
     660                  break;
     661                case 1:
     662                  // Can't tell which image is bad, so be sure to get it
     663                  if (bad01) {
     664                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     665                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     666                      break;
     667                  }
     668                  if (bad12) {
     669                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     670                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     671                      break;
     672                  }
     673                  if (bad20) {
     674                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     675                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     676                      break;
     677                  }
     678                  psAbort("Should never get here");
     679                case 2:
     680                  if (bad01 && bad12) {
     681                      // 2 and 0 are good
     682                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     683                      break;
     684                  }
     685                  if (bad12 && bad20) {
     686                      // 0 and 1 are good
     687                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     688                      break;
     689                  }
     690                  if (bad20 && bad01) {
     691                      // 1 and 2 are good
     692                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     693                      break;
     694                  }
     695                  psAbort("Should never get here");
     696                case 3:
     697                  // Everything's bad
     698                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     699                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     700                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     701                  break;
     702              }
     703              break;
     704          }
     705#endif
     706          default: {
     707              if (useVariance) {
     708                  float median = combinationWeightedOlympic(pixelData, pixelWeights,
     709                                                            olympic, buffer->sort); // Median for stack
     710#ifdef TESTING
     711                  if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     712                      fprintf(stderr, "Flag with variance pixel %d,%d: median = %f\n", x, y, median);
     713                  }
     714#endif
     715                  float worst = -INFINITY; // Largest deviation
     716                  for (int j = 0; j < num; j++) {
     717                      float diff = pixelData->data.F32[j] - median; // Difference from expected
     718#ifdef TESTING
     719                      if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     720                          fprintf(stderr, "Testing input %d for pixel %d,%d: %f\n", j, x, y, diff);
     721                      }
     722#endif
     723
     724                      // Comparing squares --- cheaper than lots of sqrts
     725                      // pixelVariances includes the rejection limit, from above
     726                      float diff2 = PS_SQR(diff); // Square difference
     727                      if (diff2 > pixelLimits->data.F32[j]) {
     728                          float dev = diff2 / pixelLimits->data.F32[j]; // Deviation
     729                          if (dev > worst) {
     730                              worst = dev;
     731                              maskIndex = j;
     732                          }
     733                      }
     734                  }
     735              } else {
     736                  float median = NAN, stdev = NAN;  // Median and stdev of the combination, for rejection
     737                  combinationMedianStdev(&median, &stdev, pixelData, buffer->sort);
     738                  float limit = rej * stdev; // Rejection limit
     739#ifdef TESTING
     740                  if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     741                      fprintf(stderr,
     742                              "Flag without variance pixel %d,%d; median = %f, stdev = %f, limit = %f\n",
     743                              x, y, median, stdev, limit);
     744                  }
     745#endif
     746                  float worst = -INFINITY; // Largest deviation
     747                  for (int j = 0; j < num; j++) {
     748                      float diff = fabsf(pixelData->data.F32[j] - median); // Difference from expected
     749
     750                      if (diff > limit) {
     751                          float dev = diff / limit; // Deviation
     752                          if (dev > worst) {
     753                              worst = dev;
     754                              maskIndex = j;
     755                          }
     756                      }
     757                  }
     758              }
     759          }
     760        }
     761
     762        // Do the actual rejection of the pixel
     763        if (maskIndex >= 0) {
     764            if (suspect) {
     765#ifdef TESTING
     766                if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     767                    fprintf(stderr, "Throwing out all suspect pixels for %d,%d\n", x, y);
     768                }
     769#endif
     770                // Throw out all suspect pixels
     771                int numGood = 0;        // Number of good pixels
     772                for (int j = 0; j < num; j++) {
     773                    if (pixelSuspects->data.U8[j]) {
     774                        combineMarkReject(inputs, x, y, pixelSources->data.U16[j]);
     775                        continue;
     776                    }
     777                    if (numGood == j) {
     778                        numGood++;
     779                        continue;
     780                    }
     781                    pixelData->data.F32[numGood] = pixelData->data.F32[j];
     782                    pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j];
     783                    pixelSources->data.U16[numGood] = pixelSources->data.U16[j];
     784                    pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j];
     785                    pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j];
     786                    numGood++;
     787                }
     788                pixelData->n = numGood;
     789                pixelWeights->n = numGood;
     790                pixelSources->n = numGood;
     791                pixelLimits->n = numGood;
     792                pixelVariances->n = numGood;
     793                totalClipped += num - numGood;
     794                num = numGood;
     795                suspect = false;
     796            } else {
     797                // Throw out masked pixel
     798#ifdef TESTING
     799                if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     800                    fprintf(stderr, "Throwing out input %d for pixel %d,%d\n", maskIndex, x, y);
     801                }
     802#endif
     803                combineMarkInspect(inputs, x, y, pixelSources->data.U16[maskIndex]);
     804                int numGood = 0;        // Number of good pixels
     805                for (int j = 0; j < num; j++) {
     806                    if (j == maskIndex) {
     807                        continue;
     808                    }
     809                    if (numGood == j) {
     810                        numGood++;
     811                        continue;
     812                    }
     813                    pixelData->data.F32[numGood] = pixelData->data.F32[j];
     814                    pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j];
     815                    pixelSources->data.U16[numGood] = pixelSources->data.U16[j];
     816                    pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j];
     817                    pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j];
     818                    numGood++;
     819                }
     820                pixelData->n = numGood;
     821                pixelWeights->n = numGood;
     822                pixelSources->n = numGood;
     823                pixelLimits->n = numGood;
     824                pixelVariances->n = numGood;
     825                totalClipped++;
     826                num--;
     827            }
     828        }
     829    }
     830
     831    return true;
    636832}
    637833
     
    639835// Ensure the input array of pmStackData is valid, and get some details out of it
    640836static bool validateInputData(bool *haveVariances, // Do we have variance maps in the sky images?
    641                               bool *haveRejects, // Do we have lists of rejected pixels?
    642837                              int *num,    // Number of inputs
    643838                              int *numCols, int *numRows, // Size of (sky) images
    644                               psArray *input // Input array of pmStackData to validate
     839                              const psArray *input, // Input array of pmStackData to validate
     840                              const pmReadout *output, // Output readout
     841                              const pmReadout *exp    // Exposure map
    645842    )
    646843{
     
    668865        PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false);
    669866    }
    670     *haveRejects = (data->reject != NULL);
     867    bool haveRejects = (data->reject != NULL); // Do we have rejected pixels?
    671868
    672869    // Make sure the rest correspond with the first
     
    681878            return false;
    682879        }
    683         if ((*haveRejects && !data->reject) || (data->reject && !*haveRejects)) {
     880        if ((haveRejects && !data->reject) || (data->reject && !haveRejects)) {
    684881            psError(PS_ERR_UNEXPECTED_NULL, true,
    685882                    "The rejected pixels are specified in some but not all inputs.");
     
    696893            PS_ASSERT_IMAGES_SIZE_EQUAL(data->readout->image, data->readout->variance, false);
    697894            PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false);
     895        }
     896    }
     897
     898    PM_ASSERT_READOUT_NON_NULL(output, false);
     899    if (output->image) {
     900        PS_ASSERT_IMAGE_NON_NULL(output->image, false);
     901        PS_ASSERT_IMAGE_TYPE(output->image, PS_TYPE_F32, false);
     902        PS_ASSERT_IMAGE_NON_NULL(output->mask, false);
     903        PS_ASSERT_IMAGE_TYPE(output->mask, PS_TYPE_IMAGE_MASK, false);
     904        PS_ASSERT_IMAGES_SIZE_EQUAL(output->image, output->mask, false);
     905    }
     906
     907    if (exp) {
     908        PM_ASSERT_READOUT_NON_NULL(exp, false);
     909        if (exp->image) {
     910            PS_ASSERT_IMAGES_SIZE_EQUAL(exp->image, output->image, false);
     911        }
     912        if (exp->mask) {
     913            PS_ASSERT_IMAGES_SIZE_EQUAL(exp->mask, output->image, false);
    698914        }
    699915    }
     
    767983
    768984/// Constructor
    769 pmStackData *pmStackDataAlloc(pmReadout *readout, float weight, float addVariance)
     985pmStackData *pmStackDataAlloc(pmReadout *readout, float weight, float exp, float addVariance)
    770986{
    771987    pmStackData *data = psAlloc(sizeof(pmStackData)); // Stack data, to return
     
    776992    data->inspect = NULL;
    777993    data->weight = weight;
     994    data->exp = exp;
    778995    data->addVariance = addVariance;
    779996
     
    782999
    7831000/// Stack input images
    784 bool pmStackCombine(pmReadout *combined, psArray *input, psImageMaskType maskVal, psImageMaskType bad,
    785                     int kernelSize, int numIter, float rej, float sys, float discard,
    786                     bool entire, bool useVariance, bool safe, bool rejectInspect)
    787 {
    788     PS_ASSERT_PTR_NON_NULL(combined, false);
     1001bool pmStackCombine(pmReadout *combined, pmReadout *expmaps, psArray *input,
     1002                    psImageMaskType maskVal, psImageMaskType maskSuspect,
     1003                    psImageMaskType bad, int kernelSize,
     1004                    float iter, float rej, float sys, float olympic,
     1005                    bool useVariance, bool safe, bool rejection)
     1006{
    7891007    bool haveVariances;                 // Do we have the variance maps?
    790     bool haveRejects;                   // Do we have lists of rejected pixels?
    7911008    int num;                            // Number of inputs
    7921009    int numCols, numRows;               // Size of (sky) images
    793     if (!validateInputData(&haveVariances, &haveRejects, &num, &numCols, &numRows, input)) {
     1010    if (!validateInputData(&haveVariances, &num, &numCols, &numRows, input, combined, expmaps)) {
    7941011        return false;
    7951012    }
    7961013    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    7971014    PS_ASSERT_INT_POSITIVE(bad, false);
    798     PS_ASSERT_INT_NONNEGATIVE(numIter, false);
    7991015    if (isnan(rej)) {
    800         PS_ASSERT_INT_EQUAL(numIter, 0, false);
     1016        PS_ASSERT_FLOAT_EQUAL(iter, 0, false);
    8011017    } else {
     1018        PS_ASSERT_FLOAT_LARGER_THAN(iter, 0, false);
    8021019        PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
    803     }
    804     if (haveRejects) {
    805         // This is a subsequent combination, so expect that the image and mask already exist
    806         PS_ASSERT_IMAGE_NON_NULL(combined->image, false);
    807         PS_ASSERT_IMAGE_TYPE(combined->image, PS_TYPE_F32, false);
    808         PS_ASSERT_IMAGE_NON_NULL(combined->mask, false);
    809         PS_ASSERT_IMAGE_TYPE(combined->mask, PS_TYPE_IMAGE_MASK, false);
    810         PS_ASSERT_IMAGES_SIZE_EQUAL(combined->image, combined->mask, false);
    8111020    }
    8121021    if (useVariance && !haveVariances) {
     
    8171026    psVector *addVariance = psVectorAlloc(num, PS_TYPE_F32); // Additional variance for each image
    8181027    psVector *weights = psVectorAlloc(num, PS_TYPE_F32); // Relative weighting for each image
     1028    psVector *exps = psVectorAlloc(num, PS_TYPE_F32);    // Exposure times for each image
    8191029    psArray *stack = psArrayAlloc(num); // Stack of readouts
     1030    float totalExpWeight = 0.0;           // Total value of all weighted exposure times
     1031    float totalExp = 0.0;                 // Total exposure time
    8201032    for (int i = 0; i < num; i++) {
    8211033        pmStackData *data = input->data[i]; // Stack data for this input
    8221034        if (!data) {
    8231035            weights->data.F32[i] = 0.0;
     1036            exps->data.F32[i] = NAN;
    8241037            continue;
    8251038        }
    8261039        weights->data.F32[i] = data->weight;
     1040        exps->data.F32[i] = data->exp;
     1041        totalExp += exps->data.F32[i];
     1042        totalExpWeight += exps->data.F32[i] * weights->data.F32[i];
    8271043        pmReadout *ro = data->readout;  // Readout of interest
    8281044        stack->data[i] = psMemIncrRefCounter(ro);
     
    8331049        }
    8341050#endif
    835         if (!haveRejects && !data->inspect) {
    836             data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
    837         }
    838     }
     1051        if (!rejection) {
     1052            // Ensure pixels can be put on the appropriate list
     1053            if (!data->inspect) {
     1054                data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     1055            }
     1056            if (!data->reject) {
     1057                data->reject = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     1058            }
     1059        }
     1060    }
     1061    totalExpWeight = totalExp / totalExpWeight;    // Convert to inverse
    8391062
    8401063    int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine
     
    8421065    if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize,
    8431066                                stack)) {
    844         psError(PS_ERR_UNKNOWN, false, "Input stack is not valid.");
     1067        psError(psErrorCodeLast(), false, "Input stack is not valid.");
    8451068        psFree(stack);
    8461069        return false;
     
    8631086    combineBuffer *buffer = combineBufferAlloc(num);
    8641087
    865     if (haveRejects) {
    866         psImage *combinedImage = combined->image; // Combined image
    867         psImage *combinedMask = combined->mask; // Combined mask
    868         psImage *combinedVariance = combined->variance; // Combined variance map
    869 
    870         psArray *pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols,
    871                                              minInputRows, maxInputRows); // Map of pixels to source
    872         psPixels *pixels = NULL;            // Total list of pixels, with no duplicates
     1088    // Pull the products out, allocate if necessary
     1089    psImage *combinedImage = combined->image; // Combined image
     1090    if (!combinedImage) {
     1091        combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1092        combinedImage = combined->image;
     1093    }
     1094    psImage *combinedMask = combined->mask; // Combined mask
     1095    if (!combinedMask) {
     1096        combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     1097        combinedMask = combined->mask;
     1098    }
     1099
     1100    psImage *combinedVariance = combined->variance; // Combined variance map
     1101    if (haveVariances && !combinedVariance) {
     1102        combined->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1103        combinedVariance = combined->variance;
     1104    }
     1105
     1106    psImage *exp = NULL, *expnum = NULL, *expweight = NULL; // Exposure map and exposure number
     1107    if (expmaps) {
     1108        if (!expmaps->image) {
     1109            expmaps->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1110        }
     1111        exp = expmaps->image;
     1112
     1113        if (!expmaps->mask) {
     1114            expmaps->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     1115        }
     1116        expnum = expmaps->mask;
     1117
     1118        if (!expmaps->variance) {
     1119            expmaps->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1120        }
     1121        expweight = expmaps->variance;
     1122    }
     1123
     1124    // Set up rejection list
     1125    psArray *pixelMap = NULL;           // Map of pixels to source
     1126    if (rejection) {
     1127        pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols, minInputRows, maxInputRows);
     1128    }
     1129
     1130    // Combine each pixel
     1131    for (int y = minInputRows; y < maxInputRows; y++) {
     1132        for (int x = minInputCols; x < maxInputCols; x++) {
     1133#ifdef TESTING
     1134            if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     1135                fprintf(stderr, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n",
     1136                        x, y, maskVal, bad, iter, rej, sys, olympic, useVariance, safe, rejection);
     1137            }
     1138#endif
     1139            psVector *reject = NULL; // Images to reject for this pixel
     1140            if (rejection) {
     1141                reject = pixelMapQuery(pixelMap, minInputCols, minInputRows, x, y);
     1142#ifdef TESTING
     1143                if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     1144                    fprintf(stderr, "Rejected inputs for pixel %d,%d: ", x, y);
     1145                    if (!reject) {
     1146                        fprintf(stderr, "<none>\n");
     1147                    } else {
     1148                        for (int i = 0; i < reject->n; i++) {
     1149                            fprintf(stderr, "%d ", reject->data.U16[i]);
     1150                        }
     1151                        fprintf(stderr, "\n");
     1152                    }
     1153                }
     1154#endif
     1155            }
     1156
     1157            int num;                    // Number of good pixels
     1158            bool suspect;               // Suspect pixels in stack?
     1159            combineExtract(&num, &suspect, buffer, combinedImage, combinedMask, combinedVariance,
     1160                           input, weights, exps, addVariance, reject, x, y, maskVal, maskSuspect);
     1161            combinePixels(combinedImage, combinedMask, combinedVariance, exp, expnum, expweight,
     1162                          num, buffer, x, y, bad, safe, totalExpWeight);
     1163
     1164            if (iter > 0) {
     1165                combineTest(num, suspect, input, buffer, x, y, iter, rej, sys, olympic,
     1166                            useVariance, safe);
     1167            }
     1168        }
     1169    }
     1170
     1171    psFree(pixelMap);
     1172    psFree(weights);
     1173    psFree(buffer);
     1174    psFree(addVariance);
     1175
     1176
     1177#ifndef PS_NO_TRACE
     1178    if (!rejection && psTraceGetLevel("psModules.imcombine") >= 5) {
    8731179        for (int i = 0; i < num; i++) {
    874             pmStackData *data = input->data[i]; // Stacking data; contains the list of pixels
    875             if (!data) {
     1180            pmStackData *data = input->data[i]; // Stack data for this input
     1181            if (!data || !data->inspect) {
    8761182                continue;
    8771183            }
    878             pixels = psPixelsConcatenate(pixels, data->reject);
    879         }
    880         pixels = psPixelsDuplicates(pixels, pixels);
    881 
    882         if (entire) {
    883             // Combine entire image
    884             for (int y = minInputRows; y < maxInputRows; y++) {
    885                 for (int x = minInputCols; x < maxInputCols; x++) {
    886                     psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows,
    887                                                      x, y); // Reject these images
    888                     combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
    889                                   addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard,
    890                                   useVariance, safe, rejectInspect, buffer);
    891                 }
    892             }
    893         } else {
    894             // Only combine previously rejected pixels
    895             for (int i = 0; i < pixels->n; i++) {
    896                 // Pixel coordinates are in the frame of the original image
    897                 int x = pixels->data[i].x, y = pixels->data[i].y; // Coordinates of interest
    898                 if (x < minInputCols || x >= maxInputCols || y < minInputRows || y >= maxInputRows) {
    899                     continue;
    900                 }
    901                 psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows,
    902                                                  x, y); // Reject these images
    903                 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
    904                               addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard,
    905                               useVariance, safe, rejectInspect, buffer);
    906             }
    907         }
    908         psFree(pixels);
    909         psFree(pixelMap);
    910     } else {
    911         // Pull the products out, allocate if necessary
    912         psImage *combinedImage = combined->image; // Combined image
    913         if (!combinedImage) {
    914             combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    915             combinedImage = combined->image;
    916         }
    917         psImage *combinedMask = combined->mask; // Combined mask
    918         if (!combinedMask) {
    919             combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    920             combinedMask = combined->mask;
    921         }
    922 
    923         psImage *combinedVariance = combined->variance; // Combined variance map
    924         if (haveVariances && !combinedVariance) {
    925             combined->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    926             combinedVariance = combined->variance;
    927         }
    928 
    929         for (int y = minInputRows; y < maxInputRows; y++) {
    930             for (int x = minInputCols; x < maxInputCols; x++) {
    931                 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
    932                               addVariance, NULL, x, y, maskVal, bad, numIter, rej, sys, discard,
    933                               useVariance, safe, rejectInspect, buffer);
    934             }
    935         }
    936 
    937 #ifndef PS_NO_TRACE
    938         if (psTraceGetLevel("psModules.imcombine") >= 5) {
    939             for (int i = 0; i < num; i++) {
    940                 pmStackData *data = input->data[i]; // Stack data for this input
    941                 if (!data || !data->inspect) {
    942                     continue;
    943                 }
    944                 psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n);
    945             }
    946         }
    947 #endif
    948     }
    949 
    950     psFree(weights);
    951     psFree(buffer);
     1184            psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n);
     1185        }
     1186    }
     1187#endif
    9521188
    9531189    return true;
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmStack.h

    r23577 r27840  
    3131    psPixels *inspect;                  ///< Pixels to inspect
    3232    float weight;                       ///< Relative weighting for image
     33    float exp;                          ///< Exposure time
    3334    float addVariance;                  ///< Additional variance when rejecting
    3435} pmStackData;
     
    3738pmStackData *pmStackDataAlloc(pmReadout *readout, ///< Warped readout (sky cell)
    3839                              float weight, ///< Weight to apply
     40                              float exp,    ///< Exposure time
    3941                              float addVariance ///< Additional variance when rejecting
    4042    );
     
    4244/// Stack input images
    4345bool pmStackCombine(pmReadout *combined,///< Combined readout (output)
     46                    pmReadout *expmaps, ///< Exposure maps (output)
    4447                    psArray *input,     ///< Input array of pmStackData
    4548                    psImageMaskType maskVal, ///< Mask value of bad pixels
     49                    psImageMaskType suspect, ///< Mask value of suspect pixels
    4650                    psImageMaskType bad,     ///< Mask value to give rejected pixels
    4751                    int kernelSize,     ///< Half-size of the convolution kernel
    48                     int numIter,        ///< Number of iterations
     52                    float iter,         ///< Number of iterations per input
    4953                    float rej,          ///< Rejection limit (standard deviations)
    5054                    float sys,          ///< Relative systematic error
    5155                    float discard,      ///< Fraction of values to discard for Olympic weighted mean
    52                     bool entire,        ///< Combine entire image even if rejection lists provided?
    5356                    bool useVariance,   ///< Use variance values for rejection?
    5457                    bool safe,          ///< Play safe with small numbers of input pixels (mask if N <= 2)?
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmStackReject.c

    r21351 r27840  
    1010#include "pmSubtractionThreads.h"
    1111#include "pmSubtractionKernels.h"
     12
     13#include "pmStackReject.h"
    1214
    1315#define PIXEL_LIST_BUFFER 100           // Number of pixels to add to list at a time
     
    3537{
    3638    int size = kernels->size;           // Half-size of convolution kernel
    37     psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
    38                                                               xMin + size + 1, yMin + size + 1); // Polynomial
     39    int x = PS_MIN(xMin + size + 1, kernels->xMax); // x coordinate of interest
     40    int y = PS_MIN(yMin + size + 1, kernels->yMax); // y coordinate of interest
     41
     42    psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, x, y); // Polynomial
    3943    int box = p_pmSubtractionBadRadius(NULL, kernels, polyValues, false, poorFrac); // Radius of bad box
    4044    psTrace("psModules.imcombine", 10, "Growing by %d", box);
     
    4347    if (box > 0) {
    4448        // Convolve a subimage, then stick it in the target
    45         // XXX if (threaded) {
    46         // XXX     psMutexLock(source);
    47         // XXX }
    4849        psImage *mask = psImageSubset(source, psRegionSet(xMin - box, xMax + box,
    4950                                                          yMin - box, yMax + box)); // Mask to convolve
    50         // XXX if (threaded) {
    51         // XXX     psMutexUnlock(source);
    52         // XXX }
    5351        psImage *convolved = psImageConvolveMask(NULL, mask, PM_STACK_MASK_BAD, PM_STACK_MASK_CONVOLVE,
    5452                                                 -box, box, -box, box); // Convolved mask
    55         // XXX if (threaded) {
    56         // XXX     psMutexLock(source);
    57         // XXX }
    5853        psFree(mask);
    59         // XXX if (threaded) {
    60         // XXX     psMutexUnlock(source);
    61         // XXX }
    6254
    6355        int numBytes = (xMax - xMin) * PSELEMTYPE_SIZEOF(PS_TYPE_IMAGE_MASK); // Number of bytes to copy
     
    111103
    112104    if (!pmSubtractionThreaded()) {
    113         pmSubtractionThreadsInit(NULL, NULL);
     105        pmSubtractionThreadsInit();
    114106    }
    115107
     
    125117
    126118
    127 psPixels *pmStackReject(const psPixels *in, int numCols, int numRows, float threshold, float poorFrac,
    128                         int stride, const psArray *subRegions, const psArray *subKernels)
     119psPixels *pmStackReject(const psPixels *in, int numCols, int numRows, float threshold, int stride,
     120                        const psArray *subRegions, const psArray *subKernels)
    129121{
    130122    PS_ASSERT_PIXELS_NON_NULL(in, NULL);
     
    162154    pmReadout *inRO = pmReadoutAlloc(NULL); // Readout with input image
    163155    inRO->image = image;
    164     // XXX if (threaded) {
    165     // XXX     psMutexInit(image);
    166     // XXX }
     156    convRO->image = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
    167157    for (int i = 0; i < numRegions; i++) {
    168158        psRegion *region = subRegions->data[i]; // Region of interest
    169159        pmSubtractionKernels *kernels = subKernels->data[i]; // Kernel of interest
    170         if (!pmSubtractionConvolve(convRO, NULL, inRO, NULL, NULL, stride, 0, 0, 1.0, 0.0,
     160        if (!pmSubtractionConvolve(NULL, convRO, NULL, inRO, NULL, stride, 0, 0, 1.0, 0.0, 0.0,
    171161                                   region, kernels, false, true)) {
    172             psError(PS_ERR_UNKNOWN, false, "Unable to convolve mask image in region %d.", i);
     162            psError(psErrorCodeLast(), false, "Unable to convolve mask image in region %d.", i);
    173163            psFree(convRO);
    174164            psFree(inRO);
     
    180170
    181171        // Image of the kernel at the centre of the region
    182         float xNorm = (region->x0 + 0.5 * (region->x1 - region->x0) - kernels->numCols/2.0) /
    183             (float)kernels->numCols;
    184         float yNorm = (region->y0 + 0.5 * (region->y1 - region->y0) - kernels->numRows/2.0) /
    185             (float)kernels->numRows;
    186         psImage *kernel = pmSubtractionKernelImage(kernels, xNorm, yNorm, false);
     172        psImage *kernel = pmSubtractionKernelImage(kernels, 0.5, 0.5, false);
    187173        if (!kernel) {
    188             psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
     174            psError(psErrorCodeLast(), false, "Unable to generate kernel image.");
    189175            psFree(convRO);
    190176            psFree(inRO);
     
    211197        }
    212198    }
    213     // XXX if (threaded) {
    214     // XXX     psMutexDestroy(image);
    215     // XXX }
    216199    psFree(inRO);
    217200    psImage *convolved = psMemIncrRefCounter(convRO->image);
     
    241224    }
    242225    psTrace("psModules.imcombine", 7, "Found %ld bad pixels", bad->n);
    243 
    244     // Now, grow the mask to include everything that touches a bad pixel in the convolution
    245     psImage *source = psPixelsToMask(NULL, bad, psRegionSet(0, numCols - 1, 0, numRows - 1),
     226    psFree(convolved);
     227
     228    return bad;
     229}
     230
     231
     232psPixels *pmStackRejectGrow(const psPixels *in, int numCols, int numRows, float poorFrac,
     233                            const psArray *subRegions, const psArray *subKernels)
     234{
     235    PS_ASSERT_PIXELS_NON_NULL(in, NULL);
     236    PS_ASSERT_ARRAY_NON_NULL(subRegions, NULL);
     237    PS_ASSERT_ARRAY_NON_NULL(subKernels, NULL);
     238    PS_ASSERT_ARRAYS_SIZE_EQUAL(subRegions, subKernels, NULL);
     239
     240    psImage *source = psPixelsToMask(NULL, in, psRegionSet(0, numCols - 1, 0, numRows - 1),
    246241                                     PM_STACK_MASK_BAD); // Mask image to grow
    247242
     
    262257    bool oldThreads = psImageConvolveSetThreads(false); // Old value of threading for psImageColvolve
    263258
    264     psImage *target = psImageRecycle(convolved, numCols, numRows, PS_TYPE_IMAGE_MASK); // Grown image
     259    psImage *target = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK); // Grown image
    265260    psImageInit(target, 0);
    266     // XXX if (threaded) {
    267     // XXX     psMutexInit(source);
    268     // XXX }
    269261    for (int i = 0; i < subRegions->n; i++) {
    270262        psRegion *region = subRegions->data[i]; // Subtraction region
     
    287279                    psArray *args = job->args; // Job arguments
    288280                    psArrayAdd(args, 1, target);
    289                     // XXX psMutexLock(source);
    290281                    psArrayAdd(args, 1, source);
    291                     // XXX psMutexUnlock(source);
    292282                    psArrayAdd(args, 1, kernels);
    293283                    PS_ARRAY_ADD_SCALAR(args, numCols, PS_TYPE_S32);
     
    307297                } else if (!stackRejectGrow(target, source, kernels, numCols, numRows,
    308298                                            i, xSubMax, j, ySubMax, poorFrac)) {
    309                     psError(PS_ERR_UNKNOWN, false, "Unable to grow bad pixels.");
     299                    psError(psErrorCodeLast(), false, "Unable to grow bad pixels.");
    310300                    psFree(source);
    311301                    psFree(target);
     
    317307
    318308    if (!psThreadPoolWait(false)) {
    319         psError(PS_ERR_UNKNOWN, false, "Unable to grow bad pixels.");
     309        psError(psErrorCodeLast(), false, "Unable to grow bad pixels.");
    320310        psFree(source);
    321311        psFree(target);
     
    332322        }
    333323
    334         // XXX psMutexDestroy(source);
    335324    }
    336325
     
    351340
    352341    psFree(source);
    353     bad = psPixelsFromMask(bad, target, PM_STACK_MASK_ALL);
     342    psPixels *bad = psPixelsFromMask(NULL, target, PM_STACK_MASK_ALL); // All bad pixels
     343    psFree(target);
    354344    psTrace("psModules.imcombine", 7, "Total %ld bad pixels", bad->n);
    355345
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmStackReject.h

    r20568 r27840  
    1212                        int numCols, int numRows, ///< Size of image of interest
    1313                        float threshold, ///< Threshold on convolved image, 0..1
    14                         float poorFrac, ///< Fraction for "poor"
    1514                        int stride,     ///< Size of convolution patches
    1615                        const psArray *regions, ///< Array of image regions for image
    1716                        const psArray *kernels ///< Array of kernel parameters for each region
     17    );
     18
     19/// Given a list of pixels from the convolved image, we grow them by convolution to get the list of all pixels
     20/// which should be rejected.
     21psPixels *pmStackRejectGrow(const psPixels *in, ///< List of pixels in the convolved image
     22                            int numCols, int numRows, ///< Size of image of interest
     23                            float poorFrac, ///< Fraction for "poor"
     24                            const psArray *regions, ///< Array of image regions for image
     25                            const psArray *kernels ///< Array of kernel parameters for each region
    1826    );
    1927
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtraction.c

    r24298 r27840  
    1717#include <pslib.h>
    1818
     19#include "pmErrorCodes.h"
    1920#include "pmHDU.h"                      // Required for pmFPA.h
    2021#include "pmFPA.h"
    2122#include "pmSubtractionStamps.h"
    2223#include "pmSubtractionEquation.h"
     24#include "pmSubtractionVisual.h"
    2325#include "pmSubtractionThreads.h"
    2426
     
    2931#define PIXEL_LIST_BUFFER 100           // Number of entries to add to pixel list at a time
    3032#define MIN_SAMPLE_STATS    7           // Minimum number to use sample statistics; otherwise use quartiles
     33#define USE_KERNEL_ERR                  // Use kernel error image?
    3134
    3235//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    3639// Generate the kernel to apply to the variance from the normal kernel
    3740static psKernel *varianceKernel(psKernel *out, // Output kernel
    38                                 psKernel *normalKernel // Normal kernel
     41                                const psKernel *normalKernel // Normal kernel
    3942                                )
    4043{
     
    4851
    4952    // Take the square of the normal kernel
    50     double sumNormal = 0.0, sumVariance = 0.0; // Sum of the normal and variance kernels
     53    double sumVariance = 0.0; // Sum of the variance kernels
    5154    for (int v = yMin; v <= yMax; v++) {
    5255        for (int u = xMin; u <= xMax; u++) {
    53             float value = normalKernel->kernel[v][u]; // Value of interest
    54             float value2 = PS_SQR(value); // Value squared
    55             sumNormal += value;
    56             sumVariance += value2;
    57             out->kernel[v][u] = value2;
     56            sumVariance += out->kernel[v][u] = PS_SQR(normalKernel->kernel[v][u]);
    5857        }
    5958    }
     
    6564    return out;
    6665}
     66
     67// Contribute to an image of the solved kernel component using the preCalculated image
     68static void solvedKernelPreCalc(psKernel *kernel, // Kernel, updated
     69                                const pmSubtractionKernels *kernels, // Kernel basis functions
     70                                float value,                         // Normalisation value for basis function
     71                                int index                  // Index of basis function of interest
     72    )
     73{
     74    int size = kernels->size;           // Kernel half-size
     75    pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[index]; // Precalculated values
     76#if 0
     77    // Iterating over the kernel
     78    for (int y = 0, v = -size; v <= size; y++, v++) {
     79        float yValue = value * preCalc->yKernel->data.F32[y];
     80        for (int x = 0, u = -size; u <= size; x++, u++) {
     81            kernel->kernel[v][u] +=  yValue * preCalc->xKernel->data.F32[x];
     82        }
     83    }
     84    // Photometric scaling for even kernels only
     85    if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
     86        kernel->kernel[0][0] -= value;
     87    }
     88#else
     89    for (int v = -size; v <= size; v++) {
     90        for (int u = -size; u <= size; u++) {
     91            kernel->kernel[v][u] +=  value * preCalc->kernel->kernel[v][u];
     92        }
     93    }
     94#endif
     95
     96    return;
     97}
     98
    6799
    68100// Generate an image of the solved kernel
     
    70102                              const pmSubtractionKernels *kernels, // Kernel basis functions
    71103                              const psImage *polyValues, // Spatial polynomial values
     104                              bool normalise,            // Add normalisation?
    72105                              bool wantDual // Want the dual (second) kernel?
    73106                              )
     
    85118    for (int i = 0; i < numKernels; i++) {
    86119        double value = p_pmSubtractionSolutionCoeff(kernels, polyValues, i, wantDual); // Polynomial value
     120        if (wantDual) {
     121            // The model is built with the dual convolution terms added, so to produce zero residual the
     122            // equation results in negative coefficients which we must undo.
     123            value *= -1.0;
     124        }
    87125
    88126        switch (kernels->type) {
     
    115153          case PM_SUBTRACTION_KERNEL_GUNK: {
    116154              if (i < kernels->inner) {
    117                   // Using pre-calculated function
    118                   psKernel *preCalc = kernels->preCalc->data[i]; // Precalculated values
    119                   // Iterating over the kernel
    120                   for (int v = -size; v <= size; v++) {
    121                       for (int u = -size; u <= size; u++) {
    122                           kernel->kernel[v][u] += preCalc->kernel[v][u] * value;
    123                           // Photometric scaling is built into the preCalc kernel --- no subtraction!
    124                       }
    125                   }
     155                  solvedKernelPreCalc(kernel, kernels, value, i);
    126156              } else {
    127157                  // Using delta function
     
    133163              break;
    134164          }
    135           case PM_SUBTRACTION_KERNEL_ISIS: {
    136               psArray *preCalc = kernels->preCalc->data[i]; // Precalculated values
    137               psVector *xKernel = preCalc->data[0]; // Kernel in x
    138               psVector *yKernel = preCalc->data[1]; // Kernel in y
    139               // Iterating over the kernel
    140               for (int y = 0, v = -size; v <= size; y++, v++) {
    141                   for (int x = 0, u = -size; u <= size; x++, u++) {
    142                       kernel->kernel[v][u] +=  value * xKernel->data.F32[x] * yKernel->data.F32[y];
    143                   }
    144               }
    145               // Photometric scaling for even kernels only
    146               if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
    147                   kernel->kernel[0][0] -= value;
    148               }
     165          case PM_SUBTRACTION_KERNEL_ISIS:
     166          case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     167          case PM_SUBTRACTION_KERNEL_HERM:
     168          case PM_SUBTRACTION_KERNEL_DECONV_HERM: {
     169              solvedKernelPreCalc(kernel, kernels, value, i);
    149170              break;
    150171          }
    151172          case PM_SUBTRACTION_KERNEL_RINGS: {
    152               psArray *preCalc = kernels->preCalc->data[i]; // Precalculated data
    153               psVector *uCoords = preCalc->data[0]; // u coordinates
    154               psVector *vCoords = preCalc->data[1]; // v coordinates
    155               psVector *poly = preCalc->data[2]; // Polynomial values
    156               int num = uCoords->n;     // Number of pixels
     173              pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[i]; // Precalculated kernels
     174              int num = preCalc->uCoords->n;     // Number of pixels
    157175
    158176              for (int j = 0; j < num; j++) {
    159                   int u = uCoords->data.S32[j], v = vCoords->data.S32[j]; // Kernel coordinates
    160                   kernel->kernel[v][u] += poly->data.F32[j] * value;
     177                  int u = preCalc->uCoords->data.S32[j];
     178                  int v = preCalc->vCoords->data.S32[j]; // Kernel coordinates
     179                  kernel->kernel[v][u] += preCalc->poly->data.F32[j] * value;
    161180              }
    162181              // Photometric scaling is built into the kernel --- no subtraction!
     
    168187    }
    169188
    170     // Put in the normalisation component
    171     kernel->kernel[0][0] += (wantDual ? 1.0 : p_pmSubtractionSolutionNorm(kernels));
     189    if (normalise) {
     190        // Put in the normalisation component
     191        kernel->kernel[0][0] += (wantDual ? 1.0 : p_pmSubtractionSolutionNorm(kernels));
     192    }
    172193
    173194    return kernel;
     
    250271static void convolveVarianceFFT(psImage *target,// Place the result in here
    251272                              psImage *variance, // Variance map to convolve
    252                               psImage *sys, // Systematic error image
     273                              psImage *kernelErr, // Kernel error image
    253274                              psImage *mask, // Mask image
    254275                              psImageMaskType maskVal, // Value to mask
     
    262283
    263284    psImage *subVariance = variance ? psImageSubset(variance, border) : NULL; // Variance map
    264     psImage *subSys = sys ? psImageSubset(sys, border) : NULL; // Systematic error image
     285    psImage *subKE = kernelErr ? psImageSubset(kernelErr, border) : NULL; // Kernel error image
    265286    psImage *subMask = mask ? psImageSubset(mask, border) : NULL; // Mask
    266287
    267288    // XXX Can trim this a little by combining the convolution: only have to take the FFT of the kernel once
    268289    psImage *convVariance = psImageConvolveFFT(NULL, subVariance, subMask, maskVal, kernel); // Convolved variance
    269     psImage *convSys = subSys ? psImageConvolveFFT(NULL, subSys, subMask, maskVal, kernel) : NULL; // Conv sys
     290    psImage *convKE = subKE ? psImageConvolveFFT(NULL, subKE, subMask, maskVal, kernel) : NULL; // Conv KE
    270291
    271292    psFree(subVariance);
    272     psFree(subSys);
     293    psFree(subKE);
    273294    psFree(subMask);
    274295
    275296    // Now, we have to stick it in where it belongs
    276297    int xMin = region.x0, xMax = region.x1, yMin = region.y0, yMax = region.y1; // Bounds of region
    277     if (convSys) {
     298    if (convKE) {
    278299        for (int yTarget = yMin, ySource = size; yTarget < yMax; yTarget++, ySource++) {
    279300            for (int xTarget = xMin, xSource = size; xTarget < xMax; xTarget++, xSource++) {
    280301                target->data.F32[yTarget][xTarget] = convVariance->data.F32[ySource][xSource] +
    281                     convSys->data.F32[ySource][xSource];
     302                    convKE->data.F32[ySource][xSource];
    282303            }
    283304        }
     
    290311
    291312    psFree(convVariance);
    292     psFree(convSys);
     313    psFree(convKE);
    293314
    294315    return;
     
    326347                                  psImage *image, // Image to convolve
    327348                                  psImage *variance, // Variance map to convolve, or NULL
    328                                   psImage *sys, // Systematic error image, or NULL
     349                                  psImage *kernelErr, // Kernel error image, or NULL
    329350                                  psImage *subMask, // Subtraction mask
    330351                                  const pmSubtractionKernels *kernels, // Kernels
     
    339360    )
    340361{
    341     *kernelImage = solvedKernel(*kernelImage, kernels, polyValues, wantDual);
     362    *kernelImage = solvedKernel(*kernelImage, kernels, polyValues, true, wantDual);
    342363    if (variance || subMask) {
    343364        *kernelVariance = varianceKernel(*kernelVariance, *kernelImage);
     
    363384        convolveFFT(convImage, image, subMask, subBad, *kernelImage, region, background, kernels->size);
    364385        if (variance) {
    365             convolveVarianceFFT(convVariance, variance, sys, subMask, subBad, *kernelVariance, region, kernels->size);
     386            convolveVarianceFFT(convVariance, variance, kernelErr, subMask, subBad, *kernelVariance,
     387                                region, kernels->size);
    366388        }
    367389    } else {
     
    373395    }
    374396
    375     // Convolve the mask for bad pixels
     397    // Convolve the mask for bad/poor pixels
    376398    if (subMask && convMask) {
    377399        int box = p_pmSubtractionBadRadius(*kernelImage, kernels, polyValues,
    378400                                           wantDual, poorFrac); // Size of bad box
     401        psAssert(box >= 0, "Bad radius must be >= 0");
     402
     403        int colMin = region.x0, colMax = region.x1, rowMin = region.y0, rowMax = region.y1; // Bounds
     404        psImage *convolved = NULL; // Convolved subtraction mask
    379405        if (box > 0) {
    380             int colMin = region.x0, colMax = region.x1, rowMin = region.y0, rowMax = region.y1; // Bounds
    381             psRegion region = psRegionSet(colMin - box, colMax + box,
    382                                           rowMin - box, rowMax + box); // Region to convolve
    383 
    384             psImage *image = subMask ? psImageSubset(subMask, region) : NULL; // Mask to convolve
    385 
    386             psImage *convolved = psImageConvolveMask(NULL, image, subBad, subConvBad,
    387                                                      -box, box, -box, box); // Convolved subtraction mask
    388 
     406            psRegion maskRegion = psRegionSet(colMin - box, colMax + box,
     407                                              rowMin - box, rowMax + box); // Region to convolve
     408            psImage *image = subMask ? psImageSubset(subMask, maskRegion) : NULL; // Mask to convolve
     409            convolved = psImageConvolveMask(NULL, image, subBad, subConvBad, -box, box, -box, box);
    389410            psFree(image);
    390 
    391             psAssert(convolved->numCols - 2 * box == colMax - colMin, "Bad number of columns");
    392             psAssert(convolved->numRows - 2 * box == rowMax - rowMin, "Bad number of rows");
    393 
    394             for (int yTarget = rowMin, ySource = box; yTarget < rowMax; yTarget++, ySource++) {
    395                 // Dereference images
    396                 psImageMaskType *target = &convMask->data.PS_TYPE_IMAGE_MASK_DATA[yTarget][colMin]; // Target values
    397                 psImageMaskType *source = &convolved->data.PS_TYPE_IMAGE_MASK_DATA[ySource][box]; // Source values
    398                 for (int xTarget = colMin; xTarget < colMax; xTarget++, target++, source++) {
    399                     if (*source & subConvBad) {
    400                         *target |= maskBad;
    401                     } else if (*source & subConvPoor) {
    402                         *target |= maskPoor;
    403                     }
     411        } else {
     412            convolved = psImageSubset(subMask, region);
     413        }
     414
     415        psAssert(convolved->numCols - 2 * box == colMax - colMin, "Bad number of columns");
     416        psAssert(convolved->numRows - 2 * box == rowMax - rowMin, "Bad number of rows");
     417
     418        for (int yTarget = rowMin, ySource = box; yTarget < rowMax; yTarget++, ySource++) {
     419            // Dereference images
     420            psImageMaskType *target = &convMask->data.PS_TYPE_IMAGE_MASK_DATA[yTarget][colMin]; // Target values
     421            psImageMaskType *source = &convolved->data.PS_TYPE_IMAGE_MASK_DATA[ySource][box]; // Source values
     422            for (int xTarget = colMin; xTarget < colMax; xTarget++, target++, source++) {
     423                if (*source & subConvBad) {
     424                    *target |= maskBad;
     425                } else if (*source & subConvPoor) {
     426                    *target &= ~maskBad;
     427                    *target |= maskPoor;
     428                } else {
     429                    *target &= ~maskBad & ~maskPoor;
    404430                }
    405431            }
    406 
    407             // No need to lock: we own this
    408             psFree(convolved);
    409         }
     432        }
     433
     434        psFree(convolved);
    410435    }
    411436
     
    413438}
    414439
    415 // Generate an image that can be used to track systematic errors
    416 static psImage *subtractionSysErrImage(const psImage *image, // Image from which to make sys err image
    417                                        float sysError // Relative systematic error
    418                                        )
    419 {
    420     if (!isfinite(sysError) || sysError == 0.0) {
     440#ifdef USE_KERNEL_ERR
     441// Generate an image that can be used to track systematic errors in the kernel
     442static psImage *subtractionKernelErrImage(const psImage *image, // Image from which to make kernel error image
     443                                          float kernelError // Relative systematic error in kernel
     444    )
     445{
     446    if (!isfinite(kernelError) || kernelError == 0.0) {
    421447        return NULL;
    422448    }
    423449
    424450    int numCols = image->numCols, numRows = image->numRows; // Size of image
    425     psImage *sys = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Systematic error image
    426 
    427     float sysError2 = PS_SQR(sysError); // Square of the systematic error
     451    psImage *kernelErr = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Kernel error image
     452
     453    float kernelError2 = PS_SQR(kernelError); // Square of the kernel error
    428454    for (int y = 0; y < numRows; y++) {
    429455        for (int x = 0; x < numCols; x++) {
    430             sys->data.F32[y][x] = PS_SQR(image->data.F32[y][x]) * sysError2;
    431         }
    432     }
    433 
    434     return sys;
     456            kernelErr->data.F32[y][x] = PS_SQR(image->data.F32[y][x]) * kernelError2;
     457        }
     458    }
     459
     460    return kernelErr;
     461}
     462#endif
     463
     464// Convolve a stamp using a pre-calculated kernel basis function
     465static psKernel *convolveStampPreCalc(const psKernel *image, // Image to convolve
     466                                      const pmSubtractionKernels *kernels, // Kernel basis functions
     467                                      int index,                            // Index of basis function of interest
     468                                      int footprint                         // Half-size of stamp
     469    )
     470{
     471    pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[index]; // Precalculated data
     472#if 0
     473    // Convolving using separable convolution
     474    int size = kernels->size;     // Size of kernel
     475
     476    // Convolve in x
     477    // Need to convolve a bit more than the footprint, for the y convolution
     478    int yMin = -size - footprint, yMax = size + footprint; // Range for y
     479    psKernel *temp = psKernelAlloc(yMin, yMax,
     480                                   -footprint, footprint); // Temporary convolution; NOTE: wrong way!
     481    for (int y = yMin; y <= yMax; y++) {
     482        for (int x = -footprint; x <= footprint; x++) {
     483            float value = 0.0;    // Value of convolved pixel
     484            int uMin = x - size, uMax = x + size; // Range for u
     485            psF32 *xKernelData = &preCalc->xKernel->data.F32[xKernel->n - 1]; // Kernel values
     486            psF32 *imageData = &image->kernel[y][uMin]; // Image values
     487            for (int u = uMin; u <= uMax; u++, xKernelData--, imageData++) {
     488                value += *xKernelData * *imageData;
     489            }
     490            temp->kernel[x][y] = value; // NOTE: putting in wrong way!
     491        }
     492    }
     493
     494    // Convolve in y
     495    psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint);// Convolved image
     496    for (int x = -footprint; x <= footprint; x++) {
     497        for (int y = -footprint; y <= footprint; y++) {
     498            float value = 0.0;    // Value of convolved pixel
     499            int vMin = y - size, vMax = y + size; // Range for v
     500            psF32 *yKernelData = &preCalc->yKernel->data.F32[yKernel->n - 1]; // Kernel values
     501            psF32 *imageData = &temp->kernel[x][vMin]; // Image values; NOTE: wrong way!
     502            for (int v = vMin; v <= vMax; v++, yKernelData--, imageData++) {
     503                value += *yKernelData * *imageData;
     504            }
     505            convolved->kernel[y][x] = value;
     506        }
     507    }
     508    psFree(temp);
     509
     510    // Photometric scaling for even kernels only
     511    if (kernels->u->data.S32[index] % 2 == 0 && kernels->v->data.S32[index] % 2 == 0) {
     512        convolveSub(convolved, image, footprint);
     513    }
     514    return convolved;
     515#else
     516    // Convolving using precalculated kernel
     517    return p_pmSubtractionConvolveStampPrecalc(image, preCalc->kernel);
     518#endif
    435519}
    436520
     
    447531    int x0 = - image->xMin, y0 = - image->yMin; // Position of centre of convolved image
    448532    psKernel *convolved = psKernelAllocFromImage(conv, x0, y0); // Kernel version
     533
     534    // pmSubtractionVisualShowSubtraction(image->image, kernel->image, conv);
     535
    449536    psFree(conv);
    450537    return convolved;
     
    456543    psKernel *kernel;                   // Kernel to use
    457544    if (!preKernel) {
    458         kernel = solvedKernel(NULL, kernels, polyValues, wantDual);
     545        kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual);
    459546    } else {
    460547        kernel = psMemIncrRefCounter(preKernel);
     
    491578}
    492579
     580void p_pmSubtractionPolynomialNormCoords(float *xOut, float *yOut, float xIn, float yIn,
     581                                         int xMin, int xMax, int yMin, int yMax)
     582{
     583    float xNormSize = xMax - xMin, yNormSize = yMax - yMin; // Size to use for normalisation
     584    *xOut = 2.0 * (float)(xIn - xMin - xNormSize/2.0) / xNormSize;
     585    *yOut = 2.0 * (float)(yIn - yMin - yNormSize/2.0) / yNormSize;
     586    return;
     587}
     588
    493589psImage *p_pmSubtractionPolynomialFromCoords(psImage *output, const pmSubtractionKernels *kernels,
    494                                              int numCols, int numRows, int x, int y)
     590                                             int x, int y)
    495591{
    496592    assert(kernels);
    497     assert(numCols > 0 && numRows > 0);
    498 
    499     // Size to use when calculating normalised coordinates (different from actual size when convolving
    500     // subimage)
    501     int xNormSize = (kernels->numCols > 0 ? kernels->numCols : numCols);
    502     int yNormSize = (kernels->numRows > 0 ? kernels->numRows : numRows);
    503 
    504     // Normalised coordinates
    505     float yNorm = 2.0 * (float)(y - yNormSize/2.0) / (float)yNormSize;
    506     float xNorm = 2.0 * (float)(x - xNormSize/2.0) / (float)xNormSize;
    507 
     593
     594    float xNorm, yNorm;                 // Normalised coordinates
     595    p_pmSubtractionPolynomialNormCoords(&xNorm, &yNorm, x, y,
     596                                        kernels->xMin, kernels->xMax, kernels->yMin, kernels->yMax);
    508597    return p_pmSubtractionPolynomial(output, kernels->spatialOrder, xNorm, yNorm);
    509598}
     
    586675          if (index < kernels->inner) {
    587676              // Photometric scaling is already built in to the precalculated kernel
    588               return p_pmSubtractionConvolveStampPrecalc(image, kernels->preCalc->data[index]);
     677              return convolveStampPreCalc(image, kernels, index, footprint);
    589678          }
    590679          // Using delta function
     
    595684          return convolved;
    596685      }
    597       case PM_SUBTRACTION_KERNEL_ISIS: {
    598           psArray *preCalc = kernels->preCalc->data[index]; // Precalculated values
    599           psVector *xKernel = preCalc->data[0]; // Kernel in x
    600           psVector *yKernel = preCalc->data[1]; // Kernel in y
    601           int size = kernels->size;     // Size of kernel
    602 
    603           // Convolve in x
    604           // Need to convolve a bit more than the footprint, for the y convolution
    605           int yMin = -size - footprint, yMax = size + footprint; // Range for y
    606           psKernel *temp = psKernelAlloc(yMin, yMax,
    607                                          -footprint, footprint); // Temporary convolution; NOTE: wrong way!
    608           for (int y = yMin; y <= yMax; y++) {
    609               for (int x = -footprint; x <= footprint; x++) {
    610                   float value = 0.0;    // Value of convolved pixel
    611                   int uMin = x - size, uMax = x + size; // Range for u
    612                   psF32 *xKernelData = &xKernel->data.F32[xKernel->n - 1]; // Kernel values
    613                   psF32 *imageData = &image->kernel[y][uMin]; // Image values
    614                   for (int u = uMin; u <= uMax; u++, xKernelData--, imageData++) {
    615                       value += *xKernelData * *imageData;
    616                   }
    617                   temp->kernel[x][y] = value; // NOTE: putting in wrong way!
    618               }
    619           }
    620 
    621           // Convolve in y
    622           psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint);// Convolved image
    623           for (int x = -footprint; x <= footprint; x++) {
    624               for (int y = -footprint; y <= footprint; y++) {
    625                   float value = 0.0;    // Value of convolved pixel
    626                   int vMin = y - size, vMax = y + size; // Range for v
    627                   psF32 *yKernelData = &yKernel->data.F32[yKernel->n - 1]; // Kernel values
    628                   psF32 *imageData = &temp->kernel[x][vMin]; // Image values; NOTE: wrong way!
    629                   for (int v = vMin; v <= vMax; v++, yKernelData--, imageData++) {
    630                       value += *yKernelData * *imageData;
    631                   }
    632                   convolved->kernel[y][x] = value;
    633               }
    634           }
    635           psFree(temp);
    636 
    637           // Photometric scaling for even kernels only
    638           if (kernels->u->data.S32[index] % 2 == 0 && kernels->v->data.S32[index] % 2 == 0) {
    639               convolveSub(convolved, image, footprint);
    640           }
    641           return convolved;
    642       }
     686      case PM_SUBTRACTION_KERNEL_ISIS:
     687      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     688      case PM_SUBTRACTION_KERNEL_HERM:
     689      case PM_SUBTRACTION_KERNEL_DECONV_HERM: {
     690            return convolveStampPreCalc(image, kernels, index, footprint);
     691        }
    643692      case PM_SUBTRACTION_KERNEL_RINGS: {
    644           psKernel *convolved = psKernelAlloc(-footprint, footprint,
    645                                               -footprint, footprint); // Convolved image
    646           psArray *preCalc = kernels->preCalc->data[index]; // Precalculated data
    647           psVector *uCoords = preCalc->data[0]; // u coordinates
    648           psVector *vCoords = preCalc->data[1]; // v coordinates
    649           psVector *poly = preCalc->data[2]; // Polynomial values
    650           int num = uCoords->n;         // Number of pixels
    651           psS32 *uData = uCoords->data.S32, *vData = vCoords->data.S32; // Dereference u,v coordinates
    652           psF32 *polyData = poly->data.F32; // Dereference polynomial values
     693          psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Convolved image
     694          pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[index]; // Precalculated data
     695
     696          int num = preCalc->uCoords->n;         // Number of pixels
     697          psS32 *uData = preCalc->uCoords->data.S32; // Dereference v coordinate
     698          psS32 *vData = preCalc->vCoords->data.S32; // Dereference u coordinate
     699          psF32 *polyData = preCalc->poly->data.F32; // Dereference polynomial values
    653700          psF32 **imageData = image->kernel;  // Dereference image
    654701          psF32 **convData = convolved->kernel; // Dereference convolved image
     
    706753
    707754    if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE) {
    708         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Stamp not marked for calculation.");
     755        psError(PM_ERR_PROG, true, "Stamp not marked for calculation.");
    709756        return false;
    710757    }
     
    730777
    731778
    732 
    733 
    734779int pmSubtractionRejectStamps(pmSubtractionKernels *kernels, pmSubtractionStampList *stamps,
    735                               const psVector *deviations, psImage *subMask, float sigmaRej, int footprint)
     780                              const psVector *deviations, psImage *subMask, float sigmaRej)
    736781{
    737782    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
     
    764809
    765810    if (numStamps == 0) {
    766         psError(PS_ERR_UNKNOWN, true, "No good stamps found.");
     811        psError(PM_ERR_STAMPS, true, "No good stamps found.");
    767812        psFree(mask);
    768813        return -1;
     
    772817                                  PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_QUARTILE); // Statistics for deviatns
    773818    if (!psVectorStats(stats, deviations, NULL, mask, 0xff)) {
    774         psError(PS_ERR_UNKNOWN, false, "Unable to measure statistics for deviations.");
     819        psError(PM_ERR_DATA, false, "Unable to measure statistics for deviations.");
    775820        psFree(stats);
    776821        psFree(mask);
     
    821866    ds9num++;
    822867
     868    int footprint = stamps->footprint;  // Half-size of stamp region of interest
    823869    int numRejected = 0;                // Number of stamps rejected
    824870    int numGood = 0;                    // Number of good stamps
    825871    double newMean = 0.0;               // New mean
     872    psString log = NULL;                // Log message
     873    psStringAppend(&log, "Rejecting stamps, mean = %f, threshold = %f\n", mean, limit);
    826874    for (int i = 0; i < stamps->num; i++) {
    827875        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    836884                // Mask out the stamp in the image so you it's not found again
    837885                psTrace("psModules.imcombine", 3, "Rejecting stamp %d (%d,%d)\n", i,
    838                         (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     886                        (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
     887                psStringAppend(&log, "Stamp %d (%d,%d): %f\n", i,
     888                               (int)(stamp->x - 0.5), (int)(stamp->y - 0.5),
     889                               fabsf(deviations->data.F32[i] - mean));
    839890                numRejected++;
    840891                for (int y = stamp->y - footprint; y <= stamp->y + footprint; y++) {
     
    857908                psFree(stamp->image1);
    858909                psFree(stamp->image2);
    859                 psFree(stamp->variance);
    860                 stamp->image1 = stamp->image2 = stamp->variance = NULL;
    861                 psFree(stamp->matrix1);
    862                 psFree(stamp->matrix2);
    863                 psFree(stamp->matrixX);
    864                 stamp->matrix1 = stamp->matrix2 = stamp->matrixX = NULL;
    865                 psFree(stamp->vector1);
    866                 psFree(stamp->vector2);
    867                 stamp->vector1 = stamp->vector2 = NULL;
     910                psFree(stamp->weight);
     911                stamp->image1 = stamp->image2 = stamp->weight = NULL;
     912                psFree(stamp->matrix);
     913                stamp->matrix = NULL;
     914                psFree(stamp->vector);
     915                stamp->vector = NULL;
    868916            } else {
    869917                numGood++;
     
    874922    }
    875923    newMean /= numGood;
     924
     925    if (numRejected == 0) {
     926        psStringAppend(&log, "<none>\n");
     927    }
     928    psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "%s", log);
     929    psFree(log);
    876930
    877931    if (ds9) {
     
    900954
    901955    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y); // Solved polynomial
    902     psKernel *kernel = solvedKernel(NULL, kernels, polyValues, wantDual); // The appropriate kernel
     956    psKernel *kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual); // The appropriate kernel
    903957    psFree(polyValues);
    904958
     
    931985    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y);
    932986
    933     psKernel *kernel = solvedKernel(NULL, kernels, polyValues, wantDual); // The appropriate kernel
     987    psKernel *kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual); // The appropriate kernel
    934988    psFree(polyValues);
    935989
     
    9481002}
    9491003
    950 #if 0
     1004#if 1
    9511005psArray *pmSubtractionKernelSolutions(const pmSubtractionKernels *kernels, float x, float y, bool wantDual)
    9521006{
     
    9561010    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NULL);
    9571011
    958     psArray *images = psArrayAlloc(solution->n - 1); // Images of each kernel to return
    959     psVector *fakeSolution = psVectorAlloc(solution->n, PS_TYPE_F64); // Fake solution vector
    960     psVectorInit(fakeSolution, 0.0);
    961 
    962     for (int i = 0; i < solution->n - 1; i++) {
    963         fakeSolution->data.F64[i] = solution->data.F64[i];
    964         images->data[i] = pmSubtractionKernelImage(kernels, x, y, wantDual);
    965         fakeSolution->data.F64[i] = 0.0;
    966     }
    967 
    968     psFree(fakeSolution);
     1012    psVector *solution = wantDual ? kernels->solution2 : kernels->solution1; // Solution of interest
     1013    psVector *backup = psVectorCopy(NULL, solution, PS_TYPE_F64);  // Backup version
     1014
     1015    int num = kernels->num;             // Number of kernel basis functions
     1016
     1017    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y); // Solved polynomial
     1018    psArray *images = psArrayAlloc(num + 1); // Images of each kernel to return
     1019
     1020    // The whole kernel
     1021    {
     1022        psKernel *kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual); // The appropriate kernel
     1023        images->data[0] = psMemIncrRefCounter(kernel->image);
     1024        psFree(kernel);
     1025    }
     1026
     1027    // The parts
     1028    psVectorInit(solution, 0.0);
     1029    for (int i = 0; i < num; i++) {
     1030        solution->data.F64[i] = backup->data.F64[i];
     1031        psKernel *kernel = solvedKernel(NULL, kernels, polyValues, false, wantDual); // The appropriate kernel
     1032#if 0
     1033        int size = kernels->size;
     1034        double sum = 0.0;
     1035        for (int v = -size; v <= size; v++) {
     1036            for (int u = -size; u <= size; u++) {
     1037                sum += kernel->kernel[v][u];
     1038            }
     1039        }
     1040        fprintf(stderr, "Kernel %d: %lf\n", i, sum);
     1041#endif
     1042        images->data[i + 1] = psMemIncrRefCounter(kernel->image);
     1043        psFree(kernel);
     1044        solution->data.F64[i] = 0.0;
     1045    }
     1046    psFree(polyValues);
     1047    psVectorCopy(solution, backup, PS_TYPE_F64);
     1048    psFree(backup);
    9691049
    9701050    return images;
     
    9791059                                     psImage *convMask, // Output convolved mask
    9801060                                     const pmReadout *ro1, const pmReadout *ro2, // Input readouts
    981                                      psImage *sys1, psImage *sys2, // Systematic error images
     1061                                     psImage *kernelErr1, psImage *kernelErr2, // Kernel error images
    9821062                                     psImage *subMask, // Input subtraction mask
    9831063                                     psImageMaskType maskBad, // Mask value to give bad pixels
     
    9981078    // Only generate polynomial values every kernel footprint, since we have already assumed
    9991079    // (with the stamps) that it does not vary rapidly on this scale.
    1000     psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
    1001                                                               xMin + x0 + size + 1,
    1002                                                               yMin + y0 + size + 1);
     1080    psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, xMin + x0 + size + 1,
     1081                                                              yMin + y0 + size + 1);        // Polynomial
    10031082    float background = doBG ? p_pmSubtractionSolutionBackground(kernels, polyValues) : 0.0; // Background term
    10041083
    10051084    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1006         convolveRegion(out1->image, out1->variance, convMask, &kernelImage, &kernelVariance,
    1007                        ro1->image, ro1->variance, sys1, subMask, kernels, polyValues, background, *region,
    1008                        maskBad, maskPoor, poorFrac, useFFT, false);
     1085        convolveRegion(out1->image, out1->variance, out1->mask, &kernelImage, &kernelVariance,
     1086                       ro1->image, ro1->variance, kernelErr1, subMask, kernels, polyValues, background,
     1087                       *region, maskBad, maskPoor, poorFrac, useFFT, false);
    10091088    }
    10101089    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1011         convolveRegion(out2->image, out2->variance, convMask, &kernelImage, &kernelVariance,
    1012                        ro2->image, ro2->variance, sys2, subMask, kernels, polyValues, background, *region,
    1013                        maskBad, maskPoor, poorFrac, useFFT, kernels->mode == PM_SUBTRACTION_MODE_DUAL);
     1090        convolveRegion(out2->image, out2->variance, out2->mask, &kernelImage, &kernelVariance,
     1091                       ro2->image, ro2->variance, kernelErr2, subMask, kernels, polyValues, background,
     1092                       *region, maskBad, maskPoor, poorFrac, useFFT,
     1093                       kernels->mode == PM_SUBTRACTION_MODE_DUAL);
    10141094    }
    10151095
     
    10191099
    10201100    if ((kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) && ro1->mask) {
    1021         psImageMaskType **target = convMask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
     1101        psImageMaskType **target = out1->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
    10221102        psImageMaskType **source = ro1->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Source mask
    10231103
     
    10291109    }
    10301110    if ((kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) && ro2->mask) {
    1031         psImageMaskType **target = convMask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
     1111        psImageMaskType **target = out2->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
    10321112        psImageMaskType **source = ro2->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Source mask
    10331113
     
    10561136    const pmReadout *ro1 = args->data[7]; // Input readout 1
    10571137    const pmReadout *ro2 = args->data[8]; // Input readout 2
    1058     psImage *sys1 = args->data[9]; // Systematic error image 1
    1059     psImage *sys2 = args->data[10]; // Systematic error image 2
     1138    psImage *kernelErr1 = args->data[9]; // Kernel error image 1
     1139    psImage *kernelErr2 = args->data[10]; // Kernel error image 2
    10601140    psImage *subMask = args->data[11]; // Subtraction mask
    10611141    psImageMaskType maskBad = PS_SCALAR_VALUE(args->data[12], PS_TYPE_IMAGE_MASK_DATA); // Output mask value for bad pixels
     
    10671147    bool useFFT = PS_SCALAR_VALUE(args->data[18], U8); // Use FFT for convolution?
    10681148
    1069     return subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2, sys1, sys2,
    1070                                     subMask, maskBad, maskPoor, poorFrac, region, kernels, doBG, useFFT);
     1149    return subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2, kernelErr1,
     1150                                    kernelErr2, subMask, maskBad, maskPoor, poorFrac, region, kernels,
     1151                                    doBG, useFFT);
    10711152}
    10721153
    10731154bool pmSubtractionConvolve(pmReadout *out1, pmReadout *out2, const pmReadout *ro1, const pmReadout *ro2,
    10741155                           psImage *subMask, int stride, psImageMaskType maskBad, psImageMaskType maskPoor,
    1075                            float poorFrac, float sysError, const psRegion *region,
     1156                           float poorFrac, float kernelError, float covarFrac, const psRegion *region,
    10761157                           const pmSubtractionKernels *kernels, bool doBG, bool useFFT)
    10771158{
     
    10821163        PM_ASSERT_READOUT_NON_NULL(ro1, false);
    10831164        PM_ASSERT_READOUT_IMAGE(ro1, false);
     1165        PM_ASSERT_READOUT_IMAGE(out1, false);
    10841166        numCols = ro1->image->numCols;
    10851167        numRows = ro1->image->numRows;
     
    10911173        PM_ASSERT_READOUT_NON_NULL(ro2, false);
    10921174        PM_ASSERT_READOUT_IMAGE(ro2, false);
     1175        PM_ASSERT_READOUT_IMAGE(out2, false);
    10931176        if (numCols == 0 && numRows == 0) {
    10941177            numCols = ro2->image->numCols;
     
    11111194    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(poorFrac, 0.0, false);
    11121195    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(poorFrac, 1.0, false);
    1113     PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(sysError, 0.0, false);
    1114     PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(sysError, 1.0, false);
     1196    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(kernelError, 0.0, false);
     1197    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(kernelError, 1.0, false);
     1198    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(covarFrac, 0.0, false);
     1199    PS_ASSERT_FLOAT_LESS_THAN(covarFrac, 1.0, false);
    11151200    if (region && psRegionIsNaN(*region)) {
    11161201        psString string = psRegionToString(*region);
    1117         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Input region (%s) contains NAN values", string);
     1202        psError(PM_ERR_PROG, true, "Input region (%s) contains NAN values", string);
    11181203        psFree(string);
    11191204        return false;
     
    11241209    bool threaded = pmSubtractionThreaded(); // Running threaded?
    11251210
    1126     // Outputs
    1127     if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1128         if (!out1->image) {
    1129             out1->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1130             // XXX if (threaded) {
    1131             // XXX     psMutexInit(out1->image);
    1132             // XXX }
    1133         }
    1134         if (ro1->variance) {
    1135             if (!out1->variance) {
    1136                 out1->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1137                 // XXX if (threaded) {
    1138                 // XXX     psMutexInit(out1->variance);
    1139                 // XXX }
    1140             }
    1141             psImageInit(out1->variance, 0.0);
    1142         }
    1143     }
    1144     if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1145         if (!out2->image) {
    1146             out2->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1147             // XXX if (threaded) {
    1148             // XXX     psMutexInit(out2->image);
    1149             // XXX }
    1150         }
    1151         if (ro2->variance) {
    1152             if (!out2->variance) {
    1153                 out2->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1154                 // XXX if (threaded) {
    1155                 // XXX     psMutexInit(out2->variance);
    1156                 // XXX }
    1157             }
    1158             psImageInit(out2->variance, 0.0);
    1159         }
    1160     }
    11611211    psImage *convMask = NULL;           // Convolved mask image (common to inputs 1 and 2)
    11621212    if (subMask) {
    1163         // XXX if (threaded) {
    1164         // XXX     psMutexInit(subMask);
    1165         // XXX }
    11661213        if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1167             if (!out1->mask) {
    1168                 out1->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    1169             }
    11701214            convMask = out1->mask;
    11711215        }
    11721216        if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1173             if (convMask) {
    1174                 if (out2->mask) {
    1175                     psFree(out2->mask);
    1176                 }
    1177                 out2->mask = psMemIncrRefCounter(convMask);
    1178             } else {
    1179                 if (!out2->mask) {
    1180                     out2->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    1181                 }
     1217            if (!convMask) {
    11821218                convMask = out2->mask;
    11831219            }
    11841220        }
    1185         psImageInit(convMask, 0);
    1186     }
    1187 
    1188     psImage *sys1 = NULL, *sys2 = NULL; // Systematic error images
     1221    }
     1222
     1223    psImage *kernelErr1 = NULL, *kernelErr2 = NULL; // Kernel error images
     1224#ifdef USE_KERNEL_ERR
    11891225    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1190         sys1 = subtractionSysErrImage(ro1->image, sysError);
    1191         // XXX if (threaded && sys1) {
    1192         // XXX     psMutexInit(sys1);
    1193         // XXX }
     1226        kernelErr1 = subtractionKernelErrImage(ro1->image, kernelError);
    11941227    }
    11951228    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1196         sys2 = subtractionSysErrImage(ro2->image, sysError);
    1197         // XXX if (threaded && sys2) {
    1198         // XXX     psMutexInit(sys2);
    1199         // XXX }
    1200     }
     1229        kernelErr2 = subtractionKernelErrImage(ro2->image, kernelError);
     1230    }
     1231#endif
    12011232
    12021233    int size = kernels->size;           // Half-size of kernel
    12031234
    12041235    // Get region for convolution: [xMin:xMax,yMin:yMax]
    1205     int xMin = size, xMax = numCols - size;
    1206     int yMin = size, yMax = numRows - size;
     1236    int xMin = kernels->xMin + size, xMax = kernels->xMax - size;
     1237    int yMin = kernels->yMin + size, yMax = kernels->yMax - size;
    12071238    if (region) {
    12081239        xMin = PS_MAX(region->x0, xMin);
     
    12471278                psArrayAdd(args, 1, (pmReadout*)ro1); // Casting away const
    12481279                psArrayAdd(args, 1, (pmReadout*)ro2); // Casting away const
    1249                 // Since adding to the array can impact the reference count, we need to lock
    1250                 // XXX if (sys1) {
    1251                 // XXX     psMutexLock(sys1);
    1252                 // XXX }
    1253                 psArrayAdd(args, 1, sys1);
    1254                 // XXX if (sys1) {
    1255                 // XXX     psMutexUnlock(sys1);
    1256                 // XXX }
    1257                 // XXX if (sys2) {
    1258                 // XXX     psMutexLock(sys2);
    1259                 // XXX }
    1260                 psArrayAdd(args, 1, sys2);
    1261                 // XXX if (sys2) {
    1262                 // XXX     psMutexUnlock(sys2);
    1263                 // XXX }
    1264                 // XXX if (subMask) {
    1265                 // XXX     psMutexLock(subMask);
    1266                 // XXX }
     1280                psArrayAdd(args, 1, kernelErr1);
     1281                psArrayAdd(args, 1, kernelErr2);
    12671282                psArrayAdd(args, 1, subMask);
    1268                 // XXX if (subMask) {
    1269                 // XXX     psMutexUnlock(subMask);
    1270                 // XXX }
    12711283                PS_ARRAY_ADD_SCALAR(args, maskBad, PS_TYPE_IMAGE_MASK);
    12721284                PS_ARRAY_ADD_SCALAR(args, maskPoor, PS_TYPE_IMAGE_MASK);
     
    12831295                psFree(job);
    12841296            } else {
    1285                 subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2, sys1, sys2,
    1286                                          subMask, maskBad, maskPoor, poorFrac, subRegion, kernels, doBG,
    1287                                          useFFT);
     1297                subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2,
     1298                                         kernelErr1, kernelErr2, subMask, maskBad, maskPoor, poorFrac,
     1299                                         subRegion, kernels, doBG, useFFT);
    12881300            }
    12891301            psFree(subRegion);
     
    12921304
    12931305    if (!psThreadPoolWait(false)) {
    1294         psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
     1306        psError(psErrorCodeLast(), false, "Error waiting for threads.");
    12951307        return false;
    12961308    }
     
    13071319            psFree(job);
    13081320        }
    1309 
    1310         // XXX if (subMask) {
    1311         // XXX     psMutexDestroy(subMask);
    1312         // XXX }
    1313         // XXX if (sys1) {
    1314         // XXX     psMutexDestroy(sys1);
    1315         // XXX }
    1316         // XXX if (sys2) {
    1317         // XXX     psMutexDestroy(sys2);
    1318         // XXX }
    13191321    }
    13201322    psImageConvolveSetThreads(oldThreads);
    13211323
    1322     psFree(sys1);
    1323     psFree(sys2);
     1324    psFree(kernelErr1);
     1325    psFree(kernelErr2);
    13241326
    13251327    // Calculate covariances
    1326     // This can take a while, so we only do it for a single instance
    1327     // XXX psImageCovarianceCalculate could be multithreaded
     1328    // This can be fairly involved, so we only do it for a single instance
     1329    // Enable threads for covariance calculation, since we're not threading on top of it.
     1330    oldThreads = psImageCovarianceSetThreads(true);
    13281331    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    13291332        psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
     1333        psKernelTruncate(kernel, covarFrac);
    13301334        out1->covariance = psImageCovarianceCalculate(kernel, ro1->covariance);
    13311335        psFree(kernel);
     
    13341338        psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0,
    13351339                                               kernels->mode == PM_SUBTRACTION_MODE_DUAL); // Conv. kernel
     1340        psKernelTruncate(kernel, covarFrac);
    13361341        out2->covariance = psImageCovarianceCalculate(kernel, ro2->covariance);
    13371342        psFree(kernel);
    13381343    }
     1344    psImageCovarianceSetThreads(oldThreads);
    13391345
    13401346    // Copy anything that wasn't convolved
     
    13761382    }
    13771383
    1378 
    13791384    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolve image: %f sec",
    13801385             psTimerClear("pmSubtractionConvolve"));
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtraction.h

    r21363 r27840  
    6868                              const psVector *deviations, ///< Deviations for each stamp
    6969                              psImage *subMask, ///< Subtraction mask
    70                               float sigmaRej, ///< Number of RMS deviations above zero at which to reject
    71                               int footprint ///< Half-size of stamp
     70                              float sigmaRej ///< Number of RMS deviations above zero at which to reject
    7271    );
    7372
     
    9493
    9594/// Generate images of the convolution kernel elements
    96 psArray *pmSubtractionKernelSolutions(const psVector *solution, ///< Solution vector
    97                                       const pmSubtractionKernels *kernels, ///< Kernel parameters
    98                                       float x, float y ///< Normalised position [-1,1] for images
     95psArray *pmSubtractionKernelSolutions(const pmSubtractionKernels *kernels, ///< Kernel parameters
     96                                      float x, float y, ///< Normalised position [-1,1] for images
     97                                      bool wantDual ///< Calculate for the dual kernel?
    9998    );
     99
    100100
    101101/// Execute a thread job to convolve a patch of the image
     
    113113                           psImageMaskType maskPoor, ///< Mask value to give poor pixels
    114114                           float poorFrac, ///< Fraction for "poor"
    115                            float sysError, ///< Relative systematic error
     115                           float kernelError, ///< Relative systematic error in kernel
     116                           float covarFrac,  ///< Truncation fraction for kernel before covariance calculation
    116117                           const psRegion *region, ///< Region to convolve (or NULL)
    117118                           const pmSubtractionKernels *kernels, ///< Kernel parameters
     
    127128    );
    128129
     130/// Return normalised coordinates
     131void p_pmSubtractionPolynomialNormCoords(
     132    float *xOut, float *yOut,           ///< Normalised coordinates, returned
     133    float xIn, float yIn,               ///< Input coordinates
     134    int xMin, int xMax, int yMin, int yMax ///< Bounds of validity
     135    );
     136
    129137/// Given (normalised) coordinates (x,y), generate a matrix where the elements (i,j) are x^i * y^j
    130138psImage *p_pmSubtractionPolynomial(psImage *output, ///< Output matrix, or NULL
     
    138146psImage *p_pmSubtractionPolynomialFromCoords(psImage *output, ///< Output matrix, or NULL
    139147                                             const pmSubtractionKernels *kernels, ///< Kernel parameters
    140                                              int numCols, int numRows, ///< Size of image of interest
    141148                                             int x, int y ///< Position of interest
    142149    );
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionAnalysis.c

    r25060 r27840  
    1616#define KERNEL_MOSAIC 2                 // Half-number of kernel instances in the mosaic image
    1717
     18//#define TESTING
    1819
    1920bool pmSubtractionAnalysis(psMetadata *analysis, psMetadata *header,
     
    4243        psFree(subRegion);
    4344
    44         psMetadataAddPtr(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_REGION,
    45                          PS_DATA_REGION | PS_META_DUPLICATE_OK,
     45        psMetadataAddStr(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_REGION, PS_META_DUPLICATE_OK,
    4646                         "Region over which subtraction was performed", string);
    4747        psFree(string);
     
    7474                                                           false); // Image of the kernel
    7575                if (!kernel) {
    76                     psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
     76                    psError(psErrorCodeLast(), false, "Unable to generate kernel image.");
    7777                    psFree(convKernels);
    7878                    return false;
     
    8181                if (psImageOverlaySection(convKernels, kernel, (i + KERNEL_MOSAIC) * fullSize,
    8282                                          (j + KERNEL_MOSAIC) * fullSize, "=") == 0) {
    83                     psError(PS_ERR_UNKNOWN, false, "Unable to overlay kernel image.");
     83                    psError(psErrorCodeLast(), false, "Unable to overlay kernel image.");
    8484                    psFree(kernel);
    8585                    psFree(convKernels);
     
    9393                                                      true); // Image of the kernel
    9494                    if (!kernel) {
    95                         psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
     95                        psError(psErrorCodeLast(), false, "Unable to generate kernel image.");
    9696                        psFree(convKernels);
    9797                        return false;
     
    101101                                              (2 * KERNEL_MOSAIC + 1 + i + KERNEL_MOSAIC) * fullSize + 4,
    102102                                              (j + KERNEL_MOSAIC) * fullSize, "=") == 0) {
    103                         psError(PS_ERR_UNKNOWN, false, "Unable to overlay kernel image.");
     103                        psError(psErrorCodeLast(), false, "Unable to overlay kernel image.");
    104104                        psFree(kernel);
    105105                        psFree(convKernels);
     
    117117    }
    118118
    119 
    120 #if 0
     119    // sample difference images
     120    {
     121        psMetadataAddArray(analysis, PS_LIST_TAIL, "SUBTRACTION.SAMPLE.STAMP.SET", PS_META_DUPLICATE_OK, "Sample Difference Stamps", kernels->sampleStamps);
     122    }
     123
     124#ifdef TESTING
    121125    // Generate images of the kernel components
    122126    {
    123127        psMetadata *header = psMetadataAlloc(); // Header
    124         for (int i = 0; i < solution->n; i++) {
     128        for (int i = 0; i < kernels->solution1->n; i++) {
    125129            psString name = NULL;       // Header keyword
    126130            psStringAppend(&name, "SOLN%04d", i);
    127             psMetadataAddF64(header, PS_LIST_TAIL, name, 0, NULL, solution->data.F64[i]);
     131            psMetadataAddF64(header, PS_LIST_TAIL, name, 0, NULL, kernels->solution1->data.F64[i]);
    128132            psFree(name);
    129133        }
    130         psArray *kernelImages = pmSubtractionKernelSolutions(solution, kernels, 0.0, 0.0);
    131         psFits *kernelFile = psFitsOpen("kernels.fits", "w");
     134        psArray *kernelImages = pmSubtractionKernelSolutions(kernels, 0.0, 0.0, false);
     135        psFits *kernelFile = psFitsOpen("kernels1.fits", "w");
    132136        (void)psFitsWriteImageCube(kernelFile, header, kernelImages, NULL);
    133137        psFitsClose(kernelFile);
     
    135139        psFree(header);
    136140    }
     141    if (kernels->solution2) {
     142        psMetadata *header = psMetadataAlloc(); // Header
     143        for (int i = 0; i < kernels->solution2->n; i++) {
     144            psString name = NULL;       // Header keyword
     145            psStringAppend(&name, "SOLN%04d", i);
     146            psMetadataAddF64(header, PS_LIST_TAIL, name, 0, NULL, kernels->solution2->data.F64[i]);
     147            psFree(name);
     148        }
     149        psArray *kernelImages = pmSubtractionKernelSolutions(kernels, 0.0, 0.0, true);
     150        psFits *kernelFile = psFitsOpen("kernels2.fits", "w");
     151        (void)psFitsWriteImageCube(kernelFile, header, kernelImages, NULL);
     152        psFitsClose(kernelFile);
     153        psFree(kernelImages);
     154        psFree(header);
     155    }
    137156#endif
    138157
     
    142161        psImage *image = pmSubtractionKernelImage(kernels, 0.5, 0.5, false); // Image of the kernel
    143162        if (!image) {
    144             psError(PS_ERR_UNKNOWN, false, "Unable to generate image of kernel.");
     163            psError(psErrorCodeLast(), false, "Unable to generate image of kernel.");
    145164            return false;
    146165        }
     
    197216        psImage *image = pmSubtractionKernelImage(kernels, 0.5, 0.5, false); // Image of the kernel
    198217        if (!image) {
    199             psError(PS_ERR_UNKNOWN, false, "Unable to generate image of kernel.");
     218            psError(psErrorCodeLast(), false, "Unable to generate image of kernel.");
    200219            return false;
    201220        }
     
    254273    // Difference in background
    255274    {
    256         psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
    257                                                                   numCols / 2.0, numRows / 2.0); // Polynomial
     275        psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, 0.0, 0.0); // Polynomial
    258276        float bg = p_pmSubtractionSolutionBackground(kernels, polyValues); // Background difference
    259277
     
    280298        psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_DEV_RMS, 0, "RMS stamp deviation",
    281299                         kernels->rms);
     300
     301        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
     302        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
     303        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMIN_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
     304        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMIN_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
     305        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMAX_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
     306        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMAX_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
     307
     308        psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
     309        psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
     310        psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMIN_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
     311        psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMIN_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
     312        psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMAX_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
     313        psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMAX_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
    282314    }
    283315
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionAnalysis.h

    r25060 r27840  
    2424#define PM_SUBTRACTION_ANALYSIS_DECONV_MAX   "SUBTRACTION.DECONV.MAX"   // Maximum deconvolution fraction
    2525
     26#define PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_MEAN  "SUBTRACTION.RES.FSIGMA.MEAN"      // RMS stamp deviation
     27#define PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_STDEV "SUBTRACTION.RES.FSIGMA.STDEV"      // RMS stamp deviation
     28#define PM_SUBTRACTION_ANALYSIS_FMIN_RES_MEAN    "SUBTRACTION.RES.FMIN.MEAN"      // RMS stamp deviation
     29#define PM_SUBTRACTION_ANALYSIS_FMIN_RES_STDEV   "SUBTRACTION.RES.FMIN.STDEV"      // RMS stamp deviation
     30#define PM_SUBTRACTION_ANALYSIS_FMAX_RES_MEAN    "SUBTRACTION.RES.FMAX.MEAN"      // RMS stamp deviation
     31#define PM_SUBTRACTION_ANALYSIS_FMAX_RES_STDEV   "SUBTRACTION.RES.FMAX.STDEV"      // RMS stamp deviation
     32
    2633// Derive QA information about the subtraction
    2734bool pmSubtractionAnalysis(
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionEquation.c

    r24844 r27840  
    77#include <pslib.h>
    88
     9#include "pmErrorCodes.h"
    910#include "pmSubtraction.h"
    1011#include "pmSubtractionKernels.h"
     
    1516#include "pmSubtractionVisual.h"
    1617
    17 // #define TESTING                         // TESTING output for debugging; may not work with threads!
    18 
    19 #define USE_VARIANCE                    // Include variance in equation?
     18//#define TESTING                         // TESTING output for debugging; may not work with threads!
     19
     20//#define USE_WEIGHT                      // Include weight (1/variance) in equation?
     21//#define USE_WINDOW                      // Include weight (1/variance) in equation?
     22
    2023
    2124//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    2326//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    2427
    25 // Calculate the sum over a stamp product
    26 static inline double calculateSumProduct(const psKernel *image1, // First image in multiplication
    27                                          const psKernel *image2, // Second image in multiplication
    28                                          const psKernel *variance, // Variance image
    29                                          int footprint // (Half-)Size of stamp
    30     )
     28// Calculate the least-squares matrix and vector
     29static bool calculateMatrixVector(psImage *matrix, // Least-squares matrix, updated
     30                                  psVector *vector, // Least-squares vector, updated
     31                                  double *norm,     // Normalisation, updated
     32                                  const psKernel *input, // Input image (target)
     33                                  const psKernel *reference, // Reference image (convolution source)
     34                                  const psKernel *weight,  // Weight image
     35                                  const psKernel *window,  // Window image
     36                                  const psArray *convolutions,         // Convolutions for each kernel
     37                                  const pmSubtractionKernels *kernels, // Kernels
     38                                  const psImage *polyValues, // Spatial polynomial values
     39                                  int footprint, // (Half-)Size of stamp
     40                                  int normWindow, // Window (half-)size for normalisation measurement
     41                                  const pmSubtractionEquationCalculationMode mode
     42                                  )
    3143{
    32     double sum = 0.0;                   // Sum of the image products
     44    // (I - R * sum_i a_i k_i - g) (R * k_j) = 0
     45    // I C_j = sum_i C_i C_j
     46
     47    // Background: C_i = 1.0
     48    // Normalisation: C_i = R
     49
     50    int numKernels = kernels->num;                      // Number of kernels
     51    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     52    int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     53    int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     54    int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     55    double poly[numPoly];                                 // Polynomial terms
     56    double poly2[numPoly][numPoly];                       // Polynomial-polynomial values
     57
     58    // Evaluate polynomial-polynomial terms
     59    // XXX we can skip this if we are not calculating kernel coeffs
     60    for (int iyOrder = 0, iIndex = 0; iyOrder <= spatialOrder; iyOrder++) {
     61        for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex++) {
     62            double iPoly = polyValues->data.F64[iyOrder][ixOrder]; // Value of polynomial
     63            poly[iIndex] = iPoly;
     64            for (int jyOrder = 0, jIndex = 0; jyOrder <= spatialOrder; jyOrder++) {
     65                for (int jxOrder = 0; jxOrder <= spatialOrder - jyOrder; jxOrder++, jIndex++) {
     66                    double jPoly = polyValues->data.F64[jyOrder][jxOrder];
     67                    poly2[iIndex][jIndex] = iPoly * jPoly;
     68                }
     69            }
     70        }
     71    }
     72
     73    // initialize the matrix and vector for NOP on all coeffs.  we only fill in the coeffs we
     74    // choose to calculate
     75    psImageInit(matrix, 0.0);
     76    psVectorInit(vector, 1.0);
     77    for (int i = 0; i < matrix->numCols; i++) {
     78        matrix->data.F64[i][i] = 1.0;
     79    }
     80
     81    // the order of the elements in the matrix and vector is:
     82    // [kernel 0, x^0 y^0][kernel 1 x^0 y^0]...[kernel N, x^0 y^0]
     83    // [kernel 0, x^1 y^0][kernel 1 x^1 y^0]...[kernel N, x^1 y^0]
     84    // [kernel 0, x^n y^m][kernel 1 x^n y^m]...[kernel N, x^n y^m]
     85    // normalization
     86    // bg 0, bg 1, bg 2 (only 0 is currently used?)
     87
     88    for (int i = 0; i < numKernels; i++) {
     89        psKernel *iConv = convolutions->data[i]; // Convolution for index i
     90        for (int j = i; j < numKernels; j++) {
     91            psKernel *jConv = convolutions->data[j]; // Convolution for index j
     92
     93            double sumCC = 0.0;         // Sum of convolution products
     94            for (int y = - footprint; y <= footprint; y++) {
     95                for (int x = - footprint; x <= footprint; x++) {
     96                    double cc = iConv->kernel[y][x] * jConv->kernel[y][x];
     97                    if (weight) {
     98                        cc *= weight->kernel[y][x];
     99                    }
     100                    if (window) {
     101                        cc *= window->kernel[y][x];
     102                    }
     103                    sumCC += cc;
     104                }
     105            }
     106
     107            // Spatial variation of kernel coeffs
     108            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     109                for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     110                    for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     111                        double value = sumCC * poly2[iTerm][jTerm];
     112                        matrix->data.F64[iIndex][jIndex] = value;
     113                        matrix->data.F64[jIndex][iIndex] = value;
     114                    }
     115                }
     116            }
     117        }
     118
     119        double sumRC = 0.0;             // Sum of the reference-convolution products
     120        double sumIC = 0.0;             // Sum of the input-convolution products
     121        double sumC = 0.0;              // Sum of the convolution
     122        for (int y = - footprint; y <= footprint; y++) {
     123            for (int x = - footprint; x <= footprint; x++) {
     124                float conv = iConv->kernel[y][x];
     125                float in = input->kernel[y][x];
     126                float ref = reference->kernel[y][x];
     127                double ic = in * conv;
     128                double rc = ref * conv;
     129                double c = conv;
     130                if (weight) {
     131                    float wtVal = weight->kernel[y][x];
     132                    ic *= wtVal;
     133                    rc *= wtVal;
     134                    c *= wtVal;
     135                }
     136                if (window) {
     137                    float winVal = window->kernel[y][x];
     138                    ic *= winVal;
     139                    rc *= winVal;
     140                    c  *= winVal;
     141                }
     142                sumIC += ic;
     143                sumRC += rc;
     144                sumC += c;
     145            }
     146        }
     147        // Spatial variation
     148        for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     149            double normTerm = sumRC * poly[iTerm];
     150            double bgTerm = sumC * poly[iTerm];
     151            if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     152                matrix->data.F64[iIndex][normIndex] = normTerm;
     153                matrix->data.F64[normIndex][iIndex] = normTerm;
     154            }
     155            if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     156                matrix->data.F64[iIndex][bgIndex] = bgTerm;
     157                matrix->data.F64[bgIndex][iIndex] = bgTerm;
     158            }
     159            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     160                vector->data.F64[iIndex] = sumIC * poly[iTerm];
     161                if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
     162                    // subtract norm * sumRC * poly[iTerm]
     163                    psAssert (kernels->solution1, "programming error: define solution first!");
     164                    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     165                    double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
     166                    vector->data.F64[iIndex] -= norm * normTerm;
     167                }
     168            }
     169        }
     170    }
     171
     172    double sumRR = 0.0;                 // Sum of the reference product
     173    double sumIR = 0.0;                 // Sum of the input-reference product
     174    double sum1 = 0.0;                  // Sum of the background
     175    double sumR = 0.0;                  // Sum of the reference
     176    double sumI = 0.0;                  // Sum of the input
     177    double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
    33178    for (int y = - footprint; y <= footprint; y++) {
    34179        for (int x = - footprint; x <= footprint; x++) {
    35             double value = image1->kernel[y][x] * image2->kernel[y][x];
    36 #ifdef USE_VARIANCE
    37             value /= variance->kernel[y][x];
    38 #endif
    39             sum += value;
    40         }
    41     }
    42     return sum;
    43 }
    44 
    45 // Calculate a single element of the least-squares matrix, with the polynomial expansions in one direction
    46 static inline bool calculateMatrixElement1(psImage *matrix, // Matrix to calculate
    47                                            int i, int j, // Coordinates of element
    48                                            const psKernel *image1, // First image in multiplication
    49                                            const psKernel *image2, // Second image in multiplication
    50                                            const psKernel *variance, // Variance image
    51                                            const psImage *polyValues, // Spatial polynomial values
    52                                            int numKernels, // Number of kernel basis functions
    53                                            int footprint, // (Half-)Size of stamp
    54                                            int spatialOrder, // Maximum order of spatial variation
    55                                            bool symmetric // Is the matrix symmetric?
    56     )
     180            double in = input->kernel[y][x];
     181            double ref = reference->kernel[y][x];
     182            double ir = in * ref;
     183            double rr = PS_SQR(ref);
     184            double one = 1.0;
     185
     186            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) {
     187                normI1 += ref;
     188                normI2 += in;
     189            }
     190
     191            if (weight) {
     192                float wtVal = weight->kernel[y][x];
     193                rr *= wtVal;
     194                ir *= wtVal;
     195                in *= wtVal;
     196                ref *= wtVal;
     197                one *= wtVal;
     198            }
     199            if (window) {
     200                float  winVal = window->kernel[y][x];
     201                rr      *= winVal;
     202                ir      *= winVal;
     203                in      *= winVal;
     204                ref *= winVal;
     205                one *= winVal;
     206            }
     207            sumRR += rr;
     208            sumIR += ir;
     209            sumR += ref;
     210            sumI += in;
     211            sum1 += one;
     212        }
     213    }
     214
     215    *norm = normI2 / normI1;
     216
     217    if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     218        matrix->data.F64[normIndex][normIndex] = sumRR;
     219        vector->data.F64[normIndex] = sumIR;
     220        // subtract sum over kernels * kernel solution
     221    }
     222    if (mode & PM_SUBTRACTION_EQUATION_BG) {
     223        matrix->data.F64[bgIndex][bgIndex] = sum1;
     224        vector->data.F64[bgIndex] = sumI;
     225    }
     226    if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
     227        matrix->data.F64[normIndex][bgIndex] = sumR;
     228        matrix->data.F64[bgIndex][normIndex] = sumR;
     229    }
     230
     231    // check for any NAN values in the result, skip if found:
     232    for (int iy = 0; iy < matrix->numRows; iy++) {
     233        for (int ix = 0; ix < matrix->numCols; ix++) {
     234            if (!isfinite(matrix->data.F64[iy][ix])) {
     235                fprintf (stderr, "WARNING: NAN in matrix\n");
     236                return false;
     237            }
     238        }
     239    }
     240    for (int ix = 0; ix < vector->n; ix++) {
     241        if (!isfinite(vector->data.F64[ix])) {
     242            fprintf (stderr, "WARNING: NAN in vector\n");
     243            return false;
     244        }
     245    }
     246
     247    return true;
     248}
     249
     250
     251// Calculate the least-squares matrix and vector for dual convolution
     252static bool calculateDualMatrixVector(psImage *matrix, // Least-squares matrix, updated
     253                                      psVector *vector, // Least-squares vector, updated
     254                                      double *norm,     // Normalisation, updated
     255                                      const psKernel *image1, // Image 1
     256                                      const psKernel *image2, // Image 2
     257                                      const psKernel *weight,  // Weight image
     258                                      const psKernel *window,  // Window image
     259                                      const psArray *convolutions1, // Convolutions of image 1 for each kernel
     260                                      const psArray *convolutions2, // Convolutions of image 2 for each kernel
     261                                      const pmSubtractionKernels *kernels, // Kernels
     262                                      const psImage *polyValues, // Spatial polynomial values
     263                                      int footprint, // (Half-)Size of stamp
     264                                      int normWindow, // Window (half-)size for normalisation measurement
     265                                      const pmSubtractionEquationCalculationMode mode
     266                                      )
    57267{
    58     double sum = calculateSumProduct(image1, image2, variance, footprint); // Sum of the image products
    59     if (!isfinite(sum)) {
    60         return false;
    61     }
    62 
    63     // Generate the pseudo-convolutions from the spatial polynomial terms
    64     for (int iyOrder = 0, iIndex = i; iyOrder <= spatialOrder; iyOrder++) {
    65         for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex += numKernels) {
    66             double convPoly = sum * polyValues->data.F64[iyOrder][ixOrder];
    67 
    68             assert(iIndex < matrix->numRows && j < matrix->numCols);
    69 
    70             matrix->data.F64[iIndex][j] = convPoly;
    71             if (symmetric) {
    72 
    73                 assert(iIndex < matrix->numCols && j < matrix->numRows);
    74 
    75                 matrix->data.F64[j][iIndex] = convPoly;
    76             }
    77         }
    78     }
    79     return true;
    80 }
    81 
    82 // Calculate a single element of the least-squares matrix, with the polynomial expansions in both directions
    83 static inline bool calculateMatrixElement2(psImage *matrix, // Matrix to calculate
    84                                            int i, int j, // Coordinates of element
    85                                            const psKernel *image1, // First image in multiplication
    86                                            const psKernel *image2, // Second image in multiplication
    87                                            const psKernel *variance, // Variance image
    88                                            const psImage *polyValues, // Spatial polynomial values
    89                                            int numKernels, // Number of kernel basis functions
    90                                            int footprint, // (Half-)Size of stamp
    91                                            int spatialOrder, // Maximum order of spatial variation
    92                                            bool symmetric // Is the matrix symmetric?
    93     )
    94 {
    95     double sum = calculateSumProduct(image1, image2, variance, footprint); // Sum of the image products
    96     if (!isfinite(sum)) {
    97         return false;
    98     }
    99 
    100     // Generate the pseudo-convolutions from the spatial polynomial terms
    101     for (int iyOrder = 0, iIndex = i; iyOrder <= spatialOrder; iyOrder++) {
    102         for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex += numKernels) {
     268    int numKernels = kernels->num;                      // Number of kernels
     269    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     270    int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     271    int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     272    int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     273    double poly[numPoly];                                 // Polynomial terms
     274    double poly2[numPoly][numPoly];                       // Polynomial-polynomial values
     275
     276    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
     277    int numParams = numKernels * numPoly + 1 + numBackground;       // Number of regular parameters
     278    int numParams2 = numKernels * numPoly;                          // Number of additional parameters for dual
     279    int numDual = numParams + numParams2;                           // Total number of parameters for dual
     280
     281    psAssert(matrix &&
     282             matrix->type.type == PS_TYPE_F64 &&
     283             matrix->numCols == numDual &&
     284             matrix->numRows == numDual,
     285             "Least-squares matrix is bad.");
     286    psAssert(vector &&
     287             vector->type.type == PS_TYPE_F64 &&
     288             vector->n == numDual,
     289             "Least-squares vector is bad.");
     290
     291    // Evaluate polynomial-polynomial terms
     292    for (int iyOrder = 0, iIndex = 0; iyOrder <= spatialOrder; iyOrder++) {
     293        for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex++) {
    103294            double iPoly = polyValues->data.F64[iyOrder][ixOrder]; // Value of polynomial
    104             for (int jyOrder = 0, jIndex = j; jyOrder <= spatialOrder; jyOrder++) {
    105                 for (int jxOrder = 0; jxOrder <= spatialOrder - jyOrder; jxOrder++, jIndex += numKernels) {
    106                     double convPoly = sum * iPoly * polyValues->data.F64[jyOrder][jxOrder];
    107 
    108                     assert(iIndex < matrix->numRows && jIndex < matrix->numCols);
    109 
    110                     matrix->data.F64[iIndex][jIndex] = convPoly;
    111                     if (symmetric) {
    112 
    113                         assert(iIndex < matrix->numCols && jIndex < matrix->numRows);
    114 
    115                         matrix->data.F64[jIndex][iIndex] = convPoly;
    116                     }
    117                 }
    118             }
    119         }
    120     }
    121     return true;
    122 }
    123 
    124 // Calculate the square part of the matrix derived from multiplying convolutions
    125 static bool calculateMatrixSquare(psImage *matrix, // Matrix to calculate
    126                                   const psArray *convolutions1, // Convolutions for element 1
    127                                   const psArray *convolutions2, // Convolutions for element 2
    128                                   const psKernel *variance, // Variance image
    129                                   const psImage *polyValues, // Polynomial values
    130                                   int numKernels, // Number of kernel basis functions
    131                                   int spatialOrder, // Order of spatial variation
    132                                   int footprint // Half-size of stamp
    133                                   )
    134 {
    135     bool symmetric = (convolutions1 == convolutions2 ? true : false); // Is matrix symmetric?
     295            poly[iIndex] = iPoly;
     296            for (int jyOrder = 0, jIndex = 0; jyOrder <= spatialOrder; jyOrder++) {
     297                for (int jxOrder = 0; jxOrder <= spatialOrder - jyOrder; jxOrder++, jIndex++) {
     298                    double jPoly = polyValues->data.F64[jyOrder][jxOrder];
     299                    poly2[iIndex][jIndex] = iPoly * jPoly;
     300                }
     301            }
     302        }
     303    }
     304
     305
     306    // initialize the matrix and vector for NOP on all coeffs.  we only fill in the coeffs we
     307    // choose to calculate
     308    psImageInit(matrix, 0.0);
     309    psVectorInit(vector, 1.0);
     310    for (int i = 0; i < matrix->numCols; i++) {
     311        matrix->data.F64[i][i] = 1.0;
     312    }
    136313
    137314    for (int i = 0; i < numKernels; i++) {
    138         psKernel *iConv = convolutions1->data[i]; // Convolution for i-th element
    139 
    140         for (int j = (symmetric ? i : 0); j < numKernels; j++) {
    141             psKernel *jConv = convolutions2->data[j]; // Convolution for j-th element
    142 
    143             if (!calculateMatrixElement2(matrix, i, j, iConv, jConv, variance, polyValues, numKernels,
    144                                          footprint, spatialOrder, symmetric)) {
    145                 psTrace("psModules.imcombine", 2, "Bad sumCC at %d, %d", i, j);
    146                 return false;
    147             }
    148         }
    149     }
    150 
    151     return true;
    152 }
    153 
    154 // Calculate least-squares matrix and vector
    155 static bool calculateMatrix(psImage *matrix, // Matrix to calculate
    156                             const pmSubtractionKernels *kernels, // Kernel components
    157                             const psArray *convolutions, // Convolutions of source with kernels
    158                             const psKernel *input, // Input stamp, or NULL
    159                             const psKernel *variance, // Variance stamp
    160                             const psImage *polyValues, // Spatial polynomial values
    161                             int footprint, // (Half-)Size of stamp
    162                             bool normAndBG // Calculate normalisation and background terms?
    163     )
    164 {
    165     int numKernels = kernels->num;      // Number of kernel components
    166     int spatialOrder = kernels->spatialOrder; // Maximum order of spatial variation
    167     int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variation terms
    168     int bgOrder = kernels->bgOrder;     // Maximum order of background fit
    169     int numBackground = normAndBG ? PM_SUBTRACTION_POLYTERMS(bgOrder) : 0; // Number of background terms
    170     int numTerms = numKernels * numSpatial + (normAndBG ? 1 + numBackground : 0); // Total number of terms
    171     assert(matrix);
    172     assert(matrix->numCols == matrix->numRows);
    173     assert(matrix->numCols == numTerms);
    174     assert(convolutions && convolutions->n == numKernels);
    175     assert(polyValues);
    176     assert(!normAndBG || input);        // If we want the normalisation and BG, then we need the input image
    177 
    178     // Square part of the matrix (convolution-convolution products)
    179     if (!calculateMatrixSquare(matrix, convolutions, convolutions, variance, polyValues, numKernels,
    180                                spatialOrder, footprint)) {
    181         return false;
    182     }
    183 
    184     // XXX To support higher-order background model than simply constant, the below code needs to be updated.
    185     if (normAndBG) {
    186         int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    187         int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    188 
    189         for (int i = 0; i < numKernels; i++) {
    190             psKernel *conv = convolutions->data[i]; // Convolution for i-th element
    191 
    192             // Normalisation-convolution terms
    193             if (!calculateMatrixElement1(matrix, i, normIndex, conv, input, variance, polyValues, numKernels,
    194                                          footprint, spatialOrder, true)) {
    195                 psTrace("psModules.imcombine", 2, "Bad sumIC at %d", i);
    196                 return false;
    197             }
    198 
    199             // Background-convolution terms
    200             double sumC = 0.0;          // Sum of the convolution
     315        psKernel *iConv1 = convolutions1->data[i]; // Convolution 1 for index i
     316        psKernel *iConv2 = convolutions2->data[i]; // Convolution 2 for index i
     317        for (int j = i; j < numKernels; j++) {
     318            psKernel *jConv1 = convolutions1->data[j]; // Convolution 1 for index j
     319            psKernel *jConv2 = convolutions2->data[j]; // Convolution 2 for index j
     320
     321            double sumAA = 0.0;         // Sum of convolution products between image 1
     322            double sumBB = 0.0;         // Sum of convolution products between image 2
     323            double sumAB = 0.0;         // Sum of convolution products across images 1 and 2
    201324            for (int y = - footprint; y <= footprint; y++) {
    202325                for (int x = - footprint; x <= footprint; x++) {
    203                     double value = conv->kernel[y][x];
    204 #ifdef USE_VARIANCE
    205                     value /= variance->kernel[y][x];
    206 #endif
    207                     sumC += value;
    208                 }
    209             }
    210             if (!isfinite(sumC)) {
    211                 psTrace("psModules.imcombine", 2, "Bad sumC at %d", i);
    212                 return false;
    213             }
    214 
    215             for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
    216                 for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
    217                     double value = sumC * polyValues->data.F64[yOrder][xOrder];
    218                     matrix->data.F64[index][bgIndex] = value;
    219                     matrix->data.F64[bgIndex][index] = value;
    220                 }
    221             }
    222         }
    223 
    224         // Background only, normalisation only, and background-normalisation terms
    225         double sum1 = 0.0;              // Sum of the weighting
    226         double sumI = 0.0;              // Sum of the input
    227         double sumII = 0.0;             // Sum of the input squared
     326                    double aa = iConv1->kernel[y][x] * jConv1->kernel[y][x];
     327                    double bb = iConv2->kernel[y][x] * jConv2->kernel[y][x];
     328                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
     329                    if (weight) {
     330                        float wtVal = weight->kernel[y][x];
     331                        aa *= wtVal;
     332                        bb *= wtVal;
     333                        ab *= wtVal;
     334                    }
     335                    if (window) {
     336                        float wtVal = window->kernel[y][x];
     337                        aa *= wtVal;
     338                        bb *= wtVal;
     339                        ab *= wtVal;
     340                    }
     341                    sumAA += aa;
     342                    sumBB += bb;
     343                    sumAB += ab;
     344                }
     345            }
     346
     347            // Spatial variation of kernel coeffs
     348            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     349                for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     350                    for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     351                        double aa = sumAA * poly2[iTerm][jTerm];
     352                        double bb = sumBB * poly2[iTerm][jTerm];
     353                        double ab = sumAB * poly2[iTerm][jTerm];
     354
     355                        matrix->data.F64[iIndex][jIndex] = aa;
     356                        matrix->data.F64[jIndex][iIndex] = aa;
     357
     358                        matrix->data.F64[iIndex + numParams][jIndex + numParams] = bb;
     359                        matrix->data.F64[jIndex + numParams][iIndex + numParams] = bb;
     360
     361                        matrix->data.F64[iIndex][jIndex + numParams] = ab;
     362                        matrix->data.F64[jIndex + numParams][iIndex] = ab;
     363                    }
     364                }
     365            }
     366        }
     367        for (int j = 0; j < i; j++) {
     368            psKernel *jConv2 = convolutions2->data[j]; // Convolution 2 for index j
     369            double sumAB = 0.0;         // Sum of convolution products for matrix C
     370            for (int y = - footprint; y <= footprint; y++) {
     371                for (int x = - footprint; x <= footprint; x++) {
     372                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
     373                    if (weight) {
     374                        ab *= weight->kernel[y][x];
     375                    }
     376                    if (window) {
     377                        ab *= window->kernel[y][x];
     378                    }
     379                    sumAB += ab;
     380                }
     381            }
     382
     383            // Spatial variation of kernel coeffs
     384            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     385                for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     386                    for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     387                        double ab = sumAB * poly2[iTerm][jTerm];
     388                        matrix->data.F64[iIndex][jIndex + numParams] = ab;
     389                        matrix->data.F64[jIndex + numParams][iIndex] = ab;
     390                    }
     391                }
     392            }
     393        }
     394
     395        double sumAI2 = 0.0;            // Sum of A.I_2 products (for vector)
     396        double sumBI2 = 0.0;            // Sum of B.I_2 products (for vector)
     397        double sumAI1 = 0.0;            // Sum of A.I_1 products (for matrix, normalisation)
     398        double sumA = 0.0;              // Sum of A (for matrix, background)
     399        double sumBI1 = 0.0;            // Sum of B.I_1 products (for matrix, normalisation)
     400        double sumB = 0.0;              // Sum of B products (for matrix, background)
     401        double sumI2 = 0.0;             // Sum of I_2 (for vector, background)
    228402        for (int y = - footprint; y <= footprint; y++) {
    229403            for (int x = - footprint; x <= footprint; x++) {
    230                 double invNoise2 = 1.0;
    231 #ifdef USE_VARIANCE
    232                 invNoise2 /= variance->kernel[y][x];
    233 #endif
    234                 double value = input->kernel[y][x] * invNoise2;
    235                 sumI += value;
    236                 sumII += value * input->kernel[y][x];
    237                 sum1 += invNoise2;
    238             }
    239         }
    240         if (!isfinite(sumI)) {
    241             psTrace("psModules.imcombine", 2, "Bad sumI detected");
     404                double a = iConv1->kernel[y][x];
     405                double b = iConv2->kernel[y][x];
     406                float i1 = image1->kernel[y][x];
     407                float i2 = image2->kernel[y][x];
     408
     409                double ai2 = a * i2;
     410                double bi2 = b * i2;
     411                double ai1 = a * i1;
     412                double bi1 = b * i1;
     413
     414                if (weight) {
     415                    float wtVal = weight->kernel[y][x];
     416                    ai2 *= wtVal;
     417                    bi2 *= wtVal;
     418                    ai1 *= wtVal;
     419                    bi1 *= wtVal;
     420                    a *= wtVal;
     421                    b *= wtVal;
     422                    i2 *= wtVal;
     423                }
     424                if (window) {
     425                    float wtVal = window->kernel[y][x];
     426                    ai2 *= wtVal;
     427                    bi2 *= wtVal;
     428                    ai1 *= wtVal;
     429                    bi1 *= wtVal;
     430                    a *= wtVal;
     431                    b *= wtVal;
     432                    i2 *= wtVal;
     433                }
     434                sumAI2 += ai2;
     435                sumBI2 += bi2;
     436                sumAI1 += ai1;
     437                sumA += a;
     438                sumBI1 += bi1;
     439                sumB += b;
     440                sumI2 += i2;
     441            }
     442        }
     443        // Spatial variation
     444        for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     445            double ai2 = sumAI2 * poly[iTerm];
     446            double bi2 = sumBI2 * poly[iTerm];
     447            double ai1 = sumAI1 * poly[iTerm];
     448            double a   = sumA * poly[iTerm];
     449            double bi1 = sumBI1 * poly[iTerm];
     450            double b   = sumB * poly[iTerm];
     451
     452            if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     453                matrix->data.F64[iIndex][normIndex] = ai1;
     454                matrix->data.F64[normIndex][iIndex] = ai1;
     455                matrix->data.F64[iIndex + numParams][normIndex] = bi1;
     456                matrix->data.F64[normIndex][iIndex + numParams] = bi1;
     457            }
     458            if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     459                matrix->data.F64[iIndex][bgIndex] = a;
     460                matrix->data.F64[bgIndex][iIndex] = a;
     461                matrix->data.F64[iIndex + numParams][bgIndex] = b;
     462                matrix->data.F64[bgIndex][iIndex + numParams] = b;
     463            }
     464            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     465                vector->data.F64[iIndex] = ai2;
     466                vector->data.F64[iIndex + numParams] = bi2;
     467                if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
     468                    // subtract norm * sumRC * poly[iTerm]
     469                    psAssert (kernels->solution1, "programming error: define solution first!");
     470                    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     471                    double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
     472                    vector->data.F64[iIndex] -= norm * ai1;
     473                    vector->data.F64[iIndex + numParams] -= norm * bi1;
     474                }
     475            }
     476        }
     477    }
     478
     479    double sumI1 = 0.0;                 // Sum of I_1 (for matrix, background-normalisation)
     480    double sumI1I1 = 0.0;               // Sum of I_1^2 (for matrix, normalisation-normalisation)
     481    double sum1 = 0.0;                  // Sum of 1 (for matrix, background-background)
     482    double sumI2 = 0.0;                 // Sum of I_2 (for vector, background)
     483    double sumI1I2 = 0.0;               // Sum of I_1.I_2 (for vector, normalisation)
     484    double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
     485    for (int y = - footprint; y <= footprint; y++) {
     486        for (int x = - footprint; x <= footprint; x++) {
     487            double i1 = image1->kernel[y][x];
     488            double i2 = image2->kernel[y][x];
     489
     490            double i1i1 = i1 * i1;
     491            double one = 1.0;
     492            double i1i2 = i1 * i2;
     493
     494            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) {
     495                normI1 += i1;
     496                normI2 += i2;
     497            }
     498
     499            if (weight) {
     500                float wtVal = weight->kernel[y][x];
     501                i1 *= wtVal;
     502                i1i1 *= wtVal;
     503                one *= wtVal;
     504                i2 *= wtVal;
     505                i1i2 *= wtVal;
     506            }
     507            if (window) {
     508                float wtVal = window->kernel[y][x];
     509                i1 *= wtVal;
     510                i1i1 *= wtVal;
     511                one *= wtVal;
     512                i2 *= wtVal;
     513                i1i2 *= wtVal;
     514            }
     515            sumI1 += i1;
     516            sumI1I1 += i1i1;
     517            sum1 += one;
     518            sumI2 += i2;
     519            sumI1I2 += i1i2;
     520        }
     521    }
     522
     523    *norm = normI2 / normI1;
     524
     525    if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     526        matrix->data.F64[normIndex][normIndex] = sumI1I1;
     527        vector->data.F64[normIndex] = sumI1I2;
     528    }
     529    if (mode & PM_SUBTRACTION_EQUATION_BG) {
     530        matrix->data.F64[bgIndex][bgIndex] = sum1;
     531        vector->data.F64[bgIndex] = sumI2;
     532    }
     533    if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
     534        matrix->data.F64[bgIndex][normIndex] = sumI1;
     535        matrix->data.F64[normIndex][bgIndex] = sumI1;
     536    }
     537
     538    // check for any NAN values in the result, skip if found:
     539    for (int iy = 0; iy < matrix->numRows; iy++) {
     540        for (int ix = 0; ix < matrix->numCols; ix++) {
     541            if (!isfinite(matrix->data.F64[iy][ix])) {
     542                fprintf (stderr, "WARNING: NAN in matrix\n");
     543                return false;
     544            }
     545        }
     546    }
     547    for (int ix = 0; ix < vector->n; ix++) {
     548        if (!isfinite(vector->data.F64[ix])) {
     549            fprintf (stderr, "WARNING: NAN in vector\n");
    242550            return false;
    243551        }
    244         if (!isfinite(sumII)) {
    245             psTrace("psModules.imcombine", 2, "Bad sumII detected");
    246             return false;
    247         }
    248         if (!isfinite(sum1)) {
    249             psTrace("psModules.imcombine", 2, "Bad sum1 detected");
    250             return false;
    251         }
    252         matrix->data.F64[normIndex][normIndex] = sumII;
    253         matrix->data.F64[bgIndex][bgIndex] = sum1;
    254         matrix->data.F64[normIndex][bgIndex] = sumI;
    255         matrix->data.F64[bgIndex][normIndex] = sumI;
    256     }
     552    }
     553
    257554
    258555    return true;
    259556}
    260557
    261 
    262 // Calculate least-squares matrix and vector
    263 static bool calculateVector(psVector *vector, // Vector to calculate, or NULL
    264                             const pmSubtractionKernels *kernels, // Kernel components
    265                             const psArray *convolutions, // Convolutions of source with kernels
    266                             const psKernel *input, // Input stamp, or NULL if !normAndBG
    267                             const psKernel *target, // Target stamp
    268                             const psKernel *variance, // Variance stamp
    269                             const psImage *polyValues, // Spatial polynomial values
    270                             int footprint, // (Half-)Size of stamp
    271                             bool normAndBG // Calculate normalisation and background terms?
    272     )
    273 {
    274     int numKernels = kernels->num;      // Number of kernel components
    275     int spatialOrder = kernels->spatialOrder; // Maximum order of spatial variation
    276     int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variation terms
    277     int bgOrder = kernels->bgOrder;     // Maximum order of background fit
    278     int numBackground = normAndBG ? PM_SUBTRACTION_POLYTERMS(bgOrder) : 0; // Number of background terms
    279     int numTerms = numKernels * numSpatial + (normAndBG ? 1 + numBackground : 0); // Total number of terms
    280     assert(vector && vector->n == numTerms);
    281     assert(convolutions && convolutions->n == numKernels);
    282     assert(target);
    283     assert(polyValues);
    284     assert(!normAndBG || input);       // If we want the normalisation and BG, then we need the input image
    285 
    286     // Convolution terms
    287     for (int i = 0; i < numKernels; i++) {
    288         psKernel *conv = convolutions->data[i]; // Convolution for i-th element
    289         double sumTC = 0.0;          // Sum of the target and convolution
    290         for (int y = - footprint; y <= footprint; y++) {
    291             for (int x = - footprint; x <= footprint; x++) {
    292                 double value = target->kernel[y][x] * conv->kernel[y][x];
    293 #ifdef USE_VARIANCE
    294                 value /= variance->kernel[y][x];
    295 #endif
    296                 sumTC += value;
    297             }
    298         }
    299         if (!isfinite(sumTC)) {
    300             psTrace("psModules.imcombine", 2, "Bad sumTC at %d", i);
    301             return false;
    302         }
    303         for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
    304             for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
    305                 vector->data.F64[index] = sumTC * polyValues->data.F64[yOrder][xOrder];
    306             }
    307         }
    308     }
    309 
    310     if (normAndBG) {
    311         // Background terms
    312         double sumT = 0.0;              // Sum of the target
    313         double sumIT = 0.0;             // Sum of the input-target product
    314         for (int y = - footprint; y <= footprint; y++) {
    315             for (int x = - footprint; x <= footprint; x++) {
    316                 double value = target->kernel[y][x];
    317 #ifdef USE_VARIANCE
    318                 value /= variance->kernel[y][x];
    319 #endif
    320                 sumIT += value * input->kernel[y][x];
    321                 sumT += value;
    322             }
    323         }
    324         if (!isfinite(sumT)) {
    325             psTrace("psModules.imcombine", 2, "Bad sumI detected");
    326             return false;
    327         }
    328         if (!isfinite(sumIT)) {
    329             psTrace("psModules.imcombine", 2, "Bad sumIT detected");
    330             return false;
    331         }
    332 
    333         int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation term
    334         vector->data.F64[normIndex] = sumIT;
    335         int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background term
    336         vector->data.F64[bgIndex] = sumT;
    337     }
    338 
    339     return true;
    340 }
    341 
    342 
    343 
    344 // Calculate the cross-matrix, composed of convolutions of each image
    345 // Note that the cross-matrix is NOT square
    346 static bool calculateMatrixCross(psImage *matrix, // Matrix to calculate
    347                                  const pmSubtractionKernels *kernels, // Kernel components
    348                                  const psArray *convolutions1, // Convolutions of image 1
    349                                  const psArray *convolutions2, // Convolutions of image 2
    350                                  const psKernel *image1, // Image 1 stamp
    351                                  const psKernel *variance, // Variance stamp
    352                                  const psImage *polyValues, // Spatial polynomial values
    353                                  int footprint // (Half-)Size of stamp
    354                                  )
    355 {
    356     assert(matrix);
    357     int numKernels = kernels->num;      // Number of kernel components
    358     int spatialOrder = kernels->spatialOrder; // Maximum order of spatial variation
    359     int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial polynomial terms
    360     int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    361     int numCols = numKernels * numSpatial + 1 + numBackground; // Number of columns
    362     int numRows = numKernels * numSpatial; // Number of rows
    363     assert(matrix->numCols == numCols && matrix->numRows == numRows);
    364     assert(convolutions1 && convolutions1->n == numKernels);
    365     assert(convolutions2 && convolutions2->n == numKernels);
    366 
    367     int normIndex, bgIndex;             // Indices in matrix for normalisation and background terms
    368     PM_SUBTRACTION_INDICES(normIndex, bgIndex, kernels);
    369 
    370     if (!calculateMatrixSquare(matrix, convolutions1, convolutions2, variance, polyValues, numKernels,
    371                                spatialOrder, footprint)) {
    372         return false;
    373     }
    374 
    375     for (int i = 0; i < numKernels; i++) {
    376         // Normalisation
    377         psKernel *conv = convolutions2->data[i]; // Convolution
    378         if (!calculateMatrixElement1(matrix, i, normIndex, conv, image1, variance, polyValues, numKernels,
    379                                      footprint, spatialOrder, false)) {
    380             psTrace("psModules.imcombine", 2, "Bad sumIC at %d", i);
    381             return false;
    382         }
    383 
    384         // Background
    385         double sumC = 0.0;              // Sum of the weighting
    386         for (int y = - footprint; y <= footprint; y++) {
    387             for (int x = - footprint; x <= footprint; x++) {
    388                 double value = conv->kernel[y][x];
    389 #ifdef USE_VARIANCE
    390                 value /= variance->kernel[y][x];
    391 #endif
    392                 sumC += value;
    393             }
    394         }
    395         if (!isfinite(sumC)) {
    396             psTrace("psModules.imcombine", 2, "Bad sumC detected at %d", i);
    397             return false;
    398         }
    399         for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
    400             for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
    401                 matrix->data.F64[index][bgIndex] = sumC * polyValues->data.F64[yOrder][xOrder];
    402             }
    403         }
    404     }
    405 
    406     return true;
    407 }
    408 
    409 
     558#if 1
    410559// Add in penalty term to least-squares vector
    411 static bool calculatePenalty(psVector *vector, // Vector to which to add in penalty term
    412                              const pmSubtractionKernels *kernels // Kernel parameters
     560bool calculatePenalty(psImage *matrix,                     // Matrix to which to add in penalty term
     561                             psVector *vector,                    // Vector to which to add in penalty term
     562                             const pmSubtractionKernels *kernels, // Kernel parameters
     563                             float norm                           // Normalisation
    413564    )
    414565{
     
    420571    int spatialOrder = kernels->spatialOrder; // Order of spatial variations
    421572    int numKernels = kernels->num; // Number of kernel components
     573    int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variations
     574    int numParams = numKernels * numSpatial;                 // Number of kernel parameters
     575
     576    // order is :
     577    // [p_0,x_0,y_0 p_1,x_0,y_0, p_2,x_0,y_0]
     578    // [p_0,x_1,y_0 p_1,x_1,y_0, p_2,x_1,y_0]
     579    // [p_0,x_0,y_1 p_1,x_0,y_1, p_2,x_0,y_1]
     580    // [norm]
     581    // [bg]
     582    // [q_0,x_0,y_0 q_1,x_0,y_0, q_2,x_0,y_0]
     583    // [q_0,x_1,y_0 q_1,x_1,y_0, q_2,x_1,y_0]
     584    // [q_0,x_0,y_1 q_1,x_0,y_1, q_2,x_0,y_1]
     585
    422586    for (int i = 0; i < numKernels; i++) {
    423587        for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
    424588            for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
    425                 vector->data.F64[index] -= penalties->data.F32[i];
     589                // Contribution to chi^2: a_i^2 P_i
     590                psAssert(isfinite(penalties->data.F32[i]), "Invalid penalty");
     591                matrix->data.F64[index][index] += norm * penalties->data.F32[i];
     592                if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     593                    matrix->data.F64[index + numParams + 2][index + numParams + 2] += norm * penalties->data.F32[i];
     594                    // matrix[i][i] is ~ (k_i * I_1)(k_i * I_1)
     595                    // penalties scale with second moments
     596                    //
     597                }
    426598            }
    427599        }
     
    430602    return true;
    431603}
     604# endif
    432605
    433606//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    439612// Calculate the value of a polynomial, specified by coefficients and polynomial values
    440613double p_pmSubtractionCalculatePolynomial(const psVector *coeff, // Coefficients
    441                                                  const psImage *polyValues, // Polynomial values
    442                                                  int order, // Order of polynomials
    443                                                  int index, // Index at which to begin
    444                                                  int step // Step between subsequent indices
    445                                                  )
     614                                          const psImage *polyValues, // Polynomial values
     615                                          int order, // Order of polynomials
     616                                          int index, // Index at which to begin
     617                                          int step // Step between subsequent indices
     618                                          )
    446619{
    447620    double sum = 0.0;                   // Value of the polynomial sum
     
    458631
    459632double p_pmSubtractionSolutionCoeff(const pmSubtractionKernels *kernels, const psImage *polyValues,
    460                                            int index, bool wantDual)
     633                                    int index, bool wantDual)
    461634{
    462635#if 0
     
    511684    const pmSubtractionKernels *kernels = job->args->data[1]; // Kernels
    512685    int index = PS_SCALAR_VALUE(job->args->data[2], S32); // Stamp index
    513 
    514     return pmSubtractionCalculateEquationStamp(stamps, kernels, index);
     686    pmSubtractionEquationCalculationMode mode  = PS_SCALAR_VALUE(job->args->data[3], S32); // calculation model
     687
     688    return pmSubtractionCalculateEquationStamp(stamps, kernels, index, mode);
    515689}
    516690
    517691bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
    518                                          int index)
     692                                         int index, const pmSubtractionEquationCalculationMode mode)
    519693{
    520694    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    529703    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    530704
     705    // numKernels is the number of unique kernel images (one for each Gaussian modified by a specific polynomial).
     706    // = \sum_i^N_Gaussians [(order + 1) * (order + 2) / 2], eg for 1 Gauss and 1st order, numKernels = 3
     707
    531708    // Total number of parameters to solve for: coefficient of each kernel basis function, multipled by the
    532709    // number of coefficients for the spatial polynomial, normalisation and a constant background offset.
    533710    int numParams = numKernels * numSpatial + 1 + numBackground;
     711    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     712        // An additional image is convolved
     713        numParams += numKernels * numSpatial;
     714    }
    534715
    535716    pmSubtractionStamp *stamp = stamps->stamps->data[index]; // Stamp of interest
    536717    psAssert(stamp->status == PM_SUBTRACTION_STAMP_CALCULATE, "We only operate on stamps with this state.");
    537718
    538     // Generate convolutions
     719    // Generate convolutions: these are generated once and saved
    539720    if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
    540         psError(PS_ERR_UNKNOWN, false, "Unable to convolve stamp %d.", index);
     721        psError(psErrorCodeLast(), false, "Unable to convolve stamp %d.", index);
    541722        return NULL;
    542723    }
     
    566747#endif
    567748
     749    // XXX visualize the set of convolved stamps
     750
    568751    psImage *polyValues = p_pmSubtractionPolynomial(NULL, spatialOrder,
    569752                                                    stamp->xNorm, stamp->yNorm); // Polynomial terms
    570753
    571     bool new = stamp->vector1 ? false : true; // Is this a new run?
     754    bool new = stamp->vector ? false : true; // Is this a new run?
    572755    if (new) {
    573         stamp->matrix1 = psImageAlloc(numParams, numParams, PS_TYPE_F64);
    574         stamp->vector1 = psVectorAlloc(numParams, PS_TYPE_F64);
     756        stamp->matrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     757        stamp->vector = psVectorAlloc(numParams, PS_TYPE_F64);
    575758    }
    576759#ifdef TESTING
    577     psImageInit(stamp->matrix1, NAN);
    578     psVectorInit(stamp->vector1, NAN);
     760    psImageInit(stamp->matrix, NAN);
     761    psVectorInit(stamp->vector, NAN);
    579762#endif
    580763
    581764    bool status;                    // Status of least-squares matrix/vector calculation
     765
     766    psKernel *weight = NULL;
     767    psKernel *window = NULL;
     768
     769#ifdef USE_WEIGHT
     770    weight = stamp->weight;
     771#endif
     772#ifdef USE_WINDOW
     773    window = stamps->window;
     774#endif
     775
    582776    switch (kernels->mode) {
    583777      case PM_SUBTRACTION_MODE_1:
    584         status = calculateMatrix(stamp->matrix1, kernels, stamp->convolutions1, stamp->image1,
    585                                  stamp->variance, polyValues, footprint, true);
    586         status &= calculateVector(stamp->vector1, kernels, stamp->convolutions1, stamp->image1,
    587                                   stamp->image2, stamp->variance, polyValues, footprint, true);
     778        status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image2, stamp->image1,
     779                                       weight, window, stamp->convolutions1, kernels,
     780                                       polyValues, footprint, stamps->normWindow, mode);
    588781        break;
    589782      case PM_SUBTRACTION_MODE_2:
    590         status = calculateMatrix(stamp->matrix1, kernels, stamp->convolutions2, stamp->image2,
    591                                  stamp->variance, polyValues, footprint, true);
    592         status &= calculateVector(stamp->vector1, kernels, stamp->convolutions2, stamp->image2,
    593                                   stamp->image1, stamp->variance, polyValues, footprint, true);
     783        status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image1, stamp->image2,
     784                                       weight, window, stamp->convolutions2, kernels,
     785                                       polyValues, footprint, stamps->normWindow, mode);
    594786        break;
    595787      case PM_SUBTRACTION_MODE_DUAL:
    596         if (new) {
    597             stamp->matrix2 = psImageAlloc(numKernels * numSpatial, numKernels * numSpatial, PS_TYPE_F64);
    598             stamp->matrixX = psImageAlloc(numParams, numKernels * numSpatial, PS_TYPE_F64);
    599             stamp->vector2 = psVectorAlloc(numKernels * numSpatial, PS_TYPE_F64);
    600         }
    601 #ifdef TESTING
    602         psImageInit(stamp->matrix2, NAN);
    603         psImageInit(stamp->matrixX, NAN);
    604         psVectorInit(stamp->vector2, NAN);
    605 #endif
    606         status  = calculateMatrix(stamp->matrix1, kernels, stamp->convolutions1, stamp->image1,
    607                                   stamp->variance, polyValues, footprint, true);
    608         status &= calculateMatrix(stamp->matrix2, kernels, stamp->convolutions2, NULL,
    609                                   stamp->variance, polyValues, footprint, false);
    610         status &= calculateMatrixCross(stamp->matrixX, kernels, stamp->convolutions1,
    611                                        stamp->convolutions2, stamp->image1, stamp->variance, polyValues,
    612                                        footprint);
    613         status &= calculateVector(stamp->vector1, kernels, stamp->convolutions1, stamp->image1,
    614                                   stamp->image2, stamp->variance, polyValues, footprint, true);
    615         status &= calculateVector(stamp->vector2, kernels, stamp->convolutions2, NULL,
    616                                   stamp->image2, stamp->variance, polyValues, footprint, false);
     788        status = calculateDualMatrixVector(stamp->matrix, stamp->vector, &stamp->norm,
     789                                           stamp->image1, stamp->image2,
     790                                           weight, window, stamp->convolutions1, stamp->convolutions2,
     791                                           kernels, polyValues, footprint, stamps->normWindow, mode);
    617792        break;
    618793      default:
     
    623798        stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    624799        psWarning("Rejecting stamp %d (%d,%d) because of bad equation",
    625                   index, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     800                  index, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
    626801    } else {
    627802        stamp->status = PM_SUBTRACTION_STAMP_USED;
     
    629804
    630805#ifdef TESTING
    631     if (psTraceGetLevel("psModules.imcombine.equation") >= 10) {
     806    {
    632807        psString matrixName = NULL;
    633         psStringAppend(&matrixName, "matrix1_%d.fits", index);
     808        psStringAppend(&matrixName, "matrix_%d.fits", index);
    634809        psFits *matrixFile = psFitsOpen(matrixName, "w");
    635810        psFree(matrixName);
    636         psFitsWriteImage(matrixFile, NULL, stamp->matrix1, 0, NULL);
     811        psFitsWriteImage(matrixFile, NULL, stamp->matrix, 0, NULL);
    637812        psFitsClose(matrixFile);
    638813
    639814        matrixName = NULL;
    640         psStringAppend(&matrixName, "vector1_%d.fits", index);
    641         psImage *dummy = psImageAlloc(stamp->vector1->n, 1, PS_TYPE_F64);
    642         memcpy(dummy->data.F64[0], stamp->vector1->data.F64,
    643                PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector1->n);
     815        psStringAppend(&matrixName, "vector_%d.fits", index);
     816        psImage *dummy = psImageAlloc(stamp->vector->n, 1, PS_TYPE_F64);
     817        memcpy(dummy->data.F64[0], stamp->vector->data.F64,
     818               PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector->n);
    644819        matrixFile = psFitsOpen(matrixName, "w");
    645820        psFree(matrixName);
     
    647822        psFree(dummy);
    648823        psFitsClose(matrixFile);
    649 
    650         if (stamp->vector2) {
    651             matrixName = NULL;
    652             psStringAppend(&matrixName, "vector2_%d.fits", index);
    653             dummy = psImageAlloc(stamp->vector2->n, 1, PS_TYPE_F64);
    654             memcpy(dummy->data.F64[0], stamp->vector2->data.F64,
    655                    PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector2->n);
    656             matrixFile = psFitsOpen(matrixName, "w");
    657             psFree(matrixName);
    658             psFitsWriteImage(matrixFile, NULL, dummy, 0, NULL);
    659             psFree(dummy);
    660             psFitsClose(matrixFile);
    661         }
    662 
    663         if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    664             matrixName = NULL;
    665             psStringAppend(&matrixName, "matrix2_%d.fits", index);
    666             matrixFile = psFitsOpen(matrixName, "w");
    667             psFree(matrixName);
    668             psFitsWriteImage(matrixFile, NULL, stamp->matrix2, 0, NULL);
    669             psFitsClose(matrixFile);
    670 
    671             matrixName = NULL;
    672             psStringAppend(&matrixName, "matrixX_%d.fits", index);
    673             matrixFile = psFitsOpen(matrixName, "w");
    674             psFree(matrixName);
    675             psFitsWriteImage(matrixFile, NULL, stamp->matrixX, 0, NULL);
    676             psFitsClose(matrixFile);
    677         }
    678824    }
    679825#endif
     
    684830}
    685831
    686 bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels)
     832bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
     833                                    const pmSubtractionEquationCalculationMode mode)
    687834{
    688835    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    699846        }
    700847
     848        if ((stamp->x <= 0.0) && (stamp->y <= 0.0)) {
     849            psAbort ("bad stamp");
     850        }
     851        if (!isfinite(stamp->x) && !isfinite(stamp->y)) {
     852            psAbort ("bad stamp");
     853        }
     854
    701855        if (pmSubtractionThreaded()) {
    702856            psThreadJob *job = psThreadJobAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION");
     
    704858            psArrayAdd(job->args, 1, (pmSubtractionKernels*)kernels); // Casting away const to put on array
    705859            PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
     860            PS_ARRAY_ADD_SCALAR(job->args, mode, PS_TYPE_S32);
    706861            if (!psThreadJobAddPending(job)) {
    707862                psFree(job);
     
    710865            psFree(job);
    711866        } else {
    712             pmSubtractionCalculateEquationStamp(stamps, kernels, i);
     867            pmSubtractionCalculateEquationStamp(stamps, kernels, i, mode);
    713868        }
    714869    }
    715870
    716871    if (!psThreadPoolWait(true)) {
    717         psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
     872        psError(psErrorCodeLast(), false, "Error waiting for threads.");
    718873        return false;
    719874    }
    720875
    721876    pmSubtractionVisualPlotLeastSquares(stamps);
     877    pmSubtractionVisualShowKernels((pmSubtractionKernels  *)kernels);
     878    pmSubtractionVisualShowBasis(stamps);
    722879
    723880    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate equation: %f sec",
     
    728885}
    729886
    730 bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps)
     887// private functions used on pmSubtractionSolveEquation
     888bool psVectorWriteFile (char *filename, const psVector *vector);
     889bool psFitsWriteImageSimple (char *filename, psImage *image, psMetadata *header);
     890
     891psImage *p_pmSubSolve_wUt (psVector *w, psImage *U);
     892psImage *p_pmSubSolve_VwUt (psImage *V, psImage *wUt);
     893
     894bool p_pmSubSolve_SetWeights (psVector *wApply, psVector *w, psVector *wMask);
     895
     896bool p_pmSubSolve_UtB (psVector **UtB, psImage *U, psVector *B);
     897bool p_pmSubSolve_wUtB (psVector **wUtB, psVector *w, psVector *UtB);
     898bool p_pmSubSolve_VwUtB (psVector **VwUtB, psImage *V, psVector *wUtB);
     899
     900bool p_pmSubSolve_Ax (psVector **B, psImage *A, psVector *x);
     901bool p_pmSubSolve_VdV (double *value, psVector *x, psVector *y);
     902bool p_pmSubSolve_y2 (double *y2, pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps);
     903
     904psImage *p_pmSubSolve_Xvar (psImage *V, psVector *w);
     905
     906double p_pmSubSolve_ChiSquare (pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps);
     907
     908bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels,
     909                                const pmSubtractionStampList *stamps,
     910                                const pmSubtractionEquationCalculationMode mode)
    731911{
    732912    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
     
    734914
    735915    // Check inputs
    736     int numParams = -1;                // Number of parameters
    737     int numParams2 = 0;                // Number of parameters for part solution (DUAL mode)
     916    int numKernels = kernels->num;      // Number of kernel basis functions
     917    int numSpatial = PM_SUBTRACTION_POLYTERMS(kernels->spatialOrder); // Number of spatial variations
     918    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
     919    int numParams = numKernels * numSpatial + 1 + numBackground;    // Number of parameters being solved for
     920    int numSolution1 = numParams, numSolution2 = 0;                 // Number of parameters for each solution
     921    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     922        // An additional image is convolved
     923        numSolution2 = numKernels * numSpatial;
     924        numParams += numSolution2;
     925    }
     926
    738927    for (int i = 0; i < stamps->num; i++) {
    739928        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    743932        }
    744933
    745         PS_ASSERT_VECTOR_NON_NULL(stamp->vector1, false);
    746         if (numParams == -1) {
    747             numParams = stamp->vector1->n;
    748         }
    749         PS_ASSERT_VECTOR_SIZE(stamp->vector1, (long)numParams, false);
    750         PS_ASSERT_VECTOR_TYPE(stamp->vector1, PS_TYPE_F64, false);
    751         PS_ASSERT_IMAGE_NON_NULL(stamp->matrix1, false);
    752         PS_ASSERT_IMAGE_SIZE(stamp->matrix1, numParams, numParams, false);
    753         PS_ASSERT_IMAGE_TYPE(stamp->matrix1, PS_TYPE_F64, false);
    754 
    755         if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    756             PS_ASSERT_IMAGE_NON_NULL(stamp->matrix2, false);
    757             PS_ASSERT_IMAGE_NON_NULL(stamp->matrixX, false);
    758             if (numParams2 == 0) {
    759                 numParams2 = stamp->matrix2->numCols;
    760             }
    761             PS_ASSERT_IMAGE_SIZE(stamp->matrix2, numParams2, numParams2, false);
    762             PS_ASSERT_IMAGE_SIZE(stamp->matrixX, numParams, numParams2, false);
    763             PS_ASSERT_IMAGE_TYPE(stamp->matrix2, PS_TYPE_F64, false);
    764             PS_ASSERT_IMAGE_TYPE(stamp->matrixX, PS_TYPE_F64, false);
    765             PS_ASSERT_VECTOR_NON_NULL(stamp->vector2, false);
    766             PS_ASSERT_VECTOR_SIZE(stamp->vector2, (long)numParams2, false);
    767             PS_ASSERT_VECTOR_TYPE(stamp->vector2, PS_TYPE_F64, false);
    768         }
    769     }
    770     if (numParams == -1) {
    771         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "No suitable stamps found.");
    772         return NULL;
     934        PS_ASSERT_VECTOR_NON_NULL(stamp->vector, false);
     935        PS_ASSERT_VECTOR_SIZE(stamp->vector, (long)numParams, false);
     936        PS_ASSERT_VECTOR_TYPE(stamp->vector, PS_TYPE_F64, false);
     937        PS_ASSERT_IMAGE_NON_NULL(stamp->matrix, false);
     938        PS_ASSERT_IMAGE_SIZE(stamp->matrix, numParams, numParams, false);
     939        PS_ASSERT_IMAGE_TYPE(stamp->matrix, PS_TYPE_F64, false);
    773940    }
    774941
     
    786953        psVectorInit(sumVector, 0.0);
    787954        psImageInit(sumMatrix, 0.0);
     955
     956        psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
     957
    788958        int numStamps = 0;              // Number of good stamps
    789959        for (int i = 0; i < stamps->num; i++) {
    790960            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    791 
    792961            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
    793 
    794 #ifdef TESTING
    795               // XXX double-check for NAN in data:
    796                 for (int iy = 0; iy < stamp->matrix1->numRows; iy++) {
    797                     for (int ix = 0; ix < stamp->matrix1->numCols; ix++) {
    798                         if (!isfinite(stamp->matrix1->data.F64[iy][ix])) {
    799                             fprintf (stderr, "WARNING: NAN in matrix1\n");
    800                         }
    801                     }
    802                 }
    803                 for (int ix = 0; ix < stamp->vector1->n; ix++) {
    804                     if (!isfinite(stamp->vector1->data.F64[ix])) {
    805                         fprintf (stderr, "WARNING: NAN in vector1\n");
    806                     }
    807                 }
    808 #endif
    809 
    810                 (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix1);
    811                 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector1);
     962                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
     963                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
     964                psVectorAppend(norms, stamp->norm);
    812965                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    813966                numStamps++;
     
    817970        }
    818971
     972#if 0
     973        int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     974        calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]);
     975#endif
     976
     977        psVector *solution = NULL;                       // Solution to equation!
     978        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     979        psVectorInit(solution, 0);
     980
     981#if 0
     982        // Regular, straight-forward solution
     983        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     984#else
     985        {
     986            // Solve normalisation and background separately
     987            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     988            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     989
     990            psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
     991            if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
     992                psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
     993                psFree(stats);
     994                psFree(sumMatrix);
     995                psFree(sumVector);
     996                psFree(norms);
     997                return false;
     998            }
     999
     1000            double normValue = stats->robustMedian;
     1001            // double bgValue = 0.0;
     1002
     1003            psFree(stats);
     1004
    8191005#ifdef TESTING
    820         for (int ix = 0; ix < sumVector->n; ix++) {
    821             if (!isfinite(sumVector->data.F64[ix])) {
    822                 fprintf (stderr, "WARNING: NAN in vector1\n");
    823             }
    824         }
    825 #endif
    826 
    827         calculatePenalty(sumVector, kernels);
    828 
    829 #ifdef TESTING
    830         for (int ix = 0; ix < sumVector->n; ix++) {
    831             if (!isfinite(sumVector->data.F64[ix])) {
    832                 fprintf (stderr, "WARNING: NAN in vector1\n");
    833             }
    834         }
    835         {
    836             psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL);
    837             psFits *fits = psFitsOpen("matrixInv.fits", "w");
    838             psFitsWriteImage(fits, NULL, inverse, 0, NULL);
    839             psFitsClose(fits);
    840             psFree(inverse);
    841         }
    842         {
    843             psImage *X = psMatrixInvert(NULL, sumMatrix, NULL);
    844             psImage *Xt = psMatrixTranspose(NULL, X);
    845             psImage *XtX = psMatrixMultiply(NULL, Xt, X);
    846             psFits *fits = psFitsOpen("matrixErr.fits", "w");
    847             psFitsWriteImage(fits, NULL, XtX, 0, NULL);
    848             psFitsClose(fits);
    849             psFree(X);
    850             psFree(Xt);
    851             psFree(XtX);
    852         }
    853 #endif
    854 
    855         psVector *permutation = NULL;       // Permutation vector, required for LU decomposition
    856         psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix);
     1006            fprintf(stderr, "Norm: %lf\n", normValue);
     1007#endif
     1008            // Solve kernel components
     1009            for (int i = 0; i < numSolution1; i++) {
     1010                sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
     1011
     1012                sumMatrix->data.F64[i][normIndex] = 0.0;
     1013                sumMatrix->data.F64[normIndex][i] = 0.0;
     1014            }
     1015            sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
     1016            sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
     1017            sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
     1018
     1019            sumMatrix->data.F64[normIndex][normIndex] = 1.0;
     1020            sumVector->data.F64[normIndex] = 0.0;
     1021
     1022            solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1023
     1024            solution->data.F64[normIndex] = normValue;
     1025        }
     1026# endif
     1027
     1028        if (!kernels->solution1) {
     1029            kernels->solution1 = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     1030            psVectorInit(kernels->solution1, 0.0);
     1031        }
     1032
     1033        // only update the solutions that we chose to calculate:
     1034        if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     1035            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1036            kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
     1037        }
     1038        if (mode & PM_SUBTRACTION_EQUATION_BG) {
     1039            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     1040            kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
     1041        }
     1042        if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     1043            int numKernels = kernels->num;
     1044            int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     1045            int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     1046            for (int i = 0; i < numKernels * numPoly; i++) {
     1047                kernels->solution1->data.F64[i] = solution->data.F64[i];
     1048            }
     1049        }
     1050
     1051        psFree(norms);
     1052        psFree(solution);
     1053        psFree(sumVector);
    8571054        psFree(sumMatrix);
    858         if (!luMatrix) {
    859             psError(PS_ERR_UNKNOWN, true, "LU Decomposition of least-squares matrix failed.\n");
    860             psFree(sumVector);
    861             psFree(luMatrix);
    862             psFree(permutation);
    863             return NULL;
    864         }
    865         kernels->solution1 = psMatrixLUSolution(kernels->solution1, luMatrix, sumVector, permutation);
    8661055
    8671056#ifdef TESTING
     
    8691058        for (int ix = 0; ix < kernels->solution1->n; ix++) {
    8701059            if (!isfinite(kernels->solution1->data.F64[ix])) {
    871                 fprintf (stderr, "WARNING: NAN in vector1\n");
    872             }
    873         }
    874 #endif
    875 
    876         psFree(sumVector);
    877         psFree(luMatrix);
    878         psFree(permutation);
    879         if (!kernels->solution1) {
    880             psError(PS_ERR_UNKNOWN, true, "Failed to solve the least-squares system.\n");
    881             return NULL;
    882         }
     1060                fprintf (stderr, "WARNING: NAN in vector\n");
     1061            }
     1062        }
     1063#endif
     1064
    8831065    } else {
    8841066        // Dual convolution solution
    8851067
    8861068        // Accumulation of stamp matrices/vectors
    887         psImage *sumMatrix1 = psImageAlloc(numParams, numParams, PS_TYPE_F64);
    888         psImage *sumMatrix2 = psImageAlloc(numParams2, numParams2, PS_TYPE_F64);
    889         psImage *sumMatrixX = psImageAlloc(numParams, numParams2, PS_TYPE_F64);
    890         psVector *sumVector1 = psVectorAlloc(numParams, PS_TYPE_F64);
    891         psVector *sumVector2 = psVectorAlloc(numParams, PS_TYPE_F64);
    892         psImageInit(sumMatrix1, 0.0);
    893         psImageInit(sumMatrix2, 0.0);
    894         psImageInit(sumMatrixX, 0.0);
    895         psVectorInit(sumVector1, 0.0);
    896         psVectorInit(sumVector2, 0.0);
     1069        psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     1070        psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64);
     1071        psImageInit(sumMatrix, 0.0);
     1072        psVectorInit(sumVector, 0.0);
     1073
     1074        psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
    8971075
    8981076        int numStamps = 0;              // Number of good stamps
     
    9001078            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    9011079            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
    902                 (void)psBinaryOp(sumMatrix1, sumMatrix1, "+", stamp->matrix1);
    903                 (void)psBinaryOp(sumMatrix2, sumMatrix2, "+", stamp->matrix2);
    904                 (void)psBinaryOp(sumMatrixX, sumMatrixX, "+", stamp->matrixX);
    905                 (void)psBinaryOp(sumVector1, sumVector1, "+", stamp->vector1);
    906                 (void)psBinaryOp(sumVector2, sumVector2, "+", stamp->vector2);
     1080                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
     1081                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
     1082
     1083                psVectorAppend(norms, stamp->norm);
     1084
    9071085                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    9081086                numStamps++;
    9091087            }
    9101088        }
    911         calculatePenalty(sumVector1, kernels);
    912         calculatePenalty(sumVector2, kernels);
    913 
    914         // Pure matrix operations
    915 
    916         // A * a = Ct * b + d
    917         // C * a = B  * b + e
    918         //
    919         // a = (Ct * Bi * C - A)i (Ct * Bi * e - d)
    920         // b = Bi * (C * a - e)
    921         psVector *a = psVectorRecycle(kernels->solution1, numParams, PS_TYPE_F64);
    922         psVector *b = psVectorRecycle(kernels->solution2, numParams2, PS_TYPE_F64);
     1089
    9231090#ifdef TESTING
    924         psVectorInit(a, NAN);
    925         psVectorInit(b, NAN);
    926 #endif
    927         psImage *A = sumMatrix1;
    928         psImage *B = sumMatrix2;
    929         psImage *C = sumMatrixX;
    930         psVector *d = sumVector1;
    931         psVector *e = sumVector2;
    932 
    933         assert(a->n == numParams);
    934         assert(b->n == numParams2);
    935         assert(A->numRows == numParams && A->numCols == numParams);
    936         assert(B->numRows == numParams2 && B->numCols == numParams2);
    937         assert(C->numRows == numParams2 && C->numCols == numParams);
    938         assert(d->n == numParams);
    939         assert(e->n == numParams2);
    940 
    941         psImage *Bi = psMatrixInvert(NULL, B, NULL);
    942         assert(Bi->numRows == numParams2 && Bi->numCols == numParams2);
    943         psImage *Ct = psMatrixTranspose(NULL, C);
    944         assert(Ct->numRows == numParams && Ct->numCols == numParams2);
    945 
    946         psImage *BiC = psMatrixMultiply(NULL, Bi, C);
    947         assert(BiC->numRows == numParams2 && BiC->numCols == numParams);
    948         psImage *CtBi = psMatrixMultiply(NULL, Ct, Bi);
    949         assert(CtBi->numRows == numParams && CtBi->numCols == numParams2);
    950 
    951         psImage *CtBiC = psMatrixMultiply(NULL, Ct, BiC);
    952         assert(CtBiC->numRows == numParams && CtBiC->numCols == numParams);
    953 
    954         psImage *F = (psImage*)psBinaryOp(NULL, CtBiC, "-", A);
    955         assert(F->numRows == numParams && F->numCols == numParams);
    956         float det = 0.0;
    957         psImage *Fi = psMatrixInvert(NULL, F, &det);
    958         assert(Fi->numRows == numParams && Fi->numCols == numParams);
    959         psTrace("psModules.imcombine", 4, "Determinant of F: %f\n", det);
    960 
    961         psVector *g = psVectorAlloc(numParams, PS_TYPE_F64);
     1091        psFitsWriteImageSimple ("sumMatrix.fits", sumMatrix, NULL);
     1092        psVectorWriteFile("sumVector.dat", sumVector);
     1093#endif
     1094
     1095#if 1
     1096        // int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1097        // calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]);
     1098
     1099        int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1100        calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[normIndex][normIndex] / 1000.0);
     1101#endif
     1102
     1103        psVector *solution = NULL;                       // Solution to equation!
     1104        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     1105        psVectorInit(solution, 0);
     1106
     1107#if 0
     1108        // Regular, straight-forward solution
     1109        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1110#else
     1111        {
     1112            // Solve normalisation and background separately
     1113            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1114            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1115
     1116#if 0
     1117            psImage *normMatrix = psImageAlloc(2, 2, PS_TYPE_F64);
     1118            psVector *normVector = psVectorAlloc(2, PS_TYPE_F64);
     1119
     1120            normMatrix->data.F64[0][0] = sumMatrix->data.F64[normIndex][normIndex];
     1121            normMatrix->data.F64[1][1] = sumMatrix->data.F64[bgIndex][bgIndex];
     1122            normMatrix->data.F64[0][1] = normMatrix->data.F64[1][0] = sumMatrix->data.F64[normIndex][bgIndex];
     1123
     1124            normVector->data.F64[0] = sumVector->data.F64[normIndex];
     1125            normVector->data.F64[1] = sumVector->data.F64[bgIndex];
     1126
     1127            psVector *normSolution = psMatrixSolveSVD(NULL, normMatrix, normVector, NAN);
     1128
     1129            double normValue = normSolution->data.F64[0];
     1130            double bgValue = normSolution->data.F64[1];
     1131
     1132            psFree(normMatrix);
     1133            psFree(normVector);
     1134            psFree(normSolution);
     1135#endif
     1136
     1137            psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
     1138            if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
     1139                psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
     1140                psFree(stats);
     1141                psFree(sumMatrix);
     1142                psFree(sumVector);
     1143                psFree(norms);
     1144                return false;
     1145            }
     1146
     1147            double normValue = stats->robustMedian;
     1148
     1149            psFree(stats);
     1150
    9621151#ifdef TESTING
    963         psVectorInit(g, NAN);
    964 #endif
    965         assert(CtBi->numRows == numParams && CtBi->numCols == numParams2);
    966         assert(e->n == numParams2);
    967         assert(d->n == numParams);
    968         for (int i = 0; i < numParams; i++) {
    969             double value = 0.0;
    970             for (int j = 0; j < numParams2; j++) {
    971                 value += CtBi->data.F64[i][j] * e->data.F64[j];
    972             }
    973             g->data.F64[i] = value - d->data.F64[i];
    974         }
    975 
    976         assert(Fi->numRows == numParams && Fi->numCols == numParams);
    977         assert(g->n == numParams);
    978         for (int i = 0; i < numParams; i++) {
    979             double value = 0.0;
    980             for (int j = 0; j < numParams; j++) {
    981                 value += Fi->data.F64[i][j] * g->data.F64[j];
    982             }
    983             a->data.F64[i] = value;
    984         }
    985 
    986         psVector *h = psVectorAlloc(numParams2, PS_TYPE_F64);
     1152            fprintf(stderr, "Norm: %lf\n", normValue);
     1153#endif
     1154
     1155            // Solve kernel components
     1156            for (int i = 0; i < numSolution2; i++) {
     1157                sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
     1158                sumVector->data.F64[i + numSolution1] -= normValue * sumMatrix->data.F64[normIndex][i + numSolution1];
     1159
     1160                sumMatrix->data.F64[i][normIndex] = 0.0;
     1161                sumMatrix->data.F64[normIndex][i] = 0.0;
     1162
     1163                sumMatrix->data.F64[i + numSolution1][normIndex] = 0.0;
     1164                sumMatrix->data.F64[normIndex][i + numSolution1] = 0.0;
     1165            }
     1166            sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
     1167            sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
     1168            sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
     1169
     1170            sumMatrix->data.F64[normIndex][normIndex] = 1.0;
     1171
     1172            sumVector->data.F64[normIndex] = 0.0;
     1173
     1174            solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1175
     1176            solution->data.F64[normIndex] = normValue;
     1177        }
     1178#endif
     1179
     1180
    9871181#ifdef TESTING
    988         psVectorInit(h, NAN);
    989 #endif
    990         assert(C->numRows == numParams2 && C->numCols == numParams);
    991         assert(a->n == numParams);
    992         assert(e->n == numParams2);
    993         for (int i = 0; i < numParams2; i++) {
    994             double value = 0.0;
    995             for (int j = 0; j < numParams; j++) {
    996                 value += C->data.F64[i][j] * a->data.F64[j];
    997             }
    998             h->data.F64[i] = value - e->data.F64[i];
    999         }
    1000 
    1001         assert(Bi->numRows == numParams2 && Bi->numCols == numParams2);
    1002         assert(h->n == numParams2);
    1003         for (int i = 0; i < numParams2; i++) {
    1004             double value = 0.0;
    1005             for (int j = 0; j < numParams2; j++) {
    1006                 value += Bi->data.F64[i][j] * h->data.F64[j];
    1007             }
    1008             b->data.F64[i] = value;
    1009         }
    1010 
    1011 
    1012 #if 0
    1013         for (int i = 0; i < numParams; i++) {
    1014             double aVal1 = 0.0, bVal1 = 0.0;
    1015             for (int j = 0; j < numParams2; j++) {
    1016                 aVal1 += A->data.F64[i][j] * a->data.F64[j];
    1017                 bVal1 += Ct->data.F64[i][j] * b->data.F64[j];
    1018             }
    1019             bVal1 += d->data.F64[i];
    1020             for (int j = numParams2; j < numParams; j++) {
    1021                 aVal1 += A->data.F64[i][j] * a->data.F64[j];
    1022             }
    1023             printf("%d: %lf\n", i, aVal1 - bVal1);
    1024         }
    1025 
    1026         for (int i = 0; i < numParams2; i++) {
    1027             double aVal2 = 0.0, bVal2 = 0.0;
    1028             for (int j = 0; j < numParams2; j++) {
    1029                 aVal2 += C->data.F64[i][j] * a->data.F64[j];
    1030                 bVal2 += B->data.F64[i][j] * b->data.F64[j];
    1031             }
    1032             bVal2 += e->data.F64[i];
    1033             for (int j = numParams2; j < numParams; j++) {
    1034                 aVal2 += C->data.F64[i][j] * a->data.F64[j];
    1035             }
    1036             printf("%d: %lf\n", i, aVal2 - bVal2);
    1037         }
    1038 #endif
    1039 
    1040 #ifdef TESTING
    1041         {
    1042             psFits *fits = psFitsOpen("sumMatrix1.fits", "w");
    1043             psFitsWriteImage(fits, NULL, sumMatrix1, 0, NULL);
    1044             psFitsClose(fits);
    1045         }
    1046         {
    1047             psFits *fits = psFitsOpen("sumMatrix2.fits", "w");
    1048             psFitsWriteImage(fits, NULL, sumMatrix2, 0, NULL);
    1049             psFitsClose(fits);
    1050         }
    1051         {
    1052             psFits *fits = psFitsOpen("sumMatrixX.fits", "w");
    1053             psFitsWriteImage(fits, NULL, sumMatrixX, 0, NULL);
    1054             psFitsClose(fits);
    1055         }
    1056         {
    1057             psFits *fits = psFitsOpen("sumFinverse.fits", "w");
    1058             psFitsWriteImage(fits, NULL, Fi, 0, NULL);
    1059             psFitsClose(fits);
    1060         }
    1061 #endif
    1062 
    1063         kernels->solution1 = a;
    1064         kernels->solution2 = b;
    1065 
    1066         // XXXXX Free temporary matrices and vectors
     1182        for (int i = 0; i < solution->n; i++) {
     1183            fprintf(stderr, "Dual solution %d: %lf\n", i, solution->data.F64[i]);
     1184        }
     1185#endif
     1186
     1187        psFree(sumMatrix);
     1188        psFree(sumVector);
     1189
     1190        psFree(norms);
     1191
     1192        if (!kernels->solution1) {
     1193            kernels->solution1 = psVectorAlloc(numSolution1, PS_TYPE_F64);
     1194            psVectorInit (kernels->solution1, 0.0);
     1195        }
     1196        if (!kernels->solution2) {
     1197            kernels->solution2 = psVectorAlloc(numSolution2, PS_TYPE_F64);
     1198            psVectorInit (kernels->solution2, 0.0);
     1199        }
     1200
     1201        // only update the solutions that we chose to calculate:
     1202        if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     1203            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1204            kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
     1205        }
     1206        if (mode & PM_SUBTRACTION_EQUATION_BG) {
     1207            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     1208            kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
     1209        }
     1210        if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     1211            int numKernels = kernels->num;
     1212            for (int i = 0; i < numKernels * numSpatial; i++) {
     1213                // XXX fprintf (stderr, "keep\n");
     1214                kernels->solution1->data.F64[i] = solution->data.F64[i];
     1215                kernels->solution2->data.F64[i] = solution->data.F64[i + numSolution1];
     1216            }
     1217        }
     1218
     1219
     1220        memcpy(kernels->solution1->data.F64, solution->data.F64,
     1221               numSolution1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
     1222        memcpy(kernels->solution2->data.F64, &solution->data.F64[numSolution1],
     1223               numSolution2 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
     1224
     1225        psFree(solution);
    10671226
    10681227    }
     
    10831242     }
    10841243
    1085     pmSubtractionVisualPlotLeastSquares((pmSubtractionStampList *) stamps); //casting away const
     1244    // pmSubtractionVisualPlotLeastSquares((pmSubtractionStampList *) stamps); //casting away const
    10861245    return true;
    10871246}
    10881247
     1248bool pmSubtractionResidualStats(psVector *fSigRes, psVector *fMaxRes, psVector *fMinRes, psKernel *target, psKernel *source, psKernel *residual, double norm, int footprint) {
     1249
     1250    // XXX measure some useful stats on the residuals
     1251    float sum = 0.0;
     1252    float peak = 0.0;
     1253    for (int y = - footprint; y <= footprint; y++) {
     1254        for (int x = - footprint; x <= footprint; x++) {
     1255            sum += 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm);
     1256            peak = PS_MAX(peak, 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm));
     1257        }
     1258    }
     1259
     1260    // only count pixels with more than X% of the source flux
     1261    // calculate stdev(dflux)
     1262    float dflux1 = 0.0;
     1263    float dflux2 = 0.0;
     1264    int npix = 0;
     1265
     1266    float dmax = 0.0;
     1267    float dmin = 0.0;
     1268
     1269    for (int y = - footprint; y <= footprint; y++) {
     1270        for (int x = - footprint; x <= footprint; x++) {
     1271            float dflux = 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm);
     1272            if (dflux < 0.02*sum) continue;
     1273            dflux1 += residual->kernel[y][x];
     1274            dflux2 += PS_SQR(residual->kernel[y][x]);
     1275            dmax = PS_MAX(residual->kernel[y][x], dmax);
     1276            dmin = PS_MIN(residual->kernel[y][x], dmin);
     1277            npix ++;
     1278        }
     1279    }
     1280    float sigma = sqrt(dflux2 / npix - PS_SQR(dflux1/npix));
     1281    if (!isfinite(sum))  return false;
     1282    if (!isfinite(dmax)) return false;
     1283    if (!isfinite(dmin)) return false;
     1284    if (!isfinite(peak)) return false;
     1285
     1286    // fprintf (stderr, "sum: %f, peak: %f, sigma: %f, fsigma: %f, fmax: %f, fmin: %f\n", sum, peak, sigma, sigma/sum, dmax/peak, dmin/peak);
     1287    psVectorAppend(fSigRes, sigma/sum);
     1288    psVectorAppend(fMaxRes, dmax/peak);
     1289    psVectorAppend(fMinRes, dmin/peak);
     1290    return true;
     1291}
     1292
    10891293psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps,
    1090                                            const pmSubtractionKernels *kernels)
     1294                                           pmSubtractionKernels *kernels)
    10911295{
    10921296    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, NULL);
     
    11031307    psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
    11041308
     1309    // set up holding images for the visualization
     1310    pmSubtractionVisualShowFitInit (stamps);
     1311
     1312    psVector *fSigRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1313    psVector *fMinRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1314    psVector *fMaxRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1315
     1316    // we want to save the residual images for the 9 brightest stamps.
     1317    // identify the 9 brightest stamps
     1318    psVector *keepStamps  = psVectorAlloc(stamps->num, PS_TYPE_S32);
     1319    psVectorInit (keepStamps, 0);
     1320    {
     1321        psVector *flux  = psVectorAlloc(stamps->num, PS_TYPE_F32);
     1322        psVectorInit (flux, 0.0);
     1323
     1324        for (int i = 0; i < stamps->num; i++) {
     1325            pmSubtractionStamp *stamp = stamps->stamps->data[i];
     1326            if (!isfinite(stamp->flux)) continue;
     1327            flux->data.F32[i] = stamp->flux;
     1328        }
     1329
     1330        psVector *index = psVectorSortIndex(NULL, flux);
     1331        for (int i = 0; (i < stamps->num) && (i < 9); i++) {
     1332            int n = stamps->num - i - 1;
     1333            keepStamps->data.S32[index->data.S32[n]] = 1;
     1334        }
     1335        psFree (flux);
     1336        psFree (index);
     1337
     1338        // this function is called multiple times in the iteration, but
     1339        // we only know after the interation is done if we will try again.
     1340        // therefore we must save the sample each time, and blow away the old one
     1341        // if it exists.
     1342        psFree (kernels->sampleStamps);
     1343        kernels->sampleStamps = psArrayAllocEmpty(9);
     1344    }
     1345
     1346    psString log = psStringCopy("Deviations:\n");               // Log message with deviations
    11051347    for (int i = 0; i < stamps->num; i++) {
    11061348        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // The stamp of interest
     
    11161358
    11171359        // Calculate residuals
    1118         psKernel *variance = stamp->variance; // Variance postage stamp
     1360        psKernel *weight = stamp->weight; // Weight postage stamp
    11191361        psImageInit(residual->image, 0.0);
    11201362        if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) {
     
    11331375                                                          false); // Kernel image
    11341376                if (!image) {
    1135                     psError(PS_ERR_UNKNOWN, false, "Unable to generate image of kernel.");
     1377                    psError(psErrorCodeLast(), false, "Unable to generate image of kernel.");
    11361378                    return false;
    11371379                }
     
    11621404                for (int y = - footprint; y <= footprint; y++) {
    11631405                    for (int x = - footprint; x <= footprint; x++) {
    1164                         residual->kernel[y][x] -= convolution->kernel[y][x] * coefficient;
     1406                        residual->kernel[y][x] += convolution->kernel[y][x] * coefficient;
    11651407                    }
    11661408                }
    11671409            }
     1410
     1411            // XXX visualize the target, source, convolution and residual
     1412            pmSubtractionVisualShowFitAddStamp (target, source, residual, background, norm, i);
     1413
    11681414            for (int y = - footprint; y <= footprint; y++) {
    11691415                for (int x = - footprint; x <= footprint; x++) {
    1170                     residual->kernel[y][x] += target->kernel[y][x] - background - source->kernel[y][x] * norm;
    1171                 }
    1172             }
     1416                    residual->kernel[y][x] += background + source->kernel[y][x] * norm - target->kernel[y][x];
     1417                }
     1418            }
     1419
     1420            if (keepStamps->data.S32[i]) {
     1421                psImage *sample = psImageCopy(NULL, residual->image, PS_TYPE_F32);
     1422                psArrayAdd (kernels->sampleStamps, 9, sample);
     1423                psFree (sample);
     1424            }
     1425
     1426            pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, target, source, residual, norm, footprint);
     1427
    11731428        } else {
    11741429            // Dual convolution
     
    11861441                for (int y = - footprint; y <= footprint; y++) {
    11871442                    for (int x = - footprint; x <= footprint; x++) {
    1188                         residual->kernel[y][x] += conv2->kernel[y][x] * coeff2 - conv1->kernel[y][x] * coeff1;
     1443                        residual->kernel[y][x] += conv2->kernel[y][x] * coeff2 + conv1->kernel[y][x] * coeff1;
    11891444                    }
    11901445                }
    11911446            }
     1447
     1448            // XXX visualize the target, source, convolution and residual
     1449            pmSubtractionVisualShowFitAddStamp (image2, image1, residual, background, norm, i);
     1450
    11921451            for (int y = - footprint; y <= footprint; y++) {
    11931452                for (int x = - footprint; x <= footprint; x++) {
    1194                     residual->kernel[y][x] += image2->kernel[y][x] - background - image1->kernel[y][x] * norm;
    1195                 }
    1196             }
     1453                    residual->kernel[y][x] += background + image1->kernel[y][x] * norm - image2->kernel[y][x];
     1454                }
     1455            }
     1456            if (keepStamps->data.S32[i]) {
     1457                psImage *sample = psImageCopy(NULL, residual->image, PS_TYPE_F32);
     1458                psArrayAdd (kernels->sampleStamps, 9, sample);
     1459                psFree (sample);
     1460            }
     1461
     1462            pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, image1, image2, residual, norm, footprint);
    11971463        }
    11981464
     
    12001466        for (int y = - footprint; y <= footprint; y++) {
    12011467            for (int x = - footprint; x <= footprint; x++) {
    1202                 double dev = PS_SQR(residual->kernel[y][x]) / variance->kernel[y][x];
     1468                double dev = PS_SQR(residual->kernel[y][x]) * weight->kernel[y][x];
    12031469                deviation += dev;
    12041470#ifdef TESTING
     
    12091475        deviations->data.F32[i] = devNorm * deviation;
    12101476        psTrace("psModules.imcombine", 5, "Deviation for stamp %d (%d,%d): %f\n",
    1211                 i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5), deviations->data.F32[i]);
     1477                i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5), deviations->data.F32[i]);
     1478        psStringAppend(&log, "Stamp %d (%d,%d): %f\n",
     1479                       i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5), deviations->data.F32[i]);
    12121480        if (!isfinite(deviations->data.F32[i])) {
    12131481            stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    12141482            psTrace("psModules.imcombine", 5,
    12151483                    "Rejecting stamp %d (%d,%d) because of non-finite deviation\n",
    1216                     i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     1484                    i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
    12171485            continue;
    12181486        }
     
    12431511            psFitsClose(fits);
    12441512        }
    1245         if (stamp->variance) {
     1513        if (stamp->weight) {
    12461514            psString filename = NULL;
    1247             psStringAppend(&filename, "stamp_variance_%03d.fits", i);
     1515            psStringAppend(&filename, "stamp_weight_%03d.fits", i);
    12481516            psFits *fits = psFitsOpen(filename, "w");
    12491517            psFree(filename);
    1250             psFitsWriteImage(fits, NULL, stamp->variance->image, 0, NULL);
     1518            psFitsWriteImage(fits, NULL, stamp->weight->image, 0, NULL);
    12511519            psFitsClose(fits);
    12521520        }
     
    12541522
    12551523    }
     1524
     1525    psFree(keepStamps);
     1526
     1527    psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "%s", log);
     1528    psFree(log);
     1529
     1530    // calculate and report the normalization and background for the image center
     1531    {
     1532        polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, 0.0, 0.0);
     1533        double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
     1534        double background = p_pmSubtractionSolutionBackground(kernels, polyValues);// Difference in background
     1535        psLogMsg("psModules.imcombine", PS_LOG_INFO, "normalization: %f, background: %f", norm, background);
     1536
     1537        pmSubtractionVisualShowFit(norm);
     1538        pmSubtractionVisualPlotFit(kernels);
     1539
     1540        psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
     1541        psVectorStats (stats, fSigRes, NULL, NULL, 0);
     1542        kernels->fSigResMean = stats->robustMedian;
     1543        kernels->fSigResStdev = stats->robustStdev;
     1544
     1545        psStatsInit (stats);
     1546        psVectorStats (stats, fMaxRes, NULL, NULL, 0);
     1547        kernels->fMaxResMean = stats->robustMedian;
     1548        kernels->fMaxResStdev = stats->robustStdev;
     1549
     1550        psStatsInit (stats);
     1551        psVectorStats (stats, fMinRes, NULL, NULL, 0);
     1552        kernels->fMinResMean = stats->robustMedian;
     1553        kernels->fMinResStdev = stats->robustStdev;
     1554
     1555        // XXX save these values somewhere
     1556        psLogMsg("psModules.imcombine", PS_LOG_INFO, "fSigma: %f +/- %f, fMaxRes: %f +/- %f, fMinRes: %f +/- %f",
     1557                 kernels->fSigResMean, kernels->fSigResStdev,
     1558                 kernels->fMaxResMean, kernels->fMaxResStdev,
     1559                 kernels->fMinResMean, kernels->fMinResStdev);
     1560
     1561        psFree (fSigRes);
     1562        psFree (fMaxRes);
     1563        psFree (fMinRes);
     1564        psFree (stats);
     1565    }
     1566
    12561567    psFree(residual);
    12571568    psFree(polyValues);
    12581569
     1570
    12591571    return deviations;
    12601572}
     1573
     1574// we are supplied U, not Ut; w represents a diagonal matrix (also, we apply 1/w instead of w)
     1575psImage *p_pmSubSolve_wUt (psVector *w, psImage *U) {
     1576
     1577    psAssert (w->n == U->numCols, "w and U dimensions do not match");
     1578
     1579    // wUt has dimensions transposed relative to Ut.
     1580    psImage *wUt = psImageAlloc (U->numRows, U->numCols, PS_TYPE_F64);
     1581    psImageInit (wUt, 0.0);
     1582
     1583    for (int i = 0; i < wUt->numCols; i++) {
     1584        for (int j = 0; j < wUt->numRows; j++) {
     1585            if (!isfinite(w->data.F64[j])) continue;
     1586            if (w->data.F64[j] == 0.0) continue;
     1587            wUt->data.F64[j][i] = U->data.F64[i][j] / w->data.F64[j];
     1588        }
     1589    }
     1590    return wUt;
     1591}
     1592
     1593// XXX this is just standard matrix multiplication: use psMatrixMultiply?
     1594psImage *p_pmSubSolve_VwUt (psImage *V, psImage *wUt) {
     1595
     1596    psAssert (V->numCols == wUt->numRows, "matrix dimensions do not match");
     1597
     1598    psImage *Ainv = psImageAlloc (wUt->numCols, V->numRows, PS_TYPE_F64);
     1599
     1600    for (int i = 0; i < Ainv->numCols; i++) {
     1601        for (int j = 0; j < Ainv->numRows; j++) {
     1602            double sum = 0.0;
     1603            for (int k = 0; k < V->numCols; k++) {
     1604                sum += V->data.F64[j][k] * wUt->data.F64[k][i];
     1605            }
     1606            Ainv->data.F64[j][i] = sum;
     1607        }
     1608    }
     1609    return Ainv;
     1610}
     1611
     1612// we are supplied U, not Ut
     1613bool p_pmSubSolve_UtB (psVector **UtB, psImage *U, psVector *B) {
     1614
     1615    psAssert (U->numRows == B->n, "U and B dimensions do not match");
     1616
     1617    UtB[0] = psVectorRecycle (UtB[0], U->numCols, PS_TYPE_F64);
     1618
     1619    for (int i = 0; i < U->numCols; i++) {
     1620        double sum = 0.0;
     1621        for (int j = 0; j < U->numRows; j++) {
     1622            sum += B->data.F64[j] * U->data.F64[j][i];
     1623        }
     1624        UtB[0]->data.F64[i] = sum;
     1625    }
     1626    return true;
     1627}
     1628
     1629// w is diagonal
     1630bool p_pmSubSolve_wUtB (psVector **wUtB, psVector *w, psVector *UtB) {
     1631
     1632    psAssert (w->n == UtB->n, "w and UtB dimensions do not match");
     1633
     1634    // wUt has dimensions transposed relative to Ut.
     1635    wUtB[0] = psVectorRecycle (wUtB[0], w->n, PS_TYPE_F64);
     1636    psVectorInit (wUtB[0], 0.0);
     1637
     1638    for (int i = 0; i < w->n; i++) {
     1639        if (!isfinite(w->data.F64[i])) continue;
     1640        if (w->data.F64[i] == 0.0) continue;
     1641        wUtB[0]->data.F64[i] = UtB->data.F64[i] / w->data.F64[i];
     1642    }
     1643    return true;
     1644}
     1645
     1646// this is basically matrix * vector
     1647bool p_pmSubSolve_VwUtB (psVector **VwUtB, psImage *V, psVector *wUtB) {
     1648
     1649    psAssert (V->numCols == wUtB->n, "V and wUtB dimensions do not match");
     1650
     1651    VwUtB[0] = psVectorRecycle (*VwUtB, V->numRows, PS_TYPE_F64);
     1652
     1653    for (int j = 0; j < V->numRows; j++) {
     1654        double sum = 0.0;
     1655        for (int i = 0; i < V->numCols; i++) {
     1656            sum += V->data.F64[j][i] * wUtB->data.F64[i];
     1657        }
     1658        VwUtB[0]->data.F64[j] = sum;
     1659    }
     1660    return true;
     1661}
     1662
     1663// this is basically matrix * vector
     1664bool p_pmSubSolve_Ax (psVector **B, psImage *A, psVector *x) {
     1665
     1666    psAssert (A->numCols == x->n, "A and x dimensions do not match");
     1667
     1668    B[0] = psVectorRecycle (*B, A->numRows, PS_TYPE_F64);
     1669
     1670    for (int j = 0; j < A->numRows; j++) {
     1671        double sum = 0.0;
     1672        for (int i = 0; i < A->numCols; i++) {
     1673            sum += A->data.F64[j][i] * x->data.F64[i];
     1674        }
     1675        B[0]->data.F64[j] = sum;
     1676    }
     1677    return true;
     1678}
     1679
     1680// this is basically Vector * vector
     1681bool p_pmSubSolve_VdV (double *value, psVector *x, psVector *y) {
     1682
     1683    psAssert (x->n == y->n, "x and y dimensions do not match");
     1684
     1685    double sum = 0.0;
     1686    for (int i = 0; i < x->n; i++) {
     1687        sum += x->data.F64[i] * y->data.F64[i];
     1688    }
     1689    *value = sum;
     1690    return true;
     1691}
     1692
     1693bool p_pmSubSolve_y2 (double *y2, pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps) {
     1694
     1695    int footprint = stamps->footprint; // Half-size of stamps
     1696
     1697    double sum = 0.0;
     1698    for (int i = 0; i < stamps->num; i++) {
     1699
     1700        pmSubtractionStamp *stamp = stamps->stamps->data[i];
     1701        if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
     1702
     1703        psKernel *weight = NULL;
     1704        psKernel *window = NULL;
     1705        psKernel *input = NULL;
     1706
     1707#ifdef USE_WEIGHT
     1708        weight = stamp->weight;
     1709#endif
     1710#ifdef USE_WINDOW
     1711        window = stamps->window;
     1712#endif
     1713
     1714        switch (kernels->mode) {
     1715            // MODE_1 : convolve image 1 to match image 2 (and vice versa)
     1716          case PM_SUBTRACTION_MODE_1:
     1717            input = stamp->image2;
     1718            break;
     1719          case PM_SUBTRACTION_MODE_2:
     1720            input = stamp->image1;
     1721            break;
     1722          default:
     1723            psAbort ("programming error");
     1724        }
     1725
     1726        for (int y = - footprint; y <= footprint; y++) {
     1727            for (int x = - footprint; x <= footprint; x++) {
     1728                double in = input->kernel[y][x];
     1729                double value = in*in;
     1730                if (weight) {
     1731                    float wtVal = weight->kernel[y][x];
     1732                    value *= wtVal;
     1733                }
     1734                if (window) {
     1735                    float  winVal = window->kernel[y][x];
     1736                    value *= winVal;
     1737                }
     1738                sum += value;
     1739            }
     1740        }
     1741    }
     1742    *y2 = sum;
     1743    return true;
     1744}
     1745
     1746double p_pmSubSolve_ChiSquare (pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps) {
     1747
     1748    int footprint = stamps->footprint; // Half-size of stamps
     1749    int numKernels = kernels->num;      // Number of kernels
     1750
     1751    double sum = 0.0;
     1752
     1753    psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
     1754    psImageInit(residual->image, 0.0);
     1755
     1756    psImage *polyValues = NULL;         // Polynomial values
     1757
     1758    for (int i = 0; i < stamps->num; i++) {
     1759
     1760        pmSubtractionStamp *stamp = stamps->stamps->data[i];
     1761        if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
     1762
     1763        psKernel *weight = NULL;
     1764        psKernel *window = NULL;
     1765        psKernel *target = NULL;
     1766        psKernel *source = NULL;
     1767
     1768        psArray *convolutions = NULL;
     1769
     1770#ifdef USE_WEIGHT
     1771        weight = stamp->weight;
     1772#endif
     1773#ifdef USE_WINDOW
     1774        window = stamps->window;
     1775#endif
     1776
     1777        switch (kernels->mode) {
     1778            // MODE_1 : convolve image 1 to match image 2 (and vice versa)
     1779          case PM_SUBTRACTION_MODE_1:
     1780            target = stamp->image2;
     1781            source = stamp->image1;
     1782            convolutions = stamp->convolutions1;
     1783            break;
     1784          case PM_SUBTRACTION_MODE_2:
     1785            target = stamp->image1;
     1786            source = stamp->image2;
     1787            convolutions = stamp->convolutions2;
     1788            break;
     1789          default:
     1790            psAbort ("programming error");
     1791        }
     1792
     1793        // Calculate coefficients of the kernel basis functions
     1794        polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, stamp->xNorm, stamp->yNorm);
     1795        double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
     1796        double background = p_pmSubtractionSolutionBackground(kernels, polyValues);// Difference in background
     1797
     1798        psImageInit(residual->image, 0.0);
     1799        for (int j = 0; j < numKernels; j++) {
     1800            psKernel *convolution = convolutions->data[j]; // Convolution
     1801            double coefficient = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, false); // Coefficient
     1802            for (int y = - footprint; y <= footprint; y++) {
     1803                for (int x = - footprint; x <= footprint; x++) {
     1804                    residual->kernel[y][x] -= convolution->kernel[y][x] * coefficient;
     1805                }
     1806            }
     1807        }
     1808
     1809        for (int y = - footprint; y <= footprint; y++) {
     1810            for (int x = - footprint; x <= footprint; x++) {
     1811                double resid = target->kernel[y][x] - background - source->kernel[y][x] * norm + residual->kernel[y][x];
     1812                double value = PS_SQR(resid);
     1813                if (weight) {
     1814                    float wtVal = weight->kernel[y][x];
     1815                    value *= wtVal;
     1816                }
     1817                if (window) {
     1818                    float  winVal = window->kernel[y][x];
     1819                    value *= winVal;
     1820                }
     1821                sum += value;
     1822            }
     1823        }
     1824    }
     1825    psFree (polyValues);
     1826    psFree (residual);
     1827
     1828    return sum;
     1829}
     1830
     1831bool p_pmSubSolve_SetWeights (psVector *wApply, psVector *w, psVector *wMask) {
     1832
     1833    for (int i = 0; i < w->n; i++) {
     1834        wApply->data.F64[i] = wMask->data.U8[i] ? 0.0 : w->data.F64[i];
     1835    }
     1836    return true;
     1837}
     1838
     1839// we are supplied V and w; w represents a diagonal matrix (also, we apply 1/w instead of w)
     1840psImage *p_pmSubSolve_Xvar (psImage *V, psVector *w) {
     1841
     1842    psAssert (w->n == V->numCols, "w and U dimensions do not match");
     1843
     1844    psImage *Vn = psImageAlloc (V->numCols, V->numRows, PS_TYPE_F64);
     1845    psImageInit (Vn, 0.0);
     1846
     1847    // generate Vn = V * w^{-1}
     1848    for (int j = 0; j < Vn->numRows; j++) {
     1849        for (int i = 0; i < Vn->numCols; i++) {
     1850            if (!isfinite(w->data.F64[i])) continue;
     1851            if (w->data.F64[i] == 0.0) continue;
     1852            Vn->data.F64[j][i] = V->data.F64[j][i] / w->data.F64[i];
     1853        }
     1854    }
     1855
     1856    psImage *Xvar = psImageAlloc (V->numCols, V->numRows, PS_TYPE_F64);
     1857    psImageInit (Xvar, 0.0);
     1858
     1859    // generate Xvar = Vn * Vn^T
     1860    for (int j = 0; j < Vn->numRows; j++) {
     1861        for (int i = 0; i < Vn->numCols; i++) {
     1862            double sum = 0.0;
     1863            for (int k = 0; k < Vn->numCols; k++) {
     1864                sum += Vn->data.F64[k][i]*Vn->data.F64[k][j];
     1865            }
     1866            Xvar->data.F64[j][i] = sum;
     1867        }
     1868    }
     1869    return Xvar;
     1870}
     1871
     1872// I get confused by the index values between the image vs matrix usage:  In terms
     1873// of the elements of an image A(x,y) = A->data.F64[y][x] = A_x,y, a matrix
     1874// multiplication is: A_k,j * B_i,k = C_i,j
     1875
     1876
     1877bool psFitsWriteImageSimple (char *filename, psImage *image, psMetadata *header) {
     1878
     1879    psFits *fits = psFitsOpen(filename, "w");
     1880    psFitsWriteImage(fits, header, image, 0, NULL);
     1881    psFitsClose(fits);
     1882
     1883    return true;
     1884}
     1885
     1886bool psVectorWriteFile (char *filename, const psVector *vector) {
     1887
     1888    FILE *f = fopen (filename, "w");
     1889    int fd = fileno(f);
     1890    p_psVectorPrint (fd, vector, "unnamed");
     1891    fclose (f);
     1892
     1893    return true;
     1894}
     1895
     1896
     1897# if 0
     1898
     1899#ifdef TESTING
     1900        psFitsWriteImageSimple("A.fits", sumMatrix, NULL);
     1901        psVectorWriteFile ("B.dat", sumVector);
     1902#endif
     1903
     1904# define SVD_ANALYSIS 0
     1905# define COEFF_SIG 0.0
     1906# define SVD_TOL 0.0
     1907
     1908        // Use SVD to determine the kernel coeffs (and validate)
     1909        if (SVD_ANALYSIS) {
     1910
     1911            // We have sumVector and sumMatrix.  we are trying to solve the following equation:
     1912            // sumMatrix * x = sumVector.
     1913
     1914            // we can use any standard matrix inversion to solve this.  However, the basis
     1915            // functions in general have substantial correlation, so that the solution may be
     1916            // somewhat poorly determined or unstable.  If not numerically ill-conditioned, the
     1917            // system of equations may be statistically ill-conditioned.  Noise in the image
     1918            // will drive insignificant, but correlated, terms in the solution.  To avoid these
     1919            // problems, we can use SVD to identify numerically unconstrained values and to
     1920            // avoid statistically badly determined value.
     1921
     1922            // A = sumMatrix, B = sumVector
     1923            // SVD: A = U w V^T  -> A^{-1} = V (1/w) U^T
     1924            // x = V (1/w) (U^T B)
     1925            // \sigma_x = sqrt(diag(A^{-1}))
     1926            // solve for x and A^{-1} to get x & dx
     1927            // identify the elements of (1/w) that are nan (1/0.0) -> set to 0.0
     1928            // identify the elements of x that are insignificant (x / dx < 1.0? < 0.5?) -> set to 0.0
     1929
     1930            // If I use the SVD trick to re-condition the matrix, I need to break out the
     1931            // kernel and normalization terms from the background term.
     1932            // XXX is this true?  or was this due to an error in the analysis?
     1933
     1934            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     1935
     1936            // now pull out the kernel elements into their own square matrix
     1937            psImage  *kernelMatrix = psImageAlloc  (sumMatrix->numCols - 1, sumMatrix->numRows - 1, PS_TYPE_F64);
     1938            psVector *kernelVector = psVectorAlloc (sumMatrix->numCols - 1, PS_TYPE_F64);
     1939
     1940            for (int ix = 0, kx = 0; ix < sumMatrix->numCols; ix++) {
     1941                if (ix == bgIndex) continue;
     1942                for (int iy = 0, ky = 0; iy < sumMatrix->numRows; iy++) {
     1943                    if (iy == bgIndex) continue;
     1944                    kernelMatrix->data.F64[ky][kx] = sumMatrix->data.F64[iy][ix];
     1945                    ky++;
     1946                }
     1947                kernelVector->data.F64[kx] = sumVector->data.F64[ix];
     1948                kx++;
     1949            }
     1950
     1951            psImage *U = NULL;
     1952            psImage *V = NULL;
     1953            psVector *w = NULL;
     1954            if (!psMatrixSVD (&U, &w, &V, kernelMatrix)) {
     1955                psError(psErrorCodeLast(), false, "failed to perform SVD on sumMatrix\n");
     1956                return NULL;
     1957            }
     1958
     1959            // calculate A_inverse:
     1960            // Ainv = V * w * U^T
     1961            psImage *wUt  = p_pmSubSolve_wUt (w, U);
     1962            psImage *Ainv = p_pmSubSolve_VwUt (V, wUt);
     1963            psImage *Xvar = NULL;
     1964            psFree (wUt);
     1965
     1966# ifdef TESTING
     1967            // kernel terms:
     1968            for (int i = 0; i < w->n; i++) {
     1969                fprintf (stderr, "w: %f\n", w->data.F64[i]);
     1970            }
     1971# endif
     1972            // loop over w adding in more and more of the values until chisquare is no longer
     1973            // dropping significantly.
     1974            // XXX this does not seem to work very well: we seem to need all terms even for
     1975            // simple cases...
     1976
     1977            psVector *Xsvd = NULL;
     1978            {
     1979                psVector *Ax = NULL;
     1980                psVector *UtB = NULL;
     1981                psVector *wUtB = NULL;
     1982
     1983                psVector *wApply = psVectorAlloc(w->n, PS_TYPE_F64);
     1984                psVector *wMask = psVectorAlloc(w->n, PS_TYPE_U8);
     1985                psVectorInit (wMask, 1); // start by masking everything
     1986
     1987                double chiSquareLast = NAN;
     1988                int maxWeight = 0;
     1989
     1990                double Axx, Bx, y2;
     1991
     1992                // XXX this is an attempt to exclude insignificant modes.
     1993                // it was not successful with the ISIS kernel set: removing even
     1994                // the least significant mode leaves additional ringing / noise
     1995                // because the terms are so coupled.
     1996                for (int k = 0; false && (k < w->n); k++) {
     1997
     1998                    // unmask the k-th weight
     1999                    wMask->data.U8[k] = 0;
     2000                    p_pmSubSolve_SetWeights(wApply, w, wMask);
     2001
     2002                    // solve for x:
     2003                    // x = V * w * (U^T * B)
     2004                    p_pmSubSolve_UtB (&UtB, U, kernelVector);
     2005                    p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
     2006                    p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
     2007
     2008                    // chi-square for this system of equations:
     2009                    // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
     2010                    // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
     2011                    p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
     2012                    p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
     2013                    p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
     2014                    p_pmSubSolve_y2 (&y2, kernels, stamps);
     2015
     2016                    // apparently, this works (compare with the brute force value below
     2017                    double chiSquare = Axx - 2.0*Bx + y2;
     2018                    double deltaChi = (k == 0) ? chiSquare : chiSquareLast - chiSquare;
     2019                    chiSquareLast = chiSquare;
     2020
     2021                    // fprintf (stderr, "chi square = %f, delta: %f\n", chiSquare, deltaChi);
     2022                    if (k && !maxWeight && (deltaChi < 1.0)) {
     2023                        maxWeight = k;
     2024                    }
     2025                }
     2026
     2027                // keep all terms or we get extra ringing
     2028                maxWeight = w->n;
     2029                psVectorInit (wMask, 1);
     2030                for (int k = 0; k < maxWeight; k++) {
     2031                    wMask->data.U8[k] = 0;
     2032                }
     2033                p_pmSubSolve_SetWeights(wApply, w, wMask);
     2034
     2035                // solve for x:
     2036                // x = V * w * (U^T * B)
     2037                p_pmSubSolve_UtB (&UtB, U, kernelVector);
     2038                p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
     2039                p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
     2040
     2041                // chi-square for this system of equations:
     2042                // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
     2043                // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
     2044                p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
     2045                p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
     2046                p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
     2047                p_pmSubSolve_y2 (&y2, kernels, stamps);
     2048
     2049                // apparently, this works (compare with the brute force value below
     2050                double chiSquare = Axx - 2.0*Bx + y2;
     2051                psLogMsg ("psModules.imcombine", PS_LOG_INFO, "model kernel with %d terms; chi square = %f\n", maxWeight, chiSquare);
     2052
     2053                // re-calculate A^{-1} to get new variances:
     2054                // Ainv = V * w * U^T
     2055                // XXX since we keep all terms, this is identical to Ainv
     2056                psImage *wUt  = p_pmSubSolve_wUt (wApply, U);
     2057                Xvar = p_pmSubSolve_VwUt (V, wUt);
     2058                psFree (wUt);
     2059
     2060                psFree (Ax);
     2061                psFree (UtB);
     2062                psFree (wUtB);
     2063                psFree (wApply);
     2064                psFree (wMask);
     2065            }
     2066
     2067            // copy the kernel solutions to the full solution vector:
     2068            solution = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     2069            solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     2070
     2071            for (int ix = 0, kx = 0; ix < sumVector->n; ix++) {
     2072                if (ix == bgIndex) {
     2073                    solution->data.F64[ix] = 0;
     2074                    solutionErr->data.F64[ix] = 0.001;
     2075                    continue;
     2076                }
     2077                solutionErr->data.F64[ix] = sqrt(Ainv->data.F64[kx][kx]);
     2078                solution->data.F64[ix] = Xsvd->data.F64[kx];
     2079                kx++;
     2080            }
     2081
     2082            psFree (kernelMatrix);
     2083            psFree (kernelVector);
     2084
     2085            psFree (U);
     2086            psFree (V);
     2087            psFree (w);
     2088
     2089            psFree (Ainv);
     2090            psFree (Xsvd);
     2091        } else {
     2092            psVector *permutation = NULL;       // Permutation vector, required for LU decomposition
     2093            psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix);
     2094            if (!luMatrix) {
     2095                psError(PM_ERR_DATA, true, "LU Decomposition of least-squares matrix failed.\n");
     2096                psFree(solution);
     2097                psFree(sumVector);
     2098                psFree(sumMatrix);
     2099                psFree(luMatrix);
     2100                psFree(permutation);
     2101                return NULL;
     2102            }
     2103
     2104            solution = psMatrixLUSolution(NULL, luMatrix, sumVector, permutation);
     2105            psFree(luMatrix);
     2106            psFree(permutation);
     2107            if (!solution) {
     2108                psError(PM_ERR_DATA, true, "Failed to solve the least-squares system.\n");
     2109                psFree(solution);
     2110                psFree(sumVector);
     2111                psFree(sumMatrix);
     2112                return NULL;
     2113            }
     2114
     2115            // XXX LUD does not provide A^{-1}?  fake the error for now
     2116            solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     2117            for (int ix = 0; ix < sumVector->n; ix++) {
     2118                solutionErr->data.F64[ix] = 0.1*solution->data.F64[ix];
     2119            }
     2120        }
     2121
     2122        if (!kernels->solution1) {
     2123            kernels->solution1 = psVectorAlloc (sumVector->n, PS_TYPE_F64);
     2124            psVectorInit (kernels->solution1, 0.0);
     2125        }
     2126
     2127        // only update the solutions that we chose to calculate:
     2128        if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     2129            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     2130            kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
     2131        }
     2132        if (mode & PM_SUBTRACTION_EQUATION_BG) {
     2133            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     2134            kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
     2135        }
     2136        if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     2137            int numKernels = kernels->num;
     2138            int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     2139            int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     2140            for (int i = 0; i < numKernels * numPoly; i++) {
     2141                // XXX fprintf (stderr, "%f +/- %f (%f) -> ", solution->data.F64[i], solutionErr->data.F64[i], fabs(solution->data.F64[i]/solutionErr->data.F64[i]));
     2142                if (fabs(solution->data.F64[i] / solutionErr->data.F64[i]) < COEFF_SIG) {
     2143                    // XXX fprintf (stderr, "drop\n");
     2144                    kernels->solution1->data.F64[i] = 0.0;
     2145                } else {
     2146                    // XXX fprintf (stderr, "keep\n");
     2147                    kernels->solution1->data.F64[i] = solution->data.F64[i];
     2148                }
     2149            }
     2150        }
     2151        // double chiSquare = p_pmSubSolve_ChiSquare (kernels, stamps);
     2152        // fprintf (stderr, "chi square Brute = %f\n", chiSquare);
     2153
     2154        psFree(solution);
     2155        psFree(sumVector);
     2156        psFree(sumMatrix);
     2157# endif
     2158
     2159#ifdef TESTING
     2160              // XXX double-check for NAN in data:
     2161                for (int iy = 0; iy < stamp->matrix->numRows; iy++) {
     2162                    for (int ix = 0; ix < stamp->matrix->numCols; ix++) {
     2163                        if (!isfinite(stamp->matrix->data.F64[iy][ix])) {
     2164                            fprintf (stderr, "WARNING: NAN in matrix\n");
     2165                        }
     2166                    }
     2167                }
     2168                for (int ix = 0; ix < stamp->vector->n; ix++) {
     2169                    if (!isfinite(stamp->vector->data.F64[ix])) {
     2170                        fprintf (stderr, "WARNING: NAN in vector\n");
     2171                    }
     2172                }
     2173#endif
     2174
     2175#ifdef TESTING
     2176        for (int ix = 0; ix < sumVector->n; ix++) {
     2177            if (!isfinite(sumVector->data.F64[ix])) {
     2178                fprintf (stderr, "WARNING: NAN in vector\n");
     2179            }
     2180        }
     2181#endif
     2182
     2183#ifdef TESTING
     2184        for (int ix = 0; ix < sumVector->n; ix++) {
     2185            if (!isfinite(sumVector->data.F64[ix])) {
     2186                fprintf (stderr, "WARNING: NAN in vector\n");
     2187            }
     2188        }
     2189        {
     2190            psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL);
     2191            psFitsWriteImageSimple("matrixInv.fits", inverse, NULL);
     2192            psFree(inverse);
     2193        }
     2194        {
     2195            psImage *X = psMatrixInvert(NULL, sumMatrix, NULL);
     2196            psImage *Xt = psMatrixTranspose(NULL, X);
     2197            psImage *XtX = psMatrixMultiply(NULL, Xt, X);
     2198            psFitsWriteImageSimple("matrixErr.fits", XtX, NULL);
     2199            psFree(X);
     2200            psFree(Xt);
     2201            psFree(XtX);
     2202        }
     2203#endif
     2204
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionEquation.h

    r19299 r27840  
    44#include "pmSubtractionStamps.h"
    55#include "pmSubtractionKernels.h"
     6
     7typedef enum {
     8    PM_SUBTRACTION_EQUATION_NONE    = 0x00,
     9    PM_SUBTRACTION_EQUATION_NORM    = 0x01,
     10    PM_SUBTRACTION_EQUATION_BG      = 0x02,
     11    PM_SUBTRACTION_EQUATION_KERNELS = 0x04,
     12    PM_SUBTRACTION_EQUATION_ALL     = 0x07, // value should be NORM | BG | KERNELS
     13} pmSubtractionEquationCalculationMode;
    614
    715/// Execute a thread job to calculate the least-squares equation for a stamp
     
    1220bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, ///< Stamps
    1321                                         const pmSubtractionKernels *kernels, ///< Kernel parameters
    14                                          int index ///< Index of stamp
    15                                     );
     22                                         int index, ///< Index of stamp
     23                                         const pmSubtractionEquationCalculationMode mode
     24    );
    1625
    1726/// Calculate the least-squares equation to match the image quality
    1827bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, ///< Stamps
    19                                     const pmSubtractionKernels *kernels ///< Kernel parameters
    20                                     );
     28                                    const pmSubtractionKernels *kernels, ///< Kernel parameters
     29                                    const pmSubtractionEquationCalculationMode mode
     30    );
    2131
    2232/// Solve the least-squares equation to match the image quality
    2333bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels, ///< Kernel parameters
    24                                 const pmSubtractionStampList *stamps ///< Stamps
     34                                const pmSubtractionStampList *stamps, ///< Stamps
     35                                const pmSubtractionEquationCalculationMode mode
    2536    );
    2637
    2738/// Calculate deviations
    2839psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps, ///< Stamps
    29                                            const pmSubtractionKernels *kernels ///< Kernel parameters
     40                                           pmSubtractionKernels *kernels ///< Kernel parameters
    3041    );
    3142
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionIO.c

    r23378 r27840  
    33#include <string.h>
    44
     5#include "pmErrorCodes.h"
    56#include "pmHDU.h"
    67#include "pmHDUUtils.h"
     
    6667    }
    6768    if (regions->n == 0) {
    68         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "No subtraction regions found.");
     69        // We wrote everything we could find
    6970        psFree(regions);
    70         return false;
     71        return true;
    7172    }
    7273
     
    8081        while ((item = psMetadataGetAndIncrement(iter))) {
    8182            assert(item->type == PS_DATA_UNKNOWN);
    82             // Set the normalisation dimensions, since these will be otherwise unavailable when reading the
    83             // images by scans.
    8483            pmSubtractionKernels *kernel = item->data.V; // Kernel used in subtraction
    85             kernel->numCols = ro->image->numCols;
    86             kernel->numRows = ro->image->numRows;
    87 
    8884            kernels = psArrayAdd(kernels, ARRAY_BUFFER, kernel);
    8985        }
     
    9288
    9389    if (regions->n != kernels->n) {
    94         psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Number of regions (%ld) and kernels (%ld) don't match.\n",
     90        psError(PM_ERR_PROG, true, "Number of regions (%ld) and kernels (%ld) don't match.\n",
    9591                regions->n, kernels->n);
    9692        psFree(regions);
     
    10399    psArray *rows = psArrayAlloc(num); // Array of FITS table rows
    104100    for (int i = 0; i < num; i++) {
    105         psMetadata *row = psMetadataAlloc(); // Row of interest
    106         rows->data[i] = psMemIncrRefCounter(row);
     101        psMetadata *row = rows->data[i] = psMetadataAlloc(); // Row of interest
    107102
    108103        psRegion *region = regions->data[i]; // Region of interest
     
    126121                         kernel->bgOrder);
    127122        psMetadataAddS32(row, PS_LIST_TAIL, NAME_MODE,  0, "Matching mode (enum)", kernel->mode);
    128         psMetadataAddS32(row, PS_LIST_TAIL, NAME_COLS,  0, "Number of columns", kernel->numCols);
    129         psMetadataAddS32(row, PS_LIST_TAIL, NAME_ROWS,  0, "Number of rows", kernel->numRows);
    130123        if (kernel->mode == PM_SUBTRACTION_MODE_1 || kernel->mode == PM_SUBTRACTION_MODE_2) {
    131124            psMetadataAddVector(row, PS_LIST_TAIL, NAME_SOL1, 0, "Solution vector 1", kernel->solution1);
     
    172165
    173166    if (!psFitsWriteTable(fits, header, rows, EXTNAME_KERNEL)) {
    174         psError(PS_ERR_IO, false, "Unable to write subtraction kernel to FITS table.");
     167        psError(psErrorCodeLast(), false, "Unable to write subtraction kernel to FITS table.");
    175168        psFree(header);
    176169        psFree(rows);
     
    182175
    183176    if (image && !psFitsWriteImage(fits, header, image, 0, EXTNAME_IMAGE)) {
    184         psError(PS_ERR_IO, false, "Unable to write subtraction kernel image.");
     177        psError(psErrorCodeLast(), false, "Unable to write subtraction kernel image.");
    185178        psFree(header);
    186179        psFree(rows);
     
    208201        thisView->readout = i;
    209202        if (!pmReadoutWriteSubtractionKernels(readout, file->fits)) {
    210             psError(PS_ERR_IO, false, "Failed to write %dth readout", i);
     203            psError(psErrorCodeLast(), false, "Failed to write %dth readout", i);
    211204            psFree(thisView);
    212205            return false;
     
    231224        thisView->cell = i;
    232225        if (!pmCellWriteSubtractionKernels(cell, thisView, file, config)) {
    233             psError(PS_ERR_IO, false, "Failed to write %dth cell", i);
     226            psError(psErrorCodeLast(), false, "Failed to write %dth cell", i);
    234227            psFree(thisView);
    235228            return false;
     
    254247        thisView->chip = i;
    255248        if (!pmChipWriteSubtractionKernels(chip, thisView, file, config)) {
    256             psError(PS_ERR_IO, false, "Failed to write %dth chip", i);
     249            psError(psErrorCodeLast(), false, "Failed to write %dth chip", i);
    257250            psFree(thisView);
    258251            return false;
     
    272265
    273266    if (!psFitsMoveExtName(fits, EXTNAME_KERNEL)) {
    274         psError(PS_ERR_IO, false, "Unable to move to subtraction kernel table.");
     267        psError(psErrorCodeLast(), false, "Unable to move to subtraction kernel table.");
    275268        return false;
    276269    }
     
    278271    psArray *table = psFitsReadTable(fits); // Table of interest
    279272    if (!table) {
    280         psError(PS_ERR_IO, false, "Unable to read FITS table");
     273        psError(psErrorCodeLast(), false, "Unable to read FITS table");
    281274        return false;
    282275    }
     
    289282        TARGET = psMetadataLookup##SUFFIX(&mdok, row, NAME); \
    290283        if (!mdok) { \
    291             psError(PS_ERR_UNKNOWN, false, "Unable to find column %s in subtraction kernel table.", NAME); \
     284            psError(PM_ERR_PROG, false, "Unable to find column %s in subtraction kernel table.", NAME); \
    292285            psFree(table); \
    293286            return false; \
     
    318311
    319312        TABLE_LOOKUP(int, S32, bg,      NAME_BG);
    320         TABLE_LOOKUP(int, S32, numCols, NAME_COLS);
    321         TABLE_LOOKUP(int, S32, numRows, NAME_ROWS);
    322313
    323314        TABLE_LOOKUP(float, F32, mean,      NAME_MEAN);
     
    325316        TABLE_LOOKUP(int,   S32, numStamps, NAME_NUMSTAMPS);
    326317
    327         pmSubtractionKernels *kernels = pmSubtractionKernelsFromDescription(description, bg, mode);
    328         kernels->numCols = numCols;
    329         kernels->numRows = numRows;
     318        pmSubtractionKernels *kernels = pmSubtractionKernelsFromDescription(description, bg, *region, mode);
    330319        kernels->mean = mean;
    331320        kernels->rms = rms;
     
    336325            kernels->solution1 = psMemIncrRefCounter(psMetadataLookupPtr(&mdok, row, NAME_SOL1));
    337326            if (!mdok) {
    338                 psError(PS_ERR_UNKNOWN, false, "Unable to find column %s in subtraction kernel table.",
     327                psError(PM_ERR_PROG, false, "Unable to find column %s in subtraction kernel table.",
    339328                        NAME_SOL1);
    340329                psFree(kernels);
     
    346335            kernels->solution1 = psMemIncrRefCounter(psMetadataLookupPtr(&mdok, row, NAME_SOL1));
    347336            if (!mdok) {
    348                 psError(PS_ERR_UNKNOWN, false, "Unable to find column %s in subtraction kernel table.",
     337                psError(PM_ERR_PROG, false, "Unable to find column %s in subtraction kernel table.",
    349338                        NAME_SOL1);
    350339                psFree(kernels);
     
    354343            kernels->solution2 = psMemIncrRefCounter(psMetadataLookupPtr(&mdok, row, NAME_SOL2));
    355344            if (!mdok) {
    356                 psError(PS_ERR_UNKNOWN, false, "Unable to find column %s in subtraction kernel table.",
     345                psError(PM_ERR_PROG, false, "Unable to find column %s in subtraction kernel table.",
    357346                        NAME_SOL2);
    358347                psFree(kernels);
     
    390379        pmReadout *readout = cell->readouts->data[i];
    391380        thisView->readout = i;
    392         pmReadoutReadSubtractionKernels(readout, file->fits);
     381        if (!pmReadoutReadSubtractionKernels(readout, file->fits)) {
     382            psError(psErrorCodeLast(), false, "Unable to read subtraction kernels from cell");
     383            psFree(thisView);
     384            return false;
     385        }
    393386        if (!readout->data_exists) {
    394387            continue;
     
    421414        pmCell *cell = chip->cells->data[i];
    422415        thisView->cell = i;
    423         pmCellReadSubtractionKernels(cell, thisView, file, config);
    424         if (!cell->data_exists) {
     416        if (!pmCellReadSubtractionKernels(cell, thisView, file, config)) {
     417            psError(psErrorCodeLast(), false, "Unable to read subtraction kernels from cell");
     418            psFree(thisView);
     419            return false;
     420        }
     421         if (!cell->data_exists) {
    425422            continue;
    426423        }
     
    430427
    431428    if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, true, true, NULL)) {
    432         psError(PS_ERR_IO, false, "Failed to read concepts for chip.\n");
     429        psError(psErrorCodeLast(), false, "Failed to read concepts for chip.\n");
    433430        return false;
    434431    }
     
    451448        pmChip *chip = fpa->chips->data[i];
    452449        thisView->chip = i;
    453         pmChipReadSubtractionKernels(chip, thisView, file, config);
     450        if (!pmChipReadSubtractionKernels(chip, thisView, file, config)) {
     451            psError(psErrorCodeLast(), false, "Unable to read subtraction kernels from chip");
     452            psFree(thisView);
     453            return false;
     454        }
    454455    }
    455456    psFree(thisView);
    456457
    457458    if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
    458         psError(PS_ERR_IO, false, "Failed to read concepts for fpa.\n");
     459        psError(psErrorCodeLast(), false, "Failed to read concepts for fpa.\n");
    459460        return false;
    460461    }
     
    475476    if (view->chip == -1) {
    476477        if (!pmFPAWriteSubtractionKernels(fpa, view, file, config)) {
    477             psError(PS_ERR_IO, false, "Failed to write subtraction kernels from fpa");
     478            psError(psErrorCodeLast(), false, "Failed to write subtraction kernels from fpa");
    478479            psFree(fpa);
    479480            return false;
     
    484485
    485486    if (view->chip >= fpa->chips->n) {
    486         psError(PS_ERR_UNKNOWN, false, "Writing chip == %d (>= chips->n == %ld)", view->chip, fpa->chips->n);
     487        psError(PM_ERR_PROG, true, "Writing chip == %d (>= chips->n == %ld)", view->chip, fpa->chips->n);
    487488        psFree(fpa);
    488489        return false;
     
    492493    if (view->cell == -1) {
    493494        if (!pmChipWriteSubtractionKernels(chip, view, file, config)) {
    494             psError(PS_ERR_IO, false, "Failed to write objects from chip");
     495            psError(psErrorCodeLast(), false, "Failed to write objects from chip");
    495496            psFree(fpa);
    496497            return false;
     
    501502
    502503    if (view->cell >= chip->cells->n) {
    503         psError(PS_ERR_UNKNOWN, false, "Writing cell == %d (>= cells->n == %ld)",
     504        psError(PM_ERR_PROG, true, "Writing cell == %d (>= cells->n == %ld)",
    504505                view->cell, chip->cells->n);
    505506        psFree(fpa);
     
    510511    if (view->readout == -1) {
    511512        if (!pmCellWriteSubtractionKernels(cell, view, file, config)) {
    512             psError(PS_ERR_IO, false, "Failed to write objects from cell");
     513            psError(psErrorCodeLast(), false, "Failed to write objects from cell");
    513514            psFree(fpa);
    514515            return false;
     
    519520
    520521    if (view->readout >= cell->readouts->n) {
    521         psError(PS_ERR_UNKNOWN, false, "Writing readout == %d (>= readouts->n == %ld)",
     522        psError(PM_ERR_PROG, true, "Writing readout == %d (>= readouts->n == %ld)",
    522523                view->readout, cell->readouts->n);
    523524        psFree(fpa);
     
    527528
    528529    if (!pmReadoutWriteSubtractionKernels(readout, file->fits)) {
    529         psError(PS_ERR_IO, false, "Failed to write objects from readout %d", view->readout);
     530        psError(psErrorCodeLast(), false, "Failed to write objects from readout %d", view->readout);
    530531        psFree(fpa);
    531532        return false;
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionKernels.c

    r24296 r27840  
    1010#include "pmSubtraction.h"
    1111#include "pmSubtractionKernels.h"
     12#include "pmSubtractionHermitian.h"
     13#include "pmSubtractionDeconvolve.h"
     14#include "pmSubtractionVisual.h"
    1215
    1316#define RINGS_BUFFER 10                 // Buffer size for RINGS data
    14 
    1517
    1618// Free function for pmSubtractionKernels
     
    2729    psFree(kernels->solution1);
    2830    psFree(kernels->solution2);
     31    psFree(kernels->sampleStamps);
     32}
     33
     34// Free function for pmSubtractionPreCalcKernel
     35static void pmSubtractionKernelPreCalcFree(pmSubtractionKernelPreCalc *kernel)
     36{
     37    psFree(kernel->xKernel);
     38    psFree(kernel->yKernel);
     39    psFree(kernel->kernel);
     40
     41    psFree(kernel->uCoords);
     42    psFree(kernel->vCoords);
     43    psFree(kernel->poly);
    2944}
    3045
     
    4560
    4661// Generate 1D convolution kernel for ISIS
    47 static psVector *subtractionKernelISIS(float sigma, // Gaussian width
     62psVector *pmSubtractionKernelISIS(float sigma, // Gaussian width
    4863                                       int order, // Polynomial order
    4964                                       int size // Kernel half-size
     
    5772    for (int i = 0, x = -size; x <= size; i++, x++) {
    5873        kernel->data.F32[i] = norm * power(x, order) * expf(expNorm * PS_SQR(x));
     74    }
     75
     76    return kernel;
     77}
     78
     79// Generate 1D convolution kernel for HERM (normalized for 2D)
     80psVector *pmSubtractionKernelHERM(float sigma, // Gaussian width
     81                                       int order, // Polynomial order
     82                                       int size // Kernel half-size
     83    )
     84{
     85    int fullSize = 2 * size + 1;        // Full size of kernel
     86    psVector *kernel = psVectorAlloc(fullSize, PS_TYPE_F32); // Kernel to return
     87
     88    // for now, we are only allowing equal orders and sigmas in X and Y
     89    float nf = exp(lgamma(order + 1));
     90    float norm = 1.0 / sqrt(nf*sigma*sqrt(M_2_PI));
     91
     92    for (int i = 0, x = -size; x <= size; i++, x++) {
     93        float xf = x / sigma;
     94        float z = -0.25*xf*xf;
     95        kernel->data.F32[i] = norm * p_pmSubtractionHermitianPolynomial(xf, order) * exp(z);
     96    }
     97
     98    return kernel;
     99}
     100
     101// Generate 1D convolution kernel for HERM (normalized for 2D)
     102psKernel *pmSubtractionKernelHERM_RADIAL(float sigma, // Gaussian width
     103                                         int order, // Polynomial order
     104                                         int size // Kernel half-size
     105    )
     106{
     107    psKernel *kernel = psKernelAlloc(-size, size, -size, size); // 2D Kernel
     108
     109    // for now, we are only allowing equal orders and sigmas in X and Y
     110    float nf = exp(lgamma(order + 1));
     111    float norm = 1.0 / sqrt(nf*sigma*sqrt(M_2_PI));
     112
     113    // generate 2D radial hermitian
     114    for (int v = -size; v <= size; v++) {
     115        for (int u = -size; u <= size; u++) {
     116            float r = hypot(u, v) / sigma;
     117            float z = -0.25*r*r;
     118            kernel->kernel[v][u] = norm * p_pmSubtractionHermitianPolynomial(r, order) * exp(z);
     119        }
    59120    }
    60121
     
    81142    kernels->penalties = psVectorRealloc(kernels->penalties, start + numNew);
    82143    kernels->inner = start;
     144    kernels->num += numNew;
    83145
    84146    // Generate a set of kernels for each (u,v)
     
    94156            kernels->v->data.S32[index] = v;
    95157            kernels->preCalc->data[index] = NULL;
    96             kernels->penalties->data.F32[index] = kernels->penalty * (PS_SQR(u) + PS_SQR(v));
    97 
     158            kernels->penalties->data.F32[index] = kernels->penalty * PS_SQR(PS_SQR(u) + PS_SQR(v));
     159            psAssert (isfinite(kernels->penalties->data.F32[index]), "invalid penalty");
    98160            psTrace("psModules.imcombine", 7, "Kernel %d: %d %d\n", index, u, v);
    99161        }
    100162    }
    101163
     164    kernels->widths->n = start + numNew;
     165    kernels->u->n = start + numNew;
     166    kernels->v->n = start + numNew;
     167    kernels->preCalc->n = start + numNew;
     168    kernels->penalties->n = start + numNew;
     169
    102170    return true;
    103171}
    104172
     173bool pmSubtractionKernelPreCalcNormalize(pmSubtractionKernels *kernels, pmSubtractionKernelPreCalc *preCalc,
     174                                         int index, int size, int uOrder, int vOrder, float fwhm,
     175                                         bool AlardLuptonStyle, bool forceZeroNull)
     176{
     177    // we have 4 cases here:
     178    // 1) for odd functions, normalize the kernel by the maximum swing / Npix
     179    // 2) for even functions, normalize the kernel to unity
     180    // 3) for alard-lupton style normalization, subtract 1 from the 0,0 pixel for all even functions
     181    // 4) for deconvolved hermitians, subtract 1 from the 0,0 pixel for the 0,0 function(s)
     182
     183    // Calculate moments
     184    double penalty = 0.0;                   // Moment, for penalty
     185    double sum = 0.0, sum2 = 0.0;           // Sum of kernel component
     186    float min = INFINITY, max = -INFINITY;  // Minimum and maximum kernel value
     187    for (int v = -size; v <= size; v++) {
     188        for (int u = -size; u <= size; u++) {
     189            double value = preCalc->kernel->kernel[v][u];
     190            double value2 = PS_SQR(value);
     191            sum += value;
     192            sum2 += value2;
     193            penalty += value2 * PS_SQR((PS_SQR(u) + PS_SQR(v)));
     194            min = PS_MIN(value, min);
     195            max = PS_MAX(value, max);
     196        }
     197    }
     198
     199#if 0
     200    fprintf(stderr, "%d raw: %lf, null: %f, min: %lf, max: %lf, moment: %lf\n", index, sum, preCalc->kernel->kernel[0][0], min, max, penalty);
     201#endif
     202
     203    bool zeroNull = false;              // Zero out using the null position?
     204    float scale2D = NAN;                // Scaling for 2-D kernels
     205
     206    if (AlardLuptonStyle) {
     207        if (uOrder % 2 == 0 && vOrder % 2 == 0) {
     208            // Even functions: normalise to unit sum and subtract null pixel so that sum is zero
     209            scale2D = 1.0 / fabs(sum);
     210            zeroNull = true;
     211        } else {
     212            // Odd functions: choose normalisation so that parameters have about the same strength as for even
     213            // functions, no subtraction of null pixel because the sum is already (near) zero
     214            scale2D = 1.0 / sqrt(sum2);
     215            zeroNull = false;
     216        }
     217    }
     218
     219    if (!AlardLuptonStyle && (uOrder == 0 && vOrder == 0)) {
     220        zeroNull = true;
     221    }
     222    if (forceZeroNull) {
     223        // Force rescaling and subtraction of null pixel even though the order doesn't indicate it's even
     224        scale2D = 1.0 / fabs(sum);
     225        zeroNull = true;
     226    }
     227    if (!forceZeroNull && ((uOrder % 2) || (vOrder % 2))) {
     228        // Odd function
     229        scale2D = 1.0 / sqrt(sum2);
     230    }
     231
     232    float scale1D = sqrtf(scale2D);     // Scaling for 1-D kernels
     233    if (preCalc->xKernel) {
     234        psBinaryOp(preCalc->xKernel, preCalc->xKernel, "*", psScalarAlloc(scale1D, PS_TYPE_F32));
     235    }
     236    if (preCalc->yKernel) {
     237        psBinaryOp(preCalc->yKernel, preCalc->yKernel, "*", psScalarAlloc(scale1D, PS_TYPE_F32));
     238    }
     239
     240    psBinaryOp(preCalc->kernel->image, preCalc->kernel->image, "*", psScalarAlloc(scale2D, PS_TYPE_F32));
     241    penalty *= 1.0 / sum2;
     242
     243    if (zeroNull) {
     244        preCalc->kernel->kernel[0][0] -= 1.0;
     245    }
     246
     247#if 0
     248    {
     249        double sum = 0.0;   // Sum of kernel component
     250        float min = INFINITY, max = -INFINITY;  // Minimum and maximum kernel value
     251        for (int v = -size; v <= size; v++) {
     252            for (int u = -size; u <= size; u++) {
     253                sum += preCalc->kernel->kernel[v][u];
     254                min = PS_MIN(preCalc->kernel->kernel[v][u], min);
     255                max = PS_MAX(preCalc->kernel->kernel[v][u], max);
     256            }
     257        }
     258        fprintf(stderr, "%d mod: %lf, null: %f, min: %lf, max: %lf, scale: %f\n", index, sum, preCalc->kernel->kernel[0][0], min, max, scale2D);
     259    }
     260#endif
     261
     262    kernels->widths->data.F32[index] = fwhm;
     263    kernels->u->data.S32[index] = uOrder;
     264    kernels->v->data.S32[index] = vOrder;
     265    if (kernels->preCalc->data[index]) {
     266        psFree(kernels->preCalc->data[index]);
     267    }
     268    kernels->preCalc->data[index] = preCalc;
     269    kernels->penalties->data.F32[index] = kernels->penalty * penalty;
     270    psAssert (isfinite(kernels->penalties->data.F32[index]), "invalid penalty");
     271    psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %f\n", index, fwhm, uOrder, vOrder, penalty);
     272
     273    return true;
     274}
     275
    105276pmSubtractionKernels *p_pmSubtractionKernelsRawISIS(int size, int spatialOrder,
    106                                                     const psVector *fwhms, const psVector *orders,
    107                                                     float penalty, pmSubtractionMode mode)
    108 {
    109     PS_ASSERT_VECTOR_NON_NULL(fwhms, NULL);
    110     PS_ASSERT_VECTOR_TYPE(fwhms, PS_TYPE_F32, NULL);
    111     PS_ASSERT_VECTOR_NON_NULL(orders, NULL);
    112     PS_ASSERT_VECTOR_TYPE(orders, PS_TYPE_S32, NULL);
    113     PS_ASSERT_VECTORS_SIZE_EQUAL(fwhms, orders, NULL);
     277                                                    const psVector *fwhmsIN, const psVector *ordersIN,
     278                                                    float penalty, psRegion bounds, pmSubtractionMode mode)
     279{
     280    PS_ASSERT_VECTOR_NON_NULL(fwhmsIN, NULL);
     281    PS_ASSERT_VECTOR_TYPE(fwhmsIN, PS_TYPE_F32, NULL);
     282    PS_ASSERT_VECTOR_NON_NULL(ordersIN, NULL);
     283    PS_ASSERT_VECTOR_TYPE(ordersIN, PS_TYPE_S32, NULL);
     284    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhmsIN, ordersIN, NULL);
    114285    PS_ASSERT_INT_POSITIVE(size, NULL);
    115286    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
     287
     288    // check the requested fwhm values: any values <= 0.0 should be dropped
     289    psVector *fwhms  = psVectorAllocEmpty (fwhmsIN->n, PS_TYPE_F32);
     290    psVector *orders = psVectorAllocEmpty (ordersIN->n, PS_TYPE_S32);
     291    for (int i = 0; i < fwhmsIN->n; i++) {
     292        if (fwhmsIN->data.F32[i] <= FLT_EPSILON) continue;
     293        psVectorAppend(fwhms, fwhmsIN->data.F32[i]);
     294        psVectorAppend(orders, ordersIN->data.S32[i]);
     295    }
    116296
    117297    int numGaussians = fwhms->n;       // Number of Gaussians
     
    126306
    127307    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS, size,
    128                                                               spatialOrder, penalty, mode); // The kernels
     308                                                              spatialOrder, penalty, bounds, mode); // Kernels
    129309    psStringAppend(&kernels->description, "ISIS(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
    130310
     
    134314
    135315    // Set the kernel parameters
    136     int fullSize = 2 * size + 1;        // Full size of kernels
    137316    for (int i = 0, index = 0; i < numGaussians; i++) {
    138317        float sigma = fwhms->data.F32[i] / (2.0 * sqrtf(2.0 * logf(2.0))); // Gaussian sigma
     
    140319        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
    141320            for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
    142                 psArray *preCalc = psArrayAlloc(2); // Array to hold precalculated values
    143                 psVector *xKernel = preCalc->data[0] = subtractionKernelISIS(sigma, uOrder, size); // x Kernel
    144                 psVector *yKernel = preCalc->data[1] = subtractionKernelISIS(sigma, vOrder, size); // y Kernel
    145 
    146                 // Calculate moments
    147                 double moment = 0.0;    // Moment, for penalty
    148                 for (int v = -size, y = 0; v <= size; v++, y++) {
    149                     for (int u = -size, x = 0; u <= size; u++, x++) {
    150                         double value = xKernel->data.F32[x] * yKernel->data.F32[y]; // Value of kernel
    151                         moment += value * (PS_SQR(u) + PS_SQR(v));
    152                     }
     321
     322                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_ISIS, uOrder, vOrder, size, sigma); // structure to hold precalculated values
     323                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
     324                // pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], false, false);
     325            }
     326        }
     327    }
     328
     329    psFree(orders);
     330    psFree(fwhms);
     331
     332    return kernels;
     333}
     334
     335pmSubtractionKernels *pmSubtractionKernelsISIS_RADIAL(int size, int spatialOrder,
     336                                                      const psVector *fwhmsIN, const psVector *ordersIN,
     337                                                      float penalty, psRegion bounds, pmSubtractionMode mode)
     338{
     339    PS_ASSERT_VECTOR_NON_NULL(fwhmsIN, NULL);
     340    PS_ASSERT_VECTOR_TYPE(fwhmsIN, PS_TYPE_F32, NULL);
     341    PS_ASSERT_VECTOR_NON_NULL(ordersIN, NULL);
     342    PS_ASSERT_VECTOR_TYPE(ordersIN, PS_TYPE_S32, NULL);
     343    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhmsIN, ordersIN, NULL);
     344    PS_ASSERT_INT_POSITIVE(size, NULL);
     345    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
     346
     347    // check the requested fwhm values: any values <= 0.0 should be dropped
     348    psVector *fwhms  = psVectorAllocEmpty (fwhmsIN->n, PS_TYPE_F32);
     349    psVector *orders = psVectorAllocEmpty (ordersIN->n, PS_TYPE_S32);
     350    for (int i = 0; i < fwhmsIN->n; i++) {
     351        if (fwhmsIN->data.F32[i] <= FLT_EPSILON) continue;
     352        psVectorAppend(fwhms, fwhmsIN->data.F32[i]);
     353        psVectorAppend(orders, ordersIN->data.S32[i]);
     354    }
     355
     356    int numGaussians = fwhms->n;       // Number of Gaussians
     357
     358    int num = 0;                        // Number of basis functions
     359    psString params = NULL;             // List of parameters
     360    for (int i = 0; i < numGaussians; i++) {
     361        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
     362        psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
     363        num += (gaussOrder + 1) * (gaussOrder + 2) / 2;
     364        num += (11 - gaussOrder - 1);   // include all higher order radial terms
     365    }
     366
     367    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS_RADIAL, size,
     368                                                              spatialOrder, penalty, bounds, mode); // Kernels
     369    psStringAppend(&kernels->description, "ISIS_RADIAL(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
     370
     371    psLogMsg("psModules.imcombine", PS_LOG_INFO, "ISIS_RADIAL kernel: %s,%d --> %d elements", params, spatialOrder, num);
     372    psFree(params);
     373
     374    // Set the kernel parameters
     375    for (int i = 0, index = 0; i < numGaussians; i++) {
     376        float sigma = fwhms->data.F32[i] / (2.0 * sqrtf(2.0 * logf(2.0))); // Gaussian sigma
     377        // Iterate over (u,v) order
     378        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
     379            for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
     380                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_ISIS, uOrder, vOrder, size, sigma); // structure to hold precalculated values
     381                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
     382            }
     383        }
     384        for (int order = orders->data.S32[i] + 1; order < 11; order ++, index ++) {
     385            // XXX modify size for hermitians to account for sqrt(2) in Hermitian definition (relative to ISIS Gaussian)
     386            pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_ISIS_RADIAL, order, order, size, sigma / sqrt(2.0)); // structure to hold precalculated values
     387            pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, order, order, fwhms->data.F32[i], true, true);
     388        }
     389    }
     390    return kernels;
     391}
     392
     393pmSubtractionKernels *pmSubtractionKernelsHERM(int size, int spatialOrder,
     394                                               const psVector *fwhmsIN, const psVector *ordersIN,
     395                                               float penalty, psRegion bounds, pmSubtractionMode mode)
     396{
     397    PS_ASSERT_VECTOR_NON_NULL(fwhmsIN, NULL);
     398    PS_ASSERT_VECTOR_TYPE(fwhmsIN, PS_TYPE_F32, NULL);
     399    PS_ASSERT_VECTOR_NON_NULL(ordersIN, NULL);
     400    PS_ASSERT_VECTOR_TYPE(ordersIN, PS_TYPE_S32, NULL);
     401    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhmsIN, ordersIN, NULL);
     402    PS_ASSERT_INT_POSITIVE(size, NULL);
     403    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
     404
     405    // check the requested fwhm values: any values <= 0.0 should be dropped
     406    psVector *fwhms  = psVectorAllocEmpty (fwhmsIN->n, PS_TYPE_F32);
     407    psVector *orders = psVectorAllocEmpty (ordersIN->n, PS_TYPE_S32);
     408    for (int i = 0; i < fwhmsIN->n; i++) {
     409        if (fwhmsIN->data.F32[i] <= FLT_EPSILON) continue;
     410        psVectorAppend(fwhms, fwhmsIN->data.F32[i]);
     411        psVectorAppend(orders, ordersIN->data.S32[i]);
     412    }
     413
     414    int numGaussians = fwhms->n;       // Number of Gaussians
     415
     416    int num = 0;                        // Number of basis functions
     417    psString params = NULL;             // List of parameters
     418    for (int i = 0; i < numGaussians; i++) {
     419        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
     420        psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
     421        num += (gaussOrder + 1) * (gaussOrder + 2) / 2;
     422    }
     423
     424    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_HERM, size,
     425                                                              spatialOrder, penalty, bounds, mode); // Kernels
     426    psStringAppend(&kernels->description, "HERM(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
     427
     428    psLogMsg("psModules.imcombine", PS_LOG_INFO, "HERM kernel: %s,%d --> %d elements",
     429             params, spatialOrder, num);
     430    psFree(params);
     431
     432    // Set the kernel parameters
     433    for (int i = 0, index = 0; i < numGaussians; i++) {
     434        float sigma = fwhms->data.F32[i] / (2.0 * sqrtf(2.0 * logf(2.0))); // Gaussian sigma
     435        // Iterate over (u,v) order
     436        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
     437            for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
     438                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_HERM, uOrder, vOrder, size, sigma); // structure to hold precalculated values
     439                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
     440            }
     441        }
     442    }
     443
     444    return kernels;
     445}
     446
     447pmSubtractionKernels *pmSubtractionKernelsDECONV_HERM(int size, int spatialOrder,
     448                                                      const psVector *fwhmsIN, const psVector *ordersIN,
     449                                                      float penalty, psRegion bounds, pmSubtractionMode mode)
     450{
     451    PS_ASSERT_VECTOR_NON_NULL(fwhmsIN, NULL);
     452    PS_ASSERT_VECTOR_TYPE(fwhmsIN, PS_TYPE_F32, NULL);
     453    PS_ASSERT_VECTOR_NON_NULL(ordersIN, NULL);
     454    PS_ASSERT_VECTOR_TYPE(ordersIN, PS_TYPE_S32, NULL);
     455    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhmsIN, ordersIN, NULL);
     456    PS_ASSERT_INT_POSITIVE(size, NULL);
     457    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
     458
     459    // check the requested fwhm values: any values <= 0.0 should be dropped
     460    psVector *fwhms  = psVectorAllocEmpty (fwhmsIN->n, PS_TYPE_F32);
     461    psVector *orders = psVectorAllocEmpty (ordersIN->n, PS_TYPE_S32);
     462    for (int i = 0; i < fwhmsIN->n; i++) {
     463        if (fwhmsIN->data.F32[i] <= FLT_EPSILON) continue;
     464        psVectorAppend(fwhms, fwhmsIN->data.F32[i]);
     465        psVectorAppend(orders, ordersIN->data.S32[i]);
     466    }
     467
     468    int numGaussians = fwhms->n;       // Number of Gaussians
     469
     470    int num = 0;                        // Number of basis functions
     471    psString params = NULL;             // List of parameters
     472    for (int i = 0; i < numGaussians; i++) {
     473        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
     474        psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
     475        num += PS_SQR(gaussOrder + 1);
     476    }
     477
     478    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_DECONV_HERM, size,
     479                                                              spatialOrder, penalty, bounds, mode); // Kernels
     480    psStringAppend(&kernels->description, "DECONV_HERM(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
     481
     482    psLogMsg("psModules.imcombine", PS_LOG_INFO, "DECONVOLVED HERM kernel: %s,%d --> %d elements", params, spatialOrder, num);
     483    psFree(params);
     484
     485    // XXXXX hard-wired reference sigma for now of 1.7 pix (== 4.0 pix fwhm == 1.0 arcsec in simtest)
     486    // generate the Gaussian deconvolution kernel
     487    # define DECONV_SIGMA 1.6
     488    psKernel *kernelGauss = pmSubtractionDeconvolveGauss (size, DECONV_SIGMA);
     489
     490# if 1
     491    psArray *deconKernels = psArrayAllocEmpty(100);
     492# endif
     493
     494    // Set the kernel parameters
     495    for (int i = 0, index = 0; i < numGaussians; i++) {
     496        float sigma = fwhms->data.F32[i] / (2.0 * sqrtf(2.0 * logf(2.0))); // Gaussian sigma
     497        // Iterate over (u,v) order
     498        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
     499            for (int vOrder = 0; vOrder <= orders->data.S32[i]; vOrder++, index++) {
     500
     501                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_HERM, uOrder, vOrder, size, sigma); // structure to hold precalculated values
     502
     503                // save the generated 2D kernel as the target, deconvolve it by Gaussian, replacing the generated 2D kernel
     504                psKernel *kernelTarget = preCalc->kernel;
     505                preCalc->kernel = pmSubtractionDeconvolveKernel(kernelTarget, kernelGauss); // Kernel
     506
     507                // XXX do we use Alard-Lupton normalization (last param true) or not?
     508                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
     509
     510                // XXXX test demo that deconvolved kernel is valid
     511# if 1
     512                psImage *kernelConv = psImageConvolveFFT(NULL, preCalc->kernel->image, NULL, 0, kernelGauss);
     513                psArrayAdd (deconKernels, 100, kernelConv);
     514                psFree (kernelConv);
     515
     516                if (!uOrder && !vOrder){
     517                    pmSubtractionVisualShowSubtraction (kernelTarget->image, preCalc->kernel->image, kernelConv);
    153518                }
    154 
    155                 // Normalise sum of kernel component to unity for even functions
    156                 if (uOrder % 2 == 0 && vOrder % 2 == 0) {
    157                     double sum = 0.0;   // Sum of kernel component
    158                     for (int v = 0; v < fullSize; v++) {
    159                         for (int u = 0; u < fullSize; u++) {
    160                             sum += xKernel->data.F32[u] * yKernel->data.F32[v];
    161                         }
    162                     }
    163                     sum = 1.0 / sqrt(sum);
    164                     psBinaryOp(xKernel, xKernel, "*", psScalarAlloc(sum, PS_TYPE_F32));
    165                     psBinaryOp(yKernel, yKernel, "*", psScalarAlloc(sum, PS_TYPE_F32));
    166                     moment *= PS_SQR(sum);
     519# endif
     520            }
     521        }
     522    }
     523
     524# if 1
     525    psImage *dot = psImageAlloc(deconKernels->n, deconKernels->n, PS_TYPE_F32);
     526    for (int i = 0; i < deconKernels->n; i++) {
     527        for (int j = 0; j <= i; j++) {
     528            psImage *t1 = deconKernels->data[i];
     529            psImage *t2 = deconKernels->data[j];
     530
     531            double sum = 0.0;
     532            for (int iy = 0; iy < t1->numRows; iy++) {
     533                for (int ix = 0; ix < t1->numCols; ix++) {
     534                    sum += t1->data.F32[iy][ix] * t2->data.F32[iy][ix];
    167535                }
    168 
    169                 kernels->widths->data.F32[index] = fwhms->data.F32[i];
    170                 kernels->u->data.S32[index] = uOrder;
    171                 kernels->v->data.S32[index] = vOrder;
    172                 if (kernels->preCalc->data[index]) {
    173                     psFree(kernels->preCalc->data[index]);
    174                 }
    175                 kernels->preCalc->data[index] = preCalc;
    176                 kernels->penalties->data.F32[index] = kernels->penalty * fabsf(moment);
    177 
    178                 psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %f\n", index,
    179                         fwhms->data.F32[i], uOrder, vOrder, fabsf(moment));
    180536            }
    181         }
    182     }
     537            dot->data.F32[j][i] = sum;
     538            dot->data.F32[i][j] = sum;
     539        }
     540    }
     541    pmSubtractionVisualShowSubtraction (dot, NULL, NULL);
     542    psFree (dot);
     543    psFree (deconKernels);
     544# endif
    183545
    184546    return kernels;
     
    190552
    191553pmSubtractionKernels *pmSubtractionKernelsAlloc(int numBasisFunctions, pmSubtractionKernelsType type,
    192                                                 int size, int spatialOrder, float penalty,
     554                                                int size, int spatialOrder, float penalty, psRegion bounds,
    193555                                                pmSubtractionMode mode)
    194556{
     
    202564    kernels->v = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
    203565    kernels->widths = psVectorAlloc(numBasisFunctions, PS_TYPE_F32);
     566    kernels->uStop = NULL;
     567    kernels->vStop = NULL;
     568    kernels->xMin = bounds.x0;
     569    kernels->xMax = bounds.x1;
     570    kernels->yMin = bounds.y0;
     571    kernels->yMax = bounds.y1;
    204572    kernels->preCalc = psArrayAlloc(numBasisFunctions);
    205573    kernels->penalty = penalty;
    206574    kernels->penalties = psVectorAlloc(numBasisFunctions, PS_TYPE_F32);
    207     kernels->uStop = NULL;
    208     kernels->vStop = NULL;
    209575    kernels->size = size;
    210576    kernels->inner = 0;
     
    212578    kernels->bgOrder = 0;
    213579    kernels->mode = mode;
    214     kernels->numCols = 0;
    215     kernels->numRows = 0;
    216580    kernels->solution1 = NULL;
    217581    kernels->solution2 = NULL;
     582    kernels->mean = NAN;
     583    kernels->rms = NAN;
     584    kernels->numStamps = 0;
     585    kernels->sampleStamps = NULL;
     586
     587    kernels->fSigResMean  = NAN;
     588    kernels->fSigResStdev = NAN;
     589    kernels->fMaxResMean  = NAN;
     590    kernels->fMaxResStdev = NAN;
     591    kernels->fMinResMean  = NAN;
     592    kernels->fMinResStdev = NAN;
    218593
    219594    return kernels;
    220595}
    221596
    222 pmSubtractionKernels *pmSubtractionKernelsPOIS(int size, int spatialOrder, float penalty,
     597pmSubtractionKernelPreCalc *pmSubtractionKernelPreCalcAlloc(pmSubtractionKernelsType type, int uOrder, int vOrder, int size, float sigma) {
     598
     599    pmSubtractionKernelPreCalc *preCalc = psAlloc(sizeof(pmSubtractionKernelPreCalc)); // Kernels, to return
     600    psMemSetDeallocator(preCalc, (psFreeFunc)pmSubtractionKernelPreCalcFree);
     601
     602    // 1D kernel realizations:
     603    switch (type) {
     604      case PM_SUBTRACTION_KERNEL_ISIS:
     605        preCalc->xKernel = pmSubtractionKernelISIS(sigma, uOrder, size);
     606        preCalc->yKernel = pmSubtractionKernelISIS(sigma, vOrder, size);
     607        preCalc->uCoords = NULL;
     608        preCalc->vCoords = NULL;
     609        preCalc->poly    = NULL;
     610        break;
     611      case PM_SUBTRACTION_KERNEL_HERM:
     612        preCalc->xKernel = pmSubtractionKernelHERM(sigma, uOrder, size);
     613        preCalc->yKernel = pmSubtractionKernelHERM(sigma, vOrder, size);
     614        preCalc->uCoords = NULL;
     615        preCalc->vCoords = NULL;
     616        preCalc->poly    = NULL;
     617        break;
     618      case PM_SUBTRACTION_KERNEL_RINGS:
     619        // the RINGS kernel uses the uCoords, vCoords, and poly elements of the structure
     620        // we allocate these vectors here, but leave the kernel generation to the main function
     621        preCalc->xKernel = NULL;
     622        preCalc->yKernel = NULL;
     623        preCalc->kernel  = NULL;
     624        preCalc->uCoords = psVectorAllocEmpty(size, PS_TYPE_S32); // u coords
     625        preCalc->vCoords = psVectorAllocEmpty(size, PS_TYPE_S32); // v coords
     626        preCalc->poly    = psVectorAllocEmpty(size, PS_TYPE_F32); // Polynomial
     627        return preCalc;
     628      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     629        preCalc->kernel  = pmSubtractionKernelHERM_RADIAL(sigma, uOrder, size);
     630        preCalc->xKernel = NULL;
     631        preCalc->yKernel = NULL;
     632        preCalc->uCoords = NULL;
     633        preCalc->vCoords = NULL;
     634        preCalc->poly    = NULL;
     635        return preCalc;
     636      default:
     637        psAbort("programming error: invalid type for PreCalc kernel");
     638    }
     639
     640    preCalc->kernel = psKernelAlloc(-size, size, -size, size); // 2D Kernel
     641
     642    // generate 2D kernel from 1D realizations
     643    for (int v = -size, y = 0; v <= size; v++, y++) {
     644        for (int u = -size, x = 0; u <= size; u++, x++) {
     645            preCalc->kernel->kernel[v][u] = preCalc->xKernel->data.F32[x] * preCalc->yKernel->data.F32[y]; // Value of kernel
     646        }
     647    }
     648
     649    return preCalc;
     650}
     651
     652pmSubtractionKernels *pmSubtractionKernelsPOIS(int size, int spatialOrder, float penalty, psRegion bounds,
    223653                                               pmSubtractionMode mode)
    224654{
     
    228658    int num = PS_SQR(2 * size + 1) - 1; // Number of basis functions
    229659
    230     pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_POIS, size,
    231                                                               spatialOrder, penalty, mode); // The kernels
     660    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(0, PM_SUBTRACTION_KERNEL_POIS, size,
     661                                                              spatialOrder, penalty, bounds, mode); // Kernels
    232662    psStringAppend(&kernels->description, "POIS(%d,%d,%.2e)", size, spatialOrder, penalty);
    233663    psLogMsg("psModules.imcombine", PS_LOG_INFO, "POIS kernel: %d,%d --> %d elements",
     
    244674pmSubtractionKernels *pmSubtractionKernelsISIS(int size, int spatialOrder,
    245675                                               const psVector *fwhms, const psVector *orders,
    246                                                float penalty, pmSubtractionMode mode)
     676                                               float penalty, psRegion bounds, pmSubtractionMode mode)
    247677{
    248678    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
    249                                                                   penalty, mode); // Kernels
     679                                                                  penalty, bounds, mode); // Kernels
    250680    if (!kernels) {
    251681        return NULL;
     
    256686
    257687pmSubtractionKernels *pmSubtractionKernelsSPAM(int size, int spatialOrder, int inner, int binning,
    258                                                float penalty, pmSubtractionMode mode)
     688                                               float penalty, psRegion bounds, pmSubtractionMode mode)
    259689{
    260690    PS_ASSERT_INT_POSITIVE(size, NULL);
     
    277707
    278708    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_SPAM, size,
    279                                                               spatialOrder, penalty, mode); // The kernels
     709                                                              spatialOrder, penalty, bounds, mode); // Kernels
    280710    kernels->inner = inner;
    281711    psStringAppend(&kernels->description, "SPAM(%d,%d,%d,%d,%.2e)", size, inner, binning, spatialOrder,
     
    348778
    349779pmSubtractionKernels *pmSubtractionKernelsFRIES(int size, int spatialOrder, int inner, float penalty,
    350                                                 pmSubtractionMode mode)
     780                                                psRegion bounds, pmSubtractionMode mode)
    351781{
    352782    PS_ASSERT_INT_POSITIVE(size, NULL);
     
    375805
    376806    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_FRIES, size,
    377                                                               spatialOrder, penalty, mode); // The kernels
     807                                                              spatialOrder, penalty, bounds, mode); // Kernels
    378808    kernels->inner = inner;
    379809    psStringAppend(&kernels->description, "FRIES(%d,%d,%d,%.2e)", size, inner, spatialOrder, penalty);
     
    441871}
    442872
    443 // Grid United with Normal Kernel
     873// Grid United with Normal Kernel [description: GUNK=ISIS(...)+POIS(...)]
    444874pmSubtractionKernels *pmSubtractionKernelsGUNK(int size, int spatialOrder, const psVector *fwhms,
    445875                                               const psVector *orders, int inner, float penalty,
    446                                                pmSubtractionMode mode)
     876                                               psRegion bounds, pmSubtractionMode mode)
    447877{
    448878    PS_ASSERT_INT_POSITIVE(size, NULL);
     
    456886    PS_ASSERT_INT_LESS_THAN(inner, size, NULL);
    457887
    458     // XXX GUNK doesn't seem to work --- doesn't add the POIS components, or at least, they're not noticed
    459 
    460888    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
    461                                                                   penalty, mode); // Kernels
     889                                                                  penalty, bounds, mode); // Kernels
     890    kernels->type = PM_SUBTRACTION_KERNEL_GUNK;
    462891    psStringPrepend(&kernels->description, "GUNK=");
    463892    psStringAppend(&kernels->description, "+POIS(%d,%d)", inner, spatialOrder);
     
    474903// RINGS --- just what it says
    475904pmSubtractionKernels *pmSubtractionKernelsRINGS(int size, int spatialOrder, int inner, int ringsOrder,
    476                                                 float penalty, pmSubtractionMode mode)
     905                                                float penalty, psRegion bounds, pmSubtractionMode mode)
    477906{
    478907    PS_ASSERT_INT_POSITIVE(size, NULL);
     
    505934
    506935    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_RINGS, size,
    507                                                               spatialOrder, penalty, mode); // The kernels
     936                                                              spatialOrder, penalty, bounds, mode); // Kernels
    508937    kernels->inner = inner;
    509938    psStringAppend(&kernels->description, "RINGS(%d,%d,%d,%d,%.2e)", size, inner, ringsOrder, spatialOrder,
     
    545974            for (int vOrder = 0; vOrder <= (i == 0 ? 0 : ringsOrder - uOrder); vOrder++, index++) {
    546975
    547                 psArray *data = psArrayAlloc(3); // Container for data
    548                 psVector *uCoords = data->data[0] = psVectorAllocEmpty(RINGS_BUFFER, PS_TYPE_S32); // u coords
    549                 psVector *vCoords = data->data[1] = psVectorAllocEmpty(RINGS_BUFFER, PS_TYPE_S32); // v coords
    550                 psVector *poly = data->data[2] = psVectorAllocEmpty(RINGS_BUFFER, PS_TYPE_F32); // Polynomial
     976                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc (PM_SUBTRACTION_KERNEL_RINGS, 0, 0, RINGS_BUFFER, 0.0);
    551977                double moment = 0.0;    // Moment, for penalty
    552978
    553979                if (i == 0) {
    554980                    // Central pixel is easy
    555                     uCoords->data.S32[0] = vCoords->data.S32[0] = 0;
    556                     poly->data.F32[0] = 1.0;
    557                     uCoords->n = vCoords->n = poly->n = 1;
     981                    preCalc->uCoords->data.S32[0] = 0;
     982                    preCalc->vCoords->data.S32[0] = 0;
     983                    preCalc->poly->data.F32[0] = 1.0;
     984                    preCalc->uCoords->n = 1;
     985                    preCalc->vCoords->n = 1;
     986                    preCalc->poly->n = 1;
    558987                    radiusLast = 0;
    559988                    moment = 0.0;
     
    5731002                                float polyVal = uPoly * vPoly; // Value of polynomial
    5741003                                if (polyVal != 0) { // No point adding it otherwise
    575                                     uCoords->data.S32[j] = u;
    576                                     vCoords->data.S32[j] = v;
    577                                     poly->data.F32[j] = polyVal;
     1004                                    preCalc->uCoords->data.S32[j] = u;
     1005                                    preCalc->vCoords->data.S32[j] = v;
     1006                                    preCalc->poly->data.F32[j] = polyVal;
    5781007                                    norm += polyVal;
    579                                     moment += polyVal * (PS_SQR(u) + PS_SQR(v));
    580 
    581                                     psVectorExtend(uCoords, RINGS_BUFFER, 1);
    582                                     psVectorExtend(vCoords, RINGS_BUFFER, 1);
    583                                     psVectorExtend(poly, RINGS_BUFFER, 1);
     1008                                    moment += PS_SQR(polyVal) * PS_SQR(PS_SQR(u) + PS_SQR(v));
     1009
     1010                                    psVectorExtend(preCalc->uCoords, RINGS_BUFFER, 1);
     1011                                    psVectorExtend(preCalc->vCoords, RINGS_BUFFER, 1);
     1012                                    psVectorExtend(preCalc->poly, RINGS_BUFFER, 1);
    5841013                                    psTrace("psModules.imcombine", 9, "u = %d, v = %d, poly = %f\n",
    585                                             u, v, poly->data.F32[j]);
     1014                                            u, v, preCalc->poly->data.F32[j]);
    5861015                                    j++;
    5871016                                }
     
    5911020                    // Normalise kernel component to unit sum
    5921021                    if (uOrder % 2 == 0 && vOrder % 2 == 0) {
    593                         psBinaryOp(poly, poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
     1022                        psBinaryOp(preCalc->poly, preCalc->poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
    5941023                        // Add subtraction of 0,0 component to preserve photometric scaling
    595                         uCoords->data.S32[j] = 0;
    596                         vCoords->data.S32[j] = 0;
    597                         poly->data.F32[j] = -1.0;
    598                         psVectorExtend(uCoords, RINGS_BUFFER, 1);
    599                         psVectorExtend(vCoords, RINGS_BUFFER, 1);
    600                         psVectorExtend(poly, RINGS_BUFFER, 1);
     1024                        preCalc->uCoords->data.S32[j] = 0;
     1025                        preCalc->vCoords->data.S32[j] = 0;
     1026                        preCalc->poly->data.F32[j] = -1.0;
     1027                        psVectorExtend(preCalc->uCoords, RINGS_BUFFER, 1);
     1028                        psVectorExtend(preCalc->vCoords, RINGS_BUFFER, 1);
     1029                        psVectorExtend(preCalc->poly, RINGS_BUFFER, 1);
    6011030                    } else {
    6021031                        norm = powf(size, uOrder) * powf(size, vOrder);
    603                         psBinaryOp(poly, poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
     1032                        psBinaryOp(preCalc->poly, preCalc->poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
    6041033                    }
    605 //                    moment /= norm;
     1034                    moment /= PS_SQR(norm);
    6061035                }
    6071036
    608                 psTrace("psModules.imcombine", 8, "%ld pixels in kernel\n", uCoords->n);
    609 
    610                 kernels->preCalc->data[index] = data;
     1037                psTrace("psModules.imcombine", 8, "%ld pixels in kernel\n", preCalc->uCoords->n);
     1038
     1039                kernels->preCalc->data[index] = preCalc;
    6111040                kernels->u->data.S32[index] = uOrder;
    6121041                kernels->v->data.S32[index] = vOrder;
    6131042                kernels->penalties->data.F32[index] = kernels->penalty * fabsf(moment);
     1043                if (!isfinite(kernels->penalties->data.F32[index])) {
     1044                    psAbort ("invalid penalty");
     1045                }
    6141046
    6151047                psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d\n", index,
     
    6241056pmSubtractionKernels *pmSubtractionKernelsGenerate(pmSubtractionKernelsType type, int size, int spatialOrder,
    6251057                                                   const psVector *fwhms, const psVector *orders, int inner,
    626                                                    int binning, int ringsOrder, float penalty,
     1058                                                   int binning, int ringsOrder, float penalty, psRegion bounds,
    6271059                                                   pmSubtractionMode mode)
    6281060{
    6291061    switch (type) {
    6301062      case PM_SUBTRACTION_KERNEL_POIS:
    631         return pmSubtractionKernelsPOIS(size, spatialOrder, penalty, mode);
     1063        return pmSubtractionKernelsPOIS(size, spatialOrder, penalty, bounds, mode);
    6321064      case PM_SUBTRACTION_KERNEL_ISIS:
    633         return pmSubtractionKernelsISIS(size, spatialOrder, fwhms, orders, penalty, mode);
     1065        return pmSubtractionKernelsISIS(size, spatialOrder, fwhms, orders, penalty, bounds, mode);
     1066      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     1067        return pmSubtractionKernelsISIS_RADIAL(size, spatialOrder, fwhms, orders, penalty, bounds, mode);
     1068      case PM_SUBTRACTION_KERNEL_HERM:
     1069        return pmSubtractionKernelsHERM(size, spatialOrder, fwhms, orders, penalty, bounds, mode);
     1070      case PM_SUBTRACTION_KERNEL_DECONV_HERM:
     1071        return pmSubtractionKernelsDECONV_HERM(size, spatialOrder, fwhms, orders, penalty, bounds, mode);
    6341072      case PM_SUBTRACTION_KERNEL_SPAM:
    635         return pmSubtractionKernelsSPAM(size, spatialOrder, inner, binning, penalty, mode);
     1073        return pmSubtractionKernelsSPAM(size, spatialOrder, inner, binning, penalty, bounds, mode);
    6361074      case PM_SUBTRACTION_KERNEL_FRIES:
    637         return pmSubtractionKernelsFRIES(size, spatialOrder, inner, penalty, mode);
     1075        return pmSubtractionKernelsFRIES(size, spatialOrder, inner, penalty, bounds, mode);
    6381076      case PM_SUBTRACTION_KERNEL_GUNK:
    639         return pmSubtractionKernelsGUNK(size, spatialOrder, fwhms, orders, inner, penalty, mode);
     1077        return pmSubtractionKernelsGUNK(size, spatialOrder, fwhms, orders, inner, penalty, bounds, mode);
    6401078      case PM_SUBTRACTION_KERNEL_RINGS:
    641         return pmSubtractionKernelsRINGS(size, spatialOrder, inner, ringsOrder, penalty, mode);
     1079        return pmSubtractionKernelsRINGS(size, spatialOrder, inner, ringsOrder, penalty, bounds, mode);
    6421080      default:
    6431081        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unknown kernel type: %x", type);
     
    6751113
    6761114pmSubtractionKernels *pmSubtractionKernelsFromDescription(const char *description, int bgOrder,
    677                                                           pmSubtractionMode mode)
     1115                                                          psRegion bounds, pmSubtractionMode mode)
    6781116{
    6791117    PS_ASSERT_STRING_NON_EMPTY(description, NULL);
     
    6941132    float penalty = 0.0;                // Penalty for wideness
    6951133
    696     if (strncmp(description, "ISIS", 4) == 0) {
    697         // XXX Support for GUNK
    698         if (strstr(description, "+POIS")) {
    699             type = PM_SUBTRACTION_KERNEL_GUNK;
    700             psAbort("Deciphering GUNK kernels (%s) is not currently supported.", description);
    701         } else {
    702             type = PM_SUBTRACTION_KERNEL_ISIS;
    703             char *ptr = (char*)description + 5;    // Eat "ISIS("
    704             PARSE_STRING_NUMBER(size, ptr, ',', parseStringInt);
    705 
    706             // Count the number of Gaussians
    707             int numGauss = 0;
    708             for (char *string = ptr; string; string = strchr(string + 1, '(')) {
    709                 numGauss++;
    710             }
    711 
    712             fwhms = psVectorAlloc(numGauss, PS_TYPE_F32);
    713             orders = psVectorAlloc(numGauss, PS_TYPE_S32);
    714 
    715             for (int i = 0; i < numGauss; i++) {
    716                 ptr++;                  // Eat the '('
    717                 PARSE_STRING_NUMBER(fwhms->data.F32[i], ptr, ',', parseStringFloat); // Eat "1.234,"
    718                 PARSE_STRING_NUMBER(orders->data.S32[i], ptr, ')', parseStringInt); // Eat "3)"
    719             }
    720 
    721             ptr++;                      // Eat ','
    722             PARSE_STRING_NUMBER(spatialOrder, ptr, ',', parseStringInt);
    723             penalty = parseStringFloat(ptr);
    724         }
    725     } else if (strncmp(description, "RINGS", 5) == 0) {
    726         type = PM_SUBTRACTION_KERNEL_RINGS;
    727         char *ptr = (char*)description + 6;
     1134    // currently known descriptions:
     1135    // ISIS(...), ISIS_RADIAL(...), HERM(...), DECONV_HERM(...), POIS(...), SPAM(...),
     1136    // FRIES(...), GUNK=ISIS(...)+POIS(...), RINGS(...),
     1137    // the descriptive name is the set of characters before the (
     1138
     1139    type = pmSubtractionKernelsTypeFromString (description);
     1140    char *ptr = strchr(description, '(') + 1;
     1141    psAssert (ptr, "description is missing kernel parameters");
     1142
     1143    switch (type) {
     1144      case PM_SUBTRACTION_KERNEL_ISIS:
     1145      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     1146      case PM_SUBTRACTION_KERNEL_HERM:
     1147      case PM_SUBTRACTION_KERNEL_DECONV_HERM:
     1148        PARSE_STRING_NUMBER(size, ptr, ',', parseStringInt);
     1149
     1150        // Count the number of Gaussians
     1151        int numGauss = 0;
     1152        for (char *string = ptr; string; string = strchr(string + 1, '(')) {
     1153            numGauss++;
     1154        }
     1155
     1156        fwhms = psVectorAlloc(numGauss, PS_TYPE_F32);
     1157        orders = psVectorAlloc(numGauss, PS_TYPE_S32);
     1158
     1159        for (int i = 0; i < numGauss; i++) {
     1160            ptr++;                                                               // Eat the '('
     1161            PARSE_STRING_NUMBER(fwhms->data.F32[i], ptr, ',', parseStringFloat); // Eat "1.234,"
     1162            PARSE_STRING_NUMBER(orders->data.S32[i], ptr, ')', parseStringInt);  // Eat "3)"
     1163        }
     1164
     1165        ptr++;                      // Eat ','
     1166        PARSE_STRING_NUMBER(spatialOrder, ptr, ',', parseStringInt);
     1167        penalty = parseStringFloat(ptr);
     1168        break;
     1169      case PM_SUBTRACTION_KERNEL_RINGS:
    7281170        PARSE_STRING_NUMBER(size, ptr, ',', parseStringInt);
    7291171        PARSE_STRING_NUMBER(inner, ptr, ',', parseStringInt);
     
    7311173        PARSE_STRING_NUMBER(spatialOrder, ptr, ',', parseStringInt);
    7321174        PARSE_STRING_NUMBER(penalty, ptr, ')', parseStringInt);
    733     } else {
    734         psAbort("Deciphering kernels other than ISIS and RINGS is not currently supported.");
    735     }
    736 
    737 
    738     return pmSubtractionKernelsGenerate(type, size, spatialOrder, fwhms, orders,
    739                                         inner, binning, ringsOrder, penalty, mode);
    740 }
    741 
    742 
     1175        break;
     1176      default:
     1177        psAbort("Deciphering kernels other than ISIS, HERM, DECONV_HERM or RINGS is not currently supported.");
     1178    }
     1179
     1180    return pmSubtractionKernelsGenerate(type, size, spatialOrder, fwhms, orders, inner, binning,
     1181                                        ringsOrder, penalty, bounds, mode);
     1182}
     1183
     1184
     1185// the input string can either be just the name or the description string.  Currently known
     1186// descriptions: ISIS(...), ISIS_RADIAL(...), HERM(...), DECONV_HERM(...), POIS(...),
     1187// SPAM(...), FRIES(...), GUNK=ISIS(...)+POIS(...), RINGS(...),
    7431188pmSubtractionKernelsType pmSubtractionKernelsTypeFromString(const char *type)
    7441189{
    745     if (strcasecmp(type, "POIS") == 0) {
     1190    // for a bare name (ISIS, HERM), use the full string length.
     1191    // otherwise, use the length up to the first '('
     1192    int nameLength = strlen(type);
     1193    char *ptr = strchr(type, '(');
     1194    if (ptr) {
     1195        nameLength = ptr - type;
     1196    }
     1197
     1198    if (strncasecmp(type, "POIS", nameLength) == 0) {
    7461199        return PM_SUBTRACTION_KERNEL_POIS;
    7471200    }
    748     if (strcasecmp(type, "ISIS") == 0) {
     1201    if (strncasecmp(type, "ISIS", nameLength) == 0) {
    7491202        return PM_SUBTRACTION_KERNEL_ISIS;
    7501203    }
    751     if (strcasecmp(type, "SPAM") == 0) {
     1204    if (strncasecmp(type, "ISIS_RADIAL", nameLength) == 0) {
     1205        return PM_SUBTRACTION_KERNEL_ISIS_RADIAL;
     1206    }
     1207    if (strncasecmp(type, "HERM", nameLength) == 0) {
     1208        return PM_SUBTRACTION_KERNEL_HERM;
     1209    }
     1210    if (strncasecmp(type, "DECONV_HERM", nameLength) == 0) {
     1211        return PM_SUBTRACTION_KERNEL_DECONV_HERM;
     1212    }
     1213    if (strncasecmp(type, "SPAM", nameLength) == 0) {
    7521214        return PM_SUBTRACTION_KERNEL_SPAM;
    7531215    }
    754     if (strcasecmp(type, "FRIES") == 0) {
     1216    if (strncasecmp(type, "FRIES", nameLength) == 0) {
    7551217        return PM_SUBTRACTION_KERNEL_FRIES;
    7561218    }
    757     if (strcasecmp(type, "GUNK") == 0) {
     1219    if (strncasecmp(type, "GUNK", nameLength) == 0) {
    7581220        return PM_SUBTRACTION_KERNEL_GUNK;
    7591221    }
    760     if (strcasecmp(type, "RINGS") == 0) {
     1222    // note that GUNK has a somewhat different description
     1223    if (strncasecmp(type, "GUNK=ISIS", nameLength) == 0) {
     1224        return PM_SUBTRACTION_KERNEL_GUNK;
     1225    }
     1226    if (strncasecmp(type, "RINGS", nameLength) == 0) {
    7611227        return PM_SUBTRACTION_KERNEL_RINGS;
    7621228    }
     
    7651231    return PM_SUBTRACTION_KERNEL_NONE;
    7661232}
     1233
     1234pmSubtractionKernels *pmSubtractionKernelsCopy(const pmSubtractionKernels *in)
     1235{
     1236    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(in, NULL);
     1237
     1238    pmSubtractionKernels *out = psAlloc(sizeof(pmSubtractionKernels)); // Kernels, to return
     1239    psMemSetDeallocator(out, (psFreeFunc)subtractionKernelsFree);
     1240
     1241    out->type = in->type;
     1242    out->description = psMemIncrRefCounter(in->description);
     1243    out->num = in->num;
     1244    out->u = psMemIncrRefCounter(in->u);
     1245    out->v = psMemIncrRefCounter(in->v);
     1246    out->widths = psMemIncrRefCounter(in->widths);
     1247    out->preCalc = psMemIncrRefCounter(in->preCalc);
     1248    out->penalty = in->penalty;
     1249    out->penalties = psMemIncrRefCounter(in->penalties);
     1250    out->uStop = psMemIncrRefCounter(in->uStop);
     1251    out->vStop = psMemIncrRefCounter(in->vStop);
     1252    out->size = in->size;
     1253    out->inner = in->inner;
     1254    out->spatialOrder = in->spatialOrder;
     1255    out->bgOrder = in->bgOrder;
     1256    out->mode = in->mode;
     1257    out->xMin = in->xMin;
     1258    out->xMax = in->xMax;
     1259    out->yMin = in->yMin;
     1260    out->yMax = in->yMax;
     1261    out->solution1 = in->solution1 ? psVectorCopy(NULL, in->solution1, PS_TYPE_F64) : NULL;
     1262    out->solution2 = in->solution2 ? psVectorCopy(NULL, in->solution2, PS_TYPE_F64) : NULL;
     1263    out->sampleStamps = psMemIncrRefCounter(in->sampleStamps);
     1264
     1265    return out;
     1266}
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionKernels.h

    r20049 r27840  
    1010    PM_SUBTRACTION_KERNEL_POIS,         ///< Pan-STARRS Optimal Image Subtraction --- delta functions
    1111    PM_SUBTRACTION_KERNEL_ISIS,         ///< Traditional kernel --- gaussians modified by polynomials
     12    PM_SUBTRACTION_KERNEL_ISIS_RADIAL,  ///< ISIS + higher-order radial Hermitians
     13    PM_SUBTRACTION_KERNEL_HERM,         ///< Hermitian polynomial kernels
     14    PM_SUBTRACTION_KERNEL_DECONV_HERM,  ///< Deconvolved Hermitian polynomial kernels
    1215    PM_SUBTRACTION_KERNEL_SPAM,         ///< Summed Pixels for Advanced Matching --- summed delta functions
    1316    PM_SUBTRACTION_KERNEL_FRIES,        ///< Fibonacci Radius Increases Excellence of Subtraction
     
    2932    pmSubtractionKernelsType type;      ///< Type of kernels --- allowing the use of multiple kernels
    3033    psString description;               ///< Description of the kernel parameters
     34    int xMin, xMax, yMin, yMax;         ///< Bounds of image (for normalisation)
    3135    long num;                           ///< Number of kernel components (not including the spatial ones)
    32     psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS)
    33     psVector *widths;                   ///< Gaussian FWHMs (ISIS)
     36    psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS, HERM or DECONV_HERM)
     37    psVector *widths;                   ///< Gaussian FWHMs (ISIS, HERM or DECONV_HERM)
    3438    psVector *uStop, *vStop;            ///< Width of kernel element (SPAM,FRIES only)
    35     psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (for ISIS)
     39    psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (for ISIS, HERM or DECONV_HERM)
    3640    float penalty;                      ///< Penalty for wideness
    3741    psVector *penalties;                ///< Penalty for each kernel component
     
    4145    int bgOrder;                        ///< The order for the background fitting
    4246    pmSubtractionMode mode;             ///< Mode for subtraction
    43     int numCols, numRows;               ///< Size of image (for normalisation), or zero to use image provided
    4447    psVector *solution1, *solution2;    ///< Solution for the PSF matching
    4548    // Quality information
    4649    float mean, rms;                    ///< Mean and RMS of chi^2 from stamps
    4750    int numStamps;                      ///< Number of good stamps
     51    float fSigResMean;                  ///< mean fractional stdev of residuals
     52    float fSigResStdev;                 ///< stdev of fractional stdev of residuals
     53    float fMaxResMean;                  ///< mean fractional positive swing in residuals
     54    float fMaxResStdev;                 ///< stdev of fractional positive swing in residuals
     55    float fMinResMean;                  ///< mean fractional negative swing in residuals
     56    float fMinResStdev;                 ///< stdev of fractional negative swing in residuals
     57    psArray *sampleStamps;              ///< array of brightest set of stamps for output visualizations
    4858} pmSubtractionKernels;
     59
     60// pmSubtractionKernels->preCalc is an array of pmSubtractionKernelPreCalc structures
     61typedef struct {
     62    psVector *uCoords;                  // used by RINGS
     63    psVector *vCoords;                  // used by RINGS
     64    psVector *poly;                     // used by RINGS
     65
     66    psVector *xKernel;                  // used by ISIS, HERM, DECONV_HERM
     67    psVector *yKernel;                  // used by ISIS, HERM, DECONV_HERM
     68    psKernel *kernel;                   // used by ISIS, HERM, DECONV_HERM
     69} pmSubtractionKernelPreCalc;
    4970
    5071// Assertion to check pmSubtractionKernels
     
    5273    PS_ASSERT_PTR_NON_NULL(KERNELS, RETURNVALUE); \
    5374    PS_ASSERT_STRING_NON_EMPTY((KERNELS)->description, RETURNVALUE); \
    54     PS_ASSERT_INT_POSITIVE((KERNELS)->num, RETURNVALUE); \
     75    PS_ASSERT_INT_NONNEGATIVE((KERNELS)->num, RETURNVALUE); \
    5576    PS_ASSERT_VECTOR_NON_NULL((KERNELS)->u, RETURNVALUE); \
    5677    PS_ASSERT_VECTOR_NON_NULL((KERNELS)->v, RETURNVALUE); \
     
    6081    PS_ASSERT_VECTOR_SIZE((KERNELS)->v, (KERNELS)->num, RETURNVALUE); \
    6182    if ((KERNELS)->type == PM_SUBTRACTION_KERNEL_ISIS) { \
     83        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->widths, RETURNVALUE); \
     84        PS_ASSERT_VECTOR_TYPE((KERNELS)->widths, PS_TYPE_F32, RETURNVALUE); \
     85        PS_ASSERT_VECTOR_SIZE((KERNELS)->widths, (KERNELS)->num, RETURNVALUE); \
     86    } \
     87    if ((KERNELS)->type == PM_SUBTRACTION_KERNEL_ISIS_RADIAL) { \
     88        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->widths, RETURNVALUE); \
     89        PS_ASSERT_VECTOR_TYPE((KERNELS)->widths, PS_TYPE_F32, RETURNVALUE); \
     90        PS_ASSERT_VECTOR_SIZE((KERNELS)->widths, (KERNELS)->num, RETURNVALUE); \
     91    } \
     92    if ((KERNELS)->type == PM_SUBTRACTION_KERNEL_HERM) { \
     93        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->widths, RETURNVALUE); \
     94        PS_ASSERT_VECTOR_TYPE((KERNELS)->widths, PS_TYPE_F32, RETURNVALUE); \
     95        PS_ASSERT_VECTOR_SIZE((KERNELS)->widths, (KERNELS)->num, RETURNVALUE); \
     96    } \
     97    if ((KERNELS)->type == PM_SUBTRACTION_KERNEL_DECONV_HERM) { \
    6298        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->widths, RETURNVALUE); \
    6399        PS_ASSERT_VECTOR_TYPE((KERNELS)->widths, PS_TYPE_F32, RETURNVALUE); \
     
    99135}
    100136
     137// Generate 1D convolution kernel for ISIS
     138psVector *pmSubtractionKernelISIS(float sigma, // Gaussian width
     139                                       int order, // Polynomial order
     140                                       int size // Kernel half-size
     141    );
     142
     143// Generate 1D convolution kernel for HERM (normalized for 2D)
     144psVector *pmSubtractionKernelHERM(float sigma, // Gaussian width
     145                                       int order, // Polynomial order
     146                                       int size // Kernel half-size
     147    );
     148
    101149/// Generate a delta-function grid for subtraction kernels (like the POIS kernel)
    102150bool p_pmSubtractionKernelsAddGrid(pmSubtractionKernels *kernels, ///< The subtraction kernels to append to
     
    114162                                                int spatialOrder, ///< Order of spatial variations
    115163                                                float penalty, ///< Penalty for wideness
     164                                                psRegion bounds,       ///< Bounds for validity
    116165                                                pmSubtractionMode mode ///< Mode for subtraction
    117166    );
     167
     168/// Allocator for pre-calculated kernel data structure
     169pmSubtractionKernelPreCalc *pmSubtractionKernelPreCalcAlloc(
     170    pmSubtractionKernelsType type, ///< type of kernel to allocate (not all can be pre-calculated)
     171    int uOrder,                    ///< order in x-direction
     172    int vOrder,                    ///< order in x-direction
     173    int size,                      ///< Half-size of the kernel
     174    float sigma                    ///< sigma of gaussian kernel
     175    );
     176
    118177
    119178/// Generate POIS kernels
     
    121180                                               int spatialOrder, ///< Order of spatial variations
    122181                                               float penalty, ///< Penalty for wideness
     182                                               psRegion bounds,       ///< Bounds for validity
    123183                                               pmSubtractionMode mode ///< Mode for subtraction
    124184    );
     
    130190                                                    const psVector *orders, ///< Polynomial order of gaussians
    131191                                                    float penalty, ///< Penalty for wideness
     192                                                    psRegion bounds,       ///< Bounds for validity
    132193                                                    pmSubtractionMode mode ///< Mode for subtraction
    133194    );
     
    139200                                               const psVector *orders, ///< Polynomial order of gaussians
    140201                                               float penalty, ///< Penalty for wideness
     202                                               psRegion bounds,       ///< Bounds for validity
    141203                                               pmSubtractionMode mode ///< Mode for subtraction
    142204                                               );
     205
     206/// Generate ISIS + RADIAL_HERM kernels
     207pmSubtractionKernels *pmSubtractionKernelsISIS_RADIAL(int size, ///< Half-size of the kernel
     208                                                      int spatialOrder, ///< Order of spatial variations
     209                                                      const psVector *fwhms, ///< Gaussian FWHMs
     210                                                      const psVector *orders, ///< Polynomial order of gaussians
     211                                                      float penalty, ///< Penalty for wideness
     212                                                      psRegion bounds,       ///< Bounds for validity
     213                                                      pmSubtractionMode mode ///< Mode for subtraction
     214                                               );
     215
     216/// Generate HERM kernels
     217pmSubtractionKernels *pmSubtractionKernelsHERM(int size, ///< Half-size of the kernel
     218                                               int spatialOrder, ///< Order of spatial variations
     219                                               const psVector *fwhms, ///< Gaussian FWHMs
     220                                               const psVector *orders, ///< order of hermitian polynomials
     221                                               float penalty, ///< Penalty for wideness
     222                                               psRegion bounds,       ///< Bounds for validity
     223                                               pmSubtractionMode mode ///< Mode for subtraction
     224                                               );
     225
     226/// Generate DECONV_HERM kernels
     227pmSubtractionKernels *pmSubtractionKernelsDECONV_HERM(int size, ///< Half-size of the kernel
     228                                                      int spatialOrder, ///< Order of spatial variations
     229                                                      const psVector *fwhms, ///< Gaussian FWHMs
     230                                                      const psVector *orders, ///< order of hermitian polynomials
     231                                                      float penalty, ///< Penalty for wideness
     232                                                      psRegion bounds,       ///< Bounds for validity
     233                                                      pmSubtractionMode mode ///< Mode for subtraction
     234    );
    143235
    144236/// Generate SPAM kernels
     
    148240                                               int binning, ///< Kernel binning factor
    149241                                               float penalty, ///< Penalty for wideness
     242                                               psRegion bounds,       ///< Bounds for validity
    150243                                               pmSubtractionMode mode ///< Mode for subtraction
    151244    );
     
    156249                                                int inner, ///< Inner radius to preserve unbinned
    157250                                                float penalty, ///< Penalty for wideness
     251                                                psRegion bounds,       ///< Bounds for validity
    158252                                                pmSubtractionMode mode ///< Mode for subtraction
    159253    );
     
    166260                                               int inner, ///< Inner radius containing grid of delta functions
    167261                                               float penalty, ///< Penalty for wideness
     262                                               psRegion bounds,       ///< Bounds for validity
    168263                                               pmSubtractionMode mode ///< Mode for subtraction
    169264    );
     
    175270                                                int ringsOrder, ///< Polynomial order
    176271                                                float penalty, ///< Penalty for wideness
     272                                                psRegion bounds,       ///< Bounds for validity
    177273                                                pmSubtractionMode mode ///< Mode for subtraction
    178274    );
     
    189285                                                   int ringsOrder, ///< Polynomial order for RINGS
    190286                                                   float penalty, ///< Penalty for wideness
     287                                                   psRegion bounds,       ///< Bounds for validity
    191288                                                   pmSubtractionMode mode ///< Mode for subtraction
    192289    );
     
    196293    const char *description,            ///< Description of kernel
    197294    int bgOrder,                        ///< Polynomial order for background fitting
     295    psRegion bounds,                    ///< Bounds for validity
    198296    pmSubtractionMode mode              ///< Mode for subtraction
    199297    );
     
    203301    );
    204302
     303/// Copy kernels
     304///
     305/// A deep copy is performed on the solution only; the other components are merely pointers.
     306pmSubtractionKernels *pmSubtractionKernelsCopy(
     307    const pmSubtractionKernels *in      // Kernels to copy
     308    );
     309
    205310
    206311#endif
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionMask.c

    r24257 r27840  
    3838//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    3939
    40 psImage *pmSubtractionMask(const psImage *mask1, const psImage *mask2, psImageMaskType maskVal,
    41                            int size, int footprint, float badFrac, pmSubtractionMode mode)
    42 {
    43     PS_ASSERT_IMAGE_NON_NULL(mask1, NULL);
    44     PS_ASSERT_IMAGE_TYPE(mask1, PS_TYPE_IMAGE_MASK, NULL);
    45     if (mask2) {
    46         PS_ASSERT_IMAGE_NON_NULL(mask2, NULL);
    47         PS_ASSERT_IMAGE_TYPE(mask2, PS_TYPE_IMAGE_MASK, NULL);
    48         PS_ASSERT_IMAGES_SIZE_EQUAL(mask2, mask1, NULL);
    49     }
     40psImage *pmSubtractionMask(psRegion *bounds, const pmReadout *ro1, const pmReadout *ro2,
     41                           psImageMaskType maskVal, int size, int footprint, float badFrac,
     42                           pmSubtractionMode mode)
     43{
     44    int numCols = 0, numRows = 0;       // Size of the images
     45    if (ro1) {
     46        PM_ASSERT_READOUT_NON_NULL(ro1, NULL);
     47        PM_ASSERT_READOUT_IMAGE(ro1, NULL);
     48        PM_ASSERT_READOUT_MASK(ro1, NULL);
     49        numCols = ro1->image->numCols;
     50        numRows = ro1->image->numRows;
     51            }
     52    if (ro2) {
     53        PM_ASSERT_READOUT_NON_NULL(ro2, NULL);
     54        PM_ASSERT_READOUT_IMAGE(ro2, NULL);
     55        PM_ASSERT_READOUT_MASK(ro2, NULL);
     56        numCols = ro2->image->numCols;
     57        numRows = ro2->image->numRows;
     58    }
     59    if (ro1 && ro2) {
     60        PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->image, ro2->image, NULL);
     61    }
     62    if (!ro1 && !ro2) {
     63        psError(PS_ERR_UNEXPECTED_NULL, true, "No image provided.");
     64        return false;
     65    }
     66    psAssert(numCols > 0 && numRows > 0, "There should be an image provided");
    5067    PS_ASSERT_INT_NONNEGATIVE(size, NULL);
    5168    PS_ASSERT_INT_NONNEGATIVE(footprint, NULL);
     
    5572    }
    5673
    57     int numCols = mask1->numCols, numRows = mask1->numRows; // Size of the images
    58 
    5974    // Dereference inputs for convenience
    60     psImageMaskType **data1 = mask1->data.PS_TYPE_IMAGE_MASK_DATA;
    61     psImageMaskType **data2 = NULL;
    62     if (mask2) {
    63         data2 = mask2->data.PS_TYPE_IMAGE_MASK_DATA;
    64     }
     75    psF32 **imageData1 = ro1 ? ro1->image->data.F32 : NULL;
     76    psF32 **imageData2 = ro2 ? ro2->image->data.F32 : NULL;
     77    psImageMaskType **maskData1 = ro1 ? ro1->mask->data.PS_TYPE_IMAGE_MASK_DATA : NULL;
     78    psImageMaskType **maskData2 = ro2 ? ro2->mask->data.PS_TYPE_IMAGE_MASK_DATA : NULL;
    6579
    6680    // First, a pass through to determine the fraction of bad pixels
    67     if (isfinite(badFrac) && badFrac != 1.0) {
     81    if (bounds || (isfinite(badFrac) && badFrac != 1.0)) {
     82        int xMin = numCols, xMax = 0, yMin = numRows, yMax = 0; // Bounds of good pixels
    6883        int numBad = 0;                 // Number of bad pixels
    6984        for (int y = 0; y < numRows; y++) {
    7085            for (int x = 0; x < numCols; x++) {
    71                 if (data1[y][x] & maskVal) {
     86                if (ro1 && ((maskData1[y][x] & maskVal) || !isfinite(imageData1[y][x]))) {
    7287                    numBad++;
    7388                    continue;
    7489                }
    75                 if (data2 && data2[y][x] & maskVal) {
     90                if (ro2 && ((maskData2[y][x] & maskVal) || !isfinite(imageData2[y][x]))) {
    7691                    numBad++;
     92                    continue;
    7793                }
    78             }
    79         }
    80         if (numBad > badFrac * numCols * numRows) {
     94                xMin = PS_MIN(xMin, x);
     95                xMax = PS_MAX(xMax, x);
     96                yMin = PS_MIN(yMin, y);
     97                yMax = PS_MAX(yMax, y);
     98            }
     99        }
     100        if (bounds) {
     101            bounds->x0 = xMin;
     102            bounds->x1 = xMax;
     103            bounds->y0 = yMin;
     104            bounds->y1 = yMax;
     105        }
     106        if (isfinite(badFrac) && badFrac != 1.0 && numBad > badFrac * numCols * numRows) {
    81107            psError(PM_ERR_SMALL_AREA, true,
    82108                    "Fraction of bad pixels (%d/%d=%f) exceeds limit (%f)\n",
     
    117143    for (int y = 0; y < numRows; y++) {
    118144        for (int x = 0; x < numCols; x++) {
    119             if (data1[y][x] & maskVal) {
     145            if (ro1 && maskData1[y][x] & maskVal) {
    120146                maskData[y][x] |= PM_SUBTRACTION_MASK_BAD_1;
    121147            }
    122             if (data2 && data2[y][x] & maskVal) {
     148            if (ro2 && maskData2[y][x] & maskVal) {
    123149                maskData[y][x] |= PM_SUBTRACTION_MASK_BAD_2;
    124150            }
     
    133159
    134160    // Pixels that will be bad (or poor) if we convolve with a bad reference pixel
    135     if (!psImageConvolveMask(mask, mask, PM_SUBTRACTION_MASK_BAD_1, PM_SUBTRACTION_MASK_CONVOLVE_1,
    136                              -size, size, -size, size)) {
    137         psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad pixels from mask 1.");
     161    if (ro1 && !psImageConvolveMask(mask, mask, PM_SUBTRACTION_MASK_BAD_1, PM_SUBTRACTION_MASK_CONVOLVE_1,
     162                                    -size, size, -size, size)) {
     163        psError(psErrorCodeLast(), false, "Unable to convolve bad pixels from mask 1.");
    138164        psFree(mask);
    139165        return NULL;
    140166    }
    141     if (!psImageConvolveMask(mask, mask, PM_SUBTRACTION_MASK_BAD_2, PM_SUBTRACTION_MASK_CONVOLVE_2,
    142                              -size, size, -size, size)) {
    143         psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad pixels from mask 2.");
     167    if (ro2 && !psImageConvolveMask(mask, mask, PM_SUBTRACTION_MASK_BAD_2, PM_SUBTRACTION_MASK_CONVOLVE_2,
     168                                    -size, size, -size, size)) {
     169        psError(psErrorCodeLast(), false, "Unable to convolve bad pixels from mask 2.");
    144170        psFree(mask);
    145171        return NULL;
     
    163189        psAbort("Unsupported subtraction mode: %x", mode);
    164190    }
    165     if (!psImageConvolveMask(mask, mask, maskRej, PM_SUBTRACTION_MASK_REJ,
    166                              -footprint, footprint, -footprint, footprint)) {
    167         psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad pixels.");
     191    if (ro1 && ro2 && !psImageConvolveMask(mask, mask, maskRej, PM_SUBTRACTION_MASK_REJ,
     192                                           -footprint, footprint, -footprint, footprint)) {
     193        psError(psErrorCodeLast(), false, "Unable to convolve bad pixels.");
    168194        psFree(mask);
    169195        return NULL;
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionMask.h

    r23851 r27840  
    55
    66/// Generate a mask for use in the subtraction process
    7 psImage *pmSubtractionMask(const psImage *refMask, ///< Mask for the reference image (will be convolved)
    8                            const psImage *inMask, ///< Mask for the input image, or NULL
    9                            psImageMaskType maskVal, ///< Value to mask out
    10                            int size, ///< Half-size of the kernel (pmSubtractionKernels.size)
    11                            int footprint, ///< Half-size of the kernel footprint
    12                            float badFrac, ///< Maximum fraction of bad input pixels to accept
    13                            pmSubtractionMode mode  ///< Subtraction mode
     7psImage *pmSubtractionMask(
     8    psRegion *bounds,                   ///< Bounds of valid pixels (or NULL), returned
     9    const pmReadout *ro1,               ///< Readout 1
     10    const pmReadout *ro2,               ///< Readout 2
     11    psImageMaskType maskVal,            ///< Value to mask out
     12    int size,                           ///< Half-size of the kernel (pmSubtractionKernels.size)
     13    int footprint,                      ///< Half-size of the kernel footprint
     14    float badFrac,                      ///< Maximum fraction of bad input pixels to accept
     15    pmSubtractionMode mode              ///< Subtraction mode
    1416    );
    1517
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionMatch.c

    r25060 r27840  
    2828static bool useFFT = true;              // Do convolutions using FFT
    2929
     30# define SEPARATE 0
     31# if (SEPARATE)
     32# define SUBMODE PM_SUBTRACTION_EQUATION_NORM
     33# else
     34# define SUBMODE PM_SUBTRACTION_EQUATION_ALL
     35# endif
    3036
    3137//#define TESTING
     
    6470                                 const psImage *subMask, // Mask for subtraction, or NULL
    6571                                 psImage *variance,  // Variance map
    66                                  const psRegion *region, // Region of interest, or NULL
     72                                 const psRegion *region, // Region of interest
    6773                                 float thresh1,  // Threshold for stamp finding on readout 1
    6874                                 float thresh2,  // Threshold for stamp finding on readout 2
    6975                                 float stampSpacing, // Spacing between stamps
     76                                 float normFrac,     // Fraction of flux in window for normalisation window
     77                                 float sysError,     // Relative systematic error in images
     78                                 float skyError,     // Relative systematic error in images
    7079                                 int size,         // Kernel half-size
    7180                                 int footprint,     // Convolution footprint for stamps
     
    7382    )
    7483{
     84    PS_ASSERT_PTR_NON_NULL(stamps, false);
     85    PM_ASSERT_READOUT_NON_NULL(ro1, false);
     86    PM_ASSERT_READOUT_NON_NULL(ro2, false);
     87    PS_ASSERT_IMAGE_NON_NULL(subMask, false);
     88    PS_ASSERT_IMAGE_NON_NULL(variance, false);
     89    PS_ASSERT_PTR_NON_NULL(region, false);
     90
    7591    psTrace("psModules.imcombine", 3, "Finding stamps...\n");
    7692
     
    7894
    7995    *stamps = pmSubtractionStampsFind(*stamps, image1, image2, subMask, region, thresh1, thresh2,
    80                                       size, footprint, stampSpacing, mode);
     96                                      size, footprint, stampSpacing, normFrac, sysError, skyError, mode);
    8197    if (!*stamps) {
    8298        psError(psErrorCodeLast(), false, "Unable to find stamps.");
     
    87103
    88104    psTrace("psModules.imcombine", 3, "Extracting stamps...\n");
    89     if (!pmSubtractionStampsExtract(*stamps, ro1->image, ro2 ? ro2->image : NULL, variance, size)) {
    90         psError(PS_ERR_UNKNOWN, false, "Unable to extract stamps.");
     105    if (!pmSubtractionStampsExtract(*stamps, ro1->image, ro2->image, variance, size, *region)) {
     106        psError(psErrorCodeLast(), false, "Unable to extract stamps.");
    91107        return false;
    92108    }
     
    101117                                  const pmReadout *ro1, const pmReadout *ro2, // Input images
    102118                                  int stride, // Size for convolution patches
    103                                   float sysError, // Relative systematic error
     119                                  float normFrac,           // Fraction of window for normalisation window
     120                                  float sysError,           // Systematic error in images
     121                                  float skyError,           // Systematic error in images
     122                                  float kernelError, // Systematic error in kernel
     123                                  float covarFrac,   // Fraction for kernel truncation before covariance
    104124                                  psImageMaskType maskVal, // Value to mask for input
    105125                                  psImageMaskType maskBad, // Mask for output bad pixels
     
    112132    if (subMode != PM_SUBTRACTION_MODE_2) {
    113133        PM_ASSERT_READOUT_NON_NULL(conv1, false);
     134        PM_ASSERT_READOUT_NON_NULL(ro1, false);
     135        PM_ASSERT_READOUT_IMAGE(ro1, false);
    114136        if (conv1->image) {
    115137            psFree(conv1->image);
     
    127149    if (subMode != PM_SUBTRACTION_MODE_1) {
    128150        PM_ASSERT_READOUT_NON_NULL(conv2, false);
     151        PM_ASSERT_READOUT_NON_NULL(ro2, false);
     152        PM_ASSERT_READOUT_IMAGE(ro2, false);
    129153        if (conv2->image) {
    130154            psFree(conv2->image);
     
    141165    }
    142166
    143     PM_ASSERT_READOUT_NON_NULL(ro1, false);
    144     PM_ASSERT_READOUT_NON_NULL(ro2, false);
    145     PM_ASSERT_READOUT_IMAGE(ro1, false);
    146     PM_ASSERT_READOUT_IMAGE(ro2, false);
    147     PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->image, ro2->image, false);
     167    if (ro1 && ro2) {
     168        PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->image, ro2->image, false);
     169    }
    148170    PS_ASSERT_INT_NONNEGATIVE(stride, false);
     171    if (isfinite(normFrac)) {
     172        PS_ASSERT_FLOAT_LARGER_THAN(normFrac, 0.0, false);
     173        PS_ASSERT_FLOAT_LESS_THAN(normFrac, 1.0, false);
     174    }
    149175    if (isfinite(sysError)) {
    150176        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(sysError, 0.0, false);
    151177        PS_ASSERT_FLOAT_LESS_THAN(sysError, 1.0, false);
    152178    }
     179    if (isfinite(sysError)) {
     180        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(skyError, 0.0, false);
     181    }
     182    if (isfinite(kernelError)) {
     183        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(kernelError, 0.0, false);
     184        PS_ASSERT_FLOAT_LESS_THAN(kernelError, 1.0, false);
     185    }
     186    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(covarFrac, 0.0, false);
     187    PS_ASSERT_FLOAT_LESS_THAN(covarFrac, 1.0, false);
    153188    // Don't care about maskVal
    154189    // Don't care about maskBad
     
    164199}
    165200
     201
     202/// Allocate images, as required
     203static void subtractionMatchAlloc(pmReadout *conv1, pmReadout *conv2, // Output readouts
     204                                  const pmReadout *ro1, const pmReadout *ro2, // Input readouts
     205                                  const psImage *subMask,                     // Subtraction mask
     206                                  psImageMaskType maskBad,                    // Mask value for bad pixels
     207                                  pmSubtractionMode subMode,          // Subtraction mode
     208                                  int numCols, int numRows            // Size of image
     209    )
     210{
     211    if (subMode == PM_SUBTRACTION_MODE_1 || subMode == PM_SUBTRACTION_MODE_UNSURE ||
     212        subMode == PM_SUBTRACTION_MODE_DUAL) {
     213        if (!conv1->image) {
     214            conv1->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     215        }
     216        psImageInit(conv1->image, NAN);
     217        if (ro1->variance) {
     218            if (!conv1->variance) {
     219                conv1->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     220            }
     221            psImageInit(conv1->variance, NAN);
     222        }
     223        if (subMask) {
     224            if (!conv1->mask) {
     225                conv1->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     226            }
     227            psImageInit(conv1->mask, maskBad);
     228        }
     229    }
     230    if (subMode == PM_SUBTRACTION_MODE_2 || subMode == PM_SUBTRACTION_MODE_UNSURE ||
     231        subMode == PM_SUBTRACTION_MODE_DUAL) {
     232        if (!conv2->image) {
     233            conv2->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     234        }
     235        psImageInit(conv2->image, NAN);
     236        if (ro2->variance) {
     237            if (!conv2->variance) {
     238                conv2->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     239            }
     240            psImageInit(conv2->variance, NAN);
     241        }
     242        if (subMask) {
     243            if (!conv2->mask) {
     244                conv2->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     245            }
     246            psImageInit(conv2->mask, maskBad);
     247        }
     248    }
     249
     250    return;
     251}
     252
     253
    166254static void subtractionAnalysisUpdate(pmReadout *conv1, pmReadout *conv2, // Convolved images
    167255                                      const psMetadata *analysis, // Analysis metadata
     
    192280}
    193281
     282bool pmSubtractionMaskInvalid (const pmReadout *readout, psImageMaskType maskVal) {
     283
     284    if (!readout) return true;
     285
     286    psImage *image = readout->image;
     287    psImage *mask  = readout->mask;
     288    psImage *variance = readout->variance;
     289    for (int y = 0; y < image->numRows; y++) {
     290        for (int x = 0; x < image->numCols; x++) {
     291            if (mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) continue;
     292            bool valid = false;
     293            valid = isfinite(image->data.F32[y][x]);
     294            if (variance) {
     295                valid &= isfinite(variance->data.F32[y][x]);
     296            }
     297            if (valid) continue;
     298            mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = maskVal;
     299        }
     300    }
     301
     302    return true;
     303}
    194304
    195305bool pmSubtractionMatchPrecalc(pmReadout *conv1, pmReadout *conv2, const pmReadout *ro1, const pmReadout *ro2,
    196                                psMetadata *analysis, int stride, float sysError,
     306                               psMetadata *analysis, int stride, float kernelError, float covarFrac,
    197307                               psImageMaskType maskVal, psImageMaskType maskBad, psImageMaskType maskPoor,
    198308                               float poorFrac, float badFrac)
     
    210320        while ((item = psMetadataGetAndIncrement(iter))) {
    211321            if (item->type != PS_DATA_UNKNOWN) {
    212                 psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unexpected type for kernel.");
     322                psError(PM_ERR_PROG, true, "Unexpected type for kernel.");
    213323                psFree(iter);
    214324                psFree(kernelList);
     
    230340    }
    231341    if (psListLength(kernelList) == 0) {
    232         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find kernels");
     342        psError(PM_ERR_PROG, true, "Unable to find kernels");
    233343        psFree(kernelList);
    234344        return false;
     
    245355        while ((item = psMetadataGetAndIncrement(iter))) {
    246356            if (item->type != PS_DATA_REGION) {
    247                 psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unexpected type for region.");
     357                psError(PM_ERR_PROG, true, "Unexpected type for region.");
    248358                psFree(iter);
    249359                psFree(kernels);
     
    257367    }
    258368    if (regions->n != kernels->n) {
    259         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Differing number of kernels (%ld) and regions (%ld)",
     369        psError(PM_ERR_PROG, true, "Differing number of kernels (%ld) and regions (%ld)",
    260370                kernels->n, regions->n);
    261371        psFree(regions);
     
    264374    }
    265375
    266     if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, sysError, maskVal, maskBad, maskPoor,
    267                                poorFrac, badFrac, mode)) {
     376    if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, NAN, NAN, NAN, kernelError, covarFrac,
     377                               maskVal, maskBad, maskPoor, poorFrac, badFrac, mode)) {
    268378        psFree(kernels);
    269379        psFree(regions);
     
    271381    }
    272382
    273     psImage *subMask = pmSubtractionMask(ro1->mask, ro2 ? ro2->mask : NULL, maskVal, size, 0,
     383    int numCols, numRows;       // Size of image
     384    if (ro1) {
     385        numCols = ro1->image->numCols;
     386        numRows = ro1->image->numRows;
     387    } else if (ro2) {
     388        numCols = ro2->image->numCols;
     389        numRows = ro2->image->numRows;
     390    } else {
     391        psAbort("No input image provided.");
     392    }
     393
     394    pmSubtractionMaskInvalid(ro1, maskVal);
     395    pmSubtractionMaskInvalid(ro2, maskVal);
     396
     397    // General background subtraction, since this is done in pmSubtractionMatch
     398    {
     399        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
     400        psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for background
     401        if (ro1) {
     402            psStatsInit(bg);
     403            if (!psImageBackground(bg, NULL, ro1->image, ro1->mask, maskVal, rng)) {
     404                psError(PM_ERR_DATA, false, "Unable to measure background statistics.");
     405                psFree(bg);
     406                psFree(rng);
     407                return false;
     408            }
     409            psBinaryOp(ro1->image, ro1->image, "-", psScalarAlloc((float)bg->robustMedian, PS_TYPE_F32));
     410        }
     411        if (ro2) {
     412            psStatsInit(bg);
     413            if (!psImageBackground(bg, NULL, ro2->image, ro2->mask, maskVal, rng)) {
     414                psError(PM_ERR_DATA, false, "Unable to measure background statistics.");
     415                psFree(bg);
     416                psFree(rng);
     417                return false;
     418            }
     419            psBinaryOp(ro2->image, ro2->image, "-", psScalarAlloc((float)bg->robustMedian, PS_TYPE_F32));
     420        }
     421        psFree(bg);
     422        psFree(rng);
     423    }
     424
     425    psRegion bounds = psRegionSet(NAN, NAN, NAN, NAN); // Bounds of valid pixels
     426
     427    psImage *subMask = pmSubtractionMask(&bounds, ro1, ro2, maskVal, size, 0,
    274428                                         badFrac, mode); // Subtraction mask
    275429    if (!subMask) {
     
    283437    psMetadata *outHeader = psMetadataAlloc(); // Output header values
    284438
     439    subtractionMatchAlloc(conv1, conv2, ro1, ro2, subMask, maskBad, mode, numCols, numRows);
     440
    285441    psTrace("psModules.imcombine", 2, "Convolving...\n");
    286442    for (int i = 0; i < kernels->n; i++) {
     
    288444        psRegion *region = regions->data[i]; // Region of interest
    289445
    290         if (!pmSubtractionAnalysis(outAnalysis, outHeader, kernel, region,
    291                                    ro1->image->numCols, ro1->image->numRows)) {
    292             psError(PS_ERR_UNKNOWN, false, "Unable to generate QA data");
     446        if (!pmSubtractionAnalysis(outAnalysis, outHeader, kernel, region, numCols, numRows)) {
     447            psError(psErrorCodeLast(), false, "Unable to generate QA data");
    293448            psFree(outAnalysis);
    294449            psFree(outHeader);
     
    300455
    301456        if (!pmSubtractionConvolve(conv1, conv2, ro1, ro2, subMask, stride, maskBad, maskPoor, poorFrac,
    302                                    sysError, region, kernel, true, useFFT)) {
    303             psError(PS_ERR_UNKNOWN, false, "Unable to convolve image.");
     457                                   kernelError, covarFrac, region, kernel, true, useFFT)) {
     458            psError(psErrorCodeLast(), false, "Unable to convolve image.");
    304459            psFree(outAnalysis);
    305460            psFree(outHeader);
     
    330485                        int inner, int ringsOrder, int binning, float penalty,
    331486                        bool optimum, const psVector *optFWHMs, int optOrder, float optThreshold,
    332                         int iter, float rej, float sysError, psImageMaskType maskVal, psImageMaskType maskBad,
     487                        int iter, float rej, float normFrac, float sysError, float skyError,
     488                        float kernelError, float covarFrac, psImageMaskType maskVal, psImageMaskType maskBad,
    333489                        psImageMaskType maskPoor, float poorFrac, float badFrac, pmSubtractionMode subMode)
    334490{
    335     if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, sysError, maskVal, maskBad, maskPoor,
    336                                poorFrac, badFrac, subMode)) {
     491    if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, normFrac, sysError, skyError, kernelError,
     492                               covarFrac, maskVal, maskBad, maskPoor, poorFrac, badFrac, subMode)) {
    337493        return false;
    338494    }
     495
     496    // We need both inputs
     497    PM_ASSERT_READOUT_NON_NULL(ro1, false);
     498    PM_ASSERT_READOUT_NON_NULL(ro2, false);
    339499
    340500    PS_ASSERT_INT_NONNEGATIVE(footprint, false);
     
    392552    // Putting important variable declarations here, since they are freed after a "goto" if there is an error.
    393553    psImage *subMask = NULL;            // Mask for subtraction
    394     psRegion *region = NULL;            // Iso-kernel region
     554    psRegion *region = psRegionAlloc(NAN, NAN, NAN, NAN); // Iso-kernel region
    395555    psString regionString = NULL;       // String for region
    396556    pmSubtractionStampList *stamps = NULL; // Stamps for matching PSF
     
    405565    memCheck("start");
    406566
    407     subMask = pmSubtractionMask(ro1->mask, ro2 ? ro2->mask : NULL, maskVal, size, footprint,
    408                                 badFrac, subMode);
     567    pmSubtractionMaskInvalid(ro1, maskVal);
     568    pmSubtractionMaskInvalid(ro2, maskVal);
     569
     570    psRegion bounds = psRegionSet(NAN, NAN, NAN, NAN); // Bounds of valid pixels
     571
     572    subMask = pmSubtractionMask(&bounds, ro1, ro2, maskVal, size, footprint, badFrac, subMode);
    409573    if (!subMask) {
    410574        psError(psErrorCodeLast(), false, "Unable to generate subtraction mask.");
     
    416580    // Get region of interest
    417581    int xRegions = 1, yRegions = 1;     // Number of iso-kernel regions
    418     float xRegionSize = 0, yRegionSize = 0; // Size of iso-kernel regions
     582    float xRegionSize = NAN, yRegionSize = NAN; // Size of iso-kernel regions
    419583    if (isfinite(regionSize) && regionSize != 0.0) {
    420         xRegions = numCols / regionSize + 1;
    421         yRegions = numRows / regionSize + 1;
    422         xRegionSize = (float)numCols / (float)xRegions;
    423         yRegionSize = (float)numRows / (float)yRegions;
    424         region = psRegionAlloc(NAN, NAN, NAN, NAN);
    425     }
    426 
     584        xRegions = (bounds.x1 - bounds.x0) / regionSize + 1;
     585        yRegions = (bounds.y1 - bounds.y0) / regionSize + 1;
     586        xRegionSize = (float)(bounds.x1 - bounds.x0) / (float)xRegions;
     587        yRegionSize = (float)(bounds.y1 - bounds.y0) / (float)yRegions;
     588    } else {
     589        xRegionSize = bounds.x1 - bounds.x0;
     590        yRegionSize = bounds.y1 - bounds.y0;
     591    }
     592
     593    // General background subtraction and measurement of stamp threshold
    427594    float stampThresh1 = NAN, stampThresh2 = NAN; // Stamp thresholds for images
    428595    {
    429         psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for backgroun
     596        psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for background
    430597        if (ro1) {
     598            psStatsInit(bg);
    431599            if (!psImageBackground(bg, NULL, ro1->image, ro1->mask, maskVal, rng)) {
    432                 psError(PS_ERR_UNKNOWN, false, "Unable to measure background statistics.");
     600                psError(PM_ERR_DATA, false, "Unable to measure background statistics.");
    433601                psFree(bg);
    434602                goto MATCH_ERROR;
    435603            }
    436             stampThresh1 = bg->robustMedian + threshold * bg->robustStdev;
     604            stampThresh1 = threshold * bg->robustStdev;
     605            psBinaryOp(ro1->image, ro1->image, "-", psScalarAlloc((float)bg->robustMedian, PS_TYPE_F32));
    437606        }
    438607        if (ro2) {
     608            psStatsInit(bg);
    439609            if (!psImageBackground(bg, NULL, ro2->image, ro2->mask, maskVal, rng)) {
    440                 psError(PS_ERR_UNKNOWN, false, "Unable to measure background statistics.");
     610                psError(PM_ERR_DATA, false, "Unable to measure background statistics.");
    441611                psFree(bg);
    442612                goto MATCH_ERROR;
    443613            }
    444             stampThresh2 = bg->robustMedian + threshold * bg->robustStdev;
     614            stampThresh2 = threshold * bg->robustStdev;
     615            psBinaryOp(ro2->image, ro2->image, "-", psScalarAlloc((float)bg->robustMedian, PS_TYPE_F32));
    445616        }
    446617        psFree(bg);
    447618    }
     619
     620    subtractionMatchAlloc(conv1, conv2, ro1, ro2, subMask, maskBad, subMode, numCols, numRows);
    448621
    449622    // Iterate over iso-kernel regions
     
    452625            psTrace("psModules.imcombine", 1, "Subtracting region %d of %d...\n",
    453626                    j * xRegions + i + 1, xRegions * yRegions);
    454             if (region) {
    455                 *region = psRegionSet((int)(i * xRegionSize), (int)((i + 1) * xRegionSize),
    456                                       (int)(j * yRegionSize), (int)((j + 1) * yRegionSize));
    457                 psFree(regionString);
    458                 regionString = psRegionToString(*region);
    459                 psTrace("psModules.imcombine", 3, "Iso-kernel region: %s out of %d,%d\n",
    460                         regionString, numCols, numRows);
    461             }
     627            *region = psRegionSet(bounds.x0 + (int)(i * xRegionSize),
     628                                  bounds.x0 + (int)((i + 1) * xRegionSize),
     629                                  bounds.y0 + (int)(j * yRegionSize),
     630                                  bounds.y0 + (int)((j + 1) * yRegionSize));
     631            psFree(regionString);
     632            regionString = psRegionToString(*region);
     633            psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "Iso-kernel region: %s out of %d,%d\n",
     634                    regionString, numCols, numRows);
    462635
    463636            if (stampsName && strlen(stampsName) > 0) {
    464637                stamps = pmSubtractionStampsSetFromFile(stampsName, ro1->image, subMask, region, size,
    465                                                         footprint, stampSpacing, subMode);
     638                                                        footprint, stampSpacing, normFrac,
     639                                                        sysError, skyError, subMode);
    466640            } else if (sources) {
    467641                stamps = pmSubtractionStampsSetFromSources(sources, ro1->image, subMask, region, size,
    468                                                            footprint, stampSpacing, subMode);
     642                                                           footprint, stampSpacing, normFrac,
     643                                                           sysError, skyError, subMode);
    469644            }
    470645
    471646            // We get the stamps here; we will also attempt to get stamps at the first iteration, but it
    472647            // doesn't matter.
    473             if (!subtractionGetStamps(&stamps, ro1, ro2, subMask, variance, NULL, stampThresh1, stampThresh2,
    474                            stampSpacing, size, footprint, subMode)) {
     648            if (!subtractionGetStamps(&stamps, ro1, ro2, subMask, variance, region, stampThresh1, stampThresh2,
     649                                      stampSpacing, normFrac, sysError, skyError, size, footprint, subMode)) {
    475650                goto MATCH_ERROR;
    476651            }
    477652
     653
     654            // generate the window function from the set of stamps
     655            if (!pmSubtractionStampsGetWindow(stamps, size)) {
     656                psError(psErrorCodeLast(), false, "Unable to get stamp window.");
     657                goto MATCH_ERROR;
     658            }
     659
     660            // Define kernel basis functions
     661            if (optimum && (type == PM_SUBTRACTION_KERNEL_ISIS || type == PM_SUBTRACTION_KERNEL_GUNK)) {
     662                kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder,
     663                                                          optFWHMs, optOrder, stamps, footprint,
     664                                                          optThreshold, penalty, bounds, subMode);
     665                if (!kernels) {
     666                    psErrorClear();
     667                    psWarning("Unable to derive optimum ISIS kernel --- switching to default.");
     668                }
     669            }
     670            if (kernels == NULL) {
     671                // Not an ISIS/GUNK kernel, or the optimum kernel search failed
     672                kernels = pmSubtractionKernelsGenerate(type, size, spatialOrder, isisWidths, isisOrders,
     673                                                       inner, binning, ringsOrder, penalty, bounds, subMode);
     674                // pmSubtractionVisualShowKernels(kernels);
     675            }
     676
     677            memCheck("kernels");
     678
    478679            if (subMode == PM_SUBTRACTION_MODE_UNSURE) {
     680#if 0
    479681                // Get backgrounds
    480682                psStats *bgStats = psStatsAlloc(BG_STAT); // Statistics for background
    481683                psVector *buffer = NULL;// Buffer for stats
    482684                if (!psImageBackground(bgStats, &buffer, ro1->image, ro1->mask, maskVal, rng)) {
    483                     psError(PS_ERR_UNKNOWN, false, "Unable to measure background of image 1.");
     685                    psError(PM_ERR_DATA, false, "Unable to measure background of image 1.");
    484686                    psFree(bgStats);
    485687                    psFree(buffer);
     
    488690                float bg1 = psStatsGetValue(bgStats, BG_STAT); // Background for image 1
    489691                if (!psImageBackground(bgStats, &buffer, ro2->image, ro2->mask, maskVal, rng)) {
    490                     psError(PS_ERR_UNKNOWN, false, "Unable to measure background of image 2.");
     692                    psError(PM_ERR_DATA, false, "Unable to measure background of image 2.");
    491693                    psFree(bgStats);
    492694                    psFree(buffer);
     
    498700
    499701                pmSubtractionMode newMode = pmSubtractionOrder(stamps, bg1, bg2); // Subtraction mode to use
     702#endif
     703                pmSubtractionMode newMode = pmSubtractionBestMode(&stamps, &kernels, subMask, rej);
    500704                switch (newMode) {
    501705                  case PM_SUBTRACTION_MODE_1:
     
    506710                    break;
    507711                  default:
    508                     psError(PS_ERR_UNKNOWN, false, "Unable to determine subtraction order.");
     712                    psError(psErrorCodeLast(), false, "Unable to determine subtraction order.");
    509713                    goto MATCH_ERROR;
    510714                }
    511715                subMode = newMode;
    512716            }
    513 
    514             // Define kernel basis functions
    515             if (optimum && (type == PM_SUBTRACTION_KERNEL_ISIS || type == PM_SUBTRACTION_KERNEL_GUNK)) {
    516                 kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder, optFWHMs, optOrder,
    517                                                           stamps, footprint, optThreshold, penalty, subMode);
    518                 if (!kernels) {
    519                     psErrorClear();
    520                     psWarning("Unable to derive optimum ISIS kernel --- switching to default.");
    521                 }
    522             }
    523             if (kernels == NULL) {
    524                 // Not an ISIS/GUNK kernel, or the optimum kernel search failed
    525                 kernels = pmSubtractionKernelsGenerate(type, size, spatialOrder, isisWidths, isisOrders,
    526                                                        inner, binning, ringsOrder, penalty, subMode);
    527             }
    528 
    529             memCheck("kernels");
    530717
    531718            int numRejected = -1;       // Number of rejected stamps in each iteration
     
    534721
    535722                if (!subtractionGetStamps(&stamps, ro1, ro2, subMask, variance, region,
    536                                           stampThresh1, stampThresh2, stampSpacing,
    537                                           size, footprint, subMode)) {
    538                     goto MATCH_ERROR;
    539                 }
    540 
    541                 psTrace("psModules.imcombine", 3, "Calculating equation...\n");
    542                 if (!pmSubtractionCalculateEquation(stamps, kernels)) {
    543                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    544                     goto MATCH_ERROR;
    545                 }
    546 
     723                                          stampThresh1, stampThresh2, stampSpacing, normFrac,
     724                                          sysError, skyError, size, footprint, subMode)) {
     725                    goto MATCH_ERROR;
     726                }
     727
     728                // generate the window function from the set of stamps
     729                if (!pmSubtractionStampsGetWindow(stamps, size)) {
     730                    psError(psErrorCodeLast(), false, "Unable to get stamps window.");
     731                    goto MATCH_ERROR;
     732                }
     733
     734                // XXX step 1: calculate normalization
     735                psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
     736                if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
     737                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     738                    goto MATCH_ERROR;
     739                }
     740
     741                psTrace("psModules.imcombine", 3, "Solving equation for normalization...\n");
     742                if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
     743                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     744                    goto MATCH_ERROR;
     745                }
     746                memCheck("  solve equation");
     747
     748# if (SEPARATE)
     749                // set USED -> CALCULATE
     750                pmSubtractionStampsResetStatus (stamps);
     751
     752                // XXX step 2: calculate kernel parameters
     753                psTrace("psModules.imcombine", 3, "Calculating equation for kernels...\n");
     754                if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
     755                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     756                    goto MATCH_ERROR;
     757                }
    547758                memCheck("  calculate equation");
    548759
    549                 psTrace("psModules.imcombine", 3, "Solving equation...\n");
    550 
    551                 if (!pmSubtractionSolveEquation(kernels, stamps)) {
    552                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    553                     goto MATCH_ERROR;
    554                 }
    555 
     760                psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
     761                if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
     762                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     763                    goto MATCH_ERROR;
     764                }
    556765                memCheck("  solve equation");
    557 
     766# endif
    558767                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    559768                if (!deviations) {
    560                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
     769                    psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
    561770                    goto MATCH_ERROR;
    562771                }
     
    565774
    566775                psTrace("psModules.imcombine", 3, "Rejecting stamps...\n");
    567                 numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, rej, footprint);
     776                numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, rej);
    568777                if (numRejected < 0) {
    569                     psError(PS_ERR_UNKNOWN, false, "Unable to reject stamps.");
     778                    psError(psErrorCodeLast(), false, "Unable to reject stamps.");
    570779                    psFree(deviations);
    571780                    goto MATCH_ERROR;
     
    576785            }
    577786
     787            // if we hit the max number of iterations and we have rejected stamps, re-solve
    578788            if (numRejected > 0) {
    579                 psTrace("psModules.imcombine", 3, "Solving equation...\n");
    580                 if (!pmSubtractionSolveEquation(kernels, stamps)) {
    581                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    582                     goto MATCH_ERROR;
    583                 }
     789                // XXX step 1: calculate normalization
     790                psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
     791                if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
     792                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     793                    goto MATCH_ERROR;
     794                }
     795
     796                // solve normalization
     797                psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
     798                if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
     799                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     800                    goto MATCH_ERROR;
     801                }
     802
     803# if (SEPARATE)
     804                // set USED -> CALCULATE
     805                pmSubtractionStampsResetStatus (stamps);
     806
     807                // XXX step 2: calculate kernel parameters
     808                psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
     809                if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
     810                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     811                    goto MATCH_ERROR;
     812                }
     813
     814                // solve kernel parameters
     815                psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
     816                if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
     817                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     818                    goto MATCH_ERROR;
     819                }
     820                memCheck("  solve equation");
     821# endif
    584822                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    585823                if (!deviations) {
    586                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
    587                     goto MATCH_ERROR;
    588                 }
    589                 pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, NAN, footprint);
     824                    psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
     825                    goto MATCH_ERROR;
     826                }
     827                pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, NAN);
    590828                psFree(deviations);
    591829            }
     
    596834
    597835            if (!pmSubtractionAnalysis(analysis, header, kernels, region, numCols, numRows)) {
    598                 psError(PS_ERR_UNKNOWN, false, "Unable to generate QA data");
     836                psError(psErrorCodeLast(), false, "Unable to generate QA data");
    599837                goto MATCH_ERROR;
    600838            }
     
    604842            psTrace("psModules.imcombine", 2, "Convolving...\n");
    605843            if (!pmSubtractionConvolve(conv1, conv2, ro1, ro2, subMask, stride, maskBad, maskPoor, poorFrac,
    606                                        sysError, region, kernels, true, useFFT)) {
    607                 psError(PS_ERR_UNKNOWN, false, "Unable to convolve image.");
     844                                       kernelError, covarFrac, region, kernels, true, useFFT)) {
     845                psError(psErrorCodeLast(), false, "Unable to convolve image.");
    608846                goto MATCH_ERROR;
    609847            }
     
    624862    variance = NULL;
    625863
    626     if (!pmSubtractionBorder(conv1->image, conv1->variance, conv1->mask, size, maskBad)) {
    627         psError(PS_ERR_UNKNOWN, false, "Unable to set border of convolved image.");
     864    if (conv1 && !pmSubtractionBorder(conv1->image, conv1->variance, conv1->mask, size, maskBad)) {
     865        psError(psErrorCodeLast(), false, "Unable to set border of convolved image.");
     866        goto MATCH_ERROR;
     867    }
     868    if (conv2 && !pmSubtractionBorder(conv2->image, conv2->variance, conv2->mask, size, maskBad)) {
     869        psError(psErrorCodeLast(), false, "Unable to set border of convolved image.");
    628870        goto MATCH_ERROR;
    629871    }
     
    8241066        } else {
    8251067            if (!pmSubtractionOrderStamp(ratios, mask, stamps, models, modelSums, i, bg1, bg2)) {
    826                 psError(PS_ERR_UNKNOWN, false, "Unable to measure PSF width for stamp %d", i);
     1068                psError(psErrorCodeLast(), false, "Unable to measure PSF width for stamp %d", i);
    8271069                psFree(models);
    8281070                psFree(modelSums);
     
    8351077
    8361078    if (!psThreadPoolWait(true)) {
    837         psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
     1079        psError(psErrorCodeLast(), false, "Error waiting for threads.");
    8381080        psFree(models);
    8391081        psFree(modelSums);
     
    8481090    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
    8491091    if (!psVectorStats(stats, ratios, NULL, mask, 0xff)) {
    850         psError(PS_ERR_UNKNOWN, false, "Unable to calculate statistics for moments ratio.");
     1092        psError(psErrorCodeLast(), false, "Unable to calculate statistics for moments ratio.");
    8511093        psFree(mask);
    8521094        psFree(ratios);
     
    8691111    return mode;
    8701112}
     1113
     1114
     1115// Test a subtraction mode by performing a single iteration
     1116static bool subtractionModeTest(pmSubtractionStampList *stamps, // Stamps to use to find best mode
     1117                                pmSubtractionKernels *kernels, // Kernel description
     1118                                const char *description, // Description for trace
     1119                                psImage *subMask,  // Subtraction mask
     1120                                float rej               // Rejection threshold
     1121                                )
     1122{
     1123    assert(stamps);
     1124    assert(kernels);
     1125
     1126    psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
     1127    if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
     1128        psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1129        return false;
     1130    }
     1131
     1132    psTrace("psModules.imcombine", 3, "Solving %s normalization equation...\n", description);
     1133    if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
     1134        psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1135        return false;
     1136    }
     1137
     1138# if (SEPARATE)
     1139    // set USED -> CALCULATE
     1140    pmSubtractionStampsResetStatus (stamps);
     1141
     1142    psTrace("psModules.imcombine", 3, "Calculating %s kernel coeffs equation...\n", description);
     1143    if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
     1144        psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1145        return false;
     1146    }
     1147
     1148    psTrace("psModules.imcombine", 3, "Solving %s kernel coeffs equation...\n", description);
     1149    if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
     1150        psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1151        return false;
     1152    }
     1153# endif
     1154
     1155    psTrace("psModules.imcombine", 3, "Calculate %s deviations...\n", description);
     1156    psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
     1157    if (!deviations) {
     1158        psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
     1159        return false;
     1160    }
     1161
     1162    psTrace("psModules.imcombine", 3, "Rejecting %s stamps...\n", description);
     1163    long numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, rej);
     1164    if (numRejected < 0) {
     1165        psError(psErrorCodeLast(), false, "Unable to reject stamps.");
     1166        psFree(deviations);
     1167        return false;
     1168    }
     1169    psFree(deviations);
     1170
     1171    if (numRejected > 0) {
     1172        // Allow re-fit with reduced stamps set
     1173        psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
     1174        if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_ALL)) {
     1175            psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1176            return false;
     1177        }
     1178
     1179        psTrace("psModules.imcombine", 3, "Resolving %s equation...\n", description);
     1180        if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_ALL)) {
     1181            psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1182            return false;
     1183        }
     1184        psTrace("psModules.imcombine", 3, "Recalculate %s deviations...\n", description);
     1185
     1186# if (SEPARATE)
     1187        // set USED -> CALCULATE
     1188        pmSubtractionStampsResetStatus (stamps);
     1189
     1190        psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
     1191        if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
     1192            psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1193            return false;
     1194        }
     1195
     1196        psTrace("psModules.imcombine", 3, "Resolving %s equation...\n", description);
     1197        if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
     1198            psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1199            return false;
     1200        }
     1201        psTrace("psModules.imcombine", 3, "Recalculate %s deviations...\n", description);
     1202# endif
     1203
     1204        psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
     1205        if (!deviations) {
     1206            psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
     1207            return false;
     1208        }
     1209        psTrace("psModules.imcombine", 3, "Measuring %s quality...\n", description);
     1210        long numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, NAN);
     1211        if (numRejected < 0) {
     1212            psError(psErrorCodeLast(), false, "Unable to reject stamps.");
     1213            psFree(deviations);
     1214            return false;
     1215        }
     1216        psFree(deviations);
     1217    }
     1218
     1219    return true;
     1220}
     1221
     1222
     1223pmSubtractionMode pmSubtractionBestMode(pmSubtractionStampList **stamps, pmSubtractionKernels **kernels,
     1224                                        const psImage *subMask, float rej)
     1225{
     1226    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(*stamps, PM_SUBTRACTION_MODE_ERR);
     1227    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(*kernels, PM_SUBTRACTION_MODE_ERR);
     1228
     1229    // Copies of the inputs
     1230    pmSubtractionStampList *stamps1 = pmSubtractionStampListCopy(*stamps);
     1231    pmSubtractionKernels *kernels1 = pmSubtractionKernelsCopy(*kernels);
     1232    psImage *subMask1 = psImageCopy(NULL, subMask, subMask->type.type);
     1233    kernels1->mode = PM_SUBTRACTION_MODE_1;
     1234
     1235    if (!subtractionModeTest(stamps1, kernels1, "convolve 1", subMask1, rej)) {
     1236        psError(psErrorCodeLast(), false, "Unable to test subtraction with convolution of image 1");
     1237        psFree(stamps1);
     1238        psFree(kernels1);
     1239        psFree(subMask1);
     1240        return PM_SUBTRACTION_MODE_ERR;
     1241    }
     1242    psFree(subMask1);
     1243
     1244    // Copies of the inputs
     1245    pmSubtractionStampList *stamps2 = pmSubtractionStampListCopy(*stamps);
     1246    pmSubtractionKernels *kernels2 = pmSubtractionKernelsCopy(*kernels);
     1247    psImage *subMask2 = psImageCopy(NULL, subMask, subMask->type.type);
     1248    kernels2->mode = PM_SUBTRACTION_MODE_2;
     1249
     1250    if (!subtractionModeTest(stamps2, kernels2, "convolve 2", subMask2, rej)) {
     1251        psError(psErrorCodeLast(), false, "Unable to test subtraction with convolution of image 2");
     1252        psFree(stamps2);
     1253        psFree(kernels2);
     1254        psFree(subMask2);
     1255        psFree(stamps1);
     1256        psFree(kernels1);
     1257        return PM_SUBTRACTION_MODE_ERR;
     1258    }
     1259    psFree(subMask2);
     1260
     1261
     1262    pmSubtractionStampList *bestStamps = NULL; // Best choice for stamps
     1263    pmSubtractionKernels *bestKernels = NULL; // Best choice for kernels
     1264    psLogMsg("psModules.imcombine", PS_LOG_INFO,
     1265             "Image 1: %f +/- %f from %d stamps\nImage 2: %f +/- %f from %d stamps\n",
     1266             kernels1->mean, kernels1->rms, kernels1->numStamps,
     1267             kernels2->mean, kernels2->rms, kernels2->numStamps);
     1268
     1269    if (kernels1->mean < kernels2->mean) {
     1270        bestStamps = stamps1;
     1271        bestKernels = kernels1;
     1272    } else {
     1273        bestStamps = stamps2;
     1274        bestKernels = kernels2;
     1275    }
     1276
     1277    psFree(*stamps);
     1278    psFree(*kernels);
     1279    *stamps = psMemIncrRefCounter(bestStamps);
     1280    *kernels = psMemIncrRefCounter(bestKernels);
     1281
     1282    psFree(stamps1);
     1283    psFree(stamps2);
     1284    psFree(kernels1);
     1285    psFree(kernels2);
     1286
     1287    return bestKernels->mode;
     1288}
     1289
     1290
     1291bool pmSubtractionParamsScale(int *kernelSize, int *stampSize, psVector *widths,
     1292                              float fwhm1, float fwhm2, float scaleRef, float scaleMin, float scaleMax)
     1293{
     1294    PS_ASSERT_PTR_NON_NULL(kernelSize, false);
     1295    PS_ASSERT_PTR_NON_NULL(stampSize, false);
     1296    PS_ASSERT_VECTOR_NON_NULL(widths, false);
     1297    PS_ASSERT_VECTOR_TYPE(widths, PS_TYPE_F32, false);
     1298    PS_ASSERT_FLOAT_LARGER_THAN(fwhm1, 0.0, false);
     1299    PS_ASSERT_FLOAT_LARGER_THAN(fwhm2, 0.0, false);
     1300    PS_ASSERT_FLOAT_LARGER_THAN(scaleRef, 0.0, false);
     1301    PS_ASSERT_FLOAT_LARGER_THAN(scaleMin, 0.0, false);
     1302    PS_ASSERT_FLOAT_LARGER_THAN(scaleMax, 0.0, false);
     1303    PS_ASSERT_FLOAT_LARGER_THAN(scaleMax, scaleMin, false);
     1304
     1305//    float diff = sqrtf(PS_SQR(PS_MAX(fwhm1, fwhm2)) - PS_SQR(PS_MIN(fwhm1, fwhm2))); // Difference
     1306    float scale = PS_MAX(fwhm1, fwhm2) / scaleRef;      // Scaling factor
     1307
     1308    if (isfinite(scaleMin) && scale < scaleMin) {
     1309        scale = scaleMin;
     1310    }
     1311    if (isfinite(scaleMax) && scale > scaleMax) {
     1312        scale = scaleMax;
     1313    }
     1314
     1315    for (int i = 0; i < widths->n; i++) {
     1316        widths->data.F32[i] *= scale;
     1317    }
     1318    *kernelSize = *kernelSize * scale + 0.5;
     1319    *stampSize = *stampSize * scale + 0.5;
     1320
     1321    psLogMsg("psModules.imcombine", PS_LOG_INFO,
     1322             "Scaling kernel parameters by %f: %d %d", scale, *kernelSize, *stampSize);
     1323
     1324    return true;
     1325}
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionMatch.h

    r23308 r27840  
    3939                        int iter,       ///< Rejection iterations
    4040                        float rej,      ///< Rejection threshold
    41                         float sysError, ///< Relative systematic error
     41                        float normFrac, ///< Fraction of flux in window for normalisation window
     42                        float sysError, ///< Relative systematic error in images
     43                        float skyError, ///< Relative systematic error in images
     44                        float kernelError, ///< Relative systematic error in kernel
     45                        float covarFrac,   ///< Fraction for kernel truncation before covariance calculation
    4246                        psImageMaskType maskVal, ///< Value to mask for input
    4347                        psImageMaskType maskBad, ///< Mask for output bad pixels
     
    5559                               psMetadata *analysis, ///< Analysis metadata with pre-calculated kernel, region
    5660                               int stride, ///< Size for convolution patches
    57                                float sysError, ///< Relative systematic error
     61                               float kernelError, ///< Relative systematic error in kernel
     62                               float covarFrac,   ///< Fraction for kernel truncation before covariance calc.
    5863                               psImageMaskType maskVal, ///< Value to mask for input
    5964                               psImageMaskType maskBad, ///< Mask for output bad pixels
     
    8388    );
    8489
     90/// Determine best subtraction mode to use
     91///
     92/// Subtractions are attempted each way, and the mode with the lower residual is taken to be the best
     93pmSubtractionMode pmSubtractionBestMode(
     94    pmSubtractionStampList **stamps,    ///< Stamps to use for solution
     95    pmSubtractionKernels **kernels,     ///< Kernels to use for solution
     96    const psImage *subMask,             ///< Subtraction mask
     97    float rej                           ///< Rejection threshold for stamps
     98    );
     99
     100
     101/// Scale subtraction parameters according to the FWHMs of the inputs
     102bool pmSubtractionParamsScale(
     103    int *kernelSize,                    ///< Half-size of the kernel
     104    int *stampSize,                     ///< Half-size of the stamp (footprint)
     105    psVector *widths,                   ///< ISIS widths
     106    float fwhm1, float fwhm2,           ///< FWHMs for inputs
     107    float scaleRef,                     ///< Reference width for scaling
     108    float scaleMin,                     ///< Minimum scaling ratio, or NAN
     109    float scaleMax                      ///< Maximum scaling ratio, or NAN
     110    );
     111
    85112
    86113#endif
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionParams.c

    r21363 r27840  
    88#include <pslib.h>
    99
     10#include "pmErrorCodes.h"
    1011#include "pmSubtractionStamps.h"
    1112#include "pmSubtraction.h"
     
    7172                            double *sumII, // Sum of I(x)^2/sigma(x)^2
    7273                            double *sumIC, // Sum of I(x)conv(x)/sigma(x)^2
    73                             const pmSubtractionStamp *stamp, // Stamp with variance
     74                            const pmSubtractionStamp *stamp, // Stamp
    7475                            const psKernel *target, // Target stamp
    7576                            int kernelIndex, // Index for kernel component
     
    7879    )
    7980{
    80     psKernel *variance = stamp->variance;   // Variance, sigma(x)^2
     81    psKernel *weight = stamp->weight;   // Weight image
    8182    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
    8283
    8384    for (int y = -footprint; y <= footprint; y++) {
    8485        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
    85         psF32 *wt = &variance->kernel[y][-footprint]; // Dereference variance
     86        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
    8687        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
    8788        for (int x = -footprint; x <= footprint; x++, in++, wt++, conv++) {
    88             double temp = *in / *wt; // Temporary product
     89            double temp = *in * *wt; // Temporary product
    8990            *sumI += temp;
    9091            *sumII += *in * temp;
     
    9899static void accumulateConvolutions(double *sumC, // Sum of conv(x)/sigma(x)^2
    99100                                   double *sumCC, // Sum of conv(x)^2/sigma(x)^2
    100                                    const pmSubtractionStamp *stamp, // Stamp with input and variance
     101                                   const pmSubtractionStamp *stamp, // Stamp with input and weight
    101102                                   int kernelIndex, // Index for kernel component
    102103                                   int footprint, // Size of region of interest
     
    104105    )
    105106{
    106     psKernel *variance = stamp->variance;   // Variance, sigma(x)^2
     107    psKernel *weight = stamp->weight;   // Weight image
    107108    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
    108109
    109110    for (int y = -footprint; y <= footprint; y++) {
    110         psF32 *wt = &variance->kernel[y][-footprint]; // Dereference variance
     111        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
    111112        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
    112113        for (int x = -footprint; x <= footprint; x++, wt++, conv++) {
    113             double convNoise = *conv / *wt; // Temporary product
     114            double convNoise = *conv * *wt; // Temporary product
    114115            *sumC += convNoise;
    115116            *sumCC += *conv * convNoise;
     
    120121
    121122static double accumulateChi2(const psKernel *target, // Target stamp
    122                              pmSubtractionStamp *stamp, // Stamp with variance
     123                             pmSubtractionStamp *stamp, // Stamp with weight
    123124                             int kernelIndex, // Index for kernel component
    124125                             double coeff, // Coefficient of convolution
     
    129130{
    130131    double chi2 = 0.0;
    131     psKernel *variance = stamp->variance;   // Variance, sigma(x)^2
     132    psKernel *weight = stamp->weight;   // Weight image
    132133    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
    133134
    134135    for (int y = -footprint; y <= footprint; y++) {
    135136        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
    136         psF32 *wt = &variance->kernel[y][-footprint]; // Dereference variance
     137        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
    137138        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
    138139        for (int x = -footprint; x <= footprint; x++, in++, wt++, conv++) {
    139             chi2 += PS_SQR(*in - bg - coeff * *conv) / *wt;
     140            chi2 += PS_SQR(*in - bg - coeff * *conv) * *wt;
    140141        }
    141142    }
     
    146147// Return the initial value of chi^2
    147148static double initialChi2(const psKernel *target, // Target stamp
    148                           const pmSubtractionStamp *stamp, // Stamp with variance
     149                          const pmSubtractionStamp *stamp, // Stamp
    149150                          int footprint, // Size of convolution
    150151                          pmSubtractionMode mode // Mode of subtraction
    151152    )
    152153{
    153     psKernel *variance = stamp->variance;   // Variance map
     154    psKernel *weight = stamp->weight;   // Weight image
    154155    psKernel *source;                   // Source stamp
    155156    switch (mode) {
     
    167168    for (int y = -footprint; y <= footprint; y++) {
    168169        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
    169         psF32 *wt = &variance->kernel[y][-footprint]; // Dereference variance
     170        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
    170171        psF32 *ref = &source->kernel[y][-footprint]; // Derference reference
    171172        for (int x = -footprint; x <= footprint; x++, in++, wt++, ref++) {
    172173            float diff = *in - *ref;    // Temporary value
    173             chi2 += PS_SQR(diff) / *wt;
     174            chi2 += PS_SQR(diff) * *wt;
    174175        }
    175176    }
     
    180181// Subtract a convolution from the input
    181182static void subtractConvolution(psKernel *target, // Target stamp
    182                                 const pmSubtractionStamp *stamp, // Stamp with variance
     183                                const pmSubtractionStamp *stamp, // Stamp
    183184                                int kernelIndex, // Index for kernel component
    184185                                float coeff, // Coefficient of subtraction
     
    204205                                                      int spatialOrder, const psVector *fwhms, int maxOrder,
    205206                                                      const pmSubtractionStampList *stamps, int footprint,
    206                                                       float tolerance, float penalty, pmSubtractionMode mode)
     207                                                      float tolerance, float penalty, psRegion bounds,
     208                                                      pmSubtractionMode mode)
    207209{
    208210    if (type != PM_SUBTRACTION_KERNEL_ISIS && type != PM_SUBTRACTION_KERNEL_GUNK) {
     
    232234    psVectorInit(orders, maxOrder);
    233235    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
    234                                                                   penalty, mode); // Kernels
     236                                                                  penalty, bounds, mode); // Kernels
    235237    psFree(orders);
    236238    psFree(kernels->description);
     
    280282        }
    281283        if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
    282             psError(PS_ERR_UNKNOWN, false, "Unable to convolve stamp %d.", i);
     284            psError(psErrorCodeLast(), false, "Unable to convolve stamp %d.", i);
    283285            psFree(targets);
    284286            psFree(kernels);
     
    288290
    289291        // This sum is invariant to the kernel
    290         psKernel *variance = stamp->variance; // Variance map for stamp
     292        psKernel *weight = stamp->weight; // Weight image
    291293        for (int v = -footprint; v <= footprint; v++) {
    292             psF32 *wt = &variance->kernel[v][-footprint]; // Dereference variance map
     294            psF32 *wt = &weight->kernel[v][-footprint]; // Dereference weight
    293295            for (int u = -footprint; u <= footprint; u++, wt++) {
    294                 sum1 += 1.0 / *wt;
     296                sum1 += 1.0 * *wt;
    295297            }
    296298        }
    297299        if (!isfinite(sum1)) {
    298             psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     300            psError(PM_ERR_DATA, true,
    299301                    "Sum of 1/sigma^2 is non-finite for stamp %d (%d,%d)\n",
    300302                    i, (int)stamp->x, (int)stamp->y);
     
    368370
    369371        if (bestIndex == -1) {
    370             psError(PS_ERR_UNKNOWN, false, "Unable to find best kernel component in round %d.", iter);
     372            psError(PM_ERR_DATA, true, "Unable to find best kernel component in round %d.", iter);
    371373            psFree(targets);
    372374            psFree(sumC);
     
    407409
    408410    if (cutIndex < 0) {
    409         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to converge to tolerance %g\n", tolerance);
     411        psError(PM_ERR_DATA, true, "Unable to converge to tolerance %g\n", tolerance);
    410412        psFree(ranking);
    411413        psFree(kernels);
     
    482484    // Maintain photometric scaling
    483485    if (type == PM_SUBTRACTION_KERNEL_ISIS) {
    484         psKernel *subtract = kernels->preCalc->data[0]; // Kernel to subtract from the rest
     486
     487        // XXX in r26035, this code was just wrong.  we had:
     488
     489        // psKernel *subtract = kernels->preCalc->data[0]
     490
     491        // but, kernels->preCalc was an array of psArray, not an array of kernels.  It is now
     492        // an array of pmSubtractionKernelPreCalc.
     493
     494        pmSubtractionKernelPreCalc *subtract = kernels->preCalc->data[0]; // Kernel to subtract from the rest
     495
    485496        for (int i = 1; i < newSize; i++) {
    486497            if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
    487                 psKernel *kernel = kernels->preCalc->data[i]; // Kernel of interest
    488                 psBinaryOp(kernel->image, kernel->image, "-", subtract->image);
     498                pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[i]; // Kernel of interest
     499                psBinaryOp(preCalc->kernel->image, preCalc->kernel->image, "-", subtract->kernel->image);
    489500            }
    490501        }
     
    495506        for (int i = 0; i < newSize; i++) {
    496507            if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
    497                 psKernel *kernel = kernels->preCalc->data[i]; // Kernel of interest
    498                 kernel->kernel[0][0] -= 1.0;
     508                pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[i]; // Kernel of interest
     509                preCalc->kernel->kernel[0][0] -= 1.0;
    499510            }
    500511        }
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionParams.h

    r18287 r27840  
    1717                                                      float tolerance, ///< Maximum difference in chi^2
    1818                                                      float penalty, ///< Penalty for wideness
    19                                                       pmSubtractionMode mode // Mode for subtraction
     19                                                      psRegion bounds,       ///< Bounds of validity
     20                                                      pmSubtractionMode mode ///< Mode for subtraction
    2021    );
    2122
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionStamps.c

    r24066 r27840  
    4646    psFree(list->y);
    4747    psFree(list->flux);
     48    psFree(list->window);
    4849}
    4950
     
    5455    psFree(stamp->image1);
    5556    psFree(stamp->image2);
    56     psFree(stamp->variance);
     57    psFree(stamp->weight);
    5758    psFree(stamp->convolutions1);
    5859    psFree(stamp->convolutions2);
    59 
    60     psFree(stamp->matrix1);
    61     psFree(stamp->matrix2);
    62     psFree(stamp->matrixX);
    63     psFree(stamp->vector1);
    64     psFree(stamp->vector2);
    65 
     60    psFree(stamp->matrix);
     61    psFree(stamp->vector);
    6662}
    6763
     
    8076
    8177// Search a region for a suitable stamp
    82 bool stampSearch(int *xStamp, int *yStamp, // Coordinates of stamp, to return
     78bool stampSearch(float *xStamp, float *yStamp, // Coordinates of stamp, to return
    8379                 float *fluxStamp, // Flux of stamp, to return
    8480                 const psImage *image1, const psImage *image2, // Images to search
     
    9389    *fluxStamp = -INFINITY;             // Flux of best stamp
    9490
     91    // fprintf (stderr, "xMin, xMax: %d, %d -> ", xMin, xMax);
     92
    9593    // Ensure we're not going to go outside the bounds of the image
    9694    xMin = PS_MAX(border, xMin);
     
    9997    yMax = PS_MIN(numRows - border - 1, yMax);
    10098
     99    if (xMax < xMin) return false;
     100    if (yMax < yMin) return false;
     101
     102    psAssert (xMin <= xMax, "x mismatch?");
     103    psAssert (yMin <= yMax, "y mismatch?");
     104
    101105    for (int y = yMin; y <= yMax; y++) {
    102106        for (int x = xMin; x <= xMax; x++) {
     
    115119                ((image1) ? image1->data.F32[y][x] : image2->data.F32[y][x]); // Flux at pixel
    116120            if (flux > *fluxStamp) {
    117                 *xStamp = x;
    118                 *yStamp = y;
     121                *xStamp = x + 0.5;
     122                *yStamp = y + 0.5;
    119123                *fluxStamp = flux;
    120124                found = true;
     
    180184
    181185pmSubtractionStampList *pmSubtractionStampListAlloc(int numCols, int numRows, const psRegion *region,
    182                                                     int footprint, float spacing)
     186                                                    int footprint, float spacing, float normFrac,
     187                                                    float sysErr, float skyErr)
    183188{
    184189    pmSubtractionStampList *list = psAlloc(sizeof(pmSubtractionStampList)); // Stamp list to return
     
    220225    list->y = NULL;
    221226    list->flux = NULL;
     227    list->window = NULL;
     228    list->normFrac = normFrac;
     229    list->normWindow = 0;
    222230    list->footprint = footprint;
     231    list->sysErr = sysErr;
     232    list->skyErr = skyErr;
    223233
    224234    return list;
     235}
     236
     237pmSubtractionStampList *pmSubtractionStampListCopy(const pmSubtractionStampList *in)
     238{
     239    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(in, NULL);
     240
     241    pmSubtractionStampList *out = psAlloc(sizeof(pmSubtractionStampList)); // Copied stamp list to return
     242    psMemSetDeallocator(out, (psFreeFunc)subtractionStampListFree);
     243
     244    int num = out->num = in->num;       // Number of stamps
     245    out->stamps = psArrayAlloc(num);
     246    out->regions = psArrayAlloc(num);
     247    out->x = NULL;
     248    out->y = NULL;
     249    out->flux = NULL;
     250    out->window = psMemIncrRefCounter(in->window);
     251    out->footprint = in->footprint;
     252    out->normWindow = in->normWindow;
     253
     254    for (int i = 0; i < num; i++) {
     255        psRegion *inRegion = in->regions->data[i]; // Input region
     256        out->regions->data[i] = psRegionAlloc(inRegion->x0, inRegion->x1, inRegion->y0, inRegion->y1);
     257
     258        pmSubtractionStamp *inStamp = in->stamps->data[i]; // Input stamp
     259        pmSubtractionStamp *outStamp = psAlloc(sizeof(pmSubtractionStamp));
     260        psMemSetDeallocator(outStamp, (psFreeFunc)subtractionStampFree);
     261        outStamp->x = inStamp->x;
     262        outStamp->y = inStamp->y;
     263        outStamp->flux = inStamp->flux;
     264        outStamp->xNorm = inStamp->xNorm;
     265        outStamp->yNorm = inStamp->yNorm;
     266        outStamp->status = inStamp->status;
     267
     268        outStamp->image1 = inStamp->image1 ? psKernelCopy(inStamp->image1) : NULL;
     269        outStamp->image2 = inStamp->image2 ? psKernelCopy(inStamp->image2) : NULL;
     270        outStamp->weight = inStamp->weight ? psKernelCopy(inStamp->weight) : NULL;
     271
     272        if (inStamp->convolutions1) {
     273            int size = inStamp->convolutions1->n; // Size of array
     274            outStamp->convolutions1 = psArrayAlloc(size);
     275            for (int j = 0; j < size; j++) {
     276                psKernel *conv = inStamp->convolutions1->data[j]; // Convolution
     277                outStamp->convolutions1->data[j] = conv ? psKernelCopy(conv) : NULL;
     278            }
     279        } else {
     280            outStamp->convolutions1 = NULL;
     281        }
     282        if (inStamp->convolutions2) {
     283            int size = inStamp->convolutions2->n; // Size of array
     284            outStamp->convolutions2 = psArrayAlloc(size);
     285            for (int j = 0; j < size; j++) {
     286                psKernel *conv = inStamp->convolutions2->data[j]; // Convolution
     287                outStamp->convolutions2->data[j] = conv ? psKernelCopy(conv) : NULL;
     288            }
     289        } else {
     290            outStamp->convolutions2 = NULL;
     291        }
     292
     293        outStamp->matrix = inStamp->matrix ? psImageCopy(NULL, inStamp->matrix, PS_TYPE_F64) : NULL;
     294        outStamp->vector = inStamp->vector ? psVectorCopy(NULL, inStamp->vector, PS_TYPE_F64) : NULL;
     295
     296        out->stamps->data[i] = outStamp;
     297    }
     298
     299    return out;
    225300}
    226301
     
    239314    stamp->image1 = NULL;
    240315    stamp->image2 = NULL;
    241     stamp->variance = NULL;
     316    stamp->weight = NULL;
    242317    stamp->convolutions1 = NULL;
    243318    stamp->convolutions2 = NULL;
    244319
    245     stamp->matrix1 = NULL;
    246     stamp->matrix2 = NULL;
    247     stamp->matrixX = NULL;
    248     stamp->vector1 = NULL;
    249     stamp->vector2 = NULL;
     320    stamp->matrix = NULL;
     321    stamp->vector = NULL;
     322    stamp->norm = NAN;
    250323
    251324    return stamp;
     
    256329                                                const psImage *image2, const psImage *subMask,
    257330                                                const psRegion *region, float thresh1, float thresh2,
    258                                                 int size, int footprint, float spacing,
    259                                                 pmSubtractionMode mode)
     331                                                int size, int footprint, float spacing, float normFrac,
     332                                                float sysErr, float skyErr, pmSubtractionMode mode)
    260333{
    261334    if (!image1 && !image2) {
     
    296369        if (psRegionIsNaN(*region)) {
    297370            psString string = psRegionToString(*region);
    298             psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Input region (%s) contains NAN values", string);
     371            psError(PM_ERR_PROG, true, "Input region (%s) contains NAN values", string);
    299372            psFree(string);
    300373            return false;
     
    303376            region->y0 < 0 || region->y1 > numRows) {
    304377            psString string = psRegionToString(*region);
    305             psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Input region (%s) does not fit in image (%dx%d)",
     378            psError(PM_ERR_PROG, true, "Input region (%s) does not fit in image (%dx%d)",
    306379                    string, numCols, numRows);
    307380            psFree(string);
     
    313386
    314387    if (!stamps) {
    315         stamps = pmSubtractionStampListAlloc(numCols, numRows, region, footprint, spacing);
     388        stamps = pmSubtractionStampListAlloc(numCols, numRows, region, footprint, spacing,
     389                                             normFrac, sysErr, skyErr);
    316390    }
    317391
     
    335409            numSearch++;
    336410
    337             int xStamp = 0, yStamp = 0; // Coordinates of stamp
     411            float xStamp = 0.0, yStamp = 0.0; // Coordinates of stamp
    338412            float fluxStamp = -INFINITY; // Flux of stamp
    339413            bool goodStamp = false;     // Found a good stamp?
     
    347421                // Take stamps off the top of the (sorted) list
    348422                for (int j = xList->n - 1; j >= 0 && !goodStamp; j--) {
    349                     int xCentre = xList->data.F32[j] + 0.5, yCentre = yList->data.F32[j] + 0.5;// Stamp centre
    350 
    351423                    // Chop off the top of the list
    352424                    xList->n = j;
     
    354426                    fluxList->n = j;
    355427
     428#if 0
    356429                    // Fish around a bit to see if we can find a pixel that isn't masked
     430                    // This is not a good idea if we're using the window feature
    357431                    psTrace("psModules.imcombine", 7, "Searching for stamp %d around %d,%d\n",
    358432                            i, xCentre, yCentre);
    359433
    360434                    // Search bounds
     435                    int xCentre = xList->data.F32[j] - 0.5, yCentre = yList->data.F32[j] - 0.5;// Stamp centre
    361436                    int search = footprint - size; // Search radius
    362437                    int xMin = PS_MAX(border, xCentre - search);
     
    367442                    goodStamp = stampSearch(&xStamp, &yStamp, &fluxStamp, image1, image2, thresh1, thresh2,
    368443                                            subMask, xMin, xMax, yMin, yMax, numCols, numRows, border);
     444                    // fprintf (stderr, "find: %d %d ==> %5.1f %5.1f (\n", xCentre, yCentre, xStamp, yStamp);
     445#else
     446                    // Only search the exact centre pixel
     447                    goodStamp = stampSearch(&xStamp, &yStamp, &fluxStamp, image1, image2, thresh1, thresh2,
     448                                            subMask, xList->data.F32[j], xList->data.F32[j],
     449                                            yList->data.F32[j], yList->data.F32[j], numCols, numRows, border);
     450#endif
     451                    if (0 && goodStamp) {
     452                        fprintf (stderr, "find: %6.1f < %6.1f < %6.1f, %6.1f < %6.1f %6.1f\n",
     453                                 region->x0 + size, xStamp, region->x1 - size,
     454                                 region->y0 + size, yStamp, region->y1 - size);
     455                    }
    369456                }
    370457            } else {
     
    386473                psFree(stamp->image1);
    387474                psFree(stamp->image2);
    388                 psFree(stamp->variance);
     475                psFree(stamp->weight);
    389476                psFree(stamp->convolutions1);
    390477                psFree(stamp->convolutions2);
    391                 stamp->image1 = stamp->image2 = stamp->variance = NULL;
     478                stamp->image1 = stamp->image2 = stamp->weight = NULL;
    392479                stamp->convolutions1 = stamp->convolutions2 = NULL;
    393480
     
    421508                                               const psImage *image, const psImage *subMask,
    422509                                               const psRegion *region, int size, int footprint,
    423                                                float spacing, pmSubtractionMode mode)
     510                                               float spacing, float normFrac, float sysErr, float skyErr,
     511                                               pmSubtractionMode mode)
    424512
    425513{
     
    441529    int numStars = x->n;                // Number of stars
    442530    pmSubtractionStampList *stamps = pmSubtractionStampListAlloc(subMask->numCols, subMask->numRows,
    443                                                                  region, footprint, spacing); // Stamp list
     531                                                                 region, footprint, spacing,
     532                                                                 normFrac, sysErr, skyErr); // Stamp list
    444533    int numStamps = stamps->num;        // Number of stamps
    445534
     
    448537    psStringAppend(&ds9name, "stamps_all_%d.ds9", ds9num);
    449538    FILE *ds9 = pmSubtractionStampsFile(stamps, ds9name, "all stamps"); // ds9 region file
     539    psFree(ds9name);
    450540    ds9num++;
    451541
     
    463553    for (int i = 0; i < numStars; i++) {
    464554        float xStamp = x->data.F32[i], yStamp = y->data.F32[i]; // Coordinates of stamp
    465         int xPix = xStamp + 0.5, yPix = yStamp + 0.5; // Pixel coordinate of stamp
     555        int xPix = xStamp - 0.5, yPix = yStamp - 0.5; // Pixel coordinate of stamp
    466556        if (!checkStampRegion(xPix, yPix, region)) {
    467557            // It's not in the big region
     
    471561            continue;
    472562        }
     563
     564        // fprintf (stderr, "stamp: %5.1f %5.1f == %d %d\n", xStamp, yStamp, xPix, yPix);
    473565
    474566        bool found = false;
     
    539631
    540632
     633bool pmSubtractionStampsGetWindow(pmSubtractionStampList *stamps, int kernelSize)
     634{
     635    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     636    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
     637
     638    int size = stamps->footprint; // Size of postage stamps
     639
     640    psFree (stamps->window);
     641    stamps->window = psKernelAlloc(-size, size, -size, size);
     642    psImageInit(stamps->window->image, 0.0);
     643
     644    // generate normalizations for each stamp
     645    psVector *norm1 = psVectorAlloc(stamps->num, PS_TYPE_F32);
     646    psVector *norm2 = psVectorAlloc(stamps->num, PS_TYPE_F32);
     647    for (int i = 0; i < stamps->num; i++) {
     648        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     649        if (!stamp) continue;
     650        if (!stamp->image1) continue;
     651        if (!stamp->image2) continue;
     652
     653        float sum1 = 0.0;
     654        float sum2 = 0.0;
     655        for (int y = -size; y <= size; y++) {
     656            for (int x = -size; x <= size; x++) {
     657                sum1 += stamp->image1->kernel[y][x];
     658                sum2 += stamp->image2->kernel[y][x];
     659            }
     660        }
     661        norm1->data.F32[i] = sum1;
     662        norm2->data.F32[i] = sum2;
     663    }
     664
     665    // storage vector for flux data
     666    psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
     667    psVector *flux1 = psVectorAllocEmpty(2*stamps->num, PS_TYPE_F32);
     668    psVector *flux2 = psVectorAllocEmpty(2*stamps->num, PS_TYPE_F32);
     669
     670    // generate the window pixels
     671    double sum = 0.0;                   // Sum inside the window
     672    float maxValue = 0.0;               // Maximum value, for normalisation
     673    for (int y = -size; y <= size; y++) {
     674        for (int x = -size; x <= size; x++) {
     675
     676            flux1->n = 0;
     677            flux2->n = 0;
     678            for (int i = 0; i < stamps->num; i++) {
     679                pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     680                if (!stamp) continue;
     681                if (!stamp->image1) continue;
     682                if (!stamp->image2) continue;
     683
     684                psVectorAppend(flux1, stamp->image1->kernel[y][x] / norm1->data.F32[i]);
     685                psVectorAppend(flux2, stamp->image2->kernel[y][x] / norm2->data.F32[i]);
     686            }
     687
     688            psStatsInit (stats);
     689            if (!psVectorStats (stats, flux1, NULL, NULL, 0)) {
     690                psAbort ("failed to generate stats");
     691            }
     692            float f1 = stats->robustMedian;
     693            psStatsInit (stats);
     694            if (!psVectorStats (stats, flux2, NULL, NULL, 0)) {
     695                psAbort ("failed to generate stats");
     696            }
     697            float f2 = stats->robustMedian;
     698
     699            stamps->window->kernel[y][x] = f1 + f2;
     700            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(size)) {
     701                sum += stamps->window->kernel[y][x];
     702            }
     703            maxValue = PS_MAX(maxValue, stamps->window->kernel[y][x]);
     704        }
     705    }
     706
     707    psTrace("psModules.imcombine", 3, "Window total: %f, threshold: %f\n",
     708            sum, (1.0 - stamps->normFrac) * sum);
     709    bool done = false;
     710    for (int radius = 1; radius <= size && !done; radius++) {
     711        double within = 0.0;
     712        for (int y = -radius; y <= radius; y++) {
     713            for (int x = -radius; x <= radius; x++) {
     714                if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(radius)) {
     715                    within += stamps->window->kernel[y][x];
     716                }
     717            }
     718        }
     719        psTrace("psModules.imcombine", 5, "Radius %d: %f\n", radius, within);
     720        if (within > (1.0 - stamps->normFrac) * sum) {
     721            stamps->normWindow = radius;
     722            done = true;
     723        }
     724    }
     725
     726    psTrace("psModules.imcombine", 3, "Normalisation window radius set to %d\n", stamps->normWindow);
     727    if (stamps->normWindow == 0 || stamps->normWindow >= size) {
     728        psError(PM_ERR_STAMPS, true, "Unable to determine normalisation window size.");
     729        return false;
     730    }
     731
     732    // re-normalize so chisquare values are sensible
     733    for (int y = -size; y <= size; y++) {
     734        for (int x = -size; x <= size; x++) {
     735            stamps->window->kernel[y][x] /= maxValue;
     736        }
     737    }
     738
     739#if 0
     740    {
     741        psFits *fits = psFitsOpen ("window.fits", "w");
     742        psFitsWriteImage (fits, NULL, stamps->window->image, 0, NULL);
     743        psFitsClose (fits);
     744    }
     745#endif
     746
     747    psFree (stats);
     748    psFree (flux1);
     749    psFree(flux2);
     750    psFree (norm1);
     751    psFree (norm2);
     752    return true;
     753}
     754
    541755bool pmSubtractionStampsExtract(pmSubtractionStampList *stamps, psImage *image1, psImage *image2,
    542                                 psImage *variance, int kernelSize)
     756                                psImage *variance, int kernelSize, psRegion bounds)
    543757{
    544758    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    550764        PS_ASSERT_IMAGE_TYPE(image2, PS_TYPE_F32, false);
    551765    }
    552     PS_ASSERT_IMAGE_NON_NULL(variance, false);
    553     PS_ASSERT_IMAGES_SIZE_EQUAL(variance, image1, false);
    554     PS_ASSERT_IMAGE_TYPE(variance, PS_TYPE_F32, false);
    555     PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    556 
    557     int numCols = image1->numCols, numRows = image1->numRows; // Size of images
     766    if (variance) {
     767        PS_ASSERT_IMAGE_NON_NULL(variance, false);
     768        PS_ASSERT_IMAGES_SIZE_EQUAL(variance, image1, false);
     769        PS_ASSERT_IMAGE_TYPE(variance, PS_TYPE_F32, false);
     770        PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
     771    }
     772
    558773    int size = kernelSize + stamps->footprint; // Size of postage stamps
    559774
     
    564779        }
    565780
    566         if (isnan(stamp->xNorm)) {
    567             stamp->xNorm = 2.0 * (stamp->x - (float)numCols/2.0) / (float)numCols;
    568         }
    569         if (isnan(stamp->yNorm)) {
    570             stamp->yNorm = 2.0 * (stamp->y - (float)numRows/2.0) / (float)numRows;
    571         }
    572 
    573         int x = stamp->x + 0.5, y = stamp->y + 0.5; // Stamp coordinates
    574         if (x < size || x > numCols - size || y < size || y > numRows - size) {
    575             psError(PS_ERR_UNKNOWN, false, "Stamp %d (%d,%d) is within the image border.\n", i, x, y);
     781        p_pmSubtractionPolynomialNormCoords(&stamp->xNorm, &stamp->yNorm, stamp->x, stamp->y,
     782                                            bounds.x0, bounds.x1, bounds.y0, bounds.y1);
     783
     784        int x = stamp->x - 0.5, y = stamp->y - 0.5; // Stamp coordinates
     785        // fprintf (stderr, "stamp: %5.1f %5.1f == %d %d (size: %d)\n", stamp->x, stamp->y, x, y, size);
     786
     787        if (x < bounds.x0 + size || x > bounds.x1 - size || y < bounds.y0 + size || y > bounds.y1 - size) {
     788            psError(PM_ERR_PROG, false, "Stamp %d (%d,%d) is within the region border.\n", i, x, y);
    576789            return false;
    577790        }
     
    580793        assert(stamp->image1 == NULL);
    581794        assert(stamp->image2 == NULL);
    582         assert(stamp->variance == NULL);
     795        assert(stamp->weight == NULL);
    583796
    584797        psRegion region = psRegionSet(x - size, x + size + 1, y - size, y + size + 1); // Region of interest
     
    594807        }
    595808
    596         psImage *wtSub = psImageSubset(variance, region); // Subimage with stamp
    597         stamp->variance = psKernelAllocFromImage(wtSub, size, size);
    598         psFree(wtSub);                  // Drop reference
     809        psKernel *weight = stamp->weight = psKernelAlloc(-size, size, -size, size); // Weight = 1/variance
     810        if (variance) {
     811            psImage *varSub = psImageSubset(variance, region); // Subimage with stamp
     812            psKernel *var = psKernelAllocFromImage(varSub, size, size); // Variance postage stamp
     813
     814            if ((isfinite(stamps->skyErr) && (stamps->skyErr > 0)) ||
     815                (isfinite(stamps->sysErr) && (stamps->sysErr > 0))) {
     816                float sysErr = 0.25 * PS_SQR(stamps->sysErr); // Systematic error
     817                float skyErr = stamps->skyErr;
     818                psKernel *image1 = stamp->image1, *image2 = stamp->image2; // Input images
     819                for (int y = -size; y <= size; y++) {
     820                    for (int x = -size; x <= size; x++) {
     821                        float additional = image1->kernel[y][x] + image2->kernel[y][x];
     822                        weight->kernel[y][x] = 1.0 / (skyErr + var->kernel[y][x] + sysErr * PS_SQR(additional));
     823                    }
     824                }
     825            } else {
     826                for (int y = -size; y <= size; y++) {
     827                    for (int x = -size; x <= size; x++) {
     828                        weight->kernel[y][x] = 1.0 / var->kernel[y][x];
     829                    }
     830                }
     831            }
     832            psFree(var);
     833            psFree(varSub);
     834        } else {
     835            psImageInit(weight->image, 1.0);
     836        }
    599837
    600838        stamp->status = PM_SUBTRACTION_STAMP_CALCULATE;
     
    607845                                                          const psImage *subMask, const psRegion *region,
    608846                                                          int size, int footprint, float spacing,
     847                                                          float normFrac, float sysErr, float skyErr,
    609848                                                          pmSubtractionMode mode)
    610849{
     
    630869            y->data.F32[numOut] = source->peak->yf;
    631870        }
     871        // fprintf (stderr, "stamp: %5.1f %5.1f\n", x->data.F32[numOut], y->data.F32[numOut]);
    632872        numOut++;
    633873    }
     
    636876
    637877    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, image, subMask, region, size,
    638                                                             footprint, spacing, mode); // Stamps to return
     878                                                            footprint, spacing, normFrac,
     879                                                            sysErr, skyErr, mode); // Stamps
    639880    psFree(x);
    640881    psFree(y);
    641882
    642883    if (!stamps) {
    643         psError(PS_ERR_UNKNOWN, false, "Unable to set stamps from sources.");
     884        psError(psErrorCodeLast(), false, "Unable to set stamps from sources.");
    644885    }
    645886
     
    650891pmSubtractionStampList *pmSubtractionStampsSetFromFile(const char *filename, const psImage *image,
    651892                                                       const psImage *subMask, const psRegion *region,
    652                                                        int size, int footprint, float spacing,
    653                                                        pmSubtractionMode mode)
     893                                                       int size, int footprint, float spacing, float normFrac,
     894                                                       float sysErr, float skyErr, pmSubtractionMode mode)
    654895{
    655896    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
     
    658899    psArray *data = psVectorsReadFromFile(filename, "%f %f");
    659900    if (!data) {
    660         psError(PS_ERR_IO, false, "Unable to read stamps file %s", filename);
     901        psError(psErrorCodeLast(), false, "Unable to read stamps file %s", filename);
    661902        return NULL;
    662903    }
     
    668909
    669910    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, image, subMask, region, size, footprint,
    670                                                             spacing, mode);
     911                                                            spacing, normFrac, sysErr, skyErr, mode);
    671912    psFree(data);
    672913
     
    674915
    675916}
     917
     918
     919bool pmSubtractionStampsResetStatus (pmSubtractionStampList *stamps) {
     920
     921    for (int i = 0; i < stamps->num; i++) {
     922        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     923        if (!stamp) continue;
     924        if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
     925        stamp->status = PM_SUBTRACTION_STAMP_CALCULATE;
     926    }
     927    return true;
     928}
     929
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionStamps.h

    r23937 r27840  
    2424    psArray *flux;                      ///< Fluxes for possible stamps (or NULL)
    2525    int footprint;                      ///< Half-size of stamps
     26    psKernel *window;                   ///< window function generated from ensemble of stamps
     27    float normFrac;                     ///< Fraction of flux in window for normalisation window
     28    int normWindow;                     ///< Size of window for measuring normalisation
     29    float sysErr;                       ///< Systematic error
     30    float skyErr;                       ///< increase effective readnoise
    2631} pmSubtractionStampList;
    2732
    2833/// Allocate a list of stamps
    29 pmSubtractionStampList *pmSubtractionStampListAlloc(int numCols, // Number of columns in image
    30                                                     int numRows, // Number of rows in image
    31                                                     const psRegion *region, // Region for stamps, or NULL
    32                                                     int footprint, // Half-size of stamps
    33                                                     float spacing // Rough average spacing between stamps
     34pmSubtractionStampList *pmSubtractionStampListAlloc(
     35    int numCols, // Number of columns in image
     36    int numRows, // Number of rows in image
     37    const psRegion *region, // Region for stamps, or NULL
     38    int footprint, // Half-size of stamps
     39    float spacing, // Rough average spacing between stamps
     40    float normFrac, // Fraction of flux in window for normalisation window
     41    float sysErr,  // Relative systematic error or NAN
     42    float skyErr  // Relative systematic error or NAN
    3443    );
    3544
     
    5362}
    5463
     64/// Copy a list of stamps
     65///
     66/// A deep copy is performed of the stamp list and the component stamps
     67pmSubtractionStampList *pmSubtractionStampListCopy(
     68    const pmSubtractionStampList *in    // Stamp list to copy
     69    );
     70
     71
    5572/// A stamp for image subtraction
    5673typedef struct {
     
    6077    psKernel *image1;                   ///< Reference image postage stamp
    6178    psKernel *image2;                   ///< Input image postage stamp
    62     psKernel *variance;                 ///< Variance image postage stamp, or NULL
     79    psKernel *weight;                   ///< Weight image (1/variance) postage stamp, or NULL
    6380    psArray *convolutions1;             ///< Convolutions of image 1 for each kernel component, or NULL
    6481    psArray *convolutions2;             ///< Convolutions of image 2 for each kernel component, or NULL
    65     psImage *matrix1, *matrix2;         ///< Least-squares matrices for each image, or NULL
    66     psImage *matrixX;                   ///< Cross-matrix (for mode DUAL), or NULL
    67     psVector *vector1, *vector2;        ///< Least-squares vectors for each image, or NULL
     82    psImage *matrix;                    ///< Least-squares matrix, or NULL
     83    psVector *vector;                   ///< Least-squares vector, or NULL
     84    double norm;                        ///< Normalisation difference
    6885    pmSubtractionStampStatus status;    ///< Status of stamp
    6986} pmSubtractionStamp;
     
    7390
    7491/// Find stamps on an image
    75 pmSubtractionStampList *pmSubtractionStampsFind(pmSubtractionStampList *stamps, ///< Output stamps, or NULL
    76                                                 const psImage *image1, ///< Image for which to find stamps
    77                                                 const psImage *image2, ///< Image for which to find stamps
    78                                                 const psImage *mask, ///< Mask, or NULL
    79                                                 const psRegion *region, ///< Region to search, or NULL
    80                                                 float thresh1, ///< Threshold for stamps in image 1
    81                                                 float thresh2, ///< Threshold for stamps in image 2
    82                                                 int size, ///< Kernel half-size
    83                                                 int footprint, ///< Half-size for stamps
    84                                                 float spacing, ///< Rough spacing for stamps
    85                                                 pmSubtractionMode mode ///< Mode for subtraction
     92pmSubtractionStampList *pmSubtractionStampsFind(
     93    pmSubtractionStampList *stamps, ///< Output stamps, or NULL
     94    const psImage *image1, ///< Image for which to find stamps
     95    const psImage *image2, ///< Image for which to find stamps
     96    const psImage *mask, ///< Mask, or NULL
     97    const psRegion *region, ///< Region to search, or NULL
     98    float thresh1, ///< Threshold for stamps in image 1
     99    float thresh2, ///< Threshold for stamps in image 2
     100    int size, ///< Kernel half-size
     101    int footprint, ///< Half-size for stamps
     102    float spacing, ///< Rough spacing for stamps
     103    float normFrac, // Fraction of flux in window for normalisation window
     104    float sysErr,  ///< Relative systematic error in images
     105    float skyErr,  ///< Relative systematic error in images
     106    pmSubtractionMode mode ///< Mode for subtraction
    86107    );
    87108
    88109/// Set stamps based on a list of x,y
    89 pmSubtractionStampList *pmSubtractionStampsSet(const psVector *x, ///< x coordinates for each stamp
    90                                                const psVector *y, ///< y coordinates for each stamp
    91                                                const psImage *image, ///< Image for flux of stamp
    92                                                const psImage *mask, ///< Mask, or NULL
    93                                                const psRegion *region, ///< Region to search, or NULL
    94                                                int size, ///< Kernel half-size
    95                                                int footprint, ///< Half-size for stamps
    96                                                float spacing, ///< Rough spacing for stamps
    97                                                pmSubtractionMode mode ///< Mode for subtraction
     110pmSubtractionStampList *pmSubtractionStampsSet(
     111    const psVector *x, ///< x coordinates for each stamp
     112    const psVector *y, ///< y coordinates for each stamp
     113    const psImage *image, ///< Image for flux of stamp
     114    const psImage *mask, ///< Mask, or NULL
     115    const psRegion *region, ///< Region to search, or NULL
     116    int size, ///< Kernel half-size
     117    int footprint, ///< Half-size for stamps
     118    float spacing, ///< Rough spacing for stamps
     119    float normFrac, // Fraction of flux in window for normalisation window
     120    float sysErr,  ///< Systematic error in images
     121    float skyErr,  ///< Systematic error in images
     122    pmSubtractionMode mode ///< Mode for subtraction
    98123    );
    99124
     
    107132    int footprint,                      ///< Half-size for stamps
    108133    float spacing,                      ///< Rough spacing for stamps
     134    float normFrac, // Fraction of flux in window for normalisation window
     135    float sysErr,                       ///< Systematic error in images
     136    float skyErr,                       ///< Systematic error in images
    109137    pmSubtractionMode mode              ///< Mode for subtraction
    110138    );
     
    119147    int footprint,                      ///< Half-size for stamps
    120148    float spacing,                      ///< Rough spacing for stamps
     149    float normFrac, // Fraction of flux in window for normalisation window
     150    float sysErr,                       ///< Systematic error in images
     151    float skyErr,                       ///< Systematic error in images
    121152    pmSubtractionMode mode              ///< Mode for subtraction
     153    );
     154
     155/// Calculate the window and normalisation window from the stamps
     156bool pmSubtractionStampsGetWindow(
     157    pmSubtractionStampList *stamps,     ///< List of stamps
     158    int kernelSize                      ///< Half-size of kernel
    122159    );
    123160
     
    127164                                psImage *image2, ///< Input image (or NULL)
    128165                                psImage *variance, ///< Variance map
    129                                 int kernelSize ///< Kernel half-size
     166                                int kernelSize, ///< Kernel half-size
     167                                psRegion bounds ///< Bounds of validity
    130168    );
    131169
     
    154192    );
    155193
     194
     195bool pmSubtractionStampsResetStatus (pmSubtractionStampList *stamps);
     196
    156197#endif
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionThreads.c

    r21363 r27840  
    1717}
    1818
    19 // Initialise a mutex in each of the input
    20 static void subtractionMutexInit(pmReadout *ro)
    21 {
    22     if (!ro) {
    23         return;
    24     }
    25 
    26     // XXX if (ro->image) {
    27     // XXX     psMutexInit(ro->image);
    28     // XXX }
    29     // XXX if (ro->variance) {
    30     // XXX     psMutexInit(ro->variance);
    31     // XXX }
    32 
    33     return;
    34 }
    35 
    36 static void subtractionMutexDestroy(pmReadout *ro)
    37 {
    38     if (!ro) {
    39         return;
    40     }
    41 
    42     // XXX if (ro->image) {
    43     // XXX     psMutexDestroy(ro->image);
    44     // XXX }
    45     // XXX if (ro->variance) {
    46     // XXX     psMutexDestroy(ro->variance);
    47     // XXX }
    48 
    49     return;
    50 }
    51 
    52 void pmSubtractionThreadsInit(pmReadout *in1, pmReadout *in2)
     19void pmSubtractionThreadsInit(void)
    5320{
    5421    if (threaded) {
     
    5724
    5825    threaded = true;
    59 
    60     subtractionMutexInit(in1);
    61     subtractionMutexInit(in2);
    6226
    6327    {
     
    6933
    7034    {
    71         psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION", 3);
     35        psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION", 4);
    7236        task->function = &pmSubtractionCalculateEquationThread;
    7337        psThreadTaskAdd(task);
     
    8650
    8751
    88 void pmSubtractionThreadsFinalize(pmReadout *in1, pmReadout *in2)
     52void pmSubtractionThreadsFinalize(void)
    8953{
    9054    if (!threaded) {
     
    9761    psThreadTaskRemove("PSMODULES_SUBTRACTION_CONVOLVE");
    9862
    99     subtractionMutexDestroy(in1);
    100     subtractionMutexDestroy(in1);
    10163    return;
    10264}
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionThreads.h

    r19340 r27840  
    77/// Set up threading for image matching
    88///
    9 /// Sets up thread tasks, and initialises mutexes in readouts
    10 void pmSubtractionThreadsInit(pmReadout *in1, pmReadout *in2 // Input readouts
    11     );
     9/// Sets up thread tasks
     10void pmSubtractionThreadsInit(void);
    1211
    1312
    1413/// Take down threading for image matching
    1514///
    16 /// Destroys thread tasks, and initialises mutexes in readouts
    17 void pmSubtractionThreadsFinalize(pmReadout *in1, pmReadout *in2);
     15/// Destroys thread tasks
     16void pmSubtractionThreadsFinalize(void);
    1817
    1918#endif
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionVisual.c

    r23487 r27840  
    1616
    1717#include "pmKapaPlots.h"
     18#include "pmSubtraction.h"
    1819#include "pmSubtractionStamps.h"
     20#include "pmSubtractionEquation.h"
    1921
    2022#include "pmVisual.h"
     
    3234static bool plotLeastSquares     = true;
    3335static bool plotImage            = true;
     36
    3437// variables to store plotting window indices
    35 static int kapa  = -1;
     38static int kapa1 = -1;
    3639static int kapa2 = -1;
     40static int kapa3 = -1;
    3741
    3842/** function prototypes*/
     
    4650bool pmSubtractionVisualClose(void)
    4751{
    48     if(kapa != -1)
    49         KiiClose(kapa);
    50     if(kapa2 != -1)
    51         KiiClose(kapa2);
     52    if(kapa1 != -1) KiiClose(kapa1);
     53    if(kapa2 != -1) KiiClose(kapa2);
    5254    return true;
    5355}
     
    6062bool pmSubtractionVisualPlotConvKernels(psImage *convKernels) {
    6163    if (!pmVisualIsVisual() || !plotConvKernels) return true;
    62     if (!pmVisualInitWindow(&kapa, "ppSub:Images")) {
    63         return false;
    64     }
    65     pmVisualScaleImage(kapa, convKernels, "Convolution_Kernels", 0, true);
     64    if (!pmVisualInitWindow(&kapa1, "ppSub:Images")) {
     65        return false;
     66    }
     67    pmVisualScaleImage(kapa1, convKernels, "Convolution_Kernels", 0, true);
    6668    pmVisualAskUser(&plotConvKernels);
    6769    return true;
     
    7476bool pmSubtractionVisualPlotStamps(pmSubtractionStampList *stamps, pmReadout *ro) {
    7577    if (!pmVisualIsVisual() || !plotStamps) return true;
    76     if (!pmVisualInitWindow (&kapa, "ppSub:Images")) {
     78    if (!pmVisualInitWindow (&kapa1, "ppSub:Images")) {
    7779        return false;
    7880    }
     
    134136        stampNum++;
    135137    }
    136     pmVisualScaleImage(kapa, canvas, "Subtraction_Stamps", 0, true);
     138    pmVisualScaleImage(kapa1, canvas, "Subtraction_Stamps", 0, true);
    137139
    138140    pmVisualAskUser(&plotStamps);
     
    144146
    145147    if (!pmVisualIsVisual() || !plotLeastSquares) return true;
    146     if (!pmVisualInitWindow (&kapa, "PPSub:Images")) {
     148    if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
    147149        return false;
    148150    }
     
    157159        if (stamp == NULL) continue;
    158160
    159         psImage *im = stamp->matrix1;
    160         if (im == NULL) im = stamp->matrix2;
    161         if (im == NULL) im = stamp->matrixX;
     161        psImage *im = stamp->matrix;
    162162        if (im == NULL) continue;
    163163
     
    187187        if (stamp == NULL) continue;
    188188
    189         psImage *im = stamp->matrix1;
    190         if (im == NULL) im = stamp->matrix2;
    191         if (im == NULL) im = stamp->matrixX;
     189        psImage *im = stamp->matrix;
    192190        if (im == NULL) continue;
    193191
     
    197195
    198196    psImage *canvas32 = pmVisualImageToFloat(canvas);
    199     pmVisualScaleImage(kapa, canvas32, "Least_Squares", 0, true);
     197    pmVisualScaleImage(kapa1, canvas32, "Least_Squares", 0, true);
    200198
    201199    pmVisualAskUser(&plotLeastSquares);
     
    207205bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub) {
    208206    if (!pmVisualIsVisual() || !plotImage) return true;
    209     if (!pmVisualInitWindow (&kapa, "PPSub:Images")) {
    210         return false;
    211     }
    212 
    213     pmVisualScaleImage(kapa, image, "Image", 0, true);
    214     pmVisualScaleImage(kapa, ref, "Reference", 1, true);
    215     pmVisualScaleImage(kapa, sub, "Subtraction", 2, true);
     207    if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
     208        return false;
     209    }
     210
     211    pmVisualScaleImage(kapa1, image, "Image", 0, true);
     212    pmVisualScaleImage(kapa1, ref, "Reference", 1, true);
     213    pmVisualScaleImage(kapa1, sub, "Subtraction", 2, true);
     214    pmVisualAskUser(&plotImage);
     215    return true;
     216}
     217
     218bool pmSubtractionVisualShowKernels(pmSubtractionKernels *kernels) {
     219
     220    if (!pmVisualIsVisual()) return true;
     221    if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
     222        return false;
     223    }
     224
     225    // get the kernel sizes
     226    int footprint = kernels->size;
     227
     228    // output image is a grid of NXsub by NYsub sub-images
     229    int NXsub = sqrt(kernels->num);
     230    int NYsub = kernels->num / NXsub;
     231    if (kernels->num % NXsub) NYsub++;
     232
     233    int NXpix = NXsub * (2*footprint + 1 + 3);
     234    int NYpix = NYsub * (2*footprint + 1 + 3);
     235
     236    psImage *output = psImageAlloc(NXpix, NYpix, PS_TYPE_F32);
     237    psImageInit (output, 0.0);
     238
     239    for (int i = 0; i < kernels->num; i++) {
     240        pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[i];
     241        psKernel *kernel = preCalc->kernel;
     242
     243        int xSub = i % NXsub;
     244        int ySub = i / NXsub;
     245
     246        int xPix = xSub * (2*footprint + 1 + 3) + footprint;
     247        int yPix = ySub * (2*footprint + 1 + 3) + footprint;
     248
     249        double sum = 0.0;
     250        for (int y = -footprint; y <= footprint; y++) {
     251            for (int x = -footprint; x <= footprint; x++) {
     252                output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
     253                sum += kernel->kernel[y][x];
     254            }
     255        }
     256        fprintf (stderr, "kernel %d, sum %f\n", i, sum);
     257    }                                                   
     258       
     259    pmVisualScaleImage(kapa1, output, "Image", 0, true);
     260    pmVisualAskUser(&plotImage);
     261    return true;
     262}
     263
     264bool pmSubtractionVisualShowBasis(pmSubtractionStampList *stamps) {
     265
     266    if (!pmVisualIsVisual()) return true;
     267    if (!pmVisualInitWindow (&kapa2, "ppSub:StampMasterImage")) {
     268        return false;
     269    }
     270
     271    // get the kernel sizes
     272    int footprint = stamps->footprint;
     273
     274    // choose the brightest stamp
     275    pmSubtractionStamp *maxStamp = NULL;
     276    float maxFlux = NAN;
     277    for (int i = 0; i < stamps->num; i++) {
     278        pmSubtractionStamp *stamp = stamps->stamps->data[i];
     279        if (!isfinite(stamp->flux)) continue;
     280        if (!stamp->convolutions1 && !stamp->convolutions2) continue;
     281        if (!maxStamp) {
     282            maxFlux = stamp->flux;
     283            maxStamp = stamp;
     284            continue;
     285        }
     286        if (stamp->flux > maxFlux) {
     287            maxFlux = stamp->flux;
     288            maxStamp = stamp;
     289        }
     290    }
     291
     292    if (!isfinite(maxStamp->flux)) {
     293        fprintf (stderr, "no valid stamps?\n");
     294    }
     295
     296    int nKernels = 0;
     297
     298    if (maxStamp->convolutions1) {
     299        // output image is a grid of NXsub by NYsub sub-images
     300        nKernels = maxStamp->convolutions1->n;
     301        int NXsub = sqrt(nKernels);
     302        int NYsub = nKernels / NXsub;
     303        if (nKernels % NXsub) NYsub++;
     304
     305        int NXpix = NXsub * (2*footprint + 1 + 3);
     306        int NYpix = NYsub * (2*footprint + 1 + 3);
     307
     308        psImage *output = psImageAlloc(NXpix, NYpix, PS_TYPE_F32);
     309        psImageInit (output, 0.0);
     310
     311        for (int i = 0; i < nKernels; i++) {
     312            psKernel *kernel = maxStamp->convolutions1->data[i];
     313           
     314            int xSub = i % NXsub;
     315            int ySub = i / NXsub;
     316           
     317            int xPix = xSub * (2*footprint + 1 + 3) + footprint;
     318            int yPix = ySub * (2*footprint + 1 + 3) + footprint;
     319           
     320            double sum = 0.0;
     321            for (int y = -footprint; y <= footprint; y++) {
     322                for (int x = -footprint; x <= footprint; x++) {
     323                    output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
     324                    sum += kernel->kernel[y][x];
     325                }
     326            }
     327            fprintf (stderr, "kernel %d, sum %f\n", i, sum);
     328        }               
     329        pmVisualScaleImage(kapa2, output, "Image", 0, true);
     330    }                                   
     331       
     332    if (maxStamp->convolutions2) {
     333        // output image is a grid of NXsub by NYsub sub-images
     334        nKernels = maxStamp->convolutions2->n;
     335        int NXsub = sqrt(nKernels);
     336        int NYsub = nKernels / NXsub;
     337        if (nKernels % NXsub) NYsub++;
     338
     339        int NXpix = NXsub * (2*footprint + 1 + 3);
     340        int NYpix = NYsub * (2*footprint + 1 + 3);
     341
     342        psImage *output = psImageAlloc(NXpix, NYpix, PS_TYPE_F32);
     343        psImageInit (output, 0.0);
     344
     345        for (int i = 0; i < nKernels; i++) {
     346            psKernel *kernel = maxStamp->convolutions2->data[i];
     347           
     348            int xSub = i % NXsub;
     349            int ySub = i / NXsub;
     350           
     351            int xPix = xSub * (2*footprint + 1 + 3) + footprint;
     352            int yPix = ySub * (2*footprint + 1 + 3) + footprint;
     353           
     354            double sum = 0.0;
     355            for (int y = -footprint; y <= footprint; y++) {
     356                for (int x = -footprint; x <= footprint; x++) {
     357                    output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
     358                    sum += kernel->kernel[y][x];
     359                }
     360            }
     361            fprintf (stderr, "kernel %d, sum %f\n", i, sum);
     362        }               
     363        pmVisualScaleImage(kapa2, output, "Image", 1, true);
     364    }                                   
     365       
    216366    pmVisualAskUser(&plotImage);
    217367    return true;
     
    240390
    241391        overlay[Noverlay].type = KII_OVERLAY_BOX;
     392        if ((stamp->x < 1.0) && (stamp->y < 1.0)) {
     393            // fprintf (stderr, "stamp zero: %f %f\n", stamp->x, stamp->y);
     394            continue;
     395        }
     396        if (!isfinite(stamp->x) && !isfinite(stamp->y)) {
     397            // fprintf (stderr, "stamp nan: %f %f\n", stamp->x, stamp->y);
     398            continue;
     399        }
    242400        overlay[Noverlay].x = stamp->x;
    243401        overlay[Noverlay].y = stamp->y;
     
    255413}
    256414
     415static int footprint = 0;
     416static int NX = 0;
     417static int NY = 0;
     418static psImage *sourceImage      = NULL;
     419static psImage *targetImage      = NULL;
     420static psImage *residualImage    = NULL;
     421static psImage *fresidualImage   = NULL;
     422static psImage *differenceImage  = NULL;
     423static psImage *convolutionImage = NULL;
     424
     425bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps) {
     426
     427    if (!pmVisualIsVisual()) return true;
     428
     429    // generate 4 storage images large enough to hold the N stamps:
     430
     431    footprint = stamps->footprint;
     432
     433    float NXf = sqrt(stamps->num);
     434    NX = (int) NXf == NXf ? NXf : NXf + 1.0;
     435   
     436    float NYf = stamps->num / NX;
     437    NY = (int) NYf == NY ? NYf : NYf + 1.0;
     438
     439    int NXpix = (2*footprint + 1) * NX;
     440    NXpix += (NX > 1) ? 3 * NX : 0;
     441
     442    int NYpix = (2*footprint + 1) * NY;
     443    NYpix += (NY > 1) ? 3 * NY : 0;
     444
     445    sourceImage      = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     446    targetImage      = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     447    residualImage    = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     448    fresidualImage   = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     449    differenceImage  = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     450    convolutionImage = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     451   
     452    psImageInit (sourceImage,      0.0);
     453    psImageInit (targetImage,      0.0);
     454    psImageInit (residualImage,    0.0);
     455    psImageInit (fresidualImage,   0.0);
     456    psImageInit (differenceImage,  0.0);
     457    psImageInit (convolutionImage, 0.0);
     458
     459    return true;
     460}
     461
     462bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index) {
     463
     464    if (!pmVisualIsVisual()) return true;
     465
     466    double sum;
     467
     468    int NXoff = index % NX;
     469    int NYoff = index / NX;
     470
     471    int NXpix = NXoff * (2*footprint + 1 + 3) + footprint;
     472    int NYpix = NYoff * (2*footprint + 1 + 3) + footprint;
     473
     474    // insert the (target) kernel into the (target) image:
     475    sum = 0.0;
     476    for (int y = -footprint; y <= footprint; y++) {
     477        for (int x = -footprint; x <= footprint; x++) {
     478            targetImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x];
     479            sum += targetImage->data.F32[y + NYpix][x + NXpix];
     480        }
     481    }
     482    targetImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     483
     484    // insert the (source) kernel into the (source) image:
     485    sum = 0.0;
     486    for (int y = -footprint; y <= footprint; y++) {
     487        for (int x = -footprint; x <= footprint; x++) {
     488            sourceImage->data.F32[y + NYpix][x + NXpix] = source->kernel[y][x];
     489            sum += sourceImage->data.F32[y + NYpix][x + NXpix];
     490        }
     491    }
     492    sourceImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     493
     494    // insert the (convolution) kernel into the (convolution) image:
     495    sum = 0.0;
     496    for (int y = -footprint; y <= footprint; y++) {
     497        for (int x = -footprint; x <= footprint; x++) {
     498            convolutionImage->data.F32[y + NYpix][x + NXpix] = -convolution->kernel[y][x];
     499            sum += convolutionImage->data.F32[y + NYpix][x + NXpix];
     500        }
     501    }
     502    convolutionImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     503   
     504    // insert the (difference) kernel into the (difference) image:
     505    sum = 0.0;
     506    for (int y = -footprint; y <= footprint; y++) {
     507        for (int x = -footprint; x <= footprint; x++) {
     508            differenceImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x] - background - source->kernel[y][x] * norm;
     509            sum += differenceImage->data.F32[y + NYpix][x + NXpix];
     510        }
     511    }
     512    differenceImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     513
     514    // insert the (residual) kernel into the (residual) image:
     515    sum = 0.0;
     516    for (int y = -footprint; y <= footprint; y++) {
     517        for (int x = -footprint; x <= footprint; x++) {
     518            residualImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x] - background - source->kernel[y][x] * norm - convolution->kernel[y][x];
     519            sum += residualImage->data.F32[y + NYpix][x + NXpix];
     520        }
     521    }
     522    residualImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     523
     524    // insert the (fresidual) kernel into the (fresidual) image:
     525    for (int y = -footprint; y <= footprint; y++) {
     526        for (int x = -footprint; x <= footprint; x++) {
     527            fresidualImage->data.F32[y + NYpix][x + NXpix] = residualImage->data.F32[y + NYpix][x + NXpix] / sqrt(PS_MAX(target->kernel[y][x], 100.0));
     528        }
     529    }
     530    return true;
     531}
     532
     533bool pmSubtractionVisualShowFit(double norm) {
     534
     535    // for testing, dump the residual image and exit
     536    if (0) {
     537        psMetadata *header = psMetadataAlloc();
     538        psMetadataAddF32 (header, PS_LIST_TAIL, "NORM", 0, "Normalization", norm);
     539        psFits *fits = psFitsOpen("resid.fits", "w");
     540        psFitsWriteImage(fits, header, residualImage, 0, NULL);
     541        psFitsClose(fits);
     542        // exit (0);
     543    }
     544
     545    if (!pmVisualIsVisual()) return true;
     546    if (!pmVisualInitWindow(&kapa1, "ppSub:Images")) return false;
     547    if (!pmVisualInitWindow(&kapa2, "ppSub:Misc")) return false;
     548
     549    KiiEraseOverlay (kapa1, "red");
     550    KiiEraseOverlay (kapa2, "red");
     551
     552    pmVisualScaleImage(kapa1, targetImage, "Target Stamps", 0, true);
     553    pmVisualScaleImage(kapa1, sourceImage, "Source Stamps", 1, true);
     554    pmVisualScaleImage(kapa1, convolutionImage, "Convolution Stamps", 2, true);
     555    KiiCenter (kapa1, 0.5*targetImage->numCols, 0.5*targetImage->numRows, 1);
     556
     557    pmVisualScaleImage(kapa2, fresidualImage, "Frac Residual Stamps", 2, true);
     558    pmVisualScaleImage(kapa2, differenceImage, "Difference Stamps", 0, true);
     559
     560    if (1) {
     561        KiiImage image;
     562        KapaImageData data;
     563        Coords coords;
     564        strcpy (coords.ctype, "RA---TAN");
     565
     566        image.data2d = residualImage->data.F32;
     567        image.Nx = residualImage->numCols;
     568        image.Ny = residualImage->numRows;
     569        strcpy (data.name, "Residual Stamps");
     570        strcpy (data.file, "Residual Stamps");
     571
     572        data.zero  = -300.0;
     573        data.range = +600.0;
     574        data.logflux = 0;
     575
     576        KiiSetChannel (kapa2, 1);
     577        KiiNewPicture2D (kapa2, &image, &data, &coords);
     578    } else {
     579        pmVisualScaleImage(kapa2, residualImage, "Residual Stamps", 1, true);
     580    }
     581
     582    KiiCenter (kapa2, 0.5*residualImage->numCols, 0.5*residualImage->numRows, 1);
     583
     584    pmVisualAskUser(NULL);
     585
     586    psFree(targetImage);
     587    psFree(sourceImage);
     588    psFree(convolutionImage);
     589    psFree(differenceImage);
     590    psFree(residualImage);
     591    psFree(fresidualImage);
     592
     593    targetImage = NULL;
     594    sourceImage = NULL;
     595    convolutionImage = NULL;
     596    differenceImage = NULL;
     597    residualImage = NULL;
     598    fresidualImage = NULL;
     599
     600    return true;
     601}
     602
     603bool pmSubtractionVisualPlotFit(const pmSubtractionKernels *kernels) {
     604
     605    Graphdata graphdata;
     606
     607    if (!pmVisualIsVisual()) return true;
     608    if (!pmVisualInitWindow(&kapa3, "ppSub:plots")) return false;
     609
     610    KapaClearSections (kapa3);
     611    KapaInitGraph (&graphdata);
     612
     613    psVector *x = psVectorAllocEmpty (kernels->num, PS_TYPE_F32);
     614    psVector *y = psVectorAllocEmpty (kernels->num, PS_TYPE_F32);
     615
     616    graphdata.xmin = -1.0;
     617    graphdata.xmax = kernels->num + 1.0;
     618    graphdata.ymin = +32.0;
     619    graphdata.ymax = -32.0;
     620
     621    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, 0.0, 0.0);
     622
     623    // construct the plot vectors
     624    for (int i = 0; i < kernels->num; i++) {
     625        x->data.F32[i] = i;
     626        y->data.F32[i] = p_pmSubtractionSolutionCoeff(kernels, polyValues, i, false);
     627        graphdata.ymin = PS_MIN(graphdata.ymin, y->data.F32[i]);
     628        graphdata.ymax = PS_MAX(graphdata.ymax, y->data.F32[i]);
     629    }
     630    x->n = y->n = kernels->num;
     631
     632    float range;
     633    range = graphdata.xmax - graphdata.xmin;
     634    graphdata.xmax += 0.05*range;
     635    graphdata.xmin -= 0.05*range;
     636    range = graphdata.ymax - graphdata.ymin;
     637    graphdata.ymax += 0.05*range;
     638    graphdata.ymin -= 0.05*range;
     639
     640    KapaSetLimits (kapa3, &graphdata);
     641
     642    KapaSetFont (kapa3, "helvetica", 14);
     643    KapaBox (kapa3, &graphdata);
     644    KapaSendLabel (kapa3, "kernel number", KAPA_LABEL_XM);
     645    KapaSendLabel (kapa3, "coeff", KAPA_LABEL_YM);
     646
     647    graphdata.color = KapaColorByName ("black");
     648    graphdata.ptype = 2;
     649    graphdata.size = 0.5;
     650    graphdata.style = 2;
     651
     652    KapaPrepPlot   (kapa3, x->n, &graphdata);
     653    KapaPlotVector (kapa3, x->n, x->data.F32, "x");
     654    KapaPlotVector (kapa3, x->n, y->data.F32, "y");
     655
     656    psFree (x);
     657    psFree (y);
     658
     659    pmVisualAskUser(NULL);
     660    return true;
     661}
     662
    257663#else
    258664bool pmSubtractionVisualClose(void) {return true;}
     
    261667bool pmSubtractionVisualPlotLeastSquares(pmSubtractionStampList *stamps) {return true;}
    262668bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub) {return true;}
     669bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps) {return true;}
     670bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index) {return true;}
     671bool pmSubtractionVisualShowFit() {return true;}
     672bool pmSubtractionVisualPlotFit(const pmSubtractionKernels *kernels);
    263673#endif
  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtractionVisual.h

    r23487 r27840  
    77bool pmSubtractionVisualPlotLeastSquares(pmSubtractionStampList *stamps);
    88bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub);
     9bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps);
     10bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index);
     11bool pmSubtractionVisualShowFit(double norm);
     12bool pmSubtractionVisualPlotFit(const pmSubtractionKernels *kernels);
     13bool pmSubtractionVisualShowKernels(pmSubtractionKernels *kernels);
     14bool pmSubtractionVisualShowBasis(pmSubtractionStampList *stamps);
    915
    1016#endif
  • branches/simtest_nebulous_branches/psModules/src/objects/Makefile.am

    r24875 r27840  
    1212        pmFootprintCullPeaks.c \
    1313        pmFootprintFind.c \
     14        pmFootprintSpans.c \
    1415        pmFootprintFindAtPoint.c \
    1516        pmFootprintIDs.c \
     
    2021        pmModelUtils.c \
    2122        pmSource.c \
     23        pmPhotObj.c \
    2224        pmSourceMasks.c \
    2325        pmSourceMoments.c \
     26        pmSourceDiffStats.c \
    2427        pmSourceExtendedPars.c \
    2528        pmSourceUtils.c \
     
    4043        pmSourceIO_CMF_PS1_V1.c \
    4144        pmSourceIO_CMF_PS1_V2.c \
     45        pmSourceIO_CMF_PS1_DV1.c \
    4246        pmSourceIO_MatchedRefs.c \
    4347        pmSourcePlots.c \
     
    5054        pmPSF_IO.c \
    5155        pmPSFtry.c \
     56        pmPSFtryModel.c \
     57        pmPSFtryFitEXT.c \
     58        pmPSFtryMakePSF.c \
     59        pmPSFtryFitPSF.c \
     60        pmPSFtryMetric.c \
    5261        pmTrend2D.c \
    5362        pmGrowthCurveGenerate.c \
    5463        pmGrowthCurve.c \
    55         pmSourceMatch.c
    56 
    57 EXTRA_DIST = \
     64        pmSourceMatch.c \
     65        pmDetEff.c \
     66        pmSourceGroups.c \
    5867        models/pmModel_GAUSS.c \
    5968        models/pmModel_PGAUSS.c \
     69        models/pmModel_PS1_V1.c \
    6070        models/pmModel_QGAUSS.c \
    61         models/pmModel_SGAUSS.c \
    6271        models/pmModel_RGAUSS.c \
    6372        models/pmModel_SERSIC.c
     
    6776        pmSpan.h \
    6877        pmFootprint.h \
     78        pmFootprintSpans.h \
    6979        pmPeaks.h \
    7080        pmMoments.h \
     
    7383        pmModelUtils.h \
    7484        pmSource.h \
     85        pmPhotObj.h \
    7586        pmSourceMasks.h \
     87        pmSourceDiffStats.h \
    7688        pmSourceExtendedPars.h \
    7789        pmSourceUtils.h \
     
    90102        pmTrend2D.h \
    91103        pmGrowthCurve.h \
    92         pmSourceMatch.h
     104        pmSourceMatch.h \
     105        pmDetEff.h \
     106        pmSourceGroups.h \
     107        models/pmModel_GAUSS.h \
     108        models/pmModel_PGAUSS.h \
     109        models/pmModel_PS1_V1.h \
     110        models/pmModel_QGAUSS.h \
     111        models/pmModel_RGAUSS.h \
     112        models/pmModel_SERSIC.h
    93113
    94114CLEANFILES = *~
  • branches/simtest_nebulous_branches/psModules/src/objects/models/pmModel_GAUSS.c

    r20001 r27840  
    11/******************************************************************************
    22 * this file defines the GAUSS source shape model.  Note that these model functions are loaded
    3  * by pmModelGroup.c using 'include', and thus need no 'include' statements of their own.  The
     3 * by pmModelClass.c using 'include', and thus need no 'include' statements of their own.  The
    44 * models use a psVector to represent the set of parameters, with the sequence used to specify
    55 * the meaning of the parameter.  The meaning of the parameters may thus vary depending on the
    6  * specifics of the model.  All models which are used a PSF representations share a few
     6 * specifics of the model.  All models which are used as a PSF representations share a few
    77 * parameters, for which # define names are listed in pmModel.h:
    88
     
    1919 *****************************************************************************/
    2020
     21#include <stdio.h>
     22#include <pslib.h>
     23
     24#include "pmMoments.h"
     25#include "pmPeaks.h"
     26#include "pmSource.h"
     27#include "pmModel.h"
     28#include "pmModel_GAUSS.h"
     29
    2130# define PM_MODEL_FUNC            pmModelFunc_GAUSS
    2231# define PM_MODEL_FLUX            pmModelFlux_GAUSS
     
    2736# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_GAUSS
    2837# define PM_MODEL_FIT_STATUS      pmModelFitStatus_GAUSS
     38# define PM_MODEL_SET_LIMITS      pmModelSetLimits_GAUSS
     39
     40// Lax parameter limits
     41static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0 };
     42static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0 };
     43
     44// Moderate parameter limits
     45static float *paramsMinModerate = paramsMinLax;
     46static float *paramsMaxModerate = paramsMaxLax;
     47
     48// Strict parameter limits
     49static float *paramsMinStrict = paramsMinLax;
     50static float *paramsMaxStrict = paramsMaxLax;
     51
     52// Parameter limits to use
     53static float *paramsMinUse = paramsMinLax;
     54static float *paramsMaxUse = paramsMaxLax;
     55static float betaUse[] = { 1000, 3e6, 5, 5, 2.0, 2.0, 0.5 };
     56
     57static bool limitsApply = true;         // Apply limits?
    2958
    3059// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
     60// 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
     61// values need to be pixel coords
    3162psF32 PM_MODEL_FUNC(psVector *deriv,
    3263                    const psVector *params,
     
    6899bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
    69100{
    70     float beta_lim = 0, params_min = 0, params_max = 0;
    71     float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
     101    if (!limitsApply) {
     102        return true;
     103    }
     104    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
    72105
    73106    // we need to calculate the limits for SXY specially
     107    float q2 = NAN;
    74108    if (nParam == PM_PAR_SXY) {
    75         f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
    76         f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
    77         q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
    78         q1 = PS_MAX (0.0, q1);
     109        float f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
     110        float f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
     111        float q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
     112        q1 = (q1 < 0.0) ? 0.0 : q1;
    79113        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
    80114        // angle and let f2,f1 fight it out
    81         q2  = 0.5*sqrt (q1);
     115        q2 = 0.5*sqrtf(q1);
    82116    }
    83117
    84118    switch (mode) {
    85     case PS_MINIMIZE_BETA_LIMIT:
    86         switch (nParam) {
    87         case PM_PAR_SKY:
    88             beta_lim = 1000;
    89             break;
    90         case PM_PAR_I0:
    91             beta_lim = 3e6;
    92             break;
    93         case PM_PAR_XPOS:
    94             beta_lim = 5;
    95             break;
    96         case PM_PAR_YPOS:
    97             beta_lim = 5;
    98             break;
    99         case PM_PAR_SXX:
    100             beta_lim = 2.0;
    101             break;
    102         case PM_PAR_SYY:
    103             beta_lim = 2.0;
    104             break;
    105         case PM_PAR_SXY:
    106             beta_lim =  0.5*q2;
    107             break;
    108         default:
    109             psAbort("invalid parameter %d for beta test", nParam);
    110         }
    111         if (fabs(beta[nParam]) > fabs(beta_lim)) {
    112             beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
    113             psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
    114                      nParam, beta[nParam], beta_lim);
    115             return false;
    116         }
    117         return true;
    118     case PS_MINIMIZE_PARAM_MIN:
    119         switch (nParam) {
    120         case PM_PAR_SKY:
    121             params_min = -1000;
    122             break;
    123         case PM_PAR_I0:
    124             params_min =   0.01;
    125             break;
    126         case PM_PAR_XPOS:
    127             params_min =  -100;
    128             break;
    129         case PM_PAR_YPOS:
    130             params_min =  -100;
    131             break;
    132         case PM_PAR_SXX:
    133             params_min =   0.5;
    134             break;
    135         case PM_PAR_SYY:
    136             params_min =   0.5;
    137             break;
    138         case PM_PAR_SXY:
    139             params_min =   -q2;
    140             break;
    141         default:
    142             psAbort("invalid parameter %d for param min test", nParam);
    143         }
    144         if (params[nParam] < params_min) {
    145             params[nParam] = params_min;
    146             psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
    147                      nParam, params[nParam], params_min);
    148             return false;
    149         }
    150         return true;
    151     case PS_MINIMIZE_PARAM_MAX:
    152         switch (nParam) {
    153         case PM_PAR_SKY:
    154             params_max =   1e5;
    155             break;
    156         case PM_PAR_I0:
    157             params_max =   1e8;
    158             break;
    159         case PM_PAR_XPOS:
    160             params_max =   1e4;
    161             break;
    162         case PM_PAR_YPOS:
    163             params_max =   1e4;
    164             break;
    165         case PM_PAR_SXX:
    166             params_max =   100;
    167             break;
    168         case PM_PAR_SYY:
    169             params_max =   100;
    170             break;
    171         case PM_PAR_SXY:
    172             params_max =   +q2;
    173             break;
    174         default:
    175             psAbort("invalid parameter %d for param max test", nParam);
    176         }
    177         if (params[nParam] > params_max) {
    178             params[nParam] = params_max;
    179             psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
    180                      nParam, params[nParam], params_max);
    181             return false;
    182         }
    183         return true;
     119      case PS_MINIMIZE_BETA_LIMIT: {
     120          psAssert(beta, "Require beta to limit beta");
     121          float limit = betaUse[nParam];
     122          if (nParam == PM_PAR_SXY) {
     123              limit *= q2;
     124          }
     125          if (fabs(beta[nParam]) > fabs(limit)) {
     126              beta[nParam] = (beta[nParam] > 0) ? fabs(limit) : -fabs(limit);
     127              psTrace("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
     128                      nParam, beta[nParam], limit);
     129              return false;
     130          }
     131          return true;
     132      }
     133      case PS_MINIMIZE_PARAM_MIN: {
     134          psAssert(params, "Require parameters to limit parameters");
     135          psAssert(paramsMinUse, "Require parameter limits to limit parameters");
     136          float limit = paramsMinUse[nParam];
     137          if (nParam == PM_PAR_SXY) {
     138              limit *= q2;
     139          }
     140          if (params[nParam] < limit) {
     141              params[nParam] = limit;
     142              psTrace("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
     143                      nParam, params[nParam], limit);
     144              return false;
     145          }
     146          return true;
     147      }
     148      case PS_MINIMIZE_PARAM_MAX: {
     149          psAssert(params, "Require parameters to limit parameters");
     150          psAssert(paramsMaxUse, "Require parameter limits to limit parameters");
     151          float limit = paramsMaxUse[nParam];
     152          if (nParam == PM_PAR_SXY) {
     153              limit *= q2;
     154          }
     155          if (params[nParam] > limit) {
     156              params[nParam] = limit;
     157              psTrace("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
     158                      nParam, params[nParam], limit);
     159              return false;
     160          }
     161          return true;
     162      }
    184163    default:
    185164        psAbort("invalid choice for limits");
     
    190169
    191170// make an initial guess for parameters
     171// 0.5 PIX: moments and peaks are in pixel coords, thus so are model parameters
    192172bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
    193173{
     
    205185    psEllipseShape shape = psEllipseAxesToShape (axes);
    206186
    207     PAR[PM_PAR_SKY]  = moments->Sky;
     187    PAR[PM_PAR_SKY]  = 0.0;
    208188    PAR[PM_PAR_I0]   = peak->flux;
    209189    PAR[PM_PAR_XPOS] = peak->xf;
     
    257237    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
    258238    psF64 radius = axes.major * sqrt (2.0 * log(PAR[PM_PAR_I0] / flux));
     239    psAssert (isfinite(radius), "fix this code: radius should not be nan for %f", PAR[PM_PAR_I0]);
     240
    259241    return (radius);
    260242}
     
    367349bool PM_MODEL_FIT_STATUS (pmModel *model)
    368350{
    369     psF32 dP;
    370351    bool  status;
    371352
     
    373354    psF32 *dPAR = model->dparams->data.F32;
    374355
    375     dP = 0;
    376     dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
    377     dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
    378     dP = sqrt (dP);
    379 
    380356    status = true;
    381     status &= (dP < 0.5);
    382357    status &= (PAR[PM_PAR_I0] > 0);
    383358    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
    384359
    385     if (status)
    386         return true;
    387     return false;
     360    return status;
     361}
     362
     363void PM_MODEL_SET_LIMITS(pmModelLimitsType type)
     364{
     365    switch (type) {
     366      case PM_MODEL_LIMITS_NONE:
     367        paramsMinUse = NULL;
     368        paramsMaxUse = NULL;
     369        limitsApply = true;
     370        break;
     371      case PM_MODEL_LIMITS_IGNORE:
     372        paramsMinUse = NULL;
     373        paramsMaxUse = NULL;
     374        limitsApply = false;
     375        break;
     376      case PM_MODEL_LIMITS_LAX:
     377        paramsMinUse = paramsMinLax;
     378        paramsMaxUse = paramsMaxLax;
     379        limitsApply = true;
     380        break;
     381      case PM_MODEL_LIMITS_MODERATE:
     382        paramsMinUse = paramsMinModerate;
     383        paramsMaxUse = paramsMaxModerate;
     384        limitsApply = true;
     385        break;
     386      case PM_MODEL_LIMITS_STRICT:
     387        paramsMinUse = paramsMinStrict;
     388        paramsMaxUse = paramsMaxStrict;
     389        limitsApply = true;
     390        break;
     391      default:
     392        psAbort("Unrecognised model limits type: %x", type);
     393    }
     394    return;
    388395}
    389396
     
    396403# undef PM_MODEL_PARAMS_FROM_PSF
    397404# undef PM_MODEL_FIT_STATUS
     405# undef PM_MODEL_SET_LIMITS
  • branches/simtest_nebulous_branches/psModules/src/objects/models/pmModel_PGAUSS.c

    r20001 r27840  
    11/******************************************************************************
    22 * this file defines the PGAUSS source shape model.  Note that these model functions are loaded
    3  * by pmModelGroup.c using 'include', and thus need no 'include' statements of their own.  The
     3 * by pmModelClass.c using 'include', and thus need no 'include' statements of their own.  The
    44 * models use a psVector to represent the set of parameters, with the sequence used to specify
    55 * the meaning of the parameter.  The meaning of the parameters may thus vary depending on the
    6  * specifics of the model.  All models which are used a PSF representations share a few
     6 * specifics of the model.  All models which are used as a PSF representations share a few
    77 * parameters, for which # define names are listed in pmModel.h:
    88
     
    1919 *****************************************************************************/
    2020
     21#include <stdio.h>
     22#include <pslib.h>
     23
     24#include "pmMoments.h"
     25#include "pmPeaks.h"
     26#include "pmSource.h"
     27#include "pmModel.h"
     28#include "pmModel_PGAUSS.h"
     29
    2130# define PM_MODEL_FUNC            pmModelFunc_PGAUSS
    2231# define PM_MODEL_FLUX            pmModelFlux_PGAUSS
     
    2736# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_PGAUSS
    2837# define PM_MODEL_FIT_STATUS      pmModelFitStatus_PGAUSS
     38# define PM_MODEL_SET_LIMITS      pmModelSetLimits_PGAUSS
     39
     40// Lax parameter limits
     41static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0 };
     42static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0 };
     43
     44// Moderate parameter limits
     45static float *paramsMinModerate = paramsMinLax;
     46static float *paramsMaxModerate = paramsMaxLax;
     47
     48// Strict parameter limits
     49static float *paramsMinStrict = paramsMinLax;
     50static float *paramsMaxStrict = paramsMaxLax;
     51
     52// Parameter limits to use
     53static float *paramsMinUse = paramsMinLax;
     54static float *paramsMaxUse = paramsMaxLax;
     55static float betaUse[] = { 1000, 3e6, 5, 5, 2.0, 2.0, 0.5 };
     56
     57static bool limitsApply = true;         // Apply limits?
    2958
    3059// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
     60// 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
     61// values need to be pixel coords
    3162psF32 PM_MODEL_FUNC(psVector *deriv,
    3263                    const psVector *params,
     
    69100bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
    70101{
    71     float beta_lim = 0, params_min = 0, params_max = 0;
    72     float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
     102    if (!limitsApply) {
     103        return true;
     104    }
     105    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
    73106
    74107    // we need to calculate the limits for SXY specially
     108    float q2 = NAN;
    75109    if (nParam == PM_PAR_SXY) {
    76         f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
    77         f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
    78         q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
    79         q1 = PS_MAX (0.0, q1);
     110        float f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
     111        float f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
     112        float q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
     113        q1 = (q1 < 0.0) ? 0.0 : q1;
    80114        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
    81115        // angle and let f2,f1 fight it out
    82         q2  = 0.5*sqrt (q1);
     116        q2 = 0.5*sqrtf(q1);
    83117    }
    84118
    85119    switch (mode) {
    86     case PS_MINIMIZE_BETA_LIMIT:
    87         switch (nParam) {
    88         case PM_PAR_SKY:
    89             beta_lim = 1000;
    90             break;
    91         case PM_PAR_I0:
    92             beta_lim = 3e6;
    93             break;
    94         case PM_PAR_XPOS:
    95             beta_lim = 5;
    96             break;
    97         case PM_PAR_YPOS:
    98             beta_lim = 5;
    99             break;
    100         case PM_PAR_SXX:
    101             beta_lim = 2.0;
    102             break;
    103         case PM_PAR_SYY:
    104             beta_lim = 2.0;
    105             break;
    106         case PM_PAR_SXY:
    107             beta_lim =  0.5*q2;
    108             break;
    109         default:
    110             psAbort("invalid parameter %d for beta test", nParam);
    111         }
    112         if (fabs(beta[nParam]) > fabs(beta_lim)) {
    113             beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
    114             psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
    115                      nParam, beta[nParam], beta_lim);
    116             return false;
    117         }
    118         return true;
    119     case PS_MINIMIZE_PARAM_MIN:
    120         switch (nParam) {
    121         case PM_PAR_SKY:
    122             params_min = -1000;
    123             break;
    124         case PM_PAR_I0:
    125             params_min =  0.01;
    126             break;
    127         case PM_PAR_XPOS:
    128             params_min =  -100;
    129             break;
    130         case PM_PAR_YPOS:
    131             params_min =  -100;
    132             break;
    133         case PM_PAR_SXX:
    134             params_min =   0.5;
    135             break;
    136         case PM_PAR_SYY:
    137             params_min =   0.5;
    138             break;
    139         case PM_PAR_SXY:
    140             params_min =   -q2;
    141             break;
    142         default:
    143             psAbort("invalid parameter %d for param min test", nParam);
    144         }
    145         if (params[nParam] < params_min) {
    146             params[nParam] = params_min;
    147             psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
    148                      nParam, params[nParam], params_min);
    149             return false;
    150         }
    151         return true;
    152     case PS_MINIMIZE_PARAM_MAX:
    153         switch (nParam) {
    154         case PM_PAR_SKY:
    155             params_max =   1e5;
    156             break;
    157         case PM_PAR_I0:
    158             params_max =   1e8;
    159             break;
    160         case PM_PAR_XPOS:
    161             params_max =   1e4;
    162             break;
    163         case PM_PAR_YPOS:
    164             params_max =   1e4;
    165             break;
    166         case PM_PAR_SXX:
    167             params_max =   100;
    168             break;
    169         case PM_PAR_SYY:
    170             params_max =   100;
    171             break;
    172         case PM_PAR_SXY:
    173             params_max =   +q2;
    174             break;
    175         default:
    176             psAbort("invalid parameter %d for param max test", nParam);
    177         }
    178         if (params[nParam] > params_max) {
    179             params[nParam] = params_max;
    180             psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
    181                      nParam, params[nParam], params_max);
    182             return false;
    183         }
    184         return true;
    185     default:
     120      case PS_MINIMIZE_BETA_LIMIT: {
     121          psAssert(beta, "Require beta to limit beta");
     122          float limit = betaUse[nParam];
     123          if (nParam == PM_PAR_SXY) {
     124              limit *= q2;
     125          }
     126          if (fabs(beta[nParam]) > fabs(limit)) {
     127              beta[nParam] = (beta[nParam] > 0) ? fabs(limit) : -fabs(limit);
     128              psTrace("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
     129                      nParam, beta[nParam], limit);
     130              return false;
     131          }
     132          return true;
     133      }
     134      case PS_MINIMIZE_PARAM_MIN: {
     135          psAssert(params, "Require parameters to limit parameters");
     136          psAssert(paramsMinUse, "Require parameter limits to limit parameters");
     137          float limit = paramsMinUse[nParam];
     138          if (nParam == PM_PAR_SXY) {
     139              limit *= q2;
     140          }
     141          if (params[nParam] < limit) {
     142              params[nParam] = limit;
     143              psTrace("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
     144                      nParam, params[nParam], limit);
     145              return false;
     146          }
     147          return true;
     148      }
     149      case PS_MINIMIZE_PARAM_MAX: {
     150          psAssert(params, "Require parameters to limit parameters");
     151          psAssert(paramsMaxUse, "Require parameter limits to limit parameters");
     152          float limit = paramsMaxUse[nParam];
     153          if (nParam == PM_PAR_SXY) {
     154              limit *= q2;
     155          }
     156          if (params[nParam] > limit) {
     157              params[nParam] = limit;
     158              psTrace("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
     159                      nParam, params[nParam], limit);
     160              return false;
     161          }
     162          return true;
     163      }
     164      default:
    186165        psAbort("invalid choice for limits");
    187166    }
     
    190169}
    191170
     171
    192172// make an initial guess for parameters
     173// 0.5 PIX: moments and peaks are in pixel coords, thus so are model parameters
    193174bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
    194175{
     
    205186    psEllipseShape shape = psEllipseAxesToShape (axes);
    206187
    207     PAR[PM_PAR_SKY]  = moments->Sky;
     188    PAR[PM_PAR_SKY]  = 0.0;
    208189    PAR[PM_PAR_I0]   = peak->flux;
    209190    PAR[PM_PAR_XPOS] = peak->xf;
     
    255236psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
    256237{
    257     psF64 z, f;
     238    psF64 z;
    258239    int Nstep = 0;
    259240    psEllipseShape shape;
     
    284265    // choose a z value guaranteed to be beyond our limit
    285266    float z0 = pow((1.0 / limit), (1.0 / 3.0));
     267    psAssert (isfinite(z0), "fix this code: z0 should not be nan for %f", PAR[PM_PAR_I0]);
    286268    float z1 = (1.0 / limit);
     269    psAssert (isfinite(z1), "fix this code: z1 should not be nan for %f", PAR[PM_PAR_I0]);
    287270    z1 = PS_MAX (z0, z1);
    288271    z0 = 0.0;
    289272
    290     // perform a type of bisection to find the value
    291     float f0 = 1.0 / (1 + z0 + z0*z0/2.0 + z0*z0*z0/6.0);
    292     float f1 = 1.0 / (1 + z1 + z1*z1/2.0 + z1*z1*z1/6.0);
    293     while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
    294         z = 0.5*(z0 + z1);
    295         f = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
    296         if (f > limit) {
    297             z0 = z;
    298             f0 = f;
    299         } else {
    300             z1 = z;
    301             f1 = f;
    302         }
    303         Nstep ++;
    304     }
     273    // starting guess:
     274    z = 0.5*(z0 + z1);
     275    float dz = 1.0;
     276
     277    for (int i = 0; (i < 10) && (fabs(dz) > 0.0001); i++) {
     278        // use Newton-Raphson to minimize f(z) - limit = 0
     279        float dqdz = (1.0 + z + z*z/2.0);
     280        float q = (dqdz + z*z*z/6.0);
     281
     282        float f = 1.0 / q;
     283        float dfdz = -dqdz * f / q;
     284
     285        dz = (f - limit) / dfdz;
     286
     287        // fprintf (stderr, "%f %f %f : %f %f\n", f, z, dz, dfdz, q);
     288        z -= dz;
     289        z = PS_MAX(z, 0.0);
     290    }
     291
    305292    psF64 radius = sigma * sqrt (2.0 * z);
    306293
     
    415402bool PM_MODEL_FIT_STATUS (pmModel *model)
    416403{
    417     psF32 dP;
    418404    bool  status;
    419405
     
    421407    psF32 *dPAR = model->dparams->data.F32;
    422408
    423     dP = 0;
    424     dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
    425     dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
    426     dP = sqrt (dP);
    427 
    428409    status = true;
    429     status &= (dP < 0.5);
    430410    status &= (PAR[PM_PAR_I0] > 0);
    431411    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
    432412
    433413    return status;
     414}
     415
     416
     417void PM_MODEL_SET_LIMITS(pmModelLimitsType type)
     418{
     419    switch (type) {
     420      case PM_MODEL_LIMITS_NONE:
     421        paramsMinUse = NULL;
     422        paramsMaxUse = NULL;
     423        limitsApply = true;
     424        break;
     425      case PM_MODEL_LIMITS_IGNORE:
     426        paramsMinUse = NULL;
     427        paramsMaxUse = NULL;
     428        limitsApply = false;
     429        break;
     430      case PM_MODEL_LIMITS_LAX:
     431        paramsMinUse = paramsMinLax;
     432        paramsMaxUse = paramsMaxLax;
     433        limitsApply = true;
     434        break;
     435      case PM_MODEL_LIMITS_MODERATE:
     436        paramsMinUse = paramsMinModerate;
     437        paramsMaxUse = paramsMaxModerate;
     438        limitsApply = true;
     439        break;
     440      case PM_MODEL_LIMITS_STRICT:
     441        paramsMinUse = paramsMinStrict;
     442        paramsMaxUse = paramsMaxStrict;
     443        limitsApply = true;
     444        break;
     445      default:
     446        psAbort("Unrecognised model limits type: %x", type);
     447    }
     448    return;
    434449}
    435450
     
    442457# undef PM_MODEL_PARAMS_FROM_PSF
    443458# undef PM_MODEL_FIT_STATUS
     459# undef PM_MODEL_SET_LIMITS
  • branches/simtest_nebulous_branches/psModules/src/objects/models/pmModel_PS1_V1.c

    r23962 r27840  
    11/******************************************************************************
    2  * this file defines the PS1_V1 source shape model (XXX need a better name!).  Note that these
    3  * model functions are loaded by pmModelGroup.c using 'include', and thus need no 'include'
    4  * statements of their own.  The models use a psVector to represent the set of parameters, with
    5  * the sequence used to specify the meaning of the parameter.  The meaning of the parameters
    6  * may thus vary depending on the specifics of the model.  All models which are used a PSF
    7  * representations share a few parameters, for which # define names are listed in pmModel.h:
     2 * this file defines the PS1_V1 source shape model.  Note that these model functions are loaded
     3 * by pmModelClass.c using 'include', and thus need no 'include' statements of their own.  The
     4 * models use a psVector to represent the set of parameters, with the sequence used to specify
     5 * the meaning of the parameter.  The meaning of the parameters may thus vary depending on the
     6 * specifics of the model.  All models which are used as a PSF representations share a few
     7 * parameters, for which # define names are listed in pmModel.h:
    88
    99   power-law with fitted linear term
     
    2020   *****************************************************************************/
    2121
     22#include <stdio.h>
     23#include <pslib.h>
     24
     25#include "pmMoments.h"
     26#include "pmPeaks.h"
     27#include "pmSource.h"
     28#include "pmModel.h"
     29#include "pmModel_PS1_V1.h"
     30
    2231# define PM_MODEL_FUNC            pmModelFunc_PS1_V1
    2332# define PM_MODEL_FLUX            pmModelFlux_PS1_V1
     
    2837# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_PS1_V1
    2938# define PM_MODEL_FIT_STATUS      pmModelFitStatus_PS1_V1
     39# define PM_MODEL_SET_LIMITS      pmModelSetLimits_PS1_V1
    3040
    3141# define ALPHA   1.666
    3242# define ALPHA_M 0.666
     43
     44// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
     45// 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
     46// values need to be pixel coords
     47
     48// Lax parameter limits
     49static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -1.0 };
     50static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
     51
     52// Moderate parameter limits
     53// Tolerate a small divot (k < 0)
     54static float paramsMinModerate[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -0.05 };
     55static float paramsMaxModerate[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
     56
     57// Strict parameter limits
     58// k = PAR_7 < 0 is very undesirable (big divot in the middle)
     59static float paramsMinStrict[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, 0.0 };
     60static float paramsMaxStrict[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
     61
     62// Parameter limits to use
     63static float *paramsMinUse = paramsMinLax;
     64static float *paramsMaxUse = paramsMaxLax;
     65static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5, 2.0 };
     66
     67static bool limitsApply = true;         // Apply limits?
    3368
    3469psF32 PM_MODEL_FUNC (psVector *deriv,
     
    84119bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
    85120{
    86     float beta_lim = 0, params_min = 0, params_max = 0;
    87     float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
     121    if (!limitsApply) {
     122        return true;
     123    }
     124    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
    88125
    89126    // we need to calculate the limits for SXY specially
     127    float q2 = NAN;
    90128    if (nParam == PM_PAR_SXY) {
    91         f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
    92         f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
    93         q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
     129        float f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
     130        float f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
     131        float q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
    94132        q1 = (q1 < 0.0) ? 0.0 : q1;
    95133        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
    96134        // angle and let f2,f1 fight it out
    97         q2  = 0.5*sqrt (q1);
     135        q2 = 0.5*sqrtf(q1);
    98136    }
    99137
    100138    switch (mode) {
    101     case PS_MINIMIZE_BETA_LIMIT:
    102         switch (nParam) {
    103         case PM_PAR_SKY:
    104             beta_lim = 1000;
    105             break;
    106         case PM_PAR_I0:
    107             beta_lim = 3e6;
    108             break;
    109         case PM_PAR_XPOS:
    110             beta_lim = 5;
    111             break;
    112         case PM_PAR_YPOS:
    113             beta_lim = 5;
    114             break;
    115         case PM_PAR_SXX:
    116             beta_lim = 1.0;
    117             break;
    118         case PM_PAR_SYY:
    119             beta_lim = 1.0;
    120             break;
    121         case PM_PAR_SXY:
    122             beta_lim =  0.5*q2;
    123             break;
    124         case PM_PAR_7:
    125             beta_lim = 2.0;
    126             break;
    127         default:
    128             psAbort("invalid parameter %d for beta test", nParam);
    129         }
    130         if (fabs(beta[nParam]) > fabs(beta_lim)) {
    131             beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
    132             psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
    133                      nParam, beta[nParam], beta_lim);
    134             return false;
    135         }
    136         return true;
    137     case PS_MINIMIZE_PARAM_MIN:
    138         switch (nParam) {
    139         case PM_PAR_SKY:
    140             params_min = -1000;
    141             break;
    142         case PM_PAR_I0:
    143             params_min =   0.01;
    144             break;
    145         case PM_PAR_XPOS:
    146             params_min =  -100;
    147             break;
    148         case PM_PAR_YPOS:
    149             params_min =  -100;
    150             break;
    151         case PM_PAR_SXX:
    152             params_min =   0.5;
    153             break;
    154         case PM_PAR_SYY:
    155             params_min =   0.5;
    156             break;
    157         case PM_PAR_SXY:
    158             params_min =  -q2;
    159             break;
    160         case PM_PAR_7:
    161             params_min =  -1.0;
    162             break;
    163         default:
    164             psAbort("invalid parameter %d for param min test", nParam);
    165         }
    166         if (params[nParam] < params_min) {
    167             params[nParam] = params_min;
    168             psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
    169                      nParam, params[nParam], params_min);
    170             return false;
    171         }
    172         return true;
    173     case PS_MINIMIZE_PARAM_MAX:
    174         switch (nParam) {
    175         case PM_PAR_SKY:
    176             params_max =   1e5;
    177             break;
    178         case PM_PAR_I0:
    179             params_max =   1e8;
    180             break;
    181         case PM_PAR_XPOS:
    182             params_max =   1e4;
    183             break;
    184         case PM_PAR_YPOS:
    185             params_max =   1e4;
    186             break;
    187         case PM_PAR_SXX:
    188             params_max =   100;
    189             break;
    190         case PM_PAR_SYY:
    191             params_max =   100;
    192             break;
    193         case PM_PAR_SXY:
    194             params_max =  +q2;
    195             break;
    196         case PM_PAR_7:
    197             params_max =  20.0;
    198             break;
    199         default:
    200             psAbort("invalid parameter %d for param max test", nParam);
    201         }
    202         if (params[nParam] > params_max) {
    203             params[nParam] = params_max;
    204             psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
    205                      nParam, params[nParam], params_max);
    206             return false;
    207         }
    208         return true;
    209     default:
     139      case PS_MINIMIZE_BETA_LIMIT: {
     140          psAssert(beta, "Require beta to limit beta");
     141          float limit = betaUse[nParam];
     142          if (nParam == PM_PAR_SXY) {
     143              limit *= q2;
     144          }
     145          if (fabs(beta[nParam]) > fabs(limit)) {
     146              beta[nParam] = (beta[nParam] > 0) ? fabs(limit) : -fabs(limit);
     147              psTrace("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
     148                      nParam, beta[nParam], limit);
     149              return false;
     150          }
     151          return true;
     152      }
     153      case PS_MINIMIZE_PARAM_MIN: {
     154          psAssert(params, "Require parameters to limit parameters");
     155          psAssert(paramsMinUse, "Require parameter limits to limit parameters");
     156          float limit = paramsMinUse[nParam];
     157          if (nParam == PM_PAR_SXY) {
     158              limit *= q2;
     159          }
     160          if (params[nParam] < limit) {
     161              params[nParam] = limit;
     162              psTrace("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
     163                      nParam, params[nParam], limit);
     164              return false;
     165          }
     166          return true;
     167      }
     168      case PS_MINIMIZE_PARAM_MAX: {
     169          psAssert(params, "Require parameters to limit parameters");
     170          psAssert(paramsMaxUse, "Require parameter limits to limit parameters");
     171          float limit = paramsMaxUse[nParam];
     172          if (nParam == PM_PAR_SXY) {
     173              limit *= q2;
     174          }
     175          if (params[nParam] > limit) {
     176              params[nParam] = limit;
     177              psTrace("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
     178                      nParam, params[nParam], limit);
     179              return false;
     180          }
     181          return true;
     182      }
     183      default:
    210184        psAbort("invalid choice for limits");
    211185    }
     
    216190
    217191// make an initial guess for parameters
     192// 0.5 PIX: moments and peaks are in pixel coords, thus so are model parameters
    218193bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
    219194{
     
    240215    if (!isfinite(shape.sxy)) return false;
    241216
    242     // XXX turn this off here for now PAR[PM_PAR_SKY]  = moments->Sky;
    243217    PAR[PM_PAR_SKY]  = 0.0;
    244218    PAR[PM_PAR_I0]   = peak->flux;
     
    293267psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
    294268{
    295     psF64 z, f;
     269    psF64 z;
    296270    int Nstep = 0;
    297271    psEllipseShape shape;
     
    299273    psF32 *PAR = params->data.F32;
    300274
    301     if (flux <= 0)
    302         return (1.0);
    303     if (PAR[PM_PAR_I0] <= 0)
    304         return (1.0);
    305     if (flux >= PAR[PM_PAR_I0])
    306         return (1.0);
     275    if (flux <= 0) return 1.0;
     276    if (PAR[PM_PAR_I0] <= 0) return 1.0;
     277    if (flux >= PAR[PM_PAR_I0]) return 1.0;
     278    if (PAR[PM_PAR_7] == 0.0) return powf(PAR[PM_PAR_I0] / flux - 1.0, 1.0 / ALPHA);
    307279
    308280    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
     
    320292
    321293    // choose a z value guaranteed to be beyond our limit
    322     float z0 = pow((1.0 / limit), (1.0 / ALPHA));
    323     float z1 = (1.0 / limit) / PAR[PM_PAR_7];
    324     z1 = PS_MAX (z0, z1);
    325     z0 = 0.0;
    326 
    327     // perform a type of bisection to find the value
    328     float f0 = 1.0 / (1 + PAR[PM_PAR_7]*z0 + pow(z0, ALPHA));
    329     float f1 = 1.0 / (1 + PAR[PM_PAR_7]*z1 + pow(z1, ALPHA));
    330     while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
    331         z = 0.5*(z0 + z1);
    332         f = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
    333         if (f > limit) {
    334             z0 = z;
    335             f0 = f;
    336         } else {
    337             z1 = z;
    338             f1 = f;
    339         }
    340         Nstep ++;
     294    float z0 = 0.0;
     295    float z1 = pow((1.0 / limit), (1.0 / ALPHA));
     296    psAssert (isfinite(z1), "fix this code: z1 should not be nan for %f", PAR[PM_PAR_7]);
     297    if (PAR[PM_PAR_7] < 0.0) z1 *= 2.0;
     298
     299    // starting guess:
     300    z = 0.5*(z0 + z1);
     301    float dz = 1.0;
     302
     303    // use Newton-Raphson to minimize f(z) - limit = 0
     304    for (int i = 0; (i < 10) && (fabs(dz) > 0.0001); i++) {
     305        float q = (1.0 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
     306        float dqdz = (PAR[PM_PAR_7] + ALPHA*pow(z, ALPHA - 1.0));
     307
     308        float f = 1.0 / q;
     309        float dfdz = -dqdz * f / q;
     310
     311        dz = (f - limit) / dfdz;
     312
     313        // fprintf (stderr, "%f %f %f : %f %f\n", f, z, dz, dfdz, q);
     314        z -= dz;
     315        z = PS_MAX(z, 0.0);
    341316    }
    342317    psF64 radius = sigma * sqrt (2.0 * z);
     
    370345    // convert to shape terms (SXX,SYY,SXY)
    371346    if (!pmPSF_FitToModel (out, 0.1)) {
    372         // psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
    373347        psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
    374348        return false;
     
    448422bool PM_MODEL_FIT_STATUS (pmModel *model)
    449423{
    450 
    451     psF32 dP;
    452424    bool  status;
    453425
     
    455427    psF32 *dPAR = model->dparams->data.F32;
    456428
    457     dP = 0;
    458     dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
    459     dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
    460     dP = sqrt (dP);
    461 
    462429    status = true;
    463 //    status &= (dP < 0.5);
    464430    status &= (PAR[PM_PAR_I0] > 0);
    465431    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
    466432
    467433    return status;
     434}
     435
     436
     437void PM_MODEL_SET_LIMITS(pmModelLimitsType type)
     438{
     439    switch (type) {
     440      case PM_MODEL_LIMITS_NONE:
     441        paramsMinUse = NULL;
     442        paramsMaxUse = NULL;
     443        limitsApply = true;
     444        break;
     445      case PM_MODEL_LIMITS_IGNORE:
     446        paramsMinUse = NULL;
     447        paramsMaxUse = NULL;
     448        limitsApply = false;
     449        break;
     450      case PM_MODEL_LIMITS_LAX:
     451        paramsMinUse = paramsMinLax;
     452        paramsMaxUse = paramsMaxLax;
     453        limitsApply = true;
     454        break;
     455      case PM_MODEL_LIMITS_MODERATE:
     456        paramsMinUse = paramsMinModerate;
     457        paramsMaxUse = paramsMaxModerate;
     458        limitsApply = true;
     459        break;
     460      case PM_MODEL_LIMITS_STRICT:
     461        paramsMinUse = paramsMinStrict;
     462        paramsMaxUse = paramsMaxStrict;
     463        limitsApply = true;
     464        break;
     465      default:
     466        psAbort("Unrecognised model limits type: %x", type);
     467    }
     468    return;
    468469}
    469470
     
    476477# undef PM_MODEL_PARAMS_FROM_PSF
    477478# undef PM_MODEL_FIT_STATUS
     479# undef PM_MODEL_SET_LIMITS
    478480# undef ALPHA
    479481# undef ALPHA_M
  • branches/simtest_nebulous_branches/psModules/src/objects/models/pmModel_QGAUSS.c

    r20001 r27840  
    11/******************************************************************************
    22 * this file defines the QGAUSS source shape model (XXX need a better name!).  Note that these
    3  * model functions are loaded by pmModelGroup.c using 'include', and thus need no 'include'
     3 * model functions are loaded by pmModelClass.c using 'include', and thus need no 'include'
    44 * statements of their own.  The models use a psVector to represent the set of parameters, with
    55 * the sequence used to specify the meaning of the parameter.  The meaning of the parameters
     
    2020   *****************************************************************************/
    2121
     22#include <stdio.h>
     23#include <pslib.h>
     24
     25#include "pmMoments.h"
     26#include "pmPeaks.h"
     27#include "pmSource.h"
     28#include "pmModel.h"
     29#include "pmModel_QGAUSS.h"
     30
    2231# define PM_MODEL_FUNC            pmModelFunc_QGAUSS
    2332# define PM_MODEL_FLUX            pmModelFlux_QGAUSS
     
    2837# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_QGAUSS
    2938# define PM_MODEL_FIT_STATUS      pmModelFitStatus_QGAUSS
     39# define PM_MODEL_SET_LIMITS      pmModelSetLimits_QGAUSS
     40
     41# define ALPHA   2.250
     42# define ALPHA_M 1.250
     43
     44// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
     45// 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
     46// values need to be pixel coords
     47
     48// Lax parameter limits
     49static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -1.0 };
     50static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
     51
     52// Moderate parameter limits
     53// Tolerate a small divot (k < 0)
     54static float paramsMinModerate[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -0.05 };
     55static float paramsMaxModerate[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
     56
     57// Strict parameter limits
     58// k = PAR_7 < 0 is very undesirable (big divot in the middle)
     59static float paramsMinStrict[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, 0.0 };
     60static float paramsMaxStrict[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
     61
     62// Parameter limits to use
     63static float *paramsMinUse = paramsMinLax;
     64static float *paramsMaxUse = paramsMaxLax;
     65static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5, 2.0 };
     66
     67static bool limitsApply = true;         // Apply limits?
    3068
    3169psF32 PM_MODEL_FUNC (psVector *deriv,
     
    4886    assert (z >= 0);
    4987
    50     psF32 zp = pow(z,1.25);
     88    psF32 zp = pow(z,ALPHA_M);
    5189    psF32 r  = 1.0 / (1 + PAR[PM_PAR_7]*z + z*zp);
    5290
     
    5997        // note difference from a pure gaussian: q = params->data.F32[PM_PAR_I0]*r
    6098        psF32 t = r1*r;
    61         psF32 q = t*(PAR[PM_PAR_7] + 2.25*zp);
     99        psF32 q = t*(PAR[PM_PAR_7] + ALPHA*zp);
    62100
    63101        dPAR[PM_PAR_SKY]  = +1.0;
     
    79117# define AR_MAX 20.0
    80118# define AR_RATIO 0.99
     119
    81120bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
    82121{
    83     float beta_lim = 0, params_min = 0, params_max = 0;
    84     float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
     122    if (!limitsApply) {
     123        return true;
     124    }
     125    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
    85126
    86127    // we need to calculate the limits for SXY specially
     128    float q2 = NAN;
    87129    if (nParam == PM_PAR_SXY) {
    88         f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
    89         f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
    90         q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
     130        float f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
     131        float f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
     132        float q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
    91133        q1 = (q1 < 0.0) ? 0.0 : q1;
    92134        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
    93135        // angle and let f2,f1 fight it out
    94         q2  = 0.5*sqrt (q1);
     136        q2 = 0.5*sqrtf(q1);
    95137    }
    96138
    97139    switch (mode) {
    98     case PS_MINIMIZE_BETA_LIMIT:
    99         switch (nParam) {
    100         case PM_PAR_SKY:
    101             beta_lim = 1000;
    102             break;
    103         case PM_PAR_I0:
    104             beta_lim = 3e6;
    105             break;
    106         case PM_PAR_XPOS:
    107             beta_lim = 5;
    108             break;
    109         case PM_PAR_YPOS:
    110             beta_lim = 5;
    111             break;
    112         case PM_PAR_SXX:
    113             beta_lim = 1.0;
    114             break;
    115         case PM_PAR_SYY:
    116             beta_lim = 1.0;
    117             break;
    118         case PM_PAR_SXY:
    119             beta_lim =  0.5*q2;
    120             break;
    121         case PM_PAR_7:
    122             beta_lim = 2.0;
    123             break;
    124         default:
    125             psAbort("invalid parameter %d for beta test", nParam);
    126         }
    127         if (fabs(beta[nParam]) > fabs(beta_lim)) {
    128             beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
    129             psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
    130                      nParam, beta[nParam], beta_lim);
    131             return false;
    132         }
    133         return true;
    134     case PS_MINIMIZE_PARAM_MIN:
    135         switch (nParam) {
    136         case PM_PAR_SKY:
    137             params_min = -1000;
    138             break;
    139         case PM_PAR_I0:
    140             params_min =   0.01;
    141             break;
    142         case PM_PAR_XPOS:
    143             params_min =  -100;
    144             break;
    145         case PM_PAR_YPOS:
    146             params_min =  -100;
    147             break;
    148         case PM_PAR_SXX:
    149             params_min =   0.5;
    150             break;
    151         case PM_PAR_SYY:
    152             params_min =   0.5;
    153             break;
    154         case PM_PAR_SXY:
    155             params_min =  -q2;
    156             break;
    157         case PM_PAR_7:
    158             params_min =   0.1;
    159             break;
    160         default:
    161             psAbort("invalid parameter %d for param min test", nParam);
    162         }
    163         if (params[nParam] < params_min) {
    164             params[nParam] = params_min;
    165             psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
    166                      nParam, params[nParam], params_min);
    167             return false;
    168         }
    169         return true;
    170     case PS_MINIMIZE_PARAM_MAX:
    171         switch (nParam) {
    172         case PM_PAR_SKY:
    173             params_max =   1e5;
    174             break;
    175         case PM_PAR_I0:
    176             params_max =   1e8;
    177             break;
    178         case PM_PAR_XPOS:
    179             params_max =   1e4;
    180             break;
    181         case PM_PAR_YPOS:
    182             params_max =   1e4;
    183             break;
    184         case PM_PAR_SXX:
    185             params_max =   100;
    186             break;
    187         case PM_PAR_SYY:
    188             params_max =   100;
    189             break;
    190         case PM_PAR_SXY:
    191             params_max =  +q2;
    192             break;
    193         case PM_PAR_7:
    194             params_max =  20.0;
    195             break;
    196         default:
    197             psAbort("invalid parameter %d for param max test", nParam);
    198         }
    199         if (params[nParam] > params_max) {
    200             params[nParam] = params_max;
    201             psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
    202                      nParam, params[nParam], params_max);
    203             return false;
    204         }
    205         return true;
     140      case PS_MINIMIZE_BETA_LIMIT: {
     141          psAssert(beta, "Require beta to limit beta");
     142          float limit = betaUse[nParam];
     143          if (nParam == PM_PAR_SXY) {
     144              limit *= q2;
     145          }
     146          if (fabs(beta[nParam]) > fabs(limit)) {
     147              beta[nParam] = (beta[nParam] > 0) ? fabs(limit) : -fabs(limit);
     148              psTrace("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
     149                      nParam, beta[nParam], limit);
     150              return false;
     151          }
     152          return true;
     153      }
     154      case PS_MINIMIZE_PARAM_MIN: {
     155          psAssert(params, "Require parameters to limit parameters");
     156          psAssert(paramsMinUse, "Require parameter limits to limit parameters");
     157          float limit = paramsMinUse[nParam];
     158          if (nParam == PM_PAR_SXY) {
     159              limit *= q2;
     160          }
     161          if (params[nParam] < limit) {
     162              params[nParam] = limit;
     163              psTrace("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
     164                      nParam, params[nParam], limit);
     165              return false;
     166          }
     167          return true;
     168      }
     169      case PS_MINIMIZE_PARAM_MAX: {
     170          psAssert(params, "Require parameters to limit parameters");
     171          psAssert(paramsMaxUse, "Require parameter limits to limit parameters");
     172          float limit = paramsMaxUse[nParam];
     173          if (nParam == PM_PAR_SXY) {
     174              limit *= q2;
     175          }
     176          if (params[nParam] > limit) {
     177              params[nParam] = limit;
     178              psTrace("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
     179                      nParam, params[nParam], limit);
     180              return false;
     181          }
     182          return true;
     183      }
    206184    default:
    207185        psAbort("invalid choice for limits");
     
    213191
    214192// make an initial guess for parameters
     193// 0.5 PIX: moments and peaks are in pixel coords, thus so are model parameters
    215194bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
    216195{
     
    237216    if (!isfinite(shape.sxy)) return false;
    238217
    239     // XXX turn this off here for now PAR[PM_PAR_SKY]  = moments->Sky;
    240218    PAR[PM_PAR_SKY]  = 0.0;
    241219    PAR[PM_PAR_I0]   = peak->flux;
     
    273251    float f1, f2;
    274252    for (z = DZ; z < 50; z += DZ) {
    275         f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
     253        f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
    276254        z += DZ;
    277         f2 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
     255        f2 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
    278256        norm += f0 + 4*f1 + f2;
    279257        f0 = f2;
     
    290268psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
    291269{
    292     psF64 z, f;
     270    psF64 z;
    293271    int Nstep = 0;
    294272    psEllipseShape shape;
     
    296274    psF32 *PAR = params->data.F32;
    297275
    298     if (flux <= 0)
    299         return (1.0);
    300     if (PAR[PM_PAR_I0] <= 0)
    301         return (1.0);
    302     if (flux >= PAR[PM_PAR_I0])
    303         return (1.0);
     276    if (flux <= 0) return 1.0;
     277    if (PAR[PM_PAR_I0] <= 0) return 1.0;
     278    if (flux >= PAR[PM_PAR_I0]) return 1.0;
     279    if (PAR[PM_PAR_7] == 0.0) return powf(PAR[PM_PAR_I0] / flux - 1.0, 1.0 / ALPHA);
    304280
    305281    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
     
    317293
    318294    // choose a z value guaranteed to be beyond our limit
    319     float z0 = pow((1.0 / limit), (1.0 / 2.25));
    320     float z1 = (1.0 / limit) / PAR[PM_PAR_7];
    321     z1 = PS_MAX (z0, z1);
    322     z0 = 0.0;
    323 
    324     // perform a type of bisection to find the value
    325     float f0 = 1.0 / (1 + PAR[PM_PAR_7]*z0 + pow(z0, 2.25));
    326     float f1 = 1.0 / (1 + PAR[PM_PAR_7]*z1 + pow(z1, 2.25));
    327     while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
    328         z = 0.5*(z0 + z1);
    329         f = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
    330         if (f > limit) {
    331             z0 = z;
    332             f0 = f;
    333         } else {
    334             z1 = z;
    335             f1 = f;
    336         }
    337         Nstep ++;
     295    float z0 = 0.0;
     296    float z1 = pow((1.0 / limit), (1.0 / ALPHA));
     297    psAssert (isfinite(z1), "fix this code: z1 should not be nan for %f", PAR[PM_PAR_7]);
     298    if (PAR[PM_PAR_7] < 0.0) z1 *= 2.0;
     299
     300    // starting guess:
     301    z = 0.5*(z0 + z1);
     302    float dz = 1.0;
     303
     304    // use Newton-Raphson to minimize f(z) - limit = 0
     305    for (int i = 0; (i < 10) && (fabs(dz) > 0.0001); i++) {
     306        float q = (1.0 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
     307        float dqdz = (PAR[PM_PAR_7] + ALPHA*pow(z, ALPHA - 1.0));
     308
     309        float f = 1.0 / q;
     310        float dfdz = -dqdz * f / q;
     311
     312        dz = (f - limit) / dfdz;
     313
     314        // fprintf (stderr, "%f %f %f : %f %f\n", f, z, dz, dfdz, q);
     315        z -= dz;
     316        z = PS_MAX(z, 0.0);
    338317    }
    339318    psF64 radius = sigma * sqrt (2.0 * z);
     
    444423bool PM_MODEL_FIT_STATUS (pmModel *model)
    445424{
    446 
    447     psF32 dP;
    448425    bool  status;
    449426
     
    451428    psF32 *dPAR = model->dparams->data.F32;
    452429
    453     dP = 0;
    454     dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
    455     dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
    456     dP = sqrt (dP);
    457 
    458430    status = true;
    459 //    status &= (dP < 0.5);
    460431    status &= (PAR[PM_PAR_I0] > 0);
    461432    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
    462433
    463434    return status;
     435}
     436
     437
     438void PM_MODEL_SET_LIMITS(pmModelLimitsType type)
     439{
     440    switch (type) {
     441      case PM_MODEL_LIMITS_NONE:
     442        paramsMinUse = NULL;
     443        paramsMaxUse = NULL;
     444        limitsApply = true;
     445        break;
     446      case PM_MODEL_LIMITS_IGNORE:
     447        paramsMinUse = NULL;
     448        paramsMaxUse = NULL;
     449        limitsApply = false;
     450        break;
     451      case PM_MODEL_LIMITS_LAX:
     452        paramsMinUse = paramsMinLax;
     453        paramsMaxUse = paramsMaxLax;
     454        limitsApply = true;
     455        break;
     456      case PM_MODEL_LIMITS_MODERATE:
     457        paramsMinUse = paramsMinModerate;
     458        paramsMaxUse = paramsMaxModerate;
     459        limitsApply = true;
     460        break;
     461      case PM_MODEL_LIMITS_STRICT:
     462        paramsMinUse = paramsMinStrict;
     463        paramsMaxUse = paramsMaxStrict;
     464        limitsApply = true;
     465        break;
     466      default:
     467        psAbort("Unrecognised model limits type: %x", type);
     468    }
     469    return;
    464470}
    465471
     
    472478# undef PM_MODEL_PARAMS_FROM_PSF
    473479# undef PM_MODEL_FIT_STATUS
     480# undef PM_MODEL_SET_LIMITS
     481# undef ALPHA
     482# undef ALPHA_M
  • branches/simtest_nebulous_branches/psModules/src/objects/models/pmModel_RGAUSS.c

    r20001 r27840  
    11/******************************************************************************
    22 * this file defines the RGAUSS source shape model (XXX need a better name!).  Note that these
    3  * model functions are loaded by pmModelGroup.c using 'include', and thus need no 'include'
     3 * model functions are loaded by pmModelClass.c using 'include', and thus need no 'include'
    44 * statements of their own.  The models use a psVector to represent the set of parameters, with
    55 * the sequence used to specify the meaning of the parameter.  The meaning of the parameters
    6  * may thus vary depending on the specifics of the model.  All models which are used a PSF
     6 * may thus vary depending on the specifics of the model.  All models which are used as a PSF
    77 * representations share a few parameters, for which # define names are listed in pmModel.h:
    88
     
    2020 *****************************************************************************/
    2121
     22#include <stdio.h>
     23#include <pslib.h>
     24
     25#include "pmMoments.h"
     26#include "pmPeaks.h"
     27#include "pmSource.h"
     28#include "pmModel.h"
     29#include "pmModel_RGAUSS.h"
     30
    2231# define PM_MODEL_FUNC            pmModelFunc_RGAUSS
    2332# define PM_MODEL_FLUX            pmModelFlux_RGAUSS
     
    2837# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_RGAUSS
    2938# define PM_MODEL_FIT_STATUS      pmModelFitStatus_RGAUSS
     39# define PM_MODEL_SET_LIMITS      pmModelSetLimits_RGAUSS
     40
     41// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
     42// 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
     43// values need to be pixel coords
     44
     45// Lax parameter limits
     46static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, 1.25 };
     47static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 4.0 };
     48
     49// Moderate parameter limits
     50static float *paramsMinModerate = paramsMinLax;
     51static float *paramsMaxModerate = paramsMaxLax;
     52
     53// Strict parameter limits
     54static float *paramsMinStrict = paramsMinLax;
     55static float *paramsMaxStrict = paramsMaxLax;
     56
     57// Parameter limits to use
     58static float *paramsMinUse = paramsMinLax;
     59static float *paramsMaxUse = paramsMaxLax;
     60static float betaUse[] = { 1000, 3e6, 5, 5, 0.5, 0.5, 0.5, 0.5 };
     61
     62static bool limitsApply = true;         // Apply limits?
    3063
    3164psF32 PM_MODEL_FUNC (psVector *deriv,
     
    6295        dPAR[PM_PAR_SXY] = -q*X*Y;
    6396
    64         // this model derivative is undefined at z = 0.0, but is actually 0.0
     97        // this model derivative is undefined at z = 0.0, but the limit is zero as z -> 0.0
    6598        dPAR[PM_PAR_7] = (z == 0.0) ? 0.0 : -5.0*t*log(z)*p*z;
    6699    }
     
    73106# define AR_MAX 20.0
    74107# define AR_RATIO 0.99
     108
    75109bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
    76110{
    77     float beta_lim = 0, params_min = 0, params_max = 0;
    78     float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
     111    if (!limitsApply) {
     112        return true;
     113    }
     114    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
    79115
    80116    // we need to calculate the limits for SXY specially
     117    float q2 = NAN;
    81118    if (nParam == PM_PAR_SXY) {
    82         f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
    83         f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
    84         q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
     119        float f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
     120        float f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
     121        float q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
    85122        q1 = (q1 < 0.0) ? 0.0 : q1;
    86123        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
    87124        // angle and let f2,f1 fight it out
    88         q2  = 0.5*sqrt (q1);
     125        q2 = 0.5*sqrtf(q1);
    89126    }
    90127
    91128    switch (mode) {
    92     case PS_MINIMIZE_BETA_LIMIT:
    93         switch (nParam) {
    94         case PM_PAR_SKY:
    95             beta_lim = 1000;
    96             break;
    97         case PM_PAR_I0:
    98             beta_lim = 3e6;
    99             break;
    100         case PM_PAR_XPOS:
    101             beta_lim = 5;
    102             break;
    103         case PM_PAR_YPOS:
    104             beta_lim = 5;
    105             break;
    106         case PM_PAR_SXX:
    107             beta_lim = 0.5;
    108             break;
    109         case PM_PAR_SYY:
    110             beta_lim = 0.5;
    111             break;
    112         case PM_PAR_SXY:
    113             beta_lim =  0.5*q2;
    114             break;
    115         case PM_PAR_7:
    116             beta_lim = 0.5;
    117             break;
    118         default:
    119             psAbort("invalid parameter %d for beta test", nParam);
    120         }
    121         if (fabs(beta[nParam]) > fabs(beta_lim)) {
    122             beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
    123             psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
    124                      nParam, beta[nParam], beta_lim);
    125             return false;
    126         }
    127         return true;
    128     case PS_MINIMIZE_PARAM_MIN:
    129         switch (nParam) {
    130         case PM_PAR_SKY:
    131             params_min = -1000;
    132             break;
    133         case PM_PAR_I0:
    134             params_min =   0.01;
    135             break;
    136         case PM_PAR_XPOS:
    137             params_min =  -100;
    138             break;
    139         case PM_PAR_YPOS:
    140             params_min =  -100;
    141             break;
    142         case PM_PAR_SXX:
    143             params_min =   0.5;
    144             break;
    145         case PM_PAR_SYY:
    146             params_min =   0.5;
    147             break;
    148         case PM_PAR_SXY:
    149             params_min =  -q2;
    150             break;
    151         case PM_PAR_7:
    152             params_min =   1.25;
    153             break;
    154         default:
    155             psAbort("invalid parameter %d for param min test", nParam);
    156         }
    157         if (params[nParam] < params_min) {
    158             params[nParam] = params_min;
    159             psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
    160                      nParam, params[nParam], params_min);
    161             return false;
    162         }
    163         return true;
    164     case PS_MINIMIZE_PARAM_MAX:
    165         switch (nParam) {
    166         case PM_PAR_SKY:
    167             params_max =   1e5;
    168             break;
    169         case PM_PAR_I0:
    170             params_max =   1e8;
    171             break;
    172         case PM_PAR_XPOS:
    173             params_max =   1e4;
    174             break;
    175         case PM_PAR_YPOS:
    176             params_max =   1e4;
    177             break;
    178         case PM_PAR_SXX:
    179             params_max =   100;
    180             break;
    181         case PM_PAR_SYY:
    182             params_max =   100;
    183             break;
    184         case PM_PAR_SXY:
    185             params_max =  +q2;
    186             break;
    187         case PM_PAR_7:
    188             params_max =  4.0;
    189             break;
    190         default:
    191             psAbort("invalid parameter %d for param max test", nParam);
    192         }
    193         if (params[nParam] > params_max) {
    194             params[nParam] = params_max;
    195             psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
    196                      nParam, params[nParam], params_max);
    197             return false;
    198         }
    199         return true;
    200     default:
     129      case PS_MINIMIZE_BETA_LIMIT: {
     130          psAssert(beta, "Require beta to limit beta");
     131          float limit = betaUse[nParam];
     132          if (nParam == PM_PAR_SXY) {
     133              limit *= q2;
     134          }
     135          if (fabs(beta[nParam]) > fabs(limit)) {
     136              beta[nParam] = (beta[nParam] > 0) ? fabs(limit) : -fabs(limit);
     137              psTrace("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
     138                      nParam, beta[nParam], limit);
     139              return false;
     140          }
     141          return true;
     142      }
     143      case PS_MINIMIZE_PARAM_MIN: {
     144          psAssert(params, "Require parameters to limit parameters");
     145          psAssert(paramsMinUse, "Require parameter limits to limit parameters");
     146          float limit = paramsMinUse[nParam];
     147          if (nParam == PM_PAR_SXY) {
     148              limit *= q2;
     149          }
     150          if (params[nParam] < limit) {
     151              params[nParam] = limit;
     152              psTrace("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
     153                      nParam, params[nParam], limit);
     154              return false;
     155          }
     156          return true;
     157      }
     158      case PS_MINIMIZE_PARAM_MAX: {
     159          psAssert(params, "Require parameters to limit parameters");
     160          psAssert(paramsMaxUse, "Require parameter limits to limit parameters");
     161          float limit = paramsMaxUse[nParam];
     162          if (nParam == PM_PAR_SXY) {
     163              limit *= q2;
     164          }
     165          if (params[nParam] > limit) {
     166              params[nParam] = limit;
     167              psTrace("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
     168                      nParam, params[nParam], limit);
     169              return false;
     170          }
     171          return true;
     172      }
     173      default:
    201174        psAbort("invalid choice for limits");
    202175    }
     
    205178}
    206179
     180
    207181// make an initial guess for parameters
     182// 0.5 PIX: moments and peaks are in pixel coords, thus so are model parameters
    208183bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
    209184{
     
    230205    if (!isfinite(shape.sxy)) return false;
    231206
    232     PAR[PM_PAR_SKY]  = moments->Sky;
     207    PAR[PM_PAR_SKY]  = 0.0;
    233208    PAR[PM_PAR_I0]   = peak->flux;
    234209    PAR[PM_PAR_XPOS] = peak->xf;
     
    282257psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
    283258{
    284     psF64 z, f;
     259    psF64 z;
    285260    int Nstep = 0;
    286261    psEllipseShape shape;
     
    310285    // choose a z value guaranteed to be beyond our limit
    311286    float z0 = pow((1.0 / limit), (1.0 / PAR[PM_PAR_7]));
     287    psAssert (isfinite(z0), "fix this code: z0 should not be nan for %f", PAR[PM_PAR_7]);
    312288    float z1 = (1.0 / limit);
     289    psAssert (isfinite(z1), "fix this code: z1 should not be nan for %f", PAR[PM_PAR_7]);
    313290    z1 = PS_MAX (z0, z1);
    314291    z0 = 0.0;
    315292
    316     // perform a type of bisection to find the value
    317     float f0 = 1.0 / (1 + z0 + pow(z0, PAR[PM_PAR_7]));
    318     float f1 = 1.0 / (1 + z1 + pow(z1, PAR[PM_PAR_7]));
    319     while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
    320         z = 0.5*(z0 + z1);
    321         f = 1.0 / (1 + z + pow(z, PAR[PM_PAR_7]));
    322         if (f > limit) {
    323             z0 = z;
    324             f0 = f;
    325         } else {
    326             z1 = z;
    327             f1 = f;
    328         }
    329         Nstep ++;
    330     }
     293    // starting guess:
     294    z = 0.5*(z0 + z1);
     295    float dz = 1.0;
     296
     297    for (int i = 0; (i < 10) && (fabs(dz) > 0.0001); i++) {
     298        // use Newton-Raphson to minimize f(z) - limit = 0
     299        float q = (1 + z + pow(z,PAR[PM_PAR_7]));
     300        float dqdz = (1.0 + PAR[PM_PAR_7]*pow(z,PAR[PM_PAR_7] - 1.0));
     301
     302        float f = 1.0 / q;
     303        float dfdz = -dqdz * f / q;
     304
     305        dz = (f - limit) / dfdz;
     306
     307        // fprintf (stderr, "%f %f %f : %f %f\n", f, z, dz, dfdz, q);
     308        z -= dz;
     309        z = PS_MAX(z, 0.0);
     310    }
     311
    331312    psF64 radius = sigma * sqrt (2.0 * z);
    332313
     
    436417bool PM_MODEL_FIT_STATUS (pmModel *model)
    437418{
    438 
    439     psF32 dP;
    440419    bool  status;
    441420
     
    443422    psF32 *dPAR = model->dparams->data.F32;
    444423
    445     dP = 0;
    446     dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
    447     dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
    448     dP = sqrt (dP);
    449 
    450424    status = true;
    451     status &= (dP < 0.5);
    452425    status &= (PAR[PM_PAR_I0] > 0);
    453426    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
    454427
    455428    return status;
     429}
     430
     431
     432void PM_MODEL_SET_LIMITS(pmModelLimitsType type)
     433{
     434    switch (type) {
     435      case PM_MODEL_LIMITS_NONE:
     436        paramsMinUse = NULL;
     437        paramsMaxUse = NULL;
     438        limitsApply = true;
     439        break;
     440      case PM_MODEL_LIMITS_IGNORE:
     441        paramsMinUse = NULL;
     442        paramsMaxUse = NULL;
     443        limitsApply = false;
     444        break;
     445      case PM_MODEL_LIMITS_LAX:
     446        paramsMinUse = paramsMinLax;
     447        paramsMaxUse = paramsMaxLax;
     448        limitsApply = true;
     449        break;
     450      case PM_MODEL_LIMITS_MODERATE:
     451        paramsMinUse = paramsMinModerate;
     452        paramsMaxUse = paramsMaxModerate;
     453        limitsApply = true;
     454        break;
     455      case PM_MODEL_LIMITS_STRICT:
     456        paramsMinUse = paramsMinStrict;
     457        paramsMaxUse = paramsMaxStrict;
     458        limitsApply = true;
     459        break;
     460      default:
     461        psAbort("Unrecognised model limits type: %x", type);
     462    }
     463    return;
    456464}
    457465
     
    464472# undef PM_MODEL_PARAMS_FROM_PSF
    465473# undef PM_MODEL_FIT_STATUS
     474# undef PM_MODEL_SET_LIMITS
  • branches/simtest_nebulous_branches/psModules/src/objects/models/pmModel_SERSIC.c

    r20001 r27840  
    11/******************************************************************************
    22 * this file defines the SERSIC source shape model.  Note that these model functions are loaded
    3  * by pmModelGroup.c using 'include', and thus need no 'include' statements of their own.  The
     3 * by pmModelClass.c using 'include', and thus need no 'include' statements of their own.  The
    44 * models use a psVector to represent the set of parameters, with the sequence used to specify
    55 * the meaning of the parameter.  The meaning of the parameters may thus vary depending on the
    6  * specifics of the model.  All models which are used a PSF representations share a few
     6 * specifics of the model.  All models which are used as a PSF representations share a few
    77 * parameters, for which # define names are listed in pmModel.h:
    88
     
    2323   *****************************************************************************/
    2424
     25#include <stdio.h>
     26#include <pslib.h>
     27
     28#include "pmMoments.h"
     29#include "pmPeaks.h"
     30#include "pmSource.h"
     31#include "pmModel.h"
     32#include "pmModel_SERSIC.h"
     33
    2534# define PM_MODEL_FUNC            pmModelFunc_SERSIC
    2635# define PM_MODEL_FLUX            pmModelFlux_SERSIC
     
    3140# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_SERSIC
    3241# define PM_MODEL_FIT_STATUS      pmModelFitStatus_SERSIC
     42# define PM_MODEL_SET_LIMITS      pmModelSetLimits_SERSIC
     43
     44// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
     45// 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
     46// values need to be pixel coords
     47
     48// Lax parameter limits
     49static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.05, 0.05, -1.0, 0.05 };
     50static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 4.0 };
     51
     52// Moderate parameter limits
     53static float *paramsMinModerate = paramsMinLax;
     54static float *paramsMaxModerate = paramsMaxLax;
     55
     56// Strict parameter limits
     57static float *paramsMinStrict = paramsMinLax;
     58static float *paramsMaxStrict = paramsMaxLax;
     59
     60// Parameter limits to use
     61static float *paramsMinUse = paramsMinLax;
     62static float *paramsMaxUse = paramsMaxLax;
     63static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5, 2.0 };
     64
     65static bool limitsApply = true;         // Apply limits?
    3366
    3467psF32 PM_MODEL_FUNC (psVector *deriv,
     
    91124bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
    92125{
    93     float beta_lim = 0, params_min = 0, params_max = 0;
    94     float f1 = 0, f2 = 0, q1 = 0, q2 = 0;
     126    if (!limitsApply) {
     127        return true;
     128    }
     129    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
    95130
    96131    // we need to calculate the limits for SXY specially
     132    float q2 = NAN;
    97133    if (nParam == PM_PAR_SXY) {
    98         f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
    99         f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
    100         q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
     134        float f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
     135        float f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
     136        float q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
    101137        q1 = (q1 < 0.0) ? 0.0 : q1;
    102138        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
    103139        // angle and let f2,f1 fight it out
    104         q2  = 0.5*sqrt (q1);
     140        q2 = 0.5*sqrtf(q1);
    105141    }
    106142
    107143    switch (mode) {
    108     case PS_MINIMIZE_BETA_LIMIT:
    109         switch (nParam) {
    110         case PM_PAR_SKY:
    111             beta_lim = 1000;
    112             break;
    113         case PM_PAR_I0:
    114             beta_lim = 3e6;
    115             break;
    116         case PM_PAR_XPOS:
    117             beta_lim = 5;
    118             break;
    119         case PM_PAR_YPOS:
    120             beta_lim = 5;
    121             break;
    122         case PM_PAR_SXX:
    123             beta_lim = 1.0;
    124             break;
    125         case PM_PAR_SYY:
    126             beta_lim = 1.0;
    127             break;
    128         case PM_PAR_SXY:
    129             beta_lim =  0.5*q2;
    130             break;
    131         case PM_PAR_7:
    132             beta_lim = 2.0;
    133             break;
    134         default:
    135             psAbort("invalid parameter %d for beta test", nParam);
    136         }
    137         if (fabs(beta[nParam]) > fabs(beta_lim)) {
    138             beta[nParam] = (beta[nParam] > 0) ? fabs(beta_lim) : -fabs(beta_lim);
    139             psTrace ("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
    140                      nParam, beta[nParam], beta_lim);
    141             return false;
    142         }
    143         return true;
    144     case PS_MINIMIZE_PARAM_MIN:
    145         switch (nParam) {
    146         case PM_PAR_SKY:
    147             params_min = -1000;
    148             break;
    149         case PM_PAR_I0:
    150             params_min =     0.01;
    151             break;
    152         case PM_PAR_XPOS:
    153             params_min =  -100;
    154             break;
    155         case PM_PAR_YPOS:
    156             params_min =  -100;
    157             break;
    158         case PM_PAR_SXX:
    159             params_min =   0.05;
    160             break;
    161         case PM_PAR_SYY:
    162             params_min =   0.05;
    163             break;
    164         case PM_PAR_SXY:
    165             params_min =  -q2;
    166             break;
    167         case PM_PAR_7:
    168             params_min =   0.05;
    169             break;
    170         default:
    171             psAbort("invalid parameter %d for param min test", nParam);
    172         }
    173         if (params[nParam] < params_min) {
    174             params[nParam] = params_min;
    175             psTrace ("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
    176                      nParam, params[nParam], params_min);
    177             return false;
    178         }
    179         return true;
    180     case PS_MINIMIZE_PARAM_MAX:
    181         switch (nParam) {
    182         case PM_PAR_SKY:
    183             params_max =   1e5;
    184             break;
    185         case PM_PAR_I0:
    186             params_max =   1e8;
    187             break;
    188         case PM_PAR_XPOS:
    189             params_max =   1e4;
    190             break;
    191         case PM_PAR_YPOS:
    192             params_max =   1e4;
    193             break;
    194         case PM_PAR_SXX:
    195             params_max =   100;
    196             break;
    197         case PM_PAR_SYY:
    198             params_max =   100;
    199             break;
    200         case PM_PAR_SXY:
    201             params_max =  +q2;
    202             break;
    203         case PM_PAR_7:
    204             params_max =   4.0;
    205             break;
    206         default:
    207             psAbort("invalid parameter %d for param max test", nParam);
    208         }
    209         if (params[nParam] > params_max) {
    210             params[nParam] = params_max;
    211             psTrace ("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
    212                      nParam, params[nParam], params_max);
    213             return false;
    214         }
    215         return true;
    216     default:
     144      case PS_MINIMIZE_BETA_LIMIT: {
     145          psAssert(beta, "Require beta to limit beta");
     146          float limit = betaUse[nParam];
     147          if (nParam == PM_PAR_SXY) {
     148              limit *= q2;
     149          }
     150          if (fabs(beta[nParam]) > fabs(limit)) {
     151              beta[nParam] = (beta[nParam] > 0) ? fabs(limit) : -fabs(limit);
     152              psTrace("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
     153                      nParam, beta[nParam], limit);
     154              return false;
     155          }
     156          return true;
     157      }
     158      case PS_MINIMIZE_PARAM_MIN: {
     159          psAssert(params, "Require parameters to limit parameters");
     160          psAssert(paramsMinUse, "Require parameter limits to limit parameters");
     161          float limit = paramsMinUse[nParam];
     162          if (nParam == PM_PAR_SXY) {
     163              limit *= q2;
     164          }
     165          if (params[nParam] < limit) {
     166              params[nParam] = limit;
     167              psTrace("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
     168                      nParam, params[nParam], limit);
     169              return false;
     170          }
     171          return true;
     172      }
     173      case PS_MINIMIZE_PARAM_MAX: {
     174          psAssert(params, "Require parameters to limit parameters");
     175          psAssert(paramsMaxUse, "Require parameter limits to limit parameters");
     176          float limit = paramsMaxUse[nParam];
     177          if (nParam == PM_PAR_SXY) {
     178              limit *= q2;
     179          }
     180          if (params[nParam] > limit) {
     181              params[nParam] = limit;
     182              psTrace("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
     183                      nParam, params[nParam], limit);
     184              return false;
     185          }
     186          return true;
     187      }
     188      default:
    217189        psAbort("invalid choice for limits");
    218190    }
     
    221193}
    222194
    223 
    224195// make an initial guess for parameters
     196// 0.5 PIX: moments and peaks are in pixel coords, thus so are model parameters
    225197bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
    226198{
     
    247219    if (!isfinite(shape.sxy)) return false;
    248220
    249     // XXX PAR[PM_PAR_SKY]  = moments->Sky;
    250221    PAR[PM_PAR_SKY]  = 0.0;
    251222    PAR[PM_PAR_I0]   = peak->flux;
     
    321292
    322293    psF64 z = pow (-log(limit), (1.0 / PAR[PM_PAR_7]));
     294    psAssert (isfinite(z), "fix this code: z should not be nan for %f", PAR[PM_PAR_7]);
    323295
    324296    psF64 radius = sigma * sqrt (2.0 * z);
     297    psAssert (isfinite(radius), "fix this code: radius should not be nan for %f, %f", PAR[PM_PAR_7], sigma);
    325298
    326299    if (isnan(radius))
     
    429402bool PM_MODEL_FIT_STATUS (pmModel *model)
    430403{
    431 
    432     psF32 dP;
    433404    bool  status;
    434405
     
    436407    psF32 *dPAR = model->dparams->data.F32;
    437408
    438     dP = 0;
    439     dP += PS_SQR(dPAR[PM_PAR_SXX] / PAR[PM_PAR_SXX]);
    440     dP += PS_SQR(dPAR[PM_PAR_SYY] / PAR[PM_PAR_SYY]);
    441     dP = sqrt (dP);
    442 
    443409    status = true;
    444 //    status &= (dP < 0.5);
    445410    status &= (PAR[PM_PAR_I0] > 0);
    446411    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
    447412
    448     fprintf (stderr, "SERSIC status pars: dP: %f, I0: %f, S/N: %f\n",
    449              dP, PAR[PM_PAR_I0], (dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]));
    450 
    451413    return status;
     414}
     415
     416
     417void PM_MODEL_SET_LIMITS(pmModelLimitsType type)
     418{
     419    switch (type) {
     420      case PM_MODEL_LIMITS_NONE:
     421        paramsMinUse = NULL;
     422        paramsMaxUse = NULL;
     423        limitsApply = true;
     424        break;
     425      case PM_MODEL_LIMITS_IGNORE:
     426        paramsMinUse = NULL;
     427        paramsMaxUse = NULL;
     428        limitsApply = false;
     429        break;
     430      case PM_MODEL_LIMITS_LAX:
     431        paramsMinUse = paramsMinLax;
     432        paramsMaxUse = paramsMaxLax;
     433        limitsApply = true;
     434        break;
     435      case PM_MODEL_LIMITS_MODERATE:
     436        paramsMinUse = paramsMinModerate;
     437        paramsMaxUse = paramsMaxModerate;
     438        limitsApply = true;
     439        break;
     440      case PM_MODEL_LIMITS_STRICT:
     441        paramsMinUse = paramsMinStrict;
     442        paramsMaxUse = paramsMaxStrict;
     443        limitsApply = true;
     444        break;
     445      default:
     446        psAbort("Unrecognised model limits type: %x", type);
     447    }
     448    return;
    452449}
    453450
     
    460457# undef PM_MODEL_PARAMS_FROM_PSF
    461458# undef PM_MODEL_FIT_STATUS
     459# undef PM_MODEL_SET_LIMITS
  • branches/simtest_nebulous_branches/psModules/src/objects/pmDetections.c

    r23487 r27840  
    2626  psFree (detections->peaks);
    2727  psFree (detections->oldPeaks);
     28  psFree (detections->oldFootprints);
     29
     30  psFree (detections->newSources);
     31  psFree (detections->allSources);
    2832  return;
    2933}
     
    3539    psMemSetDeallocator(detections, (psFreeFunc) pmDetectionsFree);
    3640
    37     detections->footprints = NULL;
    38     detections->peaks      = NULL;
    39     detections->oldPeaks   = NULL;
    40     detections->last       = 0;
     41    detections->footprints    = NULL;
     42    detections->peaks         = NULL;
     43    detections->oldPeaks      = NULL;
     44    detections->oldFootprints = NULL;
     45    detections->newSources    = NULL;
     46    detections->allSources    = NULL;
     47    detections->last          = 0;
    4148
    4249    return (detections);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmDetections.h

    r23487 r27840  
    2121typedef struct {
    2222  psArray *footprints;        // collection of footprints in the image
     23  psArray *oldFootprints;     // collection of footprints previously found
    2324  psArray *peaks;             // collection of all peaks contained by the footprints
    2425  psArray *oldPeaks;          // collection of all peaks previously found
     26  psArray *newSources;        // collection of sources
     27  psArray *allSources;        // collection of sources
    2528  int last;
    2629} pmDetections;
  • branches/simtest_nebulous_branches/psModules/src/objects/pmFootprint.c

    r23187 r27840  
    5353    assert(nspan >= 0);
    5454    footprint->npix = 0;
     55    footprint->nspans = 0; // we may allocate more spans than we set -- this is the number of active spans
    5556    footprint->spans = psArrayAllocEmpty(nspan);
    5657    footprint->peaks = psArrayAlloc(0);
     
    7374}
    7475
     76bool pmFootprintAllocEmptySpans (pmFootprint *footprint, int nSpans) {
     77
     78    psArrayRealloc (footprint->spans, nSpans);
     79    for (int i = 0; i < nSpans; i++) {
     80        footprint->spans->data[i] = pmSpanAlloc(-1, -1, -1);
     81    }
     82    footprint->spans->n = nSpans;
     83    return true;
     84}
     85
     86// reset the footprint containers
     87bool pmFootprintInit(pmFootprint *footprint) {
     88
     89    footprint->bbox.x0 = footprint->bbox.y0 = 0;
     90    footprint->bbox.x1 = footprint->bbox.y1 = -1;
     91    footprint->nspans = 0;
     92    return true;
     93}
     94
    7595bool pmFootprintTest(const psPtr ptr) {
    7696    return (psMemGetDeallocator(ptr) == (psFreeFunc)footprintFree);
     
    103123    psArrayAdd(fp->spans, 1, sp);
    104124    psFree(sp);
    105    
     125
     126    fp->nspans ++;
     127
    106128    fp->npix += x1 - x0 + 1;
    107129
    108     if (fp->spans->n == 1) {
     130    if (fp->nspans == 1) {
    109131        fp->bbox.x0 = x0;
    110132        fp->bbox.x1 = x1;
     
    121143}
    122144
     145
     146// Set the next available elements of the nSpan entry in footprint->spans
     147pmSpan *pmFootprintSetSpan(pmFootprint *fp,     // the footprint to add to
     148                           const int y,         // row to add
     149                           int x0,              // range of
     150                           int x1) {            // columns
     151
     152    if (x1 < x0) {
     153        int tmp = x0;
     154        x0 = x1;
     155        x1 = tmp;
     156    }
     157
     158    int N = fp->nspans;
     159    if (N == fp->spans->n) {
     160        // if we need more space, extend fp->spans as needed
     161        int Nalloc = fp->spans->n + 100;
     162        psArrayRealloc(fp->spans, Nalloc);
     163        fp->spans->n = Nalloc;
     164        for (int i = N; i < fp->spans->n; i++) {
     165            fp->spans->data[i] = pmSpanAlloc(-1, -1, -1);
     166        }
     167    }
     168
     169    pmSpan *span = fp->spans->data[N];
     170    span->y = y;
     171    span->x0 = x0;
     172    span->x1 = x1;
     173
     174    fp->nspans ++;
     175
     176    fp->npix += x1 - x0 + 1;
     177
     178    if (fp->nspans == 1) {
     179        fp->bbox.x0 = x0;
     180        fp->bbox.x1 = x1;
     181        fp->bbox.y0 = y;
     182        fp->bbox.y1 = y;
     183    } else {
     184        if (x0 < fp->bbox.x0) fp->bbox.x0 = x0;
     185        if (x1 > fp->bbox.x1) fp->bbox.x1 = x1;
     186        if (y < fp->bbox.y0) fp->bbox.y0 = y;
     187        if (y > fp->bbox.y1) fp->bbox.y1 = y;
     188    }
     189
     190    return span;
     191}
     192
    123193void pmFootprintSetBBox(pmFootprint *fp) {
    124194    assert (fp != NULL);
    125     if (fp->spans->n == 0) {
     195    if (fp->nspans == 0) {
    126196        return;
    127197    }
     
    132202    int y1 = sp->y;
    133203
    134     for (int i = 1; i < fp->spans->n; i++) {
     204    for (int i = 1; i < fp->nspans; i++) {
    135205        sp = fp->spans->data[i];
    136206       
     
    150220   assert (fp != NULL);
    151221   int npix = 0;
    152    for (int i = 0; i < fp->spans->n; i++) {
     222   for (int i = 0; i < fp->nspans; i++) {
    153223       pmSpan *span = fp->spans->data[i];
    154224       npix += span->x1 - span->x0 + 1;
  • branches/simtest_nebulous_branches/psModules/src/objects/pmFootprint.h

    r24875 r27840  
    1313#include <pslib.h>
    1414#include "pmSpan.h"
    15 
     15#include "pmFootprintSpans.h"
    1616
    1717typedef struct {
    1818    const int id;                       //!< unique ID
    1919    int npix;                           //!< number of pixels in this pmFootprint
    20     psArray *spans;                     //!< the pmSpans
     20    int nspans;
     21    psArray *spans;                     //!< the allocated pmSpans
    2122    psRegion bbox;                      //!< the pmFootprint's bounding box
    2223    psArray *peaks;                     //!< the peaks lying in this footprint
     
    2627
    2728pmFootprint *pmFootprintAlloc(int nspan, const psImage *img);
     29bool pmFootprintInit(pmFootprint *footprint);
    2830bool pmFootprintTest(const psPtr ptr);
     31
     32bool pmFootprintAllocEmptySpans (pmFootprint *footprint, int nSpans);
    2933
    3034pmFootprint *pmFootprintNormalize(pmFootprint *fp);
     
    3741                           int x1);    //          columns
    3842
     43pmSpan *pmFootprintSetSpan(pmFootprint *fp,     // the footprint to add to
     44                           const int y,         // row to add
     45                           int x0,              // range of
     46                           int x1);             // columns
     47
    3948psArray *pmFootprintsFind(const psImage *img, const float threshold, const int npixMin);
    40 pmFootprint *pmFootprintsFindAtPoint(const psImage *img,
    41                                     const float threshold,
    42                                     const psArray *peaks,
    43                                     int row, int col);
     49
     50bool pmFootprintsFindAtPoint(pmFootprint *fp,
     51                             pmFootprintSpans *fpSpans,
     52                             const psImage *img,     // image to search
     53                             psImage *mask,
     54                             const float threshold,   // Threshold
     55                             const psArray *peaks, // array of peaks; finding one terminates search for footprint
     56                             int row, int col);
     57
     58// pmFootprint *pmFootprintsFindAtPoint(const psImage *img,
     59//                                     const float threshold,
     60//                                     const psArray *peaks,
     61//                                     int row, int col);
     62
     63bool pmFootprintSpansBuild(pmFootprint *fp, // the footprint that we're building
     64                           pmFootprintSpans *fpSpans,
     65                           const psImage *img, // the psImage we're working on
     66                           psImage *mask, // the associated masks
     67                           const float threshold // Threshold
     68    );
    4469
    4570psArray *pmFootprintArrayGrow(const psArray *footprints, int r);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmFootprintCullPeaks.c

    r24888 r27840  
    2020#include "pmSpan.h"
    2121#include "pmFootprint.h"
     22#include "pmFootprintSpans.h"
    2223#include "pmPeaks.h"
     24
     25bool dumpfootprints (pmFootprint *fp, pmFootprintSpans *fpSp);
    2326
    2427 /*
     
    2932  */
    3033
    31 # define IN_PEAK 1 
     34# define IN_PEAK 1
    3235psErrorCode pmFootprintCullPeaks(const psImage *img, // the image wherein lives the footprint
    33                                  const psImage *weight, // corresponding variance image
    34                                 pmFootprint *fp, // Footprint containing mortal peaks
    35                                 const float nsigma_delta, // how many sigma above local background a peak needs to be to survive
    36                                 const float fPadding, // fractional padding added to stdev since bright peaks have unreasonably high significance
    37                                 const float min_threshold) { // minimum permitted coll height
     36                                 const psImage *weight, // corresponding variance image
     37                                pmFootprint *fp, // Footprint containing mortal peaks
     38                                const float nsigma_delta, // how many sigma above local background a peak needs to be to survive
     39                                const float fPadding, // fractional padding added to stdev since bright peaks have unreasonably high significance
     40                                const float min_threshold) { // minimum permitted coll height
    3841    assert (img != NULL); assert (img->type.type == PS_TYPE_F32);
    3942    assert (weight != NULL); assert (weight->type.type == PS_TYPE_F32);
     
    4245
    4346    if (fp->peaks == NULL || fp->peaks->n == 0) { // nothing to do
    44         return PS_ERR_NONE;
     47        return PS_ERR_NONE;
    4548    }
    4649
    47     psRegion subRegion;                 // desired subregion; 1 larger than bounding box (grr)
     50    psRegion subRegion;                 // desired subregion; 1 larger than bounding box (grr)
    4851    subRegion.x0 = fp->bbox.x0; subRegion.x1 = fp->bbox.x1 + 1;
    4952    subRegion.y0 = fp->bbox.y0; subRegion.y1 = fp->bbox.y1 + 1;
     
    5558    psImage *idImg = psImageAlloc(subImg->numCols, subImg->numRows, PS_TYPE_S32);
    5659
    57     // We need a psArray of peaks brighter than the current peak. 
     60    // We need a psArray of peaks brighter than the current peak.
    5861    // We reject peaks which either:
    5962    // 1) are below the local threshold
     
    6568    psArrayAdd (brightPeaks, 128, fp->peaks->data[0]);
    6669
     70    // allocate the peakFootprint and peakFPSpans containers -- these are re-used by pmFootprintsFindAtPoint to minimize allocs in this function
     71    pmFootprint *peakFootprint = pmFootprintAlloc(fp->nspans, subImg);
     72    pmFootprintSpans *peakFPSpans = pmFootprintSpansAlloc(2*fp->nspans);
     73
     74    // allocate empty spans for the footprints
     75    pmFootprintAllocEmptySpans(peakFootprint, fp->nspans);
     76
     77    psImage *subMask = psImageCopy(NULL, subImg, PS_TYPE_IMAGE_MASK);
     78
    6779    // The brightest peak is always safe; go through other peaks trying to cull them
    6880    for (int i = 1; i < fp->peaks->n; i++) { // n.b. fp->peaks->n can change within the loop
    69         const pmPeak *peak = fp->peaks->data[i];
    70         int x = peak->x - subImg->col0;
    71         int y = peak->y - subImg->row0;
    72         //
    73         // Find the level nsigma below the peak that must separate the peak
    74         // from any of its friends
    75         //
    76         assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
     81        const pmPeak *peak = fp->peaks->data[i];
     82        int x = peak->x - subImg->col0;
     83        int y = peak->y - subImg->row0;
     84        //
     85        // Find the level nsigma below the peak that must separate the peak
     86        // from any of its friends
     87        //
     88        assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
    7789
    78         // const float stdev = sqrt(subWt->data.F32[y][x]);
    79         // float threshold = subImg->data.F32[y][x] - nsigma_delta*stdev;
     90        // const float stdev = sqrt(subWt->data.F32[y][x]);
     91        // float threshold = subImg->data.F32[y][x] - nsigma_delta*stdev;
    8092
    81         const float stdev = sqrt(subWt->data.F32[y][x]);
    82         const float flux = subImg->data.F32[y][x];
    83         const float fStdev = fabs(stdev/flux);
    84         const float stdev_pad = fabs(flux * hypot(fStdev, fPadding));
    85         // if flux is negative, careful with fStdev
     93        const float stdev = sqrt(subWt->data.F32[y][x]);
     94        const float flux = subImg->data.F32[y][x];
     95        const float fStdev = fabs(stdev/flux);
     96        const float stdev_pad = fabs(flux * hypot(fStdev, fPadding));
     97        // if flux is negative, careful with fStdev
    8698
    87         float threshold = flux - nsigma_delta*stdev_pad;
    88        
    89         if (isnan(threshold) || threshold < min_threshold) {
    90             // min_threshold is assumed to be below the detection threshold,
    91             // so all the peaks are pmFootprint, and this isn't the brightest
     99        float threshold = flux - nsigma_delta*stdev_pad;
     100
     101        if (isnan(threshold) || threshold < min_threshold) {
     102            // min_threshold is assumed to be below the detection threshold,
     103            // so all the peaks are pmFootprint, and this isn't the brightest
     104            continue;
     105        }
     106
     107        // XXX EAM : if stdev >= 0, i'm not sure how this can ever be true?
     108        if (threshold > subImg->data.F32[y][x]) {
     109            threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
     110        }
     111
     112        // init peakFootprint here?
     113        pmFootprintsFindAtPoint(peakFootprint, peakFPSpans, subImg, subMask, threshold, brightPeaks, peak->y, peak->x);
     114        if (peakFPSpans->nStartSpans > 2000) {
     115            // dumpfootprints(peakFootprint, peakFPSpans);
     116            // fprintf (stderr, "big footprint %d : %d\n", peakFootprint->nspans, peakFPSpans->nStartSpans);
     117            fprintf (stderr, "big test footprint: %f %f to %f %f (%d pix)\n", peakFootprint->bbox.x0, peakFootprint->bbox.y0, peakFootprint->bbox.x1, peakFootprint->bbox.y1, peakFootprint->npix);
     118        }
     119
     120        // at this point brightPeaks only has the peaks brighter than the current
     121
     122        // we set the IDs to either 1 (in peak) or 0 (not in peak)
     123        pmSetFootprintID (idImg, peakFootprint, IN_PEAK);
     124
     125        // If this peak has not already been assigned to a source, then we can look for any
     126        // brighter peaks within its footprint. Check if any of the previous (brighter) peaks
     127        // are within the footprint of this peak If so, the current peak is bogus; drop it.
     128        bool keep = true;
     129        for (int j = 0; keep && !peak->assigned && (j < brightPeaks->n); j++) {
     130            const pmPeak *peak2 = fp->peaks->data[j];
     131            int x2 = peak2->x - subImg->col0;
     132            int y2 = peak2->y - subImg->row0;
     133            if (idImg->data.S32[y2][x2] == IN_PEAK) {
     134                // There's a brighter peak within the footprint above threshold; so cull our initial peak
     135                keep = false;
     136            }
     137        }
     138        if (!keep) {
     139            psAssert (!peak->assigned, "logic error: trying to drop a previously-assigned peak");  // we should not drop any already assigned peaks.
    92140            continue;
    93141        }
    94142
    95         // XXX EAM : if stdev >= 0, i'm not sure how this can ever be true?
    96         if (threshold > subImg->data.F32[y][x]) {
    97             threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
    98         }
    99 
    100         // XXX this is a bit expensive: psImageAlloc for every peak contained in this footprint
    101         // perhaps this should alloc a single ID image above and pass it in to be set.
    102 
    103         // XXX optionally use the faster pmFootprintsFind if the subimage size is large (eg, M31)
    104 
    105         // XXX this is not quite there yet:
    106         // pmFootprint *peakFootprint = NULL;
    107         // int area = subImg->numCols * subImg->numRows;
    108         // if (area > 30000) {
    109 
    110         pmFootprint *peakFootprint = pmFootprintsFindAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);
    111 
    112         // at this point brightPeaks only has the peaks brighter than the current
    113 
    114         // XXX need to supply the image here
    115         // we set the IDs to either 1 (in peak) or 0 (not in peak)
    116         pmSetFootprintID (idImg, peakFootprint, IN_PEAK);
    117         psFree(peakFootprint);
    118 
    119         // Check if any of the previous (brighter) peaks are within the footprint of this peak
    120         // If so, the current peak is bogus; drop it.
    121         bool keep = true;
    122         for (int j = 0; keep && (j < brightPeaks->n); j++) {
    123             const pmPeak *peak2 = fp->peaks->data[j];
    124             int x2 = peak2->x - subImg->col0;
    125             int y2 = peak2->y - subImg->row0;
    126             if (idImg->data.S32[y2][x2] == IN_PEAK)
    127                 // There's a brighter peak within the footprint above threshold; so cull our initial peak
    128                 keep = false;
    129         }
    130         if (!keep) continue;
    131 
    132         psArrayAdd (brightPeaks, 128, fp->peaks->data[i]);
     143        psArrayAdd (brightPeaks, 128, fp->peaks->data[i]);
    133144    }
    134145
     
    139150    psFree(subImg);
    140151    psFree(subWt);
     152    psFree(subMask);
     153    psFree(peakFootprint);
     154    psFree(peakFPSpans);
    141155
    142156    return PS_ERR_NONE;
     
    144158
    145159
    146  /*
    147   * Examine the peaks in a pmFootprint, and throw away the ones that are not sufficiently
    148   * isolated.  More precisely, for each peak find the highest coll that you'd have to traverse
    149   * to reach a still higher peak --- and if that coll's more than nsigma DN below your
    150   * starting point, discard the peak.
    151   */
    152 psErrorCode pmFootprintCullPeaks_OLD(const psImage *img, // the image wherein lives the footprint
    153                                  const psImage *weight, // corresponding variance image
    154                                  pmFootprint *fp, // Footprint containing mortal peaks
    155                                  const float nsigma_delta, // how many sigma above local background a peak needs to be to survive
    156                                  const float fPadding, // fractional padding added to stdev since bright peaks have unreasonably high significance
    157                                  const float min_threshold) { // minimum permitted coll height
    158     assert (img != NULL); assert (img->type.type == PS_TYPE_F32);
    159     assert (weight != NULL); assert (weight->type.type == PS_TYPE_F32);
    160     assert (img->row0 == weight->row0 && img->col0 == weight->col0);
    161     assert (fp != NULL);
     160bool dumpfootprints (pmFootprint *fp, pmFootprintSpans *fpSp) {
    162161
    163     if (fp->peaks == NULL || fp->peaks->n == 0) { // nothing to do
    164         return PS_ERR_NONE;
     162    FILE *f1 = fopen ("fp.dat", "w");
     163    if (!f1) return false;
     164
     165    for (int i = 0; i < fp->nspans; i++) {
     166        pmSpan *sp = fp->spans->data[i];
     167        fprintf (f1, "%d - %d : %d\n", sp->x0, sp->x1, sp->y);
    165168    }
     169    fclose (f1);
    166170
    167     psRegion subRegion;                 // desired subregion; 1 larger than bounding box (grr)
    168     subRegion.x0 = fp->bbox.x0; subRegion.x1 = fp->bbox.x1 + 1;
    169     subRegion.y0 = fp->bbox.y0; subRegion.y1 = fp->bbox.y1 + 1;
    170     const psImage *subImg = psImageSubset((psImage *)img, subRegion);
    171     const psImage *subWt = psImageSubset((psImage *)weight, subRegion);
    172     assert (subImg != NULL && subWt != NULL);
    173     //
    174     // We need a psArray of peaks brighter than the current peak.  We'll fake this
    175     // by reusing the fp->peaks but lying about n.
    176     //
    177     // We do this for efficiency (otherwise I'd need two peaks lists), and we are
    178     // rather too chummy with psArray in consequence.  But it works.
    179     //
    180     psArray *brightPeaks = psArrayAlloc(0);
    181     psFree(brightPeaks->data);
    182     brightPeaks->data = psMemIncrRefCounter(fp->peaks->data);// use the data from fp->peaks
    183     //
    184     // The brightest peak is always safe; go through other peaks trying to cull them
    185     //
    186     for (int i = 1; i < fp->peaks->n; i++) { // n.b. fp->peaks->n can change within the loop
    187         const pmPeak *peak = fp->peaks->data[i];
    188         int x = peak->x - subImg->col0;
    189         int y = peak->y - subImg->row0;
    190         //
    191         // Find the level nsigma below the peak that must separate the peak
    192         // from any of its friends
    193         //
    194         assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
     171    FILE *f2 = fopen ("fpSp.dat", "w");
     172    if (!f2) return false;
    195173
    196         // stdev ~ sqrt(flux) : for bright regions (f ~ 1e6, sqrt(f) = 1e3 -- unrealistically small deviations)
    197         // add additional padding in quadrature:
    198 
    199         const float stdev = sqrt(subWt->data.F32[y][x]);
    200         const float flux = subImg->data.F32[y][x];
    201         const float fStdev = stdev/flux;
    202         const float stdev_pad = flux * hypot(fStdev, fPadding);
    203         float threshold = flux - nsigma_delta*stdev_pad;
    204 
    205         if (isnan(threshold) || threshold < min_threshold) {
    206 #if 1     // min_threshold is assumed to be below the detection threshold,
    207           // so all the peaks are pmFootprint, and this isn't the brightest
    208             // XXX mark peak to be dropped
    209             (void)psArrayRemoveIndex(fp->peaks, i);
    210             i--;                        // we moved everything down one
    211             continue;
    212 #else
    213 #error n.b. We will be running LOTS of checks at this threshold, so only find the footprint once
    214             threshold = min_threshold;
    215 #endif
    216         }
    217 
    218         // XXX EAM : if stdev >= 0, i'm not sure how this can ever be true?
    219         if (threshold > subImg->data.F32[y][x]) {
    220             threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
    221         }
    222 
    223         // XXX this is a bit expensive: psImageAlloc for every peak contained in this footprint
    224         // perhaps this should alloc a single ID image above and pass it in to be set.
    225 
    226         const int peak_id = 1;          // the ID for the peak of interest
    227         brightPeaks->n = i;             // only stop at a peak brighter than we are
    228 
    229         // XXX optionally use the faster pmFootprintsFind if the subimage size is large (eg, M31)
    230 
    231         pmFootprint *peakFootprint = pmFootprintsFindAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);
    232         brightPeaks->n = 0;             // don't double free
    233         psImage *idImg = pmSetFootprintID(NULL, peakFootprint, peak_id);
    234         psFree(peakFootprint);
    235 
    236         // Check if any of the previous (brighter) peaks are within the footprint of this peak
    237         // If so, the current peak is bogus; drop it.
    238         int j;
    239         for (j = 0; j < i; j++) {
    240             const pmPeak *peak2 = fp->peaks->data[j];
    241             int x2 = peak2->x - subImg->col0;
    242             int y2 = peak2->y - subImg->row0;
    243             const int peak2_id = idImg->data.S32[y2][x2]; // the ID for some other peak
    244 
    245             if (peak2_id == peak_id) {  // There's a brighter peak within the footprint above
    246                 ;                       // threshold; so cull our initial peak
    247                 (void)psArrayRemoveIndex(fp->peaks, i);
    248                 i--;                    // we moved everything down one
    249                 break;
    250             }
    251         }
    252         if (j == i) {
    253             j++;
    254         }
    255 
    256         psFree(idImg);
     174    for (int i = 0; i < fpSp->nStartSpans; i++) {
     175        pmStartSpan *sp = fpSp->startspans->data[i];
     176        if (!sp->span) continue;
     177        fprintf (f2, "%d - %d : %d\n", sp->span->x0, sp->span->x1, sp->span->y);
    257178    }
    258 
    259     brightPeaks->n = 0; psFree(brightPeaks);
    260     psFree((psImage *)subImg);
    261     psFree((psImage *)subWt);
    262 
    263     return PS_ERR_NONE;
     179    fclose (f2);
     180    return true;
    264181}
    265 
  • branches/simtest_nebulous_branches/psModules/src/objects/pmFootprintFindAtPoint.c

    r21183 r27840  
    2121#include "pmFootprint.h"
    2222#include "pmPeaks.h"
    23 
    24 /*
    25  * A data structure to hold the starting point for a search for pixels above threshold,
    26  * used by pmFootprintsFindAtPoint
    27  *
    28  * We don't want to find this span again --- it's already part of the footprint ---
    29  * so we set appropriate mask bits
    30  *
    31  * EAM : these function were confusingly using "startspan" and "spartspan" 
    32  * I've rationalized them all to 'startspan'
    33  */
    34 
    35 //
    36 // An enum for what we should do with a Startspan
    37 //
    38 typedef enum {PM_SSPAN_DOWN = 0,        // scan down from this span
    39               PM_SSPAN_UP,              // scan up from this span
    40               PM_SSPAN_RESTART,         // restart scanning from this span
    41               PM_SSPAN_DONE             // this span is processed
    42 } PM_SSPAN_DIR;                         // How to continue searching
    43 //
    44 // An enum for mask's pixel values.  We're looking for pixels that are above threshold, and
    45 // we keep extra book-keeping information in the PM_SSPAN_STOP plane.  It's simpler to be
    46 // able to check for
    47 //
    48 enum {
    49     PM_SSPAN_INITIAL = 0x0,             // initial state of pixels.
    50     PM_SSPAN_DETECTED = 0x1,            // we've seen this pixel
    51     PM_SSPAN_STOP = 0x2                 // you may stop searching when you see this pixel
    52 };
    53 //
    54 // The struct that remembers how to [re-]start scanning the image for pixels
    55 //
    56 typedef struct {
    57     const pmSpan *span;                 // save the pixel range
    58     PM_SSPAN_DIR direction;             // How to continue searching
    59     bool stop;                          // should we stop searching?
    60 } Startspan;
    61 
    62 static void startspanFree(Startspan *sspan) {
    63     psFree((void *)sspan->span);
    64 }
    65 
    66 static Startspan *
    67 StartspanAlloc(const pmSpan *span,      // The span in question
    68                psImage *mask,           // Pixels that we've already detected
    69                const PM_SSPAN_DIR dir   // Should we continue searching towards the top of the image?
    70     ) {
    71     Startspan *sspan = psAlloc(sizeof(Startspan));
    72     psMemSetDeallocator(sspan, (psFreeFunc)startspanFree);
    73 
    74     sspan->span = psMemIncrRefCounter((void *)span);
    75     sspan->direction = dir;
    76     sspan->stop = false;
    77    
    78     if (mask != NULL) {                 // remember that we've detected these pixels
    79         psImageMaskType *mpix = &mask->data.PS_TYPE_IMAGE_MASK_DATA[span->y - mask->row0][span->x0 - mask->col0];
    80 
    81         for (int i = 0; i <= span->x1 - span->x0; i++) {
    82             mpix[i] |= PM_SSPAN_DETECTED;
    83             if (mpix[i] & PM_SSPAN_STOP) {
    84                 sspan->stop = true;
    85             }
    86         }
    87     }
    88    
    89     return sspan;
    90 }
    91 
    92 //
    93 // Add a new Startspan to an array of Startspans.  Iff we see a stop bit, return true
    94 //
    95 static bool add_startspan(psArray *startspans, // the saved Startspans
    96                           const pmSpan *sp, // the span in question
    97                           psImage *mask, // mask of detected/stop pixels
    98                           const PM_SSPAN_DIR dir) { // the desired direction to search
    99     if (dir == PM_SSPAN_RESTART) {
    100         if (add_startspan(startspans, sp, mask,  PM_SSPAN_UP) ||
    101             add_startspan(startspans, sp, NULL, PM_SSPAN_DOWN)) {
    102             return true;
    103         }
    104     } else {
    105         Startspan *sspan = StartspanAlloc(sp, mask, dir);
    106         if (sspan->stop) {              // we detected a stop bit
    107             psFree(sspan);              // don't allocate new span
    108 
    109             return true;
    110         } else {
    111             psArrayAdd(startspans, 1, sspan);
    112             psFree(sspan);              // as it's now owned by startspans
    113         }
    114     }
    115 
    116     return false;
    117 }
     23#include "pmFootprintSpans.h"
    11824
    11925/************************************************************************************************************/
    12026/*
    121  * Search the image for pixels above threshold, starting at a single Startspan.
     27 * Search the image for pixels above threshold, starting at a single pmStartSpan.
    12228 * We search the array looking for one to process; it'd be better to move the
    12329 * ones that we're done with to the end, but it probably isn't worth it for
     
    12632 * This is the guts of pmFootprintsFindAtPoint
    12733 */
    128 static bool do_startspan(pmFootprint *fp, // the footprint that we're building
    129                          const psImage *img, // the psImage we're working on
    130                          psImage *mask, // the associated masks
    131                          const float threshold, // Threshold
    132                          psArray *startspans) { // specify which span to process next
    133     bool F32 = false;                   // is this an F32 image?
     34bool pmFootprintSpansBuild(pmFootprint *fp, // the footprint that we're building
     35                           pmFootprintSpans *fpSpans,
     36                           const psImage *img, // the psImage we're working on
     37                           psImage *mask, // the associated masks
     38                           const float threshold // Threshold
     39    ) {
     40    bool F32 = false;                   // is this an F32 image?
     41    if (img->type.type == PS_TYPE_F32) {
     42        F32 = true;
     43    } else if (img->type.type == PS_TYPE_S32) {
     44        F32 = false;
     45    } else {                            // N.b. You can't trivially add more cases here; F32 is just a bool
     46        psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);
     47        return NULL;
     48    }
     49
     50    psF32 *imgRowF32 = NULL;            // row pointer if F32
     51    psS32 *imgRowS32 = NULL;            //  "   "   "  "  !F32
     52    psImageMaskType *maskRow = NULL;            //  masks's row pointer
     53
     54    const int row0 = img->row0;
     55    const int col0 = img->col0;
     56    const int numRows = img->numRows;
     57    const int numCols = img->numCols;
     58
     59    /********************************************************************************************************/
     60
     61    pmStartSpan *startspan = NULL;
     62    for (int i = 0; i < fpSpans->nStartSpans; i++) {
     63        startspan = fpSpans->startspans->data[i];
     64        if (startspan->direction != PM_STARTSPAN_DONE) {
     65            break;
     66        }
     67        if (startspan->stop) {
     68            break;
     69        }
     70    }
     71    if (startspan == NULL || startspan->direction == PM_STARTSPAN_DONE) { // no more pmStartSpans to process
     72        return false;
     73    }
     74    if (startspan->stop) {                  // they don't want any more spans processed
     75        return false;
     76    }
     77
     78    /*
     79     * Work
     80     */
     81    const PM_STARTSPAN_DIR dir = startspan->direction;
     82    /*
     83     * Set initial span to the startspan
     84     */
     85    int x0 = startspan->span->x0 - col0, x1 = startspan->span->x1 - col0;
     86    /*
     87     * Go through image identifying objects
     88     */
     89    int nx0, nx1 = -1;                  // new values of x0, x1
     90    const int di = (dir == PM_STARTSPAN_UP) ? 1 : -1; // how much i changes to get to the next row
     91    bool stop = false;                  // should I stop searching for spans?
     92
     93    for (int i = startspan->span->y - row0 + di; i < numRows && i >= 0; i += di) {
     94        imgRowF32 = img->data.F32[i];   // only one of
     95        imgRowS32 = img->data.S32[i];   //      these is valid!
     96        maskRow = mask->data.PS_TYPE_IMAGE_MASK_DATA[i];
     97        //
     98        // Search left from the pixel diagonally to the left of (i - di, x0). If there's
     99        // a connected span there it may need to grow up and/or down, so push it onto
     100        // the stack for later consideration
     101        //
     102        nx0 = -1;
     103        for (int j = x0 - 1; j >= -1; j--) {
     104            double pixVal = (j < 0) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
     105            if ((maskRow[j] & PM_STARTSPAN_DETECTED) || pixVal < threshold) {
     106                if (j < x0 - 1) {       // we found some pixels above threshold
     107                    nx0 = j + 1;
     108                }
     109                break;
     110            }
     111        }
     112
     113        if (nx0 < 0) {                  // no span to the left
     114            nx1 = x0 - 1;               // we're going to resume searching at nx1 + 1
     115        } else {
     116            //
     117            // Search right in leftmost span
     118            //
     119            for (int j = nx0 + 1; j <= numCols; j++) {
     120                double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
     121                if ((maskRow[j] & PM_STARTSPAN_DETECTED) || pixVal < threshold) {
     122                    nx1 = j - 1;
     123                    break;
     124                }
     125            }
     126
     127            pmSpan *sp = pmFootprintSetSpan(fp, i + row0, nx0 + col0, nx1 + col0);
     128            bool status = pmFootprintSpansSet(fpSpans, sp, mask, PM_STARTSPAN_RESTART);
     129            // fprintf (stderr, "set 1: %d vs %d\n", fp->nspans, fpSpans->nStartSpans);
     130            if (status) {
     131                stop = true;
     132                break;
     133            }
     134        }
     135        //
     136        // Now look for spans connected to the old span.  The first of these we'll
     137        // simply process, but others will have to be deferred for later consideration.
     138        //
     139        // In fact, if the span overhangs to the right we'll have to defer the overhang
     140        // until later too, as it too can grow in both directions
     141        //
     142        // Note that column numCols exists virtually, and always ends the last span; this
     143        // is why we claim below that sx1 is always set
     144        //
     145        bool first = false;             // is this the first new span detected?
     146        for (int j = nx1 + 1; j <= x1 + 1; j++) {
     147            double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
     148            if (!(maskRow[j] & PM_STARTSPAN_DETECTED) && pixVal >= threshold) {
     149                int sx0 = j++;          // span that we're working on is sx0:sx1
     150                int sx1 = -1;           // We know that if we got here, we'll also set sx1
     151                for (; j <= numCols; j++) {
     152                    double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
     153                    if ((maskRow[j] & PM_STARTSPAN_DETECTED) || pixVal < threshold) { // end of span
     154                        sx1 = j;
     155                        break;
     156                    }
     157                }
     158                assert (sx1 >= 0);
     159
     160                pmSpan *sp;
     161                if (first) {
     162                    if (sx1 <= x1) {
     163                        sp = pmFootprintSetSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
     164                        bool status = pmFootprintSpansSet(fpSpans, sp, mask, PM_STARTSPAN_DONE);
     165                        // fprintf (stderr, "set 2: %d vs %d\n", fp->nspans, fpSpans->nStartSpans);
     166                        if (status) {
     167                            stop = true;
     168                            break;
     169                        }
     170                    } else {            // overhangs to right
     171                        sp = pmFootprintSetSpan(fp, i + row0, sx0 + col0, x1 + col0);
     172                        bool status = pmFootprintSpansSet(fpSpans, sp, mask, PM_STARTSPAN_DONE);
     173                        // fprintf (stderr, "set 3: %d vs %d\n", fp->nspans, fpSpans->nStartSpans);
     174                        if (status) {
     175                            stop = true;
     176                            break;
     177                        }
     178                        sp = pmFootprintSetSpan(fp, i + row0, x1 + 1 + col0, sx1 + col0 - 1);
     179                        status = pmFootprintSpansSet(fpSpans, sp, mask, PM_STARTSPAN_RESTART);
     180                        // fprintf (stderr, "set 4: %d vs %d\n", fp->nspans, fpSpans->nStartSpans);
     181                        if (status) {
     182                            stop = true;
     183                            break;
     184                        }
     185                    }
     186                    first = false;
     187                } else {
     188                    sp = pmFootprintSetSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
     189                    bool status = pmFootprintSpansSet(fpSpans, sp, mask, PM_STARTSPAN_RESTART);
     190                    // fprintf (stderr, "set 5: %d vs %d\n", fp->nspans, fpSpans->nStartSpans);
     191                    if (status) {
     192                        stop = true;
     193                        break;
     194                    }
     195                }
     196            }
     197        }
     198
     199        if (stop || first == false) {   // we're done
     200            break;
     201        }
     202
     203        x0 = nx0; x1 = nx1;
     204    }
     205    /*
     206     * Cleanup
     207     */
     208
     209    startspan->direction = PM_STARTSPAN_DONE;
     210    return stop ? false : true;
     211}
     212
     213/*
     214 * Go through an image, starting at (row, col) and assembling all the pixels
     215 * that are connected to that point (in a chess kings-move sort of way) into
     216 * a pmFootprint.
     217 *
     218 * This is much slower than pmFootprintsFind if you want to find lots of
     219 * footprints, but if you only want a small region about a given point it
     220 * can be much faster
     221 *
     222 * N.b. The returned pmFootprint is not in "normal form"; that is the pmSpans
     223 * are not sorted by increasing y, x0, x1.  If this matters to you, call
     224 * pmFootprintNormalize()
     225 *
     226 * The calling function must supply a footprint allocated with a reasonable amount of space
     227 *
     228 */
     229
     230bool pmFootprintsFindAtPoint(pmFootprint *fp,
     231                             pmFootprintSpans *fpSpans,
     232                             const psImage *img,     // image to search
     233                             psImage *mask,
     234                             const float threshold,   // Threshold
     235                             const psArray *peaks, // array of peaks; finding one terminates search for footprint
     236                             int row, int col) { // starting position (in img's parent's coordinate system)
     237    psAssert(img, "image must be supplied");
     238    psAssert(fp, "footprint must be supplied");
     239    psAssert(fpSpans, "footprint spans must be supplied");
     240
     241    bool F32 = false;                    // is this an F32 image?
    134242    if (img->type.type == PS_TYPE_F32) {
    135243        F32 = true;
    136244    } else if (img->type.type == PS_TYPE_S32) {
    137245        F32 = false;
    138     } else {                            // N.b. You can't trivially add more cases here; F32 is just a bool
     246    } else {                             // N.b. You can't trivially add more cases here; F32 is just a bool
    139247        psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);
    140         return NULL;
    141     }
    142 
    143     psF32 *imgRowF32 = NULL;            // row pointer if F32
    144     psS32 *imgRowS32 = NULL;            //  "   "   "  "  !F32
    145     psImageMaskType *maskRow = NULL;            //  masks's row pointer
    146    
     248        return false;
     249    }
     250    psF32 *imgRowF32 = NULL;             // row pointer if F32
     251    psS32 *imgRowS32 = NULL;             //  "   "   "  "  !F32
     252
    147253    const int row0 = img->row0;
    148254    const int col0 = img->col0;
    149255    const int numRows = img->numRows;
    150256    const int numCols = img->numCols;
    151    
    152     /********************************************************************************************************/
    153    
    154     Startspan *sspan = NULL;
    155     for (int i = 0; i < startspans->n; i++) {
    156         sspan = startspans->data[i];
    157         if (sspan->direction != PM_SSPAN_DONE) {
    158             break;
     257
     258    /*
     259     * Is point in image, and above threshold?
     260     */
     261    row -= row0; col -= col0;
     262    if (row < 0 || row >= numRows ||
     263        col < 0 || col >= numCols) {
     264        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     265                "row/col == (%d, %d) are out of bounds [%d--%d, %d--%d]",
     266                row + row0, col + col0, row0, row0 + numRows - 1, col0, col0 + numCols - 1);
     267        return false;
     268    }
     269
     270    double pixVal = F32 ? img->data.F32[row][col] : img->data.S32[row][col];
     271    if (pixVal < threshold) {
     272        return true;
     273    }
     274
     275    /*
     276     * We need a mask for two purposes; to indicate which pixels are already detected,
     277     * and to store the "stop" pixels --- those that, once reached, should stop us
     278     * looking for the rest of the pmFootprint.  These are generally set from peaks.
     279     */
     280
     281    pmFootprintInit(fp);
     282    pmFootprintSpansInit(fpSpans);
     283    psImageInit(mask, PM_STARTSPAN_INITIAL);
     284
     285    // fprintf (stderr, "init: %d vs %d\n", fp->nspans, fpSpans->nStartSpans);
     286
     287    //
     288    // Set stop bits from peaks list
     289    //
     290    assert (peaks == NULL || peaks->n == 0 || psMemCheckPeak(peaks->data[0]));
     291    if (peaks != NULL) {
     292        for (int i = 0; i < peaks->n; i++) {
     293            pmPeak *peak = peaks->data[i];
     294            mask->data.PS_TYPE_IMAGE_MASK_DATA[peak->y - mask->row0][peak->x - mask->col0] |= PM_STARTSPAN_STOP;
    159295        }
    160         if (sspan->stop) {
    161             break;
    162         }
    163     }
    164     if (sspan == NULL || sspan->direction == PM_SSPAN_DONE) { // no more Startspans to process
    165         return false;
    166     }
    167     if (sspan->stop) {                  // they don't want any more spans processed
    168         return false;
    169     }
    170     /*
    171      * Work
    172      */
    173     const PM_SSPAN_DIR dir = sspan->direction;
    174     /*
    175      * Set initial span to the startspan
    176      */
    177     int x0 = sspan->span->x0 - col0, x1 = sspan->span->x1 - col0;
    178     /*
    179      * Go through image identifying objects
    180      */
    181     int nx0, nx1 = -1;                  // new values of x0, x1
    182     const int di = (dir == PM_SSPAN_UP) ? 1 : -1; // how much i changes to get to the next row
    183     bool stop = false;                  // should I stop searching for spans?
    184 
    185     for (int i = sspan->span->y -row0 + di; i < numRows && i >= 0; i += di) {
    186         imgRowF32 = img->data.F32[i];   // only one of
    187         imgRowS32 = img->data.S32[i];   //      these is valid!
    188         maskRow = mask->data.PS_TYPE_IMAGE_MASK_DATA[i];
    189         //
    190         // Search left from the pixel diagonally to the left of (i - di, x0). If there's
    191         // a connected span there it may need to grow up and/or down, so push it onto
    192         // the stack for later consideration
    193         //
    194         nx0 = -1;
    195         for (int j = x0 - 1; j >= -1; j--) {
    196             double pixVal = (j < 0) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
    197             if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {
    198                 if (j < x0 - 1) {       // we found some pixels above threshold
    199                     nx0 = j + 1;
    200                 }
     296    }
     297
     298    /*
     299     * Find starting span passing through (row, col)
     300     */
     301    imgRowF32 = img->data.F32[row];      // only one of
     302    imgRowS32 = img->data.S32[row];      //      these is valid!
     303    psImageMaskType *maskRow = mask->data.PS_TYPE_IMAGE_MASK_DATA[row];
     304    {
     305        int i;
     306        for (i = col; i >= 0; i--) {
     307            pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
     308            if ((maskRow[i] & PM_STARTSPAN_DETECTED) || pixVal < threshold) {
    201309                break;
    202310            }
    203311        }
    204 
    205         if (nx0 < 0) {                  // no span to the left
    206             nx1 = x0 - 1;               // we're going to resume searching at nx1 + 1
    207         } else {
    208             //
    209             // Search right in leftmost span
    210             //
    211             //nx1 = 0;                  // make gcc happy
    212             for (int j = nx0 + 1; j <= numCols; j++) {
    213                 double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
    214                 if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {
    215                     nx1 = j - 1;
    216                     break;
    217                 }
    218             }
    219            
    220             const pmSpan *sp = pmFootprintAddSpan(fp, i + row0, nx0 + col0, nx1 + col0);
    221            
    222             if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
    223                 stop = true;
     312        int i0 = i;
     313        for (i = col; i < numCols; i++) {
     314            pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
     315            if ((maskRow[i] & PM_STARTSPAN_DETECTED) || pixVal < threshold) {
    224316                break;
    225317            }
    226318        }
    227         //
    228         // Now look for spans connected to the old span.  The first of these we'll
    229         // simply process, but others will have to be deferred for later consideration.
    230         //
    231         // In fact, if the span overhangs to the right we'll have to defer the overhang
    232         // until later too, as it too can grow in both directions
    233         //
    234         // Note that column numCols exists virtually, and always ends the last span; this
    235         // is why we claim below that sx1 is always set
    236         //
    237         bool first = false;             // is this the first new span detected?
    238         for (int j = nx1 + 1; j <= x1 + 1; j++) {
    239             double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
    240             if (!(maskRow[j] & PM_SSPAN_DETECTED) && pixVal >= threshold) {
    241                 int sx0 = j++;          // span that we're working on is sx0:sx1
    242                 int sx1 = -1;           // We know that if we got here, we'll also set sx1
    243                 for (; j <= numCols; j++) {
    244                     double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
    245                     if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) { // end of span
    246                         sx1 = j;
    247                         break;
    248                     }
    249                 }
    250                 assert (sx1 >= 0);
    251 
    252                 const pmSpan *sp;
    253                 if (first) {
    254                     if (sx1 <= x1) {
    255                         sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
    256                         if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {
    257                             stop = true;
    258                             break;
    259                         }
    260                     } else {            // overhangs to right
    261                         sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, x1 + col0);
    262                         if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {
    263                             stop = true;
    264                             break;
    265                         }
    266                         sp = pmFootprintAddSpan(fp, i + row0, x1 + 1 + col0, sx1 + col0 - 1);
    267                         if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
    268                             stop = true;
    269                             break;
    270                         }
    271                     }
    272                     first = false;
    273                 } else {
    274                     sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
    275                     if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
    276                         stop = true;
    277                         break;
    278                     }
    279                 }
    280             }
    281         }
    282 
    283         if (stop || first == false) {   // we're done
    284             break;
    285         }
    286 
    287         x0 = nx0; x1 = nx1;
    288     }
     319        int i1 = i;
     320        pmSpan *sp = pmFootprintSetSpan(fp, row + row0, i0 + col0 + 1, i1 + col0 - 1);
     321        pmFootprintSpansSet(fpSpans, sp, mask, PM_STARTSPAN_RESTART);
     322        // fprintf (stderr, "first: %d vs %d\n", fp->nspans, fpSpans->nStartSpans);
     323    }
     324    /*
     325     * Now workout from those pmStartSpans, searching for pixels above threshold
     326     */
     327    while (pmFootprintSpansBuild(fp, fpSpans, img, mask, threshold)) continue;
    289328    /*
    290329     * Cleanup
    291330     */
    292 
    293     sspan->direction = PM_SSPAN_DONE;
    294     return stop ? false : true;
     331    // psFree(mask);
     332    // psFree(startspans);                  // restores the image pixel
     333
     334    return fp;                           // pmFootprint really
    295335}
    296 
    297 /*
    298  * Go through an image, starting at (row, col) and assembling all the pixels
    299  * that are connected to that point (in a chess kings-move sort of way) into
    300  * a pmFootprint.
    301  *
    302  * This is much slower than pmFootprintsFind if you want to find lots of
    303  * footprints, but if you only want a small region about a given point it
    304  * can be much faster
    305  *
    306  * N.b. The returned pmFootprint is not in "normal form"; that is the pmSpans
    307  * are not sorted by increasing y, x0, x1.  If this matters to you, call
    308  * pmFootprintNormalize()
    309  */
    310 pmFootprint *
    311 pmFootprintsFindAtPoint(const psImage *img,     // image to search
    312                        const float threshold,   // Threshold
    313                        const psArray *peaks, // array of peaks; finding one terminates search for footprint
    314                        int row, int col) { // starting position (in img's parent's coordinate system)
    315    assert(img != NULL);
    316 
    317    bool F32 = false;                    // is this an F32 image?
    318    if (img->type.type == PS_TYPE_F32) {
    319        F32 = true;
    320    } else if (img->type.type == PS_TYPE_S32) {
    321        F32 = false;
    322    } else {                             // N.b. You can't trivially add more cases here; F32 is just a bool
    323        psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);
    324        return NULL;
    325    }
    326    psF32 *imgRowF32 = NULL;             // row pointer if F32
    327    psS32 *imgRowS32 = NULL;             //  "   "   "  "  !F32
    328    
    329    const int row0 = img->row0;
    330    const int col0 = img->col0;
    331    const int numRows = img->numRows;
    332    const int numCols = img->numCols;
    333 /*
    334  * Is point in image, and above threshold?
    335  */
    336    row -= row0; col -= col0;
    337    if (row < 0 || row >= numRows ||
    338        col < 0 || col >= numCols) {
    339         psError(PS_ERR_BAD_PARAMETER_VALUE, true,
    340                 "row/col == (%d, %d) are out of bounds [%d--%d, %d--%d]",
    341                 row + row0, col + col0, row0, row0 + numRows - 1, col0, col0 + numCols - 1);
    342        return NULL;
    343    }
    344 
    345    double pixVal = F32 ? img->data.F32[row][col] : img->data.S32[row][col];
    346    if (pixVal < threshold) {
    347        return pmFootprintAlloc(0, img);
    348    }
    349    
    350    pmFootprint *fp = pmFootprintAlloc(1 + img->numRows/10, img);
    351 /*
    352  * We need a mask for two purposes; to indicate which pixels are already detected,
    353  * and to store the "stop" pixels --- those that, once reached, should stop us
    354  * looking for the rest of the pmFootprint.  These are generally set from peaks.
    355  */
    356    psImage *mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    357    P_PSIMAGE_SET_ROW0(mask, row0);
    358    P_PSIMAGE_SET_COL0(mask, col0);
    359    psImageInit(mask, PM_SSPAN_INITIAL);
    360    //
    361    // Set stop bits from peaks list
    362    //
    363    assert (peaks == NULL || peaks->n == 0 || psMemCheckPeak(peaks->data[0]));
    364    if (peaks != NULL) {
    365        for (int i = 0; i < peaks->n; i++) {
    366            pmPeak *peak = peaks->data[i];
    367            mask->data.PS_TYPE_IMAGE_MASK_DATA[peak->y - mask->row0][peak->x - mask->col0] |= PM_SSPAN_STOP;
    368        }
    369    }
    370 /*
    371  * Find starting span passing through (row, col)
    372  */
    373    psArray *startspans = psArrayAllocEmpty(1); // spans where we have to restart the search
    374    
    375    imgRowF32 = img->data.F32[row];      // only one of
    376    imgRowS32 = img->data.S32[row];      //      these is valid!
    377    psImageMaskType *maskRow = mask->data.PS_TYPE_IMAGE_MASK_DATA[row];
    378    {
    379        int i;
    380        for (i = col; i >= 0; i--) {
    381            pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
    382            if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {
    383                break;
    384            }
    385        }
    386        int i0 = i;
    387        for (i = col; i < numCols; i++) {
    388            pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
    389            if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {
    390                break;
    391            }
    392        }
    393        int i1 = i;
    394        const pmSpan *sp = pmFootprintAddSpan(fp, row + row0, i0 + col0 + 1, i1 + col0 - 1);
    395 
    396        (void)add_startspan(startspans, sp, mask, PM_SSPAN_RESTART);
    397    }
    398    /*
    399     * Now workout from those Startspans, searching for pixels above threshold
    400     */
    401    while (do_startspan(fp, img, mask, threshold, startspans)) continue;
    402    /*
    403     * Cleanup
    404     */
    405    psFree(mask);
    406    psFree(startspans);                  // restores the image pixel
    407 
    408    return fp;                           // pmFootprint really
    409 }
  • branches/simtest_nebulous_branches/psModules/src/objects/pmFootprintIDs.c

    r20937 r27840  
    3232   const int row0 = fp->region.y0;
    3333
    34    for (int j = 0; j < fp->spans->n; j++) {
     34   for (int j = 0; j < fp->nspans; j++) {
    3535       const pmSpan *span = fp->spans->data[j];
    3636       psS32 *imgRow = idImage->data.S32[span->y - row0];
  • branches/simtest_nebulous_branches/psModules/src/objects/pmGrowthCurveGenerate.c

    r23989 r27840  
    6565
    6666            // use the center of the center pixel of the image
     67            // 0.5 PIX: is this offset needed? probably -- the psf model uses 0.5,0.5 as the center, double check
    6768            float xc = (int)(ix*readout->image->numCols + 0.5*readout->image->numCols) + readout->image->col0 + 0.5;
    6869            float yc = (int)(iy*readout->image->numRows + 0.5*readout->image->numRows) + readout->image->row0 + 0.5;
     
    156157
    157158    // measure the fitMag for this model
    158     pmSourcePhotometryModel (&fitMag, model);
     159    pmSourcePhotometryModel (&fitMag, NULL, model);
    159160    growth->fitMag = fitMag;
    160161
     
    195196            return NULL;
    196197        }
    197         psImageKeepCircle (mask, xc, yc, radius, "AND", PS_NOT_IMAGE_MASK(markVal));
     198        psImageMaskPixels (mask, "AND", PS_NOT_IMAGE_MASK(markVal)); // clear the circular mask
    198199
    199200        // the 'ignore' mode is for testing
  • branches/simtest_nebulous_branches/psModules/src/objects/pmModel.c

    r23187 r27840  
    5656
    5757    tmp->type = type;
    58     tmp->chisq = 0.0;
    59     tmp->chisqNorm = 0.0;
     58    tmp->mag = NAN;
     59    tmp->chisq = NAN;
     60    tmp->chisqNorm = NAN;
    6061    tmp->nDOF  = 0;
    6162    tmp->nPix  = 0;
    6263    tmp->nIter = 0;
    63     tmp->radiusFit = 0;
     64    tmp->fitRadius = 0;
    6465    tmp->flags = PM_MODEL_STATUS_NONE;
    6566    tmp->residuals = NULL;              // XXX should the model own this memory?
     
    8687    tmp->modelParamsFromPSF = class->modelParamsFromPSF;
    8788    tmp->modelFitStatus     = class->modelFitStatus;
     89    tmp->modelSetLimits     = class->modelSetLimits;
    8890
    8991    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     
    108110    new->nIter     = model->nIter;
    109111    new->flags     = model->flags;
    110     new->radiusFit = model->radiusFit;
     112    new->fitRadius = model->fitRadius;
    111113
    112114    for (int i = 0; i < new->params->n; i++) {
     
    189191    psVector *params = model->params;
    190192
    191     psS32 imageCol;
    192     psS32 imageRow;
    193     psF32 pixelValue;
     193    float imageCol;
     194    float imageRow;
     195    float pixelValue;
    194196
    195197    // save original values; restore before returning
     
    232234    psF32 **Rx = NULL;
    233235    psF32 **Ry = NULL;
    234     psImageMaskType **Rm = NULL;
     236    pmResidMaskType **Rm = NULL;
    235237
    236238    if (model->residuals) {
     
    240242        Rx = (model->residuals->Rx)   ? model->residuals->Rx->data.F32 : NULL;
    241243        Ry = (model->residuals->Ry)   ? model->residuals->Ry->data.F32 : NULL;
    242         Rm = (model->residuals->mask) ? model->residuals->mask->data.PS_TYPE_IMAGE_MASK_DATA : NULL;
     244        Rm = (model->residuals->mask) ? model->residuals->mask->data.PM_TYPE_RESID_MASK_DATA : NULL;
    243245        if (Ro) {
    244246            NX = model->residuals->Ro->numCols;
     
    255257                continue;
    256258
    257             // XXX should we use using 0.5 pixel offset?
    258             // Convert to coordinate in parent image, with offset (dx,dy)
    259             imageCol = ix + image->col0 - dx;
    260             imageRow = iy + image->row0 - dy;
    261 
    262             x->data.F32[0] = (float) imageCol;
    263             x->data.F32[1] = (float) imageRow;
     259            // Convert to coordinate in parent image, with offset (dx,dy)
     260            // 0.5 PIX: the model take pixel coordinates so convert the pixel index here
     261            imageCol = ix + 0.5 + image->col0 - dx;
     262            imageRow = iy + 0.5 + image->row0 - dy;
     263
     264            x->data.F32[0] = imageCol;
     265            x->data.F32[1] = imageRow;
    264266
    265267            pixelValue = 0.0;
     
    276278                float rx = xBin*ix + DX;
    277279
    278                 int rx0 = rx - 0.5;
    279                 int rx1 = rx + 0.5;
    280                 int ry0 = ry - 0.5;
    281                 int ry1 = ry + 0.5;
    282 
    283                 if (rx0 < 0) goto skip;
    284                 if (ry0 < 0) goto skip;
    285                 if (rx1 >= NX) goto skip;
    286                 if (ry1 >= NY) goto skip;
    287 
    288                 // these go from 0.0 to 1.0 between the centers of the pixels
    289                 float fx = rx - 0.5 - rx0;
    290                 float Fx = 1.0 - fx;
    291                 float fy = ry - 0.5 - ry0;
    292                 float Fy = 1.0 - fy;
    293 
    294                 // check the residual image mask (if set). give up if any of the 4 pixels are masked.
    295                 if (Rm) {
    296                     if (Rm[ry0][rx0]) goto skip;
    297                     if (Rm[ry0][rx1]) goto skip;
    298                     if (Rm[ry1][rx0]) goto skip;
    299                     if (Rm[ry1][rx1]) goto skip;
    300                 }
    301 
    302                 // a possible further optimization if we re-use these values
    303                 // XXX allow for masked pixels, and add pixel weights
    304                 float V0 = (Ro[ry0][rx0]*Fx + Ro[ry0][rx1]*fx);
    305                 float V1 = (Ro[ry1][rx0]*Fx + Ro[ry1][rx1]*fx);
    306                 float Vo = V0*Fy + V1*fy;
    307                 if (!isfinite(Vo)) goto skip;
    308 
    309                 float Vx = 0.0;
    310                 float Vy = 0.0;
    311 
    312                 // skip Rx,Ry if Ro is masked
    313                 if (Rx && Ry && (mode & PM_MODEL_OP_RES1)) {
    314                     V0 = (Rx[ry0][rx0]*Fx + Rx[ry0][rx1]*fx);
    315                     V1 = (Rx[ry1][rx0]*Fx + Rx[ry1][rx1]*fx);
    316                     Vx = V0*Fy + V1*fy;
    317 
    318                     V0 = (Ry[ry0][rx0]*Fx + Ry[ry0][rx1]*fx);
    319                     V1 = (Ry[ry1][rx0]*Fx + Ry[ry1][rx1]*fx);
    320                     Vy = V0*Fy + V1*fy;
    321                 }
    322                 if (!isfinite(Vx)) goto skip;
    323                 if (!isfinite(Vy)) goto skip;
    324 
    325                 // 2D residual variations are set for the true source position
    326                 pixelValue += Io*(Vo + XoSave*Vx + XoSave*Vy);
     280                int rx0 = rx - 0.5;
     281                int rx1 = rx + 0.5;
     282                int ry0 = ry - 0.5;
     283                int ry1 = ry + 0.5;
     284
     285                if (rx0 < 0) goto skip;
     286                if (ry0 < 0) goto skip;
     287                if (rx1 >= NX) goto skip;
     288                if (ry1 >= NY) goto skip;
     289
     290                // these go from 0.0 to 1.0 between the centers of the pixels
     291                float fx = rx - 0.5 - rx0;
     292                float Fx = 1.0 - fx;
     293                float fy = ry - 0.5 - ry0;
     294                float Fy = 1.0 - fy;
     295
     296                // check the residual image mask (if set). give up if any of the 4 pixels are masked.
     297                if (Rm) {
     298                    if (Rm[ry0][rx0]) goto skip;
     299                    if (Rm[ry0][rx1]) goto skip;
     300                    if (Rm[ry1][rx0]) goto skip;
     301                    if (Rm[ry1][rx1]) goto skip;
     302                }
     303
     304                // a possible further optimization if we re-use these values
     305                // XXX allow for masked pixels, and add pixel weights
     306                float V0 = (Ro[ry0][rx0]*Fx + Ro[ry0][rx1]*fx);
     307                float V1 = (Ro[ry1][rx0]*Fx + Ro[ry1][rx1]*fx);
     308                float Vo = V0*Fy + V1*fy;
     309                if (!isfinite(Vo)) goto skip;
     310
     311                float Vx = 0.0;
     312                float Vy = 0.0;
     313
     314                // skip Rx,Ry if Ro is masked
     315                if (Rx && Ry && (mode & PM_MODEL_OP_RES1)) {
     316                    V0 = (Rx[ry0][rx0]*Fx + Rx[ry0][rx1]*fx);
     317                    V1 = (Rx[ry1][rx0]*Fx + Rx[ry1][rx1]*fx);
     318                    Vx = V0*Fy + V1*fy;
     319
     320                    V0 = (Ry[ry0][rx0]*Fx + Ry[ry0][rx1]*fx);
     321                    V1 = (Ry[ry1][rx0]*Fx + Ry[ry1][rx1]*fx);
     322                    Vy = V0*Fy + V1*fy;
     323                }
     324                if (!isfinite(Vx)) goto skip;
     325                if (!isfinite(Vy)) goto skip;
     326
     327                // 2D residual variations are set for the true source position
     328                pixelValue += Io*(Vo + XoSave*Vx + XoSave*Vy);
    327329            }
    328330
    329         skip:
     331        skip:
    330332            // add or subtract the value
    331333            if (add) {
  • branches/simtest_nebulous_branches/psModules/src/objects/pmModel.h

    r21516 r27840  
    4444} pmModelOpMode;
    4545
     46/// Parameter limit types
     47typedef enum {
     48    PM_MODEL_LIMITS_NONE,               ///< Apply no limits: suitable for debugging
     49    PM_MODEL_LIMITS_IGNORE,             ///< Ignore all limits: fit can go to town
     50    PM_MODEL_LIMITS_LAX,                ///< Lax limits: attempting to reproduce even bad data
     51    PM_MODEL_LIMITS_MODERATE,           ///< Moderate limits: cope with mildly bad data
     52    PM_MODEL_LIMITS_STRICT,             ///< Strict limits: insist on good quality data
     53} pmModelLimitsType;
     54
    4655typedef struct pmModel pmModel;
    4756typedef struct pmSource pmSource;
     
    7483//  This function returns the success / failure status of the given model fit
    7584typedef bool (*pmModelFitStatusFunc)(pmModel *model);
     85
     86//  This function sets the parameter limits for the given model
     87typedef bool (*pmModelSetLimitsFunc)(pmModelLimitsType type);
    7688
    7789/** pmModel data structure
     
    96108    int nIter;                          ///< number of iterations to reach min
    97109    pmModelStatus flags;                ///< model status flags
    98     float radiusFit;                    ///< fit radius actually used
     110    float fitRadius;                    ///< fit radius actually used
    99111    pmResiduals *residuals;             ///< normalized PSF residuals
    100112
     
    108120    pmModelParamsFromPSF modelParamsFromPSF;
    109121    pmModelFitStatusFunc modelFitStatus;
     122    pmModelSetLimitsFunc modelSetLimits;
    110123};
    111124
     
    151164    pmModel *model,                     ///< The input pmModel
    152165    pmModelOpMode mode,                 ///< mode to control how the model is added into the image
    153     psImageMaskType maskVal             ///< Value to mask
     166    psImageMaskType maskVal             ///< Value to mask
    154167);
    155168
     
    169182    pmModel *model,                     ///< The input pmModel
    170183    pmModelOpMode mode,                 ///< mode to control how the model is added into the image
    171     psImageMaskType maskVal             ///< Value to mask
     184    psImageMaskType maskVal             ///< Value to mask
    172185);
    173186
     
    202215);
    203216
     217
     218/// Set the model parameter limits for the given model
     219///
     220/// Wraps the model-specific pmModelSetLimitsFunc function.
     221bool pmModelSetLimits(
     222    const pmModel *model,               ///< Model of interest
     223    pmModelLimits type                  ///< Type of limits
     224    );
     225
     226
    204227/// @}
    205228# endif /* PM_MODEL_H */
  • branches/simtest_nebulous_branches/psModules/src/objects/pmModelClass.c

    r20937 r27840  
    3636#include "pmErrorCodes.h"
    3737
    38 // XXX shouldn't these be defined for us in pslib.h ???
     38// XXX shouldn't these be defined for us in math.h ???
    3939double hypot(double x, double y);
    4040double sqrt (double x);
    4141
    42 # include "models/pmModel_GAUSS.c"
    43 # include "models/pmModel_PGAUSS.c"
    44 # include "models/pmModel_QGAUSS.c"
    45 # include "models/pmModel_PS1_V1.c"
    46 # include "models/pmModel_RGAUSS.c"
    47 # include "models/pmModel_SERSIC.c"
     42# include "models/pmModel_GAUSS.h"
     43# include "models/pmModel_PGAUSS.h"
     44# include "models/pmModel_QGAUSS.h"
     45# include "models/pmModel_PS1_V1.h"
     46# include "models/pmModel_RGAUSS.h"
     47# include "models/pmModel_SERSIC.h"
    4848
    4949static pmModelClass defaultModels[] = {
    50     {"PS_MODEL_GAUSS",        7, pmModelFunc_GAUSS,   pmModelFlux_GAUSS,   pmModelRadius_GAUSS,   pmModelLimits_GAUSS,   pmModelGuess_GAUSS,  pmModelFromPSF_GAUSS,  pmModelParamsFromPSF_GAUSS,  pmModelFitStatus_GAUSS},
    51     {"PS_MODEL_PGAUSS",       7, pmModelFunc_PGAUSS,  pmModelFlux_PGAUSS,  pmModelRadius_PGAUSS,  pmModelLimits_PGAUSS,  pmModelGuess_PGAUSS, pmModelFromPSF_PGAUSS, pmModelParamsFromPSF_PGAUSS, pmModelFitStatus_PGAUSS},
    52     {"PS_MODEL_QGAUSS",       8, pmModelFunc_QGAUSS,  pmModelFlux_QGAUSS,  pmModelRadius_QGAUSS,  pmModelLimits_QGAUSS,  pmModelGuess_QGAUSS, pmModelFromPSF_QGAUSS, pmModelParamsFromPSF_QGAUSS, pmModelFitStatus_QGAUSS},
    53     {"PS_MODEL_PS1_V1",       8, pmModelFunc_PS1_V1,  pmModelFlux_PS1_V1,  pmModelRadius_PS1_V1,  pmModelLimits_PS1_V1,  pmModelGuess_PS1_V1, pmModelFromPSF_PS1_V1, pmModelParamsFromPSF_PS1_V1, pmModelFitStatus_PS1_V1},
    54     {"PS_MODEL_RGAUSS",       8, pmModelFunc_RGAUSS,  pmModelFlux_RGAUSS,  pmModelRadius_RGAUSS,  pmModelLimits_RGAUSS,  pmModelGuess_RGAUSS, pmModelFromPSF_RGAUSS, pmModelParamsFromPSF_RGAUSS, pmModelFitStatus_RGAUSS},
    55     {"PS_MODEL_SERSIC",       8, pmModelFunc_SERSIC,  pmModelFlux_SERSIC,  pmModelRadius_SERSIC,  pmModelLimits_SERSIC,  pmModelGuess_SERSIC, pmModelFromPSF_SERSIC, pmModelParamsFromPSF_SERSIC, pmModelFitStatus_SERSIC}
     50    {"PS_MODEL_GAUSS",        7, (pmModelFunc)pmModelFunc_GAUSS,   (pmModelFlux)pmModelFlux_GAUSS,   (pmModelRadius)pmModelRadius_GAUSS,   (pmModelLimits)pmModelLimits_GAUSS,   (pmModelGuessFunc)pmModelGuess_GAUSS,  (pmModelFromPSFFunc)pmModelFromPSF_GAUSS,  (pmModelParamsFromPSF)pmModelParamsFromPSF_GAUSS,  (pmModelFitStatusFunc)pmModelFitStatus_GAUSS,  (pmModelSetLimitsFunc)pmModelSetLimits_GAUSS  },
     51    {"PS_MODEL_PGAUSS",       7, (pmModelFunc)pmModelFunc_PGAUSS,  (pmModelFlux)pmModelFlux_PGAUSS,  (pmModelRadius)pmModelRadius_PGAUSS,  (pmModelLimits)pmModelLimits_PGAUSS,  (pmModelGuessFunc)pmModelGuess_PGAUSS, (pmModelFromPSFFunc)pmModelFromPSF_PGAUSS, (pmModelParamsFromPSF)pmModelParamsFromPSF_PGAUSS, (pmModelFitStatusFunc)pmModelFitStatus_PGAUSS, (pmModelSetLimitsFunc)pmModelSetLimits_PGAUSS },
     52    {"PS_MODEL_QGAUSS",       8, (pmModelFunc)pmModelFunc_QGAUSS,  (pmModelFlux)pmModelFlux_QGAUSS,  (pmModelRadius)pmModelRadius_QGAUSS,  (pmModelLimits)pmModelLimits_QGAUSS,  (pmModelGuessFunc)pmModelGuess_QGAUSS, (pmModelFromPSFFunc)pmModelFromPSF_QGAUSS, (pmModelParamsFromPSF)pmModelParamsFromPSF_QGAUSS, (pmModelFitStatusFunc)pmModelFitStatus_QGAUSS, (pmModelSetLimitsFunc)pmModelSetLimits_QGAUSS },
     53    {"PS_MODEL_PS1_V1",       8, (pmModelFunc)pmModelFunc_PS1_V1,  (pmModelFlux)pmModelFlux_PS1_V1,  (pmModelRadius)pmModelRadius_PS1_V1,  (pmModelLimits)pmModelLimits_PS1_V1,  (pmModelGuessFunc)pmModelGuess_PS1_V1, (pmModelFromPSFFunc)pmModelFromPSF_PS1_V1, (pmModelParamsFromPSF)pmModelParamsFromPSF_PS1_V1, (pmModelFitStatusFunc)pmModelFitStatus_PS1_V1, (pmModelSetLimitsFunc)pmModelSetLimits_PS1_V1 },
     54    {"PS_MODEL_RGAUSS",       8, (pmModelFunc)pmModelFunc_RGAUSS,  (pmModelFlux)pmModelFlux_RGAUSS,  (pmModelRadius)pmModelRadius_RGAUSS,  (pmModelLimits)pmModelLimits_RGAUSS,  (pmModelGuessFunc)pmModelGuess_RGAUSS, (pmModelFromPSFFunc)pmModelFromPSF_RGAUSS, (pmModelParamsFromPSF)pmModelParamsFromPSF_RGAUSS, (pmModelFitStatusFunc)pmModelFitStatus_RGAUSS, (pmModelSetLimitsFunc)pmModelSetLimits_RGAUSS },
     55    {"PS_MODEL_SERSIC",       8, (pmModelFunc)pmModelFunc_SERSIC,  (pmModelFlux)pmModelFlux_SERSIC,  (pmModelRadius)pmModelRadius_SERSIC,  (pmModelLimits)pmModelLimits_SERSIC,  (pmModelGuessFunc)pmModelGuess_SERSIC, (pmModelFromPSFFunc)pmModelFromPSF_SERSIC, (pmModelParamsFromPSF)pmModelParamsFromPSF_SERSIC, (pmModelFitStatusFunc)pmModelFitStatus_SERSIC, (pmModelSetLimitsFunc)pmModelSetLimits_SERSIC }
    5656};
    5757
     
    168168    return (models[type].name);
    169169}
     170
     171
     172void pmModelClassSetLimits(pmModelLimitsType type)
     173{
     174    if (!models) {
     175        pmModelClassInit();
     176    }
     177
     178    for (int i = 0; i < Nmodels; i++) {
     179        if (models[i].modelSetLimits) {
     180            models[i].modelSetLimits(type);
     181        }
     182    }
     183
     184}
     185
  • branches/simtest_nebulous_branches/psModules/src/objects/pmModelClass.h

    r15697 r27840  
    2929# define PM_MODEL_CLASS_H
    3030
     31#include <pmModel.h>
     32
    3133/// @addtogroup Objects Object Detection / Analysis Functions
    3234/// @{
    3335
    34 typedef struct
    35 {
     36typedef struct {
    3637    char *name;
    3738    int nParams;
     
    4445    pmModelParamsFromPSF modelParamsFromPSF;
    4546    pmModelFitStatusFunc modelFitStatus;
     47    pmModelSetLimitsFunc modelSetLimits;
    4648} pmModelClass;
    4749
     
    7375pmModelType pmModelClassGetType (const char *name);
    7476
     77/// Set parameter limits for all models
     78void pmModelClassSetLimits(pmModelLimitsType type);
     79
     80
    7581/// @}
    7682# endif /* PM_MODEL_CLASS_H */
  • branches/simtest_nebulous_branches/psModules/src/objects/pmModelUtils.c

    r19999 r27840  
    4848        return NULL;
    4949    }
    50     // XXX note that model->residuals is just a reference
     50    // note that model->residuals is just a reference
    5151    modelPSF->residuals = psf->residuals;
    5252
     
    6565    // set model parameters for this source based on PSF information
    6666    if (!modelPSF->modelParamsFromPSF (modelPSF, psf, Xo, Yo, Io)) {
    67         // XXX we do not want to raise an error here, just note that we failed
    68         // psError(PM_ERR_PSF, false, "Failed to set model params from PSF");
    6967        psFree(modelPSF);
    7068        return NULL;
    7169    }
    7270
    73     // XXX note that model->residuals is just a reference
     71    // note that model->residuals is just a reference
    7472    modelPSF->residuals = psf->residuals;
    7573
  • branches/simtest_nebulous_branches/psModules/src/objects/pmPSF.c

    r24206 r27840  
    4242#include "pmErrorCodes.h"
    4343
     44
     45#define MAX_AXIS_RATIO 20.0             // Maximum axis ratio for PSF model
     46
    4447/*****************************************************************************/
    4548/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
     
    405408    return psf;
    406409}
     410
     411
     412float pmPSFtoFWHM(const pmPSF *psf, float x, float y)
     413{
     414    PS_ASSERT_PTR_NON_NULL(psf, NAN);
     415
     416    pmModel *model = pmModelFromPSFforXY(psf, x, y, 1.0); // Model of source
     417    if (!model) {
     418        psError(PS_ERR_UNKNOWN, false, "Unable to determine PSF model at %f,%f\n", x, y);
     419        return NAN;
     420    }
     421    psF32 *params = model->params->data.F32; // Model parameters
     422    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO); // Ellipse axes
     423
     424    // Curiously, the minor axis can be larger than the major axis, so need to check.
     425    float fwhm = 2.355 * PS_MAX(axes.minor, axes.major); // FWHM, converted from sigma
     426
     427    psFree(model);
     428
     429    return fwhm;
     430}
  • branches/simtest_nebulous_branches/psModules/src/objects/pmPSF.h

    r24206 r27840  
    3838 *
    3939 */
    40 typedef struct
    41 {
     40typedef struct {
    4241    pmModelType type;                   ///< PSF Model in use
    4342    psArray *params;                    ///< Model parameters (psPolynomial2D)
     
    6564    pmGrowthCurve *growth;              ///< apMag vs Radius
    6665    pmResiduals *residuals;             ///< normalized residual image (no spatial variation)
    67 }
    68 pmPSF;
     66} pmPSF;
    6967
    7068typedef struct {
     
    8179    bool          poissonErrorsPhotLin; ///< use poission errors for linear model fitting
    8280    bool          poissonErrorsParams; ///< use poission errors for model parameter fitting
    83     float         radius;
     81    float         fitRadius;
     82    float         apRadius;
    8483    bool          chiFluxTrend;         // Fit a trend in Chi2 as a function of flux?
    8584} pmPSFOptions;
     
    112111psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR);
    113112
     113/// Calculate FWHM value from a PSF
     114float pmPSFtoFWHM(
     115    const pmPSF *psf,                   // PSF of interest
     116    float x, float y                    // Position of interest
     117    );
     118
     119
    114120/// @}
    115121# endif
  • branches/simtest_nebulous_branches/psModules/src/objects/pmPSF_IO.c

    r21183 r27840  
    2828#include "pmConfig.h"
    2929#include "pmDetrendDB.h"
     30#include "pmErrorCodes.h"
    3031
    3132#include "pmHDU.h"
     
    4950#include "pmSource.h"
    5051#include "pmModelClass.h"
     52#include "pmModelUtils.h"
    5153#include "pmSourceIO.h"
    5254
     
    121123    if (view->chip == -1) {
    122124        if (!pmPSFmodelWriteFPA(fpa, view, file, config)) {
    123             psError(PS_ERR_IO, false, "Failed to write PSF for fpa");
     125            psError(psErrorCodeLast(), false, "Failed to write PSF for fpa");
    124126            return false;
    125127        }
     
    134136    if (view->cell == -1) {
    135137        if (!pmPSFmodelWriteChip (chip, view, file, config)) {
    136             psError(PS_ERR_IO, false, "Failed to write PSF for chip");
     138            psError(psErrorCodeLast(), false, "Failed to write PSF for chip");
    137139            return false;
    138140        }
     
    140142    }
    141143
    142     psError(PS_ERR_IO, false, "PSF must be written at the chip level");
     144    psError(PM_ERR_CONFIG, true, "PSF must be written at the chip level");
    143145    return false;
    144146}
     
    157159        thisView->chip = i;
    158160        if (!pmPSFmodelWriteChip (chip, thisView, file, config)) {
    159             psError(PS_ERR_IO, false, "Failed to write PSF for %dth chip", i);
     161            psError(psErrorCodeLast(), false, "Failed to write PSF for %dth chip", i);
    160162            psFree(thisView);
    161163            return false;
     
    172174    PS_ASSERT_PTR_NON_NULL(chip, false);
    173175
    174     if (!pmPSFmodelWrite (chip->analysis, view, file, config)) {
    175         psError(PS_ERR_IO, false, "Failed to write PSF for chip");
     176    if (!pmPSFmodelWrite(chip->analysis, view, file, config)) {
     177        psError(psErrorCodeLast(), false, "Failed to write PSF for chip");
    176178        return false;
    177179    }
     
    196198    char *headName, *tableName, *residName;
    197199
    198     if (!analysis) return false;
     200    if (!analysis) {
     201        psError(PM_ERR_PROG, true, "No analysis metadata for chip.");
     202        return false;
     203    }
    199204
    200205    // select the current recipe
    201206    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, "PSPHOT");
    202207    if (!recipe) {
    203         psError(PS_ERR_UNKNOWN, false, "missing recipe %s", "PSPHOT");
     208        psError(PM_ERR_CONFIG, false, "missing recipe %s", "PSPHOT");
    204209        return false;
    205210    }
     
    212217    // get the current header
    213218    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
     219    if (!fpa) {
     220        psError(psErrorCodeLast(), false, "Unable to get FPA for writing.");
     221        return false;
     222    }
    214223    pmHDU *hdu = psMemIncrRefCounter(pmFPAviewThisHDU(view, fpa));
    215224    psFree(fpa);
    216225    if (!hdu) {
    217         psError(PS_ERR_UNKNOWN, false, "Unable to find HDU");
     226        psError(PM_ERR_CONFIG, false, "Unable to find HDU");
    218227        return false;
    219228    }
     
    233242        psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "EXTNAME.RULES");
    234243        if (!menu) {
    235             psError(PS_ERR_UNKNOWN, true, "missing EXTNAME.RULES in camera.config");
     244            psError(PM_ERR_CONFIG, true, "missing EXTNAME.RULES in camera.config");
    236245            psFree(hdu);
    237246            return false;
     
    241250        rule = psMetadataLookupStr(&status, menu, "PSF.HEAD");
    242251        if (!rule) {
    243             psError(PS_ERR_UNKNOWN, false, "missing entry for PSF.HEAD in EXTNAME.RULES in camera.config");
     252            psError(PM_ERR_CONFIG, false, "missing entry for PSF.HEAD in EXTNAME.RULES in camera.config");
    244253            psFree(hdu);
    245254            return false;
     
    250259        rule = psMetadataLookupStr(&status, menu, "PSF.TABLE");
    251260        if (!rule) {
    252             psError(PS_ERR_UNKNOWN, false, "missing entry for PSF.TABLE in EXTNAME.RULES in camera.config");
     261            psError(PM_ERR_CONFIG, false, "missing entry for PSF.TABLE in EXTNAME.RULES in camera.config");
    253262            psFree (headName);
    254263            psFree(hdu);
     
    260269        rule = psMetadataLookupStr(&status, menu, "PSF.RESID");
    261270        if (!rule) {
    262             psError(PS_ERR_UNKNOWN, false, "missing entry for PSF.RESID in EXTNAME.RULES in camera.config");
     271            psError(PM_ERR_CONFIG, false, "missing entry for PSF.RESID in EXTNAME.RULES in camera.config");
    263272            psFree (headName);
    264273            psFree (tableName);
     
    267276        }
    268277        residName = pmFPAfileNameFromRule (rule, file, view);
     278
     279        // EXTNAME for psf image
     280        // rule = psMetadataLookupStr(&status, menu, "PSF.RESID");
     281        // if (!rule) {
     282        //     psError(PS_ERR_UNKNOWN, false, "missing entry for PSF.RESID in EXTNAME.RULES in camera.config");
     283        //     psFree (headName);
     284        //     psFree (tableName);
     285        //     psFree(hdu);
     286        //     return false;
     287        // }
     288        // residName = pmFPAfileNameFromRule (rule, file, view);
    269289    }
    270290
     
    282302        }
    283303
    284         psFitsWriteBlank (file->fits, hdu->header, headName);
     304        if (!psFitsWriteBlank(file->fits, hdu->header, headName)) {
     305            psError(psErrorCodeLast(), false, "Unable to write PSF PHU.");
     306            psFree(hdu);
     307            return false;
     308        }
    285309        psTrace ("pmFPAfile", 5, "wrote ext head %s (type: %d)\n", file->filename, file->type);
    286310        file->header = hdu->header;
     
    292316    pmPSF *psf = psMetadataLookupPtr (&status, analysis, "PSPHOT.PSF");
    293317    if (!psf) {
    294         psError(PS_ERR_UNKNOWN, true, "missing PSF for this analysis metadata");
     318        psError(PM_ERR_PROG, true, "missing PSF for this analysis metadata");
    295319        psFree (tableName);
    296320        psFree (residName);
     
    310334        psMetadataAddBool (header, PS_LIST_TAIL, "ERR_PAR",  0, "Use Poisson errors in fits?", psf->poissonErrorsParams);
    311335
    312         int nPar = pmModelClassParameterCount (psf->type)    ;
     336        int nPar = pmModelClassParameterCount (psf->type);
    313337        psMetadataAdd (header, PS_LIST_TAIL, "PSF_NPAR", PS_DATA_S32, "PSF model parameter count", nPar);
    314338
     
    321345        pmPSFClump psfClump;
    322346
    323         // we now save clump parameters for each region : need to save all of those
    324         int nRegions = psMetadataLookupS32 (&status, recipe, "PSF.CLUMP.NREGIONS");
    325         psMetadataAddS32 (header, PS_LIST_TAIL, "PSF_CLN", PS_META_REPLACE, "number of psf clump regions", nRegions);
    326         for (int i = 0; i < nRegions; i++) {
    327             char regionName[64];
    328             snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
    329             psMetadata *regionMD = psMetadataLookupPtr (&status, recipe, regionName);
    330 
    331             psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X");   assert (status);
    332             psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   assert (status);
    333             psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  assert (status);
    334             psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  assert (status);
    335 
    336             char key[16];
    337             snprintf (key, 9, "CLX_%03d", i);
    338             psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump center", psfClump.X);
    339             snprintf (key, 9, "CLY_%03d", i);
    340             psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump center", psfClump.Y);
    341             snprintf (key, 9, "CLDX_%03d", i);
    342             psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump size", psfClump.dX);
    343             snprintf (key, 9, "CLDY_%03d", i);
    344             psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump size", psfClump.dY);
    345         }
     347        // we now save clump parameters for each region : need to save all of those
     348        int nRegions = psMetadataLookupS32 (&status, analysis, "PSF.CLUMP.NREGIONS");
     349        psMetadataAddS32 (header, PS_LIST_TAIL, "PSF_CLN", PS_META_REPLACE, "number of psf clump regions", nRegions);
     350        for (int i = 0; i < nRegions; i++) {
     351            char regionName[64];
     352            snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
     353            psMetadata *regionMD = psMetadataLookupPtr (&status, analysis, regionName);
     354
     355            psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X");   assert (status);
     356            psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   assert (status);
     357            psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  assert (status);
     358            psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  assert (status);
     359
     360            char key[16];
     361            snprintf (key, 9, "CLX_%03d", i);
     362            psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump center", psfClump.X);
     363            snprintf (key, 9, "CLY_%03d", i);
     364            psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump center", psfClump.Y);
     365            snprintf (key, 9, "CLDX_%03d", i);
     366            psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump size", psfClump.dX);
     367            snprintf (key, 9, "CLDY_%03d", i);
     368            psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump size", psfClump.dY);
     369        }
    346370
    347371        // save the dimensions of each parameter
     
    424448        // write an empty FITS segment if we have no PSF information
    425449        if (psfTable->n == 0) {
    426             // XXX this is probably an error (if we have a PSF, how do we have no data?)
    427             psFitsWriteBlank (file->fits, header, tableName);
     450            psError(PM_ERR_PROG, true, "No PSF data to write.");
     451            psFree(tableName);
     452            psFree(residName);
     453            psFree(psfTable);
     454            psFree(header);
     455            return false;
    428456        } else {
    429457            psTrace ("pmFPAfile", 5, "writing psf data %s\n", tableName);
    430             if (!psFitsWriteTable (file->fits, header, psfTable, tableName)) {
    431                 psError(PS_ERR_IO, false, "writing psf table data %s\n", tableName);
     458            if (!psFitsWriteTable(file->fits, header, psfTable, tableName)) {
     459                psError(psErrorCodeLast(), false, "Error writing psf table data %s\n", tableName);
    432460                psFree (tableName);
    433461                psFree (residName);
     
    447475        if (psf->residuals == NULL) {
    448476            // set some header keywords to make it clear there are no residuals?
    449             psFitsWriteBlank (file->fits, header, residName);
     477            if (!psFitsWriteBlank(file->fits, header, residName)) {
     478                psError(psErrorCodeLast(), false, "Unable to write blank PSF residual image.");
     479                psFree(residName);
     480                psFree(header);
     481                return false;
     482            }
    450483            psFree (residName);
    451484            psFree (header);
     
    458491        psMetadataAddS32 (header, PS_LIST_TAIL, "YCENTER", 0, "", psf->residuals->yCenter);
    459492
    460         // write the residuals as three planes of the image
    461         // this call creates an extension with NAXIS3 = 3
     493        // write the residuals as planes of the image
     494        psArray *images = psArrayAllocEmpty (1);
     495        psArrayAdd (images, 1, psf->residuals->Ro);  // z = 0 is Ro
     496
    462497        if (psf->residuals->Rx) {
    463             // this call creates an extension with NAXIS3 = 3
    464             psArray *images = psArrayAllocEmpty (3);
    465             psArrayAdd (images, 1, psf->residuals->Ro);
    466498            psArrayAdd (images, 1, psf->residuals->Rx);
    467499            psArrayAdd (images, 1, psf->residuals->Ry);
    468 
    469             psFitsWriteImageCube (file->fits, header, images, residName);
    470             psFree (images);
    471         } else {
    472             // this call creates an extension with NAXIS3 = 1
    473             psFitsWriteImage(file->fits, header, psf->residuals->Ro, 0, residName);
    474         }
     500        }
     501
     502        // note that all N plane are implicitly of the same type, so we convert the mask
     503        if (psf->residuals->mask) {
     504            psImage *mask = psImageCopy (NULL, psf->residuals->mask, psf->residuals->Ro->type.type);
     505            psArrayAdd (images, 1, mask);
     506            psFree (mask);
     507        }
     508
     509        // psFitsWriteImageCube (file->fits, header, images, residName);
     510        // psFree (images);
     511
     512        if (!psFitsWriteImageCube (file->fits, header, images, residName)) {
     513            psError(psErrorCodeLast(), false, "Unable to write PSF residuals.");
     514            psFree(images);
     515            psFree(residName);
     516            psFree(header);
     517            return false;
     518        }
     519        psFree (images);
    475520        psFree (residName);
     521        psFree (header);
     522    }
     523
     524    // write a representation of the psf model
     525    {
     526        psMetadata *header = psMetadataAlloc ();
     527
     528        if (0) {
     529            // set some header keywords to make it clear there are no residuals?
     530            if (!psFitsWriteBlank (file->fits, header, residName)) {
     531                psError(psErrorCodeLast(), false, "Unable to write blank PSF residuals.");
     532                psFree(residName);
     533                psFree(header);
     534                return false;
     535            }
     536            psFree (residName);
     537            psFree (header);
     538            return true;
     539        }
     540
     541        int DX = 65;
     542        int DY = 65;
     543
     544        psImage *psfMosaic = psImageAlloc (DX, DY, PS_TYPE_F32);
     545        psImageInit (psfMosaic, 0.0);
     546
     547        pmModel *modelRef = pmModelAlloc(psf->type);
     548
     549        // use the center of the center pixel of the image
     550        float xc = 0.5*psf->fieldNx;
     551        float yc = 0.5*psf->fieldNy;
     552
     553        // assign the x and y coords to the image center
     554        // create an object with center intensity of 1000
     555        modelRef->params->data.F32[PM_PAR_SKY] = 0;
     556        modelRef->params->data.F32[PM_PAR_I0] = 1.000;
     557        modelRef->params->data.F32[PM_PAR_XPOS] = xc;
     558        modelRef->params->data.F32[PM_PAR_YPOS] = yc;
     559
     560        // create modelPSF from this model
     561        pmModel *model = pmModelFromPSF (modelRef, psf);
     562        if (model) {
     563            // place the reference object in the image center
     564            pmModelAddWithOffset (psfMosaic, NULL, model, PM_MODEL_OP_FULL | PM_MODEL_OP_CENTER, 0, 0.0, 0.0);
     565            psFree (model);
     566
     567            if (false) {
     568                // this call creates an extension with NAXIS3 = 3
     569                psArray *images = psArrayAllocEmpty (3);
     570                psArrayAdd (images, 1, psfMosaic);
     571                // psArrayAdd (images, 1, psfModel);
     572                // psArrayAdd (images, 1, psfModel);
     573
     574                if (!psFitsWriteImageCube (file->fits, header, images, "PSF_MODEL")) {
     575                    psError(psErrorCodeLast(), false, "Unable to write PSF representation.");
     576                    psFree(images);
     577                    psFree(psfMosaic);
     578                    psFree(modelRef);
     579                    psFree(header);
     580                    return false;
     581                }
     582                psFree (images);
     583            } else {
     584                // this call creates an extension with NAXIS3 = 1
     585                // XXX need to replace PSF_MODEL with rule-based name like residName
     586                if (!psFitsWriteImage(file->fits, header, psfMosaic, 0, "PSF_MODEL")) {
     587                    psError(psErrorCodeLast(), false, "Unable to write PSF representation.");
     588                    psFree(psfMosaic);
     589                    psFree(modelRef);
     590                    psFree(header);
     591                    return false;
     592                }
     593            }
     594        }
     595
     596        psFree (psfMosaic);
     597        psFree (modelRef);
    476598        psFree (header);
    477599    }
     
    523645    // find the FPA phu
    524646    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
     647    if (!fpa) {
     648        psError(psErrorCodeLast(), false, "Unable to build FPA to write.");
     649        return false;
     650    }
    525651    pmHDU *phu = psMemIncrRefCounter(pmFPAviewThisPHU(view, fpa));
    526652    psFree(fpa);
     
    533659        psMetadataCopy (outhead, phu->header);
    534660    } else {
    535         pmConfigConformHeader (outhead, file->format);
     661        if (!pmConfigConformHeader (outhead, file->format)) {
     662            psError(psErrorCodeLast(), false, "Unable to conform header of PSF PHU.");
     663            psFree(phu);
     664            return false;
     665        }
    536666    }
    537667    psFree(phu);
    538668
    539669    psMetadataAddBool (outhead, PS_LIST_TAIL, "EXTEND", PS_META_REPLACE, "this file has extensions", true);
    540     psFitsWriteBlank (file->fits, outhead, "");
     670    if (!psFitsWriteBlank (file->fits, outhead, "")) {
     671        psError(psErrorCodeLast(), false, "Unable to write PHU for PSF.");
     672        psFree(outhead);
     673        return false;
     674    }
    541675    file->wrote_phu = true;
    542676
     
    568702    }
    569703
    570     psError(PS_ERR_IO, false, "PSF must be read at the chip level");
     704    psError(PM_ERR_CONFIG, true, "PSF must be read at the chip level");
    571705    return false;
    572706}
     
    595729
    596730    if (!pmPSFmodelRead (chip->analysis, view, file, config)) {
    597         psError(PS_ERR_IO, false, "Failed to write PSF for chip");
     731        psError(psErrorCodeLast(), false, "Failed to write PSF for chip");
    598732        return false;
    599733    }
     
    618752    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, "PSPHOT");
    619753    if (!recipe) {
    620         psError(PS_ERR_UNKNOWN, false, "missing recipe %s", "PSPHOT");
     754        psError(PM_ERR_CONFIG, false, "missing recipe %s", "PSPHOT");
    621755        return false;
    622756    }
     
    625759    psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "EXTNAME.RULES");
    626760    if (!menu) {
    627         psError(PS_ERR_UNKNOWN, true, "missing EXTNAME.RULES in camera.config");
     761        psError(PM_ERR_CONFIG, true, "missing EXTNAME.RULES in camera.config");
    628762        return false;
    629763    }
     
    631765    rule = psMetadataLookupStr(&status, menu, "PSF.TABLE");
    632766    if (!rule) {
    633         psError(PS_ERR_UNKNOWN, true, "missing entry for PSF.TABLE in EXTNAME.RULES in camera.config");
     767        psError(PM_ERR_CONFIG, true, "missing entry for PSF.TABLE in EXTNAME.RULES in camera.config");
    634768        return false;
    635769    }
     
    638772    rule = psMetadataLookupStr(&status, menu, "PSF.RESID");
    639773    if (!rule) {
    640         psError(PS_ERR_UNKNOWN, true, "missing entry for PSF.RESID in EXTNAME.RULES in camera.config");
     774        psError(PM_ERR_CONFIG, true, "missing entry for PSF.RESID in EXTNAME.RULES in camera.config");
    641775        return false;
    642776    }
     
    646780    // advance to the table data extension
    647781    // since we have read the IMAGE header, the TABLE header should exist
    648     if (!psFitsMoveExtName (file->fits, tableName)) {
    649         psAbort("cannot find data extension %s in %s", tableName, file->filename);
     782    if (!psFitsMoveExtName(file->fits, tableName)) {
     783        psError(psErrorCodeLast(), false, "cannot find data extension %s in %s", tableName, file->filename);
     784        return false;
    650785    }
    651786
    652787    // load the PSF model table header
    653788    header = psFitsReadHeader (NULL, file->fits);
    654     if (!header) psAbort("cannot read table header");
     789    if (!header) {
     790        psError(psErrorCodeLast(), false, "Cannot read PSF table header.");
     791        return false;
     792    }
    655793
    656794    pmPSFOptions *options = pmPSFOptionsAlloc();
     
    667805    int nRegions = psMetadataLookupS32 (&status, header, "PSF_CLN");
    668806    if (!status) {
    669         // read old-style psf clump data
    670 
    671         char regionName[64];
    672         snprintf (regionName, 64, "PSF.CLUMP.REGION.000");
    673         psMetadataAddS32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", 1);
    674 
    675         psMetadata *regionMD = psMetadataLookupPtr (&status, recipe, regionName);
    676         if (!regionMD) {
    677             regionMD = psMetadataAlloc();
    678             psMetadataAddMetadata (recipe, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
    679             psFree (regionMD);
    680         }
    681 
    682         // psf clump data
    683         pmPSFClump psfClump;
    684 
    685         psfClump.X  = psMetadataLookupF32 (&status, header, "PSF_CLX" );  assert(status);
    686         psfClump.Y  = psMetadataLookupF32 (&status, header, "PSF_CLY" );  assert(status);
    687         psfClump.dX = psMetadataLookupF32 (&status, header, "PSF_CLDX");  assert(status);
    688         psfClump.dY = psMetadataLookupF32 (&status, header, "PSF_CLDY");  assert(status);
    689 
    690         psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
    691         psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
    692         psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
    693         psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
     807        // read old-style psf clump data
     808
     809        char regionName[64];
     810        snprintf (regionName, 64, "PSF.CLUMP.REGION.000");
     811        psMetadataAddS32 (analysis, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", 1);
     812
     813        psMetadata *regionMD = psMetadataLookupPtr (&status, analysis, regionName);
     814        if (!regionMD) {
     815            regionMD = psMetadataAlloc();
     816            psMetadataAddMetadata (analysis, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
     817            psFree (regionMD);
     818        }
     819
     820        // psf clump data
     821        pmPSFClump psfClump;
     822
     823        psfClump.X  = psMetadataLookupF32 (&status, header, "PSF_CLX" );  assert(status);
     824        psfClump.Y  = psMetadataLookupF32 (&status, header, "PSF_CLY" );  assert(status);
     825        psfClump.dX = psMetadataLookupF32 (&status, header, "PSF_CLDX");  assert(status);
     826        psfClump.dY = psMetadataLookupF32 (&status, header, "PSF_CLDY");  assert(status);
     827
     828        psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
     829        psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
     830        psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
     831        psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
    694832    } else {
    695         psMetadataAddS32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", nRegions);
    696 
    697         for (int i = 0; i < nRegions; i++) {
    698             char key[10];
    699             char regionName[64];
    700             snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
    701 
    702             psMetadata *regionMD = psMetadataLookupPtr (&status, recipe, regionName);
    703             if (!regionMD) {
    704                 regionMD = psMetadataAlloc();
    705                 psMetadataAddMetadata (recipe, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
    706                 psFree (regionMD);
    707             }
    708 
    709             // psf clump data
    710             pmPSFClump psfClump;
    711 
    712             snprintf (key, 9, "CLX_%03d", i);
    713             psfClump.X  = psMetadataLookupF32 (&status, header, key);  assert(status);
    714             snprintf (key, 9, "CLY_%03d", i);
    715             psfClump.Y  = psMetadataLookupF32 (&status, header, key);  assert(status);
    716             snprintf (key, 9, "CLDX_%03d", i);
    717             psfClump.dX = psMetadataLookupF32 (&status, header, key);  assert(status);
    718             snprintf (key, 9, "CLDY_%03d", i);
    719             psfClump.dY = psMetadataLookupF32 (&status, header, key);  assert(status);
    720 
    721             psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
    722             psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
    723             psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
    724             psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
    725         }
     833        psMetadataAddS32 (analysis, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", nRegions);
     834
     835        for (int i = 0; i < nRegions; i++) {
     836            char key[10];
     837            char regionName[64];
     838            snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
     839
     840            psMetadata *regionMD = psMetadataLookupPtr (&status, analysis, regionName);
     841            if (!regionMD) {
     842                regionMD = psMetadataAlloc();
     843                psMetadataAddMetadata (analysis, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
     844                psFree (regionMD);
     845            }
     846
     847            // psf clump data
     848            pmPSFClump psfClump;
     849
     850            snprintf (key, 9, "CLX_%03d", i);
     851            psfClump.X  = psMetadataLookupF32 (&status, header, key);  assert(status);
     852            snprintf (key, 9, "CLY_%03d", i);
     853            psfClump.Y  = psMetadataLookupF32 (&status, header, key);  assert(status);
     854            snprintf (key, 9, "CLDX_%03d", i);
     855            psfClump.dX = psMetadataLookupF32 (&status, header, key);  assert(status);
     856            snprintf (key, 9, "CLDY_%03d", i);
     857            psfClump.dY = psMetadataLookupF32 (&status, header, key);  assert(status);
     858
     859            psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
     860            psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
     861            psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
     862            psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
     863        }
    726864    }
    727865
     
    764902        char *modeName = psMetadataLookupStr (&status, header, name);
    765903        if (!status) {
    766             psError(PS_ERR_UNKNOWN, true, "inconsistent PSF header: NX & NY defined for PAR %d, but not MD", i);
     904            psError(PM_ERR_PROG, true, "inconsistent PSF header: NX & NY defined for PAR %d, but not MD", i);
    767905            return false;
    768906        }
     
    798936    // read the raw table data
    799937    psArray *table = psFitsReadTable (file->fits);
     938    if (!table) {
     939        psError(psErrorCodeLast(), false, "Unable to read PSF table.");
     940        psFree(header);
     941        return false;
     942    }
    800943
    801944    // fill in the matching psf->params entries
     
    842985    // since we have read the IMAGE header, the TABLE header should exist
    843986    if (!psFitsMoveExtName (file->fits, imageName)) {
    844         psAbort("cannot find data extension %s in %s", imageName, file->filename);
     987        psError(psErrorCodeLast(), false, "Cannot find PSF data extension %s in %s",
     988                imageName, file->filename);
     989        return false;
    845990    }
    846991
    847992    header = psFitsReadHeader (NULL, file->fits);
     993    if (!header) {
     994        psError(psErrorCodeLast(), false, "Unable to read PSF header.");
     995        return false;
     996    }
    848997    int Naxis = psMetadataLookupS32 (&status, header, "NAXIS");
    849998    if (Naxis != 0) {
     
    8651014
    8661015        psRegion fullImage = {0, 0, 0, 0};
    867         psFitsReadImageBuffer(psf->residuals->Ro, file->fits, fullImage, 0); // Desired pixels
    868         if (Nz > 1) {
    869             assert (Nz == 3);
    870             psFitsReadImageBuffer(psf->residuals->Rx, file->fits, fullImage, 1); // Desired pixels
    871             psFitsReadImageBuffer(psf->residuals->Ry, file->fits, fullImage, 2); // Desired pixels
    872         }
    873         // XXX notice that we are not saving the resid->mask
     1016        if (!psFitsReadImageBuffer(psf->residuals->Ro, file->fits, fullImage, 0)) {
     1017            psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
     1018            return false;
     1019        }
     1020
     1021        // note that all N plane are implicitly of the same type, so we convert the mask
     1022        psImage *mask = psImageCopy(NULL, psf->residuals->mask, psf->residuals->Ro->type.type);
     1023        psImageInit (psf->residuals->mask, 0);
     1024        psImageInit (psf->residuals->Rx, 0.0);
     1025        psImageInit (psf->residuals->Ry, 0.0);
     1026        switch (Nz) {
     1027          case 1: // Ro only
     1028            break;
     1029          case 2: // Ro and mask
     1030            if (!psFitsReadImageBuffer(mask, file->fits, fullImage, 1)) {
     1031                psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
     1032                return false;
     1033            }
     1034            psImageCopy (psf->residuals->mask, mask, PM_TYPE_RESID_MASK);
     1035            break;
     1036          case 3: // Ro, Rx and Ry, no mask
     1037            if (!psFitsReadImageBuffer(psf->residuals->Rx, file->fits, fullImage, 1)) {
     1038                psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
     1039                return false;
     1040            }
     1041            if (!psFitsReadImageBuffer(psf->residuals->Ry, file->fits, fullImage, 2)) {
     1042                psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
     1043                return false;
     1044            }
     1045            break;
     1046          case 4: // Ro, Rx, Ry, and mask:
     1047            if (!psFitsReadImageBuffer(psf->residuals->Rx, file->fits, fullImage, 1)) {
     1048                psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
     1049                return false;
     1050            }
     1051            if (!psFitsReadImageBuffer(psf->residuals->Ry, file->fits, fullImage, 2)) {
     1052                psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
     1053                return false;
     1054            }
     1055            if (!psFitsReadImageBuffer(mask, file->fits, fullImage, 3)) {
     1056                psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
     1057                return false;
     1058            }
     1059            psImageCopy (psf->residuals->mask, mask, PM_TYPE_RESID_MASK);
     1060            break;
     1061        }
     1062        psFree (mask);
    8741063    }
    8751064
  • branches/simtest_nebulous_branches/psModules/src/objects/pmPSFtry.c

    r24206 r27840  
    3737#include "pmSourceVisual.h"
    3838
    39 bool printTrendMap (pmTrend2D *trend) {
    40 
    41     if (!trend->map) return false;
    42     if (!trend->map->map) return false;
    43 
    44     for (int j = 0; j < trend->map->map->numRows; j++) {
    45         for (int i = 0; i < trend->map->map->numCols; i++) {
    46             fprintf (stderr, "%5.2f  ", trend->map->map->data.F32[j][i]);
    47         }
    48         fprintf (stderr, "\t\t\t");
    49         for (int i = 0; i < trend->map->map->numCols; i++) {
    50             fprintf (stderr, "%5.2f  ", trend->map->error->data.F32[j][i]);
    51         }
    52         fprintf (stderr, "\n");
    53     }
    54     return true;
    55 }
    56 
    57 bool psImageMapCleanup (psImageMap *map) {
    58 
    59     if ((map->map->numRows == 1) && (map->map->numCols == 1)) return true;
    60 
    61     // find the weighted average of all pixels
    62     float Sum = 0.0;
    63     float Wt = 0.0;
    64     for (int j = 0; j < map->map->numRows; j++) {
    65         for (int i = 0; i < map->map->numCols; i++) {
    66             if (!isfinite(map->map->data.F32[j][i])) continue;
    67             Sum += map->map->data.F32[j][i] * map->error->data.F32[j][i];
    68             Wt += map->error->data.F32[j][i];
    69         }
    70     }
    71 
    72     float Mean = Sum / Wt;
    73 
    74     // do any of the pixels in the map need to be repaired?
    75     // XXX for now, we are just replacing bad pixels with the Mean
    76     for (int j = 0; j < map->map->numRows; j++) {
    77         for (int i = 0; i < map->map->numCols; i++) {
    78             if (isfinite(map->map->data.F32[j][i]) &&
    79                 (map->error->data.F32[j][i] > 0.0)) continue;
    80             map->map->data.F32[j][i] = Mean;
    81         }
    82     }
    83     return true;
    84 }
    85 
    8639// ********  pmPSFtry functions  **************************************************
    8740// * pmPSFtry holds a single pmPSF model test, with the input sources, the freely
     
    11063    psMemSetDeallocator(test, (psFreeFunc) pmPSFtryFree);
    11164
    112     test->psf       = pmPSFAlloc (options);
     65    test->psf       = NULL;
    11366    test->metric    = psVectorAlloc (sources->n, PS_TYPE_F32);
    11467    test->metricErr = psVectorAlloc (sources->n, PS_TYPE_F32);
     
    13689}
    13790
     91float psVectorSystematicError (psVector *residuals, psVector *errors, float clipFraction) {
    13892
    139 // build a pmPSFtry for the given model:
    140 // - fit each source with the free-floating model
    141 // - construct the pmPSF from the collection of models
    142 // - fit each source with the PSF-parameter models
    143 // - measure the pmPSF quality metric (dApResid)
     93    psAssert(residuals, "residuals cannot be NULL");
     94    psAssert(errors, "errors cannot be NULL");
     95    psAssert(residuals->n == errors->n, "residuals and errors must be the same length");
    14496
    145 // sources used in for pmPSFtry may be masked by the analysis
    146 // mask values indicate the reason the source was rejected:
     97    // given a vector of residuals and their formal errors, calculated the necessary systematic
     98    // error needed to yield a reduced chisq of 1.0, after first tossing out the clipFraction
     99    // highest chi-square contributors (allowed outliers)
    147100
    148 // generate a pmPSFtry with a copy of the test PSF sources
    149 pmPSFtry *pmPSFtryModel (const psArray *sources, const char *modelName, pmPSFOptions *options, psImageMaskType maskVal, psImageMaskType markVal)
    150 {
    151     bool status;
    152     int Next = 0;
    153     int Npsf = 0;
     101    psVector *mask  = psVectorAlloc(residuals->n, PS_TYPE_VECTOR_MASK);
     102    psVector *chisq = psVectorAlloc(residuals->n, PS_TYPE_F32);
    154103
    155     // validate the requested model name
    156     options->type = pmModelClassGetType (modelName);
    157     if (options->type == -1) {
    158         psError (PS_ERR_UNKNOWN, true, "invalid model name %s", modelName);
    159         return NULL;
     104    // calculate the chisq vector:
     105    int Ngood = 0;
     106    for (int i = 0; i < residuals->n; i++) {
     107        chisq->data.F32[i] = PS_MAX_F32;
     108        if (!isfinite(residuals->data.F32[i])) continue;
     109        if (!isfinite(errors->data.F32[i])) continue;
     110        if (errors->data.F32[i] <= 0.0) continue;
     111        chisq->data.F32[i] = PS_SQR(residuals->data.F32[i] / errors->data.F32[i]);
     112        Ngood ++;
    160113    }
    161114
    162     pmPSFtry *psfTry = pmPSFtryAlloc (sources, options);
    163     if (psfTry == NULL) {
    164         psError (PS_ERR_UNKNOWN, false, "failed to allocate psf model");
    165         return NULL;
     115    psVector *index = psVectorSortIndex(NULL, chisq);
     116
     117    // toss out the clipFraction highest chisq values
     118    for (int i = 0; i < residuals->n; i++) {
     119        int n = index->data.S32[i];
     120        if (i < (1.0 - clipFraction)*Ngood) {
     121            mask->data.PS_TYPE_VECTOR_MASK_DATA[n] = 0;
     122        } else {
     123            mask->data.PS_TYPE_VECTOR_MASK_DATA[n] = 1;
     124        }
    166125    }
    167126
    168     // maskVal is used to test for rejected pixels, and must include markVal
    169     maskVal |= markVal;
     127    // Ndof ~= Ngood
     128    // Chisq_Ndof = sum(residuals_i^2 / error_i^2) / Ndof
     129    // choose S2 such than Chisq^sys_Ndof = sum(residuals_i^2 / (error_i^2 + S2)) / Ndof = 1.0
     130   
     131    // use Newton-Raphson to solve for S2:
    170132
    171     // stage 1:  fit an EXT model to all candidates PSF sources -- this is independent of the modeled 2D variations in the PSF
    172     psTimerStart ("psf.fit");
    173     for (int i = 0; i < psfTry->sources->n; i++) {
     133    // use median sigma to calculate the initial guess for S2:
     134    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
     135    psVectorStats (stats, errors, NULL, mask, 1);
     136    float errorMedian = stats->sampleMedian;
     137   
     138    float nPts = 0.0;
     139    float res2mean = 0.0;
     140    float ChiSq = 0.0;
     141    for (int i = 0; i < residuals->n; i++) {
     142        int n = index->data.S32[i];
     143        if (mask->data.PS_TYPE_VECTOR_MASK_DATA[n]) continue;
     144        res2mean += PS_SQR(residuals->data.F32[n]);
     145        ChiSq += PS_SQR(residuals->data.F32[n]) / PS_SQR(errors->data.F32[n]);
     146        nPts += 1.0;
     147    }
     148    res2mean /= nPts;
     149    ChiSq /= nPts;
     150   
     151    float S2guess = res2mean - PS_SQR(errorMedian);
    174152
    175         pmSource *source = psfTry->sources->data[i];
    176         if (!source->moments) {
    177             psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_EXT_FAIL;
    178             continue;
    179         }
    180         if (!source->moments->nPixels) {
    181             psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_EXT_FAIL;
    182             continue;
    183         }
     153    psLogMsg ("psModules", 10, "ChiSquare: %f, Ntotal: %ld, Ngood: %d, Nkeep: %.0f, S2 guess: %f\n",
     154              ChiSq, residuals->n, Ngood, nPts, S2guess);
    184155
    185         source->modelEXT = pmSourceModelGuess (source, psfTry->psf->type);
    186         if (source->modelEXT == NULL) {
    187             psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_EXT_FAIL;
    188             psTrace ("psModules.objects", 4, "masking %d (%d,%d) : failed to generate model guess\n", i, source->peak->x, source->peak->y);
    189             continue;
    190         }
     156    for (int iter = 0; iter < 10; iter++) {
    191157
    192         // set object mask to define valid pixels
    193         psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "OR", markVal);
     158        ChiSq = 0.0;
     159        float dRdS = 0.0;
     160        for (int i = 0; i < residuals->n; i++) {
     161            int n = index->data.S32[i];
     162            if (mask->data.PS_TYPE_VECTOR_MASK_DATA[n]) continue;
     163            float error2 = PS_SQR(errors->data.F32[n]) + S2guess;
     164            ChiSq += PS_SQR(residuals->data.F32[n]) / error2;
     165            dRdS += PS_SQR(residuals->data.F32[n]) / PS_SQR(error2);
     166        }
     167        ChiSq /= nPts;
     168        dRdS /= nPts;
    194169
    195         // fit model as EXT, not PSF
    196         status = pmSourceFitModel (source, source->modelEXT, PM_SOURCE_FIT_EXT, maskVal);
     170        // Note the sign on dS: dRdS above is -1 * dR/dS formally
     171        float dS = (ChiSq - 1.0) / dRdS;
     172        S2guess += dS;
     173        S2guess = PS_MAX(0.0, S2guess);
    197174
    198         // clear object mask to define valid pixels
    199         psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "AND", PS_NOT_IMAGE_MASK(markVal));
    200 
    201         // exclude the poor fits
    202         if (!status) {
    203             psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_EXT_FAIL;
    204             psTrace ("psModules.objects", 4, "masking %d (%d,%d) : status is poor\n", i, source->peak->x, source->peak->y);
    205             continue;
    206         }
    207         Next ++;
    208     }
    209     psLogMsg ("psphot.psftry", PS_LOG_MINUTIA, "fit ext:   %f sec for %d of %ld sources\n", psTimerMark ("psf.fit"), Next, sources->n);
    210     psTrace ("psModules.object", 3, "keeping %d of %ld PSF candidates (EXT)\n", Next, sources->n);
    211 
    212     if (Next == 0) {
    213         psError(PS_ERR_UNKNOWN, false, "No sources with good extended fits from which to determine PSF.");
    214         psFree(psfTry);
    215         return NULL;
     175        psLogMsg ("psModules", 10, "ChiSquare: %f, dS: %f, S2 guess: %f\n", ChiSq, dS, S2guess);
    216176    }
    217177
    218     // stage 2: construct a psf (pmPSF) from this collection of model fits, including the 2D variation
    219     if (!pmPSFFromPSFtry (psfTry)) {
    220         psError(PS_ERR_UNKNOWN, false, "failed to construct a psf model from collection of sources");
    221         psFree(psfTry);
    222         return NULL;
    223     }
     178    // free local allocations
     179    psFree (mask);
     180    psFree (chisq);
     181    psFree (stats);
     182    psFree (index);
    224183
    225     // stage 3: refit with fixed shape parameters
    226     psTimerStart ("psf.fit");
    227     for (int i = 0; i < psfTry->sources->n; i++) {
    228 
    229         pmSource *source = psfTry->sources->data[i];
    230 
    231         // masked for: bad model fit, outlier in parameters
    232         if (psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PSFTRY_MASK_ALL) {
    233             psTrace ("psModules.objects", 4, "dropping %d (%d,%d) : source is masked\n", i, source->peak->x, source->peak->y);
    234             continue;
    235         }
    236 
    237         // set shape for this model based on PSF
    238         source->modelPSF = pmModelFromPSF (source->modelEXT, psfTry->psf);
    239         if (source->modelPSF == NULL) {
    240             psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_BAD_MODEL;
    241             abort();
    242             continue;
    243         }
    244         source->modelPSF->radiusFit = options->radius;
    245 
    246         // set object mask to define valid pixels
    247         psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "OR", markVal);
    248 
    249         // fit the PSF model to the source
    250         status = pmSourceFitModel (source, source->modelPSF, PM_SOURCE_FIT_PSF, maskVal);
    251 
    252         // skip poor fits
    253         if (!status) {
    254             psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "AND", PS_NOT_IMAGE_MASK(markVal));
    255             psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_PSF_FAIL;
    256             psTrace ("psModules.objects", 4, "dropping %d (%d,%d) : failed PSF fit\n", i, source->peak->x, source->peak->y);
    257             continue;
    258         }
    259 
    260         status = pmSourceMagnitudes (source, psfTry->psf, PM_SOURCE_PHOT_INTERP, maskVal);
    261         if (!status || isnan(source->apMag)) {
    262             psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "AND", PS_NOT_IMAGE_MASK(markVal));
    263             psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_BAD_PHOT;
    264             psTrace ("psModules.objects", 4, "dropping %d (%d,%d) : poor photometry\n", i, source->peak->x, source->peak->y);
    265             continue;
    266         }
    267 
    268         // clear object mask to define valid pixels
    269         psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, options->radius, "AND", PS_NOT_IMAGE_MASK(markVal));
    270 
    271         psfTry->fitMag->data.F32[i] = source->psfMag;
    272         psfTry->metric->data.F32[i] = source->apMag - source->psfMag;
    273         psfTry->metricErr->data.F32[i] = source->errMag;
    274 
    275         psTrace ("psModules.object", 6, "keeping source %d (%d) of %ld\n", i, Npsf, psfTry->sources->n);
    276         Npsf ++;
    277     }
    278     psfTry->psf->nPSFstars = Npsf;
    279 
    280     psLogMsg ("psphot.psftry", PS_LOG_MINUTIA, "fit psf:   %f sec for %d of %ld sources\n", psTimerMark ("psf.fit"), Npsf, sources->n);
    281     psTrace ("psModules.object", 3, "keeping %d of %ld PSF candidates (PSF)\n", Npsf, sources->n);
    282 
    283     if (Npsf == 0) {
    284         psError(PS_ERR_UNKNOWN, false, "No sources with good PSF fits after model is built.");
    285         psFree(psfTry);
    286         return NULL;
    287     }
    288 
    289     // measure the chi-square trend as a function of flux (PAR[PM_PAR_I0])
    290     // this should be linear for Poisson errors and quadratic for constant sky errors
    291     psVector *flux  = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
    292     psVector *chisq = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
    293     psVector *mask  = psVectorAlloc (psfTry->sources->n, PS_TYPE_VECTOR_MASK);
    294 
    295     // generate the x and y vectors, and mask missing models
    296     for (int i = 0; i < psfTry->sources->n; i++) {
    297         pmSource *source = psfTry->sources->data[i];
    298         if (source->modelPSF == NULL) {
    299             flux->data.F32[i] = 0.0;
    300             chisq->data.F32[i] = 0.0;
    301             mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0xff;
    302         } else {
    303             flux->data.F32[i] = source->modelPSF->params->data.F32[PM_PAR_I0];
    304             chisq->data.F32[i] = source->modelPSF->chisq / source->modelPSF->nDOF;
    305             mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0;
    306         }
    307     }
    308 
    309     // use 3hi/3lo sigma clipping on the chisq fit
    310     psStats *stats = options->stats;
    311 
    312     // linear clipped fit of chisq trend vs flux
    313     if (options->chiFluxTrend) {
    314         bool result = psVectorClipFitPolynomial1D(psfTry->psf->ChiTrend, options->stats,
    315                                                   mask, 0xff, chisq, NULL, flux);
    316         psStatsOptions meanStat = psStatsMeanOption(options->stats->options); // Statistic for mean
    317         psStatsOptions stdevStat = psStatsStdevOption(options->stats->options); // Statistic for stdev
    318 
    319         psLogMsg ("pmPSFtry", 4, "chisq vs flux fit: %f +/- %f\n",
    320                   psStatsGetValue(stats, meanStat), psStatsGetValue(stats, stdevStat));
    321 
    322         psFree(flux);
    323         psFree(mask);
    324         psFree(chisq);
    325 
    326         if (!result) {
    327             psError(PS_ERR_UNKNOWN, false, "Failed to fit psf->ChiTrend");
    328             psFree(psfTry);
    329             return NULL;
    330         }
    331     }
    332 
    333     for (int i = 0; i < psfTry->psf->ChiTrend->nX + 1; i++) {
    334         psLogMsg ("pmPSFtry", 4, "chisq vs flux fit term %d: %f +/- %f\n", i,
    335                   psfTry->psf->ChiTrend->coeff[i]*pow(10000, i),
    336                   psfTry->psf->ChiTrend->coeffErr[i]*pow(10000,i));
    337     }
    338 
    339     // XXX this function wants aperture radius for pmSourcePhotometry
    340     if (!pmPSFtryMetric (psfTry, options)) {
    341         psError(PS_ERR_UNKNOWN, false, "Attempt to fit PSF with model %s failed.", modelName);
    342         psFree (psfTry);
    343         return NULL;
    344     }
    345 
    346     psLogMsg ("psphot.pspsf", 3, "try model %s, ap-fit: %f +/- %f : sky bias: %f\n",
    347               modelName, psfTry->psf->ApResid, psfTry->psf->dApResid, psfTry->psf->skyBias);
    348 
    349     return (psfTry);
     184    return (sqrt(S2guess));
    350185}
    351186
    352 bool pmPSFtryMetric (pmPSFtry *psfTry, pmPSFOptions *options)
    353 {
    354     PS_ASSERT_PTR_NON_NULL(psfTry, false);
    355     PS_ASSERT_PTR_NON_NULL(options, false);
    356     PS_ASSERT_PTR_NON_NULL(psfTry->sources, false);
    357 
    358     float RADIUS = options->radius;
    359 
    360     // the measured (aperture - fit) magnitudes (dA == psfTry->metric)
    361     //   depend on both the true ap-fit (dAo) and the bias in the sky measurement:
    362     //     dA = dAo + dsky/flux
    363     //   where flux is the flux of the star
    364     // we fit this trend to find the infinite flux aperture correction (dAo),
    365     //   the nominal sky bias (dsky), and the error on dAo
    366     // the values of dA are contaminated by stars with close neighbors in the aperture
    367     //   we use an outlier rejection to avoid this bias
    368 
    369     // r2rflux = radius^2 * ten(0.4*fitMag);
    370     psVector *r2rflux = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
    371 
    372     for (int i = 0; i < psfTry->sources->n; i++) {
    373         if (psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PSFTRY_MASK_ALL)
    374             continue;
    375         r2rflux->data.F32[i] = PS_SQR(RADIUS) * pow(10.0, 0.4*psfTry->fitMag->data.F32[i]);
    376     }
    377 
    378     // XXX test dump of aperture residual data
    379     if (psTraceGetLevel("psModules.objects") >= 5) {
    380         FILE *f = fopen ("apresid.dat", "w");
    381         for (int i = 0; i < psfTry->sources->n; i++) {
    382             int keep = (psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PSFTRY_MASK_ALL);
    383 
    384             pmSource *source = psfTry->sources->data[i];
    385             float x = source->peak->x;
    386             float y = source->peak->y;
    387 
    388             fprintf (f, "%d  %d, %f %f %f  %f %f %f \n",
    389                      i, keep, x, y,
    390                      psfTry->fitMag->data.F32[i],
    391                      r2rflux->data.F32[i],
    392                      psfTry->metric->data.F32[i],
    393                      psfTry->metricErr->data.F32[i]);
    394         }
    395         fclose (f);
    396     }
    397 
    398     // This analysis of the apResid statistics is only approximate.  The fitted magnitudes
    399     // measured at this point (in the PSF fit) use Poisson errors, and are thus biased as a
    400     // function of magnitude.  We re-measure the apResid statistics later in psphot using the
    401     // linear, constant-error fitting.  Do not reject outliers with excessive vigor here.
    402 
    403     // fit ApTrend only to r2rflux, ignore x,y,flux variations for now
    404     // linear clipped fit of ApResid to r2rflux
    405     psPolynomial1D *poly = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 1);
    406     poly->coeffMask[1] = PS_POLY_MASK_SET; // fit only a constant offset (no SKYBIAS)
    407 
    408     // XXX replace this with a psVectorStats call?  since we are not fitting the trend
    409     bool result = psVectorClipFitPolynomial1D(poly, options->stats, psfTry->mask, PSFTRY_MASK_ALL,
    410                                               psfTry->metric, psfTry->metricErr, r2rflux);
    411     if (!result) {
    412         psError(PS_ERR_UNKNOWN, false, "Failed to fit clipped poly");
    413 
    414         psFree(poly);
    415         psFree(r2rflux);
    416 
    417         return false;
    418     }
    419     psStatsOptions stdevStat = psStatsStdevOption(options->stats->options); // Statistic for stdev
    420     psLogMsg ("pmPSFtryMetric", 4, "apresid: %f +/- %f; from statistics of %ld psf stars\n", poly->coeff[0],
    421               psStatsGetValue(options->stats, stdevStat), psfTry->sources->n);
    422 
    423 
    424 
    425     // XXX test dump of fitted model (dump when tracing?)
    426     if (psTraceGetLevel("psModules.objects") >= 4) {
    427         FILE *f = fopen ("resid.dat", "w");
    428         psVector *apfit = psPolynomial1DEvalVector (poly, r2rflux);
    429         for (int i = 0; i < psfTry->sources->n; i++) {
    430             int keep = (psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PSFTRY_MASK_ALL);
    431 
    432             pmSource *source = psfTry->sources->data[i];
    433             float x = source->peak->x;
    434             float y = source->peak->y;
    435 
    436             fprintf (f, "%d  %d, %f %f %f  %f %f %f  %f\n",
    437                      i, keep, x, y,
    438                      psfTry->fitMag->data.F32[i],
    439                      r2rflux->data.F32[i],
    440                      psfTry->metric->data.F32[i],
    441                      psfTry->metricErr->data.F32[i],
    442                      apfit->data.F32[i]);
    443         }
    444         fclose (f);
    445         psFree (apfit);
    446     }
    447 
    448     // XXX drop the skyBias value, or include above??
    449     psfTry->psf->skyBias  = poly->coeff[1];
    450     psfTry->psf->ApResid  = poly->coeff[0];
    451     psfTry->psf->dApResid = psStatsGetValue(options->stats, stdevStat);
    452 
    453     psFree (r2rflux);
    454     psFree (poly);
    455 
    456     return true;
    457 }
    458 
    459 /*
    460   (aprMag' - fitMag) = rflux*skyBias + ApTrend(x,y)
    461   (aprMag - rflux*skyBias) - fitMag = ApTrend(x,y)
    462   (aprMag - rflux*skyBias) = fitMag + ApTrend(x,y)
    463 */
    464 
    465 /*****************************************************************************
    466 pmPSFFromPSFtry (psfTry): build a PSF model from a collection of
    467 source->modelEXT entries.  The PSF ignores the first 4 (independent) model
    468 parameters and constructs a polynomial fit to the remaining as a function of
    469 image coordinate.
    470     input: psfTry with fitted source->modelEXT collection, pre-allocated psf
    471 Note: some of the array entries may be NULL (failed fits); ignore them.
    472  *****************************************************************************/
    473 bool pmPSFFromPSFtry (pmPSFtry *psfTry)
    474 {
    475     PS_ASSERT_PTR_NON_NULL(psfTry, false);
    476     PS_ASSERT_PTR_NON_NULL(psfTry->sources, false);
    477 
    478     pmPSF *psf = psfTry->psf;
    479 
    480     // construct the fit vectors from the collection of objects
    481     psVector *x  = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
    482     psVector *y  = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
    483     psVector *z  = psVectorAlloc (psfTry->sources->n, PS_TYPE_F32);
    484 
    485     // construct the x,y terms
    486     for (int i = 0; i < psfTry->sources->n; i++) {
    487         pmSource *source = psfTry->sources->data[i];
    488         if (source->modelEXT == NULL)
    489             continue;
    490 
    491         x->data.F32[i] = source->modelEXT->params->data.F32[PM_PAR_XPOS];
    492         y->data.F32[i] = source->modelEXT->params->data.F32[PM_PAR_YPOS];
    493     }
    494 
    495     if (!pmPSFFitShapeParams (psf, psfTry->sources, x, y, psfTry->mask)) {
    496         psFree(x);
    497         psFree(y);
    498         psFree(z);
    499         return false;
    500     }
    501 
    502     // skip the unfitted parameters (X, Y, Io, Sky) and the shape parameters (SXX, SYY, SXY)
    503     // apply the values of Nx, Ny determined above for E0,E1,E2 to the remaining parameters
    504     for (int i = 0; i < psf->params->n; i++) {
    505         switch (i) {
    506           case PM_PAR_SKY:
    507           case PM_PAR_I0:
    508           case PM_PAR_XPOS:
    509           case PM_PAR_YPOS:
    510           case PM_PAR_SXX:
    511           case PM_PAR_SYY:
    512           case PM_PAR_SXY:
    513             continue;
    514           default:
    515             break;
    516         }
    517 
    518         // select the per-object fitted data for this parameter
    519         for (int j = 0; j < psfTry->sources->n; j++) {
    520             pmSource *source = psfTry->sources->data[j];
    521             if (source->modelEXT == NULL) continue;
    522             z->data.F32[j] = source->modelEXT->params->data.F32[i];
    523         }
    524 
    525         psImageBinning *binning = psImageBinningAlloc();
    526         binning->nXruff = psf->trendNx;
    527         binning->nYruff = psf->trendNy;
    528         binning->nXfine = psf->fieldNx;
    529         binning->nYfine = psf->fieldNy;
    530 
    531         if (psf->psfTrendMode == PM_TREND_MAP) {
    532             psImageBinningSetScale (binning, PS_IMAGE_BINNING_CENTER);
    533             psImageBinningSetSkipByOffset (binning, psf->fieldXo, psf->fieldYo);
    534         }
    535 
    536         // free existing trend, re-alloc
    537         psFree (psf->params->data[i]);
    538         psf->params->data[i] = pmTrend2DNoImageAlloc (psf->psfTrendMode, binning, psf->psfTrendStats);
    539         psFree (binning);
    540 
    541         // fit the collection of measured parameters to the PSF 2D model
    542         // the mask is carried from previous steps and updated with this operation
    543         // the weight is either the flux error or NULL, depending on 'psf->poissonErrorParams'
    544         if (!pmTrend2DFit (psf->params->data[i], psfTry->mask, 0xff, x, y, z, NULL)) {
    545             psError(PS_ERR_UNKNOWN, false, "failed to build psf model for parameter %d", i);
    546             psFree(x);
    547             psFree(y);
    548             psFree(z);
    549             return false;
    550         }
    551     }
    552 
    553     // test dump of star parameters vs position (compare with fitted values)
    554     if (psTraceGetLevel("psModules.objects") >= 4) {
    555         FILE *f = fopen ("params.dat", "w");
    556 
    557         for (int j = 0; j < psfTry->sources->n; j++) {
    558             pmSource *source = psfTry->sources->data[j];
    559             if (source == NULL) continue;
    560             if (source->modelEXT == NULL) continue;
    561 
    562             pmModel *modelPSF = pmModelFromPSF (source->modelEXT, psf);
    563 
    564             fprintf (f, "%f %f : ", source->modelEXT->params->data.F32[PM_PAR_XPOS], source->modelEXT->params->data.F32[PM_PAR_YPOS]);
    565 
    566             for (int i = 0; i < psf->params->n; i++) {
    567                 if (psf->params->data[i] == NULL) continue;
    568                 fprintf (f, "%f %f : ", source->modelEXT->params->data.F32[i], modelPSF->params->data.F32[i]);
    569             }
    570             fprintf (f, "%f %d\n", source->modelEXT->chisq, source->modelEXT->nIter);
    571 
    572             psFree(modelPSF);
    573         }
    574         fclose (f);
    575     }
    576 
    577     psFree (x);
    578     psFree (y);
    579     psFree (z);
    580     return true;
    581 }
    582 
    583 
    584 bool pmPSFFitShapeParams (pmPSF *psf, psArray *sources, psVector *x, psVector *y, psVector *srcMask) {
    585 
    586     // we are doing a robust fit.  after each pass, we drop points which are more deviant than
    587     // three sigma.  mask is currently updated for each pass.
    588 
    589     // The shape parameters (SXX, SXY, SYY) are strongly coupled.  We have to handle them very
    590     // carefully.  First, we convert them to the Ellipse Polarization terms (E0, E1, E2) for
    591     // each source and fit this set of parameters.  These values are less tightly coupled, but
    592     // are still inter-related.  The fitted values do a good job of constraining the major axis
    593     // and the position angle, but the minor axis is weakly measured.  When we apply the PSF
    594     // model to construct a source model, we convert the fitted values of E0,E1,E2 to the shape
    595     // parameters, with the constraint that the minor axis must be greater than a minimum
    596     // threshold.
    597 
    598     // convert the measured source shape paramters to polarization terms
    599     psVector *e0   = psVectorAlloc (sources->n, PS_TYPE_F32);
    600     psVector *e1   = psVectorAlloc (sources->n, PS_TYPE_F32);
    601     psVector *e2   = psVectorAlloc (sources->n, PS_TYPE_F32);
    602     psVector *mag  = psVectorAlloc (sources->n, PS_TYPE_F32);
    603 
    604     for (int i = 0; i < sources->n; i++) {
    605         // skip any masked sources (failed to fit one of the model steps or get a magnitude)
    606         if (srcMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
    607 
    608         pmSource *source = sources->data[i];
    609         assert (source->modelEXT); // all unmasked sources should have modelEXT
    610 
    611         psEllipsePol pol = pmPSF_ModelToFit (source->modelEXT->params->data.F32);
    612 
    613         e0->data.F32[i] = pol.e0;
    614         e1->data.F32[i] = pol.e1;
    615         e2->data.F32[i] = pol.e2;
    616 
    617         float flux = source->modelEXT->params->data.F32[PM_PAR_I0];
    618         mag->data.F32[i] = (flux > 0.0) ? -2.5*log(flux) : -100.0;
    619     }
    620 
    621     if (psf->psfTrendMode == PM_TREND_MAP) {
    622         float scatterTotal = 0.0;
    623         float scatterTotalMin = FLT_MAX;
    624         int entryMin = -1;
    625 
    626         psVector *dz = NULL;
    627         psVector *mask = psVectorAlloc (sources->n, PS_TYPE_VECTOR_MASK);
    628 
    629         // check the fit residuals and increase Nx,Ny until the error is minimized
    630         // pmPSFParamTrend increases the number along the longer of x or y
    631         for (int i = 1; i <= PS_MAX (psf->trendNx, psf->trendNy); i++) {
    632 
    633             // copy srcMask to mask (we do not want the mask values set in pmPSFFitShapeParamsMap to be sticky)
    634             for (int i = 0; i < mask->n; i++) {
    635                 mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = srcMask->data.PS_TYPE_VECTOR_MASK_DATA[i];
    636             }
    637             if (!pmPSFFitShapeParamsMap (psf, i, &scatterTotal, mask, x, y, mag, e0, e1, e2, dz)) {
    638                 break;
    639             }
    640 
    641             // store the resulting scatterTotal values and the scales, redo the best
    642             if (scatterTotal < scatterTotalMin) {
    643                 scatterTotalMin = scatterTotal;
    644                 entryMin = i;
    645             }
    646         }
    647         if (entryMin == -1) {
    648             psError (PS_ERR_UNKNOWN, false, "failed to find image map for shape params");
    649             return false;
    650         }
    651 
    652         // copy srcMask to mask (we do not want the mask values set in pmPSFFitShapeParamsMap to be sticky)
    653         for (int i = 0; i < mask->n; i++) {
    654             mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = srcMask->data.PS_TYPE_VECTOR_MASK_DATA[i];
    655         }
    656         if (!pmPSFFitShapeParamsMap (psf, entryMin, &scatterTotal, mask, x, y, mag, e0, e1, e2, dz)) {
    657             psAbort ("failed pmPSFFitShapeParamsMap on second pass?");
    658         }
    659 
    660         pmTrend2D *trend = psf->params->data[PM_PAR_E0];
    661         psf->trendNx = trend->map->map->numCols;
    662         psf->trendNy = trend->map->map->numRows;
    663 
    664         // copy mask back to srcMask
    665         for (int i = 0; i < mask->n; i++) {
    666             srcMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = mask->data.PS_TYPE_VECTOR_MASK_DATA[i];
    667         }
    668 
    669         psFree (mask);
    670         psFree (dz);
    671     } else {
    672 
    673         // XXX iterate Nx, Ny based on scatter?
    674         // XXX we force the x & y order to be the same
    675         // modify the order to correspond to the actual number of matched stars:
    676         int order = PS_MAX (psf->trendNx, psf->trendNy);
    677         if ((sources->n < 15) && (order >= 3)) order = 2;
    678         if ((sources->n < 11) && (order >= 2)) order = 1;
    679         if ((sources->n <  8) && (order >= 1)) order = 0;
    680         if ((sources->n <  3)) {
    681             psError (PS_ERR_UNKNOWN, true, "failed to fit polynomial to shape params");
    682             return false;
    683         }
    684         psf->trendNx = order;
    685         psf->trendNy = order;
    686 
    687         // we run 'clipIter' cycles clipping in each of x and y, with only one iteration each.
    688         // This way, the parameters masked by one of the fits will be applied to the others
    689         for (int i = 0; i < psf->psfTrendStats->clipIter; i++) {
    690 
    691             psStatsOptions meanOption = psStatsMeanOption(psf->psfTrendStats->options);
    692             psStatsOptions stdevOption = psStatsStdevOption(psf->psfTrendStats->options);
    693 
    694             pmTrend2D *trend = NULL;
    695             float mean, stdev;
    696 
    697             // XXX we are using the same stats structure on each pass: do we need to re-init it?
    698             bool status = true;
    699 
    700             trend = psf->params->data[PM_PAR_E0];
    701             status &= pmTrend2DFit (trend, srcMask, 0xff, x, y, e0, NULL);
    702             mean = psStatsGetValue (trend->stats, meanOption);
    703             stdev = psStatsGetValue (trend->stats, stdevOption);
    704             psTrace ("psModules.objects", 4, "clipped E0 : %f +/- %f keeping %ld of %ld\n", mean, stdev, psf->psfTrendStats->clippedNvalues, e0->n);
    705             pmSourceVisualPSFModelResid (trend, x, y, e0, srcMask);
    706 
    707             trend = psf->params->data[PM_PAR_E1];
    708             status &= pmTrend2DFit (trend, srcMask, 0xff, x, y, e1, NULL);
    709             mean = psStatsGetValue (trend->stats, meanOption);
    710             stdev = psStatsGetValue (trend->stats, stdevOption);
    711             psTrace ("psModules.objects", 4, "clipped E1 : %f +/- %f keeping %ld of %ld\n", mean, stdev, psf->psfTrendStats->clippedNvalues, e1->n);
    712             pmSourceVisualPSFModelResid (trend, x, y, e1, srcMask);
    713 
    714             trend = psf->params->data[PM_PAR_E2];
    715             status &= pmTrend2DFit (trend, srcMask, 0xff, x, y, e2, NULL);
    716             mean = psStatsGetValue (trend->stats, meanOption);
    717             stdev = psStatsGetValue (trend->stats, stdevOption);
    718             psTrace ("psModules.objects", 4, "clipped E2 : %f +/- %f keeping %ld of %ld\n", mean, stdev, psf->psfTrendStats->clippedNvalues, e2->n);
    719             pmSourceVisualPSFModelResid (trend, x, y, e2, srcMask);
    720 
    721             if (!status) {
    722                 psError (PS_ERR_UNKNOWN, true, "failed to fit polynomial to shape params");
    723                 return false;
    724             }
    725         }
    726     }
    727 
    728     // test dump of the psf parameters
    729     if (psTraceGetLevel("psModules.objects") >= 4) {
    730         FILE *f = fopen ("pol.dat", "w");
    731         fprintf (f, "# x y  :  e0obs e1obs e2obs  : e0fit e1fit e2fit : mask\n");
    732         for (int i = 0; i < e0->n; i++) {
    733             fprintf (f, "%f %f  :  %f %f %f  : %f %f %f  : %d\n",
    734                      x->data.F32[i], y->data.F32[i],
    735                      e0->data.F32[i], e1->data.F32[i], e2->data.F32[i],
    736                      pmTrend2DEval (psf->params->data[PM_PAR_E0], x->data.F32[i], y->data.F32[i]),
    737                      pmTrend2DEval (psf->params->data[PM_PAR_E1], x->data.F32[i], y->data.F32[i]),
    738                      pmTrend2DEval (psf->params->data[PM_PAR_E2], x->data.F32[i], y->data.F32[i]),
    739                      srcMask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
    740         }
    741         fclose (f);
    742     }
    743 
    744     psFree (e0);
    745     psFree (e1);
    746     psFree (e2);
    747     psFree (mag);
    748     return true;
    749 }
    750 
    751 // fit the shape variations as a psImageMap for the given scale factor
    752 bool pmPSFFitShapeParamsMap (pmPSF *psf, int scale, float *scatterTotal, psVector *mask, psVector *x, psVector *y, psVector *mag, psVector *e0obs, psVector *e1obs, psVector *e2obs, psVector *dz) {
    753 
    754     int Nx, Ny;
    755 
    756     // set the map scale to match the aspect ratio : for a scale of 1, we guarantee
    757     // that we have a single cell
    758     if (psf->fieldNx > psf->fieldNy) {
    759         Nx = scale;
    760         float AR = psf->fieldNy / (float) psf->fieldNx;
    761         Ny = (int) (Nx * AR + 0.5);
    762         Ny = PS_MAX (1, Ny);
    763     } else {
    764         Ny = scale;
    765         float AR = psf->fieldNx / (float) psf->fieldNy;
    766         Nx = (int) (Ny * AR + 0.5);
    767         Nx = PS_MAX (1, Nx);
    768     }
    769 
    770     // do we have enough sources for this fine of a grid?
    771     if (x->n < 10*Nx*Ny) {
    772         return false;
    773     }
    774 
    775     // XXX check this against the exising type
    776     pmTrend2DMode psfTrendMode = PM_TREND_MAP;
    777 
    778     psImageBinning *binning = psImageBinningAlloc();
    779     binning->nXruff = Nx;
    780     binning->nYruff = Ny;
    781     binning->nXfine = psf->fieldNx;
    782     binning->nYfine = psf->fieldNy;
    783     psImageBinningSetScale (binning, PS_IMAGE_BINNING_CENTER);
    784     psImageBinningSetSkipByOffset (binning, psf->fieldXo, psf->fieldYo);
    785 
    786     psFree (psf->params->data[PM_PAR_E0]);
    787     psFree (psf->params->data[PM_PAR_E1]);
    788     psFree (psf->params->data[PM_PAR_E2]);
    789 
    790     int nIter = psf->psfTrendStats->clipIter;
    791     psf->psfTrendStats->clipIter = 1;
    792     psf->params->data[PM_PAR_E0] = pmTrend2DNoImageAlloc (psfTrendMode, binning, psf->psfTrendStats);
    793     psf->params->data[PM_PAR_E1] = pmTrend2DNoImageAlloc (psfTrendMode, binning, psf->psfTrendStats);
    794     psf->params->data[PM_PAR_E2] = pmTrend2DNoImageAlloc (psfTrendMode, binning, psf->psfTrendStats);
    795     psFree (binning);
    796 
    797     // if the map is 1x1 (a single value), we measure the resulting ensemble scatter
    798 
    799     // if the map is more finely sampled, divide the values into two sets: measure the fit from
    800     // one set and the scatter from the other set.
    801     psVector *x_fit = NULL;
    802     psVector *y_fit = NULL;
    803     psVector *x_tst = NULL;
    804     psVector *y_tst = NULL;
    805 
    806     psVector *e0obs_fit = NULL;
    807     psVector *e1obs_fit = NULL;
    808     psVector *e2obs_fit = NULL;
    809     psVector *e0obs_tst = NULL;
    810     psVector *e1obs_tst = NULL;
    811     psVector *e2obs_tst = NULL;
    812 
    813     if (scale == 1) {
    814         x_fit  = psMemIncrRefCounter (x);
    815         y_fit  = psMemIncrRefCounter (y);
    816         x_tst  = psMemIncrRefCounter (x);
    817         y_tst  = psMemIncrRefCounter (y);
    818         e0obs_fit = psMemIncrRefCounter (e0obs);
    819         e1obs_fit = psMemIncrRefCounter (e1obs);
    820         e2obs_fit = psMemIncrRefCounter (e2obs);
    821         e0obs_tst = psMemIncrRefCounter (e0obs);
    822         e1obs_tst = psMemIncrRefCounter (e1obs);
    823         e2obs_tst = psMemIncrRefCounter (e2obs);
    824     } else {
    825         x_fit  = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    826         y_fit  = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    827         x_tst  = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    828         y_tst  = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    829         e0obs_fit = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    830         e1obs_fit = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    831         e2obs_fit = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    832         e0obs_tst = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    833         e1obs_tst = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    834         e2obs_tst = psVectorAlloc (e0obs->n/2, PS_TYPE_F32);
    835         for (int i = 0; i < e0obs_fit->n; i++) {
    836             // e0obs->n ==  8 or 9:
    837             // i = 0, 1, 2, 3 : 2i+0 = 0, 2, 4, 6
    838             // i = 0, 1, 2, 3 : 2i+1 = 1, 3, 5, 7
    839             x_fit->data.F32[i] = x->data.F32[2*i+0];
    840             x_tst->data.F32[i] = x->data.F32[2*i+1];
    841             y_fit->data.F32[i] = y->data.F32[2*i+0];
    842             y_tst->data.F32[i] = y->data.F32[2*i+1];
    843 
    844             e0obs_fit->data.F32[i] = e0obs->data.F32[2*i+0];
    845             e0obs_tst->data.F32[i] = e0obs->data.F32[2*i+1];
    846             e1obs_fit->data.F32[i] = e1obs->data.F32[2*i+0];
    847             e1obs_tst->data.F32[i] = e1obs->data.F32[2*i+1];
    848             e2obs_fit->data.F32[i] = e2obs->data.F32[2*i+0];
    849             e2obs_tst->data.F32[i] = e2obs->data.F32[2*i+1];
    850         }
    851     }
    852 
    853     // the mask marks the values not used to calculate the ApTrend
    854     psVector *fitMask = psVectorAlloc (x_fit->n, PS_TYPE_VECTOR_MASK);
    855     // copy mask values to fitMask as a starting point
    856     for (int i = 0; i < fitMask->n; i++) {
    857         fitMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = mask->data.PS_TYPE_VECTOR_MASK_DATA[i];
    858     }
    859 
    860     // we run 'clipIter' cycles clipping in each of x and y, with only one iteration each.
    861     // This way, the parameters masked by one of the fits will be applied to the others
    862     for (int i = 0; i < nIter; i++) {
    863         // XXX we are using the same stats structure on each pass: do we need to re-init it?
    864         psStatsOptions meanOption = psStatsMeanOption(psf->psfTrendStats->options);
    865         psStatsOptions stdevOption = psStatsStdevOption(psf->psfTrendStats->options);
    866 
    867         pmTrend2D *trend = NULL;
    868         float mean, stdev;
    869 
    870         // XXX we are using the same stats structure on each pass: do we need to re-init it?
    871         bool status = true;
    872 
    873         trend = psf->params->data[PM_PAR_E0];
    874         status &= pmTrend2DFit (trend, fitMask, 0xff, x_fit, y_fit, e0obs_fit, NULL);
    875         mean = psStatsGetValue (trend->stats, meanOption);
    876         stdev = psStatsGetValue (trend->stats, stdevOption);
    877         psTrace ("psModules.objects", 4, "clipped E0 : %f +/- %f keeping %ld of %ld\n", mean, stdev, psf->psfTrendStats->clippedNvalues, e0obs_fit->n);
    878         // printTrendMap (trend);
    879         psImageMapCleanup (trend->map);
    880         // printTrendMap (trend);
    881         pmSourceVisualPSFModelResid (trend, x, y, e0obs, mask);
    882 
    883         trend = psf->params->data[PM_PAR_E1];
    884         status &= pmTrend2DFit (trend, fitMask, 0xff, x_fit, y_fit, e1obs_fit, NULL);
    885         mean = psStatsGetValue (trend->stats, meanOption);
    886         stdev = psStatsGetValue (trend->stats, stdevOption);
    887         psTrace ("psModules.objects", 4, "clipped E1 : %f +/- %f keeping %ld of %ld\n", mean, stdev, psf->psfTrendStats->clippedNvalues, e1obs_fit->n);
    888         // printTrendMap (trend);
    889         psImageMapCleanup (trend->map);
    890         // printTrendMap (trend);
    891         pmSourceVisualPSFModelResid (trend, x, y, e1obs, mask);
    892 
    893         trend = psf->params->data[PM_PAR_E2];
    894         status &= pmTrend2DFit (trend, fitMask, 0xff, x_fit, y_fit, e2obs_fit, NULL);
    895         mean = psStatsGetValue (trend->stats, meanOption);
    896         stdev = psStatsGetValue (trend->stats, stdevOption);
    897         psTrace ("psModules.objects", 4, "clipped E2 : %f +/- %f keeping %ld of %ld\n", mean, stdev, psf->psfTrendStats->clippedNvalues, e2obs->n);
    898         // printTrendMap (trend);
    899         psImageMapCleanup (trend->map);
    900         // printTrendMap (trend);
    901         pmSourceVisualPSFModelResid (trend, x, y, e2obs, mask);
    902     }
    903     psf->psfTrendStats->clipIter = nIter; // restore default setting
    904 
    905     // construct the fitted values and the residuals
    906     psVector *e0fit = pmTrend2DEvalVector (psf->params->data[PM_PAR_E0], fitMask, 0xff, x_tst, y_tst);
    907     psVector *e1fit = pmTrend2DEvalVector (psf->params->data[PM_PAR_E1], fitMask, 0xff, x_tst, y_tst);
    908     psVector *e2fit = pmTrend2DEvalVector (psf->params->data[PM_PAR_E2], fitMask, 0xff, x_tst, y_tst);
    909 
    910     psVector *e0res = (psVector *) psBinaryOp (NULL, (void *) e0obs_tst, "-", (void *) e0fit);
    911     psVector *e1res = (psVector *) psBinaryOp (NULL, (void *) e1obs_tst, "-", (void *) e1fit);
    912     psVector *e2res = (psVector *) psBinaryOp (NULL, (void *) e2obs_tst, "-", (void *) e2fit);
    913 
    914     // measure scatter for the unfitted points
    915     // psTraceSetLevel ("psLib.math.vectorSampleStdev", 10);
    916     // psTraceSetLevel ("psLib.math.vectorClippedStats", 10);
    917     pmPSFShapeParamsScatter (scatterTotal, e0res, e1res, e2res, fitMask, 0xff, psStatsStdevOption(psf->psfTrendStats->options));
    918     // psTraceSetLevel ("psLib.math.vectorSampleStdev", 0);
    919     // psTraceSetLevel ("psLib.math.vectorClippedStats", 0);
    920 
    921     psLogMsg ("psphot.psftry", PS_LOG_INFO, "result of %d x %d grid\n", Nx, Ny);
    922     psLogMsg ("psphot.psftry", PS_LOG_INFO, "systematic scatter: %f\n", *scatterTotal);
    923 
    924     psFree (x_fit);
    925     psFree (y_fit);
    926     psFree (x_tst);
    927     psFree (y_tst);
    928 
    929     psFree (e0obs_fit);
    930     psFree (e1obs_fit);
    931     psFree (e2obs_fit);
    932     psFree (e0obs_tst);
    933     psFree (e1obs_tst);
    934     psFree (e2obs_tst);
    935 
    936     psFree (e0fit);
    937     psFree (e1fit);
    938     psFree (e2fit);
    939 
    940     psFree (e0res);
    941     psFree (e1res);
    942     psFree (e2res);
    943 
    944     // XXX copy fitMask values back to mask
    945     for (int i = 0; i < fitMask->n; i++) {
    946         mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = fitMask->data.PS_TYPE_VECTOR_MASK_DATA[i];
    947     }
    948     psFree (fitMask);
    949 
    950     return true;
    951 }
    952 
    953 // calculate the scatter of the parameters
    954 bool pmPSFShapeParamsScatter(float *scatterTotal, psVector *e0res, psVector *e1res, psVector *e2res, psVector *mask, psVectorMaskType maskValue, psStatsOptions stdevOpt)
    955 {
    956 
    957     // psStats *stats = psStatsAlloc(stdevOpt);
    958     psStats *stats = psStatsAlloc(PS_STAT_CLIPPED_STDEV);
    959 
    960     // calculate the root-mean-square of E0, E1, E2
    961     float dEsquare = 0.0;
    962     psStatsInit (stats);
    963     if (!psVectorStats (stats, e0res, NULL, mask, maskValue)) {
    964         psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
    965         return false;
    966     }
    967     dEsquare += PS_SQR(psStatsGetValue(stats, stdevOpt));
    968 
    969     psStatsInit (stats);
    970     if (!psVectorStats (stats, e1res, NULL, mask, maskValue)) {
    971         psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
    972         return false;
    973     }
    974     dEsquare += PS_SQR(psStatsGetValue(stats, stdevOpt));
    975 
    976     psStatsInit (stats);
    977     if (!psVectorStats (stats, e2res, NULL, mask, maskValue)) {
    978         psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
    979         return false;
    980     }
    981     dEsquare += PS_SQR(psStatsGetValue(stats, stdevOpt));
    982 
    983     *scatterTotal = sqrtf(dEsquare);
    984 
    985     psFree(stats);
    986     return true;
    987 }
    988 
    989 // calculate the minimum scatter of the parameters
    990 bool pmPSFShapeParamsErrors(float *errorFloor, psVector *mag, psVector *e0res, psVector *e1res,
    991                             psVector *e2res, psVector *mask, int nGroup, psStatsOptions stdevOpt)
    992 {
    993 
    994     psStats *statsS = psStatsAlloc(stdevOpt);
    995 
    996     // measure the trend in bins with 10 values each; if < 10 total, use them all
    997     int nBin = PS_MAX (mag->n / nGroup, 1);
    998 
    999     // use mag to group parameters in sequence
    1000     psVector *index = psVectorSortIndex (NULL, mag);
    1001 
    1002     // subset vectors for mag and dap values within the given range
    1003     psVector *dE0subset = psVectorAllocEmpty (nGroup, PS_TYPE_F32);
    1004     psVector *dE1subset = psVectorAllocEmpty (nGroup, PS_TYPE_F32);
    1005     psVector *dE2subset = psVectorAllocEmpty (nGroup, PS_TYPE_F32);
    1006     psVector *mkSubset  = psVectorAllocEmpty (nGroup, PS_TYPE_VECTOR_MASK);
    1007 
    1008     int n = 0;
    1009     float min = INFINITY;               // Minimum error
    1010     for (int i = 0; i < nBin; i++) {
    1011         int j;
    1012         int nValid = 0;
    1013         for (j = 0; (j < nGroup) && (n < mag->n); j++, n++) {
    1014             int N = index->data.U32[n];
    1015             dE0subset->data.F32[j] = e0res->data.F32[N];
    1016             dE1subset->data.F32[j] = e1res->data.F32[N];
    1017             dE2subset->data.F32[j] = e2res->data.F32[N];
    1018 
    1019             mkSubset->data.PS_TYPE_VECTOR_MASK_DATA[j]   = mask->data.PS_TYPE_VECTOR_MASK_DATA[N];
    1020             if (!mask->data.PS_TYPE_VECTOR_MASK_DATA[N]) nValid ++;
    1021         }
    1022         if (nValid < 3) continue;
    1023 
    1024         dE0subset->n = j;
    1025         dE1subset->n = j;
    1026         dE2subset->n = j;
    1027         mkSubset->n = j;
    1028 
    1029         // calculate the root-mean-square of E0, E1, E2
    1030         float dEsquare = 0.0;
    1031         psStatsInit (statsS);
    1032         if (!psVectorStats (statsS, dE0subset, NULL, mkSubset, 0xff)) {
    1033         }
    1034         dEsquare += PS_SQR(psStatsGetValue(statsS, stdevOpt));
    1035 
    1036         psStatsInit (statsS);
    1037         if (!psVectorStats (statsS, dE1subset, NULL, mkSubset, 0xff)) {
    1038             psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
    1039             return false;
    1040         }
    1041         dEsquare += PS_SQR(psStatsGetValue(statsS, stdevOpt));
    1042 
    1043         psStatsInit (statsS);
    1044         if (!psVectorStats (statsS, dE2subset, NULL, mkSubset, 0xff)) {
    1045             psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
    1046             return false;
    1047         }
    1048         dEsquare += PS_SQR(psStatsGetValue(statsS, stdevOpt));
    1049 
    1050         if (isfinite(dEsquare)) {
    1051             float err = sqrtf(dEsquare);
    1052             if (err < min) {
    1053                 min = err;
    1054             }
    1055         }
    1056     }
    1057     *errorFloor = min;
    1058 
    1059     psFree (dE0subset);
    1060     psFree (dE1subset);
    1061     psFree (dE2subset);
    1062     psFree (mkSubset);
    1063 
    1064     psFree(index);
    1065 
    1066     psFree(statsS);
    1067 
    1068     return true;
    1069 }
  • branches/simtest_nebulous_branches/psModules/src/objects/pmPSFtry.h

    r21183 r27840  
    8989 *
    9090 */
    91 pmPSFtry *pmPSFtryModel (const psArray *sources, const char *modelName, pmPSFOptions *options, psImageMaskType maskVal, psImageMaskType mark);
     91pmPSFtry *pmPSFtryModel (
     92    const psArray *sources,             ///< PSF sources to use in the pmPSF model analysis
     93    const char *modelName,              ///< human-readable name of desired model
     94    pmPSFOptions *options,
     95    psImageMaskType maskVal,
     96    psImageMaskType mark
     97    );
     98
     99/** fit EXT models to all possible psf sources */
     100bool pmPSFtryFitEXT (pmPSFtry *psfTry, pmPSFOptions *options, psImageMaskType maskVal, psImageMaskType markVal);
     101
     102bool pmPSFtryMakePSF (pmPSFtry *psfTry);
     103
     104bool pmPSFtryFitPSF (pmPSFtry *psfTry, pmPSFOptions *options, psImageMaskType maskVal, psImageMaskType markVal);
    92105
    93106/** pmPSFtryMetric()
     
    97110 *
    98111 */
    99 bool pmPSFtryMetric(
    100     pmPSFtry *psfTry,                  ///< Add comment.
    101     pmPSFOptions *options              ///< PSF fitting options
    102 );
     112bool pmPSFtryMetric(pmPSFtry *psfTry);
    103113
    104114/** pmPSFtryMetric_Alt()
     
    112122    float RADIUS                        ///< Add comment.
    113123);
     124
     125bool pmPSFFitShapeParams (pmPSF *psf, psArray *sources, psVector *x, psVector *y, psVector *srcMask);
     126
     127float psVectorSystematicError (psVector *residuals, psVector *errors, float clipFraction);
     128
     129/// @}
     130# endif
    114131
    115132/**
     
    125142 *
    126143 */
    127 bool pmPSFFromPSFtry (pmPSFtry *psfTry);
    128144
    129 bool pmPSFFitShapeParams (pmPSF *psf, psArray *sources, psVector *x, psVector *y, psVector *srcMask);
    130 bool pmPSFFitShapeParamsMap (pmPSF *psf, int scale, float *scatterTotal, psVector *mask, psVector *x, psVector *y, psVector *mag, psVector *e0obs, psVector *e1obs, psVector *e2obs, psVector *dz);
    131 bool pmPSFShapeParamsScatter(float *scatterTotal, psVector *e0res, psVector *e1res, psVector *e2res, psVector *mask, psVectorMaskType maskValue, psStatsOptions stdevOpt);
    132 bool pmPSFShapeParamsErrors (float *errorFloor, psVector *mag, psVector *e0res, psVector *e1res, psVector *e2res, psVector *mask, int nGroup, psStatsOptions stdevOpt);
    133 
    134 /// @}
    135 # endif
  • branches/simtest_nebulous_branches/psModules/src/objects/pmPeaks.c

    r24623 r27840  
    6060    // if min point is too deviant, use the peak value
    6161    // XXX need to calculate dx, dy correctly
     62    // 0.5 PIX: peaks are calculated using the pixel index and converted here to pixel coords
    6263    if ((fabs(min.x) < 1.5) && (fabs(min.y) < 1.5)) {
    63         peak->xf = min.x + ix + image->col0;
    64         peak->yf = min.y + iy + image->row0;
     64        peak->xf = min.x + ix + image->col0 + 0.5;
     65        peak->yf = min.y + iy + image->row0 + 0.5;
    6566
    6667        // These errors are fractional errors, and should be scaled by the
     
    7374        peak->yf = PS_MAX (PS_MIN (peak->yf, image->numRows - 1), image->row0);
    7475    } else {
    75         peak->xf = ix;
    76         peak->yf = iy;
     76        peak->xf = ix + 0.5;
     77        peak->yf = iy + 0.5;
    7778        peak->dx = NAN;
    7879        peak->dy = NAN;
     
    374375    psU32 col = 0;
    375376    psU32 row = 0;
    376     psArray *list = NULL;
     377    psArray *list = psArrayAllocEmpty(100);
    377378
    378379    // Find peaks in row 0 only.
     
    415416
    416417        } else {
    417             psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
     418            psLogMsg ("psModules.objects", 5, "peak specified outside valid column range.");
    418419        }
    419420    }
     
    500501                }
    501502            } else {
    502                 psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
     503                psLogMsg ("psModules.objects", 5, "peak specified outside valid column range.");
    503504            }
    504505
     
    544545            }
    545546        } else {
    546             psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
     547            psLogMsg ("psModules.objects", 5, "peak specified outside valid column range.");
    547548        }
    548549    }
  • branches/simtest_nebulous_branches/psModules/src/objects/pmPeaks.h

    r20945 r27840  
    6363    bool assigned;                      ///< is peak assigned to a source?
    6464    pmPeakType type;                    ///< Description of peak.
    65     pmFootprint *footprint;     ///< reference to containing footprint
     65    pmFootprint *footprint;             ///< reference to containing footprint
    6666}
    6767pmPeak;
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSource.c

    r24874 r27840  
    33 *  Functions to define and manipulate sources on images
    44 *
    5  *  @author GLG, MHPCC
    6  *  @author EAM, IfA: significant modifications.
     5 *  @author EAM, IfA
     6 *  @author GLG, MHPCC (initial code base)
    77 *
    88 *  @version $Revision: 1.70 $ $Name: not supported by cvs2svn $
    99 *  @date $Date: 2009-02-16 22:29:59 $
    10  *
    11  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
    12  *
     10 *  Copyright 2009 Institute for Astronomy, University of Hawaii
    1311 */
    1412
     
    4846    psFree(tmp->maskView);
    4947    psFree(tmp->modelFlux);
    50     psFree(tmp->psfFlux);
     48    psFree(tmp->psfImage);
    5149    psFree(tmp->moments);
    5250    psFree(tmp->modelPSF);
     
    5452    psFree(tmp->modelFits);
    5553    psFree(tmp->extpars);
     54    psFree(tmp->moments);
     55    psFree(tmp->diffStats);
    5656    psFree(tmp->blends);
    5757    psTrace("psModules.objects", 10, "---- end ----\n");
     
    7070    psFree (source->maskView);
    7171    psFree (source->modelFlux);
    72     psFree (source->psfFlux);
     72    psFree (source->psfImage);
    7373
    7474    source->pixels = NULL;
     
    7777    source->maskView = NULL;
    7878    source->modelFlux = NULL;
    79     source->psfFlux = NULL;
     79    source->psfImage = NULL;
    8080    return;
    8181}
     
    105105    source->maskView = NULL;
    106106    source->modelFlux = NULL;
    107     source->psfFlux = NULL;
     107    source->psfImage = NULL;
    108108    source->moments = NULL;
    109109    source->blends = NULL;
     
    115115    source->tmpFlags = 0;
    116116    source->extpars = NULL;
     117    source->diffStats = NULL;
     118
    117119    source->region = psRegionSet(NAN, NAN, NAN, NAN);
    118120    psMemSetDeallocator(source, (psFreeFunc) sourceFree);
    119121
    120122    // default values are NAN
    121     source->psfMag = NAN;
     123    source->psfMag     = NAN;
     124    source->psfFlux    = NAN;
     125    source->psfFluxErr = NAN;
    122126    source->extMag = NAN;
    123127    source->errMag = NAN;
     
    176180    source->type = in->type;
    177181    source->mode = in->mode;
     182    source->imageID = in->imageID;
    178183
    179184    return(source);
     
    261266        mySource->modelFlux = NULL;
    262267
    263         // drop the old psfFlux pixels and force the user to re-create
    264         psFree (mySource->psfFlux);
    265         mySource->psfFlux = NULL;
     268        // drop the old psfImage pixels and force the user to re-create
     269        psFree (mySource->psfImage);
     270        mySource->psfImage = NULL;
    266271    }
    267272    return extend;
     
    277282// psphot-specific function which applies the recipe values
    278283// only apply selection to sources within specified region
    279 pmPSFClump pmSourcePSFClump(psRegion *region, psArray *sources, psMetadata *recipe)
     284pmPSFClump pmSourcePSFClump(psImage **savedImage, psRegion *region, psArray *sources, float PSF_SN_LIM, float PSF_CLUMP_GRID_SCALE, psF32 SX_MAX, psF32 SY_MAX, psF32 AR_MAX)
    280285{
    281286    psTrace("psModules.objects", 10, "---- begin ----\n");
    282287
    283288    psArray *peaks  = NULL;
    284     pmPSFClump errorClump = {-1.0, -1.0, 0.0, 0.0};
    285     pmPSFClump emptyClump = {+0.0, +0.0, 0.0, 0.0};
     289    pmPSFClump errorClump = {-1.0, -1.0, 0.0, 0.0, 0, 0.0};
     290    pmPSFClump emptyClump = {+0.0, +0.0, 0.0, 0.0, 0, 0.0};
    286291    pmPSFClump psfClump;
    287292
    288293    PS_ASSERT_PTR_NON_NULL(sources, errorClump);
    289     PS_ASSERT_PTR_NON_NULL(recipe, errorClump);
    290 
    291     bool status = true;                 // Status of MD lookup
    292     float PSF_SN_LIM = psMetadataLookupF32(&status, recipe, "PSF_SN_LIM");
    293     if (!status) {
    294         PSF_SN_LIM = 0;
    295     }
    296     float PSF_CLUMP_GRID_SCALE = psMetadataLookupF32(&status, recipe, "PSF_CLUMP_GRID_SCALE");
    297     if (!status) {
    298         PSF_CLUMP_GRID_SCALE = 0.1;
    299     }
    300294
    301295    // find the sigmaX, sigmaY clump
    302296    {
    303         psF32 SX_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_SX_MAX");
    304         if (!status) {
    305             psWarning("MOMENTS_SX_MAX not set in recipe");
    306             SX_MAX = 10.0;
    307         }
    308         psF32 SY_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_SY_MAX");
    309         if (!status) {
    310             psWarning("MOMENTS_SY_MAX not set in recipe");
    311             SY_MAX = 10.0;
    312         }
    313         psF32 AR_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_AR_MAX");
    314         if (!status) {
    315             psWarning("MOMENTS_AR_MAX not set in recipe");
    316             AR_MAX =  3.0;
    317         }
    318297        psF32 AR_MIN = 1.0 / AR_MAX;
    319298
    320299        // construct a sigma-plane image
    321         int numCols = SX_MAX / PSF_CLUMP_GRID_SCALE, numRows = SY_MAX / PSF_CLUMP_GRID_SCALE; // Size of sigma-plane image
     300        int numCols = 1 + SX_MAX / PSF_CLUMP_GRID_SCALE; // Size of sigma-plane image
     301        int numRows = 1 + SY_MAX / PSF_CLUMP_GRID_SCALE; // Size of sigma-plane image
    322302        psTrace("psModules.objects", 10, "sigma-plane dimensions: %dx%d\n", numCols, numRows);
    323303        psImage *splane = psImageAlloc(numCols, numRows, PS_TYPE_F32); // sigma-plane image
     
    332312            }
    333313
    334             int x = src->peak->x, y = src->peak->y; // Coordinates of peak
    335             if (x < region->x0 || x > region->x1 || y < region->y0 || y > region->y1) {
    336                 continue;
    337             }
     314            if (region) {
     315                int x = src->peak->x, y = src->peak->y; // Coordinates of peak
     316                if (x < region->x0 || x > region->x1 || y < region->y0 || y > region->y1) {
     317                    continue;
     318                }
     319            }
    338320
    339321            if (src->mode & PM_SOURCE_MODE_BLEND) {
    340322                continue;
    341323            }
     324
     325            if (!src->moments->nPixels) continue;
    342326
    343327            if (src->moments->SN < PSF_SN_LIM) {
     
    384368
    385369        // find the peak in this image
    386         psStats *stats = psStatsAlloc (PS_STAT_MAX);
     370        psStats *stats = psStatsAlloc (PS_STAT_MAX | PS_STAT_SAMPLE_STDEV);
    387371        if (!psImageStats (stats, splane, NULL, 0)) {
    388372            psError(PS_ERR_UNKNOWN, false, "Unable to get image statistics.\n");
     
    394378        psTrace ("psModules.objects", 2, "clump threshold is %f\n", stats[0].max/2);
    395379
    396         const bool keep_psf_clump = psMetadataLookupBool(NULL, recipe, "KEEP_PSF_CLUMP");
    397         if (keep_psf_clump)
    398         {
    399             psMetadataAdd(recipe, PS_LIST_TAIL,
    400                           "PSF_CLUMP", PS_DATA_IMAGE, "Image of PSF coefficients", splane);
     380        psfClump.nSigma = stats->sampleStdev;
     381
     382        if (savedImage) {
     383            *savedImage = psMemIncrRefCounter(splane);
    401384        }
    402385        psFree (splane);
     
    404387
    405388        // if we failed to find a valid peak, return the empty clump (failure signal)
    406         if (peaks == NULL)
     389        if (peaks == NULL) {
     390            psError(PS_ERR_UNKNOWN, false, "failure in peak analysis for PSF clump.\n");
     391            psFree (peaks);
     392            return emptyClump;
     393        }
     394
     395        if (peaks->n == 0)
    407396        {
    408397            psLogMsg ("psphot", 3, "failed to find a peak in the PSF clump image\n");
     
    412401                psLogMsg ("psphot", 3, "no significant peak\n");
    413402            }
     403            psFree (peaks);
    414404            return (emptyClump);
    415405        }
     
    430420        psTrace ("psModules.objects", 2, "clump is at %d, %d (%f)\n", clump->x, clump->y, clump->value);
    431421
     422        // XXX store the mean sigma?
     423        float meanSigma = psfClump.nSigma;
     424        psfClump.nStars = clump->value;
     425        psfClump.nSigma = clump->value / meanSigma;
     426
    432427        // define section window for clump
    433428        minSx = clump->x * PSF_CLUMP_GRID_SCALE - 2.0*PSF_CLUMP_GRID_SCALE;
     
    452447                continue;
    453448
    454             if (tmpSrc->peak->x < region->x0) continue;
    455             if (tmpSrc->peak->x > region->x1) continue;
    456             if (tmpSrc->peak->y < region->y0) continue;
    457             if (tmpSrc->peak->y > region->y1) continue;
     449            if (region) {
     450                if (tmpSrc->peak->x < region->x0) continue;
     451                if (tmpSrc->peak->x > region->x1) continue;
     452                if (tmpSrc->peak->y < region->y0) continue;
     453                if (tmpSrc->peak->y > region->y1) continue;
     454            }
    458455
    459456            if (tmpSrc->moments->Mxx < minSx)
     
    511508*****************************************************************************/
    512509
    513 bool pmSourceRoughClass(psRegion *region, psArray *sources, psMetadata *recipe, pmPSFClump clump, psImageMaskType maskSat)
     510bool pmSourceRoughClass(psRegion *region, psArray *sources, float PSF_SN_LIM, float PSF_CLUMP_NSIGMA, pmPSFClump clump, psImageMaskType maskSat)
    514511{
    515512    psTrace("psModules.objects", 10, "---- begin ----");
    516513
    517514    PS_ASSERT_PTR_NON_NULL(sources, false);
    518     PS_ASSERT_PTR_NON_NULL(recipe, false);
    519515
    520516    int Nsat     = 0;
     
    529525    psVector *starsn_peaks = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
    530526    psVector *starsn_moments = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
    531 
    532     // get basic parameters, or set defaults
    533     bool status;
    534     float PSF_SN_LIM = psMetadataLookupF32 (&status, recipe, "PSF_SN_LIM");
    535     if (!status) PSF_SN_LIM = 20.0;
    536     float PSF_CLUMP_NSIGMA = psMetadataLookupF32 (&status, recipe, "PSF_CLUMP_NSIGMA");
    537     if (!status) PSF_CLUMP_NSIGMA = 1.5;
    538 
    539     // float INNER_RADIUS = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
    540527
    541528    pmSourceMode noMoments = PM_SOURCE_MODE_MOMENTS_FAILURE | PM_SOURCE_MODE_SKYVAR_FAILURE | PM_SOURCE_MODE_SKY_FAILURE | PM_SOURCE_MODE_BELOW_MOMENTS_SN;
     
    893880
    894881    // if we already have a cached image, re-use that memory
    895     source->psfFlux = psImageCopy (source->psfFlux, source->pixels, PS_TYPE_F32);
    896     psImageInit (source->psfFlux, 0.0);
     882    source->psfImage = psImageCopy (source->psfImage, source->pixels, PS_TYPE_F32);
     883    psImageInit (source->psfImage, 0.0);
    897884
    898885    // in some places (psphotEnsemble), we need a normalized version
    899886    // in others, we just want the model.  which is more commonly used?
    900     // psfFlux always has unity normalization (I0 = 1.0)
    901     pmModelAdd (source->psfFlux, source->maskObj, source->modelPSF, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM, maskVal);
     887    // psfImage always has unity normalization (I0 = 1.0)
     888    pmModelAdd (source->psfImage, source->maskObj, source->modelPSF, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM, maskVal);
    902889    return true;
    903890}
     
    918905    pmModel *model = pmSourceGetModel (NULL, source);
    919906    if (model == NULL) return false;  // model must be defined
     907
     908    bool addNoise = mode & PM_MODEL_OP_NOISE;
    920909
    921910    if (source->modelFlux) {
     
    940929
    941930        psF32 **target = source->pixels->data.F32;
    942         if (mode & PM_MODEL_OP_NOISE) {
    943             // XXX need to scale by the gain...
     931        if (addNoise) {
     932            // when adding noise, we assume the shape and Io have been modified
    944933            target = source->variance->data.F32;
    945934        }
    946935
    947         // XXX need to respect the source and model masks?
    948936        for (int iy = 0; iy < source->modelFlux->numRows; iy++) {
    949937            int oy = iy + dY;
     
    959947            }
    960948        }
     949        if (!addNoise) {
     950            if (add) {
     951                source->tmpFlags &= ~PM_SOURCE_TMPF_SUBTRACTED;
     952            } else {
     953                source->tmpFlags |= PM_SOURCE_TMPF_SUBTRACTED;
     954            }
     955        }
    961956        return true;
    962957    }
    963958
    964959    psImage *target = source->pixels;
    965     if (mode & PM_MODEL_OP_NOISE) {
     960    if (addNoise) {
    966961        target = source->variance;
    967962    }
    968963
    969     if (add) {
    970         status = pmModelAddWithOffset (target, source->maskObj, model, PM_MODEL_OP_FULL, maskVal, dx, dy);
    971     } else {
    972         status = pmModelSubWithOffset (target, source->maskObj, model, PM_MODEL_OP_FULL, maskVal, dx, dy);
     964    if (!addNoise) {
     965        if (add) {
     966            status = pmModelAddWithOffset (target, source->maskObj, model, PM_MODEL_OP_FULL, maskVal, dx, dy);
     967            source->tmpFlags &= ~PM_SOURCE_TMPF_SUBTRACTED;
     968        } else {
     969            status = pmModelSubWithOffset (target, source->maskObj, model, PM_MODEL_OP_FULL, maskVal, dx, dy);
     970            source->tmpFlags |= PM_SOURCE_TMPF_SUBTRACTED;
     971        }
    973972    }
    974973
     
    10601059    psF32 fA = (A->peak == NULL) ? 0 : A->peak->y;
    10611060    psF32 fB = (B->peak == NULL) ? 0 : B->peak->y;
     1061
     1062    psF32 diff = fA - fB;
     1063    if (diff > FLT_EPSILON) return (+1);
     1064    if (diff < FLT_EPSILON) return (-1);
     1065    return (0);
     1066}
     1067
     1068// sort by X (ascending)
     1069int pmSourceSortByX (const void **a, const void **b)
     1070{
     1071    pmSource *A = *(pmSource **)a;
     1072    pmSource *B = *(pmSource **)b;
     1073
     1074    psF32 fA = (A->peak == NULL) ? 0 : A->peak->x;
     1075    psF32 fB = (B->peak == NULL) ? 0 : B->peak->x;
    10621076
    10631077    psF32 diff = fA - fB;
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSource.h

    r24579 r27840  
    1616#include "pmMoments.h"
    1717#include "pmSourceExtendedPars.h"
     18#include "pmSourceDiffStats.h"
    1819
    1920/// @addtogroup Objects Object Detection / Analysis Functions
     
    3839
    3940typedef enum {
    40     PM_SOURCE_TMPF_MODEL_GUESS = 0x0001,
    41     PM_SOURCE_TMPF_SUBTRACTED  = 0x0002,
     41    PM_SOURCE_TMPF_MODEL_GUESS       = 0x0001,
     42    PM_SOURCE_TMPF_SUBTRACTED        = 0x0002,
     43    PM_SOURCE_TMPF_SIZE_MEASURED     = 0x0004,
     44    PM_SOURCE_TMPF_SIZE_CR_CANDIDATE = 0x0008,
     45    PM_SOURCE_TMPF_MOMENTS_MEASURED  = 0x0010,
    4246} pmSourceTmpF;
    4347
     
    6367    psImage *maskView;                  ///< view into global image mask for this object region
    6468    psImage *modelFlux;                 ///< cached copy of the best model for this source
    65     psImage *psfFlux;                   ///< cached copy of the psf model for this source
     69    psImage *psfImage;                   ///< cached copy of the psf model for this source
    6670    pmMoments *moments;                 ///< Basic moments measured for the object.
    6771    pmModel *modelPSF;                  ///< PSF Model fit (parameters and type)
     
    7377    psArray *blends;                    ///< collection of sources thought to be confused with object
    7478    float psfMag;                       ///< calculated from flux in modelPSF
     79    float psfFlux;                      ///< calculated from flux in modelPSF
     80    float psfFluxErr;                   ///< calculated from flux in modelPSF
    7581    float extMag;                       ///< calculated from flux in modelEXT
    7682    float errMag;                       ///< error in psfMag OR extMag (depending on type)
     
    8187    float extNsigma;                    ///< Nsigma deviation from PSF to EXT
    8288    float sky, skyErr;                  ///< The sky and its error at the center of the object
     89    float apRadius;
    8390    psRegion region;                    ///< area on image covered by selected pixels
    8491    pmSourceExtendedPars *extpars;      ///< extended source parameters
     92    pmSourceDiffStats *diffStats;       ///< extra parameters for difference detections
     93    int imageID;
    8594};
    8695
     
    98107    float Y;
    99108    float dY;
     109    int nStars;
     110    float nSigma;
    100111}
    101112pmPSFClump;
     
    172183 *
    173184 * The return value indicates the success (TRUE) of the operation.
    174  *
    175  * XXX: Limit the S/N of the candidate sources (part of Metadata)? (TBD).
    176  * XXX: Save the clump parameters on the Metadata (TBD)
    177  *
    178  */
     185 */
     186
    179187pmPSFClump pmSourcePSFClump(
     188    psImage **savedImage,
    180189    psRegion *region,                   ///< restrict measurement to specified region
    181190    psArray *source,                    ///< The input pmSource
    182     psMetadata *metadata                ///< Contains classification parameters
     191    float PSF_SN_LIM,
     192    float PSF_CLUMP_GRID_SCALE,
     193    psF32 SX_MAX,
     194    psF32 SY_MAX,
     195    psF32 AR_MAX
    183196);
    184197
     
    196209    psRegion *region,                   ///< restrict measurement to specified region
    197210    psArray *sources,                    ///< The input pmSources
    198     psMetadata *metadata,               ///< Contains classification parameters
     211    float PSF_SN_LIM,                    ///< min S/N for source to be used for PSF model
     212    float PSF_CLUMP_NSIGMA,              ///< size of region around peak of clump for PSF stars
    199213    pmPSFClump clump,                   ///< Statistics about the PSF clump
    200214    psImageMaskType maskSat             ///< Mask value for saturated pixels
     
    216230    float radius,     ///< Use a circle of pixels around the peak
    217231    float sigma,      ///< size of Gaussian window function (<= 0.0 -> skip window)
    218     float minSN       ///< minimum pixel significance
     232    float minSN,              ///< minimum pixel significance
     233    psImageMaskType maskVal
    219234);
    220235
     
    232247int  pmSourceSortBySN (const void **a, const void **b);
    233248int  pmSourceSortByY (const void **a, const void **b);
     249int  pmSourceSortByX (const void **a, const void **b);
    234250int  pmSourceSortBySeq (const void **a, const void **b);
    235251
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceExtendedPars.c

    r23487 r27840  
    1717#endif
    1818
    19 #include <stdio.h>
    20 #include <math.h>
    21 #include <string.h>
     19// #include <stdio.h>
     20// #include <math.h>
     21// #include <string.h>
    2222#include <pslib.h>
    23 #include "pmHDU.h"
    24 #include "pmFPA.h"
    25 #include "pmFPAMaskWeight.h"
    26 #include "pmSpan.h"
    27 #include "pmFootprint.h"
    28 #include "pmPeaks.h"
    29 #include "pmMoments.h"
    30 #include "pmResiduals.h"
    31 #include "pmGrowthCurve.h"
    32 #include "pmTrend2D.h"
    33 #include "pmPSF.h"
    34 #include "pmModel.h"
    35 #include "pmSource.h"
    36 
     23// #include "pmHDU.h"
     24// #include "pmFPA.h"
     25// #include "pmFPAMaskWeight.h"
     26// #include "pmSpan.h"
     27// #include "pmFootprint.h"
     28// #include "pmPeaks.h"
     29// #include "pmMoments.h"
     30// #include "pmResiduals.h"
     31// #include "pmGrowthCurve.h"
     32// #include "pmTrend2D.h"
     33// #include "pmPSF.h"
     34// #include "pmModel.h"
     35// #include "pmSource.h"
     36#include "pmSourceExtendedPars.h"
     37
     38// pmSourceRadialFlux carries the raw radial flux information, including angular bins
     39static void pmSourceRadialFluxFree(pmSourceRadialFlux *flux)
     40{
     41    if (!flux) return;
     42    psFree(flux->radii);
     43    psFree(flux->fluxes);
     44    psFree(flux->theta);
     45    psFree(flux->isophotalRadii);
     46}
     47
     48pmSourceRadialFlux *pmSourceRadialFluxAlloc()
     49{
     50    pmSourceRadialFlux *flux = (pmSourceRadialFlux *)psAlloc(sizeof(pmSourceRadialFlux));
     51    psMemSetDeallocator(flux, (psFreeFunc) pmSourceRadialFluxFree);
     52
     53    flux->radii = NULL;
     54    flux->fluxes = NULL;
     55    flux->theta = NULL;
     56    flux->isophotalRadii = NULL;
     57
     58    return flux;
     59}
     60
     61bool psMemCheckSourceRadialFlux(psPtr ptr)
     62{
     63    PS_ASSERT_PTR(ptr, false);
     64    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceRadialFluxFree);
     65}
     66
     67// pmSourceEllipticalFlux carries the elliptical renormalized radial flux info
     68static void pmSourceEllipticalFluxFree(pmSourceEllipticalFlux *flux)
     69{
     70    if (!flux) return;
     71    psFree(flux->radiusElliptical);
     72    psFree(flux->fluxElliptical);
     73}
     74
     75pmSourceEllipticalFlux *pmSourceEllipticalFluxAlloc()
     76{
     77    pmSourceEllipticalFlux *flux = (pmSourceEllipticalFlux *)psAlloc(sizeof(pmSourceEllipticalFlux));
     78    psMemSetDeallocator(flux, (psFreeFunc) pmSourceEllipticalFluxFree);
     79
     80    flux->radiusElliptical = NULL;
     81    flux->fluxElliptical = NULL;
     82
     83    return flux;
     84}
     85
     86bool psMemCheckSourceEllipticalFlux(psPtr ptr)
     87{
     88    PS_ASSERT_PTR(ptr, false);
     89    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceEllipticalFluxFree);
     90}
     91
     92// pmSourceRadialProfile defines flux information in radial bins
     93static void pmSourceRadialProfileFree(pmSourceRadialProfile *profile)
     94{
     95    if (!profile) return;
     96    psFree(profile->binSB);
     97    psFree(profile->binSBstdev);
     98    psFree(profile->binSBerror);
     99    psFree(profile->binSum);
     100    psFree(profile->binFill);
     101    psFree(profile->radialBins);
     102    psFree(profile->area);
     103}
     104
     105pmSourceRadialProfile *pmSourceRadialProfileAlloc()
     106{
     107    pmSourceRadialProfile *profile = (pmSourceRadialProfile *)psAlloc(sizeof(pmSourceRadialProfile));
     108    psMemSetDeallocator(profile, (psFreeFunc) pmSourceRadialProfileFree);
     109
     110    profile->binSB = NULL;
     111    profile->binSBstdev = NULL;
     112    profile->binSBerror = NULL;
     113    profile->binSum = NULL;
     114    profile->binFill = NULL;
     115    profile->radialBins = NULL;
     116    profile->area = NULL;
     117    return profile;
     118}
     119
     120bool psMemCheckSourceRadialProfile(psPtr ptr)
     121{
     122    PS_ASSERT_PTR(ptr, false);
     123    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceRadialProfileFree);
     124}
     125
     126# if (0)
     127// pmSourceRadialProfileFreeVectors frees the intermediate data values
     128bool pmSourceRadialProfileFreeVectors(pmSourceRadialProfile *profile) {
     129
     130    psFree(profile->radii);
     131    psFree(profile->fluxes);
     132    psFree(profile->theta);
     133    psFree(profile->isophotalRadii);
     134
     135    psFree(profile->radiusElliptical);
     136    psFree(profile->fluxElliptical);
     137
     138    // psFree(profile->binSB);
     139    // psFree(profile->binSBstdev);
     140    // psFree(profile->binSBerror);
     141   
     142    // psFree(profile->radialBins);
     143    psFree(profile->area);
     144
     145    profile->radii = NULL;
     146    profile->fluxes = NULL;
     147    profile->theta = NULL;
     148    profile->isophotalRadii = NULL;
     149
     150    profile->radiusElliptical = NULL;
     151    profile->fluxElliptical = NULL;
     152
     153    // profile->binSB = NULL;
     154    // profile->binSBstdev = NULL;
     155    // profile->binSBerror = NULL;
     156   
     157    // profile->radialBins = NULL;
     158    profile->area = NULL;
     159
     160    return true;
     161}
     162# endif
     163
     164// *** pmSourceRadialProfileSortPair is a utility function for sorting a pair of vectors
     165# define COMPARE_INDEX(A,B) (index->data.F32[A] < index->data.F32[B])
     166# define SWAP_INDEX(TYPE,A,B) { \
     167  float tmp; \
     168  if (A != B) { \
     169    tmp = index->data.F32[A]; \
     170    index->data.F32[A] = index->data.F32[B]; \
     171    index->data.F32[B] = tmp; \
     172    tmp = extra->data.F32[A]; \
     173    extra->data.F32[A] = extra->data.F32[B]; \
     174    extra->data.F32[B] = tmp; \
     175  } \
     176}
     177
     178bool pmSourceRadialProfileSortPair (psVector *index, psVector *extra) {
     179
     180    // sort the vector set by the radius
     181    PSSORT (index->n, COMPARE_INDEX, SWAP_INDEX, NONE);
     182    return true;
     183}
     184
     185// *** pmSourceExtendedPars describes the possible collection of extended flux measurements for a source
    37186static void pmSourceExtendedParsFree (pmSourceExtendedPars *pars) {
    38187    if (!pars) return;
    39188
    40     psFree(pars->profile);
    41     psFree(pars->annuli);
    42     psFree(pars->isophot);
    43     psFree(pars->petrosian);
    44     psFree(pars->kron);
     189    psFree(pars->radFlux);
     190    psFree(pars->ellipticalFlux);
     191    psFree(pars->radProfile);
     192    psFree(pars->petProfile);
    45193    return;
    46194}
     
    50198    psMemSetDeallocator(pars, (psFreeFunc) pmSourceExtendedParsFree);
    51199
    52     pars->profile = NULL;
    53     pars->annuli = NULL;
    54     pars->isophot = NULL;
    55     pars->petrosian = NULL;
    56     pars->kron = NULL;
    57 
     200    pars->radFlux = NULL;
     201    pars->ellipticalFlux = NULL;
     202    pars->radProfile = NULL;
     203    pars->petProfile = NULL;
     204
     205    pars->petrosianFlux = NAN;
     206    pars->petrosianFluxErr = NAN;
     207    pars->petrosianRadius = NAN;
     208    pars->petrosianRadiusErr = NAN;
     209    pars->petrosianR90 = NAN;
     210    pars->petrosianR90Err = NAN;
     211    pars->petrosianR50 = NAN;
     212    pars->petrosianR50Err = NAN;
    58213    return pars;
    59214}
     
    66221
    67222
    68 static void pmSourceRadialProfileFree (pmSourceRadialProfile *profile) {
    69     if (!profile) return;
    70 
    71     psFree(profile->radius);
    72     psFree(profile->flux);
    73     psFree(profile->variance);
     223// *** pmSourceExtendedFlux describes the flux within an elliptical aperture of some kind
     224static void pmSourceExtendedFluxFree (pmSourceExtendedFlux *flux) {
     225    if (!flux) return;
    74226    return;
    75227}
    76228
    77 pmSourceRadialProfile *pmSourceRadialProfileAlloc (void) {
    78 
    79     pmSourceRadialProfile *profile = (pmSourceRadialProfile *) psAlloc(sizeof(pmSourceRadialProfile));
    80     psMemSetDeallocator(profile, (psFreeFunc) pmSourceRadialProfileFree);
    81 
    82     profile->radius = NULL;
    83     profile->flux = NULL;
    84     profile->variance = NULL;
    85 
    86     return profile;
    87 }
    88 
    89 bool psMemCheckSourceRadialProfile(psPtr ptr)
    90 {
    91     PS_ASSERT_PTR(ptr, false);
    92     return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceRadialProfileFree);
    93 }
    94 
    95 
    96 static void pmSourceIsophotalValuesFree (pmSourceIsophotalValues *isophot) {
    97     if (!isophot) return;
    98     return;
    99 }
    100 
    101 pmSourceIsophotalValues *pmSourceIsophotalValuesAlloc (void) {
    102 
    103     pmSourceIsophotalValues *isophot = (pmSourceIsophotalValues *) psAlloc(sizeof(pmSourceIsophotalValues));
    104     psMemSetDeallocator(isophot, (psFreeFunc) pmSourceIsophotalValuesFree);
    105 
    106     isophot->mag = 0.0;
    107     isophot->magErr = 0.0;
    108     isophot->rad = 0.0;
    109     isophot->radErr = 0.0;
    110 
    111     return isophot;
    112 }
    113 
    114 
    115 bool psMemCheckSourceIsophotalValues(psPtr ptr)
    116 {
    117     PS_ASSERT_PTR(ptr, false);
    118     return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceIsophotalValuesFree);
    119 }
    120 
    121 
    122 static void pmSourcePetrosianValuesFree (pmSourcePetrosianValues *petrosian) {
    123     if (!petrosian) return;
    124     return;
    125 }
    126 
    127 pmSourcePetrosianValues *pmSourcePetrosianValuesAlloc (void) {
    128 
    129     pmSourcePetrosianValues *petrosian = (pmSourcePetrosianValues *) psAlloc(sizeof(pmSourcePetrosianValues));
    130     psMemSetDeallocator(petrosian, (psFreeFunc) pmSourcePetrosianValuesFree);
    131 
    132     petrosian->mag = 0.0;
    133     petrosian->magErr = 0.0;
    134     petrosian->rad = 0.0;
    135     petrosian->radErr = 0.0;
    136 
    137     return petrosian;
    138 }
    139 
    140 
    141 bool psMemCheckSourcePetrosianValues(psPtr ptr)
    142 {
    143     PS_ASSERT_PTR(ptr, false);
    144     return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourcePetrosianValuesFree);
    145 }
    146 
    147 static void pmSourceKronValuesFree (pmSourceKronValues *kron) {
    148     if (!kron) return;
    149     return;
    150 }
    151 
    152 pmSourceKronValues *pmSourceKronValuesAlloc (void) {
    153 
    154     pmSourceKronValues *kron = (pmSourceKronValues *) psAlloc(sizeof(pmSourceKronValues));
    155     psMemSetDeallocator(kron, (psFreeFunc) pmSourceKronValuesFree);
    156 
    157     kron->mag = 0.0;
    158     kron->magErr = 0.0;
    159     kron->rad = 0.0;
    160     kron->radErr = 0.0;
    161 
    162     return kron;
    163 }
    164 
    165 
    166 bool psMemCheckSourceKronValues(psPtr ptr)
    167 {
    168     PS_ASSERT_PTR(ptr, false);
    169     return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceKronValuesFree);
    170 }
    171 
    172 
    173 static void pmSourceAnnuliFree (pmSourceAnnuli *annuli) {
    174     if (!annuli) return;
    175 
    176     psFree (annuli->flux);
    177     psFree (annuli->fluxErr);
    178     psFree (annuli->fluxVar);
    179 
    180     return;
    181 }
    182 
    183 pmSourceAnnuli *pmSourceAnnuliAlloc (void) {
    184 
    185     pmSourceAnnuli *annuli = (pmSourceAnnuli *) psAlloc(sizeof(pmSourceAnnuli));
    186     psMemSetDeallocator(annuli, (psFreeFunc) pmSourceAnnuliFree);
    187 
    188     annuli->flux = NULL;
    189     annuli->fluxErr = NULL;
    190     annuli->fluxVar = NULL;
    191 
    192     return annuli;
    193 }
    194 
    195 
    196 bool psMemCheckSourceAnnuli(psPtr ptr)
    197 {
    198     PS_ASSERT_PTR(ptr, false);
    199     return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceAnnuliFree);
    200 }
     229pmSourceExtendedFlux *pmSourceExtendedFluxAlloc (void) {
     230
     231    pmSourceExtendedFlux *flux = (pmSourceExtendedFlux *) psAlloc(sizeof(pmSourceExtendedFlux));
     232    psMemSetDeallocator(flux, (psFreeFunc) pmSourceExtendedFluxFree);
     233
     234    flux->flux = 0.0;
     235    flux->fluxErr = 0.0;
     236    flux->radius = 0.0;
     237    flux->radiusErr = 0.0;
     238
     239    return flux;
     240}
     241
     242
     243bool psMemCheckSourceExtendedFlux(psPtr ptr)
     244{
     245    PS_ASSERT_PTR(ptr, false);
     246    return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceExtendedFluxFree);
     247}
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceExtendedPars.h

    r23487 r27840  
    1515
    1616typedef struct {
    17   psVector *radius;
    18   psVector *flux;
    19   psVector *variance;
     17    psArray  *radii;                    // radii for raw radial profiles at evenly-spaced angles
     18    psArray  *fluxes;                   // fluxes measured at above radii
     19    psVector *theta;                    // angles corresponding to above radial profiles
     20    psVector *isophotalRadii;           // isophotal radius for the above angles
     21} pmSourceRadialFlux;
     22
     23typedef struct {
     24    psVector *radiusElliptical;         // normalized radial coordinates for all relevant pixels
     25    psVector *fluxElliptical;           // flux for the above radial coordinates
     26} pmSourceEllipticalFlux;
     27
     28typedef struct {
     29    psVector *binSB;                    // mean surface brightness within radial bins
     30    psVector *binSBstdev;               // scatter of mean surface brightness within radial bins
     31    psVector *binSBerror;               // formal error on mean surface brightness within radial bins
     32    psVector *binSum;                   // sum of flux within radial bins
     33    psVector *binFill;                  // fraction of area actually lit
     34    psVector *radialBins;               // radii corresponding to above binnedFlux
     35    psVector *area;                     // differential area of the non-overlapping radial bins
    2036} pmSourceRadialProfile;
    2137
    2238typedef struct {
    23   psVector *flux;
    24   psVector *fluxErr;
    25   psVector *fluxVar;
    26 } pmSourceAnnuli;
     39    float flux;
     40    float fluxErr;
     41    float radius;
     42    float radiusErr;
     43} pmSourceExtendedFlux;
    2744
    2845typedef struct {
    29   float mag;
    30   float magErr;
    31   float rad;
    32   float radErr;
    33 } pmSourceIsophotalValues;
    34 
    35 typedef struct {
    36   float mag;
    37   float magErr;
    38   float rad;
    39   float radErr;
    40 } pmSourcePetrosianValues;
    41 
    42 typedef struct {
    43   float mag;
    44   float magErr;
    45   float rad;
    46   float radErr;
    47 } pmSourceKronValues;
    48 
    49 typedef struct {
    50   pmSourceRadialProfile   *profile;
    51   pmSourceAnnuli          *annuli;
    52   pmSourceIsophotalValues *isophot;
    53   pmSourcePetrosianValues *petrosian;
    54   pmSourceKronValues      *kron;
     46    pmSourceRadialFlux     *radFlux;        // raw radial flux information
     47    pmSourceEllipticalFlux *ellipticalFlux; // flux for elliptically-renormalized radii
     48    pmSourceRadialProfile  *radProfile;     // surface brightness profile in specified fixed bins
     49    pmSourceRadialProfile  *petProfile;     // surface brightness profile in petrosian bins
     50    psEllipseAxes axes;                     // shape of elliptical contour
     51    float petrosianFlux;
     52    float petrosianFluxErr;
     53    float petrosianRadius;
     54    float petrosianRadiusErr;
     55    float petrosianR90;
     56    float petrosianR90Err;
     57    float petrosianR50;
     58    float petrosianR50Err;
    5559} pmSourceExtendedPars;
    5660
    57 pmSourceExtendedPars *pmSourceExtendedParsAlloc(void);
     61pmSourceRadialFlux *pmSourceRadialFluxAlloc();
     62bool psMemCheckSourceRadialFlux(psPtr ptr);
     63
     64pmSourceEllipticalFlux *pmSourceEllipticalFluxAlloc();
     65bool psMemCheckSourceEllipticalFlux(psPtr ptr);
     66
     67// *** pmSourceRadialProfile describes the radial profile of a source in elliptical contours, and
     68// intermediate data used to measure the profile
     69pmSourceRadialProfile *pmSourceRadialProfileAlloc();
     70bool psMemCheckSourceRadialProfile(psPtr ptr);
     71
     72// *** pmSourceExtendedPars describes the possible collection of extended flux measurements for a source
     73pmSourceExtendedPars *pmSourceExtendedParsAlloc (void);
    5874bool psMemCheckSourceExtendedPars(psPtr ptr);
    59 pmSourceRadialProfile *pmSourceRadialProfileAlloc(void);
    60 bool psMemCheckSourceRadialProfile(psPtr ptr);
    61 pmSourceIsophotalValues *pmSourceIsophotalValuesAlloc(void);
    62 bool psMemCheckSourceIsophotalValues(psPtr ptr);
    63 pmSourcePetrosianValues *pmSourcePetrosianValuesAlloc(void);
    64 bool psMemCheckSourcePetrosianValues(psPtr ptr);
    65 pmSourceKronValues *pmSourceKronValuesAlloc(void);
    66 bool psMemCheckSourceKronValues(psPtr ptr);
    67 pmSourceAnnuli *pmSourceAnnuliAlloc(void);
    68 bool psMemCheckSourceAnnuli(psPtr ptr);
     75
     76// *** pmSourceExtendedFlux describes the flux within an elliptical aperture of some kind
     77pmSourceExtendedFlux *pmSourceExtendedFluxAlloc(void);
     78bool psMemCheckSourceExtendedFlux(psPtr ptr);
     79
     80// *** pmSourceRadialProfileSortPair is a utility function for sorting a pair of vectors
     81bool pmSourceRadialProfileSortPair(psVector *index, psVector *extra);
     82
     83
    6984
    7085/// @}
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceFitModel.c

    r23989 r27840  
    9393            }
    9494
     95            // skip nan values in image
     96            if (!isfinite(source->variance->data.F32[i][j])) {
     97              fprintf (stderr, "impossible! %x vs %x\n", source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[i][j], maskVal);
     98              continue;
     99            }
     100
    95101            psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
    96102
    97103            // Convert i/j to image space:
    98             coord->data.F32[0] = (psF32) (j + source->pixels->col0);
    99             coord->data.F32[1] = (psF32) (i + source->pixels->row0);
     104            // 0.5 PIX: the coordinate values must be in pixel coords, not index           
     105            coord->data.F32[0] = (psF32) (j + 0.5 + source->pixels->col0);
     106            coord->data.F32[1] = (psF32) (i + 0.5 + source->pixels->row0);
    100107            x->data[nPix] = (psPtr *) coord;
    101108            y->data.F32[nPix] = source->pixels->data.F32[i][j];
     
    175182            continue;
    176183        dparams->data.F32[i] = sqrt(covar->data.F32[i][i]);
    177         if (psTraceGetLevel("psModules.objects") >= 4) {
    178             fprintf (stderr, "%f +/- %f\n", params->data.F32[i], dparams->data.F32[i]);
    179         }
     184        psTrace ("psModules.objects", 4, "%f +/- %f", params->data.F32[i], dparams->data.F32[i]);
    180185    }
    181186    psTrace ("psModules.objects", 4, "niter: %d, chisq: %f", myMin->iter, myMin->value);
     
    186191    model->nPix  = y->n;
    187192    model->nDOF  = y->n - nParams;
     193    model->chisqNorm = model->chisq / model->nDOF;
    188194    model->flags |= PM_MODEL_STATUS_FITTED;
    189195    if (!fitStatus) model->flags |= PM_MODEL_STATUS_NONCONVERGE;
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceFitSet.c

    r23487 r27840  
    321321
    322322        for (int j = 0; j < model->params->n; j++, n++) {
    323             if (psTraceGetLevel("psModules.objects") >= 4) {
    324                 fprintf (stderr, "%f ", param->data.F32[n]);
    325             }
    326323            model->params->data.F32[j] = param->data.F32[n];
    327324            model->dparams->data.F32[j] = dparam->data.F32[n];
     325            psTrace ("psModules.objects", 4, "%f +/- %f", param->data.F32[n], dparam->data.F32[n]);
    328326        }
    329327        psTrace ("psModules.objects", 4, " src %d", i);
     
    483481
    484482            // Convert i/j to image space:
    485             coord->data.F32[0] = (psF32) (j + source->pixels->col0);
    486             coord->data.F32[1] = (psF32) (i + source->pixels->row0);
     483            // 0.5 PIX: the coordinate values must be in pixel coords, not index           
     484            coord->data.F32[0] = (psF32) (j + 0.5 + source->pixels->col0);
     485            coord->data.F32[1] = (psF32) (i + 0.5 + source->pixels->row0);
    487486            x->data[nPix] = (psPtr *) coord;
    488487            y->data.F32[nPix] = source->pixels->data.F32[i][j];
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO.c

    r24694 r27840  
    4040#include "pmPSF.h"
    4141#include "pmModel.h"
     42#include "pmDetections.h"
    4243#include "pmSource.h"
    4344#include "pmModelClass.h"
     45#include "pmDetEff.h"
    4446#include "pmSourceIO.h"
    4547
    4648#define BLANK_HEADERS "BLANK.HEADERS"   // Name of metadata in camera configuration containing header names
    4749                                        // for putting values into a blank PHU
     50
     51// lookup the EXTNAME values used for table data and image header segments
     52static bool sourceExtensions(psString *headname, // Extension name for header
     53                             psString *dataname, // Extension name for data
     54                             psString *deteffname, // Extension name for detection efficiency
     55                             psString *xsrcname, // Extension name for extended sources
     56                             psString *xfitname, // Extension name for extended fits
     57                             const pmFPAfile *file, // File of interest
     58                             const pmFPAview *view // View to level of interest
     59                             )
     60{
     61    bool status;                        // Status of MD lookup
     62
     63    // Menu of EXTNAME rules
     64    psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "EXTNAME.RULES");
     65    if (!menu) {
     66        psError(PS_ERR_UNKNOWN, true, "missing EXTNAME.RULES in camera.config");
     67        return false;
     68    }
     69
     70    // EXTNAME for image header
     71    if (headname) {
     72        const char *rule = psMetadataLookupStr(&status, menu, "CMF.HEAD");
     73        if (!rule) {
     74            psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.HEAD in EXTNAME.RULES in camera.config");
     75            return false;
     76        }
     77        *headname = pmFPAfileNameFromRule(rule, file, view);
     78    }
     79
     80    // EXTNAME for table data
     81    if (dataname) {
     82        const char *rule = psMetadataLookupStr(&status, menu, "CMF.DATA");
     83        if (!rule) {
     84            psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.DATA in EXTNAME.RULES in camera.config");
     85            return false;
     86        }
     87        *dataname = pmFPAfileNameFromRule(rule, file, view);
     88    }
     89
     90    // EXTNAME for detection efficiency
     91    if (deteffname) {
     92        const char *rule = psMetadataLookupStr(&status, menu, "CMF.DETEFF");
     93        if (!rule) {
     94            psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.DETEFF in EXTNAME.RULES in camera.config");
     95            return false;
     96        }
     97        *deteffname = pmFPAfileNameFromRule(rule, file, view);
     98    }
     99
     100    // EXTNAME for extended source data table
     101    if (xsrcname) {
     102        const char *rule = psMetadataLookupStr(&status, menu, "CMF.XSRC");
     103        if (!rule) {
     104            psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.XSRC in EXTNAME.RULES in camera.config");
     105            return false;
     106        }
     107        *xsrcname = pmFPAfileNameFromRule (rule, file, view);
     108    }
     109
     110    if (xfitname) {
     111        // EXTNAME for extended source data table
     112        const char *rule = psMetadataLookupStr(&status, menu, "CMF.XFIT");
     113        if (!rule) {
     114            psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.XFIT in EXTNAME.RULES in camera.config");
     115            return false;
     116        }
     117        *xfitname = pmFPAfileNameFromRule (rule, file, view);
     118    }
     119
     120    return true;
     121}
     122
    48123
    49124// translations between psphot object types and dophot object types
     
    268343    pmHDU *hdu;
    269344    psMetadata *updates;
    270     psMetadata *outhead;
    271 
    272     char *exttype  = NULL;
    273     char *dataname = NULL;
    274     char *xsrcname = NULL;
    275     char *xfitname = NULL;
    276     char *headname = NULL;
    277345
    278346    // if sources is NULL, write out an empty table
    279     // input / output sources are stored on the readout->analysis as "PSPHOT.SOURCES" -- a better name might be something like PM_SOURCE_DATA
    280     psArray *sources = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.SOURCES");
     347    // input / output sources are stored on the readout->analysis as "PSPHOT.DETECTIONS"
     348
     349    psArray *sources = NULL;
     350    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     351    if (detections) {
     352        sources = detections->allSources;
     353    }
    281354    if (!sources) {
     355        detections = pmDetectionsAlloc();
    282356        sources = psArrayAlloc(0);
    283         psMetadataAddArray(readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE, "Blank array of sources", sources);
    284         psFree(sources); // Held onto by the metadata, so we can continue to use
     357        detections->allSources = sources;
     358        psMetadataAddPtr(readout->analysis, PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_DATA_UNKNOWN | PS_META_REPLACE, "Blank array of sources", detections);
     359        psFree(detections); // Held onto by the metadata, so we can continue to use
    285360    }
    286361
     
    298373        break;
    299374
    300       case PM_FPA_FILE_CMP:
    301         // a SPLIT format : only one header and object table per file
    302         hdu = pmFPAviewThisHDU (view, fpa);
    303         if (!hdu) {
    304             psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find HDU to write sources.");
    305             return false;
    306         }
    307 
    308         // copy the header to an output header, add the output header data
    309         outhead = psMetadataCopy (NULL, hdu->header);
    310 
    311         // copy over the entries saved by PSPHOT
    312         updates = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.HEADER");
    313         if (updates) {
    314             psMetadataCopy (outhead, updates);
    315         }
    316 
    317         // copy over the entries saved by PSASTRO
    318         updates = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.HEADER");
    319         if (updates) {
    320             psMetadataCopy (outhead, updates);
    321         }
    322 
    323         bool status = pmSourcesWriteCMP (sources, file->filename, outhead);
    324         psFree (outhead);
    325 
    326         if (!status) {
    327             psError(PS_ERR_IO, false, "Failed to write CMP file\n");
    328             return false;
    329         }
    330         break;
    331 
    332       case PM_FPA_FILE_CMF:
     375      case PM_FPA_FILE_CMP: {
     376          // a SPLIT format : only one header and object table per file
     377          hdu = pmFPAviewThisHDU (view, fpa);
     378          if (!hdu) {
     379              psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find HDU to write sources.");
     380              return false;
     381          }
     382
     383          // copy the header to an output header, add the output header data
     384          psMetadata *outhead = psMetadataCopy (NULL, hdu->header);
     385
     386          // copy over the entries saved by PSPHOT
     387          updates = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.HEADER");
     388          if (updates) {
     389              psMetadataCopy (outhead, updates);
     390          }
     391
     392          // copy over the entries saved by PSASTRO
     393          updates = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.HEADER");
     394          if (updates) {
     395              psMetadataCopy (outhead, updates);
     396          }
     397
     398          bool status = pmSourcesWriteCMP (sources, file->filename, outhead);
     399          psFree (outhead);
     400
     401          if (!status) {
     402              psError(PS_ERR_IO, false, "Failed to write CMP file\n");
     403              return false;
     404          }
     405          break;
     406      }
     407
     408      case PM_FPA_FILE_CMF:
    333409        // write a header? (only if this is the first readout for cell)
    334410        //   note that the file->header is set to track the last hdu->header written
     
    345421        psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "PSPHOT");
    346422        if (!status) {
    347           psError(PS_ERR_UNKNOWN, true, "missing recipe PSPHOT in config data");
    348           return false;
     423            psError(PS_ERR_UNKNOWN, true, "missing recipe PSPHOT in config data");
     424            return false;
    349425        }
    350426
     
    354430
    355431        // define the EXTNAME values for the different data segments:
    356         {
    357             // lookup the EXTNAME values used for table data and image header segments
    358             char *rule = NULL;
    359 
    360             // Menu of EXTNAME rules
    361             psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "EXTNAME.RULES");
    362             if (!menu) {
    363                 psError(PS_ERR_UNKNOWN, true, "missing EXTNAME.RULES in camera.config");
    364                 return false;
    365             }
    366 
    367             // EXTNAME for image header
    368             rule = psMetadataLookupStr(&status, menu, "CMF.HEAD");
    369             if (!rule) {
    370                 psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.HEAD in EXTNAME.RULES in camera.config");
    371                 return false;
    372             }
    373             headname = pmFPAfileNameFromRule (rule, file, view);
    374 
    375             // EXTNAME for table data
    376             rule = psMetadataLookupStr(&status, menu, "CMF.DATA");
    377             if (!rule) {
    378                 psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.DATA in EXTNAME.RULES in camera.config");
    379                 return false;
    380             }
    381             dataname = pmFPAfileNameFromRule (rule, file, view);
    382 
    383             if (XSRC_OUTPUT) {
    384               // EXTNAME for extended source data table
    385               rule = psMetadataLookupStr(&status, menu, "CMF.XSRC");
    386               if (!rule) {
    387                 psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.XSRC in EXTNAME.RULES in camera.config");
    388                 return false;
    389               }
    390               xsrcname = pmFPAfileNameFromRule (rule, file, view);
    391             }
    392             if (XFIT_OUTPUT) {
    393               // EXTNAME for extended source data table
    394               rule = psMetadataLookupStr(&status, menu, "CMF.XFIT");
    395               if (!rule) {
    396                 psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.XFIT in EXTNAME.RULES in camera.config");
    397                 return false;
    398               }
    399               xfitname = pmFPAfileNameFromRule (rule, file, view);
    400             }
     432        psString headname = NULL;
     433        psString dataname = NULL;
     434        psString deteffname = NULL;
     435        psString xsrcname = NULL;
     436        psString xfitname = NULL;
     437        if (!sourceExtensions(&headname, &dataname, &deteffname,
     438                              XSRC_OUTPUT ? &xsrcname : NULL,
     439                              XFIT_OUTPUT ? &xfitname : NULL,
     440                              file, view)) {
     441            return false;
    401442        }
    402443
     
    456497        }
    457498
    458         // write out the TABLE data segment
     499        // write out the Object TABLE data segment(s)
    459500        {
    460501            // create a header to hold the output data
    461             outhead = psMetadataAlloc ();
    462 
    463             exttype = psMemIncrRefCounter (psMetadataLookupStr(&status, recipe, "OUTPUT.FORMAT"));
     502            psMetadata *outhead = psMetadataAlloc ();
     503           
     504            char *exttype = psMemIncrRefCounter (psMetadataLookupStr(&status, recipe, "OUTPUT.FORMAT"));
    464505            if (!exttype) {
    465506                exttype = psStringCopy ("SMPDATA");
     
    469510            psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTHEAD", PS_META_REPLACE, "name of image extension w/", headname);
    470511            psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTTYPE", PS_META_REPLACE, "extension type", exttype);
    471             psFree (exttype);
    472512
    473513            // if we request XSRC output, add the XSRC name to this header
    474514            if (xsrcname) {
    475               psMetadataAddStr (outhead, PS_LIST_TAIL, "XSRCNAME", PS_META_REPLACE, "name of XSRC table extension", xsrcname);
     515                psMetadataAddStr (outhead, PS_LIST_TAIL, "XSRCNAME", PS_META_REPLACE, "name of XSRC table extension", xsrcname);
    476516            }
    477517            if (xfitname) {
    478               psMetadataAddStr (outhead, PS_LIST_TAIL, "XFITNAME", PS_META_REPLACE, "name of XFIT table extension", xfitname);
     518                psMetadataAddStr (outhead, PS_LIST_TAIL, "XFITNAME", PS_META_REPLACE, "name of XFIT table extension", xfitname);
    479519            }
    480520
    481521            // XXX these are case-sensitive since the EXTYPE is case-sensitive
    482             status = false;
     522            status = true;
    483523            if (!strcmp (exttype, "SMPDATA")) {
    484                 status = pmSourcesWrite_SMPDATA (file->fits, sources, file->header, outhead, dataname);
     524                status &= pmSourcesWrite_SMPDATA (file->fits, sources, file->header, outhead, dataname);
    485525            }
    486526            if (!strcmp (exttype, "PS1_DEV_0")) {
    487                 status = pmSourcesWrite_PS1_DEV_0 (file->fits, sources, file->header, outhead, dataname);
     527                status &= pmSourcesWrite_PS1_DEV_0 (file->fits, sources, file->header, outhead, dataname);
    488528            }
    489529            if (!strcmp (exttype, "PS1_DEV_1")) {
    490                 status = pmSourcesWrite_PS1_DEV_1 (file->fits, sources, file->header, outhead, dataname);
     530                status &= pmSourcesWrite_PS1_DEV_1 (file->fits, sources, file->header, outhead, dataname);
    491531            }
    492532            if (!strcmp (exttype, "PS1_CAL_0")) {
    493                 status = pmSourcesWrite_PS1_CAL_0 (file->fits, readout, sources, file->header, outhead, dataname);
     533                status &= pmSourcesWrite_PS1_CAL_0 (file->fits, readout, sources, file->header, outhead, dataname);
    494534            }
    495535            if (!strcmp (exttype, "PS1_V1")) {
    496                 status = pmSourcesWrite_CMF_PS1_V1 (file->fits, readout, sources, file->header, outhead, dataname);
     536                status &= pmSourcesWrite_CMF_PS1_V1 (file->fits, readout, sources, file->header, outhead, dataname);
    497537            }
    498538            if (!strcmp (exttype, "PS1_V2")) {
    499                 status = pmSourcesWrite_CMF_PS1_V2 (file->fits, readout, sources, file->header, outhead, dataname);
    500             }
     539                status &= pmSourcesWrite_CMF_PS1_V2 (file->fits, readout, sources, file->header, outhead, dataname);
     540            }
     541            if (!strcmp (exttype, "PS1_DV1")) {
     542                status &= pmSourcesWrite_CMF_PS1_DV1 (file->fits, readout, sources, file->header, outhead, dataname);
     543            }
     544
    501545            if (xsrcname) {
    502               if (!strcmp (exttype, "PS1_DEV_1")) {
    503                   status = pmSourcesWrite_PS1_DEV_1_XSRC (file->fits, sources, xsrcname, recipe);
    504               }
    505               if (!strcmp (exttype, "PS1_CAL_0")) {
    506                   status = pmSourcesWrite_PS1_CAL_0_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
    507               }
    508               if (!strcmp (exttype, "PS1_V1")) {
    509                   status = pmSourcesWrite_CMF_PS1_V1_XSRC (file->fits, sources, xsrcname, recipe);
    510               }
    511               if (!strcmp (exttype, "PS1_V2")) {
    512                   status = pmSourcesWrite_CMF_PS1_V2_XSRC (file->fits, sources, xsrcname, recipe);
    513               }
     546                if (!strcmp (exttype, "PS1_DEV_1")) {
     547                    status &= pmSourcesWrite_PS1_DEV_1_XSRC (file->fits, sources, xsrcname, recipe);
     548                }
     549                if (!strcmp (exttype, "PS1_CAL_0")) {
     550                    status &= pmSourcesWrite_PS1_CAL_0_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
     551                }
     552                if (!strcmp (exttype, "PS1_V1")) {
     553                    status &= pmSourcesWrite_CMF_PS1_V1_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
     554                }
     555                if (!strcmp (exttype, "PS1_V2")) {
     556                    status &= pmSourcesWrite_CMF_PS1_V2_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
     557                }
     558                if (!strcmp (exttype, "PS1_DV1")) {
     559                    status &= pmSourcesWrite_CMF_PS1_DV1_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
     560                }
    514561            }
    515562            if (xfitname) {
    516               if (!strcmp (exttype, "PS1_DEV_1")) {
    517                   status = pmSourcesWrite_PS1_DEV_1_XFIT (file->fits, sources, xfitname);
    518               }
    519               if (!strcmp (exttype, "PS1_CAL_0")) {
    520                   status = pmSourcesWrite_PS1_CAL_0_XFIT (file->fits, readout, sources, file->header, xfitname);
    521               }
    522               if (!strcmp (exttype, "PS1_V1")) {
    523                   status = pmSourcesWrite_CMF_PS1_V1_XFIT (file->fits, sources, xfitname);
    524               }
    525               if (!strcmp (exttype, "PS1_V2")) {
    526                   status = pmSourcesWrite_CMF_PS1_V2_XFIT (file->fits, sources, xfitname);
    527               }
    528             }
     563                if (!strcmp (exttype, "PS1_DEV_1")) {
     564                    status &= pmSourcesWrite_PS1_DEV_1_XFIT (file->fits, sources, xfitname);
     565                }
     566                if (!strcmp (exttype, "PS1_CAL_0")) {
     567                    status &= pmSourcesWrite_PS1_CAL_0_XFIT (file->fits, readout, sources, file->header, xfitname);
     568                }
     569                if (!strcmp (exttype, "PS1_V1")) {
     570                    status &= pmSourcesWrite_CMF_PS1_V1_XFIT (file->fits, readout, sources, xfitname);
     571                }
     572                if (!strcmp (exttype, "PS1_V2")) {
     573                    status &= pmSourcesWrite_CMF_PS1_V2_XFIT (file->fits, readout, sources, xfitname);
     574                }
     575                if (!strcmp (exttype, "PS1_DV1")) {
     576                    status &= pmSourcesWrite_CMF_PS1_DV1_XFIT (file->fits, readout, sources, xfitname);
     577                }
     578            }
     579            psFree (outhead);
     580            psFree (exttype);
     581
    529582            if (!status) {
    530583                psError(PS_ERR_IO, false, "writing CMF data to %s with format %s\n", file->filename, exttype);
    531                 psFree (headname);
    532                 psFree (dataname);
    533                 psFree (xsrcname);
    534                 psFree (xfitname);
    535                 psFree (outhead);
    536                 return false;
    537             }
    538         }
     584                goto escape;
     585            }
     586        }
     587
     588
     589        // write out the detection efficiency TABLE segments
     590        if (deteffname) {
     591            // create a header to hold the output data
     592            psMetadata *outhead = psMetadataAlloc ();
     593            psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTHEAD", PS_META_REPLACE, "name of image extension w/", headname);
     594            psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTTYPE", PS_META_REPLACE, "extension type", "DETEFF");
     595
     596            status = pmReadoutWriteDetEff(file->fits, readout, outhead, deteffname);
     597            psFree (outhead);
     598
     599            if (!status) {
     600                psError(PS_ERR_IO, false, "writing DETEFF data to %s\n", file->filename);
     601                goto escape;
     602            }
     603        }
     604        psFree (headname);
     605        psFree (dataname);
     606        psFree (xsrcname);
     607        psFree (xfitname);
     608        psFree (deteffname);
    539609
    540610        psTrace ("pmFPAfile", 5, "wrote ext data %s (type: %d)\n", file->filename, file->type);
    541 
    542         psFree (headname);
    543         psFree (dataname);
    544         psFree (xsrcname);
    545         psFree (xfitname);
    546         psFree (outhead);
    547611        break;
     612
     613      escape:
     614        psFree (headname);
     615        psFree (dataname);
     616        psFree (xsrcname);
     617        psFree (xfitname);
     618        psFree (deteffname);
     619        return false;
    548620
    549621      default:
     
    552624    }
    553625    return true;
     626
    554627}
    555628// a MEF CMF file has: PHU, CELL-HEAD, TABLE, CELL-HEAD, TABLE, TABLE, TABLE...
     
    572645    // not needed if only one chip
    573646    if (file->fpa->chips->n == 1) {
    574         pmSourceIO_WriteMatchedRefs (file->fits, file->fpa, config);
    575         return true;
     647        pmSourceIO_WriteMatchedRefs (file->fits, file->fpa, config);
     648        return true;
    576649    }
    577650
     
    885958        hdu = pmFPAviewThisHDU (view, file->fpa);
    886959
    887         // lookup the EXTNAME values used for table data and image header segments
    888         char *rule = NULL;
    889         // Menu of EXTNAME rules
    890         psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "EXTNAME.RULES");
    891         if (!menu) {
    892             psError(PS_ERR_UNKNOWN, true, "missing EXTNAME.RULES in camera.config");
    893             return false;
    894         }
    895         // EXTNAME for image header
    896         rule = psMetadataLookupStr(&status, menu, "CMF.HEAD");
    897         if (!rule) {
    898             psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.HEAD in EXTNAME.RULES in camera.config");
    899             return false;
    900         }
    901         char *headname = pmFPAfileNameFromRule (rule, file, view);
    902         // EXTNAME for table data
    903         rule = psMetadataLookupStr(&status, menu, "CMF.DATA");
    904         if (!rule) {
    905             psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.DATA in EXTNAME.RULES in camera.config");
    906             return false;
    907         }
    908         char *dataname = pmFPAfileNameFromRule (rule, file, view);
     960        // define the EXTNAME values for the different data segments:
     961        psString headname = NULL;
     962        psString dataname = NULL;
     963        psString deteffname = NULL;
     964        if (!sourceExtensions(&headname, &dataname, &deteffname, NULL, NULL, file, view)) {
     965            return false;
     966        }
    909967
    910968        // advance to the IMAGE HEADER extension
    911969        if (hdu->header == NULL) {
    912970            // if the IMAGE header does not exist, we have no data for this view
    913             if (!psFitsMoveExtName (file->fits, headname)) {
     971            if (!psFitsMoveExtNameClean (file->fits, headname)) {
    914972                readout->data_exists = false;
    915973                psFree (headname);
    916974                psFree (dataname);
     975                psFree (deteffname);
    917976                return true;
    918977            }
     
    938997        if (!tableHeader) psAbort("cannot read table header");
    939998
     999        char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
     1000        if (!xtension) psAbort("cannot read table type");
     1001        if (strcmp (xtension, "BINTABLE")) {
     1002            psWarning ("no binary table in extension %s, skipping\n", dataname);
     1003            return false;
     1004        }
     1005
    9401006        char *exttype = psMetadataLookupStr (NULL, tableHeader, "EXTTYPE");
    9411007        if (!exttype) psAbort("cannot read table type");
     
    9581024                sources = pmSourcesRead_CMF_PS1_V2 (file->fits, hdu->header);
    9591025            }
     1026            if (!strcmp (exttype, "PS1_DV1")) {
     1027                sources = pmSourcesRead_CMF_PS1_DV1 (file->fits, hdu->header);
     1028            }
     1029
     1030            if (!pmReadoutReadDetEff(file->fits, readout, deteffname)) {
     1031#if 0
     1032                psError(PS_ERR_IO, false, "Unable to read detection efficiency");
     1033                return false;
     1034#else
     1035                // No great loss
     1036                psErrorClear();
     1037#endif
     1038            }
    9601039        }
    9611040
     
    9631042        psFree (headname);
    9641043        psFree (dataname);
     1044        psFree (deteffname);
    9651045        psFree (tableHeader);
    9661046        break;
     
    9711051    }
    9721052    readout->data_exists = true;
    973     status = psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY, "input sources", sources);
    974     psFree (sources);
     1053
     1054    pmDetections *detections = pmDetectionsAlloc();
     1055    detections->allSources = sources;
     1056    status = psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_DATA_ARRAY, "input sources", detections);
     1057    psFree (detections);
    9751058    return true;
    9761059}
     
    10641147    bool status;
    10651148
    1066     // select the psf of interest
    1067     pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.SOURCES");
    1068     if (!psf) return false;
    1069     return true;
    1070 }
    1071 
    1072    
     1149    // select the detections of interest
     1150    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     1151    if (!detections) return false;
     1152    if (!detections->allSources) return false;
     1153    return true;
     1154}
     1155
     1156
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO.h

    r24694 r27840  
    3636
    3737bool pmSourcesWrite_CMF_PS1_V1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
    38 bool pmSourcesWrite_CMF_PS1_V1_XSRC (psFits *fits, psArray *sources, char *extname, psMetadata *recipe);
    39 bool pmSourcesWrite_CMF_PS1_V1_XFIT (psFits *fits, psArray *sources, char *extname);
     38bool pmSourcesWrite_CMF_PS1_V1_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
     39bool pmSourcesWrite_CMF_PS1_V1_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname);
    4040
    4141bool pmSourcesWrite_CMF_PS1_V2 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
    42 bool pmSourcesWrite_CMF_PS1_V2_XSRC (psFits *fits, psArray *sources, char *extname, psMetadata *recipe);
    43 bool pmSourcesWrite_CMF_PS1_V2_XFIT (psFits *fits, psArray *sources, char *extname);
     42bool pmSourcesWrite_CMF_PS1_V2_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
     43bool pmSourcesWrite_CMF_PS1_V2_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname);
     44
     45bool pmSourcesWrite_CMF_PS1_DV1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
     46bool pmSourcesWrite_CMF_PS1_DV1_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
     47bool pmSourcesWrite_CMF_PS1_DV1_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname);
    4448
    4549bool pmSource_CMF_WritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
     
    5357psArray *pmSourcesRead_CMF_PS1_V1 (psFits *fits, psMetadata *header);
    5458psArray *pmSourcesRead_CMF_PS1_V2 (psFits *fits, psMetadata *header);
     59psArray *pmSourcesRead_CMF_PS1_DV1 (psFits *fits, psMetadata *header);
    5560
    5661bool pmSourcesWritePSFs (psArray *sources, char *filename);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c

    r24401 r27840  
    6969    float magOffset = NAN;
    7070    float exptime   = psMetadataLookupF32 (&status1, fpa->concepts, "FPA.EXPOSURE");
    71     float zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
    72     float zeroptErr = psMetadataLookupF32 (&status2, imageHeader, "ZPT_ERR");
     71    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
     72    if (!isfinite(zeropt)) {
     73        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
     74    }
    7375    if (status1 && status2 && (exptime > 0.0)) {
    7476        magOffset = zeropt + 2.5*log10(exptime);
    7577    }
     78    float zeroptErr = psMetadataLookupF32 (&status2, imageHeader, "ZPT_ERR");
    7679
    7780    // if the sequence is defined, write these in seq order; otherwise
     
    128131            nDOF = model->nDOF;
    129132            nPix = model->nPix;
    130             apRadius = model->radiusFit; // XXX should we really use the fitRadius for aperture Radius?
     133            apRadius = source->apRadius;
    131134            errMag = model->dparams->data.F32[PM_PAR_I0] / model->params->data.F32[PM_PAR_I0];
    132135        } else {
     
    213216
    214217    if (table->n == 0) {
    215         psFitsWriteBlank(fits, header, extname);
     218        if (!psFitsWriteBlank(fits, header, extname)) {
     219            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
     220            psFree(table);
     221            psFree(header);
     222            return false;
     223        }
    216224        psFree(table);
    217225        psFree(header);
     
    221229    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    222230    if (!psFitsWriteTable(fits, header, table, extname)) {
    223         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
     231        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
    224232        psFree(table);
    225233        psFree(header);
     
    308316        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
    309317        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
     318        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
    310319
    311320        // note that some older versions used PSF_PROBABILITY: this was not well defined.
     
    313322        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
    314323        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
    315         model->radiusFit  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
    316324
    317325        source->moments = pmMomentsAlloc ();
     
    331339
    332340// XXX this layout is still the same as PS1_DEV_1
    333 bool pmSourcesWrite_CMF_PS1_V1_XSRC (psFits *fits, psArray *sources, char *extname, psMetadata *recipe)
     341bool pmSourcesWrite_CMF_PS1_V1_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
    334342{
    335343
     
    353361
    354362    // which extended source analyses should we perform?
    355     bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
    356     bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
    357     bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
    358     bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
     363    // bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
     364    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
     365    // bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
     366    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
    359367
    360368    psVector *radialBinsLower = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
     
    396404        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
    397405
     406# if (0)
    398407        // Petrosian measurements
    399408        // XXX insert header data: petrosian ref radius, flux ratio
     
    476485        }
    477486
     487# endif
    478488        psArrayAdd (table, 100, row);
    479489        psFree (row);
     
    481491
    482492    if (table->n == 0) {
    483         psFitsWriteBlank (fits, outhead, extname);
     493        if (!psFitsWriteBlank (fits, outhead, extname)) {
     494            psError(psErrorCodeLast(), false, "Unable to write empty sources.");
     495            psFree(outhead);
     496            psFree(table);
     497            return false;
     498        }
    484499        psFree (outhead);
    485500        psFree (table);
     
    489504    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    490505    if (!psFitsWriteTable (fits, outhead, table, extname)) {
    491         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
     506        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
    492507        psFree (outhead);
    493508        psFree(table);
     
    501516
    502517// XXX this layout is still the same as PS1_DEV_1
    503 bool pmSourcesWrite_CMF_PS1_V1_XFIT (psFits *fits, psArray *sources, char *extname)
     518bool pmSourcesWrite_CMF_PS1_V1_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname)
    504519{
    505520
     
    607622
    608623    if (table->n == 0) {
    609         psFitsWriteBlank (fits, outhead, extname);
     624        if (!psFitsWriteBlank (fits, outhead, extname)) {
     625            psError(psErrorCodeLast(), false, "Unable to write empty sources.");
     626            psFree(outhead);
     627            psFree(table);
     628            return false;
     629        }
    610630        psFree (outhead);
    611631        psFree (table);
     
    615635    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    616636    if (!psFitsWriteTable (fits, outhead, table, extname)) {
    617         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
     637        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
    618638        psFree (outhead);
    619639        psFree(table);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c

    r24403 r27840  
    4545// followed by a zero-size matrix, followed by the table data
    4646
    47 bool pmSourcesWrite_CMF_PS1_V2 (psFits *fits, pmReadout *readout, psArray *sources,
    48                                 psMetadata *imageHeader, psMetadata *tableHeader, char *extname)
     47bool pmSourcesWrite_CMF_PS1_V2 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname)
    4948{
    5049    PS_ASSERT_PTR_NON_NULL(fits, false);
     
    5453    psArray *table;
    5554    psMetadata *row;
    56     int i;
    5755    psF32 *PAR, *dPAR;
    5856    psEllipseAxes axes;
     
    6967    float magOffset = NAN;
    7068    float exptime   = psMetadataLookupF32 (&status1, fpa->concepts, "FPA.EXPOSURE");
    71     float zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
    72     float zeroptErr = psMetadataLookupF32 (&status2, imageHeader, "ZPT_ERR");
     69    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
     70    if (!isfinite(zeropt)) {
     71        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
     72    }
    7373    if (status1 && status2 && (exptime > 0.0)) {
    7474        magOffset = zeropt + 2.5*log10(exptime);
    7575    }
     76    float zeroptErr = psMetadataLookupF32 (&status2, imageHeader, "ZPT_ERR");
    7677
    7778    // if the sequence is defined, write these in seq order; otherwise
     
    8081        pmSource *source = (pmSource *) sources->data[0];
    8182        if (source->seq == -1) {
    82           // let's write these out in S/N order
    83           sources = psArraySort (sources, pmSourceSortBySN);
     83            // let's write these out in S/N order
     84            sources = psArraySort (sources, pmSourceSortBySN);
    8485        } else {
    85           sources = psArraySort (sources, pmSourceSortBySeq);
     86            sources = psArraySort (sources, pmSourceSortBySeq);
    8687        }
    8788    }
     
    9192    // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
    9293    // by the time we call this function, all values should be assigned.  let's use asserts to be sure in some cases.
    93     for (i = 0; i < sources->n; i++) {
     94    for (int i = 0; i < sources->n; i++) {
    9495        pmSource *source = (pmSource *) sources->data[i];
    9596
     
    99100        // generated on Alloc, and would thus be wrong for read in sources.
    100101        if (source->seq == -1) {
    101           source->seq = i;
     102            source->seq = i;
    102103        }
    103104
     
    111112            yPos = PAR[PM_PAR_YPOS];
    112113            if (source->mode & PM_SOURCE_MODE_NONLINEAR_FIT) {
    113               xErr = dPAR[PM_PAR_XPOS];
    114               yErr = dPAR[PM_PAR_YPOS];
     114                xErr = dPAR[PM_PAR_XPOS];
     115                yErr = dPAR[PM_PAR_YPOS];
    115116            } else {
    116               // in linear-fit mode, there is no error on the centroid
    117               xErr = source->peak->dx;
    118               yErr = source->peak->dy;
     117                // in linear-fit mode, there is no error on the centroid
     118                xErr = source->peak->dx;
     119                yErr = source->peak->dy;
    119120            }
    120121            if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX])) {
     
    128129            nDOF = model->nDOF;
    129130            nPix = model->nPix;
    130             apRadius = model->radiusFit; // XXX should we really use the fitRadius for aperture Radius?
     131            apRadius = source->apRadius;
    131132            errMag = model->dparams->data.F32[PM_PAR_I0] / model->params->data.F32[PM_PAR_I0];
    132133        } else {
     
    209210    }
    210211
     212    // XXX why do we make a copy here to be supplemented with the masks?  why not do this in the calling function?
    211213    psMetadata *header = psMetadataCopy(NULL, tableHeader);
    212214    pmSourceMasksHeader(header);
    213215
    214216    if (table->n == 0) {
    215         psFitsWriteBlank(fits, header, extname);
     217        if (!psFitsWriteBlank(fits, header, extname)) {
     218            psError(psErrorCodeLast(), false, "Unable to write blank sources file.");
     219            psFree(table);
     220            psFree(header);
     221            return false;
     222        }
    216223        psFree(table);
    217224        psFree(header);
     
    221228    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    222229    if (!psFitsWriteTable(fits, header, table, extname)) {
    223         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
     230        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
    224231        psFree(table);
    225232        psFree(header);
     
    261268    for (int i = 0; i < numSources; i++) {
    262269        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
     270        if (!row) {
     271            psError(psErrorCodeLast(), false, "Unable to read row %d of sources", i);
     272            psFree(sources);
     273            return NULL;
     274        }
    263275
    264276        pmSource *source = pmSourceAlloc ();
     
    304316        source->peak->dx   = dPAR[PM_PAR_XPOS];
    305317        source->peak->dy   = dPAR[PM_PAR_YPOS];
     318        source->peak->SN   = sqrt(source->peak->flux); // XXX a proxy: various functions sort by peak S/N
    306319
    307320        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
    308321        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
    309322        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
     323        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
    310324
    311325        // note that some older versions used PSF_PROBABILITY: this was not well defined.
     
    313327        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
    314328        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
    315         model->radiusFit  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
    316329
    317330        source->moments = pmMomentsAlloc ();
     
    330343}
    331344
    332 // XXX this layout is still the same as PS1_DEV_1
    333 bool pmSourcesWrite_CMF_PS1_V2_XSRC (psFits *fits, psArray *sources, char *extname, psMetadata *recipe)
     345bool pmSourcesWrite_CMF_PS1_V2_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
    334346{
    335347
     
    340352    psF32 xPos, yPos;
    341353    psF32 xErr, yErr;
     354    int nRow = -1;
    342355
    343356    // create a header to hold the output data
     
    347360    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
    348361
     362    pmChip *chip = readout->parent->parent;
     363    pmFPA  *fpa  = chip->parent;
     364
     365    // zero point corrections
     366    bool status1 = false;
     367    bool status2 = false;
     368    float magOffset = 0.0;
     369    float exptime   = psMetadataLookupF32(&status1, fpa->concepts, "FPA.EXPOSURE");
     370    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
     371    if (!isfinite(zeropt)) {
     372        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
     373    }
     374    if (status1 && status2 && (exptime > 0.0)) {
     375        magOffset = zeropt + 2.5*log10(exptime);
     376    }
     377
    349378    // let's write these out in S/N order
    350379    sources = psArraySort (sources, pmSourceSortBySN);
     
    353382
    354383    // which extended source analyses should we perform?
     384    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
    355385    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
    356     bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
    357     bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
    358     bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
    359 
    360     psVector *radialBinsLower = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
    361     psVector *radialBinsUpper = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
    362     assert (radialBinsLower->n == radialBinsUpper->n);
     386    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
     387    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
     388
     389    psVector *radMin = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
     390    psVector *radMax = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
     391    psAssert (radMin->n == radMax->n, "inconsistent annular bins");
     392
     393    // int nRadialBins = 0;
     394    // if (doAnnuli) {
     395    //  // get the max count of radial bins
     396    //  for (int i = 0; i < sources->n; i++) {
     397    //      pmSource *source = sources->data[i];
     398    //      if (!source->extpars) continue;
     399    //      if (!source->extpars->radProfile ) continue;
     400    //         if (!source->extpars->radProfile->binSB) continue;
     401    //      nRadialBins = PS_MAX(nRadialBins, source->extpars->radProfile->binSB->n);
     402    //  }
     403    // }
    363404
    364405    // we write out all sources, regardless of quality.  the source flags tell us the state
     
    396437        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
    397438
     439        float AxialRatio = NAN;
     440        float AxialTheta = NAN;
     441        pmSourceExtendedPars *extpars = source->extpars;
     442        if (extpars) {
     443            AxialRatio = extpars->axes.minor / extpars->axes.major;
     444            AxialTheta = extpars->axes.theta;
     445        }
     446        psMetadataAdd (row, PS_LIST_TAIL, "F25_ARATIO",       PS_DATA_F32, "Axial Ratio of radial profile",              AxialRatio);
     447        psMetadataAdd (row, PS_LIST_TAIL, "F25_THETA",        PS_DATA_F32, "Angle of radial profile ellipse",                  AxialTheta);
     448
    398449        // Petrosian measurements
    399450        // XXX insert header data: petrosian ref radius, flux ratio
     451        // XXX check flags to see if Pet was measured
    400452        if (doPetrosian) {
    401             pmSourcePetrosianValues *petrosian = source->extpars->petrosian;
    402             if (petrosian) {
    403                 psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       petrosian->mag);
    404                 psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", petrosian->magErr);
    405                 psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          petrosian->rad);
    406                 psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    petrosian->radErr);
     453            pmSourceExtendedPars *extpars = source->extpars;
     454            if (extpars) {
     455                // XXX note that this mag is either calibrated or instrumental depending on existence of zero point
     456                float mag = (extpars->petrosianFlux > 0.0) ? -2.5*log10(extpars->petrosianFlux) + magOffset : NAN; // XXX zero point
     457                float magErr = (extpars->petrosianFlux > 0.0) ? extpars->petrosianFlux / extpars->petrosianFluxErr : NAN; // XXX zero point
     458                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude", mag);
     459                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", magErr);
     460                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius (pix)", extpars->petrosianRadius);
     461                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error (pix)", extpars->petrosianRadiusErr);
     462                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", extpars->petrosianR50);
     463                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)", extpars->petrosianR50Err);
     464                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", extpars->petrosianR90);
     465                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)", extpars->petrosianR90Err);
    407466            } else {
    408467                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
     
    410469                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
    411470                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
     471                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", NAN);
     472                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)",NAN);
     473                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", NAN);
     474                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)",NAN);
    412475            }
    413476        }
    414477
     478# if (0)
    415479        // Kron measurements
    416480        if (doKron) {
     
    445509            }
    446510        }
    447 
    448         // Flux Annuli
     511# endif
     512
     513        // Flux Annuli (if we have extended source measurements, we have these.  only optionally save them)
    449514        if (doAnnuli) {
    450             pmSourceAnnuli *annuli = source->extpars->annuli;
    451             if (annuli) {
    452                 psVector *fluxVal = annuli->flux;
    453                 psVector *fluxErr = annuli->fluxErr;
    454                 psVector *fluxVar = annuli->fluxVar;
    455 
    456                 for (int j = 0; j < fluxVal->n; j++) {
    457                     char name[32];
    458                     sprintf (name, "FLUX_VAL_R_%02d", j);
    459                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", fluxVal->data.F32[j]);
    460                     sprintf (name, "FLUX_ERR_R_%02d", j);
    461                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", fluxErr->data.F32[j]);
    462                     sprintf (name, "FLUX_VAR_R_%02d", j);
    463                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", fluxVar->data.F32[j]);
    464                 }
    465             } else {
    466                 for (int j = 0; j < radialBinsLower->n; j++) {
    467                     char name[32];
    468                     sprintf (name, "FLUX_VAL_R_%02d", j);
    469                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", NAN);
    470                     sprintf (name, "FLUX_ERR_R_%02d", j);
    471                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", NAN);
    472                     sprintf (name, "FLUX_VAR_R_%02d", j);
    473                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", NAN);
    474                 }
    475             }
    476         }
    477 
    478         psArrayAdd (table, 100, row);
    479         psFree (row);
    480     }
    481 
     515            psVector *radSB   = psVectorAlloc(radMin->n, PS_TYPE_F32);
     516            psVector *radFlux = psVectorAlloc(radMin->n, PS_TYPE_F32);
     517            psVector *radFill = psVectorAlloc(radMin->n, PS_TYPE_F32);
     518            psVectorInit (radSB, NAN);
     519            psVectorInit (radFlux, NAN);
     520            psVectorInit (radFill, NAN);
     521            if (!source->extpars) goto empty_annuli;
     522            if (!source->extpars->radProfile) goto empty_annuli;
     523            if (!source->extpars->radProfile->binSB) goto empty_annuli;
     524            psAssert (source->extpars->radProfile->binSum, "programming error");
     525            psAssert (source->extpars->radProfile->binFill, "programming error");
     526            psAssert (source->extpars->radProfile->binSB->n <= radFlux->n, "inconsistent vector lengths");
     527            psAssert (source->extpars->radProfile->binSum->n <= radFlux->n, "inconsistent vector lengths");
     528            psAssert (source->extpars->radProfile->binFill->n <= radFlux->n, "inconsistent vector lengths");
     529
     530            // copy the data from fluxVal (which is not guaranteed to be the full length) to radFlux
     531            for (int j = 0; j < source->extpars->radProfile->binSB->n; j++) {
     532                radSB->data.F32[j]   = source->extpars->radProfile->binSB->data.F32[j];
     533                radFlux->data.F32[j] = source->extpars->radProfile->binSum->data.F32[j];
     534                radFill->data.F32[j] = source->extpars->radProfile->binFill->data.F32[j];
     535            }
     536
     537        empty_annuli:
     538            psMetadataAdd (row, PS_LIST_TAIL, "PROF_SB", PS_DATA_VECTOR, "mean surface brightness annuli", radSB);
     539            psMetadataAdd (row, PS_LIST_TAIL, "PROF_FLUX", PS_DATA_VECTOR, "flux within annuli", radFlux);
     540            psMetadataAdd (row, PS_LIST_TAIL, "PROF_FILL", PS_DATA_VECTOR, "fill factor of annuli", radFill);
     541            psFree (radSB);
     542            psFree (radFlux);
     543            psFree (radFill);
     544        }
     545        if (nRow < 0) {
     546            nRow = row->list->n;
     547        } else {
     548            psAssert (nRow == row->list->n, "inconsistent row lengths");
     549        }
     550        psArrayAdd (table, 100, row);
     551        psFree (row);
     552    }
     553   
    482554    if (table->n == 0) {
    483         psFitsWriteBlank (fits, outhead, extname);
    484         psFree (outhead);
    485         psFree (table);
    486         return true;
    487     }
    488 
     555        if (!psFitsWriteBlank (fits, outhead, extname)) {
     556            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
     557            psFree(outhead);
     558            psFree(table);
     559            return false;
     560        }
     561        psFree (outhead);
     562        psFree (table);
     563        return true;
     564    }
     565   
    489566    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    490567    if (!psFitsWriteTable (fits, outhead, table, extname)) {
    491         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
    492         psFree (outhead);
    493         psFree(table);
    494         return false;
     568        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
     569        psFree (outhead);
     570    psFree(table);
     571    return false;
    495572    }
    496573    psFree (outhead);
    497574    psFree (table);
    498 
     575   
    499576    return true;
    500577}
    501578
    502579// XXX this layout is still the same as PS1_DEV_1
    503 bool pmSourcesWrite_CMF_PS1_V2_XFIT (psFits *fits, psArray *sources, char *extname)
     580bool pmSourcesWrite_CMF_PS1_V2_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname)
    504581{
    505582
     
    607684
    608685    if (table->n == 0) {
    609         psFitsWriteBlank (fits, outhead, extname);
     686        if (!psFitsWriteBlank (fits, outhead, extname)) {
     687            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
     688            psFree(outhead);
     689            psFree(table);
     690            return false;
     691        }
    610692        psFree (outhead);
    611693        psFree (table);
     
    615697    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    616698    if (!psFitsWriteTable (fits, outhead, table, extname)) {
    617         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
     699        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
    618700        psFree (outhead);
    619701        psFree(table);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO_MatchedRefs.c

    r24801 r27840  
    6767
    6868    if (!table) {
    69         table = psArrayAllocEmpty (0x1000);
    70         pmFPAview *view = pmFPAviewAlloc (0);
     69        table = psArrayAllocEmpty (0x1000);
     70        pmFPAview *view = pmFPAviewAlloc (0);
    7171
    72         // this loop selects the matched stars for all chips
    73         while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
    74             psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
    75             if (!chip->process || !chip->file_exists) continue;
     72        // this loop selects the matched stars for all chips
     73        while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
     74            psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
     75            if (!chip->process || !chip->file_exists) continue;
    7676
    77             char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
     77            char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
    7878
    79             while ((cell = pmFPAviewNextCell (view, fpa, 1)) != NULL) {
    80                 psTrace ("psastro", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
    81                 if (!cell->process || !cell->file_exists) continue;
     79            while ((cell = pmFPAviewNextCell (view, fpa, 1)) != NULL) {
     80                psTrace ("psastro", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
     81                if (!cell->process || !cell->file_exists) continue;
    8282
    83                 // process each of the readouts
    84                 // XXX there can only be one readout per chip, right?
    85                 while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
    86                     if (! readout->data_exists) continue;
     83                // process each of the readouts
     84                // XXX there can only be one readout per chip, right?
     85                while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
     86                    if (! readout->data_exists) continue;
    8787
    88                     // select the raw objects for this readout
    89                     psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
    90                     if (rawstars == NULL) continue;
     88                    // select the raw objects for this readout
     89                    psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
     90                    if (rawstars == NULL) continue;
    9191
    92                     // select the raw objects for this readout
    93                     psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
    94                     if (refstars == NULL) continue;
    95                     psTrace ("psastro", 4, "Trying %ld refstars\n", refstars->n);
     92                    // select the raw objects for this readout
     93                    psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
     94                    if (refstars == NULL) continue;
     95                    psTrace ("psastro", 4, "Trying %ld refstars\n", refstars->n);
    9696
    9797# if (0)
    98                     // XXX test
    99                     FILE *outfile = fopen ("refstars.dat", "w");
    100                     assert (outfile);
    101                     for (int nn = 0; nn < refstars->n; nn++) {
    102                         pmAstromObj *ref = refstars->data[nn];
    103                         fprintf (outfile, "%lf %lf\n", ref->sky->r*PS_DEG_RAD, ref->sky->d*PS_DEG_RAD);
    104                     }
    105                     fclose (outfile);
     98                    // XXX test
     99                    FILE *outfile = fopen ("refstars.dat", "w");
     100                    assert (outfile);
     101                    for (int nn = 0; nn < refstars->n; nn++) {
     102                        pmAstromObj *ref = refstars->data[nn];
     103                        fprintf (outfile, "%lf %lf\n", ref->sky->r*PS_DEG_RAD, ref->sky->d*PS_DEG_RAD);
     104                    }
     105                    fclose (outfile);
    106106# endif
    107107
    108                     psArray *matches = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
    109                     if (matches == NULL) continue;
     108                    psArray *matches = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
     109                    if (matches == NULL) continue;
    110110
    111                     for (int i = 0; i < matches->n; i++) {
    112                         pmAstromMatch *match = matches->data[i];
     111                    for (int i = 0; i < matches->n; i++) {
     112                        pmAstromMatch *match = matches->data[i];
    113113
    114                         pmAstromObj *raw = rawstars->data[match->raw];
    115                         pmAstromObj *ref = refstars->data[match->ref];
     114                        pmAstromObj *raw = rawstars->data[match->raw];
     115                        pmAstromObj *ref = refstars->data[match->ref];
    116116
    117                         psMetadata *row = psMetadataAlloc ();
    118                         psMetadataAdd (row, PS_LIST_TAIL, "RA",       PS_DATA_F64, "right ascension (deg, J2000)", PM_DEG_RAD*ref->sky->r);
    119                         psMetadataAdd (row, PS_LIST_TAIL, "DEC",      PS_DATA_F64, "declination (deg, J2000)",     PM_DEG_RAD*ref->sky->d);
    120                         psMetadataAdd (row, PS_LIST_TAIL, "X_CHIP",   PS_DATA_F32, "x coord on chip",              raw->chip->x);
    121                         psMetadataAdd (row, PS_LIST_TAIL, "Y_CHIP",   PS_DATA_F32, "y coord on chip",              raw->chip->y);
    122                         psMetadataAdd (row, PS_LIST_TAIL, "X_FPA",    PS_DATA_F32, "x coord on focal plane",       raw->FP->x);
    123                         psMetadataAdd (row, PS_LIST_TAIL, "Y_FPA",    PS_DATA_F32, "y coord on focal plane",       raw->FP->y);
    124                         psMetadataAdd (row, PS_LIST_TAIL, "MAG_INST", PS_DATA_F32, "instrumental magnitude",       raw->Mag);
    125                         psMetadataAdd (row, PS_LIST_TAIL, "MAG_REF",  PS_DATA_F32, "reference star magnitude",     ref->Mag);
    126                         psMetadataAdd (row, PS_LIST_TAIL, "COLOR_REF",PS_DATA_F32, "reference star color",         ref->Color);
    127                         psMetadataAdd (row, PS_LIST_TAIL, "CHIP_ID",  PS_DATA_STRING, "chip identifier",           chipName);
    128                         // XXX need to add the reference color, but this needs getstar / dvo.photcodes for the reference to be refined.
     117                        psMetadata *row = psMetadataAlloc ();
     118                        psMetadataAdd (row, PS_LIST_TAIL, "RA",       PS_DATA_F64, "right ascension (deg, J2000)", PM_DEG_RAD*ref->sky->r);
     119                        psMetadataAdd (row, PS_LIST_TAIL, "DEC",      PS_DATA_F64, "declination (deg, J2000)",     PM_DEG_RAD*ref->sky->d);
     120                        psMetadataAdd (row, PS_LIST_TAIL, "X_CHIP",   PS_DATA_F32, "x coord on chip",              raw->chip->x);
     121                        psMetadataAdd (row, PS_LIST_TAIL, "Y_CHIP",   PS_DATA_F32, "y coord on chip",              raw->chip->y);
     122                        psMetadataAdd (row, PS_LIST_TAIL, "X_CHIP_FIT",PS_DATA_F32, "x fitted coord on chip",      ref->chip->x);
     123                        psMetadataAdd (row, PS_LIST_TAIL, "Y_CHIP_FIT",PS_DATA_F32, "y fitted coord on chip",      ref->chip->y);
     124                        psMetadataAdd (row, PS_LIST_TAIL, "X_FPA",    PS_DATA_F32, "x coord on focal plane",       raw->FP->x);
     125                        psMetadataAdd (row, PS_LIST_TAIL, "Y_FPA",    PS_DATA_F32, "y coord on focal plane",       raw->FP->y);
     126                        psMetadataAdd (row, PS_LIST_TAIL, "MAG_INST", PS_DATA_F32, "instrumental magnitude",       raw->Mag);
     127                        psMetadataAdd (row, PS_LIST_TAIL, "MAG_REF",  PS_DATA_F32, "reference star magnitude",     ref->Mag);
     128                        psMetadataAdd (row, PS_LIST_TAIL, "COLOR_REF",PS_DATA_F32, "reference star color",         ref->Color);
     129                        psMetadataAdd (row, PS_LIST_TAIL, "CHIP_ID",  PS_DATA_STRING, "chip identifier",           chipName);
     130                        // XXX need to add the reference color, but this needs getstar / dvo.photcodes for the reference to be refined.
    129131
    130                         psArrayAdd (table, 100, row);
    131                         psFree (row);
    132                     }
    133                 }
    134             }
    135         }
    136         psFree (view);
     132                        psArrayAdd (table, 100, row);
     133                        psFree (row);
     134                    }
     135                }
     136            }
     137        }
     138        psFree (view);
    137139
    138         if (table->n == 0) {
    139             psFree(table);
    140             return true;
    141         }
     140        if (table->n == 0) {
     141            psFree(table);
     142            return true;
     143        }
    142144    }
    143145
    144146    if (!psFitsWriteTable(fits, NULL, table, "MATCHED_REFS")) {
    145         psError(PS_ERR_IO, false, "writing MATCHED_REFS\n");
     147        psError(psErrorCodeLast(), false, "writing MATCHED_REFS\n");
    146148        psFree(table);
    147149        return false;
     
    161163
    162164    // try find the MATCHED_REFS extension.  if non-existent, note that we tried, and move on.
    163     if (!psFitsMoveExtName (fits, "MATCHED_REFS")) {
    164         psMetadataAddBool (fpa->analysis, PS_LIST_TAIL, "READ.REFMATCH", PS_META_REPLACE, "attempted to read MATCHED_REFS", true);
    165         return true;
     165    // It is not an error to lack this entry -- psFitsMoveExtNameClean does not raise an error
     166    if (!psFitsMoveExtNameClean (fits, "MATCHED_REFS")) {
     167        psMetadataAddBool (fpa->analysis, PS_LIST_TAIL, "READ.REFMATCH", PS_META_REPLACE, "attempted to read MATCHED_REFS", true);
     168        return true;
    166169    }
    167    
     170
    168171    // We get the size of the table, and allocate the array of sources first because the table
    169172    // is large and ephemeral --- when the table gets blown away, whatever is allocated after
     
    175178    for (int i = 0; i < numRows; i++) {
    176179        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
    177         rows->data[i] = row;
     180        if (!row) {
     181            psError(psErrorCodeLast(), false, "Unable to read row %d of matched references.", i);
     182            psFree(rows);
     183            return false;
     184        }
     185        rows->data[i] = row;
    178186    }
    179187
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO_PS1_CAL_0.c

    r21516 r27840  
    282282        pmPSF_AxesToModel (PAR, axes);
    283283
    284         float lflux = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
    285         float flux = (isfinite(lflux)) ? pow(10.0, -0.4*lflux) : NAN;
    286         source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], flux, PM_PEAK_LONE);
    287         source->peak->flux = flux;
     284        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
     285        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
     286
     287        source->peak       = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
     288        source->peak->flux = peakFlux;
     289        source->peak->dx   = dPAR[PM_PAR_XPOS];
     290        source->peak->dy   = dPAR[PM_PAR_YPOS];
    288291
    289292        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
     
    349352
    350353    // which extended source analyses should we perform?
    351     bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
    352     bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
    353     bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
    354     bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
     354    // bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
     355    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
     356    // bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
     357    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
    355358
    356359    psVector *radialBinsLower = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
     
    410413        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
    411414
     415# if (0)
    412416        // Petrosian measurements
    413417        // XXX insert header data: petrosian ref radius, flux ratio
     
    501505            }
    502506        }
     507# endif
    503508
    504509        psArrayAdd (table, 100, row);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO_PS1_DEV_0.c

    r20937 r27840  
    208208        pmPSF_AxesToModel (PAR, axes);
    209209
    210         float lflux = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
    211         float flux = (isfinite(lflux)) ? pow(10.0, -0.4*lflux) : NAN;
    212         source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], flux, PM_PEAK_LONE);
    213         source->peak->flux = flux;
     210        float peakMag = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
     211        float peakFlux = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
     212
     213        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
     214        source->peak->flux = peakFlux;
     215        source->peak->dx   = dPAR[PM_PAR_XPOS];
     216        source->peak->dy   = dPAR[PM_PAR_YPOS];
    214217
    215218        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO_PS1_DEV_1.c

    r21516 r27840  
    252252        pmPSF_AxesToModel (PAR, axes);
    253253
    254         float lflux = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
    255         float flux = (isfinite(lflux)) ? pow(10.0, -0.4*lflux) : NAN;
    256         source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], flux, PM_PEAK_LONE);
    257         source->peak->flux = flux;
     254        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
     255        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
     256
     257        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
     258        source->peak->flux = peakFlux;
     259        source->peak->dx   = dPAR[PM_PAR_XPOS];
     260        source->peak->dy   = dPAR[PM_PAR_YPOS];
    258261
    259262        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
     
    300303
    301304    // which extended source analyses should we perform?
    302     bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
    303     bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
    304     bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
    305     bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
     305    // bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
     306    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
     307    // bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
     308    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
    306309
    307310    psVector *radialBinsLower = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
     
    343346        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
    344347
     348// XXX disable these outputs until we clean up the names
     349# if (0)
    345350        // Petrosian measurements
    346351        // XXX insert header data: petrosian ref radius, flux ratio
     
    422427            }
    423428        }
     429# endif
    424430
    425431        psArrayAdd (table, 100, row);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO_RAW.c

    r24581 r27840  
    119119                 logChi, logChiNorm,
    120120                 source[0].peak->SN,
    121                  model[0].radiusFit,
     121                 source[0].apRadius,
    122122                 source[0].pixWeight,
    123123                 model[0].nDOF,
     
    181181                 log10(model[0].chisqNorm/model[0].nDOF),
    182182                 source[0].peak->SN,
    183                  model[0].radiusFit,
     183                 source[0].apRadius,
    184184                 source[0].pixWeight,
    185185                 model[0].nDOF,
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceIO_SMPDATA.c

    r20937 r27840  
    184184        pmPSF_AxesToModel (PAR, axes);
    185185
    186 
    187186        source->psfMag = psMetadataLookupF32 (&status, row, "MAG_RAW") - ZERO_POINT;
    188187        source->extMag = psMetadataLookupF32 (&status, row, "MAG_GAL") - ZERO_POINT;
     
    198197
    199198        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
     199        source->peak->flux = peakFlux;
     200
    200201        sources->data[i] = source;
    201202    }
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceMatch.c

    r24262 r27840  
    88
    99#include "pmSource.h"
     10#include "pmErrorCodes.h"
    1011
    1112#include "pmSourceMatch.h"
     
    112113    psFree(match->mag);
    113114    psFree(match->magErr);
     115    psFree(match->x);
     116    psFree(match->y);
    114117    psFree(match->image);
    115118    psFree(match->index);
     119    psFree(match->mask);
    116120}
    117121
     
    124128    match->mag = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
    125129    match->magErr = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
     130    match->x = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
     131    match->y = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
    126132    match->image = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_U32);
    127133    match->index = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_U32);
     
    133139void pmSourceMatchAdd(pmSourceMatch *match, // Match data
    134140                      float mag, float magErr, // Magnitude and error
     141                      float x, float y,        // Position
    135142                      int image, // Image index
    136143                      int index // Source index
     
    141148    match->mag = psVectorExtend(match->mag, match->mag->nalloc, 1);
    142149    match->magErr = psVectorExtend(match->magErr, match->magErr->nalloc, 1);
     150    match->x = psVectorExtend(match->x, match->x->nalloc, 1);
     151    match->y = psVectorExtend(match->y, match->y->nalloc, 1);
    143152    match->image = psVectorExtend(match->image, match->image->nalloc, 1);
    144153    match->index = psVectorExtend(match->index, match->index->nalloc, 1);
     
    147156    match->mag->data.F32[num] = mag;
    148157    match->magErr->data.F32[num] = magErr;
     158    match->x->data.F32[num] = x;
     159    match->y->data.F32[num] = y;
    149160    match->image->data.S32[num] = image;
    150161    match->index->data.S32[num] = index;
     
    172183    for (int i = 0; i < numImages; i++) {
    173184        psArray *sources = sourceArrays->data[i]; // Sources in image
    174         if (!sources) {
     185        if (!sources || sources->n == 0) {
    175186            continue;
    176187        }
     
    192203                pmSourceMatch *match = pmSourceMatchAlloc(); // Match data
    193204                pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j],
    194                                  i, indices->data.S32[j]);
     205                                 xImage->data.F32[j], yImage->data.F32[j], i, indices->data.S32[j]);
    195206                matches->data[j] = match;
    196207            }
     
    212223                pmSourceMatch *match = pmSourceMatchAlloc(); // Match data
    213224                pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j],
    214                                  i, indices->data.S32[j]);
     225                                 xImage->data.F32[j], yImage->data.F32[j], i, indices->data.S32[j]);
    215226                matches->data[k] = match;
    216227            }
     
    221232        } else {
    222233            // Match with the master list
    223             psTree *tree = psTreePlant(2, SOURCES_MAX_LEAF, xMaster, yMaster); // kd Tree with sources
     234            psTree *tree = psTreePlant(2, SOURCES_MAX_LEAF, PS_TREE_EUCLIDEAN, xMaster, yMaster); // kd Tree
    224235            long numMatch = 0;          // Number of matches
    225236
     
    238249                    pmSourceMatch *match = matches->data[index]; // Match data
    239250                    pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j],
    240                                      i, indices->data.S32[j]);
     251                                     xImage->data.F32[j], yImage->data.F32[j], i, indices->data.S32[j]);
    241252                    numMatch++;
    242253                } else {
     
    244255                    pmSourceMatch *match = pmSourceMatchAlloc(); // Match data
    245256                    pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j],
    246                                      i, indices->data.S32[j]);
     257                                     xImage->data.F32[j], yImage->data.F32[j], i, indices->data.S32[j]);
    247258                    xMaster->data.F32[numMaster] = xImage->data.F32[j];
    248259                    yMaster->data.F32[numMaster] = yImage->data.F32[j];
     
    262273        psFree(magImage);
    263274        psFree(magErrImage);
    264     }
     275        psFree(indices);
     276    }
     277
     278    psFree(xMaster);
     279    psFree(yMaster);
     280    psFree(boundsMaster);
    265281
    266282    if (cullSingles) {
     
    304320        pmSourceMatch *match = matches->data[i]; // Match of interest
    305321        for (int j = 0; j < match->num && !source; j++) {
    306             if (!isfinite(match->mag->data.F32[j]) || !isfinite(match->magErr->data.F32[j])) {
     322            if (!isfinite(match->mag->data.F32[j]) || !isfinite(match->magErr->data.F32[j]) ||
     323                !isfinite(match->x->data.F32[j]) || !isfinite(match->y->data.F32[j])) {
    307324                continue;
    308325            }
     
    365382        double star = 0.0, starErr = 0.0; // Accumulators for star
    366383        for (int j = 0; j < match->num; j++) {
    367             if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     384            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_PHOT) {
    368385                continue;
    369386            }
     
    396413        pmSourceMatch *match = matches->data[i]; // Matched stars
    397414        for (int j = 0; j < match->num; j++) {
    398             if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     415            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_PHOT) {
    399416                continue;
    400417            }
     
    424441        pmSourceMatch *match = matches->data[i]; // Matched stars
    425442        for (int j = 0; j < match->num; j++) {
    426             if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     443            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_PHOT) {
    427444                continue;
    428445            }
     
    543560        pmSourceMatch *match = matches->data[i]; // Matched stars
    544561        for (int j = 0; j < match->num; j++) {
    545             if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     562            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_PHOT) {
    546563                continue;
    547564            }
     
    564581                if (PS_SQR(dev) > starClip * (PS_SQR(magErr) + sysErr2)) {
    565582                    numRejected++;
    566                     match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xFF;
     583                    match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] |= PM_SOURCE_MATCH_MASK_PHOT;
    567584                }
    568585            }
     
    599616    int numImages = zp->n;              // Number of images
    600617    int numStars = matches->n;          // Number of stars
     618    psVector *badImage = psVectorAlloc(numImages, PS_TYPE_U8); // Bad image?
     619    psVectorInit(badImage, 0);
     620
     621    // Check for data integrity
     622    {
     623        psVector *num = psVectorAlloc(numImages, PS_TYPE_S32); // Number of stars per image
     624        psVectorInit(num, 0);
     625        for (int i = 0; i < numStars; i++) {
     626            pmSourceMatch *match = matches->data[i]; // Matched stars
     627            for (int j = 0; j < match->num; j++) {
     628                int index = match->image->data.U32[j]; // Image index
     629                psAssert(index >= 0 && index < numImages, "Bad index: %d", index);
     630                num->data.S32[index]++;
     631            }
     632        }
     633        int numGood = 0;                // Number of good images
     634        for (int i = 0; i < numImages; i++) {
     635            if (num->data.S32[i] == 0 || !isfinite(zp->data.F32[i])) {
     636                badImage->data.U8[i] = 0xFF;
     637                continue;
     638            }
     639            numGood++;
     640        }
     641        psFree(num);
     642        if (numGood == 0) {
     643            psError(PM_ERR_DATA, true, "No images with good stars.");
     644            psFree(badImage);
     645            return false;
     646        }
     647    }
     648
    601649    psVector *trans = psVectorAlloc(numImages, PS_TYPE_F32); // Transparencies for each image, magnitudes
    602650    psVectorInit(trans, 0.0);
    603651    psVector *photo = psVectorAlloc(numImages, PS_TYPE_U8); // Photometric determination for each image
    604652    psVectorInit(photo, 0);
    605     psVector *badImage = psVectorAlloc(numImages, PS_TYPE_U8); // Bad image?
    606     psVectorInit(badImage, 0);
    607653    psVector *stars = psVectorAlloc(numStars, PS_TYPE_F32); // Magnitudes for each star
    608654
     
    627673            return NULL;
    628674        }
    629         psTrace("psModules.objects", 3, "Pass 1: Determined %d/%d are photometric", numPhoto, numImages);
     675        psTrace("psModules.objects", 3, "Pass 1: Determined %d/%d are photometric\n", numPhoto, numImages);
    630676
    631677        fracRej = sourceMatchRelphotReject(trans, stars, matches, zp, photo, badImage, rej1, sys1);
    632         psTrace("psModules.objects", 3, "Pass 1: %f%% of measurements rejected", fracRej * 100);
     678        psTrace("psModules.objects", 3, "Pass 1: %f%% of measurements rejected\n", fracRej * 100);
    633679
    634680        chi2 = sourceMatchRelphotIterate(trans, stars, badImage, matches, zp, photo, sys1);
     
    649695            return NULL;
    650696        }
    651         psTrace("psModules.objects", 3, "Pass 2: Determined %d/%d are photometric", numPhoto, numImages);
     697        psTrace("psModules.objects", 3, "Pass 2: Determined %d/%d are photometric\n", numPhoto, numImages);
    652698
    653699        fracRej = sourceMatchRelphotReject(trans, stars, matches, zp, photo, badImage, rej2, sys2);
    654         psTrace("psModules.objects", 3, "Pass 2: %f%% of measurements rejected", fracRej * 100);
     700        psTrace("psModules.objects", 3, "Pass 2: %f%% of measurements rejected\n", fracRej * 100);
    655701
    656702        chi2 = sourceMatchRelphotIterate(trans, stars, badImage, matches, zp, photo, sys2);
     
    668714    return trans;
    669715}
     716
     717
     718// Iterate on the star positions and image shifts
     719// Returns the solution chi^2
     720static float sourceMatchRelastroIterate(psVector *xShift, psVector *yShift, // Shift for image
     721                                        psVector *xStar, psVector *yStar,   // Position for star
     722                                        const psArray *matches // Array of matches
     723    )
     724{
     725    psAssert(matches, "Need list of matches");
     726
     727    int numImages = xShift->n;          // Number of images
     728    int numStars = matches->n;          // Number of stars
     729
     730    psAssert(xShift && xShift->type.type == PS_TYPE_F32 && yShift && yShift->type.type == PS_TYPE_F32,
     731             "Need shifts");
     732    psAssert(yShift->n == numImages, "Not enough shifts: %ld\n", yShift->n);
     733    psAssert(xStar && xStar->type.type == PS_TYPE_F32 && yStar && yStar->type.type == PS_TYPE_F32,
     734             "Need star positions");
     735    psAssert(xStar->n == numStars && yStar->n == numStars, "Not enough stars\n");
     736
     737    // Solve the star positions
     738    psVectorInit(xStar, NAN);
     739    psVectorInit(yStar, NAN);
     740    psVector *starMask = psVectorAlloc(numStars, PS_TYPE_U8); // Mask for stars
     741    psVectorInit(starMask, 0xFF);
     742    int numGoodStars = 0;               // Number of stars with good measurements
     743    for (int i = 0; i < numStars; i++) {
     744        pmSourceMatch *match = matches->data[i]; // Matched stars
     745        int numMeasurements = 0;        // Number of unmasked measurements for star
     746        double xSum = 0.0, ySum = 0.0;  // Accumulators for star
     747        for (int j = 0; j < match->num; j++) {
     748            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_ASTRO) {
     749                continue;
     750            }
     751            numMeasurements++;
     752            int index = match->image->data.U32[j]; // Image index
     753
     754            xSum += match->x->data.F32[j] - xShift->data.F32[index];
     755            ySum += match->y->data.F32[j] - yShift->data.F32[index];
     756        }
     757        if (numMeasurements > 1) {
     758            // It's only a good star (contributing to the chi^2) if there's more than 1 measurement
     759            numGoodStars++;
     760            xStar->data.F32[i] = xSum / numMeasurements;
     761            yStar->data.F32[i] = ySum / numMeasurements;
     762            starMask->data.U8[i] = 0;
     763        }
     764    }
     765
     766    // Solve for the shifts
     767    psVectorInit(xShift, 0.0);
     768    psVectorInit(yShift, 0.0);
     769    psVector *num = psVectorAlloc(numImages, PS_TYPE_S32);    // Number of stars
     770    psVectorInit(num, 0);
     771    for (int i = 0; i < numStars; i++) {
     772        if (starMask->data.U8[i]) {
     773            continue;
     774        }
     775        pmSourceMatch *match = matches->data[i]; // Matched stars
     776        for (int j = 0; j < match->num; j++) {
     777            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_ASTRO) {
     778                continue;
     779            }
     780            int index = match->image->data.U32[j]; // Image index
     781
     782            xShift->data.F32[index] += match->x->data.F32[j] - xStar->data.F32[i];
     783            yShift->data.F32[index] += match->y->data.F32[j] - yStar->data.F32[i];
     784            num->data.S32[index]++;
     785        }
     786    }
     787    for (int i = 0; i < numImages; i++) {
     788        xShift->data.F32[i] /= num->data.S32[i];
     789        yShift->data.F32[i] /= num->data.S32[i];
     790        psTrace("psModules.objects", 3, "Shift for image %d: %f,%f\n",
     791                i, xShift->data.F32[i], yShift->data.F32[i]);
     792    }
     793    psFree(num);
     794
     795    // Once more through to evaluate chi^2
     796    float chi2 = 0.0;                   // chi^2 for iteration
     797    int dof = 0;                        // Degrees of freedom
     798    for (int i = 0; i < numStars; i++) {
     799        pmSourceMatch *match = matches->data[i]; // Matched stars
     800        if (starMask->data.U8[i]) {
     801            continue;
     802        }
     803        for (int j = 0; j < match->num; j++) {
     804            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     805                continue;
     806            }
     807
     808            int index = match->image->data.U32[j]; // Image index
     809            float dx = match->x->data.F32[j] - xShift->data.F32[index] - xStar->data.F32[i];
     810            float dy = match->y->data.F32[j] - yShift->data.F32[index] - yStar->data.F32[i];
     811
     812            chi2 += PS_SQR(dx) + PS_SQR(dy);
     813            dof++;
     814        }
     815    }
     816    dof -= numGoodStars + numImages;
     817    chi2 /= dof;
     818
     819    return chi2;
     820}
     821
     822// Reject star measurements
     823// Returns the fraction of measurements that were rejected
     824static float sourceMatchRelastroReject(const psVector *xShift, const psVector *yShift, // Shifts for each image
     825                                       const psVector *xStar, const psVector *yStar, // Positions for each star
     826                                       const psArray *matches, // Array of matches
     827                                       float chi2,             // chi^2 from fit
     828                                       float rej               // Rejection threshold
     829                                )
     830{
     831    psAssert(matches, "Need list of matches");
     832
     833    int numImages = xShift->n;          // Number of images
     834    int numStars = matches->n;          // Number of stars
     835
     836    psAssert(xShift && xShift->type.type == PS_TYPE_F32 && yShift && yShift->type.type == PS_TYPE_F32,
     837             "Need shifts");
     838    psAssert(yShift->n == numImages, "Not enough shifts: %ld\n", yShift->n);
     839    psAssert(xStar && xStar->type.type == PS_TYPE_F32 && yStar && yStar->type.type == PS_TYPE_F32,
     840             "Need star positions");
     841    psAssert(xStar->n == numStars && yStar->n == numStars, "Not enough stars\n");
     842
     843    int numRejected = 0;                // Number rejected
     844    int numMeasurements = 0;            // Number of measurements
     845
     846    float thresh = PS_SQR(rej) * chi2;    // Threshold for rejection
     847
     848    for (int i = 0; i < numStars; i++) {
     849        pmSourceMatch *match = matches->data[i]; // Matched stars
     850        for (int j = 0; j < match->num; j++) {
     851            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_ASTRO) {
     852                continue;
     853            }
     854            numMeasurements++;
     855            int index = match->image->data.U32[j]; // Image index
     856
     857            float dx = match->x->data.F32[j] - xShift->data.F32[index] - xStar->data.F32[i];
     858            float dy = match->y->data.F32[j] - yShift->data.F32[index] - yStar->data.F32[i];
     859
     860            if (PS_SQR(dx) + PS_SQR(dy) > thresh) {
     861                numRejected++;
     862                match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] |= PM_SOURCE_MATCH_MASK_ASTRO;
     863            }
     864        }
     865    }
     866
     867    return (float)numRejected / (float)numMeasurements;
     868}
     869
     870psArray *pmSourceMatchRelastro(const psArray *matches, // Array of matches
     871                               int numImages,          // Number of images
     872                               float tol, // Relative tolerance for convergence
     873                               int iter1, // Number of iterations for pass 1
     874                               float rej1, // Limit on rejection between iterations for pass 1
     875                               int iter2, // Number of iterations for pass 2
     876                               float rej2, // Limit on rejection between iterations for pass 2
     877                               float rejLimit // Limit on rejection between iterations
     878    )
     879{
     880    PS_ASSERT_ARRAY_NON_NULL(matches, NULL);
     881
     882    int numStars = matches->n;          // Number of stars
     883    psVector *xShift = psVectorAlloc(numImages, PS_TYPE_F32); // x shift for each image
     884    psVector *yShift = psVectorAlloc(numImages, PS_TYPE_F32); // y shift for each image
     885    psVectorInit(xShift, 0.0);
     886    psVectorInit(yShift, 0.0);
     887    psVector *xStar = psVectorAlloc(numStars, PS_TYPE_F32); // x position for each star
     888    psVector *yStar = psVectorAlloc(numStars, PS_TYPE_F32); // y position for each star
     889
     890    float chi2 = sourceMatchRelastroIterate(xShift, yShift, xStar, yStar, matches); // chi^2 for solution
     891    psTrace("psModules.objects", 1, "Initial: chi^2 = %f\n", chi2);
     892    float lastChi2 = INFINITY;          // chi^2 on last iteration
     893    float fracRej = INFINITY;           // Fraction of measurements rejected
     894
     895    // In the first passes, the shifts are not well deteremined: use high systematic error and
     896    // rejection thresholds
     897    for (int i = 0; i < iter1; i++) {
     898        fracRej = sourceMatchRelastroReject(xShift, yShift, xStar, yStar, matches, chi2, rej1);
     899        psTrace("psModules.objects", 3, "Pass 1: %f%% of measurements rejected\n", fracRej * 100);
     900
     901        chi2 = sourceMatchRelastroIterate(xShift, yShift, xStar, yStar, matches);
     902        psTrace("psModules.objects", 1, "Pass 1: iter = %d: chi^2 = %f rejected = %f\n", i, chi2, fracRej);
     903    }
     904
     905    for (int i = 0; i < iter2 && (fabsf(lastChi2 - chi2) > tol * chi2 || fracRej > rejLimit); i++) {
     906        lastChi2 = chi2;
     907
     908        fracRej = sourceMatchRelastroReject(xShift, yShift, xStar, yStar, matches, chi2, rej2);
     909        psTrace("psModules.objects", 3, "Pass 2: %f%% of measurements rejected\n", fracRej * 100);
     910
     911        chi2 = sourceMatchRelastroIterate(xShift, yShift, xStar, yStar, matches);
     912        psTrace("psModules.objects", 1, "Pass 2: iter = %d: chi^2 = %f rejected = %f\n", i, chi2, fracRej);
     913    }
     914
     915    psFree(xStar);
     916    psFree(yStar);
     917
     918    if (fabsf(lastChi2 - chi2) > tol * chi2 || fracRej > rejLimit) {
     919        psWarning("Unable to converge to relphot solution (%f,%f)", (lastChi2 - chi2) / chi2, fracRej);
     920    }
     921
     922    psArray *results = psArrayAlloc(numImages); // Array of results
     923    for (int i = 0; i < numImages; i++) {
     924        psVector *offset = results->data[i] = psVectorAlloc(2, PS_TYPE_F32); // Offset for image
     925        offset->data.F32[0] = xShift->data.F32[i];
     926        offset->data.F32[1] = yShift->data.F32[i];
     927    }
     928    psFree(xShift);
     929    psFree(yShift);
     930
     931    return results;
     932}
     933
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceMatch.h

    r23241 r27840  
    11#ifndef PM_SOURCE_MATCH_H
    22#define PM_SOURCE_MATCH_H
     3
     4#include <pslib.h>
     5
     6/// Mask values for matched sources
     7typedef enum {
     8    PM_SOURCE_MATCH_MASK_PHOT = 0x01,   // Source was rejected from photometry fit
     9    PM_SOURCE_MATCH_MASK_ASTRO = 0x02,     // Source was rejected from astrometry fit
     10} pmSourceMatchMask;
    311
    412/// Matched sources
     
    1220    psVector *mag;                      // Magnitudes
    1321    psVector *magErr;                   // Magnitude errors
     22    psVector *x, *y;                    // Positions
    1423    psVector *mask;                     // Mask for measurements
    1524} pmSourceMatch;
     
    2635    PS_ASSERT_VECTOR_NON_NULL((MATCH)->mag, RVAL); \
    2736    PS_ASSERT_VECTOR_NON_NULL((MATCH)->magErr, RVAL); \
     37    PS_ASSERT_VECTOR_NON_NULL((MATCH)->x, RVAL); \
     38    PS_ASSERT_VECTOR_NON_NULL((MATCH)->y, RVAL); \
    2839    PS_ASSERT_VECTOR_SIZE((MATCH)->image, (MATCH)->num, RVAL); \
    2940    PS_ASSERT_VECTOR_SIZE((MATCH)->index, (MATCH)->num, RVAL); \
    3041    PS_ASSERT_VECTOR_SIZE((MATCH)->mag, (MATCH)->num, RVAL); \
    3142    PS_ASSERT_VECTOR_SIZE((MATCH)->magErr, (MATCH)->num, RVAL); \
     43    PS_ASSERT_VECTOR_SIZE((MATCH)->x, (MATCH)->num, RVAL); \
     44    PS_ASSERT_VECTOR_SIZE((MATCH)->y, (MATCH)->num, RVAL); \
    3245}
    3346
     
    3851void pmSourceMatchAdd(pmSourceMatch *match, // Match data
    3952                      float mag, float magErr, // Magnitude and error
     53                      float x, float y,        // Position
    4054                      int image, // Image index
    4155                      int index // Source index
     
    7589    );
    7690
     91/// Perform relative astrometry to calibrate images
     92psArray *pmSourceMatchRelastro(const psArray *matches, // Array of matches
     93                               int numImages,          // Number of images
     94                               float tol, // Relative tolerance for convergence
     95                               int iter1, // Number of iterations for pass 1
     96                               float rej1, // Limit on rejection between iterations for pass 1
     97                               int iter2, // Number of iterations for pass 2
     98                               float rej2, // Limit on rejection between iterations for pass 2
     99                               float rejLimit // Limit on rejection between iterations
     100    );
     101
    77102#endif
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceMoments.c

    r24577 r27840  
    3636#include "pmSource.h"
    3737
    38 bool pmSourceMoments_Old(pmSource *source, psF32 radius);
    39 
    4038/******************************************************************************
    4139pmSourceMoments(source, radius): this function takes a subImage defined in the
     
    5048    pmSource->mask (optional)
    5149
    52 XXX: The peak calculations are done in image coords, not subImage coords.
    53 
    54 this version clips input pixels on S/N
    55 XXX EAM : this version returns false for several reasons
     50The peak calculations are done in image coords, not subImage coords.
     51
     52this version optionally clips input pixels on S/N
    5653*****************************************************************************/
    5754# define VALID_RADIUS(X,Y,RAD2) (((RAD2) >= (PS_SQR(X) + PS_SQR(Y))) ? 1 : 0)
    5855
    59 bool pmSourceMoments(pmSource *source, psF32 radius, psF32 sigma, psF32 minSN)
     56bool pmSourceMoments(pmSource *source, psF32 radius, psF32 sigma, psF32 minSN, psImageMaskType maskVal)
    6057{
    6158    PS_ASSERT_PTR_NON_NULL(source, false);
     
    6461    PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
    6562
    66     // XXX supply sky in a different way?
     63    // use sky from moments if defined, 0.0 otherwise
    6764    psF32 sky = 0.0;
    6865    if (source->moments == NULL) {
     
    9188    // col0,row0: a pixel (x,y) in the primary image has coordinates of (x-col0, y-row0) in
    9289    // this subimage.  we subtract off the peak coordinates, adjusted to this subimage, to have
    93     // minimal round-off error in the sums
    94 
    95     psF32 xOff  = source->peak->x;
    96     psF32 yOff  = source->peak->y;
    97     psF32 xPeak = source->peak->x - source->pixels->col0; // coord of peak in subimage
    98     psF32 yPeak = source->peak->y - source->pixels->row0; // coord of peak in subimage
     90    // minimal round-off error in the sums.  since these values are subtracted just to minimize
     91    // the dynamic range and are added back below, the exact value does not matter. these are
     92    // (int) so they can be used in the image index below.
     93
     94    int xOff  = source->peak->x;
     95    int yOff  = source->peak->y;
     96    int xPeak = source->peak->x - source->pixels->col0; // coord of peak in subimage
     97    int yPeak = source->peak->y - source->pixels->row0; // coord of peak in subimage
     98
     99    // we are guaranteed to have a valid pixel and variance at this location (right? right?)
     100    // psF32 weightNorm = source->pixels->data.F32[yPeak][xPeak] / sqrt (source->variance->data.F32[yPeak][xPeak]);
     101    // psAssert (isfinite(source->pixels->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
     102    // psAssert (isfinite(source->variance->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
     103    // psAssert (source->variance->data.F32[yPeak][xPeak] > 0, "peak must be on valid pixel");
    99104
    100105    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
     
    109114        for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
    110115            if (vMsk) {
    111                 if (*vMsk) {
     116                if (*vMsk & maskVal) {
    112117                    vMsk++;
    113118                    continue;
     
    126131            psF32 wDiff = *vWgt;
    127132
    128             // skip pixels below specified significance level
     133            // skip pixels below specified significance level.  this is allowed, but should be
     134            // avoided -- the over-weights the wings of bright stars compared to those of faint
     135            // stars.
    129136            if (PS_SQR(pDiff) < minSN2*wDiff) continue;
    130             if (pDiff < 0) continue;
    131 
     137            // if (pDiff < 0) continue; // XXX : MWV says I should include < 0.0 valued points...
     138
     139            // Apply a Gaussian window function.  Be careful with the window function.  S/N
     140            // weighting over weights the sky for faint sources
    132141            if (sigma > 0.0) {
    133               // apply a pseudo-gaussian weight
    134 
    135               // XXX a lot of extra flops; can we do pre-calculate?
    136               psF32 z  = (PS_SQR(xDiff) + PS_SQR(yDiff))*rsigma2;
    137               assert (z >= 0.0);
    138               psF32 t = 1.0 + z*(1.0 + z/2.0*(1.0 + z/3.0));
    139               psF32 weight  = 1.0 / t;
    140 
    141               // fprintf (stderr, "%6.1f %6.1f  %8.1f %8.1f  %5.3f  ", xDiff, yDiff, pDiff, wDiff, weight);
    142 
    143               wDiff *= weight;
    144               pDiff *= weight;
     142                // XXX a lot of extra flops; can we pre-calculate?
     143                psF32 z  = (PS_SQR(xDiff) + PS_SQR(yDiff))*rsigma2;
     144                assert (z >= 0.0);
     145                psF32 weight  = exp(-z);
     146
     147                wDiff *= weight;
     148                pDiff *= weight;
    145149            }
    146150
     
    154158            Y1  += yWght;
    155159
    156             // fprintf (stderr, " : %6.1f %6.1f  %8.1f %8.1f\n", X1, Y1, Sum, Var);
    157 
    158             peakPixel = PS_MAX (*vPix, peakPixel);
    159             numPixels++;
    160         }
    161     }
    162 
    163     // if we have less than (1/2) of the possible pixels, force a retry
     160            peakPixel = PS_MAX (*vPix, peakPixel);
     161            numPixels++;
     162        }
     163    }
     164
     165    // if we have less than (1/4) of the possible pixels (in circle or box), force a retry
     166    int minPixels = PS_MIN(0.75*R2, source->pixels->numCols*source->pixels->numRows/4.0);
     167
    164168    // XXX EAM - the limit is a bit arbitrary.  make it user defined?
    165     if ((numPixels < 0.75*R2) || (Sum <= 0)) {
    166         psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n", numPixels, (int)(0.75*R2), Sum);
    167         return (false);
     169    if ((numPixels < minPixels) || (Sum <= 0)) {
     170        psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n", numPixels, minPixels, Sum);
     171        return (false);
    168172    }
    169173
     
    172176    float My = Y1/Sum;
    173177    if ((fabs(Mx) > radius) || (fabs(My) > radius)) {
    174         psTrace ("psModules.objects", 3, "large centroid swing; invalid peak %d, %d\n", source->peak->x, source->peak->y);
    175         return (false);
     178        psTrace ("psModules.objects", 3, "large centroid swing; invalid peak %d, %d\n", source->peak->x, source->peak->y);
     179        return (false);
    176180    }
    177181
     
    179183
    180184    // add back offset of peak in primary image
    181     source->moments->Mx = Mx + xOff;
    182     source->moments->My = My + yOff;
     185    // also offset from pixel index to pixel coordinate
     186    // (the calculation above uses pixel index instead of coordinate)
     187    // 0.5 PIX: moments are calculated using the pixel index and converted here to pixel coords
     188    source->moments->Mx = Mx + xOff + 0.5;
     189    source->moments->My = My + yOff + 0.5;
    183190
    184191    source->moments->Sum = Sum;
     
    205212    Sum = 0.0;  // the second pass may include slightly different pixels, re-determine Sum
    206213
    207     // center of mass in subimage
     214    // center of mass in subimage.  Note: the calculation below uses pixel index, so we do not
     215    // correct xCM, yCM to pixel coordinate here.
    208216    psF32 xCM = Mx + xPeak; // coord of peak in subimage
    209217    psF32 yCM = My + yPeak; // coord of peak in subimage
     
    211219    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
    212220
    213         psF32 yDiff = row - yCM;
     221        psF32 yDiff = row - yCM;
    214222        if (fabs(yDiff) > radius) continue;
    215223
    216         psF32 *vPix = source->pixels->data.F32[row];
    217         psF32 *vWgt = source->variance->data.F32[row];
    218         psImageMaskType  *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
    219 
    220         for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
    221             if (vMsk) {
    222                 if (*vMsk) {
    223                     vMsk++;
    224                     continue;
    225                 }
    226                 vMsk++;
    227             }
    228             if (isnan(*vPix)) continue;
    229 
    230             psF32 xDiff = col - xCM;
     224        psF32 *vPix = source->pixels->data.F32[row];
     225        psF32 *vWgt = source->variance->data.F32[row];
     226        psImageMaskType  *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
     227
     228        for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
     229            if (vMsk) {
     230                if (*vMsk & maskVal) {
     231                    vMsk++;
     232                    continue;
     233                }
     234                vMsk++;
     235            }
     236            if (isnan(*vPix)) continue;
     237
     238            psF32 xDiff = col - xCM;
    231239            if (fabs(xDiff) > radius) continue;
    232240
    233             // radius is just a function of (xDiff, yDiff)
    234             psF32 r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
    235             psF32 r  = sqrt(r2);
    236             if (r > radius) continue;
    237 
    238             psF32 pDiff = *vPix - sky;
    239             psF32 wDiff = *vWgt;
    240 
    241             // XXX EAM : should this limit be user-defined?
    242 
    243             if (PS_SQR(pDiff) < minSN2*wDiff) continue;
    244             if (pDiff < 0) continue;
    245 
     241            // radius is just a function of (xDiff, yDiff)
     242            psF32 r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
     243            psF32 r  = sqrt(r2);
     244            if (r > radius) continue;
     245
     246            psF32 pDiff = *vPix - sky;
     247            psF32 wDiff = *vWgt;
     248
     249            // skip pixels below specified significance level.  this is allowed, but should be
     250            // avoided -- the over-weights the wings of bright stars compared to those of faint
     251            // stars.
     252            if (PS_SQR(pDiff) < minSN2*wDiff) continue;
     253            // if (pDiff < 0) continue;
     254
     255            // Apply a Gaussian window function.  Be careful with the window function.  S/N
     256            // weighting over weights the sky for faint sources
    246257            if (sigma > 0.0) {
    247               // apply a pseudo-gaussian weight
    248 
    249               // XXX a lot of extra flops; can we do pre-calculate?
    250               psF32 z  = (PS_SQR(xDiff) + PS_SQR(yDiff))*rsigma2;
    251               assert (z >= 0.0);
    252               psF32 t = 1.0 + z*(1.0 + z/2.0*(1.0 + z/3.0));
    253               psF32 weight  = 1.0 / t;
    254 
    255               // fprintf (stderr, "%6.1f %6.1f  %8.1f %8.1f  %5.3f  ", xDiff, yDiff, pDiff, wDiff, weight);
    256 
    257               wDiff *= weight;
    258               pDiff *= weight;
     258                // XXX a lot of extra flops; can we do pre-calculate?
     259                psF32 z  = (PS_SQR(xDiff) + PS_SQR(yDiff))*rsigma2;
     260                assert (z >= 0.0);
     261                psF32 weight  = exp(-z);
     262
     263                wDiff *= weight;
     264                pDiff *= weight;
    259265            }
    260266
    261             Sum += pDiff;
    262 
    263             psF32 x = xDiff * pDiff;
    264             psF32 y = yDiff * pDiff;
    265 
    266             psF32 xx = xDiff * x;
    267             psF32 xy = xDiff * y;
    268             psF32 yy = yDiff * y;
    269 
    270             psF32 xxx = xDiff * xx / r;
    271             psF32 xxy = xDiff * xy / r;
    272             psF32 xyy = xDiff * yy / r;
    273             psF32 yyy = yDiff * yy / r;
    274 
    275             psF32 xxxx = xDiff * xxx / r2;
    276             psF32 xxxy = xDiff * xxy / r2;
    277             psF32 xxyy = xDiff * xyy / r2;
    278             psF32 xyyy = xDiff * yyy / r2;
    279             psF32 yyyy = yDiff * yyy / r2;
    280 
    281             XX  += xx;
    282             XY  += xy;
    283             YY  += yy;
    284 
    285             XXX  += xxx;
    286             XXY  += xxy;
    287             XYY  += xyy;
    288             YYY  += yyy;
    289 
    290             XXXX  += xxxx;
    291             XXXY  += xxxy;
    292             XXYY  += xxyy;
    293             XYYY  += xyyy;
    294             YYYY  += yyyy;
    295         }
     267            Sum += pDiff;
     268
     269            psF32 x = xDiff * pDiff;
     270            psF32 y = yDiff * pDiff;
     271
     272            psF32 xx = xDiff * x;
     273            psF32 xy = xDiff * y;
     274            psF32 yy = yDiff * y;
     275
     276            psF32 xxx = xDiff * xx / r;
     277            psF32 xxy = xDiff * xy / r;
     278            psF32 xyy = xDiff * yy / r;
     279            psF32 yyy = yDiff * yy / r;
     280
     281            psF32 xxxx = xDiff * xxx / r2;
     282            psF32 xxxy = xDiff * xxy / r2;
     283            psF32 xxyy = xDiff * xyy / r2;
     284            psF32 xyyy = xDiff * yyy / r2;
     285            psF32 yyyy = yDiff * yyy / r2;
     286
     287            XX  += xx;
     288            XY  += xy;
     289            YY  += yy;
     290
     291            XXX  += xxx;
     292            XXY  += xxy;
     293            XYY  += xyy;
     294            YYY  += yyy;
     295
     296            XXXX  += xxxx;
     297            XXXY  += xxxy;
     298            XXYY  += xxyy;
     299            XYYY  += xyyy;
     300            YYYY  += yyyy;
     301        }
    296302    }
    297303
     
    311317    source->moments->Myyyy = YYYY/Sum;
    312318
    313     if (source->moments->Mxx < 0) {
    314       fprintf (stderr, "error: neg second moment??\n");
    315     }
    316     if (source->moments->Myy < 0) {
    317       fprintf (stderr, "error: neg second moment??\n");
    318     }
     319    // if (source->moments->Mxx < 0) {
     320    //  fprintf (stderr, "error: neg second moment??\n");
     321    // }
     322    // if (source->moments->Myy < 0) {
     323    //  fprintf (stderr, "error: neg second moment??\n");
     324    // }
    319325
    320326    psTrace ("psModules.objects", 4, "Mxx: %f  Mxy: %f  Myy: %f  Mxxx: %f  Mxxy: %f  Mxyy: %f  Myyy: %f  Mxxxx: %f  Mxxxy: %f  Mxxyy: %f  Mxyyy: %f  Mxyyy: %f\n",
    321              source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy,
    322              source->moments->Mxxx,  source->moments->Mxxy,  source->moments->Mxyy,  source->moments->Myyy,
    323              source->moments->Mxxxx, source->moments->Mxxxy, source->moments->Mxxyy, source->moments->Mxyyy, source->moments->Myyyy);
     327             source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy,
     328             source->moments->Mxxx,  source->moments->Mxxy,  source->moments->Mxyy,  source->moments->Myyy,
     329             source->moments->Mxxxx, source->moments->Mxxxy, source->moments->Mxxyy, source->moments->Mxyyy, source->moments->Myyyy);
    324330
    325331    psTrace ("psModules.objects", 3, "peak %f %f (%f = %f) Mx: %f  My: %f  Sum: %f  Mxx: %f  Mxy: %f  Myy: %f  sky: %f  Npix: %d\n",
    326              source->peak->xf, source->peak->yf, source->peak->flux, source->peak->SN, source->moments->Mx,   source->moments->My, Sum, source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy, sky, numPixels);
    327 
    328     if (source->moments->Mxx < 0) {
    329         fprintf (stderr, "error: neg second moment??\n");
    330     }
    331     if (source->moments->Myy < 0) {
    332         fprintf (stderr, "error: neg second moment??\n");
    333     }
    334 
    335     // XXX TEST:
    336     // pmSourceMoments_Old (source, radius);
     332             source->peak->xf, source->peak->yf, source->peak->flux, source->peak->SN, source->moments->Mx,   source->moments->My, Sum, source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy, sky, numPixels);
     333
    337334    return(true);
    338335}
    339 
    340 
    341 bool pmSourceMoments_Old(pmSource *source, psF32 radius)
    342 {
    343     PS_ASSERT_PTR_NON_NULL(source, false);
    344     PS_ASSERT_PTR_NON_NULL(source->peak, false);
    345     PS_ASSERT_PTR_NON_NULL(source->pixels, false);
    346     PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
    347 
    348     psF32 sky = 0.0;
    349     if (source->moments == NULL) {
    350         source->moments = pmMomentsAlloc();
    351     } else {
    352         sky = source->moments->Sky;
    353     }
    354 
    355     // Sum = SUM (z - sky)
    356     // X1  = SUM (x - xc)*(z - sky)
    357     // X2  = SUM (x - xc)^2 * (z - sky)
    358     // XY  = SUM (x - xc)*(y - yc)*(z - sky)
    359     psF32 peakPixel = -PS_MAX_F32;
    360     psS32 numPixels = 0;
    361     psF32 Sum = 0.0;
    362     psF32 Var = 0.0;
    363     psF32 X1 = 0.0;
    364     psF32 Y1 = 0.0;
    365     psF32 X2 = 0.0;
    366     psF32 Y2 = 0.0;
    367     psF32 XY = 0.0;
    368     psF32 x  = 0;
    369     psF32 y  = 0;
    370     psF32 R2 = PS_SQR(radius);
    371 
    372     // a note about coordinates: coordinates of objects throughout psphot refer to the primary
    373     // image coordinates.  the source->pixels image has an offset relative to its parent of
    374     // col0,row0: a pixel (x,y) in the primary image has coordinates of (x-col0, y-row0) in
    375     // this subimage.  we subtract off the peak coordinates, adjusted to this subimage, to have
    376     // minimal round-off error in the sums
    377 
    378     psF32 xPeak = source->peak->x - source->pixels->col0; // coord of peak in subimage
    379     psF32 yPeak = source->peak->y - source->pixels->row0; // coord of peak in subimage
    380 
    381     for (psS32 row = 0; row < source->pixels->numRows ; row++) {
    382 
    383         psF32 *vPix = source->pixels->data.F32[row];
    384         psF32 *vWgt = source->variance->data.F32[row];
    385         psImageMaskType  *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
    386 
    387         for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
    388             if (vMsk) {
    389                 if (*vMsk) {
    390                     vMsk++;
    391                     continue;
    392                 }
    393                 vMsk++;
    394             }
    395             if (isnan(*vPix)) continue;
    396 
    397             psF32 xDiff = col - xPeak;
    398             psF32 yDiff = row - yPeak;
    399 
    400             // radius is just a function of (xDiff, yDiff)
    401             if (!VALID_RADIUS(xDiff, yDiff, R2)) continue;
    402 
    403             psF32 pDiff = *vPix - sky;
    404             psF32 wDiff = *vWgt;
    405 
    406             // XXX EAM : should this limit be user-defined?
    407             if (PS_SQR(pDiff) < wDiff) continue;
    408 
    409             Var += wDiff;
    410             Sum += pDiff;
    411 
    412             psF32 xWght = xDiff * pDiff;
    413             psF32 yWght = yDiff * pDiff;
    414 
    415             X1  += xWght;
    416             Y1  += yWght;
    417 
    418             X2  += xDiff * xWght;
    419             XY  += xDiff * yWght;
    420             Y2  += yDiff * yWght;
    421 
    422             peakPixel = PS_MAX (*vPix, peakPixel);
    423             numPixels++;
    424         }
    425     }
    426 
    427     // if we have less than (1/4) of the possible pixels, force a retry
    428     // XXX EAM - the limit is a bit arbitrary.  make it user defined?
    429     if ((numPixels < 0.75*R2) || (Sum <= 0)) {
    430         psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n", numPixels, (int)(0.75*R2), Sum);
    431         return (false);
    432     }
    433 
    434     psTrace ("psModules.objects", 4, "sky: %f  Sum: %f  X1: %f  Y1: %f  X2: %f  Y2: %f  XY: %f  Npix: %d\n",
    435              sky, Sum, X1, Y1, X2, Y2, XY, numPixels);
    436 
    437     //
    438     // first moment X  = X1/Sum + xc
    439     // second moment X = sqrt (X2/Sum - (X1/Sum)^2)
    440     // Sxy             = XY / Sum
    441     //
    442     x = X1/Sum;
    443     y = Y1/Sum;
    444     if ((fabs(x) > radius) || (fabs(y) > radius)) {
    445         psTrace ("psModules.objects", 3, "large centroid swing; invalid peak %d, %d\n",
    446                  source->peak->x, source->peak->y);
    447         return (false);
    448     }
    449 
    450 # if (PS_TRACE_ON)
    451     float Sxx = PS_MAX(X2/Sum - PS_SQR(x), 0);
    452     float Sxy = XY / Sum;
    453     float Syy = PS_MAX(Y2/Sum - PS_SQR(y), 0);
    454 
    455     psTrace ("psModules.objects", 3,
    456              "sky: %f  Sum: %f  x: %f  y: %f  Sx: %f  Sy: %f  Sxy: %f\n",
    457              sky, Sum, x, y, Sxx, Sxy, Syy);
    458 # endif
    459 
    460     return(true);
    461 }
    462 
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourcePhotometry.c

    r21511 r27840  
    8484
    8585    // we must have a valid model
     86    // XXX allow aperture magnitudes for sources without a model
    8687    model = pmSourceGetModel (&isPSF, source);
    8788    if (model == NULL) {
     
    9091    }
    9192
     93    // XXX handle negative flux, low-significance
    9294    if (model->dparams->data.F32[PM_PAR_I0] > 0) {
    9395        SN = model->params->data.F32[PM_PAR_I0] / model->dparams->data.F32[PM_PAR_I0];
     
    101103
    102104    // measure PSF model photometry
    103     if (psf->FluxScale) {
     105    // XXX TEST: do not use flux scale
     106    if (0 && psf->FluxScale) {
    104107        // the source peak pixel is guaranteed to be on the image, and only minimally different from the source center
    105108        double fluxScale = pmTrend2DEval (psf->FluxScale, (float)source->peak->x, (float)source->peak->y);
    106         if (!isfinite(fluxScale)) {
    107             // XXX objects on the edge can be slightly outside -- if we get an
    108             // error, use the full fit.
    109             psErrorClear();
    110             status = pmSourcePhotometryModel (&source->psfMag, source->modelPSF);
    111         } else {
    112             if (isfinite(fluxScale) && (fluxScale > 0.0)) {
    113                 source->psfMag = -2.5*log10(fluxScale * source->modelPSF->params->data.F32[PM_PAR_I0]);
    114             } else {
    115                 source->psfMag = NAN;
    116             }
    117         }
     109        psAssert (isfinite(fluxScale), "how can the flux scale be invalid? source at %d, %d\n", source->peak->x, source->peak->y);
     110        psAssert (fluxScale > 0.0, "how can the flux scale be negative? source at %d, %d\n", source->peak->x, source->peak->y);
     111        source->psfFlux = fluxScale * source->modelPSF->params->data.F32[PM_PAR_I0];
     112        source->psfFluxErr = fluxScale * source->modelPSF->dparams->data.F32[PM_PAR_I0];
     113        source->psfMag = -2.5*log10(source->psfFlux);
    118114    } else {
    119         status = pmSourcePhotometryModel (&source->psfMag, source->modelPSF);
    120     }
    121 
    122     // if we have a collection of model fits, one of them is a pointer to modelEXT?
    123     // XXX not guaranteed
     115        status = pmSourcePhotometryModel (&source->psfMag, &source->psfFlux, source->modelPSF);
     116        source->psfFluxErr = source->psfFlux * (source->modelPSF->dparams->data.F32[PM_PAR_I0] / source->modelPSF->params->data.F32[PM_PAR_I0]);
     117    }
     118
     119    // if we have a collection of model fits, check if one of them is a pointer to modelEXT
    124120    if (source->modelFits) {
    125       for (int i = 0; i < source->modelFits->n; i++) {
    126         pmModel *model = source->modelFits->data[i];
    127         status = pmSourcePhotometryModel (&model->mag, model);
    128       }
    129       if (source->modelEXT) {
    130         source->extMag = source->modelEXT->mag;
    131       }
     121        bool foundEXT = false;
     122        for (int i = 0; i < source->modelFits->n; i++) {
     123            pmModel *model = source->modelFits->data[i];
     124            status = pmSourcePhotometryModel (&model->mag, NULL, model);
     125            if (model == source->modelEXT) foundEXT = true;
     126        }
     127        if (foundEXT) {
     128            source->extMag = source->modelEXT->mag;
     129        } else {
     130            status = pmSourcePhotometryModel (&source->extMag, NULL, source->modelEXT);
     131        }
    132132    } else {
    133       if (source->modelEXT) {
    134         status = pmSourcePhotometryModel (&source->extMag, source->modelEXT);
    135       }
     133        if (source->modelEXT) {
     134            status = pmSourcePhotometryModel (&source->extMag, NULL, source->modelEXT);
     135        }
    136136    }
    137137
     
    148148    }
    149149
     150    // measure the contribution of included pixels
     151    if (mode & PM_SOURCE_PHOT_DIFFSTATS) {
     152        pmSourceMeasureDiffStats (source, maskVal);
     153    }
     154
    150155    // measure the aperture magnitude, if (SN > AP_MIN_SN)
    151156    if (!isfinite(SN)) {
     
    160165    }
    161166
    162     // replace source flux
    163     // XXX need to be certain which model and size of mask for prior subtraction
    164     // XXX full model or just analytical?
    165     // XXX use pmSourceAdd instead?
    166     if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
    167         pmModelAdd (source->pixels, source->maskObj, model, PM_MODEL_OP_FULL, maskVal);
    168     }
     167    // if we measure aperture magnitudes, the source must not currently be subtracted!
     168    psAssert (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED), "cannot measure ap mags if source is subtracted!");
    169169
    170170    // if we are measuring aperture photometry and applying the growth correction,
     
    201201    if (isfinite (source->apMag) && isPSF && psf) {
    202202        if (psf->growth && (mode & PM_SOURCE_PHOT_GROWTH)) {
    203             source->apMag += pmGrowthCurveCorrect (psf->growth, model->radiusFit);
     203            source->apMag += pmGrowthCurveCorrect (psf->growth, source->apRadius);
    204204        }
    205205        if (mode & PM_SOURCE_PHOT_APCORR) {
     206            // XXX this should be removed -- we no longer fit for the 'sky bias'
    206207            rflux   = pow (10.0, 0.4*source->psfMag);
    207             source->apMag -= PS_SQR(model->radiusFit)*rflux * psf->skyBias + psf->skySat / rflux;
     208            source->apMag -= PS_SQR(source->apRadius)*rflux * psf->skyBias + psf->skySat / rflux;
    208209        }
    209210    }
     
    211212        psFree(flux);
    212213        psFree(mask);
    213     }
    214 
    215     // if source was originally subtracted, re-subtract object, leave local sky
    216     // XXX replace with pmSourceSub...
    217     if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
    218         pmModelSub (source->pixels, source->maskObj, model, PM_MODEL_OP_FULL, maskVal);
    219214    }
    220215
     
    230225
    231226// return source model magnitude
    232 bool pmSourcePhotometryModel (float *fitMag, pmModel *model)
    233 {
    234     PS_ASSERT_PTR_NON_NULL(fitMag, false);
    235     if (model == NULL) {
    236         return false;
    237     }
    238 
    239     float fitSum = 0;
    240     *fitMag = NAN;
     227bool pmSourcePhotometryModel (float *fitMag, float *fitFlux, pmModel *model)
     228{
     229    psAssert (fitMag || fitFlux, "at least one of magnitude or flux must be requested (not NULL)");
     230    if (model == NULL) return false;
     231
     232    float mag  = NAN;
     233    float flux = NAN;
    241234
    242235    // measure fitMag
    243     fitSum = model->modelFlux (model->params);
    244     if (fitSum <= 0)
    245         return false;
    246     if (!isfinite(fitSum))
    247         return false;
    248     *fitMag = -2.5*log10(fitSum);
     236    flux = model->modelFlux (model->params);
     237    if (flux > 0) {
     238        mag = -2.5*log10(flux);
     239    }
     240    if (fitMag) {
     241        *fitMag = mag;
     242    }
     243    if (fitFlux) {
     244        *fitFlux = flux;
     245    }
     246
     247    if (flux <= 0) return false;
     248    if (!isfinite(flux)) return false;
    249249
    250250    return (true);
     
    369369
    370370    *pixWeight = validSum / modelSum;
     371    return (true);
     372}
     373
     374# define FLUX_LIMIT 3.0
     375
     376// return source aperture magnitude
     377bool pmSourceMeasureDiffStats (pmSource *source, psImageMaskType maskVal)
     378{
     379    PS_ASSERT_PTR_NON_NULL(source, false);
     380
     381    if (source->diffStats == NULL) {
     382        source->diffStats = pmSourceDiffStatsAlloc();
     383    }
     384
     385    float fGood = 0.0;
     386    float fBad  = 0.0;
     387    int   nGood = 0;
     388    int   nMask = 0;
     389    int   nBad  = 0;
     390   
     391    psImage *flux     = source->pixels;
     392    psImage *variance = source->variance;
     393    psImage *mask     = source->maskObj;
     394
     395    for (int iy = 0; iy < flux->numRows; iy++) {
     396        for (int ix = 0; ix < flux->numCols; ix++) {
     397            if (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & maskVal) {
     398                nMask ++;
     399                continue;
     400            }
     401
     402            float SN = flux->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
     403
     404            if (SN > +FLUX_LIMIT) {
     405                nGood ++;
     406                fGood += fabs(flux->data.F32[iy][ix]);
     407            }
     408
     409            if (SN < -FLUX_LIMIT) {
     410                nBad ++;
     411                fBad += fabs(flux->data.F32[iy][ix]);
     412            }
     413        }
     414    }
     415
     416    source->diffStats->nGood      = nGood;
     417    source->diffStats->fRatio     = (fGood + fBad         == 0.0) ? NAN : fGood / (fGood + fBad);         
     418    source->diffStats->nRatioBad  = (nGood + nBad         == 0)   ? NAN : nGood / (float) (nGood + nBad);         
     419    source->diffStats->nRatioMask = (nGood + nMask        == 0)   ? NAN : nGood / (float) (nGood + nMask);         
     420    source->diffStats->nRatioAll  = (nGood + nMask + nBad == 0)   ? NAN : nGood / (float) (nGood + nMask + nBad);
     421
    371422    return (true);
    372423}
     
    558609
    559610// determine chisq, etc for linear normalization-only fit
    560 bool pmSourceChisq (pmModel *model, psImage *image, psImage *mask, psImage *variance,
    561                     psImageMaskType maskVal)
     611bool pmSourceChisq (pmModel *model, psImage *image, psImage *mask, psImage *variance, psImageMaskType maskVal, const float covarFactor)
    562612{
    563613    PS_ASSERT_PTR_NON_NULL(model, false);
     
    574624            if (variance->data.F32[j][i] <= 0)
    575625                continue;
    576             dC += PS_SQR (image->data.F32[j][i]) / variance->data.F32[j][i];
     626            dC += PS_SQR (image->data.F32[j][i]) / (covarFactor * variance->data.F32[j][i]);
    577627            Npix ++;
    578628        }
     
    586636
    587637
    588 double pmSourceModelWeight(const pmSource *Mi,
    589                       int term,
    590                       const bool unweighted_sum) // should the cross product be weighted?
     638double pmSourceModelWeight(const pmSource *Mi, int term, const bool unweighted_sum, const float covarFactor)
    591639{
    592640    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
     
    607655                continue;
    608656            if (!unweighted_sum) {
    609                 wt = Wi->data.F32[yi][xi];
     657                wt = covarFactor * Wi->data.F32[yi][xi];
    610658                if (wt == 0)
    611659                    continue;
     
    636684}
    637685
    638 double pmSourceModelDotModel (const pmSource *Mi,
    639                               const pmSource *Mj,
    640                               const bool unweighted_sum) // should the cross product be weighted?
     686double pmSourceModelDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor)
    641687{
    642688    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
     
    690736                flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]);
    691737            } else {
    692                 wt = Wi->data.F32[yi][xi];
     738                wt = covarFactor * Wi->data.F32[yi][xi];
    693739                if (wt > 0) {
    694740                    flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]) / wt;
     
    700746}
    701747
    702 double pmSourceDataDotModel (const pmSource *Mi,
    703                              const pmSource *Mj,
    704                              const bool unweighted_sum) // should the cross product be weighted?
     748double pmSourceDataDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor)
    705749{
    706750    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
     
    754798                flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]);
    755799            } else {
    756                 wt = Wi->data.F32[yi][xi];
     800                wt = covarFactor * Wi->data.F32[yi][xi];
    757801                if (wt > 0) {
    758802                    flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]) / wt;
     
    763807    return flux;
    764808}
    765 
    766 // XXX this is test code to verify the shift is doing the right thing (seems to be)
    767 # if (0)
    768 // measure centroid of unshifted gaussian (should be 16.0,16.0)
    769         {
    770           psImage *image = source->pixels;
    771           float xo = 0.0;
    772           float yo = 0.0;
    773           float xo2 = 0.0;
    774           float yo2 = 0.0;
    775           float no = 0.0;
    776           for (int j = 0; j < image->numRows; j++)
    777           {
    778             for (int i = 0; i < image->numCols; i++) {
    779               xo += i*image->data.F32[j][i];
    780               yo += j*image->data.F32[j][i];
    781               xo2 += i*i*image->data.F32[j][i];
    782               yo2 += j*j*image->data.F32[j][i];
    783               no += image->data.F32[j][i];
    784             }
    785           }
    786           xo /= no;
    787           yo /= no;
    788           xo2 = sqrt (xo2/no - xo*xo);
    789           yo2 = sqrt (yo2/no - yo*yo);
    790           fprintf (stderr, "pre-shift centroid: %f,%f, sigma: %f,%f: flux: %f\n", xo, yo, xo2, yo2, no);
    791         }
    792 
    793 // measure centroid of unshifted gaussian (should be 16.0,16.0)
    794         {
    795           psImage *image = flux;
    796           float xo = 0.0;
    797           float yo = 0.0;
    798           float xo2 = 0.0;
    799           float yo2 = 0.0;
    800           float no = 0.0;
    801           for (int j = 0; j < image->numRows; j++)
    802           {
    803             for (int i = 0; i < image->numCols; i++) {
    804               xo += i*image->data.F32[j][i];
    805               yo += j*image->data.F32[j][i];
    806               xo2 += i*i*image->data.F32[j][i];
    807               yo2 += j*j*image->data.F32[j][i];
    808               no += image->data.F32[j][i];
    809             }
    810           }
    811           xo /= no;
    812           yo /= no;
    813           xo2 = sqrt (xo2/no - xo*xo);
    814           yo2 = sqrt (yo2/no - yo*yo);
    815           fprintf (stderr, "pre-shift centroid: %f,%f, sigma: %f,%f: flux: %f\n", xo, yo, xo2, yo2, no);
    816         }
    817 # endif
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourcePhotometry.h

    r21511 r27840  
    2929
    3030typedef enum {
    31     PM_SOURCE_PHOT_NONE   = 0x0000,
    32     PM_SOURCE_PHOT_GROWTH = 0x0001,
    33     PM_SOURCE_PHOT_APCORR = 0x0002,
    34     PM_SOURCE_PHOT_WEIGHT = 0x0004,
    35     PM_SOURCE_PHOT_INTERP = 0x0008,
     31    PM_SOURCE_PHOT_NONE      = 0x0000,
     32    PM_SOURCE_PHOT_GROWTH    = 0x0001,
     33    PM_SOURCE_PHOT_APCORR    = 0x0002,
     34    PM_SOURCE_PHOT_WEIGHT    = 0x0004,
     35    PM_SOURCE_PHOT_INTERP    = 0x0008,
     36    PM_SOURCE_PHOT_DIFFSTATS = 0x0010,
    3637} pmSourcePhotometryMode;
    3738
    3839bool pmSourcePhotometryModel(
    3940    float *fitMag,                      ///< integrated fit magnitude
     41    float *fitFlux,                     ///< integrated fit magnitude
    4042    pmModel *model                      ///< model used for photometry
    4143);
     
    5254bool pmSourceMagnitudes (pmSource *source, pmPSF *psf, pmSourcePhotometryMode mode, psImageMaskType maskVal);
    5355bool pmSourcePixelWeight (float *pixWeight, pmModel *model, psImage *mask, psImageMaskType maskVal);
    54 bool pmSourceChisq (pmModel *model, psImage *image, psImage *mask, psImage *weight, psImageMaskType maskVal);
     56bool pmSourceChisq (pmModel *model, psImage *image, psImage *mask, psImage *weight, psImageMaskType maskVal, const float covarFactor);
    5557
     58bool pmSourceMeasureDiffStats (pmSource *source, psImageMaskType maskVal);
    5659
    57 double pmSourceDataDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum);
    58 double pmSourceModelDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum);
    59 double pmSourceModelWeight(const pmSource *Mi, int term, const bool unweighted_sum);
     60double pmSourceDataDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor);
     61double pmSourceModelDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor);
     62double pmSourceModelWeight(const pmSource *Mi, int term, const bool unweighted_sum, const float covarFactor);
    6063
    6164// retire these:
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourcePlotApResid.c

    r20937 r27840  
    3434#include "pmPSF.h"
    3535#include "pmModel.h"
     36#include "pmDetections.h"
    3637#include "pmSource.h"
    3738#include "pmSourcePlots.h"
     
    5354    PS_ASSERT_PTR_NON_NULL(layout, false);
    5455
     56    bool status;
    5557    Graphdata graphdata;
    5658    KapaSection section;
     
    6163    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
    6264
    63     psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
    64     if (sources == NULL)
    65         return false;
     65    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     66    if (detections == NULL) return false;
     67
     68    psArray *sources = detections->allSources;
     69    if (sources == NULL) return false;
    6670
    6771    int kapa = pmKapaOpen (false);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourcePlotMoments.c

    r20937 r27840  
    3737#include "pmPSF.h"
    3838#include "pmModel.h"
     39#include "pmDetections.h"
    3940#include "pmSource.h"
    4041#include "pmSourcePlots.h"
     
    5455    PS_ASSERT_PTR_NON_NULL(layout, false);
    5556
     57    bool status;
    5658    Graphdata graphdata;
    5759    KapaSection section;
     
    6264    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
    6365
    64     psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
    65     if (sources == NULL)
    66         return false;
     66    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     67    if (detections == NULL) return false;
     68
     69    psArray *sources = detections->allSources;
     70    if (sources == NULL) return false;
    6771
    6872    int kapa = pmKapaOpen (false);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourcePlotPSFModel.c

    r20937 r27840  
    3737#include "pmPSF.h"
    3838#include "pmModel.h"
     39#include "pmDetections.h"
    3940#include "pmSource.h"
    4041#include "pmSourcePlots.h"
     
    5657    PS_ASSERT_PTR_NON_NULL(layout, false);
    5758
     59    bool status;
    5860    Graphdata graphdata;
    5961    KapaSection section;
     
    6466    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
    6567
    66     psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
    67     if (sources == NULL)
    68         return false;
     68    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     69    if (detections == NULL) return false;
     70
     71    psArray *sources = detections->allSources;
     72    if (sources == NULL) return false;
    6973
    7074    int kapa = pmKapaOpen (false);
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceVisual.c

    r23242 r27840  
    55#include <pslib.h>
    66#include "pmTrend2D.h"
     7#include "pmPSF.h"
     8#include "pmPSFtry.h"
     9#include "pmSource.h"
    710#include "pmSourceVisual.h"
    811
     
    1518
    1619static int kapa1 = -1;
     20static int kapa2 = -1;
    1721static bool plotPSF = true;
    18 // static int kapa2 = -1;
    1922// static int kapa3 = -1;
    2023
     
    2730bool pmSourcePlotPoints3D (int myKapa, Graphdata *graphdata, psVector *xn, psVector *yn, psVector *zn, float theta, float phi);
    2831
    29 
    30 bool pmSourceVisualPSFModelResid (pmTrend2D *trend, psVector *x, psVector *y, psVector *param, psVector *mask) {
    31 
    32     KapaSection section;  // put the positive profile in one and the residuals in another?
     32bool pmSourceVisualPlotPSFMetric (pmPSFtry *psfTry) {
    3333
    3434    Graphdata graphdata;
    3535
    36     if (!pmVisualIsVisual() || !plotPSF) return true;
     36    if (!pmVisualIsVisual()) return true;
    3737
    3838    if (kapa1 == -1) {
     
    4545    }
    4646
    47     KapaClearPlots (kapa1);
     47    KapaClearSections (kapa1);
    4848    KapaInitGraph (&graphdata);
    4949
    50     float min = +1e32;
    51     float max = -1e32;
    52     float Min = +1e32;
    53     float Max = -1e32;
    54 
    55     psVector *resid = psVectorAlloc (x->n, PS_TYPE_F32);
    56     psVector *model = psVectorAlloc (x->n, PS_TYPE_F32);
    57 
    58     for (int i = 0; i < x->n; i++) {
    59         model->data.F32[i] = pmTrend2DEval (trend, x->data.F32[i], y->data.F32[i]);
    60         resid->data.F32[i] = param->data.F32[i] - model->data.F32[i];
    61         if (mask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
    62         min = PS_MIN (min, resid->data.F32[i]);
    63         max = PS_MAX (max, resid->data.F32[i]);
    64         Min = PS_MIN (min, param->data.F32[i]);
    65         Max = PS_MAX (max, param->data.F32[i]);
    66     }
    67 
    68     psVector *xn = psVectorAlloc (x->n, PS_TYPE_F32);
    69     psVector *yn = psVectorAlloc (x->n, PS_TYPE_F32);
    70     psVector *zn = psVectorAlloc (x->n, PS_TYPE_F32);
    71     psVector *Zn = psVectorAlloc (x->n, PS_TYPE_F32);
    72     psVector *Fn = psVectorAlloc (x->n, PS_TYPE_F32);
    73     for (int i = 0; i < x->n; i++) {
    74         xn->data.F32[i] = x->data.F32[i] / 5000.0;
    75         yn->data.F32[i] = y->data.F32[i] / 5000.0;
    76         zn->data.F32[i] = (resid->data.F32[i] - min) / (max - min);
    77         Zn->data.F32[i] = (param->data.F32[i] - Min) / (Max - Min);
    78         Fn->data.F32[i] = (model->data.F32[i] - Min) / (Max - Min);
    79     }
    80 
    81     // view 1 on resid
    82     section.dx = 0.5;
    83     section.dy = 0.33;
     50    psVector *x = psVectorAllocEmpty (psfTry->sources->n, PS_TYPE_F32);
     51    psVector *y = psVectorAllocEmpty (psfTry->sources->n, PS_TYPE_F32);
     52    psVector *dy = psVectorAllocEmpty(psfTry->sources->n, PS_TYPE_F32);
     53
     54    graphdata.xmin = +32.0;
     55    graphdata.xmax = -32.0;
     56    graphdata.ymin = +32.0;
     57    graphdata.ymax = -32.0;
     58
     59    // construct the plot vectors
     60    int n = 0;
     61    for (int i = 0; i < psfTry->sources->n; i++) {
     62        if (psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PSFTRY_MASK_ALL) continue;
     63        x->data.F32[n] = psfTry->fitMag->data.F32[i];
     64        y->data.F32[n] = psfTry->metric->data.F32[i];
     65        dy->data.F32[n] = psfTry->metricErr->data.F32[i];
     66        graphdata.xmin = PS_MIN(graphdata.xmin, x->data.F32[n]);
     67        graphdata.xmax = PS_MAX(graphdata.xmax, x->data.F32[n]);
     68        graphdata.ymin = PS_MIN(graphdata.ymin, y->data.F32[n]);
     69        graphdata.ymax = PS_MAX(graphdata.ymax, y->data.F32[n]);
     70        n++;
     71    }
     72    x->n = y->n = dy->n = n;
     73
     74    float range;
     75    range = graphdata.xmax - graphdata.xmin;
     76    graphdata.xmax += 0.05*range;
     77    graphdata.xmin -= 0.05*range;
     78    range = graphdata.ymax - graphdata.ymin;
     79    graphdata.ymax += 0.05*range;
     80    graphdata.ymin -= 0.05*range;
     81
     82    // better choice for range?
     83    // graphdata.xmin = -17.0;
     84    // graphdata.xmax =  -9.0;
     85    graphdata.ymin = -0.51;
     86    graphdata.ymax = +0.51;
     87
     88    KapaSetLimits (kapa1, &graphdata);
     89
     90    KapaSetFont (kapa1, "helvetica", 14);
     91    KapaBox (kapa1, &graphdata);
     92    KapaSendLabel (kapa1, "PSF Mag", KAPA_LABEL_XM);
     93    KapaSendLabel (kapa1, "Ap Mag - PSF Mag", KAPA_LABEL_YM);
     94
     95    graphdata.color = KapaColorByName ("black");
     96    graphdata.ptype = 2;
     97    graphdata.size = 0.5;
     98    graphdata.style = 2;
     99    graphdata.etype |= 0x01;
     100
     101    KapaPrepPlot (kapa1, n, &graphdata);
     102    KapaPlotVector (kapa1, n, x->data.F32, "x");
     103    KapaPlotVector (kapa1, n, y->data.F32, "y");
     104    KapaPlotVector (kapa1, n, dy->data.F32, "dym");
     105    KapaPlotVector (kapa1, n, dy->data.F32, "dyp");
     106
     107    psFree (x);
     108    psFree (y);
     109    psFree (dy);
     110
     111    pmVisualAskUser(NULL);
     112    return true;
     113}
     114
     115bool pmSourceVisualPlotPSFMetricSubpix (pmPSFtry *psfTry) {
     116
     117    KapaSection section;  // put the positive profile in one and the residuals in another?
     118    Graphdata graphdata;
     119
     120    if (!pmVisualIsVisual()) return true;
     121
     122    if (kapa1 == -1) {
     123        kapa1 = KapaOpenNamedSocket ("kapa", "pmSource:plots");
     124        if (kapa1 == -1) {
     125            fprintf (stderr, "failure to open kapa; visual mode disabled\n");
     126            pmVisualSetVisual(false);
     127            return false;
     128        }
     129    }
     130
     131    KapaClearSections (kapa1);
     132    KapaInitGraph (&graphdata);
     133
     134    int n;
     135    float range;
     136    psVector *x = psVectorAllocEmpty (psfTry->sources->n, PS_TYPE_F32);
     137    psVector *y = psVectorAllocEmpty (psfTry->sources->n, PS_TYPE_F32);
     138    psVector *dy = psVectorAllocEmpty(psfTry->sources->n, PS_TYPE_F32);
     139
     140    // section a: fractional-x pixel
     141    section.dx = 1.0;
     142    section.dy = 0.5;
    84143    section.x = 0.0;
    85144    section.y = 0.0;
     
    88147    KapaSetSection (kapa1, &section);
    89148    psFree (section.name);
    90     pmSourcePlotPoints3D (kapa1, &graphdata, xn, yn, zn, 30.0*PS_RAD_DEG, -15.0*PS_RAD_DEG);
    91 
    92     // view 2 on resid
    93     section.dx = 0.5;
    94     section.dy = 0.33;
    95     section.x = 0.5;
    96     section.y = 0.0;
     149
     150    graphdata.xmin = +32.0;
     151    graphdata.xmax = -32.0;
     152    graphdata.ymin = +32.0;
     153    graphdata.ymax = -32.0;
     154
     155    // construct the plot vectors
     156    n = 0;
     157    for (int i = 0; i < psfTry->sources->n; i++) {
     158        if (psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PSFTRY_MASK_ALL) continue;
     159
     160        pmSource *source = psfTry->sources->data[i];
     161        x->data.F32[n] = source->modelEXT->params->data.F32[PM_PAR_XPOS] - (int)source->modelEXT->params->data.F32[PM_PAR_XPOS];
     162
     163        y->data.F32[n] = psfTry->metric->data.F32[i];
     164        dy->data.F32[n] = psfTry->metricErr->data.F32[i];
     165        graphdata.xmin = PS_MIN(graphdata.xmin, x->data.F32[n]);
     166        graphdata.xmax = PS_MAX(graphdata.xmax, x->data.F32[n]);
     167        graphdata.ymin = PS_MIN(graphdata.ymin, y->data.F32[n]);
     168        graphdata.ymax = PS_MAX(graphdata.ymax, y->data.F32[n]);
     169        n++;
     170    }
     171    x->n = y->n = dy->n = n;
     172
     173    range = graphdata.xmax - graphdata.xmin;
     174    graphdata.xmax += 0.05*range;
     175    graphdata.xmin -= 0.05*range;
     176    range = graphdata.ymax - graphdata.ymin;
     177    graphdata.ymax += 0.05*range;
     178    graphdata.ymin -= 0.05*range;
     179
     180    // better choice for range?
     181    // graphdata.xmin = -17.0;
     182    // graphdata.xmax =  -9.0;
     183    graphdata.ymin = -0.51;
     184    graphdata.ymax = +0.51;
     185
     186    KapaSetLimits (kapa1, &graphdata);
     187
     188    KapaSetFont (kapa1, "helvetica", 14);
     189    KapaBox (kapa1, &graphdata);
     190    KapaSendLabel (kapa1, "PSF Mag", KAPA_LABEL_XM);
     191    KapaSendLabel (kapa1, "Ap Mag - PSF Mag", KAPA_LABEL_YM);
     192
     193    graphdata.color = KapaColorByName ("black");
     194    graphdata.ptype = 2;
     195    graphdata.size = 0.5;
     196    graphdata.style = 2;
     197    graphdata.etype |= 0x01;
     198
     199    KapaPrepPlot (kapa1, n, &graphdata);
     200    KapaPlotVector (kapa1, n, x->data.F32, "x");
     201    KapaPlotVector (kapa1, n, y->data.F32, "y");
     202    KapaPlotVector (kapa1, n, dy->data.F32, "dym");
     203    KapaPlotVector (kapa1, n, dy->data.F32, "dyp");
     204
     205    // *** section b: fractional-x pixel
     206    section.dx = 1.0;
     207    section.dy = 0.5;
     208    section.x = 0.0;
     209    section.y = 0.5;
    97210    section.name = NULL;
    98211    psStringAppend (&section.name, "a2");
    99212    KapaSetSection (kapa1, &section);
    100213    psFree (section.name);
    101     pmSourcePlotPoints3D (kapa1, &graphdata, xn, yn, zn, -60.0*PS_RAD_DEG, -15.0*PS_RAD_DEG);
    102 
    103     // view 3 on resid
    104     section.dx = 0.5;
    105     section.dy = 0.33;
     214
     215    graphdata.xmin = +32.0;
     216    graphdata.xmax = -32.0;
     217    graphdata.ymin = +32.0;
     218    graphdata.ymax = -32.0;
     219
     220    // construct the plot vectors
     221    n = 0;
     222    for (int i = 0; i < psfTry->sources->n; i++) {
     223        if (psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PSFTRY_MASK_ALL) continue;
     224
     225        pmSource *source = psfTry->sources->data[i];
     226        x->data.F32[n] = source->modelEXT->params->data.F32[PM_PAR_YPOS] - (int)source->modelEXT->params->data.F32[PM_PAR_YPOS];
     227
     228        y->data.F32[n] = psfTry->metric->data.F32[i];
     229        dy->data.F32[n] = psfTry->metricErr->data.F32[i];
     230        graphdata.xmin = PS_MIN(graphdata.xmin, x->data.F32[n]);
     231        graphdata.xmax = PS_MAX(graphdata.xmax, x->data.F32[n]);
     232        graphdata.ymin = PS_MIN(graphdata.ymin, y->data.F32[n]);
     233        graphdata.ymax = PS_MAX(graphdata.ymax, y->data.F32[n]);
     234        n++;
     235    }
     236    x->n = y->n = dy->n = n;
     237
     238    range = graphdata.xmax - graphdata.xmin;
     239    graphdata.xmax += 0.05*range;
     240    graphdata.xmin -= 0.05*range;
     241    range = graphdata.ymax - graphdata.ymin;
     242    graphdata.ymax += 0.05*range;
     243    graphdata.ymin -= 0.05*range;
     244
     245    // better choice for range?
     246    // graphdata.xmin = -17.0;
     247    // graphdata.xmax =  -9.0;
     248    graphdata.ymin = -0.51;
     249    graphdata.ymax = +0.51;
     250
     251    KapaSetLimits (kapa1, &graphdata);
     252
     253    KapaSetFont (kapa1, "helvetica", 14);
     254    KapaBox (kapa1, &graphdata);
     255    KapaSendLabel (kapa1, "PSF Mag", KAPA_LABEL_XM);
     256    KapaSendLabel (kapa1, "Ap Mag - PSF Mag", KAPA_LABEL_YM);
     257
     258    graphdata.color = KapaColorByName ("black");
     259    graphdata.ptype = 2;
     260    graphdata.size = 0.5;
     261    graphdata.style = 2;
     262    graphdata.etype |= 0x01;
     263
     264    KapaPrepPlot (kapa1, n, &graphdata);
     265    KapaPlotVector (kapa1, n, x->data.F32, "x");
     266    KapaPlotVector (kapa1, n, y->data.F32, "y");
     267    KapaPlotVector (kapa1, n, dy->data.F32, "dym");
     268    KapaPlotVector (kapa1, n, dy->data.F32, "dyp");
     269
     270    psFree (x);
     271    psFree (y);
     272    psFree (dy);
     273
     274    pmVisualAskUser(NULL);
     275    return true;
     276}
     277
     278// to see the structure of the psf model, place the sources in a fake image 1/10th the size
     279// at their appropriate relative location. later sources stomp on earlier sources
     280bool pmSourceVisualShowModelFits (pmPSF *psf, psArray *sources, psImageMaskType maskVal) {
     281
     282    if (!pmVisualIsVisual()) return true;
     283
     284    if (kapa2 == -1) {
     285        kapa2 = KapaOpenNamedSocket ("kapa", "pmSource:images");
     286        if (kapa2 == -1) {
     287            fprintf (stderr, "failure to open kapa; visual mode disabled\n");
     288            pmVisualSetVisual(false);
     289            return false;
     290        }
     291    }
     292
     293    // create images 1/10 scale:
     294    psImage *image = psImageAlloc (0.1*psf->fieldNx, 0.1*psf->fieldNy, PS_TYPE_F32);
     295    psImage *model = psImageAlloc (0.1*psf->fieldNx, 0.1*psf->fieldNy, PS_TYPE_F32);
     296    psImage *resid = psImageAlloc (0.1*psf->fieldNx, 0.1*psf->fieldNy, PS_TYPE_F32);
     297    psImageInit (image, 0.0);
     298    psImageInit (model, 0.0);
     299    psImageInit (resid, 0.0);
     300
     301    for (int i = sources->n - 1; i >= 0; i--) {
     302        pmSource *source = sources->data[i];
     303        if (!source) continue;
     304        if (!source->pixels) continue;
     305
     306        pmSourceCacheModel (source, maskVal);
     307        if (!source->modelFlux) continue;
     308
     309        pmModel *srcModel = pmSourceGetModel (NULL, source);
     310        if (!model) continue;
     311
     312        float norm = srcModel->params->data.F32[PM_PAR_I0];
     313
     314        int Xo = 0.1*srcModel->params->data.F32[PM_PAR_XPOS];
     315        int Yo = 0.1*srcModel->params->data.F32[PM_PAR_YPOS];
     316
     317        // insert source pixels in the image at 1/10th offset
     318        for (int iy = 0; iy < source->pixels->numRows; iy++) {
     319            int jy = iy + Yo;
     320            if (jy >= image->numRows) continue;
     321            for (int ix = 0; ix < source->pixels->numCols; ix++) {
     322                int jx = ix + Xo;
     323                if (jx >= image->numCols) continue;
     324                if (source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix]) continue;
     325                if (source->modelFlux->data.F32[iy][ix] < 0.001) continue;
     326                image->data.F32[jy][jx] = source->pixels->data.F32[iy][ix];
     327                model->data.F32[jy][jx] = source->modelFlux->data.F32[iy][ix];
     328                resid->data.F32[jy][jx] = source->pixels->data.F32[iy][ix] - norm*source->modelFlux->data.F32[iy][ix];
     329            }
     330        }
     331    }
     332
     333    // KapaClearSections (kapa2);
     334    pmVisualScaleImage (kapa2, image, "image", 0, true);
     335    pmVisualScaleImage (kapa2, model, "model", 1, true);
     336    pmVisualScaleImage (kapa2, resid, "resid", 2, true);
     337
     338# ifdef DEBUG
     339    {
     340        psFits *fits = psFitsOpen ("image.fits", "w");
     341        psFitsWriteImage (fits, NULL, image, 0, NULL);
     342        psFitsClose (fits);
     343        fits = psFitsOpen ("model.fits", "w");
     344        psFitsWriteImage (fits, NULL, model, 0, NULL);
     345        psFitsClose (fits);
     346        fits = psFitsOpen ("resid.fits", "w");
     347        psFitsWriteImage (fits, NULL, resid, 0, NULL);
     348        psFitsClose (fits);
     349    }
     350# endif
     351
     352    psFree (image);
     353    psFree (model);
     354    psFree (resid);
     355
     356    pmVisualAskUser(NULL);
     357    return true;
     358}
     359
     360bool pmSourceVisualShowModelFit (pmSource *source) {
     361
     362    if (!pmVisualIsVisual()) return true;
     363    if (!source->pixels) return false;
     364    if (!source->modelFlux) return false;
     365
     366    if (kapa2 == -1) {
     367        kapa2 = KapaOpenNamedSocket ("kapa", "pmSource:images");
     368        if (kapa2 == -1) {
     369            fprintf (stderr, "failure to open kapa; visual mode disabled\n");
     370            pmVisualSetVisual(false);
     371            return false;
     372        }
     373    }
     374
     375    // KapaClearSections (kapa2);
     376    pmVisualScaleImage (kapa2, source->pixels, "source", 0, false);
     377    pmVisualScaleImage (kapa2, source->modelFlux, "model", 1, false);
     378
     379    pmModel *model = pmSourceGetModel (NULL, source);
     380    float norm = model->params->data.F32[PM_PAR_I0];
     381
     382    psImage *resid = psImageAlloc (source->pixels->numCols, source->pixels->numRows, PS_TYPE_F32);
     383    for (int iy = 0; iy < source->pixels->numRows; iy++) {
     384        for (int ix = 0; ix < source->pixels->numCols; ix++) {
     385            if (source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix]) {
     386                resid->data.F32[iy][ix] = NAN;
     387                continue;
     388            }
     389            resid->data.F32[iy][ix] = source->pixels->data.F32[iy][ix] - norm*source->modelFlux->data.F32[iy][ix];
     390        }
     391    }
     392    pmVisualScaleImage (kapa2, resid, "resid", 2, false);
     393
     394    psFree (resid);
     395
     396    pmVisualAskUser(NULL);
     397    return true;
     398}
     399
     400bool pmSourceVisualPSFModelResid (pmTrend2D *trend, psVector *x, psVector *y, psVector *param, psVector *mask) {
     401
     402    KapaSection section;  // put the positive profile in one and the residuals in another?
     403
     404    Graphdata graphdata;
     405
     406    if (!pmVisualIsVisual() || !plotPSF) return true;
     407
     408    if (kapa1 == -1) {
     409        kapa1 = KapaOpenNamedSocket ("kapa", "pmSource:plots");
     410        if (kapa1 == -1) {
     411            fprintf (stderr, "failure to open kapa; visual mode disabled\n");
     412            pmVisualSetVisual(false);
     413            return false;
     414        }
     415    }
     416
     417    KapaClearPlots (kapa1);
     418    KapaInitGraph (&graphdata);
     419
     420    float Xmin = +1e32;
     421    float Xmax = -1e32;
     422    float Ymin = +1e32;
     423    float Ymax = -1e32;
     424    float Fmin = +1e32;
     425    float Fmax = -1e32;
     426
     427    psVector *resid = psVectorAlloc (x->n, PS_TYPE_F32);
     428    psVector *model = psVectorAlloc (x->n, PS_TYPE_F32);
     429
     430    psVector *xm = psVectorAlloc (x->n, PS_TYPE_F32);
     431    psVector *ym = psVectorAlloc (x->n, PS_TYPE_F32);
     432    psVector *Fm = psVectorAlloc (x->n, PS_TYPE_F32);
     433
     434    int n = 0;
     435    for (int i = 0; i < x->n; i++) {
     436        model->data.F32[i] = pmTrend2DEval (trend, x->data.F32[i], y->data.F32[i]);
     437        resid->data.F32[i] = param->data.F32[i] - model->data.F32[i];
     438        if (mask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
     439        Xmin = PS_MIN (Xmin, x->data.F32[i]);
     440        Xmax = PS_MAX (Xmax, x->data.F32[i]);
     441        Ymin = PS_MIN (Ymin, y->data.F32[i]);
     442        Ymax = PS_MAX (Ymax, y->data.F32[i]);
     443        Fmin = PS_MIN (Fmin, param->data.F32[i]);
     444        Fmax = PS_MAX (Fmax, param->data.F32[i]);
     445        xm->data.F32[n] = x->data.F32[i];
     446        ym->data.F32[n] = y->data.F32[i];
     447        Fm->data.F32[n] = param->data.F32[i];
     448        n++;
     449    }
     450    xm->n = ym->n = Fm->n = n;
     451
     452    // view 1 on resid
     453    section.dx = 1.0;
     454    section.dy = 0.5;
    106455    section.x = 0.0;
    107     section.y = 0.33;
     456    section.y = 0.0;
    108457    section.name = NULL;
    109     psStringAppend (&section.name, "a3");
     458    psStringAppend (&section.name, "a1");
    110459    KapaSetSection (kapa1, &section);
    111460    psFree (section.name);
    112     pmSourcePlotPoints3D (kapa1, &graphdata, xn, yn, Zn, 30.0*PS_RAD_DEG, -15.0*PS_RAD_DEG);
    113 
    114     // view 4 on resid
    115     section.dx = 0.5;
    116     section.dy = 0.33;
    117     section.x = 0.5;
    118     section.y = 0.33;
     461
     462    graphdata.color = KapaColorByName ("black");
     463    graphdata.xmin = Xmin;
     464    graphdata.xmax = Xmax;
     465    graphdata.ymin = Fmin;
     466    graphdata.ymax = Fmax;
     467
     468    {
     469        float range;
     470        range = graphdata.xmax - graphdata.xmin;
     471        graphdata.xmax += 0.05*range;
     472        graphdata.xmin -= 0.05*range;
     473        range = graphdata.ymax - graphdata.ymin;
     474        graphdata.ymax += 0.05*range;
     475        graphdata.ymin -= 0.05*range;
     476    }
     477
     478    KapaSetLimits (kapa1, &graphdata);
     479    KapaSetFont (kapa1, "helvetica", 14);
     480    KapaBox (kapa1, &graphdata);
     481    KapaSendLabel (kapa1, "X (pixels)", KAPA_LABEL_XM);
     482    KapaSendLabel (kapa1, "Model Param", KAPA_LABEL_YM);
     483
     484    graphdata.ptype = 2;
     485    graphdata.size = 1.0;
     486    graphdata.style = 2;
     487    KapaPrepPlot (kapa1,   x->n, &graphdata);
     488    KapaPlotVector (kapa1, x->n, x->data.F32, "x");
     489    KapaPlotVector (kapa1, x->n, param->data.F32, "y");
     490
     491    graphdata.color = KapaColorByName ("red");
     492    graphdata.ptype = 1;
     493    KapaPrepPlot (kapa1,   xm->n, &graphdata);
     494    KapaPlotVector (kapa1, xm->n, xm->data.F32, "x");
     495    KapaPlotVector (kapa1, xm->n, Fm->data.F32, "y");
     496
     497    graphdata.color = KapaColorByName ("blue");
     498    graphdata.ptype = 1;
     499    KapaPrepPlot (kapa1,   x->n, &graphdata);
     500    KapaPlotVector (kapa1, x->n, x->data.F32, "x");
     501    KapaPlotVector (kapa1, x->n, model->data.F32, "y");
     502
     503    // view 2 on resid
     504    section.dx = 1.0;
     505    section.dy = 0.5;
     506    section.x = 0.0;
     507    section.y = 0.5;
    119508    section.name = NULL;
    120     psStringAppend (&section.name, "a4");
     509    psStringAppend (&section.name, "a2");
    121510    KapaSetSection (kapa1, &section);
    122511    psFree (section.name);
    123     pmSourcePlotPoints3D (kapa1, &graphdata, xn, yn, Zn, -60.0*PS_RAD_DEG, -15.0*PS_RAD_DEG);
    124 
    125     // view 5 on resid
    126     section.dx = 0.5;
    127     section.dy = 0.33;
    128     section.x = 0.0;
    129     section.y = 0.66;
    130     section.name = NULL;
    131     psStringAppend (&section.name, "a5");
    132     KapaSetSection (kapa1, &section);
    133     psFree (section.name);
    134     pmSourcePlotPoints3D (kapa1, &graphdata, xn, yn, Fn, 30.0*PS_RAD_DEG, -15.0*PS_RAD_DEG);
    135 
    136     // view 6 on resid
    137     section.dx = 0.5;
    138     section.dy = 0.33;
    139     section.x = 0.5;
    140     section.y = 0.66;
    141     section.name = NULL;
    142     psStringAppend (&section.name, "a6");
    143     KapaSetSection (kapa1, &section);
    144     psFree (section.name);
    145     pmSourcePlotPoints3D (kapa1, &graphdata, xn, yn, Fn, -60.0*PS_RAD_DEG, -15.0*PS_RAD_DEG);
     512
     513    graphdata.color = KapaColorByName ("black");
     514    graphdata.xmin = Ymin;
     515    graphdata.xmax = Ymax;
     516    graphdata.ymin = Fmin;
     517    graphdata.ymax = Fmax;
     518    {
     519        float range;
     520        range = graphdata.xmax - graphdata.xmin;
     521        graphdata.xmax += 0.05*range;
     522        graphdata.xmin -= 0.05*range;
     523        range = graphdata.ymax - graphdata.ymin;
     524        graphdata.ymax += 0.05*range;
     525        graphdata.ymin -= 0.05*range;
     526    }
     527
     528    KapaSetLimits (kapa1, &graphdata);
     529    KapaSetFont (kapa1, "helvetica", 14);
     530    KapaBox (kapa1, &graphdata);
     531    KapaSendLabel (kapa1, "Y (pixels)", KAPA_LABEL_XM);
     532    KapaSendLabel (kapa1, "Model Param", KAPA_LABEL_YM);
     533
     534    graphdata.ptype = 2;
     535    graphdata.size = 1.0;
     536    graphdata.style = 2;
     537    KapaPrepPlot (kapa1,   y->n, &graphdata);
     538    KapaPlotVector (kapa1, y->n, y->data.F32, "x");
     539    KapaPlotVector (kapa1, y->n, param->data.F32, "y");
     540
     541    graphdata.color = KapaColorByName ("red");
     542    graphdata.ptype = 1;
     543    KapaPrepPlot (kapa1,   xm->n, &graphdata);
     544    KapaPlotVector (kapa1, xm->n, ym->data.F32, "x");
     545    KapaPlotVector (kapa1, xm->n, Fm->data.F32, "y");
     546
     547    graphdata.color = KapaColorByName ("blue");
     548    graphdata.ptype = 1;
     549    KapaPrepPlot (kapa1,   y->n, &graphdata);
     550    KapaPlotVector (kapa1, y->n, y->data.F32, "x");
     551    KapaPlotVector (kapa1, y->n, model->data.F32, "y");
     552
     553    psFree (xm);
     554    psFree (ym);
     555    psFree (Fm);
    146556
    147557    psFree (resid);
    148 
    149     psFree (xn);
    150     psFree (yn);
    151     psFree (zn);
    152     psFree (Zn);
     558    psFree (model);
    153559
    154560    // pause and wait for user input:
     
    159565}
    160566
    161 // send in normalized points
     567// Somewhat broken 3D plotting function (was used by pmSourceVisualPSFModelResid, but not anymore)
    162568bool pmSourcePlotPoints3D (int myKapa, Graphdata *graphdata, psVector *xn, psVector *yn, psVector *zn, float theta, float phi) {
     569
     570    return true;
    163571
    164572    psVector *xv = psVectorAlloc (PS_MAX(6, 2*xn->n), PS_TYPE_F32);
     
    192600    KapaSetLimits (myKapa, graphdata);
    193601
    194     // KapaSetFont (myKapa, "helvetica", 14);
    195     // KapaBox (myKapa, graphdata);
    196     // KapaSendLabel (myKapa, "&ss&h_x| (pixels)", KAPA_LABEL_XM);
    197     // KapaSendLabel (myKapa, "&ss&h_y| (pixels)", KAPA_LABEL_YM);
    198 
    199602    graphdata->color = KapaColorByName ("black");
    200603    graphdata->ptype = 100;
  • branches/simtest_nebulous_branches/psModules/src/objects/pmSourceVisual.h

    r23242 r27840  
    1818
    1919bool pmSourceVisualPSFModelResid (pmTrend2D *trend, psVector *x, psVector *y, psVector *param, psVector *mask);
     20bool pmSourceVisualPlotPSFMetric (pmPSFtry *try);
     21bool pmSourceVisualPlotPSFMetricSubpix (pmPSFtry *try);
     22
     23bool pmSourceVisualShowModelFit (pmSource *source);
     24bool pmSourceVisualShowModelFits (pmPSF *psf, psArray *sources, psImageMaskType maskVal);
    2025
    2126/// @}
  • branches/simtest_nebulous_branches/psModules/src/objects/pmTrend2D.c

    r21183 r27840  
    298298    return PM_TREND_NONE;
    299299}
     300
     301bool pmTrend2DPrintMap (pmTrend2D *trend) {
     302
     303    if (!trend->map) return false;
     304    if (!trend->map->map) return false;
     305
     306    for (int j = 0; j < trend->map->map->numRows; j++) {
     307        for (int i = 0; i < trend->map->map->numCols; i++) {
     308            fprintf (stderr, "%5.2f  ", trend->map->map->data.F32[j][i]);
     309        }
     310        fprintf (stderr, "\t\t\t");
     311        for (int i = 0; i < trend->map->map->numCols; i++) {
     312            fprintf (stderr, "%5.2f  ", trend->map->error->data.F32[j][i]);
     313        }
     314        fprintf (stderr, "\n");
     315    }
     316    return true;
     317}
     318
  • branches/simtest_nebulous_branches/psModules/src/objects/pmTrend2D.h

    r21183 r27840  
    9797pmTrend2DMode pmTrend2DModeFromString(psString name);
    9898
     99bool pmTrend2DPrintMap (pmTrend2D *trend);
     100
    99101/// @}
    100102# endif
  • branches/simtest_nebulous_branches/psModules/src/psmodules.h

    r24891 r27840  
    1010#include <pmKapaPlots.h>
    1111#include <pmVisual.h>
     12#include <ippStages.h>
     13#include <ippDiffMode.h>
    1214
    1315// XXX the following headers define constructs needed by the elements below
     
    2931#include <pmConfigDump.h>
    3032#include <pmConfigRun.h>
     33#include <pmConfigRecipeValue.h>
    3134#include <pmVersion.h>
    3235
     
    7881#include <pmRemnance.h>
    7982#include <pmPattern.h>
     83#include <pmPatternIO.h>
    8084
    8185// the following headers are from psModule:astrom
     
    96100#include <pmSubtractionStamps.h>
    97101#include <pmSubtractionKernels.h>
     102#include <pmSubtractionDeconvolve.h>
    98103#include <pmSubtractionAnalysis.h>
    99104#include <pmSubtractionMatch.h>
     
    108113// the following headers are from psModule:objects
    109114#include <pmSpan.h>
     115#include <pmFootprintSpans.h>
    110116#include <pmFootprint.h>
    111117#include <pmPeaks.h>
    112118#include <pmDetections.h>
    113119#include <pmMoments.h>
     120#include <pmSourceExtendedPars.h>
     121#include <pmSourceDiffStats.h>
    114122#include <pmResiduals.h>
    115123#include <pmGrowthCurve.h>
     
    119127#include <pmSourceMasks.h>
    120128#include <pmSource.h>
     129#include <pmPhotObj.h>
    121130#include <pmSourceUtils.h>
    122131#include <pmSourceIO.h>
     
    133142#include <pmSourceVisual.h>
    134143#include <pmSourceMatch.h>
     144#include <pmDetEff.h>
    135145
    136146// The following headers are from random locations, here because they cross bounds
  • branches/simtest_nebulous_branches/psModules/test/objects/tap_pmGrowthCurve.c

    r24851 r27840  
    131131        source->mode = PM_SOURCE_MODE_PSFSTAR;
    132132
    133         source->modelPSF->radiusFit = 15.0;
     133        source->apRadius = 15.0;
    134134
    135135        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    136136        double refMag = source->apMag;
    137137
    138         source->modelPSF->radiusFit = 10.0;
    139         pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    140         ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
    141 
    142         source->modelPSF->radiusFit = 8.0;
    143         pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    144         ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
    145 
    146         source->modelPSF->radiusFit = 6.0;
     138        source->apRadius = 10.0;
     139        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
     140        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
     141
     142        source->apRadius = 8.0;
     143        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
     144        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
     145
     146        source->apRadius = 6.0;
    147147        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    148148        ok_float_tol(refMag - source->apMag, +0.0003, 0.0001, "growth offset is is %f", refMag - source->apMag);
    149149
    150         source->modelPSF->radiusFit = 4.0;
     150        source->apRadius = 4.0;
    151151        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    152152        ok_float_tol(refMag - source->apMag, +0.0020, 0.0001, "growth offset is is %f", refMag - source->apMag);
    153153
    154         source->modelPSF->radiusFit = 3.0;
     154        source->apRadius = 3.0;
    155155        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    156156        ok_float_tol(refMag - source->apMag, -0.0001, 0.0001, "growth offset is is %f", refMag - source->apMag);
    157157
    158         source->modelPSF->radiusFit = 2.0;
     158        source->apRadius = 2.0;
    159159        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    160160        ok_float_tol(refMag - source->apMag, -0.0075, 0.0001, "growth offset is is %f", refMag - source->apMag);
     
    234234        source->mode = PM_SOURCE_MODE_PSFSTAR;
    235235
    236         source->modelPSF->radiusFit = 15.0;
     236        source->apRadius = 15.0;
    237237        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    238238        double refMag = source->apMag;
    239239
    240         source->modelPSF->radiusFit = 10.0;
    241         pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    242         ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
    243 
    244         source->modelPSF->radiusFit = 8.0;
    245         pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    246         ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
    247 
    248         source->modelPSF->radiusFit = 6.0;
     240        source->apRadius = 10.0;
     241        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
     242        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
     243
     244        source->apRadius = 8.0;
     245        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
     246        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
     247
     248        source->apRadius = 6.0;
    249249        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    250250        ok_float_tol(refMag - source->apMag, +0.0004, 0.0001, "growth offset is is %f", refMag - source->apMag);
    251251
    252         source->modelPSF->radiusFit = 4.0;
     252        source->apRadius = 4.0;
    253253        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    254254        ok_float_tol(refMag - source->apMag, +0.0026, 0.0001, "growth offset is is %f", refMag - source->apMag);
    255255
    256         source->modelPSF->radiusFit = 3.0;
     256        source->apRadius = 3.0;
    257257        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    258258        ok_float_tol(refMag - source->apMag, -0.0001, 0.0001, "growth offset is is %f", refMag - source->apMag);
    259259
    260         source->modelPSF->radiusFit = 2.0;
     260        source->apRadius = 2.0;
    261261        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    262262        ok_float_tol(refMag - source->apMag, -0.0103, 0.0001, "growth offset is is %f", refMag - source->apMag);
     
    336336        source->mode = PM_SOURCE_MODE_PSFSTAR;
    337337
    338         source->modelPSF->radiusFit = 15.0;
     338        source->apRadius = 15.0;
    339339        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    340340        double refMag = source->apMag;
    341341
    342         source->modelPSF->radiusFit = 10.0;
    343         pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    344         ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
    345 
    346         source->modelPSF->radiusFit = 8.0;
    347         pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    348         ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
    349 
    350         source->modelPSF->radiusFit = 6.0;
     342        source->apRadius = 10.0;
     343        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
     344        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
     345
     346        source->apRadius = 8.0;
     347        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
     348        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
     349
     350        source->apRadius = 6.0;
    351351        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    352352        ok_float_tol(refMag - source->apMag, +0.0006, 0.0001, "growth offset is is %f", refMag - source->apMag);
    353353
    354         source->modelPSF->radiusFit = 4.0;
     354        source->apRadius = 4.0;
    355355        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    356356        ok_float_tol(refMag - source->apMag, +0.0038, 0.0001, "growth offset is is %f", refMag - source->apMag);
    357357
    358         source->modelPSF->radiusFit = 3.0;
    359         pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    360         ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
    361 
    362         source->modelPSF->radiusFit = 2.0;
     358        source->apRadius = 3.0;
     359        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
     360        ok_float_tol(refMag - source->apMag, +0.0000, 0.0001, "growth offset is is %f", refMag - source->apMag);
     361
     362        source->apRadius = 2.0;
    363363        pmSourceMagnitudes (source, psf, PM_SOURCE_PHOT_GROWTH | PM_SOURCE_PHOT_INTERP, 0);
    364364        ok_float_tol(refMag - source->apMag, -0.0164, 0.0001, "growth offset is is %f", refMag - source->apMag);
  • branches/simtest_nebulous_branches/psModules/test/objects/tap_pmModel.c

    r21471 r27840  
    7777        ok(model->nDOF == 0, "pmModelAlloc() set pmModel->nDOF correctly");
    7878        ok(model->nIter == 0, "pmModelAlloc() set pmModel->nIter correctly");
    79         ok(model->radiusFit == 0, "pmModelAlloc() set pmModel->radiusFit correctly");
     79        ok(model->fitRadius == 0, "pmModelAlloc() set pmModel->fitRadius correctly");
    8080        ok(model->flags == PM_MODEL_STATUS_NONE, "pmModelAlloc() set pmModel->flags correctly");
    8181        ok(model->residuals == NULL, "pmModelAlloc() set pmModel->residuals correctly");
     
    132132        modelSrc->nIter = 3;
    133133        modelSrc->flags = PM_MODEL_STATUS_NONE;
    134         modelSrc->radiusFit = 4;
     134        modelSrc->fitRadius = 4;
    135135        pmModel *modelDst = pmModelCopy(modelSrc);
    136136        ok(modelDst != NULL && psMemCheckModel(modelDst), "pmModelCopy() returned a non-NULL pmModel");
     
    139139        ok(modelDst->nIter == 3, "pmModelCopy() set the pmModel->nIter member correctly");
    140140        ok(modelDst->flags == PM_MODEL_STATUS_NONE, "pmModelCopy() set the pmModel->flags member correctly");
    141         ok(modelDst->radiusFit == 4, "pmModelCopy() set the pmModel->radiusFit member correctly");
     141        ok(modelDst->fitRadius == 4, "pmModelCopy() set the pmModel->fitRadius member correctly");
    142142
    143143        psFree(modelSrc);
  • branches/simtest_nebulous_branches/psModules/test/objects/tap_pmModelUtils.c

    r15985 r27840  
    8181        ok(tmpModel->nIter == testModelPSF->nIter, "pmModelFromPSF() set the model->nIter correctly");
    8282        ok(tmpModel->flags == testModelPSF->flags, "pmModelFromPSF() set the model->flags correctly");
    83         ok(tmpModel->radiusFit == testModelPSF->radiusFit, "pmModelFromPSF() set the model->radiusFit correctly");
     83        ok(tmpModel->fitRadius == testModelPSF->fitRadius, "pmModelFromPSF() set the model->fitRadius correctly");
    8484        ok(tmpModel->modelFunc == testModelPSF->modelFunc, "pmModelFromPSF() set the model->modelFunc correctly");
    8585        ok(tmpModel->modelFlux == testModelPSF->modelFlux, "pmModelFromPSF() set the model->modelFlux correctly");
     
    140140        ok(tmpModel->nIter == testModelPSF->nIter, "pmModelFromPSF() set the model->nIter correctly");
    141141        ok(tmpModel->flags == testModelPSF->flags, "pmModelFromPSF() set the model->flags correctly");
    142         ok(tmpModel->radiusFit == testModelPSF->radiusFit, "pmModelFromPSF() set the model->radiusFit correctly");
     142        ok(tmpModel->fitRadius == testModelPSF->fitRadius, "pmModelFromPSF() set the model->fitRadius correctly");
    143143        ok(tmpModel->modelFunc == testModelPSF->modelFunc, "pmModelFromPSF() set the model->modelFunc correctly");
    144144        ok(tmpModel->modelFlux == testModelPSF->modelFlux, "pmModelFromPSF() set the model->modelFlux correctly");
  • branches/simtest_nebulous_branches/psModules/test/objects/tap_pmSourcePhotometry.c

    r9922 r27840  
    9696    source->modelPSF = pmModelFromPSF (modelRef, psf);
    9797    source->modelPSF->dparams->data.F32[PM_PAR_I0] = 1;
    98     source->modelPSF->radiusFit = radius;
     98    source->apRadius = radius;
    9999
    100100    // measure photometry for centered source (fractional pix : 0.5,0.5)
Note: See TracChangeset for help on using the changeset viewer.