Index: /trunk/archive/scripts/phase2-alt.c
===================================================================
--- /trunk/archive/scripts/phase2-alt.c	(revision 2520)
+++ /trunk/archive/scripts/phase2-alt.c	(revision 2520)
@@ -0,0 +1,307 @@
+# include <pslib.h>
+
+main (int argc, char **argv) {
+
+    psArray *headers;
+    psMetadata *base, *site, *camera, *recipe;
+
+    pmConfigLoadBasic (&site, &base);
+
+    fits = psFitsAlloc (input);
+    headers = psFitsReadHeaderSet (fits);
+
+    pmConfigLoadCamera (&camera, site, headers->data[0]);
+    pmConfigLoadRecipe (&recipe, camera, "PHASE2");
+
+    // check the validity of the given set of headers based on expectactions for this camera
+    pmCameraValidateHeaders (headers, camera);
+
+    // construct a complete, empty (ie, no pixel data) FPA structure for this camera / header set
+    FPA = pmFPAfromHeader (headers, camera);
+  
+    /* ideally, the sequence of cells will be the same as the sequence of extensions */
+    for (i = 0; i < FPA[0].chips.n; i++) {
+	chip = FPA[0].chips.data[i];
+	for (j = 0; j < chip[0].cells.n; j++) {
+	    cell = chip[0].cells.data[j];
+
+	    cellMD  = psMetadataLookupPtr (cell->metadata, "CELL");
+	    Nextend = psMetadataLookupS32 (cellMD, "EXT.NUMBER");
+	    f = psFitsMoveExtNum(f, extNum, FALSE); // this should not move if we are already there
+
+	    for (k = 0; k < cell[0].readouts.n; k++) {
+		readout = cell[0].readouts[k];
+
+		// load the specific readout pixels.  put this in a function?  
+		cell[0].readouts[k] = pmReadoutLoad (cell[0].readouts[k], fits, cell, k);
+		bias_subtract_readout (readout, NULL, recipe);
+		
+	    }
+	}
+    }	
+}
+
+// bias subtract the given readout according to the recipe (bias image may be NULL)
+// no mask needed yet
+bias_subtract_readout (psReadout *readout, psImage *bias, psMetadata *recipe) {
+  
+    char *biassec;
+    psImage *overscan;
+    psList *overscanlist;
+    pmOverscanAxis direction;
+    psRectangle biassec;
+
+    // determine overscan option
+    biassec = pmConfigLookupRegion (recipe, readout[0].metadata, "BIAS.OVERSCAN"); 
+    overscan = psImageSubsection (readout[0].image, biassec);
+
+    psListAdd (overscanlist, overscan, PS_LIST_HEAD);             // list of overscans (allocate list first)
+
+    // the direction is implied by noting the long dimension of the biassec
+    direction = DirectionFromOverscan (biassec);  // weak, but do we want a function to deal with this or add it inline?
+
+    // the type of statistic is defined by the recipe: BIAS.OVERSCAN.STATS
+    statname = psMetadataLookup (recipe, "BIAS.OVERSCAN.STATS");  // string defining overscan stats
+    stats = DefineStats (statname);
+
+    // identify the type of fit requested and relevant parameters
+    // the type of fit is defined by the recipe: BIAS.OVERSCAN.STATS
+    fitname = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT");  // string defining overscan stats
+
+    if (!strcmp (fitname, "NONE")) {
+	fitType = PM_FIT_NONE;
+	fitSpec = NULL;
+	nBin = 0;
+    }
+
+    if (!strcmp (fitname, "SPLINE")) {
+	fitType = PM_FIT_SPLINE;
+	nBin = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NBIN");
+	nPts = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NPTS");
+	order = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.ORDER");  // only 1 or 3 is allowed for 'order'
+	fitSpec = psSpline1DAlloc (nPts, 3, 0, Npix);                  // Npix is either NAXIS1 or NAXIS2 (Nx or Ny);
+    }
+
+    if (!strcmp (fitname, "POLYNOMIAL")) {
+	fitType = PM_FIT_POLYNOMIAL;
+	nBin = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NBIN");
+	nPts = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.NPTS");
+	order = psMetadataLookup (recipe, "BIAS.OVERSCAN.FIT.ORDER");
+	fitSpec = psPolynomial1DAlloc (nPts, PS_POLYNOMIAL_CHEB);
+    }
+
+    readout[0].image = pmSubtractBias (readout[0].image, fitSpec, overscanlist, direction, stats, nBin, PM_FIT_NONE, bias);
+
+}
+
+// subtract dark image using the appropriate scaling factor for this exposure time
+// no mask needed yet
+dark_subtract_readout (psReadout *readout, psImage *dark, psMetadata *recipe) {
+  
+    char *biassec;
+    psImage *overscan;
+    psList *overscanlist;
+    pmOverscanAxis direction;
+
+    if (dark == NULL) {
+	return;
+    }
+    
+    // need to get a lookup table from the database, apply exptime for readout and dark
+    factor_source = psMetadataLookup (recipe, "DARK.FACTOR"); // use the recipe to determine the dark scaling
+    factor = getScalingFactor (readout, dark, factor_source); 
+
+    // out = in - dark * factor
+    tmpdark = psBinaryOp (NULL, dark, "*", factor);
+    readout[0].image = psBinaryOp (readout[0].image, readout[0].image, "-", tmpdark);
+
+    psFree (tmpdark);
+}
+
+// this corrects the non-linear response, but probably should use data determined at the cell level
+// we need to set a mask value for pixels which have were out of range (TBD)
+nonlinearity_correct_readout (psReadout *readout, psReadout *mask, psMetadata *recipe) {
+  
+    char *method;
+    psPolynomial1D correction;
+    psVector inFlux, outFlux;
+
+    method = psMetadataLookup (recipe, "NONLINEAR.METHOD");
+
+    // use a polynomial; get coeffs from recipe
+    if (!strcmp (method, "POLYNOMIAL")) {
+	correction.n = psMetadataLookup (recipe, "NONLINEAR.ORDER"); 
+	correction.coeffs = psMetadataLookup (recipe, "NONLINEAR.COEFFS"); 
+	pmNonLinearityPolynomial (readout, correction);
+    }
+
+    if (!strcmp (method, "LOOKUP")) {
+	lookup_table = DetrendDatabaseLookup (time, cell, camera, etc?);
+	inFlux = psFITSTableRead (lookup_table, "IN_FLUX"); 
+	outFlux = psFITSTableRead (lookup_table, "OUT_FLUX"); 
+	pmNonLinearityLookup (readout, inFlux, outFlux);
+    }
+}
+
+// trim the image and mask based on the requested region
+trim_readout (psReadout *readout, psReadout *mask, psMetadata *recipe) {
+  
+    datasec = pmRecipeDefineSection (recipe, readout[0].metadata, "TRIM.DATASEC"); 
+
+    psImageTrim (readout[0].image, datasec);
+    psImageTrim (mask[0].image, datasec);
+
+}
+
+// apply the bad pixel mask to the image mask
+mask_readout (psReadout *readout, psImage *mask, psImage *badpix, psKernel *kernel, psMetadata *recipe) {
+  
+}
+
+psImage *load_bias_image (psMetadata *recipe, psMetadata *header, char *extname) {
+
+    // load in the complete corresponding bias image (this should be done once per cell)
+    // we are implying that the bias image is guaranteed to have the corresponding EXTNAME
+    bias_source = psMetadataLookup (recipe, "BIAS.IMAGE");
+    biasname = NULL;
+    if (!strncmp (bias_source, "FILE:", 5)) {
+	biasname = &bias_source[5];
+    }
+    if (!strcmp (bias_source, "DB:BEST")) {
+	biasname = DetrendDatabaseLookup ("best", header);   // these APIs need to be seriously fleshed out!!
+    }
+    if (!strcmp (bias_source, "DB:CLOSE")) {
+	biasname = DetrendDatabaseLookup ("close", header);   // these APIs need to be seriously fleshed out!!
+    }
+    if (biasname != NULL) {
+	bias     = psImageReadSection (NULL, 0, 0, 0, 0, 0, extname, 0, biasname);
+    }
+
+    return (bias);
+}
+
+psImage *load_dark_image (psMetadata *recipe, psMetadata *header, char *extname) {
+
+    // load in the complete corresponding bias image (this should be done once per cell)
+    // we are implying that the bias image is guaranteed to have the corresponding EXTNAME
+    dark_source = psMetadataLookup (recipe, "DARK.IMAGE");
+    darkname = NULL;
+    if (!strncmp (dark_source, "FILE:", 5)) {
+	darkname = &dark_source[5];
+    }
+    if (!strcmp (dark_source, "DB:BEST")) {
+	darkname = DetrendDatabaseLookup ("best", header);   // these APIs need to be seriously fleshed out!!
+    }
+    if (!strcmp (dark_source, "DB:CLOSE")) {
+	darkname = DetrendDatabaseLookup ("close", header);   // these APIs need to be seriously fleshed out!!
+    }
+    if (darkname != NULL) {
+	dark     = psImageReadSection (NULL, 0, 0, 0, 0, 0, extname, 0, darkname);
+    }
+
+    return (dark);
+}
+
+bool pmConfigLoadSite (psMetadata **site, int *argc, char **argv) {
+
+    int N;
+    char *sitefile;
+
+    if ((N = psArgsGet (argc, argv, "-site"))) {
+	if (N == argc - 1) {
+	    psError ("syntax error: -site (file)");
+	    return (FALSE);
+	}
+	psArgsRemove (&argc, argv, N);
+	sitefile = psStringCopy (argv[N]);
+	psArgsRemove (&argc, argv, N);
+    }
+
+    if (sitefile == NULL) {
+	sitefile = psStringCopy ("site.cnf");
+    }
+    *site = psMetadataParseConfig (NULL, sitefile, FALSE);  
+    return (TRUE);
+}
+
+bool pmConfigLoadCamera (psMetadata **camera, psMetadata *site, psMetadata *header) {
+  
+    int N;
+    char *cameraname = NULL;
+    char *camerafile = NULL;
+
+    if (site == NULL) return (FALSE);
+
+    // try to determine camera from command-line arguments 
+    if ((N = psArgsGet (argc, argv, "-camera"))) {
+	if (N == argc - 1) {
+	    psError ("syntax error: -camera (file)");
+	    return (FALSE);
+	}
+	psArgsRemove (&argc, argv, N);
+	cameraname = psStringCopy (argv[N]);
+	psArgsRemove (&argc, argv, N);
+    }
+
+    // try to determine camera from header 
+    if ((cameraname == NULL) && (header != NULL)) {
+	cameraname = pmCameraFromHeader (header);
+    }
+
+    // try to determine camera from general configuration information
+    if (cameraname == NULL) {
+	cameraname = psMetadataLookupPtr (site, "CAMERA");
+    }
+
+    // if there is no source for camera info, fail
+    if (cameraname == NULL) {
+	return (FALSE);
+    }
+
+    // find appropriate camera definition file
+    camerafile = psMetadataLookup (site, "CAMERA.%s", cameraname);
+
+    // load the camera details for the specific camera
+    *camera = psMetadataParseConfig (NULL, camerafile, FALSE);
+
+    return (TRUE);
+}
+
+bool pmConfigLoadRecipe (psMetadata **recipe, psMetadata *camera, int *argc, char **argv, char *script) {
+
+    int N;
+    char *recipename = NULL;
+    char *recipefile = NULL;
+
+    if (camera == NULL) return (FALSE);
+
+    // try to determine camera from command-line arguments 
+    if ((N = psArgsGet (argc, argv, "-recipe"))) {
+	if (N == argc - 1) {
+	    psError ("syntax error: -recipe (file)");
+	    return (FALSE);
+	}
+	psArgsRemove (&argc, argv, N);
+	recipefile = psStringCopy (argv[N]);
+	psArgsRemove (&argc, argv, N);
+    }
+
+    // recipefile is defined in the camera metadata, if not specified on command line
+    if (recipefile == NULL) {
+	recipename = psAlloc (sizeof(script) + 8);
+	sprintf (recipename, "RECIPE.%s", script);
+
+	// option 1: recipe is in the camera definition 
+	recipefile = psMetadataLookup (camera, recipename);
+	psFree (recipename);
+
+	// option 2: recipe is in the metadata database (equiv to a detrend file)
+	// this option requires a different API (header data, etc)
+	// recipefile = DetrendDatabaseLookup (recipename, site, camera, header);
+    }
+
+    *recipe = psMetadataParseConfig (NULL, recipefile, FALSE);  
+    psFree (recipefile);
+
+    return (TRUE);
+}
Index: /trunk/archive/scripts/phase2.c
===================================================================
--- /trunk/archive/scripts/phase2.c	(revision 2519)
+++ /trunk/archive/scripts/phase2.c	(revision 2520)
@@ -17,23 +17,12 @@
   outroot = argv[2];
 
