この記事は、Zenn に公開した「dB と dBm」のうち、計算の部分だけをコードにしたものです。
「なぜ dBm どうしを足してはいけないのか」といった考え方は原本のほうが詳しいので、あわせて参照してください。
光伝送の資料には、「送信 0dBm、受信 −14.8dBm」のように、dBm と dB が混ざって出てきます。手計算でも足りますが、dBm どうしの足し算で間違えやすいので、変換の関数をひとつ用意しておくと安全です。
前提: 定義
-
dBm は、1mW を基準にした絶対的な光パワーです。
dBm = 10 × log10(P[mW] / 1mW) - dB は、2つの量の比(損失や利得)です。
- 区間の受信パワーは、送信パワー [dBm] − 損失の合計 [dB] で求めます。損失は dB なので、引き算で足し込めます。
手順1: 変換の関数
import math
def mw_to_dbm(p_mw: float) -> float:
"""光パワー [mW] → [dBm]"""
return 10 * math.log10(p_mw)
def dbm_to_mw(p_dbm: float) -> float:
"""光パワー [dBm] → [mW]"""
return 10 ** (p_dbm / 10)
手順2: dBm どうしの足し算
dBm の数字をそのまま足すと、意味のない値になります。mW に戻して足し、dBm に戻します。
def add_dbm(*levels_dbm: float) -> float:
"""dBm どうしの足し算は、mW に戻して足してから dBm に戻す"""
return mw_to_dbm(sum(dbm_to_mw(x) for x in levels_dbm))
手順3: リンクバジェット
def link_budget(tx_dbm, fiber_km, loss_db_per_km, n_connectors, connector_db,
n_splices, splice_db, margin_db=0.0):
"""送信パワーから、区間の損失を引いた受信パワー [dBm] を返す"""
loss = (fiber_km * loss_db_per_km + n_connectors * connector_db
+ n_splices * splice_db + margin_db)
return tx_dbm - loss, loss
手順4: 動かしてみる
print(f"0 dBm = {dbm_to_mw(0):.3f} mW")
print(f"3 dBm = {dbm_to_mw(3):.3f} mW")
print(f"-20 dBm = {dbm_to_mw(-20):.4f} mW")
print(f"1 mW = {mw_to_dbm(1):.2f} dBm, 2 mW = {mw_to_dbm(2):.2f} dBm")
print(f"0 dBm + 0 dBm = {add_dbm(0, 0):.2f} dBm")
print(f"0 dBm x 40 本 = {add_dbm(*([0]*40)):.2f} dBm")
rx, loss = link_budget(tx_dbm=0, fiber_km=40, loss_db_per_km=0.25,
n_connectors=2, connector_db=0.5,
n_splices=8, splice_db=0.1, margin_db=3)
print(f"損失 {loss:.1f} dB -> 受信 {rx:.1f} dBm")
出力:
0 dBm = 1.000 mW
3 dBm = 1.995 mW
-20 dBm = 0.0100 mW
1 mW = 0.00 dBm, 2 mW = 3.01 dBm
0 dBm + 0 dBm = 3.01 dBm
0 dBm x 40 本 = 16.02 dBm
損失 14.8 dB -> 受信 -14.8 dBm
手順5: 受信パワーが範囲に入っているか判定する
計算した受信パワーは、受信器の感度(これより弱いと受けられない)と過負荷(これより強いと誤る・壊れる)の間に入っている必要があります。
def check_rx(rx_dbm, sensitivity_dbm, overload_dbm):
"""受信パワーが、感度と過負荷の間に入っているかを判定する"""
if rx_dbm < sensitivity_dbm:
return f"NG: 感度不足(あと {sensitivity_dbm - rx_dbm:.1f} dB 足りない)"
if rx_dbm > overload_dbm:
return f"NG: 過負荷({rx_dbm - overload_dbm:.1f} dB 以上の減衰が必要)"
return f"OK: 感度まで {rx_dbm - sensitivity_dbm:.1f} dB の余り"
SENS, OVL = -18, -3 # 説明用の仮定値。実際は受信器のデータシートで確認する
for km, margin in ((40, 3), (60, 3), (2, 0)):
rx, loss = link_budget(tx_dbm=0, fiber_km=km, loss_db_per_km=0.25,
n_connectors=2, connector_db=0.5,
n_splices=8, splice_db=0.1, margin_db=margin)
print(f"{km:>3} km (余裕 {margin} dB): 受信 {rx:6.1f} dBm -> {check_rx(rx, SENS, OVL)}")
出力:
40 km (余裕 3 dB): 受信 -14.8 dBm -> OK: 感度まで 3.2 dB の余り
60 km (余裕 3 dB): 受信 -19.8 dBm -> NG: 感度不足(あと 1.8 dB 足りない)
2 km (余裕 0 dB): 受信 -2.3 dBm -> NG: 過負荷(0.7 dB 以上の減衰が必要)
過負荷の確認では、余裕(margin)を 0 にします。 余裕は「将来の劣化で弱くなる分」を見込むためのもので、過負荷は「いちばん強く届くとき」に起きるからです。短い区間ほど過負荷に注意します。
結果の読み方
- 0dBm どうしを足すと 0dBm ではなく、約 3dBm になります。 同じ強さの光が2本あれば、パワーは2倍(約 +3dB)になるからです。1mW の倍の 2mW は 3.01dBm です。
- 0dBm の光が 40 本で約 16dBm(40mW)です。 波長多重で本数が増えると、全体のパワーが大きくなるので、増幅器の出力上限が効いてきます。
- 例のリンクバジェットは、40km × 0.25dB/km = 10dB、コネクタ 2 個で 1dB、融着 8 箇所で 0.8dB、余裕 3dB を足して、合計 14.8dB です。送信 0dBm から引くと、受信は −14.8dBm になります。
注意
- 例の数値(0.25dB/km、コネクタ 0.5dB、融着 0.1dB、余裕 3dB、感度 −18dBm、過負荷 −3dBm)は説明用の仮定値です。実際の設計では、使うファイバ・部品のデータシートと、規格の上限値を確認します。
- ここで扱うのは、1 区間の光パワーの収支だけです。OSNR や分散など、距離を制限する他の要因は別の話です。
参考
- ITU-T G.652「Characteristics of a single-mode optical fibre and cable」— https://www.itu.int/rec/T-REC-G.652(損失の上限などの規格値の出典)
前回の記事: DWDM のグリッド(ITU-T G.694.1)の周波数と波長を Python で換算する
dB と dBm の考え方は Zenn の記事にまとめています: dB と dBm



