Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcamera.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcamera.c	(revision 42181)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcamera.c	(revision 42182)
@@ -16,7 +16,7 @@
 
 void usage(void) {
-    fprintf (stderr, "USAGE: one of the following\n");
-    fprintf (stderr, "fpcamera -file filename[,filename,...] -mask maskfile[,maskfile,...] -variance varfile[,varfile,...] -astrom-file (smffile) -ref (dvopath) -OutFileBaseName\n\n");
-    fprintf (stderr, "fpcamera -list filelist -masklist masklist -varlist varlist -astrom-file (smffile) -ref (dvopath) -OutFileBaseName\n\n");
+    fprintf (stderr, "USAGE: one of the following:\n\n");
+    fprintf (stderr, "  fpcamera -file filename[,filename,...] -mask maskfile[,maskfile,...] -variance varfile[,varfile,...] -astrom-file (smffile) OutFileBaseName\n");
+    fprintf (stderr, "  fpcamera -list filelist -masklist masklist -varlist varlist -astrom-file (smffile) -ref (dvopath) -OutFileBaseName\n\n");
     fprintf (stderr, "where:\n");
     fprintf (stderr, "  FileNameList is a text file containing filenames, one per line\n");
@@ -25,6 +25,7 @@
     fprintf (stderr, "  OutFileBaseName is the 'root name' for output files\n\n");
     fprintf(stderr, "Optional arguments:\n");
-    fprintf(stderr, "\t-stats STATS.mdc: Output statistics into STATS.mdc\n");
-    fprintf(stderr, "\t-chip CHIPNUM: Only process this chip number.\n\n");
+    fprintf(stderr, "   -stats STATS.mdc: Output statistics into STATS.mdc\n");
+    fprintf(stderr, "   -chip CHIPNUM: Only process this chip number.\n\n");
+    fprintf(stderr, "   -D FPCAMERA.CATDIR (catdir): specify the reference catalog.\n\n");
     exit (PS_EXIT_CONFIG_ERROR);
 }
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraArguments.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraArguments.c	(revision 42181)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraArguments.c	(revision 42182)
@@ -63,9 +63,9 @@
 
     // specify the reference data source (DVO format or ??)
-    N = psArgumentGet (argc, argv, "-ref");
-    if (!N) ESCAPE (FPCAMERA_ERR_ARGUMENTS, "Missing -ref (database)");
-    psArgumentRemove (N, &argc, argv);
-    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "FPCAMERA.CATDIR", PS_META_REPLACE, "", argv[N]);
-    psArgumentRemove (N, &argc, argv);
+    // N = psArgumentGet (argc, argv, "-ref");
+    // if (!N) ESCAPE (FPCAMERA_ERR_ARGUMENTS, "Missing -ref (database)");
+    // psArgumentRemove (N, &argc, argv);
+    // psMetadataAddStr (config->arguments, PS_LIST_TAIL, "FPCAMERA.CATDIR", PS_META_REPLACE, "", argv[N]);
+    // psArgumentRemove (N, &argc, argv);
 
     // specify the astrometry calibration
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataLoad.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataLoad.c	(revision 42181)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataLoad.c	(revision 42182)
@@ -10,9 +10,8 @@
 
     // select the input data sources
-    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "FPCAMERA.INPUT.SMF");
-    if (!input) ESCAPE (PS_ERR_UNKNOWN, "FPCAMERA.INPUT.SMF not listed in config->files");
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "FPCAMERA.INPUT.ASTROM");
+    if (!input) ESCAPE (PS_ERR_UNKNOWN, "FPCAMERA.INPUT.ASTROM not listed in config->files");
 
-    // XXX pass input to fpcameraReadAstrometry?
-    // XXX change name of function?
+    // read the astrometry calibration information (from cmf/smf)
     if (!fpcameraReadAstrometry (input, config)) ESCAPE (PS_ERR_UNKNOWN, "Failure to read astrometry calibration");
 
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataSave.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataSave.c	(revision 42181)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataSave.c	(revision 42182)
@@ -23,4 +23,5 @@
     pmFPAfileActivate (config->files, false, NULL);
     pmFPAfileActivate (config->files, true, "FPCAMERA.OUTPUT");
+    pmFPAfileActivate (config->files, true, "FPCAMERA.INPUT.ASTROM");
 
     view = pmFPAviewAlloc (0);
