Index: /branches/eam_branches/ipp-20101205/psModules/src/camera/pmFPAfile.c
===================================================================
--- /branches/eam_branches/ipp-20101205/psModules/src/camera/pmFPAfile.c	(revision 30106)
+++ /branches/eam_branches/ipp-20101205/psModules/src/camera/pmFPAfile.c	(revision 30107)
@@ -36,5 +36,5 @@
         return;
     }
-    psTrace ("pmFPAfileFree", 5, "freeing %s %ld\n", file->name,(psU64) file->fits);
+    psTrace ("pmFPAfileFree", 5, "freeing %s %p\n", file->name, file->fits);
     psAssert(!fpaFileFreeStrict || file->fits == NULL, "File %s wasn't closed.", file->name);
 
Index: /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConcepts.c
===================================================================
--- /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConcepts.c	(revision 30106)
+++ /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConcepts.c	(revision 30107)
@@ -249,5 +249,5 @@
 CONCEPT_REGISTER_FUNCTION(S32, Enum, -1); // For enums: set default to -1
 CONCEPT_REGISTER_FUNCTION(S32, S32, 0); // For values: set default to 0
-//CONCEPT_REGISTER_FUNCTION(Bool, Bool, NULL); // For values: set default to 0
+CONCEPT_REGISTER_FUNCTION(Bool, Bool, NULL); // For values: set default to 0
 
 static void conceptRegisterTime(const char *name, /* Name of concept */ \
@@ -353,4 +353,5 @@
         conceptRegisterStr("CHIP.ID", "Chip identifier", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
         conceptRegisterF32("CHIP.SEEING", "Seeing FWHM (pixels)", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
+	conceptRegisterBool("CHIP.VIDEOCELL", "Does this OTA have any video cells", p_pmConceptParse_VideoCell,NULL,NULL,false,PM_FPA_LEVEL_CHIP);
     }
 
Index: /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConceptsAverage.c
===================================================================
--- /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConceptsAverage.c	(revision 30106)
+++ /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConceptsAverage.c	(revision 30107)
@@ -136,4 +136,51 @@
     return average;
 }
+float medianWithDropouts (psList *sources, char *name) {
+
+    bool status;
+
+    psListIterator *sourcesIter = psListIteratorAlloc(sources, PS_LIST_HEAD, false); // Iterator for sources
+    pmCell *cell = NULL;                // Source cell from iteration
+
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    psVector *values = psVectorAlloc(sources->n, PS_TYPE_F32);
+    int nvalues = 0;
+    while ((cell = psListGetAndIncrement(sourcesIter))) {
+        if (!cell) {
+            continue;
+        }
+
+        float value = psMetadataLookupF32(&status, cell->concepts, name);
+        if (!status) continue;
+        if (!isfinite(value)) continue;
+
+        values->data.F32[nvalues++] = value;
+    }
+    psFree (sourcesIter);
+    if (!nvalues) {
+        psWarning("no valid values found for %s\n", name);
+        psFree(values);
+        psFree(stats);
+        return INFINITY;
+    }
+    if (!(values = psVectorRealloc(values, nvalues))) {
+        psWarning("failed to reallocate values vector for %s\n", name);
+        psFree(stats);
+        return INFINITY;
+    }
+    if (!psVectorStats(stats, values, NULL, NULL, 0)) {
+        psWarning("psVectorStats failed for %s\n", name);
+        psFree(values);
+        psFree(stats);
+        return INFINITY;
+    }
+
+    psF32 median = psStatsGetValue(stats, PS_STAT_SAMPLE_MEDIAN);
+
+    psFree(values);
+    psFree(stats);
+
+    return median;
+}
 
 // Set a variety of concepts in a cell by averaging over several
@@ -145,5 +192,4 @@
     PS_ASSERT_INT_POSITIVE(sources->n, false);
 
-    float saturation = INFINITY;        // Saturation level
     float bad        = -INFINITY;       // Bad level
     double time      = 0.0;             // Time of observation
@@ -158,4 +204,5 @@
     float exposure  = averageWithDropouts (sources, "CELL.EXPOSURE");
     float darktime  = averageWithDropouts (sources, "CELL.DARKTIME");
+    float saturation = medianWithDropouts(sources, "CELL.SATURATION");
 
     // other concepts are a bit more "special"
@@ -221,13 +268,4 @@
         }
 
-        float cellSaturation = psMetadataLookupF32(NULL, cell->concepts, "CELL.SATURATION");
-	if (cellSaturation > 10000) {
-	    // do not allow invalid values to polute this calculation
-	    // XXX really need to do this on the basis of the fraction of the cell that contributes..
-	    // if a cell is completely masked, it should not be included.
-	    if (cellSaturation < saturation) {
-		saturation = cellSaturation;
-	    }
-	}
         float cellBad = psMetadataLookupF32(NULL, cell->concepts, "CELL.BAD");
         if (cellBad > bad) {
Index: /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConceptsStandard.c
===================================================================
--- /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConceptsStandard.c	(revision 30106)
+++ /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConceptsStandard.c	(revision 30107)
@@ -737,4 +737,33 @@
     return psMetadataItemAllocS32(pattern->name, pattern->comment, binning);
 }
+
+// VIDEOCELLS
+psMetadataItem *p_pmConceptParse_VideoCell(const psMetadataItem *concept,
+					  const psMetadataItem *pattern,
+					  pmConceptSource source,
+					  const psMetadata *cameraFormat,
+					  const pmFPA *fpa,
+					  const pmChip *chip,
+					  const pmCell *cell)
+{
+  assert(concept);
+  assert(pattern);
+  bool has_video_cell = false;
+
+  if (concept->type != PS_DATA_STRING) {
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not string\n",
+	    concept->name, concept->type);
+    return NULL;
+  }
+
+  char *Vptr = strchr(concept->data.V,'V');
+  if (Vptr) {
+    has_video_cell = true;
+  }
+
+  return psMetadataItemAllocBool(pattern->name, pattern->comment, has_video_cell);
+}
+  
+   
 
 // BTOOLAPP
Index: /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConceptsStandard.h
===================================================================
--- /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConceptsStandard.h	(revision 30106)
+++ /branches/eam_branches/ipp-20101205/psModules/src/concepts/pmConceptsStandard.h	(revision 30107)
@@ -136,4 +136,15 @@
     );
 
+// Parse the CHIP.VIDEOCELL concept
+psMetadataItem *p_pmConceptParse_VideoCell(
+   const psMetadataItem *concept, ///< Concept to parse
+   const psMetadataItem *pattern, ///< Pattern to use in parsing
+   pmConceptSource source, ///< Source for concept
+   const psMetadata *cameraFormat, ///< Camera format definition
+   const pmFPA *fpa, ///< FPA for concept, or NULL
+   const pmChip *chip, ///< Chip for concept, or NULL
+   const pmCell *cell ///< Cell for concept, or NULL
+   );
+
 /// Format for the BTOOLAPP conceptn
 psMetadataItem *p_pmConceptParse_BTOOLAPP(
Index: /branches/eam_branches/ipp-20101205/psModules/src/objects/pmPSFtry.h
===================================================================
--- /branches/eam_branches/ipp-20101205/psModules/src/objects/pmPSFtry.h	(revision 30106)
+++ /branches/eam_branches/ipp-20101205/psModules/src/objects/pmPSFtry.h	(revision 30107)
@@ -100,5 +100,5 @@
 bool pmPSFtryFitEXT (pmPSFtry *psfTry, pmPSFOptions *options, psImageMaskType maskVal, psImageMaskType markVal);
 
-bool pmPSFtryMakePSF (bool *goodFit, pmPSFtry *psfTry);
+bool pmPSFtryMakePSF (bool *pGoodFit, pmPSFtry *psfTry);
 
 bool pmPSFtryFitPSF (pmPSFtry *psfTry, pmPSFOptions *options, psImageMaskType maskVal, psImageMaskType markVal);
@@ -123,5 +123,5 @@
 );
 
-bool pmPSFFitShapeParams (bool *goodFit, pmPSF *psf, psArray *sources, psVector *x, psVector *y, psVector *srcMask);
+bool pmPSFFitShapeParams (bool *pGoodFit, pmPSF *psf, psArray *sources, psVector *x, psVector *y, psVector *srcMask);
 
 float psVectorSystematicError (psVector *residuals, psVector *errors, float clipFraction);
Index: /branches/eam_branches/ipp-20101205/psModules/src/objects/pmPSFtryMakePSF.c
===================================================================
--- /branches/eam_branches/ipp-20101205/psModules/src/objects/pmPSFtryMakePSF.c	(revision 30106)
+++ /branches/eam_branches/ipp-20101205/psModules/src/objects/pmPSFtryMakePSF.c	(revision 30107)
@@ -50,5 +50,5 @@
 Note: some of the array entries may be NULL (failed fits); ignore them.
  *****************************************************************************/
-bool pmPSFtryMakePSF (bool *goodFit, pmPSFtry *psfTry)
+bool pmPSFtryMakePSF (bool *pGoodFit, pmPSFtry *psfTry)
 {
     PS_ASSERT_PTR_NON_NULL(psfTry, false);
@@ -74,10 +74,10 @@
 
     // fit the shape parameters (SXX, SYY, SXY) as a function of position
-    if (!pmPSFFitShapeParams (goodFit, psf, psfTry->sources, x, y, srcMask)) {
+    if (!pmPSFFitShapeParams (pGoodFit, psf, psfTry->sources, x, y, srcMask)) {
         psFree(x);
         psFree(y);
         return false;
     }
-    if (!goodFit) {
+    if (!*pGoodFit) {
 	psWarning ("poor fit to PSF shape parameters for trend order %d, %d, skipping\n", psf->trendNx, psf->trendNy);
 	psFree(x);
@@ -121,5 +121,5 @@
         // the mask is carried from previous steps and updated with this operation
         // the weight is either the flux error or NULL, depending on 'psf->poissonErrorParams'
-        if (!pmTrend2DFit (goodFit, trend, srcMask, 0xff, x, y, z, NULL)) {
+        if (!pmTrend2DFit (pGoodFit, trend, srcMask, 0xff, x, y, z, NULL)) {
             psError(PS_ERR_UNKNOWN, false, "failed to build psf model for parameter %d", i);
             psFree(x);
@@ -128,5 +128,5 @@
             return false;
         }
-	if (!goodFit) {
+	if (!*pGoodFit) {
 	    // if we do not get a good fit (but do not actually hit an error), 
 	    // tell the calling program to try something else
@@ -154,4 +154,12 @@
 
             pmModel *modelPSF = pmModelFromPSF (source->modelEXT, psf);
+            if (!modelPSF) {
+                fprintf(f, "modelPSF is NULL\n");
+                break;
+            }
+            if (!source->modelEXT) {
+                fprintf(f, "source->modelEXT is NULL\n");
+                break;
+            }
 
             fprintf (f, "%f %f : ", source->modelEXT->params->data.F32[PM_PAR_XPOS], source->modelEXT->params->data.F32[PM_PAR_YPOS]);
@@ -178,5 +186,5 @@
 
 // fit the shape parameters using the supplied order (pmPSF->trendNx,trendNy)
-bool pmPSFFitShapeParams (bool *goodFit, pmPSF *psf, psArray *sources, psVector *x, psVector *y, psVector *srcMask) {
+bool pmPSFFitShapeParams (bool *pGoodFit, pmPSF *psf, psArray *sources, psVector *x, psVector *y, psVector *srcMask) {
 
     // we are doing a robust fit.  after each pass, we drop points which are more deviant than
@@ -234,6 +242,6 @@
 	trend = psf->params->data[PM_PAR_E0];
 	trend->stats->clipIter = 1; // in allocation, this value is set to the value of nIter, but we should use 1 here
-	status &= pmTrend2DFit (goodFit, trend, srcMask, 0xff, x, y, e0, NULL);
-	if (!goodFit) {
+	status &= pmTrend2DFit (pGoodFit, trend, srcMask, 0xff, x, y, e0, NULL);
+	if (!*pGoodFit) {
 	    psFree (e0);
 	    psFree (e1);
@@ -249,6 +257,6 @@
 	trend = psf->params->data[PM_PAR_E1];
 	trend->stats->clipIter = 1; // in allocation, this value is set to the value of nIter, but we should use 1 here
-	status &= pmTrend2DFit (goodFit, trend, srcMask, 0xff, x, y, e1, NULL);
-	if (!goodFit) {
+	status &= pmTrend2DFit (pGoodFit, trend, srcMask, 0xff, x, y, e1, NULL);
+	if (!*pGoodFit) {
 	    psFree (e0);
 	    psFree (e1);
@@ -264,6 +272,6 @@
 	trend = psf->params->data[PM_PAR_E2];
 	trend->stats->clipIter = 1; // in allocation, this value is set to the value of nIter, but we should use 1 here
-	status &= pmTrend2DFit (goodFit, trend, srcMask, 0xff, x, y, e2, NULL);
-	if (!goodFit) {
+	status &= pmTrend2DFit (pGoodFit, trend, srcMask, 0xff, x, y, e2, NULL);
+	if (!*pGoodFit) {
 	    psFree (e0);
 	    psFree (e1);
Index: /branches/eam_branches/ipp-20101205/psModules/src/objects/pmTrend2D.c
===================================================================
--- /branches/eam_branches/ipp-20101205/psModules/src/objects/pmTrend2D.c	(revision 30106)
+++ /branches/eam_branches/ipp-20101205/psModules/src/objects/pmTrend2D.c	(revision 30107)
@@ -179,5 +179,5 @@
 }
 
-bool pmTrend2DFit(bool *goodFit, pmTrend2D *trend, psVector *mask, psVectorMaskType maskVal, const psVector *x,
+bool pmTrend2DFit(bool *pGoodFit, pmTrend2D *trend, psVector *mask, psVectorMaskType maskVal, const psVector *x,
                   const psVector *y, const psVector *f, const psVector *df)
 {
@@ -189,10 +189,10 @@
 
     bool status = false;
-    *goodFit = false;
+    *pGoodFit = false;
     // for the psImageMap fit, it is possible to have valid data but no valid solution for
     // example, an isolated cell may not be reached from other cells, making the solution
     // degenerate.  psImageMapFit should probably handle this case, but until it does, we allow
-    // it to fail on the result, but not yield an error (goodFit = false).
-    // psVectorClipFitPolynomial2D can not fail in this way (really?), so goodFit is always
+    // it to fail on the result, but not yield an error (pGoodFit = false).
+    // psVectorClipFitPolynomial2D can not fail in this way (really?), so pGoodFit is always
     // true
 
@@ -204,5 +204,5 @@
         // of points in the image, and potentially based on the fractional range of the
         // data?
-	*goodFit = true;
+	*pGoodFit = true;
         break;
 
@@ -210,5 +210,5 @@
         // XXX supply fraction from trend elements
         // XXX need to add the API which adjusts the scale
-        status = psImageMapClipFit(goodFit, trend->map, trend->stats, mask, maskVal, x, y, f, df);
+        status = psImageMapClipFit(pGoodFit, trend->map, trend->stats, mask, maskVal, x, y, f, df);
         break;
 
