パルスドップラーレーダーの原理を解説して実装する

パルスドップラーレーダーは、パルスレーダーの距離計測能力と、ドップラー効果による速度計測能力を組み合わせたレーダーシステムです。目標の位置だけでなく速度も同時に計測でき、さらに静止クラッタ(地面や建物からの反射)を抑圧して移動目標を検出できるため、航空管制・気象観測・軍事レーダー・自動車レーダーなど、現代のレーダーシステムの中核技術となっています。

本記事では、ドップラー効果の数学的導出から出発し、パルスレーダーの距離計測原理、距離分解能と速度分解能のトレードオフ、MTI(Moving Target Indication)フィルタ、パルスドップラー処理の原理を解説します。最後にPythonでパルスドップラーレーダーのシミュレーションを実装し、Range-Dopplerマップを生成します。

本記事の内容

  • ドップラー効果の数学的導出(往復経路での位相変化から)
  • パルスレーダーの距離測定原理
  • 距離分解能 $\Delta R = cT_p/2$
  • PRFと最大無曖昧距離・最大無曖昧速度のトレードオフ
  • MTI(Moving Target Indication)の原理
  • パルスドップラー処理(レンジゲートごとのFFT)
  • Range-Dopplerマップの生成
  • Pythonによるシミュレーション

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

ドップラー効果の数学的導出

往復経路での位相変化

レーダーと目標の間の距離が $R(t)$ のとき、送信波が目標まで往復する経路長は $2R(t)$ です。送信波の搬送波周波数を $f_0$、波長を $\lambda_0 = c/f_0$ とすると、往復経路で生じる位相変化は

$$ \phi(t) = \frac{2\pi}{\lambda_0} \cdot 2R(t) = \frac{4\pi R(t)}{\lambda_0} $$

受信信号の瞬時角周波数は $\phi(t)$ の時間微分です。

$$ \omega_r(t) = \frac{d\phi(t)}{dt} = \frac{4\pi}{\lambda_0} \frac{dR(t)}{dt} = \frac{4\pi v_r(t)}{\lambda_0} $$

ここで $v_r(t) = dR/dt$ は目標の 視線方向速度(radial velocity)です。$v_r > 0$ は目標が遠ざかる方向です。

受信信号の周波数は送信周波数と異なり、その差がドップラー周波数 $f_d$ です。

$$ f_d = \frac{\omega_r}{2\pi} = \frac{2v_r}{\lambda_0} $$

符号の慣習として、$v_r > 0$(遠ざかる)のとき $f_d > 0$ とする定義と、$v_r < 0$(近づく)のとき $f_d > 0$ とする定義がありますが、ここでは前者を採用します。

$$ \begin{equation} \boxed{f_d = \frac{2v_r}{\lambda_0} = \frac{2v_r f_0}{c}} \end{equation} $$

具体例

  • 航空機($v_r = 300$ m/s)、Xバンドレーダー($f_0 = 10$ GHz、$\lambda_0 = 0.03$ m)の場合: $f_d = 2 \times 300 / 0.03 = 20{,}000$ Hz $= 20$ kHz

  • 自動車($v_r = 30$ m/s)、ミリ波レーダー($f_0 = 77$ GHz、$\lambda_0 = 0.0039$ m)の場合: $f_d = 2 \times 30 / 0.0039 \approx 15{,}385$ Hz $\approx 15.4$ kHz

パルスレーダーでのドップラー

パルスレーダーでは、連続的な信号ではなくパルスの列を送信します。第 $m$ 番目のパルス(送信時刻 $t_m = mT_{\text{PRI}}$、$T_{\text{PRI}}$ はパルス繰り返し間隔)の受信信号の位相を考えます。

目標が等速で移動している場合($v_r$ 一定)、$m$ 番目のパルス受信時の目標距離は

$$ R_m = R_0 + v_r \cdot mT_{\text{PRI}} $$

受信パルスの位相は

$$ \phi_m = \frac{4\pi R_m}{\lambda_0} = \frac{4\pi R_0}{\lambda_0} + \frac{4\pi v_r}{\lambda_0} mT_{\text{PRI}} $$

パルス間の位相差は

$$ \Delta\phi = \phi_{m+1} – \phi_m = \frac{4\pi v_r T_{\text{PRI}}}{\lambda_0} = 2\pi f_d T_{\text{PRI}} $$

これは、パルスごとに $2\pi f_d T_{\text{PRI}}$ ずつ位相が回転することを意味します。この位相回転をFFTで検出するのがパルスドップラー処理です。

パルスレーダーの距離測定原理

距離の計算

パルスを送信してから反射波を受信するまでの時間遅延 $\tau$ から、目標距離 $R$ を求めます。

$$ \begin{equation} R = \frac{c\tau}{2} \end{equation} $$

電波は光速 $c = 3 \times 10^8$ m/s で伝搬し、目標まで往復するので $2$ で割ります。

距離分解能

パルス幅 $T_p$ のパルスを使用した場合、2つの目標を区別できる最小距離差が 距離分解能 $\Delta R$ です。

2つの目標の距離差が $\Delta R$ のとき、反射パルスの時間差は $\Delta\tau = 2\Delta R/c$ です。2つのパルスが重ならない条件は $\Delta\tau \ge T_p$ なので、

$$ \begin{equation} \Delta R = \frac{cT_p}{2} \end{equation} $$

距離分解能を改善するにはパルス幅を短くする必要がありますが、短いパルスは送信エネルギーが小さくなり、探知距離が短くなるという問題があります。

この問題を解決するのが パルス圧縮 です。長いパルスに周波数変調(チャープ: LFM)を施し、受信時にマッチドフィルタで圧縮します。帯域幅 $B$ のLFM信号を使うと、パルス圧縮後の距離分解能は

$$ \begin{equation} \Delta R = \frac{c}{2B} \end{equation} $$

パルス幅 $T_p$ と帯域幅 $B$ の積 $BT_p$(時間-帯域幅積)がパルス圧縮比であり、探知距離と距離分解能を同時に改善できます。

PRFと最大無曖昧距離・速度のトレードオフ

最大無曖昧距離

PRF(Pulse Repetition Frequency)$f_{\text{PRF}} = 1/T_{\text{PRI}}$ のパルスレーダーでは、次のパルスを送信するまでに反射波が戻ってこないと、距離の曖昧さが生じます。

$$ \begin{equation} R_{\text{ua}} = \frac{cT_{\text{PRI}}}{2} = \frac{c}{2f_{\text{PRF}}} \end{equation} $$

$R_{\text{ua}}$ を超える距離の目標は、次のパルスの反射として受信され、実際より近い距離にあるように見えます。

最大無曖昧速度

ドップラー周波数の検出には、ナイキストのサンプリング定理が適用されます。パルスごとの位相サンプリングレートは $f_{\text{PRF}}$ なので、

$$ |f_d| < \frac{f_{\text{PRF}}}{2} $$

$f_d = 2v_r/\lambda_0$ より、最大無曖昧速度は

$$ \begin{equation} v_{\text{ua}} = \frac{\lambda_0 f_{\text{PRF}}}{4} \end{equation} $$

トレードオフ

$R_{\text{ua}}$ と $v_{\text{ua}}$ を掛けると、

$$ R_{\text{ua}} \cdot v_{\text{ua}} = \frac{c}{2f_{\text{PRF}}} \cdot \frac{\lambda_0 f_{\text{PRF}}}{4} = \frac{c\lambda_0}{8} $$

$$ \begin{equation} R_{\text{ua}} \cdot v_{\text{ua}} = \frac{c\lambda_0}{8} \end{equation} $$

この積は周波数だけで決まる定数であり、PRFの選択によって $R_{\text{ua}}$ と $v_{\text{ua}}$ のどちらかを大きくすると、他方が小さくなります。

PRF 最大無曖昧距離 最大無曖昧速度 用途
Low PRF 大きい 小さい 捜索レーダー
Medium PRF 中間 中間 航空レーダー
High PRF 小さい 大きい 速度計測重視

この制約を緩和するために、複数のPRFを切り替えて使用する PRFスタッガリング 手法が実用されています。

MTI(Moving Target Indication)

MTIの原理

MTIは、静止クラッタ($f_d = 0$)を抑圧して移動目標のみを検出する技術です。最も簡単なMTIフィルタは、連続する2つのパルスの差分をとることです。

$$ \begin{equation} y[m] = x[m] – x[m-1] \end{equation} $$

