Optimize->Support Routines->Vignetting->Set Apertures... 源码分析
它的功能是给定全视场的子午渐晕去设置面的孔径,及设置相关面的孔径检查,它调用apset(int apset_option, double lav, double uav, int nbr_dgts, int prt_err),下面分析下它的代码
/**********************************************************************/
/* */
/* Command to set aperture radii to give specified lower and */
/* upper aperture vignetting at full field. The command assumes */
/* that the lens is rotationally symmetric. The default is to */
/* set all of the apertures in the lens. To set only the apertures */
/* that are currently "solved", select "no" for the */
/* "Set_all_apertures" argument. */
/* */
/* The vignetting is entered as fractional pupil coordinates, i.e., */
/* a lower vignetting factor of -1.0 and an upper vignetting */
/* factor of 1.0 means there is no vignetting. */
/* */
/**********************************************************************/
#include "..\..\public\ccl\inc\gendefs.h"
#include "..\..\public\ccl\inc\strdefs.h"
#define APSET_USER_SPECIFIED 0 /* The aperture value will not be changed */
#define APSET_SET_APERTURE 1 /* The aperture value will be set */
#define APSET_SET_CHECKED_APERTURE 2 /* The aperture will be marked as "checked" */
#define APSET_MAX_AP_CV 0.99 /* Maximum value of ap*cv */
#define APSET_LOWER_RIM_RAY -1
#define APSET_UPPER_RIM_RAY 1
/* Error codes */
#define APSET_NO_CHECK_ERROR 0
#define APSET_LOWER_APER_ERROR 1
#define APSET_UPPER_APER_ERROR 2
#define APSET_BOTH_APER_ERROR 3
/* Allowed deviation of vignetting factor for no error */
#define APSET_VIGNETTING_EPSILON 0.05
/* Command arguments */
int apset_option {list = apset_options, prompt = "Option", default(onprompt) = "all"}
double lav {prompt = "Target value for lower aperture vignetting", default(onprompt) = -1.0}
double uav {prompt = "Target value for upper aperture vignetting", default(onprompt) = 1.0}
int nbr_dgts {lolim = 1, prompt = "Number of digits to round to ", default(noquery) = 3}
int prt_err {list = Yes_no, prompt = "Report ray error", default(noquery) = "Yes"}
/* Functions */
static int on_axis_apertures(double aps[]);
static int full_field_apertures(int rimtype, double aps[], double y_pup, double raycoord[], int dydfy[]);
static cmd element_apertures(double aps[]);
static int find_check_srfs(double aps[], int ok_to_set[], double l_rim[], double u_rim[], int low_drv[], int up_drv[]);
void apset_eh(void);
/* Global variables */
char ray_error_occurred;
cmd
apset(int apset_option, double lav, double uav, int nbr_dgts, int prt_err)
// hlp: Click <a href="../lens/aperture/set.htm">here</a> for additional help.
// kwd: vignetting, aperture
// cat: design tools
{
int i, j, sbn, axerr, uperr, lowerr, chkerr;
int setape[ims], updrv[ims], lowdrv[ims];
char apckstatus;
char str1[32];
char err_hdl_sav[50];
double oldfby, oldfbx, oldfbz, oldfyrf, oldfxrf, aux, aux2;
double aprad[ims], uprim[ims], lowrim[ims];
// env = LENSSOK;
if (fptssopen)
{
prefs;
abort(E_FPTSSOPEN);
}
if (fabs(uav - lav) < 1.0e-20 || lav > uav)
cclabort("Invalid vignetting factors.","apset");
SAVE_DISPLAY_PREFS;
strcpy(err_hdl_sav, error_handler);
install_error_handler("apset_eh");
apckstatus = apck;
set_preference(output_text, off);
aperture_check(off);//取消孔径检查,故下面的光线追迹将不受checked的孔径影响
/* Save current field point data */
oldfby = Trr_fby;
oldfbx = Trr_fbx;
oldfbz = Trr_fbz;
oldfyrf = Trr_fyrf;
oldfxrf = Trr_fxrf;
for (i = 0; i < ims; i++)
{
setape[i] = APSET_USER_SPECIFIED;
aprad[i] = 0.0;
}
/* On-axis apertures */
ray_error_occurred = 0;
axerr = on_axis_apertures(aprad);
if (ray_error_occurred)
{
ray_error_occurred = 0;
if (apckstatus)
aperture_check(on);
RESTORE_DISPLAY_PREFS;
ieh("prefs");
if (prt_err)
{
prefs;
abort(E_REFRAYFAILED);
}
else
return;
}
/* Full-field, upper rim ray */
ray_error_occurred = 0;
uperr = full_field_apertures(APSET_UPPER_RIM_RAY, aprad, uav, uprim, updrv);
if (ray_error_occurred)
{
ray_error_occurred = 0;
if (apckstatus)
aperture_check(on);
RESTORE_DISPLAY_PREFS;
ieh("prefs");
if (prt_err)
{
prefs;
abort(E_REFRAYFAILED);
}
else
return;
}
/* Full-field, lower rim ray */
ray_error_occurred = 0;
lowerr = full_field_apertures(APSET_LOWER_RIM_RAY, aprad, lav, lowrim, lowdrv);
if (ray_error_occurred)
{
ray_error_occurred = 0;
if (apckstatus)
aperture_check(on);
RESTORE_DISPLAY_PREFS;
ieh("prefs");
if (prt_err)
{
prefs;
abort(E_REFRAYFAILED);
}
else
return;
}
if (apset_option)
{
for (i = 1; i < ims; i++)
{
/* Set all apertures, unless "fixed" */
if (aptyp[i] != 7)
setape[i] = APSET_SET_APERTURE;
}
}
else
{
for (i = 1; i < ims; i++)
{
/* Only set apertures that are currently "solved" */
if (aptyp[i] == 0)
setape[i] = APSET_SET_APERTURE;
}
}
/* Round-off aperture radius values to specified precision */
for (i = 1; i < ims; i++)
{
aux = round(aprad[i], nbr_dgts);
if (aux >= aprad[i])
aprad[i] = aux;
else
{
/* Round up a bit to avoid on-axis vignetting */
sprintf(str1, "%e", aprad[i]);//将aprad[i]用科学计数法表示,存到str1中
//将str1中,'e'之前的数都替换为0,除小数点外
str1[0] = '0';
j = 2;
while (str1[j] != 'e')
str1[j++] = '0';
if (nbr_dgts > 1)
j = nbr_dgts;
else
j = 0;
//将第nbr_dgts个有效位置为1,然后将这个数加aprad[]上
//就是将aprad[i]截取nbr_dgts个有效位后,不会变小,只会变大
str1[j] = '1';
aux2 = atof(str1);
aprad[i] = aux + aux2;
}
}
/* Set front and back apertures of elements to larger value */
element_apertures(aprad);
//下面这个find_check_srfs()是最重要的函数
/* Find surfaces that limit beam to set as checked apertures */
chkerr = find_check_srfs(aprad, setape, lowrim, uprim, lowdrv, updrv);
sbn = sbrow();
//开始将孔径大小设置到每个面上
/* Set aperture radius values */
ssbuf_reset(sbn, ims);
for (i = 1; i < ims; i++)
{
if (setape[i])
{
if (setape[i] == APSET_SET_CHECKED_APERTURE)
ap(i, chk, aprad[i]);
else
ap(i, aprad[i]);
}
}
ssbuf_reset(-sbn, ims);
if (apckstatus)
aperture_check(on);
/* Restore current field point */
ssbuf_reset(sbn, 3);
trace_ref_ray(oldfby, oldfbx, oldfbz, oldfyrf, oldfxrf);
ssbuf_reset(-sbn, 3);
install_error_handler(err_hdl_sav);
RESTORE_DISPLAY_PREFS;
/* Display new aperture data */
aperture_data(all);
if (chkerr == APSET_LOWER_APER_ERROR || chkerr == APSET_BOTH_APER_ERROR)
lowerr = 1;
if (chkerr == APSET_UPPER_APER_ERROR || chkerr == APSET_BOTH_APER_ERROR)
uperr = 1;
if (uperr && lowerr)
message("Unable to achieve both lower\nand upper aperture vignetting.");
else if (uperr)
message("Unable to achieve upper aperture vignetting.");
else if (lowerr)
message("Unable to achieve lower aperture vignetting.");
}
//追迹轴上的,入瞳孔径坐标为(1,0)的光线,得到光线在每个面上的y坐标值,将它们的绝对值保存到aps[]中
//这得到的是每个面的最小孔径值
static int
on_axis_apertures(double aps[])
// com: Get aperture radii for on-axis beam.
// com: These are the minimum values for the aperture radii.
// com: Modifies apertures[] = current axial ray height.
// com: Returns 1 if successful, 0 if error occured.
{
int sbn, maxrow, rn, sn;
sbn = sbrow();
ssbuf_reset(sbn, 3);
trace_ref_ray(0.0, 0.0, 0.0, 0.0, 0.0);
ssbuf_reset(-sbn, 3);
if (ray_error_occurred)
return (0);
ssbuf_reset(sbn, 2*(ims + 1));
#ifndef _OSLO_LIGHT_
trace_ray(standard, local, all, usr, 1.0, 0.0, yes);
#endif
#ifdef _OSLO_LIGHT_
trace_ray(standard, local, all, 1.0, 0.0, yes);
#endif
maxrow = sbcount();
rn = 1;
while (rn <= maxrow)
{
sn = rint(ssb(rn, 7));
if (sn < ims)
aps[sn] = fabs(ssb(rn, 1));
rn++;
}
ssbuf_reset(-sbn, 2*(ims + 1));
return (0);
}
static int
full_field_apertures(int rimtype, double aps[], double y_pup, double raycoord[], int dydfy[])
// com: Get aperture radii for full-field beam.
// com: Modifies aps[] = max of (current full-field ray heights of y_pup ray, surface aperture value).
// com: Modifies raycoord[] = full-field ray heights of y_pup ray.
// com: Modifies dydfy[] = 1 if ref ray is 'inside' the y_pup ray limit, -1 otherwise.norm=1才是这样的,但norm=0时,不是这样的啊
// com: Returns 0 if apertures[] had to be modified, 1 otherwise.
{
int sbn, maxrow, rn, sn, dovig, norm;
double tmp, fyref, fxref;
double yref[ims];
sbn = sbrow();
ssbuf_reset(sbn, 3);
trace_ref_ray(1.0, 0.0, 0.0, 0.0, 0.0);
fyref = ssb(2, 3);
fxref = ssb(2, 4);
ssbuf_reset(-sbn, 3);
if (ray_error_occurred)
return (0);
ssbuf_reset(sbn, ims + 1);
#ifndef _OSLO_LIGHT_
trace_ray(standard, local, all, usr, fyref, fxref, yes);
#endif
#ifdef _OSLO_LIGHT_
trace_ray(standard, local, all, fyref, fxref, yes);
#endif
//追迹全视场的参考光线(主光线),将它在各个面上的y坐标的绝对值保存到yref[]中
maxrow = sbcount();
rn = 1;
while (rn <= maxrow)
{
sn = rint(ssb(rn, 7));
tmp = fabs(ssb(rn, 1));
if (sn < ims)
yref[sn] = tmp;
rn++;
}
ssbuf_reset(-sbn, ims + 1);
//当是渐晕上边缘光线时,若此渐晕系数大于0,则norm=1,否则为0
//当是渐晕下边缘光线时,若此渐晕系数小于0,则norm=1,否则为0
//故若norm=0的话,主光线一定不穿过渐晕光瞳,只有2次调用full_field_apertures,norm都为1时,主光线才会穿过渐晕光瞳
if (rimtype == APSET_LOWER_RIM_RAY)
{
if (fyref - y_pup >= 0.0)
norm = 1;
else
norm = 0;
}
else
{
if (y_pup - fyref >= 0.0)
norm = 1;
else
norm = 0;
}
ssbuf_reset(sbn, 2*(ims + 1));
#ifndef _OSLO_LIGHT_
trace_ray(standard, local, all, usr, y_pup, 0.0, yes);
#endif
#ifdef _OSLO_LIGHT_
trace_ray(standard, local, all, y_pup, 0.0, yes);
#endif
//追迹渐晕光瞳的边缘光线,将其与各个面的交点的y坐标绝对值保存到raycoord[]中
maxrow = sbcount();
dovig = 1;
rn = 1;
while (rn <= maxrow)
{
sn = rint(ssb(rn, 7));
tmp = fabs(ssb(rn, 1));
if (sn < ims)
{
raycoord[sn] = tmp;
if (norm)
{
if (tmp - yref[sn] < 0.0)
dydfy[sn] = -1;//渐晕边缘光线比主光线更靠近光轴时
else
dydfy[sn] = +1;//渐晕边缘光线比主光线更远离光轴时
}
else
{
if (tmp - yref[sn] > 0.0)
dydfy[sn] = -1;//渐晕边缘光线比主光线更远离光轴时
else
dydfy[sn] = +1;//渐晕边缘光线比主光线更靠近光轴时
}
//如果在任一非孔径面上,渐晕边缘光线光高比轴上光线(1,0)的光高大,则将aps[sn]置换成渐晕边缘光线的光高
//同时将dovig=0,否则dovig为1
if (sn != ast)
{
if (tmp > aps[sn])
{
aps[sn] = tmp;
dovig = 0;
}
}
}
rn++;
}
ssbuf_reset(-sbn, 2*(ims + 1));
return (dovig);
}
static cmd
element_apertures(double aps[])
// com: Set front and back apertures of elements to larger value.
// com: Finds the max aperture on each glass element.
// com: Modifies aps[].
{
int sn, snp, snm;
double ap1, ap2;
for (sn = 1; sn < ims - 1; sn++)
{
snp = sn + 1;
snm = sn - 1;
if (gltyp[sn] != 1 && gltyp[sn] != 10 && fabs(rn[sn][1] - 1.0) > 1.0e-20)
{
ap1 = aps[sn];
ap2 = aps[snp];
if (ap1 > ap2)
{
if ((gltyp[snp] == 1 || gltyp[snp] == 10 || fabs(rn[snp][0] - 1.0) > 1.0e-20) && cv[snp] > 0.0)
continue;
if (fabs(ap1*cv[snp]) < APSET_MAX_AP_CV)
aps[snp] = ap1;
}
else
{
if ((gltyp[snm] == 1 || gltyp[snm] == 10 || fabs(rn[snm][0] - 1.0) > 1.0e-20) && cv[sn] < 0.0)
continue;
if (fabs(ap2*cv[sn]) < APSET_MAX_AP_CV)
aps[sn] = ap2;
}
}
}
}
static int
find_check_srfs(double aps[], int ok_to_set[], double l_rim[], double u_rim[], int low_drv[], int up_drv[])
// com: Finds surfaces that limit beam to set as checked apertures.
// com: Returns error number (LOWER_APER_ERROR, UPPER_APER_ERROR, BOTH_APER_ERROR)
{
int lowchksrf, upchksrf, sn, error;
double lowmin, upmin, tmp;
error = APSET_NO_CHECK_ERROR;
//如果光阑没被排除在设置之外,就一定将其设为孔径检查
if (ok_to_set[ast])
ok_to_set[ast] = APSET_SET_CHECKED_APERTURE;
lowchksrf = upchksrf = 0;
lowmin = upmin = 0.0;
//下面开始对渐晕光瞳上下边缘光线,搜寻哪个面该被设为checked,对于这2条光线,搜寻过程是独立的
//注意,最终的结果,对一条边缘光线,它只决定一个孔径检查面
for (sn = 1; sn < ims; sn++)
{
//aps[]存的是由轴上(1,0)光线和渐晕光瞳上下边缘光线决定的面半径,它存的是这三者与每个面的光高的绝对值最大值
//但孔径面的半径仅由轴上(1,0)光线的光高决定
if (aps[sn] > 0.0 && ok_to_set[sn])
{
//搜寻渐晕光瞳下边缘光线,该光线与每个面的交点光高的绝对值放在l_rim[]中
//它在这样的面中选择,它满足如下2条件
//1、若下边缘光线的入瞳比例坐标>0(此时主光线明显不应该可以穿过系统),这个面能将主光线(0,0)给挡掉
//2、若下边缘光线的入瞳比例坐标<0(此时主光线能否穿过系统,应该由上边缘光线入瞳比例坐标决定,故这里不应该被挡掉),这个面则不能将主光线(0,0)给挡掉
//在满足以上2条件的面中,选择l_rim[]/aps[](<1)最大者为孔径检查面
//故在满足轴上光线无渐晕的条件下,最终的结果是渐晕系数可能达不到预设的lav(绝对值比|lav|大),只有边缘光线的光高不比轴上边缘光线的光高小,才可与预设结果一样
tmp = l_rim[sn]/aps[sn];
if (tmp > lowmin && low_drv[sn] > 0)
{
lowmin = tmp;
lowchksrf = sn;
}
//搜寻渐晕光瞳上边缘光线,该光线与每个面的交点光高的绝对值放在u_rim[]中
//它在这样的面中选择,它满足如下2条件
//1、若上边缘光线的入瞳比例坐标<0(此时主光线明显不应该可以穿过系统),这个面能将主光线(0,0)给挡掉
//2、若下边缘光线的入瞳比例坐标>0(此时主光线能否穿过系统,应该由下边缘光线入瞳比例坐标决定,故这里不应该被挡掉),这个面则不能将主光线(0,0)给挡掉
//在满足以上2条件的面中,选择u_rim[]/aps[](<1)最大者为孔径检查面
//故在满足轴上光线无渐晕的条件下,最终的结果是渐晕系数可能达不到预设的uav(绝对值比|uav|大)
tmp = u_rim[sn]/aps[sn];
if (tmp > upmin && up_drv[sn] > 0)
{
upmin = tmp;
upchksrf = sn;
}
}
}
//下面将搜寻的结果保存到ok_to_set[]中
if (lowchksrf > 0 && ok_to_set[lowchksrf])
{
ok_to_set[lowchksrf] = APSET_SET_CHECKED_APERTURE;
if (fabs(lowmin - 1.0) > APSET_VIGNETTING_EPSILON)
error = APSET_LOWER_APER_ERROR;
}
else
error = APSET_LOWER_APER_ERROR;
if (upchksrf > 0 && ok_to_set[upchksrf])
{
ok_to_set[upchksrf] = APSET_SET_CHECKED_APERTURE;
if (fabs(upmin - 1.0) > APSET_VIGNETTING_EPSILON)
{
if (error)
error = APSET_BOTH_APER_ERROR;
else
error = APSET_UPPER_APER_ERROR;
}
}
else
{
if (error)
error = APSET_BOTH_APER_ERROR;
else
error = APSET_UPPER_APER_ERROR;
}
return (error);
}
void
apset_eh(void)
{
errno = 0;
ray_error_occurred = 1;
}
由此总结如下几点:
1、轴上光束是没有的渐晕的,在满足此条件下再去搜寻尽量满足全视场的渐晕要求的面半径及相应的孔径检查面
2、渐晕光瞳的上下边缘各确定一个孔径检查面,而光栏面无论如何都被设成孔径检查的
3、全视场的渐晕系数可能达不到预设值(即不如预设那样渐晕的厉害),只有相应边缘光线的光高不比轴上边缘光线的光高小,其相应的渐晕才可与预设结果一样
4、最终的面半径是尽量满足轴上边缘光束恰好通过,这估计也是OSLO中所谓的“特别当需要确定光学系统中光学元件的最佳大小和外形时,该软件能够体现出强大的优势”原因吧