Index: trunk/psastro/src/Makefile.am
===================================================================
--- trunk/psastro/src/Makefile.am	(revision 25906)
+++ trunk/psastro/src/Makefile.am	(revision 26173)
@@ -90,4 +90,5 @@
 	psastroMaskUtils.c          \
 	psastroChooseGlintStars.c   \
+	psastroLoadCrosstalk.c \
 	psastroLoadGlints.c   \
 	psastroLoadGhosts.c         \
Index: trunk/psastro/src/psastro.h
===================================================================
--- trunk/psastro/src/psastro.h	(revision 25906)
+++ trunk/psastro/src/psastro.h	(revision 26173)
@@ -75,4 +75,6 @@
 bool              psastroLoadGlints (pmConfig *config);
 bool              psastroChooseGlintStars (pmConfig *config, psArray *refs, const char *source);
+
+bool              psastroLoadCrosstalk (pmConfig *config);
 
 psArray          *psastroLoadRefstars (pmConfig *config, const char *source);
Index: trunk/psastro/src/psastroLoadCrosstalk.c
===================================================================
--- trunk/psastro/src/psastroLoadCrosstalk.c	(revision 26173)
+++ trunk/psastro/src/psastroLoadCrosstalk.c	(revision 26173)
@@ -0,0 +1,307 @@
+/** @file psastroMaskUpdates.c
+ *
+ *  @brief 
+ *
+ *  @ingroup libpsastro
+ *
+ *  @author IfA
+ *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-07 02:03:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "psastroInternal.h"
+
+# define ESCAPE { \
+  psError(PS_ERR_UNKNOWN, false, "I/O failure in psastroMaskUpdate"); \
+  psFree (view); \
+  return false; \
+}
+
+pmChip * getChipByName (pmFPA *fpa, psString chipname) {
+  int i;
+
+  for (i = 0; i < fpa->chips->n; i++) {
+    pmChip *chip = fpa->chips->data[i];
+    if (strcmp(chipname,psMetadataLookupStr(NULL,chip->concepts,"CHIP.NAME")) == 0) {
+      return(chip);
+    }
+  }
+  return(NULL);
+}
+pmCell * getCellByName (pmChip *chip, psString cellname) {
+  int i;
+
+  for (i = 0; i < chip->cells->n; i++) {
+    pmCell *cell = chip->cells->data[i];
+    if (strcmp(cellname,(char *) psMetadataLookupStr(NULL,cell->concepts,"CELL.NAME")) == 0) {
+      return(cell);
+    }
+  }
+  return(NULL);
+}
+
+			  
+bool psastroLoadCrosstalk (pmConfig *config) {
+
+  bool status;
+  pmChip *chip = NULL;
+  pmCell *cell = NULL;
+  pmReadout *readout = NULL;
+
+  float zeropt, exptime, MAX_MAG;
+  
+  psLogMsg ("psastro", PS_LOG_INFO, "determine crosstalk positions");
+
+  // select the current recipe
+  psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSASTRO_RECIPE);
+  if (!recipe) {
+    psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe");
+    return false;
+  }
+
+  // Determine if we really want to do this masking:
+  bool CROSSTALK_MASK  = psMetadataLookupBool (&status, recipe, "CROSSTALK_MASK");
+  if (!CROSSTALK_MASK) {
+    return(true);
+  }
+  
+  // select the input astrometry data (also carries the refstars)
+  pmFPAfile *astrom = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+  if (!astrom) {
+    psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
+    goto escape;
+  }
+  pmFPA *fpa = astrom->fpa;
+
+  // select the reference mask fpa :: we use this to determine cell boundaries
+  pmFPAfile *refMask = psMetadataLookupPtr (NULL, config->files, "PSASTRO.REFMASK");
+  if (!refMask) {
+    psError(PSASTRO_ERR_CONFIG, true, "Can't find mask reference");
+    return false;
+  }
+  // Activate teh reference mask to generate an FPA structure we can use to map stars down to cells
+  
+  pmFPAfileActivate (config->files, true, "PSASTRO.REFMASK");
+  
+  //  pmFPA *refFPA = refMask->fpa;
+
+  pmFPAview *view = pmFPAviewAlloc (0);
+  pmFPAview *viewMask = pmFPAviewAlloc (0);  
+
+  if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+  // really error-out here?  or just skip?
+  if (!psastroZeroPointFromRecipe (&zeropt, &exptime, &MAX_MAG, fpa, recipe)) {
+    psLogMsg ("psastro", PS_LOG_INFO, "failed to load zeropt data from recipe");
+    goto escape;
+  }
+
+  MAX_MAG = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_CROSSTALK_MAG_MAX");
+  // recipe values are given in instrumental magnitudes
+  // use the zero point and exposure time to convert to apparent mags: M_ap = M_inst + C_0 + 2.5*log(exptime)
+  float MagOffset = zeropt + 2.5*log10(exptime);
+  MAX_MAG += MagOffset;
+  
+  psTrace("psastro.crosstalk",2,"%f %f %f %f\n",zeropt,exptime,MAX_MAG,MagOffset);
+  
+  // Load each chip.  
+  while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
+    psTrace ("psastro.crosstalk", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+    if (!chip->process || !chip->file_exists) { continue; }
+    if (!chip->fromFPA) { continue; }
+
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+    // Get the current chip's name, and parse out the grid location.
+    pmChip *refChip  = pmFPAviewThisChip (view, refMask->fpa);
+    const char *chipName = psMetadataLookupStr(NULL,refChip->concepts, "CHIP.NAME");
+    int X = chipName[2] - '0';
+    int Y = chipName[3] - '0';
+
+    // Read each cell (at this stage, we should only have one of them)
+      while ((cell = pmFPAviewNextCell (view, fpa, 1)) != NULL) {
+	psTrace ("psastro.crosstalk", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+	
+	if (!cell->process || !cell->file_exists) { continue; }
+	
+	// Read each readout (similarly, we should only have one of these, too)
+	while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+	  if (! readout->data_exists) { continue; }
+
+	  // If this chip doesn't crosstalk, skip to the next one.
+	  if (! ((X == 2)||(X == 3)||(X == 4)||(X == 5))) {
+	    psTrace ("psastro.crosstalk",2,"Chip (%d%d) not a known crosstalk source.",X,Y);
+	    continue;
+	  }
+
+	  // Get the array of reference stars we're concerned with.
+	  psArray *refstars = psMetadataLookupPtr (&status, readout->analysis, "PSASTRO.REFSTARS");
+	  if (refstars == NULL) { return(true); }
+
+	  // Check each reference star
+	  for (int i = 0; i < refstars->n; i++) {
+	    pmAstromObj *ref = refstars->data[i];
+	    
+	    if (ref->Mag > MAX_MAG) {
+	      continue;
+	    }
+
+	    // Identify which cell holds the cell
+	    pmChip *CTsourceChip = refChip;
+	    pmCell *CTsourceCell = pmCellInChip(refChip,ref->chip->x,ref->chip->y);
+	    
+	    if (!CTsourceCell) {
+	      continue;
+	    }
+
+	    psTrace ("psastro.crosstalk",2,"REF: %d %f (%f %f) %f %f :: %f %f\n",
+		     i,ref->Mag, ref->sky->r,ref->sky->d, ref->FP->x,ref->FP->y, ref->chip->x,ref->chip->y);
+
+	    
+	    const char *cellName = psMetadataLookupStr(NULL,CTsourceCell->concepts, "CELL.NAME");
+	    int U = cellName[2] - '0';
+	    int V = cellName[3] - '0';
+
+	    int Xt = X,Yt = Y,Ut = U,Vt = V;
+	    float x_cell,y_cell;
+	    float x_t_cell,y_t_cell;
+	    
+	    float x_t_chip,y_t_chip;
+
+	    pmCellCoordsForChip(&x_cell,&y_cell,CTsourceCell,ref->chip->x,ref->chip->y);
+	    
+	    x_t_cell = x_cell;
+	    y_t_cell = y_cell;
+
+	    // Determine if this combination of chip and cell produces a crosstalk artifact
+	    if ((X == 2)&&(! ((U == 3)||(U == 5)||(U == 6)))) {
+	      psTrace ("psastro.crosstalk",2,"Cell (%d%d) on chip (%d%d) not a known crosstalk source.",U,V,X,Y);
+	      continue;
+	    }
+	    if ((X == 3)&&(! ((U == 3)))) {
+	      psTrace ("psastro.crosstalk",2,"Cell (%d%d) on chip (%d%d) not a known crosstalk source.",U,V,X,Y);
+	      continue;
+	    }
+	    if ((X == 4)&&(! ((U == 3)))) {
+	      psTrace ("psastro.crosstalk",2,"Cell (%d%d) on chip (%d%d) not a known crosstalk source.",U,V,X,Y);
+	      continue;
+	    }
+	    if ((X == 5)&&(! ((U == 3)||(U == 5)||(U == 6)))) {
+	      psTrace ("psastro.crosstalk",2,"Cell (%d%d) on chip (%d%d) not a known crosstalk source.",U,V,X,Y);
+	      continue;
+	    }
+	    
+	    // Calculate destination positions
+	    if (X == 2) {
+	      if (U == 3) {
+		Xt = 3;
+	      }
+	      if (U == 5) {
+		Ut = 6;
+	      }
+	      if (U == 6) {
+		Ut = 5;
+	      }
+	    }
+	    else if (X == 3) {
+	      Xt = 2;
+	    }
+	    else if (X == 4) {
+	      Xt = 5;
+	    }
+	    if (X == 5) {
+	      if (U == 3) {
+		Xt = 4;
+	      }
+	      if (U == 5) {
+		Ut = 6;
+	      }
+	      if (U == 6) {
+		Ut = 5;
+	      }
+	    }
+
+	    // Convert target cell coordinates to target chip coordinates 
+	    psString targetChipName = NULL;
+	    psStringAppend(&targetChipName,"XY%d%d",Xt,Yt);
+	    psString targetCellName = NULL;
+	    psStringAppend(&targetCellName,"xy%d%d",Ut,Vt);
+	    
+	    psTrace ("psastro.crosstalk",2,"CTsource @ OTA%d%dxy%d%d@%f,%f CTtarget OTA%d%dxy%d%d@%f,%f [%s %s]",
+		     X,Y,U,V,x_cell,y_cell,Xt,Yt,Ut,Vt,x_t_cell,y_t_cell,
+		     targetChipName,targetCellName);
+	    
+	    pmCell *CTtargetCell = getCellByName (CTsourceChip,targetCellName);
+	    if (!CTtargetCell) continue;
+	    
+	    pmChipCoordsForCell(&x_t_chip,&y_t_chip,CTtargetCell,x_t_cell,y_t_cell);
+	    
+	    psTrace ("psastro.crosstalk",2,"CTsource @ OTA%d%dxy%d%d@%f,%f CTtarget OTA%d%dxy%d%d@%f,%f ChipLoc: (%f,%f)",
+		     X,Y,U,V,x_cell,y_cell,Xt,Yt,Ut,Vt,x_t_cell,y_t_cell,x_t_chip,y_t_chip);
+
+	    // Hunt down the readout for the target, and save the chip position and magnitude of source star.
+	    pmChip *targetChip = getChipByName(fpa,targetChipName);
+	    if (!targetChip) continue;
+	    if (!targetChip->cells) continue;
+	    if (!targetChip->cells->n) continue;
+	    
+	    pmCell *targetCell = targetChip->cells->data[0];
+	    if (!targetCell) continue;
+	    if (!targetCell->readouts) continue;
+	    if (!targetCell->readouts->n) continue;
+	    pmReadout *targetReadout = targetCell->readouts->data[0];
+	    if (!targetReadout) continue;
+
+	    pmAstromObj *crosstalk = pmAstromObjAlloc();
+	    
+	    crosstalk->Mag = ref->Mag - MagOffset;
+	    crosstalk->chip->x = x_t_chip;
+	    crosstalk->chip->y = y_t_chip;
+	    
+	    psArray *crosstalks = psMetadataLookupPtr (&status, targetReadout->analysis, "PSASTRO.CROSSTALKS");
+	    if (crosstalks == NULL) {
+	      crosstalks = psArrayAllocEmpty(100);
+	      if (!psMetadataAdd(targetReadout->analysis,PS_LIST_TAIL,"PSASTRO.CROSSTALKS", PS_DATA_ARRAY, "crosstalk locations", crosstalks)) {
+		psError(PSASTRO_ERR_CONFIG, false, "failure to add crosstalks to readout");
+		goto escape;
+	      }
+	      psFree(crosstalks);
+	    }
+	    psArrayAdd(crosstalks,100,crosstalk);
+
+	    psFree(targetChipName);
+	    psFree(targetCellName);
+	    psFree(crosstalk);
+	  } // refstars
+	} // readout
+      } // cell
+      //    }
+    
+      // File cleanup code.
+    *viewMask = *view; 
+    while ((cell = pmFPAviewNextCell (viewMask, refMask->fpa, 1)) != NULL) {
+      psTrace ("psastro", 4, "Mask Cell %d: %x %x\n", viewMask->cell, cell->file_exists, cell->process);
+      if (!cell->process || !cell->file_exists) { continue; }
+      
+      while ((readout = pmFPAviewNextReadout (viewMask, refMask->fpa, 1)) != NULL) {
+	if (! readout->data_exists) { continue; }
+	if (!pmFPAfileIOChecks (config, viewMask, PM_FPA_AFTER)) ESCAPE;
+      }
+      if (!pmFPAfileIOChecks (config, viewMask, PM_FPA_AFTER)) ESCAPE;
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+  }
+  if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+
+  psTrace ("psastro.crosstalk", 2, "Leaving crosstalk code.\n");
+
+  psFree(view);
+  psFree(viewMask);
+  return(true);
+
+ escape:
+  psFree(view);
+  return(false);
+}
+
Index: trunk/psastro/src/psastroMaskUpdates.c
===================================================================
--- trunk/psastro/src/psastroMaskUpdates.c	(revision 25906)
+++ trunk/psastro/src/psastroMaskUpdates.c	(revision 26173)
@@ -34,5 +34,6 @@
     psImageMaskType spikeMaskValue = pmConfigMaskGet("SPIKE", config); // Mask value for ghost pixels
     psImageMaskType starMaskValue  = pmConfigMaskGet("STARCORE", config); // Mask value for ghost pixels
-
+    psImageMaskType crosstalkMaskValue = pmConfigMaskGet("GHOST", config); // Mask value for crosstalk ghosts
+    
     // psImageMaskType maskBlank  = pmConfigMaskGet("BLANK", config); // Mask value for blank pixels
 
@@ -48,4 +49,11 @@
     if (!REFSTAR_MASK) return true;
 
+    // convert star positions to crosstalk artifact positions and add to the readout->analysis data
+    bool CROSSTALK_MASK                    = psMetadataLookupBool (&status, recipe, "CROSSTALK_MASK");
+    if (CROSSTALK_MASK && !psastroLoadCrosstalk (config)) {
+      psError(PSASTRO_ERR_CONFIG, false, "Error loading crosstalk data");
+      return(false);
+    }
+    
     // convert star positions to ghost positions and add to the readout->analysis data
     if (!psastroLoadGhosts (config)) {
@@ -76,4 +84,7 @@
     double REFSTAR_MASK_BLEED_MAG_SLOPE    = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_BLEED_MAG_SLOPE");
 
+    double REFSTAR_MASK_CROSSTALK_MAG_MAX  = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_CROSSTALK_MAG_MAX");
+    double REFSTAR_MASK_CROSSTALK_MAG_SLOPE= psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_CROSSTALK_MAG_SLOPE");
+    
     // select the input data sources
     pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
@@ -201,4 +212,5 @@
 
                     if (ref->Mag > REFSTAR_MASK_MAX_MAG) continue;
+
 
 		    // the reference magnitudes have been converted from the instrumental
@@ -282,4 +294,20 @@
 		}
 
+		// Select the crosstalk artifact regions for this readout, and mask a circular region
+		// corresponding to the source star
+
+		psArray *crosstalks = psMetadataLookupPtr (&status, readout->analysis, "PSASTRO.CROSSTALKS");
+		if (crosstalks) {
+		  for (int i = 0; i < crosstalks->n; i++) {
+		    pmAstromObj *ref = crosstalks->data[i];
+		    float radius = REFSTAR_MASK_CROSSTALK_MAG_SLOPE * (REFSTAR_MASK_CROSSTALK_MAG_MAX - ref->Mag);
+		    psTrace("psastro.crosstalk",2,"Masking star on Chip %s @ (%f,%f) Magnitude: %f Radius %f\n",
+			    psMetadataLookupStr(&status,readout->parent->parent->concepts,"CHIP.NAME"),
+			    ref->chip->x,ref->chip->y,ref->Mag,radius);
+		    // XXX for now, assume cell binning is 1x1 relative to chip
+		    psastroMaskCircle (readoutMask->mask, crosstalkMaskValue, ref->chip->x, ref->chip->y, radius, radius);
+		  }
+		}
+		
 		// this probably should move into a function of its own:
 		{