ここで $x[m]$ は $m$ 番目のパルスの受信信号(特定のレンジゲートにおける複素振幅)です。

MTIフィルタの周波数応答

$z$ 変換を用いると、MTIフィルタの伝達関数は

$$ H(z) = 1 – z^{-1} $$

周波数応答($z = e^{j2\pi f/f_{\text{PRF}}}$)は

$$ \begin{align} H(e^{j2\pi f/f_{\text{PRF}}}) &= 1 – e^{-j2\pi f/f_{\text{PRF}}} \\ &= e^{-j\pi f/f_{\text{PRF}}} \left(e^{j\pi f/f_{\text{PRF}}} – e^{-j\pi f/f_{\text{PRF}}}\right) \\ &= 2j e^{-j\pi f/f_{\text{PRF}}} \sin\left(\frac{\pi f}{f_{\text{PRF}}}\right) \end{align} $$

振幅特性は

$$ |H(f)| = 2\left|\sin\left(\frac{\pi f}{f_{\text{PRF}}}\right)\right| $$

$f = 0$(静止クラッタ)で $|H| = 0$ となり、$f = f_{\text{PRF}}/2$ で最大値 $|H| = 2$ となります。つまり、静止目標は完全に抑圧され、$f_{\text{PRF}}/2$ 付近のドップラー周波数を持つ目標が最もよく通過します。

$f = nf_{\text{PRF}}$($n$ は整数)でも $|H| = 0$ となります。これらの周波数を ブラインドスピード と呼び、対応する速度 $v_{\text{blind}} = n\lambda_0 f_{\text{PRF}}/2$ の目標は検出できません。

2段MTIフィルタ

ブラインドスピード近傍での性能を改善するため、2段キャンセラを使うことがあります。

$$ y[m] = x[m] – 2x[m-1] + x[m-2] $$

伝達関数は

$$ H(z) = (1 – z^{-1})^2 $$

振幅特性は $|H(f)| = 4\sin^2(\pi f/f_{\text{PRF}})$ となり、DC近傍でのクラッタ抑圧がより強力になります。

パルスドップラー処理

レンジゲートとドップラー処理

パルスドップラーレーダーでは、受信信号を距離方向(レンジ)と速度方向(ドップラー)の2次元で処理します。

  1. レンジ処理: 各パルスの受信信号を距離方向にサンプリングし、$K$ 個のレンジビン(レンジゲート)に分割
  2. ドップラー処理: 各レンジゲートにおける $M$ パルスの複素振幅系列に対してFFTを適用

$M$ パルス、$K$ レンジビンの受信データを行列 $\bm{X}$ で表すと、

$$ \bm{X} = \begin{bmatrix} x[0,0] & x[0,1] & \cdots & x[0,K-1] \\ x[1,0] & x[1,1] & \cdots & x[1,K-1] \\ \vdots & & \ddots & \vdots \\ x[M-1,0] & x[M-1,1] & \cdots & x[M-1,K-1] \end{bmatrix} $$

行方向がパルス番号(slow time)、列方向がレンジビン番号(fast time)です。

Range-Dopplerマップ

各列(各レンジゲート)にFFTを適用すると、ドップラー周波数スペクトルが得られます。

$$ Y[k, l] = \sum_{m=0}^{M-1} x[m, l] \cdot e^{-j2\pi mk/M}, \quad k = 0, 1, \ldots, M-1 $$

$|Y[k, l]|^2$ を2次元プロットしたものが Range-Dopplerマップ です。横軸が距離、縦軸がドップラー周波数(速度)を表し、目標はマップ上のピークとして現れます。

速度分解能

$M$ パルスの観測時間は $T_{\text{obs}} = MT_{\text{PRI}}$ です。ドップラーFFTの周波数分解能は

$$ \Delta f_d = \frac{f_{\text{PRF}}}{M} = \frac{1}{MT_{\text{PRI}}} = \frac{1}{T_{\text{obs}}} $$

対応する速度分解能は

$$ \begin{equation} \Delta v = \frac{\lambda_0 \Delta f_d}{2} = \frac{\lambda_0}{2T_{\text{obs}}} = \frac{\lambda_0 f_{\text{PRF}}}{2M} \end{equation} $$

