帯域通過サンプリング(アンダーサンプリング)の理論と実装

FMラジオの放送波は約100 MHz、Wi-Fiは2.4 GHz、衛星測位のGPS L1信号は1.575 GHzで飛んできます。これらをそのままディジタル化しようとすると、ナイキストの定理どおり読めば「信号の最高周波数の2倍」、つまり数GHzのサンプリングレートが必要になりそうに思えます。ところが実際のソフトウェア無線(SDR)受信機やディジタルレシーバは、しばしば数十MHz程度の、搬送波周波数よりはるかに低いサンプリングレートで動いています。なぜそんなことが可能なのでしょうか。

答えは「信号が占める周波数の幅(帯域幅)は狭い」という事実にあります。1.575 GHzのGPS信号も、実際に情報が詰まっているのはその周囲±数MHzの狭い帯域だけです。この「中心は高いが幅は狭い」という帯域通過信号の性質を逆手に取り、わざとエイリアシング(折返し)を起こして信号を低い周波数へ畳み込んでサンプリングするのが帯域通過サンプリング(bandpass sampling)、別名アンダーサンプリング(undersampling)です。

この技術は応用範囲がとても広い分野です。たとえば、(1) SDR受信機やソフトウェア無線では高価で消費電力の大きい高速ADCを避けつつ高周波(RF/IF)信号を直接ディジタル化でき、(2) レーダーや計測機器では中間周波数(IF)段のミキサを減らして回路を簡素化できます。本記事では、帯域通過サンプリングが「いつ成功し、いつ失敗するか」を決める許容サンプリング周波数の条件式を、ナイキスト帯のスペクトル折返し図から一行ずつ導出します。そして帯域が反転してしまう条件まで明らかにし、Pythonで実際に折返しの様子をシミュレーションして確かめます。

本記事の内容

  • 帯域通過サンプリングが成立する直感と、なぜ低レートで読めるのか
  • エイリアシングの折返しから許容サンプリング周波数 $\frac{2f_c – B}{n} \le f_s \le \frac{2f_c + B}{n-1}$ を一行ずつ導出
  • 折返し後にスペクトルが反転する(帯域反転)条件
  • 許容 $f_s$ ゾーンを整数 $n$ 別に塗り分けるPython可視化
  • IF信号を意図的にアンダーサンプリングしてベースバンドへ折り返すシミュレーション
  • SDR受信機での利用意義

前提知識

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

帯域通過サンプリングとは

ふつうサンプリングというと、「信号に含まれる最高周波数 $f_{max}$ の2倍より速く標本化しなければエイリアシングで信号が壊れる」というナイキストの定理を思い出します。実際、ベースバンド信号(0 Hzから $f_{max}$ までびっしり詰まった信号)に対してはこの通りです。しかし通信で扱う信号の多くは、0 Hzから連続して詰まっているのではなく、ある中心周波数 $f_c$ のまわりの狭い幅 $B$ だけにエネルギーが集中しています。FM放送なら $f_c \approx 100$ MHz、$B \approx 200$ kHz といった具合で、$f_c$ に比べて $B$ は桁違いに小さいのが典型です。

ここで「エイリアシングは悪者」という思い込みを一度外してみましょう。サンプリングとは数学的には、信号スペクトルを $f_s$ おきに無限にコピーして並べる操作です(後で式で示します)。ベースバンド信号でエイリアシングが問題になるのは、隣り合うコピー同士が重なって混ざるからです。ところが帯域通過信号のスペクトルは幅 $B$ の細い島が2つ(正の周波数側 $+f_c$ 付近と負の周波数側 $-f_c$ 付近)あるだけです。コピーを $f_s$ おきに並べたとき、これらの島同士が重ならないように $f_s$ をうまく選べば、たとえ $f_s$ が $f_c$ よりずっと小さくても、もとの島の形をそっくりそのまま $0 \sim f_s/2$ の低い周波数帯に「移してくる」ことができます。

つまり帯域通過サンプリングとは、狭帯域信号に対して、コピーされたスペクトルの島が重ならないように $f_s$ を選ぶことで、わざと折り返してベースバンド付近へ周波数変換しつつ標本化する技術です。アナログのミキサ(局部発振器との掛け算)で周波数を下げる代わりに、サンプリングそのものが周波数変換器として働くのが面白いところです。次節では、このサンプリングによる「スペクトルのコピー」という現象を数式で正確に押さえます。

サンプリングがスペクトルを周期化する仕組み

許容条件を導く前に、サンプリングが周波数領域で何を起こすかを正確にしておきます。連続時間信号 $x(t)$ を周期 $T_s = 1/f_s$ で標本化する操作は、インパルス列(ディラックの櫛)

$$ \delta_{T_s}(t) = \sum_{n=-\infty}^{\infty} \delta(t – n T_s) $$

を掛けることに相当します。標本化された信号は

$$ x_s(t) = x(t) \, \delta_{T_s}(t) = \sum_{n=-\infty}^{\infty} x(n T_s)\, \delta(t – n T_s) $$

です。時間領域の掛け算は周波数領域では畳み込みになります。インパルス列のフーリエ変換は再びインパルス列で、

$$ \mathcal{F}\{\delta_{T_s}(t)\} = f_s \sum_{k=-\infty}^{\infty} \delta(f – k f_s) $$

となることが知られています。これを使うと、標本化信号のスペクトルは

$$ X_s(f) = X(f) * \left[ f_s \sum_{k=-\infty}^{\infty} \delta(f – k f_s) \right] $$

と書けます。インパルスとの畳み込みは「平行移動」なので、$\delta(f – k f_s)$ との畳み込みは $X(f)$ を $k f_s$ だけずらすことを意味します。よって

$$ X_s(f) = f_s \sum_{k=-\infty}^{\infty} X(f – k f_s) $$

が得られます。この式が本記事の出発点です。すなわちサンプリングすると、もとのスペクトル $X(f)$ が $f_s$ 間隔で無限にコピーされて重ね合わさるのです。$k=0$ がもとのスペクトル、$k = \pm 1, \pm 2, \dots$ がエイリアス(像)です。

ベースバンド信号で $f_s < 2 f_{max}$ にすると隣のコピーが食い込んで重なる、これがいわゆるエイリアシングによる劣化です。しかし帯域通過信号では、$X(f)$ が $\pm f_c$ 付近の幅 $B$ の島でしかないので、コピー同士が重ならないように $f_s$ を選ぶ余地が生まれます。次節では、この「重ならない条件」を幾何学的に書き下し、許容サンプリング周波数の不等式を導きます。

許容サンプリング周波数の導出

設定と記号

正の周波数側に、下端 $f_L$ から上端 $f_H$ までの幅 $B = f_H – f_L$ の帯域を占める実信号を考えます。中心周波数は

$$ f_c = \frac{f_L + f_H}{2}, \qquad B = f_H – f_L $$

です。実信号なのでスペクトルは複素共役対称で、負の周波数側 $-f_H \sim -f_L$ にも鏡像の島があります。サンプリングするとこれら2種類の島がそれぞれ $f_s$ おきにコピーされます。観測できるのは $0 \sim f_s/2$ の第1ナイキスト帯(基本帯)で、ここに正側の島と負側の島の像が重ならずに落ちてくれれば信号は無傷で復元できます。

折返しの幾何 — 何個めのナイキスト帯に島が入るか

周波数軸を幅 $f_s/2$ のナイキスト帯(ゾーン)に区切ります。第 $m$ ゾーンは $\left[ (m-1)\frac{f_s}{2},\ m\frac{f_s}{2} \right]$ です。サンプリングによる折返しは、鏡を $f_s/2$ おきに立てたように、奇数ゾーンの中身はそのまま、偶数ゾーンの中身は反転して、すべて第1ゾーン $[0, f_s/2]$ に重ねて映し出します。したがって信号が無傷で取れる条件は、正側の帯域 $[f_L, f_H]$ が、ただ1つのナイキストゾーンの中にすっぽり収まることです。境界(ゾーンの仕切り線 $m \cdot f_s/2$)を帯域がまたいでしまうと、帯域の一部が折り返されて自分自身の別の部分と第1ゾーンで重なり、復元不能になります。

