-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcam_chart.html
More file actions
381 lines (317 loc) · 12.7 KB
/
cam_chart.html
File metadata and controls
381 lines (317 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
<!DOCTYPE html>
<head>
<title>camera chart</title>
</head>
<h2>Encircled region shows what mirror types can be tested with that camera</h2>
<body>
<div id='aaa' style='width:1100px;height:550px;'></div>
<input type='button' value='zoom out (default)' id='idBtnZoomout'>
<input type='button' value='zoom out more' id='idBtnZoomoutMore'>
<p>
<h3><font color='blue'>click and drag mouse over graph to zoom in</font></h3>
<p>
<input type="checkbox" id="c1" value="c1" onclick="radioChange()" checked ><label for="c1">Nikon D3100 w/50mm f/1.8</label><br>
<input type="checkbox" id="c2" value="c2" onclick="radioChange()" ><label for="c2">Nikon D3200 w/50mm f/1.8</label><br>
<input type="checkbox" id="c3" value="c3" onclick="radioChange()" ><label for="c3">Nikon D3200 w/85mm f/1.8</label><br>
<input type="checkbox" id="c4" value="c4" onclick="radioChange()" ><label for="c4">Nikon D3200 w/35mm f/1.8</label><br>
<input type="checkbox" id="c5" value="c5" onclick="radioChange()" ><label for="c5">Nikon Z7 full frame w/85mm f/1.8</label><br>
<input type="checkbox" id="c5b" value="c5b" onclick="radioChange()" ><label for="c5b">Rasp Pi HQ cam w/16mm F/1.4</label><br>
<input type="checkbox" id="c6" value="c6" onclick="radioChange()" ><label for="c6">Custom</label>
sensor size vertical(mm): <input type='text' id='idCamVert' size=2 value=24>
sensor pixels vertical: <input type='text' id='idCamPix' size=3 value=4000>
lens FL (mm) <input type='text' id='idCamLens' size=2 value=50>
Lens minimum focus distance (meters): <input type='text' id='idCamMinFocus' size=2 value=.45>
<input type='button' value='Calculate' onclick='makeCustomPoints()'> This doesn't take into account stop cropping (virtual pin hole). Many lenses have stop cropping issues.
<br>
<p>
<input type="checkbox" id="chkInch" value="chkInch" onclick="unitsChange()" ><label for="chkInch">change units to cm</label><br>
Add your own mirror - diameter:
<input type='text' id='idDiam' size=3 value='8'> f/#: <input type='text' id='idFnum' size=2 value='5'>
<input type='button' onclick='addCustomMirror()' value='Set'>
<p>
How to use this chart. If your mirrors to be tested on a Bath Interferometer are within the encircled blue region then a used Nikon D3100 with a 50mm f/1.8 lens will work for you ($200 for used body, $50 for used lens on ebay). If one of your mirrors is outside this region, try some of the other options here (these are not the only options). If your mirror is right on the edge, try this utility for more info:
<a href="cam.html">camera chooser</a>
<p>
The top of the region is set by camera resolution and the size of the igram on the camera sensor. I chose a minimum of 600 pixels across the diameter of the igram. This is a bit arbitrary and you would be fine to test with just 200 pixels across the mirror.
<p>The left side of the region is set by the minimum focal distance of the lens being used. The mirror to be tested will be at RoC (double the focal length) from the Bath and camera. It's unlikely you have a mirror so small it hits this limit. I included 1 inch for the distance from the camera lens to the Coc.
<p>The bottom of the graph is set based on the size of the igram in the field of view - any lower f/# mirror and the igram will be cropped at the top and bottom of the field of
view. I included a very small margin (about 10% arbitrarily) as if you are right on the edge then it's a royal pain to position the camera such that it isn't cropping at the top or bottom of the igram. There is another type of cropping, "crop stopping" or "virtual pinhole cropping". I own the 3 lenses above and they don't have stop cropping issues - the worst is the 35mm F/1.8 which needs to be almost touching the Bath splitter cube or it will crop the field of view. The other 2 lenses can be quite far behind the splitter cube.
<p>The right edge of the region is a curve set by how tiny the fringes are and the pixel resolution of these fringes on the camera. Cameras with more megapixels will move this curve to the right. I picked 6 pixels per fringe in the best case igram to set the position of this line but in reality you can go down to 4 pixels (just barely) if the fringes are vertical or horizontal (worst case - can go even lower if none of the fringes are vertical or horizontal).
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="flotr-0.2.0-alpha.js"></script>
<script>
function getRadio(n)
{
return document.getElementById('c'+n).checked;
}
function radioChange()
{
load_up_data();
makeCustomPoints();
}
var customDiam=0;
var customFnum=0;
function addCustomMirror()
{
bCM = document.getElementById('chkInch').checked;
customDiam = parseFloat(document.getElementById('idDiam').value);
customFnum = parseFloat(document.getElementById('idFnum').value);
if (bCM) customDiam/=2.54;
load_up_data();
makeCustomPoints();
}
function unitsChange()
{
bCM = document.getElementById('chkInch').checked;
var multiplier = bCM ? 2.54 : 1/2.54;
load_up_data();
if (lastArea != null && lastArea != "out")
{
lastArea.x1 *= multiplier;
lastArea.x2 *= multiplier;
}
makeCustomPoints();
}
/*
tilt=-z8*7+4;
defocus=-z8*1.2+.3;
astig=0;
*/
function fill_left(data, min_focus) // min_focus in inches
{
var minf = data[0][1];
var maxf = data[data.length-1][1];
var mirror_diam;
for (f=maxf; f> minf; f-=0.3)
{
mirror_diam = (min_focus-1)/f/2;
data.push([mirror_diam, f]);
}
mirror_diam = (min_focus-1)/minf/2;
data.push([mirror_diam, minf]);
data.push([data[0][0], minf]);
}
var customSeries=[];
var series;
function load_up_data()
{
series=[];
if (getRadio(1))
series.push({label:'D3100 w/50mm', color:"#00A8F0",data:makePoints(15.4,3072,50,0.45)});
if (getRadio(2))
series.push({label:'D3200 w/50mm', color:"#CB4B4B",data:[[9.6,1.8],[18.6,2.5],[26.7,3],[36.3,3.5],[47.3,4],[105,6],[185,8],[332,10.8]]}); // 600 pixels f13.0
if (getRadio(3))
series.push({label:'D3200 w/85mm', color:"#4DA74D",data:[[45.5,3],[81,4],[179,6],[498,10],[1630,18.4]]});//600
if (getRadio(4))
series.push({label:'D3200 w/35mm', color:"#9440ED",data:[[4,1.4],[8.2,2],[18.3,3],[32.3,4],[72,6],[113,7.57]]});
if (getRadio(5))
series.push({label:'Z7 w/85mm', color:"#000000",data:[[18,2],[28,2.5],[40,3],[55,3.5], [75,4.1], [125,5.3], [179,6.35], [500,10.7], [1140,16.3]]});
if (getRadio('5b'))
{
series.push({label:'HQ cam w/16mm', color:"#d0d000",data:[]});
series[series.length-1].data=makePoints(4.712, 3040, 16, 0.275);
}
if (getRadio(6))
{
series.push({label:'custom', color:"#ff8000",data:[]});
series[series.length-1].data = customSeries.slice(); // makes fresh copy
}
series.push({label:'typical amateur mirrors', color:"#808080",data:[[4,10],[6,5],[6,8],[6,10],[8,6],[12,6],[12,5],[12,4],[18,4],[18,3.6],[29,3.3],[32,3]],points:{show:true}});
if (customDiam !=0)
{
series.push({label:'your mirror', color:'#00c000',data:[],points:{show:true}});
series[series.length-1].data.push([customDiam, customFnum]);
}
var i=0;
if (getRadio(1))
i++;
if (getRadio(2))
fill_left(series[i++].data, 17.76);
if (getRadio(3))
fill_left(series[i++].data, 31.44);
if (getRadio(4))
fill_left(series[i++].data, 11.8);
if (getRadio(5))
fill_left(series[i++].data, 31.44);
if (document.getElementById('chkInch').checked)
{
// convert all values from inches to cm
var i,j,data;
for (i=0;i<series.length;i++)
{
data=series[i].data;
for (j=0;j<data.length;j++)
{
data[j][0] *= 2.54;
}
}
}
}
function calc_ppf(z8, tilt, defocus, astig, pixels)
{
// now find highest slope - look only along the X axis and only check X slope
let maxSlope=0;
let maxSlopeX=0;
let slope=0;
for (x=-1; x<=1; x+=.01) // check 200 positions
{
let x3 = x*x*x;
slope = z8*6*(4*x3-2*x) + tilt + defocus*4*x + astig*2*x;
if (Math.abs(slope) > Math.abs(maxSlope))
{
maxSlope = slope;
maxSlopeX=x;
}
}
// maxSlope is now in waves per unit circle radius
maxSlope = maxSlope/(pixels/2); // now in waves per pixel
return Math.abs(1/maxSlope);
}
function inch_fnum_to_ppf(inch, fnum, pixels)
{
const wl=650;
let mirror_diam = inch*25.4;
const cc= -1;
let z8 = cc*(mirror_diam/(wl/1000000))/(3072 * fnum * fnum * fnum);
let tilt=-z8*7+4;
let defocus=-z8*1.2+.3;
const astig=0;
return calc_ppf(z8, tilt, defocus, astig, pixels);
}
function right_edge_fnum_to_inch(fnum, minpixels, camSizeLens, camSizeMM, camSizePix)
{
camFOV = camSizeLens/camSizeMM;
mirrorFOV = fnum*2;
inch_low=0.01;
inch_high=10000;
igramPixels = camSizePix * camFOV/mirrorFOV;
for(i=1; i<20; i++)
{
inch = (inch_low+inch_high)/2;
if (inch_fnum_to_ppf(inch, fnum, igramPixels)>minpixels)
{
// we can go more inches
inch_low = inch;
}
else
{
inch_high = inch;
}
}
inch = (inch_low+inch_high)/2;
return inch;
}
function makePoints(camSizeMM, camSizePix, camSizeLens, camFocus)
{
// last parameter, camFocus is in meters but I convert to inches
camFocus *=1000; // convert meters to mm
camFocus /=25.4; // convert mm to inches
camFOV = camSizeLens/camSizeMM;
//mirrorFOV = mirrorFNum*2;
//igramPixels = sensorPixels * camFOV/mirrorFOV;
// calculate topfnum
var mirrorFOV = camSizePix * camFOV / 600;
var topfnum = mirrorFOV / 2;
// calculate botfnum where mirrorFOV=CAMfov*85%
var mirrorFOV = camFOV/.9;
var botfnum = mirrorFOV / 2;
let inch = right_edge_fnum_to_inch(botfnum, 6, camSizeLens, camSizeMM, camSizePix)
let mirror_diam = (camFocus-1)/botfnum/2;
if (inch < mirror_diam)
{
// need to find intersection of two curves
let f_min = botfnum;
let f_max = topfnum;
for (let i=0; i<10; i++)
{
f = (f_min+f_max)/2;
inch = right_edge_fnum_to_inch(f, 6, camSizeLens, camSizeMM, camSizePix)
mirror_diam = (camFocus-1)/f/2;
if (inch < mirror_diam)
f_min = f; // increase f
else
f_max = f;
}
botfnum = (f_min+f_max)/2;
}
ret_series = new Array();
for(let f = botfnum; f < topfnum; f *= 1.2)
{
ret_series.push(new Array(right_edge_fnum_to_inch(f, 6, camSizeLens, camSizeMM, camSizePix),f));
}
ret_series.push(new Array(right_edge_fnum_to_inch(topfnum, 6, camSizeLens, camSizeMM, camSizePix),topfnum));
// fill_left expects first element to be bottom right corner of graph and last element to be top right corner
fill_left(ret_series, camFocus);
return ret_series;
}
function makeCustomPoints()
{
customSeries = [];
var camSizeMM = parseFloat(document.getElementById('idCamVert').value);
var camSizePix = parseFloat(document.getElementById('idCamPix').value);
var camSizeLens = parseFloat(document.getElementById('idCamLens').value);
var camFocus = parseFloat(document.getElementById('idCamMinFocus').value);
customSeries = makePoints(camSizeMM, camSizePix, camSizeLens, camFocus);
load_up_data();
plot("nochange");
}
var lastArea="";
var options;
function plot(area)
{
bCM = document.getElementById('chkInch').checked;
units = bCM ? "cm" : "in";
options={selection:{mode:'xy',fps:30,color:'#000000'},
legend:{position:'se',backgroundColor: '#c0c0ff'},
xaxis:{min:null},
yaxis:{max:null},
xaxis:{title:"Diameter of mirror in inches"},
yaxis:{title:"F/#"},
mouse:{ track:true,
relative:true,
position: 'ne',
sensibility: 20, // => The smaller this value, the more precise you've to point
trackDecimals: 2,
trackFormatter: function(obj){ return obj.x+" "+units+" F/"+obj.y }
},
};
if (area == "nochange")
area=lastArea;
if (bCM)
options.xaxis.title="Diameter of mirror in cm";
if (area=="out")
{
options.xaxis.min=0;
options.xaxis.max=80;
options.yaxis.min=0;
if (bCM)
options.xaxis.max *= 2.54;
}
else if (area)
{
options.xaxis.min=area.x1;
options.xaxis.max=area.x2;
options.yaxis.min=area.y1;
options.yaxis.max=area.y2;
} else
{
options.xaxis.min=0;
options.yaxis.min=0;
}
Flotr.draw($('aaa'), series ,options);
lastArea=area;
}
makeCustomPoints();
load_up_data();
plot("out");
//
// handle zoom
//
$('aaa').observe('flotr:select', function(evt){
var area = evt.memo[0];
plot(area);
});
$('idBtnZoomout').observe('click', function(){plot("out")});
$('idBtnZoomoutMore').observe('click', function(){plot()});
</script>
</body>