Astrometry of a CCD image with python - python

I am trying to implement a really easy astrometry code. I have manually found the coordinates of a couple of stars in my picture (RA/DEC and x/y in pixel).
Everything seems straight forward but I still get weird results, that are off by a couple of degrees.
I am trying to solve for the plate constants of a CCD image I took, where I found the stars coordinates and position in the picture by hand and now I want to try and find the (0,0) point's real worlds coordinates.
I hope someone can help me with my code or can tell me how to do it properly.
Thanks a lot in advance!
Here is my code:
import numpy as np
import os
def astrometry(star_pos, xpix, ypix, focallength, target_RA, target_DEC):
pi = np.pi
DegToRad = pi / 180
RadToDeg = 180 / pi
n = len(star_pos)
(target_RA, target_DEC) = (target_RA, target_DEC)
print(target_RA, target_DEC)
# 1) Obtain star coordinates in pixel and RA/DEC
x_pix = [row[0] for row in star_pos]
y_pix = [row[1] for row in star_pos]
ra_star = [row[2] for row in star_pos]
dec_star = [row[3] for row in star_pos]
# 2) Calculate the standard coordinates of the stars
X_star = np.zeros(n)
Y_star = np.zeros(n)
for i in range(n):
X_star[i] = -(np.cos(DegToRad*dec_star[i])*np.sin(DegToRad*(ra_star[i] - target_RA)))/(np.cos(DegToRad*target_DEC)*np.cos(DegToRad*dec_star[i])*np.cos(DegToRad*(ra_star[i]-target_RA)) + np.sin(DegToRad*target_DEC)*np.sin(DegToRad*dec_star[i]))
Y_star[i] = -(np.sin(DegToRad*target_DEC)*np.cos(DegToRad*dec_star[i])*np.cos(DegToRad*(ra_star[i]-target_RA)) - np.cos(DegToRad*target_DEC)*np.sin(DegToRad*dec_star[i]))/(np.cos(DegToRad*target_DEC)*np.cos(DegToRad*dec_star[i])*np.cos(DegToRad*(ra_star[i]-target_RA)) + np.sin(DegToRad*target_DEC)*np.sin(DegToRad*dec_star[i]))
# 3) Calculate the plate constants (Check my notes)
def calc_plate_const(k,x,y,X):
c_down = ((x[k+1]-x[k])*(y[k]*x[k+2]-y[k+2]*x[k])-(x[k+2]-x[k])*(y[k]*x[k+1]-y[k+1]*x[k]))
c_up = (X[k]*x[k+1]*(y[k]*x[k+2]-y[k+2]*x[k])-X[k+1]*x[k]*(y[k]*x[k+2]-y[k+2]*x[k])-X[k]*x[k+2]*(y[k]*x[k+1]-y[k+1]*x[k])-X[k+2]*x[k]*(y[k]*x[k+1]-y[k+1]*x[k]))
c = c_up/c_down
print('c',c)
b = ((X[k]*x[k+1]-X[k+1]*x[k])-(x[k+1]-x[k])*c)/(y[k]*x[k+1]-y[k+1]*x[k])
print('b',b)
a = (X[k]-b*y[k]-c)/x[k]
print('a', a)
return(a,b,c)
(a,b,c) = calc_plate_const(0,x_pix,y_pix,X_star)
(d,e,f) = calc_plate_const(0,x_pix,y_pix,Y_star)
print(target_RA,target_DEC)
# 4) Calculate the standard coordinates for the object
# HIER object at (0,0)
(x_ob, y_ob) = (0,0)
X_ob = a*x_ob + b*y_ob + c
Y_ob = d*x_ob + e*y_ob + f
print('x', x_pix, x_ob, 'y', y_pix, y_ob)
print('X', X_star, X_ob, 'Y', Y_star, Y_ob)
print('RA', ra_star, 'DEC', dec_star)
# 5) Calculate the RA/DEC of the objects standard coordinates
a = target_RA + np.arctan(DegToRad*((-X_ob)/(np.cos(DegToRad*target_DEC)- Y_ob*np.sin(DegToRad*target_DEC))))
d = target_DEC - np.arcsin(DegToRad*((np.sin(DegToRad*target_DEC) + Y_ob*np.cos(DegToRad*target_DEC))/(np.sqrt(1 + X_ob**2 + Y_ob**2))))
print('RA in rad', a, 'DEC in rad', d)
print('RA',a,target_RA, 'DEC',d, target_DEC)
return(a,d)
The Input is for example an array with the stars position in pixel of the image and degree of the real world
star pos = [[1948.2, 1205.8, 132.34058333333334, -3.4429722222222225], [153.90000000000001, 1892.5, 131.08620833333333, -5.0947499999999994]
# star_pos [x_pos in pix, y_pos in pix, RA, DEC]
(x_pix, y_pix) = (0.0135, 0.0135)
# pixel size
focallength = 0.7168
(target_RA, target_DEC) = (131.683014444 -3.91890194444)
# But I am not sure how accurate that is, it is more of an assumption. I would say if I look at the star map manually, it looks quite a bit off...
I would expect to see for the (0,0) point RA to be around 133° and DEC -5.75°

Related

Is there a way to dynamically change variables depending on length of list

May seem like a silly question is there any way to create variables depending on the number of objects in a list. The context to this is I am trying to create an n body simulation. One in which the user can manually chose the planets they want in the solar system and then run the script with only those planets. So prior to them running the chosing the planets and running the script I will not know how many variables to create. This problem of how many variables to create is show by:
The mass is created by a class objects:
class Objects():
position = np.full((1, 3), 0)
velocity = np.full((1, 3), 0)
acceleration = np.full((1, 3), 0)
name = ""
mass = np.full((1, 1), 0)
planets_init = np.full((1, 3), 0)
def __init__(self, Name, Mass, initPosition, initVelocity, initAcceleration):
au = 149597870.700e3
v_factor = 1731460
self.name = Name
self.mass = Mass
The function is solved by using scipy.integrate.solve_ivp by:
three_body_sol = sci.integrate.solve_ivp(fun=Objects.ThreeBodyEquations,t_span=domain,y0=init_params,args=(G,planets_mass,N), max_step=max_step)
Where the function is:
def ThreeBodyEquations(t,w,G,mass,N):
# N being the number of objects in the system so in this case 2
m1, m2 = mass
#Unpack all the variables from the array "w"
r1=w[:3]
r2=w[3:6]
v1=w[6:9]
v2=w[9:12]
# Harry's attempt
G = G
planets_pos = np.vstack((r1, r2))
planets_mass = mass # np.vstack((m1, m2))
# positions r = [x,y,z] for all particles
x = planets_pos[:,0:1]
y = planets_pos[:,1:2]
z = planets_pos[:,2:3]
# matrix that stores all pairwise particle separations: r_j - r_i
dx = x.T - x
dy = y.T - y
dz = z.T - z
# matrix that stores 1/r^3 for all particle pairwise particle separations
inv_r3 = (dx**2 + dy**2 + dz**2)
inv_r3[inv_r3>0] = inv_r3[inv_r3>0]**(-1.5)
ax = G * (dx * inv_r3) # planets_mass
ay = G * (dy * inv_r3) # planets_mass
az = G * (dz * inv_r3) # planets_mass
# planets_acceleration = np.sqrt(ax**2 + ay**2 + az**2)
planets_acceleration = np.vstack((ax,ay,az))
planets_acceleration = planets_acceleration.flatten()
dv1bydt=planets_acceleration[0::N]
dv2bydt=planets_acceleration[1::N]
# print(planets_acceleration)
dr1bydt=v1
dr2bydt=v2
#Package the derivatives into one final size-18 array
r12_derivs=np.concatenate((dr1bydt,dr2bydt))
r_derivs = r12derivs
v12_derivs=np.concatenate((dv1bydt,dv2bydt))
v_derivs= v12_derivs
derivs=np.concatenate((r_derivs,v_derivs))
return derivs
My main question centres around this function. When the user defines what planets they want to use I have no idea what the number of planets will be. I know the range which is might be but that’s all. Is there a feasible way to do this or is this a lost cause?
I hope this clarifys the question with some additional code. Sorry about the previous lack of code its, I didn’t realise it was valuable at first and didn’t want to burden with too much code.
I would create a function that calculates the mass first, such as
mass = 0
for planet in planet_list:
mass = mass+ planet.mass
return mass
total_mass = calculate_combined_mass([earth, Mars, jupiter] )
three_body_equations(t,w,G,total_mass,N)

GPS data outliers

I have set of latitude and longitude points but i am having hard time to build algorithm that could remove outlier points(shown in orange circles). What i tried is to get the vector angle between points and then remove if reach certain threshold(i.e. large angle), is there a better way? also, when i use use the dataframe in pandas, i have problem with the looping. loop when the vector is large, back to the base point(red circle) then remove until the next point which is somewhat straight. thanks in advance.enter image description here
Here is my sample code:
df_4 = df_3.copy()
def angle(vector1, vector2):
unit_vector_1 = vector1 / np.linalg.norm(vector1)
unit_vector_2 = vector2 / np.linalg.norm(vector2)
dot_product = np.dot(unit_vector_1, unit_vector_2)
angle = np.arccos(dot_product)
return np.degrees(angle)
df_4.reset_index(inplace=True)
th = 20
angle_vector = 0
idx=0
a=0
for x in range(len(df_4)):
print('for:',str(idx))
vector1 = [(df_4.loc[idx+1,'Longitude'] - df_4.loc[idx,'Longitude']),(df_4.loc[idx+1,'Latitude']-df_4.loc[idx,'Latitude'])]
vector2 = [(df_4.loc[idx+2,'Longitude'] - df_4.loc[idx+1,'Longitude']),(df_4.loc[idx+2,'Latitude']-df_4.loc[idx+1,'Latitude'])]
angle_vector = angle(vector1,vector2)
df_4.at[idx+1,'angle_vector'] = angle_vector
#print(angle_vector)
print('before:',len(df_4))
i = 0
while(angle_vector > th & (angle_vector not in [a for a in range(86,96)])):
df_4.drop(df_4.index[idx+1], inplace=True )
df_4.reset_index(inplace=True,drop=True)
print('after:',len(df_4))
print('while:',str(i))
vector3 = [(df_4.loc[idx+i,'Longitude'] - df_4.loc[idx+i-a,'Longitude']), (df_4.loc[idx+i,'Latitude']-df_4.loc[idx+i-a,'Latitude'])]
angle_vector = angle(vector1,vector3)
i += 1
idx+=1
`

Modeling satellite's orbit around earth in python?

I've been following along with a tutorial, which models Earth, Venus, and the Sun. The code runs just fine. However, I'm trying to model a satellite's orbit about the earth, where the earth is the center of its "universe" at position (0,0). I'm running into issues as my satellite flies off on a tangent but I'm not sure why.
import numpy as np
import numpy as np
class Planet():
def __init__(self,vx=0.0,vy=0.0,px=0.0,py=0.0,mass=0.0):
self.vx=vx
self.vy=vy
self.px=px
self.py=py
self.mass = mass
def attract(p1,p2,grav=6.67428e-11):
dx = p1.px - p2.px
dy = p1.py - p2.py
d = np.sqrt(dx**2 + dy**2)
force = (grav * p1.mass * p2.mass)/d**2
theta = math.atan2(dy,dx)
fx = math.cos(theta)*force
fy = math.sin(theta)*force
return fx,fy
def loop(bodies,epochs=10,timestep=10):
# compute forces
forces = {}
for i in range(0,len(bodies)):
total_fx = 0
total_fy = 0
for j in range(0,len(bodies)):
if i == j:
continue
fx,fy = attract(bodies[i],bodies[j])
total_fx += fx
total_fy += fy
forces[i] = (total_fx,total_fy)
#apply forces
sat_tups = []
for e in range(epochs):
for i in range(len(bodies)):
if bodies[i] == satellite:
sat_tups.append((bodies[i].px,bodies[i].py))
fx,fy = forces[i]
bodies[i].vx += (fx/bodies[i].mass*timestep)
bodies[i].vy += (fy/bodies[i].mass*timestep)
bodies[i].px += bodies[i].vx*timestep
bodies[i].py += bodies[i].vy*timestep
return sat_tups
earth = Planet(mass = 5.9742 * 10**24)
satellite = Planet(mass= 3000.0, px= 1414.0, py= 1414.0, vx=2300.0,vy=2300.0)
tups = loop(bodies=[earth,satellite])
tups
>>>
[(1414.0, 1414.0),
(7050856421.79636, 7050856421.79636),
(21152543437.38908, 21152543437.38908),
(42305062460.77817, 42305062460.77817),
(70508413491.96361, 70508413491.96361),
(105762596530.9454, 105762596530.9454),
(148067611577.72357, 148067611577.72357),
(197423458632.2981, 197423458632.2981),
(253830137694.66898, 253830137694.66898),
(317287648764.83624, 317287648764.83624)]
I'm not sure what the issue is. Perhaps, centering Earth at (0,0) simply isn't going to work. Or perhaps these laws of physics are only applicable when bodies are of relatively similar sizes (else more intricate rules are at play.)
To answer this question, please address what is causing the satellite to fly off? (I would have guessed that it would crash into earth, not sling-shot away).

Is there a fast Numpy algorithm for mapping a Polar grid into a Cartesian grid?

I have a grid containing some data in polar coordinates, simulating data obtained from a LIDAR for the SLAM problem. Each row in the grid represents the angle, and each column represents a distance. The values contained in the grid store a weighted probability of the occupancy map for a Cartesian world.
After converting to Cartesian Coordinates, I obtain something like this:
This mapping is intended to work in a FastSLAM application, with at least 10 particles. The performance I am obtaining isn't good enough for a reliable application.
I have tried with nested loops, using the scipy.ndimage.geometric_transform library and accessing directly the grid with pre-computed coordinates.
In those examples, I am working with a 800x800 grid.
Nested loops: aprox 300ms
i = 0
for scan in scans:
hit = scan < laser.range_max
if hit:
d = np.linspace(scan + wall_size, 0, num=int((scan+ wall_size)/cell_size))
else:
d = np.linspace(scan, 0, num=int(scan/cell_size))
for distance in distances:
x = int(pos[0] + d * math.cos(angle[i]+pos[2]))
y = int(pos[1] + d * math.sin(angle[i]+pos[2]))
if distance > scan:
grid_cart[y][x] = grid_cart[y][x] + hit_weight
else:
grid_cart[y][x] = grid_cart[y][x] + miss_weight
i = i + 1
Scipy library (Described here): aprox 2500ms (Gives a smoother result since it interpolates the empty cells)
grid_cart = S.ndimage.geometric_transform(weight_mat, polar2cartesian,
order=0,
output_shape = (weight_mat.shape[0] * 2, weight_mat.shape[0] * 2),
extra_keywords = {'inputshape':weight_mat.shape,
'origin':(weight_mat.shape[0], weight_mat.shape[0])})
def polar2cartesian(outcoords, inputshape, origin):
"""Coordinate transform for converting a polar array to Cartesian coordinates.
inputshape is a tuple containing the shape of the polar array. origin is a
tuple containing the x and y indices of where the origin should be in the
output array."""
xindex, yindex = outcoords
x0, y0 = origin
x = xindex - x0
y = yindex - y0
r = np.sqrt(x**2 + y**2)
theta = np.arctan2(y, x)
theta_index = np.round((theta + np.pi) * inputshape[1] / (2 * np.pi))
return (r,theta_index)
Pre-computed indexes: 80ms
for i in range(0, 144000):
gird_cart[ys[i]][xs[i]] = grid_polar_1d[i]
I am not very used to python and Numpy, and I feel I am skipping an easy and fast way to solve this problem. Are there any other alternatives to solve that?
Many thanks to you all!
I came across a piece of code that seems to behave x10 times faster (8ms):
angle_resolution = 1
range_max = 400
a, r = np.mgrid[0:int(360/angle_resolution),0:range_max]
x = (range_max + r * np.cos(a*(2*math.pi)/360.0)).astype(int)
y = (range_max + r * np.sin(a*(2*math.pi)/360.0)).astype(int)
for i in range(0, int(360/angle_resolution)):
cart_grid[y[i,:],x[i,:]] = polar_grid[i,:]

Trilinear Interpolation on Voxels at specific angle

I'm currently attempting to implement this algorithm for volume rendering in Python, and am conceptually confused about their method of generating the LH histogram (see section 3.1, page 4).
I have a 3D stack of DICOM images, and calculated its gradient magnitude and the 2 corresponding azimuth and elevation angles with it (which I found out about here), as well as finding the second derivative.
Now, the algorithm is asking me to iterate through a set of voxels, and "track a path by integrating the gradient field in both directions...using the second order Runge-Kutta method with an integration step of one voxel".
What I don't understand is how to use the 2 angles I calculated to integrate the gradient field in said direction. I understand that you can use trilinear interpolation to get intermediate voxel values, but I don't understand how to get the voxel coordinates I want using the angles I have.
In other words, I start at a given voxel position, and want to take a 1 voxel step in the direction of the 2 angles calculated for that voxel (one in the x-y direction, the other in the z-direction). How would I take this step at these 2 angles and retrieve the new (x, y, z) voxel coordinates?
Apologies in advance, as I have a very basic background in Calc II/III, so vector fields/visualization of 3D spaces is still a little rough for me.
Creating 3D stack of DICOM images:
def collect_data(data_path):
print "collecting data"
files = [] # create an empty list
for dirName, subdirList, fileList in os.walk(data_path):
for filename in fileList:
if ".dcm" in filename:
files.append(os.path.join(dirName,filename))
# Get reference file
ref = dicom.read_file(files[0])
# Load dimensions based on the number of rows, columns, and slices (along the Z axis)
pixel_dims = (int(ref.Rows), int(ref.Columns), len(files))
# Load spacing values (in mm)
pixel_spacings = (float(ref.PixelSpacing[0]), float(ref.PixelSpacing[1]), float(ref.SliceThickness))
x = np.arange(0.0, (pixel_dims[0]+1)*pixel_spacings[0], pixel_spacings[0])
y = np.arange(0.0, (pixel_dims[1]+1)*pixel_spacings[1], pixel_spacings[1])
z = np.arange(0.0, (pixel_dims[2]+1)*pixel_spacings[2], pixel_spacings[2])
# Row and column directional cosines
orientation = ref.ImageOrientationPatient
# This will become the intensity values
dcm = np.zeros(pixel_dims, dtype=ref.pixel_array.dtype)
origins = []
# loop through all the DICOM files
for filename in files:
# read the file
ds = dicom.read_file(filename)
#get pixel spacing and origin information
origins.append(ds.ImagePositionPatient) #[0,0,0] coordinates in real 3D space (in mm)
# store the raw image data
dcm[:, :, files.index(filename)] = ds.pixel_array
return dcm, origins, pixel_spacings, orientation
Calculating gradient magnitude:
def calculate_gradient_magnitude(dcm):
print "calculating gradient magnitude"
gradient_magnitude = []
gradient_direction = []
gradx = np.zeros(dcm.shape)
sobel(dcm,0,gradx)
grady = np.zeros(dcm.shape)
sobel(dcm,1,grady)
gradz = np.zeros(dcm.shape)
sobel(dcm,2,gradz)
gradient = np.sqrt(gradx**2 + grady**2 + gradz**2)
azimuthal = np.arctan2(grady, gradx)
elevation = np.arctan(gradz,gradient)
azimuthal = np.degrees(azimuthal)
elevation = np.degrees(elevation)
return gradient, azimuthal, elevation
Converting to patient coordinate system to get actual voxel position:
def get_patient_position(dcm, origins, pixel_spacing, orientation):
"""
Image Space --> Anatomical (Patient) Space is an affine transformation
using the Image Orientation (Patient), Image Position (Patient), and
Pixel Spacing properties from the DICOM header
"""
print "getting patient coordinates"
world_coordinates = np.empty((dcm.shape[0], dcm.shape[1],dcm.shape[2], 3))
affine_matrix = np.zeros((4,4), dtype=np.float32)
rows = dcm.shape[0]
cols = dcm.shape[1]
num_slices = dcm.shape[2]
image_orientation_x = np.array([ orientation[0], orientation[1], orientation[2] ]).reshape(3,1)
image_orientation_y = np.array([ orientation[3], orientation[4], orientation[5] ]).reshape(3,1)
pixel_spacing_x = pixel_spacing[0]
# Construct affine matrix
# Method from:
# http://nipy.org/nibabel/dicom/dicom_orientation.html
T_1 = origins[0]
T_n = origins[num_slices-1]
affine_matrix[0,0] = image_orientation_y[0] * pixel_spacing[0]
affine_matrix[0,1] = image_orientation_x[0] * pixel_spacing[1]
affine_matrix[0,3] = T_1[0]
affine_matrix[1,0] = image_orientation_y[1] * pixel_spacing[0]
affine_matrix[1,1] = image_orientation_x[1] * pixel_spacing[1]
affine_matrix[1,3] = T_1[1]
affine_matrix[2,0] = image_orientation_y[2] * pixel_spacing[0]
affine_matrix[2,1] = image_orientation_x[2] * pixel_spacing[1]
affine_matrix[2,3] = T_1[2]
affine_matrix[3,3] = 1
k1 = (T_1[0] - T_n[0])/ (1 - num_slices)
k2 = (T_1[1] - T_n[1])/ (1 - num_slices)
k3 = (T_1[2] - T_n[2])/ (1 - num_slices)
affine_matrix[:3, 2] = np.array([k1,k2,k3])
for z in range(num_slices):
for r in range(rows):
for c in range(cols):
vector = np.array([r, c, 0, 1]).reshape((4,1))
result = np.matmul(affine_matrix, vector)
result = np.delete(result, 3, axis=0)
result = np.transpose(result)
world_coordinates[r,c,z] = result
# print "Finished slice ", str(z)
# np.save('./data/saved/world_coordinates_3d.npy', str(world_coordinates))
return world_coordinates
Now I'm at the point where I want to write this function:
def create_lh_histogram(patient_positions, dcm, magnitude, azimuthal, elevation):
print "constructing LH histogram"
# Get 2nd derivative
second_derivative = gaussian_filter(magnitude, sigma=1, order=1)
# Determine if voxels lie on boundary or not (thresholding)
# Still have to code out: let's say the thresholded voxels are in
# a numpy array called voxels
#Iterate through all thresholded voxels and integrate gradient field in
# both directions using 2nd-order Runge-Kutta
vox_it = voxels.nditer(voxels, flags=['multi_index'])
while not vox_it.finished:
# ???

Categories