Ronchigram ported from BASIC

Scott Prahl

April 2021

This notebook is only of historical interest but might be of help to someone trying to write their first ronchigram program.

From the April 1991 issue of Sky and Telescope, page 419, has code written in BASIC that creates a ronchigram for a parabolic mirror.

https://skyandtelescope.org/astronomy-resources/basic-programs-from-sky-telescope/

[1]:
import time
import numpy as np
import matplotlib.pyplot as plt
import lenstest
%config InlineBackend.figure_format='retina'

Original Source

Here is a listing of the original basic program. It is only 51 lines long and was pretty easy to port to python.

10 REM       RONCHI.BAS
20 REM
30 INPUT "Mirror diameter    ";D
40 INPUT "Radius of curvature";R
50 INPUT "Grating frequency  ";F
60 PRINT "Grating distance Delta"
70 PRINT "  from the mirror's
80 PRINT "  center of curvature"
90 INPUT "  (+ is outside)   ";DL
100 W=1/(2*F): REM  Line width
120 CLS
130 SCREEN 2
140 X0=300: Y0=100: C=300
150 K=.42
160 CIRCLE (X0,Y0),C/2
170 FOR I=1 TO 10000
180 X=D*(RND(1)-.5)
190 Y=D*(RND(1)-.5)
200 REM   X and Y are the ray's
210 REM   coordinates on the face
220 REM   of the mirror
230 S2=X*X+Y*Y
240 IF SQR(S2)>D/2 THEN 390
250 Z=R+S2/R
260 L=R+DL-Z
270 U=L*X/Z
280 REM   Now, test to see if the
290 REM   ray is blocked (FL=0)
300 REM   or transmitted (FL=1)
310 REM   by the grating
320   FL=0: REM  Reset flag
330   T=INT(ABS(U/W)+.5)
340   IF T/2=INT(T/2) THEN FL=1
350   IF FL=0 THEN 390
360 XP=X0+X*C/D
370 YP=Y0+Y*K*C/D
380 PSET(XP,YP): REM  Plot point
390 NEXT I
400 LOCATE 1,1
410   PRINT "Diameter =   ";D
420 LOCATE 2,1
430   PRINT "R of C =     ";R
440 LOCATE 3,1
450   PRINT "Ronchi freq =";F
460 LOCATE 4,1
470   PRINT "Delta =      ";DL
480 END
490 REM ************************************
500 REM   APPEARED IN ASTRONOMICAL COMPUTING
510 REM   SKY & TELESCOPE - APRIL 1991 ISSUE
520 REM ************************************

First translation of the BASIC program

This version is slow.

[2]:
start = time.time()

Fnumber = 4
D = 10     # inches mirror diameter
F = 100    # lines per inch (grating frequency)
DL = -0.62 # Grating to mirrors center
R = 160    # inches

W=1/(2*F)  # lines per inch

X0=0
Y0=0
C=300
K=0.42
Rsqr = (D/2)**2
K=1

print("           Diameter %6.2f in" % D)
print("Radius of Curvature %6.2f in" % R)
print("              Delta %6.2f in" % DL)
print("       Focal Length %6.2f in" % (R/2))
print("                 F# %6.2f" % (R/2/D))
print("        Ronchi freq %6.2f lines per in" % F)
print()

lenstest.lenstest.draw_circle(R,X0,Y0)

for i in range(10000):
    # X,Y are coordinates on the face of the mirror
    X=D*(np.random.random()-0.5)
    Y=D*(np.random.random()-0.5)
    S2=X*X+Y*Y
    if S2 <= Rsqr:
        Z=R+S2/R
        L=R+DL-Z
        U=L*X/Z

# Now, test to see if the ray is blocked (FL=0) or transmitted (FL=1) by the grating
        T=int(abs(U/W)+.5)
        if T % 2 > 0:
            XP=X0+X*C/D
            YP=Y0+Y*K*C/D
            plt.plot([XP],[YP],'o', markersize=1, color='black')

plt.gca().set_aspect('equal')
plt.show()
end = time.time()
print("elapsed time = %.0f ms" % (1000*(end - start)))
           Diameter  10.00 in
Radius of Curvature 160.00 in
              Delta  -0.62 in
       Focal Length  80.00 in
                 F#   8.00
        Ronchi freq 100.00 lines per in

_images/ronchi-origin_4_1.png
elapsed time = 3133 ms

Revised version without loops

This version is some 30X faster and does 10X more points.

[3]:
start = time.time()

Fnumber = 4
D = 10     # inches mirror diameter
F = 100    # lines per inch (grating frequency)
DL = -0.62 # Grating to mirrors center
R = 160    # inches

W=1/(2*F)  # lines per inch

npoints = 100000
X0=300
Y0=100
C=300
K=0.42

K=1

print("           Diameter %6.2f in" % D)
print("Radius of Curvature %6.2f in" % R)
print("              Delta %6.2f in" % DL)
print("       Focal Length %6.2f in" % (R/2))
print("                 F# %6.2f" % (R/2/D))
print("        Ronchi freq %6.2f lines per in" % F)
print()


plt.subplots(1,1,figsize=(6,4))
plt.subplot(1,1,1)
lenstest.lenstest.draw_circle(R,X0,Y0)

U1 = np.random.uniform(size = npoints)
U2 = np.random.uniform(size = npoints)
X = D/2 * np.sqrt(U2) * np.cos(2 * np.pi * U1)
Y = D/2 * np.sqrt(U2) * np.sin(2 * np.pi * U1)

Z=R+(X**2+Y**2)/R

U=(R+DL)*X/Z - X

T=(np.abs(U/W)+0.5).astype(int)
Tmask = T%2==1
XP=X0+X*C/D
YP=Y0+Y*K*C/D

xp_mask = np.ma.masked_where(Tmask, XP)
yp_mask = np.ma.masked_where(Tmask, YP)

plt.plot(xp_mask,yp_mask,'o', markersize=0.1, color='black')

plt.gca().set_aspect('equal')
plt.show()
end = time.time()
print("elapsed time = %.0f ms" % (1000*(end - start)))
           Diameter  10.00 in
Radius of Curvature 160.00 in
              Delta  -0.62 in
       Focal Length  80.00 in
                 F#   8.00
        Ronchi freq 100.00 lines per in

_images/ronchi-origin_6_1.png
elapsed time = 158 ms

version in the lenstest library

[4]:
F = 100                # grating frequency in [lines/inch]
conic = 0              # spherical
D = 10 * 25.4          # mirror diameter [mm]
z_offset = 0.71*25.4   # Ronchi location relative to focus [mm}
RoC = 160 * 25.4       # mm
lp_per_mm = 100/25.4   # line pairs per mi

fig, ax = lenstest.ronchi.plot_ruling_and_screen(D, RoC, lp_per_mm, z_offset, conic)
fig.set_size_inches(11, 4)

plt.show()
_images/ronchi-origin_8_0.png
[ ]: