Index: trunk/psastro/src/psastroChipAstrom.c
===================================================================
--- trunk/psastro/src/psastroChipAstrom.c	(revision 39926)
+++ trunk/psastro/src/psastroChipAstrom.c	(revision 41285)
@@ -28,4 +28,13 @@
     }
 
+    // In this function, we are fitting the chip->FPA model, keeping the toTPA static.  
+    // If the toTPA model has order > 1, then the WCS terms need to use the BiLevel
+    // model.  It is possible for this conversion to fail.  However, if we are going to try
+    // again with the mosaic model (fitting both), then we need to react to failure differently
+    bool mosastro  = psMetadataLookupBool (&status, config->arguments, "PSASTRO.MOSAIC.MODE");
+    if (!status) {
+        mosastro  = psMetadataLookupBool (&status, recipe, "PSASTRO.MOSAIC.MODE");
+    }
+
     // select the input data sources
     pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PSASTRO.INPUT");
@@ -39,4 +48,6 @@
 
     int numGoodChips = 0;               // Number of chips for which astrometry succeeds
+
+    bool useBilevelWCS = (fpa->toTPA->x->nX > 1) || (fpa->toTPA->x->nY > 1);
 
     while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
@@ -145,12 +156,23 @@
                 psU64 astrom_chip_val = 1;
                 astrom_chip_val <<= view->chip;
-                psMetadataAddU64 (updates, PS_LIST_TAIL, "ASTROM_CHIPS", PS_META_REPLACE, 
-                    "chips that passed astrometry", astrom_chip_val);
+                psMetadataAddU64 (updates, PS_LIST_TAIL, "ASTROM_CHIPS", PS_META_REPLACE, "chips that passed astrometry", astrom_chip_val);
 
                 // write the elapsed time here; this will be updated in psastroMosaicAstrometry, if called
                 psMetadataAddF32 (updates, PS_LIST_TAIL, "DT_ASTR", PS_META_REPLACE, "elapsed psastro time", psTimerMark ("psastroAnalysis"));
 		
-		fpa->wcsCDkeys = psMetadataLookupBool(&status, recipe , "PSASTRO.WCS.USECDKEYS");
-                pmAstromWriteWCS (updates, fpa, chip, NONLIN_TOL);
+		// if the toTPA distortion model uses higher-order terms, then we need to use BiLevel astrometry 
+		if (useBilevelWCS) {
+		    // create the header keywords to descripe the results
+		    if (!pmAstromWriteBilevelChip (updates, chip, NONLIN_TOL)) {
+			if (!mosastro) { readout->data_exists = false; } // unless we are going to try again, give up on this chip
+			psError(PS_ERR_UNKNOWN, false, "invalid solution for %d,%d,%d\n", view->chip, view->cell, view->readout);
+			psErrorStackPrint(stderr, "failure to generate WCS keywords for one chip\n");
+			psErrorClear();
+			continue;
+		    }
+		} else {
+		    fpa->wcsCDkeys = psMetadataLookupBool(&status, recipe , "PSASTRO.WCS.USECDKEYS");
+		    pmAstromWriteWCS (updates, fpa, chip, NONLIN_TOL);
+		}
 
                 if (psTraceGetLevel("psastro.dump") > 0) {
@@ -179,4 +201,19 @@
     }
 
+    if (useBilevelWCS) {
+	// save WCS and analysis metadata in update header.
+	// (pull or create local view to entry on readout->analysis)
+	psMetadata *updates = psMetadataLookupMetadata (&status, fpa->analysis, "PSASTRO.HEADER");
+	if (!updates) {
+	    updates = psMetadataAlloc ();
+	    psMetadataAddMetadata (fpa->analysis, PS_LIST_TAIL, "PSASTRO.HEADER",  PS_META_REPLACE, "psastro header stats", updates);
+	    psFree (updates);
+	}
+	if (!pmAstromWriteBilevelMosaic (updates, fpa, NONLIN_TOL)) {
+	    psError(psErrorCodeLast(), false, "Failed to save header terms");
+	    return false;
+	}
+    }
+
     if (fpa->chips->n == 1 && numGoodChips == 0) {
         psError(PSASTRO_ERR_UNKNOWN, false, "Failed to fit single chip.");