正側の帯域が第 $m$ ゾーンに完全に収まる条件は、下端が $m$ 番目のゾーンの左壁以上、上端が右壁以下、すなわち

$$ (m-1)\frac{f_s}{2} \le f_L \quad \text{かつ} \quad f_H \le m\frac{f_s}{2} $$

です。ここで整数 $n$ を $n = m$ と置き換えて整理していきます。

不等式を $f_s$ について解く

上の2つの条件をそれぞれ $f_s$ について解きます。まず右側(上端がゾーン右壁以下)の条件 $f_H \le m \frac{f_s}{2}$ の両辺に $\frac{2}{m}$ を掛けると

$$ f_s \ge \frac{2 f_H}{m} $$

次に左側(下端がゾーン左壁以上)の条件 $(m-1)\frac{f_s}{2} \le f_L$ ですが、$m=1$ のとき左辺は0なので常に成り立ち、ナイキスト本来の条件に帰着します。$m \ge 2$ のときは両辺に $\frac{2}{m-1}$ を掛けて

$$ f_s \le \frac{2 f_L}{m-1} $$

を得ます。この2つをまとめると、整数 $m$($=n$ と書きます)に対して許容サンプリング周波数は

$$ \frac{2 f_H}{n} \le f_s \le \frac{2 f_L}{n-1} $$

という区間で与えられます。ここで $f_H = f_c + B/2$、$f_L = f_c – B/2$ を代入すると、$2 f_H = 2 f_c + B$、$2 f_L = 2 f_c – B$ なので、冒頭で予告した形

$$ \boxed{\ \frac{2 f_c – B}{n-1} \ge f_s \ge \frac{2 f_c + B}{n}\ } $$

すなわち

$$ \frac{2 f_c + B}{n} \le f_s \le \frac{2 f_c – B}{n-1} $$

が導かれました。$n$ は折返しの「次数」で、信号が落ちるナイキストゾーンの番号 $m$ に一致します。$n=1$ はオーバーサンプリング(通常のナイキスト)、$n \ge 2$ がアンダーサンプリング領域です。

区間が空でないための条件 — 最小サンプリングレート

この区間が意味を持つ(下端 $\le$ 上端)ためには

$$ \frac{2 f_c + B}{n} \le \frac{2 f_c – B}{n-1} $$

が必要です。両辺に正の量 $n(n-1)$ を掛けて分母を払うと

$$ (n-1)(2 f_c + B) \le n (2 f_c – B) $$

左辺を展開すると $(n-1)(2 f_c + B) = n(2 f_c + B) – (2 f_c + B)$ なので、これを移項して整理します。

$$ n(2 f_c + B) – (2 f_c + B) \le n(2 f_c – B) $$

両辺から $n(2 f_c – B)$ を引くと、左辺の $f_c$ の項が打ち消し合って

$$ n(2 f_c + B) – n(2 f_c – B) – (2 f_c + B) \le 0 $$

$$ n \cdot 2B – (2 f_c + B) \le 0 $$

よって

$$ n \le \frac{2 f_c + B}{2B} = \frac{f_H}{B} $$

が得られます。$n$ は整数なので、許容される最大の折返し次数は

$$ n_{max} = \left\lfloor \frac{f_H}{B} \right\rfloor = \left\lfloor \frac{f_c + B/2}{B} \right\rfloor $$

です。$\lfloor \cdot \rfloor$ は床関数(切り捨て)です。$n = n_{max}$ のとき区間は最も狭くなり(理想的には1点)、対応する $f_s$ が理論上の最小サンプリングレートに近づきます。一方、どんな帯域通過信号でも $f_s = 2 f_H$(上端の2倍)を取れば必ず安全にサンプリングできます($n=1$ が常に有効だから)。重要なのは、理論的下限としては $f_s$ を $2B$ まで下げられる($f_c$ が $B$ の整数倍に都合よく乗っているとき)という事実で、これは搬送波周波数 $f_c$ ではなく帯域幅 $B$ で決まります。これこそ帯域通過サンプリングの威力です。

