Restructure repository and pep8 changes
Also all executables now use argparse for arguments and helpers.pull/5/head
parent
626cbbb3df
commit
96389ccdc8
|
@ -11,7 +11,7 @@ from astropy.coordinates import EarthLocation
|
||||||
from astropy.time import Time
|
from astropy.time import Time
|
||||||
from astropy.io import fits
|
from astropy.io import fits
|
||||||
import astropy.units as u
|
import astropy.units as u
|
||||||
from utils import get_sunset_and_sunrise
|
from stvid.utils import get_sunset_and_sunrise
|
||||||
import logging
|
import logging
|
||||||
import configparser
|
import configparser
|
||||||
import argparse
|
import argparse
|
||||||
|
|
177
astrometry.py
177
astrometry.py
|
@ -1,177 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
from __future__ import print_function
|
|
||||||
import os
|
|
||||||
import numpy as np
|
|
||||||
import astropy.units as u
|
|
||||||
from astropy.io import fits
|
|
||||||
from astropy import wcs
|
|
||||||
from astropy.coordinates import SkyCoord,FK5,ICRS
|
|
||||||
from astropy.time import Time
|
|
||||||
from scipy import optimize
|
|
||||||
|
|
||||||
# Class for the Tycho 2 catalog
|
|
||||||
class tycho2_catalog:
|
|
||||||
"""Tycho2 catalog"""
|
|
||||||
|
|
||||||
def __init__(self,maxmag=9.0):
|
|
||||||
hdu=fits.open(os.path.join(os.getenv("ST_DATADIR"),"data/tyc2.fits"))
|
|
||||||
|
|
||||||
ra=hdu[1].data.field('RA')*u.deg
|
|
||||||
dec=hdu[1].data.field('DEC')*u.deg
|
|
||||||
mag=hdu[1].data.field('MAG_VT')
|
|
||||||
|
|
||||||
c=mag<maxmag
|
|
||||||
self.ra=ra[c]
|
|
||||||
self.dec=dec[c]
|
|
||||||
self.mag=mag[c]
|
|
||||||
|
|
||||||
# Estimate the WCS from a reference file
|
|
||||||
def estimate_wcs_from_reference(ref,fname):
|
|
||||||
# Read header of reference
|
|
||||||
hdu=fits.open(ref)
|
|
||||||
hdu[0].header["NAXIS"]=2
|
|
||||||
w=wcs.WCS(hdu[0].header)
|
|
||||||
|
|
||||||
# Get time and position from reference
|
|
||||||
tref=Time(hdu[0].header["MJD-OBS"],format="mjd",scale="utc")
|
|
||||||
pref=SkyCoord(ra=w.wcs.crval[0],dec=w.wcs.crval[1],unit="deg",frame="icrs").transform_to(FK5(equinox=tref))
|
|
||||||
|
|
||||||
# Read time from target
|
|
||||||
hdu=fits.open(fname)
|
|
||||||
t=Time(hdu[0].header["MJD-OBS"],format="mjd",scale="utc")
|
|
||||||
|
|
||||||
# Correct wcs
|
|
||||||
dra=t.sidereal_time("mean","greenwich")-tref.sidereal_time("mean","greenwich")
|
|
||||||
p=FK5(ra=pref.ra+dra,dec=pref.dec,equinox=t).transform_to(ICRS)
|
|
||||||
w.wcs.crval=np.array([p.ra.degree,p.dec.degree])
|
|
||||||
|
|
||||||
return w
|
|
||||||
|
|
||||||
# Match the astrometry and pixel catalog
|
|
||||||
def match_catalogs(ast_catalog,pix_catalog,w,rmin):
|
|
||||||
# Select stars towards pointing center
|
|
||||||
ra,dec=w.wcs.crval*u.deg
|
|
||||||
d=np.arccos(np.sin(dec)*np.sin(ast_catalog.dec)+np.cos(dec)*np.cos(ast_catalog.dec)*np.cos(ra-ast_catalog.ra))
|
|
||||||
c=(d<30.0*u.deg)
|
|
||||||
ra,dec,mag=ast_catalog.ra[c],ast_catalog.dec[c],ast_catalog.mag[c]
|
|
||||||
|
|
||||||
# Convert RA/Dec to pixels
|
|
||||||
pix=w.wcs_world2pix(np.stack((ra,dec),axis=-1),0)
|
|
||||||
xs,ys=pix[:,0],pix[:,1]
|
|
||||||
|
|
||||||
# Loop over stars
|
|
||||||
nmatch=0
|
|
||||||
for i in range(len(pix_catalog.x)):
|
|
||||||
dx=xs-pix_catalog.x[i]
|
|
||||||
dy=ys-pix_catalog.y[i]
|
|
||||||
r=np.sqrt(dx*dx+dy*dy)
|
|
||||||
if np.min(r)<rmin:
|
|
||||||
j=np.argmin(r)
|
|
||||||
pix_catalog.ra[i]=ra[j].value
|
|
||||||
pix_catalog.dec[i]=dec[j].value
|
|
||||||
pix_catalog.imag[i]=mag[j]
|
|
||||||
pix_catalog.flag[i]=1
|
|
||||||
nmatch+=1
|
|
||||||
|
|
||||||
return nmatch
|
|
||||||
|
|
||||||
# Residual function
|
|
||||||
def residual(a,x,y,z):
|
|
||||||
return z-(a[0]+a[1]*x+a[2]*y)
|
|
||||||
|
|
||||||
# Fit transformation
|
|
||||||
def fit_wcs(w,pix_catalog):
|
|
||||||
x0,y0=w.wcs.crpix
|
|
||||||
ra0,dec0=w.wcs.crval
|
|
||||||
dx,dy=pix_catalog.x-x0,pix_catalog.y-y0
|
|
||||||
|
|
||||||
# Iterate to remove outliers
|
|
||||||
nstars=np.sum(pix_catalog.flag==1)
|
|
||||||
for j in range(10):
|
|
||||||
w=wcs.WCS(naxis=2)
|
|
||||||
w.wcs.crpix=np.array([0.0,0.0])
|
|
||||||
w.wcs.cd=np.array([[1.0,0.0],[0.0,1.0]])
|
|
||||||
w.wcs.ctype=["RA---TAN","DEC--TAN"]
|
|
||||||
w.wcs.set_pv([(2,1,45.0)])
|
|
||||||
c=pix_catalog.flag==1
|
|
||||||
|
|
||||||
# Iterate to move crval to crpix location
|
|
||||||
for i in range(5):
|
|
||||||
w.wcs.crval=np.array([ra0,dec0])
|
|
||||||
|
|
||||||
world=np.stack((pix_catalog.ra,pix_catalog.dec),axis=-1)
|
|
||||||
pix=w.wcs_world2pix(world,1)
|
|
||||||
rx,ry=pix[:,0],pix[:,1]
|
|
||||||
|
|
||||||
ax,cov_q,infodict,mesg,ierr=optimize.leastsq(residual,[0.0,0.0,0.0],args=(dx[c],dy[c],rx[c]),full_output=1)
|
|
||||||
ay,cov_q,infodict,mesg,ierr=optimize.leastsq(residual,[0.0,0.0,0.0],args=(dx[c],dy[c],ry[c]),full_output=1)
|
|
||||||
|
|
||||||
ra0,dec0=w.wcs_pix2world([[ax[0],ay[0]]],1)[0]
|
|
||||||
|
|
||||||
# Compute residuals
|
|
||||||
drx=ax[0]+ax[1]*dx+ax[2]*dy-rx
|
|
||||||
dry=ay[0]+ay[1]*dx+ay[2]*dy-ry
|
|
||||||
dr=np.sqrt(drx*drx+dry*dry)
|
|
||||||
rms=np.sqrt(np.sum(dr[c]**2)/len(dr[c]))
|
|
||||||
|
|
||||||
dr[~c]=1.0
|
|
||||||
c=(dr<2.0*rms)
|
|
||||||
pix_catalog.flag[~c]=0
|
|
||||||
|
|
||||||
# Break if converged
|
|
||||||
if np.sum(c)==nstars:
|
|
||||||
break
|
|
||||||
nstars=np.sum(c)
|
|
||||||
|
|
||||||
# Compute residuals
|
|
||||||
rmsx=np.sqrt(np.sum(drx[c]**2)/len(drx[c]))
|
|
||||||
rmsy=np.sqrt(np.sum(dry[c]**2)/len(dry[c]))
|
|
||||||
|
|
||||||
# Store header
|
|
||||||
w=wcs.WCS(naxis=2)
|
|
||||||
w.wcs.crpix=np.array([x0,y0])
|
|
||||||
w.wcs.crval=np.array([ra0,dec0])
|
|
||||||
w.wcs.cd=np.array([[ax[1],ax[2]],[ay[1],ay[2]]])
|
|
||||||
w.wcs.ctype=["RA---TAN","DEC--TAN"]
|
|
||||||
w.wcs.set_pv([(2,1,45.0)])
|
|
||||||
|
|
||||||
return w,rmsx,rmsy,rms
|
|
||||||
|
|
||||||
def add_wcs(fname,w,rmsx,rmsy):
|
|
||||||
# Read fits
|
|
||||||
hdu=fits.open(fname)
|
|
||||||
|
|
||||||
whdr={"CRPIX1":w.wcs.crpix[0],"CRPIX2":w.wcs.crpix[1],"CRVAL1":w.wcs.crval[0],"CRVAL2":w.wcs.crval[1],"CD1_1":w.wcs.cd[0,0],"CD1_2":w.wcs.cd[0,1],"CD2_1":w.wcs.cd[1,0],"CD2_2":w.wcs.cd[1,1],"CTYPE1":"RA---TAN","CTYPE2":"DEC--TAN","CUNIT1":"DEG","CUNIT2":"DEG","CRRES1":rmsx,"CRRES2":rmsy}
|
|
||||||
|
|
||||||
# Add keywords
|
|
||||||
hdr=hdu[0].header
|
|
||||||
for k,v in whdr.items():
|
|
||||||
hdr[k]=v
|
|
||||||
hdu=fits.PrimaryHDU(header=hdr,data=hdu[0].data)
|
|
||||||
hdu.writeto(fname,overwrite=True,output_verify="ignore")
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def calibrate_from_reference(fname,ref,pix_catalog):
|
|
||||||
# Estimated WCS
|
|
||||||
w=estimate_wcs_from_reference(ref,fname)
|
|
||||||
|
|
||||||
# Default rms values
|
|
||||||
rmsx=0.0
|
|
||||||
rmsy=0.0
|
|
||||||
|
|
||||||
# Read catalogs
|
|
||||||
if (pix_catalog.nstars>4):
|
|
||||||
ast_catalog=tycho2_catalog(10.0)
|
|
||||||
|
|
||||||
# Match catalogs
|
|
||||||
nmatch=match_catalogs(ast_catalog,pix_catalog,w,10.0)
|
|
||||||
|
|
||||||
# Fit transformation
|
|
||||||
if nmatch>4:
|
|
||||||
w,rmsx,rmsy,rms=fit_wcs(w,pix_catalog)
|
|
||||||
|
|
||||||
# Add wcs
|
|
||||||
add_wcs(fname,w,rmsx,rmsy)
|
|
||||||
|
|
||||||
return w,rmsx,rmsy
|
|
13
capture_1.sh
13
capture_1.sh
|
@ -1,13 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Sleep
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
# Does /dev/video1 exist
|
|
||||||
ls -l /dev/video1 >/tmp/camera_1.log 2>&1
|
|
||||||
|
|
||||||
# Run mplayer for 10 frames to setup easycap
|
|
||||||
mplayer -frames 10 tv:// -tv device=/dev/video1 >/tmp/camera_1.log 2>&1
|
|
||||||
|
|
||||||
# Start script
|
|
||||||
python ./acquire.py 1 >/tmp/camera_1.log 2>&1 &
|
|
13
capture_2.sh
13
capture_2.sh
|
@ -1,13 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Sleep
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
# Does /dev/video2 exist
|
|
||||||
ls -l /dev/video2 >/tmp/camera_2.log 2>&1
|
|
||||||
|
|
||||||
# Run mplayer for 10 frames to setup easycap
|
|
||||||
mplayer -frames 10 tv:// -tv device=/dev/video2 >/tmp/camera_2.log 2>&1
|
|
||||||
|
|
||||||
# Start script
|
|
||||||
python ./acquire.py 2 >/tmp/camera_2.log 2>&1 &
|
|
|
@ -1,13 +1,14 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import time
|
|
||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
import shutil
|
import shutil
|
||||||
from stio import fourframe, satid, observation
|
from stvid.stio import fourframe, satid, observation
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import ppgplot as ppg
|
import ppgplot as ppg
|
||||||
from scipy import optimize, ndimage
|
from scipy import optimize, ndimage
|
||||||
|
import configparser
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
# Gaussian model
|
# Gaussian model
|
||||||
|
@ -454,15 +455,35 @@ if __name__ == '__main__':
|
||||||
# Minimum track points
|
# Minimum track points
|
||||||
ntrkmin = 10
|
ntrkmin = 10
|
||||||
|
|
||||||
|
# Read commandline options
|
||||||
|
conf_parser = argparse.ArgumentParser(description='Extract satellite' +
|
||||||
|
' tracks from frames.')
|
||||||
|
conf_parser.add_argument("-c", "--conf_file",
|
||||||
|
help="Specify configuration file. If no file" +
|
||||||
|
" is specified 'configuration.ini' is used.",
|
||||||
|
metavar="FILE")
|
||||||
|
conf_parser.add_argument("-d", "--directory",
|
||||||
|
help="Specify directory of observations. If no" +
|
||||||
|
" directory is specified parent will be used.",
|
||||||
|
metavar='DIR', dest='file_dir', default=".")
|
||||||
|
|
||||||
|
args = conf_parser.parse_args()
|
||||||
|
|
||||||
|
# Process commandline options and parse configuration
|
||||||
|
cfg = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
|
||||||
|
if args.conf_file:
|
||||||
|
cfg.read([args.conf_file])
|
||||||
|
else:
|
||||||
|
cfg.read('configuration.ini')
|
||||||
|
|
||||||
# Create output dirs
|
# Create output dirs
|
||||||
path = os.getenv("ST_OBSDIR")+"/"+time.strftime("%Y%m%d/%H%M%S",
|
path = args.file_dir
|
||||||
time.gmtime())
|
os.makedirs(path+"classfd")
|
||||||
os.makedirs(path+"/classfd")
|
os.makedirs(path+"catalog")
|
||||||
os.makedirs(path+"/catalog")
|
os.makedirs(path+"unid")
|
||||||
os.makedirs(path+"/unid")
|
|
||||||
|
|
||||||
# Get files
|
# Get files
|
||||||
files = sorted(glob.glob("2*.fits"))
|
files = sorted(glob.glob(path+"2*.fits"))
|
||||||
|
|
||||||
# Process files
|
# Process files
|
||||||
for file in files:
|
for file in files:
|
||||||
|
|
|
@ -4,38 +4,66 @@ import numpy as np
|
||||||
from astropy.io import ascii
|
from astropy.io import ascii
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import astropy.units as u
|
import astropy.units as u
|
||||||
from astropy.coordinates import SkyCoord,FK5,AltAz,EarthLocation
|
from astropy.coordinates import SkyCoord, AltAz, EarthLocation
|
||||||
from astropy.time import Time
|
from astropy.time import Time
|
||||||
|
import configparser
|
||||||
|
import argparse
|
||||||
|
|
||||||
table=ascii.read("imgstat.csv",format="csv")
|
# Read commandline options
|
||||||
|
conf_parser = argparse.ArgumentParser(description='Plot image statistics')
|
||||||
|
conf_parser.add_argument("-c", "--conf_file",
|
||||||
|
help="Specify configuration file. If no file" +
|
||||||
|
" is specified 'configuration.ini' is used.",
|
||||||
|
metavar="FILE")
|
||||||
|
conf_parser.add_argument("-i", "--input",
|
||||||
|
help="Specify file to be processed. If no file" +
|
||||||
|
" is specified ./imgstat.csv will be used.",
|
||||||
|
metavar='FILE', default="./imgstat.csv")
|
||||||
|
conf_parser.add_argument("-o", "--output",
|
||||||
|
help="Specify output file. Default is 'imgstat.png'",
|
||||||
|
metavar='FILE', default="./imgstat.png")
|
||||||
|
|
||||||
t=Time(table['mjd'],format="mjd",scale="utc")
|
args = conf_parser.parse_args()
|
||||||
pos=SkyCoord(ra=table['ra'],dec=table['de'],frame="icrs",unit="deg")
|
|
||||||
|
|
||||||
loc=EarthLocation(lat=52.8344*u.deg,lon=6.3785*u.deg,height=10*u.m)
|
# Process commandline options and parse configuration
|
||||||
|
cfg = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
|
||||||
|
if args.conf_file:
|
||||||
|
cfg.read([args.conf_file])
|
||||||
|
else:
|
||||||
|
cfg.read('configuration.ini')
|
||||||
|
|
||||||
pa=pos.transform_to(AltAz(obstime=t,location=loc))
|
table = ascii.read(args.input, format="csv")
|
||||||
|
|
||||||
mjd0=np.floor(np.min(table['mjd']))
|
t = Time(table['mjd'], format="mjd", scale="utc")
|
||||||
|
pos = SkyCoord(ra=table['ra'], dec=table['de'], frame="icrs", unit="deg")
|
||||||
|
|
||||||
plt.figure(figsize=(20,10))
|
# Set location
|
||||||
|
loc = EarthLocation(lat=cfg.getfloat('Common', 'observer_lat')*u.deg,
|
||||||
|
lon=cfg.getfloat('Common', 'observer_lon')*u.deg,
|
||||||
|
height=cfg.getfloat('Common', 'observer_el')*u.m)
|
||||||
|
|
||||||
|
pa = pos.transform_to(AltAz(obstime=t, location=loc))
|
||||||
|
|
||||||
|
mjd0 = np.floor(np.min(table['mjd']))
|
||||||
|
|
||||||
|
plt.figure(figsize=(20, 10))
|
||||||
plt.subplot(411)
|
plt.subplot(411)
|
||||||
|
|
||||||
plt.plot(table['mjd']-mjd0,table['mean'],label='Brightness')
|
plt.plot(table['mjd']-mjd0, table['mean'], label='Brightness')
|
||||||
plt.plot(table['mjd']-mjd0,table['std'],label='Variation')
|
plt.plot(table['mjd']-mjd0, table['std'], label='Variation')
|
||||||
plt.ylabel("ADU")
|
plt.ylabel("ADU")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.subplot(412)
|
plt.subplot(412)
|
||||||
plt.plot(table['mjd']-mjd0,pa.az.degree)
|
plt.plot(table['mjd']-mjd0, pa.az.degree)
|
||||||
plt.ylabel("Azimuth (deg)")
|
plt.ylabel("Azimuth (deg)")
|
||||||
plt.subplot(413)
|
plt.subplot(413)
|
||||||
plt.plot(table['mjd']-mjd0,pa.alt.degree)
|
plt.plot(table['mjd']-mjd0, pa.alt.degree)
|
||||||
plt.ylabel("Altitude (deg)")
|
plt.ylabel("Altitude (deg)")
|
||||||
plt.subplot(414)
|
plt.subplot(414)
|
||||||
plt.plot(table['mjd']-mjd0,table['rmsx'],label='RA')
|
plt.plot(table['mjd']-mjd0, table['rmsx'], label='RA')
|
||||||
plt.plot(table['mjd']-mjd0,table['rmsy'],label='Dec')
|
plt.plot(table['mjd']-mjd0, table['rmsy'], label='Dec')
|
||||||
plt.ylim(0,60)
|
plt.ylim(0, 60)
|
||||||
plt.ylabel("Residual (arcseconds)")
|
plt.ylabel("Residual (arcseconds)")
|
||||||
plt.xlabel("MJD - %.0f"%mjd0)
|
plt.xlabel("MJD - %.0f" % mjd0)
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.savefig("imgstat.png")
|
plt.savefig(args.output)
|
||||||
|
|
|
@ -2,46 +2,79 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import glob
|
import glob
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from stio import fourframe
|
from stvid.stio import fourframe
|
||||||
from stars import pixel_catalog, generate_star_catalog
|
from stvid.stars import generate_star_catalog
|
||||||
from astrometry import calibrate_from_reference
|
from stvid.astrometry import calibrate_from_reference
|
||||||
import astropy.units as u
|
import astropy.units as u
|
||||||
from astropy.utils.exceptions import AstropyWarning
|
from astropy.utils.exceptions import AstropyWarning
|
||||||
from astropy.coordinates import EarthLocation, AltAz
|
from astropy.coordinates import EarthLocation
|
||||||
import warnings
|
import warnings
|
||||||
|
import configparser
|
||||||
|
import argparse
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# Read commandline options
|
||||||
|
conf_parser = argparse.ArgumentParser(description='Process captured' +
|
||||||
|
' video frames.')
|
||||||
|
conf_parser.add_argument("-c", "--conf_file",
|
||||||
|
help="Specify configuration file. If no file" +
|
||||||
|
" is specified 'configuration.ini' is used.",
|
||||||
|
metavar="FILE")
|
||||||
|
conf_parser.add_argument("-d", "--directory",
|
||||||
|
help="Specify directory of observations. If no" +
|
||||||
|
" directory is specified parent will be used.",
|
||||||
|
metavar='DIR', dest='file_dir', default=".")
|
||||||
|
|
||||||
|
args = conf_parser.parse_args()
|
||||||
|
|
||||||
|
# Process commandline options and parse configuration
|
||||||
|
cfg = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
|
||||||
|
if args.conf_file:
|
||||||
|
cfg.read([args.conf_file])
|
||||||
|
else:
|
||||||
|
cfg.read('configuration.ini')
|
||||||
|
|
||||||
# Set warnings
|
# Set warnings
|
||||||
warnings.filterwarnings("ignore", category=UserWarning, append=True)
|
warnings.filterwarnings("ignore", category=UserWarning, append=True)
|
||||||
warnings.simplefilter("ignore", AstropyWarning)
|
warnings.simplefilter("ignore", AstropyWarning)
|
||||||
|
|
||||||
# Set location
|
# Set location
|
||||||
loc=EarthLocation(lat=52.8344*u.deg,lon=6.3785*u.deg,height=10*u.m)
|
loc = EarthLocation(lat=cfg.getfloat('Common', 'observer_lat')*u.deg,
|
||||||
|
lon=cfg.getfloat('Common', 'observer_lon')*u.deg,
|
||||||
|
height=cfg.getfloat('Common', 'observer_el')*u.m)
|
||||||
|
|
||||||
# Get files
|
# Get files
|
||||||
files=sorted(glob.glob("2*.fits"))
|
files = sorted(glob.glob(args.file_dir+"2*.fits"))
|
||||||
|
|
||||||
# Statistics file
|
# Statistics file
|
||||||
fstat=open("imgstat.csv","w")
|
fstat = open(args.file_dir+"imgstat.csv", "w")
|
||||||
fstat.write("fname,mjd,ra,de,rmsx,rmsy,mean,std,nstars,nused\n")
|
fstat.write("fname,mjd,ra,de,rmsx,rmsy,mean,std,nstars,nused\n")
|
||||||
|
|
||||||
# Loop over files
|
# Loop over files
|
||||||
for fname in files:
|
for fname in files:
|
||||||
# Generate star catalog
|
# Generate star catalog
|
||||||
pix_catalog=generate_star_catalog(fname)
|
pix_catalog = generate_star_catalog(fname)
|
||||||
|
|
||||||
# Calibrate astrometry
|
# Calibrate astrometry
|
||||||
calibrate_from_reference(fname,"test.fits",pix_catalog)
|
calibrate_from_reference(fname, args.file_dir+"test.fits", pix_catalog)
|
||||||
|
|
||||||
# Stars available and used
|
# Stars available and used
|
||||||
nused=np.sum(pix_catalog.flag==1)
|
nused = np.sum(pix_catalog.flag == 1)
|
||||||
nstars=pix_catalog.nstars
|
nstars = pix_catalog.nstars
|
||||||
|
|
||||||
# Get properties
|
# Get properties
|
||||||
ff=fourframe(fname)
|
ff = fourframe(fname)
|
||||||
|
|
||||||
print("%s,%.8lf,%.6f,%.6f,%.3f,%.3f,%.3f,%.3f,%d,%d"%(ff.fname,ff.mjd,ff.crval[0],ff.crval[1],3600*ff.crres[0],3600*ff.crres[1],np.mean(ff.zavg),np.std(ff.zavg),nstars,nused))
|
|
||||||
fstat.write("%s,%.8lf,%.6f,%.6f,%.3f,%.3f,%.3f,%.3f,%d,%d\n"%(ff.fname,ff.mjd,ff.crval[0],ff.crval[1],3600*ff.crres[0],3600*ff.crres[1],np.mean(ff.zavg),np.std(ff.zavg),nstars,nused))
|
|
||||||
|
|
||||||
|
print(("%s,%.8lf,%.6f,%.6f,%.3f,%.3f," +
|
||||||
|
"%.3f,%.3f,%d,%d") % (ff.fname, ff.mjd, ff.crval[0],
|
||||||
|
ff.crval[1], 3600*ff.crres[0],
|
||||||
|
3600*ff.crres[1], np.mean(ff.zavg),
|
||||||
|
np.std(ff.zavg), nstars, nused))
|
||||||
|
fstat.write(("%s,%.8lf,%.6f,%.6f,%.3f,%.3f,%.3f," +
|
||||||
|
"%.3f,%d,%d\n") % (ff.fname, ff.mjd, ff.crval[0],
|
||||||
|
ff.crval[1], 3600*ff.crres[0],
|
||||||
|
3600*ff.crres[1], np.mean(ff.zavg),
|
||||||
|
np.std(ff.zavg), nstars, nused))
|
||||||
|
|
||||||
fstat.close()
|
fstat.close()
|
||||||
|
|
|
@ -3,3 +3,10 @@ numpy==1.14.2
|
||||||
opencv-python==3.4.0.12
|
opencv-python==3.4.0.12
|
||||||
scipy==1.0.1
|
scipy==1.0.1
|
||||||
git+https://github.com/cbassa/ppgplot.git@master
|
git+https://github.com/cbassa/ppgplot.git@master
|
||||||
|
cycler==0.10.0
|
||||||
|
kiwisolver==1.0.1
|
||||||
|
matplotlib==2.2.2
|
||||||
|
pyparsing==2.2.0
|
||||||
|
python-dateutil==2.7.2
|
||||||
|
pytz==2018.4
|
||||||
|
six==1.11.0
|
||||||
|
|
47
stars.py
47
stars.py
|
@ -1,47 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
from __future__ import print_function
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
class pixel_catalog:
|
|
||||||
"""Pixel catalog"""
|
|
||||||
|
|
||||||
def __init__(self,fname):
|
|
||||||
d=np.loadtxt(fname)
|
|
||||||
if len(d.shape)==2:
|
|
||||||
self.x=d[:,0]
|
|
||||||
self.y=d[:,1]
|
|
||||||
self.mag=d[:,2]
|
|
||||||
self.ra=np.empty_like(self.x)
|
|
||||||
self.dec=np.empty_like(self.x)
|
|
||||||
self.imag=np.empty_like(self.x)
|
|
||||||
self.flag=np.zeros_like(self.x)
|
|
||||||
self.nstars=len(self.mag)
|
|
||||||
else:
|
|
||||||
self.x=None
|
|
||||||
self.y=None
|
|
||||||
self.mag=None
|
|
||||||
self.ra=None
|
|
||||||
self.dec=None
|
|
||||||
self.imag=None
|
|
||||||
self.flag=None
|
|
||||||
self.nstars=0
|
|
||||||
|
|
||||||
def generate_star_catalog(fname):
|
|
||||||
# Skip if file already exists
|
|
||||||
if not os.path.exists(fname+".cat"):
|
|
||||||
# Get sextractor location
|
|
||||||
env=os.getenv("ST_DATADIR")
|
|
||||||
|
|
||||||
# Format command
|
|
||||||
command="sextractor %s -c %s/sextractor/default.sex"%(fname,env)
|
|
||||||
|
|
||||||
# Run sextractor
|
|
||||||
output=subprocess.check_output(command,shell=True,stderr=subprocess.STDOUT)
|
|
||||||
|
|
||||||
# Rename file
|
|
||||||
if os.path.exists("test.cat"):
|
|
||||||
os.rename("test.cat",fname+".cat")
|
|
||||||
|
|
||||||
return pixel_catalog(fname+".cat")
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from __future__ import print_function
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import astropy.units as u
|
||||||
|
from astropy.io import fits
|
||||||
|
from astropy import wcs
|
||||||
|
from astropy.coordinates import SkyCoord, FK5, ICRS
|
||||||
|
from astropy.time import Time
|
||||||
|
from scipy import optimize
|
||||||
|
|
||||||
|
|
||||||
|
# Class for the Tycho 2 catalog
|
||||||
|
class tycho2_catalog:
|
||||||
|
"""Tycho2 catalog"""
|
||||||
|
|
||||||
|
def __init__(self, maxmag=9.0):
|
||||||
|
hdu = fits.open(os.path.join(os.getenv("ST_DATADIR"),
|
||||||
|
"data/tyc2.fits"))
|
||||||
|
|
||||||
|
ra = hdu[1].data.field('RA')*u.deg
|
||||||
|
dec = hdu[1].data.field('DEC')*u.deg
|
||||||
|
mag = hdu[1].data.field('MAG_VT')
|
||||||
|
|
||||||
|
c = mag < maxmag
|
||||||
|
self.ra = ra[c]
|
||||||
|
self.dec = dec[c]
|
||||||
|
self.mag = mag[c]
|
||||||
|
|
||||||
|
|
||||||
|
# Estimate the WCS from a reference file
|
||||||
|
def estimate_wcs_from_reference(ref, fname):
|
||||||
|
# Read header of reference
|
||||||
|
hdu = fits.open(ref)
|
||||||
|
hdu[0].header["NAXIS"] = 2
|
||||||
|
w = wcs.WCS(hdu[0].header)
|
||||||
|
|
||||||
|
# Get time and position from reference
|
||||||
|
tref = Time(hdu[0].header["MJD-OBS"], format="mjd", scale="utc")
|
||||||
|
pref = SkyCoord(ra=w.wcs.crval[0],
|
||||||
|
dec=w.wcs.crval[1],
|
||||||
|
unit="deg",
|
||||||
|
frame="icrs").transform_to(FK5(equinox=tref))
|
||||||
|
|
||||||
|
# Read time from target
|
||||||
|
hdu = fits.open(fname)
|
||||||
|
t = Time(hdu[0].header["MJD-OBS"], format="mjd", scale="utc")
|
||||||
|
|
||||||
|
# Correct wcs
|
||||||
|
dra = (t.sidereal_time("mean", "greenwich")
|
||||||
|
- tref.sidereal_time("mean", "greenwich"))
|
||||||
|
p = FK5(ra=pref.ra+dra, dec=pref.dec, equinox=t).transform_to(ICRS)
|
||||||
|
w.wcs.crval = np.array([p.ra.degree, p.dec.degree])
|
||||||
|
|
||||||
|
return w
|
||||||
|
|
||||||
|
|
||||||
|
# Match the astrometry and pixel catalog
|
||||||
|
def match_catalogs(ast_catalog, pix_catalog, w, rmin):
|
||||||
|
# Select stars towards pointing center
|
||||||
|
ra, dec = w.wcs.crval*u.deg
|
||||||
|
d = np.arccos(np.sin(dec)
|
||||||
|
* np.sin(ast_catalog.dec)
|
||||||
|
+ np.cos(dec)
|
||||||
|
* np.cos(ast_catalog.dec)
|
||||||
|
* np.cos(ra-ast_catalog.ra))
|
||||||
|
c = (d < 30.0*u.deg)
|
||||||
|
ra, dec, mag = ast_catalog.ra[c], ast_catalog.dec[c], ast_catalog.mag[c]
|
||||||
|
|
||||||
|
# Convert RA/Dec to pixels
|
||||||
|
pix = w.wcs_world2pix(np.stack((ra, dec), axis=-1), 0)
|
||||||
|
xs, ys = pix[:, 0], pix[:, 1]
|
||||||
|
|
||||||
|
# Loop over stars
|
||||||
|
nmatch = 0
|
||||||
|
for i in range(len(pix_catalog.x)):
|
||||||
|
dx = xs-pix_catalog.x[i]
|
||||||
|
dy = ys-pix_catalog.y[i]
|
||||||
|
r = np.sqrt(dx*dx+dy*dy)
|
||||||
|
if np.min(r) < rmin:
|
||||||
|
j = np.argmin(r)
|
||||||
|
pix_catalog.ra[i] = ra[j].value
|
||||||
|
pix_catalog.dec[i] = dec[j].value
|
||||||
|
pix_catalog.imag[i] = mag[j]
|
||||||
|
pix_catalog.flag[i] = 1
|
||||||
|
nmatch += 1
|
||||||
|
|
||||||
|
return nmatch
|
||||||
|
|
||||||
|
|
||||||
|
# Residual function
|
||||||
|
def residual(a, x, y, z):
|
||||||
|
return z-(a[0]+a[1]*x+a[2]*y)
|
||||||
|
|
||||||
|
|
||||||
|
# Fit transformation
|
||||||
|
def fit_wcs(w, pix_catalog):
|
||||||
|
x0, y0 = w.wcs.crpix
|
||||||
|
ra0, dec0 = w.wcs.crval
|
||||||
|
dx, dy = pix_catalog.x-x0, pix_catalog.y-y0
|
||||||
|
|
||||||
|
# Iterate to remove outliers
|
||||||
|
nstars = np.sum(pix_catalog.flag == 1)
|
||||||
|
for j in range(10):
|
||||||
|
w = wcs.WCS(naxis=2)
|
||||||
|
w.wcs.crpix = np.array([0.0, 0.0])
|
||||||
|
w.wcs.cd = np.array([[1.0, 0.0], [0.0, 1.0]])
|
||||||
|
w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
|
||||||
|
w.wcs.set_pv([(2, 1, 45.0)])
|
||||||
|
c = pix_catalog.flag == 1
|
||||||
|
|
||||||
|
# Iterate to move crval to crpix location
|
||||||
|
for i in range(5):
|
||||||
|
w.wcs.crval = np.array([ra0, dec0])
|
||||||
|
|
||||||
|
world = np.stack((pix_catalog.ra, pix_catalog.dec), axis=-1)
|
||||||
|
pix = w.wcs_world2pix(world, 1)
|
||||||
|
rx, ry = pix[:, 0], pix[:, 1]
|
||||||
|
|
||||||
|
ax, cov_q, infodict, mesg, ierr = optimize.leastsq(residual,
|
||||||
|
[0.0, 0.0, 0.0],
|
||||||
|
args=(dx[c],
|
||||||
|
dy[c],
|
||||||
|
rx[c]),
|
||||||
|
full_output=1)
|
||||||
|
ay, cov_q, infodict, mesg, ierr = optimize.leastsq(residual,
|
||||||
|
[0.0, 0.0, 0.0],
|
||||||
|
args=(dx[c],
|
||||||
|
dy[c],
|
||||||
|
ry[c]),
|
||||||
|
full_output=1)
|
||||||
|
|
||||||
|
ra0, dec0 = w.wcs_pix2world([[ax[0], ay[0]]], 1)[0]
|
||||||
|
|
||||||
|
# Compute residuals
|
||||||
|
drx = ax[0]+ax[1]*dx+ax[2]*dy-rx
|
||||||
|
dry = ay[0]+ay[1]*dx+ay[2]*dy-ry
|
||||||
|
dr = np.sqrt(drx*drx+dry*dry)
|
||||||
|
rms = np.sqrt(np.sum(dr[c]**2)/len(dr[c]))
|
||||||
|
|
||||||
|
dr[~c] = 1.0
|
||||||
|
c = (dr < 2.0*rms)
|
||||||
|
pix_catalog.flag[~c] = 0
|
||||||
|
|
||||||
|
# Break if converged
|
||||||
|
if np.sum(c) == nstars:
|
||||||
|
break
|
||||||
|
nstars = np.sum(c)
|
||||||
|
|
||||||
|
# Compute residuals
|
||||||
|
rmsx = np.sqrt(np.sum(drx[c]**2)/len(drx[c]))
|
||||||
|
rmsy = np.sqrt(np.sum(dry[c]**2)/len(dry[c]))
|
||||||
|
|
||||||
|
# Store header
|
||||||
|
w = wcs.WCS(naxis=2)
|
||||||
|
w.wcs.crpix = np.array([x0, y0])
|
||||||
|
w.wcs.crval = np.array([ra0, dec0])
|
||||||
|
w.wcs.cd = np.array([[ax[1], ax[2]], [ay[1], ay[2]]])
|
||||||
|
w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
|
||||||
|
w.wcs.set_pv([(2, 1, 45.0)])
|
||||||
|
|
||||||
|
return w, rmsx, rmsy, rms
|
||||||
|
|
||||||
|
|
||||||
|
def add_wcs(fname, w, rmsx, rmsy):
|
||||||
|
# Read fits
|
||||||
|
hdu = fits.open(fname)
|
||||||
|
|
||||||
|
whdr = {"CRPIX1": w.wcs.crpix[0], "CRPIX2": w.wcs.crpix[1],
|
||||||
|
"CRVAL1": w.wcs.crval[0], "CRVAL2": w.wcs.crval[1],
|
||||||
|
"CD1_1": w.wcs.cd[0, 0], "CD1_2": w.wcs.cd[0, 1],
|
||||||
|
"CD2_1": w.wcs.cd[1, 0], "CD2_2": w.wcs.cd[1, 1],
|
||||||
|
"CTYPE1": "RA---TAN", "CTYPE2": "DEC--TAN", "CUNIT1": "DEG",
|
||||||
|
"CUNIT2": "DEG", "CRRES1": rmsx, "CRRES2": rmsy}
|
||||||
|
|
||||||
|
# Add keywords
|
||||||
|
hdr = hdu[0].header
|
||||||
|
for k, v in whdr.items():
|
||||||
|
hdr[k] = v
|
||||||
|
hdu = fits.PrimaryHDU(header=hdr, data=hdu[0].data)
|
||||||
|
hdu.writeto(fname, overwrite=True, output_verify="ignore")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def calibrate_from_reference(fname, ref, pix_catalog):
|
||||||
|
# Estimated WCS
|
||||||
|
w = estimate_wcs_from_reference(ref, fname)
|
||||||
|
|
||||||
|
# Default rms values
|
||||||
|
rmsx = 0.0
|
||||||
|
rmsy = 0.0
|
||||||
|
|
||||||
|
# Read catalogs
|
||||||
|
if (pix_catalog.nstars > 4):
|
||||||
|
ast_catalog = tycho2_catalog(10.0)
|
||||||
|
|
||||||
|
# Match catalogs
|
||||||
|
nmatch = match_catalogs(ast_catalog, pix_catalog, w, 10.0)
|
||||||
|
|
||||||
|
# Fit transformation
|
||||||
|
if nmatch > 4:
|
||||||
|
w, rmsx, rmsy, rms = fit_wcs(w, pix_catalog)
|
||||||
|
|
||||||
|
# Add wcs
|
||||||
|
add_wcs(fname, w, rmsx, rmsy)
|
||||||
|
|
||||||
|
return w, rmsx, rmsy
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from __future__ import print_function
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class pixel_catalog:
|
||||||
|
"""Pixel catalog"""
|
||||||
|
|
||||||
|
def __init__(self, fname):
|
||||||
|
d = np.loadtxt(fname)
|
||||||
|
if len(d.shape) == 2:
|
||||||
|
self.x = d[:, 0]
|
||||||
|
self.y = d[:, 1]
|
||||||
|
self.mag = d[:, 2]
|
||||||
|
self.ra = np.empty_like(self.x)
|
||||||
|
self.dec = np.empty_like(self.x)
|
||||||
|
self.imag = np.empty_like(self.x)
|
||||||
|
self.flag = np.zeros_like(self.x)
|
||||||
|
self.nstars = len(self.mag)
|
||||||
|
else:
|
||||||
|
self.x = None
|
||||||
|
self.y = None
|
||||||
|
self.mag = None
|
||||||
|
self.ra = None
|
||||||
|
self.dec = None
|
||||||
|
self.imag = None
|
||||||
|
self.flag = None
|
||||||
|
self.nstars = 0
|
||||||
|
|
||||||
|
|
||||||
|
def generate_star_catalog(fname):
|
||||||
|
# Skip if file already exists
|
||||||
|
if not os.path.exists(fname+".cat"):
|
||||||
|
# Get sextractor location
|
||||||
|
env = os.getenv("ST_DATADIR")
|
||||||
|
|
||||||
|
# Format command
|
||||||
|
command = "sextractor %s -c %s/sextractor/default.sex" % (fname, env)
|
||||||
|
|
||||||
|
# Run sextractor
|
||||||
|
output = subprocess.check_output(command, shell=True,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
# Rename file
|
||||||
|
if os.path.exists("test.cat"):
|
||||||
|
os.rename("test.cat", fname+".cat")
|
||||||
|
|
||||||
|
return pixel_catalog(fname+".cat")
|
Loading…
Reference in New Issue