From 9c946e1081e383f232f97c685a3ded3a877d4714 Mon Sep 17 00:00:00 2001 From: JacobTh98 Date: Wed, 25 Oct 2023 13:46:25 +0200 Subject: [PATCH] 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)))" ] }, {