パルス数 $M$ を増やすほど速度分解能は向上しますが、データ収集時間が長くなります。

Pythonでの実装

パルスドップラーレーダーシミュレーション

LFM(Linear Frequency Modulation)パルスの生成、パルス圧縮、ドップラー処理、Range-Dopplerマップの表示までを一貫して実装します。

import numpy as np
import matplotlib.pyplot as plt

# =============================================================
# レーダーパラメータ
# =============================================================
c = 3e8            # 光速 [m/s]
f0 = 10e9          # 搬送波周波数 [Hz] (Xバンド)
lam = c / f0       # 波長 [m]
B = 5e6            # LFM帯域幅 [Hz]
Tp = 10e-6         # パルス幅 [s]
PRF = 1000         # PRF [Hz]
T_PRI = 1 / PRF    # PRI [s]
fs_fast = 20e6     # Fast-timeサンプリング周波数 [Hz]
M = 64             # パルス数
Pt = 1e3           # 送信電力 [W](シミュレーション上の概念値)

# 導出されるパラメータ
delta_R = c / (2 * B)  # 距離分解能
R_ua = c * T_PRI / 2   # 最大無曖昧距離
v_ua = lam * PRF / 4   # 最大無曖昧速度
delta_v = lam * PRF / (2 * M)  # 速度分解能

print('=== Radar Parameters ===')
print(f'Carrier frequency: {f0/1e9:.1f} GHz')
print(f'Wavelength: {lam*100:.2f} cm')
print(f'LFM bandwidth: {B/1e6:.1f} MHz')
print(f'Pulse width: {Tp*1e6:.1f} μs')
print(f'PRF: {PRF} Hz')
print(f'Number of pulses: {M}')
print(f'Range resolution: {delta_R:.1f} m')
print(f'Max unambiguous range: {R_ua/1e3:.1f} km')
print(f'Max unambiguous velocity: {v_ua:.1f} m/s')
print(f'Velocity resolution: {delta_v:.2f} m/s')

# =============================================================
# 目標の定義
# =============================================================
targets = [
    {'range': 30e3, 'velocity': 50, 'rcs': 10},    # 目標1: 30km, 50m/s
    {'range': 50e3, 'velocity': -100, 'rcs': 5},    # 目標2: 50km, -100m/s
    {'range': 30e3, 'velocity': -30, 'rcs': 3},     # 目標3: 30km, -30m/s(目標1と同距離)
    {'range': 70e3, 'velocity': 0, 'rcs': 20},      # 目標4: 70km, 静止(クラッタ)
]

# =============================================================
# LFMパルスの生成
# =============================================================
t_pulse = np.arange(0, Tp, 1/fs_fast)  # パルス内の時間
N_pulse = len(t_pulse)

# LFMチャープ信号(ベースバンド)
chirp_rate = B / Tp
tx_pulse = np.exp(1j * np.pi * chirp_rate * (t_pulse - Tp/2)**2)

# マッチドフィルタ(送信パルスの時間反転複素共役)
matched_filter = np.conj(tx_pulse[::-1])

# =============================================================
# Fast-timeサンプル数(レンジ方向)
# =============================================================
R_max_sim = 100e3  # シミュレーション最大距離
tau_max = 2 * R_max_sim / c
N_fast = int(np.ceil((tau_max + Tp) * fs_fast))

# =============================================================
# 受信信号の生成
# =============================================================
np.random.seed(42)
rx_data = np.zeros((M, N_fast), dtype=complex)

for m in range(M):
    for tgt in targets:
        R = tgt['range'] + tgt['velocity'] * m * T_PRI
        tau = 2 * R / c  # 遅延時間
        fd = 2 * tgt['velocity'] / lam  # ドップラー周波数

        # 遅延サンプル数
        delay_samples = int(np.round(tau * fs_fast))

        if delay_samples + N_pulse <= N_fast and delay_samples >= 0:
            # 受信パルス = 遅延・ドップラーシフトされた送信パルス
            amplitude = np.sqrt(tgt['rcs'])
            phase_shift = np.exp(1j * 2 * np.pi * fd * m * T_PRI)
            rx_data[m, delay_samples:delay_samples+N_pulse] += (
                amplitude * phase_shift * tx_pulse
            )

    # 受信ノイズ
    noise_power = 0.1
    rx_data[m] += np.sqrt(noise_power/2) * (
        np.random.randn(N_fast) + 1j * np.random.randn(N_fast)
    )

