Index: /trunk/ippScripts/scripts/night_report_json.pl
===================================================================
--- /trunk/ippScripts/scripts/night_report_json.pl	(revision 41338)
+++ /trunk/ippScripts/scripts/night_report_json.pl	(revision 41338)
@@ -0,0 +1,595 @@
+#!/usr/bin/env perl
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use DBI;
+use JSON;
+use POSIX;
+use constant DB_SOCKET => '/var/run/mysqld/mysqld.sock'; # Socket for mysql
+use DateTime;
+
+# this saves the original arguments for later reporting
+my @ARGS = @ARGV;
+
+GetOptions(
+    'date=s'            => \$date,
+    'dbname|d=s'        => \$dbname,   # Database name
+    'final'             => \$final,    # Final version for the day
+    'verbose'           => \$verbose,  # Print output to screen
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option(s): @ARGV", -exitval => 2 ) if @ARGV;
+
+# example if I require some options
+# pod2usage( -msg => "Required options: --exp_id --chip_id", -exitval => 3) unless defined $exp_id and defined $chip_id;
+
+$date   = `date --utc --rfc-3339=date` unless defined $date;
+chomp $date;
+$date=~ s/\//-/g;
+$date=~ s/\./-/g;
+
+# database connection information:
+$dbhost = 'ippdb08';
+$dbname = 'gpc1' unless defined $dbname;
+$dbuser = 'ippuser';
+$dbpass = 'ippuser';
+
+# final report boolean
+$final = 0 unless defined $final;
+ 
+
+# dataGroupDate : formatted to match data_group values (YYYYMMDD)
+$dataGroupDate = $date;
+$dataGroupDate=~ s/-//g;
+$dataGroupDate=~ s/\.//g;
+
+# set up the database connection:
+my $dsn = "DBI:mysql:host=$dbhost;database=$dbname";
+my $dbh = DBI->connect( $dsn, $dbuser, $dbpass ) or die "Unable to connect to database: $DBI::errstr";
+
+# find a useful exp_id (ALWAYS do this)
+$refExpID = 0;
+if (1) { 
+    # print "Exposures 10 days ago:\n";
+
+    my $query = "SELECT MAX(exp_id)";
+    $query .= " FROM rawExp";
+    $query .= " WHERE dateobs >= date_sub('$date', interval 20 day)";
+    $query .= " AND dateobs <= date_sub('$date', interval 10 day)";
+
+    my $result = &mysql_select ($query);
+
+    @row = $result->fetchrow_array();
+    $refExpID = $row[0];
+    if ($refExpID eq "") { 
+      $refExpID = 0; 
+      print "*** WARNING: no data in period 10-20 days before requested date (query will be slower) **\n";
+    }
+    if ($verbose2) { print "Reference exp_id: $refExpID\n"; }
+}
+
+##################
+# testing how to get more information parsed by chunk
+if (1) {
+    if($verbose) {print "\n\n\n"};
+    if($verbose) {print "--- MOPS report for $dbname : $date ---\n\n"};
+
+    my $badcamflag = 0;
+    my $badwarpflag = 0;
+    my $baddiffflag = 0;
+
+    #compute the truncated julian date
+    my ($year,$month,$day) = split /-/, $date;
+
+    #grab the current data/time in the ISO8601 format
+    my $datetimenow = DateTime->now()->iso8601().'Z';
+
+    # In leap years, -1 for Jan, Feb, else 0
+    $L = POSIX::ceil(($month - 14) / 12);
+
+    #convert to JD
+    $p1 = $day - 32075 + POSIX::floor (1461 * ($year + 4800 + $L) / 4);
+    $p2 = POSIX::floor (367 * ($month - 2 - $L * 12) / 12);
+    $p3 = 3 * POSIX::floor (POSIX::floor (($year + 4900 + $L) / 100) / 4);
+    $julian = $p1 + $p2 - $p3;        
+    $julian = $julian + ($hr / 24.0) - 0.5;
+    $tjd = $julian - 2440000.5;
+    $tjd = $tjd - 10000;
+
+    #add a JSON version number ("a.b.c". Bug fixes: c->c+1; Non-breaking changes in JSON format:  b->b+1, c=0;  Breaking  changes in JSON format: a->a+1, b=0, c=0)
+    $version = "0.0.1";
+
+    my $struct ={
+        date => $datetimenow,
+        tjd => $tjd,
+        telescope => $dbname,
+        version => $version,
+        final => $final
+    };
+
+    #do a query of raw/chip/cam/warp counts for this night, split by chunk. 
+    #We want to stick to only MOPS products, so we do labels %SS%, %EU% and %Bright.% (to avoid BrightTwi and Bright3PI which are not in quads)
+    #we need to do a subquery to find the total number of skycells in warpSkyfile, as well as the number of those with a bad quality
+    my $query = "SELECT suba.chunk,suba.Nvis1,suba.Nvis2,suba.Nvis3,suba.Nvis4,suba.Ncamgood,suba.Ncambad,subb.Nwarpgood,subb.Nwarpbad,suba.uniNvis1,suba.uniNvis2,suba.uniNvis3,suba.uniNvis4 FROM ";
+    $query .= "    (SELECT substr(comment, 1, position(' ' in comment)) AS chunk,count(if(rawExp.comment LIKE '%visit 1%',1,NULL)) AS Nvis1,";
+    $query .= "    count(if(rawExp.comment LIKE '%visit 2%',1,NULL)) AS Nvis2,count(if(rawExp.comment LIKE '%visit 3%',1,NULL)) AS Nvis3,";
+    $query .= "    count(if(rawExp.comment LIKE '%visit 4%',1,NULL)) AS Nvis4, count(if(camProcessedExp.sigma_ra <= 5 AND camProcessedExp.sigma_dec <= 5 AND camProcessedExp.quality = 0,1,NULL)) AS Ncamgood,";
+    $query .= "    count(if(camProcessedExp.sigma_ra > 5 OR camProcessedExp.sigma_dec > 5 OR camProcessedExp.quality > 0,1,NULL)) AS Ncambad, COUNT(DISTINCT CASE WHEN rawExp.comment LIKE '%visit 1%' THEN rawExp.comment END) AS uniNvis1,";
+    $query .= "    COUNT(DISTINCT CASE WHEN rawExp.comment LIKE '%visit 2%' THEN rawExp.comment END) AS uniNvis2,COUNT(DISTINCT CASE WHEN rawExp.comment LIKE '%visit 3%' THEN rawExp.comment END) AS uniNvis3,";
+    $query .= "    COUNT(DISTINCT CASE WHEN rawExp.comment LIKE '%visit 4%' THEN rawExp.comment END) AS uniNvis4";
+    $query .= "    FROM rawExp LEFT JOIN chipRun USING (exp_id) LEFT JOIN camRun USING (chip_id) JOIN camProcessedExp USING (cam_id) LEFT JOIN fakeRun USING (cam_id) LEFT JOIN warpRun USING (fake_id)";
+    $query .= "    WHERE dateobs like '$date%' AND exp_id > $refExpID";
+    $query .= "    AND (camRun.label LIKE '%SS%' OR camRun.label LIKE '%EU%' OR camRun.label LIKE '%Bright.%') AND obs_mode NOT LIKE 'ENGINEERING' AND obs_mode NOT LIKE 'MANUAL'";
+    $query .= "    GROUP BY chunk ORDER BY dateobs) as suba ";
+    $query .= "LEFT JOIN";
+    $query .= "    (SELECT subc.chunk, count(if(subc.Nwarpskycellbad < subc.Nwarpskycell,1,NULL)) AS Nwarpgood, count(if(subc.Nwarpskycellbad = subc.Nwarpskycell,1,NULL)) AS Nwarpbad";
+    $query .= "    FROM ";
+    $query .= "      (SELECT substr(comment, 1, position(' ' in comment)) AS chunk,count(warpSkyfile.quality) AS Nwarpskycell,count(if(warpSkyfile.quality > 0,1,NULL)) AS Nwarpskycellbad";
+    $query .= "      FROM rawExp LEFT JOIN chipRun USING (exp_id) LEFT JOIN camRun USING (chip_id) LEFT JOIN fakeRun USING (cam_id) LEFT JOIN warpRun USING (fake_id) JOIN warpSkyfile USING (warp_id)";
+    $query .= "      WHERE dateobs like '$date%' AND exp_id > $refExpID";
+    $query .= "      AND (camRun.label LIKE '%SS%' OR camRun.label LIKE '%EU%' OR camRun.label LIKE '%Bright.%') AND obs_mode NOT LIKE 'ENGINEERING' AND obs_mode NOT LIKE 'MANUAL'";
+    $query .= "      GROUP BY exp_name) as subc";
+    $query .= "    GROUP BY chunk) AS subb ";
+    $query .= "ON suba.chunk=subb.chunk";
+
+
+    my $result = &mysql_select ($query);
+
+    if($verbose) {printf "%30s | %11s | %4s | %4s | %7s | %4s | %7s | %4s | %7s |\n", "chunk", "Nobs", "Ncam","Nwrp","Nwrp_pub", "Nwwd", "Nwwd_pub", "Nwsd", "Nwsd_pub"}; 
+    while (@row = $result->fetchrow_array()) {
+          my $Nvis1 = 0;
+          my $Nvis2 = 0;
+          my $Nvis3 = 0;
+          my $Nvis4 = 0;
+          my $Ncamgood = 0;
+          my $Ncambad = 0;
+          my $Nwarpgood = 0;
+          my $Nwarpbad = 0;
+          my $NexpWWdiff = 0;
+          my $Npubwarps = 0;
+          my $uniwarps = undef;
+          my $uniNvis1 = 0;
+          my $uniNvis2 = 0;
+          my $uniNvis3 = 0;
+          my $uniNvis4 = 0;
+
+          my $chunkname = $row[0];
+          $chunkname =~ s/^\s+|\s+$//g;
+          if($row[1] gt 0) {$Nvis1 = $row[1]};
+          if($row[2] gt 0) {$Nvis2 = $row[2]};
+          if($row[3] gt 0) {$Nvis3 = $row[3]};
+          if($row[4] gt 0) {$Nvis4 = $row[4]};
+          if($row[5] gt 0) {$Ncamgood = $row[5]};
+          if($row[6] gt 0) {$Ncambad = $row[6]};
+          if($row[7] gt 0) {$Nwarpgood = $row[7]};
+          if($row[8] gt 0) {$Nwarpbad = $row[8]};
+          if($row[9] gt 0) {$uniNvis1 = $row[9]};
+          if($row[10] gt 0) {$uniNvis2 = $row[10]};
+          if($row[11] gt 0) {$uniNvis3 = $row[11]};
+          if($row[12] gt 0) {$uniNvis4 = $row[12]};
+
+          #set flags if present
+          if($Ncambad >= 1) {$badcamflag = 1};
+          if($Nwarpbad >= 1) {$badwarpflag = 1};
+
+          #Find the expected number of diffs for this chunk
+          my $query1 = "SELECT suba.chunk, SUM(IF(suba.Nvisits = 2,1,NULL)) as totvis2, SUM(IF(suba.Nvisits = 3 OR suba.Nvisits = 4,2,NULL)) as totvis34 FROM";
+          $query1 .= "    (SELECT substr(comment, 1, position(' ' in comment)) AS chunk,count(distinct(comment)) AS Nvisits FROM";
+          $query1 .= "    rawExp LEFT JOIN chipRun USING (exp_id) LEFT JOIN camRun USING (chip_id) WHERE dateobs like '$date%'";
+          $query1 .= "    AND exp_id > $refExpID AND (camRun.label LIKE '%SS%' OR camRun.label LIKE '%EU%' OR camRun.label LIKE '%Bright.%') AND obs_mode NOT LIKE 'ENGINEERING' AND obs_mode NOT LIKE 'MANUAL'";
+          $query1 .= "    AND substr(comment, 1, position(' ' in comment)) LIKE '$chunkname%' group by object) as suba group by chunk;";
+          my $result1 = &mysql_select ($query1);
+          while (@row = $result1->fetchrow_array()) {$NexpWWdiff = $row[1] + $row[2];}
+
+          #find the unique diff_ids for each chunk, to be matched to the diffRun catalog
+          my $query2 = "     SELECT diff_id,warp1,stack1,warp2,stack2 FROM ";
+          $query2 .= "           diffInputSkyfile JOIN warpRun ON (warp1=warp_id OR warp2=warp_id) JOIN fakeRun USING (fake_id) JOIN camRun USING (cam_id) JOIN camProcessedExp USING (cam_id) JOIN chipRun USING (chip_id) JOIN rawExp USING (exp_id)";
+          $query2 .= "           WHERE rawExp.dateobs LIKE '$date%' AND exp_id > $refExpID AND (camRun.label LIKE '%SS%' OR camRun.label LIKE '%EU%' OR camRun.label LIKE '%Bright.%')";
+          $query2 .= "            AND obs_mode NOT LIKE 'ENGINEERING' AND obs_mode NOT LIKE 'MANUAL' AND substr(comment, 1, position(' ' in comment)) LIKE '$chunkname%' GROUP By diff_id";
+          my $result2 = &mysql_select ($query2);
+
+          my $NWWdiffgood = 0;
+          my $NWWdiffbad = 0;
+          my $NWWdiffpub = 0;
+
+          my $NWSdiffgood = 0;
+          my $NWSdiffbad = 0;
+          my $NWSdiffpub = 0;
+          my %wrpcnt = ();
+
+          while (@row2 = $result2->fetchrow_array()) {
+              #distinguish between WW and WS diffs based on stack2
+              if ($row2[4] == 0) {
+                  #find the total number of skycell and the number of skycell with a bad quality flag as well as the full publish state 
+                  my $query3 = "     SELECT count(diffSkyfile.quality) AS Ndiff,count(if(diffSkyfile.quality > 0,1,NULL)) AS Ndiffbad,count(if(publishRun.state LIKE 'full',1,NULL)) AS Npub FROM ";
+                  $query3 .= "           diffRun JOIN diffSkyfile USING (diff_id) LEFT JOIN publishRun ON (diffRun.diff_id = stage_id)";
+                  $query3 .= "           WHERE diff_id='$row2[0]' GROUP BY diffRun.diff_id";
+                  my $result3 = &mysql_select ($query3);
+                  while (@row3 = $result3->fetchrow_array()) {
+                      #if all skycells have bad quality, this is a bad diff. If all the skycells are in a full state in publishRun, it is fully published 
+                      if ($row3[1]<$row3[0]) {$NWWdiffgood ++;}
+                      if ($row3[1]==$row3[0]) {$NWWdiffbad ++;}
+                      if ($row3[2]==$row3[0]) {$NWWdiffpub ++;}
+
+                      #if this wwdiff is published, record its corresponding warp_ids to see how many unique ones there are
+                      if ($row3[2]==$row3[0]) {$wrpcnt{$row2[1]}++;}  # record the presence of warp1
+                      if ($row3[2]==$row3[0]) {$wrpcnt{$row2[3]}++;}  # record the presence of warp2
+                  }
+              }
+
+              if ($row2[4] > 0) {
+                  #find the total number of skycell and the number of skycell with a bad quality flag as well as the full publish state 
+                  my $query3 = "     SELECT count(diffSkyfile.quality) AS Ndiff,count(if(diffSkyfile.quality > 0,1,NULL)) AS Ndiffbad,count(if(publishRun.state LIKE 'full',1,NULL)) AS Npub FROM ";
+                  $query3 .= "           diffRun JOIN diffSkyfile USING (diff_id) LEFT JOIN publishRun ON (diffRun.diff_id = stage_id)";
+                  $query3 .= "           WHERE diff_id='$row2[0]' GROUP BY diffRun.diff_id";
+                  my $result3 = &mysql_select ($query3);
+                  while (@row3 = $result3->fetchrow_array()) {
+                      #if all skycells have bad quality, this is a bad diff. If all the skycells are in a full state in publishRun, it is fully published 
+                      if ($row3[1]<$row3[0]) {$NWSdiffgood ++;}
+                      if ($row3[1]==$row3[0]) {$NWSdiffbad ++;}
+                      if ($row3[2]==$row3[0]) {$NWSdiffpub ++;}
+                  }
+              }
+          }
+          #pull out the unique warp_ids
+          $Npubwarps = keys %wrpcnt;
+
+          #check for bad diffs 
+          if(($NWWdiffbad >= 1) or ($NWSdiffbad >= 1)) {$baddiffflag = 1;}
+
+          if (($uniNvis1 ne $uniNvis2) or ($uniNvis1 ne $uniNvis3) or ($uniNvis1 ne $uniNvis4)) {
+            if ($NWWdiffpub ne $NexpWWdiff) {
+              if($verbose) {printf "%30s | %2s%1s%2s%1s%2s%1s%2s | %2s%1s%1s | %2s%1s%1s | %8s | %2s%1s%1s | %8s | %2s%1s%1s | %8s | %20s\n", $chunkname, $Nvis1, "/",$Nvis2, "/",$Nvis3, "/",$Nvis4,$Ncamgood, "/",$Ncambad,$Nwarpgood, "/",$Nwarpbad,$Npubwarps,$NWWdiffgood, "/",$NWWdiffbad,$NWWdiffpub,$NWSdiffgood, "/",$NWSdiffbad,$NWSdiffpub, "chunk incomplete, not done"};
+              push @{ $struct->{chunk} }, {
+                 chunkname     => $chunkname,
+                 Nvis1   => $Nvis1*1,
+                 Nvis2  => $Nvis2*1,
+                 Nvis3  => $Nvis3*1,
+                 Nvis4  => $Nvis4*1,
+                 Ncamgood => $Ncamgood*1,
+                 Ncambad => $Ncambad*1,
+                 Nwarpgood => $Nwarpgood*1,
+                 Nwarpbad => $Nwarpbad*1,
+                 Npubwarps => $Npubwarps*1,
+                 NWWdiffgood => $NWWdiffgood*1,
+                 NWWdiffbad => $NWWdiffbad*1,
+                 NWWdiffpub => $NWWdiffpub*1,
+                 NWSdiffgood => $NWSdiffgood*1,
+                 NWSdiffbad => $NWSdiffbad*1,
+                 NWSdiffpub => $NWSdiffpub*1,
+                 ippstatus => "incomplete",
+                 chunkcomp => "not done",
+              };
+            }  
+            if ($NWWdiffpub eq $NexpWWdiff) {
+              if($verbose) {printf "%30s | %2s%1s%2s%1s%2s%1s%2s | %2s%1s%1s | %2s%1s%1s | %8s | %2s%1s%1s | %8s | %2s%1s%1s | %8s | %20s\n", $chunkname, $Nvis1, "/",$Nvis2, "/",$Nvis3, "/",$Nvis4,$Ncamgood, "/",$Ncambad,$Nwarpgood, "/",$Nwarpbad,$Npubwarps,$NWWdiffgood, "/",$NWWdiffbad,$NWWdiffpub,$NWSdiffgood, "/",$NWSdiffbad,$NWSdiffpub, "chunk incomplete, done"};
+              push @{ $struct->{chunk} }, {
+                 chunkname     => $chunkname,
+                 Nvis1   => $Nvis1*1,
+                 Nvis2  => $Nvis2*1,
+                 Nvis3  => $Nvis3*1,
+                 Nvis4  => $Nvis4*1,
+                 Ncamgood => $Ncamgood*1,
+                 Ncambad => $Ncambad*1,
+                 Nwarpgood => $Nwarpgood*1,
+                 Nwarpbad => $Nwarpbad*1,
+                 Npubwarps => $Npubwarps*1,
+                 NWWdiffgood => $NWWdiffgood*1,
+                 NWWdiffbad => $NWWdiffbad*1,
+                 NWWdiffpub => $NWWdiffpub*1,
+                 NWSdiffgood => $NWSdiffgood*1,
+                 NWSdiffbad => $NWSdiffbad*1,
+                 NWSdiffpub => $NWSdiffpub*1,
+                 ippstatus => "incomplete",
+                 chunkcomp => "done",
+              };
+            }
+          }
+          if (($uniNvis1 eq $uniNvis2) and ($uniNvis1 eq $uniNvis3) and ($uniNvis1 eq $uniNvis4)) {
+            if ($NWWdiffpub ne $NexpWWdiff) {
+              if($verbose) {printf "%30s | %2s%1s%2s%1s%2s%1s%2s | %2s%1s%1s | %2s%1s%1s | %8s | %2s%1s%1s | %8s | %2s%1s%1s | %8s | %20s\n", $chunkname, $Nvis1, "/",$Nvis2, "/",$Nvis3, "/",$Nvis4,$Ncamgood, "/",$Ncambad,$Nwarpgood, "/",$Nwarpbad,$Npubwarps,$NWWdiffgood, "/",$NWWdiffbad,$NWWdiffpub,$NWSdiffgood, "/",$NWSdiffbad,$NWSdiffpub, "chunk complete, not done"};
+              push @{ $struct->{chunk} }, {
+                 chunkname     => $chunkname,
+                 Nvis1   => $Nvis1*1,
+                 Nvis2  => $Nvis2*1,
+                 Nvis3  => $Nvis3*1,
+                 Nvis4  => $Nvis4*1,
+                 Ncamgood => $Ncamgood*1,
+                 Ncambad => $Ncambad*1,
+                 Nwarpgood => $Nwarpgood*1,
+                 Nwarpbad => $Nwarpbad*1,
+                 Npubwarps => $Npubwarps*1,
+                 NWWdiffgood => $NWWdiffgood*1,
+                 NWWdiffbad => $NWWdiffbad*1,
+                 NWWdiffpub => $NWWdiffpub*1,
+                 NWSdiffgood => $NWSdiffgood*1,
+                 NWSdiffbad => $NWSdiffbad*1,
+                 NWSdiffpub => $NWSdiffpub*1,
+                 ippstatus => "complete",
+                 chunkcomp => "not done",
+              };
+
+            }
+            if ($NWWdiffpub eq $NexpWWdiff) {
+              if($verbose) {printf "%30s | %2s%1s%2s%1s%2s%1s%2s | %2s%1s%1s | %2s%1s%1s | %8s | %2s%1s%1s | %8s | %2s%1s%1s | %8s | %20s\n", $chunkname, $Nvis1, "/",$Nvis2, "/",$Nvis3, "/",$Nvis4,$Ncamgood, "/",$Ncambad,$Nwarpgood, "/",$Nwarpbad,$Npubwarps,$NWWdiffgood, "/",$NWWdiffbad,$NWWdiffpub,$NWSdiffgood, "/",$NWSdiffbad,$NWSdiffpub, "chunk complete, done"};
+              push @{ $struct->{chunk} }, {
+                 chunkname     => $chunkname,
+                 Nvis1   => $Nvis1*1,
+                 Nvis2  => $Nvis2*1,
+                 Nvis3  => $Nvis3*1,
+                 Nvis4  => $Nvis4*1,
+                 Ncamgood => $Ncamgood*1,
+                 Ncambad => $Ncambad*1,
+                 Nwarpgood => $Nwarpgood*1,
+                 Nwarpbad => $Nwarpbad*1,
+                 Npubwarps => $Npubwarps*1,
+                 NWWdiffgood => $NWWdiffgood*1,
+                 NWWdiffbad => $NWWdiffbad*1,
+                 NWWdiffpub => $NWWdiffpub*1,
+                 NWSdiffgood => $NWSdiffgood*1,
+                 NWSdiffbad => $NWSdiffbad*1,
+                 NWSdiffpub => $NWSdiffpub*1,
+                 ippstatus => "complete",
+                 chunkcomp => "done",
+              };
+
+            }
+          }
+    }
+
+    if($verbose) {print "\n"};
+
+    #-----------------------------------------
+    #list bad quality cam exposures
+    if ($badcamflag != 0) {
+        my $query = "SELECT exp_name,cam_id, camProcessedExp.quality, camProcessedExp.sigma_ra, camProcessedExp.sigma_dec, comment";
+        $query .= " FROM rawExp";
+        $query .= " JOIN chipRun USING (exp_id)";
+        $query .= " JOIN camRun USING (chip_id)";
+        $query .= " JOIN camProcessedExp USING (cam_id)";
+        $query .= " WHERE dateobs LIKE '$date%'";
+        $query .= " AND exp_id > $refExpID"; 
+        $query .= " AND exp_type = 'OBJECT'";
+        $query .= " AND (camRun.label LIKE '%SS%' OR camRun.label LIKE '%EU%' OR camRun.label LIKE '%Bright.%') AND obs_mode NOT LIKE 'ENGINEERING' AND obs_mode NOT LIKE 'MANUAL'";
+        $query .= " AND (camProcessedExp.quality > 0 OR camProcessedExp.sigma_ra > 5 OR camProcessedExp.sigma_dec > 5)";
+        $query .= " ORDER BY comment";
+
+        my $result = &mysql_select ($query);
+
+        $doHeader = 1; 
+
+        while (@row = $result->fetchrow_array()) {
+            push @{ $struct->{badcam} }, {
+               exp_name     => $row[0],
+               cam_id   => $row[1]*1,
+               quality  => $row[2]*1,
+               sigma_ra  => $row[3]*1,
+               sigma_dec  => $row[4]*1,
+               comment => $row[5],
+            };
+
+            if ($doHeader) {
+                if($verbose) {print "Bad quality camRun exposures:\n"};
+                if($verbose) {printf "%11s | %7s | %7s | %9s | %9s | %15s\n", "exp_name","cam_id", "quality", "sigma_ra", "sigma_dec", "comment"};
+                $doHeader = 0;
+	    }
+            if($verbose) {printf "%11s | %7s | %7d | %9s | %9s | %15s\n", $row[0], $row[1], $row[2], $row[3], $row[4], $row[5]}; 
+        }
+        if (!$doHeader) { print "\n"; }
+    }
+
+    #-----------------------------------------
+    #list bad quality warp exposures
+    if ($badwarpflag != 0) {
+        $query = "SELECT exp_name,warp_id,count(warpSkyfile.quality) AS Nwarpskycell,count(if(warpSkyfile.quality > 0,1,NULL)) AS Nwarpskycellbad, comment";
+        $query .= "      FROM rawExp LEFT JOIN chipRun USING (exp_id) LEFT JOIN camRun USING (chip_id) LEFT JOIN fakeRun USING (cam_id) LEFT JOIN warpRun USING (fake_id) JOIN warpSkyfile USING (warp_id)";
+        $query .= "      WHERE dateobs like '$date%' AND exp_id > $refExpID";
+        $query .= "      AND (camRun.label LIKE '%SS%' OR camRun.label LIKE '%EU%' OR camRun.label LIKE '%Bright.%') AND obs_mode NOT LIKE 'ENGINEERING' AND obs_mode NOT LIKE 'MANUAL'";
+        $query .= "      GROUP BY exp_name ORDER BY comment";
+
+        my $result = &mysql_select ($query);
+
+        $doHeader = 1; 
+
+        while (@row = $result->fetchrow_array()) {
+    	if ($row[2] == $row[3]) {
+               push @{ $struct->{badwarp} }, {
+                  exp_name     => $row[0],
+                  warp_id   => $row[1]*1,
+                  comment => $row[4],
+               };
+ 
+               if ($doHeader) {
+                    if($verbose) {print "Bad quality warpRun exposures:\n"};
+                    if($verbose) {printf "%11s | %7s | %15s\n", "exp_name", "warp_id", "comment"};
+                    $doHeader = 0;
+	        }
+                if($verbose) {printf "%11s | %7d | %15s \n", $row[0], $row[1], $row[4]}; 
+             }
+        }
+        if (!$doHeader) { print "\n"; }
+    }
+
+    #-----------------------------------------
+    #list bad quality diff exposures
+    if ($baddiffflag != 0) {
+        my $query = "     SELECT diff_id,warp1,stack1,warp2,stack2,exp_name,comment FROM ";
+        $query .= "           diffInputSkyfile JOIN warpRun ON (warp1=warp_id OR warp2=warp_id) JOIN fakeRun USING (fake_id) JOIN camRun USING (cam_id) JOIN camProcessedExp USING (cam_id) JOIN chipRun USING (chip_id) JOIN rawExp USING (exp_id)";
+        $query .= "           WHERE rawExp.dateobs LIKE '$date%' AND exp_id > $refExpID AND (camRun.label LIKE '%SS%' OR camRun.label LIKE '%EU%' OR camRun.label LIKE '%Bright.%') AND obs_mode NOT LIKE 'ENGINEERING' AND obs_mode NOT LIKE 'MANUAL'";
+        $query .= "           GROUP By diff_id";
+        my $result = &mysql_select ($query);
+
+        $doHeader = 1; 
+
+        while (@row = $result->fetchrow_array()) {
+            if ($doHeader) {
+                if($verbose) {print "Bad quality diffRun exposures:\n"};
+                if($verbose) {printf "%11s | %7s | %7s | %7s | %7s | %7s | %15s\n", "exp_name", "diff_id", "warp1", "stack1", "warp2", "stack2", "comment"};
+                $doHeader = 0;
+            }
+
+            #distinguish between WW and WS diffs based on stack2
+            if ($row[4] == 0) {
+                  #find the total number of skycell and the number of skycell with a bad quality flag as well as the full publish state 
+                  my $query3 = "     SELECT count(diffSkyfile.quality) AS Ndiff,count(if(diffSkyfile.quality > 0,1,NULL)) AS Ndiffbad,count(if(publishRun.state LIKE 'full',1,NULL)) AS Npub FROM ";
+                  $query3 .= "           diffRun JOIN diffSkyfile USING (diff_id) LEFT JOIN publishRun ON (diffRun.diff_id = stage_id)";
+                  $query3 .= "           WHERE diff_id='$row[0]' GROUP BY diffRun.diff_id";
+                  my $result3 = &mysql_select ($query3);
+                  while (@row3 = $result3->fetchrow_array()) {
+                      #if all skycells have bad quality, this is a bad diff. If all the skycells are in a full state in publishRun, it is fully published 
+                      if ($row3[1]==$row3[0]) {
+                        if($verbose) {printf "%11s | %7d | %7d | %7d | %7d | %7d | %15s \n", $row[5], $row[0], $row[1], $row[2], $row[3], $row[4], $row[6]};
+
+                        push @{ $struct->{badwwdiff} }, {
+                           exp_name     => $row[5],
+                           diff_id   => $row[0]*1,
+                           warp1  => $row[1]*1,
+                           stack1  => $row[2]*1,
+                           warp2  => $row[3]*1,
+                           stack2 => $row[4]*1,
+                           comment => $row[6],
+                        };
+                      } 
+                  }
+            }
+
+            if ($row[4] > 0) {
+                  #find the total number of skycell and the number of skycell with a bad quality flag as well as the full publish state 
+                  my $query3 = "     SELECT count(diffSkyfile.quality) AS Ndiff,count(if(diffSkyfile.quality > 0,1,NULL)) AS Ndiffbad,count(if(publishRun.state LIKE 'full',1,NULL)) AS Npub FROM ";
+                  $query3 .= "           diffRun JOIN diffSkyfile USING (diff_id) LEFT JOIN publishRun ON (diffRun.diff_id = stage_id)";
+                  $query3 .= "           WHERE diff_id='$row[0]' GROUP BY diffRun.diff_id";
+                  my $result3 = &mysql_select ($query3);
+                  while (@row3 = $result3->fetchrow_array()) {
+                      #if all skycells have bad quality, this is a bad diff. If all the skycells are in a full state in publishRun, it is fully published 
+                      if ($row3[1]==$row3[0]) {
+                        if($verbose) {printf "%11s | %7d | %7d | %7d | %7d | %7d | %15s \n", $row[5], $row[0], $row[1], $row[2], $row[3], $row[4], $row[6]};
+
+                        push @{ $struct->{badwsdiff} }, {
+                           exp_name     => $row[5],
+                           diff_id   => $row[0]*1,
+                           warp1  => $row[1]*1,
+                           stack1  => $row[2]*1,
+                           warp2  => $row[3]*1,
+                           stack2 => $row[4]*1,
+                           comment => $row[6],
+                        };
+                      }
+                  }
+            }
+        }
+        if (!$doHeader) { print "\n"; }
+    }
+
+    #-----------------------------------------
+    #check for overridden exposures
+    my $query = "SELECT COUNT(comment), comment";
+    $query .= " FROM rawExp";
+    $query .= " JOIN chipRun USING (exp_id)";
+    $query .= " JOIN camRun USING (chip_id)";
+    $query .= " WHERE dateobs LIKE '$date%'";
+    $query .= " AND exp_id > $refExpID"; 
+    $query .= " AND exp_type = 'OBJECT'";
+    $query .= " AND (camRun.label LIKE '%SS%' OR camRun.label LIKE '%EU%' OR camRun.label LIKE '%Bright.%') AND obs_mode NOT LIKE 'ENGINEERING' AND obs_mode NOT LIKE 'MANUAL'";
+    $query .= " GROUP BY comment";
+
+    my $result = &mysql_select ($query);
+
+    $doHeader = 1; 
+    while (@row = $result->fetchrow_array()) {
+        if ($row[0] > 1) {
+            my $query = "SELECT exp_name,dateobs, comment";
+            $query .= " FROM rawExp";
+            $query .= " JOIN chipRun USING (exp_id)";
+            $query .= " JOIN camRun USING (chip_id)";
+            $query .= " WHERE dateobs LIKE '$date%' AND comment LIKE '$row[1]'";
+            $query .= " AND exp_id > $refExpID"; 
+            $query .= " AND exp_type = 'OBJECT'";
+            $query .= " AND (camRun.label LIKE '%SS%' OR camRun.label LIKE '%EU%' OR camRun.label LIKE '%Bright.%') AND obs_mode NOT LIKE 'ENGINEERING' AND obs_mode NOT LIKE 'MANUAL'";
+            $query .= " ORDER BY dateobs DESC";
+
+            my $result = &mysql_select ($query);
+
+            $doskip = 0;
+            while (@row2 = $result->fetchrow_array()) {
+                if ($doskip) {
+                    push @{ $struct->{overridden} }, {
+                       exp_name     => $row2[0],
+                       dateobs   => $row2[1],
+                       comment => $row2[2],
+                    };
+
+                    if ($doHeader) {
+                        if($verbose) {print "Overridden exposures:\n"};
+                        if($verbose) {printf "%11s |  %9s | %15s\n", "exp_name", "dateobs", "comment"};
+                        $doHeader = 0;
+                    }
+                    if($verbose) {printf "%11s | %9s | %15s\n", $row2[0], $row2[1], $row2[2]}; 
+                }
+                $doskip = 1;
+            }
+        }
+    }
+    if (!$doHeader) { print "\n"; }
+
+
+    my $jsonizer = JSON->new->pretty;
+    $jsonizer = $jsonizer->canonical();
+    my $outname = "ippmopsreport_".$dbname."_".$tjd;
+    if($final) {$outname = $outname."_final"};
+    $outname = $outname.".json";
+
+    open my $fh, ">", $outname;
+      print $fh $jsonizer->encode($struct);
+    close $fh;
+
+    #Copy the file to the MOPS cluster nmops15
+    my $cpcmd = "scp -r $outname ippmonitor\@nmops15.ifa.hawaii.edu:/home/ippmonitor/ippMopsReports/0.0.1/."; 
+    system($cpcmd);
+
+}
+
+exit 0;
+
+# global dbh?
+sub mysql_select {
+    
+    my $query = $_[0];
+    
+    my $statement = $dbh->prepare($query);
+    
+    $statement->execute();
+    
+    return $statement;
+}
+
+sub mysql_dump_result {
+    
+    my $result = $_[0];
+    
+    $Nfields = $result->{NUM_OF_FIELDS};
+
+    print "Nfields: $Nfields\n";
+
+    for ($i = 0; $i < $Nfields; $i++) {
+	printf "Field %d : %s\n", $i, $result->{NAME}->[$i];
+    }
+
+    while (@row = $result->fetchrow_array()) {
+	for ($i = 0; $i < $Nfields; $i++) {
+	    print "$row[$i]  ";
+	}
+	print "\n";
+    }
+    return;
+}
+
+# example using the dbh prepare, execute, fetch commands:
+if (0) {
+
+    my $result = &mysql_select ("SELECT * from rawExp limit 2");
+    
+    &mysql_dump_result ($result);
+    
+    exit 1;
+}
Index: /trunk/ippconfig/gpc2/Makefile.am
===================================================================
--- /trunk/ippconfig/gpc2/Makefile.am	(revision 41337)
+++ /trunk/ippconfig/gpc2/Makefile.am	(revision 41338)
@@ -8,4 +8,5 @@
 	format_mef.config \
 	format_20180131.config \
+        ghost.model.mdc \
 	ppImage.config \
         ppMerge.config \
Index: /trunk/ippconfig/gpc2/psastro.config
===================================================================
--- /trunk/ippconfig/gpc2/psastro.config	(revision 41337)
+++ /trunk/ippconfig/gpc2/psastro.config	(revision 41338)
@@ -130,6 +130,6 @@
 DVO.GETSTAR.PHOTCODE        STR      i
 DVO.GETSTAR.MAX.RHO         F32      3000.0
-DVO.GETSTAR.MIN.MAG         F32      12.0
-DVO.GETSTAR.MIN.MAG.INST    F32     -25.0
+DVO.GETSTAR.MIN.MAG         F32      2.0
+DVO.GETSTAR.MIN.MAG.INST    F32     -35.0
 
 # rather than mean, use specified EDGE:
@@ -156,5 +156,5 @@
   ZEROPT   F32 24.611
   PHOTCODE STR i
-  GHOST_MAX_MAG                   F32 -25.0
+  GHOST_MAX_MAG                   F32 -23.5
 END
 PHOTCODE.DATA METADATA
@@ -174,18 +174,22 @@
   ZEROPT   F32 26.3
   PHOTCODE STR r
-  GHOST_MAX_MAG                   F32 -20.0
-END
-
-REFSTAR_MASK                    BOOL FALSE
+  GHOST_MAX_MAG                   F32 -23.0
+END
+
+REFSTAR_MASK                    BOOL TRUE
 REFSTAR_MASK_MAX_MAG            F32 -15.0
 REFSTAR_MASK_SATSTAR_MAG_MAX    F32 -15.0
 REFSTAR_MASK_SATSTAR_MAG_SLOPE  F32  10.15
-# CZW: Original 2010-02-08 position angle offset
-REFSTAR_MASK_SATSTAR_POS_ZERO   F32  -0.798
-# CZW: Updated 2011-02-18 position angle offset. As above, offset 0.5 degree.
-# REFSTAR_MASK_SATSTAR_POS_ZERO   F32  -0.78927
-#REFSTAR_MASK_SATSTAR_POS_ZERO   F32  -0.80237
-#REFSTAR_MASK_SATSTAR_POS_ZERO   F32  -0.81109
-# Length = 10**(SLOPE*(MAG_MAX - InstMag)) - OFFSET
+
+#TdB20200214: Position of gpc2 spikes determined on image
+REFSTAR_MASK_SATSTAR_POS_ZERO   F32  -0.7892
+#TdB20200225: The rotator angle for gpc2 goes the other way around as gpc1
+PSASTRO.MODEL.ROT.PARITY      S32      -1
+
+#TdB20200229: The size of saturated star circles should be an exponential for gpc2
+#rad(mag) = REFSTAR_MASK_SATSTAR_RAD_OFFSET + REFSTAR_MASK_SATSTAR_EXP**(REFSTAR_MASK_SATSTAR_MAG_MAX-mag)
+REFSTAR_MASK_SATSTAR_MAG_MAX    F32 -11.363
+REFSTAR_MASK_SATSTAR_RAD_OFFSET F32  17.631
+REFSTAR_MASK_SATSTAR_EXP        F32   1.493
 
 REFSTAR_MASK_SATSPIKE_MAG_SLOPE F32  0.096
@@ -204,6 +208,4 @@
 
 
-
-
 REFSTAR_MASK_BLEED              BOOL FALSE
 REFSTAR_MASK_BLEED_MAG_MAX      F32 -15.0
@@ -216,27 +218,27 @@
 
 # ghost model file is relative to PATH (eg, ippconfig directory)
-#GHOST_MODEL                     STR  gpc1/ghost.model.mdc
+GHOST_MODEL                     STR  gpc2/ghost.model.mdc
 # GHOST_MAX_MAG                   F32 -20.0
 
-GLINT_LENGTH_MAG_SLOPE          F32 2500.0
-GLINT_LENGTH_MAG_ZERO           F32 -20.0
-GLINT_WIDTH                     F32  150.0
 REFSTAR_COUNT_GHOSTS            BOOL TRUE
 REFSTAR_MASK_GHOST              BOOL TRUE
-
-# GHOST_CENTER_X_0         	F32   66.2
-# GHOST_CENTER_X_SLOPE         	F32   -0.02454
-# GHOST_CENTER_Y_0         	F32   16.6
-# GHOST_CENTER_Y_SLOPE         	F32   +0.01565
-# 
-# GHOST_INNER_MAJOR_0         	F32   32.9
-# GHOST_INNER_MAJOR_SLOPE         F32   +0.00612
-# GHOST_INNER_MINOR_0         	F32   58.7
-# GHOST_INNER_MINOR_SLOPE         F32   -0.00258
-# 
-# GHOST_OUTER_MAJOR_0         	F32   90.6
-# GHOST_OUTER_MAJOR_SLOPE         F32   +0.01548
-# GHOST_OUTER_MINOR_0         	F32  133.9
-# GHOST_OUTER_MINOR_SLOPE         F32   -0.00288
+PSASTRO.SAVE.GHOSTS             BOOL TRUE  # save ghost positions as table in output smf file
+
+#TdB20200305: The length of glints as function of mag is recomputed for gpc2
+#glint_length(mag) = GLINT_LENGTH_MAG_SLOPE*(GLINT_LENGTH_MAG_ZERO-mag)
+GLINT_LENGTH_MAG_SLOPE          F32 1901.0
+GLINT_LENGTH_MAG_ZERO           F32 -16.8
+#TdB20200308: The length of glints also depends on position off the focal plane (perpendicular to pixel FPA)
+#glint_length(pos) = GLINT_LENGTH_POS_SLOPE*(GLINT_LENGTH_POS_REF-pos)
+GLINT_LENGTH_POS_SLOPE          F32 0.00019
+GLINT_LENGTH_POS_REF            F32 33821
+#TdB20200312: The angle that glints have depends on position along the focal plane (parallel to pixel FPA)
+#glint_angle(pos) = GLINT_ANGLE_SLOPE*(GLINT_ANGLE_POS_REF-(pos/1000))
+GLINT_ANGLE_POS_SLOPE           F32  0.07903  
+GLINT_ANGLE_POS_REF             F32 -575.5
+GLINT_WIDTH                     F32  30.0
+
+# save glint positions?
+PSASTRO.SAVE.GLINTS             BOOL TRUE  # save glint positions as table in output smf file
 
 PR_RECIPE METADATA
@@ -271,5 +273,5 @@
 REFSTAR_MASK_CROSSTALK_MAG_MAX   F32 -14.47
 CROSSTALK_MAX_MAG                F32  20.0
-CROSSTALK_MASK                   BOOL TRUE
+CROSSTALK_MASK                   BOOL FALSE
 
 PS1_REFERENCE METADATA
Index: /trunk/ippconfig/recipes/psastro.config
===================================================================
--- /trunk/ippconfig/recipes/psastro.config	(revision 41337)
+++ /trunk/ippconfig/recipes/psastro.config	(revision 41338)
@@ -150,5 +150,4 @@
 REFSTAR_MASK_REGIONS            BOOL FALSE
 REFSTAR_MASK_MAX_MAG            F32 -15.0
-REFSTAR_MASK_SATSTAR_MAG_MAX    F32 -17.0
 REFSTAR_MASK_SATSTAR_MAG_SLOPE  F32  10.0
 REFSTAR_MASK_SATSTAR_POS_ZERO   F32   0.0
@@ -161,4 +160,9 @@
 REFSTAR_MASK_SATSPIKE_WIDTH_SLOPE F32 0.00
 
+REFSTAR_MASK_SATSTAR_MAG_MAX    F32 -11.363
+REFSTAR_MASK_SATSTAR_RAD_OFFSET F32  17.631
+REFSTAR_MASK_SATSTAR_EXP        F32   1.493
+
+PSASTRO.SAVE.GHOSTS             BOOL FALSE
 REFSTAR_MASK_GHOST              BOOL FALSE
 REFSTAR_COUNT_GHOSTS            BOOL FALSE
@@ -184,4 +188,11 @@
 GLINT_LENGTH_MAG_SLOPE          F32 0.0
 GLINT_LENGTH_MAG_ZERO           F32 0.0
+GLINT_LENGTH_POS_SLOPE          F32 0.0
+GLINT_LENGTH_POS_REF            F32 0.0
+GLINT_ANGLE_POS_SLOPE           F32 0.0
+GLINT_ANGLE_POS_REF             F32 0.0
+# save glint positions?
+PSASTRO.SAVE.GLINTS             BOOL FALSE
+
 GLINT_WIDTH                     F32 0.0
 GLINT.REGION			MULTI
Index: /trunk/psModules/src/objects/Makefile.am
===================================================================
--- /trunk/psModules/src/objects/Makefile.am	(revision 41337)
+++ /trunk/psModules/src/objects/Makefile.am	(revision 41338)
@@ -60,4 +60,6 @@
 	pmSourceIO_CMF_PS1_DV5.c \
 	pmSourceIO_MatchedRefs.c \
+	pmSourceIO_Glints.c \
+	pmSourceIO_Ghosts.c \
 	pmSourcePlots.c \
 	pmSourcePlotPSFModel.c \
Index: /trunk/psModules/src/objects/pmSourceIO.c
===================================================================
--- /trunk/psModules/src/objects/pmSourceIO.c	(revision 41337)
+++ /trunk/psModules/src/objects/pmSourceIO.c	(revision 41338)
@@ -701,4 +701,5 @@
     if (file->fpa->chips->n == 1) {
         pmSourceIO_WriteMatchedRefs (file->fits, file->fpa, config);
+        pmSourceIO_WriteGlints (file->fits, file->fpa, config);
         return true;
     }
@@ -808,4 +809,5 @@
 
     pmSourceIO_WriteMatchedRefs (file->fits, file->fpa, config);
+    pmSourceIO_WriteGlints (file->fits, file->fpa, config);
     return true;
 }
