Beginner · Lesson 4 of 10

Noise, SNR and capacity

Where the −174 dBm/Hz thermal noise floor comes from, how noise figure and cascaded stages set receiver sensitivity, and what Shannon capacity says about bandwidth and power

Beginner40 minUpdated 15 Nov 2026Free until 31 Jan 2027

You will learn

  • Compute the thermal noise power kTB in dBm for any bandwidth and reproduce it from simulated noise samples
  • Calculate the noise figure of a cascade of receiver stages and explain why the LNA comes first
  • Compute receiver sensitivity from bandwidth, noise figure and required SNR
  • Use the Shannon capacity formula to compare the effect of more power and more bandwidth on achievable data rate

Before this: Decibels and link budgets

Lesson 03 ended its link budget with a receiver sensitivity and took the number on trust. This lesson derives it. Sensitivity comes from three things: the thermal noise that every receiver collects, the extra noise the receiver’s own circuits add, and the signal-to-noise ratio the demodulator needs. The lesson then asks how many bits per second a given SNR and bandwidth can carry, which is Shannon’s question. The answer explains why modern systems chase bandwidth rather than power.

Thermal noise

Any conductor above absolute zero has electrons in random thermal motion, and that motion produces a random voltage across its terminals. John B. Johnson measured these fluctuations in 1928. In a companion paper the same year, Harry Nyquist calculated their size from thermodynamics and statistical mechanics. The result for radio engineers is simple: a resistor connected to a matched load delivers a noise power of

N = k T B

where k is the Boltzmann constant, T the temperature in kelvin and B the bandwidth in hertz. Since the revision of the SI agreed in 2018, k is fixed at exactly 1.380 649 × 10⁻²³ J/K. The noise is white: it has the same power in every hertz, so the power you collect grows in proportion to the bandwidth you accept.

Radio engineering fixes a reference temperature, T0 = 290 K. Recommendation ITU-R P.372-18, the ITU-R reference on radio noise, uses the same 290 K and notes that 10 log10(k T0) is −204 dBW/Hz. In dBm that is −174 dBm/Hz, the most-used number in receiver design. It is the thermal noise density at room temperature.

The first block computes it and then generates complex white Gaussian noise with that power, as an SDR would deliver it at 1 MS/s, to check that the samples carry the power the formula predicts.

import numpy as np

K_B = 1.380649e-23        # Boltzmann constant, J/K (exact since the 2019 SI)
T0 = 290.0                # reference temperature for noise figure, K

n0_dbm_hz = 10 * np.log10(K_B * T0 / 1e-3)
print(f"kT0 = {K_B * T0:.4e} W/Hz = {n0_dbm_hz:.2f} dBm/Hz")

for b_hz in [125e3, 1e6, 20e6, 100e6]:
    print(f"B = {b_hz / 1e6:7.3f} MHz: noise floor {n0_dbm_hz + 10 * np.log10(b_hz):7.1f} dBm")

# The same number from samples: complex Gaussian noise of power kT0 B at fs = B
rng = np.random.default_rng(7)
fs = 1e6                                   # complex sample rate, Hz (noise bandwidth 1 MHz)
p_noise_w = K_B * T0 * fs
w = np.sqrt(p_noise_w / 2) * (rng.standard_normal(1_000_000) + 1j * rng.standard_normal(1_000_000))
print(f"measured over 1e6 complex128 samples: {10 * np.log10(np.mean(np.abs(w)**2) / 1e-3):.2f} dBm")
kT0 = 4.0039e-21 W/Hz = -173.98 dBm/Hz
B =   0.125 MHz: noise floor  -123.0 dBm
B =   1.000 MHz: noise floor  -114.0 dBm
B =  20.000 MHz: noise floor  -101.0 dBm
B = 100.000 MHz: noise floor   -94.0 dBm
measured over 1e6 complex128 samples: -113.98 dBm

kT0 comes to −173.98 dBm/Hz, which rounds to the familiar −174 dBm/Hz. Each tenfold increase in bandwidth adds 10 dB of noise. A 1 MHz receiver sees −114.0 dBm and a 100 MHz receiver sees −94.0 dBm. The simulated noise, split equally between I and Q, measures −113.98 dBm. In simulation code, noise of a chosen power is usually generated this way: half the power in the real part and half in the imaginary part.

Signal-to-noise ratio

The signal-to-noise ratio is the signal power divided by the noise power in the same bandwidth. In dB it is a subtraction: a −90 dBm signal in a 1 MHz bandwidth, against the −114 dBm floor, has an SNR of 24 dB before the receiver adds anything of its own.

SNR is the quantity that sets bit error rate and data rate. Two related measures appear constantly. Es/N0 is the energy per symbol divided by the noise density. Eb/N0 is the energy per bit divided by the noise density. They relate to SNR through the symbol rate and the bandwidth, and lesson 05 uses Eb/N0 to compare modulations fairly.

Noise figure

A real receiver adds noise of its own. Transistors, resistors in bias networks and lossy filters all contribute. The noise figure measures how much. The noise factor F is the SNR at the input divided by the SNR at the output, with the input noise set by a source at 290 K. Noise figure is 10 log10(F), in dB. A perfect receiver has F = 1, a noise figure of 0 dB. A receiver with a 5 dB noise figure behaves as if its input noise were 5 dB above kT0B.

A receiver is a chain of stages: filters, an LNA, a mixer, more amplifiers. Each has a gain G and a noise factor F. The cascade formula gives the noise factor of the whole chain:

F_total = F1 + (F2 − 1)/G1 + (F3 − 1)/(G1 G2) + ...

Each stage’s excess noise is divided by the total gain in front of it. A stage with high gain at the front makes everything after it nearly irrelevant. A lossy stage at the front, a filter or a long cable, is a gain less than 1. Its loss adds directly to the noise figure and multiplies the contribution of every later stage.

The block below applies the formula to an example chain and then to two rearrangements: the filter moved in front of the LNA, and the LNA removed. The stage values are assumptions for teaching, not the figures of any product.

import numpy as np

def db(x: float) -> float:
    return 10 * np.log10(x)

def lin(x_db: float) -> float:
    return 10 ** (x_db / 10)

def cascade_nf_db(stages: list[tuple[str, float, float]]) -> float:
    """Friis formula for noise factor. Each stage is (name, gain dB, noise figure dB)."""
    f_total, g_before = 0.0, 1.0
    for i, (_, g_db, nf_db) in enumerate(stages):
        f = lin(nf_db)
        f_total += f if i == 0 else (f - 1) / g_before
        g_before *= lin(g_db)
    return db(f_total)

# Example receive chain (assumed values, not a particular product)
lna_first = [("LNA", 20.0, 1.5), ("filter", -2.0, 2.0), ("mixer", -7.0, 7.0), ("IF amp", 20.0, 4.0)]
filter_first = [("filter", -2.0, 2.0), ("LNA", 20.0, 1.5), ("mixer", -7.0, 7.0), ("IF amp", 20.0, 4.0)]
no_lna = [("filter", -2.0, 2.0), ("mixer", -7.0, 7.0), ("IF amp", 20.0, 4.0)]

for name, chain in [("LNA first", lna_first), ("filter first", filter_first), ("no LNA", no_lna)]:
    print(f"{name:>12}: system noise figure {cascade_nf_db(chain):5.2f} dB")

# Sensitivity: noise floor + noise figure + the SNR the demodulator needs
b_hz, snr_req_db = 1e6, 10.0
for name, chain in [("LNA first", lna_first), ("no LNA", no_lna)]:
    sens = -173.98 + db(b_hz) + cascade_nf_db(chain) + snr_req_db
    print(f"{name:>12}: sensitivity {sens:6.1f} dBm in {b_hz / 1e6:.0f} MHz for {snr_req_db:.0f} dB SNR")
   LNA first: system noise figure  2.05 dB
filter first: system noise figure  3.84 dB
      no LNA: system noise figure 13.00 dB
   LNA first: sensitivity -101.9 dBm in 1 MHz for 10 dB SNR
      no LNA: sensitivity  -91.0 dBm in 1 MHz for 10 dB SNR

With a 20 dB LNA at the front, the whole chain has a noise figure of 2.05 dB, only 0.55 dB worse than the LNA alone. Put the 2 dB filter in front and the system noise figure rises to 3.84 dB, almost the full 2 dB of filter loss added on. Remove the LNA and the 13.00 dB noise figure is set by the filter and mixer losses ahead of the IF amplifier. That is an 11 dB penalty, which in a free-space link is a factor of 3.5 in range. This is why the LNA sits as close to the antenna as the design allows, and why tower-mounted amplifiers exist for long feeder cables.

The filter-first case is still common, for a good reason. A strong signal outside the wanted band can overload the LNA, so a band filter often comes first despite its cost in noise figure. The RF front ends hub covers that trade.

Receiver sensitivity

Receiver sensitivity is the weakest signal the receiver can demodulate at its target error rate. It is the noise floor plus the noise figure plus the SNR the demodulator needs:

sensitivity (dBm) = −174 + 10 log10(B) + NF + SNR_required

The previous block evaluated it. In a 1 MHz bandwidth, with 10 dB of required SNR, the chain with the LNA reaches −101.9 dBm and the chain without it reaches −91.0 dBm. Every term trades against the others. Halve the bandwidth and the sensitivity improves by 3 dB. Pick a modulation that needs 6 dB less SNR and it improves by 6 dB. This is how low-power wide-area systems reach very low sensitivities: very narrow bandwidths and modulation that works at low SNR, at the price of a low data rate. The LPWAN and IoT hub has examples.

Shannon capacity

How much data can a channel carry at all? Claude Shannon answered this for a channel of bandwidth B with white Gaussian noise. Theorem 2 of his 1949 paper states that, with sufficiently elaborate coding, binary digits can be sent with as small an error rate as you like at any rate up to

C = B log2(1 + S/N)    bit/s

and that no scheme can do better. C is the Shannon capacity. Dividing by B gives the best possible spectral efficiency in bit/s/Hz at a given SNR. The formula says nothing about how to build such a code. Practical codes that come close to it, such as LDPC codes, are the subject of the channel coding hub.

import numpy as np

# Shannon capacity per hertz of bandwidth against SNR
snr_db = np.arange(-10, 31, 2)
eff = np.log2(1 + 10 ** (snr_db / 10))          # bit/s/Hz
for s, e in zip(snr_db, eff):
    if s % 10 == 0:
        print(f"SNR {s:3d} dB: {e:5.2f} bit/s/Hz")
print("chart:", ", ".join(f"[{s}, {e:.3f}]" for s, e in zip(snr_db, eff)))
high = snr_db >= 0
approx = np.log2(10 ** (snr_db[high] / 10))         # high-SNR approximation log2(SNR)
print("approx:", ", ".join(f"[{s}, {a:.3f}]" for s, a in zip(snr_db[high], approx)))

# Fixed received power, growing bandwidth: capacity saturates
p_rx_dbm, nf_db = -90.0, 5.0
n0_w_hz = 10 ** ((-173.98 + nf_db) / 10) * 1e-3   # noise density including the noise figure
p_rx_w = 10 ** (p_rx_dbm / 10) * 1e-3
for b_hz in [1e5, 1e6, 1e7, 1e8, 1e9]:
    snr = p_rx_w / (n0_w_hz * b_hz)
    c = b_hz * np.log2(1 + snr)
    print(f"B = {b_hz / 1e6:7.1f} MHz: SNR {10 * np.log10(snr):6.1f} dB, C = {c / 1e6:6.2f} Mbit/s")
print(f"wideband limit P/(N0 ln 2) = {p_rx_w / (n0_w_hz * np.log(2)) / 1e6:.2f} Mbit/s")
print(f"minimum Eb/N0 for reliable transmission: {10 * np.log10(np.log(2)):.2f} dB")
SNR -10 dB:  0.14 bit/s/Hz
SNR   0 dB:  1.00 bit/s/Hz
SNR  10 dB:  3.46 bit/s/Hz
SNR  20 dB:  6.66 bit/s/Hz
SNR  30 dB:  9.97 bit/s/Hz
chart: [-10, 0.138], [-8, 0.212], [-6, 0.323], [-4, 0.483], [-2, 0.706], [0, 1.000], [2, 1.370], [4, 1.812], [6, 2.316], [8, 2.870], [10, 3.459], [12, 4.075], [14, 4.707], [16, 5.351], [18, 6.002], [20, 6.658], [22, 7.317], [24, 7.978], [26, 8.641], [28, 9.304], [30, 9.967]
approx: [0, 0.000], [2, 0.664], [4, 1.329], [6, 1.993], [8, 2.658], [10, 3.322], [12, 3.986], [14, 4.651], [16, 5.315], [18, 5.979], [20, 6.644], [22, 7.308], [24, 7.973], [26, 8.637], [28, 9.301], [30, 9.966]
B =     0.1 MHz: SNR   29.0 dB, C =   0.96 Mbit/s
B =     1.0 MHz: SNR   19.0 dB, C =   6.32 Mbit/s
B =    10.0 MHz: SNR    9.0 dB, C =  31.55 Mbit/s
B =   100.0 MHz: SNR   -1.0 dB, C =  84.05 Mbit/s
B =  1000.0 MHz: SNR  -11.0 dB, C = 109.79 Mbit/s
wideband limit P/(N0 ln 2) = 114.07 Mbit/s
minimum Eb/N0 for reliable transmission: -1.59 dB

At 0 dB SNR the limit is exactly 1 bit/s/Hz. At 10 dB it is 3.46 bit/s/Hz, at 20 dB 6.66 bit/s/Hz and at 30 dB 9.97 bit/s/Hz. Above about 10 dB SNR the curve is almost a straight line: each extra 3 dB of SNR buys one more bit/s/Hz. Doubling the power adds a fixed increment of capacity. Doubling the bandwidth, at high SNR, nearly doubles the capacity. That asymmetry is why system designers reach for wider channels, and for MIMO, which adds parallel spatial channels, before they reach for more power.

0246810-100102030capacitylog2(SNR)spectral efficiency (bit/s/Hz)SNR (dB)
Shannon capacity per hertz, log2(1 + SNR), against SNR in dB, from the code above, with the high-SNR approximation log2(SNR). Above about 10 dB the two curves merge and each extra 3 dB of SNR adds one bit/s/Hz.

More bandwidth with the same power

The second half of the block holds the received power fixed at −90 dBm, with a 5 dB noise figure, and widens the bandwidth. More bandwidth also lets in more noise, so the SNR falls as the bandwidth grows. At 1 MHz the SNR is 19.0 dB and the capacity 6.32 Mbit/s. At 10 MHz the SNR drops to 9.0 dB but the capacity rises to 31.55 Mbit/s. At 1000 MHz the SNR is −11.0 dB and the capacity 109.79 Mbit/s, close to the limit of 114.07 Mbit/s that no amount of bandwidth can exceed.

That limit is P / (N0 ln 2), where P is the received power and N0 the noise density. Rewriting it per bit gives a hard floor on energy: Eb/N0 must exceed ln 2, which is −1.59 dB. No modulation and code, however clever, works reliably below it. Links designed to work at negative SNR, with heavy coding or spreading, operate in this power-limited corner of the curve, where bandwidth is cheap and energy is not.

Practical systems sit some decibels below the Shannon curve. Modulation and coding schemes are finite, receivers have implementation losses, and real channels fade. The gap between a real link and the curve is a useful figure of merit, and the capacity and spectral efficiency cheat sheet is the place to look up the related formulas.

Exercise

  1. A satellite receiver has a system noise temperature of 150 K instead of 290 K. What is its noise density in dBm/Hz? Rewrite the first block to take T as a parameter.
  2. In the cascade block, add a 3 dB cable between the antenna and the LNA. What happens to the system noise figure? Move the cable to after the LNA and compare.
  3. A receiver has a 7 dB noise figure and a 200 kHz bandwidth, and its demodulator needs an SNR of 4 dB. What is its sensitivity?
  4. Using the Shannon formula, find the minimum SNR needed for 5 bit/s/Hz. Compare it with the SNR at which the high-SNR approximation is within 0.1 bit/s/Hz of the exact value.
  5. For the fixed −90 dBm example, find the bandwidth at which the capacity reaches 90 % of the wideband limit.

Summary

  • Thermal noise power is kTB. At the 290 K reference it is −174 dBm/Hz, so a 1 MHz receiver starts with a −114 dBm floor and every tenfold increase in bandwidth adds 10 dB.
  • Noise figure is the ratio of input SNR to output SNR in dB. In a cascade, each stage’s excess noise is divided by the gain in front of it, so a low-noise, high-gain first stage sets the system noise figure.
  • Sensitivity is −174 dBm/Hz + 10 log10(B) + NF + required SNR. Narrower bandwidth, lower noise figure and modulation that tolerates a lower SNR all improve it.
  • Shannon capacity is C = B log2(1 + SNR). At high SNR each 3 dB buys one bit/s/Hz. With fixed power, capacity saturates at P / (N0 ln 2) as bandwidth grows, and reliable transmission needs Eb/N0 above −1.59 dB.

Further reading