Index: trunk/psphot/src/psphotModelTest.c
===================================================================
--- trunk/psphot/src/psphotModelTest.c	(revision 14326)
+++ trunk/psphot/src/psphotModelTest.c	(revision 14338)
@@ -1,11 +1,10 @@
 # include "psphotInternal.h"
-static char DEFAULT_MODE[] = "EXT";
-
-// XXX consider this function : add more test information?
-bool psphotModelTest (pmReadout *readout, psMetadata *recipe, psMaskType maskVal, psMaskType mark) {
+# define PM_SOURCE_FIT_PSF_X_EXT PM_SOURCE_FIT_PSF_AND_SKY
+
+// XXX add more test information?
+bool psphotModelTest (pmConfig *config, const pmFPAview *view, psMetadata *recipe, psMaskType maskVal, psMaskType mark) {
 
     bool status;
     int modelType;
-    unsigned int Nfail;
     float obsMag, fitMag, value;
     char name[64];
@@ -13,5 +12,10 @@
     pmSourceFitMode fitMode;
 
-    psMetadataItem *item  = NULL;
+    // run model fitting tests on a single source?
+    if (!psMetadataLookupBool (&status, recipe, "TEST_FIT")) return false;
+
+    // find the currently selected readout
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
+    PS_ASSERT_PTR_NON_NULL (readout, false);
 
     // use poissonian errors or local-sky errors
@@ -20,24 +24,41 @@
     pmSourceFitModelInit (15, 0.1, 1.0, POISSON_ERRORS);
 
-    // run model fitting tests on a single source
-    if (!psMetadataLookupBool (&status, recipe, "TEST_FIT")) return false;
+    // find the various fitting parameters (try test values first)
+    float INNER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_INNER_RADIUS");
+    if (!status || !isfinite(INNER)) {
+        INNER = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
+    }
+    float OUTER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_OUTER_RADIUS");
+    if (!status || !isfinite(OUTER)) {
+        OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
+    }
+    float RADIUS = psMetadataLookupF32 (&status, recipe, "TEST_FIT_RADIUS");
+    if (!status || !isfinite(RADIUS)) {
+        RADIUS = psMetadataLookupF32 (&status, recipe, "PSF_FIT_RADIUS");
+    }
+    float mRADIUS = psMetadataLookupF32 (&status, recipe, "TEST_MOMENTS_RADIUS");
+    if (!status || !isfinite(mRADIUS)) {
+        mRADIUS = psMetadataLookupF32 (&status, recipe, "PSF_MOMENTS_RADIUS");
+    }
+
+    // define the source of interest
+    float xObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_X");
+    float yObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_Y");
+    if (!isfinite(xObj) || !isfinite(yObj)) psAbort ("object position is not defined");
 
     // what fitting mode to use?
+    fitMode = PM_SOURCE_FIT_EXT;
     char *fitModeWord = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODE");
-    if (!status || !strcasecmp (fitModeWord, "DEFAULT")) {
-        fitModeWord = DEFAULT_MODE;
-    }
-    fitMode = PM_SOURCE_FIT_EXT;
-    if (!strcasecmp (fitModeWord, "PSF")) fitMode = PM_SOURCE_FIT_PSF;
+    if (fitModeWord && !strcasecmp (fitModeWord, "PSF")) fitMode = PM_SOURCE_FIT_PSF;
+    if (fitModeWord && !strcasecmp (fitModeWord, "CONV")) fitMode = PM_SOURCE_FIT_PSF_X_EXT;
+    if (fitModeWord && !strcasecmp (fitModeWord, "DEFAULT")) fitMode = PM_SOURCE_FIT_EXT;
 
     // in fitMode, psf sets the model type
     if (fitMode == PM_SOURCE_FIT_PSF) {
-	// XXX load psf using psphotLoadPSF
-        char *psfFile = psMetadataLookupStr (&status, recipe, "PSF_INPUT_FILE");
-        if (!status) psAbort("PSF_INPUT_FILE not supplied");
-        psMetadata *psfData = psMetadataConfigRead(NULL, &Nfail, psfFile, FALSE);
-        psf = pmPSFfromMetadata (psfData);
+	psf = psphotLoadPSF (config, view, recipe);
+        if (!psf) psAbort("PSF_INPUT_FILE not supplied");
         modelType = psf->type;
-    } else {
+    }
+    if (fitMode == PM_SOURCE_FIT_EXT) {
         // find the model: supplied by user or first in the PSF_MODEL list
         char *modelName  = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODEL");
@@ -57,5 +78,5 @@
 
             // take the first list element
-            item = psListGet (list, PS_LIST_HEAD);
+            psMetadataItem *item = psListGet (list, PS_LIST_HEAD);
             modelName = item->data.V;
         }
@@ -63,29 +84,32 @@
         if (modelType < 0) psAbort("unknown model %s", modelName);
     }
-
-    // find the fitting parameters (try test values first)
-    float INNER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_INNER_RADIUS");
-    if (!status || !isfinite(INNER)) {
-        INNER = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
-    }
-
-    float OUTER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_OUTER_RADIUS");
-    if (!status || !isfinite(OUTER)) {
-        OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
-    }
-
-    float RADIUS = psMetadataLookupF32 (&status, recipe, "TEST_FIT_RADIUS");
-    if (!status || !isfinite(RADIUS)) {
-        RADIUS = psMetadataLookupF32 (&status, recipe, "PSF_FIT_RADIUS");
-    }
-
-    float mRADIUS = psMetadataLookupF32 (&status, recipe, "TEST_MOMENTS_RADIUS");
-    if (!status || !isfinite(mRADIUS)) {
-        mRADIUS = psMetadataLookupF32 (&status, recipe, "PSF_MOMENTS_RADIUS");
-    }
-
-    // define the source of interest
-    float xObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_X");
-    float yObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_Y");
+    if (fitMode == PM_SOURCE_FIT_PSF_X_EXT) {
+	// we need to load BOTH a psf and an ext model
+	psf = psphotLoadPSF (config, view, recipe);
+        if (!psf) psAbort("PSF_INPUT_FILE not supplied");
+
+        // find the model: supplied by user or first in the PSF_MODEL list
+        char *modelName  = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODEL");
+        if (!status || !strcasecmp (modelName, "DEFAULT")) {
+            // get the list pointers for the PSF_MODEL entries
+
+            psList *list = NULL;
+            psMetadataItem *mdi = psMetadataLookup (recipe, "PSF_MODEL");
+            if (mdi == NULL) psAbort("missing PSF_MODEL selection");
+            if (mdi->type == PS_DATA_STRING) {
+                list = psListAlloc(NULL);
+                psListAdd (list, PS_LIST_HEAD, mdi);
+            } else {
+                if (mdi->type != PS_DATA_METADATA_MULTI) psAbort("missing PSF_MODEL selection");
+                list = psMemIncrRefCounter(mdi->data.list);
+            }
+
+            // take the first list element
+            psMetadataItem *item = psListGet (list, PS_LIST_HEAD);
+            modelName = item->data.V;
+        }
+        modelType = pmModelSetType (modelName);
+        if (modelType < 0) psAbort("unknown model %s", modelName);
+    }
 
     // construct the source structures
@@ -100,5 +124,5 @@
     // get the source moments
     status = pmSourceMoments (source, mRADIUS);
-    if (!status) psAbort("pmSourceLocalSky error");
+    if (!status) psAbort("psSourceMoments error");
     source->peak->value = source->moments->Peak;
 
@@ -116,16 +140,14 @@
     // get the initial model parameter guess
     pmModel *model = pmSourceModelGuess (source, modelType);
-    // if any parameters are defined, use those values
+
+    // if any parameters are defined by the user, take those values
     int nParams = pmModelParameterCount (modelType);
     psF32 *params = model->params->data.F32;
+    params[PM_PAR_XPOS] = xObj; // XXX use the user-supplied value, 
+    params[PM_PAR_YPOS] = yObj; // XXX or use the centroid
     for (int i = 0; i < nParams; i++) {
-        if (i == 2) {
-            params[i] = xObj;
-            continue;
-        }
-        if (i == 3) {
-            params[i] = yObj;
-            continue;
-        }
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+
         sprintf (name, "TEST_FIT_PAR%d", i);
         value = psMetadataLookupF32 (&status, recipe, name);
@@ -138,4 +160,5 @@
     fprintf (stderr, "peak: %f @ (%f, %f)\n", source->moments->Sum*area, (double)source->peak->x, (double)source->peak->y);
 
+    // for PSF fitting, set the shape parameters based on the PSF & source position
     if (fitMode == PM_SOURCE_FIT_PSF) {
         pmModel *modelPSF = pmModelFromPSF (model, psf);
@@ -154,5 +177,4 @@
     fprintf (stderr, "guess: %f @ (%f, %f)\n", axes.theta*180/M_PI, axes.major, axes.minor);
 
-
     fprintf (stderr, "input parameters: \n");
     for (int i = 0; i < nParams; i++) {
@@ -162,4 +184,5 @@
     // define the pixels used for the fit
     psImageKeepCircle (source->maskObj, xObj, yObj, RADIUS, "OR", mark);
+    psphotSaveImage (NULL, source->maskObj, "mask1.fits");
 
     char *fitset = psMetadataLookupStr (&status, recipe, "TEST_FIT_SET");
@@ -169,5 +192,14 @@
     }
 
-    status = pmSourceFitModel (source, model, fitMode, maskVal);
+    if (fitMode == PM_SOURCE_FIT_PSF_X_EXT) {
+	// build the psf for the object
+	source->modelPSF = pmModelFromPSF (model, psf);
+	source->modelEXT = model;
+	status = psphotPSFConvModel (source, recipe, maskVal);
+	model = source->modelConv;
+	params = model->params->data.F32;
+    } else {
+	status = pmSourceFitModel (source, model, fitMode, maskVal);
+    }
 
     // measure the source mags