-  // define command-line options
-  cameraname = NULL;
-  cameraname = getargs ("-camera");  // pseudo-code, not a valid API
-  recipefile = NULL;
-  recipefile = getargs ("-recipe");  // pseudo-code, not a valid API
-
-  // base file needs to be hardwired, from environment, or based on program name
-  base   = psMetadataParseConfig (NULL, basefile, FALSE);  
-
-  // load site-specific information (such as database servers, etc)
-  sitefile = psMetadataLookup (base, "SITE");
-  site   = psMetadataParseConfig (NULL, sitefile, FALSE);  
-
-  // load only the headers from the given file input a list of headers
-  headers = psFITSReadHeaderSet (input);
+  fits = psFitsAlloc (input);
+
+  // load only the headers from the given file
+  headers = psFitsReadHeaderSet (fits);
  
   // identify camera (may be specified on command line)
   if (cameraname == NULL) {
-    cameraname = pmCameraFromHeader (headers.data[0]);
+    cameraname = pmCameraFromHeader (psListGet (headers->list, 0));
   }
   camerafile = psMetadataLookup (site, "CAMERA.%s", cameraname);
@@ -63,10 +52,21 @@
   // Is EXTNAME invarient?  (probably not; does not always exist)
 
+  /* This will work, but it is not efficient: 
+     we should work with the extensions in sequence */
   for (i = 0; i < FPA[0].chips.n; i++) {
     chip = FPA[0].chips.data[i];
 
+    if (Base == CELL) {
+      f = psFitsMoveExtName(f, extname);
+    }
+    
     for (j = 0; j < chip[0].cells.n; j++) {
       cell = chip[0].cells.data[j];
 
+      if (Base == CELL) {
+	f = psFitsMoveExtName(f, extname);
+      }
+
+      /* these need to work with the psFits pointer correctly 
       bias = load_bias_image (recipe, cell[0].metadata, cell[0].extname);
       dark = load_dark_image (recipe, cell[0].metadata, cell[0].extname);
@@ -74,4 +74,5 @@
       badpix = load_badpix_image (recipe, cell[0].metadata, cell[0].extname);
       fringe = load_fringe_image (recipe, cell[0].metadata, cell[0].extname);
+      */
 
       for (k = 0; k < cell[0].readouts.n; k++) {
@@ -79,5 +80,5 @@
 
 	// load the specific readout pixels.  put this in a function?  
-	readout[0].image = psImageReadSection (NULL, 0, 0, 0, 0, 0, cell[0].extname, k, input);
+	status = pmReadoutLoad (fits, cell, k);
 
 	kernel = make_kernel ();
@@ -299,2 +300,65 @@
 }
 
+
+bool pmConfigLoadBasic (psMetadata **site, psMetadata **base) {
+
+  // base file needs to be hardwired, from environment, or based on program name
+  base   = psMetadataParseConfig (NULL, basefile, FALSE);  
+
+  // load site-specific information (such as database servers, etc)
+  sitefile = psMetadataLookup (base, "SITE");
+  site   = psMetadataParseConfig (NULL, sitefile, FALSE);  
+
+}
+
+bool pmConfigLoadCamera (psMetadata **camera, psMetadata *site, psMetadata *header) {
+  
+  if (site == NULL) return (FALSE);
+
+  // identify camera (may be specified on command line)
+  cameraname = getargs ("-camera");  // pseudo-code, not a valid API
+
+  // if there is no source for camera info, fail
+  if ((cameraname == NULL) && (header == NULL)) {
+    return (FALSE);
+  }
+
+  // try to determine camera from header 
+  if (cameraname == NULL) {
+    cameraname = pmCameraFromHeader (header);
+  }
+
+  // find appropriate camera definition file
+  camerafile = psMetadataLookup (site, "CAMERA.%s", cameraname);
+
+  // load the camera details for the specific camera
+  *camera = psMetadataParseConfig (NULL, camerafile, FALSE);
+
+  return (TRUE);
+}
+
+bool pmConfigLoadRecipe (psMetadata **recipe, psMetadata *camera, char *script) {
+
+  char recipename[64];
+
+  if (site   == NULL) return (FALSE);
+  if (camera == NULL) return (FALSE);
+
+  // define command-line options
+  recipefile = getargs ("-recipe");  // pseudo-code, not a valid API
+
+  // recipefile is defined in the camera metadata, if not specified on command line
+  if (recipefile == NULL) {
+    sprintf (recipename, "RECIPE.%s", script);
+
+    // option 1: recipe is in the camera definition 
+    recipefile = psMetadataLookup (camera, recipename);
+
+    // option 2: recipe is in the metadata database (equiv to a detrend file)
+    // this option requires a different API (header data, etc)
+    recipefile = DetrendDatabaseLookup (recipename, site, camera, header);
+  }
+
+  *recipe = psMetadataParseConfig (NULL, recipefile, FALSE);  
+  return (TRUE);
+}
Index: /trunk/archive/scripts/phase2.pl
===================================================================
--- /trunk/archive/scripts/phase2.pl	(revision 2519)
+++ /trunk/archive/scripts/phase2.pl	(revision 2520)
@@ -2,5 +2,7 @@
 
 sub usage {
-    print STDERR "USAGE: phase2.pl\n";
+    # input is foo.fits
+    # outputs are outroot.im.fits (image) outroot.ob.fits (object list), outroot.log, etc
+    print STDERR "USAGE: phase2 (input) (outroot)\n";
     exit 2;
 }
@@ -8,76 +10,50 @@
 if (@ARGV != 4) { &usage; }
 
-# phase2.pl has extensive intelligence about data sources
+# base file needs to be hardwired, from environment, or based on program name
+$base   = psMetadataParseConfig (NULL, basefile, FALSE);  
 
-# input: filename.fits
-# output: flattened image: filename.f.fits? filename.flt? (chosen externally?)
-# output: extracted stars: filename.p.tbl
-# output: constructed kernel: filename.k.fits
+# probably defined in the base file?
+$site   = psMetadataParseConfig (NULL, sitefile, FALSE);  
 
-# determine camera from image or externally?
+# this should be optionally specified as part of the command-line options
+# alternatively, this should be derived from the input file header data 
+$camera = psMetadataParseConfig (NULL, camerafile, FALSE);
 
-# load configuration information:
+# we need a way to define an optional alternative recipe
+$recipe = psMetadataParseConfig (NULL, recipefile, FALSE);  # defined in the camera file
 
-$config = psMetadataReadFile (NULL, $configfile);
+# load only the headers from the given file input a list of headers
+@headers = psFITSReadHeaderSet ($input);
 
-# check image:
-# is it a FITS image?
-# find camera
-# find mode:
-#  one cell per extension
-#  one chip per extension
-#  one FPA per extension
+# check the validity of the given set of headers based on expectactions for this camera
+# ** where does camera come from?
+# ** how do we do this if the camera is derived from the header info?
+$status = pmCameraValidateHeaders ($headers, $camera);
 
-@chips = getChips ($input);
-foreach $chip () {
-    @cells = getCells ($input, $chip);
-}
+# this gets a bit tenuous: what do we do with the collection?  
 
+# construct a complete, empty (ie, no pixel data) FPA structure for this camera / header set
+# we need to construct the output container.  is it big enough to fit in memory?  assume so?
+$FPA = pmFPAfromHeader ($headers, $camera);
 
+### now we work on individual readouts:
 
-# construct kernel
+# we have the FPA, already validated
+# as part of the FPA, we have access to a specified READOUT and we know the corresponding EXTNAME
 
-# load bias
-# load dark 
-# apply bias and dark
+# load in the complete readout:
+$image    = psImageReadSection (NULL, 0, 0, 0, 0, 0, $extname, $Nreadout, $input);
 
-# all possible detrend operations
-#   - bias
-#   - dark
-#   - trim
-#   - mask bad pixels (astrometry independent)
-#   - mask artifacts (astrometry dependent)
-#   - flatten
-#   - fringe correct
+# load in the complete corresponding bias image (this should be done once per cell)
+# in this form, we are implying that the bias image is guaranteed to have the corresponding EXTNAME
+$bias     = psImageReadSection (NULL, 0, 0, 0, 0, 0, $extname, 0, $biasname);
 
-# convolution kernel is applied to:
-#   - flat
-#   - fringe
-#   - bias?
+# overscan options:
+# - overscan is defined by the recipe (BIAS.OVERSCAN has form [Xs,Xe:Ys:Ye])
+# - overscan is defined by the header (BIAS.OVERSCAN has value BIASSEC)
+# - overscan is not used (BIAS.OVERSCAN has value NONE)
 
-#
-
-
-$out = pmSubtractBias ($in, $fit);
-
-
-##### example: single chip, single cell, single readout:
-
-# input filename: $input
-
-# bias recipe:
-#   bias image: filename
-#   overscan: header BIASSE
-
-# bias subtraction options:
-# - use a bias image?
-# - 
-
-
-
-$image    = psImageReadSection (NULL, 0, 0, 0, 0, 0, $extname, -1, $input);
-$metadata = psMetadataReadHeader (NULL, $extname, -1, $input);
-
-$metaitem = psMetadataLookup ($metadata, "BIASSEC");
+# bias section from header:
+$ = psMetadataLookup ($readout->metadata, "BIASSEC");
 $overscan = psImageSubsection ($image, $metaitem->data.void);
 
@@ -88,6 +64,2 @@
 $image_b = pmSubtractBias ($image, NULL, $overlist, PM_OVERSCAN_COLUMNS, $stats, 0, PM_FIT_NONE, $bias);
 
-
-
-#######################
-
