Module 2 — Cell Search

PSS Detection —
Finding timing from nothing

The UE has tuned to GSCN 7845 = 3498.24 MHz and collected 307,200 raw samples. It has no timing, no structure — just complex numbers. This section explains exactly how it finds the Primary Synchronization Signal, step by step, with real numbers.

TS 38.211 §7.4.2.2 TS 38.104 §5.4.3.1 TS 38.304 §5.2.3.1

What is PSS?

The Primary Synchronization Signal (PSS) is the very first signal a UE detects when searching for a cell. It is transmitted as part of every SSB on 127 specific subcarriers in the center of the SSB's frequency band.

PSS serves one purpose: give the UE a known pattern to search for. Because both gNB and UE know exactly what the PSS sequence looks like, the UE can slide this known pattern across its received samples and detect the exact moment it appears. That moment reveals the OFDM symbol timing — the single most important piece of information the UE needs to begin processing the signal.

The UE shall attempt to detect the presence of the SS/PBCH block on carrier frequencies. The UE shall be able to detect the PSS on any carrier frequency within the supported frequency range of the UE.
3GPP TS 38.304, Section 5.2.3.1

There are exactly 3 PSS sequences, one for each value of N²_ID ∈ {0, 1, 2}. The UE does not know which one the cell is using, so it tries all three simultaneously. Whichever produces a strong match identifies the cell's N²_ID — the first component of the Physical Cell ID.

127
subcarriers
Center of SSB, zeros on sides
3
sequences
One per N²_ID ∈ {0,1,2}
Symbol 0
SSB position
Always the first symbol of SSB
±1
BPSK values
Each of 127 values is +1 or −1

PSS sequence generation

The 3GPP specification defines the PSS as a binary m-sequence of length 127, mapped to BPSK symbols (+1 or −1). The sequence is generated by a linear feedback shift register with a specific recurrence relation. All three PSS sequences are the same underlying m-sequence, read starting from different positions.

The sequence d(n) used for the primary synchronisation signal is defined by

    d(n) = 1 − 2x(m),    m = (n + 43·N²ID) mod 127,    n = 0, 1, ..., 126

where the binary sequence x is given by

    x(i+7) = (x(i+4) + x(i)) mod 2

with initial conditions x(0) = x(1) = x(2) = x(3) = x(4) = x(5) = x(6) = 1.
3GPP TS 38.211, Section 7.4.2.2, Equation 7.4.2.2-1

Step-by-step: generating the m-sequence x

The recurrence x(i+7) = (x(i+4) + x(i)) mod 2 means: XOR the bit 4 positions back with the bit 7 positions back to get the next bit.

m-sequence generation — first 10 steps TS 38.211 §7.4.2.2
Initial: x(0..6) = [1, 1, 1, 1, 1, 1, 1]
Step iFormulax(i+4)x(i)XORResult x(i+7)
0x(7) = x(4) ⊕ x(0)111⊕10
1x(8) = x(5) ⊕ x(1)111⊕10
2x(9) = x(6) ⊕ x(2)111⊕10
3x(10) = x(7) ⊕ x(3)010⊕11
4x(11) = x(8) ⊕ x(4)010⊕11
5x(12) = x(9) ⊕ x(5)010⊕11
6x(13) = x(10) ⊕ x(6)111⊕10
7x(14) = x(11) ⊕ x(7)101⊕01
8x(15) = x(12) ⊕ x(8)101⊕01
9x(16) = x(13) ⊕ x(9)000⊕00
x sequence starts: [1,1,1,1,1,1,1,0,0,0,1,1,1,0,1,1,0, ...]
Mapping d(n) = 1−2x(n): 0→+1, 1→−1

Three PSS sequences from one m-sequence

The shift m = (n + 43·N²_ID) mod 127 reads the same x sequence starting from position 0, 43, or 86 for N²_ID = 0, 1, 2 respectively. This creates three different-looking sequences that are easy to distinguish by correlation.

PSS sequences — first 24 values of d(n) for each N²_ID TS 38.211 §7.4.2.2
■ N²_ID = 0 (read x from position 0) ■ N²_ID = 1 (read x from position 43) ← our cell ■ N²_ID = 2 (read x from position 86)

How the UE detects PSS — cross-correlation

The UE has 307,200 raw complex samples and needs to find a known pattern somewhere in them. The technique is cross-correlation: slide the known PSS template across the buffer one sample at a time, computing at each position how well the template and signal match.

When the template aligns exactly with the PSS in the received signal, every multiplication rx[n] × conj(PSS[n]) gives a positive real contribution — they all add coherently to produce a large peak. Everywhere else, the products are random complex numbers that partially cancel, giving a small result near the noise floor.

PSS correlation — formula and numerical exampleTS 38.211 §7.4.2.2
// For each offset t = 0..307,199 and each N²_ID = 0,1,2:
C(t, N²_ID) = | Σ  rx_buffer[t+n] × conj(PSS_time[N²_ID][n]) |
                n=0 to 2047

// At t=0 (noise only — terms cancel):
C(0, 1) = |0.1×1 + (−0.2)×(−1) + 0.3×10.1×1 + ...|
          = |−0.3| = 0.3  ← noise floor

// At t=4523 (PSS present — coherent addition):
C(4523, 1) = |0.98×1 + 1.02×1 + 0.97×1 + 1.01×1 + ...|
            = 68.7  ← PEAK → PSS found!

// N²_ID=0 and N²_ID=2: no peak at any offset → not this cell's PSS
Interactive — Correlation output C(t) across 5 ms buffer Adjust SNR to see peak emerge from noise
10 dB
Peak: 68.7 At: t = 4523 samples Noise floor: ~0.4 Result: ✓ N²_ID = 1 detected

Frequency offset correction

After PSS detection, the UE must correct its frequency. A typical TCXO crystal oscillator has ±10 ppm accuracy. At 3498.24 MHz, this means up to ±35 kHz of frequency error — larger than one 30 kHz subcarrier. Without correction, the FFT produces completely wrong results.

The correction uses the cyclic prefix. Because CP = copy of the last 144 samples of the useful part, we have rx[t] ≈ rx[t + 2048] in the CP region. The phase difference between these two versions equals 2π × Δf × T_useful, from which Δf is directly extracted.

Frequency offset calculation — our exampleTS 38.211 §5.3.1
// CP autocorrelation (sum over 144 CP samples):
R = Σ rx[t] × conj(rx[t + 2048]),  t = 0..143
  = 142.3 × e^(j × 0.000120)

// Extract frequency offset:
Δf_fine = angle(R) / (2π × T_useful)
        = 0.000120 / (2π × 33.33×10⁻⁶)
        = 573 Hz

// Coarse correction from integer subcarrier shift (PSS position in FFT):
Δf_coarse = −120,000 Hz

// Total correction applied sample by sample:
rx_corrected[t] = rx[t] × e^(−j × 2π × 120573 × t / 61,440,000)

Full worked example — from sample buffer to PSS result

1
Tune to GSCN 7845, collect 307,200 samples
RF front end tunes to 3498.24 MHz, downconverts to baseband, samples at 61.44 Msps. Buffer represents 5 ms of received signal. Looks like pure noise.
2
Generate PSS_time[0], PSS_time[1], PSS_time[2]
Compute all three 2048-sample time-domain PSS reference sequences using the m-sequence formula. These are known — same for every UE and every cell.
3
Slide each template — compute C(t) for all offsets
307,200 offsets × 3 sequences. Hardware uses FFT-based convolution for speed. Result: peak at t = 4523 for N²_ID = 1 with value 68.7. No peak for N²_ID = 0 or 2.
4
Confirm SCS hypothesis — 30 kHz vs 15 kHz
UE runs two correlators: 2192-sample window (30 kHz) and 4384-sample window (15 kHz). Only the 2192-sample window gives a peak — SCS = 30 kHz confirmed.
5
Apply frequency correction
Coarse: −120,000 Hz from integer subcarrier shift. Fine: −573 Hz from CP autocorrelation. Total: −120,573 Hz applied to all samples before SSS processing.
PSS complete — UE now knows:
Symbol timing     → OFDM symbol starts at sample t = 4523
N²_ID             → N²_ID = 1
SCS               → 30 kHz (2192-sample hypothesis matched)
Freq correction   → −120,573 Hz applied
SSS location      → sample 4523 + 2×2192 = 8907
Key insight: The PSS peak position t = 4523 anchors all subsequent processing. SSS is found at 4523 + 2×2192 = 8907. PBCH at 4523 + 2192 = 6715 and 4523 + 3×2192 = 11099. One number cascades into everything that follows.