Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plot improvements #383

Merged
merged 8 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Hostetter"
given-names: "Matt"
title: "sdr: A software-defined radio package for Python"
date-released: 2023-07-09
url: "https://github.com/mhostetter/sdr"
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ View all available classes and functions in the [API Reference](https://mhostett
infinite impulse response (IIR) filters, polyphase interpolators, polyphase decimators, polyphase resamplers,
polyphase channelizers, Farrow arbitrary resamplers, fractional delay FIR filters, FIR moving averagers,
FIR differentiators, IIR integrators, IIR leaky integrators, complex mixing, real/complex conversion.
- **Sequences**: Binary, Gray, Barker, Hadamard, Walsh, Kasami, Zadoff-Chu, m-sequences, Fibonacci LFSRs, Galois LFSRs,
- **Sequences**: Binary, Gray, Barker, Hadamard, Walsh, Kasami, Zadoff-Chu, $m$-sequences, Fibonacci LFSRs, Galois LFSRs,
LFSR synthesis.
- **Coding**: Block interleavers, additive scramblers.
- **Modulation**: Phase-shift keying (PSK), $\pi/M$ PSK, offset QPSK, continuous-phase modulation (CPM),
Expand Down Expand Up @@ -81,3 +81,27 @@ View all available classes and functions in the [API Reference](https://mhostett

There are detailed examples published at <https://mhostetter.github.io/sdr/latest/examples/pulse-shapes/>.
The Jupyter notebooks behind the examples are available for experimentation in `docs/examples/`.

## Citation

If this library was useful to you in your research, please cite us. Following the
[GitHub citation standards](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-citation-files),
here is the recommended citation.

### BibTeX

```bibtex
@software{Hostetter_SDR_2023,
title = {{sdr: A software-defined radio package for Python}},
author = {Hostetter, Matt},
month = {7},
year = {2023},
url = {https://github.com/mhostetter/sdr},
}
```

### APA

```
Hostetter, M. (2023). sdr: A software-defined radio package for Python [Computer software]. https://github.com/mhostetter/sdr
```
27 changes: 26 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ View all available classes and functions in the `API Reference <https://mhostett
infinite impulse response (IIR) filters, polyphase interpolators, polyphase decimators, polyphase resamplers,
polyphase channelizers, Farrow arbitrary resamplers, fractional delay FIR filters, FIR moving averagers,
FIR differentiators, IIR integrators, IIR leaky integrators, complex mixing, real/complex conversion.
- **Sequences**: Binary, Gray, Barker, Hadamard, Walsh, Kasami, Zadoff-Chu, m-sequences, Fibonacci LFSRs, Galois LFSRs,
- **Sequences**: Binary, Gray, Barker, Hadamard, Walsh, Kasami, Zadoff-Chu, $m$-sequences, Fibonacci LFSRs, Galois LFSRs,
LFSR synthesis.
- **Coding**: Block interleavers, additive scramblers.
- **Modulation**: Phase-shift keying (PSK), $\pi/M$ PSK, offset QPSK, continuous-phase modulation (CPM),
Expand All @@ -84,6 +84,31 @@ View all available classes and functions in the `API Reference <https://mhostett
detection PDFs, impulse response, step response, zeros/poles, magnitude response, phase response,
phase delay, and group delay.

Citation
--------

If this library was useful to you in your research, please cite us. Following the `GitHub citation standards <https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-citation-files>`_, here is the recommended citation.

.. md-tab-set::

.. md-tab-item:: BibTeX

.. code-block:: latex

@software{Hostetter_SDR_2023,
title = {{sdr: A software-defined radio package for Python}},
author = {Hostetter, Matt},
month = {7},
year = {2023},
url = {https://github.com/mhostetter/sdr},
}

.. md-tab-item:: APA

.. code-block:: text

Hostetter, M. (2023). sdr: A software-defined radio package for Python [Computer software]. https://github.com/mhostetter/sdr

.. toctree::
:caption: Examples
:hidden:
Expand Down
2 changes: 1 addition & 1 deletion src/sdr/_sequence/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

from ._correlation import *
from ._lfsr import *
from ._maximal import *
from ._maximum import *
from ._symbol_mapping import *
172 changes: 60 additions & 112 deletions src/sdr/_sequence/_correlation.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from .._data import pack, unpack
from .._helper import export
from ._conversion import code_to_field, code_to_sequence, sequence_to_code
from ._maximal import m_sequence
from ._maximum import m_sequence


@overload
Expand Down Expand Up @@ -62,15 +62,11 @@ def barker_code(length: Any, output: Any = "binary") -> Any:

.. ipython:: python

seq = sdr.barker_code(13, output="bipolar")
corr = np.correlate(seq, seq, mode="full")
lag = np.arange(-seq.size + 1, seq.size)
x = sdr.barker_code(13, output="bipolar")

@savefig sdr_barker_code_1.png
plt.figure(); \
sdr.plot.time_domain(lag, np.abs(corr)); \
plt.xlabel("Lag"); \
plt.title("Autocorrelation of length-13 Barker sequence");
sdr.plot.correlation(x, x, mode="circular");

Group:
sequences-correlation
Expand Down Expand Up @@ -159,52 +155,40 @@ def hadamard_code(length: Any, index: Any, output: Any = "binary") -> Any:

.. ipython:: python

seq1 = sdr.hadamard_code(32, 4, output="bipolar"); \
seq2 = sdr.hadamard_code(32, 10, output="bipolar"); \
seq3 = sdr.hadamard_code(32, 15, output="bipolar");
x1 = sdr.hadamard_code(32, 30, output="bipolar"); \
x2 = sdr.hadamard_code(32, 18, output="bipolar"); \
x3 = sdr.hadamard_code(32, 27, output="bipolar");

@savefig sdr_hadamard_code_1.png
plt.figure(); \
sdr.plot.time_domain(seq1 + 3, label="Index 4"); \
sdr.plot.time_domain(seq2 + 0, label="Index 10"); \
sdr.plot.time_domain(seq3 - 3, label="Index 15")
sdr.plot.time_domain(x1 + 3); \
sdr.plot.time_domain(x2 + 0); \
sdr.plot.time_domain(x3 - 3)

Hadamard sequences have zero cross correlation when time aligned. However, the sidelobes can be quite
large when time misaligned. Because of this, Hadamard sequences for spreading codes are useful only when
precise time information is known.
Hadamard sequence autocorrelation sidelobes are not uniform as a function of sequence index.
In fact, the sidelobes can be quite high.

.. ipython:: python

lag = np.arange(-seq1.size + 1, seq1.size); \
xcorr12 = np.correlate(seq1, seq2, mode="full"); \
xcorr13 = np.correlate(seq1, seq3, mode="full"); \
xcorr23 = np.correlate(seq2, seq3, mode="full");

@savefig sdr_hadamard_code_2.png
plt.figure(); \
sdr.plot.time_domain(lag, np.abs(xcorr12), label="4 and 10"); \
sdr.plot.time_domain(lag, np.abs(xcorr13), label="4 and 15"); \
sdr.plot.time_domain(lag, np.abs(xcorr23), label="10 and 15"); \
plt.xlabel("Lag"); \
plt.title("Cross correlation of length-32 Hadamard sequences");
sdr.plot.correlation(x1, x1, mode="circular"); \
sdr.plot.correlation(x2, x2, mode="circular"); \
sdr.plot.correlation(x3, x3, mode="circular"); \
plt.ylim(0, 32);

Hadamard sequence autocorrelation sidelobes are not uniform as a function of sequence index.
In fact, the sidelobes can be quite high.
Hadamard sequences have zero cross correlation when time aligned. However, the sidelobes can be quite
large when time misaligned. Because of this, Hadamard sequences for spreading codes are useful only when
precise time information is known.

.. ipython:: python

lag = np.arange(-seq1.size + 1, seq1.size); \
acorr1 = np.correlate(seq1, seq1, mode="full"); \
acorr2 = np.correlate(seq2, seq2, mode="full"); \
acorr3 = np.correlate(seq3, seq3, mode="full");

@savefig sdr_hadamard_code_3.png
plt.figure(); \
sdr.plot.time_domain(lag, np.abs(acorr1), label="Index 4"); \
sdr.plot.time_domain(lag, np.abs(acorr2), label="Index 10"); \
sdr.plot.time_domain(lag, np.abs(acorr3), label="Index 15"); \
plt.xlabel("Lag"); \
plt.title("Autocorrelation of length-32 Hadamard sequences");
sdr.plot.correlation(x1, x2, mode="circular"); \
sdr.plot.correlation(x1, x3, mode="circular"); \
sdr.plot.correlation(x2, x3, mode="circular"); \
plt.ylim(0, 32);

Group:
sequences-correlation
Expand Down Expand Up @@ -289,52 +273,40 @@ def walsh_code(length: Any, index: Any, output: Any = "binary") -> Any:

.. ipython:: python

seq1 = sdr.walsh_code(32, 4, output="bipolar"); \
seq2 = sdr.walsh_code(32, 10, output="bipolar"); \
seq3 = sdr.walsh_code(32, 15, output="bipolar");
x1 = sdr.walsh_code(32, 10, output="bipolar"); \
x2 = sdr.walsh_code(32, 14, output="bipolar"); \
x3 = sdr.walsh_code(32, 18, output="bipolar");

@savefig sdr_walsh_code_1.png
plt.figure(); \
sdr.plot.time_domain(seq1 + 3, label="Index 4"); \
sdr.plot.time_domain(seq2 + 0, label="Index 10"); \
sdr.plot.time_domain(seq3 - 3, label="Index 15")
sdr.plot.time_domain(x1 + 3); \
sdr.plot.time_domain(x2 + 0); \
sdr.plot.time_domain(x3 - 3)

Walsh sequences have zero cross correlation when time aligned. However, the sidelobes can be quite
large when time misaligned. Because of this, Walsh sequences for spreading codes are useful only when
precise time information is known.
Walsh sequence autocorrelation sidelobes are not uniform as a function of sequence index.
In fact, the sidelobes can be quite high.

.. ipython:: python

lag = np.arange(-seq1.size + 1, seq1.size); \
xcorr12 = np.correlate(seq1, seq2, mode="full"); \
xcorr13 = np.correlate(seq1, seq3, mode="full"); \
xcorr23 = np.correlate(seq2, seq3, mode="full");

@savefig sdr_walsh_code_2.png
plt.figure(); \
sdr.plot.time_domain(lag, np.abs(xcorr12), label="4 and 10"); \
sdr.plot.time_domain(lag, np.abs(xcorr13), label="4 and 15"); \
sdr.plot.time_domain(lag, np.abs(xcorr23), label="10 and 15"); \
plt.xlabel("Lag"); \
plt.title("Cross correlation of length-32 Walsh sequences");
sdr.plot.correlation(x1, x1, mode="circular"); \
sdr.plot.correlation(x2, x2, mode="circular"); \
sdr.plot.correlation(x3, x3, mode="circular"); \
plt.ylim(0, 32);

Walsh sequence autocorrelation sidelobes are not uniform as a function of sequence index.
In fact, the sidelobes can be quite high.
Walsh sequences have zero cross correlation when time aligned. However, the sidelobes can be quite
large when time misaligned. Because of this, Walsh sequences for spreading codes are useful only when
precise time information is known.

.. ipython:: python

lag = np.arange(-seq1.size + 1, seq1.size); \
acorr1 = np.correlate(seq1, seq1, mode="full"); \
acorr2 = np.correlate(seq2, seq2, mode="full"); \
acorr3 = np.correlate(seq3, seq3, mode="full");

@savefig sdr_walsh_code_3.png
plt.figure(); \
sdr.plot.time_domain(lag, np.abs(acorr1), label="Index 4"); \
sdr.plot.time_domain(lag, np.abs(acorr2), label="Index 10"); \
sdr.plot.time_domain(lag, np.abs(acorr3), label="Index 15"); \
plt.xlabel("Lag"); \
plt.title("Autocorrelation of length-32 Walsh sequences");
sdr.plot.correlation(x1, x2, mode="circular"); \
sdr.plot.correlation(x1, x3, mode="circular"); \
sdr.plot.correlation(x2, x3, mode="circular"); \
plt.ylim(0, 32);

Group:
sequences-correlation
Expand Down Expand Up @@ -433,49 +405,37 @@ def kasami_code(length: Any, index: Any = 0, poly: Any = None, output: Any = "bi

.. ipython:: python

seq1 = sdr.kasami_code(63, 0, output="bipolar"); \
seq2 = sdr.kasami_code(63, 1, output="bipolar"); \
seq3 = sdr.kasami_code(63, 2, output="bipolar");
x1 = sdr.kasami_code(63, 0, output="bipolar"); \
x2 = sdr.kasami_code(63, 1, output="bipolar"); \
x3 = sdr.kasami_code(63, 2, output="bipolar");

@savefig sdr_kasami_code_1.png
plt.figure(); \
sdr.plot.time_domain(seq1 + 3, label="Index 0"); \
sdr.plot.time_domain(seq2 + 0, label="Index 1"); \
sdr.plot.time_domain(seq3 - 3, label="Index 2")
sdr.plot.time_domain(x1 + 3); \
sdr.plot.time_domain(x2 + 0); \
sdr.plot.time_domain(x3 - 3)

Examine the autocorrelation of the Kasami sequences.

.. ipython:: python

lag = np.arange(-seq1.size + 1, seq1.size); \
acorr12 = np.correlate(seq1, seq1, mode="full"); \
acorr13 = np.correlate(seq2, seq2, mode="full"); \
acorr23 = np.correlate(seq3, seq3, mode="full");

@savefig sdr_kasami_code_2.png
plt.figure(); \
sdr.plot.time_domain(lag, np.abs(acorr12), label="0"); \
sdr.plot.time_domain(lag, np.abs(acorr13), label="1"); \
sdr.plot.time_domain(lag, np.abs(acorr23), label="2"); \
plt.xlabel("Lag"); \
plt.title("Autocorrelation of length-63 Kasami sequences");
sdr.plot.correlation(x1, x1, mode="circular"); \
sdr.plot.correlation(x2, x2, mode="circular"); \
sdr.plot.correlation(x3, x3, mode="circular"); \
plt.ylim(0, 63);

Examine the cross correlation of the Kasami sequences.

.. ipython:: python

lag = np.arange(-seq1.size + 1, seq1.size); \
xcorr12 = np.correlate(seq1, seq2, mode="full"); \
xcorr13 = np.correlate(seq1, seq3, mode="full"); \
xcorr23 = np.correlate(seq2, seq3, mode="full");

@savefig sdr_kasami_code_3.png
plt.figure(); \
sdr.plot.time_domain(lag, np.abs(xcorr12), label="0 and 1"); \
sdr.plot.time_domain(lag, np.abs(xcorr13), label="0 and 2"); \
sdr.plot.time_domain(lag, np.abs(xcorr23), label="1 and 2"); \
plt.xlabel("Lag"); \
plt.title("Cross correlation of length-63 Kasami sequences");
sdr.plot.correlation(x1, x2, mode="circular"); \
sdr.plot.correlation(x1, x3, mode="circular"); \
sdr.plot.correlation(x2, x3, mode="circular"); \
plt.ylim(0, 63);

Group:
sequences-correlation
Expand Down Expand Up @@ -609,20 +569,14 @@ def zadoff_chu_sequence(length: int, root: int, shift: int = 0) -> npt.NDArray[n
sdr.plot.constellation(x3, linestyle="-", linewidth=0.5); \
plt.title(f"Root-3 Zadoff-Chu sequence of length {N}");

The *periodic* autocorrelation of a Zadoff-Chu sequence has sidelobes of magnitude 0.
The *periodic* autocorrelation of a Zadoff-Chu sequence has sidelobes with magnitude 0.

.. ipython:: python

# Perform periodic autocorrelation
corr = np.correlate(np.roll(np.tile(x3, 2), -N//2), x3, mode="valid")
lag = np.arange(-N//2 + 1, N//2 + 2)

@savefig sdr_zadoff_chu_2.png
plt.figure(); \
sdr.plot.time_domain(lag, np.abs(corr) / N); \
plt.ylim(0, 1); \
plt.xlabel("Lag"); \
plt.title(f"Periodic autocorrelation of root-3 Zadoff-Chu sequence of length {N}");
sdr.plot.correlation(x3, x3, mode="circular"); \
plt.ylim(0, N);

Create a root-5 Zadoff-Chu sequence $x_5[n]$ with length 139.

Expand All @@ -640,16 +594,10 @@ def zadoff_chu_sequence(length: int, root: int, shift: int = 0) -> npt.NDArray[n

.. ipython:: python

# Perform periodic cross correlation
xcorr = np.correlate(np.roll(np.tile(x3, 2), -N//2), x5, mode="valid")
lag = np.arange(-N//2 + 1, N//2 + 2)

@savefig sdr_zadoff_chu_4.png
plt.figure(); \
sdr.plot.time_domain(lag, np.abs(xcorr) / N); \
plt.ylim(0, 1); \
plt.xlabel("Lag"); \
plt.title(f"Periodic cross correlation of root-3 and root-5 Zadoff-Chu sequences of length {N}");
sdr.plot.correlation(x3, x5, mode="circular"); \
plt.ylim(0, N);

Group:
sequences-correlation
Expand Down
Loading
Loading