帯域を無傷で取り込めるかは分かりましたが、「取り込んだスペクトルが正しい向きを向いているか」はまだ分かりません。次節では、折返し後にスペクトルが左右反転してしまう条件を調べます。

帯域反転(スペクトル反転)の条件

なぜ反転が起きるのか

前節で、正側帯域が落ちるナイキストゾーンの番号を $n$(= $m$)としました。折返しの幾何で述べたとおり、奇数ゾーンの中身はそのままの向きで第1ゾーンに写りますが、偶数ゾーンの中身は鏡で反射されたように周波数の高い側と低い側が入れ替わって写ります。これがスペクトル反転(帯域反転)です。直感的には、$f_s/2$ ごとに立てた鏡で信号を何回跳ね返すか、その回数の偶奇で向きが決まる、と考えると分かりやすいでしょう。

具体的には、第 $n$ ゾーンに入った信号がベースバンドに現れる見かけの周波数 $f_{apparent}$ は、もとの周波数 $f$ に対して、$n$ が奇数のときは

$$ f_{apparent} = f – (n-1)\frac{f_s}{2} $$

で、これは $f$ について増加関数なので向きは保たれます。$n$ が偶数のときは

$$ f_{apparent} = n\frac{f_s}{2} – f $$

で、$f$ について減少関数なので、もとの帯域内で高かった周波数ほどベースバンドでは低く現れ、スペクトルが左右反転します。

判定則

まとめると、帯域反転の有無は折返し次数 $n$ の偶奇だけで決まります。

$$ \begin{cases} n\ \text{が奇数} & \Rightarrow\ \text{正立(向きが保たれる)} \\ n\ \text{が偶数} & \Rightarrow\ \text{反転(スペクトルが左右反転)} \end{cases} $$

実用上これは重要です。たとえば直交変調された信号で I/Q の関係や側波帯の上下が反転すると、復調時にそのまま処理すると正しく復号できません。SDRの受信ソフトでは、$n$ が偶数になるように $f_s$ を選んだ場合、ディジタル処理側でスペクトルを反転(複素共役を取る、あるいは1サンプルおきに符号を反転する等)して補正します。逆に言えば、設計時に $n$ が奇数になるよう $f_s$ を選べば、反転補正そのものが不要になります。

反転の有無まで整理できたので、次は具体的な数値で許容 $f_s$ の区間を計算し、その後Pythonでゾーンの塗り分けと実際の折返しを可視化します。

具体例 — IF信号 70 MHz, 帯域 4 MHz

中間周波数 $f_c = 70$ MHz、帯域幅 $B = 4$ MHz の信号を考えます。すると $f_L = 68$ MHz、$f_H = 72$ MHz です。許容される最大の折返し次数は

$$ n_{max} = \left\lfloor \frac{f_H}{B} \right\rfloor = \left\lfloor \frac{72}{4} \right\rfloor = 18 $$

です。つまり最大18番目のゾーンまで折返しを使えます。いくつかの $n$ について許容区間 $\left[ \frac{2 f_c + B}{n},\ \frac{2 f_c – B}{n-1} \right]$ を計算してみましょう。$2 f_c + B = 144$ MHz、$2 f_c – B = 136$ MHz です。

  • $n=1$: $f_s \ge 144$ MHz(通常のナイキスト。上端72 MHzの2倍)
  • $n=2$: $72 \le f_s \le 136$ MHz(偶数なので反転)
  • $n=4$: $36 \le f_s \le 45.33$ MHz(偶数なので反転)
  • $n=9$: $16 \le f_s \le 17$ MHz(奇数なので正立)
  • $n=18$: $f_s = 8$ MHz(区間が1点に縮退。$144/18 = 8$、$136/17 = 8$)

$n=18$ のとき下端と上端がともに 8 MHz に一致し、これが理論的な最小サンプリングレートです。これは帯域幅 $B=4$ MHz の2倍、すなわち $2B = 8$ MHz にちょうど等しく、先に述べた「理論下限は搬送波ではなく帯域幅で決まる」ことが具体例でも確認できます。70 MHzの信号を、わずか 8 MHz でサンプリングできるわけです。ただし $n_{max}$ ぎりぎりでは許容区間が極端に狭く、クロックのわずかなずれで帯域がゾーン境界をまたいで壊れるため、実用では少し余裕を持たせた $f_s$ を選びます。

