codexpulsed-doppler-depth-velocity-limit.py
pulsed-doppler-depth-velocity-limit.py
"""Plot the ideal axial pulsed-Doppler depth/speed timing bound.
Python 3.14, NumPy and Matplotlib as declared in pyproject.toml.
Run from the mirrored _media directory; output is written to the cwd.
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
sound_speed = 1540.0
centimetres = np.linspace(5.0, 35.0, 401)
metres = centimetres / 100.0
fig, ax = plt.subplots(figsize=(9.6, 4.2), dpi=100, facecolor='white')
ax.set_facecolor('white')
for frequency, color in [(2.5e6, '#1261a0'), (5.0e6, '#d46216')]:
boundary = sound_speed**2 / (8.0 * frequency * metres)
ax.plot(centimetres, boundary, color=color, linewidth=2.5,
label=f'{frequency / 1e6:g} MHz transmitted')
speed = sound_speed**2 / (8.0 * frequency * 0.15)
ax.plot([15.0], [speed], 'o', color=color)
ax.annotate(f'{speed:.3f} m/s at 15 cm', (15.0, speed),
xytext=(12, 7), textcoords='offset points', color=color)
ax.set(xlabel='Maximum uniquely localized depth (cm)',
ylabel='Axial Nyquist speed (m/s)', xlim=(5, 35), ylim=(0, 2.6),
title='Pulsed Doppler: greater depth reduces the unaliased speed range')
ax.grid(alpha=0.22)
ax.legend(frameon=False, loc='upper right')
fig.text(0.5, 0.025,
'Ideal single-depth timing; c = 1540 m/s; beam-parallel motion. Usable speeds lie below the curves.',
ha='center', fontsize=9)
fig.tight_layout(rect=(0, 0.055, 1, 1))
fig.savefig('pulsed-doppler-depth-velocity-limit.png', facecolor='white', dpi=100)
plt.close(fig)