def distmatch(sexlist, catlist, maxrad=180, minrad=10, reqmatch=3, patolerance=1.2, uncpa=-1, showmatches=0, fastmatch=1): if reqmatch < 2: print 'Warning: reqmatch >=3 suggested' if patolerance <= 0: print 'PA tolerance cannot be negative!!!' patolerance = abs(patolerance) if uncpa < 0: uncpa = 720 declist = [] for s in sexlist: declist.append(s.dec_rad) avdec_rad = astrometrystats.median(declist) # faster distance computation rascale = numpy.cos(avdec_rad) # will mess up meridian crossings, however #Calculates distances between objects in same list for both image catalog and reference catalog (sexdists, sexmatchids) = calcdist(sexlist, maxrad, minrad, rascale) (catdists, catmatchids) = calcdist(catlist, maxrad, minrad, rascale) # Now look for matches in the reference catalog to distances in the image catalog. countgreatmatches = 0 smatch = [] cmatch = [] mpa = [] offset = [] offpa = [] nmatch = [] primarymatchs = [] primarymatchc = [] #For each sextractor source A for si in range(len(sexdists)): sexdistarr = sexdists[si] sexidarr = sexmatchids[si] if len(sexdistarr) < 2: continue #For each catalog source B for ci in range(len(catdists)): catdistarr = catdists[ci] catidarr = catmatchids[ci] if len(catdistarr) < 2: continue match = 0 smatchin = [] cmatchin = [] #For each sextractor source A, use the distances to other sextractor sources #that are within the radius range for sj in range(len(sexdistarr)): sexdist = sexdistarr[sj] newmatch = 1 #For each catalog source B, use the distances to other catalog sources #that are within the radius range and compare the ratio to #(sextractor source A - sextractor sources) to (catalog source B - catalog sources) #If within 3% then save the match (only count 1 match per catalog row) for cj in range(len(catdistarr)): catdist = catdistarr[cj] if abs((sexdist / catdist) - 1.0) < 0.03: match += newmatch newmatch = 0 #further matches before the next sj loop indicate degeneracies smatchin.append(sexmatchids[si][sj]) cmatchin.append(catmatchids[ci][cj]) #If number of matches is above or equal to required number of matches for sextractor source A #(i.e. sextractor source A's distance to other sources makes it a likely candidate to be a catalog source) #Find difference in position angle between matched angle between sextractor source A and matched source with #catalog source B and matched catalog source. #Remove values that are larger than user specified value or if above position angle tolerance. if match >= reqmatch: dpa = [] # Here, dpa[n] is the mean rotation of the PA from the primary star of this match # to the stars in its match RELATIVE TO those same angles for those same stars # in the catalog. Therefore it is a robust measurement of the rotation. for i in range(len(smatchin)): ddpa = posangle(sexlist[si], sexlist[smatchin[i]]) - posangle( catlist[ci], catlist[cmatchin[i]]) while ddpa > 200: ddpa -= 360. while ddpa < -160: ddpa += 360. dpa.append(ddpa) #If user was confident the initial PA was right, remove bad PA's right away for i in range(len(smatchin) - 1, -1, -1): if abs(dpa[i]) > uncpa: del smatchin[i] del cmatchin[i] del dpa[i] if len(smatchin) < 2: continue #Finds mode of difference position angle but allowing values to be +/- patolerance dpamode = astrometrystats.most(dpa, vmin=patolerance * 3, vmax=patolerance * 3) #Remove deviant matches by PA for i in range(len(smatchin) - 1, -1, -1): if abs(dpa[i] - dpamode) > patolerance: del smatchin[i] del cmatchin[i] del dpa[i] if len(smatchin) < 2: continue #Finds the number of degenerate matches ndegeneracies = len(smatchin) - len( astrometrystats.unique(smatchin)) + len(cmatchin) - len( astrometrystats.unique(cmatchin)) # this isn't quite accurate (overestimates if degeneracies are mixed up) #Save values mpa.append(dpamode) primarymatchs.append(si) primarymatchc.append(ci) smatch.append(smatchin) cmatch.append(cmatchin) nmatch.append(len(smatchin) - ndegeneracies) #If the number of unique sextractor sources is greater than 6, count as a great match if (len(smatchin) - ndegeneracies > 6): countgreatmatches += 1 #Breaks loop if over 16 great matches are found and fastmatch if countgreatmatches > 16 and fastmatch == 1: break #save processing time #If no matches found end program and return empty lists nmatches = len(smatch) if (nmatches == 0): print 'Found no potential matches of any sort (including pairs).' print 'The algorithm is probably not finding enough real stars to solve the field. Check seeing.' return [], [], [] #Get rid of matches that don't pass the reqmatch cut (2nd cut after removing bad position angles) for i in range(len(primarymatchs) - 1, -1, -1): if nmatch[i] < reqmatch: del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] #If no remaining matches then exit program if len(smatch) < 1: print 'Found no matching clusters of reqmatch =', reqmatch return [], [], [] #If we still have lots of matches, get rid of those with the minimum number of submatches #(that is, increase reqmatch by 1) minmatch = min(nmatch) countnotmin = 0 #Finds how many matches have more than the minimum require matches for n in nmatch: if n > minmatch: countnotmin += 1 #If the number of matches is above 16 and there are more than 3 sources with more than the #required number of matches, then delete any source with the bare minimum number of matches if len(nmatch) > 16 and countnotmin > 3: print 'Too many matches: increasing reqmatch to', reqmatch + 1 for i in range(len(primarymatchs) - 1, -1, -1): if nmatch[i] == minmatch: del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] nmatches = len( smatch ) # recalculate with the new reqmatch and with prunes supposedly removed print 'Found', nmatches, 'candidate matches.' # Kill the bad matches rejects = 0 # Use only matches with a consistent PA (finds mode counting those within 3 patolerance) offpa = astrometrystats.most(mpa, vmin=3 * patolerance, vmax=3 * patolerance) #Removes values with rotational positional offsets that are above tolerance, then removes #values that are above 2 sigma of those values if len(smatch) > 2: #Coarse iteration for anything away from the mode for i in range(len(primarymatchs) - 1, -1, -1): if abs(mpa[i] - offpa) > patolerance: del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] rejects += 1 medpa = astrometrystats.median(mpa) stdevpa = astrometrystats.stdev(mpa) refinedtolerance = ( 2.0 * stdevpa ) #VLT Changed from arbitrary value of 2.2 from original script #Fine iteration to flag outliers now that we know most are reliable for i in range(len(primarymatchs) - 1, -1, -1): if abs(mpa[i] - offpa) > refinedtolerance: del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] rejects += 1 #these aren't necessarily bad, just making more manageable. # New verification step: calculate distances and PAs between central stars of matches ndistflags = [0] * len(primarymatchs) for v in range(2): #two iterations if len(primarymatchs) == 0: break #Find distances between central stars of matches and compare sextractor source distances to catalog sources for i in range(len(primarymatchs)): for j in range(len(primarymatchs)): if i == j: continue si = primarymatchs[i] ci = primarymatchc[i] sj = primarymatchs[j] cj = primarymatchc[j] sexdistij = distance(sexlist[si], sexlist[sj]) catdistij = distance(catlist[ci], catlist[cj]) try: if abs((sexdistij / catdistij) - 1.0) > 0.03: ndistflags[i] += 1 except: # (occasionally will get divide by zero) pass #Delete bad clusters that were flagged for every match ntestmatches = len(primarymatchs) for i in range(ntestmatches - 1, -1, -1): if ndistflags[ i] == ntestmatches - 1: #if every comparison is bad, this is a bad match del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] rejects += 1 print 'Rejected', rejects, 'bad matches.' nmatches = len(primarymatchs) print 'Found', nmatches, 'good matches.' #If no remaining matches, return empty lists if nmatches == 0: return [], [], [] #Returns pixel scale (great circle distance in catalog [ra, dec]/cartesian distance in sextractor source [x,y]) pixscalelist = [] if len(primarymatchs) >= 2: for i in range(len(primarymatchs) - 1): for j in range(i + 1, len(primarymatchs)): si = primarymatchs[i] ci = primarymatchc[i] sj = primarymatchs[j] cj = primarymatchc[j] try: pixscalelist.append( distance(catlist[ci], catlist[cj]) / imdistance(sexlist[si], sexlist[sj])) except: pass pixelscale = astrometrystats.median(pixscalelist) pixelscalestd = astrometrystats.stdev(pixscalelist) if len(primarymatchs) >= 3: print 'Refined pixel scale measurement: %.4f"/pix (+/- %.4f)' % ( pixelscale, pixelscalestd) else: print 'Refined pixel scale measurement: %.4f"/pix' % pixelscale #If showmatches keyword set then print which objects match for i in range(len(primarymatchs)): si = primarymatchs[i] ci = primarymatchc[i] print '%3i' % si, 'matches', '%3i' % ci, ' (dPA =%7.3f)' % mpa[i], #Keyword set in main program if showmatches: print if len(smatch[i]) < 16: print ' ', si, '-->', smatch[i], if len(smatch[i]) >= 7: print print ' ', ci, '-->', cmatch[i] else: print ' ', si, '-->', smatch[i][0:10], '+', len( smatch[i]) - 10, 'more' print ' ', ci, '-->', cmatch[i][ 0:10], '+' #, len(cmatch[i])-10, ' more' if i + 1 >= 10 and len(primarymatchs) - 10 > 0: print(len(primarymatchs) - 10), 'additional matches not shown.' break else: print ':', str(len(smatch[i])).strip(), 'rays' #Create region files for DS9 with the sextractor sources (matchlines.im.reg) and catalog (matchlines.wcs.reg) out = open('matchlines.im.reg', 'w') i = -1 color = 'red' out.write( '# Region file format: DS9 version 4.0\nglobal color=' + color + ' font="helvetica 10 normal" select=1 highlite=1 edit=1 move=1 delete=1 include=1 fixed=0 source\n' ) out.write('image\n') for i in range(len(primarymatchs)): si = primarymatchs[i] for j in range(len(smatch[i])): sj = smatch[i][j] out.write( "line(%.3f,%.3f,%.3f,%.3f) # line=0 0\n" % (sexlist[si].x, sexlist[si].y, sexlist[sj].x, sexlist[sj].y)) out.close() out = open('matchlines.wcs.reg', 'w') i = -1 color = 'green' out.write( '# Region file format: DS9 version 4.0\nglobal color=' + color + ' font="helvetica 10 normal" select=1 highlite=1 edit=1 move=1 delete=1 include=1 fixed=0 source\n' ) out.write('fk5\n') for i in range(len(primarymatchs)): ci = primarymatchc[i] for j in range(len(smatch[i])): cj = cmatch[i][j] out.write("line(%.5f,%.5f,%.5f,%.5f) # line=0 0\n" % (catlist[ci].ra, catlist[ci].dec, catlist[cj].ra, catlist[cj].dec)) out.close() #future project: if not enough, go to the secondary offsets #Returns sextractor sources and catalog sources that appear to have matches along with the mode of the position angle return (primarymatchs, primarymatchc, mpa)
def sextract( sexfilename, nxpix, nypix, border=3, corner=12, minfwhm=1.5, maxfwhm=25, maxellip=0.5, saturation=-1, sexpath="" ): if maxellip == -1: maxellip = 0.5 if saturation > 0: sexsaturation = saturation else: sexsaturation = 9e5 # Runs sextractor and reads in sextractor output file (temp.cat) try: # Sextract the image ! print sexpath + "sex " + sexfilename + " -c sex.config -SATUR_LEVEL " + str(sexsaturation) os.system(sexpath + "sex " + sexfilename + " -c sex.config -SATUR_LEVEL " + str(sexsaturation)) except: print " Error: Problem running sextractor" print " Check that program is installed and runs at command line using " + sexpath + "sex" sys.exit(1) # Read in the sextractor catalog try: cat = numpy.loadtxt("temp.cat", dtype="float", comments="#") except: print "Cannot load sextractor output file!" sys.exit(1) if len(cat) == 0: print "Sextractor catalog is empty: try a different catalog?" sys.exit(1) minx = border miny = border maxx = nxpix - border # This should be generalized maxy = nypix - border # Separate columns from sextractor output x = cat[:, 0] y = cat[:, 1] ra = cat[:, 2] dec = cat[:, 3] mag = cat[:, 4] magerr = cat[:, 5] ellip = cat[:, 6] fwhm = cat[:, 7] flag = cat[:, 8] # Initial filtering creates mask that will remove borders, corners, # values above max ellipticity (galaxies), and within fwhm constraints mask = ( (ellip <= maxellip) & (fwhm >= minfwhm) & (fwhm <= maxfwhm) & (x >= minx) & (x <= maxx) & (y >= miny) & (y <= maxy) & (x + y >= corner) & (x + nypix - y >= corner) & (nxpix - x >= corner) & (nxpix - x + nypix - y >= corner) ) # Removes flagged values if saturation level set if saturation > 0: mask = mask & (flag == 0) # VLT Removed code from autoastrometry.py line 387-432 that rules out false detections # If too many false positives need to rewrite section in more coherent way # OC: Might also be good to screen for false detections created by bad columns/rows fwhmlist = fwhm[mask] # Calculates the 20% value of the sextractor masked FWHM and the mode # to distinguish stars from galaxies (removed by ellipticity) and cosmic rays if len(fwhmlist) > 5: sfwhmlist = sorted(fwhmlist) fwhm20 = sfwhmlist[len(fwhmlist) / 5] # percentile values fwhmmode = astrometrystats.most(sfwhmlist, vmax=0, vmin=0) else: fwhmmode = minfwhm fwhm20 = minfwhm # Creates new FWHM cutoff to distinguish stars from cosmic rays # This method done from trial and error from original program to calculate typical cutoffs between stars and cosmic # OC: if CR's are bigger and more common than stars, this is dangerous... refinedminfwhm = numpy.median([0.75 * fwhmmode, 0.9 * fwhm20, minfwhm]) print "Refined min FWHM:", refinedminfwhm, "pix" # Masks out fwhm with more stringent fwhm condition and creates newmask # that combines the new masked out fwhm values refwhmmask = fwhm > refinedminfwhm newmask = refwhmmask & mask # Create array with bad values removed and then sort by magnitude (column 4) goodsext = cat[newmask] sortedgoodsext = goodsext[goodsext[:, 4].argsort()] print len(fwhmlist), "objects detected in image (" + str(len(fwhmlist) - len(sortedgoodsext)) + " discarded)" # Save RA, DEC, and mag into class and save class objects into list goodsexlist = [] for value in sortedgoodsext: # RA = column 2, DEC = column 3, mag = column 4 with columns starting at 0 indObj = SexObj(value) goodsexlist.append(indObj) return goodsexlist
def sextract(sexfilename, nxpix, nypix, border=3, corner=12, minfwhm=1.5, maxfwhm=25, maxellip=0.5, saturation=-1, sexpath='', quiet=False): if maxellip == -1: maxellip = 0.5 if saturation > 0: sexsaturation = saturation else: sexsaturation = 9e5 #Runs sextractor and reads in sextractor output file (temp.cat) try: # Sextract the image ! if quiet == False: print sexpath + "sex " + sexfilename + " -c sex.config -SATUR_LEVEL " + str( sexsaturation) os.system(sexpath + "sex " + sexfilename + " -c sex.config -SATUR_LEVEL " + str(sexsaturation)) except: print ' Error: Problem running sextractor' print ' Check that program is installed and runs at command line using ' + sexpath + 'sex' sys.exit(1) # Read in the sextractor catalog try: cat = numpy.loadtxt('temp.cat', dtype='float', comments='#') except: print 'Cannot load sextractor output file!' sys.exit(1) if len(cat) == 0: print 'Sextractor catalog is empty: try a different catalog?' sys.exit(1) minx = border miny = border maxx = nxpix - border # This should be generalized maxy = nypix - border #Separate columns from sextractor output x = cat[:, 0] y = cat[:, 1] ra = cat[:, 2] dec = cat[:, 3] mag = cat[:, 4] magerr = cat[:, 5] ellip = cat[:, 6] fwhm = cat[:, 7] flag = cat[:, 8] a_imag = cat[:, 9] b_imag = cat[:, 10] #Initial filtering creates mask that will remove borders, corners, #values above max ellipticity (galaxies), and within fwhm constraints mask = (ellip <= maxellip) & (fwhm >= minfwhm) & (fwhm <= maxfwhm) \ & (x >= minx) & (x <= maxx) & (y >= miny) & (y <= maxy) \ & (x+y >= corner) & (x+nypix-y >= corner) \ & (nxpix-x >= corner) & (nxpix-x+nypix-y >= corner) \ & (a_imag > 1) & (b_imag > 1) #Removes flagged values if saturation level set if saturation > 0: mask = mask & (flag == 0) #VLT Removed code from autoastrometry.py line 387-432 that rules out false detections #If too many false positives need to rewrite section in more coherent way #OC: Might also be good to screen for false detections created by bad columns/rows fwhmlist = fwhm[mask] #Calculates the 20% value of the sextractor masked FWHM and the mode #to distinguish stars from galaxies (removed by ellipticity) and cosmic rays if len(fwhmlist) > 5: sfwhmlist = sorted(fwhmlist) fwhm20 = sfwhmlist[len(fwhmlist) / 5] #percentile values fwhmmode = astrometrystats.most(sfwhmlist, vmax=0, vmin=0) else: fwhmmode = minfwhm fwhm20 = minfwhm #Creates new FWHM cutoff to distinguish stars from cosmic rays #This method done from trial and error from original program to calculate typical cutoffs between stars and cosmic #OC: if CR's are bigger and more common than stars, this is dangerous... refinedminfwhm = numpy.median([0.75 * fwhmmode, 0.9 * fwhm20, minfwhm]) if quiet == False: print 'Refined min FWHM:', refinedminfwhm, 'pix' #Masks out fwhm with more stringent fwhm condition and creates newmask #that combines the new masked out fwhm values refwhmmask = fwhm > refinedminfwhm newmask = refwhmmask & mask #Create array with bad values removed and then sort by magnitude (column 4) goodsext = cat[newmask] sortedgoodsext = goodsext[goodsext[:, 4].argsort()] if quiet == False: print len(fwhmlist), 'objects detected in image (' + str( len(fwhmlist) - len(sortedgoodsext)) + ' discarded)' #Save RA, DEC, and mag into class and save class objects into list goodsexlist = [] for value in sortedgoodsext: #RA = column 2, DEC = column 3, mag = column 4 with columns starting at 0 indObj = SexObj(value) goodsexlist.append(indObj) return goodsexlist
def distmatch(sexlist, catlist, maxrad=180, minrad=10, reqmatch=3, patolerance=1.2,uncpa=-1, showmatches=0, fastmatch=1): if reqmatch < 2: print 'Warning: reqmatch >=3 suggested' if patolerance <= 0: print 'PA tolerance cannot be negative!!!' patolerance = abs(patolerance) if uncpa < 0: uncpa = 720 declist = [] for s in sexlist: declist.append(s.dec_rad) avdec_rad = astrometrystats.median(declist) # faster distance computation rascale = numpy.cos(avdec_rad) # will mess up meridian crossings, however #Calculates distances between objects in same list for both image catalog and reference catalog (sexdists, sexmatchids) = calcdist(sexlist, maxrad, minrad, rascale) (catdists, catmatchids) = calcdist(catlist, maxrad, minrad, rascale) # Now look for matches in the reference catalog to distances in the image catalog. countgreatmatches = 0 smatch = [] cmatch = [] mpa = [] offset = [] offpa = [] nmatch = [] primarymatchs = [] primarymatchc = [] #For each sextractor source A for si in range(len(sexdists)): sexdistarr = sexdists[si] sexidarr = sexmatchids[si] if len(sexdistarr) < 2: continue #For each catalog source B for ci in range(len(catdists)): catdistarr = catdists[ci] catidarr = catmatchids[ci] if len(catdistarr) < 2: continue match = 0 smatchin = [] cmatchin = [] #For each sextractor source A, use the distances to other sextractor sources #that are within the radius range for sj in range(len(sexdistarr)): sexdist = sexdistarr[sj] newmatch = 1 #For each catalog source B, use the distances to other catalog sources #that are within the radius range and compare the ratio to #(sextractor source A - sextractor sources) to (catalog source B - catalog sources) #If within 3% then save the match (only count 1 match per catalog row) for cj in range(len(catdistarr)): catdist = catdistarr[cj] if abs((sexdist/catdist)-1.0) < 0.03: match += newmatch newmatch = 0 #further matches before the next sj loop indicate degeneracies smatchin.append(sexmatchids[si][sj]) cmatchin.append(catmatchids[ci][cj]) #If number of matches is above or equal to required number of matches for sextractor source A #(i.e. sextractor source A's distance to other sources makes it a likely candidate to be a catalog source) #Find difference in position angle between matched angle between sextractor source A and matched source with #catalog source B and matched catalog source. #Remove values that are larger than user specified value or if above position angle tolerance. if match >= reqmatch: dpa = [] # Here, dpa[n] is the mean rotation of the PA from the primary star of this match # to the stars in its match RELATIVE TO those same angles for those same stars # in the catalog. Therefore it is a robust measurement of the rotation. for i in range(len(smatchin)): ddpa = posangle(sexlist[si],sexlist[smatchin[i]]) - posangle(catlist[ci],catlist[cmatchin[i]]) while ddpa > 200: ddpa -= 360. while ddpa < -160: ddpa += 360. dpa.append(ddpa) #If user was confident the initial PA was right, remove bad PA's right away for i in range(len(smatchin)-1,-1,-1): if abs(dpa[i]) > uncpa: del smatchin[i] del cmatchin[i] del dpa[i] if len(smatchin) < 2: continue #Finds mode of difference position angle but allowing values to be +/- patolerance dpamode = astrometrystats.most(dpa, vmin=patolerance*3, vmax=patolerance*3) #Remove deviant matches by PA for i in range(len(smatchin)-1,-1,-1): if abs(dpa[i] - dpamode) > patolerance: del smatchin[i] del cmatchin[i] del dpa[i] if len(smatchin) < 2: continue #Finds the number of degenerate matches ndegeneracies = len(smatchin)-len(astrometrystats.unique(smatchin)) + len(cmatchin)-len(astrometrystats.unique(cmatchin)) # this isn't quite accurate (overestimates if degeneracies are mixed up) #Save values mpa.append(dpamode) primarymatchs.append(si) primarymatchc.append(ci) smatch.append(smatchin) cmatch.append(cmatchin) nmatch.append(len(smatchin)-ndegeneracies) #If the number of unique sextractor sources is greater than 6, count as a great match if (len(smatchin)-ndegeneracies > 6): countgreatmatches += 1 #Breaks loop if over 16 great matches are found and fastmatch if countgreatmatches > 16 and fastmatch == 1: break #save processing time #If no matches found end program and return empty lists nmatches = len(smatch) if (nmatches == 0): print 'Found no potential matches of any sort (including pairs).' print 'The algorithm is probably not finding enough real stars to solve the field. Check seeing.' return [], [], [] #Get rid of matches that don't pass the reqmatch cut (2nd cut after removing bad position angles) for i in range(len(primarymatchs)-1,-1,-1): if nmatch[i] < reqmatch: del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] #If no remaining matches then exit program if len(smatch) < 1: print 'Found no matching clusters of reqmatch =', reqmatch return [], [], [] #If we still have lots of matches, get rid of those with the minimum number of submatches #(that is, increase reqmatch by 1) minmatch = min(nmatch) countnotmin = 0 #Finds how many matches have more than the minimum require matches for n in nmatch: if n > minmatch: countnotmin += 1 #If the number of matches is above 16 and there are more than 3 sources with more than the #required number of matches, then delete any source with the bare minimum number of matches if len(nmatch) > 16 and countnotmin > 3: print 'Too many matches: increasing reqmatch to', reqmatch+1 for i in range(len(primarymatchs)-1,-1,-1): if nmatch[i] == minmatch: del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] nmatches = len(smatch) # recalculate with the new reqmatch and with prunes supposedly removed print 'Found',nmatches,'candidate matches.' # Kill the bad matches rejects = 0 # Use only matches with a consistent PA (finds mode counting those within 3 patolerance) offpa = astrometrystats.most(mpa, vmin=3*patolerance, vmax=3*patolerance) #Removes values with rotational positional offsets that are above tolerance, then removes #values that are above 2 sigma of those values if len(smatch) > 2: #Coarse iteration for anything away from the mode for i in range(len(primarymatchs)-1,-1,-1): if abs(mpa[i] - offpa) > patolerance: del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] rejects += 1 medpa = astrometrystats.median(mpa) stdevpa = astrometrystats.stdev(mpa) refinedtolerance = (2.0 * stdevpa) #VLT Changed from arbitrary value of 2.2 from original script #Fine iteration to flag outliers now that we know most are reliable for i in range(len(primarymatchs)-1,-1,-1): if abs(mpa[i] - offpa) > refinedtolerance: del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] rejects += 1 #these aren't necessarily bad, just making more manageable. # New verification step: calculate distances and PAs between central stars of matches ndistflags = [0]*len(primarymatchs) for v in range(2): #two iterations if len(primarymatchs) == 0: break #Find distances between central stars of matches and compare sextractor source distances to catalog sources for i in range(len(primarymatchs)): for j in range(len(primarymatchs)): if i == j: continue si = primarymatchs[i] ci = primarymatchc[i] sj = primarymatchs[j] cj = primarymatchc[j] sexdistij = distance(sexlist[si], sexlist[sj]) catdistij = distance(catlist[ci], catlist[cj]) try: if abs((sexdistij/catdistij)-1.0) > 0.03: ndistflags[i] += 1 except: # (occasionally will get divide by zero) pass #Delete bad clusters that were flagged for every match ntestmatches = len(primarymatchs) for i in range(ntestmatches-1,-1,-1): if ndistflags[i] == ntestmatches-1: #if every comparison is bad, this is a bad match del mpa[i] del primarymatchs[i] del primarymatchc[i] del smatch[i] del cmatch[i] del nmatch[i] rejects += 1 print 'Rejected', rejects, 'bad matches.' nmatches = len(primarymatchs) print 'Found', nmatches, 'good matches.' #If no remaining matches, return empty lists if nmatches == 0: return [], [], [] #Returns pixel scale (great circle distance in catalog [ra, dec]/cartesian distance in sextractor source [x,y]) pixscalelist = [] if len(primarymatchs) >= 2: for i in range(len(primarymatchs)-1): for j in range(i+1,len(primarymatchs)): si = primarymatchs[i] ci = primarymatchc[i] sj = primarymatchs[j] cj = primarymatchc[j] try: pixscalelist.append(distance(catlist[ci],catlist[cj])/imdistance(sexlist[si],sexlist[sj])) except: pass pixelscale = astrometrystats.median(pixscalelist) pixelscalestd = astrometrystats.stdev(pixscalelist) if len(primarymatchs) >= 3: print 'Refined pixel scale measurement: %.4f"/pix (+/- %.4f)' % (pixelscale, pixelscalestd) else: print 'Refined pixel scale measurement: %.4f"/pix' % pixelscale #If showmatches keyword set then print which objects match for i in range(len(primarymatchs)): si = primarymatchs[i] ci = primarymatchc[i] print '%3i' % si, 'matches', '%3i' % ci, ' (dPA =%7.3f)' % mpa[i], #Keyword set in main program if showmatches: print if len(smatch[i]) < 16: print ' ', si, '-->', smatch[i], if len(smatch[i]) >= 7: print print ' ', ci, '-->', cmatch[i] else: print ' ', si, '-->', smatch[i][0:10], '+', len(smatch[i])-10, 'more' print ' ', ci, '-->', cmatch[i][0:10], '+'#, len(cmatch[i])-10, ' more' if i+1 >= 10 and len(primarymatchs)-10 > 0: print (len(primarymatchs)-10), 'additional matches not shown.' break else: print ':', str(len(smatch[i])).strip(), 'rays' #Create region files for DS9 with the sextractor sources (matchlines.im.reg) and catalog (matchlines.wcs.reg) out = open('matchlines.im.reg','w') i = -1 color='red' out.write('# Region file format: DS9 version 4.0\nglobal color='+color+' font="helvetica 10 normal" select=1 highlite=1 edit=1 move=1 delete=1 include=1 fixed=0 source\n') out.write('image\n') for i in range(len(primarymatchs)): si = primarymatchs[i] for j in range(len(smatch[i])): sj = smatch[i][j] out.write("line(%.3f,%.3f,%.3f,%.3f) # line=0 0\n" % (sexlist[si].x, sexlist[si].y, sexlist[sj].x, sexlist[sj].y)) out.close() out = open('matchlines.wcs.reg','w') i = -1 color='green' out.write('# Region file format: DS9 version 4.0\nglobal color='+color+' font="helvetica 10 normal" select=1 highlite=1 edit=1 move=1 delete=1 include=1 fixed=0 source\n') out.write('fk5\n') for i in range(len(primarymatchs)): ci = primarymatchc[i] for j in range(len(smatch[i])): cj = cmatch[i][j] out.write("line(%.5f,%.5f,%.5f,%.5f) # line=0 0\n" % (catlist[ci].ra, catlist[ci].dec, catlist[cj].ra, catlist[cj].dec)) out.close() #future project: if not enough, go to the secondary offsets #Returns sextractor sources and catalog sources that appear to have matches along with the mode of the position angle return (primarymatchs, primarymatchc, mpa)