@@ -74,6 +75,6 @@
 
     // activate all files except FPCAMERA.OUTPUT, and PSPHOT.OUTPUT.CFF
-    pmFPAfileActivate (config->files, true, NULL);
-    pmFPAfileActivate (config->files, false, "FPCAMERA.OUTPUT");
+    // pmFPAfileActivate (config->files, true, NULL);
+    // pmFPAfileActivate (config->files, false, "FPCAMERA.OUTPUT");
 
     psFree (view);
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraLoadRefstars.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraLoadRefstars.c	(revision 42181)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraLoadRefstars.c	(revision 42182)
@@ -13,9 +13,9 @@
     psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, FPCAMERA_RECIPE);
 
-    // DVO APIs expect decimal degrees
-    float RAmin  = DEG_RAD*psMetadataLookupF32(NULL, recipe, "RA_MIN");
-    float RAmax  = DEG_RAD*psMetadataLookupF32(NULL, recipe, "RA_MAX");
-    float DECmin = DEG_RAD*psMetadataLookupF32(NULL, recipe, "DEC_MIN");
-    float DECmax = DEG_RAD*psMetadataLookupF32(NULL, recipe, "DEC_MAX");
+    // FPA FOV is determined by fpcameraReadAstrometry & save on fpa->analysis
+    float RAmin  = psMetadataLookupF32(NULL, input->fpa->analysis, "RA_MIN");
+    float RAmax  = psMetadataLookupF32(NULL, input->fpa->analysis, "RA_MAX");
+    float DECmin = psMetadataLookupF32(NULL, input->fpa->analysis, "DEC_MIN");
+    float DECmax = psMetadataLookupF32(NULL, input->fpa->analysis, "DEC_MAX");
 
     // extra field fraction to add
@@ -37,4 +37,7 @@
     DECmax += dDEC * fieldPadding;
 
+    // XXX: fpcameraArguments.c looks for this explicitly on the command line, but stores it in arguments.
+    // this could be a recipe value which can also be defined as a command-line option 
+
     // grab the FPCAMERA.CATDIR name from the FPCAMERA recipe
     char *catdir_recipe = psMetadataLookupStr(&status, recipe, "FPCAMERA.CATDIR");
@@ -43,5 +46,7 @@
     // substitute abstract name with concrete name, if present in FPCAMERA.CATDIRS
     psMetadata *catdirs = psMetadataLookupMetadata(&status, config->site, "FPCAMERA.CATDIRS"); // List of cameras
-    if (!catdirs) ESCAPE(FPCAMERA_ERR_CONFIG, "Unable to find FPCAMERA.CATDIRS in the system configuration.\n");
+
+    // XXX it is allowed that FPCAMERA.CATDIRS is not defined in site.config: the value must be a real path
+    // if (!catdirs) ESCAPE(FPCAMERA_ERR_CONFIG, "Unable to find FPCAMERA.CATDIRS in the system configuration.\n");
 
     // the name in the recipe may be one of:
@@ -49,7 +54,10 @@
     // (B) a reference to the name in the FPCAMERA.CATDIRS folder in site.config
     // (C) a reference to a folder in the FPCAMERA.CATDIRS folder in site.config, containing multiple copy locations
-    char *catdir_virtual = psMetadataLookupStr(&status, catdirs, catdir_recipe);
-
-    psMetadata *catdir_folder = psMetadataLookupMetadata(&status, catdirs, catdir_recipe);
+
+    // can we find a plain string matching catdir_recipe in the catdirs folder?
+    char *catdir_virtual = catdirs ? psMetadataLookupStr(&status, catdirs, catdir_recipe) : NULL;
+
+    // OR, can we find a subfolder in the catdirs folder?
+    psMetadata *catdir_folder = catdirs ? psMetadataLookupMetadata(&status, catdirs, catdir_recipe) : NULL;
     if (catdir_folder) {
         // randomly choose one of the entries
@@ -69,17 +77,18 @@
     }
 
+    // if catdir_virtual is NULL, catdir_recipe must be a file name
     char *catdir = (catdir_virtual == NULL) ? catdir_recipe : catdir_virtual;
 
     // convert the uri to a real filename (ie, path://foobar)
     psString CATDIR = pmConfigConvertFilename(catdir, config, false, false); // Resolved filename
