aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/slides/slides.pdfbin0 -> 10248 bytes
-rw-r--r--doc/thesis/Fading.pdfbin0 -> 469520 bytes
-rw-r--r--doc/thesis/Makefile2
-rw-r--r--doc/thesis/chapters/implementation.tex148
-rw-r--r--doc/thesis/chapters/theory.tex17
-rw-r--r--doc/thesis/tex/docstyle.sty3
-rw-r--r--notebooks/FrameSynchronization.ipynb73
-rw-r--r--src/gr-fadingui/grc/fadingui_xor_frame_sync.block.yml37
-rw-r--r--src/gr-fadingui/python/datasource.py3
-rw-r--r--src/gr-fadingui/python/xor_frame_sync.py58
-rw-r--r--tests/fadingui/QAM/qam_nogui.grc25
-rwxr-xr-xtests/fadingui/QAM/qam_nogui.py4
12 files changed, 209 insertions, 161 deletions
diff --git a/doc/slides/slides.pdf b/doc/slides/slides.pdf
new file mode 100644
index 0000000..9e7e104
--- /dev/null
+++ b/doc/slides/slides.pdf
Binary files differ
diff --git a/doc/thesis/Fading.pdf b/doc/thesis/Fading.pdf
new file mode 100644
index 0000000..9896dc4
--- /dev/null
+++ b/doc/thesis/Fading.pdf
Binary files differ
diff --git a/doc/thesis/Makefile b/doc/thesis/Makefile
index c207a17..be2b4bb 100644
--- a/doc/thesis/Makefile
+++ b/doc/thesis/Makefile
@@ -47,7 +47,7 @@ all: $(PDF)
biber $(basename $(MAIN))
$(TEX) $(TEXARGS) $<
# embed fonts
- gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dEmbedAllFonts=true -sOutputFile=$@_font_embedded.pdf -f $@
+ # gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dEmbedAllFonts=true -sOutputFile=$@_font_embedded.pdf -f $@
.PHONY: clean cleanall
clean:
diff --git a/doc/thesis/chapters/implementation.tex b/doc/thesis/chapters/implementation.tex
index 1052f6b..21235ea 100644
--- a/doc/thesis/chapters/implementation.tex
+++ b/doc/thesis/chapters/implementation.tex
@@ -2,11 +2,72 @@
\chapter{Implementation}
-\section{Simulaton}
-%%TO DO: quelle https://wiki.gnuradio.org/
+\section{Overview}
+% TODO: quelle https://wiki.gnuradio.org/
For the simulation task and after for the Hardware part, the open-source Software GNU Radio has been chosen. This software uses toolboxes for signal processing systems too simulate or/and implement a software-defined radio, based on Python and some C++ implementations for some rapid-application-development environments. The toolboxes can simply, with the help of the graphical user interface, used by drag-and-drop. The Boxes are used to write applications, to receive or to transmit date for a digital system. Some blocks like different filters, channel codes or demodulator elements and a lot more are already implemented. For missing application new elements can be added by coding own blocks. With the help of the GNU Radio software those toolboxes can easily get connected to each other, creating data streams.
+\section{Sender chain}
+\subsection{Data source}
+
+%% TODO: replace with file file
+In this simulation a random source has been chosen.
+
+\subsection{Modulation}
+
+The constellation modulator block is used for a root-raised-cosine-filtered basis modulation. The block gives an input of a byte stream as complex modulated signal in the baseband back.
+Further more it's possible to chose the modulation type here, in this example it is 16QAM, but QPSK, 8PSK and BPSK would also be possible.
+
+\section{Receiver chain}
+
+\subsection{Envelope detector}
+
+\paragraph{Polyphase Clock Sync}
+%% To Do : nochmals anschauen ob dieese erklärung verständlich ist und richtig interpretiert wurde.
+With the the polyphase clock sync the symbols can be synchronized by preforming a time synchronization with the help of multiple filterbanks. For that the derivation of the filtered signal should be minimized whish turns to a better SNR. This works with the help of two filterbanks, one of them contains the filters of the signal adapted to the pulse shaping with several phases. The other contains its derivative. So in the time domain it has a sinc shape, for the output Signal the sinc peak should be on a sample, with the fact that sinc(0) = 1 and sinc(0)' = 0 an error signal can be generated which tells how far away from the peak it is. This error Signal should be zero this is possible with the help of a loop second order whish constants the number of the filterbank and the rate. This rate is generated because of the clock difference between the transmitter and reviver to synchronies the receiver the filter goes through the phases. For the output one sample per symbol is enough.
+
+\paragraph{Equalizer}
+
+\paragraph{Costas Loop}
+
+The Costas Loop is used for frequency and phase adjustment it locks the center frequency of the signal additional it converts it back to de baseband. For different modulation types different orders of the loop had to be chosen
+
+\paragraph{Constellation decoder}
+
+From the complex space the constellation points are decode to bits.
+
+\subsection{Frame synchronization}
+
+\section{Channel simulations}
+
+Here its possible to add some AWGN noise in the channel line. Different parameters can be changed like the noise voltage, time or the frequency offset.
+
+\skelpar[5]{
+ Discuss the multitap FIR model we used. How it is possible to set the delay etc. Also mathematics for the interpolation.
+}
+
+To get a basic line for further simulations a 16QAM has been made. The results of this simulation are shown in \figref{fig:simul16QAM} and \figref{fig:simul16QAM_1} as the red Signal. In \tabref{tab:modulation_settings} some importer Parameter settings for a different modulation scheme are mentioned.
+
+A FIR-Filter was added in the Channel to create a time delay between tow paths. In \figref{fig:simul16QAM} the result includes a direct path and a delayed one. In the plot of \figref{fig:simul16QAM_1} the transmission line dosn't include a direct path. It's impotent to mention that the delay should be smaller than the symbol rate or a multiple of it.
+
+For the a first simulation with some fading the 16QAM simulation model has been extended with a FIR-Filter in the Chanel. The results of this simulation are shown in \figref{fig:simul16QAM} and \figref{fig:simul16QAM_1} as the blue Signal.
+
+
+\section{Hardware}
+
+As Hardware we chosen the USRP B210 from Ettus Research, with the following specifications shown in \tabref{tab:USRP B210 specifications}. Because this SDR is more than enough for our requires.
+
+For the Hardware setup up some changes are made in the file from the 16QAM simulation to fit with the SDRs. For the first test a coaxial cable was used as transmission line, after the cabel were been replaced with two antennas. The gnu radio block scheme is shown in \figref{fig:simul16QAM_Hardware_Aufbau}. The results for s anntena set uo with a transmission line of 20cm are plotted in \figref{fig:simul16QAM__Hardware}.
+
+Instead of the channel modeling block the USRP blocks are used. The sink as transmitter and the source as resiver. The Signal is sended on a center frequency of 2.4GHz.
+
+\subsection{Empirical BER}
+\subsection{Measurements}
+
+%
+%
+%
+
\begin{figure}
\includegraphics[width=\linewidth]{./figures/pdfs/qam_nogui.pdf}
\caption{GNU Radio Blocks}
@@ -25,9 +86,6 @@ For the simulation task and after for the Hardware part, the open-source Softwar
\label{fig:simul16QAM_1}
\end{figure}
-\subsection{16QAM Simulation}
-To get a basic line for further simulations a 16QAM has been made. The results of this simulation are shown in \figref{fig:simul16QAM} and \figref{fig:simul16QAM_1} as the red Signal. In \tabref{tab:modulation_settings} some importer Parameter settings for a different modulation scheme are mentioned.
-
\begin{table}[]
\centering
\caption{Modulation settings for different scheme}
@@ -44,59 +102,6 @@ To get a basic line for further simulations a 16QAM has been made. The results o
\label{tab:modulation_settings}
\end{table}
-
-\subsubsection{Transmitter}
-\paragraph{Source}
-In this simulation a random source has been chosen.
-\paragraph{Modulator}
-The constellation modulator block is used for a root-raised-cosine-filtered basis modulation. The block gives an input of a byte stream as complex modulated signal in the baseband back.
-Further more it's possible to chose the modulation type here, in this example it is 16QAM, but QPSK, 8PSK and BPSK would also be possible.
-
-\subsubsection{Channel}
-\paragraph{Channel Mode}
-Here its possible to add some AWGN noise in the channel line. Different parameters can be changed like the noise voltage, time or the frequency offset.
-
-\subsubsection{Receiver}
-\paragraph{Polyphase Clock Sync}
-%% To Do : nochmals anschauen ob dieese erklärung verständlich ist und richtig interpretiert wurde.
-With the the polyphase clock sync the symbols can be synchronized by preforming a time synchronization with the help of multiple filterbanks. For that the derivation of the filtered signal should be minimized whish turns to a better SNR. This works with the help of two filterbanks, one of them contains the filters of the signal adapted to the pulse shaping with several phases. The other contains its derivative. So in the time domain it has a sinc shape, for the output Signal the sinc peak should be on a sample, with the fact that sinc(0) = 1 and sinc(0)' = 0 an error signal can be generated which tells how far away from the peak it is. This error Signal should be zero this is possible with the help of a loop second order whish constants the number of the filterbank and the rate. This rate is generated because of the clock difference between the transmitter and reviver to synchronies the receiver the filter goes through the phases.
-
-For the output one sample per symbol is enough.
-
-\paragraph{Equalizer}
-?
-
-
-\paragraph{Costas Loop}
-
-The Costas Loop is used for frequency and phase adjustment it locks the center frequency of the signal additional it converts it back to de baseband. For different modulation types different orders of the loop had to be chosen
-
-\paragraph{Decoder}
-
-From the complex space the constellation points are decode to bits.
-
-
-
-\subsection{Simulation Fading}
-For the a first simulation with some fading the 16QAM simulation model has been extended with a FIR-Filter in the Chanel. The results of this simulation are shown in \figref{fig:simul16QAM} and \figref{fig:simul16QAM_1} as the blue Signal.
-
-\subsubsection{Channel}
-\subsubsection{FIR-Filter}
-A FIR-Filter was added in the Channel to create a time delay between tow paths. In \figref{fig:simul16QAM} the result includes a direct path and a delayed one. In the plot of \figref{fig:simul16QAM_1} the transmission line dosn't include a direct path. It's impotent to mention that the delay should be smaller than the symbol rate or a multiple of it.
-
-\newpage
-\section{Hardware}
-
-As Hardware we chosen the USRP B210 from Ettus Research, with the following specifications shown in \tabref{tab:USRP B210 specifications}. Because this SDR is more than enough for our requires.
-
-
-\subsection{16QAM Hardware setup}
-For the Hardware setup up some changes are made in the file from the 16QAM simulation to fit with the SDRs. For the first test a coaxial cable was used as transmission line, after the cabel were been replaced with two antennas. The gnu radio block scheme is shown in \figref{fig:simul16QAM_Hardware_Aufbau}. The results for s anntena set uo with a transmission line of 20cm are plotted in \figref{fig:simul16QAM__Hardware}.
-
-\subsubsection{Channel}
-\paragraph{UHD: USRP Sink and Source}
-Instead of the channel modeling block the USRP blocks are used. The sink as transmitter and the source as resiver. The Signal is sended on a center frequency of 2.4GHz.
-
\begin{figure}
\includegraphics[width=\linewidth]{./figures/pdfs/qam_Hardware_1711.pdf}
\caption{GNU Radio Blocks Hardware}
@@ -109,34 +114,21 @@ Instead of the channel modeling block the USRP blocks are used. The sink as tran
\label{fig:simul16QAM__Hardware}
\end{figure}
-
-
% To Do: Picture of the setup
-
\begin{table}[]
%To DO sepzifikationen ampssen / genauer? https://www.ettus.com/wp-content/uploads/2019/01/b200-b210_spec_sheet.pdf
%https://kb.ettus.com/B200/B210/B200mini/B205mini#FAQ
\centering
\caption{USRP B210 specifications}
- \begin{tabular}{ccc}
- \midrule
- Dimensions & 9.7 x 15.5 x 1.5 cm \\
- Ports &
- 2 TX , 2 RX, Half or Full Duplex\\
- RF frequencies & from 70MHz to 6GHz\\
- Bandwidth & 200kHz-56MHz\\
- External reference input & 10 MHz \\
+ \begin{tabular}{ll}
+ \toprule
+ Dimensions & \(9.7 \times 15.5 \times 1.5\) cm \\
+ Ports & 2 TX, 2 RX, Half or Full Duplex \\
+ RF frequencies & from 70MHz to 6GHz \\
+ Bandwidth & 200kHz -- 56MHz \\
+ External reference input & 10 MHz \\
\bottomrule
\end{tabular}
\label{tab:USRP B210 specifications}
\end{table}
-
-
-
-
-\section{Measurements}
-
-
-
-\section{Results}
diff --git a/doc/thesis/chapters/theory.tex b/doc/thesis/chapters/theory.tex
index 9d61b60..92dea24 100644
--- a/doc/thesis/chapters/theory.tex
+++ b/doc/thesis/chapters/theory.tex
@@ -314,20 +314,3 @@ Because as mentioned earlier it is difficult to estimate the time-dependent para
\sim \mathcal{N} \left( \frac{A_l}{\sqrt{2}}, \frac{1}{2} \sigma_l^2 \right)
\end{equation}
\skelpar[4]
-
-\section{Receiver DSP chain}
-
-\skelpar[3]{Overview of the DSP chain.}
-
-\begin{figure}
- \centering
- \skelfig[width = .8\linewidth]
- \caption{
- Signal processing chain of the receiver.
- \label{fig:rx-dsp-chain}
- }
-\end{figure}
-
-\paragraph{Synchronization} \skelpar[4]{Polyphase filter bank.}
-\paragraph{Equalization} \skelpar[4]{CMA Equalizer.}
-\paragraph{Fine tuning} \skelpar[4]{Costas Loop.}
diff --git a/doc/thesis/tex/docstyle.sty b/doc/thesis/tex/docstyle.sty
index b9a0b8d..def2495 100644
--- a/doc/thesis/tex/docstyle.sty
+++ b/doc/thesis/tex/docstyle.sty
@@ -45,6 +45,7 @@
\RequirePackage{roboto}
% Bera for monospaced font
\setmonofont[Path=misc/,
+ Scale=0.85,
BoldFont = VeraMoBd,
ItalicFont = VeraMoIt,
BoldItalicFont = VeraMoBI,
@@ -116,7 +117,7 @@
language = TeX,
showstringspaces = false,
% font
- basicstyle = \ttfamily\small,
+ basicstyle = \ttfamily,
identifierstyle = \color{black},
keywordstyle = \bfseries \color{blue!70!black},
commentstyle = \color{gray},
diff --git a/notebooks/FrameSynchronization.ipynb b/notebooks/FrameSynchronization.ipynb
index a659a7b..e0187b2 100644
--- a/notebooks/FrameSynchronization.ipynb
+++ b/notebooks/FrameSynchronization.ipynb
@@ -3,7 +3,7 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "5d5acf43",
+ "id": "57a55a8c",
"metadata": {},
"outputs": [],
"source": [
@@ -16,7 +16,7 @@
},
{
"cell_type": "markdown",
- "id": "4b294887",
+ "id": "dc4b9dfd",
"metadata": {},
"source": [
"# Digital Frame Synchronization\n"
@@ -24,22 +24,21 @@
},
{
"cell_type": "code",
- "execution_count": 2,
- "id": "5bec50ff",
- "metadata": {},
+ "execution_count": 27,
+ "id": "025c6919",
+ "metadata": {
+ "scrolled": false
+ },
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Header sequence (N=16): [1 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1]\n",
- "Cross correlation: [ 3. 4. 5. 5. 6. 4. 4. 6. 4. 4. 5. 8. 7. 10. 8. 7. 8. 10.\n",
- " 7. 8. 8. 9. 14. 11. 9. 10. 12. 9. 13. 9. 9. 11. 13. 9. 12. 11.\n",
- " 9. 9. 13. 10. 9. 11. 9. 14. 12. 9. 10. 11. 8. 11. 6. 9. 8. 11.\n",
- " 6. 7. 9. 8. 7. 4. 4. 9. 9. 7. 6. 7. 9. 11. 8. 7. 10. 12.\n",
- " 10. 11. 11. 10. 9. 10. 9. 7. 10. 7. 9. 6. 7. 6. 5. 4. 5. 2.\n",
- " 4. 3.]\n",
- "Correlation peak value: 14.0\n"
+ "Header (N=16): [1 0 1 1 1 1 1 0 1 1 1 0 1 1 1 1]\n",
+ "Stream (N=80): [0 0 1 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 1 1 0 1 1 1\n",
+ " 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 1 0 1 0 1 1 0 0 1 1 1 0 0 1 1 1 1 1 0\n",
+ " 0 1 1 1 0 1]\n",
+ "Correlation peak value: 16 at i=47\n"
]
},
{
@@ -48,13 +47,13 @@
"<StemContainer object of 3 artists>"
]
},
- "execution_count": 2,
+ "execution_count": 27,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
- "image/png": "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\n",
+ "image/png": "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\n",
"text/plain": [
"<Figure size 1800x360 with 1 Axes>"
]
@@ -67,42 +66,40 @@
],
"source": [
"# Create test data\n",
- "seq = np.array([(0xbeef >> i) & 0x1 for i in range(4 * 4)])\n",
- "# seq = np.array([1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1])\n",
- "\n",
+ "seq = np.unpackbits(np.array([0xbe, 0xef], dtype=np.dtype(\"uint8\")))\n",
"stream = np.concatenate([\n",
- " np.random.randint(low=0, high=2, size=30), seq, np.random.randint(low=0, high=2, size=30)\n",
+ " np.random.randint(low=0, high=2, size=32), seq, np.random.randint(low=0, high=2, size=32)\n",
"])\n",
"\n",
- "print(f\"Header sequence (N={len(seq)}): {seq}\")\n",
- "# print(f\"Data stream: {stream}\")\n",
+ "print(f\"Header (N={len(seq)}): {seq}\")\n",
+ "print(f\"Stream (N={len(stream)}): {stream}\")\n",
"\n",
- "# compute cross correlation\n",
- "def distance(v):\n",
- " return len(seq) - sum(np.logical_xor(v, seq))\n",
- "\n",
- "fifo = RingBuffer(len(seq))\n",
- "xcorr = RingBuffer(len(stream) + len(seq))\n",
+ "# Create buffers for cross correlation\n",
+ "fifo = RingBuffer(len(seq), dtype=np.dtype(\"uint8\"))\n",
+ "xcorr = RingBuffer(len(stream) + len(seq), dtype=np.dtype(\"uint8\"))\n",
"\n",
"## fill FIFO with zeros\n",
"fifo.extend(np.zeros(fifo.maxlen))\n",
"\n",
- "for i in range(len(stream) + len(seq)):\n",
- " #print(np.array(fifo))\n",
- " xcorr.append(distance(np.array(fifo)))\n",
+ "def correlation(v):\n",
+ " n = len(seq)\n",
+ " d = np.logical_xor(v, seq) # or bitwise_xor, no difference in this case\n",
+ " return n - sum(d)\n",
+ " \n",
+ "for i in range(len(stream) + len(seq) + 1):\n",
+ " xcorr.append(correlation(np.array(fifo)))\n",
" \n",
+ " # append stream data\n",
" # if the stream is finished use zeros\n",
- " fifo.pop()\n",
- " if i < len(stream):\n",
- " fifo.appendleft(stream[i])\n",
- " else:\n",
- " fifo.appendleft(0)\n",
+ " fifo.append(stream[i] if i < len(stream) else 0)\n",
"\n",
- "print(f\"Cross correlation: {np.array(xcorr)}\")\n",
- "print(f\"Correlation peak value: {max(np.array(xcorr))}\")\n",
+ "# unwrap values\n",
+ "xc = np.array(xcorr)\n",
+ "# print(f\"Cross correlation: {xc}\")\n",
+ "print(f\"Correlation peak value: {np.amax(xc)} at i={np.argmax(xc)}\")\n",
"\n",
"plt.figure(figsize = (25, 5))\n",
- "plt.stem(np.array(xcorr))"
+ "plt.stem(xc)"
]
}
],
diff --git a/src/gr-fadingui/grc/fadingui_xor_frame_sync.block.yml b/src/gr-fadingui/grc/fadingui_xor_frame_sync.block.yml
index 92be2a8..35eb132 100644
--- a/src/gr-fadingui/grc/fadingui_xor_frame_sync.block.yml
+++ b/src/gr-fadingui/grc/fadingui_xor_frame_sync.block.yml
@@ -1,10 +1,10 @@
id: fadingui_xor_frame_sync
-label: xor_frame_sync
+label: XOR Correlation Synchronizer
category: '[fadingui]'
templates:
imports: import fadingui
- make: fadingui.xor_frame_sync()
+ make: fadingui.xor_frame_sync(sync_pattern=${pattern}, buffer_size=${buffer_size})
# Make one 'parameters' list entry for every parameter you want settable from the GUI.
# Keys include:
@@ -12,12 +12,17 @@ templates:
# * label (label shown in the GUI)
# * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
parameters:
-- id: ...
- label: ...
- dtype: ...
-- id: ...
- label: ...
- dtype: ...
+- id: pattern
+ label: Bit pattern
+ dtype: raw
+
+- id: pattern_len
+ label: Pattern length
+ dtype: raw
+
+- id: buffer_size
+ label: Delay buffer size
+ dtype: int
# Make one 'inputs' list entry per input and one 'outputs' list entry per output.
# Keys include:
@@ -27,18 +32,14 @@ parameters:
# * vlen (optional - data stream vector length. Default is 1)
# * optional (optional - set to 1 for optional inputs. Default is 0)
inputs:
-- label: ...
- domain: ...
- dtype: ...
- vlen: ...
- optional: ...
+- label: in
+ domain: stream
+ dtype: byte
outputs:
-- label: ...
- domain: ...
- dtype: ...
- vlen: ...
- optional: ...
+- label: out
+ domain: stream
+ dtype: byte
# 'file_format' specifies the version of the GRC yml format used in the file
# and should usually not be changed.
diff --git a/src/gr-fadingui/python/datasource.py b/src/gr-fadingui/python/datasource.py
index 764b4d5..ab2f441 100644
--- a/src/gr-fadingui/python/datasource.py
+++ b/src/gr-fadingui/python/datasource.py
@@ -10,7 +10,8 @@ from gnuradio import gr
class datasource(gr.sync_block):
"""
- Loads data from a file choosen in the graphical user interface.
+ Loads data from a file choosen in the graphical user interface, splits into
+ chunks and puts a preamble in front of it(frame).
"""
HEADER_LEN = 11;
diff --git a/src/gr-fadingui/python/xor_frame_sync.py b/src/gr-fadingui/python/xor_frame_sync.py
index 9d9064f..735a031 100644
--- a/src/gr-fadingui/python/xor_frame_sync.py
+++ b/src/gr-fadingui/python/xor_frame_sync.py
@@ -4,24 +4,76 @@
# Copyright 2021 Naoki Pross.
-import numpy
+import numpy as np
+from numpy_ringbuffer import RingBuffer
+
from gnuradio import gr
+
class xor_frame_sync(gr.sync_block):
"""
docstring for block xor_frame_sync
"""
- def __init__(self, sync_pattern):
+ def __init__(self, sync_pattern, buffer_size):
gr.sync_block.__init__(self,
name="xor_frame_sync",
in_sig=[np.byte],
out_sig=[np.byte])
+ # binary pattern to match
+ self.pattern = np.array(sync_pattern, dtype=np.dtype("uint8"))
+ self.nbits = len(sync_pattern)
+
+ # buffer to delay the data
+ self.delay_fifo = RingBuffer(buffer_size, dtype=np.byte)
+
+ # buffers to store cross correlation data
+ self.xcorr = RingBuffer(buffer_size, dtype=np.dtype("uint8"))
+
+ # synchronization state
+ self.synchronized = False
+ self.delay = 0
+
+ def xcorrelation(self):
+ """
+ Compute the binary correlation between the stream and the stored
+ pattern. Binary correlation between two bit vectors is just size of the
+ vector(s) minus the number of bits that differ.
+ """
+ unpacked = np.unpackbits(self.delay_fifo[0])
+ return self.nbits - sum(np.logical_xor(unpacked, self.pattern))
+
def work(self, input_items, output_items):
+ """
+ Process the inputs, that means:
+
+ - Check that the buffer is synchronized, i.e. there is the sync
+ pattern appears every k bits, where k is the size of the packet.
+
+ - If the buffer is not synchronized, compute a binary cross
+ correlation to find how by much the stream should be delayed.
+ """
inp = input_items[0]
out = output_items[0]
- out[:] = inp
+ # Add data to delay buffer
+ self.delay_fifo.appendleft(inp)
+
+ # TODO: check for synchronization, else compute
+
+ # Compute correlation
+ if not self.synchronized:
+ self.xcorr.append(self.xcorrelation())
+
+ peak = np.argmax(self.xcorr)
+ if self.xcorr[peak] != self.nbits:
+ print(f"Warning! XOR correlation is not perfect (peak value = {self.xcorr[peak]})")
+
+ self.delay = peak
+ self.synchronized = True
+
+ # return data with delay
+ out[:] = self.delay_fifo[self.delay]
return len(output_items[0])
diff --git a/tests/fadingui/QAM/qam_nogui.grc b/tests/fadingui/QAM/qam_nogui.grc
index 2c6abf8..e9028b6 100644
--- a/tests/fadingui/QAM/qam_nogui.grc
+++ b/tests/fadingui/QAM/qam_nogui.grc
@@ -265,7 +265,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
- coordinate: [2336, 424.0]
+ coordinate: [2584, 456.0]
rotation: 0
state: true
- name: blocks_throttle_0
@@ -429,7 +429,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
- coordinate: [2136, 420.0]
+ coordinate: [2144, 452.0]
rotation: 0
state: true
- name: digital_pfb_clock_sync_xxx_0
@@ -489,6 +489,24 @@ blocks:
coordinate: [2096, 180.0]
rotation: 0
state: true
+- name: fadingui_xor_frame_sync_0
+ id: fadingui_xor_frame_sync
+ parameters:
+ affinity: ''
+ alias: ''
+ buffer_size: '128'
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ pattern: '[]'
+ pattern_len: '10'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [2304, 436.0]
+ rotation: 0
+ state: true
connections:
- [blocks_throttle_0, '0', channels_channel_model_0, '0']
@@ -500,9 +518,10 @@ connections:
- [digital_costas_loop_cc_0, '0', digital_constellation_decoder_cb_0, '0']
- [digital_costas_loop_cc_0, '0', fadingui_dearpygui_sink_0, '0']
- [digital_diff_decoder_bb_0, '0', digital_map_bb_0, '0']
-- [digital_map_bb_0, '0', blocks_null_sink_0, '0']
+- [digital_map_bb_0, '0', fadingui_xor_frame_sync_0, '0']
- [digital_pfb_clock_sync_xxx_0, '0', digital_cma_equalizer_cc_0, '0']
- [fadingui_datasource_0, '0', blocks_vector_to_stream_0, '0']
+- [fadingui_xor_frame_sync_0, '0', blocks_null_sink_0, '0']
metadata:
file_format: 1
diff --git a/tests/fadingui/QAM/qam_nogui.py b/tests/fadingui/QAM/qam_nogui.py
index e9aa15b..56fc49b 100755
--- a/tests/fadingui/QAM/qam_nogui.py
+++ b/tests/fadingui/QAM/qam_nogui.py
@@ -52,6 +52,7 @@ class qam_nogui(gr.top_block):
##################################################
# Blocks
##################################################
+ self.fadingui_xor_frame_sync_0 = fadingui.xor_frame_sync(sync_pattern=[], buffer_size=128)
self.fadingui_dearpygui_sink_0 = fadingui.dearpygui_sink(sock_addr='udp://localhost:31415', ui_element_id=0)
self.fadingui_datasource_0 = fadingui.datasource(vec_len=2037, header_len=11, sock_addr='udp://', file_list=["./lena512color.tiff"])
self.digital_pfb_clock_sync_xxx_0 = digital.pfb_clock_sync_ccf(sps, timing_loop_bw, rrc_taps, nfilts, nfilts/2, 1.5, 1)
@@ -93,9 +94,10 @@ class qam_nogui(gr.top_block):
self.connect((self.digital_costas_loop_cc_0, 0), (self.digital_constellation_decoder_cb_0, 0))
self.connect((self.digital_costas_loop_cc_0, 0), (self.fadingui_dearpygui_sink_0, 0))
self.connect((self.digital_diff_decoder_bb_0, 0), (self.digital_map_bb_0, 0))
- self.connect((self.digital_map_bb_0, 0), (self.blocks_null_sink_0, 0))
+ self.connect((self.digital_map_bb_0, 0), (self.fadingui_xor_frame_sync_0, 0))
self.connect((self.digital_pfb_clock_sync_xxx_0, 0), (self.digital_cma_equalizer_cc_0, 0))
self.connect((self.fadingui_datasource_0, 0), (self.blocks_vector_to_stream_0, 0))
+ self.connect((self.fadingui_xor_frame_sync_0, 0), (self.blocks_null_sink_0, 0))
def get_sps(self):