# =============================================================
# パルス圧縮(各パルスにマッチドフィルタ適用)
# =============================================================
N_conv = N_fast + N_pulse - 1
pc_data = np.zeros((M, N_conv), dtype=complex)

for m in range(M):
    pc_data[m] = np.convolve(rx_data[m], matched_filter, mode='full')

# パルス圧縮後のデータをレンジ方向にトリム
pc_data = pc_data[:, N_pulse-1:N_fast+N_pulse-1]

# レンジ軸
range_axis = np.arange(pc_data.shape[1]) * c / (2 * fs_fast)

# =============================================================
# ドップラー処理(各レンジゲートでFFT)
# =============================================================
# 窓関数を適用(サイドローブ低減)
window = np.hamming(M)
rd_map = np.zeros_like(pc_data)

for k in range(pc_data.shape[1]):
    rd_map[:, k] = np.fft.fftshift(np.fft.fft(pc_data[:, k] * window))

# ドップラー軸
doppler_axis = np.fft.fftshift(np.fft.fftfreq(M, T_PRI))
velocity_axis = doppler_axis * lam / 2

# =============================================================
# Range-Dopplerマップの表示
# =============================================================
rd_power = np.abs(rd_map)**2
rd_dB = 10 * np.log10(rd_power / np.max(rd_power) + 1e-12)

fig, axes = plt.subplots(2, 2, figsize=(16, 12))

# Range-Dopplerマップ
im = axes[0, 0].pcolormesh(
    range_axis / 1e3, velocity_axis,
    rd_dB, cmap='jet', vmin=-40, vmax=0, shading='auto'
)
plt.colorbar(im, ax=axes[0, 0], label='Power (dB)')
for tgt in targets:
    axes[0, 0].plot(tgt['range']/1e3, tgt['velocity'], 'wo',
                    markersize=10, markeredgecolor='k', markeredgewidth=2)
axes[0, 0].set_xlabel('Range (km)')
axes[0, 0].set_ylabel('Velocity (m/s)')
axes[0, 0].set_title('Range-Doppler Map')
axes[0, 0].set_xlim([0, R_max_sim/1e3])

# パルス圧縮結果(1パルス目)
axes[0, 1].plot(range_axis / 1e3,
                20*np.log10(np.abs(pc_data[0])/np.max(np.abs(pc_data[0]))+1e-12),
                'b', linewidth=1)
for tgt in targets:
    axes[0, 1].axvline(x=tgt['range']/1e3, color='r', linestyle='--', alpha=0.5)
axes[0, 1].set_xlabel('Range (km)')
axes[0, 1].set_ylabel('Amplitude (dB)')
axes[0, 1].set_title('Pulse Compression Output (Pulse #0)')
axes[0, 1].set_xlim([0, R_max_sim/1e3])
axes[0, 1].set_ylim([-40, 5])
axes[0, 1].grid(True)

# ドップラースペクトル(目標1のレンジゲート)
tgt1_range_bin = int(np.round(targets[0]['range'] * 2 / c * fs_fast))
doppler_spectrum = 20 * np.log10(
    np.abs(rd_map[:, tgt1_range_bin])
    / np.max(np.abs(rd_map[:, tgt1_range_bin])) + 1e-12
)
axes[1, 0].plot(velocity_axis, doppler_spectrum, 'b', linewidth=2)
axes[1, 0].axvline(x=targets[0]['velocity'], color='r', linestyle='--',
                    label=f"Target 1: {targets[0]['velocity']} m/s")
axes[1, 0].axvline(x=targets[2]['velocity'], color='g', linestyle='--',
                    label=f"Target 3: {targets[2]['velocity']} m/s")
axes[1, 0].set_xlabel('Velocity (m/s)')
axes[1, 0].set_ylabel('Amplitude (dB)')
axes[1, 0].set_title(f"Doppler Spectrum at R={targets[0]['range']/1e3:.0f} km")
axes[1, 0].legend()
axes[1, 0].grid(True)
axes[1, 0].set_ylim([-40, 5])

# MTIの効果
# 簡易2パルスMTIフィルタ
mti_data = pc_data[1:] - pc_data[:-1]
mti_power = np.mean(np.abs(mti_data)**2, axis=0)
no_mti_power = np.mean(np.abs(pc_data)**2, axis=0)

axes[1, 1].plot(range_axis/1e3,
                10*np.log10(no_mti_power/np.max(no_mti_power)+1e-12),
                'b', alpha=0.5, label='Without MTI')
axes[1, 1].plot(range_axis/1e3,
                10*np.log10(mti_power/np.max(no_mti_power)+1e-12),
                'r', linewidth=2, label='With MTI')
for tgt in targets:
    if tgt['velocity'] != 0:
        axes[1, 1].axvline(x=tgt['range']/1e3, color='g', linestyle=':', alpha=0.5)
axes[1, 1].set_xlabel('Range (km)')
axes[1, 1].set_ylabel('Power (dB)')
axes[1, 1].set_title('MTI Effect: Clutter Suppression')
axes[1, 1].set_xlim([0, R_max_sim/1e3])
axes[1, 1].set_ylim([-50, 5])
axes[1, 1].legend()
axes[1, 1].grid(True)

plt.tight_layout()
plt.show()

解説

上記のシミュレーションでは以下の処理を行っています。

  1. LFMパルス生成: 帯域幅 5 MHz、パルス幅 10 μs のチャープ信号を生成(時間-帯域幅積 $BT_p = 50$)
  2. 受信信号生成: 4つの目標(異なる距離・速度)からの反射を模擬。各パルスごとにドップラー位相シフトを付加
  3. パルス圧縮: マッチドフィルタ(送信パルスの時間反転複素共役)との相関で距離分解能を向上
  4. ドップラー処理: 各レンジゲートで $M = 64$ パルスのFFTを実行し、速度情報を抽出
  5. Range-Dopplerマップ: 距離-速度の2次元マップで4目標を表示
  6. MTIフィルタ: 2パルス差分による静止クラッタ(目標4)の抑圧を確認

PRFトレードオフの可視化

import numpy as np
import matplotlib.pyplot as plt

c = 3e8
f0 = 10e9
lam = c / f0

# PRF範囲
PRF_range = np.logspace(2, 5, 500)  # 100 Hz ~ 100 kHz

# 最大無曖昧距離と速度
R_ua = c / (2 * PRF_range)
v_ua = lam * PRF_range / 4

fig, axes = plt.subplots(1, 3, figsize=(16, 5))

# R_ua vs PRF
axes[0].loglog(PRF_range, R_ua / 1e3, 'b', linewidth=2)
axes[0].set_xlabel('PRF (Hz)')
axes[0].set_ylabel('R_ua (km)')
axes[0].set_title('Max Unambiguous Range vs PRF')
axes[0].grid(True, which='both', alpha=0.3)
axes[0].axhspan(0, 10, alpha=0.1, color='red', label='Short range (<10 km)')
axes[0].axhspan(10, 100, alpha=0.1, color='yellow', label='Medium range')
axes[0].axhspan(100, 2000, alpha=0.1, color='green', label='Long range')
axes[0].legend(fontsize=8)

# v_ua vs PRF
axes[1].loglog(PRF_range, v_ua, 'r', linewidth=2)
axes[1].set_xlabel('PRF (Hz)')
axes[1].set_ylabel('v_ua (m/s)')
axes[1].set_title('Max Unambiguous Velocity vs PRF')
axes[1].grid(True, which='both', alpha=0.3)

# R_ua × v_ua = const のプロット
axes[2].loglog(R_ua/1e3, v_ua, 'g', linewidth=2)
product = c * lam / 8
axes[2].set_xlabel('R_ua (km)')
axes[2].set_ylabel('v_ua (m/s)')
axes[2].set_title(f'R_ua × v_ua = cλ/8 = {product:.0f} m²/s (X-band)')
axes[2].grid(True, which='both', alpha=0.3)

# PRFの代表的な値をプロット
for prf_val, label in [(500, 'Low'), (5000, 'Medium'), (50000, 'High')]:
    r = c / (2*prf_val) / 1e3
    v = lam * prf_val / 4
    axes[2].plot(r, v, 'ko', markersize=10)
    axes[2].annotate(f'{label} PRF\n({prf_val} Hz)',
                     xy=(r, v), xytext=(r*1.5, v*1.5),
                     arrowprops=dict(arrowstyle='->', color='black'),
                     fontsize=9)