-    ESCAPE(FPCAMERA_ERR_CONFIG, "unable to determine filename for CATDIR %s", catdir);
+    if (!CATDIR) ESCAPE(FPCAMERA_ERR_CONFIG, "unable to determine filename for CATDIR %s", catdir);
 
     psLogMsg ("fpcamera", 3, "looking up reference objects in %s\n", CATDIR);
 
     char *getstarCommand = psStringCopy(psMetadataLookupStr(NULL, recipe, "DVO.GETSTAR"));
-    ESCAPE(FPCAMERA_ERR_CONFIG, "unable to find DVO.GETSTAR in recipe");
+    if (!getstarCommand) ESCAPE(FPCAMERA_ERR_CONFIG, "unable to find DVO.GETSTAR in recipe");
 
     char *outformat = psMetadataLookupStr(NULL, recipe, "DVO.GETSTAR.OUTFORMAT");
-    ESCAPE(FPCAMERA_ERR_CONFIG, "unable to find DVO.GETSTAR.OUTFORMAT in recipe");
+    if (!outformat) ESCAPE(FPCAMERA_ERR_CONFIG, "unable to find DVO.GETSTAR.OUTFORMAT in recipe");
 
     // issue the following command:
@@ -166,4 +175,6 @@
     
     psMetadataAdd (input->fpa->analysis, PS_LIST_TAIL, "FPCAMERA.REFSTARS", PS_DATA_ARRAY, "reference sources", refstars);
+    psFree (refstars);
+
     return true;
 }
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraParseCamera.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraParseCamera.c	(revision 42181)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraParseCamera.c	(revision 42182)
@@ -23,5 +23,5 @@
     // the input smf file is a required argument
     pmFPAfileDefineFromArgs (&status, config, "FPCAMERA.INPUT.ASTROM", "INPUT.ASTROM");
-    if (!status) ESCAPE(FPCAMERA_ERR_CONFIG, "Failed to build FPA from FPCAMERA.INPUT.SMF");
+    if (!status) ESCAPE(FPCAMERA_ERR_CONFIG, "Failed to build FPA from FPCAMERA.INPUT.ASTROM");
 
     // select the current recipe
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraReadAstrometry.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraReadAstrometry.c	(revision 42181)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraReadAstrometry.c	(revision 42182)
@@ -1,3 +1,5 @@
 # include "fpcamera.h"
+
+# define ESCAPE(ERROR, MSG) { psError(ERROR, false, MSG); psFree(view); return false; }
 
 /* \brief this function loads the astrometry calibration from the input smf file */
@@ -5,20 +7,35 @@
 
     bool status;
+    pmChip *chip = NULL;
+    pmFPAview *view = NULL;
 
     // select the current recipe (just needed for pixel scale)
+    // XXX this is defined in psastro.config : use that value instead of one defined in fpcamera.config?
     psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, FPCAMERA_RECIPE);
-    if (!recipe) {
-	psError(FPCAMERA_ERR_CONFIG, true, "Can't find FPCAMERA recipe!\n");
-	return false;
-    }
+    if (!recipe) ESCAPE(FPCAMERA_ERR_CONFIG, "Can't find FPCAMERA recipe!");
 
     // physical pixel scale in microns per pixel (used in case of non-bilevel astrometry)
     double pixelScale = psMetadataLookupF32 (&status, recipe, "FPCAMERA.PIXEL.SCALE");
-    if (!status) {
-	psError(PS_ERR_IO, true, "Failed to lookup pixel scale"); 
-	return false; 
-    } 
+    if (!status) ESCAPE(PS_ERR_IO, "Failed to lookup pixel scale"); 
 
-    pmFPAview *view = pmFPAviewAlloc (0);
+    // de-activate all files except FPCAMERA.INPUT.ASTROM (where we get the astrometric calibration)
+    pmFPAfileActivate (config->files, false, NULL);
+    pmFPAfileActivate (config->files, true, "FPCAMERA.INPUT.ASTROM");
+
+    view = pmFPAviewAlloc (0);
+
+    // load headers for astrometry calibration
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE(FPCAMERA_ERR_DATA, "failed to load header at FPA level");
+
+    // add in chip headers
+    while ((chip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+        psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process) { continue; }
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE(FPCAMERA_ERR_DATA, "failed to load header at Chip level");
+    }
+    psLogMsg ("psastro", 3, "load headers : %f sec\n", psTimerMark ("psastro"));
+
+    // reset to loop over all chips:
+    pmFPAviewReset (view);
 
     // check PHU header to see if we are using mosaic-level or per-chip astrometry
