Index: /trunk/psastro/src/psastroAnalysis.c
===================================================================
--- /trunk/psastro/src/psastroAnalysis.c	(revision 19047)
+++ /trunk/psastro/src/psastroAnalysis.c	(revision 19048)
@@ -79,4 +79,6 @@
     }
 
+    // psastroZeroPoint (rawstars, refstars, matches);
+
     // XXX how do we specify stack astrometry?
     // psastroStackAstrom (config, refs);
Index: /trunk/psastro/src/psastroZeroPoint.c
===================================================================
--- /trunk/psastro/src/psastroZeroPoint.c	(revision 19048)
+++ /trunk/psastro/src/psastroZeroPoint.c	(revision 19048)
@@ -0,0 +1,102 @@
+# include "psastroInternal.h"
+
+bool GetZeroPoint(psArray *rawstars, psArray *refstars, psArray *matches);
+
+bool psastroZeroPoint (pmConfig *config) {
+
+    bool status;
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+    psArray *gradients = NULL;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+	psError(PSASTRO_ERR_CONFIG, false, "Can't find PSASTRO recipe!\n");
+	return false;
+    }
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    if (!input) {
+	psError(PSASTRO_ERR_CONFIG, false, "Can't find input data!\n");
+	return false;
+    }
+    pmFPA *fpa = input->fpa;
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // this loop selects the matched stars for all chips
+    while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
+        psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) { continue; }
+	if (!chip->toFPA) { continue; }
+
+	while ((cell = pmFPAviewNextCell (view, fpa, 1)) != NULL) {
+            psTrace ("psastro", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) { continue; }
+
+	    // process each of the readouts
+	    // XXX there can only be one readout per chip, right?
+	    while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+		if (! readout->data_exists) { continue; }
+
+		// select the raw objects for this readout
+		psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+		if (rawstars == NULL) { continue; }
+
+		// select the raw objects for this readout
+		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+		if (refstars == NULL) { continue; }
+
+		psArray *match = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
+		if (match == NULL) { continue; }
+
+		// calculate dMag for the matched stars
+		GetZeroPoint (rawstars, refstars, matches);
+	    }
+	}
+    }
+
+    psFree (view);
+    return true;
+}
+
+// XXX for now, let's just measure <dMag> and sigma_dMag
+bool GetZeroPoint(psArray *rawstars, psArray *refstars, psArray *matches) {
+
+    psVector *dMag  = psVectorAllocEmpty (100, PS_TYPE_F32);
+
+    int Npts = 0;
+    for (int i = 0; i < matches->n; i++) {
+
+      pmAstromMatch *match = matches->data[i];
+
+      pmAstromObj *raw = rawstars->data[match->raw];
+
+      pmAstromObj *ref = refstars->data[match->ref];
+
+      dMag->data.F32[Npts] = ref->Mag - raw->Mag;
+      psVectorExtend (dMag, 100, 1);
+      Npts++;
+    }
+
+    psTrace ("psModules.astrom", 4, "Npts: %d\n", Npts);
+
+    if (Npts < 3) {
+      fprintf (stderr, "zero point NaN +/- NaN\n");
+      psFree (dMag);
+      return false;
+    }
+
+    // stats structure and mask for use in measuring the clipping statistic
+    // this analysis has too few data points to use the robust median method
+    psStats *stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+    psVectorStats (stats, dMag, NULL, NULL, 0);
+    fprintf (stderr, "zero point %f +/- %f\n", stats->clippedMean, stats->clippedStdev);
+
+    psFree (dMag);
+    psFree (stats);
+    return true;
+}