数値で区間が求まりました。次はこれらの区間を $n$ 別に塗り分けて、許容される $f_s$ の「島」がどう並ぶかをPythonで可視化します。

Pythonでの実装

許容サンプリング周波数ゾーンの可視化

まず、中心周波数 $f_c$ と帯域 $B$ を固定したとき、$f_s$ 軸上で「サンプリング可能な区間」が $n$ ごとにどこに現れるかを塗り分けてみます。許容区間に入る $f_s$ なら復元可能、区間と区間のあいだの $f_s$ では帯域がゾーン境界をまたいで壊れる、というのが視覚的に分かります。

import numpy as np
import matplotlib.pyplot as plt

# IF信号のパラメータ
fc = 70.0      # 中心周波数 [MHz]
B = 4.0        # 帯域幅 [MHz]
fL = fc - B/2  # 下端 [MHz]
fH = fc + B/2  # 上端 [MHz]

# 最大折返し次数 n_max = floor(fH / B)
n_max = int(np.floor(fH / B))
print(f"fL={fL} MHz, fH={fH} MHz, n_max={n_max}")

# 各 n の許容区間 [ (2fc+B)/n , (2fc-B)/(n-1) ] を計算
intervals = []
for n in range(1, n_max + 1):
    lo = (2*fc + B) / n
    if n == 1:
        hi = 1e9  # n=1 は上限なし(便宜上大きな値)
    else:
        hi = (2*fc - B) / (n - 1)
    if lo <= hi:
        intervals.append((n, lo, hi))
        print(f"n={n:2d}: {lo:7.3f} <= fs <= {hi:7.3f} MHz"
              f"  ({'正立' if n % 2 == 1 else '反転'})")

このコードは、各折返し次数 $n$ に対する許容サンプリング周波数の区間を計算して表示します。出力を見ると、$n$ が大きいほど区間が低い $f_s$ 側に移動し、かつ幅が狭くなっていくこと、そして $n=18$ で区間が事実上1点(8 MHz)に縮退することが読み取れます。各行末の「正立/反転」は $n$ の偶奇による帯域反転の有無を示しており、設計時にどの区間を選べば反転補正が不要かが一目で分かります。

次に、この区間を $f_s$ 軸上に帯として塗り、許容ゾーンの並びを図にします。

import numpy as np
import matplotlib.pyplot as plt

fc, B = 70.0, 4.0
fL, fH = fc - B/2, fc + B/2
n_max = int(np.floor(fH / B))

fig, ax = plt.subplots(figsize=(11, 5))
fs_view_max = 80.0  # 表示する fs の上限 [MHz]

for n in range(2, n_max + 1):  # n>=2 のアンダーサンプリング領域
    lo = (2*fc + B) / n
    hi = (2*fc - B) / (n - 1)
    if lo > hi:
        continue
    color = 'tab:green' if n % 2 == 1 else 'tab:orange'
    ax.axhspan(lo, hi, xmin=0.0, xmax=1.0, color=color, alpha=0.5)
    ax.text(0.02, (lo + hi) / 2, f"n={n}", va='center', fontsize=9)

# 参照線: 通常ナイキスト 2*fH と 理論下限 2B
ax.axhline(2*fH, color='red', ls='--', label=f'Nyquist 2·fH = {2*fH} MHz')
ax.axhline(2*B, color='blue', ls=':', label=f'theoretical min 2B = {2*B} MHz')

ax.set_ylim(0, fs_view_max)
ax.set_ylabel('Sampling frequency $f_s$ [MHz]')
ax.set_xticks([])
ax.set_title(f'Allowed bandpass sampling zones (fc={fc} MHz, B={B} MHz)\n'
             'green = no spectral inversion (odd n), orange = inverted (even n)')
