Binocular design

This example has been inspired by the book Fischer, Tadic-Galeb, Yoder. Optical system design. Second edition; chapter 22, p642 where the design of a binocular is studied. Although the book does not provide any specific optical component to be used in the design, it does provide guidelines on how to choose and locate the optical components.

The binocular proposed in the book is a 7 \times 50 binocular. It is recommended to use an achromatic doublet of 200mm of focal length and 50mm of diameter as an objective and a double Porro prism of SK5 glass. It is suggested to leave at least 40mm from the exit surface of the second prism to the image plane. A symmetrical eyepiece, composed of two achromatic doublets, is suggested. The suggested eyepiece focal length is f is 30mm.

The example is developed step by step. The binocular has been designed using pyOpTools predefined components and lenses from the Edmund Optics Inc catalogue.

[1]:
from pyoptools.all import *
from math import pi
[2]:
# Lenses choice: We look in the catalogue for the appropriate objective lens

L1 = library.Edmund.get("45179")  # f=200 r= 25

OA = Ray(pos=(0, 0, -10000), dir=(0, 0, 1), wavelength=0.55)  # Optical axis

C = CCD(size=(10, 10))

S = System(
    complist=[(L1, (0, 0, 100), (0, pi, 0)), (C, (0, 0, 320.053), (0, 0, 0))], n=1
)

PB = parallel_beam_c(
    origin=(0, 0, 50),
    direction=(0, 0, 0),
    size=(15, 15),
    num_rays=(5, 5),
    wavelength=0.55,
)

S.ray_add(PB)

S.propagate()

display(Plot3D(S, center=(0, 0, 180), size=(250, 70), scale=4, rot=[(0, pi / 2, 0)]))

f = (nearest_points(PB[7].get_final_rays()[0], PB[8].get_final_rays()[0])[0][2]) - (
    find_ppp(S, OA)[2]
)
print(f)
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/pyoptools/raytrace/library/library.py:148: DeprecationWarning: This method is deprecated, you can use dictionary-style access instead
  warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py:3699: DeprecationWarning: The 'pos' keyword argument is deprecated and will be removed in a future version. Please use 'origin' instead.
  exec(code_obj, self.user_global_ns, self.user_ns)
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py:3699: DeprecationWarning: The 'dir' keyword argument is deprecated and will be removed in a future version. Please use 'direction' instead.
  exec(code_obj, self.user_global_ns, self.user_ns)
Ray(array([     0.,      0., -10000.]), array([0., 0., 1.]), intensity=1.0, wavelength=0.55, n=nan, label='', orig_surf=None, order=0) Ray(array([-1.e-01,  0.e+00, -1.e+04]), array([0., 0., 1.]), intensity=1.0, wavelength=0.55, n=nan, label='', orig_surf=None, order=0)
nan
[3]:
# Placing the objective and the Porro prism

L1 = library.Edmund.get("45179")  # f=200 r= 25

RP1 = RightAnglePrism(width=55, height=55, material=material.schott["N-SK5"])
RP2 = RightAnglePrism(width=40, height=40, material=material.schott["N-SK5"])

CC = CCD(size=(50, 50))

S = System(
    complist=[
        (L1, (0, 0, 100), (0, 0, 0)),
        (RP1, (0, -22.5, 150), (pi, -pi / 4, pi / 2)),
        (RP2, (-20, -40, 140), (0, pi / 4, 0)),
        (CC, (-40, -46.41421356, 195.64187845), (0, 0, 0)),
    ],
    n=1,
)

PB = parallel_beam_c(
    origin=(0, 0, 0),
    direction=(0, 0, 0),
    size=(15, 15),
    num_rays=(5, 5),
    wavelength=0.55,
)

S.ray_add(PB)
S.propagate()

display(
    Plot3D(
        S,
        center=(0, -30, 140),
        size=(250, 150),
        scale=5,
        rot=[(0, pi / 2.0, 0), (pi / 6, 0, 0)],
    )
)

