import openpyxl as pyxl
class Surface:
'''
Surface结构体
变量含:曲率半径(radius), 厚度(thickness),
物方折射率及像方折射率(nd0, nF0, nC0, nd1, nF1, nC1),
半通光孔径(semi_dia) (物在无穷远时代表视场角)
'''
radius = 0
thickness = 0
nd0 = 0
nF0 = 0
nC0 = 0
nd1 = 0
nF1 = 0
nC1 = 0
semi_dia = 0
def __init__(self, _radius, _thickness, _nd1, _nF1, _nC1, _semi_dia):
self.radius = _radius
self.thickness = _thickness
self.nd1 = _nd1
self.nF1 = _nF1
self.nC1 = _nC1
self.semi_dia = _semi_dia
def read_excel(input_file_name = 'input.xlsx'):
"""
作用: 读取输入表中的物面、镜面、入瞳、像面数据
输入: 同一目录下的文件名,或指定文件路径。缺省默认为'input.xlsx'
输出: object; lens[ ]数组,元素是每面数据; entrance_pupil; image
"""
wb = pyxl.load_workbook(input_file_name)
ws = wb['Sheet1']
temp = ws.iter_rows()
print(temp)
parameter = []
for row in ws.rows:
for cell in row:
parameter.append(cell.value)
i = 0
objdata = []
lensdata = []
imgdata = []
entrance_pupil = []
"""
作用:
首先略过输入表的列标题,读取物面信息,
再逐面读取光学面信息,
最后读取像面信息;
"""
for i in range(len(parameter)):
if(i >= 14 and i < 21):
objdata.append(parameter[i])
elif(i >= 21 and i < len(parameter)-7):
lensdata.append(parameter[i])
elif(i >= len(parameter)-7):
imgdata.append(parameter[i])
lensnumber = int(len(lensdata) / 7)
object = Surface(objdata[1], objdata[2], objdata[3], objdata[4], objdata[5], objdata[6])
lens = []
for i in range(lensnumber-1):
lens.append(Surface(lensdata[i*7+1], lensdata[i*7+2], lensdata[i*7+3], lensdata[i*7+4], lensdata[i*7+5], lensdata[i*7+6]))
lens[i].nd0 = parameter[(i+2)*7+3]
lens[i].nF0 = parameter[(i+2)*7+4]
lens[i].nC0 = parameter[(i+2)*7+5]
#读取光阑信息
entrance_pupil = Surface(lensdata[(lensnumber-1)*7+1], lensdata[(lensnumber-1)*7+2], lensdata[(lensnumber-1)*7+3], lensdata[(lensnumber-1)*7+4],\
lensdata[(lensnumber-1)*7+5], lensdata[(lensnumber-1)*7+6])
image = Surface(imgdata[1], imgdata[2], imgdata[3], imgdata[4], imgdata[5], imgdata[6])
return object, lens, entrance_pupil, image
def write_excel(data1 = [], data2 = [], data3 = [], data4 = [], data5 = [], data6 = [], filename = 'output.xlsx'):
"""
作用: 写EXCEL
输入: 共38个数据,分为
data1: f', l', lH', lp', y0', y0, 0.707', xt', xs', delta_x', F光近轴像位置, C光近轴像位置; 共12个数据
data2: 全孔径的 d光像点, F光像点, C光像点, 球差, 位置色差; 共5个数据
data3: 0.707孔径的 d光像点, F光像点, C光像点, 球差, 位置色差; 共5个数据
data4: 0孔径位置色差; 共1个数据
data5: 全视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差; 共8个数据
data6: 0.707视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差; 共8个数据
filename: 输出文件名
"""
wb = pyxl.Workbook()
ws = wb.active
label1 = ['f\'', 'l\'', 'lH\'', 'lp\'', 'y0\'', 'y0, 0.707\'', 'xt\'', 'xs\'', 'delta_x\'', 'F光近轴像位置', 'C光近轴像位置']
for i in range(len(label1)):
ws.cell(row = 1, column = i+1, value = label1[i])
ws.cell(row = 2, column = i+1, value = data1[i])
ws.merge_cells('A4:E4')
ws['A4'] = '轴上点全孔径d、F、C光'
label_DFC = ['d光像点', 'F光像点', 'C光像点', '球差', '位置色差']
for i in range(len(label_DFC)):
ws.cell(row = 5, column = i+1, value = label_DFC[i])
ws.cell(row = 6, column = i+1, value = data2[i])
ws.merge_cells('A8:E8')
ws['A8'] = '轴上点0.7孔径d、F、C光'
for i in range(len(label_DFC)):
ws.cell(row = 9, column = i+1, value = label_DFC[i])
ws.cell(row = 10, column = i+1, value = data3[i])
ws['A12'] = '轴上点0孔径位置色差'
ws['A13'] = data4[0]
ws.merge_cells('A15:H15')
ws['A15'] = '全视场'
label3 = ['全孔径子午彗差', '0.7孔径子午彗差', 'd光实际像高', 'F光实际像高', 'C光实际像高', '绝对畸变', '相对畸变', '倍率色差']
for i in range(len(label3)):
ws.cell(row = 16, column = i+1, value = label3[i])
ws.cell(row = 17, column = i+1, value = data5[i])
ws.merge_cells('A19:H19')
ws['A19'] = '0.7视场'
for i in range(len(label3)):
ws.cell(row = 20, column = i+1, value = label3[i])
ws.cell(row = 21, column = i+1, value = data6[i])
for c in range(1, 12): # 注意,列序数从1开始,但必须转变为A\B等字母
w = pyxl.utils.get_column_letter(c) # 把列序数转变为字母
ws.column_dimensions[w].width = 15
wb.save(filename)
'''
---迭代递推模块---
包含:
lens_recursion(...), 给定入射光线L、U,对各镜面进行光线追迹并返回各面L、U数据
astigmatism_recursion(...), 给定各面L、U、I、PA参数,求子午场曲,弧矢场曲,像散
'''
EPS = 1e-5 #PA校验精度
def lens_recursion(L, U, H, lens, raytype, isParallel):
"""
函数作用: 给定轴上光线入射初值, 对每个镜面进行递推
输入: 初始物方截距(L)、物方倾斜角(U)、镜面数据(lens)、光线类型(raytype = 'd', 'F', 'C')
是否平行于主光轴(isParallel, 1平行, 0倾斜)
输出: 光线经过每面镜面后的L, L', U', I, I', PA数组
"""
lensnumber = len(lens)
recursion = 0
sinI = 0; sinII = 0
I = 0; II = 0
UU = 0
LL = 0
L_data = [L]
LL_data = []
UU_data = [U]
I_data = []
II_data = []
PA_data = []
if(raytype == 'd'): #d光,折射率取nd
for recursion in range(lensnumber):
if(recursion == 0 and isParallel):
sinI = H / lens[recursion].radius
else:
sinI = (L - lens[recursion].radius) / lens[recursion].radius * np.sin(U)
sinII = lens[recursion].nd0 / lens[recursion].nd1 * sinI
if(np.abs(sinI) < 1 and np.abs(sinII) < 1):
I = np.arcsin(sinI); II = np.arcsin(sinII)
I_data.append(I); II_data.append(II)
UU = U + I - II
LL = lens[recursion].radius + lens[recursion].radius * sinII / np.sin(UU)
LL_data.append(LL); UU_data.append(UU)
#PA校对法
PA1 = L * np.sin(U) / np.cos((I-U)/2)
PA2 = LL * np.sin(UU) / np.cos((II-UU)/2)
if(np.abs(PA1-PA2) > EPS and isParallel == 0):
print("Recursion not right.")
print(f'PA1 = {PA1}, PA2 = {PA2}')
PA_data.append(PA2)
#过渡公式
U = UU
L = LL - lens[recursion].thickness
#舍去最后一个过渡的物方截距
if(recursion != lensnumber - 1):
L_data.append(L)
if(raytype == 'F'): #F光,折射率取nF
for recursion in range(lensnumber):
if(recursion == 0 and isParallel):
sinI = H / lens[recursion].radius
else:
sinI = (L - lens[recursion].radius) / lens[recursion].radius * np.sin(U)
sinII = lens[recursion].nF0 / lens[recursion].nF1 * sinI
if(np.abs(sinI) < 1 and np.abs(sinII) < 1):
I = np.arcsin(sinI); II = np.arcsin(sinII)
I_data.append(I); II_data.append(II)
UU = U + I - II
LL = lens[recursion].radius + lens[recursion].radius * sinII / np.sin(UU)
LL_data.append(LL); UU_data.append(UU)
#PA校对法
PA1 = L * np.sin(U) / np.cos((I-U)/2)
PA2 = LL * np.sin(UU) / np.cos((II-UU)/2)
if(np.abs(PA1-PA2) > EPS and isParallel == 0):
print("Recursion not right.")
print(f'PA1 = {PA1}, PA2 = {PA2}')
PA_data.append(PA2)
#过渡公式
U = UU
L = LL - lens[recursion].thickness
#舍去最后一个过渡的物方截距
if(recursion != lensnumber - 1):
L_data.append(L)
if(raytype == 'C'): #C光,折射率取nC
for recursion in range(lensnumber):
if(recursion == 0 and isParallel):
sinI = H / lens[recursion].radius
else:
sinI = (L - lens[recursion].radius) / lens[recursion].radius * np.sin(U)
sinII = lens[recursion].nC0 / lens[recursion].nC1 * sinI
if(np.abs(sinI) < 1 and np.abs(sinII) < 1):
I = np.arcsin(sinI); II = np.arcsin(sinII)
I_data.append(I); II_data.append(II)
UU = U + I - II
LL = lens[recursion].radius + lens[recursion].radius * sinII / np.sin(UU)
LL_data.append(LL); UU_data.append(UU)
#PA校对法
PA1 = L * np.sin(U) / np.cos((I-U)/2)
PA2 = LL * np.sin(UU) / np.cos((II-UU)/2)
if(np.abs(PA1-PA2) > EPS and isParallel == 0):
print("Recursion not right.")
print(f'PA1 = {PA1}, PA2 = {PA2}')
PA_data.append(PA2)
#过渡公式
U = UU
L = LL - lens[recursion].thickness
#舍去最后一个过渡的物方截距
if(recursion != lensnumber - 1):
L_data.append(L)
#print(f'LEN_DATA\n{len(L_data)}, {len(LL_data)}, {len(UU_data)}, {len(I_data)}, {len(II_data)}, {len(PA_data)}')
return L_data, LL_data, UU_data, I_data, II_data, PA_data
def astigmatism_recursion(L_data, LL_data, UU_data, I_data, II_data, PA_data, lens, raytype, isParallel):
"""
函数作用: 求像散, 场曲
输入: 由lens_recursion算得的各参数
输出: 子午场曲(xt), 弧矢场曲(xs), 像散(delta_x)
"""
lensnumber = len(lens)
for recursion in range(lensnumber):
if(raytype == 'd'):
x = PA_data[recursion] ** 2 / (2 * lens[recursion].radius)
#求t、s初值
if(recursion == 0 and isParallel == 0):
t = s = (L_data[0] - x) / np.cos(UU_data[0])
if(recursion == 1 and isParallel == 1):
t = s = (L_data[1] - x) / np.cos(UU_data[1])
temp_tt = (lens[recursion].nd1 * np.cos(II_data[recursion]) - lens[recursion].nd0 * np.cos(I_data[recursion]))\
/ lens[recursion].radius + lens[recursion].nd0 * np.cos(I_data[recursion]) ** 2 / t
tt = 1 / temp_tt * lens[recursion].nd1 * np.cos(II_data[recursion]) ** 2 #t'
temp_ss = (lens[recursion].nd1 * np.cos(II_data[recursion]) - lens[recursion].nd0 * np.cos(I_data[recursion]))\
/ lens[recursion].radius + lens[recursion].nd0 / s
ss = 1 / temp_ss * lens[recursion].nd1 #s'
if(recursion < lensnumber-1):
#过渡公式
xx = PA_data[recursion + 1] ** 2 / (2 * lens[recursion+1].radius) #x(i+1)
D = (lens[recursion].thickness - x + xx) / np.cos(UU_data[recursion+1])
t = tt - D
s = ss - D
if(recursion == lensnumber-1):
#最后一面
lt = tt * np.cos(UU_data[recursion + 1]) + x
ls = ss * np.cos(UU_data[recursion + 1]) + x
xt = lt - LL_data[-1]
xs = ls - LL_data[-1]
delta_x = xt - xs
if(raytype == 'C'):
x = PA_data[recursion] ** 2 / (2 * lens[recursion].radius)
#求t、s初值
if(recursion == 0 and isParallel == 0):
t = s = (L_data[0] - x) / np.cos(UU_data[0])
if(recursion == 1 and isParallel == 1):
t = s = (L_data[1] - x) / np.cos(UU_data[1])
temp_tt = (lens[recursion].nC1 * np.cos(II_data[recursion]) - lens[recursion].nC0 * np.cos(I_data[recursion]))\
/ lens[recursion].radius + lens[recursion].nC0 * np.cos(I_data[recursion]) ** 2 / t
tt = 1 / temp_tt * lens[recursion].nC1 * np.cos(II_data[recursion]) ** 2 #t'
temp_ss = (lens[recursion].nC1 * np.cos(II_data[recursion]) - lens[recursion].nC0 * np.cos(I_data[recursion]))\
/ lens[recursion].radius + lens[recursion].nC0 / s
ss = 1 / temp_ss * lens[recursion].nC1 #s'
if(recursion < lensnumber-1):
#过渡公式
xx = PA_data[recursion + 1] ** 2 / (2 * lens[recursion+1].radius) #x(i+1)
D = (lens[recursion].thickness - x + xx) / np.cos(UU_data[recursion+1])
t = tt - D
s = ss - D
if(recursion == lensnumber-1):
#最后一面
lt = tt * np.cos(UU_data[recursion + 1]) + x
ls = ss * np.cos(UU_data[recursion + 1]) + x
xt = lt - LL_data[recursion]
xs = ls - LL_data[recursion]
delta_x = xt - xs
if(raytype == 'F'):
x = PA_data[recursion] ** 2 / (2 * lens[recursion].radius)
#求t、s初值
if(recursion == 0 and isParallel == 0):
t = s = (L_data[0] - x) / np.cos(UU_data[0])
if(recursion == 1 and isParallel == 1):
t = s = (L_data[1] - x) / np.cos(UU_data[1])
temp_tt = (lens[recursion].nF1 * np.cos(II_data[recursion]) - lens[recursion].nF0 * np.cos(I_data[recursion]))\
/ lens[recursion].radius + lens[recursion].nF0 * np.cos(I_data[recursion]) ** 2 / t
tt = 1 / temp_tt * lens[recursion].nF1 * np.cos(II_data[recursion]) ** 2 #t'
temp_ss = (lens[recursion].nF1 * np.cos(II_data[recursion]) - lens[recursion].nF0 * np.cos(I_data[recursion]))\
/ lens[recursion].radius + lens[recursion].nF0 / s
ss = 1 / temp_ss * lens[recursion].nF1 #s'
if(recursion < lensnumber-1):
#过渡公式
xx = PA_data[recursion + 1] ** 2 / (2 * lens[recursion+1].radius) #x(i+1)
D = (lens[recursion].thickness - x + xx) / np.cos(UU_data[recursion+1])
t = tt - D
s = ss - D
if(recursion == lensnumber-1):
#最后一面
lt = tt * np.cos(UU_data[recursion + 1]) + x
ls = ss * np.cos(UU_data[recursion + 1]) + x
xt = lt - LL_data[recursion]
xs = ls - LL_data[recursion]
delta_x = xt - xs
return xt, xs, delta_x
'''
---L、U计算模块---
包含:
Calc(...), 给定孔径系数、视场系数、物面、镜面、入瞳信息,光线类型,利用迭代递推模块进行中间计算,返回各面L、U、I、PA信息。
'''
def Calc(K_eta, K_W, object, lens, pupil, image, raytype):
"""
作用:计算某一孔径、视场光线在逐个面的(L,U)坐标数据
输入:K_eta为孔径取点系数,K_W为视场取点系数
"""
#初始化(L,U)坐标列表
L0 = []
L1 = []
U = []
I0 = []
I1 = []
PA = []
A = pupil.semi_dia #入瞳半径
eta = K_eta * A #像高
#物在无穷远且光线与光轴平行时的情形
if(object.thickness == None and K_W == 0):
h0 = K_eta * pupil.semi_dia
L0, L1, U, I0, I1, PA = lens_recursion(0, 0, h0, lens, raytype, 1)
#其余情形 -> 转化为轴上点在有限远处
else:
if((object.thickness == None and K_W != 0)):
U = -K_W * object.semi_dia * np.pi/180
L = pupil.thickness + eta/np.tan(U)
elif(object.semi_dia == 0):
L = -object.thickness
U = np.arcsin(K_eta * A/np.sqrt(A**2 + Lp**2))
elif(object.semi_dia != 0):
Lp = pupil.thickness #第一面到入瞳的距离
y = K_W * object.semi_dia #物方线视场
U = np.arctan((y-eta)/(Lp+object.thickness))
L = Lp + eta/((y-eta)/(Lp+object.thickness))
L0, L1, U, I0, I1, PA = lens_recursion(L, U, 0, lens, raytype, 0)
return L0, L1, U, I0, I1, PA
'''
---数据处理模块---
包含:
data_processing(...), 总输出函数,给定物面、镜面、入瞳、像面信息,向主函数返回所有待求量
Calc_ff(...), 计算计算像方焦距、焦点位置、主点位置
Calc_ll(...), 计算像距、像高、像散、场曲
Calc_lp(...), 计算出瞳距
Calc_sp(...), 计算全孔径和0.7孔径时的实际像距、球差、位置色差
Calc_coma(...), 计算全视场、0.7视场、正负全孔径、正负0.7孔径的彗差、两个视场dFC光实际像高,绝对畸变、相对畸变、倍率色差
'''
def data_processing(object, lens, pupil, image):
'''
共输出38个数据
data1:f', l', lH', lp', y0', y0, 0.707', xt', xs', delta_x', F光近轴像位置, C光近轴像位置;共12个数据
data2:轴上点全孔径的 d光像点, F光像点, C光像点, 球差, 位置色差;共5个数据
data3:轴上点0.707孔径的 d光像点, F光像点, C光像点, 球差, 位置色差;共5个数据
data4:轴上点0孔径位置色差;共1个数据
data5:全视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差;共8个数据
data6:0.707视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差;共8个数据
'''
oo = 1e-20 #定义近轴小量
ff, l_H = Calc_ff(object, lens, pupil, image, oo)
ll, ll_F, ll_C, y0, y0_707, xt, xs, delta_x = Calc_ll(object, lens, pupil, image, oo, l_H, ff)
l_p = Calc_lp(lens, pupil, image, oo)
data1 = [ff, ll, l_H, l_p, y0, y0_707, xt, xs, delta_x, ll_F, ll_C]
data2, data3, data4 = Calc_sp(object, lens, pupil, image, ll, oo)
data5, data6 = Calc_coma(object, lens, pupil, image, oo, y0, y0_707)
return data1, data2, data3, data4, data5, data6
#1、计算像方焦距、焦点位置、主点位置
def Calc_ff(object, lens, pupil, image, oo):
#定义一个近轴无穷远物面
object_ = Surface(0, None, object.nd1, object.nF1, object.nC1, None)
#计算平行入射的近轴光线
L0 = Calc(oo, 0, object_, lens, pupil, image, 'd')[0]
L1 = Calc(oo, 0, object_, lens, pupil, image, 'd')[1]
#计算L0,L1的乘积
L0_product = L1_product = L0F_product = L1F_product = L0C_product = L1C_product = 1
for i in range(len(L0)-1):
L0_product = L0_product * L0[i+1]
for i in range(len(L1)):
L1_product = L1_product * L1[i]
#利用正切计算法计算焦距
ff = L1_product/L0_product
#计算像方焦点和主点位置
l_F = L1[-1]
l_H = ff - l_F
return ff, l_H
#2、计算像距和像高,像散及场曲
def Calc_ll(object, lens, pupil, image, oo, l_H, ff):
#计算物面入射的近轴光线
if(object.thickness == None):
ll = ff-l_H
else:
L0 = Calc(oo, 0, object, lens, pupil, image, 'd')[0]
L1 = Calc(oo, 0, object, lens, pupil, image, 'd')[1]
ll = L1[-1] #最后一个光学面的像距
#计算像高
if(object.thickness == None):
y0 = ff * np.tan(object.semi_dia * np.pi/180)
y0_707 = ff * np.tan(0.70 * object.semi_dia * np.pi/180)
else:
A = object.semi_dia
y0 = A
for i in range(len(L1)):
n0 = lens[i].nd0
n1 = lens[i].nd1
l0 = L0[i]
l1 = L1[i]
beta = (n0*l1)/(n1*l0) #理想的横向放大倍率
y0 = y0 * beta
y0_707 = 0.70 * y0
if(object.thickness == None and object.semi_dia == None):
isparallel = 1
else:
isparallel = 0
#计算C光和F光的近轴像距
L1F = Calc(oo, 0, object, lens, pupil, image, 'F')[1]
ll_F = L1F[-1] #F光
L1C = Calc(oo, 0, object, lens, pupil, image, 'C')[1]
ll_C = L1C[-1] #C光
#计算场曲及像散
L0, L1, U, I0, I1, PA = Calc(0, 1, object, lens, pupil, image, 'd')
if(object.thickness == None):
L0[0] = 10**15
else:
L0[0] = -object.thickness
L1 = Calc(oo, 0, object, lens, pupil, image, 'd')[1]
xt, xs, delta_x = astigmatism_recursion(L0, L1, U, I0, I1, PA, lens, 'd', isparallel)
return ll, ll_F, ll_C, y0, y0_707, xt, xs, delta_x
#3、计算出瞳距
def Calc_lp(lens, pupil, image, oo):
#定义入瞳面
object_ = Surface(pupil.radius, pupil.thickness, pupil.nd1, pupil.nC1, pupil.nF1, pupil.semi_dia)
L1 = Calc(oo, 0, object_, lens, pupil, image, 'd')[1]
l_p = L1[-1]
return l_p
"""
4、计算全孔径和0.7孔径时的实际像距、球差、位置色差
变量:SPAB代表球差spherical aberration,PCA代表位置色差Position Chromatic Aberration
"""
def Calc_sp(object, lens, pupil, image, ll, oo):
L1d1 = Calc(1, 0, object, lens, pupil, image, 'd')[1]
ll_d1 = L1d1[-1]
SPAB1 = ll_d1 - ll
L1d707 = Calc(0.70, 0, object, lens, pupil, image, 'd')[1]
ll_d707 = L1d707[-1]
SPAB707 = ll_d707 - ll
L1F1 = Calc(1, 0, object, lens, pupil, image, 'F')[1]
ll_F1 = L1F1[-1]
L1C1 = Calc(1, 0, object, lens, pupil, image, 'C')[1]
ll_C1 = L1C1[-1]
L1F707 = Calc(0.70, 0, object, lens, pupil, image, 'F')[1]
ll_F707 = L1F707[-1]
L1C707 = Calc(0.70, 0, object, lens, pupil, image, 'C')[1]
ll_C707 = L1C707[-1]
L1F0 = Calc(oo, 0, object, lens, pupil, image, 'F')[1]
ll_F0 = L1F0[-1]
L1C0 = Calc(oo, 0, object, lens, pupil, image, 'C')[1]
ll_C0 = L1C0[-1]
PCA1 = ll_F1 - ll_C1
PCA707 = ll_F707 - ll_C707
PCA0 = ll_F0 - ll_C0
data2 = [ll_d1, ll_F1, ll_C1, SPAB1, PCA1]
data3 = [ll_d707, ll_F707, ll_C707, SPAB707, PCA707]
data4 = [PCA0]
return data2, data3, data4
#5、计算全视场、0.7视场、正负全孔径、正负0.7孔径的彗差、两个视场dFC光实际像高,绝对畸变、相对畸变、倍率色差
def Calc_coma(object, lens, pupil, image, oo, y0, y0_707):
#轴上物点,求理想像面
LL_perfect_d = Calc(oo, 0, object, lens, pupil, image, 'd')[1]
'''获取实际光线数据'''
#全视场,全孔径数据
LL_upper_d = Calc(1, -1, object, lens, pupil, image, 'd')[1]
UU_upper_d = Calc(1, -1, object, lens, pupil, image, 'd')[2]
LL_central_d = Calc(0, -1, object, lens, pupil, image, 'd')[1]
UU_central_d = Calc(0, -1, object, lens, pupil, image, 'd')[2]
LL_down_d = Calc(-1, -1, object, lens, pupil, image, 'd')[1]
UU_down_d = Calc(-1, -1, object, lens, pupil, image, 'd')[2]
#全视场,0.7孔径数据
LL_upper_d_1_07 = Calc(0.7, -1, object, lens, pupil, image, 'd')[1]
UU_upper_d_1_07 = Calc(0.7, -1, object, lens, pupil, image, 'd')[2]
LL_central_d_1_07 = Calc(0, -1, object, lens, pupil, image, 'd')[1]
UU_central_d_1_07 = Calc(0, -1, object, lens, pupil, image, 'd')[2]
LL_down_d_1_07 = Calc(-0.7, -1, object, lens, pupil, image, 'd')[1]
UU_down_d_1_07 = Calc(-0.7, -1, object, lens, pupil, image, 'd')[2]
#0.7视场,全孔径数据
LL_upper_d_07_1 = Calc(1, -0.7, object, lens, pupil, image, 'd')[1]
UU_upper_d_07_1 = Calc(1, -0.7, object, lens, pupil, image, 'd')[2]
LL_central_d_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'd')[1]
UU_central_d_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'd')[2]
LL_down_d_07_1 = Calc(-1, -0.7, object, lens, pupil, image, 'd')[1]
UU_down_d_07_1 = Calc(-1, -0.7, object, lens, pupil, image, 'd')[2]
#0.7视场,0.7孔径数据
LL_upper_d_07_07 = Calc(0.7, -0.7, object, lens, pupil, image, 'd')[1]
UU_upper_d_07_07 = Calc(0.7, -0.7, object, lens, pupil, image, 'd')[2]
LL_central_d_07_07 = Calc(0, -0.7, object, lens, pupil, image, 'd')[1]
UU_central_d_07_07 = Calc(0, -0.7, object, lens, pupil, image, 'd')[2]
LL_down_d_07_07 = Calc(-0.7, -0.7, object, lens, pupil, image, 'd')[1]
UU_down_d_07_07 = Calc(-0.7, -0.7, object, lens, pupil, image, 'd')[2]
'''计算彗差'''
#全视场、全孔径彗差
y_upper = (LL_perfect_d[-1] - LL_upper_d[-1]) * np.tan(UU_upper_d[-1])
y_down = (LL_perfect_d[-1] - LL_down_d[-1]) * np.tan(UU_down_d[-1])
y_central = (LL_perfect_d[-1] - LL_central_d[-1]) * np.tan(UU_central_d[-1])
COMA1 = (y_upper + y_down) / 2 - y_central
#全视场,0.7孔径彗差
y_upper_1_07 = (LL_perfect_d[-1] - LL_upper_d_1_07[-1]) * np.tan(UU_upper_d_1_07[-1])
y_down_1_07 = (LL_perfect_d[-1] - LL_down_d_1_07[-1]) * np.tan(UU_down_d_1_07[-1])
y_central_1_07 = (LL_perfect_d[-1] - LL_central_d_1_07[-1]) * np.tan(UU_central_d_1_07[-1])
COMA2 = (y_upper_1_07 + y_down_1_07) / 2 - y_central_1_07
#0.7视场,全孔径
y_upper_07_1 = (LL_perfect_d[-1] - LL_upper_d_07_1[-1]) * np.tan(UU_upper_d_07_1[-1])
y_down_07_1 = (LL_perfect_d[-1] - LL_down_d_07_1[-1]) * np.tan(UU_down_d_07_1[-1])
y_central_07_1 = (LL_perfect_d[-1] - LL_central_d_07_1[-1]) * np.tan(UU_central_d_07_1[-1])
COMA3 = (y_upper_07_1 + y_down_07_1) / 2 - y_central_07_1
#0.7视场,0.7孔径
y_upper_07_07 = (LL_perfect_d[-1] - LL_upper_d_07_07[-1]) * np.tan(UU_upper_d_07_07[-1])
y_down_07_07 = (LL_perfect_d[-1] - LL_down_d_07_07[-1]) * np.tan(UU_down_d_07_07[-1])
y_central_07_07 = (LL_perfect_d[-1] - LL_central_d_07_07[-1]) * np.tan(UU_central_d_07_07[-1])
COMA4 = (y_upper_07_07 + y_down_07_07) / 2 - y_central_07_07
'''计算实际像高'''
#全视场dFC光实际像高
LL_central_C = Calc(0, -1, object, lens, pupil, image, 'C')[1]
UU_central_C = Calc(0, -1, object, lens, pupil, image, 'C')[2]
LL_central_F = Calc(0, -1, object, lens, pupil, image, 'F')[1]
UU_central_F = Calc(0, -1, object, lens, pupil, image, 'F')[2]
y_d_1 = (LL_perfect_d[-1] - LL_central_d[-1]) * np.tan(UU_central_d[-1])
y_C_1 = (LL_perfect_d[-1] - LL_central_C[-1]) * np.tan(UU_central_C[-1])
y_F_1 = (LL_perfect_d[-1] - LL_central_F[-1]) * np.tan(UU_central_F[-1])
#0.7视场dFC光实际像高
LL_central_C_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'C')[1]
UU_central_C_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'C')[2]
LL_central_F_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'F')[1]
UU_central_F_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'F')[2]
y_d_07 = (LL_perfect_d[-1] - LL_central_d_07_1[-1]) * np.tan(UU_central_d_07_1[-1])
y_C_07 = (LL_perfect_d[-1] - LL_central_C_07_1[-1]) * np.tan(UU_central_C_07_1[-1])
y_F_07 = (LL_perfect_d[-1] - LL_central_F_07_1[-1]) * np.tan(UU_central_F_07_1[-1])
'''计算倍率色差'''
MCA1 = (y_F_1 - y_C_1) #全视场倍率色差
MCA07 = (y_F_07 - y_C_07) #0.7视场倍率色差
'''计算绝对畸变,相对畸变'''
DIS1 = y_d_1 - y0
rel_DIS1 = DIS1 / y0
DIS07 = y_d_07 - y0_707
rel_DIS07 = DIS07 / y0_707
#全视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差
data5 = [COMA1, COMA2, y_d_1, y_F_1, y_C_1, DIS1, rel_DIS1, MCA1]
#0.707视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差
data6 = [COMA3, COMA4, y_d_07, y_F_07, y_C_07, DIS07, rel_DIS07, MCA07]
return data5, data6
input_label = ['输入-无限远平行光.xlsx', '输入-轴上有限远物.xlsx']
output_label = ['输出-无限远平行光.xlsx', '输出-轴上有限远物.xlsx']
for i in range(2):
object, lens, pupil, image = read_excel(input_label[i])
data1, data2, data3, data4, data5, data6 = data_processing(object, lens, pupil, image)
write_excel(data1, data2, data3, data4, data5, data6, output_label[i])
print("\nOUTPUT SUCCESSFULLY!\n")以上是一个光学计算软件的程序,请为其编写一个GUI界面包括各种镜头参数的输入,光学表面及其参数的输入,结果的输出与显示,类似下面这种(形式差不多)class MainApplication:
def __init__(self, root):
self.root = root
self.root.title("光学系统计算程序")
self.root.geometry("1000x850")
# 创建光学系统对象
self.system = OpticalSystem()
# 创建GUI组件
self.create_widgets()
# 加载默认设置(如果有)
self.load_default_settings()
def create_widgets(self):
# 主框架
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 文件操作框架
file_frame = ttk.LabelFrame(main_frame, text="文件操作")
file_frame.pack(fill=tk.X, padx=5, pady=5)
# 文件路径输入
ttk.Label(file_frame, text="文件路径:").grid(row=0, column=0, padx=5, pady=5)
self.file_path_entry = ttk.Entry(file_frame, width=50)
self.file_path_entry.grid(row=0, column=1, padx=5, pady=5)
# 文件操作按钮
browse_btn = ttk.Button(file_frame, text="浏览...", command=self.browse_file)
browse_btn.grid(row=0, column=2, padx=5, pady=5)
load_btn = ttk.Button(file_frame, text="加载系统参数", command=self.load_system)
load_btn.grid(row=0, column=3, padx=5, pady=5)
save_btn = ttk.Button(file_frame, text="保存系统参数", command=self.save_system)
save_btn.grid(row=0, column=4, padx=5, pady=5)
# 左侧面板 - 输入参数
left_frame = ttk.LabelFrame(main_frame, text="系统参数输入")
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
# 右侧面板 - 结果展示
right_frame = ttk.LabelFrame(main_frame, text="计算结果")
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5)
# 创建左侧面板的子组件
self.create_input_panel(left_frame)
# 创建右侧面板的子组件
self.create_result_panel(right_frame)
# 计算按钮
calc_frame = ttk.Frame(main_frame)
calc_frame.pack(fill=tk.X, padx=5, pady=10)
calc_btn = ttk.Button(calc_frame, text="开始计算", command=self.calculate, width=15)
calc_btn.pack(side=tk.LEFT, padx=10)
save_result_btn = ttk.Button(calc_frame, text="保存计算结果", command=self.save_results, width=15)
save_result_btn.pack(side=tk.LEFT, padx=10)
clear_btn = ttk.Button(calc_frame, text="清除所有", command=self.clear_all, width=15)
clear_btn.pack(side=tk.LEFT, padx=10)
def browse_file(self):
"""浏览文件按钮处理函数"""
file_path = filedialog.askopenfilename(
title="选择文件",
filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
)
if file_path:
self.file_path_entry.delete(0, tk.END)
self.file_path_entry.insert(0, file_path)
def create_input_panel(self, parent):
# 入瞳参数
pupil_frame = ttk.LabelFrame(parent, text="入瞳参数")
pupil_frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Label(pupil_frame, text="入瞳直径 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.entrance_diameter_entry = ttk.Entry(pupil_frame, width=15)
self.entrance_diameter_entry.grid(row=0, column=1, padx=5, pady=5)
ttk.Label(pupil_frame, text="入瞳位置 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w")
self.entrance_position_entry = ttk.Entry(pupil_frame, width=15)
self.entrance_position_entry.grid(row=0, column=3, padx=5, pady=5)
# 色光类型选择
ttk.Label(pupil_frame, text="色光类型:").grid(row=1, column=0, padx=5, pady=5, sticky="w")
self.light_type_var = tk.StringVar(value="d")
self.light_type_combo = ttk.Combobox(pupil_frame, textvariable=self.light_type_var, width=12, state="readonly")
self.light_type_combo["values"] = ("d", "f", "c")
self.light_type_combo.grid(row=1, column=1, padx=5, pady=5, sticky="w")
# 物方参数
object_frame = ttk.LabelFrame(parent, text="物方参数")
object_frame.pack(fill=tk.X, padx=5, pady=5)
# 物距选择
self.object_var = tk.BooleanVar(value=True) # True: 无穷远, False: 有限远
ttk.Radiobutton(object_frame, text="物在无穷远", variable=self.object_var, value=True,
command=self.toggle_object_input).grid(row=0, column=0, padx=5, pady=5)
ttk.Radiobutton(object_frame, text="物在有限远", variable=self.object_var, value=False,
command=self.toggle_object_input).grid(row=0, column=1, padx=5, pady=5)
# 无穷远参数
self.infinite_frame = ttk.Frame(object_frame)
self.infinite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5)
ttk.Label(self.infinite_frame, text="半视场角 (度):").grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.field_angle_entry = ttk.Entry(self.infinite_frame, width=15)
self.field_angle_entry.grid(row=0, column=1, padx=5, pady=5)
# 有限远参数 (初始隐藏)
self.finite_frame = ttk.Frame(object_frame)
self.finite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5)
self.finite_frame.grid_remove() # 初始隐藏
ttk.Label(self.finite_frame, text="物距 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.object_distance_entry = ttk.Entry(self.finite_frame, width=15)
self.object_distance_entry.grid(row=0, column=1, padx=5, pady=5)
ttk.Label(self.finite_frame, text="物高 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w")
self.object_height_entry = ttk.Entry(self.finite_frame, width=15)
self.object_height_entry.grid(row=0, column=3, padx=5, pady=5)
ttk.Label(self.finite_frame, text="孔径角 (度):").grid(row=0, column=4, padx=5, pady=5, sticky="w")
self.aperture_angle_entry = ttk.Entry(self.finite_frame, width=15)
self.aperture_angle_entry.grid(row=0, column=5, padx=5, pady=5)
# 光学表面输入
surface_frame = ttk.LabelFrame(parent, text="光学表面参数")
surface_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 表面输入控件
input_frame = ttk.Frame(surface_frame)
input_frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Label(input_frame, text="曲率半径 (mm):").grid(row=0, column=0, padx=5, pady=5)
self.radius_entry = ttk.Entry(input_frame, width=10)
self.radius_entry.grid(row=0, column=1, padx=5, pady=5)
self.radius_entry.insert(0, "50")
ttk.Label(input_frame, text="厚度 (mm):").grid(row=0, column=2, padx=5, pady=5)
self.thickness_entry = ttk.Entry(input_frame, width=10)
self.thickness_entry.grid(row=0, column=3, padx=5, pady=5)
self.thickness_entry.insert(0, "5")
ttk.Label(input_frame, text="折射率 (nd):").grid(row=0, column=4, padx=5, pady=5)
self.nd_entry = ttk.Entry(input_frame, width=10)
self.nd_entry.grid(row=0, column=5, padx=5, pady=5)
self.nd_entry.insert(0, "1.5")
ttk.Label(input_frame, text="阿贝数 (vd):").grid(row=0, column=6, padx=5, pady=5)
self.vd_entry = ttk.Entry(input_frame, width=10)
self.vd_entry.grid(row=0, column=7, padx=5, pady=5)
self.vd_entry.insert(0, "60")
button_frame = ttk.Frame(input_frame)
button_frame.grid(row=0, column=8, padx=10)
add_btn = ttk.Button(button_frame, text="添加表面", command=self.add_surface)
add_btn.pack(side=tk.LEFT, padx=5)
remove_btn = ttk.Button(button_frame, text="删除表面", command=self.remove_surface)
remove_btn.pack(side=tk.LEFT, padx=5)
# 表面列表
list_frame = ttk.Frame(surface_frame)
list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
columns = ("#", "曲率半径 (mm)", "厚度 (mm)", "折射率 (nd)", "阿贝数 (vd)")
self.surface_tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=8)
for col in columns:
self.surface_tree.heading(col, text=col)
self.surface_tree.column(col, width=100, anchor=tk.CENTER)
vsb = ttk.Scrollbar(list_frame, orient="vertical", command=self.surface_tree.yview)
self.surface_tree.configure(yscrollcommand=vsb.set)
self.surface_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
vsb.pack(side=tk.RIGHT, fill=tk.Y)
def create_result_panel(self, parent):
# 结果文本框
self.result_text = tk.Text(parent, wrap=tk.WORD)
result_scroll_y = ttk.Scrollbar(parent, orient="vertical", command=self.result_text.yview)
result_scroll_x = ttk.Scrollbar(parent, orient="horizontal", command=self.result_text.xview)
self.result_text.configure(yscrollcommand=result_scroll_y.set, xscrollcommand=result_scroll_x.set)
result_scroll_y.pack(side=tk.RIGHT, fill=tk.Y)
result_scroll_x.pack(side=tk.BOTTOM, fill=tk.X)
self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 设置初始文本
self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n")
self.result_text.configure(state=tk.DISABLED)
def toggle_object_input(self):
"""切换物方参数输入界面"""
if self.object_var.get(): # 无穷远
self.infinite_frame.grid()
self.finite_frame.grid_remove()
else: # 有限远
self.infinite_frame.grid_remove()
self.finite_frame.grid()
def add_surface(self):
"""添加光学表面"""
try:
# 获取输入值
r = self.radius_entry.get().strip()
d = self.thickness_entry.get().strip()
nd = self.nd_entry.get().strip()
vd = self.vd_entry.get().strip()
# 处理输入值
r = float(r) if r and r.lower() != "inf" else float('inf')
d = float(d) if d else 0.0
nd = float(nd) if nd else 1.0
vd = float(vd) if vd else 0.0
# 添加到系统
surface = Surface(r, d, nd, vd)
self.system.surfaces.append(surface)
# 添加到树形视图
r_str = "平面" if r == float('inf') else f"{r:.2f}"
self.surface_tree.insert("", "end", values=(
len(self.system.surfaces),
r_str,
f"{d:.2f}",
f"{nd:.4f}",
f"{vd:.1f}"
))
# 清空输入框
self.radius_entry.delete(0, tk.END)
self.thickness_entry.delete(0, tk.END)
self.nd_entry.delete(0, tk.END)
self.vd_entry.delete(0, tk.END)
self.radius_entry.focus_set()
except ValueError:
messagebox.showerror("输入错误", "请输入有效的数字")
def remove_surface(self):
"""删除选中的光学表面"""
selected = self.surface_tree.selection()
if selected:
# 从树形视图中删除
for item in selected:
index = int(self.surface_tree.item(item, "values")[0]) - 1
self.surface_tree.delete(item)
# 从系统中删除
if 0 <= index < len(self.system.surfaces):
self.system.surfaces.pop(index)
# 更新剩余表面的序号
for i, item in enumerate(self.surface_tree.get_children()):
values = list(self.surface_tree.item(item, "values"))
values[0] = i + 1
self.surface_tree.item(item, values=values)
def load_system(self):
"""加载系统参数文件"""
file_path = self.file_path_entry.get().strip()
if not file_path:
messagebox.showwarning("警告", "请输入文件路径")
return
try:
with open(file_path, 'r') as f:
data = json.load(f)
# 加载系统参数
self.system.entrance_pupil_diameter = data.get("entrance_pupil_diameter")
self.system.entrance_pupil_position = data.get("entrance_pupil_position")
self.system.object_infinite = data.get("object_infinite", True)
self.system.field_angle = data.get("field_angle")
self.system.object_distance = data.get("object_distance")
self.system.object_height = data.get("object_height")
self.system.aperture_angle = data.get("aperture_angle")
# 更新UI中的参数值
if self.system.entrance_pupil_diameter is not None:
self.entrance_diameter_entry.delete(0, tk.END)
self.entrance_diameter_entry.insert(0, str(self.system.entrance_pupil_diameter))
if self.system.entrance_pupil_position is not None:
self.entrance_position_entry.delete(0, tk.END)
self.entrance_position_entry.insert(0, str(self.system.entrance_pupil_position))
self.object_var.set(self.system.object_infinite)
self.toggle_object_input()
if self.system.field_angle is not None:
self.field_angle_entry.delete(0, tk.END)
self.field_angle_entry.insert(0, str(self.system.field_angle))
if self.system.object_distance is not None:
self.object_distance_entry.delete(0, tk.END)
self.object_distance_entry.insert(0, str(self.system.object_distance))
if self.system.object_height is not None:
self.object_height_entry.delete(0, tk.END)
self.object_height_entry.insert(0, str(self.system.object_height))
if self.system.aperture_angle is not None:
self.aperture_angle_entry.delete(0, tk.END)
self.aperture_angle_entry.insert(0, str(self.system.aperture_angle))
# 加载表面数据
self.system.surfaces = []
self.surface_tree.delete(*self.surface_tree.get_children())
surfaces_data = data.get("surfaces", [])
for surf_data in surfaces_data:
surface = Surface.from_dict(surf_data)
self.system.surfaces.append(surface)
r_str = "平面" if surface.r == float('inf') else f"{surface.r:.2f}"
self.surface_tree.insert("", "end", values=(
len(self.system.surfaces),
r_str,
f"{surface.d:.2f}",
f"{surface.nd:.4f}",
f"{surface.vd:.1f}"
))
messagebox.showinfo("成功", f"系统参数已从 {os.path.basename(file_path)} 加载")
# 显示加载的系统信息
self.result_text.configure(state=tk.NORMAL)
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, f"已加载系统参数文件: {file_path}\n")
self.result_text.insert(tk.END, f"包含 {len(self.system.surfaces)} 个光学表面\n")
self.result_text.configure(state=tk.DISABLED)
except FileNotFoundError:
messagebox.showerror("错误", f"文件不存在: {file_path}")
except Exception as e:
messagebox.showerror("加载错误", f"加载文件失败: {str(e)}")
def save_system(self):
"""保存系统参数文件"""
# 更新系统参数
if not self.update_system_from_ui():
return
# 如果没有表面数据,提示用户
if not self.system.surfaces:
messagebox.showwarning("警告", "没有光学表面数据,无法保存")
return
file_path = self.file_path_entry.get().strip()
if not file_path:
messagebox.showwarning("警告", "请输入保存路径")
return
try:
# 准备保存数据
data = {
"entrance_pupil_diameter": self.system.entrance_pupil_diameter,
"entrance_pupil_position": self.system.entrance_pupil_position,
"object_infinite": self.system.object_infinite,
"field_angle": self.system.field_angle,
"object_distance": self.system.object_distance,
"object_height": self.system.object_height,
"aperture_angle": self.system.aperture_angle,
"surfaces": [surf.to_dict() for surf in self.system.surfaces]
}
with open(file_path, 'w') as f:
json.dump(data, f, indent=4)
messagebox.showinfo("成功", f"系统参数已保存到 {os.path.basename(file_path)}")
# 显示保存信息
self.result_text.configure(state=tk.NORMAL)
self.result_text.insert(tk.END, f"系统参数已保存到: {file_path}\n")
self.result_text.configure(state=tk.DISABLED)
except Exception as e:
messagebox.showerror("保存错误", f"保存文件失败: {str(e)}")
def save_results(self):
"""保存计算结果到文件"""
if not self.system.results or all(v is None for v in self.system.results.values()):
messagebox.showwarning("警告", "没有计算结果可保存")
return
file_path = filedialog.asksaveasfilename(
title="保存计算结果",
defaultextension=".txt",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
if not file_path:
return
try:
with open(file_path, 'w') as f:
# 写入系统参数摘要
f.write("===== 光学系统参数 =====\n")
f.write(f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n")
f.write(f"入瞳位置: {self.system.entrance_pupil_position} mm\n")
f.write(f"色光类型: {self.system.light_type}\n")
f.write("物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n")
if self.system.object_infinite:
f.write(f"半视场角: {self.system.field_angle} 度\n")
else:
f.write(f"物距: {self.system.object_distance} mm\n")
f.write(f"物高: {self.system.object_height} mm\n")
f.write(f"孔径角: {self.system.aperture_angle} 度\n")
f.write("\n光学表面参数:\n")
for i, surf in enumerate(self.system.surfaces):
r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm"
f.write(f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.4f}, vd={surf.vd:.1f}\n")
# 写入计算结果
f.write("\n\n===== 计算结果 =====\n")
for key, value in self.system.results.items():
if value is not None:
# 格式化键名
label = self.format_result_label(key)
f.write(f"{label}: {value}\n")
messagebox.showinfo("成功", f"计算结果已保存到 {os.path.basename(file_path)}")
# 显示保存信息
self.result_text.configure(state=tk.NORMAL)
self.result_text.insert(tk.END, f"计算结果已保存到: {file_path}\n")
self.result_text.configure(state=tk.DISABLED)
except Exception as e:
messagebox.showerror("保存错误", f"保存计算结果失败: {str(e)}")
def format_result_label(self, key):
"""格式化结果标签为中文描述"""
labels = {
"focal_length": "焦距 f' (mm)",
"ideal_image_distance": "理想像距 l' (mm)",
"actual_image_position": "实际像位置 (mm)",
"image_principal_plane": "像方主面位置 lH' (mm)",
"exit_pupil_distance": "出瞳距 lp' (mm)",
"ideal_image_height": "理想像高 y0' (mm)",
"spherical_aberration": "球差 (mm)",
"longitudinal_chromatic": "位置色差 (mm)",
"tangential_field_curvature": "子午场曲 xt' (mm)",
"sagittal_field_curvature": "弧矢场曲 xs' (mm)",
"astigmatism": "像散 Δxts' (mm)",
"actual_image_height": "实际像高 (mm)",
"relative_distortion": "相对畸变 (%)",
"absolute_distortion": "绝对畸变 (mm)",
"lateral_chromatic": "倍率色差 (mm)",
"tangential_coma": "子午慧差 (mm)"
}
return labels.get(key, key)
def update_system_from_ui(self):
"""从UI更新系统参数"""
try:
# 入瞳参数
if self.entrance_diameter_entry.get():
self.system.entrance_pupil_diameter = float(self.entrance_diameter_entry.get())
if self.entrance_position_entry.get():
self.system.entrance_pupil_position = float(self.entrance_position_entry.get())
# 色光类型
self.system.light_type = self.light_type_var.get()
# 物方参数
self.system.object_infinite = self.object_var.get()
if self.system.object_infinite:
if self.field_angle_entry.get():
self.system.field_angle = float(self.field_angle_entry.get())
else:
if self.object_distance_entry.get():
self.system.object_distance = float(self.object_distance_entry.get())
if self.object_height_entry.get():
self.system.object_height = float(self.object_height_entry.get())
if self.aperture_angle_entry.get():
self.system.aperture_angle = float(self.aperture_angle_entry.get())
except ValueError:
messagebox.showerror("输入错误", "请输入有效的数字")
return False
return True
def calculate(self):
"""执行光学计算"""
# 更新系统参数
if not self.update_system_from_ui():
return
# 检查必要参数
if not self.system.surfaces:
messagebox.showwarning("警告", "请至少添加一个光学表面")
return
if self.system.entrance_pupil_diameter is None:
messagebox.showwarning("警告", "请输入入瞳直径")
return
if self.system.object_infinite and self.system.field_angle is None:
messagebox.showwarning("警告", "请输入半视场角")
return
if not self.system.object_infinite and (
self.system.object_distance is None or
self.system.object_height is None or
self.system.aperture_angle is None
):
messagebox.showwarning("警告", "请输入完整的有限远参数")
return
# 执行计算 - 这里调用您的计算函数
self.perform_calculations()
# 显示结果
self.display_results()
# 显示计算完成消息
messagebox.showinfo("计算完成", "光学计算已完成,结果已显示在结果区域")
def perform_calculations(self):
"""执行光学计算 - 这里调用您的实际计算函数"""
# 重置结果
for key in self.system.results:
self.system.results[key] = None
# 示例:设置一些假结果用于演示
# 实际应用中,您需要调用您自己的计算函数
self.system.results["focal_length"] = Calculate.calculate_focal_length(self.system)[0]
self.system.results["ideal_image_distance"] = 0#Calculate.calculate_ideal_image_distance(self.system)
self.system.results["actual_image_position"] =0# Calculate.calculate_actual_image_position(self.system)
self.system.results["image_principal_plane"] = Calculate.calculate_focal_length(self.system)[1]
self.system.results["exit_pupil_distance"] = 0#Calculate.calculate_exit_pupil_distance(self.system)
self.system.results["ideal_image_height"] =0# Calculate.calculate_ideal_image_height(self.system)
self.system.results["spherical_aberration"] = 0#Calculate.calculate_spherical_aberration(self.system)
self.system.results["longitudinal_chromatic"] = 0#Calculate.calculate_longitudinal_chromatic(self.system)
self.system.results["tangential_field_curvature"] = 0.15
self.system.results["sagittal_field_curvature"] = 0.12
self.system.results["astigmatism"] = 0#Calculate.calculate_astigmatism(self.system)
self.system.results["actual_image_height"] =0#Calculate.calculate_actual_image_height(self.system)
self.system.results["relative_distortion"] = -0.5
self.system.results["absolute_distortion"] =0# Calculate.calculate_distortion(self.system)
self.system.results["lateral_chromatic"] =0# Calculate.calculate_lateral_chromatic(self.system)
self.system.results["tangential_coma"] =0# Calculate.calculate_tangential_coma(self.system)
def display_results(self):
"""在结果文本框中显示计算结果"""
self.result_text.configure(state=tk.NORMAL)
self.result_text.delete(1.0, tk.END)
# 添加系统参数摘要
self.result_text.insert(tk.END, "===== 光学系统参数 =====\n", "title")
self.result_text.insert(tk.END, f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n")
self.result_text.insert(tk.END, f"入瞳位置: {self.system.entrance_pupil_position} mm\n")
self.result_text.insert(tk.END, "物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n")
self.result_text.insert(tk.END, f"色光类型: {self.system.light_type}\n")
if self.system.object_infinite:
self.result_text.insert(tk.END, f"半视场角: {self.system.field_angle} 度\n")
else:
self.result_text.insert(tk.END, f"物距: {self.system.object_distance} mm\n")
self.result_text.insert(tk.END, f"物高: {self.system.object_height} mm\n")
self.result_text.insert(tk.END, f"孔径角: {self.system.aperture_angle} 度\n")
self.result_text.insert(tk.END, "\n光学表面参数:\n")
for i, surf in enumerate(self.system.surfaces):
r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm"
self.result_text.insert(tk.END,
f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.4f}, vd={surf.vd:.1f}\n")
# 添加计算结果
self.result_text.insert(tk.END, "\n\n===== 计算结果 =====\n", "title")
# 计算关键结果
key_results = [
"focal_length", "ideal_image_distance", "actual_image_position",
"image_principal_plane", "exit_pupil_distance", "ideal_image_height"
]
for key in key_results:
if self.system.results[key] is not None:
label = self.format_result_label(key)
self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n")
# 添加像差结果
self.result_text.insert(tk.END, "\n像差分析:\n", "subtitle")
aberrations = [
"spherical_aberration", "longitudinal_chromatic", "tangential_field_curvature",
"sagittal_field_curvature", "astigmatism", "actual_image_height",
"relative_distortion", "absolute_distortion", "lateral_chromatic", "tangential_coma"
]
for key in aberrations:
if self.system.results[key] is not None:
label = self.format_result_label(key)
self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n")
self.result_text.configure(state=tk.DISABLED)
def clear_all(self):
"""清除所有输入和结果"""
# 清除系统参数
self.system = OpticalSystem()
# 清除UI输入
self.file_path_entry.delete(0, tk.END)
self.entrance_diameter_entry.delete(0, tk.END)
self.entrance_position_entry.delete(0, tk.END)
self.field_angle_entry.delete(0, tk.END)
self.object_distance_entry.delete(0, tk.END)
self.object_height_entry.delete(0, tk.END)
self.aperture_angle_entry.delete(0, tk.END)
self.radius_entry.delete(0, tk.END)
self.radius_entry.insert(0, "50")
self.thickness_entry.delete(0, tk.END)
self.thickness_entry.insert(0, "5")
self.nd_entry.delete(0, tk.END)
self.nd_entry.insert(0, "1.5")
self.vd_entry.delete(0, tk.END)
self.vd_entry.insert(0, "60")
# 清除表面列表
self.surface_tree.delete(*self.surface_tree.get_children())
self.system.surfaces = []
# 清除结果
self.result_text.configure(state=tk.NORMAL)
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n")
self.result_text.configure(state=tk.DISABLED)
# 重置物距类型
self.object_var.set(True)
self.toggle_object_input()
messagebox.showinfo("清除完成", "所有输入和结果已清除")
def load_default_settings(self):
"""加载默认设置(可选)"""
# 这里可以添加加载默认设置的逻辑
pass