ax.legend(loc='upper right')
plt.tight_layout()
plt.savefig('bandpass_sampling_zones.png', dpi=150, bbox_inches='tight')
plt.show()

このグラフから3つの重要な特徴が読み取れます。第一に、許容される $f_s$ は連続的ではなく、緑(正立)とオレンジ(反転)の帯が階段状に飛び飛びに並んでいます。帯と帯のあいだの白い隙間の $f_s$ を選ぶと信号は折返しで壊れます。第二に、$n$ が大きくなるほど帯は下(低い $f_s$)へ移り、青い点線で示した理論下限 $2B = 8$ MHz に向かって幅が狭まりながら収束します。第三に、緑とオレンジが交互に現れることから、わずかに $f_s$ を変えるだけでスペクトルの向き(正立か反転か)が切り替わることが分かり、設計時に向きを意識して $f_s$ を選ぶ必要性が見て取れます。

許容ゾーンの全体像が分かったので、次に実際に信号をアンダーサンプリングして、高い周波数の信号がベースバンドに折り返される様子を確かめます。

IF信号を意図的にアンダーサンプリングしてベースバンドへ折り返す

連続信号を十分速いレートで作っておき(疑似的な連続時間)、それを目標の低い $f_s$ で間引き(デシメーション)することで、実際のADCによるアンダーサンプリングを模擬します。70 MHz付近の帯域信号を $n=9$ の許容区間内の $f_s = 16$ MHz でサンプリングしてみましょう。

import numpy as np
import matplotlib.pyplot as plt

# パラメータ
fc, B = 70.0e6, 4.0e6        # 中心周波数・帯域 [Hz]
fs_target = 16.0e6           # アンダーサンプリング周波数 [Hz] (n=9, 正立)

# 疑似連続時間: 十分高速なオーバーサンプルで波形を作る
fs_hi = 1.6e9                # 擬似的な「連続」レート [Hz]
T = 200e-6                   # 観測時間 [s]
t = np.arange(0, T, 1/fs_hi)

# 帯域信号: fc を中心に等間隔の3トーン(帯域 B 内に収める)
tones = [fc - 1.5e6, fc, fc + 1.5e6]
x = sum(np.cos(2*np.pi*f*t + 0.3*k) for k, f in enumerate(tones))

# アンダーサンプリング: fs_hi を fs_target まで間引く
decim = int(round(fs_hi / fs_target))
x_us = x[::decim]            # 間引いた標本
fs_eff = fs_hi / decim       # 実効サンプリング周波数
print(f"decim={decim}, 実効 fs={fs_eff/1e6:.3f} MHz")

このコードでは、70 MHzを中心とする帯域 4 MHz 内に収まる3本のトーン(68.5, 70, 71.5 MHz)を擬似連続信号として生成し、それを 16 MHz 相当まで間引いてアンダーサンプリングを模擬しています。間引き率 decim が100、実効サンプリング周波数が 16 MHz になっていることが出力から確認でき、これは $n=9$ の許容区間 $16 \le f_s \le 17$ MHz の下端にあたります。次に、間引いた標本のスペクトルを計算して、トーンがどこに現れるかを見ます。

import numpy as np
import matplotlib.pyplot as plt

# 折返し後の見かけ周波数を理論計算する関数
def apparent_freq(f, fs):
    """周波数 f がサンプリング fs で第1ナイキスト帯[0, fs/2]の
    どこに折り返されるかを返す"""
    fr = f % fs                 # まず [0, fs) に折り込む
    if fr > fs/2:               # ナイキストを超えたら鏡で折り返す
        fr = fs - fr
    return fr

for f in tones:
    fa = apparent_freq(f, fs_target)
    print(f"{f/1e6:6.2f} MHz -> ベースバンド {fa/1e6:6.3f} MHz")

# 標本のスペクトル(片側振幅)
N = len(x_us)
X = np.abs(np.fft.rfft(x_us)) / N * 2
freq = np.fft.rfftfreq(N, 1/fs_eff)

plt.figure(figsize=(10, 4))
plt.plot(freq/1e6, X, lw=1)
plt.xlim(0, fs_eff/2/1e6)
plt.xlabel('Apparent frequency [MHz]')
plt.ylabel('Amplitude')
plt.title(f'Undersampled spectrum (fc=70MHz, fs={fs_target/1e6:.0f}MHz, n=9)')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('undersampled_spectrum.png', dpi=150, bbox_inches='tight')
plt.show()

このグラフから、70 MHzという高い中心周波数の信号が、わずか 16 MHz のサンプリングによってベースバンド($0 \sim 8$ MHz)へ折り返されていることが確認できます。理論計算した見かけ周波数(apparent_freqの出力)とFFTのピーク位置が一致しており、許容区間内の $f_s$ なら3本のトーンが互いに重ならずに、もとの 1.5 MHz 間隔を保ったままベースバンドに現れていることが読み取れます。$n=9$ は奇数なので、トーンの並び順(低い周波数のトーンがベースバンドでも低い側)も保たれており、スペクトル反転は起きていません。

折返しが理論どおり起きることを確認できたので、次に $n$ の偶奇でスペクトルの向きがどう変わるかを直接比較します。

帯域反転の有無をスペクトルで確認する

同じ信号を、正立になる $f_s$(奇数 $n$)と反転になる $f_s$(偶数 $n$)の両方でサンプリングし、ベースバンドでのトーンの並びが入れ替わるかを比べます。トーンに非対称な配置(中心からの距離が異なる)を与えると、反転が一目で分かります。

import numpy as np
import matplotlib.pyplot as plt

fc, B = 70.0e6, 4.0e6
fs_hi = 1.6e9
T = 200e-6
t = np.arange(0, T, 1/fs_hi)

# 非対称な3トーン(反転が見やすいよう間隔を変える)
tones = [fc - 1.8e6, fc - 0.5e6, fc + 1.5e6]
x = sum(np.cos(2*np.pi*f*t + 0.2*k) for k, f in enumerate(tones))

def undersample_spectrum(x, fs_hi, fs_target):
    decim = int(round(fs_hi / fs_target))
    x_us = x[::decim]
    fs_eff = fs_hi / decim
    N = len(x_us)
    X = np.abs(np.fft.rfft(x_us)) / N * 2
    freq = np.fft.rfftfreq(N, 1/fs_eff)
    return freq, X, fs_eff

# n=9 (奇数→正立) と n=8 (偶数→反転) の許容点を選ぶ
fs_odd = 16.0e6    # n=9, 正立
fs_even = 18.0e6   # n=8, 反転 (区間 18.0 <= fs <= 18.857)

fig, axes = plt.subplots(1, 2, figsize=(13, 4))
for ax, fs_t, n in [(axes[0], fs_odd, 9), (axes[1], fs_even, 8)]:
    freq, X, fs_eff = undersample_spectrum(x, fs_hi, fs_t)
    ax.plot(freq/1e6, X, lw=1)
    ax.set_xlim(0, fs_eff/2/1e6)
    ax.set_xlabel('Apparent frequency [MHz]')
    ax.set_ylabel('Amplitude')
    kind = '正立 (odd n)' if n % 2 == 1 else '反転 (even n)'
    ax.set_title(f'fs={fs_t/1e6:.0f} MHz, n={n}  {kind}')
    ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('spectral_inversion.png', dpi=150, bbox_inches='tight')
plt.show()

2つのスペクトルを並べて比較すると、帯域反転の効果が明確に読み取れます。左の $n=9$(奇数)では、もとの信号で中心より下にあった2本のトーン($-1.8$ MHz と $-0.5$ MHz の側)がベースバンドでも近接して低い側に、上の1本($+1.5$ MHz)が高い側に現れ、もとの相対配置が保たれています。一方、右の $n=8$(偶数)では、もとの低い側のトーンがベースバンドの高い側に、高い側のトーンが低い側に来て、並び順が左右反転していることが分かります。これは判定則「$n$ が偶数なら反転」を実際のスペクトルで裏付けるもので、偶数ゾーンを使う設計ではディジタル側で反転補正が必要になる理由を視覚的に示しています。

最後に、これらの結果がSDR受信機の設計でどんな意味を持つかを整理します。