# Calculating the coordinates of the paraxial focal point
nearest_points(PB[10].get_final_rays()[0], PB[15].get_final_rays()[0])
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/pyoptools/raytrace/library/library.py:148: DeprecationWarning: This method is deprecated, you can use dictionary-style access instead
  warnings.warn(
[3]:
(array([-40.        , -45.00001128, 195.64006397]),
 array([-40.        , -45.00000001, 195.64006439]),
 np.float64(1.128128785165642e-05),
 np.False_)
[4]:
# Looking for the right eyepiece configuration

L2 = library.Edmund.get("45175")  # f=30 ; r=10

CC = CCD(size=(50, 50))
S = System(
    complist=[
        (L2, (0, 0, 89.84), (0, pi, 0)),
        (L2, (0, 0, 100), (0, 0, 0)),
        (CC, (0, 0, 1.18656541e02), (0, 0, 0)),
    ],
    n=1,
)

## The distance between the two doublets is equal to the total lense thickness

PB = parallel_beam_c(
    origin=(0, 0, 0),
    direction=(0, 0, 0),
    size=(10, 10),
    num_rays=(5, 5),
    wavelength=0.55,
)

S.ray_add(PB)
S.propagate()

display(Plot3D(S, center=(0, 0, 90), size=(100, 50), scale=5, rot=[(0, pi / 2, 0)]))

# Distance between the center of the lense and the paraxial focal point
nearest_points(PB[7].get_final_rays()[0], PB[8].get_final_rays()[0])
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/pyoptools/raytrace/library/library.py:148: DeprecationWarning: This method is deprecated, you can use dictionary-style access instead
  warnings.warn(
[4]:
(array([-3.34259427e-02,  0.00000000e+00,  1.13920118e+02]),
 array([-4.23616191e-04,  4.23616191e-04,  1.13914704e+02]),
 np.float64(0.03344612357266048),
 np.False_)
[5]:
## Placing the eyepiece: Binocular 7x50

L1 = library.Edmund.get("45179")  # f=200 r= 25
L2 = library.Edmund.get("45175")  # f=30 ; r=10


RP1 = RightAnglePrism(width=55, height=55, material=material.schott["N-SK5"])
RP2 = RightAnglePrism(width=40, height=40, material=material.schott["N-SK5"])


CC = CCD(size=(50, 50))

S = System(
    complist=[
        (L1, (0, 0, 100), (0, 0, 0)),
        (RP1, (0, -22.5, 150), (pi, -pi / 4, pi / 2)),
        (RP2, (-20, -40, 140), (0, pi / 4, 0)),
        # (L2,(-40,-46.41421356,195.64187847+12.32427176),(0,pi,0)),
        # (L2,(-40,-46.41421356,195.64187847+12.32427176+10.16),(0,0,0)),
        (L2, (-40, -44.99, 195.64187847 + 12.32427176), (0, pi, 0)),
        (L2, (-40, -44.99, 195.64187847 + 12.32427176 + 10.16), (0, 0, 0)),
        (CC, (-40, -44.99, 260), (0, 0, 0)),
    ],
    n=1,
)

OA = Ray(
    pos=(0, 0, 100), dir=(0, 0, 10), intensity=100, wavelength=0.55
)  # Optical axis
PB = parallel_beam_c(
    origin=(0, 0, 0),
    direction=(0, 0, 0),
    size=(15, 15),
    num_rays=(5, 5),
    wavelength=0.55,
)

S.ray_add(OA)
S.ray_add(PB)
S.propagate()

%pylab inline
display(
    Plot3D(
        S,
        center=(0, -20, 170),
        size=(250, 130),
        scale=2,
        rot=[(0, pi / 2.0, 0), (0, 0, 0)],
    )
)
figure()
spot_diagram_c(CC)
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/pyoptools/raytrace/library/library.py:148: DeprecationWarning: This method is deprecated, you can use dictionary-style access instead
  warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/pyoptools/raytrace/library/library.py:148: DeprecationWarning: This method is deprecated, you can use dictionary-style access instead
  warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py:3699: DeprecationWarning: The 'pos' keyword argument is deprecated and will be removed in a future version. Please use 'origin' instead.
  exec(code_obj, self.user_global_ns, self.user_ns)
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py:3699: DeprecationWarning: The 'dir' keyword argument is deprecated and will be removed in a future version. Please use 'direction' instead.
  exec(code_obj, self.user_global_ns, self.user_ns)
/home/docs/checkouts/readthedocs.org/user_builds/pyoptools/envs/latest/lib/python3.12/site-packages/IPython/core/magics/pylab.py:166: UserWarning: pylab import has clobbered these variables: ['pi', 'f', 'sqrt', 'cross', 'Polygon']
`%matplotlib` prevents importing * from pylab and numpy
  warn("pylab import has clobbered these variables: %s"  % clobbered +
%pylab is deprecated, use %matplotlib inline and import the required libraries.
Populating the interactive namespace from numpy and matplotlib
../../_images/notebooks_basic_Binocular_5_3.png
[ ]: