.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/signal_processing/sampling/plot_01_bennett_quantization.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code or to run this example in your browser via JupyterLite. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_signal_processing_sampling_plot_01_bennett_quantization.py: Bennett's quantization noise (1948) =================================== Pulse-code modulation, worked out at Bell Laboratories during the Second World War, rounds every sample to one of :math:`2^N` levels (:func:`~engineeringkit.signal_processing.quantize`). W. R. Bennett asked what that rounding does to the signal. For a signal that is busy compared with the step :math:`q` (:func:`~engineeringkit.signal_processing.quantization_step`), the error is spread evenly over :math:`[-q/2, q/2]`, so it behaves like added white noise of power :math:`q^2/12`. For a full-scale sine this gives the rule every ADC data sheet quotes, :math:`\mathrm{SNR} = 6.02N + 1.76` dB: each extra bit buys 6 dB. .. GENERATED FROM PYTHON SOURCE LINES 18-23 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from engineeringkit.signal_processing import quantization_step, quantize, rms, snr_db .. GENERATED FROM PYTHON SOURCE LINES 24-30 The error is uniform with power q^2/12 -------------------------------------- A full-scale sine at an irrational fraction of the sampling rate, so the samples do not repeat, quantized to 8 bits over [-1, 1] V. The model is an approximation: here it is about 1.5% low in rms (0.14 dB). .. GENERATED FROM PYTHON SOURCE LINES 30-40 .. code-block:: Python n = np.arange(200_000) x = np.sin(2 * np.pi * n * (np.sqrt(2) - 1) / 10) * (1 - 1e-9) bits = 8 q = quantization_step(-1.0, 1.0, bits) error = quantize(x, -1.0, 1.0, bits) - x print(f"q = {q * 1e3:.3f} mV; rms error {rms(error) * 1e3:.4f} mV, q/sqrt(12) = {q / np.sqrt(12) * 1e3:.4f} mV") assert np.abs(error).max() <= q / 2 + 1e-15 assert np.isclose(rms(error), q / np.sqrt(12), rtol=0.03) .. rst-class:: sphx-glr-script-out .. code-block:: none q = 7.812 mV; rms error 2.2914 mV, q/sqrt(12) = 2.2553 mV .. GENERATED FROM PYTHON SOURCE LINES 41-46 Six decibels per bit -------------------- The rule is a large-N result: with only a few levels the error is not yet uniform, and the measured SNR falls short. .. GENERATED FROM PYTHON SOURCE LINES 46-55 .. code-block:: Python resolutions = np.arange(2, 17) measured = [snr_db(x, quantize(x, -1.0, 1.0, int(b)) - x) for b in resolutions] rule = 6.02 * resolutions + 1.76 for b, m in zip(resolutions[::4], measured[::4]): print(f"{b:2d} bits: SNR {m:6.2f} dB, 6.02N + 1.76 = {6.02 * b + 1.76:6.2f} dB") assert np.allclose(measured[4:], rule[4:], atol=0.3) assert measured[0] < rule[0] - 0.5 .. rst-class:: sphx-glr-script-out .. code-block:: none 2 bits: SNR 12.81 dB, 6.02N + 1.76 = 13.80 dB 6 bits: SNR 37.61 dB, 6.02N + 1.76 = 37.88 dB 10 bits: SNR 61.90 dB, 6.02N + 1.76 = 61.96 dB 14 bits: SNR 86.03 dB, 6.02N + 1.76 = 86.04 dB .. GENERATED FROM PYTHON SOURCE LINES 56-61 When the signal is not busy --------------------------- A slow ramp spanning only a few steps gives a sawtooth error, correlated with the signal: Bennett's noise model needs many levels crossed. .. GENERATED FROM PYTHON SOURCE LINES 61-78 .. code-block:: Python ramp = np.linspace(-0.1, 0.1, 2000) ramp_error = quantize(ramp, -1.0, 1.0, 5) - ramp fig, axes = plt.subplots(1, 3, figsize=(12, 3.6)) axes[0].hist(error / q, bins=40, density=True) axes[0].axhline(1.0, color="k", ls="--") axes[0].set(xlabel="error / q", ylabel="density", title="8-bit error is uniform") axes[1].plot(resolutions, measured, "o", label="measured") axes[1].plot(resolutions, rule, label="6.02 N + 1.76 dB") axes[1].set(xlabel="bits N", ylabel="SNR [dB]", title="Full-scale sine") axes[1].legend() axes[2].plot(ramp, ramp_error / quantization_step(-1.0, 1.0, 5)) axes[2].set(xlabel="input [V]", ylabel="error / q", title="5-bit ramp: a sawtooth, not noise") fig.tight_layout() plt.show() .. image-sg:: /api/gallery/signal_processing/sampling/images/sphx_glr_plot_01_bennett_quantization_001.png :alt: 8-bit error is uniform, Full-scale sine, 5-bit ramp: a sawtooth, not noise :srcset: /api/gallery/signal_processing/sampling/images/sphx_glr_plot_01_bennett_quantization_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.081 seconds) .. _sphx_glr_download_api_gallery_signal_processing_sampling_plot_01_bennett_quantization.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: lite-badge .. image:: images/jupyterlite_badge_logo.svg :target: ../../../../lite/lab/index.html?path=api/gallery/signal_processing/sampling/plot_01_bennett_quantization.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_01_bennett_quantization.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_01_bennett_quantization.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_01_bennett_quantization.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_