@@ -33,5 +50,10 @@
     } 
 
-    pmChip *chip;
+    // we need the min/max RA & DEC for each of the chips and for the entire FOV
+    double rMinFPA = +FLT_MAX;
+    double rMaxFPA = -FLT_MAX;
+    double dMinFPA = +FLT_MAX;
+    double dMaxFPA = -FLT_MAX;
+
     while ((chip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
 	psTrace ("fpcamera", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
@@ -55,5 +77,53 @@
 	    } 
 	}
+
+	// we need the min/max RA & DEC for each of the chips and for the entire FOV
+	double rMinChip = +FLT_MAX;
+	double rMaxChip = -FLT_MAX;
+	double dMinChip = +FLT_MAX;
+	double dMaxChip = -FLT_MAX;
+	
+	// this region defines the data area of the chip
+	psRegion *region = pmChipPixels (chip);
+	psPlane ptCH[4];
+	
+	// save the 4 corners
+	ptCH[0].x = region->x0; ptCH[0].y = region->y0;
+	ptCH[1].x = region->x1; ptCH[1].y = region->y0;
+	ptCH[2].x = region->x1; ptCH[2].y = region->y1;
+	ptCH[3].x = region->x0;	ptCH[3].y = region->y1;
+	psFree (region);
+	
+        // report and save the current best guess for the chip 0,0 pixel coordinates
+        for (int i = 0; i < 4; i++) {
+            psPlane ptFP, ptTP;
+            psSphere ptSky;
+
+	    // 4 corners
+            psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH[i]);
+            psPlaneTransformApply (&ptTP, input->fpa->toTPA, &ptFP);
+            psDeproject (&ptSky, &ptTP, input->fpa->toSky);
+	    rMinChip = PS_MIN(rMinChip, DEG_RAD*ptSky.r);
+	    rMaxChip = PS_MAX(rMaxChip, DEG_RAD*ptSky.r);
+	    dMinChip = PS_MIN(dMinChip, DEG_RAD*ptSky.d);
+	    dMaxChip = PS_MAX(dMaxChip, DEG_RAD*ptSky.d);
+        }
+	psMetadataAddF32 (chip->analysis, PS_LIST_TAIL, "RA_MIN",  PS_META_REPLACE, "", rMinChip);
+	psMetadataAddF32 (chip->analysis, PS_LIST_TAIL, "RA_MAX",  PS_META_REPLACE, "", rMaxChip);
+	psMetadataAddF32 (chip->analysis, PS_LIST_TAIL, "DEC_MIN", PS_META_REPLACE, "", dMinChip);
+	psMetadataAddF32 (chip->analysis, PS_LIST_TAIL, "DEC_MAX", PS_META_REPLACE, "", dMaxChip);
+	psLogMsg ("fpcamera", 3, "chip %3d = (%f,%f) - (%f,%f)\n", view->chip, rMinChip, dMinChip, rMaxChip, dMaxChip);
+
+	rMinFPA = PS_MIN(rMinFPA, rMinChip);
+	rMaxFPA = PS_MAX(rMaxFPA, rMaxChip);
+	dMinFPA = PS_MIN(dMinFPA, dMinChip);
+	dMaxFPA = PS_MAX(dMaxFPA, dMaxChip);
     }
+    psMetadataAddF32 (input->fpa->analysis, PS_LIST_TAIL, "RA_MIN",  PS_META_REPLACE, "", rMinFPA);
+    psMetadataAddF32 (input->fpa->analysis, PS_LIST_TAIL, "RA_MAX",  PS_META_REPLACE, "", rMaxFPA);
+    psMetadataAddF32 (input->fpa->analysis, PS_LIST_TAIL, "DEC_MIN", PS_META_REPLACE, "", dMinFPA);
+    psMetadataAddF32 (input->fpa->analysis, PS_LIST_TAIL, "DEC_MAX", PS_META_REPLACE, "", dMaxFPA);
+
+    psLogMsg ("fpcamera", 3, "FPA FOV = (%f,%f) - (%f,%f)\n", rMinFPA, dMinFPA, rMaxFPA, dMaxFPA);
     psLogMsg ("fpcamera", 3, "convert wcs terms to internal format : %f sec\n", psTimerMark ("fpcamera"));
 
