IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

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

updates from trunk

Location:
branches/tap_branches
Files:
2 deleted
118 edited
18 copied

Legend:

Unmodified
Added
Removed
  • branches/tap_branches

  • branches/tap_branches/psModules

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

    r19595 r27838  
    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/tap_branches/psModules/src/astrom/pmAstrometryObjects.c

    r24034 r27838  
    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/tap_branches/psModules/src/astrom/pmAstrometryObjects.h

    r24021 r27838  
    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/tap_branches/psModules/src/astrom/pmAstrometryVisual.c

    r25729 r27838  
    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
  • branches/tap_branches/psModules/src/astrom/pmAstrometryVisual.h

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

    r25876 r27838  
    378378    // XXX make it optional to write out CDi_j terms, or other versions
    379379    // apply CDELT1,2 (degrees / pixel) to yield PCi,j terms of order unity
    380     if (!(wcs->wcsCDkeys)) {
     380    if (!wcs->wcsCDkeys) {
    381381
    382382      double cdelt1 = wcs->cdelt1;
     
    419419        psMetadataRemoveKey(header, "CD2_2");
    420420      }
    421     }
    422     if (wcs->wcsCDkeys) {
     421    } else {
    423422
    424423      psMetadataAddF64 (header, PS_LIST_TAIL, "CD1_1", PS_META_REPLACE, "", wcs->trans->x->coeff[1][0]);
     
    845844    int k=0;
    846845    for (int j=0; j<nSamples; j++) {
    847         double y = j * deltaY / nSamples;
     846        double y = bounds->y0 + (j * deltaY / nSamples);
    848847        for (int i=0; i<nSamples; i++) {
    849848            psPlane *s = psPlaneAlloc();
    850             s->x = i * deltaX / nSamples;
     849            s->x = bounds->x0 + (i * deltaX / nSamples);
    851850            s->y = y;
    852851            psArraySet(src, k, s);
    853852            psPlane *d = psPlaneTransformApply(NULL, trans, s);
    854853            psArraySet(dst, k, d);
    855             psFree(s);
     854            psFree(s);  // drop our refs to s and d
    856855            psFree(d);
    857856            ++k;
     
    866865    }
    867866
     867#define noCOMPARE_TRANS
    868868#ifdef COMPARE_TRANS
    869869    // compare the computed coordintes from this transform with the original
    870870    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");
    871872    for (int i=0; i<psArrayLength(dst); i++) {
    872873        psPlane *d = (psPlane *) psArrayGet(dst, i);
     
    875876        new = psPlaneTransformApply(new, newTrans, s);
    876877
    877         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" : "");
    878883    }
    879884    psFree(new);
     
    887892}
    888893
    889 bool pmAstromLinearizeTransforms(pmFPA *fpa, pmChip *chip)
    890 {
    891     psRegion    *chipBounds = pmChipPixels(chip);
    892 
    893     psPlaneTransform *newToFPA = linearFitToTransform(chip->toFPA, chipBounds);
    894     if (!newToFPA) {
    895         psFree(chipBounds);
    896         psError(PS_ERR_UNKNOWN, false, "linear fit for toFPA failed");
    897         return false;
    898     }
    899 
    900     psFree(chip->toFPA);
    901     chip->toFPA = newToFPA;
    902 
    903     psFree(chip->fromFPA);
    904     chip->fromFPA = psPlaneTransformInvert(NULL, chip->toFPA, *chipBounds, 50);
    905     if (!chip->fromFPA) {
    906         psError(PS_ERR_UNKNOWN, false, "failed to invert linear fit for toFPA");
    907         return false;
    908     }
    909 
    910     psPlane *chip0 = psPlaneAlloc();
    911     chip0->x = 0;
    912     chip0->y = 0;
    913     psPlane *chip1 = psPlaneAlloc();
    914     chip1->x = chipBounds->x1;
    915     chip1->y = chipBounds->y1;
    916 
    917     // compute bounding region for fpa
    918     psPlane *fpa0 = psPlaneTransformApply(NULL, newToFPA, chip0);
    919     psPlane *fpa1 = psPlaneTransformApply(NULL, newToFPA, chip1);
    920 
    921     psRegion *fpaBounds = psRegionAlloc(fpa0->x, fpa1->x, fpa0->y, fpa1->y);
    922     psFree(chip0);
    923     psFree(chip1);
    924     psFree(fpa0);
    925     psFree(fpa1);
    926 
    927     psPlaneTransform *newToTPA = linearFitToTransform(fpa->toTPA, fpaBounds);
    928     if (!newToTPA) {
    929         psError(PS_ERR_UNKNOWN, false, "failed to perform linear fit to toTPA");
    930         psFree(fpaBounds);
    931         return false;
    932     }
    933     psFree(fpa->toTPA);
    934     fpa->toTPA = newToTPA;
    935 
    936     // XXX: is this region ok?
    937     psFree(fpa->fromTPA);
    938     fpa->fromTPA = psPlaneTransformInvert(NULL, fpa->toTPA, *fpaBounds, 50);
    939     if (!fpa->fromTPA) {
    940         psError(PS_ERR_UNKNOWN, false, "failed to invert linear fit to toTPA");
    941         return false;
    942     }
    943 
    944     fpa->toSky->type = PS_PROJ_TAN;
    945 
    946     psFree(chipBounds);
    947     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);
    9481065
    9491066    return true;
  • branches/tap_branches/psModules/src/astrom/pmAstrometryWCS.h

    r25299 r27838  
    6363bool pmAstromWriteBilevelMosaic (psMetadata *header, const pmFPA *fpa, double tol);
    6464
    65 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);
    6667
    6768// move to pslib
  • branches/tap_branches/psModules/src/camera/pmFPA.c

    r23740 r27838  
    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/tap_branches/psModules/src/camera/pmFPAMaskWeight.c

    r25379 r27838  
    370370
    371371    psImage *image = readout->image, *mask = readout->mask, *variance = readout->variance; // Readout parts
    372 
    373372    int numCols = image->numCols, numRows = image->numRows; // Size of image
    374     int numPix = numCols * numRows;                         // Number of pixels
    375     int num = PS_MAX(sample, numPix);                       // Number we care about
     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) {
     383                    continue;
     384                }
     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
    376402    psVector *signoise = psVectorAllocEmpty(num, PS_TYPE_F32);   // Signal-to-noise values
    377403
     
    379405        // We have an image smaller than Nsubset, so just loop over the image pixels
    380406        int index = 0;                  // Index for vector
    381         for (int y = 0; y < numRows; y++) {
    382             for (int x = 0; x < numCols; x++) {
     407        for (int y = yMin; y < yMax; y++) {
     408            for (int x = xMin; x < xMax; x++) {
    383409                if ((mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) ||
    384410                    !isfinite(image->data.F32[y][x]) || !isfinite(variance->data.F32[y][x])) {
     
    397423            // Pixel coordinates
    398424            int pixel = numPix * psRandomUniform(rng);
    399             int x = pixel % numCols;
    400             int y = pixel / numCols;
     425            int x = xMin + pixel % xNum;
     426            int y = yMin + pixel / xNum;
    401427
    402428            if ((mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) ||
     
    428454    // Check valid range of correction factor
    429455    if ((isfinite(minValid) && correction < minValid) || (isfinite(maxValid) && correction > maxValid)) {
    430         psWarning("Variance renormalisation is outside valid range: %f vs %f:%f --- no correction made",
    431                   correction, minValid, maxValid);
    432         return true;
    433     }
    434 
    435     psBinaryOp(variance, variance, "*", psScalarAlloc(PS_SQR(correction), PS_TYPE_F32));
     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));
     458        return false;
     459    }
     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);
    436464
    437465    pmHDU *hdu = pmHDUFromReadout(readout); // HDU for readout
     
    444472    }
    445473
    446     return true;
     474    return psMetadataAddF32(readout->analysis, PS_LIST_TAIL, PM_READOUT_ANALYSIS_RENORM, 0,
     475                            "Renormalisation of variance", PS_SQR(correction));
    447476}
    448477
  • branches/tap_branches/psModules/src/camera/pmFPAMaskWeight.h

    r25372 r27838  
    1515/// @addtogroup Camera Camera Layout
    1616/// @{
     17
     18#define PM_READOUT_ANALYSIS_RENORM "READOUT.RENORM" // Name on analysis metadata for renormalisation
    1719
    1820/// Set a temporary readout mask using CELL.SATURATION and CELL.BAD
  • branches/tap_branches/psModules/src/camera/pmFPAMosaic.c

    r25383 r27838  
    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        }
  • branches/tap_branches/psModules/src/camera/pmFPARead.c

    r25754 r27838  
    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);
     389                psFree(iter);
    390390                return false;
    391391            }
     
    486486                             psFits *fits, // FITS file
    487487                             int z,     // Desired image plane
    488                              int *zMax, // Max plane number in this cell
     488                             int *zMax, // Max plane number in this cell
    489489                             int numScans, // Number of scans (row or col depends on CELL.READDIR); 0 for all
    490490                             int overlap, // Number of scans (row/col) to overlap between scans
     
    628628    // Blow away existing data.
    629629    // Do this before returning, so that we're not returning data from a previous read
    630     psFree(*image);
    631     *image = NULL;
     630//    psFree(*image);
     631//    *image = NULL;
    632632    *image = readoutReadComponent(*image, fits, trimsec, readdir, thisScan, lastScan, z, bad, pixelTypes[type]);
    633633
  • branches/tap_branches/psModules/src/camera/pmFPAfile.c

    r23746 r27838  
    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/tap_branches/psModules/src/camera/pmFPAfile.h

    r23487 r27838  
    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/tap_branches/psModules/src/camera/pmFPAfileDefine.c

    r23370 r27838  
    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/tap_branches/psModules/src/camera/pmFPAfileDefine.h

    r23354 r27838  
    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/tap_branches/psModules/src/camera/pmFPAfileIO.c

    r25880 r27838  
    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             if (!pmFPAfileWrite (file, view, config)) {
     79          case PM_FPA_AFTER:
     80            if (!pmFPAfileWrite (file, view, config)) {
    8081                psError(PS_ERR_IO, false, "failed WRITE in FPA_AFTER block for %s", file->name);
    8182                goto failure;
     
    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/tap_branches/psModules/src/camera/pmReadoutFake.c

    • Property svn:mergeinfo deleted
    r25883 r27838  
    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
    3030#define MODEL_MASK (PM_MODEL_STATUS_NONCONVERGE | PM_MODEL_STATUS_OFFIMAGE | \
    3131                    PM_MODEL_STATUS_BADARGS | PM_MODEL_STATUS_LIMITS) // Mask to apply to models
     32
     33
     34static bool threaded = false;           // Running threaded?
     35
     36
    3237
    3338
     
    4752    }
    4853    return pmPSF_AxesToModel(params, axes);
     54}
     55
     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;
    49236}
    50237
     
    86273    psImageInit(readout->image, 0);
    87274
    88     for (long i = 0; i < numSources; i++) {
    89         float flux = powf(10.0, -0.4 * mag->data.F32[i]); // Flux of source
    90         float xSrc = x->data.F32[i], ySrc = y->data.F32[i]; // Coordinates of source
    91 
    92         if (normalisePeak) {
    93             // Normalise flux
    94             pmModel *normModel = pmModelFromPSFforXY(psf, xSrc, ySrc, 1.0); // Model for normalisation
    95             if (!normModel || (normModel->flags & MODEL_MASK)) {
    96                 psFree(normModel);
    97                 continue;
    98             }
    99             // check that all params are valid:
    100             bool validParams = true;
    101             for (int n = 0; validParams && (n < normModel->params->n); n++) {
    102                 if (n == PM_PAR_SKY) continue;
    103                 if (n == PM_PAR_I0) continue;
    104                 if (n == PM_PAR_XPOS) continue;
    105                 if (n == PM_PAR_YPOS) continue;
    106                 if (!isfinite(normModel->params->data.F32[n])) validParams = false;
    107             }
    108             if (!validParams) {
    109                 psFree(normModel);
    110                 continue;
    111             }           
    112             if (circularise && !circulariseModel(normModel)) {
    113                 psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
    114                 psFree(normModel);
     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);
    115314                return false;
    116315            }
    117 
    118             flux /= normModel->modelFlux(normModel->params);
    119             psFree(normModel);
    120         }
    121 
    122         pmModel *fakeModel = pmModelFromPSFforXY(psf, xSrc, ySrc, flux);
    123         if (!fakeModel || (fakeModel->flags & MODEL_MASK)) {
    124             psFree(fakeModel);
    125             continue;
    126         }
    127         // check that all params are valid:
    128         bool validParams = true;
    129         for (int n = 0; validParams && (n < fakeModel->params->n); n++) {
    130             if (n == PM_PAR_SKY) continue;
    131             if (n == PM_PAR_I0) continue;
    132             if (n == PM_PAR_XPOS) continue;
    133             if (n == PM_PAR_YPOS) continue;
    134             if (!isfinite(fakeModel->params->data.F32[n])) validParams = false;
    135         }
    136         if (!validParams) {
    137             psFree(fakeModel);
    138             continue;
    139         }               
    140         if (circularise && !circulariseModel(fakeModel)) {
    141             psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
    142             psFree(fakeModel);
    143             return false;
    144         }
    145 
    146         psTrace("psModules.camera", 10, "Adding source at %f,%f with flux %f\n",
    147                 fakeModel->params->data.F32[PM_PAR_XPOS], fakeModel->params->data.F32[PM_PAR_YPOS],
    148                 fakeModel->params->data.F32[PM_PAR_I0]);
    149 
    150         pmSource *fakeSource = pmSourceAlloc(); // Fake source to generate
    151         fakeSource->peak = pmPeakAlloc(xSrc, ySrc, fakeModel->params->data.F32[PM_PAR_I0], PM_PEAK_LONE);
    152         float fakeRadius = 1.0;         // Radius of fake source
    153         if (isfinite(minFlux)) {
    154             fakeRadius = PS_MAX(fakeRadius, fakeModel->modelRadius(fakeModel->params, minFlux));
    155         }
    156         if (radius > 0) {
    157             fakeRadius = PS_MAX(fakeRadius, radius);
    158         }
    159 
    160         if (xOffset) {
    161             if (!pmSourceDefinePixels(fakeSource, readout, xSrc + xOffset->data.S32[i],
    162                                       ySrc + yOffset->data.S32[i], fakeRadius)) {
    163                 psErrorClear();
    164                 continue;
    165             }
    166             if (!pmModelAddWithOffset(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0,
    167                                       xOffset->data.S32[i], yOffset->data.S32[i])) {
    168                 psErrorClear();
    169                 continue;
    170             }
    171         } else {
    172             if (!pmSourceDefinePixels(fakeSource, readout, xSrc, ySrc, fakeRadius)) {
    173                 psErrorClear();
    174                 continue;
    175             }
    176             if (!pmModelAdd(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0)) {
    177                 psErrorClear();
    178                 continue;
    179             }
    180         }
    181         psFree(fakeSource);
    182         psFree(fakeModel);
     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);
    183340    }
    184341
    185342    return true;
     343
    186344}
    187345
  • branches/tap_branches/psModules/src/camera/pmReadoutFake.h

    r25383 r27838  
    1212#include <pmPSF.h>
    1313#include <pmSourceMasks.h>
     14
     15/// Set threading
     16///
     17/// Returns old threading state
     18bool pmReadoutFakeThreads(
     19    bool new                            // New threading state
     20    );
    1421
    1522/// Generate a fake readout from vectors
  • branches/tap_branches/psModules/src/concepts/pmConcepts.c

    r25882 r27838  
    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);
    293         // XXX p_pmConceptParse_FPA_FILTER -> p_pmConceptParse_FPA_FILTERID (and Format as well)?
     294        // XXX p_pmConceptParse_FPA_FILTER -> p_pmConceptParse_FPA_FILTERID (and Format as well)?
    294295        conceptRegisterStr("FPA.FILTERID", "Filter used (parsed, abstract name)", p_pmConceptParse_FPA_FILTER, p_pmConceptFormat_FPA_FILTER, NULL, false, PM_FPA_LEVEL_FPA);
    295296        conceptRegisterStr("FPA.FILTER", "Filter used (instrument name)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     
    333334        conceptRegisterF32("FPA.TELTEMP.EXTRA", "Telescope Temperatures: extra", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
    334335        conceptRegisterF32("FPA.PON.TIME", "Power On Time", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
    335         conceptRegisterS32("FPA.BURNTOOL.APPLIED", "Status of burntool processing", p_pmConceptParse_BTOOLAPP,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);
    336337        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);
    337339    }
    338340    if (!conceptsChip) {
     
    349351        conceptRegisterF32("CHIP.TEMP", "Temperature of chip", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
    350352        conceptRegisterStr("CHIP.ID", "Chip identifier", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
    351 
     353        conceptRegisterF32("CHIP.SEEING", "Seeing FWHM (pixels)", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
    352354    }
    353355
  • branches/tap_branches/psModules/src/concepts/pmConceptsAverage.c

    r22706 r27838  
    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/tap_branches/psModules/src/concepts/pmConceptsRead.c

    r25425 r27838  
    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",
  • branches/tap_branches/psModules/src/concepts/pmConceptsStandard.c

    r25882 r27838  
    753753    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not BOOL\n",
    754754            concept->name, concept->type);
    755     return NULL;
     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    }
    756768  }
    757769
     
    765777  return psMetadataItemAllocS32(concept->name, concept->comment, bt_status);
    766778
     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
    767807
    768808// Get the current value of a concept
  • branches/tap_branches/psModules/src/concepts/pmConceptsStandard.h

    r25882 r27838  
    146146    const pmCell *cell ///< Cell for concept, or NULL
    147147    );
     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    );
    148156
    149157
  • branches/tap_branches/psModules/src/concepts/pmConceptsWrite.c

    r23623 r27838  
    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/tap_branches/psModules/src/config/Makefile.am

    r23794 r27838  
    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/tap_branches/psModules/src/config/pmConfig.c

    r24496 r27838  
    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/tap_branches/psModules/src/config/pmConfigCamera.c

    r23452 r27838  
    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/tap_branches/psModules/src/config/pmConfigDump.c

    r23753 r27838  
    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/tap_branches/psModules/src/config/pmConfigRun.c

    r23748 r27838  
    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/tap_branches/psModules/src/detrend/Makefile.am

    r24891 r27838  
    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/tap_branches/psModules/src/detrend/pmDark.c

    r24869 r27838  
    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/tap_branches/psModules/src/detrend/pmFringeStats.c

    r24912 r27838  
    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/tap_branches/psModules/src/detrend/pmPattern.c

    r24905 r27838  
    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/tap_branches/psModules/src/detrend/pmPattern.h

    r24903 r27838  
    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/tap_branches/psModules/src/detrend/pmShutterCorrection.c

    r23989 r27838  
    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/tap_branches/psModules/src/extras/Makefile.am

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

    r24880 r27838  
    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/tap_branches/psModules/src/imcombine/Makefile.am

    r23242 r27838  
    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/tap_branches/psModules/src/imcombine/pmImageCombine.c

    r23989 r27838  
    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/tap_branches/psModules/src/imcombine/pmPSFEnvelope.c

    r25754 r27838  
    6565                     int radius,        // Radius of each PSF
    6666                     const char *modelName,// Name of PSF model to use
    67                      int xOrder, int yOrder // Order for PSF variation fit
     67                     int xOrder, int yOrder, // Order for PSF variation fit
     68                     psImageMaskType maskVal
    6869                     )
    6970{
     
    151152        // Test PSF
    152153        {
    153             bool goodPSF = true;                                                                // Good PSF?
    154             pmModelClassSetLimits(PM_MODEL_LIMITS_IGNORE);
    155             pmModel *model = pmModelFromPSFforXY(psf, numCols / 2.0, numRows / 2.0, PEAK_FLUX); // Test model
    156             model->modelSetLimits(PM_MODEL_LIMITS_STRICT);
    157             for (int j = 0; j < model->params->n && goodPSF; j++) {
    158                 if (!model->modelLimits(PS_MINIMIZE_PARAM_MIN, j, model->params->data.F32, NULL) ||
    159                     !model->modelLimits(PS_MINIMIZE_PARAM_MAX, j, model->params->data.F32, NULL)) {
    160                     goodPSF = false;
     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                    }
    161177                }
    162178            }
    163             psFree(model);
    164179            if (!goodPSF) {
    165                 psWarning("PSF %d is bad --- not including in envelope calculation.", i);
     180                psWarning("PSF %d is completely bad --- not including in envelope calculation.", i);
    166181                continue;
    167182            }
     
    170185        pmResiduals *resid = psf->residuals;// PSF residuals
    171186        psf->residuals = NULL;
     187        pmModelClassSetLimits(PM_MODEL_LIMITS_MODERATE);
    172188        if (!pmReadoutFakeFromSources(fakeRO, fakeSize, fakeSize, fakes, 0, xOffset, yOffset, psf,
    173189                                      NAN, radius, true, false)) {
     
    224240            }
    225241            float srcRadius = model->modelRadius(model->params, PS_SQR(VARIANCE_VAL)); // Radius for source
     242            psFree(model);
    226243            if (srcRadius == 0) {
    227244                continue;
     
    360377
    361378        // measure the source moments: tophat windowing, no pixel S/N cutoff
    362         if (!pmSourceMoments(source, maxRadius, 0.0, 1.0)) {
     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)) {
    363381            // Can't do anything about it; limp along as best we can
    364382            psErrorClear();
     
    393411
    394412    pmSourceFitModelInit(SOURCE_FIT_ITERATIONS, 0.01, VARIANCE_VAL, true);
     413    pmModelClassSetLimits(PM_MODEL_LIMITS_STRICT); // Important for getting a good stack target PSF
    395414
    396415    pmPSFtry *try = pmPSFtryModel(fakes, modelName, options, 0, 0xff);
  • branches/tap_branches/psModules/src/imcombine/pmPSFEnvelope.h

    r15837 r27838  
    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/tap_branches/psModules/src/imcombine/pmStack.c

    r25380 r27838  
    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/tap_branches/psModules/src/imcombine/pmStack.h

    r23577 r27838  
    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/tap_branches/psModules/src/imcombine/pmStackReject.c

    r25468 r27838  
    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);
     
    99103
    100104    if (!pmSubtractionThreaded()) {
    101         pmSubtractionThreadsInit(NULL, NULL);
     105        pmSubtractionThreadsInit();
    102106    }
    103107
     
    113117
    114118
    115 psPixels *pmStackReject(const psPixels *in, int numCols, int numRows, float threshold, float poorFrac,
    116                         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)
    117121{
    118122    PS_ASSERT_PIXELS_NON_NULL(in, NULL);
     
    150154    pmReadout *inRO = pmReadoutAlloc(NULL); // Readout with input image
    151155    inRO->image = image;
     156    convRO->image = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
    152157    for (int i = 0; i < numRegions; i++) {
    153158        psRegion *region = subRegions->data[i]; // Region of interest
    154159        pmSubtractionKernels *kernels = subKernels->data[i]; // Kernel of interest
    155         if (!pmSubtractionConvolve(NULL, convRO, NULL, inRO, 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,
    156161                                   region, kernels, false, true)) {
    157             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);
    158163            psFree(convRO);
    159164            psFree(inRO);
     
    165170
    166171        // Image of the kernel at the centre of the region
    167         float xNorm = (region->x0 + 0.5 * (region->x1 - region->x0) - kernels->numCols/2.0) /
    168             (float)kernels->numCols;
    169         float yNorm = (region->y0 + 0.5 * (region->y1 - region->y0) - kernels->numRows/2.0) /
    170             (float)kernels->numRows;
    171         psImage *kernel = pmSubtractionKernelImage(kernels, xNorm, yNorm, false);
     172        psImage *kernel = pmSubtractionKernelImage(kernels, 0.5, 0.5, false);
    172173        if (!kernel) {
    173             psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
     174            psError(psErrorCodeLast(), false, "Unable to generate kernel image.");
    174175            psFree(convRO);
    175176            psFree(inRO);
     
    223224    }
    224225    psTrace("psModules.imcombine", 7, "Found %ld bad pixels", bad->n);
    225 
    226     // Now, grow the mask to include everything that touches a bad pixel in the convolution
    227     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),
    228241                                     PM_STACK_MASK_BAD); // Mask image to grow
    229242
     
    244257    bool oldThreads = psImageConvolveSetThreads(false); // Old value of threading for psImageColvolve
    245258
    246     psImage *target = psImageRecycle(convolved, numCols, numRows, PS_TYPE_IMAGE_MASK); // Grown image
     259    psImage *target = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK); // Grown image
    247260    psImageInit(target, 0);
    248261    for (int i = 0; i < subRegions->n; i++) {
     
    284297                } else if (!stackRejectGrow(target, source, kernels, numCols, numRows,
    285298                                            i, xSubMax, j, ySubMax, poorFrac)) {
    286                     psError(PS_ERR_UNKNOWN, false, "Unable to grow bad pixels.");
     299                    psError(psErrorCodeLast(), false, "Unable to grow bad pixels.");
    287300                    psFree(source);
    288301                    psFree(target);
     
    294307
    295308    if (!psThreadPoolWait(false)) {
    296         psError(PS_ERR_UNKNOWN, false, "Unable to grow bad pixels.");
     309        psError(psErrorCodeLast(), false, "Unable to grow bad pixels.");
    297310        psFree(source);
    298311        psFree(target);
     
    327340
    328341    psFree(source);
    329     bad = psPixelsFromMask(bad, target, PM_STACK_MASK_ALL);
     342    psPixels *bad = psPixelsFromMask(NULL, target, PM_STACK_MASK_ALL); // All bad pixels
     343    psFree(target);
    330344    psTrace("psModules.imcombine", 7, "Total %ld bad pixels", bad->n);
    331345
  • branches/tap_branches/psModules/src/imcombine/pmStackReject.h

    r20568 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtraction.c

    r25279 r27838  
    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,
    735780                              const psVector *deviations, psImage *subMask, float sigmaRej)
     
    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);
     
    825870    int numGood = 0;                    // Number of good stamps
    826871    double newMean = 0.0;               // New mean
     872    psString log = NULL;                // Log message
     873    psStringAppend(&log, "Rejecting stamps, mean = %f, threshold = %f\n", mean, limit);
    827874    for (int i = 0; i < stamps->num; i++) {
    828875        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    837884                // Mask out the stamp in the image so you it's not found again
    838885                psTrace("psModules.imcombine", 3, "Rejecting stamp %d (%d,%d)\n", i,
    839                         (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));
    840890                numRejected++;
    841891                for (int y = stamp->y - footprint; y <= stamp->y + footprint; y++) {
     
    858908                psFree(stamp->image1);
    859909                psFree(stamp->image2);
    860                 psFree(stamp->variance);
    861                 stamp->image1 = stamp->image2 = stamp->variance = NULL;
    862                 psFree(stamp->matrix1);
    863                 psFree(stamp->matrix2);
    864                 psFree(stamp->matrixX);
    865                 stamp->matrix1 = stamp->matrix2 = stamp->matrixX = NULL;
    866                 psFree(stamp->vector1);
    867                 psFree(stamp->vector2);
    868                 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;
    869916            } else {
    870917                numGood++;
     
    875922    }
    876923    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);
    877930
    878931    if (ds9) {
     
    901954
    902955    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y); // Solved polynomial
    903     psKernel *kernel = solvedKernel(NULL, kernels, polyValues, wantDual); // The appropriate kernel
     956    psKernel *kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual); // The appropriate kernel
    904957    psFree(polyValues);
    905958
     
    932985    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y);
    933986
    934     psKernel *kernel = solvedKernel(NULL, kernels, polyValues, wantDual); // The appropriate kernel
     987    psKernel *kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual); // The appropriate kernel
    935988    psFree(polyValues);
    936989
     
    9491002}
    9501003
    951 #if 0
     1004#if 1
    9521005psArray *pmSubtractionKernelSolutions(const pmSubtractionKernels *kernels, float x, float y, bool wantDual)
    9531006{
     
    9571010    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NULL);
    9581011
    959     psArray *images = psArrayAlloc(kernels->solution1->n - 1); // Images of each kernel to return
    960     psVector *fakeSolution = psVectorAlloc(kernels->solution1->n, PS_TYPE_F64); // Fake solution vector
    961     psVectorInit(fakeSolution, 0.0);
    962 
    963     for (int i = 0; i < kernels->solution1->n - 1; i++) {
    964         fakeSolution->data.F64[i] = kernels->solution1->data.F64[i];
    965         images->data[i] = pmSubtractionKernelImage(kernels, x, y, wantDual);
    966         fakeSolution->data.F64[i] = 0.0;
    967     }
    968 
    969     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);
    9701049
    9711050    return images;
     
    9801059                                     psImage *convMask, // Output convolved mask
    9811060                                     const pmReadout *ro1, const pmReadout *ro2, // Input readouts
    982                                      psImage *sys1, psImage *sys2, // Systematic error images
     1061                                     psImage *kernelErr1, psImage *kernelErr2, // Kernel error images
    9831062                                     psImage *subMask, // Input subtraction mask
    9841063                                     psImageMaskType maskBad, // Mask value to give bad pixels
     
    9991078    // Only generate polynomial values every kernel footprint, since we have already assumed
    10001079    // (with the stamps) that it does not vary rapidly on this scale.
    1001     psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
    1002                                                               xMin + x0 + size + 1,
    1003                                                               yMin + y0 + size + 1);
     1080    psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, xMin + x0 + size + 1,
     1081                                                              yMin + y0 + size + 1);        // Polynomial
    10041082    float background = doBG ? p_pmSubtractionSolutionBackground(kernels, polyValues) : 0.0; // Background term
    10051083
    10061084    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1007         convolveRegion(out1->image, out1->variance, convMask, &kernelImage, &kernelVariance,
    1008                        ro1->image, ro1->variance, sys1, subMask, kernels, polyValues, background, *region,
    1009                        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);
    10101088    }
    10111089    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1012         convolveRegion(out2->image, out2->variance, convMask, &kernelImage, &kernelVariance,
    1013                        ro2->image, ro2->variance, sys2, subMask, kernels, polyValues, background, *region,
    1014                        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);
    10151094    }
    10161095
     
    10201099
    10211100    if ((kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) && ro1->mask) {
    1022         psImageMaskType **target = convMask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
     1101        psImageMaskType **target = out1->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
    10231102        psImageMaskType **source = ro1->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Source mask
    10241103
     
    10301109    }
    10311110    if ((kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) && ro2->mask) {
    1032         psImageMaskType **target = convMask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
     1111        psImageMaskType **target = out2->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
    10331112        psImageMaskType **source = ro2->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Source mask
    10341113
     
    10571136    const pmReadout *ro1 = args->data[7]; // Input readout 1
    10581137    const pmReadout *ro2 = args->data[8]; // Input readout 2
    1059     psImage *sys1 = args->data[9]; // Systematic error image 1
    1060     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
    10611140    psImage *subMask = args->data[11]; // Subtraction mask
    10621141    psImageMaskType maskBad = PS_SCALAR_VALUE(args->data[12], PS_TYPE_IMAGE_MASK_DATA); // Output mask value for bad pixels
     
    10681147    bool useFFT = PS_SCALAR_VALUE(args->data[18], U8); // Use FFT for convolution?
    10691148
    1070     return subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2, sys1, sys2,
    1071                                     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);
    10721152}
    10731153
    10741154bool pmSubtractionConvolve(pmReadout *out1, pmReadout *out2, const pmReadout *ro1, const pmReadout *ro2,
    10751155                           psImage *subMask, int stride, psImageMaskType maskBad, psImageMaskType maskPoor,
    1076                            float poorFrac, float sysError, const psRegion *region,
     1156                           float poorFrac, float kernelError, float covarFrac, const psRegion *region,
    10771157                           const pmSubtractionKernels *kernels, bool doBG, bool useFFT)
    10781158{
     
    10831163        PM_ASSERT_READOUT_NON_NULL(ro1, false);
    10841164        PM_ASSERT_READOUT_IMAGE(ro1, false);
     1165        PM_ASSERT_READOUT_IMAGE(out1, false);
    10851166        numCols = ro1->image->numCols;
    10861167        numRows = ro1->image->numRows;
     
    10921173        PM_ASSERT_READOUT_NON_NULL(ro2, false);
    10931174        PM_ASSERT_READOUT_IMAGE(ro2, false);
     1175        PM_ASSERT_READOUT_IMAGE(out2, false);
    10941176        if (numCols == 0 && numRows == 0) {
    10951177            numCols = ro2->image->numCols;
     
    11121194    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(poorFrac, 0.0, false);
    11131195    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(poorFrac, 1.0, false);
    1114     PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(sysError, 0.0, false);
    1115     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);
    11161200    if (region && psRegionIsNaN(*region)) {
    11171201        psString string = psRegionToString(*region);
    1118         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);
    11191203        psFree(string);
    11201204        return false;
     
    11251209    bool threaded = pmSubtractionThreaded(); // Running threaded?
    11261210
    1127     // Outputs
    1128     if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1129         if (!out1->image) {
    1130             out1->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1131             // XXX if (threaded) {
    1132             // XXX     psMutexInit(out1->image);
    1133             // XXX }
    1134         }
    1135         if (ro1->variance) {
    1136             if (!out1->variance) {
    1137                 out1->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1138                 // XXX if (threaded) {
    1139                 // XXX     psMutexInit(out1->variance);
    1140                 // XXX }
    1141             }
    1142             psImageInit(out1->variance, 0.0);
    1143         }
    1144     }
    1145     if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1146         if (!out2->image) {
    1147             out2->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1148             // XXX if (threaded) {
    1149             // XXX     psMutexInit(out2->image);
    1150             // XXX }
    1151         }
    1152         if (ro2->variance) {
    1153             if (!out2->variance) {
    1154                 out2->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1155                 // XXX if (threaded) {
    1156                 // XXX     psMutexInit(out2->variance);
    1157                 // XXX }
    1158             }
    1159             psImageInit(out2->variance, 0.0);
    1160         }
    1161     }
    11621211    psImage *convMask = NULL;           // Convolved mask image (common to inputs 1 and 2)
    11631212    if (subMask) {
    1164         // XXX if (threaded) {
    1165         // XXX     psMutexInit(subMask);
    1166         // XXX }
    11671213        if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1168             if (!out1->mask) {
    1169                 out1->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    1170             }
    11711214            convMask = out1->mask;
    11721215        }
    11731216        if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1174             if (convMask) {
    1175                 if (out2->mask) {
    1176                     psFree(out2->mask);
    1177                 }
    1178                 out2->mask = psMemIncrRefCounter(convMask);
    1179             } else {
    1180                 if (!out2->mask) {
    1181                     out2->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    1182                 }
     1217            if (!convMask) {
    11831218                convMask = out2->mask;
    11841219            }
    11851220        }
    1186         psImageInit(convMask, 0);
    1187     }
    1188 
    1189     psImage *sys1 = NULL, *sys2 = NULL; // Systematic error images
     1221    }
     1222
     1223    psImage *kernelErr1 = NULL, *kernelErr2 = NULL; // Kernel error images
     1224#ifdef USE_KERNEL_ERR
    11901225    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1191         sys1 = subtractionSysErrImage(ro1->image, sysError);
    1192         // XXX if (threaded && sys1) {
    1193         // XXX     psMutexInit(sys1);
    1194         // XXX }
     1226        kernelErr1 = subtractionKernelErrImage(ro1->image, kernelError);
    11951227    }
    11961228    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1197         sys2 = subtractionSysErrImage(ro2->image, sysError);
    1198         // XXX if (threaded && sys2) {
    1199         // XXX     psMutexInit(sys2);
    1200         // XXX }
    1201     }
     1229        kernelErr2 = subtractionKernelErrImage(ro2->image, kernelError);
     1230    }
     1231#endif
    12021232
    12031233    int size = kernels->size;           // Half-size of kernel
    12041234
    12051235    // Get region for convolution: [xMin:xMax,yMin:yMax]
    1206     int xMin = size, xMax = numCols - size;
    1207     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;
    12081238    if (region) {
    12091239        xMin = PS_MAX(region->x0, xMin);
     
    12481278                psArrayAdd(args, 1, (pmReadout*)ro1); // Casting away const
    12491279                psArrayAdd(args, 1, (pmReadout*)ro2); // Casting away const
    1250                 // Since adding to the array can impact the reference count, we need to lock
    1251                 // XXX if (sys1) {
    1252                 // XXX     psMutexLock(sys1);
    1253                 // XXX }
    1254                 psArrayAdd(args, 1, sys1);
    1255                 // XXX if (sys1) {
    1256                 // XXX     psMutexUnlock(sys1);
    1257                 // XXX }
    1258                 // XXX if (sys2) {
    1259                 // XXX     psMutexLock(sys2);
    1260                 // XXX }
    1261                 psArrayAdd(args, 1, sys2);
    1262                 // XXX if (sys2) {
    1263                 // XXX     psMutexUnlock(sys2);
    1264                 // XXX }
    1265                 // XXX if (subMask) {
    1266                 // XXX     psMutexLock(subMask);
    1267                 // XXX }
     1280                psArrayAdd(args, 1, kernelErr1);
     1281                psArrayAdd(args, 1, kernelErr2);
    12681282                psArrayAdd(args, 1, subMask);
    1269                 // XXX if (subMask) {
    1270                 // XXX     psMutexUnlock(subMask);
    1271                 // XXX }
    12721283                PS_ARRAY_ADD_SCALAR(args, maskBad, PS_TYPE_IMAGE_MASK);
    12731284                PS_ARRAY_ADD_SCALAR(args, maskPoor, PS_TYPE_IMAGE_MASK);
     
    12841295                psFree(job);
    12851296            } else {
    1286                 subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2, sys1, sys2,
    1287                                          subMask, maskBad, maskPoor, poorFrac, subRegion, kernels, doBG,
    1288                                          useFFT);
     1297                subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2,
     1298                                         kernelErr1, kernelErr2, subMask, maskBad, maskPoor, poorFrac,
     1299                                         subRegion, kernels, doBG, useFFT);
    12891300            }
    12901301            psFree(subRegion);
     
    12931304
    12941305    if (!psThreadPoolWait(false)) {
    1295         psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
     1306        psError(psErrorCodeLast(), false, "Error waiting for threads.");
    12961307        return false;
    12971308    }
     
    13081319            psFree(job);
    13091320        }
    1310 
    1311         // XXX if (subMask) {
    1312         // XXX     psMutexDestroy(subMask);
    1313         // XXX }
    1314         // XXX if (sys1) {
    1315         // XXX     psMutexDestroy(sys1);
    1316         // XXX }
    1317         // XXX if (sys2) {
    1318         // XXX     psMutexDestroy(sys2);
    1319         // XXX }
    13201321    }
    13211322    psImageConvolveSetThreads(oldThreads);
    13221323
    1323     psFree(sys1);
    1324     psFree(sys2);
     1324    psFree(kernelErr1);
     1325    psFree(kernelErr2);
    13251326
    13261327    // Calculate covariances
    1327     // This can take a while, so we only do it for a single instance
    1328     // 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);
    13291331    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    13301332        psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
     1333        psKernelTruncate(kernel, covarFrac);
    13311334        out1->covariance = psImageCovarianceCalculate(kernel, ro1->covariance);
    13321335        psFree(kernel);
     
    13351338        psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0,
    13361339                                               kernels->mode == PM_SUBTRACTION_MODE_DUAL); // Conv. kernel
     1340        psKernelTruncate(kernel, covarFrac);
    13371341        out2->covariance = psImageCovarianceCalculate(kernel, ro2->covariance);
    13381342        psFree(kernel);
    13391343    }
     1344    psImageCovarianceSetThreads(oldThreads);
    13401345
    13411346    // Copy anything that wasn't convolved
     
    13771382    }
    13781383
    1379 
    13801384    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolve image: %f sec",
    13811385             psTimerClear("pmSubtractionConvolve"));
  • branches/tap_branches/psModules/src/imcombine/pmSubtraction.h

    r25279 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionAnalysis.c

    r25279 r27838  
    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,
     
    7374                                                           false); // Image of the kernel
    7475                if (!kernel) {
    75                     psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
     76                    psError(psErrorCodeLast(), false, "Unable to generate kernel image.");
    7677                    psFree(convKernels);
    7778                    return false;
     
    8081                if (psImageOverlaySection(convKernels, kernel, (i + KERNEL_MOSAIC) * fullSize,
    8182                                          (j + KERNEL_MOSAIC) * fullSize, "=") == 0) {
    82                     psError(PS_ERR_UNKNOWN, false, "Unable to overlay kernel image.");
     83                    psError(psErrorCodeLast(), false, "Unable to overlay kernel image.");
    8384                    psFree(kernel);
    8485                    psFree(convKernels);
     
    9293                                                      true); // Image of the kernel
    9394                    if (!kernel) {
    94                         psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
     95                        psError(psErrorCodeLast(), false, "Unable to generate kernel image.");
    9596                        psFree(convKernels);
    9697                        return false;
     
    100101                                              (2 * KERNEL_MOSAIC + 1 + i + KERNEL_MOSAIC) * fullSize + 4,
    101102                                              (j + KERNEL_MOSAIC) * fullSize, "=") == 0) {
    102                         psError(PS_ERR_UNKNOWN, false, "Unable to overlay kernel image.");
     103                        psError(psErrorCodeLast(), false, "Unable to overlay kernel image.");
    103104                        psFree(kernel);
    104105                        psFree(convKernels);
     
    116117    }
    117118
    118 
    119 #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
    120125    // Generate images of the kernel components
    121126    {
     
    128133        }
    129134        psArray *kernelImages = pmSubtractionKernelSolutions(kernels, 0.0, 0.0, false);
    130         psFits *kernelFile = psFitsOpen("kernels.fits", "w");
     135        psFits *kernelFile = psFitsOpen("kernels1.fits", "w");
    131136        (void)psFitsWriteImageCube(kernelFile, header, kernelImages, NULL);
    132137        psFitsClose(kernelFile);
     
    134139        psFree(header);
    135140    }
     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    }
    136156#endif
    137157
     
    141161        psImage *image = pmSubtractionKernelImage(kernels, 0.5, 0.5, false); // Image of the kernel
    142162        if (!image) {
    143             psError(PS_ERR_UNKNOWN, false, "Unable to generate image of kernel.");
     163            psError(psErrorCodeLast(), false, "Unable to generate image of kernel.");
    144164            return false;
    145165        }
     
    196216        psImage *image = pmSubtractionKernelImage(kernels, 0.5, 0.5, false); // Image of the kernel
    197217        if (!image) {
    198             psError(PS_ERR_UNKNOWN, false, "Unable to generate image of kernel.");
     218            psError(psErrorCodeLast(), false, "Unable to generate image of kernel.");
    199219            return false;
    200220        }
     
    253273    // Difference in background
    254274    {
    255         psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
    256                                                                   numCols / 2.0, numRows / 2.0); // Polynomial
     275        psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, 0.0, 0.0); // Polynomial
    257276        float bg = p_pmSubtractionSolutionBackground(kernels, polyValues); // Background difference
    258277
     
    279298        psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_DEV_RMS, 0, "RMS stamp deviation",
    280299                         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);
    281314    }
    282315
  • branches/tap_branches/psModules/src/imcombine/pmSubtractionAnalysis.h

    r25060 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionEquation.c

    r24844 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionEquation.h

    r19299 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionIO.c

    r23378 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionKernels.c

    r25120 r27838  
    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    }
     
    7891255    out->bgOrder = in->bgOrder;
    7901256    out->mode = in->mode;
    791     out->numCols = in->numCols;
    792     out->numRows = in->numRows;
     1257    out->xMin = in->xMin;
     1258    out->xMax = in->xMax;
     1259    out->yMin = in->yMin;
     1260    out->yMax = in->yMax;
    7931261    out->solution1 = in->solution1 ? psVectorCopy(NULL, in->solution1, PS_TYPE_F64) : NULL;
    7941262    out->solution2 = in->solution2 ? psVectorCopy(NULL, in->solution2, PS_TYPE_F64) : NULL;
     1263    out->sampleStamps = psMemIncrRefCounter(in->sampleStamps);
    7951264
    7961265    return out;
  • branches/tap_branches/psModules/src/imcombine/pmSubtractionKernels.h

    r25101 r27838  
    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    );
  • branches/tap_branches/psModules/src/imcombine/pmSubtractionMask.c

    r24257 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionMask.h

    r23851 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionMatch.c

    r25464 r27838  
    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            }
    478659
    479660            // Define kernel basis functions
    480661            if (optimum && (type == PM_SUBTRACTION_KERNEL_ISIS || type == PM_SUBTRACTION_KERNEL_GUNK)) {
    481                 kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder, optFWHMs, optOrder,
    482                                                           stamps, footprint, optThreshold, penalty, subMode);
     662                kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder,
     663                                                          optFWHMs, optOrder, stamps, footprint,
     664                                                          optThreshold, penalty, bounds, subMode);
    483665                if (!kernels) {
    484666                    psErrorClear();
     
    489671                // Not an ISIS/GUNK kernel, or the optimum kernel search failed
    490672                kernels = pmSubtractionKernelsGenerate(type, size, spatialOrder, isisWidths, isisOrders,
    491                                                        inner, binning, ringsOrder, penalty, subMode);
     673                                                       inner, binning, ringsOrder, penalty, bounds, subMode);
     674                // pmSubtractionVisualShowKernels(kernels);
    492675            }
    493676
     
    500683                psVector *buffer = NULL;// Buffer for stats
    501684                if (!psImageBackground(bgStats, &buffer, ro1->image, ro1->mask, maskVal, rng)) {
    502                     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.");
    503686                    psFree(bgStats);
    504687                    psFree(buffer);
     
    507690                float bg1 = psStatsGetValue(bgStats, BG_STAT); // Background for image 1
    508691                if (!psImageBackground(bgStats, &buffer, ro2->image, ro2->mask, maskVal, rng)) {
    509                     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.");
    510693                    psFree(bgStats);
    511694                    psFree(buffer);
     
    527710                    break;
    528711                  default:
    529                     psError(PS_ERR_UNKNOWN, false, "Unable to determine subtraction order.");
     712                    psError(psErrorCodeLast(), false, "Unable to determine subtraction order.");
    530713                    goto MATCH_ERROR;
    531714                }
     
    538721
    539722                if (!subtractionGetStamps(&stamps, ro1, ro2, subMask, variance, region,
    540                                           stampThresh1, stampThresh2, stampSpacing,
    541                                           size, footprint, subMode)) {
    542                     goto MATCH_ERROR;
    543                 }
    544 
    545                 psTrace("psModules.imcombine", 3, "Calculating equation...\n");
    546                 if (!pmSubtractionCalculateEquation(stamps, kernels)) {
    547                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    548                     goto MATCH_ERROR;
    549                 }
    550 
     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                }
    551758                memCheck("  calculate equation");
    552759
    553                 psTrace("psModules.imcombine", 3, "Solving equation...\n");
    554 
    555                 if (!pmSubtractionSolveEquation(kernels, stamps)) {
    556                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    557                     goto MATCH_ERROR;
    558                 }
    559 
     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                }
    560765                memCheck("  solve equation");
    561 
     766# endif
    562767                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    563768                if (!deviations) {
    564                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
     769                    psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
    565770                    goto MATCH_ERROR;
    566771                }
     
    571776                numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, rej);
    572777                if (numRejected < 0) {
    573                     psError(PS_ERR_UNKNOWN, false, "Unable to reject stamps.");
     778                    psError(psErrorCodeLast(), false, "Unable to reject stamps.");
    574779                    psFree(deviations);
    575780                    goto MATCH_ERROR;
     
    580785            }
    581786
     787            // if we hit the max number of iterations and we have rejected stamps, re-solve
    582788            if (numRejected > 0) {
    583                 psTrace("psModules.imcombine", 3, "Solving equation...\n");
    584                 if (!pmSubtractionSolveEquation(kernels, stamps)) {
    585                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    586                     goto MATCH_ERROR;
    587                 }
     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
    588822                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    589823                if (!deviations) {
    590                     psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
     824                    psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
    591825                    goto MATCH_ERROR;
    592826                }
     
    600834
    601835            if (!pmSubtractionAnalysis(analysis, header, kernels, region, numCols, numRows)) {
    602                 psError(PS_ERR_UNKNOWN, false, "Unable to generate QA data");
     836                psError(psErrorCodeLast(), false, "Unable to generate QA data");
    603837                goto MATCH_ERROR;
    604838            }
     
    608842            psTrace("psModules.imcombine", 2, "Convolving...\n");
    609843            if (!pmSubtractionConvolve(conv1, conv2, ro1, ro2, subMask, stride, maskBad, maskPoor, poorFrac,
    610                                        sysError, region, kernels, true, useFFT)) {
    611                 psError(PS_ERR_UNKNOWN, false, "Unable to convolve image.");
     844                                       kernelError, covarFrac, region, kernels, true, useFFT)) {
     845                psError(psErrorCodeLast(), false, "Unable to convolve image.");
    612846                goto MATCH_ERROR;
    613847            }
     
    629863
    630864    if (conv1 && !pmSubtractionBorder(conv1->image, conv1->variance, conv1->mask, size, maskBad)) {
    631         psError(PS_ERR_UNKNOWN, false, "Unable to set border of convolved image.");
     865        psError(psErrorCodeLast(), false, "Unable to set border of convolved image.");
    632866        goto MATCH_ERROR;
    633867    }
    634868    if (conv2 && !pmSubtractionBorder(conv2->image, conv2->variance, conv2->mask, size, maskBad)) {
    635         psError(PS_ERR_UNKNOWN, false, "Unable to set border of convolved image.");
     869        psError(psErrorCodeLast(), false, "Unable to set border of convolved image.");
    636870        goto MATCH_ERROR;
    637871    }
     
    8321066        } else {
    8331067            if (!pmSubtractionOrderStamp(ratios, mask, stamps, models, modelSums, i, bg1, bg2)) {
    834                 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);
    8351069                psFree(models);
    8361070                psFree(modelSums);
     
    8431077
    8441078    if (!psThreadPoolWait(true)) {
    845         psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
     1079        psError(psErrorCodeLast(), false, "Error waiting for threads.");
    8461080        psFree(models);
    8471081        psFree(modelSums);
     
    8561090    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
    8571091    if (!psVectorStats(stats, ratios, NULL, mask, 0xff)) {
    858         psError(PS_ERR_UNKNOWN, false, "Unable to calculate statistics for moments ratio.");
     1092        psError(psErrorCodeLast(), false, "Unable to calculate statistics for moments ratio.");
    8591093        psFree(mask);
    8601094        psFree(ratios);
     
    8901124    assert(kernels);
    8911125
    892     psTrace("psModules.imcombine", 3, "Calculating %s equation...\n", description);
    893     if (!pmSubtractionCalculateEquation(stamps, kernels)) {
    894         psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     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.");
    8951129        return false;
    8961130    }
    8971131
    898     psTrace("psModules.imcombine", 3, "Solving %s equation...\n", description);
    899     if (!pmSubtractionSolveEquation(kernels, stamps)) {
    900         psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     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.");
    9011135        return false;
    9021136    }
     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
    9031154
    9041155    psTrace("psModules.imcombine", 3, "Calculate %s deviations...\n", description);
    9051156    psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    9061157    if (!deviations) {
    907         psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
     1158        psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
    9081159        return false;
    9091160    }
     
    9121163    long numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, rej);
    9131164    if (numRejected < 0) {
    914         psError(PS_ERR_UNKNOWN, false, "Unable to reject stamps.");
     1165        psError(psErrorCodeLast(), false, "Unable to reject stamps.");
    9151166        psFree(deviations);
    9161167        return false;
     
    9201171    if (numRejected > 0) {
    9211172        // 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
    9221179        psTrace("psModules.imcombine", 3, "Resolving %s equation...\n", description);
    923         if (!pmSubtractionSolveEquation(kernels, stamps)) {
    924             psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     1180        if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_ALL)) {
     1181            psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    9251182            return false;
    9261183        }
    9271184        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
    9281204        psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    9291205        if (!deviations) {
    930             psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
     1206            psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
    9311207            return false;
    9321208        }
     
    9341210        long numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, NAN);
    9351211        if (numRejected < 0) {
    936             psError(PS_ERR_UNKNOWN, false, "Unable to reject stamps.");
     1212            psError(psErrorCodeLast(), false, "Unable to reject stamps.");
    9371213            psFree(deviations);
    9381214            return false;
     
    9581234
    9591235    if (!subtractionModeTest(stamps1, kernels1, "convolve 1", subMask1, rej)) {
    960         psError(PS_ERR_UNKNOWN, false, "Unable to test subtraction with convolution of image 1");
     1236        psError(psErrorCodeLast(), false, "Unable to test subtraction with convolution of image 1");
    9611237        psFree(stamps1);
    9621238        psFree(kernels1);
     
    9731249
    9741250    if (!subtractionModeTest(stamps2, kernels2, "convolve 2", subMask2, rej)) {
    975         psError(PS_ERR_UNKNOWN, false, "Unable to test subtraction with convolution of image 2");
     1251        psError(psErrorCodeLast(), false, "Unable to test subtraction with convolution of image 2");
    9761252        psFree(stamps2);
    9771253        psFree(kernels2);
     
    10121288}
    10131289
     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/tap_branches/psModules/src/imcombine/pmSubtractionMatch.h

    r25120 r27838  
    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
     
    9398    );
    9499
     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
     112
    95113#endif
  • branches/tap_branches/psModules/src/imcombine/pmSubtractionParams.c

    r21363 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionParams.h

    r18287 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionStamps.c

    r25120 r27838  
    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;
     
    238248    out->y = NULL;
    239249    out->flux = NULL;
     250    out->window = psMemIncrRefCounter(in->window);
    240251    out->footprint = in->footprint;
     252    out->normWindow = in->normWindow;
    241253
    242254    for (int i = 0; i < num; i++) {
     
    256268        outStamp->image1 = inStamp->image1 ? psKernelCopy(inStamp->image1) : NULL;
    257269        outStamp->image2 = inStamp->image2 ? psKernelCopy(inStamp->image2) : NULL;
    258         outStamp->variance = inStamp->variance ? psKernelCopy(inStamp->variance) : NULL;
     270        outStamp->weight = inStamp->weight ? psKernelCopy(inStamp->weight) : NULL;
    259271
    260272        if (inStamp->convolutions1) {
     
    279291        }
    280292
    281         outStamp->matrix1 = inStamp->matrix1 ? psImageCopy(NULL, inStamp->matrix1, PS_TYPE_F64) : NULL;
    282         outStamp->matrix2 = inStamp->matrix2 ? psImageCopy(NULL, inStamp->matrix2, PS_TYPE_F64) : NULL;
    283         outStamp->matrixX = inStamp->matrixX ? psImageCopy(NULL, inStamp->matrixX, PS_TYPE_F64) : NULL;
    284         outStamp->vector1 = inStamp->vector1 ? psVectorCopy(NULL, inStamp->vector1, PS_TYPE_F64) : NULL;
    285         outStamp->vector2 = inStamp->vector2 ? psVectorCopy(NULL, inStamp->vector2, PS_TYPE_F64) : NULL;
     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;
    286295
    287296        out->stamps->data[i] = outStamp;
     
    305314    stamp->image1 = NULL;
    306315    stamp->image2 = NULL;
    307     stamp->variance = NULL;
     316    stamp->weight = NULL;
    308317    stamp->convolutions1 = NULL;
    309318    stamp->convolutions2 = NULL;
    310319
    311     stamp->matrix1 = NULL;
    312     stamp->matrix2 = NULL;
    313     stamp->matrixX = NULL;
    314     stamp->vector1 = NULL;
    315     stamp->vector2 = NULL;
     320    stamp->matrix = NULL;
     321    stamp->vector = NULL;
     322    stamp->norm = NAN;
    316323
    317324    return stamp;
     
    322329                                                const psImage *image2, const psImage *subMask,
    323330                                                const psRegion *region, float thresh1, float thresh2,
    324                                                 int size, int footprint, float spacing,
    325                                                 pmSubtractionMode mode)
     331                                                int size, int footprint, float spacing, float normFrac,
     332                                                float sysErr, float skyErr, pmSubtractionMode mode)
    326333{
    327334    if (!image1 && !image2) {
     
    362369        if (psRegionIsNaN(*region)) {
    363370            psString string = psRegionToString(*region);
    364             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);
    365372            psFree(string);
    366373            return false;
     
    369376            region->y0 < 0 || region->y1 > numRows) {
    370377            psString string = psRegionToString(*region);
    371             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)",
    372379                    string, numCols, numRows);
    373380            psFree(string);
     
    379386
    380387    if (!stamps) {
    381         stamps = pmSubtractionStampListAlloc(numCols, numRows, region, footprint, spacing);
     388        stamps = pmSubtractionStampListAlloc(numCols, numRows, region, footprint, spacing,
     389                                             normFrac, sysErr, skyErr);
    382390    }
    383391
     
    401409            numSearch++;
    402410
    403             int xStamp = 0, yStamp = 0; // Coordinates of stamp
     411            float xStamp = 0.0, yStamp = 0.0; // Coordinates of stamp
    404412            float fluxStamp = -INFINITY; // Flux of stamp
    405413            bool goodStamp = false;     // Found a good stamp?
     
    413421                // Take stamps off the top of the (sorted) list
    414422                for (int j = xList->n - 1; j >= 0 && !goodStamp; j--) {
    415                     int xCentre = xList->data.F32[j] + 0.5, yCentre = yList->data.F32[j] + 0.5;// Stamp centre
    416 
    417423                    // Chop off the top of the list
    418424                    xList->n = j;
     
    420426                    fluxList->n = j;
    421427
     428#if 0
    422429                    // 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
    423431                    psTrace("psModules.imcombine", 7, "Searching for stamp %d around %d,%d\n",
    424432                            i, xCentre, yCentre);
    425433
    426434                    // Search bounds
     435                    int xCentre = xList->data.F32[j] - 0.5, yCentre = yList->data.F32[j] - 0.5;// Stamp centre
    427436                    int search = footprint - size; // Search radius
    428437                    int xMin = PS_MAX(border, xCentre - search);
     
    433442                    goodStamp = stampSearch(&xStamp, &yStamp, &fluxStamp, image1, image2, thresh1, thresh2,
    434443                                            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                    }
    435456                }
    436457            } else {
     
    452473                psFree(stamp->image1);
    453474                psFree(stamp->image2);
    454                 psFree(stamp->variance);
     475                psFree(stamp->weight);
    455476                psFree(stamp->convolutions1);
    456477                psFree(stamp->convolutions2);
    457                 stamp->image1 = stamp->image2 = stamp->variance = NULL;
     478                stamp->image1 = stamp->image2 = stamp->weight = NULL;
    458479                stamp->convolutions1 = stamp->convolutions2 = NULL;
    459480
     
    487508                                               const psImage *image, const psImage *subMask,
    488509                                               const psRegion *region, int size, int footprint,
    489                                                float spacing, pmSubtractionMode mode)
     510                                               float spacing, float normFrac, float sysErr, float skyErr,
     511                                               pmSubtractionMode mode)
    490512
    491513{
     
    507529    int numStars = x->n;                // Number of stars
    508530    pmSubtractionStampList *stamps = pmSubtractionStampListAlloc(subMask->numCols, subMask->numRows,
    509                                                                  region, footprint, spacing); // Stamp list
     531                                                                 region, footprint, spacing,
     532                                                                 normFrac, sysErr, skyErr); // Stamp list
    510533    int numStamps = stamps->num;        // Number of stamps
    511534
     
    514537    psStringAppend(&ds9name, "stamps_all_%d.ds9", ds9num);
    515538    FILE *ds9 = pmSubtractionStampsFile(stamps, ds9name, "all stamps"); // ds9 region file
     539    psFree(ds9name);
    516540    ds9num++;
    517541
     
    529553    for (int i = 0; i < numStars; i++) {
    530554        float xStamp = x->data.F32[i], yStamp = y->data.F32[i]; // Coordinates of stamp
    531         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
    532556        if (!checkStampRegion(xPix, yPix, region)) {
    533557            // It's not in the big region
     
    537561            continue;
    538562        }
     563
     564        // fprintf (stderr, "stamp: %5.1f %5.1f == %d %d\n", xStamp, yStamp, xPix, yPix);
    539565
    540566        bool found = false;
     
    605631
    606632
     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
    607755bool pmSubtractionStampsExtract(pmSubtractionStampList *stamps, psImage *image1, psImage *image2,
    608                                 psImage *variance, int kernelSize)
     756                                psImage *variance, int kernelSize, psRegion bounds)
    609757{
    610758    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    616764        PS_ASSERT_IMAGE_TYPE(image2, PS_TYPE_F32, false);
    617765    }
    618     PS_ASSERT_IMAGE_NON_NULL(variance, false);
    619     PS_ASSERT_IMAGES_SIZE_EQUAL(variance, image1, false);
    620     PS_ASSERT_IMAGE_TYPE(variance, PS_TYPE_F32, false);
    621     PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    622 
    623     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
    624773    int size = kernelSize + stamps->footprint; // Size of postage stamps
    625774
     
    630779        }
    631780
    632         if (isnan(stamp->xNorm)) {
    633             stamp->xNorm = 2.0 * (stamp->x - (float)numCols/2.0) / (float)numCols;
    634         }
    635         if (isnan(stamp->yNorm)) {
    636             stamp->yNorm = 2.0 * (stamp->y - (float)numRows/2.0) / (float)numRows;
    637         }
    638 
    639         int x = stamp->x + 0.5, y = stamp->y + 0.5; // Stamp coordinates
    640         if (x < size || x > numCols - size || y < size || y > numRows - size) {
    641             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);
    642789            return false;
    643790        }
     
    646793        assert(stamp->image1 == NULL);
    647794        assert(stamp->image2 == NULL);
    648         assert(stamp->variance == NULL);
     795        assert(stamp->weight == NULL);
    649796
    650797        psRegion region = psRegionSet(x - size, x + size + 1, y - size, y + size + 1); // Region of interest
     
    660807        }
    661808
    662         psImage *wtSub = psImageSubset(variance, region); // Subimage with stamp
    663         stamp->variance = psKernelAllocFromImage(wtSub, size, size);
    664         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        }
    665837
    666838        stamp->status = PM_SUBTRACTION_STAMP_CALCULATE;
     
    673845                                                          const psImage *subMask, const psRegion *region,
    674846                                                          int size, int footprint, float spacing,
     847                                                          float normFrac, float sysErr, float skyErr,
    675848                                                          pmSubtractionMode mode)
    676849{
     
    696869            y->data.F32[numOut] = source->peak->yf;
    697870        }
     871        // fprintf (stderr, "stamp: %5.1f %5.1f\n", x->data.F32[numOut], y->data.F32[numOut]);
    698872        numOut++;
    699873    }
     
    702876
    703877    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, image, subMask, region, size,
    704                                                             footprint, spacing, mode); // Stamps to return
     878                                                            footprint, spacing, normFrac,
     879                                                            sysErr, skyErr, mode); // Stamps
    705880    psFree(x);
    706881    psFree(y);
    707882
    708883    if (!stamps) {
    709         psError(PS_ERR_UNKNOWN, false, "Unable to set stamps from sources.");
     884        psError(psErrorCodeLast(), false, "Unable to set stamps from sources.");
    710885    }
    711886
     
    716891pmSubtractionStampList *pmSubtractionStampsSetFromFile(const char *filename, const psImage *image,
    717892                                                       const psImage *subMask, const psRegion *region,
    718                                                        int size, int footprint, float spacing,
    719                                                        pmSubtractionMode mode)
     893                                                       int size, int footprint, float spacing, float normFrac,
     894                                                       float sysErr, float skyErr, pmSubtractionMode mode)
    720895{
    721896    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
     
    724899    psArray *data = psVectorsReadFromFile(filename, "%f %f");
    725900    if (!data) {
    726         psError(PS_ERR_IO, false, "Unable to read stamps file %s", filename);
     901        psError(psErrorCodeLast(), false, "Unable to read stamps file %s", filename);
    727902        return NULL;
    728903    }
     
    734909
    735910    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, image, subMask, region, size, footprint,
    736                                                             spacing, mode);
     911                                                            spacing, normFrac, sysErr, skyErr, mode);
    737912    psFree(data);
    738913
     
    740915
    741916}
     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/tap_branches/psModules/src/imcombine/pmSubtractionStamps.h

    r25101 r27838  
    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
     
    6877    psKernel *image1;                   ///< Reference image postage stamp
    6978    psKernel *image2;                   ///< Input image postage stamp
    70     psKernel *variance;                 ///< Variance image postage stamp, or NULL
     79    psKernel *weight;                   ///< Weight image (1/variance) postage stamp, or NULL
    7180    psArray *convolutions1;             ///< Convolutions of image 1 for each kernel component, or NULL
    7281    psArray *convolutions2;             ///< Convolutions of image 2 for each kernel component, or NULL
    73     psImage *matrix1, *matrix2;         ///< Least-squares matrices for each image, or NULL
    74     psImage *matrixX;                   ///< Cross-matrix (for mode DUAL), or NULL
    75     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
    7685    pmSubtractionStampStatus status;    ///< Status of stamp
    7786} pmSubtractionStamp;
     
    8190
    8291/// Find stamps on an image
    83 pmSubtractionStampList *pmSubtractionStampsFind(pmSubtractionStampList *stamps, ///< Output stamps, or NULL
    84                                                 const psImage *image1, ///< Image for which to find stamps
    85                                                 const psImage *image2, ///< Image for which to find stamps
    86                                                 const psImage *mask, ///< Mask, or NULL
    87                                                 const psRegion *region, ///< Region to search, or NULL
    88                                                 float thresh1, ///< Threshold for stamps in image 1
    89                                                 float thresh2, ///< Threshold for stamps in image 2
    90                                                 int size, ///< Kernel half-size
    91                                                 int footprint, ///< Half-size for stamps
    92                                                 float spacing, ///< Rough spacing for stamps
    93                                                 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
    94107    );
    95108
    96109/// Set stamps based on a list of x,y
    97 pmSubtractionStampList *pmSubtractionStampsSet(const psVector *x, ///< x coordinates for each stamp
    98                                                const psVector *y, ///< y coordinates for each stamp
    99                                                const psImage *image, ///< Image for flux of stamp
    100                                                const psImage *mask, ///< Mask, or NULL
    101                                                const psRegion *region, ///< Region to search, or NULL
    102                                                int size, ///< Kernel half-size
    103                                                int footprint, ///< Half-size for stamps
    104                                                float spacing, ///< Rough spacing for stamps
    105                                                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
    106123    );
    107124
     
    115132    int footprint,                      ///< Half-size for stamps
    116133    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
    117137    pmSubtractionMode mode              ///< Mode for subtraction
    118138    );
     
    127147    int footprint,                      ///< Half-size for stamps
    128148    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
    129152    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
    130159    );
    131160
     
    135164                                psImage *image2, ///< Input image (or NULL)
    136165                                psImage *variance, ///< Variance map
    137                                 int kernelSize ///< Kernel half-size
     166                                int kernelSize, ///< Kernel half-size
     167                                psRegion bounds ///< Bounds of validity
    138168    );
    139169
     
    162192    );
    163193
     194
     195bool pmSubtractionStampsResetStatus (pmSubtractionStampList *stamps);
     196
    164197#endif
  • branches/tap_branches/psModules/src/imcombine/pmSubtractionThreads.c

    r21363 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionThreads.h

    r19340 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionVisual.c

    r23487 r27838  
    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/tap_branches/psModules/src/imcombine/pmSubtractionVisual.h

    r23487 r27838  
    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/tap_branches/psModules/src/objects/Makefile.am

    r25754 r27838  
    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 \
     
    6064        pmSourceMatch.c \
    6165        pmDetEff.c \
     66        pmSourceGroups.c \
    6267        models/pmModel_GAUSS.c \
    6368        models/pmModel_PGAUSS.c \
     
    7176        pmSpan.h \
    7277        pmFootprint.h \
     78        pmFootprintSpans.h \
    7379        pmPeaks.h \
    7480        pmMoments.h \
     
    7783        pmModelUtils.h \
    7884        pmSource.h \
     85        pmPhotObj.h \
    7986        pmSourceMasks.h \
     87        pmSourceDiffStats.h \
    8088        pmSourceExtendedPars.h \
    8189        pmSourceUtils.h \
     
    96104        pmSourceMatch.h \
    97105        pmDetEff.h \
     106        pmSourceGroups.h \
    98107        models/pmModel_GAUSS.h \
    99108        models/pmModel_PGAUSS.h \
  • branches/tap_branches/psModules/src/objects/models/pmModel_GAUSS.c

    r25754 r27838  
    4141static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0 };
    4242static 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;
    4347
    4448// Strict parameter limits
     
    375379        limitsApply = true;
    376380        break;
     381      case PM_MODEL_LIMITS_MODERATE:
     382        paramsMinUse = paramsMinModerate;
     383        paramsMaxUse = paramsMaxModerate;
     384        limitsApply = true;
     385        break;
    377386      case PM_MODEL_LIMITS_STRICT:
    378387        paramsMinUse = paramsMinStrict;
  • branches/tap_branches/psModules/src/objects/models/pmModel_PGAUSS.c

    r25754 r27838  
    4141static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0 };
    4242static 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;
    4347
    4448// Strict parameter limits
     
    232236psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
    233237{
    234     psF64 z, f;
     238    psF64 z;
    235239    int Nstep = 0;
    236240    psEllipseShape shape;
     
    267271    z0 = 0.0;
    268272
    269     // perform a type of bisection to find the value
    270     float f0 = 1.0 / (1 + z0 + z0*z0/2.0 + z0*z0*z0/6.0);
    271     float f1 = 1.0 / (1 + z1 + z1*z1/2.0 + z1*z1*z1/6.0);
    272     while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
    273         z = 0.5*(z0 + z1);
    274         f = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
    275         if (f > limit) {
    276             z0 = z;
    277             f0 = f;
    278         } else {
    279             z1 = z;
    280             f1 = f;
    281         }
    282         Nstep ++;
    283     }
     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
    284292    psF64 radius = sigma * sqrt (2.0 * z);
    285293
     
    425433        limitsApply = true;
    426434        break;
     435      case PM_MODEL_LIMITS_MODERATE:
     436        paramsMinUse = paramsMinModerate;
     437        paramsMaxUse = paramsMaxModerate;
     438        limitsApply = true;
     439        break;
    427440      case PM_MODEL_LIMITS_STRICT:
    428441        paramsMinUse = paramsMinStrict;
  • branches/tap_branches/psModules/src/objects/models/pmModel_PS1_V1.c

    r25754 r27838  
    4949static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -1.0 };
    5050static 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 };
    5156
    5257// Strict parameter limits
     
    262267psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
    263268{
    264     psF64 z, f;
     269    psF64 z;
    265270    int Nstep = 0;
    266271    psEllipseShape shape;
     
    268273    psF32 *PAR = params->data.F32;
    269274
    270     if (flux <= 0) {
    271         return 1.0;
    272     }
    273     if (PAR[PM_PAR_I0] <= 0) {
    274         return 1.0;
    275     }
    276     if (flux >= PAR[PM_PAR_I0]) {
    277         return 1.0;
    278     }
    279     if (PAR[PM_PAR_7] == 0.0) {
    280         return powf(PAR[PM_PAR_I0] / flux - 1.0, 1.0 / ALPHA);
    281     }
     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);
    282279
    283280    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
     
    300297    if (PAR[PM_PAR_7] < 0.0) z1 *= 2.0;
    301298
    302     // perform a type of bisection to find the value
    303     float f0 = 1.0 / (1 + PAR[PM_PAR_7]*z0 + pow(z0, ALPHA));
    304     float f1 = 1.0 / (1 + PAR[PM_PAR_7]*z1 + pow(z1, ALPHA));
    305     while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
    306         z = 0.5*(z0 + z1);
    307         f = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
    308         if (f > limit) {
    309             z0 = z;
    310             f0 = f;
    311         } else {
    312             z1 = z;
    313             f1 = f;
    314         }
    315         Nstep ++;
     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);
    316316    }
    317317    psF64 radius = sigma * sqrt (2.0 * z);
     
    345345    // convert to shape terms (SXX,SYY,SXY)
    346346    if (!pmPSF_FitToModel (out, 0.1)) {
    347         // psError(PM_ERR_PSF, false, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
    348347        psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
    349348        return false;
     
    454453        limitsApply = true;
    455454        break;
     455      case PM_MODEL_LIMITS_MODERATE:
     456        paramsMinUse = paramsMinModerate;
     457        paramsMaxUse = paramsMaxModerate;
     458        limitsApply = true;
     459        break;
    456460      case PM_MODEL_LIMITS_STRICT:
    457461        paramsMinUse = paramsMinStrict;
     
    464468    return;
    465469}
    466 
    467470
    468471# undef PM_MODEL_FUNC
  • branches/tap_branches/psModules/src/objects/models/pmModel_QGAUSS.c

    r25754 r27838  
    3939# define PM_MODEL_SET_LIMITS      pmModelSetLimits_QGAUSS
    4040
     41# define ALPHA   2.250
     42# define ALPHA_M 1.250
     43
    4144// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
    4245// 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
     
    4447
    4548// Lax parameter limits
    46 static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, 0.1 };
     49static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -1.0 };
    4750static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
    4851
     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
    4957// Strict parameter limits
    50 static float *paramsMinStrict = paramsMinLax;
    51 static float *paramsMaxStrict = paramsMaxLax;
     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 };
    5261
    5362// Parameter limits to use
    5463static float *paramsMinUse = paramsMinLax;
    5564static float *paramsMaxUse = paramsMaxLax;
    56 static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5 };
     65static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5, 2.0 };
    5766
    5867static bool limitsApply = true;         // Apply limits?
     
    7786    assert (z >= 0);
    7887
    79     psF32 zp = pow(z,1.25);
     88    psF32 zp = pow(z,ALPHA_M);
    8089    psF32 r  = 1.0 / (1 + PAR[PM_PAR_7]*z + z*zp);
    8190
     
    8897        // note difference from a pure gaussian: q = params->data.F32[PM_PAR_I0]*r
    8998        psF32 t = r1*r;
    90         psF32 q = t*(PAR[PM_PAR_7] + 2.25*zp);
     99        psF32 q = t*(PAR[PM_PAR_7] + ALPHA*zp);
    91100
    92101        dPAR[PM_PAR_SKY]  = +1.0;
     
    242251    float f1, f2;
    243252    for (z = DZ; z < 50; z += DZ) {
    244         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));
    245254        z += DZ;
    246         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));
    247256        norm += f0 + 4*f1 + f2;
    248257        f0 = f2;
     
    259268psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
    260269{
    261     psF64 z, f;
     270    psF64 z;
    262271    int Nstep = 0;
    263272    psEllipseShape shape;
     
    265274    psF32 *PAR = params->data.F32;
    266275
    267     if (flux <= 0)
    268         return (1.0);
    269     if (PAR[PM_PAR_I0] <= 0)
    270         return (1.0);
    271     if (flux >= PAR[PM_PAR_I0])
    272         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);
    273280
    274281    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
     
    286293
    287294    // choose a z value guaranteed to be beyond our limit
    288     float z0 = pow((1.0 / limit), (1.0 / 2.25));
    289     psAssert (isfinite(z0), "fix this code: z0 should not be nan for %f", PAR[PM_PAR_7]);
    290     float z1 = (1.0 / limit) / PAR[PM_PAR_7];
     295    float z0 = 0.0;
     296    float z1 = pow((1.0 / limit), (1.0 / ALPHA));
    291297    psAssert (isfinite(z1), "fix this code: z1 should not be nan for %f", PAR[PM_PAR_7]);
    292     z1 = PS_MAX (z0, z1);
    293     z0 = 0.0;
    294 
    295     // perform a type of bisection to find the value
    296     float f0 = 1.0 / (1 + PAR[PM_PAR_7]*z0 + pow(z0, 2.25));
    297     float f1 = 1.0 / (1 + PAR[PM_PAR_7]*z1 + pow(z1, 2.25));
    298     while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
    299         z = 0.5*(z0 + z1);
    300         f = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
    301         if (f > limit) {
    302             z0 = z;
    303             f0 = f;
    304         } else {
    305             z1 = z;
    306             f1 = f;
    307         }
    308         Nstep ++;
     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);
    309317    }
    310318    psF64 radius = sigma * sqrt (2.0 * z);
     
    446454        limitsApply = true;
    447455        break;
     456      case PM_MODEL_LIMITS_MODERATE:
     457        paramsMinUse = paramsMinModerate;
     458        paramsMaxUse = paramsMaxModerate;
     459        limitsApply = true;
     460        break;
    448461      case PM_MODEL_LIMITS_STRICT:
    449462        paramsMinUse = paramsMinStrict;
     
    466479# undef PM_MODEL_FIT_STATUS
    467480# undef PM_MODEL_SET_LIMITS
     481# undef ALPHA
     482# undef ALPHA_M
  • branches/tap_branches/psModules/src/objects/models/pmModel_RGAUSS.c

    r25754 r27838  
    4646static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, 1.25 };
    4747static 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;
    4852
    4953// Strict parameter limits
     
    253257psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
    254258{
    255     psF64 z, f;
     259    psF64 z;
    256260    int Nstep = 0;
    257261    psEllipseShape shape;
     
    287291    z0 = 0.0;
    288292
    289     // perform a type of bisection to find the value
    290     float f0 = 1.0 / (1 + z0 + pow(z0, PAR[PM_PAR_7]));
    291     float f1 = 1.0 / (1 + z1 + pow(z1, PAR[PM_PAR_7]));
    292     while ((Nstep < 10) && (fabs(z1 - z0) > 0.5)) {
    293         z = 0.5*(z0 + z1);
    294         f = 1.0 / (1 + z + pow(z, PAR[PM_PAR_7]));
    295         if (f > limit) {
    296             z0 = z;
    297             f0 = f;
    298         } else {
    299             z1 = z;
    300             f1 = f;
    301         }
    302         Nstep ++;
    303     }
     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
    304312    psF64 radius = sigma * sqrt (2.0 * z);
    305313
     
    440448        limitsApply = true;
    441449        break;
     450      case PM_MODEL_LIMITS_MODERATE:
     451        paramsMinUse = paramsMinModerate;
     452        paramsMaxUse = paramsMaxModerate;
     453        limitsApply = true;
     454        break;
    442455      case PM_MODEL_LIMITS_STRICT:
    443456        paramsMinUse = paramsMinStrict;
  • branches/tap_branches/psModules/src/objects/models/pmModel_SERSIC.c

    r25763 r27838  
    4949static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.05, 0.05, -1.0, 0.05 };
    5050static 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;
    5155
    5256// Strict parameter limits
     
    429433        limitsApply = true;
    430434        break;
     435      case PM_MODEL_LIMITS_MODERATE:
     436        paramsMinUse = paramsMinModerate;
     437        paramsMaxUse = paramsMaxModerate;
     438        limitsApply = true;
     439        break;
    431440      case PM_MODEL_LIMITS_STRICT:
    432441        paramsMinUse = paramsMinStrict;
  • branches/tap_branches/psModules/src/objects/pmDetections.c

    r23487 r27838  
    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/tap_branches/psModules/src/objects/pmDetections.h

    r23487 r27838  
    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/tap_branches/psModules/src/objects/pmFootprint.c

    r23187 r27838  
    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/tap_branches/psModules/src/objects/pmFootprint.h

    r24875 r27838  
    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/tap_branches/psModules/src/objects/pmFootprintCullPeaks.c

    r24888 r27838  
    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/tap_branches/psModules/src/objects/pmFootprintFindAtPoint.c

    r21183 r27838  
    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/tap_branches/psModules/src/objects/pmFootprintIDs.c

    r20937 r27838  
    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/tap_branches/psModules/src/objects/pmGrowthCurveGenerate.c

    r25754 r27838  
    157157
    158158    // measure the fitMag for this model
    159     pmSourcePhotometryModel (&fitMag, model);
     159    pmSourcePhotometryModel (&fitMag, NULL, model);
    160160    growth->fitMag = fitMag;
    161161
  • branches/tap_branches/psModules/src/objects/pmModel.h

    r25754 r27838  
    4848    PM_MODEL_LIMITS_NONE,               ///< Apply no limits: suitable for debugging
    4949    PM_MODEL_LIMITS_IGNORE,             ///< Ignore all limits: fit can go to town
    50     PM_MODEL_LIMITS_LAX,                ///< Lax limits: attempting to reproduce mildly bad data
    51     PM_MODEL_LIMITS_STRICT,             ///< Strict limits: good quality data
     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
    5253} pmModelLimitsType;
    5354
  • branches/tap_branches/psModules/src/objects/pmPSF.c

    r24206 r27838  
    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/tap_branches/psModules/src/objects/pmPSF.h

    r25754 r27838  
    111111psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR);
    112112
     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
    113120/// @}
    114121# endif
  • branches/tap_branches/psModules/src/objects/pmPSF_IO.c

    r25854 r27838  
    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
     
    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();
     
    693831        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);
     833        psMetadataAddS32 (analysis, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", nRegions);
    696834
    697835        for (int i = 0; i < nRegions; i++) {
     
    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/tap_branches/psModules/src/objects/pmPSFtryFitPSF.c

    r25754 r27838  
    7171        if (source->modelPSF == NULL) {
    7272            psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_BAD_MODEL;
    73             abort();
    7473            continue;
    7574        }
  • branches/tap_branches/psModules/src/objects/pmPSFtryMetric.c

    r25754 r27838  
    7676
    7777    pmSourceVisualPlotPSFMetric (psfTry);
     78    pmSourceVisualPlotPSFMetricSubpix (psfTry);
    7879
    7980    psFree (stats);
  • branches/tap_branches/psModules/src/objects/pmPSFtryModel.c

    r25819 r27838  
    7272    }
    7373
    74     // XXX set the min number of needed source more carefully (depends on psfTrendMode?)
    75     int orderMax = PS_MAX (options->psfTrendNx, options->psfTrendNy);
    76     if ((sources->n < 15) && (orderMax >= 3)) orderMax = 2;
    77     if ((sources->n < 11) && (orderMax >= 2)) orderMax = 1;
    78     if ((sources->n <  8) && (orderMax >= 1)) orderMax = 0;
     74    // hard limit on minimum number of stars
    7975    if ((sources->n <  3)) {
    8076        psError (PS_ERR_UNKNOWN, true, "failed to determine PSF parameters");
     
    8278    }
    8379
    84     int orderMin;
     80    // this is a bit tricky, because we have two cases (MAP vs POLY), and they have a different
     81    // definition for 'order' (order_MAP = order_POLY + 1).  in addition, we have a
     82    // user-specified MAX order, which we should respect, regardless of the mode
     83
     84    // set the max order (0 = constant) which the number of psf stars can support:
     85    // rule of thumb: require 3 stars per 'cell' (order+1)^2
     86    int MaxOrderForStars = 0;
     87    if (sources->n >= 12) MaxOrderForStars = 1; // 4 cells
     88    if (sources->n >= 27) MaxOrderForStars = 2; // 9 cells
     89    if (sources->n >= 48) MaxOrderForStars = 3; // 16 cells
     90    if (sources->n >  75) MaxOrderForStars = 4; // 25 cells
     91
     92    int orderMax = PS_MAX (options->psfTrendNx, options->psfTrendNy);
     93    int orderMin = 0;
    8594    if (options->psfTrendMode == PM_TREND_MAP) {
    86         orderMin = 1;
    87         orderMax = PS_MAX(orderMax, 1);
    88     } else {
    89         orderMin = 0;
    90     }
     95        MaxOrderForStars ++;
     96        orderMin ++;
     97    }
     98    orderMax = PS_MIN (orderMax, MaxOrderForStars);
    9199
    92100    // save the raw source mask (generated by pmPSFtryFitEXT)
     
    99107    // as we loop over orders, we need to refer to the initial selection, but we modify the
    100108    // option values to match the current guess: save the max values here:
    101     int Nx = options->psfTrendNx;
    102     int Ny = options->psfTrendNy;
     109    int Nx = (options->psfTrendMode == PM_TREND_MAP) ? options->psfTrendNx : options->psfTrendNx + 1;
     110    int Ny = (options->psfTrendMode == PM_TREND_MAP) ? options->psfTrendNy : options->psfTrendNy + 1;
    103111    for (int i = orderMin; i <= orderMax; i++) {
    104112
     
    196204                  psStatsGetValue(stats, meanStat), psStatsGetValue(stats, stdevStat));
    197205
    198         psFree(flux);
    199         psFree(mask);
    200         psFree(chisq);
    201 
    202206        if (!result) {
    203207            psError(PS_ERR_UNKNOWN, false, "Failed to fit psf->ChiTrend");
    204208            psFree(psfTry);
    205             return NULL;
    206         }
    207     }
     209            psFree(flux);
     210            psFree(mask);
     211            psFree(chisq);
     212            return NULL;
     213        }
     214    }
     215
     216    psFree(flux);
     217    psFree(mask);
     218    psFree(chisq);
    208219
    209220    for (int i = 0; i < psfTry->psf->ChiTrend->nX + 1; i++) {
  • branches/tap_branches/psModules/src/objects/pmPeaks.c

    r25754 r27838  
    375375    psU32 col = 0;
    376376    psU32 row = 0;
    377     psArray *list = NULL;
     377    psArray *list = psArrayAllocEmpty(100);
    378378
    379379    // Find peaks in row 0 only.
     
    416416
    417417        } else {
    418             psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
     418            psLogMsg ("psModules.objects", 5, "peak specified outside valid column range.");
    419419        }
    420420    }
     
    501501                }
    502502            } else {
    503                 psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
     503                psLogMsg ("psModules.objects", 5, "peak specified outside valid column range.");
    504504            }
    505505
     
    545545            }
    546546        } else {
    547             psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
     547            psLogMsg ("psModules.objects", 5, "peak specified outside valid column range.");
    548548        }
    549549    }
  • branches/tap_branches/psModules/src/objects/pmPeaks.h

    r20945 r27838  
    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/tap_branches/psModules/src/objects/pmSource.c

    r25754 r27838  
    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");
     
    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
     
    401380        psfClump.nSigma = stats->sampleStdev;
    402381
    403         const bool keep_psf_clump = psMetadataLookupBool(NULL, recipe, "KEEP_PSF_CLUMP");
    404         if (keep_psf_clump)
    405         {
    406             psMetadataAdd(recipe, PS_LIST_TAIL,
    407                           "PSF_CLUMP", PS_DATA_IMAGE, "Image of PSF coefficients", splane);
     382        if (savedImage) {
     383            *savedImage = psMemIncrRefCounter(splane);
    408384        }
    409385        psFree (splane);
     
    411387
    412388        // if we failed to find a valid peak, return the empty clump (failure signal)
    413         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)
    414396        {
    415397            psLogMsg ("psphot", 3, "failed to find a peak in the PSF clump image\n");
     
    419401                psLogMsg ("psphot", 3, "no significant peak\n");
    420402            }
     403            psFree (peaks);
    421404            return (emptyClump);
    422405        }
     
    525508*****************************************************************************/
    526509
    527 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)
    528511{
    529512    psTrace("psModules.objects", 10, "---- begin ----");
    530513
    531514    PS_ASSERT_PTR_NON_NULL(sources, false);
    532     PS_ASSERT_PTR_NON_NULL(recipe, false);
    533515
    534516    int Nsat     = 0;
     
    543525    psVector *starsn_peaks = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
    544526    psVector *starsn_moments = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
    545 
    546     // get basic parameters, or set defaults
    547     bool status;
    548     float PSF_SN_LIM = psMetadataLookupF32 (&status, recipe, "PSF_SN_LIM");
    549     if (!status) PSF_SN_LIM = 20.0;
    550     float PSF_CLUMP_NSIGMA = psMetadataLookupF32 (&status, recipe, "PSF_CLUMP_NSIGMA");
    551     if (!status) PSF_CLUMP_NSIGMA = 1.5;
    552 
    553     // float INNER_RADIUS = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
    554527
    555528    pmSourceMode noMoments = PM_SOURCE_MODE_MOMENTS_FAILURE | PM_SOURCE_MODE_SKYVAR_FAILURE | PM_SOURCE_MODE_SKY_FAILURE | PM_SOURCE_MODE_BELOW_MOMENTS_SN;
     
    907880
    908881    // if we already have a cached image, re-use that memory
    909     source->psfFlux = psImageCopy (source->psfFlux, source->pixels, PS_TYPE_F32);
    910     psImageInit (source->psfFlux, 0.0);
     882    source->psfImage = psImageCopy (source->psfImage, source->pixels, PS_TYPE_F32);
     883    psImageInit (source->psfImage, 0.0);
    911884
    912885    // in some places (psphotEnsemble), we need a normalized version
    913886    // in others, we just want the model.  which is more commonly used?
    914     // psfFlux always has unity normalization (I0 = 1.0)
    915     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);
    916889    return true;
    917890}
     
    10861059    psF32 fA = (A->peak == NULL) ? 0 : A->peak->y;
    10871060    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;
    10881076
    10891077    psF32 diff = fA - fB;
  • branches/tap_branches/psModules/src/objects/pmSource.h

    r25754 r27838  
    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,
    42     PM_SOURCE_TMPF_SIZE_MEASURED = 0x0004,
     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,
    4346} pmSourceTmpF;
    4447
     
    6467    psImage *maskView;                  ///< view into global image mask for this object region
    6568    psImage *modelFlux;                 ///< cached copy of the best model for this source
    66     psImage *psfFlux;                   ///< cached copy of the psf model for this source
     69    psImage *psfImage;                   ///< cached copy of the psf model for this source
    6770    pmMoments *moments;                 ///< Basic moments measured for the object.
    6871    pmModel *modelPSF;                  ///< PSF Model fit (parameters and type)
     
    7477    psArray *blends;                    ///< collection of sources thought to be confused with object
    7578    float psfMag;                       ///< calculated from flux in modelPSF
     79    float psfFlux;                      ///< calculated from flux in modelPSF
     80    float psfFluxErr;                   ///< calculated from flux in modelPSF
    7681    float extMag;                       ///< calculated from flux in modelEXT
    7782    float errMag;                       ///< error in psfMag OR extMag (depending on type)
     
    8590    psRegion region;                    ///< area on image covered by selected pixels
    8691    pmSourceExtendedPars *extpars;      ///< extended source parameters
     92    pmSourceDiffStats *diffStats;       ///< extra parameters for difference detections
     93    int imageID;
    8794};
    8895
     
    176183 *
    177184 * The return value indicates the success (TRUE) of the operation.
    178  *
    179  * XXX: Limit the S/N of the candidate sources (part of Metadata)? (TBD).
    180  * XXX: Save the clump parameters on the Metadata (TBD)
    181  *
    182  */
     185 */
     186
    183187pmPSFClump pmSourcePSFClump(
     188    psImage **savedImage,
    184189    psRegion *region,                   ///< restrict measurement to specified region
    185190    psArray *source,                    ///< The input pmSource
    186     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
    187196);
    188197
     
    200209    psRegion *region,                   ///< restrict measurement to specified region
    201210    psArray *sources,                    ///< The input pmSources
    202     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
    203213    pmPSFClump clump,                   ///< Statistics about the PSF clump
    204214    psImageMaskType maskSat             ///< Mask value for saturated pixels
     
    220230    float radius,     ///< Use a circle of pixels around the peak
    221231    float sigma,      ///< size of Gaussian window function (<= 0.0 -> skip window)
    222     float minSN       ///< minimum pixel significance
     232    float minSN,              ///< minimum pixel significance
     233    psImageMaskType maskVal
    223234);
    224235
     
    236247int  pmSourceSortBySN (const void **a, const void **b);
    237248int  pmSourceSortByY (const void **a, const void **b);
     249int  pmSourceSortByX (const void **a, const void **b);
    238250int  pmSourceSortBySeq (const void **a, const void **b);
    239251
  • branches/tap_branches/psModules/src/objects/pmSourceExtendedPars.c

    r25754 r27838  
    3636#include "pmSourceExtendedPars.h"
    3737
    38 // *** pmSourceRadialProfile describes the radial profile of a source in elliptical contours, and
    39 // intermediate data used to measure the profile
     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
    4093static void pmSourceRadialProfileFree(pmSourceRadialProfile *profile)
    4194{
    42     if (!profile) {
    43         return;
    44     }
    45     psFree(profile->radii);
    46     psFree(profile->fluxes);
    47     psFree(profile->theta);
    48     psFree(profile->isophotalRadii);
    49 
    50     psFree(profile->radiusElliptical);
    51     psFree(profile->fluxElliptical);
    52 
     95    if (!profile) return;
    5396    psFree(profile->binSB);
    5497    psFree(profile->binSBstdev);
    5598    psFree(profile->binSBerror);
    56 
     99    psFree(profile->binSum);
     100    psFree(profile->binFill);
    57101    psFree(profile->radialBins);
    58102    psFree(profile->area);
     
    63107    pmSourceRadialProfile *profile = (pmSourceRadialProfile *)psAlloc(sizeof(pmSourceRadialProfile));
    64108    psMemSetDeallocator(profile, (psFreeFunc) pmSourceRadialProfileFree);
    65 
    66     profile->radii = NULL;
    67     profile->fluxes = NULL;
    68     profile->theta = NULL;
    69     profile->isophotalRadii = NULL;
    70 
    71     profile->radiusElliptical = NULL;
    72     profile->fluxElliptical = NULL;
    73109
    74110    profile->binSB = NULL;
    75111    profile->binSBstdev = NULL;
    76112    profile->binSBerror = NULL;
    77 
     113    profile->binSum = NULL;
     114    profile->binFill = NULL;
    78115    profile->radialBins = NULL;
    79116    profile->area = NULL;
    80 
    81117    return profile;
    82118}
     
    88124}
    89125
    90 
    91 // *** pmSourceRadialProfileFreeVectors frees the intermediate data values
     126# if (0)
     127// pmSourceRadialProfileFreeVectors frees the intermediate data values
    92128bool pmSourceRadialProfileFreeVectors(pmSourceRadialProfile *profile) {
    93129
     
    124160    return true;
    125161}
     162# endif
    126163
    127164// *** pmSourceRadialProfileSortPair is a utility function for sorting a pair of vectors
     
    150187    if (!pars) return;
    151188
    152     psFree(pars->profile);
    153     psFree(pars->petrosian_50);
    154     psFree(pars->petrosian_80);
     189    psFree(pars->radFlux);
     190    psFree(pars->ellipticalFlux);
     191    psFree(pars->radProfile);
     192    psFree(pars->petProfile);
    155193    return;
    156194}
     
    160198    psMemSetDeallocator(pars, (psFreeFunc) pmSourceExtendedParsFree);
    161199
    162     pars->profile = NULL;
    163     pars->petrosian_50 = NULL;
    164     pars->petrosian_80 = NULL;
    165 
     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;
    166213    return pars;
    167214}
  • branches/tap_branches/psModules/src/objects/pmSourceExtendedPars.h

    r25754 r27838  
    1919    psVector *theta;                    // angles corresponding to above radial profiles
    2020    psVector *isophotalRadii;           // isophotal radius for the above angles
     21} pmSourceRadialFlux;
    2122
     23typedef struct {
    2224    psVector *radiusElliptical;         // normalized radial coordinates for all relevant pixels
    2325    psVector *fluxElliptical;           // flux for the above radial coordinates
     26} pmSourceEllipticalFlux;
    2427
     28typedef struct {
    2529    psVector *binSB;                    // mean surface brightness within radial bins
    2630    psVector *binSBstdev;               // scatter of mean surface brightness within radial bins
    2731    psVector *binSBerror;               // formal error on mean surface brightness within radial bins
    28 
    29     psVector *radialBins;               // radii corresponding to above binnedBlux
     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
    3035    psVector *area;                     // differential area of the non-overlapping radial bins
    31 
    32     psEllipseAxes axes;                 // shape of elliptical contour
    3336} pmSourceRadialProfile;
    3437
    3538typedef struct {
    36   float flux;
    37   float fluxErr;
    38   float radius;
    39   float radiusErr;
     39    float flux;
     40    float fluxErr;
     41    float radius;
     42    float radiusErr;
    4043} pmSourceExtendedFlux;
    4144
    4245typedef struct {
    43   pmSourceRadialProfile   *profile;
    44   pmSourceExtendedFlux    *petrosian_50;
    45   pmSourceExtendedFlux    *petrosian_80;
     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;
    4659} pmSourceExtendedPars;
     60
     61pmSourceRadialFlux *pmSourceRadialFluxAlloc();
     62bool psMemCheckSourceRadialFlux(psPtr ptr);
     63
     64pmSourceEllipticalFlux *pmSourceEllipticalFluxAlloc();
     65bool psMemCheckSourceEllipticalFlux(psPtr ptr);
    4766
    4867// *** pmSourceRadialProfile describes the radial profile of a source in elliptical contours, and
     
    5069pmSourceRadialProfile *pmSourceRadialProfileAlloc();
    5170bool psMemCheckSourceRadialProfile(psPtr ptr);
    52 
    53 // *** pmSourceRadialProfileFreeVectors frees the intermediate data values
    54 bool pmSourceRadialProfileFreeVectors(pmSourceRadialProfile *profile);
    5571
    5672// *** pmSourceExtendedPars describes the possible collection of extended flux measurements for a source
     
    6581bool pmSourceRadialProfileSortPair(psVector *index, psVector *extra);
    6682
     83
     84
    6785/// @}
    6886# endif /* PM_SOURCE_H */
  • branches/tap_branches/psModules/src/objects/pmSourceFitModel.c

    r25754 r27838  
    9191            if (!isfinite(source->pixels->data.F32[i][j])) {
    9292                continue;
     93            }
     94
     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;
    9399            }
    94100
  • branches/tap_branches/psModules/src/objects/pmSourceIO.c

    r25754 r27838  
    4040#include "pmPSF.h"
    4141#include "pmModel.h"
     42#include "pmDetections.h"
    4243#include "pmSource.h"
    4344#include "pmModelClass.h"
     
    342343    pmHDU *hdu;
    343344    psMetadata *updates;
    344     psMetadata *outhead;
    345 
    346     char *exttype  = NULL;
    347345
    348346    // if sources is NULL, write out an empty table
    349     // input / output sources are stored on the readout->analysis as "PSPHOT.SOURCES" -- a better name might be something like PM_SOURCE_DATA
    350     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    }
    351354    if (!sources) {
     355        detections = pmDetectionsAlloc();
    352356        sources = psArrayAlloc(0);
    353         psMetadataAddArray(readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE, "Blank array of sources", sources);
    354         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
    355360    }
    356361
     
    368373        break;
    369374
    370       case PM_FPA_FILE_CMP:
    371         // a SPLIT format : only one header and object table per file
    372         hdu = pmFPAviewThisHDU (view, fpa);
    373         if (!hdu) {
    374             psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find HDU to write sources.");
    375             return false;
    376         }
    377 
    378         // copy the header to an output header, add the output header data
    379         outhead = psMetadataCopy (NULL, hdu->header);
    380 
    381         // copy over the entries saved by PSPHOT
    382         updates = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.HEADER");
    383         if (updates) {
    384             psMetadataCopy (outhead, updates);
    385         }
    386 
    387         // copy over the entries saved by PSASTRO
    388         updates = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.HEADER");
    389         if (updates) {
    390             psMetadataCopy (outhead, updates);
    391         }
    392 
    393         bool status = pmSourcesWriteCMP (sources, file->filename, outhead);
    394         psFree (outhead);
    395 
    396         if (!status) {
    397             psError(PS_ERR_IO, false, "Failed to write CMP file\n");
    398             return false;
    399         }
    400         break;
    401 
    402       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:
    403409        // write a header? (only if this is the first readout for cell)
    404410        //   note that the file->header is set to track the last hdu->header written
     
    415421        psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "PSPHOT");
    416422        if (!status) {
    417           psError(PS_ERR_UNKNOWN, true, "missing recipe PSPHOT in config data");
    418           return false;
     423            psError(PS_ERR_UNKNOWN, true, "missing recipe PSPHOT in config data");
     424            return false;
    419425        }
    420426
     
    429435        psString xsrcname = NULL;
    430436        psString xfitname = NULL;
    431         if (!sourceExtensions(&headname, &dataname, &deteffname, XSRC_OUTPUT ? &xsrcname : NULL,
    432                               XFIT_OUTPUT ? &xfitname : NULL, file, view)) {
     437        if (!sourceExtensions(&headname, &dataname, &deteffname,
     438                              XSRC_OUTPUT ? &xsrcname : NULL,
     439                              XFIT_OUTPUT ? &xfitname : NULL,
     440                              file, view)) {
    433441            return false;
    434442        }
     
    489497        }
    490498
    491         // write out the TABLE data segment
     499        // write out the Object TABLE data segment(s)
    492500        {
    493501            // create a header to hold the output data
    494             outhead = psMetadataAlloc ();
    495 
    496             exttype = psMemIncrRefCounter (psMetadataLookupStr(&status, recipe, "OUTPUT.FORMAT"));
     502            psMetadata *outhead = psMetadataAlloc ();
     503           
     504            char *exttype = psMemIncrRefCounter (psMetadataLookupStr(&status, recipe, "OUTPUT.FORMAT"));
    497505            if (!exttype) {
    498506                exttype = psStringCopy ("SMPDATA");
     
    502510            psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTHEAD", PS_META_REPLACE, "name of image extension w/", headname);
    503511            psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTTYPE", PS_META_REPLACE, "extension type", exttype);
    504             psFree (exttype);
    505512
    506513            // if we request XSRC output, add the XSRC name to this header
    507514            if (xsrcname) {
    508               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);
    509516            }
    510517            if (xfitname) {
    511               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);
    512519            }
    513520
     
    532539                status &= pmSourcesWrite_CMF_PS1_V2 (file->fits, readout, sources, file->header, outhead, dataname);
    533540            }
    534 
    535             if (deteffname) {
    536                 status &= pmReadoutWriteDetEff(file->fits, readout, outhead, deteffname);
     541            if (!strcmp (exttype, "PS1_DV1")) {
     542                status &= pmSourcesWrite_CMF_PS1_DV1 (file->fits, readout, sources, file->header, outhead, dataname);
    537543            }
    538544
    539545            if (xsrcname) {
    540               if (!strcmp (exttype, "PS1_DEV_1")) {
    541                   status &= pmSourcesWrite_PS1_DEV_1_XSRC (file->fits, sources, xsrcname, recipe);
    542               }
    543               if (!strcmp (exttype, "PS1_CAL_0")) {
    544                   status &= pmSourcesWrite_PS1_CAL_0_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
    545               }
    546               if (!strcmp (exttype, "PS1_V1")) {
    547                   status &= pmSourcesWrite_CMF_PS1_V1_XSRC (file->fits, sources, xsrcname, recipe);
    548               }
    549               if (!strcmp (exttype, "PS1_V2")) {
    550                   status &= pmSourcesWrite_CMF_PS1_V2_XSRC (file->fits, sources, xsrcname, recipe);
    551               }
     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                }
    552561            }
    553562            if (xfitname) {
    554               if (!strcmp (exttype, "PS1_DEV_1")) {
    555                   status &= pmSourcesWrite_PS1_DEV_1_XFIT (file->fits, sources, xfitname);
    556               }
    557               if (!strcmp (exttype, "PS1_CAL_0")) {
    558                   status &= pmSourcesWrite_PS1_CAL_0_XFIT (file->fits, readout, sources, file->header, xfitname);
    559               }
    560               if (!strcmp (exttype, "PS1_V1")) {
    561                   status &= pmSourcesWrite_CMF_PS1_V1_XFIT (file->fits, sources, xfitname);
    562               }
    563               if (!strcmp (exttype, "PS1_V2")) {
    564                   status &= pmSourcesWrite_CMF_PS1_V2_XFIT (file->fits, sources, xfitname);
    565               }
    566             }
     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
    567582            if (!status) {
    568583                psError(PS_ERR_IO, false, "writing CMF data to %s with format %s\n", file->filename, exttype);
    569                 psFree (headname);
    570                 psFree (dataname);
    571                 psFree (xsrcname);
    572                 psFree (xfitname);
    573                 psFree (outhead);
    574                 psFree (deteffname);
    575                 return false;
    576             }
    577         }
     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);
    578609
    579610        psTrace ("pmFPAfile", 5, "wrote ext data %s (type: %d)\n", file->filename, file->type);
    580 
    581         psFree (headname);
    582         psFree (dataname);
    583         psFree (xsrcname);
    584         psFree (xfitname);
    585         psFree (outhead);
     611        break;
     612
     613      escape:
     614        psFree (headname);
     615        psFree (dataname);
     616        psFree (xsrcname);
     617        psFree (xfitname);
    586618        psFree (deteffname);
    587         break;
     619        return false;
    588620
    589621      default:
     
    592624    }
    593625    return true;
     626
    594627}
    595628// a MEF CMF file has: PHU, CELL-HEAD, TABLE, CELL-HEAD, TABLE, TABLE, TABLE...
     
    936969        if (hdu->header == NULL) {
    937970            // if the IMAGE header does not exist, we have no data for this view
    938             if (!psFitsMoveExtName (file->fits, headname)) {
     971            if (!psFitsMoveExtNameClean (file->fits, headname)) {
    939972                readout->data_exists = false;
    940973                psFree (headname);
     
    964997        if (!tableHeader) psAbort("cannot read table header");
    965998
     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
    9661006        char *exttype = psMetadataLookupStr (NULL, tableHeader, "EXTTYPE");
    9671007        if (!exttype) psAbort("cannot read table type");
     
    9831023            if (!strcmp (exttype, "PS1_V2")) {
    9841024                sources = pmSourcesRead_CMF_PS1_V2 (file->fits, hdu->header);
     1025            }
     1026            if (!strcmp (exttype, "PS1_DV1")) {
     1027                sources = pmSourcesRead_CMF_PS1_DV1 (file->fits, hdu->header);
    9851028            }
    9861029
     
    10081051    }
    10091052    readout->data_exists = true;
    1010     status = psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY, "input sources", sources);
    1011     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);
    10121058    return true;
    10131059}
     
    11011147    bool status;
    11021148
    1103     // select the psf of interest
    1104     pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.SOURCES");
    1105     if (!psf) return false;
    1106     return true;
    1107 }
    1108 
    1109 
     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/tap_branches/psModules/src/objects/pmSourceIO.h

    r24694 r27838  
    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/tap_branches/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c

    r25754 r27838  
    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
     
    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);
     
    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
     
    477485        }
    478486
    479 # endif 
     487# endif
    480488        psArrayAdd (table, 100, row);
    481489        psFree (row);
     
    483491
    484492    if (table->n == 0) {
    485         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        }
    486499        psFree (outhead);
    487500        psFree (table);
     
    491504    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    492505    if (!psFitsWriteTable (fits, outhead, table, extname)) {
    493         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
     506        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
    494507        psFree (outhead);
    495508        psFree(table);
     
    503516
    504517// XXX this layout is still the same as PS1_DEV_1
    505 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)
    506519{
    507520
     
    609622
    610623    if (table->n == 0) {
    611         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        }
    612630        psFree (outhead);
    613631        psFree (table);
     
    617635    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    618636    if (!psFitsWriteTable (fits, outhead, table, extname)) {
    619         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
     637        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
    620638        psFree (outhead);
    621639        psFree(table);
  • branches/tap_branches/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c

    r25754 r27838  
    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])) {
     
    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");
     
    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?
    355     // bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
     384    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
     385    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
    356386    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
    357     // bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
    358387    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
    359388
    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);
     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
    398 # if (0)
     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
    399449        // Petrosian measurements
    400450        // XXX insert header data: petrosian ref radius, flux ratio
     451        // XXX check flags to see if Pet was measured
    401452        if (doPetrosian) {
    402             pmSourcePetrosianValues *petrosian = source->extpars->petrosian;
    403             if (petrosian) {
    404                 psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       petrosian->mag);
    405                 psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", petrosian->magErr);
    406                 psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          petrosian->rad);
    407                 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);
    408466            } else {
    409467                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
     
    411469                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
    412470                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);
    413475            }
    414476        }
    415477
     478# if (0)
    416479        // Kron measurements
    417480        if (doKron) {
     
    446509            }
    447510        }
    448 
    449         // Flux Annuli
     511# endif
     512
     513        // Flux Annuli (if we have extended source measurements, we have these.  only optionally save them)
    450514        if (doAnnuli) {
    451             pmSourceAnnuli *annuli = source->extpars->annuli;
    452             if (annuli) {
    453                 psVector *fluxVal = annuli->flux;
    454                 psVector *fluxErr = annuli->fluxErr;
    455                 psVector *fluxVar = annuli->fluxVar;
    456 
    457                 for (int j = 0; j < fluxVal->n; j++) {
    458                     char name[32];
    459                     sprintf (name, "FLUX_VAL_R_%02d", j);
    460                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", fluxVal->data.F32[j]);
    461                     sprintf (name, "FLUX_ERR_R_%02d", j);
    462                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", fluxErr->data.F32[j]);
    463                     sprintf (name, "FLUX_VAR_R_%02d", j);
    464                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", fluxVar->data.F32[j]);
    465                 }
    466             } else {
    467                 for (int j = 0; j < radialBinsLower->n; j++) {
    468                     char name[32];
    469                     sprintf (name, "FLUX_VAL_R_%02d", j);
    470                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", NAN);
    471                     sprintf (name, "FLUX_ERR_R_%02d", j);
    472                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", NAN);
    473                     sprintf (name, "FLUX_VAR_R_%02d", j);
    474                     psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", NAN);
    475                 }
    476             }
    477         }
    478 
    479 # endif
    480         psArrayAdd (table, 100, row);
    481         psFree (row);
    482     }
    483 
     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   
    484554    if (table->n == 0) {
    485         psFitsWriteBlank (fits, outhead, extname);
    486         psFree (outhead);
    487         psFree (table);
    488         return true;
    489     }
    490 
     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   
    491566    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    492567    if (!psFitsWriteTable (fits, outhead, table, extname)) {
    493         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
    494         psFree (outhead);
    495         psFree(table);
    496         return false;
     568        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
     569        psFree (outhead);
     570    psFree(table);
     571    return false;
    497572    }
    498573    psFree (outhead);
    499574    psFree (table);
    500 
     575   
    501576    return true;
    502577}
    503578
    504579// XXX this layout is still the same as PS1_DEV_1
    505 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)
    506581{
    507582
     
    609684
    610685    if (table->n == 0) {
    611         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        }
    612692        psFree (outhead);
    613693        psFree (table);
     
    617697    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
    618698    if (!psFitsWriteTable (fits, outhead, table, extname)) {
    619         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
     699        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
    620700        psFree (outhead);
    621701        psFree(table);
  • branches/tap_branches/psModules/src/objects/pmSourceIO_MatchedRefs.c

    r24801 r27838  
    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/tap_branches/psModules/src/objects/pmSourceIO_PS1_CAL_0.c

    r25754 r27838  
    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");
  • branches/tap_branches/psModules/src/objects/pmSourceIO_PS1_DEV_0.c

    r20937 r27838  
    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/tap_branches/psModules/src/objects/pmSourceIO_PS1_DEV_1.c

    r25754 r27838  
    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");
  • branches/tap_branches/psModules/src/objects/pmSourceIO_SMPDATA.c

    r20937 r27838  
    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/tap_branches/psModules/src/objects/pmSourceMatch.c

    r25256 r27838  
    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            }
     
    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/tap_branches/psModules/src/objects/pmSourceMatch.h

    r23241 r27838  
    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/tap_branches/psModules/src/objects/pmSourceMoments.c

    r25754 r27838  
    5454# define VALID_RADIUS(X,Y,RAD2) (((RAD2) >= (PS_SQR(X) + PS_SQR(Y))) ? 1 : 0)
    5555
    56 bool pmSourceMoments(pmSource *source, psF32 radius, psF32 sigma, psF32 minSN)
     56bool pmSourceMoments(pmSource *source, psF32 radius, psF32 sigma, psF32 minSN, psImageMaskType maskVal)
    5757{
    5858    PS_ASSERT_PTR_NON_NULL(source, false);
     
    114114        for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
    115115            if (vMsk) {
    116                 if (*vMsk) {
     116                if (*vMsk & maskVal) {
    117117                    vMsk++;
    118118                    continue;
     
    135135            // stars.
    136136            if (PS_SQR(pDiff) < minSN2*wDiff) continue;
    137             if (pDiff < 0) continue;
     137            // if (pDiff < 0) continue; // XXX : MWV says I should include < 0.0 valued points...
    138138
    139139            // Apply a Gaussian window function.  Be careful with the window function.  S/N
     
    163163    }
    164164
    165     // if we have less than (1/2) of the possible pixels, force a retry
     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
    166168    // XXX EAM - the limit is a bit arbitrary.  make it user defined?
    167     if ((numPixels < 0.75*R2) || (Sum <= 0)) {
    168         psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n", numPixels, (int)(0.75*R2), Sum);
     169    if ((numPixels < minPixels) || (Sum <= 0)) {
     170        psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n", numPixels, minPixels, Sum);
    169171        return (false);
    170172    }
     
    226228        for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
    227229            if (vMsk) {
    228                 if (*vMsk) {
     230                if (*vMsk & maskVal) {
    229231                    vMsk++;
    230232                    continue;
     
    249251            // stars.
    250252            if (PS_SQR(pDiff) < minSN2*wDiff) continue;
    251             if (pDiff < 0) continue;
     253            // if (pDiff < 0) continue;
    252254
    253255            // Apply a Gaussian window function.  Be careful with the window function.  S/N
     
    315317    source->moments->Myyyy = YYYY/Sum;
    316318
    317     if (source->moments->Mxx < 0) {
    318         fprintf (stderr, "error: neg second moment??\n");
    319     }
    320     if (source->moments->Myy < 0) {
    321         fprintf (stderr, "error: neg second moment??\n");
    322     }
     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    // }
    323325
    324326    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",
  • branches/tap_branches/psModules/src/objects/pmSourcePhotometry.c

    r25754 r27838  
    109109        psAssert (isfinite(fluxScale), "how can the flux scale be invalid? source at %d, %d\n", source->peak->x, source->peak->y);
    110110        psAssert (fluxScale > 0.0, "how can the flux scale be negative? source at %d, %d\n", source->peak->x, source->peak->y);
    111         source->psfMag = -2.5*log10(fluxScale * source->modelPSF->params->data.F32[PM_PAR_I0]);
     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);
    112114    } else {
    113         status = pmSourcePhotometryModel (&source->psfMag, source->modelPSF);
     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]);
    114117    }
    115118
     
    119122        for (int i = 0; i < source->modelFits->n; i++) {
    120123            pmModel *model = source->modelFits->data[i];
    121             status = pmSourcePhotometryModel (&model->mag, model);
     124            status = pmSourcePhotometryModel (&model->mag, NULL, model);
    122125            if (model == source->modelEXT) foundEXT = true;
    123126        }
     
    125128            source->extMag = source->modelEXT->mag;
    126129        } else {
    127             status = pmSourcePhotometryModel (&source->extMag, source->modelEXT);
     130            status = pmSourcePhotometryModel (&source->extMag, NULL, source->modelEXT);
    128131        }
    129132    } else {
    130133        if (source->modelEXT) {
    131             status = pmSourcePhotometryModel (&source->extMag, source->modelEXT);
     134            status = pmSourcePhotometryModel (&source->extMag, NULL, source->modelEXT);
    132135        }
    133136    }
     
    143146    if (mode & PM_SOURCE_PHOT_WEIGHT) {
    144147        pmSourcePixelWeight (&source->pixWeight, model, source->maskObj, maskVal);
     148    }
     149
     150    // measure the contribution of included pixels
     151    if (mode & PM_SOURCE_PHOT_DIFFSTATS) {
     152        pmSourceMeasureDiffStats (source, maskVal);
    145153    }
    146154
     
    217225
    218226// return source model magnitude
    219 bool pmSourcePhotometryModel (float *fitMag, pmModel *model)
    220 {
    221     PS_ASSERT_PTR_NON_NULL(fitMag, false);
    222     if (model == NULL) {
    223         return false;
    224     }
    225 
    226     float fitSum = 0;
    227     *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;
    228234
    229235    // measure fitMag
    230     fitSum = model->modelFlux (model->params);
    231     if (fitSum <= 0)
    232         return false;
    233     if (!isfinite(fitSum))
    234         return false;
    235     *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;
    236249
    237250    return (true);
     
    356369
    357370    *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
    358422    return (true);
    359423}
     
    545609
    546610// determine chisq, etc for linear normalization-only fit
    547 bool pmSourceChisq (pmModel *model, psImage *image, psImage *mask, psImage *variance,
    548                     psImageMaskType maskVal)
     611bool pmSourceChisq (pmModel *model, psImage *image, psImage *mask, psImage *variance, psImageMaskType maskVal, const float covarFactor)
    549612{
    550613    PS_ASSERT_PTR_NON_NULL(model, false);
     
    561624            if (variance->data.F32[j][i] <= 0)
    562625                continue;
    563             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]);
    564627            Npix ++;
    565628        }
     
    573636
    574637
    575 double pmSourceModelWeight(const pmSource *Mi,
    576                       int term,
    577                       const bool unweighted_sum) // should the cross product be weighted?
     638double pmSourceModelWeight(const pmSource *Mi, int term, const bool unweighted_sum, const float covarFactor)
    578639{
    579640    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
     
    594655                continue;
    595656            if (!unweighted_sum) {
    596                 wt = Wi->data.F32[yi][xi];
     657                wt = covarFactor * Wi->data.F32[yi][xi];
    597658                if (wt == 0)
    598659                    continue;
     
    623684}
    624685
    625 double pmSourceModelDotModel (const pmSource *Mi,
    626                               const pmSource *Mj,
    627                               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)
    628687{
    629688    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
     
    677736                flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]);
    678737            } else {
    679                 wt = Wi->data.F32[yi][xi];
     738                wt = covarFactor * Wi->data.F32[yi][xi];
    680739                if (wt > 0) {
    681740                    flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]) / wt;
     
    687746}
    688747
    689 double pmSourceDataDotModel (const pmSource *Mi,
    690                              const pmSource *Mj,
    691                              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)
    692749{
    693750    PS_ASSERT_PTR_NON_NULL(Mi, NAN);
     
    741798                flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]);
    742799            } else {
    743                 wt = Wi->data.F32[yi][xi];
     800                wt = covarFactor * Wi->data.F32[yi][xi];
    744801                if (wt > 0) {
    745802                    flux += (Pi->data.F32[yi][xi] * Pj->data.F32[yj][xj]) / wt;
  • branches/tap_branches/psModules/src/objects/pmSourcePhotometry.h

    r21511 r27838  
    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/tap_branches/psModules/src/objects/pmSourcePlotApResid.c

    r20937 r27838  
    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/tap_branches/psModules/src/objects/pmSourcePlotMoments.c

    r20937 r27838  
    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/tap_branches/psModules/src/objects/pmSourcePlotPSFModel.c

    r20937 r27838  
    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/tap_branches/psModules/src/objects/pmSourceVisual.c

    r25754 r27838  
    7373
    7474    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;
     143    section.x = 0.0;
     144    section.y = 0.0;
     145    section.name = NULL;
     146    psStringAppend (&section.name, "a1");
     147    KapaSetSection (kapa1, &section);
     148    psFree (section.name);
     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;
     210    section.name = NULL;
     211    psStringAppend (&section.name, "a2");
     212    KapaSetSection (kapa1, &section);
     213    psFree (section.name);
     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
    75238    range = graphdata.xmax - graphdata.xmin;
    76239    graphdata.xmax += 0.05*range;
     
    388551    KapaPlotVector (kapa1, y->n, model->data.F32, "y");
    389552
     553    psFree (xm);
     554    psFree (ym);
     555    psFree (Fm);
     556
    390557    psFree (resid);
    391558    psFree (model);
  • branches/tap_branches/psModules/src/objects/pmSourceVisual.h

    r25754 r27838  
    1919bool pmSourceVisualPSFModelResid (pmTrend2D *trend, psVector *x, psVector *y, psVector *param, psVector *mask);
    2020bool pmSourceVisualPlotPSFMetric (pmPSFtry *try);
     21bool pmSourceVisualPlotPSFMetricSubpix (pmPSFtry *try);
     22
    2123bool pmSourceVisualShowModelFit (pmSource *source);
    2224bool pmSourceVisualShowModelFits (pmPSF *psf, psArray *sources, psImageMaskType maskVal);
  • branches/tap_branches/psModules/src/psmodules.h

    r25383 r27838  
    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>
Note: See TracChangeset for help on using the changeset viewer.