SDR受信機での利用意義

ソフトウェア無線(SDR)受信機の素朴な構成では、アンテナで受けたRF信号をアナログのミキサと局部発振器でIF(中間周波数)、さらにベースバンドへと段階的に周波数変換してからADCでディジタル化します。帯域通過サンプリングを使うと、このミキサ段の一部、あるいは全部を「サンプリングそのもの」に肩代わりさせることができます。下のコードで、必要なADCレートが帯域通過サンプリングでどれだけ下がるかを数値で比べてみます。

import numpy as np

fc, B = 70.0e6, 4.0e6
fH = fc + B/2

# 素朴なナイキスト(IFをそのまま2*fHで標本化)
fs_naive = 2 * fH
# 帯域通過サンプリングの理論最小(2B 近傍, n_max)
n_max = int(np.floor(fH / B))
fs_bp_min = (2*fc + B) / n_max

print(f"素朴ナイキスト fs = {fs_naive/1e6:.1f} MHz")
print(f"帯域通過サンプリング最小 fs = {fs_bp_min/1e6:.3f} MHz (n={n_max})")
print(f"レート削減率 = {fs_naive/fs_bp_min:.1f} 倍")

この出力は、素朴なナイキストサンプリングでは 144 MHz 必要だったADCレートが、帯域通過サンプリングでは 8 MHz 前後まで、約18倍も削減できることを示します。ADCの消費電力とコストはサンプリングレートにほぼ比例して増えるため、この削減はSDR受信機の電力・コスト設計に直接効きます。実際の意義をまとめると、(1) 高価で消費電力の大きい高速ADCを使わずに高いIF/RF信号を直接ディジタル化でき、(2) アナログミキサと局部発振器を減らせるため回路が簡素で温度ドリフトや位相雑音の影響を受けにくくなり、(3) 周波数変換がディジタル領域(サンプリング)で確定的に行われるため再現性が高い、という利点があります。

その代償として、(a) アナログ・アンチエイリアシングフィルタは「目的の帯域だけを通し、他のナイキストゾーンの雑音や妨害波を落とす」狭帯域な帯域通過フィルタである必要があり(さもないと他ゾーンの雑音まで折り返して混入する)、(b) サンプリングクロックのジッタの影響が高い $f_c$ ほど深刻になり($f_c$ に比例して位相雑音が増える)、(c) 許容 $f_s$ 区間が狭いためクロック精度に注意が必要、という制約も生まれます。これらを踏まえて $f_s$ と $n$ を選ぶのが帯域通過サンプリング設計の勘どころです。

まとめ

本記事では、帯域通過サンプリング(アンダーサンプリング)について、許容条件の導出から実装まで解説しました。

  • サンプリングの本質: 標本化はスペクトルを $f_s$ おきにコピーして重ねる操作 $X_s(f) = f_s \sum_k X(f – k f_s)$ であり、帯域通過信号ではコピーの島が重ならないように $f_s$ を選べる
  • 許容サンプリング周波数: 正側帯域が第 $n$ ナイキストゾーンに収まる条件から $\frac{2 f_c + B}{n} \le f_s \le \frac{2 f_c – B}{n-1}$ が導かれる
  • 最大折返し次数と最小レート: 区間が空でない条件から $n_{max} = \lfloor f_H / B \rfloor$ が得られ、理論最小レートは搬送波 $f_c$ ではなく帯域幅で決まり $2B$ まで下げられる
  • 帯域反転: 折返し次数 $n$ が奇数なら正立、偶数ならスペクトルが左右反転する。偶数ゾーンを使う場合はディジタル側で反転補正が必要
  • SDRでの意義: 高速ADCとアナログミキサ段を削減でき、本例では 144 MHz → 8 MHz と約18倍のレート削減が可能。代償としてアンチエイリアシングフィルタの狭帯域化とクロックジッタ対策が要る

帯域通過サンプリングは、ナイキストの定理を「最高周波数」ではなく「帯域幅」の言葉で読み替えることで、エイリアシングを敵から味方に変える技術でした。次のステップとして、以下の記事も参考にしてください。