Index: trunk/psastro/src/psastroZeroPoint.c
===================================================================
--- trunk/psastro/src/psastroZeroPoint.c	(revision 19290)
+++ trunk/psastro/src/psastroZeroPoint.c	(revision 20268)
@@ -1,8 +1,7 @@
 # include "psastroInternal.h"
-
-bool psastroZeroPointReadout(psArray *rawstars, psArray *refstars, psArray *matches);
 
 bool psastroZeroPoint (pmConfig *config) {
 
+    float zeropt, exptime;
     pmChip *chip = NULL;
     pmCell *cell = NULL;
@@ -29,5 +28,16 @@
     // is this needed? yes, if we didn't do SingleChip astrometry first
     if (!psastroMosaicSetMatch (fpa, recipe, 0)) {
-	psError(PSASTRO_ERR_UNKNOWN, false, "failed to match raw and ref stars for mosaic");
+        psError(PSASTRO_ERR_UNKNOWN, false, "failed to match raw and ref stars for mosaic");
+        return false;
+    }
+
+    // XXX eventually: look up the photcode info from the recipe, use the ZEROPT and A_0 terms
+    // to determine transparency = Mref - Mraw - 2.5*log10(exptime) - ZEROPT - A_0*Color_ref
+    // to get there, we need: a) getstar to write out the color-term magnitudes and b)  the
+    // pmAstromObj to carry a color value as well.
+
+    // really error-out here?  or just skip?
+    if (!psastroZeroPointFromRecipe (&zeropt, &exptime, fpa, recipe)) {
+	psLogMsg ("psastro", PS_LOG_INFO, "failed to load zeropt data from recipe");
 	return false;
     }
@@ -48,17 +58,7 @@
 		if (! readout->data_exists) { continue; }
 
-		// select the raw objects for this readout
-		psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
-		if (rawstars == NULL) { continue; }
+		// calculate dMag for the matched stars
+		psastroZeroPointReadout (readout, zeropt, exptime);
 
-		// select the raw objects for this readout
-		psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
-		if (refstars == NULL) { continue; }
-
-		psArray *matches = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
-		if (matches == NULL) { continue; }
-
-		// calculate dMag for the matched stars
-		psastroZeroPointReadout (rawstars, refstars, matches);
 	    }
 	}
@@ -69,7 +69,19 @@
 }
 
-// XXX for now, let's just measure <dMag> and sigma_dMag
-// XXX a test comment
-bool psastroZeroPointReadout(psArray *rawstars, psArray *refstars, psArray *matches) {
+// we measure <dMag> and \sigma_dMag and write them to the header
+bool psastroZeroPointReadout(pmReadout *readout, float zeropt, float exptime) {
+
+    bool status;
+
+    // select the raw objects for this readout
+    psArray *rawstars = psMetadataLookupPtr (&status, readout->analysis, "PSASTRO.RAWSTARS");
+    if (rawstars == NULL) return false;
+
+    // select the raw objects for this readout
+    psArray *refstars = psMetadataLookupPtr (&status, readout->analysis, "PSASTRO.REFSTARS");
+    if (refstars == NULL) return false;
+
+    psArray *matches = psMetadataLookupPtr (&status, readout->analysis, "PSASTRO.MATCH");
+    if (matches == NULL) return false;
 
     psVector *dMag  = psVectorAllocEmpty (100, PS_TYPE_F32);
@@ -84,5 +96,5 @@
       pmAstromObj *ref = refstars->data[match->ref];
 
-      dMag->data.F32[Npts] = ref->Mag - raw->Mag;
+      dMag->data.F32[Npts] = ref->Mag - raw->Mag - 2.5*log10(exptime);
       psVectorExtend (dMag, 100, 1);
       Npts++;
@@ -101,5 +113,12 @@
     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);
+    fprintf (stderr, "zero point %f +/- %f using %d stars; transparency %f\n", stats->clippedMean, stats->clippedStdev, Npts, zeropt - stats->clippedMean);
+
+    psMetadata *header = psMetadataLookupMetadata (&status, readout->analysis, "PSASTRO.HEADER");
+
+    psMetadataAddF32 (header, PS_LIST_TAIL, "ZPT_OBS", PS_META_REPLACE, "measured zero point", stats->clippedMean);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "ZPT_REF", PS_META_REPLACE, "measured zero point", zeropt);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "ZPT_ERR", PS_META_REPLACE, "measured zero point", stats->clippedStdev);
+    psMetadataAddF32 (header, PS_LIST_TAIL, "ZPT_OFF", PS_META_REPLACE, "measured zero point", zeropt - stats->clippedMean);
 
     psFree (dMag);
@@ -107,2 +126,52 @@
     return true;
 }
+
+# define ESCAPE(MSG) { \
+  psLogMsg ("psastro", PS_LOG_INFO, MSG); \
+  return false; }
+
+bool psastroZeroPointFromRecipe (float *zeropt, float *exptime, pmFPA *fpa, psMetadata *recipe) {
+
+    bool status;
+
+    // select the filter; default to fixed photcode and mag limit otherwise
+    char *filter = psMetadataLookupStr (&status, fpa->concepts, "FPA.FILTERID");
+    if (!status) ESCAPE ("missing FPA.FILTER in concepts");
+
+    *exptime = psMetadataLookupF32 (&status, fpa->concepts, "FPA.EXPOSURE");
+    if (!status) ESCAPE ("missing FPA.EXPOSURE in concepts");
+
+    // we need to select the PHOTCODE.DATA folder that matches our filter
+    psMetadataItem *item = psMetadataLookup (recipe, "PHOTCODE.DATA");
+    if (!item) ESCAPE ("PHOTCODE.DATA folders missing");
+    if (item->type != PS_DATA_METADATA_MULTI) ESCAPE ("PHOTCODE.DATA not a multi");
+
+    // PHOTCODE.DATA is a multi of metadata items 
+    psListIterator *iter = psListIteratorAlloc(item->data.list, PS_LIST_HEAD, false);
+
+    psMetadataItem *refItem = NULL;
+    while ((refItem = psListGetAndIncrement (iter))) {
+	if (refItem->type != PS_DATA_METADATA) ESCAPE ("PHOTCODE.DATA entry is not a metadata folder");
+    
+	char *refFilter = psMetadataLookupStr (&status, refItem->data.md, "FILTER");
+	if (!status) {
+	    psLogMsg ("psastro", PS_LOG_INFO, "a PHOTCODE.DATA recipe folder is missing FILTER");
+	    continue;
+	}
+
+	// does this entry match the current filter?
+	if (strcmp (refFilter, filter)) continue;
+
+	psLogMsg ("psastro", PS_LOG_DETAIL, "PHOTCODE.DATA found for filter %s", filter);
+
+	*zeropt = psMetadataLookupF32 (&status, refItem->data.md, "ZEROPT");
+	if (!status) {
+	    psLogMsg ("psastro", PS_LOG_INFO, "a PHOTCODE.DATA recipe folder is missing FILTER");
+	    continue;
+	}
+	psFree (iter);
+	return true;
+    }
+    psFree (iter);
+    return false;
+}