Index: /trunk/psModules/src/objects/pmSourceIO.h
===================================================================
--- /trunk/psModules/src/objects/pmSourceIO.h	(revision 41337)
+++ /trunk/psModules/src/objects/pmSourceIO.h	(revision 41338)
@@ -91,4 +91,6 @@
 bool pmSourceIO_ReadMatchedRefs (psFits *fits, pmFPA *fpa, const pmConfig *config);
 
+bool pmSourceIO_WriteGlints (psFits *fits, pmFPA *fpa, pmConfig *config);
+
 /// @}
 # endif /* PM_SOURCE_IO_H */
Index: /trunk/psastro/src/psastroLoadGhosts.c
===================================================================
--- /trunk/psastro/src/psastroLoadGhosts.c	(revision 41337)
+++ /trunk/psastro/src/psastroLoadGhosts.c	(revision 41338)
@@ -110,5 +110,14 @@
     psPolynomial1D *innerMinor = NULL;
 
+
     pmFPAview *view = pmFPAviewAlloc (0);
+
+    GET_2D_POLY ("GHOST.CENTER.X", centerX);
+    GET_2D_POLY ("GHOST.CENTER.Y", centerY);
+
+    GET_1D_POLY ("GHOST.OUTER.MAJOR", outerMajor);
+    GET_1D_POLY ("GHOST.OUTER.MINOR", outerMinor);
+    GET_1D_POLY ("GHOST.INNER.MAJOR", innerMajor);
+    GET_1D_POLY ("GHOST.INNER.MINOR", innerMinor);
 
     // select the input astrometry data (also carries the refstars)
