Index: /trunk/Ohana/src/addstar/doc/2mass.txt
===================================================================
--- /trunk/Ohana/src/addstar/doc/2mass.txt	(revision 16966)
+++ /trunk/Ohana/src/addstar/doc/2mass.txt	(revision 16967)
@@ -1,2 +1,69 @@
+
+2MASS PSC fields:
+
+ra, dec : obvious
+
+m       -> mag
+cmsig   -> dM
+msigcom -> dMcal (dMcal^2 + dM^2 = msigcom^2)
+stdap   -> Map 
+snr     : skipped
+
+err_maj -> fhwm_x
+err_min -> fhwm_y
+err_ang -> angle
+
+the following 2MASS values are used to set bits in the phot_flags field:
+
+ph_qual:
+
+X = 0x0000  There is a detection at this location, but no valid brightness estimate can be extracted using any algorithm. rd_flg="9" and default magnitude is null.
+U = 0x0001  Upper limit on magnitude. Source is not detected in this band (rd_flg="0"), or it is detected, but not resolved in a consistent fashion with other bands (rd_flg="6"). A value of ph_qual="U" does not necessarily mean that there is no flux detected in this band at the location. Whether or not flux has been detected can be determined from the value of rd_flg. When rd_flg="0", no flux has been detected. When rd_flg="6", flux has been detected at the location where the images were not deblended consistently in all three bands (JHKs).
+F = 0x0002  This category includes rd_flg="1" or rd_flg="3" sources where a reliable estimate of the photometric error, [jhk]_cmsig, could not be determined. The uncertainties reported for these sources in [jhk]_cmsig and [jhk]_msigcom are flags and have numeric values >8.0.
+E = 0x0003  This category includes detections where the goodness-of-fit quality of the profile-fit photometry was very poor (rd_flg=2 and [jhk]psf_chi>10.0), or detections where psf fit photometry did not converge and an aperture magnitude is reported (rd_flg=4), or detections where the number of frames was too small in relation to the number of frames in which a detection was geometrically possible (rd_flg="1" or rd_flg="2").
+A = 0x0004  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>10 AND [jhk]_cmsig<0.10857.
+B = 0x0005  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>7 AND [jhk]_cmsig<0.15510.
+C = 0x0006  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>5 AND [jhk]_cmsig<0.21714.
+D = 0x0007  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with no [jhk]_snr or [jhk]_cmsig requirement.
+
+rd_flag:
+
+0 = 0x0000  Source is not detected in this band. The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20".
+1 = 0x0010  The default magnitude is derived from aperture photometry measurements on the 51 ms "Read_1" exposures. The aperture radius is 4", with the sky background measured in an annulus with an inner radius of 14" and an outer radius of 20". Used for sources that saturate one or more of the 1.3s "Read_2" exposures, but are not saturated on at least one of the 51 ms "Read_1" frames.
+2 = 0x0020  The default magnitude is derived from a profile-fitting measurement made on the 1.3 sec "Read_2" exposures. The profile-fit magnitudes are normalized to curve-of-growth-corrected aperture magnitudes. This is the most common type in the PSC, and is used for sources that have no saturated pixels in any of the 1.3 sec exposures.
+3 = 0x0030  The default magnitude is derived from a 1-d radial profile fitting measurement made on the 51 ms "Read_1" exposures. Used for very bright sources that saturate all of the 51 ms "Read 1" exposures.
+4 = 0x0040  The default magnitude is derived from curve-of-growth-corrected 4" radius aperture photometry measurements on the 1.3 s "Read_2" exposures. This is used for sources that are not saturated in any of the Read_2 frames, but where the profile-fitting measurements fail to converge to a solution. These magnitudes are the same as the standard aperture magnitudes (j_m_stdap, h_m_stdap, k_m_stdap), but when they are the default magnitudes, it generally implies that they are low quality measurements.
+6 = 0x0050  The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for pairs of sources which are detected and resolved in another band, but are detected and not resolved in this band. This differs from a rd_flg="0" because in this case there is a detection of the source in this band, but it is not consistently resolved across all bands.
+9 = 0x0060  The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for sources that were nominally detected in this band, but which could not have a useful brightness measurement from either profile fitting or aperture photometry. This often occurs in highly confused regions, or very near Tile edges where a significant fraction of the measurement aperture of sky annulus falls off the focal plane.
+
+cc_flag:
+
+p = 0x0000  Persistence. Source may be contaminated by a latent image left by a nearby bright star.
+c = 0x0100  Photometric Confusion. Source photometry is biased by a nearby star that has contaminated the background estimation. This is very common in high source density regions.
+d = 0x0200  Diffraction spike confusion. Source may be contaminated by a diffraction spike from a nearby star.
+s = 0x0300  Electronic stripe. Source measurement may be contaminated by a stripe from a nearby bright star.
+b = 0x0400  Bandmerge confusion. In the process of merging detections in the different bands for this source, there was more than one possible match between the different band components. This occurs in regions of very high source density, or when multiple sources were split in one band but not another.
+0 = 0x0500  Source is unaffected by known artifacts, or is not detected in the band.
+
+bl_flag: (0,1 : not blended)
+
+anything except 0 or 1 -> 0x0008
+
+gal_flag: (0,1 : not extended)
+
+anything except 0 or 1 -> 0x0080
+
+mp_flag: 
+
+anything except 0 or 1 -> 0x0800
+
+dup_flag:
+
+anything except 0 or 1 -> 0x1000
+
+use_flag:
+
+anything except 0 or 1 -> 0x2000
+
 
 2MASS PSC fields:
Index: /trunk/Ohana/src/addstar/doc/pmm.txt
===================================================================
--- /trunk/Ohana/src/addstar/doc/pmm.txt	(revision 16967)
+++ /trunk/Ohana/src/addstar/doc/pmm.txt	(revision 16967)
@@ -0,0 +1,164 @@
+	
+--
+
+As usual, I have totally forgotten the previous discussion with respect
+to rummaging around in the USNO-B database, but that is just normal for me.
+I think that there are two ways to proceed, and both stem from the exercise
+of attaching WCS headers to the individual PMM camera footprints that we
+did for Google.  I should note that Andy Connolly swears that Google
+will soon serve the 10 TPixels that we gave them, but I am not going
+to hold my breath.
+
+The PMM scanned each Schmidt plate as 588 individual footprints.  The
+scale is about 0.9 arcsec/pixel (2X optics, 6.8 micron camera pixels,
+67.5 arcsec/mm for Schmidt plates).  The readout format was 1394x1037
+(not even an integer multiple of 4!) of which we claimed that the
+useful area was from (67:1378 and 3:1035).  There are no dead pixels
+in either camera, the bias frames are zero, and we have the flats.
+Each image covers 17.8 by 14.0 millimeters but the step-and-stop
+was done on a 16 by 12 millimeter grid giving about 2 millimeters
+(about 2.2 arcminutes) overlap between footprints.
+
+I wrote software that extracted each footprint from each plate,
+made a short, paired list of (X,Y) and (RA,Dec), fed this to imwcs(),
+and saved the image+WCS as separate FITS files.  We scanned 12070
+Schmidt plates of which we saved the pixels for all but the 897 UJ
+(3 minute exposure blue plates from POSS-II).  So far as I know,
+We have 588 times 11,173 FITS files spinning on our disks somewhere.
+These files have been given to Google and SDSC, and they are available
+to anybody else that could seriously use them.  We do not have the
+bandwidth to make soft copies, so we must negotiate for reasonable
+ways to copy the 10 TPixels involved.  We have yet to play the same
+game with the Yale Astrograph plates, but will eventually add these
+3 TPixels of FITS images to the archive.
+
+So Option 1 would be to just hand you guys the FITS WCS images and let you
+play.  You can figure out whether IPP or SExtractor works on photographic
+pixels, how to do photometry, how to handle the overlap zones, and
+all of the other unpleasant things I had to do for PMM for which I
+have been so roundly criticized.
+
+Option 2 would require a small hack in the pixel processing pipeline
+that would save the PMM detection lists and the WCS transformations
+in some reasonable way.  This would save you the work and bandwidth
+of dealing with the images, but would then rely on the PMM's image
+processing software for computation of the detection parameters.
+
+Depending on which, if either, seems to be a reasonable way to
+proceed, I think that we have enough bandwidth (or could use DVDs)
+so send a small sample of what the data would look like.  Did I send
+you the flat ASCII file of the nominal pointings and epochs (12070 lines)?
+
+Let me know.  I will be in Flagstaff for the next couple of weeks,
+but then my travel schedule goes nuts.
+
+-Dave
+
+--
+
+I had thought that I passed you this file before.  The files
+I would be passing you would start with the 7 character root
+in the third column, SSnnnnn where SS is a unique survey
+identifier, and nnnnn is a unique plate number from that
+observatory.  You can safely ignore the first 2 columns.
+The plate is the third, followed by DDMonYYYY HH:MM
+HHMMSS sDDMMSS Emulsion Filter Exposure H:MM.  It is
+believed that the UT is for the start of the exposure,
+but it might be for the middle.  Many things are lost in
+the cobwebs of antiquity.  I can give you a table of
+nominal wavelengths for various Emulsion+Filter combinations.
+The final column is the hour angle of mid-exposure.
+
+-Dave
+
+example:
+
+0         1         2         3         4         5         6         7         8         9         0         
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+so0001 /nofs/leo1/sel/jb0/se100/so0001a.inf PO00570 23AUG1952 09:22 194300 +900000 103AO NONE    10 0:13E
+so0002 /nofs/leo1/sel/jb0/se100/so0002a.inf PO00567 22AUG1952 08:21 223100 +900000 103AO NONE    10 1:14E
+so0003 /nofs/leo1/sel/jb0/se100/so0003a.inf PO00568 22AUG1952 10:53 000624 +843140 103AO NONE    10 1:10W
+so0004 /nofs/leo1/sel/jb0/se100/so0004a.inf PO01277 23NOV1954 06:45 025031 +842224 103AO NONE    12 0:29W
+so0005 /nofs/leo1/sel/jb0/se100/so0005a.inf PO01328 29JAN1955 06:42 052420 +840404 103AO NONE    12 2:19W
+
+
+emulsions:
+098
+098-0
+103AD
+103AE
+103AO
+IIIAF
+IIIAJ
+IVN
+
+filters:
+#12
+AMB2
+AMB3
+AMB4
+AMB5
+AMB6
+AMB7
+AMB8
+GG358
+GG385
+GG395
+MULTI
+NONE
+OG590
+RED
+RED66
+RED67
+RED68
+RED69
+RED70
+RED71
+RED73
+RG2444
+RG600
+RG610
+RG630
+RG715
+RG9
+RP2444
+WR88A
+YEL3
+YEL8
+
+combinations:
+098 RED 
+098-0 RED70 
+098-0 RG630 
+103AD MULTI 
+103AD YEL3 
+103AD YEL8 
+103AE #12 
+103AE AMB2 
+103AE AMB3 
+103AE AMB4 
+103AE AMB5 
+103AE AMB6 
+103AE AMB7 
+103AE AMB8 
+103AE NONE 
+103AE RED66 
+103AE RED67 
+103AE RED68 
+103AE RED69 
+103AE RED70 
+103AE RED71 
+103AE RED73 
+103AE RG2444 
+103AE RP2444 
+103AO NONE 
+IIIAF OG590 
+IIIAF RG600 
+IIIAF RG610 
+IIIAF RG630 
+IIIAJ GG358 
+IIIAJ GG385 
+IIIAJ GG395 
+IVN RG715 
+IVN RG9 
+IVN WR88A 
Index: /trunk/Ohana/src/addstar/src/LoadDataPMM.c
===================================================================
--- /trunk/Ohana/src/addstar/src/LoadDataPMM.c	(revision 16967)
+++ /trunk/Ohana/src/addstar/src/LoadDataPMM.c	(revision 16967)
@@ -0,0 +1,445 @@
+# include "addstar.h"
+
+/* .asc files look like:
+0         1         2         3
+0123456789012345678901234567890123456789
+187.498117^  2.659253^21.06$
+187.498672^  2.713833^17.80$
+(^ = tab char)
+*/
+
+# define NLINE_ASC 10000
+# define NBYTE_ASC_TABLE 28
+# define RA_INDEX_ASC 0
+# define DEC_INDEX_ASC 11
+# define MAG_INDEX_ASC 22
+
+int LoadDataPMM (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars) {
+
+  char *name, *buffer;
+  int i, fd, Nbyte, Nline, Nvalid, code;
+  double ra, dec, mag, airmass, az, ZeroPoint, ZeroPt;
+  unsigned int Ninstars, NINSTARS;
+  Stars *inStars;
+  PhotCode *photcode;
+  gzFile gz;
+
+  double minR0, minR1, maxR0, maxR1, minD, maxD;
+
+  if (images[0] == NULL) {
+    Nvalid = 0;
+    NVALID = 1;
+    ALLOCATE (images[0], Image, NVALID);
+  } else {
+    Nvalid = *nvalid;
+    NVALID = Nvalid ++;
+    REALLOCATE (images[0], Image, NVALID);
+  }    
+
+  // find image rootname
+  name = filebasename (file);
+
+  // there is only one PMM image per file
+  if (VERBOSE) fprintf (stderr, "reading data for %s\n", file);
+
+  ZeroPt = GetZeroPoint();
+
+  // need to get the metadata from the PMM_CCD_TABLE
+  photcode = LoadMetadataPMM (name, &images[0][Nvalid]);
+  code = photcode[0].code;
+  ZeroPoint = 0.001*photcode[0].C;
+  // XXX NOTE : as of 2008.02.27, the zero point is still carried internally in millimags
+
+  ALLOCATE (buffer, char, NLINE_ASC*NBYTE_ASC_TABLE);
+
+  // use the following to get alt, az:
+  // altaz (&alt, &az, 15.0*images[N].sidtime - images[N].coords.crval1, images[N].coords.crval2, Latitude);
+  // these two can be calculated from HA and LATITUDE (need a table of observatory LAT)
+  airmass = 1.0;
+  az = 0.0;
+
+  NINSTARS = 10000;
+  ALLOCATE (inStars, Stars, NINSTARS);
+
+  minR0 = minR1 = 360.0;
+  maxR0 = maxR1 =   0.0;
+  minD = +90.0;
+  maxD = -90.0;
+
+  fd = fileno (f);
+  gz = gzdopen (dup(fd), "rb");
+
+  // read in a big chunk at a time, parse the lines assuming fixes line sizes and fields
+  Ninstars = 0;
+  while (1) {
+
+    Nbyte = gzread (gz, buffer, NLINE_ASC*NBYTE_ASC_TABLE);
+    if (Nbyte == 0) break;
+
+    assert (Nbyte % NBYTE_ASC_TABLE == 0);
+    Nline = Nbyte / NBYTE_ASC_TABLE;
+
+    for (i = 0; i < Nline; i++) {
+      // fscanf (f, "%lf %lf %lf", &ra, &dec, &mag) != EOF) {
+
+      dparse (&ra,  1, &buffer[i*NBYTE_ASC_TABLE]);
+      dparse (&dec, 2, &buffer[i*NBYTE_ASC_TABLE]);
+      dparse (&mag, 3, &buffer[i*NBYTE_ASC_TABLE]);
+
+      memset (&inStars[Ninstars], 0, sizeof(Stars));
+
+      if (ra > 180) {
+	minR1 = MIN(minR1, ra);
+	maxR1 = MAX(maxR1, ra);
+      } else {
+	minR0 = MIN(minR0, ra);
+	maxR0 = MAX(maxR0, ra);
+      }
+      minD = MIN(minD, dec);
+      maxD = MAX(maxD, dec);
+
+      inStars[Ninstars].M       = mag - ZeroPoint + ZeroPt;
+      inStars[Ninstars].R       = ra;
+      inStars[Ninstars].D       = dec;
+      inStars[Ninstars].t       = images[0][0].tzero;
+      inStars[Ninstars].dt      = images[0][0].exptime;
+      inStars[Ninstars].code    = code;
+      inStars[Ninstars].airmass = airmass;
+      inStars[Ninstars].az      = az;
+      inStars[Ninstars].found   = -1; // found starts at -1 == not yet found
+      Ninstars++;
+      CHECK_REALLOCATE (inStars, Stars, NINSTARS, Ninstars, 10000);
+    }
+  }
+
+  fprintf (stderr, "ra ranges: %f - %f, %f - %f; dec ranges: %f - %f\n", minR0, maxR0, minR1, maxR1, minD, maxD);
+
+  images[0][0].nstar = Ninstars;
+  images[0][0].imageID = 0;
+
+  *stars = MergeStars (*stars, Nstars, inStars, Ninstars);
+
+  free (inStars);
+  free (name);
+  *nvalid = Nvalid + 1;
+
+  gzclose (gz);
+  free (buffer);
+  return (TRUE);
+}
+
+# define NBYTE_PMM_TABLE 106
+# define FILE_ID_INDEX  44
+# define DATE_INDEX     52
+# define TIME_INDEX     62
+# define RA_INDEX       68
+# define DEC_INDEX      75
+# define EMULSION_INDEX 83
+# define FILTER_INDEX   89
+# define EXPTIME_INDEX  89
+
+// these are a guess...
+# define PLATE_NX 26500
+# define PLATE_NY 26500
+PhotCode *LoadMetadataPMM (char *datafile, Image *image) {
+
+  PhotCode *photcode;
+  char fileID[8], date[10], timestr[6], RA[7], DEC[8], emulsion[6], filter[7], EXPTIME[4];
+  char line[NBYTE_PMM_TABLE+1];
+  FILE *f;
+
+  if (!PMM_CCD_TABLE) abort ();
+
+  strncpy (fileID, datafile, 7);
+  fileID[7] = 0;
+
+  f = fopen (PMM_CCD_TABLE, "r");
+  if (f == NULL) {
+    fprintf (stderr, "unable to open PMM table: %s\n", PMM_CCD_TABLE);
+    exit (2);
+  }
+  
+  while (fread (line, 1, NBYTE_PMM_TABLE, f) == NBYTE_PMM_TABLE) {
+    line[NBYTE_PMM_TABLE] = 0;
+    
+    if (strncmp (fileID, &line[FILE_ID_INDEX], 7)) continue;
+    
+    strncpy (date, &line[DATE_INDEX], 9);
+    date[9] = 0;
+
+    strncpy (timestr, &line[TIME_INDEX], 5);
+    timestr[5] = 0;
+
+    strncpy (RA, &line[RA_INDEX], 6);
+    RA[6] = 0;
+
+    strncpy (DEC, &line[DEC_INDEX], 7);
+    DEC[7] = 0;
+
+    strncpy (emulsion, &line[EMULSION_INDEX], 5);
+    emulsion[5] = 0;
+
+    strncpy (filter, &line[FILTER_INDEX], 6);
+    filter[6] = 0;
+
+    strncpy (EXPTIME, &line[EXPTIME_INDEX], 3);
+    EXPTIME[3] = 0;
+
+    image[0].tzero   = pmm_date_to_sec (date, timestr);
+    image[0].exptime = atof(EXPTIME)*60.0;
+
+    photcode = pmm_get_photcode (emulsion, filter);
+    image[0].photcode = photcode[0].code;
+
+    // XXX for now, we define a totally fake coordinate system centered on the plate center
+    strcpy (image[0].coords.ctype, "RA---TAN");
+    
+    image[0].coords.crval1  = pmm_get_ra (RA);
+    image[0].coords.crval2  = pmm_get_dec (DEC);
+
+    coords_precess (&image[0].coords.crval1, &image[0].coords.crval2, 1950.0, 2000.0);
+
+    image[0].coords.crpix1 = 0.5*PLATE_NX;
+    image[0].coords.crpix2 = 0.5*PLATE_NY;
+    image[0].coords.cdelt1 = image[0].coords.cdelt2 = 0.9 / 3600.0;
+
+    image[0].coords.pc1_1 = 0.0;
+    image[0].coords.pc1_2 = 1.0;
+    image[0].coords.pc2_1 = 1.0;
+    image[0].coords.pc2_2 = 0.0;
+
+    image[0].coords.Npolyterms = 0;
+    memset (image[0].coords.polyterms, 0, 2*7*sizeof(float));
+
+    image[0].NX = PLATE_NX;
+    image[0].NY = PLATE_NY;
+
+    image[0].cerror = 0.0;
+ 
+    image[0].apmifit = 0.0;
+    image[0].dapmifit = 0.0;
+    image[0].detection_limit = 0.0; 
+    image[0].saturation_limit = 0.0;
+    image[0].fwhm_x = 0.0;
+    image[0].fwhm_y = 0.0;
+
+    // XXX need to determine long & lat for observatories
+    // jd = ohana_sec_to_jd (image[0].tzero);
+    // image[0].sidtime  = ohana_lst (jd, Longitude);
+    // image[0].latitude = Latitude;
+    // altaz (&alt, &az, 15.0*image[0].sidtime - image[0].coords.crval1, image[0].coords.crval2, Latitude);
+
+    image[0].trate = 0.0;
+    image[0].secz = 1.0;
+    image[0].ccdnum = 0;
+
+    // secz is in units milli-airmass
+    image[0].Mcal = 0.0;
+    image[0].Xm   = NAN_S_SHORT;
+    image[0].code = 0;
+
+    image[0].nstar = 0;
+  
+    image[0].imageID  = 0;
+    image[0].externID = 0;
+    image[0].sourceID = 0;
+
+    // save the filename
+    snprintf (image[0].name, 64, "%s", datafile);
+    return photcode;
+  }
+  fprintf (stderr, "failed to match image!\n");
+  abort ();
+}
+
+/* emulsion / filter combinations:
+   098 RED 
+   098-0 RED70 
+   098-0 RG630 
+   103AD MULTI 
+   103AD YEL3 
+   103AD YEL8 
+   103AE #12 
+   103AE AMB2 
+   103AE AMB3 
+   103AE AMB4 
+   103AE AMB5 
+   103AE AMB6 
+   103AE AMB7 
+   103AE AMB8 
+   103AE NONE 
+   103AE RED66 
+   103AE RED67 
+   103AE RED68 
+   103AE RED69 
+   103AE RED70 
+   103AE RED71 
+   103AE RED73 
+   103AE RG2444 
+   103AE RP2444 
+   103AO NONE 
+   IIIAF OG590 
+   IIIAF RG600 
+   IIIAF RG610 
+   IIIAF RG630 
+   IIIAJ GG358 
+IIIAJ GG385 
+IIIAJ GG395 
+IVN RG715 
+IVN RG9 
+IVN WR88A 
+*/
+
+// date in format DDMonYYYY
+// time in format HH:MM
+time_t pmm_date_to_sec (char *date, char *time) {
+  
+  time_t second;
+  double jd;
+  struct tm now;
+  char *p1, *p2;
+  
+  bzero (&now, sizeof(now));
+
+  p1 = date;
+  now.tm_mday = strtod (p1, &p2);
+  assert (p2 == p1 + 2);
+  
+  // month runs from 0 - 11
+  p1 = date + 2;
+  if (!strncasecmp (p1, "JAN", 3)) { now.tm_mon =  0; goto got_month; }
+  if (!strncasecmp (p1, "FEB", 3)) { now.tm_mon =  1; goto got_month; }
+  if (!strncasecmp (p1, "MAR", 3)) { now.tm_mon =  2; goto got_month; }
+  if (!strncasecmp (p1, "APR", 3)) { now.tm_mon =  3; goto got_month; }
+  if (!strncasecmp (p1, "MAY", 3)) { now.tm_mon =  4; goto got_month; }
+  if (!strncasecmp (p1, "JUN", 3)) { now.tm_mon =  5; goto got_month; }
+  if (!strncasecmp (p1, "JUL", 3)) { now.tm_mon =  6; goto got_month; }
+  if (!strncasecmp (p1, "AUG", 3)) { now.tm_mon =  7; goto got_month; }
+  if (!strncasecmp (p1, "SEP", 3)) { now.tm_mon =  8; goto got_month; }
+  if (!strncasecmp (p1, "OCT", 3)) { now.tm_mon =  9; goto got_month; }
+  if (!strncasecmp (p1, "NOV", 3)) { now.tm_mon = 10; goto got_month; }
+  if (!strncasecmp (p1, "DEC", 3)) { now.tm_mon = 11; goto got_month; }
+  fprintf (stderr, "error interpretting month: %s\n", date);
+  abort ();
+
+got_month:
+  p1 = date + 5;
+  now.tm_year = strtod (p1, &p2);
+  assert (p2 == p1 + 4);
+
+  p1 = time;
+  now.tm_hour = strtod (p1, &p2);
+  assert (p2 == p1 + 2);
+
+  p1 = time + 3;
+  now.tm_min = strtod (p1, &p2);
+  assert (p2 == p1 + 2);
+
+  jd = now.tm_mday - 32075 + (int)(1461*(now.tm_year + 4800 + (int)(((now.tm_mon+1)-14)/12))/4)
+    + (int)(367*((now.tm_mon+1) - 2 - (int)(((now.tm_mon+1) - 14)/12)*12)/12)
+    - (int)(3*(int)((1900 + now.tm_year + 4900 + (int)(((now.tm_mon+1) - 14)/12))/100)/4) - 0.5;
+  
+  second = (jd - 2440587.5)*86400 + 3600.0*now.tm_hour + now.tm_min*60.0 + now.tm_sec;
+
+  return (second);
+}
+
+// RA in format HHMMSS
+double pmm_get_ra (char *RA) {
+  
+  char tmp[3];
+  double h, m, s, ra;
+  
+  strncpy (tmp, &RA[0], 2);
+  tmp[2] = 0;
+  h = atof (tmp);
+
+  strncpy (tmp, &RA[2], 2);
+  tmp[2] = 0;
+  m = atof (tmp);
+
+  strncpy (tmp, &RA[4], 2);
+  tmp[2] = 0;
+  s = atof (tmp);
+
+  ra = 15.0 * (h + m / 60.0 + s / 3600.0);
+  return (ra);
+}
+
+// DEC in format sDDMMSS
+double pmm_get_dec (char *DEC) {
+
+  char tmp[3];
+  double d, m, s, dec;
+
+  strncpy (tmp, &DEC[1], 2);
+  tmp[2] = 0;
+  d = atof (tmp);
+
+  strncpy (tmp, &DEC[3], 2);
+  tmp[2] = 0;
+  m = atof (tmp);
+
+  strncpy (tmp, &DEC[5], 2);
+  tmp[2] = 0;
+  s = atof (tmp);
+
+  dec = d + m / 60.0 + s / 3600.0;
+
+  if (tmp[0] == '-') dec *= -1.0;
+  return (dec);
+}
+
+PhotCode *pmm_get_photcode (char *emulsion, char *filter) {
+
+  PhotCode *photcode;
+  char codename[32];
+
+  /* emulsion / filter combinations */
+  if (!strcmp(emulsion, "098  ") && !strcmp(filter, "RED   ")) { strcpy (codename, "USNO.098.RED");      goto got_photcode; }
+  if (!strcmp(emulsion, "098-0") && !strcmp(filter, "RED70 ")) { strcpy (codename, "USNO.098-0.RED70");  goto got_photcode; }
+  if (!strcmp(emulsion, "098-0") && !strcmp(filter, "RG630 ")) { strcpy (codename, "USNO.098-0.RG630");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AD") && !strcmp(filter, "MULTI ")) { strcpy (codename, "USNO.103AD.MULTI");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AD") && !strcmp(filter, "YEL3  ")) { strcpy (codename, "USNO.103AD.YEL3");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AD") && !strcmp(filter, "YEL8  ")) { strcpy (codename, "USNO.103AD.YEL8");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "#12   ")) { strcpy (codename, "USNO.103AE.#12");    goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB2  ")) { strcpy (codename, "USNO.103AE.AMB2");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB3  ")) { strcpy (codename, "USNO.103AE.AMB3");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB4  ")) { strcpy (codename, "USNO.103AE.AMB4");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB5  ")) { strcpy (codename, "USNO.103AE.AMB5");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB6  ")) { strcpy (codename, "USNO.103AE.AMB6");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB7  ")) { strcpy (codename, "USNO.103AE.AMB7");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB8  ")) { strcpy (codename, "USNO.103AE.AMB8");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "NONE  ")) { strcpy (codename, "USNO.103AE.NONE");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED66 ")) { strcpy (codename, "USNO.103AE.RED66");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED67 ")) { strcpy (codename, "USNO.103AE.RED67");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED68 ")) { strcpy (codename, "USNO.103AE.RED68");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED69 ")) { strcpy (codename, "USNO.103AE.RED69");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED70 ")) { strcpy (codename, "USNO.103AE.RED70");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED71 ")) { strcpy (codename, "USNO.103AE.RED71");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED73 ")) { strcpy (codename, "USNO.103AE.RED73");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RG2444")) { strcpy (codename, "USNO.103AE.RG2444"); goto got_photcode; } 
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RP2444")) { strcpy (codename, "USNO.103AE.RP2444"); goto got_photcode; } 
+  if (!strcmp(emulsion, "103AO") && !strcmp(filter, "NONE  ")) { strcpy (codename, "USNO.103AO.NONE");   goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "OG590 ")) { strcpy (codename, "USNO.IIIAF.OG590");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "RG600 ")) { strcpy (codename, "USNO.IIIAF.RG600");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "RG610 ")) { strcpy (codename, "USNO.IIIAF.RG610");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "RG630 ")) { strcpy (codename, "USNO.IIIAF.RG630");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAJ") && !strcmp(filter, "GG358 ")) { strcpy (codename, "USNO.IIIAJ.GG358");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAJ") && !strcmp(filter, "GG385 ")) { strcpy (codename, "USNO.IIIAJ.GG385");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAJ") && !strcmp(filter, "GG395 ")) { strcpy (codename, "USNO.IIIAJ.GG395");  goto got_photcode; }
+  if (!strcmp(emulsion, "IVN  ") && !strcmp(filter, "RG715 ")) { strcpy (codename, "USNO.IVN.RG715");    goto got_photcode; }
+  if (!strcmp(emulsion, "IVN  ") && !strcmp(filter, "RG9   ")) { strcpy (codename, "USNO.IVN.RG9");      goto got_photcode; }
+  if (!strcmp(emulsion, "IVN  ") && !strcmp(filter, "WR88A ")) { strcpy (codename, "USNO.IVN.WR88A");    goto got_photcode; }
+  fprintf (stderr, "error interpretting emulsion and filter: %s, %s\n", emulsion, filter);
+  abort ();
+
+got_photcode:
+  photcode = GetPhotcodebyName (codename);
+  if (photcode == NULL) {
+    fprintf (stderr, "unknown photcode %s\n", codename);
+    abort ();
+  }    
+
+  return photcode;
+}
Index: /trunk/Ohana/src/addstar/src/LoadDataSDSS.c
===================================================================
--- /trunk/Ohana/src/addstar/src/LoadDataSDSS.c	(revision 16966)
+++ /trunk/Ohana/src/addstar/src/LoadDataSDSS.c	(revision 16967)
@@ -38,4 +38,5 @@
   fseek (f, Nskip, SEEK_SET); 
 	 
+  // XXX I think this is an error?  should this be &images[0][Nvalid] ??
   inStars = ReadStarsSDSS (f, name, headers[Nhead], headers[Ndata], images[0], &Nvalid, &Ninstars);
   *stars = MergeStars (*stars, Nstars, inStars, Ninstars);
