This question is somewhat related to the one asked here: How is multiplexing achieved in spread-spectrum modulations like CSS?

I am looking at the bit error rate (BER) performance of communication systems based on chirp spread spectrum (CSS) like the one implemented by Semtech's LoRa CSS.

Authors of the research paper Range and coexistence analysis of long range unlicensed communication give an analytical expression for computing the $BER$ given the spreading factor ($sf$) and the energy per bit to noise ratio ($E_b/E_0$). The expression is as follows: \begin{equation} BER = Q(\frac{log_{12}(sf)}{\sqrt{2}} \frac{E_b}{N_0} ) \end{equation} where $Q(x)$ is the Q-function. Authors also show some results comparing the BER for a LoRa CSS system with a BPSK system. If useful, I have reproduced the results: BER versus $E_{b}/N_{0}$ My question is related to the conversion from $E_b/N_0$ to $SNR$. This is something I have done many times (a very useful resource to relate these two metrics can be found here), but results from such conversion are a bit confusing to me.

The general expression (in dB) that relates $SNR$ and $E_b/N_0$ is:

\begin{equation} SNR(dB) = {E_b}/{N_0} + 10\cdot log_{10}(R_s) + 10\cdot log_{10}(k) + 10\cdot log_{10}(R) - 10\cdot log_{10}(BW_n) \end{equation}

where $R_s$ is the symbol rate; $k$ is the number of information bits per symbol, $R$ is the code rate and $BW_n$ is the noise bandwidth.

Say for an uncoded 2PSK system (R=1), k is 1, and if considering matched transmit and receive filters, $Rs = BW_n$, then $SNR = E_b/N_0$.

Now, I am confused with LoRa CSS. Let's consider there is no FEC either (R again is 1). To my knowledge, the number of information bits per symbol, $k$, is $k = log_2(2^{sf}) = sf$; and the symbol rate, $R_s$, is $R_s=BW/2^{sf}$, where $BW$ is one of LoRa's bandwidths (e.g., 125kHz, 250kHz or 500kHz).

Could I take the same consideration regarding matched transmit and receive filters? If so, $Rs = BW_n$, and this would yield the following expression (in dB): $SNR = E_{b}/N_{0} + 10\cdot log_{10}(sf)$. The figure below shows the BER versus SNR using such expression, and I am not sure if they make sense:

enter image description here

Okay, say now that I do not consider matched transmit and receive filters. The equation for calculating SNR would be (R = 1):

\begin{equation} SNR(dB) = {E_b}/{N_0} + 10\cdot log_{10}(BW/2^{sf}) + 10\cdot log_{10}(sf) - 10\cdot log_{10}(BW_n) \end{equation}

What would be the value of $BW_n$? Can I consider the same as BW (e.g., 125kHz)? If so, I got the following results:

enter image description here

Is this okay? What am I doing or considering wrong? I would appreciate if anyone could shed some light in what I am doing.


If useful, I generated the previous figures on python, and the code is:

from __future__ import division
from numpy.random import rand, randn
import numpy as np
import matplotlib.pyplot as plt
import scipy.special
import math

qfunc = lambda x: 0.5 * scipy.special.erfc(x/np.sqrt(2))

sf_range = [7,8,9,10,11,12]
EbN0dB_range = range(-10,15)

itr = len(EbN0dB_range)
itr_sf = len(sf_range)

bpsk_ber = [None for _ in range(itr)] # BER
bpsk_SNR_dB = [None for _ in range(itr)]  # Signal to noise ratio in dB
bpsk_M = 2               # Modulation Order
bpsk_k = np.log2(bpsk_M) # No of bits per symbol (BPSK is 1 bit per symbol)
bpsk_Rfec = 1
bpsk_Rs = 1
bpsk_BWn = 1  # Noise bandwidth

## LoRa
lora_ber = [[None for _ in range(itr)] for _ in range(itr_sf)]
lora_SNR_dB = [[None for _ in range(itr)] for _ in range(itr_sf)]  # Signal to noise ratio in dB
lora_Rs = [None for _ in range(itr_sf)]
lora_Rb = [None for _ in range(itr_sf)]
lora_Rfec = 1
lora_bw = 125e3
lora_BWn = 125e3

for s in range (0, itr_sf):
  sf = sf_range[s]
  lora_Rs[s] = ( lora_bw / ( 2 ** sf ) )
  lora_Rb[s] = lora_Rs[s] * sf

for n in range (0, itr):
  EbNOdB = EbN0dB_range[n]

  # For a matched-filter BPSK system, BWn = Rs
  bpsk_SNR_dB[n] = EbNOdB  + 10 * np.log10(bpsk_Rs) + 10 * np.log10(bpsk_k) + 10 * np.log10(bpsk_Rfec) - 10 * np.log10(bpsk_BWn)

  EbN0 = 10.0 ** (EbNOdB / 10.0)
  bpsk_ber[n] = qfunc(np.sqrt(2 * EbN0)) # or ber[n] = Pb(np.sqrt(EbN0))

  # LoRa
  for s in range (0, itr_sf):
    sf = sf_range[s]

    # Convert EbN0 to SNR
    # We consider matched transmit and receive filters (lora_Rs == BWn)
    lora_SNR_dB[s][n] = EbNOdB + 10 * np.log10(sf)
    # lora_SNR_dB[s][n] = EbNOdB + 10 * np.log10(lora_Rs[s]) + 10 * np.log10(sf) + 10 * np.log10(lora_Rfec) - 10 * np.log10(lora_BWn)
    # Calculate BER for LoRa CSS
    lora_ber[s][n] = qfunc(( log12(sf) / np.sqrt(2) ) * EbN0)

plt.plot(EbN0dB_range, bpsk_ber, '-o', label='BPSK')
for i,b in enumerate(css_ber):
  plt.plot(EbN0dB_range, b,'-o', label='LoRa CSS, SF {}'.format(sf_range[i]))
axes = plt.gca()
axes.set_xlim([EbN0dB_range[0], EbN0dB_range[-1]])
axes.set_ylim([1e-4, 1e-0])
plt.xlabel('$E_{b}/N_{0}$ (dB)')
plt.ylabel('Bit error rate, BER')
plt.legend(loc = 'best')

plt.plot(bpsk_SNR_dB, bpsk_ber, '-o', label='BPSK')
for i,b in enumerate(lora_ber):
    plt.plot(lora_SNR_dB[i], lora_ber[i], '-o', label='LoRa (sf {})'.format(sf_range[i]))
axes = plt.gca()
axes.set_ylim([1e-4, 1e-0])
plt.ylabel('Bit error rate')
plt.xlabel('Signal to noise ratio, SNR (dB)')
plt.legend(loc = 'best')
  • $\begingroup$ What do you mean in writing with matched filter $R_s = BW_n$ ? If you use $R_s = BW_n$, there is no spreading at all. $\endgroup$
    – AlexTP
    Commented Jan 8, 2018 at 20:32
  • $\begingroup$ $$BER = Q(\frac{log_{12}(sf)}{\sqrt{2}} \frac{E_b}{N_0} ).$$ Logarithms to base $12$?? $\endgroup$ Commented Jan 9, 2018 at 15:33
  • $\begingroup$ @DilipSarwate so strange but it is a regression formula (approximated to simulated results, I think) according to the paper. $\endgroup$
    – AlexTP
    Commented Jan 9, 2018 at 20:10
  • $\begingroup$ @DilipSarwate yeah, according to the paper the formula was obtained using Matlab's curve fitting toolbox. I had an implementation in Matlab of the LoRa CSS encoder/decoder before coming across the paper. Such implementation uses SNR instead of Eb/N0, so I wanted to validate my results against the paper's ones, hence my question. $\endgroup$
    – sigur_ros
    Commented Jan 10, 2018 at 17:44
  • $\begingroup$ I can not understand why in the plot SNR-BER there is so much distance between the different SF while in the plot EbN0-BER the distance is to small. Specifically, I don't understand the meaning of each plot. Why do I need EbNo and why do I need SNR. In the first plot the BPSK is really close to CSS but in the third plot the BPSK is worse by far than CSS $\endgroup$ Commented Feb 23, 2019 at 18:44

2 Answers 2


Let's start by fixing a symbol rate $R_s$ symbols per second.

To modulate $R_s$ symbols per second without ISI, Nyquist says that we need a bandwidth at least $BW_0=1/R_s$ Hz.

With spread spectrum, we use more bandwidth, say $M$ times, than what Nyquist advised. The used bandwidth is $BW=M\times BW_0=M/R_s$. In LoRa specifications, $BW=125, 250, 500 \textrm{ kHz}$. $M$ is called spreading factor. Note that LoRa specifications use "spreading factor" for $SF=\log_2(M)$, maybe to emphasize that $M$ must be a power of 2, who know.

This $BW$ is also $BW_n$ in your formula. The noise power $P_n = N_0\times BW_n$.

The signal power is watt per second $P= R_s (\textrm{symbol per second}) \times P_{symb} (\textrm{watt per symbol}) = R_s (\textrm{symbol per second}) \times E_b (\textrm{watt per bit}) \times k (\textrm{bits per symbol})$

Finally, in dB,

$$\mathrm{SNR} = 10\log_{10}(P/P_n)= {E_b}/{N_0} + 10\log_{10}(R_s) + 10\log_{10}(k) - 10\log_{10}(BW_n)$$

Your first formula $\mathrm{SNR} = {E_b}/{N_0} + 10\log_{10}(k)$ is equivalent to the case of no spreading. For a given $\mathrm{SNR}$, if you increase $k$, the power of bit per symbol $E_b$ is reduced. This is not spread spectrum (this could be QAM modulation) and you cannot use the approximated formula of LoRa.

Note that you can start the story by fixing a bandwidth $BW$, then say we do not need a high symbol rate $R_s = BW$, let's reduce the symbol rate by a factor $M$, and everybody still live happily ever after.

Another note is that matched filter does not mean $R_s=BW$. Please follow this wikipedia link for more details.


You are using log with base 12, also in the code you mentioned about css for plotting, but you didn't used it anywhere else.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.