@@ -119,12 +128,4 @@
     }
     pmFPA *fpa = astrom->fpa;
-
-    GET_2D_POLY ("GHOST.CENTER.X", centerX);
-    GET_2D_POLY ("GHOST.CENTER.Y", centerY);
-
-    GET_1D_POLY ("GHOST.OUTER.MAJOR", outerMajor);
-    GET_1D_POLY ("GHOST.OUTER.MINOR", outerMinor);
-    GET_1D_POLY ("GHOST.INNER.MAJOR", innerMajor);
-    GET_1D_POLY ("GHOST.INNER.MINOR", innerMinor);
 
     // raise an error if the config is broken
@@ -202,4 +203,28 @@
 		    ghostChip = psastroFindChip (&ghost->chip->x, &ghost->chip->y, fpa, ghost->FP->x, ghost->FP->y);
 		    // fprintf (stderr, "-> model chip position: %f, %f\n", ghost->chip->x, ghost->chip->y);
+
+	            psPlane coordFPA,ptCH2;
+    	            psPlane coordCell,ptFP2;
+	            coordCell.x = ghost->chip->x;
+	            coordCell.y = ghost->chip->y;
+                    psPlaneTransformApply(&coordFPA,ghostChip->toFPA,&coordCell);
+	            ptCH2.x = ghost->chip->x;
+               	    ptCH2.y = ghost->chip->y;
+                    psPlaneTransformApply(&ptFP2,ghostChip->toFPA,&ptCH2);
+
+	            psPlane ptCH, ptFP;
+	            ptFP.x = ghost->FP->x;
+               	    ptFP.y = ghost->FP->y;
+                    psPlaneTransformApply (&ptCH, ghostChip->fromFPA, &ptFP);
+
+                    psastroUpdateChipToFPA (fpa, ghostChip);
+	            psPlane ptCH3, ptFP3;
+	            ptCH3.x = ghost->chip->x;
+               	    ptCH3.y = ghost->chip->y;
+                    psPlaneTransformApply(&ptFP3,ghostChip->toFPA,&ptCH3);
+
+
+
+                    psLogMsg ("psastro", 3, "Ghost refpos: %f %f %s pos: %f %f %f %f %f %f %f %f %f %f %f %f mag: %f inshape: %f %f %f outshape: %f %f %f \n", ref->FP->x,ref->FP->y,psMetadataLookupStr(NULL,chip->concepts,"CHIP.NAME"), ghost->FP->x,ghost->FP->y,coordFPA.x,coordFPA.y,ghost->chip->x, ghost->chip->y,ptCH.x,ptCH.y,ptFP2.x,ptFP2.y,ptFP3.x,ptFP3.y, ghost->Mag,ghost->inner.major,ghost->inner.minor,ghost->inner.theta/PM_RAD_DEG,ghost->outer.major,ghost->outer.minor,ghost->outer.theta/PM_RAD_DEG);
 
 		    if (ghostChip) {
Index: /trunk/psastro/src/psastroLoadGlints.c
===================================================================
--- /trunk/psastro/src/psastroLoadGlints.c	(revision 41337)
+++ /trunk/psastro/src/psastroLoadGlints.c	(revision 41338)
@@ -43,7 +43,10 @@
     double GLINT_LENGTH_MAG_SLOPE = psMetadataLookupF32 (&status, recipe, "GLINT_LENGTH_MAG_SLOPE");
     double GLINT_LENGTH_MAG_ZERO = psMetadataLookupF32 (&status, recipe, "GLINT_LENGTH_MAG_ZERO");
+    double GLINT_LENGTH_POS_SLOPE = psMetadataLookupF32 (&status, recipe, "GLINT_LENGTH_POS_SLOPE");
+    double GLINT_LENGTH_POS_REF = psMetadataLookupF32 (&status, recipe, "GLINT_LENGTH_POS_REF");
+    double GLINT_ANGLE_POS_SLOPE = psMetadataLookupF32 (&status, recipe, "GLINT_ANGLE_POS_SLOPE");
+    double GLINT_ANGLE_POS_REF = psMetadataLookupF32 (&status, recipe, "GLINT_ANGLE_POS_REF");
     double glintWidth = psMetadataLookupF32 (&status, recipe, "GLINT_WIDTH");
     double pixelScale = psMetadataLookupF32 (&status, recipe, "PSASTRO.PIXEL.SCALE");
-    
 
     // select the set of glint regions (GLINT.REGION is a MULTI of METADATA items)
@@ -88,7 +91,9 @@
     psastroChipBounds (fpa);
 
-    // find the possible glint star:s for each glint star, convert the position to FPA coordinates.
+
+    // find the possible glint stars, and convert the position to FPA coordinates.
     // search for stars within the glint regions
     for (int i = 0; i < glintStars->n; i++) {
+
 	pmAstromObj *star = glintStars->data[i];
 	if (star->Mag > GLINT_MAX_MAG) continue; // XXX should not be needed...
@@ -115,11 +120,9 @@
 	    psRegion glintRegion = psRegionFromString (glintRegionString);
 
-	    // select stars that land in this regio
+	    // select stars that land in this region
 	    if (star->FP->x < glintRegion.x0) continue;
 	    if (star->FP->x > glintRegion.x1) continue;
 	    if (star->FP->y < glintRegion.y0) continue;
 	    if (star->FP->y > glintRegion.y1) continue;
-
-	    double glintLength = GLINT_LENGTH_MAG_SLOPE*(GLINT_LENGTH_MAG_ZERO - star->Mag);
 
 	    char *glintType = psMetadataLookupStr (&status, glintItem->data.md, "GLINT.TYPE");
@@ -129,175 +132,318 @@
 	    }
 
-	    // depending on the glint type, we need to find either the chips in the row or in the column.
+	    double glintLength = GLINT_LENGTH_MAG_SLOPE*(GLINT_LENGTH_MAG_ZERO - star->Mag);
+            double glintAngle = 0;
+
+            //Besides brightness, the length of the glints also depends on the position of the star compared to the focal plane. But, seemingly only for stars closer than 30k pixels     
+	    if ((!strcasecmp(glintType, "TOP") || !strcasecmp(glintType, "BOTTOM")) && abs(star->FP->y) < 30000 ){ 
+              glintLength /= GLINT_LENGTH_POS_SLOPE*(GLINT_LENGTH_POS_REF - abs(star->FP->y));
+
+            }
+	    if ((!strcasecmp(glintType, "LEFT") || !strcasecmp(glintType, "RIGHT")) && abs(star->FP->x) < 30000 ) {
+              glintLength /= GLINT_LENGTH_POS_SLOPE*(GLINT_LENGTH_POS_REF - abs(star->FP->x));
+            }
+            //also compute the angle of the glint, which depends on position parallel to the FPX
+	    if (!strcasecmp(glintType, "TOP") || !strcasecmp(glintType, "BOTTOM") ){ 
+	      glintAngle = PM_RAD_DEG * (GLINT_ANGLE_POS_SLOPE*((GLINT_ANGLE_POS_REF - star->FP->x)/1000.));
+
+            }
+	    if (!strcasecmp(glintType, "LEFT") || !strcasecmp(glintType, "RIGHT") ) {
+ 	      glintAngle = PM_RAD_DEG * (GLINT_ANGLE_POS_SLOPE*((GLINT_ANGLE_POS_REF - star->FP->y)/1000.));
+            }
+
+
+	    psVector *x_glint = psVectorAlloc(2,PS_TYPE_F32);
+	    psVector *y_glint = psVectorAlloc(2,PS_TYPE_F32);
+
 	    if (!strcasecmp(glintType, "TOP") || !strcasecmp(glintType, "BOTTOM")) {
-		for (int nChip = 0; nChip < fpa->chips->n; nChip++) {
-
-		    pmChip *chip = fpa->chips->data[nChip];
-		    if (!chip) continue;
-
-		    if (!psastroFindChipInXrange (fpa, nChip, star->FP->x, star->FP->y)) {
-			continue;
-		    }
-
-		    // find the coordinate of the end point
-		    double xFPA  = star->FP->x;
-		    double yFPA0 = star->FP->y;
-		    double yFPA1;
-		    if (!strcasecmp(glintType, "TOP")) {
-			yFPA1 = yFPA0 - glintLength;
-		    } else {
-			yFPA1 = yFPA0 + glintLength;
-		    }
-
-		    // FPA coordinates of intersections with chip edges 
-		    double yFPAs, yFPAe;
-		    psastroFindChipYedges (&yFPAs, &yFPAe, fpa, nChip);
-
-		    if (yFPA0 > yFPA1) PS_SWAP (yFPA0, yFPA1);
-		    if (yFPAs > yFPAe) PS_SWAP (yFPAs, yFPAe);
-
-		    // does this glint cross this chip?
-		    if (yFPA0 > yFPAe) continue;
-		    if (yFPA1 < yFPAs) continue;
-
-		    yFPA0 = PS_MAX (yFPA0, yFPAs);
-		    yFPA1 = PS_MIN (yFPA1, yFPAe);
-
-		    double xChip0, yChip0, xChip1, yChip1;
-		    psastroFPAtoChip (&xChip0, &yChip0, fpa, nChip, xFPA, yFPA0);
-		    psastroFPAtoChip (&xChip1, &yChip1, fpa, nChip, xFPA, yFPA1);
-
-		    // we now have the location of the glint ends or edge-intersections in chip coordinates
-		    // double-check if this chip overlaps this glint
-		    if (xChip0 > xChip1) PS_SWAP (xChip0, xChip1);
-		    if (yChip0 > yChip1) PS_SWAP (yChip0, yChip1);
-
-		    // bounds of this chip
-		    psRegion *region = pmChipPixels (chip);
-		    if (yChip1 < region->y0) continue;
-		    if (yChip0 > region->y1) continue;
-
-		    // this glint touches this chip. calculate the start and end
-		    // coordinates on this chip
-		    double xChipS = PS_MAX(xChip0 - 0.5*glintWidth, region->x0);
-		    double xChipE = PS_MIN(xChip1 + 0.5*glintWidth, region->x1);
-		    double yChipS = PS_MAX (yChip0, region->y0);
-		    double yChipE = PS_MIN (yChip1, region->y1);
-
-		    // select the 0th readout of the 0th cell for this chip
-		    if (!chip->cells) continue;
-		    if (!chip->cells->n) continue;
-		    pmCell *glintCell = chip->cells->data[0];
-		    if (!glintCell) continue;
-		    if (!glintCell->readouts) continue;
-		    if (!glintCell->readouts->n) continue;
-		    pmReadout *glintReadout = glintCell->readouts->data[0];
-		    if (!glintReadout) continue;
-
-		    // save the glints on the readout->analysis metadata, creating if needed
-		    psArray *glints = psMetadataLookupPtr (&status, glintReadout->analysis, "PSASTRO.GLINTS");
-		    if (glints == NULL) { 
-			glints = psArrayAllocEmpty (100);
-			if (!psMetadataAdd (glintReadout->analysis, PS_LIST_TAIL, "PSASTRO.GLINTS", PS_DATA_ARRAY, "astrometry matches", glints)) {
-			    psWarning("failure to add glints to readout");
-			    psFree (glints);
-			    continue;
-			}
-			psFree (glints);
-		    }
-
-		    fprintf (stderr, "glint %s : %f,%f to %f,%f (%f,%f to %f,%f)\n", glintType, xChip0, yChip0, xChip1, yChip1, xChipS, yChipS, xChipE, yChipE);
-		    psRegion *glint = psRegionAlloc(xChipS, xChipE, yChipS, yChipE);
-		    psArrayAdd (glints, 100, glint);
-		    psFree (glint);
-		    psFree (region);
+		// We want to find the coordinates of the glint end points. However, the glint is straight off the pixel focal plane and has an angle only on the focal plane. So, first find the edge
+		double xFPA0 = star->FP->x;
+		double yFPA0 = star->FP->y;
+		double xFPA1;
+		double yFPA1;
+                //angles for TOP and LEFT have been flipped in the fitting
+		if (!strcasecmp(glintType, "TOP")) {
+                    //find the edge chip and determine the FPA coords of the edge. Then, grab the edge coord and new glint length
+		    for (int nChip = 0; nChip < fpa->chips->n; nChip++) {
+ 			pmChip *chip = fpa->chips->data[nChip];
+ 			if (!chip) continue;
+
+		   	if (!psastroFindChipInXrange (fpa, nChip, star->FP->x, 20000.)) {
+		   	  continue;
+		   	}
+		   	if (!psastroFindChipInYrange (fpa, nChip, star->FP->x, 20000.)) {
+		   	  continue;
+		   	}
+
+		        // FPA coordinates of intersections with chip edges 
+		        double yFPAs, yFPAe;
+		        psastroFindChipYedges (&yFPAs, &yFPAe, fpa, nChip);
+
+  		        xFPA0 = star->FP->x;
+		        yFPA0 = yFPAe;
+                        glintLength-= (star->FP->y-yFPAe);
+                    }
+
+                    xFPA1 = xFPA0 + glintLength*sin(glintAngle*-1.);
+                    yFPA1 = yFPA0 - glintLength*cos(glintAngle*-1.);
+
+		} else {
+                    //find the edge chip and determine the FPA coords of the edge. Then, grab the edge coord and new glint length
+		    for (int nChip = 0; nChip < fpa->chips->n; nChip++) {
+ 			pmChip *chip = fpa->chips->data[nChip];
+ 			if (!chip) continue;
+
+		   	if (!psastroFindChipInXrange (fpa, nChip, star->FP->x, -20000.)) {
+		   	  continue;
+		   	}
+		   	if (!psastroFindChipInYrange (fpa, nChip, star->FP->x, -20000.)) {
+		   	  continue;
+		   	}
+
+		        // FPA coordinates of intersections with chip edges 
+		        double yFPAs, yFPAe;
+		        psastroFindChipYedges (&yFPAs, &yFPAe, fpa, nChip);
+
+  		        xFPA0 = star->FP->x;
+		        yFPA0 = yFPAs;
+                        glintLength-= (yFPAs-star->FP->y);
+                    }
+
+                    xFPA1 = xFPA0 - glintLength*sin(glintAngle);
+                    yFPA1 = yFPA0 + glintLength*cos(glintAngle);
+		}
+
+	        x_glint->data.F32[0] = xFPA0;
+	        y_glint->data.F32[0] = yFPA0;
+	        x_glint->data.F32[1] = xFPA1;
+	        y_glint->data.F32[1] = yFPA1;
+
+                //we need to loop over each corner to select the chips that can have the glint on it
+	        for (int glint_point = 0; glint_point < 2; glint_point++) {
+		    for (int nChip = 0; nChip < fpa->chips->n; nChip++) {
+
+ 			pmChip *chip = fpa->chips->data[nChip];
+ 			if (!chip) continue;
+
+		   	if (!psastroFindChipInXrange (fpa, nChip, x_glint->data.F32[glint_point], y_glint->data.F32[glint_point])) {
+		   	  continue;
+		   	}
+
+		        // FPA coordinates of intersections with chip edges 
+		        double yFPAs, yFPAe;
+		        psastroFindChipYedges (&yFPAs, &yFPAe, fpa, nChip);
+
+		        if (yFPAs > yFPAe) PS_SWAP (yFPAs, yFPAe);
+			if (yFPA0 > yFPA1) {
+			    PS_SWAP (xFPA0, xFPA1);
+			    PS_SWAP (yFPA0, yFPA1);
+                        } 
+
+		        // does this glint cross this chip?
+		        if (yFPA0 > yFPAe) continue;
+		        if (yFPA1 < yFPAs) continue;
+
+                        //find the y-coord positions for this chip
+                        double ycFPA0, ycFPA1; 
+		        ycFPA0 = PS_MAX (yFPA0, yFPAs);
+    		        ycFPA1 = PS_MIN (yFPA1, yFPAe);
+
+                        //now calculate the proper x-coord positions given the angle, for this chip
+                        double xcFPA0, xcFPA1, angle; 
+		  	angle = atan2(xFPA1 - xFPA0,yFPA1 - yFPA0);
+                        xcFPA0 = xFPA0 + (ycFPA0-yFPA0)*tan(angle);
+                        xcFPA1 = xFPA0 + (ycFPA1-yFPA0)*tan(angle);
+			
+
+		  	double xChip0, yChip0, xChip1, yChip1, chip_angle, glint_length, xChip2,yChip2;
+		  	psastroFPAtoChip (&xChip0, &yChip0, fpa, nChip, xcFPA0, ycFPA0);
+		  	psastroFPAtoChip (&xChip1, &yChip1, fpa, nChip, xcFPA1, ycFPA1);
+		  	psastroFPAtoChip (&xChip2, &yChip2, fpa, nChip, star->FP->x,star->FP->y);
+
+		  	chip_angle = atan2(yChip1 - yChip0, xChip1 - xChip0);
+ 		  	glint_length = sqrt(pow(yChip1 - yChip0,2) + pow(xChip1 - xChip0,2));
+
+		   	// select the 0th readout of the 0th cell for this chip
+		   	if (!chip->cells) continue;
+		   	if (!chip->cells->n) continue;
+		   	pmCell *glintCell = chip->cells->data[0];
+		   	if (!glintCell) continue;
+		   	if (!glintCell->readouts) continue;
+		   	if (!glintCell->readouts->n) continue;
+		   	pmReadout *glintReadout = glintCell->readouts->data[0];
+		   	if (!glintReadout) continue;
+		   	
+		   	// save the glints on the readout->analysis metadata, creating if needed
+		   	psArray *glints = psMetadataLookupPtr (&status, glintReadout->analysis, "PSASTRO.GLINTS");
+		   	if (glints == NULL) { 
+		   	  glints = psArrayAllocEmpty (100);
+		   	  if (!psMetadataAdd (glintReadout->analysis, PS_LIST_TAIL, "PSASTRO.GLINTS", PS_DATA_ARRAY, "astrometry matches", glints)) {
+		   	    psWarning("failure to add glints to readout");
+		   	    psFree (glints);
+		   	    continue;
+		   	}
+		   	  psFree (glints);
+		   	}
+		   	
+		   	fprintf (stderr, "glint %s : %d %f,%f to %f,%f (%f %f %f)\n", glintType, nChip, xChip0, yChip0, xChip1, yChip1, glint_length, glintWidth, chip_angle);
+		   	psVector *glint = psVectorAlloc(5,PS_TYPE_F32);
+		   	glint->data.F32[0] = xChip0;
+		   	glint->data.F32[1] = yChip0;
+		   	glint->data.F32[2] = glint_length;
+		   	glint->data.F32[3] = glintWidth;
+		   	glint->data.F32[4] = chip_angle;
+
+		   	psArrayAdd (glints, 100, glint);
+
+		   	psFree (glint);
+	  	    }
 		}
 	    }
 
-	    // depending on the glint type, we need to find either the chips in the row or in the column.
 	    if (!strcasecmp(glintType, "LEFT") || !strcasecmp(glintType, "RIGHT")) {
-		for (int nChip = 0; nChip < fpa->chips->n; nChip++) {
-
-		    pmChip *chip = fpa->chips->data[nChip];
-		    if (!chip) continue;
-
-		    if (!psastroFindChipInYrange (fpa, nChip, star->FP->x, star->FP->y)) {
-			continue;
-		    }
-
-		    // find the coordinate of the end point
-		    double yFPA  = star->FP->y;
-		    double xFPA0 = star->FP->x;
-		    double xFPA1;
-		    if (!strcasecmp(glintType, "RIGHT")) {
-			xFPA1 = xFPA0 - glintLength;
-		    } else {
-			xFPA1 = xFPA0 + glintLength;
-		    }
-
-		    // FPA coordinates of intersections with chip edges 
-		    double xFPAs, xFPAe;
-		    psastroFindChipXedges (&xFPAs, &xFPAe, fpa, nChip);
-
-		    if (xFPA0 > xFPA1) PS_SWAP (xFPA0, xFPA1);
-		    if (xFPAs > xFPAe) PS_SWAP (xFPAs, xFPAe);
-
-		    // does this glint cross this chip?
-		    if (xFPA0 > xFPAe) continue;
-		    if (xFPA1 < xFPAs) continue;
-
-		    xFPA0 = PS_MAX (xFPA0, xFPAs);
-		    xFPA1 = PS_MIN (xFPA1, xFPAe);
-
-		    double xChip0, yChip0, xChip1, yChip1;
-		    psastroFPAtoChip (&xChip0, &yChip0, fpa, nChip, xFPA0, yFPA);
-		    psastroFPAtoChip (&xChip1, &yChip1, fpa, nChip, xFPA1, yFPA);
-
-		    // we now have the location of the glint ends or edge-intersections in chip coordinates
-		    // double-check if this chip overlaps this glint
-		    if (xChip0 > xChip1) PS_SWAP (xChip0, xChip1);
-		    if (yChip0 > yChip1) PS_SWAP (yChip0, yChip1);
-
-		    // bounds of this chip
-		    psRegion *region = pmChipPixels (chip);
-		    if (xChip1 < region->x0) continue;
-		    if (xChip0 > region->x1) continue;
-
-		    // this glint touches this chip. calculate the start and end
-		    // coordinates on this chip
-		    double yChipS = PS_MAX (yChip0 - 0.5*glintWidth, region->y0);
-		    double yChipE = PS_MIN (yChip1 + 0.5*glintWidth, region->y1);
-		    double xChipS = PS_MAX (xChip0, region->x0);
-		    double xChipE = PS_MIN (xChip1, region->x1);
-
-		    // select the 0th readout of the 0th cell for this chip
-		    if (!chip->cells) continue;
-		    if (!chip->cells->n) continue;
-		    pmCell *glintCell = chip->cells->data[0];
-		    if (!glintCell) continue;
-		    if (!glintCell->readouts) continue;
-		    if (!glintCell->readouts->n) continue;
-		    pmReadout *glintReadout = glintCell->readouts->data[0];
-		    if (!glintReadout) continue;
-
-		    // save the glints on the readout->analysis metadata, creating if needed
-		    psArray *glints = psMetadataLookupPtr (&status, glintReadout->analysis, "PSASTRO.GLINTS");
-		    if (glints == NULL) { 
-			glints = psArrayAllocEmpty (100);
-			if (!psMetadataAdd (glintReadout->analysis, PS_LIST_TAIL, "PSASTRO.GLINTS", PS_DATA_ARRAY, "astrometry matches", glints)) {
-			    psWarning("failure to add glints to readout");
-			    psFree (glints);
-			    continue;
-			}
-			psFree (glints);
-		    }
-
-		    fprintf (stderr, "glint %s : %f,%f to %f,%f (%f,%f to %f,%f)\n", glintType, xChip0, yChip0, xChip1, yChip1, xChipS, yChipS, xChipE, yChipE);
-		    psRegion *glint = psRegionAlloc(xChipS, xChipE, yChipS, yChipE);
-		    psArrayAdd (glints, 100, glint);
-		    psFree (glint);
-		    psFree (region);
+		// We want to find the coordinates of the glint end points. However, the glint is straight off the pixel focal plane and has an angle only on the focal plane. So, first find the edge
+		double xFPA0 = star->FP->x;
+		double yFPA0 = star->FP->y;
+		double xFPA1;
+		double yFPA1;
+                //angles for TOP and LEFT have been flipped in the fitting
+		if (!strcasecmp(glintType, "RIGHT")) {
+                    //find the edge chip and determine the FPA coords of the edge. Then, grab the edge coord and new glint length
+		    for (int nChip = 0; nChip < fpa->chips->n; nChip++) {
+ 			pmChip *chip = fpa->chips->data[nChip];
+ 			if (!chip) continue;
+
+		   	if (!psastroFindChipInXrange (fpa, nChip, 20000.,star->FP->y)) {
+		   	  continue;
+		   	}
+		   	if (!psastroFindChipInYrange (fpa, nChip, 20000.,star->FP->y)) {
+		   	  continue;
+		   	}
+
+		        // FPA coordinates of intersections with chip edges 
+		        double yFPAs, yFPAe;
+		        psastroFindChipYedges (&yFPAs, &yFPAe, fpa, nChip);
+
+  		        xFPA0 = yFPAe;
+		        yFPA0 = star->FP->y;
+                        glintLength-= (star->FP->x-yFPAe);
+                    }
+
+                    xFPA1 = xFPA0 - glintLength*cos(glintAngle);
+                    yFPA1 = yFPA0 - glintLength*sin(glintAngle);
+
+		} else {
+                    //find the edge chip and determine the FPA coords of the edge. Then, grab the edge coord and new glint length
+		    for (int nChip = 0; nChip < fpa->chips->n; nChip++) {
+ 			pmChip *chip = fpa->chips->data[nChip];
+ 			if (!chip) continue;
+
+		   	if (!psastroFindChipInXrange (fpa, nChip, -20000.,star->FP->y)) {
+		   	  continue;
+		   	}
+		   	if (!psastroFindChipInYrange (fpa, nChip, -20000.,star->FP->y)) {
+		   	  continue;
+		   	}
+
+		        // FPA coordinates of intersections with chip edges 
+		        double yFPAs, yFPAe;
+		        psastroFindChipYedges (&yFPAs, &yFPAe, fpa, nChip);
+
+  		        xFPA0 = yFPAs;
+		        yFPA0 = star->FP->y;
+                        glintLength-= (yFPAs-star->FP->x);
+                    }
+
+                    xFPA1 = xFPA0 + glintLength*cos(glintAngle*-1.);
+                    yFPA1 = yFPA0 + glintLength*sin(glintAngle*-1.);
+		}
+
+	        x_glint->data.F32[0] = xFPA0;
+	        y_glint->data.F32[0] = yFPA0;
+	        x_glint->data.F32[1] = xFPA1;
+	        y_glint->data.F32[1] = yFPA1;
+
+                //we need to loop over each corner to select the chips that can have the glint on it
+	        for (int glint_point = 0; glint_point < 2; glint_point++) {
+		    for (int nChip = 0; nChip < fpa->chips->n; nChip++) {
+
+ 			pmChip *chip = fpa->chips->data[nChip];
+ 			if (!chip) continue;
+
+		   	if (!psastroFindChipInYrange (fpa, nChip, x_glint->data.F32[glint_point], y_glint->data.F32[glint_point])) {
+		   	  continue;
+		   	}
+
+		        // FPA coordinates of intersections with chip edges 
+		        double xFPAs, xFPAe;
+		        psastroFindChipXedges (&xFPAs, &xFPAe, fpa, nChip);
+
+		        if (xFPAs > xFPAe) PS_SWAP (xFPAs, xFPAe);
+			if (xFPA0 > xFPA1) {
+			    PS_SWAP (xFPA0, xFPA1);
+			    PS_SWAP (yFPA0, yFPA1);
+                        } 
+
+		        // does this glint cross this chip?
+		        if (xFPA0 > xFPAe) continue;
+		        if (xFPA1 < xFPAs) continue;
+
+                        //find the x-coord positions for this chip
+                        double xcFPA0, xcFPA1; 
+		        xcFPA0 = PS_MAX (xFPA0, xFPAs);
+		        xcFPA1 = PS_MIN (xFPA1, xFPAe);
+
+                        //now calculate the proper x-coord positions given the angle, for this chip
+                        double ycFPA0, ycFPA1, angle; 
+		  	angle = atan2(xFPA1 - xFPA0,yFPA1 - yFPA0);
+                        ycFPA0 = yFPA0 + (xcFPA0-xFPA0)/tan(angle);
+                        ycFPA1 = yFPA0 + (xcFPA1-xFPA0)/tan(angle);
+
+		  	double xChip0, yChip0, xChip1, yChip1, chip_angle, glint_length,xChip2,yChip2;
+		  	psastroFPAtoChip (&xChip0, &yChip0, fpa, nChip, xcFPA0, ycFPA0);
+		  	psastroFPAtoChip (&xChip1, &yChip1, fpa, nChip, xcFPA1, ycFPA1);
+		  	psastroFPAtoChip (&xChip2, &yChip2, fpa, nChip, star->FP->x,star->FP->y);
+
+		  	chip_angle = atan2(yChip1 - yChip0, xChip1 - xChip0);
+		  	glint_length = sqrt(pow(yChip1 - yChip0,2) + pow(xChip1 - xChip0,2));
+
+		   	// select the 0th readout of the 0th cell for this chip
+		   	if (!chip->cells) continue;
+		   	if (!chip->cells->n) continue;
+		   	pmCell *glintCell = chip->cells->data[0];
+		   	if (!glintCell) continue;
+		   	if (!glintCell->readouts) continue;
+		   	if (!glintCell->readouts->n) continue;
+		   	pmReadout *glintReadout = glintCell->readouts->data[0];
+		   	if (!glintReadout) continue;
+		   	
+		   	// save the glints on the readout->analysis metadata, creating if needed
+		   	psArray *glints = psMetadataLookupPtr (&status, glintReadout->analysis, "PSASTRO.GLINTS");
+		   	if (glints == NULL) { 
+		   	  glints = psArrayAllocEmpty (100);
+		   	  if (!psMetadataAdd (glintReadout->analysis, PS_LIST_TAIL, "PSASTRO.GLINTS", PS_DATA_ARRAY, "astrometry matches", glints)) {
+		   	    psWarning("failure to add glints to readout");
+		   	    psFree (glints);
+		   	    continue;
+		   	}
+		   	  psFree (glints);
+		   	}
+		   	
+		   	fprintf (stderr, "glint %s : %d %f,%f to %f,%f (%f %f %f)\n", glintType, nChip, xChip0, yChip0, xChip1, yChip1, glint_length, glintWidth, chip_angle);
+		   	psVector *glint = psVectorAlloc(5,PS_TYPE_F32);
+		   	glint->data.F32[0] = xChip0;
+		   	glint->data.F32[1] = yChip0;
+		   	glint->data.F32[2] = glint_length;
+		   	glint->data.F32[3] = glintWidth;
+		   	glint->data.F32[4] = chip_angle;
+
+		   	psArrayAdd (glints, 100, glint);
+
+		   	psFree (glint);
+	  	    }
 		}
 	    }
+
+
 	    if (!strcasecmp(glintType, "HSC")) {
 	      // It's inefficient to keep looking these up.
Index: /trunk/psastro/src/psastroMaskUpdates.c
===================================================================
--- /trunk/psastro/src/psastroMaskUpdates.c	(revision 41337)
+++ /trunk/psastro/src/psastroMaskUpdates.c	(revision 41338)
@@ -134,6 +134,7 @@
     
     double REFSTAR_MASK_MAX_MAG            = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_MAX_MAG");
-    double REFSTAR_MASK_SATSTAR_MAG_SLOPE  = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_SATSTAR_MAG_SLOPE");
     double REFSTAR_MASK_SATSTAR_MAG_MAX    = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_SATSTAR_MAG_MAX");
+    double REFSTAR_MASK_SATSTAR_RAD_OFFSET = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_SATSTAR_RAD_OFFSET");
+    double REFSTAR_MASK_SATSTAR_EXP        = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_SATSTAR_EXP");
     double REFSTAR_MASK_SATSTAR_POS_ZERO   = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_SATSTAR_POS_ZERO");
     double REFSTAR_MASK_SATSPIKE_MAG_SLOPE = psMetadataLookupF32 (&status, recipe, "REFSTAR_MASK_SATSPIKE_MAG_SLOPE");
@@ -232,4 +233,14 @@
     double ROTANGLE = PM_RAD_DEG * psMetadataLookupF64 (&status, fpa->concepts, "FPA.ROTANGLE");
     psAssert (status, "ROTANGLE missing");
+    int ROT_PARITY = psMetadataLookupS32(&status, recipe, "PSASTRO.MODEL.ROT.PARITY");
+    if (!status) psAbort ("Can't find recipe option PSASTRO.MODEL.ROT.PARITY");
+
+    //select the astromeric rotation angle from the header
+    psMetadata *header = psMetadataLookupMetadata (&status, fpa->analysis, "PSASTRO.HEADER");
+    if (!header) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO header");
+        return false;
+    }
+    float AST_T0 = PM_RAD_DEG * psMetadataLookupF32(&status, header, "AST_T0");
 
     // de-activate all files except PSASTRO.INPUT.MASK and PSASTRO.OUTPUT.MASK
@@ -327,5 +338,5 @@
 
                     // CIRCLE around the stars (scaled by magnitude)
-                    float radius = REFSTAR_MASK_SATSTAR_MAG_SLOPE * (REFSTAR_MASK_SATSTAR_MAG_MAX - ref->Mag);
+                    float radius = REFSTAR_MASK_SATSTAR_RAD_OFFSET + pow(REFSTAR_MASK_SATSTAR_EXP,(REFSTAR_MASK_SATSTAR_MAG_MAX - ref->Mag));
 
                     // XXX for now, assume cell binning is 1x1 relative to chip
@@ -333,9 +344,8 @@
 
                     for (float theta = 0.0; theta < 2*M_PI; theta += M_PI / 2.0) {
-
-                        float Theta = theta - ROTANGLE - REFSTAR_MASK_SATSTAR_POS_ZERO;
+                        float Theta = theta - (ROT_PARITY * ROTANGLE) - AST_T0 - REFSTAR_MASK_SATSTAR_POS_ZERO;
 
                         // LINE for boundaries of the saturation spikes (scaled by magnitude)
-                        // float MAG_MAX = (theta == 0.0) || (theta == M_PI) ? REFSTAR_MASK_SATSPIKE_MAG_MAX1 : REFSTAR_MASK_SATSPIKE_MAG_MAX2;
+                        //float MAG_MAX = (theta == 0.0) || (theta == M_PI) ? REFSTAR_MASK_SATSPIKE_MAG_MAX1 : REFSTAR_MASK_SATSPIKE_MAG_MAX2;
 
                         float MAG_MAX = REFSTAR_MASK_SATSPIKE_MAG_MAX;
@@ -400,4 +410,5 @@
                         }
                     }
+
                 }
 
@@ -412,4 +423,7 @@
                     for (int i = 0; i < ghosts->n; i++) {
                         psastroGhost *ghost = ghosts->data[i];
+                        //offset the ghost angles with the astrometric rotation angle
+                        ghost->inner.theta -= (ROT_PARITY * ROTANGLE)
+                        ghost->outer.theta -= (ROT_PARITY * ROTANGLE)
                         // XXX bright vs faint ghost bits? (OR with SUSPECT)
                         psastroMaskEllipticalAnnulus (readoutMask->mask, ghostMaskValue, ghost->chip->x, ghost->chip->y, ghost->inner, ghost->outer);
@@ -418,5 +432,5 @@
 
                 // Select the glint mask regions for this readout (loaded in
-                // psastroExtractGlints.c).  These glint regions are defined as rectangular
+                // psastroChooseGlintStars.c).  These glint regions are defined as rectangular
                 // boxes and are generated for each chip based on the position of the bright
                 // stars beyond the edge of the focal plane.  This masking is currently very
@@ -426,6 +440,11 @@
                     // mask the glints on this readout
                     for (int i = 0; i < glints->n; i++) {
-                        psRegion *glint = glints->data[i];
-                        psastroMaskRectangle (readoutMask->mask, glintMaskValue, glint->x0, glint->y0, glint->x1, glint->y1);
+                        psVector *glint = glints->data[i];
+                        //psLogMsg ("psastro", 3, "glint: %f %f %f %f\n", glint->x0, glint->y0, glint->x1, glint->y1);
+                        //psastroMaskRectangle (readoutMask->mask, glintMaskValue, glint->x0, glint->y0, glint->x1, glint->y1);
+
+		        psastroMaskBox (readoutMask->mask, glintMaskValue, glint->data.F32[0], glint->data.F32[1],
+				    glint->data.F32[2], glint->data.F32[3], glint->data.F32[4]);
+
                     }
                 }