plt.tight_layout()
plt.show()

MTIフィルタの周波数応答

import numpy as np
import matplotlib.pyplot as plt

PRF = 1000
f0 = 10e9
lam = 3e8 / f0

# 正規化周波数
f_norm = np.linspace(-0.5, 0.5, 1000)
f = f_norm * PRF  # 実際のドップラー周波数
v = f * lam / 2   # 対応する速度

# 1段MTI: H(z) = 1 - z^{-1}
H1 = 2 * np.abs(np.sin(np.pi * f_norm))

# 2段MTI: H(z) = (1 - z^{-1})^2
H2 = 4 * np.sin(np.pi * f_norm)**2

# 3段MTI: H(z) = (1 - z^{-1})^3
H3 = 8 * np.abs(np.sin(np.pi * f_norm))**3

fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# 振幅特性(リニア)
axes[0].plot(v, H1/np.max(H1), 'b', linewidth=2, label='Single canceller')
axes[0].plot(v, H2/np.max(H2), 'r', linewidth=2, label='Double canceller')
axes[0].plot(v, H3/np.max(H3), 'g', linewidth=2, label='Triple canceller')
axes[0].set_xlabel('Velocity (m/s)')
axes[0].set_ylabel('Normalized Amplitude')
axes[0].set_title('MTI Filter Frequency Response')
axes[0].legend()
axes[0].grid(True)

# 振幅特性(dB)
H1_dB = 20 * np.log10(H1/np.max(H1) + 1e-12)
H2_dB = 20 * np.log10(H2/np.max(H2) + 1e-12)
H3_dB = 20 * np.log10(H3/np.max(H3) + 1e-12)

axes[1].plot(v, H1_dB, 'b', linewidth=2, label='Single canceller')
axes[1].plot(v, H2_dB, 'r', linewidth=2, label='Double canceller')
axes[1].plot(v, H3_dB, 'g', linewidth=2, label='Triple canceller')
axes[1].set_xlabel('Velocity (m/s)')
axes[1].set_ylabel('Amplitude (dB)')
axes[1].set_title('MTI Filter Response (dB)')
axes[1].set_ylim([-60, 5])
axes[1].legend()
axes[1].grid(True)

# ブラインドスピードをマーク
v_blind = lam * PRF / 2
for ax in axes:
    for n in range(1, 3):
        ax.axvline(x=n*v_blind/2, color='orange', linestyle=':', alpha=0.5)
        ax.axvline(x=-n*v_blind/2, color='orange', linestyle=':', alpha=0.5)

plt.tight_layout()
plt.show()

print(f'Blind speed (1st): {v_blind:.1f} m/s = {v_blind*3.6:.1f} km/h')

MTIフィルタの段数を増やすほどDC($v = 0$)近傍での抑圧が強くなりますが、ブラインドスピード付近ではどの段数でも検出感度がゼロになります。

まとめ

本記事では、パルスドップラーレーダーの原理について解説しました。

  • ドップラー効果による周波数シフト $f_d = 2v_r/\lambda_0$ は、往復経路での位相変化の時間微分から導出される
  • パルスレーダーの距離分解能は $\Delta R = cT_p/2$ であり、パルス圧縮(LFM)により $\Delta R = c/(2B)$ に改善できる
  • PRFの選択において、最大無曖昧距離 $R_{\text{ua}} = c/(2f_{\text{PRF}})$ と最大無曖昧速度 $v_{\text{ua}} = \lambda_0 f_{\text{PRF}}/4$ にはトレードオフがあり、$R_{\text{ua}} \cdot v_{\text{ua}} = c\lambda_0/8$ は定数
  • MTIフィルタ($1 – z^{-1}$)は静止クラッタを抑圧するが、ブラインドスピードでは検出感度がゼロになる
  • パルスドップラー処理では、レンジゲートごとのFFTにより距離-速度マップ(Range-Dopplerマップ)を生成する
  • 速度分解能は $\Delta v = \lambda_0 f_{\text{PRF}} / (2M)$ であり、パルス数 $M$ を増やすことで改善される

次のステップとして、以下の記事も参考にしてください。