From 5f55d0a890e1c6a09e15450919420c8eb868caa1 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 13:45:28 +0200 Subject: [PATCH 01/11] Add linting yml-files. --- .github/workflows/lint_nb.yml | 46 +++++++++++++++++++++++++++ .github/workflows/run_nb.yml | 60 +++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 .github/workflows/lint_nb.yml create mode 100644 .github/workflows/run_nb.yml diff --git a/.github/workflows/lint_nb.yml b/.github/workflows/lint_nb.yml new file mode 100644 index 0000000..a10fe83 --- /dev/null +++ b/.github/workflows/lint_nb.yml @@ -0,0 +1,46 @@ +name: Linting Notebooks with Black + +on: + push: + branches: + - ci # Change this to your repository's main branch + pull_request: + branches: + - ci +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.12" # Change this to your desired Python version + + - name: Install dependencies + run: | + pip install black==23.1.0 nbformat black[jupyter] + continue-on-error: false + + - name: Find notebooks + id: find-notebooks + run: | + find . -name "*.ipynb" > notebooks.txt + continue-on-error: true + + - name: Lint notebooks + run: | + cat notebooks.txt | xargs -I {} black --line-length 88 --check {} + continue-on-error: true + + - name: Check lint results + run: | + if grep -q "would reformat" notebooks.txt; then + echo "Linting issues found. Run 'black' to auto-format the notebooks." + exit 1 + else + echo "All notebooks are properly formatted." + fi \ No newline at end of file diff --git a/.github/workflows/run_nb.yml b/.github/workflows/run_nb.yml new file mode 100644 index 0000000..e63bdfc --- /dev/null +++ b/.github/workflows/run_nb.yml @@ -0,0 +1,60 @@ +name: Run Notebooks + +on: + push: + branches: + - ci # Change this to your repository's main branch + pull_request: + branches: + - ci + schedule: + - cron: "0 0 * * 0" # Run every Sunday at midnight UTC (4 week interval) + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + # Testing matrix by printing the current Python version: + - name: Display Python version + run: python -c "import sys; print(sys.version)" + + - name: Install dependencies + run: pip install jupyter nbconvert + - run: pip install -r requirements.txt + + - name: Find notebooks + id: find-notebooks + run: | + find . -name "*.ipynb" > notebooks.txt + cat notebooks.txt + shell: bash + + - name: Execute notebooks + run: | + cat notebooks.txt | while read -r notebook; do + jupyter nbconvert --to notebook --execute --inplace "$notebook" + done + continue-on-error: false + shell: bash + + - name: Check for errors + run: | + if grep "raise Exception(" *.ipynb; then + echo "Error found in notebook(s)." + exit 1 + else + echo "No errors found in notebooks." + fi + shell: bash \ No newline at end of file From 9c946e1081e383f232f97c685a3ded3a877d4714 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 13:46:25 +0200 Subject: [PATCH 02/11] Linted files with black==23.1.0 --- conf.py | 173 ++--- filter_design/audiofilter.ipynb | 121 ++-- filter_design/audiofilter.py | 596 ++++++++++-------- filter_design/bilinear_transform.ipynb | 84 +-- filter_design/comparison_non_recursive.ipynb | 63 +- filter_design/frequency_sampling_method.ipynb | 96 +-- filter_design/window_method.ipynb | 67 +- ipython_kernel_config.py | 5 +- nonrecursive_filters/fast_convolution.ipynb | 74 ++- .../quantization_effects.ipynb | 82 +-- .../segmented_convolution.ipynb | 95 ++- quantization/introduction.ipynb | 16 +- .../linear_uniform_characteristic.ipynb | 28 +- .../linear_uniform_quantization_error.ipynb | 84 +-- quantization/noise_shaping.ipynb | 28 +- ...nonlinear_quantization_speech_signal.ipynb | 84 +-- quantization/oversampling.ipynb | 28 +- .../requantization_speech_signal.ipynb | 42 +- random_signals/correlation_functions.ipynb | 122 ++-- random_signals/distributions.ipynb | 23 +- random_signals/ensemble_averages.ipynb | 72 +-- random_signals/important_distributions.ipynb | 57 +- random_signals/independent.ipynb | 59 +- random_signals/introduction.ipynb | 10 +- random_signals/power_spectral_densities.ipynb | 30 +- random_signals/stationary_ergodic.ipynb | 98 +-- random_signals/superposition.ipynb | 95 +-- random_signals/white_noise.ipynb | 37 +- ...coustic_impulse_response_measurement.ipynb | 20 +- .../correlation_functions.ipynb | 44 +- random_signals_LTI_systems/introduction.ipynb | 28 +- random_signals_LTI_systems/linear_mean.ipynb | 18 +- .../linear_prediction.ipynb | 72 +-- .../power_spectral_densities.ipynb | 44 +- .../wiener_filter.ipynb | 100 +-- recursive_filters/cascaded_structures.ipynb | 52 +- recursive_filters/direct_forms.ipynb | 10 +- recursive_filters/introduction.ipynb | 43 +- .../quantization_of_coefficients.ipynb | 115 ++-- .../quantization_of_variables.ipynb | 78 +-- .../leakage_effect.ipynb | 73 ++- .../stft.ipynb | 30 +- .../summary.ipynb | 19 +- .../window_functions.ipynb | 50 +- .../zero_padding.ipynb | 112 ++-- .../introduction.ipynb | 10 +- .../parametric_methods.ipynb | 39 +- .../periodogram.ipynb | 15 +- .../welch_method.ipynb | 21 +- 49 files changed, 1770 insertions(+), 1592 deletions(-) diff --git a/conf.py b/conf.py index 06b4cc5..0a9a038 100644 --- a/conf.py +++ b/conf.py @@ -21,88 +21,95 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'nbsphinx', - 'sphinx.ext.mathjax', + "nbsphinx", + "sphinx.ext.mathjax", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: -source_suffix = ['.rst'] +source_suffix = [".rst"] # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'Digital Signal Processing' -copyright = 'Sascha Spors, University of Rostock' -author = 'Sascha Spors' +project = "Digital Signal Processing" +copyright = "Sascha Spors, University of Rostock" +author = "Sascha Spors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.9' +version = "0.9" # The full version, including alpha/beta/rc tags. -release = 'Wintersemester 2022/23' +release = "Wintersemester 2022/23" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = 'en' +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', '**.ipynb_checkpoints', 'demos', 'data', 'export', '**/_*.ipynb'] +exclude_patterns = [ + "_build", + "**.ipynb_checkpoints", + "demos", + "data", + "export", + "**/_*.ipynb", +] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -112,18 +119,18 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'bootstrap' +html_theme = "bootstrap" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { - 'navbar_title': 'DSP', - 'navbar_site_name': 'Chapters', - 'navbar_pagenav_name': 'This Page', - 'navbar_fixed_top': 'false', - 'source_link_position': 'none', - 'bootswatch_theme': 'cosmo', + "navbar_title": "DSP", + "navbar_site_name": "Chapters", + "navbar_pagenav_name": "This Page", + "navbar_fixed_top": "false", + "source_link_position": "none", + "bootswatch_theme": "cosmo", #'bootswatch_theme': 'lumen', #'bootswatch_theme': 'sandstone', #'bootswatch_theme': 'spacelab', @@ -134,138 +141,135 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' -#html_search_language = 'en' +# html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} +# html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' +# html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'Digital_Signal_Processing_doc' +htmlhelp_basename = "Digital_Signal_Processing_doc" # -- Options for LaTeX output --------------------------------------------- nbsphinx_execute_arguments = ['--InlineBackend.figure_formats={"svg", "pdf"}'] -nbsphinx_execute = 'always' +nbsphinx_execute = "always" nbsphinx_timeout = 300 nbsphinx_allow_errors = False latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). - 'papersize': 'a4paper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. - 'preamble': r""" + # The paper size ('letterpaper' or 'a4paper'). + "papersize": "a4paper", + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + "preamble": r""" \usepackage{amssymb} \setcounter{tocdepth}{2} """, - -# Latex figure (float) alignment -#'figure_align': 'htbp', + # Latex figure (float) alignment + #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'Digital_Signal_Processing.tex', project, author, 'manual'), + (master_doc, "Digital_Signal_Processing.tex", project, author, "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- @@ -273,12 +277,11 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'digitalsignalprocessing', 'Digital Signal Processing', - [author], 1) + (master_doc, "digitalsignalprocessing", "Digital Signal Processing", [author], 1) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -287,19 +290,25 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'Digital_Signal_Processing', 'Digital Signal Processing', - author, 'Digital_Signal_Processing', 'Lecture notes featuring computational examples', - 'Miscellaneous'), + ( + master_doc, + "Digital_Signal_Processing", + "Digital Signal Processing", + author, + "Digital_Signal_Processing", + "Lecture notes featuring computational examples", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/filter_design/audiofilter.ipynb b/filter_design/audiofilter.ipynb index 38e935f..8ff23a6 100644 --- a/filter_design/audiofilter.ipynb +++ b/filter_design/audiofilter.ipynb @@ -41,6 +41,7 @@ "from scipy import signal\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", + "\n", "%load_ext autoreload\n", "%autoreload 2\n", "%matplotlib inline" @@ -75,7 +76,7 @@ "metadata": {}, "outputs": [], "source": [ - "np.set_printoptions(floatmode='unique')" + "np.set_printoptions(floatmode=\"unique\")" ] }, { @@ -339,7 +340,7 @@ "metadata": {}, "outputs": [], "source": [ - "wc = 2*np.pi*fc" + "wc = 2 * np.pi * fc" ] }, { @@ -355,7 +356,7 @@ "metadata": {}, "outputs": [], "source": [ - "B = np.array([0., 0, 1])\n", + "B = np.array([0.0, 0, 1])\n", "A = np.array([0, 1 / wc, 1])" ] }, @@ -3935,8 +3936,8 @@ ], "source": [ "wcpre = audiofilter.f_prewarping(fc, fs)\n", - "print(\"wc=\", wc/2/np.pi)\n", - "print(\"wcpre=\", wcpre/2/np.pi)" + "print(\"wc=\", wc / 2 / np.pi)\n", + "print(\"wcpre=\", wcpre / 2 / np.pi)" ] }, { @@ -3969,7 +3970,7 @@ "metadata": {}, "outputs": [], "source": [ - "b, a = audiofilter.bilinear_biquad(Bpre, Apre, fs) " + "b, a = audiofilter.bilinear_biquad(Bpre, Apre, fs)" ] }, { @@ -25313,10 +25314,10 @@ "BW = 2 # bandwidth in octaves\n", "fm = 1000 # Hz\n", "\n", - "fcl = 2**(-BW/2) * fm # lower cut (-3 dB) frequency in Hz\n", - "fch = 2**(+BW/2) * fm # higher cut (-3 dB) frequency in Hz\n", + "fcl = 2 ** (-BW / 2) * fm # lower cut (-3 dB) frequency in Hz\n", + "fch = 2 ** (+BW / 2) * fm # higher cut (-3 dB) frequency in Hz\n", "QBP = audiofilter.q_from_bw(BW)\n", - "np.testing.assert_allclose(QBP, fm / (fch-fcl))\n", + "np.testing.assert_allclose(QBP, fm / (fch - fcl))\n", "B, A, b, a = audiofilter.biquad_bp2nd(fm, QBP, fs)\n", "\n", "bode_plot(B, A, b, a)\n", @@ -28985,7 +28986,7 @@ ], "source": [ "fm = 1000 # Hz\n", - "QBS = 2/3\n", + "QBS = 2 / 3\n", "B, A, b, a = audiofilter.biquad_bs2nd(fm, QBS, fs)\n", "bode_plot(B, A, b, a)\n", "print(\"fcut_low =\", fcl, \"Hz\")\n", @@ -55575,15 +55576,15 @@ "B1, A1, b, a = audiofilter.biquad_hshv2nd(fc, G, fs, \"I\") # fc at 15 dB\n", "B2, A2, b, a = audiofilter.biquad_hshv2nd(fc, G, fs, \"II\") # fc at 3 dB\n", "B3, A3, b, a = audiofilter.biquad_hshv2nd(fc, G, fs, \"III\") # fc at 9 dB\n", - "f = np.arange(1, fs/2, 1)\n", - "s = 2*np.pi*f\n", + "f = np.arange(1, fs / 2, 1)\n", + "s = 2 * np.pi * f\n", "s, H1 = signal.freqs(B1, A1, s)\n", "s, H2 = signal.freqs(B2, A2, s)\n", "s, H3 = signal.freqs(B3, A3, s)\n", "\n", "x = np.column_stack((f, f, f))\n", "y = np.column_stack((H1, H2, H3))\n", - "title = 'High-Shelving Filter 2nd order'\n", + "title = \"High-Shelving Filter 2nd order\"\n", "legend = [\"type I\", \"type II\", \"type III\"]\n", "magnitude_plot(x, y, title, legend)" ] @@ -57427,8 +57428,8 @@ "B1, A1, b, a = audiofilter.biquad_peq2nd(fm, G, QBP, fs, \"I\") # fc at 15 dB\n", "B2, A2, b, a = audiofilter.biquad_peq2nd(fm, G, QBP, fs, \"II\") # fc at 15 dB\n", "B3, A3, b, a = audiofilter.biquad_peq2nd(fm, G, QBP, fs, \"III\") # fc at 9 dB\n", - "f = np.arange(1, fs/2, 1)\n", - "s = 2*np.pi*f\n", + "f = np.arange(1, fs / 2, 1)\n", + "s = 2 * np.pi * f\n", "s, H1 = signal.freqs(B1, A1, s)\n", "s, H2 = signal.freqs(B2, A2, s)\n", "s, H3 = signal.freqs(B3, A3, s)\n", @@ -57436,7 +57437,7 @@ "\n", "x = np.column_stack((f, f, f))\n", "y = np.column_stack((H1, H2, H3))\n", - "title = 'PEQ 2nd order for boosting, type I == type II'\n", + "title = \"PEQ 2nd order for boosting, type I == type II\"\n", "legend = [\"type I\", \"type II\", \"type III\"]\n", "magnitude_plot(x, y, title, legend)" ] @@ -59243,15 +59244,15 @@ "B1, A1, b, a = audiofilter.biquad_peq2nd(fm, G, QBP, fs, \"I\") # fc at -3dB\n", "B2, A2, b, a = audiofilter.biquad_peq2nd(fm, G, QBP, fs, \"II\") # fc at -15dB\n", "B3, A3, b, a = audiofilter.biquad_peq2nd(fm, G, QBP, fs, \"III\") # fc at -9dB\n", - "f = np.arange(1, fs/2, 1)\n", - "s = 2*np.pi*f\n", + "f = np.arange(1, fs / 2, 1)\n", + "s = 2 * np.pi * f\n", "s, H1 = signal.freqs(B1, A1, s)\n", "s, H2 = signal.freqs(B2, A2, s)\n", "s, H3 = signal.freqs(B3, A3, s)\n", "\n", "x = np.column_stack((f, f, f))\n", "y = np.column_stack((H1, H2, H3))\n", - "title = 'PEQ 2nd order for cutting'\n", + "title = \"PEQ 2nd order for cutting\"\n", "legend = [\"type I\", \"type II\", \"type III\"]\n", "magnitude_plot(x, y, title, legend)" ] @@ -61144,17 +61145,17 @@ "B, A, bsin, asin = audiofilter.biquad_peq2nd(fm, G, QBP, fs, \"III\", \"sin\")\n", "B, A, bcos, acos = audiofilter.biquad_peq2nd(fm, G, QBP, fs, \"III\", \"cos\")\n", "B, A, btan, atan = audiofilter.biquad_peq2nd(fm, G, QBP, fs, \"III\", \"tan\")\n", - "f = np.arange(1, fs/2, 1)\n", - "s = 2*np.pi*f\n", - "W = s/fs\n", - "s, H = signal.freqs(B, A, 2*np.pi*f)\n", + "f = np.arange(1, fs / 2, 1)\n", + "s = 2 * np.pi * f\n", + "W = s / fs\n", + "s, H = signal.freqs(B, A, 2 * np.pi * f)\n", "W, Hsin = signal.freqz(bsin, asin, W)\n", "W, Hcos = signal.freqz(bcos, acos, W)\n", "W, Htan = signal.freqz(btan, atan, W)\n", "\n", "x = np.column_stack((f, f, f, f))\n", "y = np.column_stack((H, Hsin, Hcos, Htan))\n", - "title = 'PEQ 2nd order'\n", + "title = \"PEQ 2nd order\"\n", "legend = [\"Analog\", \"sin Q-warp\", \"cos Q-warp\", \"tan Q-warp\"]\n", "magnitude_plot(x, y, title, legend)" ] @@ -63322,8 +63323,8 @@ } ], "source": [ - "f = np.arange(1, fs/2, 1)\n", - "s = 2*np.pi*f\n", + "f = np.arange(1, fs / 2, 1)\n", + "s = 2 * np.pi * f\n", "bi = 1\n", "ai = np.sqrt(2) # Butterworth\n", "\n", @@ -63349,8 +63350,8 @@ "\n", "# Butterworth Lowpass^2 * Butterwoth Highpass^2\n", "# results in a Linkwitz-Riley Bandpass:\n", - "LF = (LF_HP*LF_HP) * (LF_LP*LF_LP)\n", - "HF = (HF_HP*HF_HP) * (HF_LP*HF_LP)\n", + "LF = (LF_HP * LF_HP) * (LF_LP * LF_LP)\n", + "HF = (HF_HP * HF_HP) * (HF_LP * HF_LP)\n", "LF[0] = 1e-15 # avoid zero at DC\n", "HF[0] = 1e-15 # avoid zero at DC\n", "# simulation of the acoustic summation on both driver's middle axis:\n", @@ -63358,10 +63359,12 @@ "\n", "x = np.column_stack((f, f, f))\n", "y = np.column_stack((LF, HF, AcousticSum))\n", - "title = '4th-order Linkwitz-Riley X-Over for a Two-Way Loudspeaker'\n", - "legend = [\"electric bandpass for low frequency driver\",\n", - " \"electric bandpass for high frequency driver\",\n", - " \"acoustic on-axis summation\"]\n", + "title = \"4th-order Linkwitz-Riley X-Over for a Two-Way Loudspeaker\"\n", + "legend = [\n", + " \"electric bandpass for low frequency driver\",\n", + " \"electric bandpass for high frequency driver\",\n", + " \"acoustic on-axis summation\",\n", + "]\n", "magnitude_plot(x, y, title, legend)" ] }, @@ -63435,11 +63438,11 @@ "outputs": [], "source": [ "fm = 1000\n", - "Q = 2/3\n", + "Q = 2 / 3\n", "G = 12\n", - "B, A, b, a = audiofilter.biquad_peq2nd(fm, G, Q, fs,\n", - " filter_type=\"II\",\n", - " q_warp_method=\"NoQPreWarp\")\n", + "B, A, b, a = audiofilter.biquad_peq2nd(\n", + " fm, G, Q, fs, filter_type=\"II\", q_warp_method=\"NoQPreWarp\"\n", + ")\n", "bZ, aZ = audiofilter.biquad_peq2nd_zoelzer(fm, G, Q, fs)\n", "np.testing.assert_allclose(bZ, b)\n", "np.testing.assert_allclose(aZ, a)" @@ -63464,11 +63467,11 @@ "outputs": [], "source": [ "fm = 1000\n", - "Q = 2/3\n", - "G = 12 \n", - "B, A, b, a = audiofilter.biquad_peq2nd(fm, G, Q, fs,\n", - " filter_type=\"III\",\n", - " q_warp_method=\"sin\")\n", + "Q = 2 / 3\n", + "G = 12\n", + "B, A, b, a = audiofilter.biquad_peq2nd(\n", + " fm, G, Q, fs, filter_type=\"III\", q_warp_method=\"sin\"\n", + ")\n", "bR, aR = audiofilter.biquad_peq2nd_RBJ(fm, G, Q, fs)\n", "np.testing.assert_allclose(bR, b)\n", "np.testing.assert_allclose(aR, a)" @@ -63490,11 +63493,10 @@ "source": [ "fc = 1000\n", "G = 12\n", - "B, A, b, a = audiofilter.biquad_lshv2nd(fc, G, fs,\n", - " filter_type=\"I\",\n", - " qz=1/np.sqrt(2),\n", - " qp=1/np.sqrt(2)) \n", - "bZ, aZ = audiofilter.biquad_lshv2nd_Zoelzer(fc, G, fs) \n", + "B, A, b, a = audiofilter.biquad_lshv2nd(\n", + " fc, G, fs, filter_type=\"I\", qz=1 / np.sqrt(2), qp=1 / np.sqrt(2)\n", + ")\n", + "bZ, aZ = audiofilter.biquad_lshv2nd_Zoelzer(fc, G, fs)\n", "np.testing.assert_allclose(bZ, b)\n", "np.testing.assert_allclose(aZ, a)" ] @@ -63516,11 +63518,10 @@ "fc = 1000\n", "G = 1\n", "S = 1\n", - "B, A, b, a = audiofilter.biquad_lshv2nd(fc, G, fs,\n", - " filter_type=\"III\",\n", - " qz=1/np.sqrt(2),\n", - " qp=1/np.sqrt(2)) \n", - "bR, aR = audiofilter.biquad_lshv2nd_RBJ(fc, G, S, fs) \n", + "B, A, b, a = audiofilter.biquad_lshv2nd(\n", + " fc, G, fs, filter_type=\"III\", qz=1 / np.sqrt(2), qp=1 / np.sqrt(2)\n", + ")\n", + "bR, aR = audiofilter.biquad_lshv2nd_RBJ(fc, G, S, fs)\n", "np.testing.assert_allclose(bR, b)\n", "np.testing.assert_allclose(aR, a)" ] @@ -63541,11 +63542,10 @@ "source": [ "fc = 1000\n", "G = 12\n", - "B, A, b, a = audiofilter.biquad_hshv2nd(fc, G, fs,\n", - " filter_type=\"I\",\n", - " qz=1/np.sqrt(2),\n", - " qp=1/np.sqrt(2)) \n", - "bZ, aZ = audiofilter.biquad_hshv2nd_Zoelzer(fc, G, fs) \n", + "B, A, b, a = audiofilter.biquad_hshv2nd(\n", + " fc, G, fs, filter_type=\"I\", qz=1 / np.sqrt(2), qp=1 / np.sqrt(2)\n", + ")\n", + "bZ, aZ = audiofilter.biquad_hshv2nd_Zoelzer(fc, G, fs)\n", "np.testing.assert_allclose(bZ, b)\n", "np.testing.assert_allclose(aZ, a)" ] @@ -63567,11 +63567,10 @@ "fc = 1000\n", "G = 12\n", "S = 1\n", - "B, A, b, a = audiofilter.biquad_hshv2nd(fc, G, fs,\n", - " filter_type=\"III\",\n", - " qz=1/np.sqrt(2),\n", - " qp=1/np.sqrt(2)) \n", - "bR, aR = audiofilter.biquad_hshv2nd_RBJ(fc, G, S, fs) \n", + "B, A, b, a = audiofilter.biquad_hshv2nd(\n", + " fc, G, fs, filter_type=\"III\", qz=1 / np.sqrt(2), qp=1 / np.sqrt(2)\n", + ")\n", + "bR, aR = audiofilter.biquad_hshv2nd_RBJ(fc, G, S, fs)\n", "np.testing.assert_allclose(bR, b)\n", "np.testing.assert_allclose(aR, a)" ] diff --git a/filter_design/audiofilter.py b/filter_design/audiofilter.py index 2185560..98b6f5a 100644 --- a/filter_design/audiofilter.py +++ b/filter_design/audiofilter.py @@ -59,14 +59,14 @@ def bilinear_biquad(B, A, fs): B0, B1, B2 = B fs2 = fs**2 - a0 = A2 + 2*A1*fs + 4*A0*fs2 - b0 = B2 + 2*B1*fs + 4*B0*fs2 + a0 = A2 + 2 * A1 * fs + 4 * A0 * fs2 + b0 = B2 + 2 * B1 * fs + 4 * B0 * fs2 - b1 = 2*B2 - 8*B0*fs2 - a1 = 2*A2 - 8*A0*fs2 + b1 = 2 * B2 - 8 * B0 * fs2 + a1 = 2 * A2 - 8 * A0 * fs2 - b2 = B2 - 2*B1*fs + 4*B0*fs2 - a2 = A2 - 2*A1*fs + 4*A0*fs2 + b2 = B2 - 2 * B1 * fs + 4 * B0 * fs2 + a2 = A2 - 2 * A1 * fs + 4 * A0 * fs2 b = np.array([b0, b1, b2]) / a0 a = np.array([a0, a1, a2]) / a0 @@ -76,12 +76,12 @@ def bilinear_biquad(B, A, fs): def bw_from_q(q): """Convert bandpass quality to bandwidth in octaves.""" - return 2/np.log(2) * np.arcsinh(1/(2*q)) + return 2 / np.log(2) * np.arcsinh(1 / (2 * q)) def q_from_bw(bw): """Convert bandwidth in octaves to bandpass quality.""" - return 1 / (2*np.sinh(np.log(2)/2*bw)) + return 1 / (2 * np.sinh(np.log(2) / 2 * bw)) def f_prewarping(f, fs): @@ -93,7 +93,7 @@ def f_prewarping(f, fs): output: prewarped angular frequency in rad/s """ - return 2*fs*np.tan(np.pi*f/fs) + return 2 * fs * np.tan(np.pi * f / fs) def q_prewarping(q, fm, fs, q_warp_method="cos"): @@ -117,13 +117,13 @@ def q_prewarping(q, fm, fs, q_warp_method="cos"): """ if q_warp_method == "sin": bandwidth = bw_from_q(q) - w0 = 2*np.pi*fm / fs - bandwidth = bandwidth*w0 / np.sin(w0) + w0 = 2 * np.pi * fm / fs + bandwidth = bandwidth * w0 / np.sin(w0) qpre = q_from_bw(bandwidth) elif q_warp_method == "cos": - qpre = q * np.cos(np.pi*fm / fs) + qpre = q * np.cos(np.pi * fm / fs) elif q_warp_method == "tan": - qpre = q * (np.pi*fm / fs) / np.tan(np.pi*fm / fs) + qpre = q * (np.pi * fm / fs) / np.tan(np.pi * fm / fs) else: qpre = q return qpre @@ -141,19 +141,19 @@ def biquad_lp1st(fc, fs): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wc = 2*np.pi*fc - B = np.array([0., 0, 1]) + wc = 2 * np.pi * fc + B = np.array([0.0, 0, 1]) A = np.array([0, 1 / wc, 1]) wcpre = f_prewarping(fc, fs) - Bp = 0., 0., 1. - Ap = 0., 1 / wcpre, 1. + Bp = 0.0, 0.0, 1.0 + Ap = 0.0, 1 / wcpre, 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a -def biquad_lp2nd(fc, fs, bi=1., ai=np.sqrt(2)): +def biquad_lp2nd(fc, fs, bi=1.0, ai=np.sqrt(2)): """Calc coeff for lowpass 2nd order. input: @@ -168,13 +168,13 @@ def biquad_lp2nd(fc, fs, bi=1., ai=np.sqrt(2)): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wc = 2*np.pi*fc - B = np.array([0., 0, 1]) + wc = 2 * np.pi * fc + B = np.array([0.0, 0, 1]) A = np.array([bi / wc**2, ai / wc, 1]) wcpre = f_prewarping(fc, fs) - Bp = 0., 0., 1. - Ap = bi / wcpre**2, ai / wcpre, 1. + Bp = 0.0, 0.0, 1.0 + Ap = bi / wcpre**2, ai / wcpre, 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a @@ -192,19 +192,19 @@ def biquad_hp1st(fc, fs): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wc = 2*np.pi*fc + wc = 2 * np.pi * fc B = np.array([0, 1 / wc, 0]) A = np.array([0, 1 / wc, 1]) wcpre = f_prewarping(fc, fs) - Bp = 0., 1 / wcpre, 0. - Ap = 0., 1 / wcpre, 1. + Bp = 0.0, 1 / wcpre, 0.0 + Ap = 0.0, 1 / wcpre, 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a -def biquad_hp2nd(fc, fs, bi=1., ai=np.sqrt(2)): +def biquad_hp2nd(fc, fs, bi=1.0, ai=np.sqrt(2)): """Calc coeff for highpass 2nd order. input: @@ -219,12 +219,12 @@ def biquad_hp2nd(fc, fs, bi=1., ai=np.sqrt(2)): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wc = 2*np.pi*fc + wc = 2 * np.pi * fc B = np.array([1 / wc**2, 0, 0]) A = np.array([1 / wc**2, ai / wc, bi]) wcpre = f_prewarping(fc, fs) - Bp = 1 / wcpre**2, 0., 0. + Bp = 1 / wcpre**2, 0.0, 0.0 Ap = 1 / wcpre**2, ai / wcpre, bi b, a = bilinear_biquad(Bp, Ap, fs) @@ -245,14 +245,14 @@ def biquad_bp2nd(fm, q, fs, q_warp_method="cos"): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wm = 2*np.pi*fm - B = np.array([0, 1 / (q*wm), 0]) - A = np.array([1 / wm**2, 1 / (q*wm), 1]) + wm = 2 * np.pi * fm + B = np.array([0, 1 / (q * wm), 0]) + A = np.array([1 / wm**2, 1 / (q * wm), 1]) wmpre = f_prewarping(fm, fs) qpre = q_prewarping(q, fm, fs, q_warp_method) - Bp = 0., 1 / (qpre*wmpre), 0. - Ap = 1 / wmpre**2, 1 / (qpre*wmpre), 1. + Bp = 0.0, 1 / (qpre * wmpre), 0.0 + Ap = 1 / wmpre**2, 1 / (qpre * wmpre), 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a @@ -272,20 +272,20 @@ def biquad_bs2nd(fm, q, fs, q_warp_method="cos"): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wm = 2*np.pi*fm + wm = 2 * np.pi * fm B = np.array([1 / wm**2, 0, 1]) - A = np.array([1 / wm**2, 1 / (q*wm), 1]) + A = np.array([1 / wm**2, 1 / (q * wm), 1]) wmpre = f_prewarping(fm, fs) qpre = q_prewarping(q, fm, fs, q_warp_method) - Bp = 1 / wmpre**2, 0., 1. - Ap = 1 / wmpre**2, 1 / (qpre*wmpre), 1. + Bp = 1 / wmpre**2, 0.0, 1.0 + Ap = 1 / wmpre**2, 1 / (qpre * wmpre), 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a -def biquad_ap1st(fc, fs, ai=1.): +def biquad_ap1st(fc, fs, ai=1.0): """Calc coeff for allpass 1st order. input: @@ -298,19 +298,19 @@ def biquad_ap1st(fc, fs, ai=1.): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wc = 2*np.pi*fc + wc = 2 * np.pi * fc B = np.array([0, -ai / wc, 1]) A = np.array([0, +ai / wc, 1]) wcpre = f_prewarping(fc, fs) - Bp = 0., -ai / wcpre, 1. - Ap = 0., +ai / wcpre, 1. + Bp = 0.0, -ai / wcpre, 1.0 + Ap = 0.0, +ai / wcpre, 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a -def biquad_ap2nd(fc, fs, bi=1., ai=np.sqrt(2)): +def biquad_ap2nd(fc, fs, bi=1.0, ai=np.sqrt(2)): """Calc coeff for allpass 2nd order. input: @@ -324,13 +324,13 @@ def biquad_ap2nd(fc, fs, bi=1., ai=np.sqrt(2)): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wc = 2*np.pi*fc + wc = 2 * np.pi * fc B = np.array([bi / wc**2, -ai / wc, 1]) A = np.array([bi / wc**2, +ai / wc, 1]) wcpre = f_prewarping(fc, fs) - Bp = bi / wcpre**2, -ai / wcpre, 1. - Ap = bi / wcpre**2, +ai / wcpre, 1. + Bp = bi / wcpre**2, -ai / wcpre, 1.0 + Ap = bi / wcpre**2, +ai / wcpre, 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a @@ -352,9 +352,9 @@ def biquad_peq2nd(fm, G, q, fs, filter_type="III", q_warp_method="cos"): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wm = 2*np.pi*fm + wm = 2 * np.pi * fm wmpre = f_prewarping(fm, fs) - g = 10**(G/20) + g = 10 ** (G / 20) qpre = q_prewarping(q, fm, fs, q_warp_method) if filter_type == "I": # aka constant-Q PEQ gamma = g @@ -362,30 +362,31 @@ def biquad_peq2nd(fm, G, q, fs, filter_type="III", q_warp_method="cos"): elif filter_type == "II": # aka symmetrical PEQ gamma = 1 delta = g - elif filter_type == 'III': # aka one-half pad loss PEQ or midpoint PEQ + elif filter_type == "III": # aka one-half pad loss PEQ or midpoint PEQ gamma = g**0.5 delta = g**0.5 else: - raise ValueError(("inappropriate filter_type, " - "please use 'I', 'II' or 'III' only")) + raise ValueError( + ("inappropriate filter_type, " "please use 'I', 'II' or 'III' only") + ) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - B = np.array([0., 0, 1]) # flat EQ + B = np.array([0.0, 0, 1]) # flat EQ A = B - b = np.array([1., 0, 0]) + b = np.array([1.0, 0, 0]) a = b elif G > 0: - B = np.array([1 / wm**2, delta / (q*wm), 1]) - A = np.array([1 / wm**2, (delta/g) / (q*wm), 1]) + B = np.array([1 / wm**2, delta / (q * wm), 1]) + A = np.array([1 / wm**2, (delta / g) / (q * wm), 1]) - Bp = 1 / wmpre**2, delta / (qpre*wmpre), 1. - Ap = 1 / wmpre**2, (delta/g) / (qpre*wmpre), 1. + Bp = 1 / wmpre**2, delta / (qpre * wmpre), 1.0 + Ap = 1 / wmpre**2, (delta / g) / (qpre * wmpre), 1.0 b, a = bilinear_biquad(Bp, Ap, fs) else: - B = np.array([1 / wm**2, gamma / (q*wm), 1]) - A = np.array([1 / wm**2, (gamma/g) / (q*wm), 1]) + B = np.array([1 / wm**2, gamma / (q * wm), 1]) + A = np.array([1 / wm**2, (gamma / g) / (q * wm), 1]) - Bp = 1 / wmpre**2, gamma / (qpre*wmpre), 1. - Ap = 1 / wmpre**2, (gamma/g) / (qpre*wmpre), 1. + Bp = 1 / wmpre**2, gamma / (qpre * wmpre), 1.0 + Ap = 1 / wmpre**2, (gamma / g) / (qpre * wmpre), 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a @@ -407,27 +408,31 @@ def biquad_peq2nd_zoelzer(fm, G, q, fs): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - K = np.tan(np.pi * fm/fs) - V0 = 10**(G/20) + K = np.tan(np.pi * fm / fs) + V0 = 10 ** (G / 20) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - b = np.array([1., 0, 0]) # flat EQ + b = np.array([1.0, 0, 0]) # flat EQ a = b elif G > 0: - tmp = 1 + K/q + K**2 - b = np.array([(1 + V0/q * K + K**2) / tmp, - 2 * (K**2 - 1) / tmp, - (1 - V0/q * K + K**2) / tmp]) - a = np.array([1, - 2 * (K**2 - 1) / tmp, - (1 - K/q + K**2) / tmp]) + tmp = 1 + K / q + K**2 + b = np.array( + [ + (1 + V0 / q * K + K**2) / tmp, + 2 * (K**2 - 1) / tmp, + (1 - V0 / q * K + K**2) / tmp, + ] + ) + a = np.array([1, 2 * (K**2 - 1) / tmp, (1 - K / q + K**2) / tmp]) else: - tmp = 1 + K / (V0*q) + K**2 - b = np.array([(1 + K/q + K**2) / tmp, - 2 * (K**2 - 1) / tmp, - (1 - K/q + K**2) / tmp]) - a = np.array([1, - 2 * (K**2 - 1) / tmp, - (1 - K/(V0*q) + K**2) / tmp]) + tmp = 1 + K / (V0 * q) + K**2 + b = np.array( + [ + (1 + K / q + K**2) / tmp, + 2 * (K**2 - 1) / tmp, + (1 - K / q + K**2) / tmp, + ] + ) + a = np.array([1, 2 * (K**2 - 1) / tmp, (1 - K / (V0 * q) + K**2) / tmp]) return b, a @@ -451,21 +456,23 @@ def biquad_peq2nd_RBJ(fm, G, q, fs): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - Ksqrt = 10**(G/40) - w0 = 2*np.pi*fm / fs + Ksqrt = 10 ** (G / 40) + w0 = 2 * np.pi * fm / fs BW = bw_from_q(q) - gamma = np.sinh(np.log(2)/2 * (BW*w0) / np.sin(w0))*np.sin(w0) - tmp = 1 + gamma/Ksqrt + gamma = np.sinh(np.log(2) / 2 * (BW * w0) / np.sin(w0)) * np.sin(w0) + tmp = 1 + gamma / Ksqrt if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - b = np.array([1., 0, 0]) # flat EQ + b = np.array([1.0, 0, 0]) # flat EQ a = b else: - b = np.array([(1 + gamma*Ksqrt) / tmp, - -2 * np.cos(w0) / tmp, - (1 - gamma*Ksqrt) / tmp]) - a = np.array([1, - -2 * np.cos(w0) / tmp, - (1 - gamma/Ksqrt) / tmp]) + b = np.array( + [ + (1 + gamma * Ksqrt) / tmp, + -2 * np.cos(w0) / tmp, + (1 - gamma * Ksqrt) / tmp, + ] + ) + a = np.array([1, -2 * np.cos(w0) / tmp, (1 - gamma / Ksqrt) / tmp]) return b, a @@ -483,9 +490,9 @@ def biquad_lshv1st(fc, G, fs, filter_type="III"): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wc = 2*np.pi*fc + wc = 2 * np.pi * fc wcpre = f_prewarping(fc, fs) - g = 10**(G/20) + g = 10 ** (G / 20) if filter_type == "I": alpha = 1 elif filter_type == "II": @@ -493,32 +500,32 @@ def biquad_lshv1st(fc, G, fs, filter_type="III"): elif filter_type == "III": # one-half pad loss, midpoint alpha = g**0.25 else: - raise ValueError(("inappropriate filter_type, " - "please use 'I', 'II' or 'III' only")) + raise ValueError( + ("inappropriate filter_type, " "please use 'I', 'II' or 'III' only") + ) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - B = np.array([0., 0, 1]) # flat EQ + B = np.array([0.0, 0, 1]) # flat EQ A = B - b = np.array([1., 0, 0]) + b = np.array([1.0, 0, 0]) a = b elif G > 0: B = np.array([0, 1 / wc, g * alpha**-2]) A = np.array([0, 1 / wc, alpha**-2]) - Bp = 0., 1 / wcpre, g * alpha**-2 - Ap = 0., 1 / wcpre, alpha**-2 + Bp = 0.0, 1 / wcpre, g * alpha**-2 + Ap = 0.0, 1 / wcpre, alpha**-2 b, a = bilinear_biquad(Bp, Ap, fs) else: B = np.array([0, 1 / wc, alpha**2]) A = np.array([0, 1 / wc, g**-1 * alpha**2]) - Bp = 0., 1 / wcpre, alpha**2 - Ap = 0., 1 / wcpre, g**-1 * alpha**2 + Bp = 0.0, 1 / wcpre, alpha**2 + Ap = 0.0, 1 / wcpre, g**-1 * alpha**2 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a -def biquad_lshv2nd(fc, G, fs, - filter_type="III", qz=1/np.sqrt(2), qp=1/np.sqrt(2)): +def biquad_lshv2nd(fc, G, fs, filter_type="III", qz=1 / np.sqrt(2), qp=1 / np.sqrt(2)): """Calc coeff for lowshelving 2nd order. input: @@ -534,8 +541,8 @@ def biquad_lshv2nd(fc, G, fs, b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - g = 10**(G/20) - wc = 2*np.pi*fc + g = 10 ** (G / 20) + wc = 2 * np.pi * fc wcpre = f_prewarping(fc, fs) if filter_type == "I": alpha = 1 @@ -544,28 +551,27 @@ def biquad_lshv2nd(fc, G, fs, elif filter_type == "III": # one-half pad loss, midpoint alpha = g**0.25 else: - raise ValueError(("inappropriate filter_type, " - "please use 'I', 'II' or 'III' only")) + raise ValueError( + ("inappropriate filter_type, " "please use 'I', 'II' or 'III' only") + ) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - B = np.array([0., 0, 1]) # flat EQ + B = np.array([0.0, 0, 1]) # flat EQ A = B - b = np.array([1., 0, 0]) + b = np.array([1.0, 0, 0]) a = b elif G > 0: - B = np.array([1 / wc**2, g**0.5 * alpha**-1 / (qz*wc), g * alpha**-2]) - A = np.array([1 / wc**2, alpha**-1 / (qp*wc), alpha**-2]) + B = np.array([1 / wc**2, g**0.5 * alpha**-1 / (qz * wc), g * alpha**-2]) + A = np.array([1 / wc**2, alpha**-1 / (qp * wc), alpha**-2]) - Bp = [1 / wcpre**2, g**0.5 * alpha**-1 / (qz*wcpre), - g * alpha**-2] - Ap = [1 / wcpre**2, alpha**-1 / (qp*wcpre), alpha**-2] + Bp = [1 / wcpre**2, g**0.5 * alpha**-1 / (qz * wcpre), g * alpha**-2] + Ap = [1 / wcpre**2, alpha**-1 / (qp * wcpre), alpha**-2] b, a = bilinear_biquad(Bp, Ap, fs) else: - B = np.array([1 / wc**2, alpha / (qz*wc), alpha**2]) - A = np.array([1 / wc**2, g**-0.5 * alpha / (qp*wc), g**-1 * alpha**2]) + B = np.array([1 / wc**2, alpha / (qz * wc), alpha**2]) + A = np.array([1 / wc**2, g**-0.5 * alpha / (qp * wc), g**-1 * alpha**2]) - Bp = [1 / wcpre**2, alpha / (qz*wcpre), alpha**2] - Ap = [1 / wcpre**2, g**-0.5 * alpha / (qp*wcpre), - g**-1 * alpha**2] + Bp = [1 / wcpre**2, alpha / (qz * wcpre), alpha**2] + Ap = [1 / wcpre**2, g**-0.5 * alpha / (qp * wcpre), g**-1 * alpha**2] b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a @@ -586,27 +592,33 @@ def biquad_lshv2nd_Zoelzer(fc, G, fs): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - V0 = 10**(G/20) - K = np.tan(np.pi*fc / fs) + V0 = 10 ** (G / 20) + K = np.tan(np.pi * fc / fs) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - b = np.array([1., 0, 0]) # flat EQ + b = np.array([1.0, 0, 0]) # flat EQ a = b elif G > 0: - tmp = 1 + np.sqrt(2)*K + K**2 - b = np.array([(1 + np.sqrt(2*V0)*K + V0*K**2) / tmp, - 2 * (V0 * K**2 - 1) / tmp, - (1 - np.sqrt(2*V0)*K + (V0 * K**2)) / tmp]) - a = np.array([1, - 2 * (K**2 - 1) / tmp, - (1 - np.sqrt(2)*K + K**2) / tmp]) + tmp = 1 + np.sqrt(2) * K + K**2 + b = np.array( + [ + (1 + np.sqrt(2 * V0) * K + V0 * K**2) / tmp, + 2 * (V0 * K**2 - 1) / tmp, + (1 - np.sqrt(2 * V0) * K + (V0 * K**2)) / tmp, + ] + ) + a = np.array([1, 2 * (K**2 - 1) / tmp, (1 - np.sqrt(2) * K + K**2) / tmp]) else: - tmp = V0 + np.sqrt(2*V0)*K + K**2 - b = np.array([V0 * (1 + np.sqrt(2)*K + K**2) / tmp, - 2*V0 * (K**2 - 1) / tmp, - V0 * (1 - np.sqrt(2)*K + K**2) / tmp]) - a = np.array([1, - 2 * (K**2 - V0) / tmp, - (V0 - np.sqrt(2*V0)*K + K**2) / tmp]) + tmp = V0 + np.sqrt(2 * V0) * K + K**2 + b = np.array( + [ + V0 * (1 + np.sqrt(2) * K + K**2) / tmp, + 2 * V0 * (K**2 - 1) / tmp, + V0 * (1 - np.sqrt(2) * K + K**2) / tmp, + ] + ) + a = np.array( + [1, 2 * (K**2 - V0) / tmp, (V0 - np.sqrt(2 * V0) * K + K**2) / tmp] + ) return b, a @@ -627,20 +639,28 @@ def biquad_lshv2nd_RBJ(fc, G, S, fs): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - A = 10**(G/40) - w0 = 2*np.pi*fc / fs - alpha = np.sin(w0)/2 * np.sqrt((A + 1/A) * (1/S - 1) + 2) + A = 10 ** (G / 40) + w0 = 2 * np.pi * fc / fs + alpha = np.sin(w0) / 2 * np.sqrt((A + 1 / A) * (1 / S - 1) + 2) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - b = np.array([1., 0, 0]) # flat EQ + b = np.array([1.0, 0, 0]) # flat EQ a = b else: - a0 = (A + 1) + (A - 1)*np.cos(w0) + 2*np.sqrt(A)*alpha - b = np.array([A * ((A + 1) - (A - 1)*np.cos(w0) + 2*np.sqrt(A)*alpha), - 2*A * ((A - 1) - (A + 1)*np.cos(w0)), - A * ((A + 1) - (A - 1)*np.cos(w0) - 2*np.sqrt(A)*alpha)]) - a = np.array([a0, - -2 * ((A - 1) + (A + 1)*np.cos(w0)), - (A + 1) + (A - 1)*np.cos(w0) - 2*np.sqrt(A)*alpha]) + a0 = (A + 1) + (A - 1) * np.cos(w0) + 2 * np.sqrt(A) * alpha + b = np.array( + [ + A * ((A + 1) - (A - 1) * np.cos(w0) + 2 * np.sqrt(A) * alpha), + 2 * A * ((A - 1) - (A + 1) * np.cos(w0)), + A * ((A + 1) - (A - 1) * np.cos(w0) - 2 * np.sqrt(A) * alpha), + ] + ) + a = np.array( + [ + a0, + -2 * ((A - 1) + (A + 1) * np.cos(w0)), + (A + 1) + (A - 1) * np.cos(w0) - 2 * np.sqrt(A) * alpha, + ] + ) a = a / a0 b = b / a0 return b, a @@ -660,9 +680,9 @@ def biquad_hshv1st(fc, G, fs, filter_type="III"): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wc = 2*np.pi*fc + wc = 2 * np.pi * fc wcpre = f_prewarping(fc, fs) - g = 10**(G/20) + g = 10 ** (G / 20) if filter_type == "I": alpha = 1 elif filter_type == "II": @@ -670,32 +690,32 @@ def biquad_hshv1st(fc, G, fs, filter_type="III"): elif filter_type == "III": # one-half pad loss, midpoint alpha = g**0.25 else: - raise ValueError(("inappropriate filter_type, " - "please use 'I', 'II' or 'III' only")) + raise ValueError( + ("inappropriate filter_type, " "please use 'I', 'II' or 'III' only") + ) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - B = np.array([0., 0, 1]) # flat EQ + B = np.array([0.0, 0, 1]) # flat EQ A = B - b = np.array([1., 0, 0]) + b = np.array([1.0, 0, 0]) a = b elif G > 0: B = np.array([0, g * alpha**-2 / wc, 1]) A = np.array([0, alpha**-2 / wc, 1]) - Bp = 0., g * alpha**-2 / wcpre, 1. - Ap = 0., alpha**-2 / wcpre, 1. + Bp = 0.0, g * alpha**-2 / wcpre, 1.0 + Ap = 0.0, alpha**-2 / wcpre, 1.0 b, a = bilinear_biquad(Bp, Ap, fs) else: B = np.array([0, alpha**2 / wc, 1]) A = np.array([0, g**-1 * alpha**2 / wc, 1]) - Bp = 0., alpha**2 / wcpre, 1. - Ap = 0., g**-1 * alpha**2 / wcpre, 1. + Bp = 0.0, alpha**2 / wcpre, 1.0 + Ap = 0.0, g**-1 * alpha**2 / wcpre, 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a -def biquad_hshv2nd(fc, G, fs, - filter_type="III", qz=1/np.sqrt(2), qp=1/np.sqrt(2)): +def biquad_hshv2nd(fc, G, fs, filter_type="III", qz=1 / np.sqrt(2), qp=1 / np.sqrt(2)): """Calc coeff for highshelving 2nd order. input: @@ -711,9 +731,9 @@ def biquad_hshv2nd(fc, G, fs, b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - wc = 2*np.pi*fc + wc = 2 * np.pi * fc wcpre = f_prewarping(fc, fs) - g = 10**(G/20) + g = 10 ** (G / 20) if filter_type == "I": alpha = 1 elif filter_type == "II": @@ -721,26 +741,27 @@ def biquad_hshv2nd(fc, G, fs, elif filter_type == "III": # one-half pad loss, midpoint alpha = g**0.25 else: - raise ValueError(("inappropriate filter_type, " - "please use 'I', 'II' or 'III' only")) + raise ValueError( + ("inappropriate filter_type, " "please use 'I', 'II' or 'III' only") + ) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - B = np.array([0., 0, 1]) # flat EQ + B = np.array([0.0, 0, 1]) # flat EQ A = B - b = np.array([1., 0, 0]) + b = np.array([1.0, 0, 0]) a = b elif G > 0: - B = np.array([g * alpha**-2 / wc**2, g**0.5 * alpha**-1 / (qz*wc), 1]) - A = np.array([alpha**-2 / wc**2, alpha**-1 / (qp*wc), 1]) + B = np.array([g * alpha**-2 / wc**2, g**0.5 * alpha**-1 / (qz * wc), 1]) + A = np.array([alpha**-2 / wc**2, alpha**-1 / (qp * wc), 1]) - Bp = g * alpha**-2 / wcpre**2, g**0.5 * alpha**-1 / (qz*wcpre), 1. - Ap = alpha**-2 / wcpre**2, alpha**-1 / (qp*wcpre), 1. + Bp = g * alpha**-2 / wcpre**2, g**0.5 * alpha**-1 / (qz * wcpre), 1.0 + Ap = alpha**-2 / wcpre**2, alpha**-1 / (qp * wcpre), 1.0 b, a = bilinear_biquad(Bp, Ap, fs) else: - B = np.array([alpha**2 / wc**2, alpha / (qz*wc), 1]) - A = np.array([g**-1 * alpha**2 / wc**2, g**-0.5 * alpha / (qp*wc), 1]) + B = np.array([alpha**2 / wc**2, alpha / (qz * wc), 1]) + A = np.array([g**-1 * alpha**2 / wc**2, g**-0.5 * alpha / (qp * wc), 1]) - Bp = alpha**2 / wcpre**2, alpha / (qz*wcpre), 1. - Ap = g**-1 * alpha**2 / wcpre**2, g**-0.5 * alpha/(qp*wcpre), 1. + Bp = alpha**2 / wcpre**2, alpha / (qz * wcpre), 1.0 + Ap = g**-1 * alpha**2 / wcpre**2, g**-0.5 * alpha / (qp * wcpre), 1.0 b, a = bilinear_biquad(Bp, Ap, fs) return B, A, b, a @@ -761,25 +782,37 @@ def biquad_hshv2nd_Zoelzer(fc, G, fs): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - V0 = 10**(G/20) - K = np.tan(np.pi*fc / fs) + V0 = 10 ** (G / 20) + K = np.tan(np.pi * fc / fs) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - b = np.array([1., 0, 0]) # flat EQ + b = np.array([1.0, 0, 0]) # flat EQ a = b elif G > 0: - tmp = 1 + np.sqrt(2)*K + K**2 - b = np.array([(V0 + np.sqrt(2*V0)*K + K**2) / tmp, - 2 * (K**2 - V0) / tmp, - (V0 - np.sqrt(2*V0)*K + K**2) / tmp]) - a = np.array([1, 2 * (K**2 - 1) / tmp, - (1 - np.sqrt(2)*K + K**2) / tmp]) + tmp = 1 + np.sqrt(2) * K + K**2 + b = np.array( + [ + (V0 + np.sqrt(2 * V0) * K + K**2) / tmp, + 2 * (K**2 - V0) / tmp, + (V0 - np.sqrt(2 * V0) * K + K**2) / tmp, + ] + ) + a = np.array([1, 2 * (K**2 - 1) / tmp, (1 - np.sqrt(2) * K + K**2) / tmp]) else: - tmp = 1 + np.sqrt(2*V0)*K + (V0 * K**2) - b = np.array([V0 * (1 + np.sqrt(2)*K + K**2) / tmp, - 2*V0 * (K**2 - 1) / tmp, - V0 * (1 - np.sqrt(2.)*K + K**2) / tmp]) - a = np.array([1, 2 * (V0 * K**2 - 1) / tmp, - (1 - np.sqrt(2*V0)*K + (V0 * K**2)) / tmp]) + tmp = 1 + np.sqrt(2 * V0) * K + (V0 * K**2) + b = np.array( + [ + V0 * (1 + np.sqrt(2) * K + K**2) / tmp, + 2 * V0 * (K**2 - 1) / tmp, + V0 * (1 - np.sqrt(2.0) * K + K**2) / tmp, + ] + ) + a = np.array( + [ + 1, + 2 * (V0 * K**2 - 1) / tmp, + (1 - np.sqrt(2 * V0) * K + (V0 * K**2)) / tmp, + ] + ) return b, a @@ -800,22 +833,28 @@ def biquad_hshv2nd_RBJ(fc, G, S, fs): b...numerator coefficients z-transfer function a...denominator coefficients z-transfer function """ - A = 10**(G/40) - w0 = 2*np.pi*fc / fs - alpha = np.sin(w0)/2 * np.sqrt((A + 1/A) * (1/S - 1) + 2) + A = 10 ** (G / 40) + w0 = 2 * np.pi * fc / fs + alpha = np.sin(w0) / 2 * np.sqrt((A + 1 / A) * (1 / S - 1) + 2) if np.isclose(G, 0, rtol=1e-05, atol=1e-08, equal_nan=False): - b = np.array([1., 0, 0]) # flat EQ + b = np.array([1.0, 0, 0]) # flat EQ a = b else: a0 = (A + 1) - (A - 1) * np.cos(w0) + 2 * np.sqrt(A) * alpha - b = np.array([A * ((A + 1) + (A - 1) * np.cos(w0) - + 2 * np.sqrt(A) * alpha), - - 2 * A * ((A - 1) + (A + 1) * np.cos(w0)), - A * ((A + 1) + (A - 1) * np.cos(w0) - - 2 * np.sqrt(A) * alpha)]) - a = np.array([a0, - 2 * ((A - 1) - (A + 1) * np.cos(w0)), - (A + 1) - (A - 1) * np.cos(w0) - 2 * np.sqrt(A) * alpha]) + b = np.array( + [ + A * ((A + 1) + (A - 1) * np.cos(w0) + 2 * np.sqrt(A) * alpha), + -2 * A * ((A - 1) + (A + 1) * np.cos(w0)), + A * ((A + 1) + (A - 1) * np.cos(w0) - 2 * np.sqrt(A) * alpha), + ] + ) + a = np.array( + [ + a0, + 2 * ((A - 1) - (A + 1) * np.cos(w0)), + (A + 1) - (A - 1) * np.cos(w0) - 2 * np.sqrt(A) * alpha, + ] + ) b = b / a0 a = a / a0 return b, a @@ -840,36 +879,35 @@ def zplane_plot(ax, z, p, k): """ # draw unit circle Nf = 2**7 - Om = np.arange(Nf) * 2*np.pi/Nf - plt.plot(np.cos(Om), np.sin(Om), 'C7') + Om = np.arange(Nf) * 2 * np.pi / Nf + plt.plot(np.cos(Om), np.sin(Om), "C7") try: # TBD: check if this pole is compensated by a zero - circle = Circle((0, 0), radius=np.max(np.abs(p)), - color='C7', alpha=0.15) + circle = Circle((0, 0), radius=np.max(np.abs(p)), color="C7", alpha=0.15) plt.gcf().gca().add_artist(circle) except ValueError: - print('no pole at all, ROC is whole z-plane') + print("no pole at all, ROC is whole z-plane") zu, zc = np.unique(z, return_counts=True) # find and count unique zeros for zui, zci in zip(zu, zc): # plot them individually - plt.plot(np.real(zui), np.imag(zui), ms=7, - color='C0', marker='o', fillstyle='none') + plt.plot( + np.real(zui), np.imag(zui), ms=7, color="C0", marker="o", fillstyle="none" + ) if zci > 1: # if multiple zeros exist then indicate the count plt.text(np.real(zui), np.imag(zui), zci) pu, pc = np.unique(p, return_counts=True) # find and count unique poles for pui, pci in zip(pu, pc): # plot them individually - plt.plot(np.real(pui), np.imag(pui), ms=7, - color='C3', marker='x') + plt.plot(np.real(pui), np.imag(pui), ms=7, color="C3", marker="x") if pci > 1: # if multiple poles exist then indicate the count plt.text(np.real(pui), np.imag(pui), pci) - plt.text(0, +1, 'k=%f' % k) - plt.text(0, -1, 'ROC for causal: white') - plt.axis('square') + plt.text(0, +1, "k=%f" % k) + plt.text(0, -1, "ROC for causal: white") + plt.axis("square") # plt.axis([-2, 2, -2, 2]) - plt.xlabel(r'$\Re\{z\}$') - plt.ylabel(r'$\Im\{z\}$') + plt.xlabel(r"$\Re\{z\}$") + plt.ylabel(r"$\Im\{z\}$") plt.grid(True) @@ -889,71 +927,99 @@ def bode_plot(B, A, b, a, fs, N, fig=None): fig = plt.figure() z, p, k = signal.tf2zpk(b, a) W, Hd = signal.freqz(b, a, N) - s, Ha = signal.freqs(B, A, fs*W) + s, Ha = signal.freqs(B, A, fs * W) if Hd[0] == 0: Hd[0] = 1e-15 # avoid zero at DC for plotting dB if Ha[0] == 0: Ha[0] = 1e-15 - f = fs*W / (2*np.pi) + f = fs * W / (2 * np.pi) gs = fig.add_gridspec(2, 2) # magnitude ax1 = fig.add_subplot(gs[0, 0]) - ax1.plot(f, 20*np.log10(np.abs(Ha)), "C0", - label=r'$|H(\omega)|$ continuous-time', - linewidth=3) - ax1.plot(f, 20*np.log10(np.abs(Hd)), "C1", - label=(r'$|H(\Omega)|$ discrete-time, fs=%5.f Hz' % fs), - linewidth=2) + ax1.plot( + f, + 20 * np.log10(np.abs(Ha)), + "C0", + label=r"$|H(\omega)|$ continuous-time", + linewidth=3, + ) + ax1.plot( + f, + 20 * np.log10(np.abs(Hd)), + "C1", + label=(r"$|H(\Omega)|$ discrete-time, fs=%5.f Hz" % fs), + linewidth=2, + ) ax1.set_xscale("log") ax1.set_yscale("linear") - ax1.set_xlabel(r'$f$ / Hz', color="xkcd:navy blue") - ax1.set_ylabel(r'$A$ / dB', color="xkcd:navy blue") + ax1.set_xlabel(r"$f$ / Hz", color="xkcd:navy blue") + ax1.set_ylabel(r"$A$ / dB", color="xkcd:navy blue") ax1.set_title("Bode plot: magnitude", color="xkcd:navy blue") ax1.set_xlim(20, 20000) ax1.set_xticks((20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000)) - ax1.set_xticklabels(["20", "50", - "100", "200", "500", - "1k", "2k", "5k", - "10k", "20k"], color="xkcd:navy blue") + ax1.set_xticklabels( + ["20", "50", "100", "200", "500", "1k", "2k", "5k", "10k", "20k"], + color="xkcd:navy blue", + ) ax1.set_ylim(-15, 15) - ax1.set_yticks(np.arange(-15, 15+3, 3)) - ax1.set_yticklabels(["-15", "-12", "-9", "-6", "-3", "0", - "3", "6", "9", "12", "15"], - color="xkcd:navy blue") + ax1.set_yticks(np.arange(-15, 15 + 3, 3)) + ax1.set_yticklabels( + ["-15", "-12", "-9", "-6", "-3", "0", "3", "6", "9", "12", "15"], + color="xkcd:navy blue", + ) ax1.legend(loc="best") - ax1.grid(True, which="both", axis="both", - linestyle="-", linewidth=0.5, color=(0.8, 0.8, 0.8)) + ax1.grid( + True, + which="both", + axis="both", + linestyle="-", + linewidth=0.5, + color=(0.8, 0.8, 0.8), + ) # phase ax2 = fig.add_subplot(gs[1, 0]) - ax2.plot(f, (np.angle(Ha)*180/np.pi), "C0", - label=r'$\mathrm{angle}(H('r'\omega))$ continuous-time', - linewidth=3) - ax2.plot(f, (np.angle(Hd)*180/np.pi), 'C1', - label=(r'$\mathrm{angle}(H(\Omega))$ discrete-time, ' - 'fs=%5.f Hz' % fs), - linewidth=2) + ax2.plot( + f, + (np.angle(Ha) * 180 / np.pi), + "C0", + label=r"$\mathrm{angle}(H(" r"\omega))$ continuous-time", + linewidth=3, + ) + ax2.plot( + f, + (np.angle(Hd) * 180 / np.pi), + "C1", + label=(r"$\mathrm{angle}(H(\Omega))$ discrete-time, " "fs=%5.f Hz" % fs), + linewidth=2, + ) ax2.set_xscale("log") ax2.set_yscale("linear") - ax2.set_xlabel(r'$f$ / Hz', color="xkcd:navy blue") - ax2.set_ylabel(r'$\phi$ / deg', color="xkcd:navy blue") + ax2.set_xlabel(r"$f$ / Hz", color="xkcd:navy blue") + ax2.set_ylabel(r"$\phi$ / deg", color="xkcd:navy blue") ax2.set_title("Bode plot: phase", color="xkcd:navy blue") ax2.set_xlim(20, 20000) ax2.set_xticks((20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000)) - ax2.set_xticklabels(["20", "50", - "100", "200", "500", - "1k", "2k", "5k", - "10k", "20k"], - color="xkcd:navy blue") + ax2.set_xticklabels( + ["20", "50", "100", "200", "500", "1k", "2k", "5k", "10k", "20k"], + color="xkcd:navy blue", + ) ax2.set_ylim(-180, +180) - ax2.set_yticks(np.arange(-180, 180+45, 45)) - ax2.set_yticklabels(["-180", "-135", "-90", "-45", "0", - "45", "90", "135", "180"], - color="xkcd:navy blue") + ax2.set_yticks(np.arange(-180, 180 + 45, 45)) + ax2.set_yticklabels( + ["-180", "-135", "-90", "-45", "0", "45", "90", "135", "180"], + color="xkcd:navy blue", + ) ax2.legend(loc="best") - ax2.grid(True, which="both", axis="both", - linestyle="-", linewidth=0.5, color=(0.8, 0.8, 0.8)) + ax2.grid( + True, + which="both", + axis="both", + linestyle="-", + linewidth=0.5, + color=(0.8, 0.8, 0.8), + ) # zplane ax3 = fig.add_subplot(gs[:, 1]) @@ -970,20 +1036,26 @@ def magnitude_plot_overlay(x, y, title, legend, fig=None): if fig is None: plt.figure() sz = y.shape - lines = plt.semilogx(x, 20*np.log10(np.abs(y))) - plt.legend(lines[:sz[1]], legend) + lines = plt.semilogx(x, 20 * np.log10(np.abs(y))) + plt.legend(lines[: sz[1]], legend) plt.autoscale("tight") plt.title(title) - plt.xlabel(r'$f$ / Hz') - plt.ylabel(r'20 log10 |$H$| / dB') + plt.xlabel(r"$f$ / Hz") + plt.ylabel(r"20 log10 |$H$| / dB") plt.axis([1000, 24000, 0, 18]) - plt.yticks(np.arange(-18, 18+3, 3)) - plt.xticks((20, 50, 100, 200, 500, - 1000, 2000, 5000, 10000, 20000), - ["20", "50", "100", "200", "500", - "1k", "2k", "5k", "10k", "20k"]) + plt.yticks(np.arange(-18, 18 + 3, 3)) + plt.xticks( + (20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000), + ["20", "50", "100", "200", "500", "1k", "2k", "5k", "10k", "20k"], + ) plt.xlim(20, x.max()) plt.ylim(-18, 18) - plt.grid(True, which="both", axis="both", - linestyle="-", linewidth=0.5, color=(0.8, 0.8, 0.8)) + plt.grid( + True, + which="both", + axis="both", + linestyle="-", + linewidth=0.5, + color=(0.8, 0.8, 0.8), + ) plt.show() diff --git a/filter_design/bilinear_transform.ipynb b/filter_design/bilinear_transform.ipynb index 01c7bee..60afbb6 100644 --- a/filter_design/bilinear_transform.ipynb +++ b/filter_design/bilinear_transform.ipynb @@ -977,16 +977,17 @@ "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", + "\n", "%matplotlib inline\n", "\n", "om = np.linspace(-10, 10, 200)\n", - "Om = 2*np.arctan(om*1/2)\n", + "Om = 2 * np.arctan(om * 1 / 2)\n", "\n", "plt.figure(figsize=(10, 4))\n", - "plt.plot(om, Om, label=r'$2 \\cdot \\arctan(\\frac{\\omega T}{2})$')\n", - "plt.plot(om, om, 'k--', label=r'$\\omega T$')\n", - "plt.xlabel(r'$\\omega$')\n", - "plt.ylabel(r'$\\Omega$')\n", + "plt.plot(om, Om, label=r\"$2 \\cdot \\arctan(\\frac{\\omega T}{2})$\")\n", + "plt.plot(om, om, \"k--\", label=r\"$\\omega T$\")\n", + "plt.xlabel(r\"$\\omega$\")\n", + "plt.ylabel(r\"$\\Omega$\")\n", "plt.axis([-10, 10, -np.pi, np.pi])\n", "plt.legend(loc=2)\n", "plt.grid()" @@ -2378,33 +2379,37 @@ "\n", "# coefficients of analog lowpass filter\n", "Qinf = 0.8\n", - "sinf = 2*np.pi*fc\n", + "sinf = 2 * np.pi * fc\n", "C = 1e-6\n", - "L = 1/(sinf**2*C)\n", - "R = sinf*L/Qinf\n", + "L = 1 / (sinf**2 * C)\n", + "R = sinf * L / Qinf\n", "\n", "B = [0, 0, 1]\n", - "A = [L*C, R*C, 1]\n", + "A = [L * C, R * C, 1]\n", "\n", "# cofficients of digital filter\n", - "T = 1/fs\n", - "b = [T**2, 2*T**2, T**2]\n", - "a = [(4*L*C+2*T*R*C+T**2), (-8*L*C+2*T**2), (4*L*C-2*T*R*C+T**2)]\n", + "T = 1 / fs\n", + "b = [T**2, 2 * T**2, T**2]\n", + "a = [\n", + " (4 * L * C + 2 * T * R * C + T**2),\n", + " (-8 * L * C + 2 * T**2),\n", + " (4 * L * C - 2 * T * R * C + T**2),\n", + "]\n", "\n", "# compute frequency responses\n", "Om, Hd = sig.freqz(b, a, worN=1024)\n", - "tmp, H = sig.freqs(B, A, worN=fs*Om)\n", + "tmp, H = sig.freqs(B, A, worN=fs * Om)\n", "\n", "# plot results\n", - "f = Om*fs/(2*np.pi)\n", + "f = Om * fs / (2 * np.pi)\n", "plt.figure(figsize=(10, 4))\n", - "plt.semilogx(f, 20*np.log10(np.abs(H)),\n", - " label=r'$|H(j \\omega)|$ of analog filter')\n", - "plt.semilogx(f, 20*np.log10(np.abs(Hd)),\n", - " label=r'$|H_d(e^{j \\Omega})|$ of digital filter')\n", - "plt.xlabel(r'$f$ in Hz')\n", - "plt.ylabel(r'dB')\n", - "plt.axis([100, fs/2, -70, 3])\n", + "plt.semilogx(f, 20 * np.log10(np.abs(H)), label=r\"$|H(j \\omega)|$ of analog filter\")\n", + "plt.semilogx(\n", + " f, 20 * np.log10(np.abs(Hd)), label=r\"$|H_d(e^{j \\Omega})|$ of digital filter\"\n", + ")\n", + "plt.xlabel(r\"$f$ in Hz\")\n", + "plt.ylabel(r\"dB\")\n", + "plt.axis([100, fs / 2, -70, 3])\n", "plt.legend()\n", "plt.grid()" ] @@ -4115,15 +4120,15 @@ } ], "source": [ - "omc = 2*np.pi*np.array([5000, 6000]) # corner frequencies of bandpass\n", + "omc = 2 * np.pi * np.array([5000, 6000]) # corner frequencies of bandpass\n", "N = 2 # order of filter\n", "\n", "# pre-warping of corner frequencies\n", - "omcp = 2*fs*np.tan(omc/(2*fs))\n", + "omcp = 2 * fs * np.tan(omc / (2 * fs))\n", "\n", "# design of analog filters with and without pre-warping\n", - "B, A = sig.butter(N, omc, btype='bandpass', analog=True)\n", - "Bp, Ap = sig.butter(N, omcp, btype='bandpass', analog=True)\n", + "B, A = sig.butter(N, omc, btype=\"bandpass\", analog=True)\n", + "Bp, Ap = sig.butter(N, omcp, btype=\"bandpass\", analog=True)\n", "\n", "# bilinear transform of analog prototypes\n", "b, a = sig.bilinear(B, A, fs)\n", @@ -4132,21 +4137,26 @@ "# compute frequency responses\n", "Om, Hdp = sig.freqz(bp, ap, worN=1024)\n", "Om, Hd = sig.freqz(b, a, worN=1024)\n", - "tmp, H = sig.freqs(B, A, worN=fs*Om)\n", + "tmp, H = sig.freqs(B, A, worN=fs * Om)\n", "\n", "# plot results\n", - "np.seterr(divide='ignore')\n", - "f = Om*fs/(2*np.pi)\n", + "np.seterr(divide=\"ignore\")\n", + "f = Om * fs / (2 * np.pi)\n", "plt.figure(figsize=(12, 8))\n", - "plt.semilogx(f, 20*np.log10(np.abs(H)),\n", - " label=r'$|H(j \\omega)|$ of analog prototype')\n", - "plt.semilogx(f, 20*np.log10(np.abs(Hd)),\n", - " label=r'$|H_d(e^{j \\Omega})|$ of digital filter without pre-warping')\n", - "plt.semilogx(f, 20*np.log10(np.abs(Hdp)),\n", - " label=r'$|H_d(e^{j \\Omega})|$ of digital filter with pre-warping')\n", - "plt.xlabel(r'$f$ in Hz')\n", - "plt.ylabel(r'dB')\n", - "plt.axis([100, fs/2, -70, 3])\n", + "plt.semilogx(f, 20 * np.log10(np.abs(H)), label=r\"$|H(j \\omega)|$ of analog prototype\")\n", + "plt.semilogx(\n", + " f,\n", + " 20 * np.log10(np.abs(Hd)),\n", + " label=r\"$|H_d(e^{j \\Omega})|$ of digital filter without pre-warping\",\n", + ")\n", + "plt.semilogx(\n", + " f,\n", + " 20 * np.log10(np.abs(Hdp)),\n", + " label=r\"$|H_d(e^{j \\Omega})|$ of digital filter with pre-warping\",\n", + ")\n", + "plt.xlabel(r\"$f$ in Hz\")\n", + "plt.ylabel(r\"dB\")\n", + "plt.axis([100, fs / 2, -70, 3])\n", "plt.legend()\n", "plt.grid()" ] diff --git a/filter_design/comparison_non_recursive.ipynb b/filter_design/comparison_non_recursive.ipynb index ae848ce..ccdebdb 100644 --- a/filter_design/comparison_non_recursive.ipynb +++ b/filter_design/comparison_non_recursive.ipynb @@ -1099,6 +1099,7 @@ "import matplotlib.pyplot as plt\n", "import matplotlib.patches as mpatches\n", "import scipy.signal as sig\n", + "\n", "%matplotlib inline\n", "\n", "\n", @@ -1106,22 +1107,30 @@ " Omp = Omp * np.pi\n", " Oms = Oms * np.pi\n", "\n", - " p = [[0, -d_p], [Omp, -d_p], [Omp, -300], [np.pi, -300],\n", - " [np.pi, a_s], [Oms, a_s], [Oms, d_p], [0, d_p]]\n", - " polygon = mpatches.Polygon(p, closed=True, facecolor='r', alpha=0.3)\n", + " p = [\n", + " [0, -d_p],\n", + " [Omp, -d_p],\n", + " [Omp, -300],\n", + " [np.pi, -300],\n", + " [np.pi, a_s],\n", + " [Oms, a_s],\n", + " [Oms, d_p],\n", + " [0, d_p],\n", + " ]\n", + " polygon = mpatches.Polygon(p, closed=True, facecolor=\"r\", alpha=0.3)\n", " plt.gca().add_patch(polygon)\n", "\n", "\n", - "Omp = .3 # normalized corner frequency of pass-band\n", - "Oms = .3 + 0.05 # normalized corner frequency of stop-band\n", + "Omp = 0.3 # normalized corner frequency of pass-band\n", + "Oms = 0.3 + 0.05 # normalized corner frequency of stop-band\n", "d_p = 1.5 # one-sided pass-band ripple in dB\n", "a_s = -60 # stop-band attenuation in dB\n", "\n", "plt.figure(figsize=(10, 5))\n", "plot_tolerance_scheme(Omp, Oms, d_p, a_s)\n", - "plt.title('Tolerance scheme')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$|H(e^{j \\Omega})|$ in dB')\n", + "plt.title(\"Tolerance scheme\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$|H(e^{j \\Omega})|$ in dB\")\n", "plt.axis([0, np.pi, -70, 3])\n", "plt.grid()" ] @@ -4295,7 +4304,9 @@ "M = 13 # order of recursive filter\n", "\n", "# design of non-recursive filter\n", - "h = sig.remez(N, [0, Omp/2, Oms/2, 1/2], [1, 10**((a_s-5)/20)], weight=[1, 1])\n", + "h = sig.remez(\n", + " N, [0, Omp / 2, Oms / 2, 1 / 2], [1, 10 ** ((a_s - 5) / 20)], weight=[1, 1]\n", + ")\n", "\n", "# design of recursive filter\n", "b, a = sig.cheby2(M, -a_s, Oms)\n", @@ -4306,22 +4317,22 @@ "\n", "# plot frequency response\n", "plt.figure(figsize=(10, 5))\n", - "plt.plot(Om, 20*np.log10(np.abs(Hn)), 'b-', label=r'non-recursive N=%d' % N)\n", - "plt.plot(Om, 20*np.log10(np.abs(Hr)), 'g-', label=r'recursive N=%d' % M)\n", + "plt.plot(Om, 20 * np.log10(np.abs(Hn)), \"b-\", label=r\"non-recursive N=%d\" % N)\n", + "plt.plot(Om, 20 * np.log10(np.abs(Hr)), \"g-\", label=r\"recursive N=%d\" % M)\n", "plot_tolerance_scheme(Omp, Oms, d_p, a_s)\n", - "plt.title('Magnitude response')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$|H(e^{j \\Omega})|$ in dB')\n", + "plt.title(\"Magnitude response\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$|H(e^{j \\Omega})|$ in dB\")\n", "plt.legend()\n", "plt.axis([0, np.pi, -70, 3])\n", "plt.grid()\n", "# plot phase\n", "plt.figure(figsize=(10, 5))\n", - "plt.plot(Om, np.unwrap(np.angle(Hn)), label=r'non-recursive N=%d' % N)\n", - "plt.plot(Om, np.unwrap(np.angle(Hr)), label=r'recursive N=%d' % M)\n", - "plt.title('Phase response')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\varphi(\\Omega)$ in rad')\n", + "plt.plot(Om, np.unwrap(np.angle(Hn)), label=r\"non-recursive N=%d\" % N)\n", + "plt.plot(Om, np.unwrap(np.angle(Hr)), label=r\"recursive N=%d\" % M)\n", + "plt.title(\"Phase response\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\varphi(\\Omega)$ in rad\")\n", "plt.legend(loc=3)\n", "plt.xlim([0, np.pi])\n", "plt.grid()" @@ -5082,19 +5093,19 @@ "reps = 1000 # number of repetitions for timeit\n", "\n", "# setup environment for timeit\n", - "tsetup = 'import numpy as np; import scipy.signal as sig; from __main__ import h, a, b; x=np.random.normal(size=int(1e5))'\n", + "tsetup = \"import numpy as np; import scipy.signal as sig; from __main__ import h, a, b; x=np.random.normal(size=int(1e5))\"\n", "# non-recursive filter\n", "tn = timeit.timeit('np.convolve(x, h, mode=\"full\")', setup=tsetup, number=reps)\n", "# recursive filter\n", - "tr = timeit.timeit('sig.lfilter(b, a, x)', setup=tsetup, number=reps)\n", + "tr = timeit.timeit(\"sig.lfilter(b, a, x)\", setup=tsetup, number=reps)\n", "\n", "# show the results\n", "plt.figure(figsize=(5, 3))\n", - "plt.bar(1, tn/reps*1000)\n", - "plt.bar(2, tr/reps*1000)\n", - "plt.title('Execution time')\n", - "plt.xticks([1, 2], ('non-recursive', 'recursive'))\n", - "plt.ylabel('time in ms')\n", + "plt.bar(1, tn / reps * 1000)\n", + "plt.bar(2, tr / reps * 1000)\n", + "plt.title(\"Execution time\")\n", + "plt.xticks([1, 2], (\"non-recursive\", \"recursive\"))\n", + "plt.ylabel(\"time in ms\")\n", "plt.grid()" ] }, diff --git a/filter_design/frequency_sampling_method.ipynb b/filter_design/frequency_sampling_method.ipynb index ffc549f..1db4af8 100644 --- a/filter_design/frequency_sampling_method.ipynb +++ b/filter_design/frequency_sampling_method.ipynb @@ -3694,16 +3694,17 @@ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import scipy.signal as sig\n", + "\n", "%matplotlib inline\n", "\n", "N = 32 # length of filter\n", - "Omc = np.pi/3 # corner frequency of low-pass\n", + "Omc = np.pi / 3 # corner frequency of low-pass\n", "\n", "# specify desired frequency response\n", - "Ommu = 2*np.pi/N*np.arange(N)\n", + "Ommu = 2 * np.pi / N * np.arange(N)\n", "Hd = np.zeros(N)\n", "Hd[Ommu <= Omc] = 1\n", - "Hd[Ommu >= (2*np.pi-Omc)] = 1\n", + "Hd[Ommu >= (2 * np.pi - Omc)] = 1\n", "\n", "# compute impulse response of filter\n", "h = np.fft.ifft(Hd)\n", @@ -3713,25 +3714,25 @@ "\n", "# plot impulse response\n", "plt.figure(figsize=(10, 3))\n", - "plt.stem(h )\n", - "plt.title('Impulse response')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$h[k]$')\n", + "plt.stem(h)\n", + "plt.title(\"Impulse response\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$h[k]$\")\n", "# plot transfer functions\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot(Om, np.abs(H), 'b-', label=r'designed $|H(e^{j \\Omega})|$')\n", - "plt.stem(Ommu, np.abs(Hd), 'g', label=r'desired $|H_d[\\mu]|$' )\n", - "plt.plot([0, Omc, Omc], [1, 1, 0], 'r--')\n", - "plt.title('Magnitude response of desired/designed filter')\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.plot(Om, np.abs(H), \"b-\", label=r\"designed $|H(e^{j \\Omega})|$\")\n", + "plt.stem(Ommu, np.abs(Hd), \"g\", label=r\"desired $|H_d[\\mu]|$\")\n", + "plt.plot([0, Omc, Omc], [1, 1, 0], \"r--\")\n", + "plt.title(\"Magnitude response of desired/designed filter\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.legend()\n", "plt.axis([0, np.pi, -0.05, 1.5])\n", "# plot phase\n", "plt.figure(figsize=(10, 3))\n", "plt.plot(Om, np.unwrap(np.angle(H)))\n", - "plt.title('Phase of designed filter')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\varphi(\\Omega)$')\n", + "plt.title(\"Phase of designed filter\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\varphi(\\Omega)$\")\n", "plt.grid()" ] }, @@ -7079,14 +7080,14 @@ ], "source": [ "N = 33 # length of filter\n", - "Omc = np.pi/3 # corner frequency of low-pass\n", + "Omc = np.pi / 3 # corner frequency of low-pass\n", "\n", "# specify desired frequency response\n", - "Ommu = 2*np.pi/N*np.arange(N)\n", + "Ommu = 2 * np.pi / N * np.arange(N)\n", "Hd = np.zeros(N)\n", "Hd[Ommu <= Omc] = 1\n", - "Hd[Ommu >= (2*np.pi-Omc)] = 1\n", - "Hd = Hd * np.exp(-1j*Ommu*(N-1)/2)\n", + "Hd[Ommu >= (2 * np.pi - Omc)] = 1\n", + "Hd = Hd * np.exp(-1j * Ommu * (N - 1) / 2)\n", "\n", "# compute impulse response of filter\n", "h = np.fft.ifft(Hd)\n", @@ -7096,26 +7097,25 @@ "\n", "# plot impulse response\n", "plt.figure(figsize=(10, 3))\n", - "plt.stem(h )\n", - "plt.title('Impulse response')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$h[k]$')\n", + "plt.stem(h)\n", + "plt.title(\"Impulse response\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$h[k]$\")\n", "# plot frequency response\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot(Om, np.abs(H), 'b-', label=r'designed $|H(e^{j \\Omega})|$')\n", - "plt.stem(Ommu, np.abs(Hd), 'g',\n", - " label=r'desired $|H_d[\\mu]|$' )\n", - "plt.plot([0, Omc, Omc], [1, 1, 0], 'r--')\n", - "plt.title('Magnitude response of desired/designed filter')\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.plot(Om, np.abs(H), \"b-\", label=r\"designed $|H(e^{j \\Omega})|$\")\n", + "plt.stem(Ommu, np.abs(Hd), \"g\", label=r\"desired $|H_d[\\mu]|$\")\n", + "plt.plot([0, Omc, Omc], [1, 1, 0], \"r--\")\n", + "plt.title(\"Magnitude response of desired/designed filter\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.legend()\n", "plt.axis([0, np.pi, -0.05, 1.5])\n", "# plot phase\n", "plt.figure(figsize=(10, 3))\n", "plt.plot(Om, np.unwrap(np.angle(H)))\n", - "plt.title('Phase of designed filter')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\varphi(\\Omega)$')\n", + "plt.title(\"Phase of designed filter\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\varphi(\\Omega)$\")\n", "plt.grid()" ] }, @@ -10560,14 +10560,14 @@ "source": [ "N = 33 # length of filter\n", "M = 8192 # number of frequency samples\n", - "Omc = np.pi/2 # corner frequency of low-pass\n", + "Omc = np.pi / 2 # corner frequency of low-pass\n", "\n", "# specify desired frequency response\n", - "Ommu = 2*np.pi/M*np.arange(M)\n", + "Ommu = 2 * np.pi / M * np.arange(M)\n", "Hd = np.zeros(M)\n", "Hd[Ommu <= Omc] = 1\n", - "Hd[Ommu >= (2*np.pi-Omc)] = 1\n", - "Hd = Hd * np.exp(-1j*Ommu*(N-1)/2)\n", + "Hd[Ommu >= (2 * np.pi - Omc)] = 1\n", + "Hd = Hd * np.exp(-1j * Ommu * (N - 1) / 2)\n", "\n", "# compute impulse response of filter\n", "h = np.fft.ifft(Hd)\n", @@ -10578,26 +10578,26 @@ "\n", "# plot impulse response\n", "plt.figure(figsize=(10, 3))\n", - "plt.stem(h )\n", - "plt.title('Impulse response')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$h[k]$')\n", + "plt.stem(h)\n", + "plt.title(\"Impulse response\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$h[k]$\")\n", "# plot frequency response\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot(Om, 20 * np.log10(abs(H)), label='rectangular window')\n", - "plt.plot([0, Omc, Omc], [0, 0, -100], 'r--')\n", - "plt.title('Magnitude response of designed filter')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$|H(e^{j \\Omega})|$ in dB')\n", + "plt.plot(Om, 20 * np.log10(abs(H)), label=\"rectangular window\")\n", + "plt.plot([0, Omc, Omc], [0, 0, -100], \"r--\")\n", + "plt.title(\"Magnitude response of designed filter\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$|H(e^{j \\Omega})|$ in dB\")\n", "plt.axis([0, np.pi, -100, 3])\n", "plt.legend()\n", "plt.grid()\n", "# plot phase\n", "plt.figure(figsize=(10, 3))\n", "plt.plot(Om, np.unwrap(np.angle(H)))\n", - "plt.title('Phase of designed filter')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\varphi(\\Omega)$')\n", + "plt.title(\"Phase of designed filter\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\varphi(\\Omega)$\")\n", "plt.grid()" ] }, diff --git a/filter_design/window_method.ipynb b/filter_design/window_method.ipynb index d7056df..e2049fc 100644 --- a/filter_design/window_method.ipynb +++ b/filter_design/window_method.ipynb @@ -3548,14 +3548,15 @@ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import scipy.signal as sig\n", + "\n", "%matplotlib inline\n", "\n", "N = 32 # length of filter\n", - "Omc = np.pi/2\n", + "Omc = np.pi / 2\n", "\n", "# compute impulse response\n", "k = np.arange(N)\n", - "hd = Omc/np.pi * np.sinc(k*Omc/np.pi)\n", + "hd = Omc / np.pi * np.sinc(k * Omc / np.pi)\n", "# windowing\n", "w = np.ones(N)\n", "h = hd * w\n", @@ -3565,27 +3566,27 @@ "\n", "# plot impulse response\n", "plt.figure(figsize=(10, 3))\n", - "plt.stem(h )\n", - "plt.title('Impulse response')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$h[k]$')\n", + "plt.stem(h)\n", + "plt.title(\"Impulse response\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$h[k]$\")\n", "# plot magnitude responses\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot([0, Omc, Omc], [0, 0, -100], 'r--', label='desired')\n", - "plt.plot(Om, 20 * np.log10(abs(H)), label='window method')\n", - "plt.title('Magnitude response')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$|H(e^{j \\Omega})|$ in dB')\n", + "plt.plot([0, Omc, Omc], [0, 0, -100], \"r--\", label=\"desired\")\n", + "plt.plot(Om, 20 * np.log10(abs(H)), label=\"window method\")\n", + "plt.title(\"Magnitude response\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$|H(e^{j \\Omega})|$ in dB\")\n", "plt.axis([0, np.pi, -20, 3])\n", "plt.grid()\n", "plt.legend()\n", "# plot phase responses\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot([0, Om[-1]], [0, 0], 'r--', label='desired')\n", - "plt.plot(Om, np.unwrap(np.angle(H)), label='window method')\n", - "plt.title('Phase')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\varphi (\\Omega)$ in rad')\n", + "plt.plot([0, Om[-1]], [0, 0], \"r--\", label=\"desired\")\n", + "plt.plot(Om, np.unwrap(np.angle(H)), label=\"window method\")\n", + "plt.title(\"Phase\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\varphi (\\Omega)$ in rad\")\n", "plt.grid()\n", "plt.legend()" ] @@ -7704,11 +7705,11 @@ ], "source": [ "N = 33 # length of filter\n", - "Omc = np.pi/2\n", + "Omc = np.pi / 2\n", "\n", "# compute impulse response\n", "k = np.arange(N)\n", - "hd = Omc/np.pi * np.sinc((k-(N-1)/2)*Omc/np.pi)\n", + "hd = Omc / np.pi * np.sinc((k - (N - 1) / 2) * Omc / np.pi)\n", "# windowing\n", "w1 = np.ones(N)\n", "w2 = np.blackman(N)\n", @@ -7721,28 +7722,28 @@ "\n", "# plot impulse response\n", "plt.figure(figsize=(10, 3))\n", - "plt.stem(h1 )\n", - "plt.title('Impulse response (rectangular window)')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$h[k]$')\n", + "plt.stem(h1)\n", + "plt.title(\"Impulse response (rectangular window)\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$h[k]$\")\n", "# plot magnitude responses\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot([0, Omc, Omc], [0, 0, -300], 'r--', label='desired')\n", - "plt.plot(Om, 20 * np.log10(abs(H1)), label='rectangular window')\n", - "plt.plot(Om, 20 * np.log10(abs(H2)), label='Blackmann window')\n", - "plt.title('Magnitude response')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$|H(e^{j \\Omega})|$ in dB')\n", + "plt.plot([0, Omc, Omc], [0, 0, -300], \"r--\", label=\"desired\")\n", + "plt.plot(Om, 20 * np.log10(abs(H1)), label=\"rectangular window\")\n", + "plt.plot(Om, 20 * np.log10(abs(H2)), label=\"Blackmann window\")\n", + "plt.title(\"Magnitude response\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$|H(e^{j \\Omega})|$ in dB\")\n", "plt.axis([0, np.pi, -120, 3])\n", "plt.legend(loc=3)\n", "plt.grid()\n", "# plot phase responses\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot(Om, np.unwrap(np.angle(H1)), label='rectangular window')\n", - "plt.plot(Om, np.unwrap(np.angle(H2)), label='Blackmann window')\n", - "plt.title('Phase')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\varphi (\\Omega)$ in rad')\n", + "plt.plot(Om, np.unwrap(np.angle(H1)), label=\"rectangular window\")\n", + "plt.plot(Om, np.unwrap(np.angle(H2)), label=\"Blackmann window\")\n", + "plt.title(\"Phase\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\varphi (\\Omega)$ in rad\")\n", "plt.legend(loc=3)\n", "plt.grid()" ] diff --git a/ipython_kernel_config.py b/ipython_kernel_config.py index f562d97..082c599 100644 --- a/ipython_kernel_config.py +++ b/ipython_kernel_config.py @@ -1,3 +1,2 @@ -c.InlineBackend.figure_formats = {'svg', 'pdf'} -c.InlineBackend.rc = {'figure.dpi': 96} - +c.InlineBackend.figure_formats = {"svg", "pdf"} +c.InlineBackend.rc = {"figure.dpi": 96} diff --git a/nonrecursive_filters/fast_convolution.ipynb b/nonrecursive_filters/fast_convolution.ipynb index a85d41b..8ed0855 100644 --- a/nonrecursive_filters/fast_convolution.ipynb +++ b/nonrecursive_filters/fast_convolution.ipynb @@ -165,10 +165,10 @@ "def periodic_summation(x, N):\n", " \"Zero-padding to length N or periodic summation with period N.\"\n", " M = len(x)\n", - " rows = int(np.ceil(M/N))\n", + " rows = int(np.ceil(M / N))\n", "\n", - " if (M < int(N*rows)):\n", - " x = np.pad(x, (0, int(N*rows-M)), 'constant')\n", + " if M < int(N * rows):\n", + " x = np.pad(x, (0, int(N * rows - M)), \"constant\")\n", "\n", " x = np.reshape(x, (rows, N))\n", "\n", @@ -180,7 +180,7 @@ " x = periodic_summation(x, P)\n", " h = periodic_summation(y, P)\n", "\n", - " return np.array([np.dot(np.roll(x[::-1], k+1), h) for k in range(P)], float)\n", + " return np.array([np.dot(np.roll(x[::-1], k + 1), h) for k in range(P)], float)\n", "\n", "\n", "# generate signals\n", @@ -188,32 +188,33 @@ "h = sig.triang(N)\n", "\n", "# linear convolution\n", - "y1 = np.convolve(x, h, 'full')\n", + "y1 = np.convolve(x, h, \"full\")\n", "# periodic convolution\n", "y2 = periodic_convolve(x, h, M)\n", "# linear convolution via periodic convolution\n", - "xp = np.append(x, np.zeros(N-1))\n", - "hp = np.append(h, np.zeros(L-1))\n", - "y3 = periodic_convolve(xp, hp, L+N-1)\n", + "xp = np.append(x, np.zeros(N - 1))\n", + "hp = np.append(h, np.zeros(L - 1))\n", + "y3 = periodic_convolve(xp, hp, L + N - 1)\n", "\n", "\n", "def plot_signal(x):\n", - " '''Plots the signals in stem plot.'''\n", + " \"\"\"Plots the signals in stem plot.\"\"\"\n", " plt.figure(figsize=(10, 3))\n", " plt.stem(x)\n", - " plt.xlabel(r'$k$')\n", - " plt.ylabel(r'$y[k]$')\n", - " plt.axis([0, N+L, 0, 1.1*x.max()])\n", + " plt.xlabel(r\"$k$\")\n", + " plt.ylabel(r\"$y[k]$\")\n", + " plt.axis([0, N + L, 0, 1.1 * x.max()])\n", + "\n", "\n", "# plot results\n", "plot_signal(x)\n", - "plt.title('Signal $x[k]$')\n", + "plt.title(\"Signal $x[k]$\")\n", "plot_signal(y1)\n", - "plt.title('Linear convolution')\n", + "plt.title(\"Linear convolution\")\n", "plot_signal(y2)\n", - "plt.title('Periodic convolution with period M = %d' % M)\n", + "plt.title(\"Periodic convolution with period M = %d\" % M)\n", "plot_signal(y3)\n", - "plt.title('Linear convolution by periodic convolution');" + "plt.title(\"Linear convolution by periodic convolution\");" ] }, { @@ -282,29 +283,29 @@ "source": [ "L = 16 # length of signal x[k]\n", "N = 16 # length of signal h[k]\n", - "M = N+L-1\n", + "M = N + L - 1\n", "\n", "# generate signals\n", "x = np.ones(L)\n", "h = sig.triang(N)\n", "\n", "# linear convolution\n", - "y1 = np.convolve(x, h, 'full')\n", + "y1 = np.convolve(x, h, \"full\")\n", "# fast convolution\n", "y2 = np.fft.ifft(np.fft.fft(x, M) * np.fft.fft(h, M))\n", "\n", "plt.figure(figsize=(10, 6))\n", "plt.subplot(211)\n", "plt.stem(y1)\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$y[k] = x_L[k] * h_N[k]$')\n", - "plt.title('Result of linear convolution')\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$y[k] = x_L[k] * h_N[k]$\")\n", + "plt.title(\"Result of linear convolution\")\n", "\n", "plt.subplot(212)\n", "plt.stem(y1)\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$y[k] = x_L[k] * h_N[k]$')\n", - "plt.title('Result of fast convolution')\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$y[k] = x_L[k] * h_N[k]$\")\n", + "plt.title(\"Result of fast convolution\")\n", "plt.tight_layout()" ] }, @@ -345,23 +346,30 @@ "for N in n:\n", " length = 2**N\n", " # setup environment for timeit\n", - " tsetup = 'import numpy as np; from numpy.fft import rfft, irfft; \\\n", - " x=np.random.randn(%d); h=np.random.randn(%d)' % (length, length)\n", + " tsetup = (\n", + " \"import numpy as np; from numpy.fft import rfft, irfft; \\\n", + " x=np.random.randn(%d); h=np.random.randn(%d)\"\n", + " % (length, length)\n", + " )\n", " # direct convolution\n", " tc = timeit.timeit('np.convolve(x, x, mode=\"full\")', setup=tsetup, number=reps)\n", " # fast convolution\n", - " tf = timeit.timeit('irfft(rfft(x, %d) * rfft(h, %d))' % (2*length, 2*length), setup=tsetup, number=reps)\n", + " tf = timeit.timeit(\n", + " \"irfft(rfft(x, %d) * rfft(h, %d))\" % (2 * length, 2 * length),\n", + " setup=tsetup,\n", + " number=reps,\n", + " )\n", " # speedup by using the fast convolution\n", - " gain[N] = tc/tf\n", + " gain[N] = tc / tf\n", "\n", "# show the results\n", - "plt.figure(figsize = (15, 10))\n", + "plt.figure(figsize=(15, 10))\n", "plt.barh(n, gain, log=True)\n", - "plt.plot([1, 1], [-1, n[-1]+1], 'r-')\n", + "plt.plot([1, 1], [-1, n[-1] + 1], \"r-\")\n", "plt.yticks(n, 2**n)\n", - "plt.xlabel('Gain of fast convolution')\n", - "plt.ylabel('Length of signals')\n", - "plt.title('Comparison between direct/fast convolution')\n", + "plt.xlabel(\"Gain of fast convolution\")\n", + "plt.ylabel(\"Length of signals\")\n", + "plt.title(\"Comparison between direct/fast convolution\")\n", "plt.grid()" ] }, diff --git a/nonrecursive_filters/quantization_effects.ipynb b/nonrecursive_filters/quantization_effects.ipynb index 3f856e7..81186f1 100644 --- a/nonrecursive_filters/quantization_effects.ipynb +++ b/nonrecursive_filters/quantization_effects.ipynb @@ -90,11 +90,11 @@ "w = 8 # wordlength of quantized coefficients\n", "A = 1 # attenuation of filter coefficients\n", "N = 256 # number of coefficients for filter\n", - "Q = 1/(2**(w-1)) # quantization stepsize\n", + "Q = 1 / (2 ** (w - 1)) # quantization stepsize\n", "\n", "\n", "def uniform_midtread_quantizer(x, Q):\n", - " '''Uniform mid-tread quantizer with limiter.'''\n", + " \"\"\"Uniform mid-tread quantizer with limiter.\"\"\"\n", " # limiter\n", " x = np.copy(x)\n", " idx = np.where(x <= -1)\n", @@ -102,28 +102,30 @@ " idx = np.where(x > 1 - Q)\n", " x[idx] = 1 - Q\n", " # linear uniform quantization\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", "\n", " return xQ\n", "\n", "\n", "# design lowpass\n", - "h = A * sig.firwin(N, .5)\n", + "h = A * sig.firwin(N, 0.5)\n", "# quantize coefficients\n", "hQ = uniform_midtread_quantizer(h, Q)\n", "\n", "# plot frequency response\n", "Om, H = sig.freqz(h)\n", "Om, HQ = sig.freqz(hQ)\n", - "Om, E = sig.freqz(hQ-h)\n", + "Om, E = sig.freqz(hQ - h)\n", "\n", "plt.figure(figsize=(10, 4))\n", - "plt.plot(Om, 20*np.log10(np.abs(H)),\n", - " label=r'$| H(e^{j \\Omega}) |$ in dB (Designed)')\n", - "plt.plot(Om, 20*np.log10(np.abs(HQ)),\n", - " label=r'$| H_Q(e^{j \\Omega}) |$ in dB (Quantized coefficients)')\n", - "plt.title('Magnitude of the transfer function w and w/o quantization of coefficients')\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.plot(Om, 20 * np.log10(np.abs(H)), label=r\"$| H(e^{j \\Omega}) |$ in dB (Designed)\")\n", + "plt.plot(\n", + " Om,\n", + " 20 * np.log10(np.abs(HQ)),\n", + " label=r\"$| H_Q(e^{j \\Omega}) |$ in dB (Quantized coefficients)\",\n", + ")\n", + "plt.title(\"Magnitude of the transfer function w and w/o quantization of coefficients\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.axis([0, np.pi, -130, 10])\n", "plt.legend(loc=3)\n", "plt.grid()" @@ -270,12 +272,12 @@ "w = 16 # wordlength of quantized coefficients/operations\n", "N = 32 # number of coefficients for filter\n", "L = 8192 # length of input signal\n", - "Q = 1/(2**(w-1)) # quantization stepsize\n", + "Q = 1 / (2 ** (w - 1)) # quantization stepsize\n", "\n", "\n", "def uniform_midtread_quantizer(x, Q):\n", - " '''Uniform mid-tread quantizer.'''\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " \"\"\"Uniform mid-tread quantizer.\"\"\"\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", "\n", " return xQ\n", "\n", @@ -284,25 +286,25 @@ "h = np.random.uniform(size=N, low=-1, high=1)\n", "hQ = uniform_midtread_quantizer(h, Q)\n", "# input signal\n", - "x = np.random.uniform(size=L, low=-1, high=1-Q)\n", + "x = np.random.uniform(size=L, low=-1, high=1 - Q)\n", "xQ = uniform_midtread_quantizer(x, Q)\n", "# output signal by convolution\n", - "y = np.zeros(L+N-1)\n", - "yQ = np.zeros(L+N-1)\n", + "y = np.zeros(L + N - 1)\n", + "yQ = np.zeros(L + N - 1)\n", "for k in np.arange(L):\n", " for kappa in np.arange(N):\n", - " if (k-kappa) >= 0:\n", - " y[k] += hQ[kappa] * xQ[k-kappa]\n", - " yQ[k] += uniform_midtread_quantizer(hQ[kappa] * xQ[k-kappa], Q)\n", + " if (k - kappa) >= 0:\n", + " y[k] += hQ[kappa] * xQ[k - kappa]\n", + " yQ[k] += uniform_midtread_quantizer(hQ[kappa] * xQ[k - kappa], Q)\n", "\n", "# overall round-off error\n", "e = yQ - y\n", "\n", "# estimate power of round-off error\n", - "sx = 10*np.log10(np.var(e))\n", - "print('Power of overall round-off noise is %f dB' % sx)\n", + "sx = 10 * np.log10(np.var(e))\n", + "print(\"Power of overall round-off noise is %f dB\" % sx)\n", "# estimated PDF of round-off error\n", - "pe, bins = np.histogram(e, bins=50, density=True, range=(-10*Q, 10*Q))\n", + "pe, bins = np.histogram(e, bins=50, density=True, range=(-10 * Q, 10 * Q))\n", "# estimate PSD of round-off error\n", "nf, Pee = sig.welch(e, nperseg=128)\n", "\n", @@ -311,17 +313,17 @@ "plt.figure(figsize=(10, 6))\n", "\n", "plt.subplot(121)\n", - "plt.bar(bins[:-1]/Q, pe*Q, width=20/len(pe))\n", - "plt.title('Estimated PDF of the round-off noise')\n", - "plt.xlabel(r'$\\theta / Q$')\n", - "plt.ylabel(r'$\\hat{p}_x(\\theta) / Q$')\n", - "#plt.axis([-1, 1, 0, 1.2])\n", + "plt.bar(bins[:-1] / Q, pe * Q, width=20 / len(pe))\n", + "plt.title(\"Estimated PDF of the round-off noise\")\n", + "plt.xlabel(r\"$\\theta / Q$\")\n", + "plt.ylabel(r\"$\\hat{p}_x(\\theta) / Q$\")\n", + "# plt.axis([-1, 1, 0, 1.2])\n", "\n", "plt.subplot(122)\n", - "plt.plot(nf*2*np.pi, Pee*6/Q**2/N)\n", - "plt.title('Estimated PSD of the round-off noise')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\hat{\\Phi}_{ee}(e^{j \\Omega}) / \\sigma_e^2$')\n", + "plt.plot(nf * 2 * np.pi, Pee * 6 / Q**2 / N)\n", + "plt.title(\"Estimated PSD of the round-off noise\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\hat{\\Phi}_{ee}(e^{j \\Omega}) / \\sigma_e^2$\")\n", "plt.axis([0, np.pi, 0, 2])\n", "plt.grid()" ] @@ -367,8 +369,8 @@ ], "source": [ "L = 1024 # length of signals x[k]\n", - "A = 1/7 # amplitude of signal\n", - "M = 2*L-1\n", + "A = 1 / 7 # amplitude of signal\n", + "M = 2 * L - 1\n", "\n", "\n", "# generate signals\n", @@ -376,17 +378,17 @@ "y = A**2 * L * sig.triang(M)\n", "\n", "# linear convolution\n", - "y1 = np.convolve(x, x, 'full')\n", + "y1 = np.convolve(x, x, \"full\")\n", "e1 = y - y1\n", "# fast convolution\n", - "y2 = np.fft.irfft(np.fft.rfft(x, M+1) * np.fft.rfft(x, M+1))[0:M]\n", + "y2 = np.fft.irfft(np.fft.rfft(x, M + 1) * np.fft.rfft(x, M + 1))[0:M]\n", "e2 = y - y2\n", "\n", "plt.figure(figsize=(10, 4))\n", - "plt.plot(np.abs(e1), label='conventional convolution')\n", - "plt.plot(np.abs(e2), label='fast convolution')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$|e[k]|$')\n", + "plt.plot(np.abs(e1), label=\"conventional convolution\")\n", + "plt.plot(np.abs(e2), label=\"fast convolution\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$|e[k]|$\")\n", "plt.legend()\n", "plt.grid()" ] diff --git a/nonrecursive_filters/segmented_convolution.ipynb b/nonrecursive_filters/segmented_convolution.ipynb index d34f083..b094c12 100644 --- a/nonrecursive_filters/segmented_convolution.ipynb +++ b/nonrecursive_filters/segmented_convolution.ipynb @@ -161,14 +161,14 @@ "h = sig.triang(N)\n", "\n", "# overlap-add convolution\n", - "xp = np.zeros((L//P, P))\n", - "yp = np.zeros((L//P, N+P-1))\n", - "y = np.zeros(L+P-1)\n", - "for p in range(L//P):\n", - " xp[p, :] = x[p*P:(p+1)*P]\n", - " yp[p, :] = np.convolve(xp[p, :], h, mode='full')\n", - " y[p*P:(p+1)*P+N-1] += yp[p, :]\n", - "y = y[0:N+L]\n", + "xp = np.zeros((L // P, P))\n", + "yp = np.zeros((L // P, N + P - 1))\n", + "y = np.zeros(L + P - 1)\n", + "for p in range(L // P):\n", + " xp[p, :] = x[p * P : (p + 1) * P]\n", + " yp[p, :] = np.convolve(xp[p, :], h, mode=\"full\")\n", + " y[p * P : (p + 1) * P + N - 1] += yp[p, :]\n", + "y = y[0 : N + L]\n", "\n", "\n", "# plot signals\n", @@ -176,36 +176,36 @@ "\n", "plt.subplot(121)\n", "plt.stem(x)\n", - "for n in np.arange(L//P)[::2]:\n", - " plt.axvspan(n*P, (n+1)*P-1, facecolor='g', alpha=0.5)\n", - "plt.title(r'Signal $x[k]$ and segments')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$x[k]$')\n", + "for n in np.arange(L // P)[::2]:\n", + " plt.axvspan(n * P, (n + 1) * P - 1, facecolor=\"g\", alpha=0.5)\n", + "plt.title(r\"Signal $x[k]$ and segments\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$x[k]$\")\n", "plt.axis([0, L, 0, 1])\n", "\n", "plt.subplot(122)\n", "plt.stem(h)\n", - "plt.title(r'Impulse response $h[k]$')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$h[k]$')\n", + "plt.title(r\"Impulse response $h[k]$\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$h[k]$\")\n", "plt.axis([0, L, 0, 1])\n", "\n", - "for p in np.arange(L//P):\n", + "for p in np.arange(L // P):\n", " plt.figure(figsize=(10, 2))\n", "\n", - " plt.stem(np.concatenate((np.zeros(p*P), yp[p, :])))\n", - " plt.title(r'Result of segment $p=%d$' % (p))\n", - " plt.xlabel(r'$k$')\n", - " plt.ylabel(r'$y_%d[k - %d P]$' % (p, p))\n", - " plt.axis([0, L+P, 0, 4])\n", + " plt.stem(np.concatenate((np.zeros(p * P), yp[p, :])))\n", + " plt.title(r\"Result of segment $p=%d$\" % (p))\n", + " plt.xlabel(r\"$k$\")\n", + " plt.ylabel(r\"$y_%d[k - %d P]$\" % (p, p))\n", + " plt.axis([0, L + P, 0, 4])\n", "\n", "\n", "plt.figure(figsize=(10, 2))\n", "plt.stem(y)\n", - "plt.title(r'Result $y[k] = x[k] * h[k]$ by overlap-add algorithm')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$y[k]$')\n", - "plt.axis([0, L+P, 0, 4])" + "plt.title(r\"Result $y[k] = x[k] * h[k]$ by overlap-add algorithm\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$y[k]$\")\n", + "plt.axis([0, L + P, 0, 4])" ] }, { @@ -372,50 +372,49 @@ "h = sig.triang(N)\n", "\n", "# overlap-save convolution\n", - "nseg = (L+N-1)//(P-N+1) + 1\n", - "x = np.concatenate((np.zeros(N-1), x, np.zeros(P)))\n", + "nseg = (L + N - 1) // (P - N + 1) + 1\n", + "x = np.concatenate((np.zeros(N - 1), x, np.zeros(P)))\n", "xp = np.zeros((nseg, P))\n", "yp = np.zeros((nseg, P))\n", - "y = np.zeros(nseg*(P-N+1))\n", + "y = np.zeros(nseg * (P - N + 1))\n", "\n", "for p in range(nseg):\n", - " xp[p, :] = x[p*(P-N+1):p*(P-N+1)+P]\n", + " xp[p, :] = x[p * (P - N + 1) : p * (P - N + 1) + P]\n", " yp[p, :] = np.fft.irfft(np.fft.rfft(xp[p, :]) * np.fft.rfft(h, P))\n", - " y[p*(P-N+1):p*(P-N+1)+P-N+1] = yp[p, N-1:]\n", - "y = y[0:N+L]\n", + " y[p * (P - N + 1) : p * (P - N + 1) + P - N + 1] = yp[p, N - 1 :]\n", + "y = y[0 : N + L]\n", "\n", "plt.figure(figsize=(10, 2))\n", "\n", "plt.subplot(121)\n", - "plt.stem(x[N-1:])\n", - "plt.title(r'Signal $x[k]$')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$x[k]$')\n", + "plt.stem(x[N - 1 :])\n", + "plt.title(r\"Signal $x[k]$\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$x[k]$\")\n", "plt.axis([0, L, 0, 1])\n", "\n", "plt.subplot(122)\n", "plt.stem(h)\n", - "plt.title(r'Impulse response $h[k]$')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$h[k]$')\n", + "plt.title(r\"Impulse response $h[k]$\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$h[k]$\")\n", "plt.axis([0, L, 0, 1])\n", "\n", "for p in np.arange(nseg):\n", " plt.figure(figsize=(10, 2))\n", " plt.stem(yp[p, :])\n", - " plt.axvspan(0, N-1+.5, facecolor='r', alpha=0.5)\n", - " plt.title(\n", - " r'Result of periodic convolution of $x_%d[k]$ and $h_N[k]$' % (p))\n", - " plt.xlabel(r'$k$')\n", - " plt.axis([0, L+P, 0, 4])\n", + " plt.axvspan(0, N - 1 + 0.5, facecolor=\"r\", alpha=0.5)\n", + " plt.title(r\"Result of periodic convolution of $x_%d[k]$ and $h_N[k]$\" % (p))\n", + " plt.xlabel(r\"$k$\")\n", + " plt.axis([0, L + P, 0, 4])\n", "\n", "\n", "plt.figure(figsize=(10, 2))\n", "plt.stem(y)\n", - "plt.title(r'Result $y[k] = x[k] * h[k]$')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$y[k]$')\n", - "plt.axis([0, L+P, 0, 4])" + "plt.title(r\"Result $y[k] = x[k] * h[k]$\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$y[k]$\")\n", + "plt.axis([0, L + P, 0, 4])" ] }, { diff --git a/quantization/introduction.ipynb b/quantization/introduction.ipynb index 06ca8ae..cb70b58 100644 --- a/quantization/introduction.ipynb +++ b/quantization/introduction.ipynb @@ -1589,26 +1589,26 @@ "N = 1024 # length of signal\n", "\n", "# generate signal\n", - "x = np.sin(2*np.pi/N * np.arange(N))\n", + "x = np.sin(2 * np.pi / N * np.arange(N))\n", "# quantize signal\n", "xi = np.round(3 * x)\n", - "xQ = 1/3 * xi\n", + "xQ = 1 / 3 * xi\n", "e = xQ - x\n", "\n", "# plot (quantized) signals\n", "fig, ax1 = plt.subplots(figsize=(10, 4))\n", "ax2 = ax1.twinx()\n", "\n", - "ax1.plot(x, 'r', label=r'signal $x[k]$')\n", - "ax1.plot(xQ, 'b', label=r'quantized signal $x_Q[k]$')\n", - "ax1.plot(e, 'g', label=r'quantization error $e[k]$')\n", - "ax1.set_xlabel('k')\n", - "ax1.set_ylabel(r'$x[k]$, $x_Q[k]$, $e[k]$')\n", + "ax1.plot(x, \"r\", label=r\"signal $x[k]$\")\n", + "ax1.plot(xQ, \"b\", label=r\"quantized signal $x_Q[k]$\")\n", + "ax1.plot(e, \"g\", label=r\"quantization error $e[k]$\")\n", + "ax1.set_xlabel(\"k\")\n", + "ax1.set_ylabel(r\"$x[k]$, $x_Q[k]$, $e[k]$\")\n", "ax1.axis([0, N, -1.2, 1.2])\n", "ax1.legend()\n", "\n", "ax2.set_ylim([-3.6, 3.6])\n", - "ax2.set_ylabel('quantization index')\n", + "ax2.set_ylabel(\"quantization index\")\n", "ax2.grid()" ] }, diff --git a/quantization/linear_uniform_characteristic.ipynb b/quantization/linear_uniform_characteristic.ipynb index a455f41..b085b19 100644 --- a/quantization/linear_uniform_characteristic.ipynb +++ b/quantization/linear_uniform_characteristic.ipynb @@ -1431,37 +1431,37 @@ "import matplotlib.pyplot as plt\n", "\n", "A = 1.2 # amplitude of signal\n", - "Q = 1/10 # quantization stepsize\n", + "Q = 1 / 10 # quantization stepsize\n", "N = 2000 # number of samples\n", "\n", "\n", "def uniform_midtread_quantizer(x, Q):\n", - " '''Uniform mid-tread quantizer with limiter.'''\n", + " \"\"\"Uniform mid-tread quantizer with limiter.\"\"\"\n", " # limiter\n", " x = np.copy(x)\n", " idx = np.where(np.abs(x) >= 1)\n", " x[idx] = np.sign(x[idx])\n", " # linear uniform quantization\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", "\n", " return xQ\n", "\n", "\n", "def plot_signals(x, xQ):\n", - " '''Plot continuous, quantized and error signal.'''\n", + " \"\"\"Plot continuous, quantized and error signal.\"\"\"\n", " e = xQ - x\n", " plt.figure(figsize=(10, 6))\n", - " plt.plot(x, label=r'signal $x[k]$')\n", - " plt.plot(xQ, label=r'quantized signal $x_Q[k]$')\n", - " plt.plot(e, label=r'quantization error $e[k]$')\n", - " plt.xlabel(r'$k$')\n", - " plt.axis([0, N, -1.1*A, 1.1*A])\n", + " plt.plot(x, label=r\"signal $x[k]$\")\n", + " plt.plot(xQ, label=r\"quantized signal $x_Q[k]$\")\n", + " plt.plot(e, label=r\"quantization error $e[k]$\")\n", + " plt.xlabel(r\"$k$\")\n", + " plt.axis([0, N, -1.1 * A, 1.1 * A])\n", " plt.legend()\n", " plt.grid()\n", "\n", "\n", "# generate signal\n", - "x = A * np.sin(2*np.pi/N * np.arange(N))\n", + "x = A * np.sin(2 * np.pi / N * np.arange(N))\n", "# quantize signal\n", "xQ = uniform_midtread_quantizer(x, Q)\n", "# plot signals\n", @@ -2879,24 +2879,24 @@ ], "source": [ "A = 1.2 # amplitude of signal\n", - "Q = 1/10 # quantization stepsize\n", + "Q = 1 / 10 # quantization stepsize\n", "N = 2000 # number of samples\n", "\n", "\n", "def uniform_midrise_quantizer(x, Q):\n", - " '''Uniform mid-rise quantizer with limiter.'''\n", + " \"\"\"Uniform mid-rise quantizer with limiter.\"\"\"\n", " # limiter\n", " x = np.copy(x)\n", " idx = np.where(np.abs(x) >= 1)\n", " x[idx] = np.sign(x[idx])\n", " # linear uniform quantization\n", - " xQ = Q * (np.floor(x/Q) + .5)\n", + " xQ = Q * (np.floor(x / Q) + 0.5)\n", "\n", " return xQ\n", "\n", "\n", "# generate signal\n", - "x = A * np.sin(2*np.pi/N * np.arange(N))\n", + "x = A * np.sin(2 * np.pi / N * np.arange(N))\n", "# quantize signal\n", "xQ = uniform_midrise_quantizer(x, Q)\n", "# plot signals\n", diff --git a/quantization/linear_uniform_quantization_error.ipynb b/quantization/linear_uniform_quantization_error.ipynb index 6981845..f3e6b9b 100644 --- a/quantization/linear_uniform_quantization_error.ipynb +++ b/quantization/linear_uniform_quantization_error.ipynb @@ -3662,7 +3662,7 @@ "\n", "\n", "def uniform_midtread_quantizer(x, Q):\n", - " '''Uniform mid-tread quantizer with limiter.'''\n", + " \"\"\"Uniform mid-tread quantizer with limiter.\"\"\"\n", " # limiter\n", " x = np.copy(x)\n", " idx = np.where(x <= -1)\n", @@ -3670,58 +3670,58 @@ " idx = np.where(x > 1 - Q)\n", " x[idx] = 1 - Q\n", " # linear uniform quantization\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", "\n", " return xQ\n", "\n", "\n", "def analyze_quantizer(x, e):\n", - " '''Compute and plot PDF, CCF and PSD of quantizer.'''\n", + " \"\"\"Compute and plot PDF, CCF and PSD of quantizer.\"\"\"\n", " # estimated PDF of error signal\n", " pe, bins = np.histogram(e, bins=20, density=True, range=(-Q, Q))\n", " # estimate cross-correlation between input and error\n", - " ccf = 1/len(x) * np.correlate(x, e, mode='full')\n", + " ccf = 1 / len(x) * np.correlate(x, e, mode=\"full\")\n", " # estimate PSD of error signal\n", " nf, Pee = sig.welch(e, nperseg=128)\n", " # estimate SNR\n", - " SNR = 10*np.log10((np.var(x)/np.var(e)))\n", - " print('SNR = %f in dB' % SNR)\n", + " SNR = 10 * np.log10((np.var(x) / np.var(e)))\n", + " print(\"SNR = %f in dB\" % SNR)\n", "\n", " # plot statistical properties of error signal\n", " plt.figure(figsize=(9, 4))\n", "\n", " plt.subplot(121)\n", - " plt.bar((bins[:-1] + bins[1:])/(2*Q), pe*Q, width=2/len(pe))\n", - " plt.title('Estimated histogram of quantization error')\n", - " plt.xlabel(r'$\\theta / Q$')\n", - " plt.ylabel(r'$\\hat{p}_x(\\theta) / Q$')\n", + " plt.bar((bins[:-1] + bins[1:]) / (2 * Q), pe * Q, width=2 / len(pe))\n", + " plt.title(\"Estimated histogram of quantization error\")\n", + " plt.xlabel(r\"$\\theta / Q$\")\n", + " plt.ylabel(r\"$\\hat{p}_x(\\theta) / Q$\")\n", " plt.axis([-1, 1, 0, 1.2])\n", " plt.grid()\n", "\n", " plt.subplot(122)\n", - " plt.plot(nf*2*np.pi, Pee*6/Q**2)\n", - " plt.title('Estimated PSD of quantization error')\n", - " plt.xlabel(r'$\\Omega$')\n", - " plt.ylabel(r'$\\hat{\\Phi}_{ee}(e^{j \\Omega}) / \\sigma_e^2$')\n", + " plt.plot(nf * 2 * np.pi, Pee * 6 / Q**2)\n", + " plt.title(\"Estimated PSD of quantization error\")\n", + " plt.xlabel(r\"$\\Omega$\")\n", + " plt.ylabel(r\"$\\hat{\\Phi}_{ee}(e^{j \\Omega}) / \\sigma_e^2$\")\n", " plt.axis([0, np.pi, 0, 2])\n", " plt.grid()\n", " plt.tight_layout()\n", "\n", " plt.figure(figsize=(10, 6))\n", - " ccf = ccf[N-K-1:N+K-1]\n", - " kappa = np.arange(-len(ccf)//2, len(ccf)//2)\n", - " plt.stem(kappa, ccf )\n", - " plt.title('Cross-correlation function between input signal and error')\n", - " plt.xlabel(r'$\\kappa$')\n", - " plt.ylabel(r'$\\varphi_{xe}[\\kappa]$')\n", + " ccf = ccf[N - K - 1 : N + K - 1]\n", + " kappa = np.arange(-len(ccf) // 2, len(ccf) // 2)\n", + " plt.stem(kappa, ccf)\n", + " plt.title(\"Cross-correlation function between input signal and error\")\n", + " plt.xlabel(r\"$\\kappa$\")\n", + " plt.ylabel(r\"$\\varphi_{xe}[\\kappa]$\")\n", " plt.grid()\n", "\n", "\n", "# quantization step\n", - "Q = 1/(2**(w-1))\n", + "Q = 1 / (2 ** (w - 1))\n", "# compute input signal\n", "np.random.seed(1)\n", - "x = np.random.uniform(size=N, low=xmin, high=(-xmin-Q))\n", + "x = np.random.uniform(size=N, low=xmin, high=(-xmin - Q))\n", "# quantize signal\n", "xQ = uniform_midtread_quantizer(x, Q)\n", "e = xQ - x\n", @@ -4912,39 +4912,42 @@ "\n", "\n", "def compute_SNR(a):\n", - " '''Numerically evaluate SNR of a quantized normally distributed signal.'''\n", + " \"\"\"Numerically evaluate SNR of a quantized normally distributed signal.\"\"\"\n", " # compute input signal\n", " x = np.random.normal(size=N, scale=a)\n", " # quantize signal\n", " xQ = uniform_midtread_quantizer(x, Q)\n", " e = xQ - x\n", " # compute SNR\n", - " SNR = 10*np.log10((np.var(x)/np.var(e)))\n", + " SNR = 10 * np.log10((np.var(x) / np.var(e)))\n", "\n", " return SNR\n", "\n", "\n", "def plot_SNR(A, SNR):\n", - " '''Plot SNR.'''\n", + " \"\"\"Plot SNR.\"\"\"\n", " # plot results\n", " plt.figure(figsize=(8, 4))\n", - " plt.plot(20*np.log10(A), SNR)\n", - " plt.xlabel(r'RMS level $\\sigma_x / x_\\mathrm{min}$ in dB')\n", - " plt.ylabel('SNR in dB')\n", + " plt.plot(20 * np.log10(A), SNR)\n", + " plt.xlabel(r\"RMS level $\\sigma_x / x_\\mathrm{min}$ in dB\")\n", + " plt.ylabel(\"SNR in dB\")\n", " plt.grid()\n", "\n", "\n", "# quantization step\n", - "Q = 1/(2**(w-1))\n", + "Q = 1 / (2 ** (w - 1))\n", "# compute SNR for given RMS levels\n", "SNR = [compute_SNR(a) for a in A]\n", "# plot results\n", "plot_SNR(A, SNR)\n", "# find maximum SNR\n", "Amax = A[np.argmax(SNR)]\n", - "Pc = 1 + erf(-1/(np.sqrt(2)*Amax))\n", - "print(r'Maximum SNR = {0:2.3f} dB for A = {1:2.1f} dB with clipping probability {2:2.1e}'\n", - " .format(np.array(SNR).max(), 20*np.log10(Amax), Pc))" + "Pc = 1 + erf(-1 / (np.sqrt(2) * Amax))\n", + "print(\n", + " r\"Maximum SNR = {0:2.3f} dB for A = {1:2.1f} dB with clipping probability {2:2.1e}\".format(\n", + " np.array(SNR).max(), 20 * np.log10(Amax), Pc\n", + " )\n", + ")" ] }, { @@ -6206,29 +6209,32 @@ "\n", "\n", "def compute_SNR(a):\n", - " '''Numerically evaluate SNR of a quantized Laplace distributed signal.'''\n", + " \"\"\"Numerically evaluate SNR of a quantized Laplace distributed signal.\"\"\"\n", " # compute input signal\n", - " x = np.random.laplace(size=N, scale=a/np.sqrt(2))\n", + " x = np.random.laplace(size=N, scale=a / np.sqrt(2))\n", " # quantize signal\n", " xQ = uniform_midtread_quantizer(x, Q)\n", " e = xQ - x\n", " # compute SNR\n", - " SNR = 10*np.log10((np.var(x)/np.var(e)))\n", + " SNR = 10 * np.log10((np.var(x) / np.var(e)))\n", "\n", " return SNR\n", "\n", "\n", "# quantization step\n", - "Q = 1/(2**(w-1))\n", + "Q = 1 / (2 ** (w - 1))\n", "# compute SNR for given RMS levels\n", "SNR = [compute_SNR(a) for a in A]\n", "# plot results\n", "plot_SNR(A, SNR)\n", "# find maximum SNR\n", "Amax = A[np.argmax(SNR)]\n", - "Pc = np.exp(-np.sqrt(2)/Amax)\n", - "print(r'Maximum SNR = {0:2.3f} dB for A = {1:2.1f} dB with clipping probability {2:2.1e}'\n", - " .format(np.array(SNR).max(), 20*np.log10(Amax), Pc))" + "Pc = np.exp(-np.sqrt(2) / Amax)\n", + "print(\n", + " r\"Maximum SNR = {0:2.3f} dB for A = {1:2.1f} dB with clipping probability {2:2.1e}\".format(\n", + " np.array(SNR).max(), 20 * np.log10(Amax), Pc\n", + " )\n", + ")" ] }, { diff --git a/quantization/noise_shaping.ipynb b/quantization/noise_shaping.ipynb index 269ff18..ced30db 100644 --- a/quantization/noise_shaping.ipynb +++ b/quantization/noise_shaping.ipynb @@ -1614,7 +1614,7 @@ "\n", "\n", "def uniform_midtread_quantizer_w_ns(x, Q):\n", - " '''Uniform mid-tread quantizer with noise shaping.'''\n", + " \"\"\"Uniform mid-tread quantizer with noise shaping.\"\"\"\n", " # limiter\n", " x = np.copy(x)\n", " idx = np.where(x <= -1)\n", @@ -1622,7 +1622,7 @@ " idx = np.where(x > 1 - Q)\n", " x[idx] = 1 - Q\n", " # linear uniform quantization with noise shaping\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", " e = xQ - x\n", " xQ = xQ - np.concatenate(([0], e[0:-1]))\n", "\n", @@ -1630,30 +1630,30 @@ "\n", "\n", "# quantization step\n", - "Q = 1/(2**(w-1))\n", + "Q = 1 / (2 ** (w - 1))\n", "# compute input signal\n", "np.random.seed(5)\n", - "x = np.random.uniform(size=N, low=xmin, high=(-xmin-Q))\n", + "x = np.random.uniform(size=N, low=xmin, high=(-xmin - Q))\n", "# quantize signal\n", "xQ = uniform_midtread_quantizer_w_ns(x, Q)\n", "e = xQ - x[1:]\n", "# estimate PSD of error signal\n", "nf, Pee = sig.welch(e, nperseg=64)\n", "# estimate SNR\n", - "SNR = 10*np.log10((np.var(x)/np.var(e)))\n", - "print('SNR = {:2.1f} dB'.format(SNR))\n", + "SNR = 10 * np.log10((np.var(x) / np.var(e)))\n", + "print(\"SNR = {:2.1f} dB\".format(SNR))\n", "\n", "\n", "plt.figure(figsize=(10, 5))\n", - "Om = nf*2*np.pi\n", - "plt.plot(Om, Pee*6/Q**2, label='estimated PSD')\n", - "plt.plot(Om, np.abs(1 - np.exp(-1j*Om))**2, label='theoretic PSD')\n", - "plt.plot(Om, np.ones(Om.shape), label='PSD w/o noise shaping')\n", - "plt.title('PSD of quantization error')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\hat{\\Phi}_{e_H e_H}(e^{j \\Omega}) / \\sigma_e^2$')\n", + "Om = nf * 2 * np.pi\n", + "plt.plot(Om, Pee * 6 / Q**2, label=\"estimated PSD\")\n", + "plt.plot(Om, np.abs(1 - np.exp(-1j * Om)) ** 2, label=\"theoretic PSD\")\n", + "plt.plot(Om, np.ones(Om.shape), label=\"PSD w/o noise shaping\")\n", + "plt.title(\"PSD of quantization error\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\hat{\\Phi}_{e_H e_H}(e^{j \\Omega}) / \\sigma_e^2$\")\n", "plt.axis([0, np.pi, 0, 4.5])\n", - "plt.legend(loc='upper left')\n", + "plt.legend(loc=\"upper left\")\n", "plt.grid()" ] }, diff --git a/quantization/nonlinear_quantization_speech_signal.ipynb b/quantization/nonlinear_quantization_speech_signal.ipynb index ef9d1b3..ae347ad 100644 --- a/quantization/nonlinear_quantization_speech_signal.ipynb +++ b/quantization/nonlinear_quantization_speech_signal.ipynb @@ -34,33 +34,33 @@ "\n", "\n", "def A_law_compander(x):\n", - " '''Compand signal according to the A-law charateristic.'''\n", + " \"\"\"Compand signal according to the A-law charateristic.\"\"\"\n", " A = 87.6\n", " y = np.zeros_like(x)\n", - " idx = np.where(np.abs(x) < 1/A)\n", - " y[idx] = A*np.abs(x[idx]) / (1 + np.log(A))\n", - " idx = np.where(np.abs(x) >= 1/A)\n", - " y[idx] = (1 + np.log(A*np.abs(x[idx]))) / (1 + np.log(A))\n", + " idx = np.where(np.abs(x) < 1 / A)\n", + " y[idx] = A * np.abs(x[idx]) / (1 + np.log(A))\n", + " idx = np.where(np.abs(x) >= 1 / A)\n", + " y[idx] = (1 + np.log(A * np.abs(x[idx]))) / (1 + np.log(A))\n", "\n", - " return np.sign(x)*y\n", + " return np.sign(x) * y\n", "\n", "\n", "def A_law_expander(y):\n", - " '''Expand signal according to the A-law charateristic.'''\n", + " \"\"\"Expand signal according to the A-law charateristic.\"\"\"\n", " A = 87.6\n", " x = np.zeros_like(y)\n", - " idx = np.where(np.abs(y) < 1/(1+np.log(A)))\n", - " x[idx] = np.abs(y[idx])*(1+np.log(A)) / A\n", - " idx = np.where(np.abs(y) >= 1/(1+np.log(A)))\n", - " x[idx] = np.exp(np.abs(y[idx])*(1+np.log(A))-1)/A\n", + " idx = np.where(np.abs(y) < 1 / (1 + np.log(A)))\n", + " x[idx] = np.abs(y[idx]) * (1 + np.log(A)) / A\n", + " idx = np.where(np.abs(y) >= 1 / (1 + np.log(A)))\n", + " x[idx] = np.exp(np.abs(y[idx]) * (1 + np.log(A)) - 1) / A\n", "\n", - " return np.sign(y)*x\n", + " return np.sign(y) * x\n", "\n", "\n", "def uniform_midtread_quantizer(x, w):\n", - " '''Uniform mid-tread quantizer with limiter.'''\n", + " \"\"\"Uniform mid-tread quantizer with limiter.\"\"\"\n", " # quantization step\n", - " Q = 1/(2**(w-1))\n", + " Q = 1 / (2 ** (w - 1))\n", " # limiter\n", " x = np.copy(x)\n", " idx = np.where(x <= -1)\n", @@ -68,19 +68,19 @@ " idx = np.where(x > 1 - Q)\n", " x[idx] = 1 - Q\n", " # linear uniform quantization\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", "\n", " return xQ\n", "\n", "\n", "def evaluate_requantization(x, xQ):\n", - " '''Compute error and SNR of requantization.'''\n", + " \"\"\"Compute error and SNR of requantization.\"\"\"\n", " e = xQ - x\n", " # SNR\n", - " SNR = 10*np.log10(np.var(x)/np.var(e))\n", - " print('SNR: {:2.1f} dB'.format(SNR))\n", + " SNR = 10 * np.log10(np.var(x) / np.var(e))\n", + " print(\"SNR: {:2.1f} dB\".format(SNR))\n", " # normalize error\n", - " e = .2 * e / np.max(np.abs(e))\n", + " e = 0.2 * e / np.max(np.abs(e))\n", " return e" ] }, @@ -2537,21 +2537,21 @@ "plt.figure(figsize=(10, 4))\n", "\n", "plt.subplot(121)\n", - "plt.plot(x, yQ4, label=r'$w=4$ bit')\n", - "plt.plot(x, yQ8, label=r'$w=8$ bit')\n", - "plt.title('Compansion and linear quantization')\n", - "plt.xlabel(r'$x$')\n", - "plt.ylabel(r'$x_Q$')\n", + "plt.plot(x, yQ4, label=r\"$w=4$ bit\")\n", + "plt.plot(x, yQ8, label=r\"$w=8$ bit\")\n", + "plt.title(\"Compansion and linear quantization\")\n", + "plt.xlabel(r\"$x$\")\n", + "plt.ylabel(r\"$x_Q$\")\n", "plt.legend(loc=2)\n", "plt.axis([-1.1, 1.1, -1.1, 1.1])\n", "plt.grid()\n", "\n", "plt.subplot(122)\n", - "plt.plot(x, xQ4, label=r'$w=4$ bit')\n", - "plt.plot(x, xQ8, label=r'$w=8$ bit')\n", - "plt.title('Overall')\n", - "plt.xlabel(r'$x$')\n", - "plt.ylabel(r'$x_Q$')\n", + "plt.plot(x, xQ4, label=r\"$w=4$ bit\")\n", + "plt.plot(x, xQ8, label=r\"$w=8$ bit\")\n", + "plt.title(\"Overall\")\n", + "plt.xlabel(r\"$x$\")\n", + "plt.ylabel(r\"$x_Q$\")\n", "plt.legend(loc=2)\n", "plt.axis([-1.1, 1.1, -1.1, 1.1])\n", "plt.grid()" @@ -3851,35 +3851,35 @@ ], "source": [ "w = 8 # wordlength of the quantizer\n", - "A = np.logspace(-50/20, -10/20, num=500) # relative RMS levels\n", + "A = np.logspace(-50 / 20, -10 / 20, num=500) # relative RMS levels\n", "N = int(1e6) # number of samples\n", "np.random.seed(1)\n", "\n", "\n", "def compute_SNR(a):\n", " # compute input signal\n", - " x = np.random.laplace(size=N, scale=a/np.sqrt(2))\n", + " x = np.random.laplace(size=N, scale=a / np.sqrt(2))\n", " # quantize signal\n", " y = A_law_compander(x)\n", " yQ = uniform_midtread_quantizer(y, 8)\n", " xQ = A_law_expander(yQ)\n", " e = xQ - x\n", " # compute SNR\n", - " SNR = 10*np.log10((np.var(x)/np.var(e)))\n", + " SNR = 10 * np.log10((np.var(x) / np.var(e)))\n", "\n", " return SNR\n", "\n", "\n", "# quantization step\n", - "Q = 1/(2**(w-1))\n", + "Q = 1 / (2 ** (w - 1))\n", "# compute SNR for given RMS levels\n", "SNR = [compute_SNR(a) for a in A]\n", "\n", "# plot results\n", "plt.figure(figsize=(8, 4))\n", - "plt.plot(20*np.log10(A), SNR)\n", - "plt.xlabel(r'RMS level $\\sigma_x / x_\\mathrm{min}$ in dB')\n", - "plt.ylabel('SNR in dB')\n", + "plt.plot(20 * np.log10(A), SNR)\n", + "plt.xlabel(r\"RMS level $\\sigma_x / x_\\mathrm{min}$ in dB\")\n", + "plt.ylabel(\"SNR in dB\")\n", "plt.grid()" ] }, @@ -3908,22 +3908,22 @@ ], "source": [ "# load speech sample\n", - "x, fs = sf.read('../data/speech_8k.wav')\n", - "x = x/np.max(np.abs(x))\n", + "x, fs = sf.read(\"../data/speech_8k.wav\")\n", + "x = x / np.max(np.abs(x))\n", "\n", "# linear quantization\n", "xQ = uniform_midtread_quantizer(x, 8)\n", "e = evaluate_requantization(x, xQ)\n", - "sf.write('speech_8k_8bit.wav', xQ, fs)\n", - "sf.write('speech_8k_8bit_error.wav', e, fs)\n", + "sf.write(\"speech_8k_8bit.wav\", xQ, fs)\n", + "sf.write(\"speech_8k_8bit_error.wav\", e, fs)\n", "\n", "# A-law quantization\n", "y = A_law_compander(x)\n", "yQ = uniform_midtread_quantizer(y, 8)\n", "xQ = A_law_expander(yQ)\n", "e = evaluate_requantization(x, xQ)\n", - "sf.write('speech_Alaw_8k_8bit.wav', xQ, fs)\n", - "sf.write('speech_Alaw_8k_8bit_error.wav', e, fs)" + "sf.write(\"speech_Alaw_8k_8bit.wav\", xQ, fs)\n", + "sf.write(\"speech_Alaw_8k_8bit_error.wav\", e, fs)" ] }, { diff --git a/quantization/oversampling.ipynb b/quantization/oversampling.ipynb index 0edae83..e9801c3 100644 --- a/quantization/oversampling.ipynb +++ b/quantization/oversampling.ipynb @@ -1239,15 +1239,15 @@ "import scipy.signal as sig\n", "\n", "w = 16 # wordlength of the quantized signal\n", - "L = 2**np.arange(1, 10) # oversampling factors\n", + "L = 2 ** np.arange(1, 10) # oversampling factors\n", "\n", "N = 8192 # length of signal\n", - "Om0 = 100*2*np.pi/N # frequency of harmonic signal\n", - "Q = 1/(2**(w-1)) # quantization step\n", + "Om0 = 100 * 2 * np.pi / N # frequency of harmonic signal\n", + "Q = 1 / (2 ** (w - 1)) # quantization step\n", "\n", "\n", "def uniform_midtread_quantizer(x, Q):\n", - " '''Uniform mid-tread qantizer with limiter.'''\n", + " \"\"\"Uniform mid-tread qantizer with limiter.\"\"\"\n", " # limiter\n", " x = np.copy(x)\n", " idx = np.where(x <= -1)\n", @@ -1255,15 +1255,15 @@ " idx = np.where(x > 1 - Q)\n", " x[idx] = 1 - Q\n", " # linear uniform quantization\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", "\n", " return xQ\n", "\n", "\n", "def SNR_oversampled_ADC(L):\n", - " '''Estimate SNR of oversampled analog-to-digital converter.'''\n", - " x = (1-Q)*np.cos(Om0*np.arange(N))\n", - " xu = (1-Q)*np.cos(Om0*np.arange(N*L)/L)\n", + " \"\"\"Estimate SNR of oversampled analog-to-digital converter.\"\"\"\n", + " x = (1 - Q) * np.cos(Om0 * np.arange(N))\n", + " xu = (1 - Q) * np.cos(Om0 * np.arange(N * L) / L)\n", " # quantize signal\n", " xQu = uniform_midtread_quantizer(xu, Q)\n", " # low-pass filtering and decimation\n", @@ -1271,7 +1271,7 @@ " # estimate SNR\n", " e = xQ - x\n", "\n", - " return 10*np.log10((np.var(x)/np.var(e)))\n", + " return 10 * np.log10((np.var(x) / np.var(e)))\n", "\n", "\n", "# compute SNR for oversampled ADC\n", @@ -1279,11 +1279,11 @@ "\n", "# plot result\n", "plt.figure(figsize=(10, 4))\n", - "plt.semilogx(L, SNR, label='SNR with oversampling')\n", - "plt.plot(L, (6.02*w+1.76)*np.ones(L.shape), label='SNR w/o oversampling')\n", - "plt.xlabel(r'oversampling factor $L$')\n", - "plt.ylabel(r'SNR in dB')\n", - "plt.legend(loc='upper left')\n", + "plt.semilogx(L, SNR, label=\"SNR with oversampling\")\n", + "plt.plot(L, (6.02 * w + 1.76) * np.ones(L.shape), label=\"SNR w/o oversampling\")\n", + "plt.xlabel(r\"oversampling factor $L$\")\n", + "plt.ylabel(r\"SNR in dB\")\n", + "plt.legend(loc=\"upper left\")\n", "plt.grid()" ] }, diff --git a/quantization/requantization_speech_signal.ipynb b/quantization/requantization_speech_signal.ipynb index 6295a3a..c5e3591 100644 --- a/quantization/requantization_speech_signal.ipynb +++ b/quantization/requantization_speech_signal.ipynb @@ -34,9 +34,9 @@ "\n", "\n", "def uniform_midtread_quantizer(x, w):\n", - " '''Uniform mid-tread quantizer with limiter.'''\n", + " \"\"\"Uniform mid-tread quantizer with limiter.\"\"\"\n", " # quantization step\n", - " Q = 1/(2**(w-1))\n", + " Q = 1 / (2 ** (w - 1))\n", " # limiter\n", " x = np.copy(x)\n", " idx = np.where(x <= -1)\n", @@ -44,34 +44,34 @@ " idx = np.where(x > 1 - Q)\n", " x[idx] = 1 - Q\n", " # linear uniform quantization\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", "\n", " return xQ\n", "\n", "\n", "def evaluate_requantization(x, xQ):\n", - " '''Evaluate rquantization by plotting signals and computing SNR.'''\n", + " \"\"\"Evaluate rquantization by plotting signals and computing SNR.\"\"\"\n", " e = xQ - x\n", " # SNR\n", - " SNR = 10*np.log10(np.var(x)/np.var(e))\n", - " print('SNR: {:2.1f} dB'.format(SNR))\n", + " SNR = 10 * np.log10(np.var(x) / np.var(e))\n", + " print(\"SNR: {:2.1f} dB\".format(SNR))\n", " # plot signals\n", " plt.figure(figsize=(10, 4))\n", - " plt.plot(x[idx:idx+100], label=r'signal $x[k]$')\n", - " plt.plot(xQ[idx:idx+100], label=r'requantized signal $x_Q[k]$')\n", - " plt.plot(e[idx:idx+100], label=r'quantization error $e[k]$')\n", - " plt.xlabel(r'sample index $k$')\n", + " plt.plot(x[idx : idx + 100], label=r\"signal $x[k]$\")\n", + " plt.plot(xQ[idx : idx + 100], label=r\"requantized signal $x_Q[k]$\")\n", + " plt.plot(e[idx : idx + 100], label=r\"quantization error $e[k]$\")\n", + " plt.xlabel(r\"sample index $k$\")\n", " plt.grid()\n", " plt.legend()\n", " # normalize error\n", - " e = .2 * e / np.max(np.abs(e))\n", + " e = 0.2 * e / np.max(np.abs(e))\n", " return e\n", "\n", "\n", "# load speech sample\n", - "x, fs = sf.read('../data/speech.wav')\n", + "x, fs = sf.read(\"../data/speech.wav\")\n", "# normalize sample\n", - "x = x/np.max(np.abs(x))" + "x = x / np.max(np.abs(x))" ] }, { @@ -1578,8 +1578,8 @@ "source": [ "xQ = uniform_midtread_quantizer(x, 8)\n", "e = evaluate_requantization(x, xQ)\n", - "sf.write('speech_8bit.wav', xQ, fs)\n", - "sf.write('speech_8bit_error.wav', e, fs)" + "sf.write(\"speech_8bit.wav\", xQ, fs)\n", + "sf.write(\"speech_8bit_error.wav\", e, fs)" ] }, { @@ -3090,8 +3090,8 @@ "source": [ "xQ = uniform_midtread_quantizer(x, 6)\n", "e = evaluate_requantization(x, xQ)\n", - "sf.write('speech_6bit.wav', xQ, fs)\n", - "sf.write('speech_6bit_error.wav', e, fs)" + "sf.write(\"speech_6bit.wav\", xQ, fs)\n", + "sf.write(\"speech_6bit_error.wav\", e, fs)" ] }, { @@ -4645,8 +4645,8 @@ "source": [ "xQ = uniform_midtread_quantizer(x, 4)\n", "e = evaluate_requantization(x, xQ)\n", - "sf.write('speech_4bit.wav', xQ, fs)\n", - "sf.write('speech_4bit_error.wav', e, fs)" + "sf.write(\"speech_4bit.wav\", xQ, fs)\n", + "sf.write(\"speech_4bit_error.wav\", e, fs)" ] }, { @@ -6084,8 +6084,8 @@ "source": [ "xQ = uniform_midtread_quantizer(x, 2)\n", "e = evaluate_requantization(x, xQ)\n", - "sf.write('speech_2bit.wav', xQ, fs)\n", - "sf.write('speech_2bit_error.wav', e, fs)" + "sf.write(\"speech_2bit.wav\", xQ, fs)\n", + "sf.write(\"speech_2bit_error.wav\", e, fs)" ] }, { diff --git a/random_signals/correlation_functions.ipynb b/random_signals/correlation_functions.ipynb index 332eb9d..2154e35 100644 --- a/random_signals/correlation_functions.ipynb +++ b/random_signals/correlation_functions.ipynb @@ -159,26 +159,26 @@ "# generate periodic random signal\n", "np.random.seed(1)\n", "x0 = np.random.normal(size=P)\n", - "x = np.tile(x0, N//P)\n", + "x = np.tile(x0, N // P)\n", "\n", "# compute and truncate ACF\n", - "acf = 1/len(x) * np.correlate(x, x, mode='full')\n", - "acf = acf[(len(x)-1)-(K-1):(len(x)-1)+K]\n", - "kappa = np.arange(-(K-1), K)\n", + "acf = 1 / len(x) * np.correlate(x, x, mode=\"full\")\n", + "acf = acf[(len(x) - 1) - (K - 1) : (len(x) - 1) + K]\n", + "kappa = np.arange(-(K - 1), K)\n", "\n", "# plot signal and its ACF\n", "plt.figure(figsize=(10, 4))\n", - "plt.stem(x[:2*K], basefmt='C0:')\n", - "plt.xlim(0, 2*K)\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$x[k]$')\n", + "plt.stem(x[: 2 * K], basefmt=\"C0:\")\n", + "plt.xlim(0, 2 * K)\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$x[k]$\")\n", "plt.grid()\n", "\n", "plt.figure(figsize=(10, 4))\n", - "plt.stem(kappa, acf, basefmt='C0:')\n", - "plt.xlabel(r'$\\kappa$')\n", - "plt.ylabel(r'$\\hat{\\varphi}_{xx}[\\kappa]$')\n", - "plt.axis([-K, K, 1.1*min(acf), 1.1*max(acf)])\n", + "plt.stem(kappa, acf, basefmt=\"C0:\")\n", + "plt.xlabel(r\"$\\kappa$\")\n", + "plt.ylabel(r\"$\\hat{\\varphi}_{xx}[\\kappa]$\")\n", + "plt.axis([-K, K, 1.1 * min(acf), 1.1 * max(acf)])\n", "plt.grid()" ] }, @@ -228,21 +228,21 @@ "K = 200 # upper/lower limit for lag in ACF\n", "\n", "# read audio file\n", - "fs, x = wavfile.read('../data/vocal_o_8k.wav')\n", + "fs, x = wavfile.read(\"../data/vocal_o_8k.wav\")\n", "# wav stored as 16Bit integer, convert it to float\n", - "x = np.asarray(x, dtype=float)/2**15\n", + "x = np.asarray(x, dtype=float) / 2**15\n", "\n", "# compute and truncate ACF\n", - "acf = 1/len(x) * np.correlate(x, x, mode='full')\n", - "acf = acf[(len(x)-1)-(K-1):(len(x)-1)+K]\n", - "kappa = np.arange(-(K-1), K)\n", + "acf = 1 / len(x) * np.correlate(x, x, mode=\"full\")\n", + "acf = acf[(len(x) - 1) - (K - 1) : (len(x) - 1) + K]\n", + "kappa = np.arange(-(K - 1), K)\n", "\n", "# plot ACF\n", "plt.figure(figsize=(10, 8))\n", "plt.plot(kappa, acf)\n", - "plt.xlabel(r'$\\kappa$')\n", - "plt.ylabel(r'$\\hat{\\varphi}_{xx}[\\kappa]$')\n", - "plt.axis([-K, K, 1.1*min(acf), 1.1*max(acf)])\n", + "plt.xlabel(r\"$\\kappa$\")\n", + "plt.ylabel(r\"$\\hat{\\varphi}_{xx}[\\kappa]$\")\n", + "plt.axis([-K, K, 1.1 * min(acf), 1.1 * max(acf)])\n", "plt.grid()" ] }, @@ -344,24 +344,24 @@ "y = (1, -1, -1, 1)\n", "N = len(x)\n", "M = len(y)\n", - "xc1 = 1/N * np.correlate(x, y, mode='full')\n", - "kappa1 = np.arange(0, N+M-1) - (M-1)\n", - "plt.stem(kappa1, xc1, basefmt='C0:')\n", - "plt.xlabel(r'$\\kappa$')\n", - "plt.ylabel(r'$\\hat{\\varphi}_{xy}[\\kappa]$')\n", - "plt.title(r'$N_x$='+str(N)+', $M_y$='+str(M))\n", + "xc1 = 1 / N * np.correlate(x, y, mode=\"full\")\n", + "kappa1 = np.arange(0, N + M - 1) - (M - 1)\n", + "plt.stem(kappa1, xc1, basefmt=\"C0:\")\n", + "plt.xlabel(r\"$\\kappa$\")\n", + "plt.ylabel(r\"$\\hat{\\varphi}_{xy}[\\kappa]$\")\n", + "plt.title(r\"$N_x$=\" + str(N) + \", $M_y$=\" + str(M))\n", "plt.ylim(-1, 1)\n", "plt.grid(True)\n", "\n", "plt.subplot(1, 2, 2) # case 2: x longer than y\n", "y, x = x, y # elegant variable swap\n", "N, M = M, N\n", - "xc2 = 1/N * np.correlate(x, y, mode='full')\n", - "kappa2 = np.arange(0, N+M-1) - (M-1)\n", - "plt.stem(kappa2, xc2, basefmt='C0:')\n", - "plt.xlabel(r'$\\kappa$')\n", - "plt.ylabel(r'$\\hat{\\varphi}_{xy}[\\kappa]$')\n", - "plt.title(r'$N_x$='+str(N)+', $M_y$='+str(M))\n", + "xc2 = 1 / N * np.correlate(x, y, mode=\"full\")\n", + "kappa2 = np.arange(0, N + M - 1) - (M - 1)\n", + "plt.stem(kappa2, xc2, basefmt=\"C0:\")\n", + "plt.xlabel(r\"$\\kappa$\")\n", + "plt.ylabel(r\"$\\hat{\\varphi}_{xy}[\\kappa]$\")\n", + "plt.title(r\"$N_x$=\" + str(N) + \", $M_y$=\" + str(M))\n", "plt.ylim(-1, 1)\n", "plt.grid(True)" ] @@ -429,20 +429,20 @@ "outputs": [], "source": [ "# load set of PRN sequences (Gold codes)\n", - "prn = np.load('../data/gold_sequences.npz')['prn']\n", + "prn = np.load(\"../data/gold_sequences.npz\")[\"prn\"]\n", "\n", "\n", "def compute_plot_CCF(x, y, ylabel, K=30):\n", - " '''Computes, truncates and plots CCF.'''\n", - " ccf = 1/len(x) * np.correlate(x, y, mode='full')\n", - " ccf = ccf[(len(y)-1)-K:len(y)+K]\n", - " kappa = np.arange(-K, K+1)\n", + " \"\"\"Computes, truncates and plots CCF.\"\"\"\n", + " ccf = 1 / len(x) * np.correlate(x, y, mode=\"full\")\n", + " ccf = ccf[(len(y) - 1) - K : len(y) + K]\n", + " kappa = np.arange(-K, K + 1)\n", "\n", " # plot CCF\n", - " plt.stem(kappa, ccf, basefmt='C0:')\n", - " plt.xlabel(r'$\\kappa$')\n", + " plt.stem(kappa, ccf, basefmt=\"C0:\")\n", + " plt.xlabel(r\"$\\kappa$\")\n", " plt.ylabel(ylabel)\n", - " plt.axis([-K, K, 1.1*min(ccf), 1.1*max(ccf)])\n", + " plt.axis([-K, K, 1.1 * min(ccf), 1.1 * max(ccf)])\n", " plt.grid()\n", "\n", " return ccf" @@ -476,13 +476,13 @@ "\n", "plt.subplot(121)\n", "xi = yi = 10 # CCF\n", - "compute_plot_CCF(prn[xi, :], prn[yi, :], r'$\\hat{\\varphi}_{x_n x_n}[\\kappa]$')\n", - "plt.title('ACF of one pseudo random noise sequence')\n", + "compute_plot_CCF(prn[xi, :], prn[yi, :], r\"$\\hat{\\varphi}_{x_n x_n}[\\kappa]$\")\n", + "plt.title(\"ACF of one pseudo random noise sequence\")\n", "\n", "plt.subplot(122)\n", "xi, yi = 10, 16 # ACF\n", - "compute_plot_CCF(prn[xi, :], prn[yi, :], r'$\\hat{\\varphi}_{x_n y_m}[\\kappa]$')\n", - "plt.title('CCF between two pseudo random noise sequences')\n", + "compute_plot_CCF(prn[xi, :], prn[yi, :], r\"$\\hat{\\varphi}_{x_n y_m}[\\kappa]$\")\n", + "plt.title(\"CCF between two pseudo random noise sequences\")\n", "plt.ylim([-1, 1])\n", "plt.tight_layout()" ] @@ -526,11 +526,11 @@ "\n", "# compute and plot CCF\n", "plt.figure(figsize=(10, 4))\n", - "ccf = compute_plot_CCF(x, y, r'$\\hat{\\varphi}_{xx}[\\kappa]$', K=K)\n", - "plt.title('CCF between transmitted and received signal')\n", + "ccf = compute_plot_CCF(x, y, r\"$\\hat{\\varphi}_{xx}[\\kappa]$\", K=K)\n", + "plt.title(\"CCF between transmitted and received signal\")\n", "\n", "# estimate the TOA\n", - "print('Estimated TOA delay is {:2.0f} samples'.format(np.argmax(ccf) - K))" + "print(\"Estimated TOA delay is {:2.0f} samples\".format(np.argmax(ccf) - K))" ] }, { @@ -582,26 +582,30 @@ "\n", "# generate two uncorrelated random signals\n", "np.random.seed(1)\n", - "x = 2 + np.random.normal(size=L//4)\n", - "y = 3 + np.random.normal(size=L*2)\n", + "x = 2 + np.random.normal(size=L // 4)\n", + "y = 3 + np.random.normal(size=L * 2)\n", "\n", "# compute CCF\n", - "ccf = 1/len(x) * np.correlate(x, y, mode='full') # biased estimator\n", - "kappa = np.arange(0, len(x)+len(y)-1) - (len(y)-1)\n", + "ccf = 1 / len(x) * np.correlate(x, y, mode=\"full\") # biased estimator\n", + "kappa = np.arange(0, len(x) + len(y) - 1) - (len(y) - 1)\n", "# ccf = L/(L-np.abs(kappa)) * ccf # unbiased estimator, only if len(x)==len(y)\n", "\n", "# print mean values of signals\n", - "print('Mean of signal x[k]: %3.2f' % np.mean(x))\n", - "print('Mean of signal y[k]: %3.2f' % np.mean(y))\n", + "print(\"Mean of signal x[k]: %3.2f\" % np.mean(x))\n", + "print(\"Mean of signal y[k]: %3.2f\" % np.mean(y))\n", "\n", "# plot CCF\n", "plt.figure(figsize=(10, 8))\n", - "plt.stem(kappa, ccf, basefmt='C0:', linefmt='C0:')\n", - "plt.title('Biased estimator of cross-correlation function, N=' +\n", - " str(len(x))+', M='+str(len(y)))\n", - "plt.ylabel(r'$\\hat{\\varphi}_{xy}[\\kappa]$')\n", - "plt.xlabel(r'$\\kappa$')\n", - "plt.axis([kappa[0], kappa[-1], 0, 1.1*max(ccf)])\n", + "plt.stem(kappa, ccf, basefmt=\"C0:\", linefmt=\"C0:\")\n", + "plt.title(\n", + " \"Biased estimator of cross-correlation function, N=\"\n", + " + str(len(x))\n", + " + \", M=\"\n", + " + str(len(y))\n", + ")\n", + "plt.ylabel(r\"$\\hat{\\varphi}_{xy}[\\kappa]$\")\n", + "plt.xlabel(r\"$\\kappa$\")\n", + "plt.axis([kappa[0], kappa[-1], 0, 1.1 * max(ccf)])\n", "plt.grid()" ] }, diff --git a/random_signals/distributions.ipynb b/random_signals/distributions.ipynb index b841a2c..6464bf2 100644 --- a/random_signals/distributions.ipynb +++ b/random_signals/distributions.ipynb @@ -193,32 +193,31 @@ "# draw sample functions from a random process\n", "np.random.seed(2)\n", "x = np.random.normal(size=(N, K))\n", - "x += np.tile(np.cos(2*np.pi/K*np.arange(K)), [N, 1])\n", + "x += np.tile(np.cos(2 * np.pi / K * np.arange(K)), [N, 1])\n", "\n", "# compute the histogram\n", "px = np.zeros((bins, K))\n", "for k in range(K):\n", - " px[:, k], edges = np.histogram(\n", - " x[:, k], bins=bins, range=(-4, 4), density=True)\n", + " px[:, k], edges = np.histogram(x[:, k], bins=bins, range=(-4, 4), density=True)\n", "\n", "# compute the CDF\n", - "Px = np.cumsum(px, axis=0) * 8/bins\n", + "Px = np.cumsum(px, axis=0) * 8 / bins\n", "\n", "# plot the PDF\n", "plt.figure(figsize=(10, 6))\n", - "plt.pcolormesh(np.arange(K+1), edges, px)\n", - "plt.title(r'Estimated PDF $\\hat{p}_x(\\theta, k)$')\n", - "plt.xlabel(r'time index $k$')\n", - "plt.ylabel(r'amplitude $\\theta$')\n", + "plt.pcolormesh(np.arange(K + 1), edges, px)\n", + "plt.title(r\"Estimated PDF $\\hat{p}_x(\\theta, k)$\")\n", + "plt.xlabel(r\"time index $k$\")\n", + "plt.ylabel(r\"amplitude $\\theta$\")\n", "plt.colorbar()\n", "plt.autoscale(tight=True)\n", "\n", "# plot the CDF\n", "plt.figure(figsize=(10, 6))\n", - "plt.pcolormesh(np.arange(K+1), edges, Px, vmin=0, vmax=1)\n", - "plt.title(r'Estimated CDF $\\hat{P}_x(\\theta, k)$')\n", - "plt.xlabel(r'time index $k$')\n", - "plt.ylabel(r'amplitude $\\theta$')\n", + "plt.pcolormesh(np.arange(K + 1), edges, Px, vmin=0, vmax=1)\n", + "plt.title(r\"Estimated CDF $\\hat{P}_x(\\theta, k)$\")\n", + "plt.xlabel(r\"time index $k$\")\n", + "plt.ylabel(r\"amplitude $\\theta$\")\n", "plt.colorbar()\n", "plt.autoscale(tight=True)" ] diff --git a/random_signals/ensemble_averages.ipynb b/random_signals/ensemble_averages.ipynb index 593a894..903c2c4 100644 --- a/random_signals/ensemble_averages.ipynb +++ b/random_signals/ensemble_averages.ipynb @@ -242,56 +242,53 @@ "# generate the sample functions\n", "np.random.seed(3)\n", "x = np.random.normal(size=(N, K))\n", - "x += np.tile(np.cos(2*np.pi/K*np.arange(K)), [N, 1])\n", + "x += np.tile(np.cos(2 * np.pi / K * np.arange(K)), [N, 1])\n", "\n", "# estimate the linear mean as ensemble average\n", - "mu = 1/N * np.sum(x, 0)\n", + "mu = 1 / N * np.sum(x, 0)\n", "# estimate the quadratic mean\n", - "qu = 1/N * np.sum(x**2, 0)\n", + "qu = 1 / N * np.sum(x**2, 0)\n", "# estimate the variance\n", - "sigma = 1/N * np.sum((x-mu)**2, 0)\n", + "sigma = 1 / N * np.sum((x - mu) ** 2, 0)\n", "\n", "\n", "# plot results\n", - "plt.rc('figure', figsize=(10, 3))\n", + "plt.rc(\"figure\", figsize=(10, 3))\n", "\n", "plt.figure()\n", - "plt.stem(x[0, :], basefmt='C0:', linefmt='C0-',\n", - " markerfmt='C0o', label=r'$x_0$')\n", - "plt.stem(x[1, :], basefmt='C1:', linefmt='C1--',\n", - " markerfmt='C1o', label=r'$x_1$')\n", - "plt.stem(x[2, :], basefmt='C2:', linefmt='C2-.',\n", - " markerfmt='C2o', label=r'$x_2$')\n", - "plt.title(r'Sample functions $x_0[k]$, $x_1[k]$, $x_2[k]$')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$x[k]$')\n", + "plt.stem(x[0, :], basefmt=\"C0:\", linefmt=\"C0-\", markerfmt=\"C0o\", label=r\"$x_0$\")\n", + "plt.stem(x[1, :], basefmt=\"C1:\", linefmt=\"C1--\", markerfmt=\"C1o\", label=r\"$x_1$\")\n", + "plt.stem(x[2, :], basefmt=\"C2:\", linefmt=\"C2-.\", markerfmt=\"C2o\", label=r\"$x_2$\")\n", + "plt.title(r\"Sample functions $x_0[k]$, $x_1[k]$, $x_2[k]$\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$x[k]$\")\n", "plt.axis([0, K, -4, 4])\n", "plt.legend()\n", "plt.grid(True)\n", "\n", "plt.figure()\n", - "plt.stem(mu, basefmt='C0:', linefmt='C0-',\n", - " markerfmt='C0o', label=r'$\\hat{\\mu}_x[k]$')\n", - "plt.stem(mu**2, basefmt='C1:', linefmt='C1--',\n", - " markerfmt='C1o', label=r'$\\hat{\\mu}^2_x[k]$')\n", - "plt.title(r'Estimate of linear mean and squared linear mean')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$\\hat{\\mu}_x[k]$, $\\hat{\\mu}^2_x[k]$')\n", + "plt.stem(mu, basefmt=\"C0:\", linefmt=\"C0-\", markerfmt=\"C0o\", label=r\"$\\hat{\\mu}_x[k]$\")\n", + "plt.stem(\n", + " mu**2, basefmt=\"C1:\", linefmt=\"C1--\", markerfmt=\"C1o\", label=r\"$\\hat{\\mu}^2_x[k]$\"\n", + ")\n", + "plt.title(r\"Estimate of linear mean and squared linear mean\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$\\hat{\\mu}_x[k]$, $\\hat{\\mu}^2_x[k]$\")\n", "plt.axis([0, K, -1.5, 1.5])\n", "plt.legend()\n", "\n", "plt.figure()\n", - "plt.stem(qu, basefmt='C0:')\n", - "plt.title(r'Estimate of quadratic mean')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$\\hat{E}\\{x^2[k]\\}$')\n", + "plt.stem(qu, basefmt=\"C0:\")\n", + "plt.title(r\"Estimate of quadratic mean\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$\\hat{E}\\{x^2[k]\\}$\")\n", "plt.axis([0, K, 0, 2.5])\n", "\n", "plt.figure()\n", - "plt.stem(sigma, basefmt='C0:')\n", - "plt.title(r'Estimate of variance')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$\\hat{\\sigma}^2_x[k]$')\n", + "plt.stem(sigma, basefmt=\"C0:\")\n", + "plt.title(r\"Estimate of variance\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$\\hat{\\sigma}^2_x[k]$\")\n", "plt.axis([0, K, 0, 1.5])" ] }, @@ -421,8 +418,9 @@ "np.random.seed(1)\n", "r = np.random.normal(size=(N, L))\n", "h = np.random.normal(size=(N, 10))\n", - "x = np.asarray([np.convolve(r[n, :], h[n, :], mode='same') for n in range(N)]) \\\n", - " + np.tile(np.cos(2*np.pi/L*np.arange(L)), [N, 1])\n", + "x = np.asarray(\n", + " [np.convolve(r[n, :], h[n, :], mode=\"same\") for n in range(N)]\n", + ") + np.tile(np.cos(2 * np.pi / L * np.arange(L)), [N, 1])\n", "\n", "# estimate the auto-correlation function (ACF)\n", "acf = np.zeros((L, L))\n", @@ -430,17 +428,17 @@ " for m in range(L):\n", " # x[0, n] * x[0, m] is the product of the 0-th sample function at two different time-steps n,m\n", " # the individual products are then summed up for all N sample functions\n", - " acf[n, m] = 1/N * np.sum(x[:, n]*x[:, m], axis=0)\n", + " acf[n, m] = 1 / N * np.sum(x[:, n] * x[:, m], axis=0)\n", "\n", "\n", "# plot ACF\n", "plt.figure(figsize=(7, 5))\n", - "plt.pcolormesh(np.arange(L+1), np.arange(L+1), acf)\n", - "plt.title(r'Estimate of ACF $\\hat{\\varphi}_{xx}[k_1, k_2]$')\n", - "plt.xlabel(r'$k_1$')\n", - "plt.ylabel(r'$k_2$')\n", + "plt.pcolormesh(np.arange(L + 1), np.arange(L + 1), acf)\n", + "plt.title(r\"Estimate of ACF $\\hat{\\varphi}_{xx}[k_1, k_2]$\")\n", + "plt.xlabel(r\"$k_1$\")\n", + "plt.ylabel(r\"$k_2$\")\n", "plt.colorbar()\n", - "plt.axis('tight')" + "plt.axis(\"tight\")" ] }, { diff --git a/random_signals/important_distributions.ipynb b/random_signals/important_distributions.ipynb index be90d88..b46e83a 100644 --- a/random_signals/important_distributions.ipynb +++ b/random_signals/important_distributions.ipynb @@ -34,22 +34,22 @@ "\n", "\n", "def plot_pdf_cdf(x, distr):\n", - " '''Plot PDF and CDF of given distribution.'''\n", + " \"\"\"Plot PDF and CDF of given distribution.\"\"\"\n", "\n", " plt.figure(figsize=(10, 5))\n", "\n", " plt.subplot(121)\n", " plt.plot(x, distr.pdf(x))\n", - " plt.xlabel(r'$\\theta$')\n", - " plt.ylabel(r'$p_x(\\theta)$')\n", - " plt.title('PDF')\n", + " plt.xlabel(r\"$\\theta$\")\n", + " plt.ylabel(r\"$p_x(\\theta)$\")\n", + " plt.title(\"PDF\")\n", " plt.grid()\n", "\n", " plt.subplot(122)\n", " plt.plot(x, distr.cdf(x))\n", - " plt.xlabel(r'$\\theta$')\n", - " plt.ylabel(r'$P_x(\\theta)$')\n", - " plt.title('CDF')\n", + " plt.xlabel(r\"$\\theta$\")\n", + " plt.ylabel(r\"$P_x(\\theta)$\")\n", + " plt.title(\"CDF\")\n", " plt.grid()" ] }, @@ -1120,7 +1120,7 @@ } ], "source": [ - "plot_pdf_cdf(np.linspace(-.5, 1.5, num=1000), stats.uniform(loc=0, scale=1))" + "plot_pdf_cdf(np.linspace(-0.5, 1.5, num=1000), stats.uniform(loc=0, scale=1))" ] }, { @@ -1178,24 +1178,24 @@ "outputs": [], "source": [ "def estimate_plot_pdf_cdf(x, nbins=100):\n", - " '''Estimate and plot PDF/CDF of a given sample function.'''\n", - " \n", - " plt.figure(figsize = (10, 6))\n", + " \"\"\"Estimate and plot PDF/CDF of a given sample function.\"\"\"\n", + "\n", + " plt.figure(figsize=(10, 6))\n", " plt.hist(x, nbins, density=True)\n", - " plt.title('Estimated PDF')\n", - " plt.xlabel(r'$\\theta$')\n", - " plt.ylabel(r'$\\hat{p}_x(\\theta)$')\n", + " plt.title(\"Estimated PDF\")\n", + " plt.xlabel(r\"$\\theta$\")\n", + " plt.ylabel(r\"$\\hat{p}_x(\\theta)$\")\n", " plt.grid()\n", - " \n", - " plt.figure(figsize = (10, 6))\n", + "\n", + " plt.figure(figsize=(10, 6))\n", " plt.hist(x, nbins, cumulative=True, density=True)\n", - " plt.title('Estimated CDF')\n", - " plt.xlabel(r'$\\theta$')\n", - " plt.ylabel(r'$\\hat{P}_x(\\theta)$')\n", + " plt.title(\"Estimated CDF\")\n", + " plt.xlabel(r\"$\\theta$\")\n", + " plt.ylabel(r\"$\\hat{P}_x(\\theta)$\")\n", " plt.grid()\n", - " \n", - " print('Estimated linear mean: {0:2.5f}'.format(np.mean(x)))\n", - " print('Estimated variance: {0:2.5f}'.format(np.var(x)))" + "\n", + " print(\"Estimated linear mean: {0:2.5f}\".format(np.mean(x)))\n", + " print(\"Estimated variance: {0:2.5f}\".format(np.var(x)))" ] }, { @@ -4569,8 +4569,7 @@ ], "source": [ "np.random.seed(1)\n", - "estimate_plot_pdf_cdf(stats.uniform.rvs(\n", - " size=100000, loc=0, scale=1), nbins=100)" + "estimate_plot_pdf_cdf(stats.uniform.rvs(size=100000, loc=0, scale=1), nbins=100)" ] }, { @@ -10644,8 +10643,7 @@ } ], "source": [ - "plot_pdf_cdf(np.linspace(-5, 5, num=100),\n", - " stats.laplace(loc=0, scale=1/np.sqrt(2)))" + "plot_pdf_cdf(np.linspace(-5, 5, num=100), stats.laplace(loc=0, scale=1 / np.sqrt(2)))" ] }, { @@ -14093,8 +14091,7 @@ } ], "source": [ - "estimate_plot_pdf_cdf(stats.laplace(\n", - " scale=1/np.sqrt(2)).rvs(size=10000), nbins=100)" + "estimate_plot_pdf_cdf(stats.laplace(scale=1 / np.sqrt(2)).rvs(size=10000), nbins=100)" ] }, { @@ -17703,8 +17700,8 @@ "source": [ "from scipy.io import wavfile\n", "\n", - "fs, x = wavfile.read('../data/speech_8k.wav')\n", - "x = np.asarray(x, dtype=float)/2**15\n", + "fs, x = wavfile.read(\"../data/speech_8k.wav\")\n", + "x = np.asarray(x, dtype=float) / 2**15\n", "estimate_plot_pdf_cdf(x, nbins=100)" ] }, diff --git a/random_signals/independent.ipynb b/random_signals/independent.ipynb index a7ca1d7..1e737bb 100644 --- a/random_signals/independent.ipynb +++ b/random_signals/independent.ipynb @@ -120,41 +120,42 @@ "\n", "\n", "def compute_plot_histograms(kappa):\n", - " '''Estimate and plot bivariate/product PDFs for given shift'''\n", + " \"\"\"Estimate and plot bivariate/product PDFs for given shift\"\"\"\n", "\n", " # shift signal\n", " x2 = np.concatenate((x1[kappa:], np.zeros(kappa)))\n", "\n", " # compute bivariate and marginal histograms\n", - " pdf_xx, x1edges, x2edges = np.histogram2d(x1, x2, bins=(\n", - " M, M), range=((-1.5, 1.5), (-1.5, 1.5)), density=True)\n", + " pdf_xx, x1edges, x2edges = np.histogram2d(\n", + " x1, x2, bins=(M, M), range=((-1.5, 1.5), (-1.5, 1.5)), density=True\n", + " )\n", " pdf_x1, _ = np.histogram(x1, bins=M, range=(-1.5, 1.5), density=True)\n", " pdf_x2, _ = np.histogram(x2, bins=M, range=(-1.5, 1.5), density=True)\n", "\n", " # plot results\n", " fig = plt.figure(figsize=(10, 10))\n", "\n", - " plt.subplot(121, aspect='equal')\n", + " plt.subplot(121, aspect=\"equal\")\n", " plt.pcolormesh(x1edges, x2edges, pdf_xx)\n", - " plt.xlabel(r'$\\theta_1$')\n", - " plt.ylabel(r'$\\theta_2$')\n", - " plt.title(r'Bivariate PDF $p_{{xy}}(\\theta_1, \\theta_2, \\kappa)$')\n", + " plt.xlabel(r\"$\\theta_1$\")\n", + " plt.ylabel(r\"$\\theta_2$\")\n", + " plt.title(r\"Bivariate PDF $p_{{xy}}(\\theta_1, \\theta_2, \\kappa)$\")\n", " plt.colorbar(fraction=0.046)\n", "\n", - " plt.subplot(122, aspect='equal')\n", + " plt.subplot(122, aspect=\"equal\")\n", " plt.pcolormesh(x1edges, x2edges, np.outer(pdf_x1, pdf_x2))\n", - " plt.xlabel(r'$\\theta_1$')\n", - " plt.ylabel(r'$\\theta_2$')\n", - " plt.title(r'Product of PDFs $p_x(\\theta_1) \\cdot p_x(\\theta_2, \\kappa)$')\n", + " plt.xlabel(r\"$\\theta_1$\")\n", + " plt.ylabel(r\"$\\theta_2$\")\n", + " plt.title(r\"Product of PDFs $p_x(\\theta_1) \\cdot p_x(\\theta_2, \\kappa)$\")\n", " plt.colorbar(fraction=0.046)\n", "\n", - " fig.suptitle('Shift $\\kappa =$ {:<2.0f}'.format(kappa), y=0.72)\n", + " fig.suptitle(\"Shift $\\kappa =$ {:<2.0f}\".format(kappa), y=0.72)\n", " fig.tight_layout()\n", "\n", "\n", "# generate signal\n", "x = np.random.normal(size=N)\n", - "x1 = np.convolve(x, [1, .5, .3, .7, .3], mode='same')\n", + "x1 = np.convolve(x, [1, 0.5, 0.3, 0.7, 0.3], mode=\"same\")\n", "\n", "# compute and plot the PDFs for various shifts\n", "compute_plot_histograms(0)\n", @@ -266,14 +267,16 @@ "outputs": [], "source": [ "def ccf_by_dotprod(x, y):\n", - " '''Computes the CCF by the dot product.'''\n", + " \"\"\"Computes the CCF by the dot product.\"\"\"\n", "\n", " N = len(x)\n", " M = len(y)\n", - " xN = np.concatenate((np.zeros(M-1), x, np.zeros(M-1)))\n", - " yM = np.concatenate((y, np.zeros(N+M-2)))\n", + " xN = np.concatenate((np.zeros(M - 1), x, np.zeros(M - 1)))\n", + " yM = np.concatenate((y, np.zeros(N + M - 2)))\n", "\n", - " return np.fromiter([np.dot(xN, np.roll(yM, kappa)) for kappa in range(N+M-1)], float)" + " return np.fromiter(\n", + " [np.dot(xN, np.roll(yM, kappa)) for kappa in range(N + M - 1)], float\n", + " )" ] }, { @@ -305,27 +308,27 @@ "# generate signals\n", "np.random.seed(1)\n", "x = np.random.normal(size=N)\n", - "y = np.convolve(x, [1, .5, .3, .7, .3], mode='same')\n", + "y = np.convolve(x, [1, 0.5, 0.3, 0.7, 0.3], mode=\"same\")\n", "\n", "# compute CCF\n", - "ccf1 = 1/N * np.correlate(x, y, mode='full')\n", - "ccf2 = 1/N * ccf_by_dotprod(x, y)\n", - "kappa = np.arange(-N+1, N)\n", + "ccf1 = 1 / N * np.correlate(x, y, mode=\"full\")\n", + "ccf2 = 1 / N * ccf_by_dotprod(x, y)\n", + "kappa = np.arange(-N + 1, N)\n", "\n", "# plot results\n", "plt.figure(figsize=(10, 4))\n", "\n", "plt.subplot(121)\n", - "plt.stem(kappa, ccf1 )\n", - "plt.xlabel('$\\kappa$')\n", - "plt.ylabel(r'$\\varphi_{xy}[\\kappa]$')\n", - "plt.title('CCF by dot product')\n", + "plt.stem(kappa, ccf1)\n", + "plt.xlabel(\"$\\kappa$\")\n", + "plt.ylabel(r\"$\\varphi_{xy}[\\kappa]$\")\n", + "plt.title(\"CCF by dot product\")\n", "plt.grid()\n", "\n", "plt.subplot(122)\n", - "plt.stem(kappa, np.abs(ccf1-ccf2) )\n", - "plt.xlabel('$\\kappa$')\n", - "plt.title('Difference (magnitude)')\n", + "plt.stem(kappa, np.abs(ccf1 - ccf2))\n", + "plt.xlabel(\"$\\kappa$\")\n", + "plt.title(\"Difference (magnitude)\")\n", "plt.tight_layout()" ] }, diff --git a/random_signals/introduction.ipynb b/random_signals/introduction.ipynb index 5e5a4aa..11c0a12 100644 --- a/random_signals/introduction.ipynb +++ b/random_signals/introduction.ipynb @@ -162,12 +162,12 @@ "# plot sample functions\n", "fig = plt.figure(figsize=(10, 12))\n", "for n in range(N):\n", - " plt.subplot(N, 1, n+1)\n", + " plt.subplot(N, 1, n + 1)\n", " plt.tight_layout()\n", - " plt.stem(x[n, :], basefmt='C0:')\n", - " plt.title('Sample Function %d' % n)\n", - " plt.xlabel(r'$k$')\n", - " plt.ylabel(r'$x_%d[k]$' % n)\n", + " plt.stem(x[n, :], basefmt=\"C0:\")\n", + " plt.title(\"Sample Function %d\" % n)\n", + " plt.xlabel(r\"$k$\")\n", + " plt.ylabel(r\"$x_%d[k]$\" % n)\n", " plt.axis([-1, 32, -3, 3])\n", " plt.grid()" ] diff --git a/random_signals/power_spectral_densities.ipynb b/random_signals/power_spectral_densities.ipynb index 9ab0935..d061e61 100644 --- a/random_signals/power_spectral_densities.ipynb +++ b/random_signals/power_spectral_densities.ipynb @@ -110,24 +110,24 @@ "from scipy.io import wavfile\n", "\n", "# read audio file\n", - "fs, x = wavfile.read('../data/vocal_o_8k.wav')\n", + "fs, x = wavfile.read(\"../data/vocal_o_8k.wav\")\n", "x = np.asarray(x, dtype=float)\n", "N = len(x)\n", "\n", "# compute ACF\n", - "acf = 1/N * np.correlate(x, x, mode='full')\n", + "acf = 1 / N * np.correlate(x, x, mode=\"full\")\n", "# compute PSD\n", "psd = np.fft.fft(acf)\n", - "psd = psd * np.exp(1j*np.arange(2*N-1)*2*np.pi*(N-1)/(2*N-1))\n", - "f = np.fft.fftfreq(2*N-1, d=1/fs)\n", + "psd = psd * np.exp(1j * np.arange(2 * N - 1) * 2 * np.pi * (N - 1) / (2 * N - 1))\n", + "f = np.fft.fftfreq(2 * N - 1, d=1 / fs)\n", "\n", "# plot PSD\n", "plt.figure(figsize=(10, 4))\n", "plt.plot(f, np.real(psd))\n", - "plt.title('Estimated power spectral density')\n", - "plt.ylabel(r'$\\hat{\\Phi}_{xx}(e^{j \\Omega})$')\n", - "plt.xlabel(r'$f / Hz$')\n", - "plt.axis([0, 500, 0, 1.1*max(np.abs(psd))])\n", + "plt.title(\"Estimated power spectral density\")\n", + "plt.ylabel(r\"$\\hat{\\Phi}_{xx}(e^{j \\Omega})$\")\n", + "plt.xlabel(r\"$f / Hz$\")\n", + "plt.axis([0, 500, 0, 1.1 * max(np.abs(psd))])\n", "plt.grid()" ] }, @@ -236,19 +236,19 @@ "M = len(y)\n", "\n", "# compute cross PSD via CCF\n", - "acf = 1/N * np.correlate(x, y, mode='full')\n", + "acf = 1 / N * np.correlate(x, y, mode=\"full\")\n", "psd = np.fft.fft(acf)\n", - "psd = psd * np.exp(1j*np.arange(N+M-1)*2*np.pi*(M-1)/(2*M-1))\n", + "psd = psd * np.exp(1j * np.arange(N + M - 1) * 2 * np.pi * (M - 1) / (2 * M - 1))\n", "psd = np.fft.fftshift(psd)\n", - "Om = 2*np.pi * np.arange(0, N+M-1) / (N+M-1)\n", + "Om = 2 * np.pi * np.arange(0, N + M - 1) / (N + M - 1)\n", "Om = Om - np.pi\n", "\n", "# plot results\n", "plt.figure(figsize=(10, 4))\n", - "plt.stem(Om, np.abs(psd), basefmt='C0:')\n", - "plt.title('Biased estimator of cross power spectral density')\n", - "plt.ylabel(r'$|\\hat{\\Phi}_{xy}(e^{j \\Omega})|$')\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.stem(Om, np.abs(psd), basefmt=\"C0:\")\n", + "plt.title(\"Biased estimator of cross power spectral density\")\n", + "plt.ylabel(r\"$|\\hat{\\Phi}_{xy}(e^{j \\Omega})|$\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.grid()" ] }, diff --git a/random_signals/stationary_ergodic.ipynb b/random_signals/stationary_ergodic.ipynb index 930661f..9d190ed 100644 --- a/random_signals/stationary_ergodic.ipynb +++ b/random_signals/stationary_ergodic.ipynb @@ -189,28 +189,28 @@ "\n", "\n", "def compute_plot_results(x):\n", - " '''Compute and plot linear mean and ACF of random process.'''\n", - " \n", + " \"\"\"Compute and plot linear mean and ACF of random process.\"\"\"\n", + "\n", " # estimate linear mean by ensemble average\n", - " mu = 1/N * np.sum(x, 0)\n", + " mu = 1 / N * np.sum(x, 0)\n", " # estimate the auto-correlation function\n", " acf = np.zeros((L, L))\n", " for n in range(L):\n", " for m in range(L):\n", - " acf[n, m] = 1/N * np.sum(x[:, n]*x[:, m], 0)\n", + " acf[n, m] = 1 / N * np.sum(x[:, n] * x[:, m], 0)\n", "\n", " plt.subplot(121)\n", - " plt.stem(mu, basefmt='C0:')\n", - " plt.title(r'Estimate of linear mean $\\hat{\\mu}_x[k]$')\n", - " plt.xlabel(r'$k$')\n", - " plt.ylabel(r'$\\hat{\\mu}[k]$')\n", + " plt.stem(mu, basefmt=\"C0:\")\n", + " plt.title(r\"Estimate of linear mean $\\hat{\\mu}_x[k]$\")\n", + " plt.xlabel(r\"$k$\")\n", + " plt.ylabel(r\"$\\hat{\\mu}[k]$\")\n", " plt.axis([0, L, -1.5, 1.5])\n", "\n", " plt.subplot(122)\n", - " plt.pcolor(np.arange(L+1), np.arange(L+1), acf, vmin=-1, vmax=3)\n", - " plt.title(r'Estimate of ACF $\\hat{\\varphi}_{xx}[k_1, k_2]$')\n", - " plt.xlabel(r'$k_1$')\n", - " plt.ylabel(r'$k_2$')\n", + " plt.pcolor(np.arange(L + 1), np.arange(L + 1), acf, vmin=-1, vmax=3)\n", + " plt.title(r\"Estimate of ACF $\\hat{\\varphi}_{xx}[k_1, k_2]$\")\n", + " plt.xlabel(r\"$k_1$\")\n", + " plt.ylabel(r\"$k_2$\")\n", " plt.colorbar()\n", " plt.autoscale(tight=True)\n", "\n", @@ -218,17 +218,17 @@ "# generate sample functions\n", "np.random.seed(1)\n", "x = np.random.normal(size=(N, L))\n", - "x1 = x + np.tile(np.cos(2*np.pi/L*np.arange(L)), [N, 1])\n", - "h = 2*np.fft.irfft([1, 1, 1, 0, 0, 0]) # simple lowpass filter\n", - "x2 = np.asarray([np.convolve(x[n, :], h, mode='same') for n in range(N)])\n", + "x1 = x + np.tile(np.cos(2 * np.pi / L * np.arange(L)), [N, 1])\n", + "h = 2 * np.fft.irfft([1, 1, 1, 0, 0, 0]) # simple lowpass filter\n", + "x2 = np.asarray([np.convolve(x[n, :], h, mode=\"same\") for n in range(N)])\n", "\n", "# plot results\n", "plt.figure(figsize=(10, 4))\n", - "plt.gcf().suptitle('Random Process 1', fontsize=12, y=1.05)\n", + "plt.gcf().suptitle(\"Random Process 1\", fontsize=12, y=1.05)\n", "compute_plot_results(x1)\n", "\n", "plt.figure(figsize=(10, 4))\n", - "plt.gcf().suptitle('Random Process 2', fontsize=12, y=1.05)\n", + "plt.gcf().suptitle(\"Random Process 2\", fontsize=12, y=1.05)\n", "compute_plot_results(x2)" ] }, @@ -348,62 +348,68 @@ "\n", "\n", "def compute_plot_results(x):\n", - " '''Compute and plot linear mean and ACF of random process.'''\n", + " \"\"\"Compute and plot linear mean and ACF of random process.\"\"\"\n", "\n", " N, L = x.shape\n", " # estimate linear mean by ensemble average\n", - " mu = 1/N * np.sum(x, 0)\n", + " mu = 1 / N * np.sum(x, 0)\n", " # estimate the auto-correlation function by ensemble average\n", " acf = np.zeros((L, L))\n", " for n in range(L):\n", " for m in range(L):\n", - " acf[n, m] = 1/N * np.sum(x[:, n]*x[:, m], 0)\n", + " acf[n, m] = 1 / N * np.sum(x[:, n] * x[:, m], 0)\n", " # estimate linear mean as temporal average\n", - " mut = 1/L * np.sum(x, 1)\n", + " mut = 1 / L * np.sum(x, 1)\n", " # estimate the auto-correlation function as temporal average\n", " acft = np.zeros((N, L))\n", " for n in range(N):\n", - " acft[n, :] = np.correlate(x[n, :], x[n, :], mode='same')\n", - " kappa = np.arange(L) - L//2\n", + " acft[n, :] = np.correlate(x[n, :], x[n, :], mode=\"same\")\n", + " kappa = np.arange(L) - L // 2\n", "\n", " for n in range(2):\n", " plt.figure(figsize=(10, 5))\n", " plt.subplot(131)\n", - " plt.stem(x[n, :], basefmt='C0:')\n", - " plt.title(r'Sample function $x_%d[k]$' % n)\n", - " plt.xlabel(r'$k$')\n", - " plt.ylabel(r'$x_%d[k]$' % n)\n", + " plt.stem(x[n, :], basefmt=\"C0:\")\n", + " plt.title(r\"Sample function $x_%d[k]$\" % n)\n", + " plt.xlabel(r\"$k$\")\n", + " plt.ylabel(r\"$x_%d[k]$\" % n)\n", " plt.axis([0, L, -4, 4])\n", "\n", " plt.subplot(132)\n", - " bar = plt.bar(0, mut[n], tick_label='')\n", - " plt.text(0, mut[n]+np.sign(mut[n])*.15,\n", - " r'$\\hat{{\\mu}}_{{x,{:d}}} = {:2.2f}$'.format(n, mut[n]), ha='center', va='bottom')\n", - " plt.title(r'Linear mean $\\overline{ x_%d[k] }$' % n)\n", - " plt.axis([-.5, .5, -1.5, 1.5])\n", + " bar = plt.bar(0, mut[n], tick_label=\"\")\n", + " plt.text(\n", + " 0,\n", + " mut[n] + np.sign(mut[n]) * 0.15,\n", + " r\"$\\hat{{\\mu}}_{{x,{:d}}} = {:2.2f}$\".format(n, mut[n]),\n", + " ha=\"center\",\n", + " va=\"bottom\",\n", + " )\n", + " plt.title(r\"Linear mean $\\overline{ x_%d[k] }$\" % n)\n", + " plt.axis([-0.5, 0.5, -1.5, 1.5])\n", "\n", " plt.subplot(133)\n", - " plt.stem(kappa, acft[n, :], basefmt='C0:')\n", + " plt.stem(kappa, acft[n, :], basefmt=\"C0:\")\n", " plt.title(\n", - " r'Autocorrelation $\\overline{ x_%d[k] \\cdot x_%d[k-\\kappa] }$' % (n, n))\n", - " plt.xlabel(r'$\\kappa$')\n", - " plt.ylabel(r'$\\hat{\\varphi}_{xx,%d}[\\kappa]$' % n)\n", - " plt.axis([-L//2, L//2, -30, 150])\n", + " r\"Autocorrelation $\\overline{ x_%d[k] \\cdot x_%d[k-\\kappa] }$\" % (n, n)\n", + " )\n", + " plt.xlabel(r\"$\\kappa$\")\n", + " plt.ylabel(r\"$\\hat{\\varphi}_{xx,%d}[\\kappa]$\" % n)\n", + " plt.axis([-L // 2, L // 2, -30, 150])\n", " plt.tight_layout()\n", "\n", " plt.figure(figsize=(10, 5))\n", " plt.subplot(131)\n", " plt.stem(mu)\n", - " plt.title(r'Linear mean $E\\{ x[k] \\}$')\n", - " plt.xlabel(r'$k$')\n", - " plt.ylabel(r'$\\hat{\\mu}[k]$')\n", + " plt.title(r\"Linear mean $E\\{ x[k] \\}$\")\n", + " plt.xlabel(r\"$k$\")\n", + " plt.ylabel(r\"$\\hat{\\mu}[k]$\")\n", " plt.axis([0, L, -1.5, 1.5])\n", "\n", " plt.figure(figsize=(4, 4))\n", - " plt.pcolor(np.arange(L+1), np.arange(L+1), acf, vmin=-1.5, vmax=2.5)\n", - " plt.title(r'ACF $E\\{ x[k_1] \\cdot x[k_2] \\}$')\n", - " plt.xlabel(r'$k_1$')\n", - " plt.ylabel(r'$k_2$')\n", + " plt.pcolor(np.arange(L + 1), np.arange(L + 1), acf, vmin=-1.5, vmax=2.5)\n", + " plt.title(r\"ACF $E\\{ x[k_1] \\cdot x[k_2] \\}$\")\n", + " plt.xlabel(r\"$k_1$\")\n", + " plt.ylabel(r\"$k_2$\")\n", " plt.colorbar()\n", " plt.autoscale(tight=True)\n", "\n", @@ -412,8 +418,8 @@ "np.random.seed(11)\n", "x = np.random.normal(size=(N, L))\n", "k = np.arange(L)\n", - "x1 = x + np.tile(np.cos(2*np.pi/L*k), [N, 1])\n", - "x2 = x + np.tile([np.ones(L), -np.ones(L)], [N//2, 1])\n", + "x1 = x + np.tile(np.cos(2 * np.pi / L * k), [N, 1])\n", + "x2 = x + np.tile([np.ones(L), -np.ones(L)], [N // 2, 1])\n", "x3 = x + np.ones([N, L])" ] }, diff --git a/random_signals/superposition.ipynb b/random_signals/superposition.ipynb index 456de7d..433b988 100644 --- a/random_signals/superposition.ipynb +++ b/random_signals/superposition.ipynb @@ -1896,16 +1896,16 @@ "\n", "# generate random signals\n", "np.random.seed(2)\n", - "x = np.random.uniform(low=-1/2, high=1/2, size=K)\n", - "n = np.random.uniform(low=-1/2, high=1/2, size=K)\n", + "x = np.random.uniform(low=-1 / 2, high=1 / 2, size=K)\n", + "n = np.random.uniform(low=-1 / 2, high=1 / 2, size=K)\n", "y = x + n\n", "\n", "# plot estimated pdf\n", "plt.figure(figsize=(10, 6))\n", "plt.hist(y, 100, density=True)\n", - "plt.title('Estimated PDF')\n", - "plt.xlabel(r'$\\theta$')\n", - "plt.ylabel(r'$\\hat{p}_y(\\theta)$')\n", + "plt.title(\"Estimated PDF\")\n", + "plt.xlabel(r\"$\\theta$\")\n", + "plt.ylabel(r\"$\\hat{p}_y(\\theta)$\")\n", "plt.grid()" ] }, @@ -3630,14 +3630,14 @@ "\n", "# generate random signals\n", "np.random.seed(2)\n", - "y = np.sum(np.random.uniform(low=-1/2, high=1/2, size=(N, K)), axis=0)\n", + "y = np.sum(np.random.uniform(low=-1 / 2, high=1 / 2, size=(N, K)), axis=0)\n", "\n", "# plot estimated pdf\n", "plt.figure(figsize=(10, 6))\n", "plt.hist(y, 100, density=True)\n", - "plt.title('Estimated PDF')\n", - "plt.xlabel(r'$\\theta$')\n", - "plt.ylabel(r'$\\hat{p}_y(\\theta)$')\n", + "plt.title(\"Estimated PDF\")\n", + "plt.xlabel(r\"$\\theta$\")\n", + "plt.ylabel(r\"$\\hat{p}_y(\\theta)$\")\n", "plt.grid()" ] }, @@ -7525,49 +7525,49 @@ "source": [ "N = 1024 # length of compound signals\n", "M = 25 # period of cosine signal\n", - "K = 2*M # maximum lag for ACF/CCF\n", + "K = 2 * M # maximum lag for ACF/CCF\n", "\n", "# generate signals\n", - "x = np.cos(2*np.pi/M * np.arange(N))\n", + "x = np.cos(2 * np.pi / M * np.arange(N))\n", "np.random.seed(2)\n", "n = np.random.normal(size=N)\n", "# superposition of signals\n", "y = x + n\n", "\n", "# compute and truncate ACF of superposition\n", - "acf = 1/N * np.correlate(y, y, mode='full')\n", - "acf = acf[(len(y)-1)-K:len(y)+K]\n", + "acf = 1 / N * np.correlate(y, y, mode=\"full\")\n", + "acf = acf[(len(y) - 1) - K : len(y) + K]\n", "# compute and truncate CCF of superposition and noise\n", - "ccf = 1/N * np.correlate(n, y, mode='full')\n", - "ccf = ccf[(len(y)-1)-K:len(y)+K]\n", + "ccf = 1 / N * np.correlate(n, y, mode=\"full\")\n", + "ccf = ccf[(len(y) - 1) - K : len(y) + K]\n", "\n", "\n", "# plot results\n", - "kappa = np.arange(-K, K+1)\n", + "kappa = np.arange(-K, K + 1)\n", "\n", "plt.figure(figsize=(10, 10))\n", "\n", "plt.subplot(311)\n", - "plt.stem(y )\n", - "plt.title('Signal')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$x[k]$')\n", + "plt.stem(y)\n", + "plt.title(\"Signal\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$x[k]$\")\n", "plt.axis([0, K, -3, 3])\n", "\n", "plt.subplot(312)\n", - "plt.stem(kappa, acf )\n", - "plt.title('ACF of superposition')\n", - "plt.xlabel(r'$\\kappa$')\n", - "plt.ylabel(r'$\\varphi_{yy}[\\kappa]$')\n", - "plt.axis([-K, K, -.75, 1.1*np.max(acf)])\n", + "plt.stem(kappa, acf)\n", + "plt.title(\"ACF of superposition\")\n", + "plt.xlabel(r\"$\\kappa$\")\n", + "plt.ylabel(r\"$\\varphi_{yy}[\\kappa]$\")\n", + "plt.axis([-K, K, -0.75, 1.1 * np.max(acf)])\n", "plt.grid()\n", "\n", "plt.subplot(313)\n", - "plt.stem(kappa, ccf )\n", - "plt.title('CCF between noise and superposition')\n", - "plt.xlabel(r'$\\kappa$')\n", - "plt.ylabel(r'$\\varphi_{ny}[\\kappa]$')\n", - "plt.axis([-K, K, -.2, 1.1])\n", + "plt.stem(kappa, ccf)\n", + "plt.title(\"CCF between noise and superposition\")\n", + "plt.xlabel(r\"$\\kappa$\")\n", + "plt.ylabel(r\"$\\varphi_{ny}[\\kappa]$\")\n", + "plt.axis([-K, K, -0.2, 1.1])\n", "plt.grid()\n", "\n", "plt.tight_layout()" @@ -10466,7 +10466,7 @@ "Nmax = 50 # number of observations\n", "\n", "# generate signals\n", - "x = np.cos(2*np.pi/50 * np.arange(N))\n", + "x = np.cos(2 * np.pi / 50 * np.arange(N))\n", "np.random.seed(2)\n", "n = np.random.normal(size=(Nmax, K))\n", "# AWGN model\n", @@ -10474,36 +10474,37 @@ "# repeated averaging up to Nmax\n", "xhat = np.zeros_like(y)\n", "for i in range(Nmax):\n", - " xhat[i, :] = 1/(i+1) * np.sum(y[:i+1, :], axis=0)\n", + " xhat[i, :] = 1 / (i + 1) * np.sum(y[: i + 1, :], axis=0)\n", "# compute SNR for all averages\n", "Px = np.var(x)\n", "Pn = np.var(xhat - x, axis=1)\n", - "SNR = 10*np.log10(Px/Pn)\n", + "SNR = 10 * np.log10(Px / Pn)\n", "\n", "# plot results\n", "plt.figure(figsize=(10, 10))\n", "\n", "plt.subplot(311)\n", - "plt.stem(y[0, :100] )\n", - "plt.title('One observation')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$y_0[k]$')\n", + "plt.stem(y[0, :100])\n", + "plt.title(\"One observation\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$y_0[k]$\")\n", "plt.ylim([-3, 3])\n", "\n", "plt.subplot(312)\n", - "plt.stem(xhat[Nmax-1, :100] )\n", - "plt.title('Average over {:2.0f} observations'.format(Nmax))\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$\\hat{x}[k]$')\n", + "plt.stem(xhat[Nmax - 1, :100])\n", + "plt.title(\"Average over {:2.0f} observations\".format(Nmax))\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$\\hat{x}[k]$\")\n", "plt.ylim([-3, 3])\n", "\n", "plt.subplot(313)\n", - "plt.plot(range(1, Nmax+1), 10*np.log10(Px *\n", - " range(1, Nmax+1)), '--', label='theory')\n", - "plt.plot(range(1, Nmax+1), SNR, label='simulated')\n", - "plt.title('SNR')\n", - "plt.xlabel('number of averages $N$')\n", - "plt.ylabel('SNR in dB')\n", + "plt.plot(\n", + " range(1, Nmax + 1), 10 * np.log10(Px * range(1, Nmax + 1)), \"--\", label=\"theory\"\n", + ")\n", + "plt.plot(range(1, Nmax + 1), SNR, label=\"simulated\")\n", + "plt.title(\"SNR\")\n", + "plt.xlabel(\"number of averages $N$\")\n", + "plt.ylabel(\"SNR in dB\")\n", "plt.grid()\n", "plt.legend()\n", "\n", diff --git a/random_signals/white_noise.ipynb b/random_signals/white_noise.ipynb index 0cc593d..a790213 100644 --- a/random_signals/white_noise.ipynb +++ b/random_signals/white_noise.ipynb @@ -60,29 +60,29 @@ "\n", "\n", "def estimate_plot_pdf_acf(x, nbins=50, acf_range=30):\n", - " '''Estimate and plot PDF/CDF of a given sample function.'''\n", + " \"\"\"Estimate and plot PDF/CDF of a given sample function.\"\"\"\n", "\n", " # compute and truncate ACF\n", - " acf = 1/len(x) * np.correlate(x, x, mode='full')\n", - " acf = acf[len(x)-acf_range-1:len(x)+acf_range-1]\n", + " acf = 1 / len(x) * np.correlate(x, x, mode=\"full\")\n", + " acf = acf[len(x) - acf_range - 1 : len(x) + acf_range - 1]\n", " kappa = np.arange(-acf_range, acf_range)\n", "\n", " # plot PDF\n", " plt.figure(figsize=(10, 6))\n", " plt.subplot(121)\n", " plt.hist(x, nbins, density=True)\n", - " plt.title('Estimated PDF')\n", - " plt.xlabel(r'$\\theta$')\n", - " plt.ylabel(r'$\\hat{p}_n(\\theta)$')\n", + " plt.title(\"Estimated PDF\")\n", + " plt.xlabel(r\"$\\theta$\")\n", + " plt.ylabel(r\"$\\hat{p}_n(\\theta)$\")\n", " plt.grid()\n", "\n", " # plot ACF\n", " plt.subplot(122)\n", - " plt.stem(kappa, acf )\n", - " plt.title('Estimated ACF')\n", - " plt.ylabel(r'$\\hat{\\varphi}_{nn}[\\kappa]$')\n", - " plt.xlabel(r'$\\kappa$')\n", - " plt.axis([-acf_range, acf_range, 1.1*min(acf), 1.1*max(acf)])\n", + " plt.stem(kappa, acf)\n", + " plt.title(\"Estimated ACF\")\n", + " plt.ylabel(r\"$\\hat{\\varphi}_{nn}[\\kappa]$\")\n", + " plt.xlabel(r\"$\\kappa$\")\n", + " plt.axis([-acf_range, acf_range, 1.1 * min(acf), 1.1 * max(acf)])\n", " plt.grid()" ] }, @@ -3607,7 +3607,7 @@ } ], "source": [ - "noise = np.load('../data/amplifier_noise.npz')['noise']\n", + "noise = np.load(\"../data/amplifier_noise.npz\")[\"noise\"]\n", "estimate_plot_pdf_acf(noise, nbins=100, acf_range=150)" ] }, @@ -3636,7 +3636,7 @@ "mean = np.mean(noise)\n", "variance = np.var(noise)\n", "\n", - "print('Mean:\\t\\t {0:1.3e} \\nVariance:\\t {1:1.3e}'.format(mean, variance))" + "print(\"Mean:\\t\\t {0:1.3e} \\nVariance:\\t {1:1.3e}\".format(mean, variance))" ] }, { @@ -3664,7 +3664,7 @@ } ], "source": [ - "print('Level of amplifier noise: {:2.2f} dB'.format(10*np.log10(variance/1)))" + "print(\"Level of amplifier noise: {:2.2f} dB\".format(10 * np.log10(variance / 1)))" ] }, { @@ -5707,7 +5707,7 @@ ], "source": [ "np.random.seed(3)\n", - "estimate_plot_pdf_acf(np.random.uniform(size=10000)-1/2)" + "estimate_plot_pdf_acf(np.random.uniform(size=10000) - 1 / 2)" ] }, { @@ -5724,10 +5724,11 @@ "outputs": [], "source": [ "from scipy.io import wavfile\n", + "\n", "fs = 44100\n", "\n", - "x = np.random.uniform(size=5*fs)-1/2\n", - "wavfile.write('uniform_white_noise.wav', fs, np.int16(x*32768))" + "x = np.random.uniform(size=5 * fs) - 1 / 2\n", + "wavfile.write(\"uniform_white_noise.wav\", fs, np.int16(x * 32768))" ] }, { @@ -7860,7 +7861,7 @@ } ], "source": [ - "estimate_plot_pdf_acf(np.random.laplace(size=10000, loc=0, scale=1/np.sqrt(2)))" + "estimate_plot_pdf_acf(np.random.laplace(size=10000, loc=0, scale=1 / np.sqrt(2)))" ] }, { diff --git a/random_signals_LTI_systems/acoustic_impulse_response_measurement.ipynb b/random_signals_LTI_systems/acoustic_impulse_response_measurement.ipynb index d0e1e1c..3bd09a8 100644 --- a/random_signals_LTI_systems/acoustic_impulse_response_measurement.ipynb +++ b/random_signals_LTI_systems/acoustic_impulse_response_measurement.ipynb @@ -53,7 +53,7 @@ "T = 5 # length of the measurement signal in sec\n", "Tr = 2 # length of the expected system response in sec\n", "\n", - "x = np.random.uniform(-.5, .5, size=T*fs)" + "x = np.random.uniform(-0.5, 0.5, size=T * fs)" ] }, { @@ -82,13 +82,13 @@ } ], "source": [ - "x = np.concatenate((x, np.zeros(Tr*fs)))\n", + "x = np.concatenate((x, np.zeros(Tr * fs)))\n", "y = sd.playrec(x, fs, channels=1)\n", "sd.wait()\n", "y = np.squeeze(y)\n", "\n", - "print('Playback level: ', 20*np.log10(max(x)), ' dB')\n", - "print('Input level: ', 20*np.log10(max(y)), ' dB')" + "print(\"Playback level: \", 20 * np.log10(max(x)), \" dB\")\n", + "print(\"Input level: \", 20 * np.log10(max(y)), \" dB\")" ] }, { @@ -106,8 +106,8 @@ "metadata": {}, "outputs": [], "source": [ - "h = 1/len(y) * sig.fftconvolve(y, x[::-1], mode='full')\n", - "h = h[fs*(T+Tr):fs*(T+2*Tr)]" + "h = 1 / len(y) * sig.fftconvolve(y, x[::-1], mode=\"full\")\n", + "h = h[fs * (T + Tr) : fs * (T + 2 * Tr)]" ] }, { @@ -128,11 +128,11 @@ ], "source": [ "plt.figure(figsize=(10, 5))\n", - "t = 1/fs * np.arange(len(h))\n", + "t = 1 / fs * np.arange(len(h))\n", "plt.plot(t, h)\n", - "plt.axis([0.0, 1.0, -1.1*np.max(np.abs(h)), 1.1*np.max(np.abs(h))])\n", - "plt.xlabel(r'$t$ in s')\n", - "plt.ylabel(r'$\\hat{h}[k]$')" + "plt.axis([0.0, 1.0, -1.1 * np.max(np.abs(h)), 1.1 * np.max(np.abs(h))])\n", + "plt.xlabel(r\"$t$ in s\")\n", + "plt.ylabel(r\"$\\hat{h}[k]$\")" ] }, { diff --git a/random_signals_LTI_systems/correlation_functions.ipynb b/random_signals_LTI_systems/correlation_functions.ipynb index 0adaa64..ea48308 100644 --- a/random_signals_LTI_systems/correlation_functions.ipynb +++ b/random_signals_LTI_systems/correlation_functions.ipynb @@ -75,20 +75,20 @@ "np.random.seed(2)\n", "x = np.random.normal(size=L)\n", "# compute system response\n", - "y = np.convolve(x, [1, 1, 1, 1, 1], mode='full')\n", + "y = np.convolve(x, [1, 1, 1, 1, 1], mode=\"full\")\n", "\n", "# compute and truncate ACF\n", - "acf = 1/len(y) * np.correlate(y, y, mode='full')\n", - "acf = acf[len(y)-K-1:len(y)+K-1]\n", + "acf = 1 / len(y) * np.correlate(y, y, mode=\"full\")\n", + "acf = acf[len(y) - K - 1 : len(y) + K - 1]\n", "kappa = np.arange(-K, K)\n", "\n", "# plot ACF\n", "plt.figure(figsize=(10, 6))\n", "plt.stem(kappa, acf)\n", - "plt.title('Estimated ACF of output signal $y[k]$')\n", - "plt.ylabel(r'$\\hat{\\varphi}_{yy}[\\kappa]$')\n", - "plt.xlabel(r'$\\kappa$')\n", - "plt.axis([-K, K, 1.2*min(acf), 1.1*max(acf)])\n", + "plt.title(\"Estimated ACF of output signal $y[k]$\")\n", + "plt.ylabel(r\"$\\hat{\\varphi}_{yy}[\\kappa]$\")\n", + "plt.xlabel(r\"$\\kappa$\")\n", + "plt.axis([-K, K, 1.2 * min(acf), 1.1 * max(acf)])\n", "plt.grid()" ] }, @@ -221,23 +221,23 @@ "# impulse response of the system\n", "h = np.concatenate((np.zeros(10), sig.triang(10), np.zeros(10)))\n", "# output signal by convolution\n", - "y = np.convolve(h, x, mode='full')\n", + "y = np.convolve(h, x, mode=\"full\")\n", "\n", "\n", "def compute_correlation_function(x, y):\n", - " '''Compute correlation function/kappa.'''\n", + " \"\"\"Compute correlation function/kappa.\"\"\"\n", " N, M = len(x), len(y)\n", - " ccf = 1/N * np.correlate(x, y, mode='full')\n", - " kappa = np.arange(-M+1, N)\n", + " ccf = 1 / N * np.correlate(x, y, mode=\"full\")\n", + " kappa = np.arange(-M + 1, N)\n", "\n", " return ccf, kappa\n", "\n", "\n", "def plot_correlation_function(cf, kappa):\n", - " '''Plot correlation function.'''\n", + " \"\"\"Plot correlation function.\"\"\"\n", " plt.stem(kappa, cf)\n", - " plt.xlabel(r'$\\kappa$')\n", - " plt.axis([-K, K, -0.2, 1.1*max(cf)])\n", + " plt.xlabel(r\"$\\kappa$\")\n", + " plt.axis([-K, K, -0.2, 1.1 * max(cf)])\n", "\n", "\n", "# compute correlation functions\n", @@ -246,22 +246,22 @@ "ccfyx, kappayx = compute_correlation_function(y, x)\n", "\n", "# plot ACFs and CCF\n", - "plt.rc('figure', figsize=(10, 3))\n", + "plt.rc(\"figure\", figsize=(10, 3))\n", "plt.figure()\n", "plot_correlation_function(acfx, kappax)\n", - "plt.title('Estimated ACF of input signal')\n", - "plt.ylabel(r'$\\hat{\\varphi}_{xx}[\\kappa]$')\n", + "plt.title(\"Estimated ACF of input signal\")\n", + "plt.ylabel(r\"$\\hat{\\varphi}_{xx}[\\kappa]$\")\n", "\n", "plt.figure()\n", "plot_correlation_function(acfy, kappay)\n", - "plt.title('Estimated ACF of output signal')\n", - "plt.ylabel(r'$\\hat{\\varphi}_{yy}[\\kappa]$')\n", + "plt.title(\"Estimated ACF of output signal\")\n", + "plt.ylabel(r\"$\\hat{\\varphi}_{yy}[\\kappa]$\")\n", "\n", "plt.figure()\n", "plot_correlation_function(ccfyx, kappayx)\n", - "plt.plot(np.arange(len(h)), h, 'g-')\n", - "plt.title('Estimated and true impulse response')\n", - "plt.ylabel(r'$\\hat{\\varphi}_{yx}[k]$, $h[k]$');" + "plt.plot(np.arange(len(h)), h, \"g-\")\n", + "plt.title(\"Estimated and true impulse response\")\n", + "plt.ylabel(r\"$\\hat{\\varphi}_{yx}[k]$, $h[k]$\");" ] }, { diff --git a/random_signals_LTI_systems/introduction.ipynb b/random_signals_LTI_systems/introduction.ipynb index 6ecdea9..9b427b6 100644 --- a/random_signals_LTI_systems/introduction.ipynb +++ b/random_signals_LTI_systems/introduction.ipynb @@ -106,43 +106,43 @@ "np.random.seed(1)\n", "x = np.random.normal(size=(N, L))\n", "# generate output signal\n", - "h = 2*np.fft.irfft([1, 1, 1, 0, 0, 0])\n", - "y = np.asarray([np.convolve(x[n, :], h, mode='same') for n in range(N)])\n", + "h = 2 * np.fft.irfft([1, 1, 1, 0, 0, 0])\n", + "y = np.asarray([np.convolve(x[n, :], h, mode=\"same\") for n in range(N)])\n", "\n", "\n", "def compute_plot_results(x):\n", - " '''Compute and plot linear mean and ACF'''\n", + " \"\"\"Compute and plot linear mean and ACF\"\"\"\n", "\n", " # estimate linear mean by ensemble average\n", - " mu = 1/N * np.sum(x, 0)\n", + " mu = 1 / N * np.sum(x, 0)\n", " # estimate the auto-correlation function\n", " acf = np.zeros((L, L))\n", " for n in range(L):\n", " for m in range(L):\n", - " acf[n, m] = 1/N * np.sum(x[:, n]*x[:, m], 0)\n", + " acf[n, m] = 1 / N * np.sum(x[:, n] * x[:, m], 0)\n", "\n", " plt.subplot(121)\n", " plt.stem(mu)\n", - " plt.title(r'Estimate of linear mean $\\hat{\\mu}[k]$')\n", - " plt.xlabel(r'$k$')\n", - " plt.ylabel(r'$\\hat{\\mu}[k]$')\n", + " plt.title(r\"Estimate of linear mean $\\hat{\\mu}[k]$\")\n", + " plt.xlabel(r\"$k$\")\n", + " plt.ylabel(r\"$\\hat{\\mu}[k]$\")\n", " plt.axis([0, L, -1.5, 1.5])\n", "\n", " plt.subplot(122)\n", - " plt.pcolor(np.arange(L+1), np.arange(L+1), acf, vmin=-2, vmax=2)\n", - " plt.title(r'Estimate of ACF $\\hat{\\varphi}[k_1, k_2]$')\n", - " plt.xlabel(r'$k_1$')\n", - " plt.ylabel(r'$k_2$')\n", + " plt.pcolor(np.arange(L + 1), np.arange(L + 1), acf, vmin=-2, vmax=2)\n", + " plt.title(r\"Estimate of ACF $\\hat{\\varphi}[k_1, k_2]$\")\n", + " plt.xlabel(r\"$k_1$\")\n", + " plt.ylabel(r\"$k_2$\")\n", " plt.colorbar()\n", " plt.autoscale(tight=True)\n", "\n", "\n", "plt.figure(figsize=(10, 5))\n", - "plt.gcf().suptitle(r'Input signal $x[k]$', fontsize=12)\n", + "plt.gcf().suptitle(r\"Input signal $x[k]$\", fontsize=12)\n", "compute_plot_results(x)\n", "\n", "plt.figure(figsize=(10, 5))\n", - "plt.gcf().suptitle(r'Output signal $y[k]$', fontsize=12)\n", + "plt.gcf().suptitle(r\"Output signal $y[k]$\", fontsize=12)\n", "compute_plot_results(y)" ] }, diff --git a/random_signals_LTI_systems/linear_mean.ipynb b/random_signals_LTI_systems/linear_mean.ipynb index 2fc14c7..2c507f2 100644 --- a/random_signals_LTI_systems/linear_mean.ipynb +++ b/random_signals_LTI_systems/linear_mean.ipynb @@ -86,29 +86,29 @@ "# generate input signal (white Gaussian noise)\n", "np.random.seed(2)\n", "x = np.random.normal(size=(N, L))\n", - "x[:, L//2] += 1\n", + "x[:, L // 2] += 1\n", "# generate output signal\n", - "h = 2*np.fft.irfft([1, 1, 1, 0, 0, 0])\n", - "y = np.asarray([np.convolve(x[n, :], h, mode='full') for n in range(N)])\n", + "h = 2 * np.fft.irfft([1, 1, 1, 0, 0, 0])\n", + "y = np.asarray([np.convolve(x[n, :], h, mode=\"full\") for n in range(N)])\n", "\n", "\n", "def estimate_plot_linear_mean(x):\n", - " '''Estimate and plot linear mean.'''\n", + " \"\"\"Estimate and plot linear mean.\"\"\"\n", " # estimate linear mean by ensemble average\n", - " mu = 1/N * np.sum(x, 0)\n", + " mu = 1 / N * np.sum(x, 0)\n", " # plot linear mean\n", " plt.stem(mu)\n", - " plt.xlabel(r'$k$')\n", - " plt.ylabel(r'$\\hat{\\mu}[k]$')\n", + " plt.xlabel(r\"$k$\")\n", + " plt.ylabel(r\"$\\hat{\\mu}[k]$\")\n", " plt.axis([0, x.shape[1], -1.2, 1.2])\n", "\n", "\n", "plt.figure(figsize=(10, 3))\n", - "plt.title(r'Estimated linear mean $\\hat{\\mu}_x[k]$ of input signal')\n", + "plt.title(r\"Estimated linear mean $\\hat{\\mu}_x[k]$ of input signal\")\n", "estimate_plot_linear_mean(x)\n", "\n", "plt.figure(figsize=(10, 3))\n", - "plt.title(r'Estimated linear mean $\\hat{\\mu}_y[k]$ of output signal')\n", + "plt.title(r\"Estimated linear mean $\\hat{\\mu}_y[k]$ of output signal\")\n", "estimate_plot_linear_mean(y)" ] }, diff --git a/random_signals_LTI_systems/linear_prediction.ipynb b/random_signals_LTI_systems/linear_prediction.ipynb index cf1795f..ea61a89 100644 --- a/random_signals_LTI_systems/linear_prediction.ipynb +++ b/random_signals_LTI_systems/linear_prediction.ipynb @@ -160,8 +160,8 @@ "N = 8 # order of predictor (number of FIR coefficients)\n", "\n", "# read and truncate audio file\n", - "fs, x = wavfile.read('../data/vocal_o_8k.wav')\n", - "x = np.asarray(x, dtype=float)/2**15 # 16 Bit integer -> float\n", + "fs, x = wavfile.read(\"../data/vocal_o_8k.wav\")\n", + "x = np.asarray(x, dtype=float) / 2**15 # 16 Bit integer -> float\n", "x = x[:L]" ] }, @@ -188,9 +188,9 @@ "r = np.zeros(shape=(N, 1))\n", "\n", "for k in np.arange(N, L):\n", - " xk = np.expand_dims(np.flip(x[k-N:k]), 1)\n", - " R += 1/(L+2-N) * xk * xk.T\n", - " r += 1/(L+2-N) * xk * x[k]" + " xk = np.expand_dims(np.flip(x[k - N : k]), 1)\n", + " R += 1 / (L + 2 - N) * xk * xk.T\n", + " r += 1 / (L + 2 - N) * xk * x[k]" ] }, { @@ -1413,10 +1413,10 @@ "source": [ "plt.figure(figsize=(3, 3))\n", "plt.matshow(R)\n", - "plt.title(r'Estimate of auto-correlation matrix $\\mathbf{R}$')\n", + "plt.title(r\"Estimate of auto-correlation matrix $\\mathbf{R}$\")\n", "plt.colorbar(fraction=0.045)\n", - "plt.xlabel('sample shift')\n", - "plt.ylabel('sample shift');" + "plt.xlabel(\"sample shift\")\n", + "plt.ylabel(\"sample shift\");" ] }, { @@ -2072,9 +2072,9 @@ "h = np.squeeze(h)\n", "\n", "plt.figure(figsize=(8, 3))\n", - "plt.stem(h, basefmt='C0:')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$h_k$')\n", + "plt.stem(h, basefmt=\"C0:\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$h_k$\")\n", "plt.grid()" ] }, @@ -3846,10 +3846,10 @@ "xp = np.convolve(x, hfp)[:L]\n", "\n", "plt.figure(figsize=(8, 4))\n", - "plt.plot(x, label=r'signal $x[k]$', linestyle='--')\n", - "plt.plot(xp, label=r'predicted signal $\\hat{x}[k]$', alpha=.8)\n", - "plt.xlabel(r'$k$')\n", - "plt.axis([10000, 10500, -.6, .6])\n", + "plt.plot(x, label=r\"signal $x[k]$\", linestyle=\"--\")\n", + "plt.plot(xp, label=r\"predicted signal $\\hat{x}[k]$\", alpha=0.8)\n", + "plt.xlabel(r\"$k$\")\n", + "plt.axis([10000, 10500, -0.6, 0.6])\n", "plt.legend()\n", "plt.grid()" ] @@ -4983,9 +4983,9 @@ "\n", "plt.figure(figsize=(8, 4))\n", "plt.plot(e)\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$e[k]$')\n", - "plt.axis([10000, 10500, -.015, .015])\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$e[k]$\")\n", + "plt.axis([10000, 10500, -0.015, 0.015])\n", "plt.grid()" ] }, @@ -5011,8 +5011,8 @@ } ], "source": [ - "print('Variance of signal {:2.6f}'.format(np.var(x)))\n", - "print('Variance of error {:2.6f}'.format(np.var(e)))" + "print(\"Variance of signal {:2.6f}\".format(np.var(x)))\n", + "print(\"Variance of error {:2.6f}\".format(np.var(e)))" ] }, { @@ -6379,9 +6379,9 @@ "\n", "plt.figure(figsize=(8, 4))\n", "plt.plot(xe)\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'artifical speech signal $x[k]$')\n", - "plt.axis([10000, 10500, -.6, .6])\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"artifical speech signal $x[k]$\")\n", + "plt.axis([10000, 10500, -0.6, 0.6])\n", "plt.grid()" ] }, @@ -7682,20 +7682,20 @@ "\n", "\n", "def glottal_signal(f0):\n", - " '''Generates a synthetic glottal signal with fundamental frequency f0 in Hertz.'''\n", + " \"\"\"Generates a synthetic glottal signal with fundamental frequency f0 in Hertz.\"\"\"\n", " w0 = 2 * np.pi * f0\n", - " t = 1/fs * np.arange(L)\n", - " g = 1/2 * (1 + square(w0*t, duty=1/10))\n", + " t = 1 / fs * np.arange(L)\n", + " g = 1 / 2 * (1 + square(w0 * t, duty=1 / 10))\n", "\n", - " return - g + 0.1\n", + " return -g + 0.1\n", "\n", "\n", "g = 0.0055 * glottal_signal(100)\n", "\n", "plt.figure(figsize=(8, 4))\n", "plt.stem(g[:100])\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'glottal signal $g[k]$')\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"glottal signal $g[k]$\")\n", "plt.grid()" ] }, @@ -9034,13 +9034,11 @@ "\n", "f, Pee = welch(e, fs, nperseg=1024)\n", "f, Pgg = welch(g, fs, nperseg=1024)\n", - "plt.plot(f, Pee,\n", - " label=r'$\\hat{\\Phi}_{ee}(e^{j \\Omega})$ error signal')\n", - "plt.plot(f, Pgg,\n", - " label=r'$\\hat{\\Phi}_{gg}(e^{j \\Omega})$ synthetic glottal signal')\n", + "plt.plot(f, Pee, label=r\"$\\hat{\\Phi}_{ee}(e^{j \\Omega})$ error signal\")\n", + "plt.plot(f, Pgg, label=r\"$\\hat{\\Phi}_{gg}(e^{j \\Omega})$ synthetic glottal signal\")\n", "\n", "plt.xlim([0, 1000])\n", - "plt.xlabel('frequency in Hertz')\n", + "plt.xlabel(\"frequency in Hertz\")\n", "plt.legend()\n", "plt.grid()" ] @@ -10374,8 +10372,8 @@ "\n", "plt.figure(figsize=(8, 4))\n", "plt.plot(xa)\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'synthetic signal $x[k]$')\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"synthetic signal $x[k]$\")\n", "plt.axis([10000, 10500, -1, 1])\n", "plt.grid()" ] @@ -10393,7 +10391,7 @@ "metadata": {}, "outputs": [], "source": [ - "wavfile.write('artificial_vocal.wav', fs, np.int16(xa*2**15))" + "wavfile.write(\"artificial_vocal.wav\", fs, np.int16(xa * 2**15))" ] }, { diff --git a/random_signals_LTI_systems/power_spectral_densities.ipynb b/random_signals_LTI_systems/power_spectral_densities.ipynb index 4590655..14d33aa 100644 --- a/random_signals_LTI_systems/power_spectral_densities.ipynb +++ b/random_signals_LTI_systems/power_spectral_densities.ipynb @@ -1517,28 +1517,30 @@ "import scipy.signal as sig\n", "\n", "fs = 44100\n", - "N = 5*fs\n", + "N = 5 * fs\n", "\n", "# generate uniformly distributed white noise\n", "np.random.seed(1)\n", - "x = np.random.uniform(size=N) - .5\n", + "x = np.random.uniform(size=N) - 0.5\n", "# filter white noise to yield pink noise\n", "# see http://www.firstpr.com.au/dsp/pink-noise/#Filtering\n", "a = np.poly([0.99572754, 0.94790649, 0.53567505]) # denominator coefficients\n", "b = np.poly([0.98443604, 0.83392334, 0.07568359]) # numerator coefficients\n", - "y = 1/3 * sig.lfilter(b, a, x)\n", + "y = 1 / 3 * sig.lfilter(b, a, x)\n", "# estimate PSDs using Welch's technique\n", "f, Pxx = sig.csd(x, x, nperseg=256)\n", "f, Pyy = sig.csd(y, y, nperseg=256)\n", "\n", "# PSDs\n", "Om = f * 2 * np.pi\n", - "plt.plot(Om, 20*np.log10(np.abs(.5*Pxx)),\n", - " label=r'$| \\Phi_{xx}(e^{j \\Omega}) |$ in dB')\n", - "plt.plot(Om, 20*np.log10(np.abs(.5*Pyy)),\n", - " label=r'$| \\Phi_{yy}(e^{j \\Omega}) |$ in dB')\n", - "plt.title('Power Spectral Density')\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.plot(\n", + " Om, 20 * np.log10(np.abs(0.5 * Pxx)), label=r\"$| \\Phi_{xx}(e^{j \\Omega}) |$ in dB\"\n", + ")\n", + "plt.plot(\n", + " Om, 20 * np.log10(np.abs(0.5 * Pyy)), label=r\"$| \\Phi_{yy}(e^{j \\Omega}) |$ in dB\"\n", + ")\n", + "plt.title(\"Power Spectral Density\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.legend()\n", "plt.axis([0, np.pi, -60, -10])\n", "plt.grid()" @@ -1559,8 +1561,8 @@ "source": [ "from scipy.io import wavfile\n", "\n", - "wavfile.write('uniform_white_noise.wav', fs, np.int16(x*32768))\n", - "wavfile.write('uniform_pink_noise.wav', fs, np.int16(y*32768))" + "wavfile.write(\"uniform_white_noise.wav\", fs, np.int16(x * 32768))\n", + "wavfile.write(\"uniform_pink_noise.wav\", fs, np.int16(y * 32768))" ] }, { @@ -2883,25 +2885,25 @@ "# impulse response of the system\n", "h = np.concatenate((np.zeros(20), np.ones(10), np.zeros(20)))\n", "# output signal by convolution\n", - "y = np.convolve(h, x, mode='full')\n", + "y = np.convolve(h, x, mode=\"full\")\n", "# add noise to the output signal\n", - "y = y + np.random.normal(size=y.shape, scale=.1)\n", + "y = y + np.random.normal(size=y.shape, scale=0.1)\n", "\n", "# zero-padding of input signal\n", - "x = np.concatenate((x, np.zeros(len(h)-1)))\n", + "x = np.concatenate((x, np.zeros(len(h) - 1)))\n", "# estimate transfer function\n", - "H = np.fft.rfft(y)/np.fft.rfft(x)\n", + "H = np.fft.rfft(y) / np.fft.rfft(x)\n", "# compute inpulse response\n", "he = np.fft.irfft(H)\n", - "he = he[0:len(h)]\n", + "he = he[0 : len(h)]\n", "\n", "# plot impulse response\n", "plt.figure()\n", - "plt.stem(he, label='estimated')\n", - "plt.plot(h, 'g-', label='true')\n", - "plt.title('Estimated impulse response')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$\\hat{h}[k]$')\n", + "plt.stem(he, label=\"estimated\")\n", + "plt.plot(h, \"g-\", label=\"true\")\n", + "plt.title(\"Estimated impulse response\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$\\hat{h}[k]$\")\n", "plt.legend();" ] }, diff --git a/random_signals_LTI_systems/wiener_filter.ipynb b/random_signals_LTI_systems/wiener_filter.ipynb index 5d41a63..e59baf4 100644 --- a/random_signals_LTI_systems/wiener_filter.ipynb +++ b/random_signals_LTI_systems/wiener_filter.ipynb @@ -3454,48 +3454,51 @@ "\n", "N = 2**14 # number of samples\n", "M = 256 # length of Wiener filter\n", - "Om0 = 0.1*np.pi # frequency of original signal\n", + "Om0 = 0.1 * np.pi # frequency of original signal\n", "N0 = 0.1 # PSD of additive white noise\n", "\n", "# generate original signal\n", "s = np.cos(Om0 * np.arange(N))\n", "# generate observed signal\n", - "g = 1/20*np.asarray([1, 2, 3, 4, 5, 4, 3, 2, 1])\n", + "g = 1 / 20 * np.asarray([1, 2, 3, 4, 5, 4, 3, 2, 1])\n", "np.random.seed(1)\n", "n = np.random.normal(size=N, scale=np.sqrt(N0))\n", - "x = np.convolve(s, g, mode='same') + n\n", + "x = np.convolve(s, g, mode=\"same\") + n\n", "# estimate (cross) PSDs using Welch technique\n", "f, Pxx = sig.csd(x, x, nperseg=M)\n", "f, Psx = sig.csd(s, x, nperseg=M)\n", "# compute Wiener filter\n", - "H = Psx/Pxx\n", - "H = H * np.exp(-1j*2*np.pi/len(H)*np.arange(len(H)) *\n", - " (len(H)//2)) # shift for causal filter\n", + "H = Psx / Pxx\n", + "H = H * np.exp(\n", + " -1j * 2 * np.pi / len(H) * np.arange(len(H)) * (len(H) // 2)\n", + ") # shift for causal filter\n", "h = np.fft.irfft(H)\n", "# apply Wiener filter to observation\n", - "y = np.convolve(x, h, mode='same')\n", + "y = np.convolve(x, h, mode=\"same\")\n", "\n", "# plot (cross) PSDs\n", "Om = np.linspace(0, np.pi, num=len(H))\n", "\n", "plt.figure(figsize=(10, 4))\n", "plt.subplot(121)\n", - "plt.plot(Om, 20*np.log10(np.abs(.5*Pxx)),\n", - " label=r'$| \\Phi_{xx}(e^{j \\Omega}) |$ in dB')\n", - "plt.plot(Om, 20*np.log10(np.abs(.5*Psx)),\n", - " label=r'$| \\Phi_{sx}(e^{j \\Omega}) |$ in dB')\n", - "plt.title('(Cross) PSDs')\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.plot(\n", + " Om, 20 * np.log10(np.abs(0.5 * Pxx)), label=r\"$| \\Phi_{xx}(e^{j \\Omega}) |$ in dB\"\n", + ")\n", + "plt.plot(\n", + " Om, 20 * np.log10(np.abs(0.5 * Psx)), label=r\"$| \\Phi_{sx}(e^{j \\Omega}) |$ in dB\"\n", + ")\n", + "plt.title(\"(Cross) PSDs\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.legend()\n", "plt.axis([0, np.pi, -60, 40])\n", "plt.grid()\n", "\n", "# plot transfer function of Wiener filter\n", "plt.subplot(122)\n", - "plt.plot(Om, 20*np.log10(np.abs(H)))\n", - "plt.title('Transfer function of Wiener filter')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$| H(e^{j \\Omega}) |$ in dB')\n", + "plt.plot(Om, 20 * np.log10(np.abs(H)))\n", + "plt.title(\"Transfer function of Wiener filter\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$| H(e^{j \\Omega}) |$ in dB\")\n", "plt.axis([0, np.pi, -150, 3])\n", "plt.grid()\n", "plt.tight_layout()\n", @@ -3503,11 +3506,11 @@ "# plot signals\n", "idx = np.arange(500, 600)\n", "plt.figure(figsize=(10, 4))\n", - "plt.plot(idx, x[idx], label=r'observed signal $x[k]$')\n", - "plt.plot(idx, s[idx], label=r'original signal $s[k]$')\n", - "plt.plot(idx, y[idx], label=r'estimated signal $y[k]$')\n", - "plt.title('Signals')\n", - "plt.xlabel(r'$k$')\n", + "plt.plot(idx, x[idx], label=r\"observed signal $x[k]$\")\n", + "plt.plot(idx, s[idx], label=r\"original signal $s[k]$\")\n", + "plt.plot(idx, y[idx], label=r\"estimated signal $y[k]$\")\n", + "plt.title(\"Signals\")\n", + "plt.xlabel(r\"$k$\")\n", "plt.axis([idx[0], idx[-1], -1.5, 1.5])\n", "plt.legend()\n", "plt.grid()" @@ -3529,8 +3532,8 @@ "from scipy.io import wavfile\n", "\n", "fs = 8000\n", - "wavfile.write('Wiener_observed_signal.wav', fs, np.int16(x*16384))\n", - "wavfile.write('Wiener_output.wav', fs, np.int16(y*16384))" + "wavfile.write(\"Wiener_observed_signal.wav\", fs, np.int16(x * 16384))\n", + "wavfile.write(\"Wiener_output.wav\", fs, np.int16(y * 16384))" ] }, { @@ -6887,49 +6890,52 @@ "source": [ "N = 2**14 # number of samples\n", "M = 256 # length of Wiener filter\n", - "Om0 = 0.1*np.pi # frequency of original signal\n", - "N0 = .1 # PSD of additive white noise\n", + "Om0 = 0.1 * np.pi # frequency of original signal\n", + "N0 = 0.1 # PSD of additive white noise\n", "\n", "# generate original signal\n", "s = np.cos(Om0 * np.arange(N))\n", "# generate observed signal\n", - "g = 1/20*np.asarray([1, 2, 3, 4, 5, 4, 3, 2, 1])\n", + "g = 1 / 20 * np.asarray([1, 2, 3, 4, 5, 4, 3, 2, 1])\n", "np.random.seed(1)\n", "n = np.random.normal(size=N, scale=np.sqrt(N0))\n", - "x = np.convolve(s, g, mode='same') + n\n", + "x = np.convolve(s, g, mode=\"same\") + n\n", "# estimate PSD\n", "f, Pss = sig.csd(s, s, nperseg=M)\n", "f, Pnn = sig.csd(n, n, nperseg=M)\n", "# compute Wiener filter\n", "G = np.fft.rfft(g, M)\n", - "H = 1/G * (np.abs(G)**2 / (np.abs(G)**2 + N0/Pss))\n", - "H = H * np.exp(-1j*2*np.pi/len(H)*np.arange(len(H)) *\n", - " (len(H)//2-8)) # shift for causal filter\n", + "H = 1 / G * (np.abs(G) ** 2 / (np.abs(G) ** 2 + N0 / Pss))\n", + "H = H * np.exp(\n", + " -1j * 2 * np.pi / len(H) * np.arange(len(H)) * (len(H) // 2 - 8)\n", + ") # shift for causal filter\n", "h = np.fft.irfft(H)\n", "# apply Wiener filter to observation\n", - "y = np.convolve(x, h, mode='same')\n", + "y = np.convolve(x, h, mode=\"same\")\n", "\n", "# plot (cross) PSDs\n", "Om = np.linspace(0, np.pi, num=len(H))\n", "\n", "plt.figure(figsize=(10, 4))\n", "plt.subplot(121)\n", - "plt.plot(Om, 20*np.log10(np.abs(.5*Pss)),\n", - " label=r'$| \\Phi_{ss}(e^{j \\Omega}) |$ in dB')\n", - "plt.plot(Om, 20*np.log10(np.abs(.5*Pnn)),\n", - " label=r'$| \\Phi_{nn}(e^{j \\Omega}) |$ in dB')\n", - "plt.title('PSDs')\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.plot(\n", + " Om, 20 * np.log10(np.abs(0.5 * Pss)), label=r\"$| \\Phi_{ss}(e^{j \\Omega}) |$ in dB\"\n", + ")\n", + "plt.plot(\n", + " Om, 20 * np.log10(np.abs(0.5 * Pnn)), label=r\"$| \\Phi_{nn}(e^{j \\Omega}) |$ in dB\"\n", + ")\n", + "plt.title(\"PSDs\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.legend()\n", "plt.axis([0, np.pi, -60, 40])\n", "plt.grid()\n", "\n", "# plot transfer function of Wiener filter\n", "plt.subplot(122)\n", - "plt.plot(Om, 20*np.log10(np.abs(H)))\n", - "plt.title('Transfer function of Wiener filter')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$| H(e^{j \\Omega}) |$ in dB')\n", + "plt.plot(Om, 20 * np.log10(np.abs(H)))\n", + "plt.title(\"Transfer function of Wiener filter\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$| H(e^{j \\Omega}) |$ in dB\")\n", "plt.axis([0, np.pi, -150, 10])\n", "plt.grid()\n", "plt.tight_layout()\n", @@ -6937,11 +6943,11 @@ "# plot signals\n", "idx = np.arange(500, 600)\n", "plt.figure(figsize=(10, 4))\n", - "plt.plot(idx, x[idx], label=r'observed signal $x[k]$')\n", - "plt.plot(idx, s[idx], label=r'original signal $s[k]$')\n", - "plt.plot(idx, y[idx], label=r'estimated signal $y[k]$')\n", - "plt.title('Signals')\n", - "plt.xlabel(r'$k$')\n", + "plt.plot(idx, x[idx], label=r\"observed signal $x[k]$\")\n", + "plt.plot(idx, s[idx], label=r\"original signal $s[k]$\")\n", + "plt.plot(idx, y[idx], label=r\"estimated signal $y[k]$\")\n", + "plt.title(\"Signals\")\n", + "plt.xlabel(r\"$k$\")\n", "plt.axis([idx[0], idx[-1], -1.5, 1.5])\n", "plt.legend()\n", "plt.grid()" diff --git a/recursive_filters/cascaded_structures.ipynb b/recursive_filters/cascaded_structures.ipynb index e1f99e9..f924141 100644 --- a/recursive_filters/cascaded_structures.ipynb +++ b/recursive_filters/cascaded_structures.ipynb @@ -4728,22 +4728,23 @@ "N = 9 # order of recursive filter\n", "\n", "\n", - "def zplane(z, p, title='Poles and Zeros'):\n", + "def zplane(z, p, title=\"Poles and Zeros\"):\n", " \"Plots zero and pole locations in the complex z-plane\"\n", " ax = plt.gca()\n", "\n", - " ax.plot(np.real(z), np.imag(z), 'bo', fillstyle='none', ms=10)\n", - " ax.plot(np.real(p), np.imag(p), 'rx', fillstyle='none', ms=10)\n", - " unit_circle = Circle((0, 0), radius=1, fill=False,\n", - " color='black', ls='solid', alpha=0.9)\n", + " ax.plot(np.real(z), np.imag(z), \"bo\", fillstyle=\"none\", ms=10)\n", + " ax.plot(np.real(p), np.imag(p), \"rx\", fillstyle=\"none\", ms=10)\n", + " unit_circle = Circle(\n", + " (0, 0), radius=1, fill=False, color=\"black\", ls=\"solid\", alpha=0.9\n", + " )\n", " ax.add_patch(unit_circle)\n", - " ax.axvline(0, color='0.7')\n", - " ax.axhline(0, color='0.7')\n", + " ax.axvline(0, color=\"0.7\")\n", + " ax.axhline(0, color=\"0.7\")\n", "\n", " plt.title(title)\n", - " plt.xlabel(r'Re{$z$}')\n", - " plt.ylabel(r'Im{$z$}')\n", - " plt.axis('equal')\n", + " plt.xlabel(r\"Re{$z$}\")\n", + " plt.ylabel(r\"Im{$z$}\")\n", + " plt.axis(\"equal\")\n", " plt.xlim((-2, 2))\n", " plt.ylim((-2, 2))\n", " plt.grid()\n", @@ -4752,37 +4753,40 @@ "# design filter\n", "b, a = sig.butter(N, 0.2)\n", "# decomposition into SOS\n", - "sos = sig.tf2sos(b, a, pairing='nearest')\n", + "sos = sig.tf2sos(b, a, pairing=\"nearest\")\n", "\n", "\n", "# print filter coefficients\n", - "print('Coefficients of the recursive part \\n')\n", - "print(['%1.2f' % ai for ai in a])\n", - "print('\\n')\n", - "print('Coefficients of the recursive part of the individual SOS \\n')\n", - "print('Section \\t a1 \\t\\t a2')\n", + "print(\"Coefficients of the recursive part \\n\")\n", + "print([\"%1.2f\" % ai for ai in a])\n", + "print(\"\\n\")\n", + "print(\"Coefficients of the recursive part of the individual SOS \\n\")\n", + "print(\"Section \\t a1 \\t\\t a2\")\n", "for n in range(sos.shape[0]):\n", - " print('%d \\t\\t %1.5f \\t %1.5f' % (n, sos[n, 4], sos[n, 5]))\n", + " print(\"%d \\t\\t %1.5f \\t %1.5f\" % (n, sos[n, 4], sos[n, 5]))\n", "\n", "# plot pole and zero locations\n", "plt.figure(figsize=(5, 5))\n", - "zplane(np.roots(b), np.roots(a), 'Poles and Zeros - Overall')\n", + "zplane(np.roots(b), np.roots(a), \"Poles and Zeros - Overall\")\n", "\n", "plt.figure(figsize=(10, 7))\n", "for n in range(sos.shape[0]):\n", - " plt.subplot(231+n)\n", - " zplane(np.roots(sos[n, 0:3]), np.roots(sos[n, 3:6]),\n", - " title='Poles and Zeros - Section %d' % n)\n", + " plt.subplot(231 + n)\n", + " zplane(\n", + " np.roots(sos[n, 0:3]),\n", + " np.roots(sos[n, 3:6]),\n", + " title=\"Poles and Zeros - Section %d\" % n,\n", + " )\n", "plt.tight_layout()\n", "\n", "# compute and plot frequency response of sections\n", "plt.figure(figsize=(10, 5))\n", "for n in range(sos.shape[0]):\n", " Om, H = sig.freqz(sos[n, 0:3], sos[n, 3:6])\n", - " plt.plot(Om, 20*np.log10(np.abs(H)), label=r'Section %d' % n)\n", + " plt.plot(Om, 20 * np.log10(np.abs(H)), label=r\"Section %d\" % n)\n", "\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$|H_n(e^{j \\Omega})|$ in dB')\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$|H_n(e^{j \\Omega})|$ in dB\")\n", "plt.legend()\n", "plt.grid()" ] diff --git a/recursive_filters/direct_forms.ipynb b/recursive_filters/direct_forms.ipynb index 1d3b8a6..cb1953a 100644 --- a/recursive_filters/direct_forms.ipynb +++ b/recursive_filters/direct_forms.ipynb @@ -1170,7 +1170,7 @@ "import matplotlib.pyplot as plt\n", "import scipy.signal as sig\n", "\n", - "p = 0.90*np.exp(-1j*np.pi/4)\n", + "p = 0.90 * np.exp(-1j * np.pi / 4)\n", "a = np.poly([p, np.conj(p)]) # denominator coefficients\n", "b = [1, 0, 0] # numerator coefficients\n", "N = 40 # number of computed samples\n", @@ -1184,10 +1184,10 @@ "\n", "# plot output signal\n", "plt.figure(figsize=(8, 4))\n", - "plt.stem(h )\n", - "plt.title('Impulse response')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$h[k]$')\n", + "plt.stem(h)\n", + "plt.title(\"Impulse response\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$h[k]$\")\n", "plt.axis([-1, N, -1.5, 1.5])\n", "plt.grid()" ] diff --git a/recursive_filters/introduction.ipynb b/recursive_filters/introduction.ipynb index 69394a1..944b49e 100644 --- a/recursive_filters/introduction.ipynb +++ b/recursive_filters/introduction.ipynb @@ -4686,29 +4686,30 @@ "L = 128 # number of computed samples\n", "\n", "\n", - "def zplane(z, p, title='Poles and Zeros'):\n", + "def zplane(z, p, title=\"Poles and Zeros\"):\n", " \"Plots zero and pole locations in the complex z-plane\"\n", " ax = plt.gca()\n", "\n", - " ax.plot(np.real(z), np.imag(z), 'bo', fillstyle='none', ms=10)\n", - " ax.plot(np.real(p), np.imag(p), 'rx', fillstyle='none', ms=10)\n", - " unit_circle = Circle((0, 0), radius=1, fill=False,\n", - " color='black', ls='solid', alpha=0.9)\n", + " ax.plot(np.real(z), np.imag(z), \"bo\", fillstyle=\"none\", ms=10)\n", + " ax.plot(np.real(p), np.imag(p), \"rx\", fillstyle=\"none\", ms=10)\n", + " unit_circle = Circle(\n", + " (0, 0), radius=1, fill=False, color=\"black\", ls=\"solid\", alpha=0.9\n", + " )\n", " ax.add_patch(unit_circle)\n", - " ax.axvline(0, color='0.7')\n", - " ax.axhline(0, color='0.7')\n", + " ax.axvline(0, color=\"0.7\")\n", + " ax.axhline(0, color=\"0.7\")\n", "\n", " plt.title(title)\n", - " plt.xlabel(r'Re{$z$}')\n", - " plt.ylabel(r'Im{$z$}')\n", - " plt.axis('equal')\n", + " plt.xlabel(r\"Re{$z$}\")\n", + " plt.ylabel(r\"Im{$z$}\")\n", + " plt.axis(\"equal\")\n", " plt.xlim((-2, 2))\n", " plt.ylim((-2, 2))\n", " plt.grid()\n", "\n", "\n", "# compute coefficients of recursive filter\n", - "b, a = sig.butter(N, 0.2, 'low')\n", + "b, a = sig.butter(N, 0.2, \"low\")\n", "# compute transfer function\n", "Om, H = sig.freqz(b, a)\n", "# compute impulse response\n", @@ -4722,24 +4723,24 @@ "# plot magnitude response\n", "plt.figure(figsize=(10, 3))\n", "plt.plot(Om, 20 * np.log10(abs(H)))\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$|H(e^{j \\Omega})|$ in dB')\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$|H(e^{j \\Omega})|$ in dB\")\n", "plt.grid()\n", - "plt.title('Magnitude response')\n", + "plt.title(\"Magnitude response\")\n", "# plot phase response\n", "plt.figure(figsize=(10, 3))\n", "plt.plot(Om, np.unwrap(np.angle(H)))\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\varphi (\\Omega)$ in rad')\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\varphi (\\Omega)$ in rad\")\n", "plt.grid()\n", - "plt.title('Phase response')\n", + "plt.title(\"Phase response\")\n", "# plot impulse response (magnitude)\n", "plt.figure(figsize=(10, 3))\n", - "plt.stem(20*np.log10(np.abs(np.squeeze(h))) )\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$|h[k]|$ in dB')\n", + "plt.stem(20 * np.log10(np.abs(np.squeeze(h))))\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$|h[k]|$ in dB\")\n", "plt.grid()\n", - "plt.title('Impulse response (magnitude)')" + "plt.title(\"Impulse response (magnitude)\")" ] }, { diff --git a/recursive_filters/quantization_of_coefficients.ipynb b/recursive_filters/quantization_of_coefficients.ipynb index a9aa6cb..e0e862e 100644 --- a/recursive_filters/quantization_of_coefficients.ipynb +++ b/recursive_filters/quantization_of_coefficients.ipynb @@ -5201,43 +5201,48 @@ "\n", "\n", "def compute_pole_locations(Q):\n", - " '''Compute grid of potential pole locations for direct form.'''\n", - " a1 = np.arange(-2, 2+Q, Q)\n", - " a2 = np.arange(0, 1+Q, Q)\n", + " \"\"\"Compute grid of potential pole locations for direct form.\"\"\"\n", + " a1 = np.arange(-2, 2 + Q, Q)\n", + " a2 = np.arange(0, 1 + Q, Q)\n", "\n", - " p = np.asarray([np.roots([1, n, m])\n", - " for (n, m) in itertools.product(a1, a2)])\n", + " p = np.asarray([np.roots([1, n, m]) for (n, m) in itertools.product(a1, a2)])\n", " p = p[np.imag(p) != 0]\n", "\n", " return p\n", "\n", "\n", "def plot_pole_locations(p, Q):\n", - " '''Visualize potential pole locations.'''\n", + " \"\"\"Visualize potential pole locations.\"\"\"\n", " ax = plt.gca()\n", - " for n in np.arange(np.ceil(2/Q)+1):\n", - " circle = Circle((0, 0), radius=np.sqrt(n*Q), fill=False,\n", - " color='black', ls='solid', alpha=0.05)\n", + " for n in np.arange(np.ceil(2 / Q) + 1):\n", + " circle = Circle(\n", + " (0, 0),\n", + " radius=np.sqrt(n * Q),\n", + " fill=False,\n", + " color=\"black\",\n", + " ls=\"solid\",\n", + " alpha=0.05,\n", + " )\n", " ax.add_patch(circle)\n", - " ax.axvline(.5*n*Q, color='0.95')\n", - " ax.axvline(-.5*n*Q, color='0.95')\n", + " ax.axvline(0.5 * n * Q, color=\"0.95\")\n", + " ax.axvline(-0.5 * n * Q, color=\"0.95\")\n", "\n", - " unit_circle = Circle((0, 0), radius=1, fill=False, color='red', ls='solid')\n", + " unit_circle = Circle((0, 0), radius=1, fill=False, color=\"red\", ls=\"solid\")\n", " ax.add_patch(unit_circle)\n", "\n", - " plt.plot(np.real(p), np.imag(p), 'b.', ms=4)\n", - " plt.xlabel(r'Re{$z$}')\n", - " plt.ylabel(r'Im{$z$}')\n", + " plt.plot(np.real(p), np.imag(p), \"b.\", ms=4)\n", + " plt.xlabel(r\"Re{$z$}\")\n", + " plt.ylabel(r\"Im{$z$}\")\n", " plt.axis([-1.1, 1.1, -1.1, 1.1])\n", "\n", "\n", "# compute and plot pole locations\n", "for w in [5, 6]:\n", - " Q = 2/(2**(w-1)) # quantization stepsize\n", + " Q = 2 / (2 ** (w - 1)) # quantization stepsize\n", " plt.figure(figsize=(5, 5))\n", " p = compute_pole_locations(Q)\n", " plot_pole_locations(p, Q)\n", - " plt.title(r'Direct form coefficient quantization to $w=%d$ bits' % w)" + " plt.title(r\"Direct form coefficient quantization to $w=%d$ bits\" % w)" ] }, { @@ -11625,27 +11630,28 @@ ], "source": [ "def compute_pole_locations(w):\n", - " '''Compute potential pole locations for coupled form.'''\n", - " Q = 1/(2**(w-1)) # quantization stepsize\n", - " a1 = np.arange(-1, 1+Q, Q)\n", - " a2 = np.arange(-1, 1+Q, Q)\n", + " \"\"\"Compute potential pole locations for coupled form.\"\"\"\n", + " Q = 1 / (2 ** (w - 1)) # quantization stepsize\n", + " a1 = np.arange(-1, 1 + Q, Q)\n", + " a2 = np.arange(-1, 1 + Q, Q)\n", "\n", " p = np.asarray(\n", - " [n+1j*m for (n, m) in itertools.product(a1, a2) if n**2+m**2 <= 1])\n", + " [n + 1j * m for (n, m) in itertools.product(a1, a2) if n ** 2 + m**2 <= 1]\n", + " )\n", "\n", " return p\n", "\n", "\n", "def plot_pole_locations(p):\n", - " '''Visualize potential pole locations.'''\n", + " \"\"\"Visualize potential pole locations.\"\"\"\n", " ax = plt.gca()\n", "\n", - " unit_circle = Circle((0, 0), radius=1, fill=False, color='red', ls='solid')\n", + " unit_circle = Circle((0, 0), radius=1, fill=False, color=\"red\", ls=\"solid\")\n", " ax.add_patch(unit_circle)\n", "\n", - " plt.plot(np.real(p), np.imag(p), 'b.', ms=4)\n", - " plt.xlabel(r'Re{$z$}')\n", - " plt.ylabel(r'Im{$z$}')\n", + " plt.plot(np.real(p), np.imag(p), \"b.\", ms=4)\n", + " plt.xlabel(r\"Re{$z$}\")\n", + " plt.ylabel(r\"Im{$z$}\")\n", " plt.axis([-1.1, 1.1, -1.1, 1.1])\n", "\n", "\n", @@ -11654,7 +11660,7 @@ " plt.figure(figsize=(5, 5))\n", " p = compute_pole_locations(w)\n", " plot_pole_locations(p)\n", - " plt.title(r'Coupled form coefficient quantization to $w=%d$ bits' % w)" + " plt.title(r\"Coupled form coefficient quantization to $w=%d$ bits\" % w)" ] }, { @@ -14096,9 +14102,9 @@ "\n", "\n", "def uniform_midtread_quantizer(x, w, xmin=1):\n", - " '''Uniform mid-tread quantizer with limiter.'''\n", + " \"\"\"Uniform mid-tread quantizer with limiter.\"\"\"\n", " # quantization step\n", - " Q = xmin/(2**(w-1))\n", + " Q = xmin / (2 ** (w - 1))\n", " # limiter\n", " x = np.copy(x)\n", " idx = np.where(x <= -xmin)\n", @@ -14106,37 +14112,38 @@ " idx = np.where(x > xmin - Q)\n", " x[idx] = 1 - Q\n", " # linear uniform quantization\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", "\n", " return xQ\n", "\n", "\n", - "def zplane(z, p, title='Poles and Zeros'):\n", + "def zplane(z, p, title=\"Poles and Zeros\"):\n", " \"Plots zero and pole locations in the complex z-plane\"\n", " ax = plt.gca()\n", "\n", - " ax.plot(np.real(z), np.imag(z), 'bo', fillstyle='none', ms=10)\n", - " ax.plot(np.real(p), np.imag(p), 'rx', fillstyle='none', ms=10)\n", - " unit_circle = Circle((0, 0), radius=1, fill=False,\n", - " color='black', ls='solid', alpha=0.9)\n", + " ax.plot(np.real(z), np.imag(z), \"bo\", fillstyle=\"none\", ms=10)\n", + " ax.plot(np.real(p), np.imag(p), \"rx\", fillstyle=\"none\", ms=10)\n", + " unit_circle = Circle(\n", + " (0, 0), radius=1, fill=False, color=\"black\", ls=\"solid\", alpha=0.9\n", + " )\n", " ax.add_patch(unit_circle)\n", - " ax.axvline(0, color='0.7')\n", - " ax.axhline(0, color='0.7')\n", + " ax.axvline(0, color=\"0.7\")\n", + " ax.axhline(0, color=\"0.7\")\n", "\n", " plt.title(title)\n", - " plt.xlabel(r'Re{$z$}')\n", - " plt.ylabel(r'Im{$z$}')\n", - " plt.axis('equal')\n", + " plt.xlabel(r\"Re{$z$}\")\n", + " plt.ylabel(r\"Im{$z$}\")\n", + " plt.axis(\"equal\")\n", " plt.xlim((-2, 2))\n", " plt.ylim((-2, 2))\n", " plt.grid()\n", "\n", "\n", "# coefficients of recursive filter\n", - "b, a = sig.butter(N, 0.2, 'low')\n", + "b, a = sig.butter(N, 0.2, \"low\")\n", "# decomposition into SOS\n", - "sos = sig.tf2sos(b, a, pairing='nearest')\n", - "sos = sos/np.amax(np.abs(sos))\n", + "sos = sig.tf2sos(b, a, pairing=\"nearest\")\n", + "sos = sos / np.amax(np.abs(sos))\n", "# quantization of SOS coefficients\n", "sosq = uniform_midtread_quantizer(sos, w, xmin=1)\n", "# compute overall transfer function of (quantized) filter\n", @@ -14151,20 +14158,20 @@ "\n", "# plot magnitude responses\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot(Om, 20 * np.log10(abs(H)), label='continuous')\n", - "plt.plot(Om, 20 * np.log10(abs(Hq)), label='quantized')\n", - "plt.title('Magnitude response')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$|H(e^{j \\Omega})|$ in dB')\n", + "plt.plot(Om, 20 * np.log10(abs(H)), label=\"continuous\")\n", + "plt.plot(Om, 20 * np.log10(abs(Hq)), label=\"quantized\")\n", + "plt.title(\"Magnitude response\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$|H(e^{j \\Omega})|$ in dB\")\n", "plt.legend(loc=3)\n", "plt.grid()\n", "# plot phase responses\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot(Om, np.unwrap(np.angle(H)), label='continuous')\n", - "plt.plot(Om, np.unwrap(np.angle(Hq)), label='quantized')\n", - "plt.title('Phase')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\varphi (\\Omega)$ in rad')\n", + "plt.plot(Om, np.unwrap(np.angle(H)), label=\"continuous\")\n", + "plt.plot(Om, np.unwrap(np.angle(Hq)), label=\"quantized\")\n", + "plt.title(\"Phase\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\varphi (\\Omega)$ in rad\")\n", "plt.legend(loc=3)\n", "plt.grid()" ] diff --git a/recursive_filters/quantization_of_variables.ipynb b/recursive_filters/quantization_of_variables.ipynb index 1cb71bc..d46f364 100644 --- a/recursive_filters/quantization_of_variables.ipynb +++ b/recursive_filters/quantization_of_variables.ipynb @@ -1658,29 +1658,29 @@ "\n", "\n", "def uniform_midtread_quantizer(x):\n", - " '''Uniform mid-tread quantizer w/o limiter.'''\n", - " return Q * np.floor(x/Q + 1/2)\n", + " \"\"\"Uniform mid-tread quantizer w/o limiter.\"\"\"\n", + " return Q * np.floor(x / Q + 1 / 2)\n", "\n", "\n", "def no_quantizer(x):\n", - " '''Dummy quantizer.'''\n", + " \"\"\"Dummy quantizer.\"\"\"\n", " return x\n", "\n", "\n", "def sos_df1(x, a, requantize=None):\n", - " '''Realization of a recursive SOS with round-off of multiplications.'''\n", - " y = np.zeros(len(x)+2) # initial value appended\n", + " \"\"\"Realization of a recursive SOS with round-off of multiplications.\"\"\"\n", + " y = np.zeros(len(x) + 2) # initial value appended\n", " for k in range(len(x)):\n", - " y[k] = x[k] - requantize(a[1]*y[k-1]) - requantize(a[2]*y[k-2])\n", + " y[k] = x[k] - requantize(a[1] * y[k - 1]) - requantize(a[2] * y[k - 2])\n", "\n", " return y[0:-2]\n", "\n", "\n", "# cofficients of the SOS\n", - "p = 0.90*np.array([np.exp(1j*np.pi/3), np.exp(-1j*np.pi/3)])\n", + "p = 0.90 * np.array([np.exp(1j * np.pi / 3), np.exp(-1j * np.pi / 3)])\n", "a = np.poly(p)\n", "# quantization step\n", - "Q = 1/(2**(w-1))\n", + "Q = 1 / (2 ** (w - 1))\n", "\n", "# compute input signal\n", "x = np.random.uniform(low=-1, high=1, size=N)\n", @@ -1688,26 +1688,26 @@ "yQ = sos_df1(x, a, requantize=uniform_midtread_quantizer)\n", "y = sos_df1(x, a, requantize=no_quantizer)\n", "# compute requantization error\n", - "e = yQ-y\n", + "e = yQ - y\n", "# Signal-to-noise ratio\n", - "SNR = 10*np.log10(np.var(y)/np.var(e))\n", - "print('SNR due to requantization: %f dB' % SNR)\n", + "SNR = 10 * np.log10(np.var(y) / np.var(e))\n", + "print(\"SNR due to requantization: %f dB\" % SNR)\n", "\n", "# estimate PSD of requantization error\n", - "nf, Pxx = sig.welch(e, window='hamming', nperseg=256, noverlap=128)\n", - "Pxx = .5*Pxx # due to normalization in scipy.signal\n", - "Om = 2*np.pi*nf\n", + "nf, Pxx = sig.welch(e, window=\"hamming\", nperseg=256, noverlap=128)\n", + "Pxx = 0.5 * Pxx # due to normalization in scipy.signal\n", + "Om = 2 * np.pi * nf\n", "# compute frequency response of system\n", "w, H = sig.freqz([1, 0, 0], a)\n", "\n", "\n", "# plot results\n", "plt.figure(figsize=(10, 4))\n", - "plt.plot(Om, Pxx/Q**2 * 12, 'b', label=r'$\\hat{\\Phi}_{ee}(e^{j \\Omega})$')\n", - "plt.plot(w, np.abs(H)**2 * 2, 'g', label=r'$|H(e^{j \\Omega})|^2$')\n", - "plt.title('Estimated PSD and transfer function of requantization noise')\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$Q^2/12$')\n", + "plt.plot(Om, Pxx / Q**2 * 12, \"b\", label=r\"$\\hat{\\Phi}_{ee}(e^{j \\Omega})$\")\n", + "plt.plot(w, np.abs(H) ** 2 * 2, \"g\", label=r\"$|H(e^{j \\Omega})|^2$\")\n", + "plt.title(\"Estimated PSD and transfer function of requantization noise\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$Q^2/12$\")\n", "plt.axis([0, np.pi, 0, 100])\n", "plt.legend()\n", "plt.grid()" @@ -4941,20 +4941,20 @@ "yQ = sos_df1(x, a, requantize=uniform_midtread_quantizer)\n", "\n", "# plot results\n", - "np.seterr(divide='ignore')\n", + "np.seterr(divide=\"ignore\")\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot(20*np.log10(np.abs(yQ)))\n", - "plt.title('Level of output signal')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$|y_Q[k]|$ in dB')\n", + "plt.plot(20 * np.log10(np.abs(yQ)))\n", + "plt.title(\"Level of output signal\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$|y_Q[k]|$ in dB\")\n", "plt.grid()\n", "\n", "plt.figure(figsize=(10, 3))\n", "k = np.arange(1000, 1050)\n", - "plt.stem(k, yQ[k]/Q )\n", - "plt.title('Output signal for zero input')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$y_Q[k] / Q$ ')\n", + "plt.stem(k, yQ[k] / Q)\n", + "plt.title(\"Output signal for zero input\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$y_Q[k] / Q$ \")\n", "plt.axis([k[0], k[-1], -3, 3])\n", "plt.grid()" ] @@ -8419,7 +8419,7 @@ ], "source": [ "def uniform_midtread_quantizer(x, xmin=1):\n", - " '''Uniform mid-tread quantizer with limiter.'''\n", + " \"\"\"Uniform mid-tread quantizer with limiter.\"\"\"\n", " # limiter\n", " x = np.copy(x)\n", " if x <= -xmin:\n", @@ -8427,7 +8427,7 @@ " if x > xmin - Q:\n", " x = 1 - Q\n", " # linear uniform quantization\n", - " xQ = Q * np.floor(x/Q + 1/2)\n", + " xQ = Q * np.floor(x / Q + 1 / 2)\n", "\n", " return xQ\n", "\n", @@ -8436,22 +8436,22 @@ "x = np.random.uniform(low=-1, high=1, size=256)\n", "x = np.concatenate((x, np.zeros(1024)))\n", "# compute output signal\n", - "yQ = sos_df1(x, 2*a, requantize=uniform_midtread_quantizer)\n", + "yQ = sos_df1(x, 2 * a, requantize=uniform_midtread_quantizer)\n", "\n", "# plot results\n", "plt.figure(figsize=(10, 3))\n", - "plt.plot(20*np.log10(np.abs(yQ)))\n", - "plt.title('Level of output signal')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$|y_Q[k]|$ in dB')\n", + "plt.plot(20 * np.log10(np.abs(yQ)))\n", + "plt.title(\"Level of output signal\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$|y_Q[k]|$ in dB\")\n", "plt.grid()\n", "\n", "plt.figure(figsize=(10, 3))\n", "k = np.arange(1000, 1050)\n", - "plt.stem(k, yQ[k] )\n", - "plt.title('Output signal for zero input')\n", - "plt.xlabel(r'$k$')\n", - "plt.ylabel(r'$y_Q[k]$ ')\n", + "plt.stem(k, yQ[k])\n", + "plt.title(\"Output signal for zero input\")\n", + "plt.xlabel(r\"$k$\")\n", + "plt.ylabel(r\"$y_Q[k]$ \")\n", "plt.grid()" ] }, diff --git a/spectral_analysis_deterministic_signals/leakage_effect.ipynb b/spectral_analysis_deterministic_signals/leakage_effect.ipynb index 8682d5c..302ff8b 100644 --- a/spectral_analysis_deterministic_signals/leakage_effect.ipynb +++ b/spectral_analysis_deterministic_signals/leakage_effect.ipynb @@ -158,17 +158,22 @@ "\n", "# DTFT of finite length exponential signal (analytic)\n", "Om = np.linspace(-np.pi, np.pi, num=1024)\n", - "XN = np.exp(-1j * (Om-Om0) * (N-1) / 2) * \\\n", - " (np.sin(N * (Om-Om0) / 2)) / (np.sin((Om-Om0) / 2))\n", + "XN = (\n", + " np.exp(-1j * (Om - Om0) * (N - 1) / 2)\n", + " * (np.sin(N * (Om - Om0) / 2))\n", + " / (np.sin((Om - Om0) / 2))\n", + ")\n", "\n", "# plot spectrum\n", "plt.figure(figsize=(10, 8))\n", "plt.plot(Om, abs(XN))\n", - "plt.title(r'Absolute value of the DTFT of a truncated exponential signal ' +\n", - " r'$e^{{j \\Omega_0 k}}$ with $\\Omega_0=${0:1.2f}'.format(Om0))\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$|X_N(e^{j \\Omega})|$')\n", - "plt.axis([-np.pi, np.pi, -0.5, N+5])\n", + "plt.title(\n", + " r\"Absolute value of the DTFT of a truncated exponential signal \"\n", + " + r\"$e^{{j \\Omega_0 k}}$ with $\\Omega_0=${0:1.2f}\".format(Om0)\n", + ")\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$|X_N(e^{j \\Omega})|$\")\n", + "plt.axis([-np.pi, np.pi, -0.5, N + 5])\n", "plt.grid()" ] }, @@ -224,41 +229,47 @@ "source": [ "N = 32 # length of the signal\n", "P = 10.33 # periodicity of the exponential signal\n", - "Om0 = P * (2*np.pi/N) # frequency of exponential signal\n", + "Om0 = P * (2 * np.pi / N) # frequency of exponential signal\n", "\n", "\n", "# truncated exponential signal\n", "k = np.arange(N)\n", - "x = np.exp(1j*Om0*k)\n", + "x = np.exp(1j * Om0 * k)\n", "\n", "# DTFT of finite length exponential signal (analytic)\n", - "Om = np.linspace(0, 2*np.pi, num=1024)\n", - "Xw = np.exp(-1j*(Om-Om0)*(N-1)/2)*(np.sin(N*(Om-Om0)/2))/(np.sin((Om-Om0)/2))\n", + "Om = np.linspace(0, 2 * np.pi, num=1024)\n", + "Xw = (\n", + " np.exp(-1j * (Om - Om0) * (N - 1) / 2)\n", + " * (np.sin(N * (Om - Om0) / 2))\n", + " / (np.sin((Om - Om0) / 2))\n", + ")\n", "\n", "# DFT of the exponential signal by FFT\n", "X = np.fft.fft(x)\n", - "mu = np.arange(N) * 2*np.pi/N\n", + "mu = np.arange(N) * 2 * np.pi / N\n", "\n", "# plot spectra\n", "plt.figure(figsize=(10, 8))\n", "ax1 = plt.gca()\n", "\n", - "plt.plot(Om, abs(Xw), label=r'$|X_N(e^{j \\Omega})|$')\n", - "plt.stem(mu, abs(X), label=r'$|X_N[\\mu]|$',\n", - " basefmt=' ', linefmt='C1', markerfmt='C1o')\n", - "plt.ylim([-0.5, N+5])\n", - "plt.title(r'Absolute value of the DTFT/DFT of a truncated exponential signal ' +\n", - " r'$e^{{j \\Omega_0 k}}$ with $\\Omega_0=${0:1.2f}'.format(Om0), y=1.08)\n", + "plt.plot(Om, abs(Xw), label=r\"$|X_N(e^{j \\Omega})|$\")\n", + "plt.stem(mu, abs(X), label=r\"$|X_N[\\mu]|$\", basefmt=\" \", linefmt=\"C1\", markerfmt=\"C1o\")\n", + "plt.ylim([-0.5, N + 5])\n", + "plt.title(\n", + " r\"Absolute value of the DTFT/DFT of a truncated exponential signal \"\n", + " + r\"$e^{{j \\Omega_0 k}}$ with $\\Omega_0=${0:1.2f}\".format(Om0),\n", + " y=1.08,\n", + ")\n", "plt.legend()\n", "\n", - "ax1.set_xlabel(r'$\\Omega$')\n", + "ax1.set_xlabel(r\"$\\Omega$\")\n", "ax1.set_xlim([Om[0], Om[-1]])\n", "ax1.grid()\n", "\n", "ax2 = ax1.twiny()\n", "ax2.set_xlim([0, N])\n", - "ax2.set_xlabel(r'$\\mu$', color='C1')\n", - "ax2.tick_params('x', colors='C1')" + "ax2.set_xlabel(r\"$\\mu$\", color=\"C1\")\n", + "ax2.tick_params(\"x\", colors=\"C1\")" ] }, { @@ -301,19 +312,19 @@ "outputs": [], "source": [ "def dft_signal_mixture(N, *, amp1, period1, amp2, period2):\n", - " '''Calculates and plots the magnitude spectrum of two superimposed exponentials.\n", + " \"\"\"Calculates and plots the magnitude spectrum of two superimposed exponentials.\n", "\n", " Keyword arguments:\n", " N: length of signal/DFT\n", " amp1, period1 : amplitude and periodicity of 1st complex exponential\n", " amp2, period2 : amplitude and periodicity of 2nd complex exponential\n", - " '''\n", + " \"\"\"\n", "\n", " # generate the signal mixture\n", - " Om0_1 = period1 * (2*np.pi/N) # frequency of 1st exponential signal\n", - " Om0_2 = period2 * (2*np.pi/N) # frequency of 2nd exponential signal\n", + " Om0_1 = period1 * (2 * np.pi / N) # frequency of 1st exponential signal\n", + " Om0_2 = period2 * (2 * np.pi / N) # frequency of 2nd exponential signal\n", " k = np.arange(N)\n", - " x = amp1 * np.exp(1j*Om0_1*k) + amp2 * np.exp(1j*Om0_2*k)\n", + " x = amp1 * np.exp(1j * Om0_1 * k) + amp2 * np.exp(1j * Om0_2 * k)\n", "\n", " # DFT of the signal mixture\n", " mu = np.arange(N)\n", @@ -321,11 +332,11 @@ "\n", " # plot spectrum\n", " plt.figure(figsize=(10, 8))\n", - " plt.stem(mu, abs(X), basefmt=' ')\n", - " plt.title(r'Absolute value of the DFT of a signal mixture')\n", - " plt.xlabel(r'$\\mu$')\n", - " plt.ylabel(r'$|X[\\mu]|$')\n", - " plt.axis([0, N, -0.5, N+5])\n", + " plt.stem(mu, abs(X), basefmt=\" \")\n", + " plt.title(r\"Absolute value of the DFT of a signal mixture\")\n", + " plt.xlabel(r\"$\\mu$\")\n", + " plt.ylabel(r\"$|X[\\mu]|$\")\n", + " plt.axis([0, N, -0.5, N + 5])\n", " plt.grid()" ] }, diff --git a/spectral_analysis_deterministic_signals/stft.ipynb b/spectral_analysis_deterministic_signals/stft.ipynb index d41f195..ee4da88 100644 --- a/spectral_analysis_deterministic_signals/stft.ipynb +++ b/spectral_analysis_deterministic_signals/stft.ipynb @@ -3423,14 +3423,14 @@ "\n", "# generate signal\n", "k = np.arange(N)\n", - "x = sig.chirp(k, 0.01, N, .49)\n", + "x = sig.chirp(k, 0.01, N, 0.49)\n", "\n", "# compute and plot magnitude spectrum\n", "plt.figure(figsize=(10, 3))\n", - "f = np.fft.rfftfreq(N, 1/2)\n", - "plt.plot(f, 20*np.log10(abs(np.fft.rfft(x))))\n", - "plt.xlabel(r'$\\Omega$ / $\\pi$')\n", - "plt.ylabel(r'$|X[\\Omega]|$ in dB')\n", + "f = np.fft.rfftfreq(N, 1 / 2)\n", + "plt.plot(f, 20 * np.log10(abs(np.fft.rfft(x))))\n", + "plt.xlabel(r\"$\\Omega$ / $\\pi$\")\n", + "plt.ylabel(r\"$|X[\\Omega]|$ in dB\")\n", "plt.ylim([0, 50])\n", "plt.grid()" ] @@ -4410,11 +4410,11 @@ "overlap = 128 # overlap between segments\n", "\n", "plt.figure(figsize=(10, 5))\n", - "plt.specgram(x, NFFT=L, Fs=2, noverlap=overlap, sides='onesided')\n", - "plt.xlabel(r'$n$')\n", - "plt.ylabel(r'$\\Omega$ / $\\pi$')\n", + "plt.specgram(x, NFFT=L, Fs=2, noverlap=overlap, sides=\"onesided\")\n", + "plt.xlabel(r\"$n$\")\n", + "plt.ylabel(r\"$\\Omega$ / $\\pi$\")\n", "cb = plt.colorbar()\n", - "cb.set_label(r'$|X[\\Omega,n]|$ in dB')\n", + "cb.set_label(r\"$|X[\\Omega,n]|$ in dB\")\n", "plt.autoscale(tight=True)" ] }, @@ -5445,16 +5445,16 @@ "overlap = 512 # overlap between segments\n", "\n", "# read speech signal from file\n", - "x, fs = sf.read('../data/speech_8k.wav')\n", - "x = x/np.max(np.abs(x))\n", + "x, fs = sf.read(\"../data/speech_8k.wav\")\n", + "x = x / np.max(np.abs(x))\n", "\n", "# compute and plot spectrogram\n", "plt.figure(figsize=(10, 5))\n", - "plt.specgram(x, NFFT=L, Fs=fs, noverlap=overlap, sides='onesided')\n", - "plt.xlabel(r'$n$ in s')\n", - "plt.ylabel(r'$f$ in Hz')\n", + "plt.specgram(x, NFFT=L, Fs=fs, noverlap=overlap, sides=\"onesided\")\n", + "plt.xlabel(r\"$n$ in s\")\n", + "plt.ylabel(r\"$f$ in Hz\")\n", "cb = plt.colorbar()\n", - "cb.set_label(r'$|X[f,n]|$ in dB')\n", + "cb.set_label(r\"$|X[f,n]|$ in dB\")\n", "plt.autoscale(tight=True)\n", "plt.ylim([0, 2000])" ] diff --git a/spectral_analysis_deterministic_signals/summary.ipynb b/spectral_analysis_deterministic_signals/summary.ipynb index 7935a56..aa2ee09 100644 --- a/spectral_analysis_deterministic_signals/summary.ipynb +++ b/spectral_analysis_deterministic_signals/summary.ipynb @@ -1675,20 +1675,19 @@ "\n", "\n", "def plot_spectrum(X, title):\n", - " '''Plot magnitude spectrum.'''\n", - " plt.axvline(x=P, linewidth=2, color='C1', alpha=.5)\n", - " plt.stem(mu, 20*np.log10(np.abs(X)), basefmt=' ',\n", - " bottom=-300 )\n", + " \"\"\"Plot magnitude spectrum.\"\"\"\n", + " plt.axvline(x=P, linewidth=2, color=\"C1\", alpha=0.5)\n", + " plt.stem(mu, 20 * np.log10(np.abs(X)), basefmt=\" \", bottom=-300)\n", " plt.title(title)\n", - " plt.xlabel(r'$\\mu$')\n", + " plt.xlabel(r\"$\\mu$\")\n", " plt.axis([0, N, -100, 40])\n", " plt.grid()\n", "\n", "\n", "# generate signal\n", "k = np.arange(N)\n", - "Om0 = P*(2*np.pi/N) # frequency of exponential signal\n", - "x = np.exp(1j*Om0*k)\n", + "Om0 = P * (2 * np.pi / N) # frequency of exponential signal\n", + "x = np.exp(1j * Om0 * k)\n", "\n", "# DFTs of the windowed signals\n", "mu = np.arange(N)\n", @@ -1698,10 +1697,10 @@ "# plot spectra\n", "plt.figure(figsize=(10, 8))\n", "ax1 = plt.subplot(2, 2, 1)\n", - "plot_spectrum(X1, 'rectangular window')\n", - "plt.ylabel(r'$|X[\\mu]|$ in dB')\n", + "plot_spectrum(X1, \"rectangular window\")\n", + "plt.ylabel(r\"$|X[\\mu]|$ in dB\")\n", "ax2 = plt.subplot(2, 2, 2, sharey=ax1)\n", - "plot_spectrum(X2, 'Hann window')\n", + "plot_spectrum(X2, \"Hann window\")\n", "plt.setp(ax2.get_yticklabels(), visible=False)\n", "plt.tight_layout()" ] diff --git a/spectral_analysis_deterministic_signals/window_functions.ipynb b/spectral_analysis_deterministic_signals/window_functions.ipynb index a231432..53c361d 100644 --- a/spectral_analysis_deterministic_signals/window_functions.ipynb +++ b/spectral_analysis_deterministic_signals/window_functions.ipynb @@ -46,11 +46,11 @@ "\n", "\n", "def dft_window_function(w):\n", - " '''Calculates and plots the magnitude spectrum of a window function.\n", - " \n", + " \"\"\"Calculates and plots the magnitude spectrum of a window function.\n", + "\n", " Arguments:\n", " w: window function\n", - " '''\n", + " \"\"\"\n", " N = len(w)\n", "\n", " # DFT of window function\n", @@ -60,17 +60,17 @@ " mu = np.linspace(-np.pi, np.pi, 8192)\n", "\n", " # plot window function and its spectrum\n", - " plt.rcParams['figure.figsize'] = 10, 5\n", - " plt.stem(w, basefmt=' ')\n", - " plt.xlabel(r'$k$')\n", - " plt.ylabel(r'$w[k]$')\n", - " plt.axis([-1, N+1, -0.1, 1.1])\n", + " plt.rcParams[\"figure.figsize\"] = 10, 5\n", + " plt.stem(w, basefmt=\" \")\n", + " plt.xlabel(r\"$k$\")\n", + " plt.ylabel(r\"$w[k]$\")\n", + " plt.axis([-1, N + 1, -0.1, 1.1])\n", " plt.grid()\n", "\n", " plt.figure()\n", - " plt.plot(mu, 20*np.log10(np.abs(W)))\n", - " plt.xlabel(r'$\\Omega$')\n", - " plt.ylabel(r'$| W(e^{j \\Omega}) |$ in dB')\n", + " plt.plot(mu, 20 * np.log10(np.abs(W)))\n", + " plt.xlabel(r\"$\\Omega$\")\n", + " plt.ylabel(r\"$| W(e^{j \\Omega}) |$ in dB\")\n", " plt.axis([-np.pi, np.pi, -100, 5])\n", " plt.grid()" ] @@ -346,19 +346,19 @@ "outputs": [], "source": [ "def dft_signal_mixture_window(N, *, amp1, period1, amp2, period2, window):\n", - " '''Calculates and plots the magnitude spectrum of two windowed superimposed exponentials.\n", - " \n", + " \"\"\"Calculates and plots the magnitude spectrum of two windowed superimposed exponentials.\n", + "\n", " Keyword arguments:\n", " N: length of signal/DFT\n", " amp1, period1 : amplitude and periodicity of 1st complex exponential\n", " amp2, period2 : amplitude and periodicity of 2nd complex exponential\n", - " '''\n", + " \"\"\"\n", "\n", " # generate the signal mixture\n", - " Om0_1 = period1 * (2*np.pi/N) # frequency of 1st exponential signal\n", - " Om0_2 = period2 * (2*np.pi/N) # frequency of 2nd exponential signal\n", + " Om0_1 = period1 * (2 * np.pi / N) # frequency of 1st exponential signal\n", + " Om0_2 = period2 * (2 * np.pi / N) # frequency of 2nd exponential signal\n", " k = np.arange(N)\n", - " x = amp1 * np.exp(1j*Om0_1*k) + amp2 * np.exp(1j*Om0_2*k)\n", + " x = amp1 * np.exp(1j * Om0_1 * k) + amp2 * np.exp(1j * Om0_2 * k)\n", " x = x * window\n", "\n", " # DFT of the signal mixture\n", @@ -366,12 +366,12 @@ " X = np.fft.fft(x)\n", "\n", " # plot spectrum\n", - " plt.figure(figsize = (10, 8))\n", - " plt.stem(mu, abs(X), basefmt = ' ')\n", - " plt.title(r'Absolute value of the DFT of a signal mixture')\n", - " plt.xlabel(r'$\\mu$')\n", - " plt.ylabel(r'$|X[\\mu]|$')\n", - " plt.axis([0, N, -0.5, abs(X).max()+5])\n", + " plt.figure(figsize=(10, 8))\n", + " plt.stem(mu, abs(X), basefmt=\" \")\n", + " plt.title(r\"Absolute value of the DFT of a signal mixture\")\n", + " plt.xlabel(r\"$\\mu$\")\n", + " plt.ylabel(r\"$|X[\\mu]|$\")\n", + " plt.axis([0, N, -0.5, abs(X).max() + 5])\n", " plt.grid()" ] }, @@ -399,7 +399,9 @@ } ], "source": [ - "dft_signal_mixture_window(32, amp1=1, period1=10.3, amp2=0.1, period2=15.2, window=np.blackman(32))" + "dft_signal_mixture_window(\n", + " 32, amp1=1, period1=10.3, amp2=0.1, period2=15.2, window=np.blackman(32)\n", + ")" ] }, { diff --git a/spectral_analysis_deterministic_signals/zero_padding.ipynb b/spectral_analysis_deterministic_signals/zero_padding.ipynb index a216e90..e84527b 100644 --- a/spectral_analysis_deterministic_signals/zero_padding.ipynb +++ b/spectral_analysis_deterministic_signals/zero_padding.ipynb @@ -85,14 +85,14 @@ "\n", "N = 16 # length of the signal\n", "M = 32 # length of zero-padded signal\n", - "Om0 = 5.33*(2*np.pi/N) # frequency of exponential signal\n", + "Om0 = 5.33 * (2 * np.pi / N) # frequency of exponential signal\n", "\n", "\n", "# DFT of the exponential signal\n", - "xN = np.exp(1j*Om0*np.arange(N))\n", + "xN = np.exp(1j * Om0 * np.arange(N))\n", "XN = np.fft.fft(xN)\n", "# DFT of the zero-padded exponential signal\n", - "xM = np.concatenate((xN, np.zeros(M-N)))\n", + "xM = np.concatenate((xN, np.zeros(M - N)))\n", "XM = np.fft.fft(xM)\n", "\n", "\n", @@ -101,17 +101,21 @@ "\n", "plt.subplot(121)\n", "plt.stem(np.arange(N), np.abs(XN))\n", - "plt.title(r'$\\mathrm{{DFT}}_{{{0}}}$ of $e^{{j \\Omega_0 k}}$ without zero-padding'.format(N))\n", - "plt.xlabel(r'$\\mu$')\n", - "plt.ylabel(r'$|X_N[\\mu]|$')\n", + "plt.title(\n", + " r\"$\\mathrm{{DFT}}_{{{0}}}$ of $e^{{j \\Omega_0 k}}$ without zero-padding\".format(N)\n", + ")\n", + "plt.xlabel(r\"$\\mu$\")\n", + "plt.ylabel(r\"$|X_N[\\mu]|$\")\n", "plt.axis([0, N, 0, 18])\n", "plt.grid()\n", "\n", "plt.subplot(122)\n", "plt.stem(np.arange(M), np.abs(XM))\n", - "plt.title(r'$\\mathrm{{DFT}}_{{{0}}}$ of $e^{{j \\Omega_0 k}}$ with zero-padding'.format(M))\n", - "plt.xlabel(r'$\\mu$')\n", - "plt.ylabel(r'$|X_M[\\mu]|$')\n", + "plt.title(\n", + " r\"$\\mathrm{{DFT}}_{{{0}}}$ of $e^{{j \\Omega_0 k}}$ with zero-padding\".format(M)\n", + ")\n", + "plt.xlabel(r\"$\\mu$\")\n", + "plt.ylabel(r\"$|X_M[\\mu]|$\")\n", "plt.axis([0, M, 0, 18])\n", "plt.grid()" ] @@ -232,17 +236,17 @@ "\n", "\n", "def psinc(x, N):\n", - " '''Periodic sinc function.'''\n", + " \"\"\"Periodic sinc function.\"\"\"\n", " x = np.asanyarray(x)\n", " y = np.where(x == 0, 1.0e-20, x)\n", - " return 1/N * np.sin(N/2*y)/np.sin(1/2*y)\n", + " return 1 / N * np.sin(N / 2 * y) / np.sin(1 / 2 * y)\n", "\n", "\n", "# plot psinc\n", "plt.figure(figsize=(10, 8))\n", "plt.plot(Om, psinc(Om, 16))\n", - "plt.xlabel(r'$\\Omega$')\n", - "plt.ylabel(r'$\\mathrm{psinc}_N (\\Omega)$')\n", + "plt.xlabel(r\"$\\Omega$\")\n", + "plt.ylabel(r\"$\\mathrm{psinc}_N (\\Omega)$\")\n", "plt.grid()" ] }, @@ -265,39 +269,45 @@ "source": [ "N = 16 # length of the signal\n", "M = 1024 # number of frequency points for DTFT\n", - "Om0 = 5.33*(2*np.pi/N) # frequency of exponential signal\n", + "Om0 = 5.33 * (2 * np.pi / N) # frequency of exponential signal\n", "\n", "\n", "# DFT of the exponential signal\n", - "xN = np.exp(1j*Om0*np.arange(N))\n", + "xN = np.exp(1j * Om0 * np.arange(N))\n", "XN = np.fft.fft(xN)\n", "\n", "# interpolation of DTFT from DFT coefficients\n", "Xi = np.asarray(np.zeros(M), dtype=complex)\n", "for mu in np.arange(M):\n", - " Omd = 2*np.pi/M*mu-2*np.pi*np.arange(N)/N\n", - " interpolator = psinc(Omd, N) * np.exp(-1j*Omd*(N-1)/2)\n", + " Omd = 2 * np.pi / M * mu - 2 * np.pi * np.arange(N) / N\n", + " interpolator = psinc(Omd, N) * np.exp(-1j * Omd * (N - 1) / 2)\n", " Xi[mu] = np.sum(XN * interpolator)\n", "\n", "# plot spectra\n", "plt.figure(figsize=(10, 8))\n", "ax1 = plt.gca()\n", "\n", - "plt.plot(np.arange(M)*2*np.pi/M, abs(Xi), label=r'$|X_N(e^{j \\Omega})|$')\n", - "plt.stem(np.arange(N)*2*np.pi/N, abs(XN), basefmt=' ', linefmt='C1',\n", - " markerfmt='C1o', label=r'$|X_N[\\mu]|$')\n", - "plt.title(r'DFT $X_N[\\mu]$ and interpolated DTFT $X_N(e^{j \\Omega})$', y=1.08)\n", - "plt.ylim([-0.5, N+2])\n", + "plt.plot(np.arange(M) * 2 * np.pi / M, abs(Xi), label=r\"$|X_N(e^{j \\Omega})|$\")\n", + "plt.stem(\n", + " np.arange(N) * 2 * np.pi / N,\n", + " abs(XN),\n", + " basefmt=\" \",\n", + " linefmt=\"C1\",\n", + " markerfmt=\"C1o\",\n", + " label=r\"$|X_N[\\mu]|$\",\n", + ")\n", + "plt.title(r\"DFT $X_N[\\mu]$ and interpolated DTFT $X_N(e^{j \\Omega})$\", y=1.08)\n", + "plt.ylim([-0.5, N + 2])\n", "plt.legend()\n", "\n", - "ax1.set_xlabel(r'$\\Omega$')\n", - "ax1.set_xlim([0, 2*np.pi])\n", + "ax1.set_xlabel(r\"$\\Omega$\")\n", + "ax1.set_xlim([0, 2 * np.pi])\n", "ax1.grid()\n", "\n", "ax2 = ax1.twiny()\n", "ax2.set_xlim([0, N])\n", - "ax2.set_xlabel(r'$\\mu$', color='C1')\n", - "ax2.tick_params('x', colors='C1')" + "ax2.set_xlabel(r\"$\\mu$\", color=\"C1\")\n", + "ax2.tick_params(\"x\", colors=\"C1\")" ] }, { @@ -358,25 +368,25 @@ "source": [ "N = 16 # length of the signal\n", "M = 32 # number of points for interpolated DFT\n", - "Om0 = 5.33*(2*np.pi/N) # frequency of exponential signal\n", + "Om0 = 5.33 * (2 * np.pi / N) # frequency of exponential signal\n", "\n", "\n", "# periodic sinc function\n", "def psinc(x, N):\n", " x = np.asanyarray(x)\n", " y = np.where(x == 0, 1.0e-20, x)\n", - " return 1/N * np.sin(N/2*y)/np.sin(1/2*y)\n", + " return 1 / N * np.sin(N / 2 * y) / np.sin(1 / 2 * y)\n", "\n", "\n", "# DFT of the exponential signal\n", - "xN = np.exp(1j*Om0*np.arange(N))\n", + "xN = np.exp(1j * Om0 * np.arange(N))\n", "XN = np.fft.fft(xN)\n", "\n", "# interpolation of DFT coefficients\n", "XM = np.asarray(np.zeros(M), dtype=complex)\n", "for mu in np.arange(M):\n", - " Omd = 2*np.pi/M*mu-2*np.pi*np.arange(N)/N\n", - " interpolator = psinc(Omd, N) * np.exp(-1j*Omd*(N-1)/2)\n", + " Omd = 2 * np.pi / M * mu - 2 * np.pi * np.arange(N) / N\n", + " interpolator = psinc(Omd, N) * np.exp(-1j * Omd * (N - 1) / 2)\n", " XM[mu] = np.sum(XN * interpolator)\n", "\n", "# plot spectra\n", @@ -384,17 +394,19 @@ "\n", "plt.subplot(121)\n", "plt.stem(np.arange(N), np.abs(XN))\n", - "plt.title(r'$\\mathrm{{DFT}}_{{{0}}}$ of $e^{{j \\Omega_0 k}}$ without zero-padding'.format(N))\n", - "plt.xlabel(r'$\\mu$')\n", - "plt.ylabel(r'$|X_N[\\mu]|$')\n", + "plt.title(\n", + " r\"$\\mathrm{{DFT}}_{{{0}}}$ of $e^{{j \\Omega_0 k}}$ without zero-padding\".format(N)\n", + ")\n", + "plt.xlabel(r\"$\\mu$\")\n", + "plt.ylabel(r\"$|X_N[\\mu]|$\")\n", "plt.axis([0, N, 0, 18])\n", "plt.grid()\n", "\n", "plt.subplot(122)\n", "plt.stem(np.arange(M), np.abs(XM))\n", - "plt.title(r'Interpolated spectrum')\n", - "plt.xlabel(r'$\\mu$')\n", - "plt.ylabel(r'$|X_M[\\mu]|$')\n", + "plt.title(r\"Interpolated spectrum\")\n", + "plt.xlabel(r\"$\\mu$\")\n", + "plt.ylabel(r\"$|X_M[\\mu]|$\")\n", "plt.axis([0, M, 0, 18])\n", "plt.grid()" ] @@ -435,15 +447,15 @@ "outputs": [], "source": [ "N = 128 # length of the signal\n", - "Om0 = 5.33*(2*np.pi/N) # frequency of exponential signal\n", + "Om0 = 5.33 * (2 * np.pi / N) # frequency of exponential signal\n", "\n", "# generate harmonic signal\n", "k = np.arange(N)\n", - "x = np.exp(1j*Om0*np.arange(N))\n", + "x = np.exp(1j * Om0 * np.arange(N))\n", "\n", "\n", "def estimate_frequency_amplitude(x, P):\n", - " '''Estimate frequency and amplitude of an exponential signal.'''\n", + " \"\"\"Estimate frequency and amplitude of an exponential signal.\"\"\"\n", "\n", " # perform zero-padding and DFT\n", " xM = np.concatenate((x, np.zeros(P)))\n", @@ -451,14 +463,20 @@ "\n", " # estimate frequency/amplitude of harmonic signal\n", " mu_max = np.argmax(abs(XM))\n", - " amplitude = 1/N * abs(XM[mu_max])\n", + " amplitude = 1 / N * abs(XM[mu_max])\n", "\n", " # print results\n", - " Om = np.fft.fftfreq(N+P, 1/(2*np.pi))\n", - " print('Normalized frequency of signal: {0:1.4f} (real) / {1:1.4f} (estimated) / {2:1.4f} (absolute error)'.format(\n", - " Om0, Om[mu_max], abs(Om0 - Om[mu_max])))\n", - " print('Amplitude of signal: {0:1.4f} (real) / {1:1.4f} (estimated) / {2:2.2f} dB (magnitude error)'.format(\n", - " 1, amplitude, 20*np.log10(abs(1/amplitude))))" + " Om = np.fft.fftfreq(N + P, 1 / (2 * np.pi))\n", + " print(\n", + " \"Normalized frequency of signal: {0:1.4f} (real) / {1:1.4f} (estimated) / {2:1.4f} (absolute error)\".format(\n", + " Om0, Om[mu_max], abs(Om0 - Om[mu_max])\n", + " )\n", + " )\n", + " print(\n", + " \"Amplitude of signal: {0:1.4f} (real) / {1:1.4f} (estimated) / {2:2.2f} dB (magnitude error)\".format(\n", + " 1, amplitude, 20 * np.log10(abs(1 / amplitude))\n", + " )\n", + " )" ] }, { @@ -508,7 +526,7 @@ } ], "source": [ - "estimate_frequency_amplitude(x, 7*N)" + "estimate_frequency_amplitude(x, 7 * N)" ] }, { diff --git a/spectral_estimation_random_signals/introduction.ipynb b/spectral_estimation_random_signals/introduction.ipynb index d57c09b..d428f50 100644 --- a/spectral_estimation_random_signals/introduction.ipynb +++ b/spectral_estimation_random_signals/introduction.ipynb @@ -1398,8 +1398,8 @@ "# generate random signal\n", "np.random.seed(1)\n", "x = np.random.normal(size=(M, N))\n", - "h = sig.firwin2(N, [0, .4, .42, .65, .67, 1], [0, 0, 1, 1, 0, 0])\n", - "x = [np.convolve(xi, h, mode='same') for xi in x]\n", + "h = sig.firwin2(N, [0, 0.4, 0.42, 0.65, 0.67, 1], [0, 0, 1, 1, 0, 0])\n", + "x = [np.convolve(xi, h, mode=\"same\") for xi in x]\n", "\n", "# DFT of signal\n", "X = np.fft.rfft(x, axis=1)\n", @@ -1408,9 +1408,9 @@ "# plot signal and its spectrum\n", "plt.figure(figsize=(10, 4))\n", "plt.plot(Om, np.abs(X.T))\n", - "plt.title('Magnitude spectrum')\n", - "plt.xlabel(r'$\\Omega[\\mu]$')\n", - "plt.ylabel(r'$|X[\\mu]|$')\n", + "plt.title(\"Magnitude spectrum\")\n", + "plt.xlabel(r\"$\\Omega[\\mu]$\")\n", + "plt.ylabel(r\"$|X[\\mu]|$\")\n", "plt.axis([0, np.pi, 0, 30])\n", "plt.grid()" ] diff --git a/spectral_estimation_random_signals/parametric_methods.ipynb b/spectral_estimation_random_signals/parametric_methods.ipynb index 3d4d612..a586221 100644 --- a/spectral_estimation_random_signals/parametric_methods.ipynb +++ b/spectral_estimation_random_signals/parametric_methods.ipynb @@ -1665,7 +1665,7 @@ "\n", "K = 4096 # length of random signal\n", "N = 3 # order of AR model\n", - "a = np.array((1, -1, .5)) # coefficients of AR model\n", + "a = np.array((1, -1, 0.5)) # coefficients of AR model\n", "\n", "# generate random signal n[k]\n", "np.random.seed(2)\n", @@ -1674,10 +1674,10 @@ "# AR model for random signal x[k]\n", "x = np.zeros(K)\n", "for k in np.arange(3, K):\n", - " x[k] = a[0]*x[k-1] + a[1]*x[k-2] + a[2]*x[k-3] + n[k]\n", + " x[k] = a[0] * x[k - 1] + a[1] * x[k - 2] + a[2] * x[k - 3] + n[k]\n", "\n", "# estimate AR parameters by Yule-Walker method\n", - "rho, sigma = sm.regression.yule_walker(x, order=N, method='mle')\n", + "rho, sigma = sm.regression.yule_walker(x, order=N, method=\"mle\")\n", "\n", "# compute true and estimated transfer function\n", "Om, H = sig.freqz(1, np.insert(-a, 0, 1), worN=256)\n", @@ -1687,25 +1687,32 @@ "\n", "# plot PSDs\n", "plt.figure(figsize=(10, 5))\n", - "plt.plot(Om, np.abs(H)**2, label=r'$\\Phi_{xx}(e^{j\\Omega})$')\n", - "plt.plot(Om2*2*np.pi, .5*Pxx, 'k-', alpha=.3,\n", - " label=r'$\\hat{\\Phi}_{xx}(e^{j\\Omega})$ (Welch)')\n", - "plt.plot(Om, np.abs(He)**2,\n", - " label=r'$\\hat{\\Phi}_{xx}(e^{j\\Omega})$ (parametric)')\n", + "plt.plot(Om, np.abs(H) ** 2, label=r\"$\\Phi_{xx}(e^{j\\Omega})$\")\n", + "plt.plot(\n", + " Om2 * 2 * np.pi,\n", + " 0.5 * Pxx,\n", + " \"k-\",\n", + " alpha=0.3,\n", + " label=r\"$\\hat{\\Phi}_{xx}(e^{j\\Omega})$ (Welch)\",\n", + ")\n", + "plt.plot(Om, np.abs(He) ** 2, label=r\"$\\hat{\\Phi}_{xx}(e^{j\\Omega})$ (parametric)\")\n", "\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.axis([0, np.pi, 0, 20])\n", "plt.legend()\n", "plt.grid()\n", "\n", "# compute bias/variance of the estimators\n", - "print('Bias of the Welch estimate: \\t\\t {0:1.4f}'.format(\n", - " np.mean(Pxx-np.abs(H)**2)))\n", - "print('Variance of the Welch estimate: \\t {0:1.4f}'.format(np.var(Pxx)))\n", - "print('Bias of the parametric estimate: \\t {0:1.4f}'.format(\n", - " np.mean(np.abs(H)**2-np.abs(He)**2)))\n", - "print('Variance of the parametric estimate: \\t {0:1.4f}'.format(\n", - " np.var(np.abs(He)**2)))" + "print(\"Bias of the Welch estimate: \\t\\t {0:1.4f}\".format(np.mean(Pxx - np.abs(H) ** 2)))\n", + "print(\"Variance of the Welch estimate: \\t {0:1.4f}\".format(np.var(Pxx)))\n", + "print(\n", + " \"Bias of the parametric estimate: \\t {0:1.4f}\".format(\n", + " np.mean(np.abs(H) ** 2 - np.abs(He) ** 2)\n", + " )\n", + ")\n", + "print(\n", + " \"Variance of the parametric estimate: \\t {0:1.4f}\".format(np.var(np.abs(He) ** 2))\n", + ")" ] }, { diff --git a/spectral_estimation_random_signals/periodogram.ipynb b/spectral_estimation_random_signals/periodogram.ipynb index 99f9f28..aaf71a9 100644 --- a/spectral_estimation_random_signals/periodogram.ipynb +++ b/spectral_estimation_random_signals/periodogram.ipynb @@ -1707,21 +1707,20 @@ "x = np.concatenate((x, np.zeros_like(x)))\n", "X = np.fft.rfft(x)\n", "Om = np.linspace(0, np.pi, len(X))\n", - "Pxx = 1/N * abs(X)**2\n", + "Pxx = 1 / N * abs(X) ** 2\n", "\n", "# plot results\n", "plt.figure(figsize=(10, 4))\n", - "plt.stem(Om, Pxx, 'C0',\n", - " label=r'$|\\hat{\\Phi}_{xx}(e^{j \\Omega})|$')\n", - "plt.plot(Om, np.ones_like(Pxx), 'C1', label=r'$\\Phi_{xx}(e^{j \\Omega})$')\n", - "plt.title('Estimated and true PSD')\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.stem(Om, Pxx, \"C0\", label=r\"$|\\hat{\\Phi}_{xx}(e^{j \\Omega})|$\")\n", + "plt.plot(Om, np.ones_like(Pxx), \"C1\", label=r\"$\\Phi_{xx}(e^{j \\Omega})$\")\n", + "plt.title(\"Estimated and true PSD\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.axis([0, np.pi, 0, 4])\n", "plt.legend()\n", "\n", "# compute bias/variance of the periodogram\n", - "print('Bias of the periodogram: \\t {0:1.4f}'.format(np.mean(Pxx-1)))\n", - "print('Variance of the periodogram: \\t {0:1.4f}'.format(np.var(Pxx)))" + "print(\"Bias of the periodogram: \\t {0:1.4f}\".format(np.mean(Pxx - 1)))\n", + "print(\"Variance of the periodogram: \\t {0:1.4f}\".format(np.var(Pxx)))" ] }, { diff --git a/spectral_estimation_random_signals/welch_method.ipynb b/spectral_estimation_random_signals/welch_method.ipynb index 12e586e..8e8b692 100644 --- a/spectral_estimation_random_signals/welch_method.ipynb +++ b/spectral_estimation_random_signals/welch_method.ipynb @@ -1489,26 +1489,25 @@ "\n", "# generate random signal\n", "np.random.seed(5)\n", - "x = np.random.normal(size=L*M)\n", + "x = np.random.normal(size=L * M)\n", "\n", "# estimate PSD by Welch's method\n", - "nf, Pxx = sig.welch(x, window='hamming', nperseg=N, noverlap=(N-M))\n", - "Pxx = .5*Pxx # due to normalization in scipy.signal\n", - "Om = 2*np.pi*nf\n", + "nf, Pxx = sig.welch(x, window=\"hamming\", nperseg=N, noverlap=(N - M))\n", + "Pxx = 0.5 * Pxx # due to normalization in scipy.signal\n", + "Om = 2 * np.pi * nf\n", "\n", "# plot results\n", "plt.figure(figsize=(10, 4))\n", - "plt.stem(Om, Pxx, 'C0',\n", - " label=r'$\\hat{\\Phi}_{xx}(e^{j \\Omega})$', basefmt=' ' )\n", - "plt.plot(Om, np.ones_like(Pxx), 'C1', label=r'$\\Phi_{xx}(e^{j \\Omega})$')\n", - "plt.title('Estimated and true PSD')\n", - "plt.xlabel(r'$\\Omega$')\n", + "plt.stem(Om, Pxx, \"C0\", label=r\"$\\hat{\\Phi}_{xx}(e^{j \\Omega})$\", basefmt=\" \")\n", + "plt.plot(Om, np.ones_like(Pxx), \"C1\", label=r\"$\\Phi_{xx}(e^{j \\Omega})$\")\n", + "plt.title(\"Estimated and true PSD\")\n", + "plt.xlabel(r\"$\\Omega$\")\n", "plt.axis([0, np.pi, 0, 2])\n", "plt.legend()\n", "\n", "# compute bias/variance of the estimator\n", - "print('Bias of the Welch estimate: \\t\\t {0:1.4f}'.format(np.mean(Pxx-1)))\n", - "print('Variance of the Welch estimate: \\t {0:1.4f}'.format(np.var(Pxx)))" + "print(\"Bias of the Welch estimate: \\t\\t {0:1.4f}\".format(np.mean(Pxx - 1)))\n", + "print(\"Variance of the Welch estimate: \\t {0:1.4f}\".format(np.var(Pxx)))" ] }, { From de00817e8ea33dd640259706c4f6d6dbfda482bb Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 13:53:57 +0200 Subject: [PATCH 03/11] deleted run nb --- .github/workflows/run_nb.yml | 60 ------------------------------------ 1 file changed, 60 deletions(-) delete mode 100644 .github/workflows/run_nb.yml diff --git a/.github/workflows/run_nb.yml b/.github/workflows/run_nb.yml deleted file mode 100644 index e63bdfc..0000000 --- a/.github/workflows/run_nb.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Run Notebooks - -on: - push: - branches: - - ci # Change this to your repository's main branch - pull_request: - branches: - - ci - schedule: - - cron: "0 0 * * 0" # Run every Sunday at midnight UTC (4 week interval) - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.10", "3.11", "3.12"] - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - # Testing matrix by printing the current Python version: - - name: Display Python version - run: python -c "import sys; print(sys.version)" - - - name: Install dependencies - run: pip install jupyter nbconvert - - run: pip install -r requirements.txt - - - name: Find notebooks - id: find-notebooks - run: | - find . -name "*.ipynb" > notebooks.txt - cat notebooks.txt - shell: bash - - - name: Execute notebooks - run: | - cat notebooks.txt | while read -r notebook; do - jupyter nbconvert --to notebook --execute --inplace "$notebook" - done - continue-on-error: false - shell: bash - - - name: Check for errors - run: | - if grep "raise Exception(" *.ipynb; then - echo "Error found in notebook(s)." - exit 1 - else - echo "No errors found in notebooks." - fi - shell: bash \ No newline at end of file From 216a28685aabfd18b0bb6d3853892307f1c413a1 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 14:04:38 +0200 Subject: [PATCH 04/11] Updated the workflows in running the notebooks --- .github/workflows/lint_nb.yml | 2 +- .github/workflows/run_nb.yml | 61 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/run_nb.yml diff --git a/.github/workflows/lint_nb.yml b/.github/workflows/lint_nb.yml index a10fe83..70e61ef 100644 --- a/.github/workflows/lint_nb.yml +++ b/.github/workflows/lint_nb.yml @@ -3,7 +3,7 @@ name: Linting Notebooks with Black on: push: branches: - - ci # Change this to your repository's main branch + - ci # Change this to the repository's main branch pull_request: branches: - ci diff --git a/.github/workflows/run_nb.yml b/.github/workflows/run_nb.yml new file mode 100644 index 0000000..40b5a8e --- /dev/null +++ b/.github/workflows/run_nb.yml @@ -0,0 +1,61 @@ +name: Run Notebooks + +on: + push: + branches: + - dev # Change this to the repository's main branch + pull_request: + branches: + - dev + schedule: + - cron: "0 0 * * 0" # Run every Sunday at midnight UTC (4 week interval) + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.12"] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + # Testing matrix by printing the current Python version: + - name: Display Python version + run: python -c "import sys; print(sys.version)" + + - name: Install dependencies + run: pip install jupyter nbconvert + - run: pip install -r requirements.txt + - run: pip install -r ci/requirements.txt + + - name: Find notebooks + id: find-notebooks + run: | + find . -name "*.ipynb" > notebooks.txt + cat notebooks.txt + shell: bash + + - name: Execute notebooks + run: | + cat notebooks.txt | while read -r notebook; do + jupyter nbconvert --to notebook --execute --inplace "$notebook" + done + continue-on-error: true # Set to false for PullRequest + shell: bash + + - name: Check for errors + run: | + if grep "raise Exception(" *.ipynb; then + echo "Error found in notebook(s)." + exit 1 + else + echo "No errors found in notebooks." + fi + shell: bash \ No newline at end of file From c7662e7422255d37e799ed46c1f9b456a90883dd Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 14:05:27 +0200 Subject: [PATCH 05/11] changed branch from dev to ci --- .github/workflows/run_nb.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_nb.yml b/.github/workflows/run_nb.yml index 40b5a8e..2f0f5bb 100644 --- a/.github/workflows/run_nb.yml +++ b/.github/workflows/run_nb.yml @@ -3,10 +3,10 @@ name: Run Notebooks on: push: branches: - - dev # Change this to the repository's main branch + - ci # Change this to the repository's main branch pull_request: branches: - - dev + - ci schedule: - cron: "0 0 * * 0" # Run every Sunday at midnight UTC (4 week interval) From d772d4e08a7cb62c3dd7d07df9811df75eecd4c8 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 14:16:13 +0200 Subject: [PATCH 06/11] Install PortAudio library for workflows. --- .github/workflows/run_nb.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/run_nb.yml b/.github/workflows/run_nb.yml index 2f0f5bb..bc28b8b 100644 --- a/.github/workflows/run_nb.yml +++ b/.github/workflows/run_nb.yml @@ -35,6 +35,10 @@ jobs: - run: pip install -r requirements.txt - run: pip install -r ci/requirements.txt + - name: Install PortAudio library + run: apt-get install libportaudio2 + - run: apt-get install libasound-dev + - name: Find notebooks id: find-notebooks run: | From 53d55f47ffc763524d6a95c96ab848047b124097 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 14:19:30 +0200 Subject: [PATCH 07/11] set portaudio installation to sudo. --- .github/workflows/run_nb.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_nb.yml b/.github/workflows/run_nb.yml index bc28b8b..a02f684 100644 --- a/.github/workflows/run_nb.yml +++ b/.github/workflows/run_nb.yml @@ -36,8 +36,8 @@ jobs: - run: pip install -r ci/requirements.txt - name: Install PortAudio library - run: apt-get install libportaudio2 - - run: apt-get install libasound-dev + run: sudo apt-get install libportaudio2 + - run: sudo apt-get install libasound-dev - name: Find notebooks id: find-notebooks From 57978a341c2e497d939c70c1b8eacfc5cc12be27 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 14:35:07 +0200 Subject: [PATCH 08/11] Set python versions: ["3.10", "3.11", "3.12"]. --- .github/workflows/run_nb.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_nb.yml b/.github/workflows/run_nb.yml index a02f684..2cf4b95 100644 --- a/.github/workflows/run_nb.yml +++ b/.github/workflows/run_nb.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.12"] + python-version: ["3.10", "3.11", "3.12"] steps: - name: Checkout code @@ -42,7 +42,7 @@ jobs: - name: Find notebooks id: find-notebooks run: | - find . -name "*.ipynb" > notebooks.txt + find . -name "*.ipynb" -not -name "acoustic_impulse_response_measurement.ipynb" > notebooks.txt cat notebooks.txt shell: bash From 2f4ceeb816dd34561ad525884c17cdeb95dd2860 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 14:53:00 +0200 Subject: [PATCH 09/11] Changed branch ci to master branch. --- .github/workflows/run_nb.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_nb.yml b/.github/workflows/run_nb.yml index 2cf4b95..4648ea9 100644 --- a/.github/workflows/run_nb.yml +++ b/.github/workflows/run_nb.yml @@ -3,10 +3,10 @@ name: Run Notebooks on: push: branches: - - ci # Change this to the repository's main branch + - master # Change this to the repository's main branch pull_request: branches: - - ci + - master schedule: - cron: "0 0 * * 0" # Run every Sunday at midnight UTC (4 week interval) From db36e5101a71e250d85f4577a078370f59416a78 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 14:53:43 +0200 Subject: [PATCH 10/11] Changed branch ci to master branch in linting workflow. --- .github/workflows/lint_nb.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_nb.yml b/.github/workflows/lint_nb.yml index 70e61ef..8e5dc2b 100644 --- a/.github/workflows/lint_nb.yml +++ b/.github/workflows/lint_nb.yml @@ -3,10 +3,10 @@ name: Linting Notebooks with Black on: push: branches: - - ci # Change this to the repository's main branch + - master # Change this to the repository's main branch pull_request: branches: - - ci + - master jobs: lint: runs-on: ubuntu-latest From 72e08877cae98178bb3dc69b730c23bd613b3be6 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 14:54:55 +0200 Subject: [PATCH 11/11] Set continue-on-error to false. --- .github/workflows/run_nb.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_nb.yml b/.github/workflows/run_nb.yml index 4648ea9..b37abdb 100644 --- a/.github/workflows/run_nb.yml +++ b/.github/workflows/run_nb.yml @@ -51,7 +51,7 @@ jobs: cat notebooks.txt | while read -r notebook; do jupyter nbconvert --to notebook --execute --inplace "$notebook" done - continue-on-error: true # Set to false for PullRequest + continue-on-error: false shell: bash - name: Check for errors pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy