2  Step-index optical fibers

Open in Colab  Run this chapter’s code in Google Colab (no installation).

Slides 40–64. A cylindrical core of radius \(a\) and index \(n_1\), inside a cladding of index \(n_2\). Two kinds of rays appear (slide 42). Meridional rays cross the axis. Skew rays spiral around it. In the wave picture the two families give the LP\(_{lm}\) modes.

2.1 Weakly guiding fibers and LP modes (slides 43–47)

When \(\Delta\ll1\) (typically 0.3 % to 2 %), the guided modes are almost transverse and linearly polarised:

\[ E_{LP}=E_{lm}(r,\phi)\,e^{j(\omega t-\beta_{lm}z)},\qquad E_{lm}(r,\phi)=f(r)\cos(l\phi)\ \text{or}\ f(r)\sin(l\phi). \]

Inside the core \(f(r)\propto J_l(ur/a)\) and in the cladding \(f(r)\propto K_l(wr/a)\), with \(u^2+w^2=V^2\). Continuity of \(f\) and \(f'\) at \(r=a\) gives the eigenvalue equation

\[ \frac{u\,J_{l-1}(u)}{J_l(u)}=-\frac{w\,K_{l-1}(w)}{K_l(w)} . \tag{2.1}\]

The mode index \(l\) sets the azimuthal order (\(2l\) maxima around a circle). The index \(m\) counts the radial maxima. The function fl.LP_b(V, l, m) solves Equation 2.1, and you can read it in fiberlib.py: it is a sign-change scan followed by Brent’s method.

Show the Python
for l, m, b in fl.LP_all(5.0):
    print(f"LP{l}{m}:  b = {b:.4f}")
print("Guided modes incl. polarisation and orientation:", fl.LP_count(5.0), " (V²/2 =", 5**2/2, ")")
LP01:  b = 0.8409
LP11:  b = 0.6024
LP21:  b = 0.3015
LP02:  b = 0.2154
Guided modes incl. polarisation and orientation: 12  (V²/2 = 12.5 )

2.1.1 The \(b\)–\(V\) chart (slide 49)

Show the Python
Vg = np.linspace(0.3, 10, 400)
modes = [(0,1),(1,1),(2,1),(0,2),(3,1),(1,2),(4,1),(2,2),(0,3),(5,1)]
curves = {}
fig, ax = plt.subplots(figsize=(8, 4.6))
for k, (l, m) in enumerate(modes):
    b = np.array([fl.LP_b(V, l, m) for V in Vg])
    curves[f"LP{l}{m}"] = b
    ok = np.isfinite(b)
    ax.plot(Vg[ok], b[ok], color=fl.PALETTE[k % 8], lw=1.8)
    i = np.where(ok)[0][-1]
    ax.text(Vg[i] + 0.05, b[i], f"LP$_{{{l}{m}}}$", fontsize=8.5, va="center")
Va = np.linspace(1.2, 3.5, 100)
ax.plot(Va, fl.b_approx(Va), "--", color=fl.INK, lw=1, label="approximation (LP01)")
for vc in [2.405, 3.832, 5.136, 5.520]:
    ax.axvline(vc, color=fl.GRID, lw=1, zorder=0)
ax.set_xlabel("V"); ax.set_ylabel("b"); ax.set_ylim(0, 1); ax.set_xlim(0, 10.8)
ax.legend(loc="lower right")
plt.show()
Figure 2.1: Normalized propagation constant b(V) of the first LP modes (exact solution of the weak-guidance eigenvalue equation). The dashed line is the LP01 approximation b ≈ (1.1428 − 0.996/V)², good for 1.5 < V < 2.5.

The cut-offs are zeros of Bessel functions. LP\(_{0m}\) is cut off at the zeros of \(J_1\) (0, 3.832, 7.016 …). LP\(_{lm}\) with \(l\ge1\) is cut off at the zeros of \(J_{l-1}\), which gives 2.405 for LP\(_{11}\).

Below you can sweep \(V\) and see which modes are guided, together with their \(b\) and effective index.

2.1.2 What the modes look like (slides 44–47)

Show the Python
V = 8.0
allm = fl.LP_all(V)
x = np.linspace(-1.6, 1.6, 241)
X, Y = np.meshgrid(x, x)
R, PHI = np.hypot(X, Y), np.arctan2(Y, X)
n = len(allm); cols = 5; rows = int(np.ceil(n / cols))
fig, axs = plt.subplots(rows, cols, figsize=(1.9 * cols, 2.0 * rows))
for ax in axs.flat: ax.axis("off")
for ax, (l, m, b) in zip(axs.flat, allm):
    E = fl.LP_field(R, V, b, l) * np.cos(l * PHI)
    E /= np.abs(E).max()
    ax.imshow(E, cmap="RdBu_r", vmin=-1, vmax=1, extent=[-1.6, 1.6, -1.6, 1.6])
    ax.add_patch(plt.Circle((0, 0), 1, fill=False, color="k", lw=0.6))
    ax.set_title(f"LP$_{{{l}{m}}}$  b={b:.2f}", fontsize=9)
plt.tight_layout(); plt.show()
Figure 2.2: Transverse field E(x,y) of the LP modes guided at V = 8 (cos lφ orientation). Red and blue are opposite signs. The circle marks the core.
Show the Python
r = np.linspace(0, 2, 400)
fig, ax = plt.subplots()
ax.axvspan(0, 1, color=fl.PALETTE[0], alpha=0.07, lw=0)
for k, (l, m, b) in enumerate(allm[:8]):
    I = fl.LP_field(r, V, b, l) ** 2
    ax.plot(r, I / I.max(), color=fl.PALETTE[k], lw=1.6, label=f"LP{l}{m}")
ax.set_xlabel("r / a"); ax.set_ylabel("normalized irradiance"); ax.legend(ncol=4, fontsize=8)
plt.show()
Figure 2.3: Radial irradiance |f(r)|² of the guided LP modes at V = 8, normalized to the core boundary value. Low-b modes reach far into the cladding.

2.2 Fiber parameters (slides 48, 61–62)

quantity formula
normalized index difference \(\Delta = \dfrac{n_1^2-n_2^2}{2n_1^2}\approx\dfrac{n_1-n_2}{n_1}\)
numerical aperture \(\mathrm{NA}=\sqrt{n_1^2-n_2^2}\approx n_1\sqrt{2\Delta}\)
acceptance angle \(\sin\alpha_\text{max} = \mathrm{NA}/n_0\)
\(V\)-number \(V=\dfrac{2\pi a}{\lambda}\,\mathrm{NA}\)
single mode \(V<2.405\), i.e. \(\lambda>\lambda_c=\dfrac{2\pi a\,\mathrm{NA}}{2.405}\)
number of modes (\(V\gg1\)) \(M\approx V^2/2\)
Show the Python
Vs = np.linspace(0.5, 25, 120)
Mex = [fl.LP_count(v) for v in Vs]
fig, ax = plt.subplots()
ax.step(Vs, Mex, where="post", label="exact count")
ax.plot(Vs, Vs**2 / 2, "--", color=fl.PALETTE[1], label="V²/2")
ax.set_xlabel("V"); ax.set_ylabel("number of modes M"); ax.legend()
plt.show()
Figure 2.4: Exact number of guided modes (both polarisations, both orientations) compared with V²/2.

2.2.1 Single-mode cut-off vs. index contrast (from ofiber_test.ipynb)

Show the Python
n_clad = 1.45
D = np.linspace(0.0005, 0.02, 300)
n_core = n_clad / (1 - D)
fig, ax = plt.subplots()
for k, a in enumerate(np.array([2, 2.5, 3, 3.5, 4, 4.5, 5]) * 1e-6):
    lc = 2 * np.pi * a * fl.NA(n_core, n_clad) / 2.405
    ax.plot(D * 100, lc * 1e9, color=fl.PALETTE[k], label=f"a = {a*1e6:.1f} µm")
for lam in (1310, 1550):
    ax.axhline(lam, color=fl.INK2, lw=0.8, ls=":")
ax.set_xlabel("Δ (%)"); ax.set_ylabel("λc (nm)"); ax.set_ylim(0, 3000); ax.legend(ncol=2, fontsize=8)
plt.show()
Figure 2.5: Cut-off wavelength of a step-index fiber versus Δ for several core radii (n2 = 1.45).

2.3 Live fiber designer

Type the fiber parameters and all the quantities of slides 48–64 update. You can also use this panel to check every worked example below.

2.4 Worked examples (slides 51–64), recomputed

Show the Python
def report(title, **kv):
    print(f"── {title}")
    for k, v in kv.items(): print(f"   {k:<28s} {v}")

# Slide 57 — multimode fiber
V = fl.V_number(50e-6, 0.85e-6, 1.468, 1.447)
report("Slide 57: MMF, 2a = 100 µm, λ = 850 nm", V=f"{V:.2f}", **{"M ≈ V²/2": f"{V**2/2:.0f}",
       "exact count": fl.LP_count(V)})

# Slide 58 — maximum radius for SMF at 1.3 µm
a_max = 2.405 * 1.3e-6 / (2 * np.pi * fl.NA(1.468, 1.447))
report("Slide 58: SMF core radius at 1.3 µm", **{"a ≤": f"{a_max*1e6:.2f} µm"})

# Slides 59-60 — cut-off and MFD
a, n1, n2 = 4.1e-6, 1.4532, 1.4485
lc = 2 * np.pi * a * fl.NA(n1, n2) / 2.405
V = fl.V_number(a, 1.31e-6, n1, n2)
report("Slides 59–60: 2a = 8.2 µm at 1.31 µm", **{
    "λc": f"{lc*1e6:.3f} µm", "V": f"{V:.3f}",
    "MFD Marcuse": f"{fl.mfd_marcuse(a, V)*1e6:.2f} µm",
    "MFD 2a(2.6/V)": f"{2*a*2.6/V*1e6:.2f} µm",
    "MFD exact Gaussian fit (new)": f"{fl.mfd_exact_gaussian(a, V)*1e6:.2f} µm",
    "MFD Petermann II (ITU, new)": f"{fl.mfd_petermann2(a, V)*1e6:.2f} µm"})

# Slide 63 — NA and acceptance
n1, n2 = 1.480, 1.460
NAv = fl.NA(n1, n2); V = fl.V_number(50e-6, 0.85e-6, n1, n2)
report("Slide 63: MMF NA", NA=f"{NAv:.4f}", **{"α_max": f"{np.degrees(np.arcsin(NAv)):.1f}°",
       "2α_max": f"{2*np.degrees(np.arcsin(NAv)):.1f}°", "V": f"{V:.2f}", "M": f"{V**2/2:.0f}",
       "Δ": f"{(n1-n2)/n1*100:.2f} %"})

# Slide 64 — SMF with Δ = 0.3 %
n1, Dl = 1.460, 0.003
NAv = n1 * np.sqrt(2 * Dl)
report("Slide 64: SMF, 2a = 8 µm, Δ = 0.3 %", NA=f"{NAv:.3f}",
       **{"2α_max": f"{2*np.degrees(np.arcsin(NAv)):.1f}°", "λc": f"{2*np.pi*4e-6*NAv/2.405*1e6:.2f} µm"})
── Slide 57: MMF, 2a = 100 µm, λ = 850 nm
   V                            91.44
   M ≈ V²/2                     4181
   exact count                  4202
── Slide 58: SMF core radius at 1.3 µm
   a ≤                          2.01 µm
── Slides 59–60: 2a = 8.2 µm at 1.31 µm
   λc                           1.251 µm
   V                            2.297
   MFD Marcuse                  9.31 µm
   MFD 2a(2.6/V)                9.28 µm
   MFD exact Gaussian fit (new) 9.23 µm
   MFD Petermann II (ITU, new)  9.14 µm
── Slide 63: MMF NA
   NA                           0.2425
   α_max                        14.0°
   2α_max                       28.1°
   V                            89.62
   M                            4016
   Δ                            1.35 %
── Slide 64: SMF, 2a = 8 µm, Δ = 0.3 %
   NA                           0.113
   2α_max                       13.0°
   λc                           1.18 µm

2.4.1 Group velocity and group delay (slides 51–52)

SMF with \(n_1=1.4480\), \(n_2=1.4400\), \(a=3\) µm at \(\lambda=1.5\) µm. What are \(v_g\) and the group delay over 1 km? Neglect material dispersion.

The slides use \(\beta \approx n_2k[1+b\Delta]\) with the approximate \(b(V)\), and a 0.1 % finite difference. We do the same and also use the exact \(b(V)\) and an exact derivative.

Show the Python
n1, n2, a = 1.4480, 1.4400, 3e-6
Dl = (n1 - n2) / n1
def beta(wl, exact):
    V = fl.V_number(a, wl, n1, n2)
    b = fl.LP_b(V, 0, 1) if exact else fl.b_approx(V)
    return n2 * 2 * np.pi / wl * (1 + b * Dl)
for exact, h in [(False, 1.5e-9), (True, 1e-12)]:
    w1, w2 = 1.5e-6, 1.5e-6 + h
    om = lambda wl: 2 * np.pi * c0 / wl
    vg = (om(w1) - om(w2)) / (beta(w1, exact) - beta(w2, exact))
    print(f"{'exact b(V), dλ→0' if exact else 'approx. b(V), dλ = 0.1%':<26s}: "
          f"b = {fl.LP_b(fl.V_number(a,w1,n1,n2),0,1) if exact else fl.b_approx(fl.V_number(a,w1,n1,n2)):.5f}, "
          f"v_g = {vg:.5e} m/s, τ_g(1 km) = {1e3/vg*1e6:.4f} µs")
approx. b(V), dλ = 0.1%   : b = 0.38609, v_g = 2.07007e+08 m/s, τ_g(1 km) = 4.8308 µs
exact b(V), dλ→0          : b = 0.38614, v_g = 2.06997e+08 m/s, τ_g(1 km) = 4.8310 µs

2.5 Mode field diameter (slides 53–56)

The LP\(_{01}\) field is very close to a Gaussian \(E\propto\exp(-r^2/w^2)\). The MFD is \(2w\). Because the intensity is Gaussian, the fraction of power inside \(r<w\) is \(1-e^{-2}\approx 86\,\%\), just like a Gaussian beam (Chapter 1).

Show the Python
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 3.9))
V = 2.30; b = fl.LP_b(V, 0, 1)
r = np.linspace(0, 3, 400)
E = fl.LP_field(r, V, b, 0); E /= E[0]
w = fl.mfd_exact_gaussian(1, V) / 2
ax1.plot(r, E**2, label="exact LP₀₁ intensity")
ax1.plot(r, np.exp(-2 * (r / w) ** 2), "--", label=f"Gaussian, w = {w:.3f} a")
ax1.axvline(1, color=fl.INK2, lw=0.8, ls=":"); ax1.text(1.02, 0.9, "core edge", color=fl.INK2)
ax1.set_xlabel("r / a"); ax1.set_ylabel("intensity"); ax1.legend()
Vs = np.linspace(1.2, 3.0, 40)
ax2.plot(Vs, [fl.mfd_exact_gaussian(1, v) / 2 for v in Vs], label="exact (Gaussian fit)")
ax2.plot(Vs, [fl.mfd_petermann2(1, v) / 2 for v in Vs], label="Petermann II")
ax2.plot(Vs, fl.mfd_marcuse(1, Vs) / 2, "--", label="Marcuse")
ax2.plot(Vs, 2.6 / Vs, ":", label="2.6/V")
ax2.axvspan(1.5, 2.5, color=fl.GRID, alpha=0.5, lw=0)
ax2.set_xlabel("V"); ax2.set_ylabel("MFD / 2a"); ax2.legend(fontsize=8)
plt.tight_layout(); plt.show()
Figure 2.6: Left: exact LP01 field at V = 2.30 and its best Gaussian fit. Right: MFD/2a versus V from three definitions. Marcuse’s formula is excellent for 1.5 < V < 2.5. The simple 2.6/V rule is not.

2.5.1 \(V\) across the telecom band (from ofiber_test.ipynb)

The indices themselves depend on \(\lambda\) (Sellmeier, see Chapter 3). Here is \(V(\lambda)\) for a 9 µm core of SiO\(_2\) doped with 2 % GeO\(_2\) inside a pure-silica cladding:

Show the Python
lam = np.linspace(1000, 1650, 200) * 1e-9
V = fl.V_number(4.5e-6, lam, fl.n_doped(lam, 0.02), fl.n_silica(lam))
fig, ax = plt.subplots()
ax.plot(lam * 1e9, V)
ax.axhspan(2.0, 2.405, color=fl.PALETTE[2], alpha=0.12, lw=0)
ax.axhline(2.405, color=fl.INK2, lw=0.8)
ax.text(1610, 2.2, "practical SM window\n2.0 < V < 2.405", ha="right", va="center", fontsize=9)
ax.set_xlabel("λ (nm)"); ax.set_ylabel("V")
plt.show()
Figure 2.7: V-number versus wavelength for 2a = 9 µm, core SiO2 + 2 mol% GeO2, cladding pure SiO2.

2.6 Try it yourself

  1. Modify the 2-D map cell to plot intensity of LP\(_{11}\) as the sum of its cos and sin variants with random phases. This is what a speckled multimode output looks like.
  2. Check the claim of slide 55: for which \(V\) range is Marcuse within 1 % of the exact Gaussian fit?
  3. Exact vector modes (advanced). LP\(_{11}\) is really HE\(_{21}\), TE\(_{01}\) and TM\(_{01}\), which have slightly different \(\beta\). Look up the full eigenvalue equation (Snyder & Love) and plot the splitting versus \(\Delta\). This is the beginning of mode-division multiplexing (MDM) with few-mode fibers.
  4. Use the fiber designer to design a fiber that is single-mode at 850 nm (VCSEL links), with an MFD larger than 5 µm.