The PSS tells the UE where a 5 ms half frame starts and which of the three cell identities NID(2) the cell uses. It cannot tell the UE where the 10 ms radio frame starts, and it cannot tell which of the 168 cell identity groups the cell belongs to. The SSS answers both questions. Together the two signals give the PCI, which is 3 NID(1) + NID(2).
SSS is a specific physical layer signal that is used for radio frame synchronization. It has characterstics as listed below.
- Mapped to 72 active sub carriers(6 resource blocks), centered around the DC subcarrier in slot 0 (Subframe 0) and slot 10 (Subframe 5) in FDD.
- The sequence of SSS in subframe 0 and the one in subframe 5 are different from each other
- Made up of 62 Scrambling Sequence (based on m-sequence)
- The value in odd indexed resource element and the one in even indexed resource elements is generated by different equation
- Used for Downlink Frame Synchronization
- One of the critical factors determining Physical Cell ID
May Not be a big issues for most of the case since it would be working fine for most of the device that you have for test. Otherwise it would have not been given to you for test.
However, If you are a developer working at early stage of LTE chipset, this would be one of the first signal you have to implement.
The exact SSS symbol calculation is done by the following formula as described in 36.211 - 6.11.2. For the specific example of generated SSS, refer to Matlab : Toolbox : LTE : SSS page.
The formulas below are those of 36.211 v19.3.0 clause 6.11.2, and the numbers on this page have been checked against that version.
Followings are the topics to be covered in this page.
Symbol Generation of SSS
How can one 62-value sequence carry 168 cell groups and also mark the frame boundary? The SSS interleaves two length-31 sequences, and it swaps them between subframe 0 and subframe 5. The diagrams below trace every term of the even and the odd elements back to the three m-sequences of the specification.
The diagram below builds the even-indexed elements d(2n). The cell identity group NID(1) gives m' and then the two shifts m0 and m1. Each shift picks a cyclic shift of the sequence s, and the scrambling sequence c0 depends on NID(2) from the PSS.

Even elements d(2n) of the SSS. Subframe 0 uses s0(m0) and subframe 5 uses s1(m1), both multiplied by c0.
The diagram below builds the odd-indexed elements d(2n+1). They take the other shift of s, the scrambling sequence c1, and a third sequence z1, whose shift is m0 mod 8 or m1 mod 8.

Odd elements d(2n+1) of the SSS. Its label reads "Symbol with even number index", but the terms drawn are the odd-indexed elements.
The swap between subframes is what gives the frame timing. In subframe 0 the even elements use m0 and the odd elements use m1. In subframe 5 the two change places. A UE that finds the SSS therefore knows whether it is looking at the first or the second half of the frame. For NID(1) = 0 and NID(2) = 0, only 34 of the 62 values are the same in both subframes.
Each cell identity group maps to its own pair of shifts, with m1 always larger than m0. 36.211 v19.3.0 Table 6.11.2.1-1 lists all 168 pairs. The table below shows a few of them, computed from the formula and checked against the table.
NID(1) | m0 | m1 |
0 | 0 | 1 |
1 | 1 | 2 |
29 | 29 | 30 |
30 | 0 | 2 |
34 | 4 | 6 |
59 | 0 | 3 |
60 | 1 | 4 |
68 | 9 | 12 |
102 | 15 | 19 |
136 | 22 | 27 |
167 | 2 | 9 |
The first 30 groups have a difference of 1 between m0 and m1. The next 29 have a difference of 2, the next 28 a difference of 3, and so on up to 7 for the last 3 groups. All 168 pairs are different, so the pair found in the SSS identifies the group without ambiguity.
Two interleaved length-31 sequences : 62 values, each +1 or -1.m0 and m1 from NID(1) : 168 distinct pairs, Table 6.11.2.1-1.c0 and c1 from NID(2) : the UE must detect the PSS first.Swap between subframes 0 and 5 : gives the 10 ms frame timing.
Resource Element Mapping
Where the SSS sits in the grid tells the UE more than its sequence does. The formula below fixes both the subcarriers and the OFDM symbol, and the symbol position differs between FDD and TDD.

SSS mapping. The 62 values sit on the central subcarriers, and the symbol is the second last of slots 0 and 10 for frame structure type 1.
The 62 values take the subcarriers around the DC subcarrier, with 5 reserved subcarriers on each side. That makes 72 subcarriers, or 6 RB, whatever the channel bandwidth. With frame structure type 1 the SSS is in the second last symbol of slots 0 and 10, directly before the PSS. With frame structure type 2 it is in the last symbol of slots 1 and 11, three symbols before the PSS. The distance between the SSS and the PSS therefore tells the UE whether the cell uses FDD or TDD. The SSS uses the same antenna port as the PSS.
72 subcarriers, 6 RB : 62 values and 10 reserved subcarriers.FDD: symbol before the PSS : TDD: three symbols before it.
Matlab code for SSS Generation
Following is the Matlab code for SSS Generation. This is only for the SSS in subframe 0 and I think you can easily modify this to generate the sequence for subframe 5.
clear all;
NID1 = 0;
NID2 = 0;
q_prime = floor(NID1/30);
q = floor(((NID1+q_prime*(q_prime+1)/2))/30);
m_prime = NID1 + q *(q+1)/2;
m0 = mod(m_prime, 31);
m1 = mod(m0 + floor(m_prime/31)+1,31);
%%%%%%%%%%%%%%%%% generate d_even() sequence %%%%%%%%%%%%%%%%
% Generate the sequence x_s() : x() for calculating s_tilda()
x_s = zeros(1,31);
x_s(1:5) = [0 0 0 0 1];
for i = 0:25
x_s((i+5)+1) = mod(x_s(i+2+1)+x_s(i+1),2);
end;
% Generate the sequence x_c() : x() for calculating c_tilda()
x_c = zeros(1,31);
x_c(1:5) = [0 0 0 0 1];
for i = 0:25
x_c((i+5)+1) = mod(x_c(i+3+1)+x_c(i+1),2);
end;
% Generate the sequence s_tilda()
s_tilda = zeros(1,31);
for i = 0:30
s_tilda(i+1) = 1 - 2*x_s(i+1);
end;
% Generate the sequence c_tilda()
c_tilda = zeros(1,31);
for i = 0:30
c_tilda(i+1) = 1 - 2*x_c(i+1);
end;
% Generate s0_m0_even()
s0_m0_even = zeros(1,31);
for n = 0:30
s0_m0_even(n+1) = s_tilda(mod(n+m0,31)+1);
end;
% Generate c0_even()
c0_even = zeros(1,31);
for n = 0:30
c0_even(n+1) = c_tilda(mod(n+NID2,31)+1);
end;
% Calculate d_even_sub0
d_even_sub0 = s0_m0_even .* c0_even;
%%%%%%%%%%%%%%%%% generate d_odd() sequence %%%%%%%%%%%%%%%%
% Generate the sequence x_s() : x() for calculating s_tilda()
x_z = zeros(1,31);
x_z(1:5) = [0 0 0 0 1];
for i = 0:25
x_z((i+5)+1) = mod(x_z(i+4+1) + x_z(i+2+1) + x_z(i+1+1)+ x_z(i+1),2);
end;
% Generate the sequence z_tilda()
z_tilda = zeros(1,31);
for i = 0:30
z_tilda(i+1) = 1 - 2*x_z(i+1);
end;
% Generate s1_m1_odd()
s1_m1_odd = zeros(1,31);
for n = 0:30
s1_m1_odd(n+1) = s_tilda(mod(n+m1,31)+1);
end;
% Generate c1_odd()
c1_odd = zeros(1,31);
for n = 0:30
c1_odd(n+1) = c_tilda(mod(n+NID2+3,31)+1);
end;
% Generate z1_m0_odd()
z1_m0_odd = zeros(1,31);
for n = 0:30
z1_m0_odd(n+1) = z_tilda(mod(n+mod(m0,8),31)+1);
end;
% Calculate d_odd_sub0
d_odd_sub0 = s1_m1_odd .* c1_odd .* z1_m0_odd;
% Calculate d_sub0
d_sub0 = zeros(1,62);
d_sub0(1:2:end-1) = d_even_sub0;
d_sub0(2:2:end) = d_odd_sub0;
subplot(1,5,1);
plot(real(d_sub0),imag(d_sub0),'ro','MarkerFaceColor',[1 0 1]);axis([-1.5 1.5 -1.5 1.5]);
subplot(1,5,[2 5]);
plot(real(d_sub0),'ro-');xlim([0 length(d_sub0)]);
The plot below shows the 62 values of the SSS in subframe 0 for NID(1) = 0 and NID(2) = 0. The constellation on the left has two points, because every value is +1 or -1.

SSS of subframe 0, NID(1) = 0 and NID(2) = 0. The values alternate between the even and the odd sequence.
Following is numberical output of d_sub0
1 1 1 -1 1 1 1 1 1 -1 1 1 -1 -1 -1 -1
-1 -1 1 -1 1 1 1 1 -1 1 1 1 -1 -1 -1 -1
-1 -1 1 -1 -1 1 -1 1 -1 -1 1 1 -1 1 1 -1
1 1 1 1 -1 1 -1 -1 -1 1 1 1 1 -1
For NID(1) = 0 the code gives m0 = 0 and m1 = 1. The printed d_sub0 matches, value by value, a separate calculation from the 36.211 v19.3.0 formulas. To generate the SSS of subframe 5, swap s0(m0) and s1(m1), and use z1(m1) instead of z1(m0) in the odd elements. The Python code below does both.
m0 = 0 and m1 = 1 : for NID(1) = 0.Output checked against the specification : all 62 values match.
Python Code for SSS generation
Following is the Python code to generate SSS. It generate two sequences with given NID1 and NID2, one for subframe 0 and the other one for subframe 5.
NOTE : The sequence generated by this code is tested to work with SSS detection from I/Q captured from eNB Simulator (Amarisoft Callbox)
def generate_sss(NID1,NID2):
q_prime = np.floor(NID1/30)
q = np.floor(((NID1+q_prime*(q_prime+1)/2))/30)
m_prime = NID1 + q *(q+1)/2
m0 = m_prime % 31
m1 = (m0 + np.floor(m_prime/31) + 1) % 31
# generate d_even() sequence
x_s = np.zeros(31)
x_s[:5] = [0, 0, 0, 0, 1]
for i in range(26):
x_s[i+5] = (x_s[i+2] + x_s[i]) % 2
x_c = np.zeros(31)
x_c[:5] = [0, 0, 0, 0, 1]
for i in range(26):
x_c[i+5] = (x_c[i+3] + x_c[i]) % 2
s_tilda = 1 - 2*x_s
c_tilda = 1 - 2*x_c
s0_m0_even = np.zeros(31)
s1_m1_even = np.zeros(31)
for n in range(31):
s0_m0_even[n] = s_tilda[int((n+m0)%31)]
s1_m1_even[n] = s_tilda[int((n+m1)%31)]
c0_even = np.zeros(31)
for n in range(31):
c0_even[n] = c_tilda[int((n+NID2)%31)]
d_even_sub0 = s0_m0_even * c0_even
d_even_sub5 = s1_m1_even * c0_even
# generate d_odd() sequence
x_z = np.zeros(31)
x_z[:5] = [0, 0, 0, 0, 1]
for i in range(26):
x_z[i+5] = (x_z[i+4] + x_z[i+2] + x_z[i+1] + x_z[i]) % 2
z_tilda = 1 - 2*x_z
s1_m1_odd = np.zeros(31)
s0_m0_odd = np.zeros(31)
for n in range(31):
s1_m1_odd[n] = s_tilda[int((n+m1)%31)]
s0_m0_odd[n] = s_tilda[int((n+m0)%31)]
c1_odd = np.zeros(31)
for n in range(31):
c1_odd[n] = c_tilda[int((n+NID2+3)%31)]
z1_m0_odd = np.zeros(31)
z1_m1_odd = np.zeros(31)
for n in range(31):
z1_m0_odd[n] = z_tilda[int((n+m0%8)%31)]
z1_m1_odd[n] = z_tilda[int((n+m1%8)%31)]
d_odd_sub0 = s1_m1_odd * c1_odd * z1_m0_odd
d_odd_sub5 = s0_m0_odd * c1_odd * z1_m1_odd
# calculate d_sub0
d_sub0 = np.zeros(62)
d_sub0[::2] = d_even_sub0
d_sub0[1::2] = d_odd_sub0
sss0 = d_sub0
d_sub5 = np.zeros(62)
d_sub5[::2] = d_even_sub5
d_sub5[1::2] = d_odd_sub5
sss5 = d_sub5
return sss0, sss5
The function returns both sequences at once, sss0 for subframe 0 and sss5 for subframe 5. It follows the same steps as the Matlab code and adds the swapped terms for subframe 5. The shifts m0 and m1 come out of np.floor as floating point values, so the code converts every index with int() before it reads the sequences.
One call, two sequences : sss0 and sss5 for the same cell.Indices converted with int() : np.floor returns floating point values.
SSS Detection Algorithm
The SSS detection algorithm can be illustrated as below. As shown here, you need to an accurate result of PSS detection and frequency error estimation which is explained in this note.
The diagram below shows the four steps, from extracting the SSS out of the resource grid to the final correlation.

SSS detection. The grid is aligned by the detected PSS, and the SSS is taken from the symbol before it. The text beside step 3 writes the group range as 0, 2, ..., 167; it means all values from 0 to 167.
The PSS has already given NID(2), the 5 ms timing and a frequency error estimate. The receiver compensates that error, generates the SSS of subframe 0 and of subframe 5 for every group, and compares each of them with the received SSS. That is 168 x 2 = 336 candidates. The best match gives NID(1), and it also says whether the received SSS came from subframe 0 or 5. From that the UE knows the start of the radio frame and can compute the PCI.
The PSS and the SSS use the same antenna port, so the receiver can use the PSS as a channel estimate and detect the SSS coherently. The cyclic prefix length and the duplex mode may still be unknown at this point. In that case the receiver tries each possible SSS position relative to the PSS and keeps the one with the strongest correlation.
With two cyclic prefix lengths and two duplex modes, there are four possible SSS positions. The receiver then compares up to 4 x 336 = 1344 candidates, although a real receiver often knows the duplex mode from the band it is searching. Each comparison is a sum of 62 products of +1 or -1 values with the received samples. The whole search is therefore cheap compared with the PSS search, which runs over every time offset.
The result of this step is needed before anything else in the cell can be read. The PBCH sits in symbols 7 to 10 of subframe 0 only, so the UE must know which half frame is subframe 0. The cell-specific reference signals depend on the full PCI, so the UE needs NID(1) before it can estimate the channel for the PBCH.
336 candidates : 168 groups, each for subframe 0 and subframe 5.Up to 1344 with unknown CP and duplex mode : four possible SSS positions.Best match gives the group and the frame start : PCI = 3 NID(1) + NID(2).PSS as channel reference : same antenna port as the SSS.Needed before the PBCH : frame start and full PCI.
Reference
[1] Synchronization and Cell Search
[2] 3GPP TS 36.211 v19.3.0 - clause 6.11.2.1, Sequence generation, and clause 6.11.2.2, Mapping to resource elements