mirror of
https://gitlab.astro-wise.org/micado/atmosphericmodel.git
synced 2026-10-30 05:01:02 +00:00
194 lines
5.9 KiB
Python
194 lines
5.9 KiB
Python
#!/usr/bin/env Python3
|
|
import numpy as np
|
|
from scipy.optimize import fsolve
|
|
|
|
|
|
class ADC:
|
|
def __init__(self, wedge=(1.6554, 6.28927, -1.48306)):
|
|
self.p1 = prism('S-LAH71', 15, wedge[0], wedge[1])
|
|
self.p2 = prism('S-FPL51', 15, wedge[1], wedge[2])
|
|
self.p3 = prism('S-FPL51', 15, wedge[2], wedge[1])
|
|
self.p4 = prism('S-LAH71', 15, wedge[1], wedge[0])
|
|
self.theta = 90
|
|
|
|
def setOrientation(self, theta):
|
|
self.p1.setPrismOrientation(theta)
|
|
self.p2.setPrismOrientation(theta)
|
|
self.p3.setPrismOrientation(180-1*theta)
|
|
self.p4.setPrismOrientation(180-1*theta)
|
|
self.theta = theta
|
|
|
|
def setFieldRotation(self, FR):
|
|
self.p1.setPrismOrientation(self.theta-FR)
|
|
self.p2.setPrismOrientation(self.theta-FR)
|
|
self.p3.setPrismOrientation(180-1*self.theta-FR)
|
|
self.p4.setPrismOrientation(180-1*self.theta-FR)
|
|
|
|
def calculateDispersion(self, l1, l2, hx=0, hy=0, refraction=False):
|
|
'''
|
|
Calculates the dispersion for the ADC using the current position,
|
|
for a normalized on sky field angle (hx, hy). Default field position
|
|
is in the center of the FoV.
|
|
'''
|
|
_prismList = [self.p1, self.p2, self.p3, self.p4]
|
|
_d1x_list, _d1y_list = [], []
|
|
_pupilRatio = 0.07943/38.542
|
|
|
|
hx = hx * 7.5e-3
|
|
hy = hy * 7.5e-3
|
|
ix = hx * 1/_pupilRatio
|
|
iy = hy * 1/_pupilRatio
|
|
|
|
for l in [l1,l2]:
|
|
_deltaListX = np.zeros(len(_prismList))
|
|
_deltaListY = np.zeros(len(_prismList))
|
|
for p in range(4):
|
|
_n_g = _prismList[p].indexOfRefraction(l)
|
|
_b1x = _prismList[p].tilt1x
|
|
_b1y = _prismList[p].tilt1y
|
|
|
|
_b2x = _prismList[p].tilt2x
|
|
_b2y = _prismList[p].tilt2y
|
|
|
|
if p==0:
|
|
_t_iz_n_x = ix # Zero before first prism
|
|
_t_iz_n_y = iy # Zero before first prism
|
|
|
|
# X direction
|
|
_t_i1_n_x = _t_iz_n_x
|
|
_d1x = deviationCalculation3(_t_i1_n_x, _b1x, _b2x, _n_g)
|
|
_t_tz_n_x = _t_iz_n_x - _d1x
|
|
_t_iz_n_x = _t_tz_n_x # Set output to new input (for the next prism)
|
|
|
|
# Y direction
|
|
_t_i1_n_y = _t_iz_n_y
|
|
_d1y = deviationCalculation3(_t_i1_n_y, _b1y, _b2y, _n_g)
|
|
_t_tz_n_y = _t_iz_n_y - _d1y
|
|
_t_iz_n_y = _t_tz_n_y # Set output to new input (for the next prism)
|
|
|
|
# Adding the deviations to the list with delta terms
|
|
_deltaListX[p] = _d1x
|
|
_deltaListY[p] = _d1y
|
|
_d1x_list.append(_deltaListX.sum())
|
|
_d1y_list.append(_deltaListY.sum())
|
|
|
|
self.dispX = ( _d1x_list[1] - _d1x_list[0] ) * 3600 * 1000 * _pupilRatio
|
|
self.dispY = ( _d1y_list[1] - _d1y_list[0] ) * 3600 * 1000 * _pupilRatio
|
|
|
|
if refraction:
|
|
rx = ( _d1x_list[0] ) * 3600 * 1000 * _pupilRatio
|
|
ry = ( _d1y_list[0] ) * 3600 * 1000 * _pupilRatio
|
|
return rx, ry
|
|
return self.dispX, self.dispY
|
|
|
|
def optimize(self, inputDispersion, l1, l2, x0=50):
|
|
_solver = fsolve(optimizeFunction, x0=x0, args=(self, inputDispersion, l1, l2))
|
|
return _solver[0]
|
|
|
|
|
|
|
|
|
|
class prism:
|
|
def __init__(self, glass, thickness, tilt1, tilt2=None):
|
|
self.glass = glass
|
|
self.thickness = thickness
|
|
self.tilt1x = tilt1
|
|
self.tilt1y = 0
|
|
self.tilt2x = tilt2
|
|
self.tilt2y = 0
|
|
|
|
# Remember original tilts for reset:
|
|
self.tilt1x_OG = tilt1
|
|
self.tilt1y_OG = 0
|
|
self.tilt2x_OG = tilt2
|
|
self.tilt2y_OG = 0
|
|
|
|
|
|
def indexOfRefraction(self, l):
|
|
if self.glass == 'S-FPL51':
|
|
a = [1.170105050E+00, 4.757107830E-02, 7.638324450E-01]
|
|
b = [6.162039240E-03, 2.633728760E-02, 1.418826420E+02]
|
|
elif self.glass == 'S-LAH71':
|
|
a = [1.982800310E+00, 3.167584500E-01, 2.444726460E+00]
|
|
b = [1.189874560E-02, 5.271560010E-02, 2.132206970E+02]
|
|
return dispersionFormula(l, a, b)
|
|
|
|
def setPrismOrientation(self, theta):
|
|
self.tilt1x, self.tilt1y = tiltRotation(self.tilt1x_OG, self.tilt1y_OG, theta)
|
|
self.tilt2x, self.tilt2y = tiltRotation(self.tilt2x_OG, self.tilt2y_OG, theta)
|
|
|
|
|
|
|
|
def tiltRotation(tx,ty, theta):
|
|
theta = np.deg2rad(theta-90)
|
|
tx_new = tx * np.cos(theta) - ty * np.sin(theta)
|
|
ty_new = tx * np.sin(theta) + ty * np.cos(theta)
|
|
return tx_new, ty_new
|
|
|
|
|
|
def dispersionFormula(l, a, b):
|
|
n = np.sqrt( (a[0]* l**2)/(l**2 - b[0]) + (a[1] * l**2) / (l**2 - b[1]) + (a[2] * l**2) / (l**2 - b[2]) + 1)
|
|
return n
|
|
|
|
|
|
# def deviationCalculation(t_i1, b1, b2, n):
|
|
# Returns deviation of the ray after the prism with apex angle alpha
|
|
# Units: degrees
|
|
# DO NOT USE!
|
|
# alpha = b2 - b1
|
|
# delta = t_i1 + np.arcsin( np.sin(np.deg2rad(alpha)) * np.sqrt(n**2-(np.sin(np.deg2rad(t_i1)))**2) - np.sin(np.deg2rad(t_i1)) * np.cos(np.deg2rad(alpha)) )*180/np.pi - alpha
|
|
# return delta
|
|
|
|
|
|
def deviationCalculation2(t_i1, b1, b2, n):
|
|
# Returns deviation of the ray after the prism with apex angle alpha
|
|
# only now using Snell's law only
|
|
# Units: degrees
|
|
t_i1 = np.deg2rad(t_i1)
|
|
b1 = np.deg2rad(b1)
|
|
b2 = np.deg2rad(b2)
|
|
|
|
t_i = b1 - t_i1
|
|
t_f = np.arcsin( 1 / n * np.sin( t_i ) )
|
|
t_f_z = b1 - t_f
|
|
|
|
t_i = b2 - t_f_z
|
|
t_f = np.arcsin( n / 1 * np.sin( t_i ) )
|
|
t_f_z = b2 - t_f
|
|
|
|
delta = t_i1 - t_f_z
|
|
delta = np.degrees(delta)
|
|
return delta
|
|
|
|
def deviationCalculation3(t_i1, b1, b2, n):
|
|
# Using the formula from Hagen & Tkazcyk (2011)
|
|
# Gives exactly the same results as deviationCaclulation2(), above
|
|
t_i1 = np.deg2rad(t_i1)
|
|
b1 = np.deg2rad(b1)
|
|
b2 = np.deg2rad(b2)
|
|
alpha = b2 - b1
|
|
delta = t_i1 - b2 - np.arcsin( n * np.sin( np.arcsin(1/n * np.sin(t_i1 - b1) ) - alpha ) )
|
|
return np.degrees(delta)
|
|
|
|
def optimizeFunction(theta, adcObject, inputDispersion, l1, l2, FR=None):
|
|
if FR:
|
|
FRdeg = np.deg2rad(FR) # 30 degrees field rotation
|
|
inputDispersionX = np.cos(FRdeg) * inputDispersion
|
|
inputDispersionY = np.sin(FRdeg) * inputDispersion
|
|
adcObject.setOrientation(theta)
|
|
ADCdisp = adcObject.calculateDispersion(l1,l2)
|
|
return np.sqrt((ADCdisp[1] - inputDispersionY)**2 + (ADCdisp[0] - inputDispersionX)**2)
|
|
else:
|
|
adcObject.setOrientation(theta)
|
|
ADCdisp = adcObject.calculateDispersion(l1,l2)
|
|
ADCdisp = ADCdisp[1]
|
|
return ADCdisp - inputDispersion
|
|
|
|
def angleFunc(x, a):
|
|
'''
|
|
Function to fit the ideal angle on
|
|
'''
|
|
return np.arccos(a * np.tan(np.deg2rad(x))) * 180/np.pi
|
|
|
|
|