├── .nojekyll ├── docs ├── .nojekyll ├── html │ ├── .nojekyll │ ├── _sources │ │ ├── filter.rst.txt │ │ ├── misc.rst.txt │ │ ├── linalg.rst.txt │ │ ├── plt.rst.txt │ │ ├── features.rst.txt │ │ ├── fft.rst.txt │ │ ├── signal.rst.txt │ │ ├── bearing.rst.txt │ │ ├── acoustics.rst.txt │ │ ├── diagnose.rst.txt │ │ ├── stats.rst.txt │ │ ├── particlefilter.rst.txt │ │ ├── index.rst.txt │ │ ├── publications.rst.txt │ │ └── gettingstarted.rst.txt │ ├── objects.inv │ ├── _static │ │ ├── file.png │ │ ├── minus.png │ │ ├── plus.png │ │ ├── css │ │ │ ├── fonts │ │ │ │ ├── lato-bold.woff │ │ │ │ ├── lato-bold.woff2 │ │ │ │ ├── lato-normal.woff │ │ │ │ ├── lato-normal.woff2 │ │ │ │ ├── Roboto-Slab-Bold.woff │ │ │ │ ├── Roboto-Slab-Bold.woff2 │ │ │ │ ├── lato-bold-italic.woff │ │ │ │ ├── lato-bold-italic.woff2 │ │ │ │ ├── Roboto-Slab-Regular.woff │ │ │ │ ├── Roboto-Slab-Regular.woff2 │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ ├── fontawesome-webfont.woff2 │ │ │ │ ├── lato-normal-italic.woff │ │ │ │ └── lato-normal-italic.woff2 │ │ │ └── badge_only.css │ │ ├── documentation_options.js │ │ ├── js │ │ │ ├── badge_only.js │ │ │ ├── html5shiv.min.js │ │ │ ├── html5shiv-printshiv.min.js │ │ │ └── theme.js │ │ ├── pygments.css │ │ ├── doctools.js │ │ └── language_data.js │ ├── .buildinfo │ ├── search.html │ ├── misc.html │ ├── acoustics.html │ ├── linalg.html │ ├── index.html │ ├── py-modindex.html │ ├── fft.html │ └── publications.html ├── CNAME ├── _config.yml ├── filter.rst ├── misc.rst ├── fft.rst ├── linalg.rst ├── plt.rst ├── bearing.rst ├── features.rst ├── signal.rst ├── diagnose.rst ├── stats.rst ├── acoustics.rst ├── particlefilter.rst ├── doctrees │ ├── fft.doctree │ ├── plt.doctree │ ├── filter.doctree │ ├── index.doctree │ ├── linalg.doctree │ ├── misc.doctree │ ├── signal.doctree │ ├── stats.doctree │ ├── bearing.doctree │ ├── diagnose.doctree │ ├── features.doctree │ ├── acoustics.doctree │ ├── environment.pickle │ ├── publications.doctree │ ├── gettingstarted.doctree │ └── particlefilter.doctree ├── index.html ├── requirements.txt ├── thesis.bib ├── index.rst ├── datasets.bib ├── Makefile ├── make.bat ├── publications.rst ├── gettingstarted.rst ├── publications.bib └── conf.py ├── CNAME ├── .gitignore ├── requirements.txt ├── README.md ├── pyvib ├── acoustics.py ├── __init__.py ├── misc.py ├── linalg.py ├── fft.py ├── bearing.py ├── stats.py ├── diagnose.py ├── ParticleFilter.py └── filter.py ├── setup.py ├── LICENSE ├── .github └── workflows │ └── default.yml └── examples └── diagnosis.py /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/html/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | vibpy.motiontech.no -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | vibpy.motiontech.no -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/filter.rst: -------------------------------------------------------------------------------- 1 | Filters 2 | ======= 3 | 4 | .. automodule:: pyvib.filter 5 | :members: 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | __pycache__/* 3 | pyvib/__pycache__* 4 | *.nc 5 | build* 6 | pyvib.egg-info* -------------------------------------------------------------------------------- /docs/misc.rst: -------------------------------------------------------------------------------- 1 | Miscellaneous 2 | ============= 3 | 4 | .. automodule:: pyvib.misc 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/fft.rst: -------------------------------------------------------------------------------- 1 | Fourier transforms 2 | ================== 3 | 4 | .. automodule:: pyvib.fft 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/_sources/filter.rst.txt: -------------------------------------------------------------------------------- 1 | Filters 2 | ======= 3 | 4 | .. automodule:: pyvib.filter 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/linalg.rst: -------------------------------------------------------------------------------- 1 | Linear algebra 2 | ============== 3 | 4 | .. automodule:: pyvib.linalg 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/plt.rst: -------------------------------------------------------------------------------- 1 | Plotting funtions 2 | ================= 3 | 4 | .. automodule:: pyvib.plt 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/bearing.rst: -------------------------------------------------------------------------------- 1 | Bearing functions 2 | ================= 3 | 4 | .. automodule:: pyvib.bearing 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/features.rst: -------------------------------------------------------------------------------- 1 | Signal features 2 | =============== 3 | 4 | .. automodule:: pyvib.features 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/signal.rst: -------------------------------------------------------------------------------- 1 | Signal processing 2 | ================= 3 | 4 | .. automodule:: pyvib.signal 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/diagnose.rst: -------------------------------------------------------------------------------- 1 | Diagnose functions 2 | ================== 3 | 4 | .. automodule:: pyvib.diagnose 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/_sources/misc.rst.txt: -------------------------------------------------------------------------------- 1 | Miscellaneous 2 | ============= 3 | 4 | .. automodule:: pyvib.misc 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/stats.rst: -------------------------------------------------------------------------------- 1 | Statistical functions 2 | ===================== 3 | 4 | .. automodule:: pyvib.stats 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/acoustics.rst: -------------------------------------------------------------------------------- 1 | Acoustics functions 2 | =================== 3 | 4 | .. automodule:: pyvib.acoustics 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/_sources/linalg.rst.txt: -------------------------------------------------------------------------------- 1 | Linear algebra 2 | ============== 3 | 4 | .. automodule:: pyvib.linalg 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/_sources/plt.rst.txt: -------------------------------------------------------------------------------- 1 | Plotting funtions 2 | ================= 3 | 4 | .. automodule:: pyvib.plt 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/objects.inv -------------------------------------------------------------------------------- /docs/particlefilter.rst: -------------------------------------------------------------------------------- 1 | Particle filter 2 | =============== 3 | 4 | .. automodule:: pyvib.ParticleFilter 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/doctrees/fft.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/fft.doctree -------------------------------------------------------------------------------- /docs/doctrees/plt.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/plt.doctree -------------------------------------------------------------------------------- /docs/html/_sources/features.rst.txt: -------------------------------------------------------------------------------- 1 | Signal features 2 | =============== 3 | 4 | .. automodule:: pyvib.features 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/_sources/fft.rst.txt: -------------------------------------------------------------------------------- 1 | Fourier transforms 2 | ================== 3 | 4 | .. automodule:: pyvib.fft 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/_sources/signal.rst.txt: -------------------------------------------------------------------------------- 1 | Signal processing 2 | ================= 3 | 4 | .. automodule:: pyvib.signal 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/doctrees/filter.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/filter.doctree -------------------------------------------------------------------------------- /docs/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/doctrees/linalg.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/linalg.doctree -------------------------------------------------------------------------------- /docs/doctrees/misc.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/misc.doctree -------------------------------------------------------------------------------- /docs/doctrees/signal.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/signal.doctree -------------------------------------------------------------------------------- /docs/doctrees/stats.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/stats.doctree -------------------------------------------------------------------------------- /docs/html/_sources/bearing.rst.txt: -------------------------------------------------------------------------------- 1 | Bearing functions 2 | ================= 3 | 4 | .. automodule:: pyvib.bearing 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/file.png -------------------------------------------------------------------------------- /docs/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/minus.png -------------------------------------------------------------------------------- /docs/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/plus.png -------------------------------------------------------------------------------- /docs/doctrees/bearing.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/bearing.doctree -------------------------------------------------------------------------------- /docs/doctrees/diagnose.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/diagnose.doctree -------------------------------------------------------------------------------- /docs/doctrees/features.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/features.doctree -------------------------------------------------------------------------------- /docs/html/_sources/acoustics.rst.txt: -------------------------------------------------------------------------------- 1 | Acoustics functions 2 | =================== 3 | 4 | .. automodule:: pyvib.acoustics 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/_sources/diagnose.rst.txt: -------------------------------------------------------------------------------- 1 | Diagnose functions 2 | ================== 3 | 4 | .. automodule:: pyvib.diagnose 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/html/_sources/stats.rst.txt: -------------------------------------------------------------------------------- 1 | Statistical functions 2 | ===================== 3 | 4 | .. automodule:: pyvib.stats 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/doctrees/acoustics.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/acoustics.doctree -------------------------------------------------------------------------------- /docs/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/html/_sources/particlefilter.rst.txt: -------------------------------------------------------------------------------- 1 | Particle filter 2 | =============== 3 | 4 | .. automodule:: pyvib.ParticleFilter 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/doctrees/publications.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/publications.doctree -------------------------------------------------------------------------------- /docs/doctrees/gettingstarted.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/gettingstarted.doctree -------------------------------------------------------------------------------- /docs/doctrees/particlefilter.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/doctrees/particlefilter.doctree -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/html/_static/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrek10/bearing-vibration-diagnostics-toolbox/HEAD/docs/html/_static/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.17.0 2 | pyfftw>=0.12.0 3 | numba>=0.54.1 4 | scipy>=1.7.3 5 | scikit-learn>=1.0.1 6 | pandas>=1.3.4 7 | matplotlib>=3.5.0 8 | psutil>=5.8.0 9 | netcdf4>=1.5.8 10 | -------------------------------------------------------------------------------- /docs/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 58de792574db530f7a0c38bc26179d4d 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | pyfftw 3 | numba 4 | scipy 5 | scikit-learn 6 | pandas 7 | matplotlib 8 | psutil 9 | recommonmark 10 | sphinx>=4.5.0 11 | sphinx-rtd-theme 12 | sphinxcontrib-bibtex 13 | sphinx-autobuild 14 | rstcheck 15 | sphinx-autodoc-typehints 16 | numpydoc 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VibPy 2 | 3 | Vibration analysis toolbox in Python for bearing fault diagnostics. 4 | 5 | VibPy is a Python module created during my PhD in Mechatronics at the University of Agder in Grimstad, Norway. 6 | 7 | The documentation for installation and usage are [here: vibpy.motiontech.no](https://vibpy.motiontech.no). 8 | -------------------------------------------------------------------------------- /docs/thesis.bib: -------------------------------------------------------------------------------- 1 | @article{andreas2019condition, 2 | author = {Klausen, Andreas}, 3 | title = {{Condition monitoring of rolling element bearings during low and variable speed conditions}}, 4 | journal = {Ph. D. thesis, University of Agder}, 5 | year = {2019}, 6 | publisher = {University of Agder}, 7 | keywords={thesis} 8 | } -------------------------------------------------------------------------------- /docs/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '1.0', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false 12 | }; -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to VibPy's documentation! 2 | ================================= 3 | 4 | Indices and tables 5 | ================== 6 | * :ref:`genindex` 7 | * :ref:`modindex` 8 | * :ref:`search` 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | :caption: Contents: 13 | 14 | gettingstarted 15 | signal 16 | fft 17 | filter 18 | features 19 | bearing 20 | plt 21 | diagnose 22 | linalg 23 | stats 24 | particlefilter 25 | acoustics 26 | misc 27 | publications 28 | -------------------------------------------------------------------------------- /docs/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | Welcome to VibPy's documentation! 2 | ================================= 3 | 4 | Indices and tables 5 | ================== 6 | * :ref:`genindex` 7 | * :ref:`modindex` 8 | * :ref:`search` 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | :caption: Contents: 13 | 14 | gettingstarted 15 | signal 16 | fft 17 | filter 18 | features 19 | bearing 20 | plt 21 | diagnose 22 | linalg 23 | stats 24 | particlefilter 25 | acoustics 26 | misc 27 | publications 28 | -------------------------------------------------------------------------------- /pyvib/acoustics.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions related to acoustics 3 | """ 4 | 5 | import numpy as np 6 | import pyfftw 7 | 8 | from .fft import rawfft, rawifft 9 | 10 | pyfftw.interfaces.cache.enable() 11 | 12 | def cepstrum(x): 13 | """ 14 | Get cepstrum of a signal 15 | 16 | Parameters 17 | ---------- 18 | x : float 1D array 19 | Signal 20 | 21 | Returns 22 | ------- 23 | xc : float 1D array 24 | Cepstrum of the signal 25 | 26 | """ 27 | 28 | return np.abs( rawifft( np.log( np.abs( rawfft(x) )**2.0 ) ) )**2.0 29 | -------------------------------------------------------------------------------- /docs/datasets.bib: -------------------------------------------------------------------------------- 1 | @article{NZJOIS_2021, 2 | author = {Andreas Klausen}, 3 | journal = {DataverseNO}, 4 | title = {{UiA Accelerated Life Time Bearing Test Rig – Test 3, Variable speed around 50rpm}}, 5 | year = {2021}, 6 | version = {DRAFT VERSION}, 7 | doi = {10.18710/NZJOIS}, 8 | url = {https://doi.org/10.18710/NZJOIS} 9 | } 10 | 11 | @article{BG1QNG_2020, 12 | author = {Klausen, Andreas}, 13 | journal = {DataverseNO}, 14 | title = {{UiA Accelerated Life Time Bearing Test Rig – Test 1, 250 rpm}}, 15 | year = {2020}, 16 | version = {DRAFT VERSION}, 17 | doi = {10.18710/BG1QNG}, 18 | url = {https://doi.org/10.18710/BG1QNG} 19 | } 20 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = . 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /pyvib/__init__.py: -------------------------------------------------------------------------------- 1 | # ./__init__.py 2 | import sys 3 | 4 | from . import acoustics as acoustics 5 | from . import bearing as bearing 6 | from . import diagnose as diagnose 7 | from . import fft as fft 8 | from . import filter as filter 9 | from . import linalg as linalg 10 | from . import misc as misc 11 | from . import plt as plt 12 | from . import signal as signal 13 | from . import stats as stats 14 | from . import ParticleFilter as ParticleFilter 15 | from . import features as features 16 | 17 | def set_seed(seed=None): 18 | for element in [acoustics, bearing, diagnose, fft, 19 | filter, linalg, misc, plt, signal, stats, 20 | ParticleFilter, features]: 21 | element.np.random.seed(seed) 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='pyvib', 5 | version='1.0.0', 6 | author='Andreas Klausen', 7 | author_email='andreas.klausen@motiontech.no', 8 | packages=find_packages(), 9 | entry_points={ 10 | }, 11 | scripts=[ 12 | ], 13 | url='https://github.com/andrek10/bearing-vibration-diagnostics-toolbox', 14 | license='LICENSE.md', 15 | description='PyVib Python Library', 16 | install_requires=[ 17 | 'numpy>=1.17.0', 18 | 'pyfftw>=0.12.0', 19 | 'numba>=0.54.1', 20 | 'scipy>=1.7.3', 21 | 'scikit-learn>=1.0.1', 22 | 'pandas>=1.3.4', 23 | 'matplotlib>=3.5.0', 24 | 'psutil>=5.8.0', 25 | 'netcdf4>=1.5.8' 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=. 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/html/_static/js/badge_only.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); -------------------------------------------------------------------------------- /docs/publications.rst: -------------------------------------------------------------------------------- 1 | Publications 2 | ============ 3 | 4 | Thesis 5 | ------ 6 | 7 | The pyvib module was built during my PhD education at University of Agder, Grimstad, Norway. 8 | Because of the open-source community, I was able to produce most of my thesis with LaTeX, Inkscape and most importantly Python. 9 | In addition, the PhD thesis was funded by the public: Ministry of Education in Norway. 10 | In this spirit, I release the pyvib module to the public. 11 | 12 | `Here is a link to download the Thesis `_ at the University of Agder AURA. 13 | 14 | .. bibliography:: thesis.bib 15 | :all: 16 | 17 | Relevant Papers 18 | --------------- 19 | 20 | Here is a list of relevant papers written during my PhD. 21 | 22 | .. bibliography:: publications.bib 23 | :all: 24 | 25 | Datasets 26 | -------- 27 | 28 | Some of the datasets that were created using a custom test-rig are available here 29 | 30 | .. bibliography:: datasets.bib 31 | :all: 32 | -------------------------------------------------------------------------------- /docs/html/_sources/publications.rst.txt: -------------------------------------------------------------------------------- 1 | Publications 2 | ============ 3 | 4 | Thesis 5 | ------ 6 | 7 | The pyvib module was built during my PhD education at University of Agder, Grimstad, Norway. 8 | Because of the open-source community, I was able to produce most of my thesis with LaTeX, Inkscape and most importantly Python. 9 | In addition, the PhD thesis was funded by the public: Ministry of Education in Norway. 10 | In this spirit, I release the pyvib module to the public. 11 | 12 | `Here is a link to download the Thesis `_ at the University of Agder AURA. 13 | 14 | .. bibliography:: thesis.bib 15 | :all: 16 | 17 | Relevant Papers 18 | --------------- 19 | 20 | Here is a list of relevant papers written during my PhD. 21 | 22 | .. bibliography:: publications.bib 23 | :all: 24 | 25 | Datasets 26 | -------- 27 | 28 | Some of the datasets that were created using a custom test-rig are available here 29 | 30 | .. bibliography:: datasets.bib 31 | :all: 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Andreas Klausen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /pyvib/misc.py: -------------------------------------------------------------------------------- 1 | """ 2 | misc 3 | """ 4 | 5 | import numpy as np 6 | import pandas as pd 7 | from numba import njit 8 | 9 | 10 | def primefactors(number): 11 | """ 12 | Determine all prime factors for the input number 13 | 14 | Parameters 15 | ---------- 16 | number : int 17 | Number to get prime factors of 18 | 19 | Returns 20 | ------- 21 | factors : 1D array of int 22 | Prime factors 23 | """ 24 | 25 | number = int(number) 26 | i = 2 27 | factors = [] 28 | while i * i <= number: 29 | if number % i: 30 | i += 1 31 | else: 32 | number //= i 33 | factors.append(i) 34 | if number > 1: 35 | factors.append(number) 36 | return np.array(factors, dtype=int) 37 | 38 | @njit(cache=True) 39 | def _XOR(x): 40 | XOR = np.zeros(x.size, dtype=int) 41 | for i in range(1, x.size): 42 | if not x[i] == x[i-1]: 43 | XOR[i] = 1 44 | return XOR 45 | 46 | def pretty(A, precision=2, maxcol=1000, maxrow=1000): 47 | pd.options.display.max_columns = maxcol 48 | pd.options.display.max_rows = maxrow 49 | pd.options.display.precision = precision 50 | print(pd.DataFrame(A)) 51 | -------------------------------------------------------------------------------- /docs/gettingstarted.rst: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | Installation 5 | ------------ 6 | 7 | There are (atleast) two ways to install this module. 8 | 9 | 1. Install using pip in your current Python environment 10 | 2. Download the repository and put `pyvib` folder in your project 11 | 12 | Pip install 13 | ^^^^^^^^^^^ 14 | 15 | Within your current Python environment (either global, through `virtualenv`, or `Anaconda`), run the following code to install from the git repository: 16 | 17 | .. code-block:: 18 | 19 | pip install git+https://github.com/andrek10/bearing-vibration-diagnostics-toolbox.git@master 20 | 21 | This will install PyVib to your activated environment, including all the dependencies. 22 | 23 | Project install 24 | ^^^^^^^^^^^^^^^ 25 | 26 | The repository can be cloned or downloaded, and then put the `pyvib` folder in your project folder. 27 | 28 | To install the required packages, you may use `pip` to install them. 29 | Navigate to the `pyvib` folder and run the following command: 30 | 31 | .. code-block:: 32 | 33 | pip install -r requirements.txt 34 | 35 | Example 36 | ------- 37 | 38 | Here follows an example on running some of the code in the repository. 39 | 40 | For this example, we will use the bearing simulator to make some signals, and perform a diagnosis. 41 | 42 | .. literalinclude:: ../examples/diagnosis.py 43 | :language: python 44 | -------------------------------------------------------------------------------- /docs/html/_sources/gettingstarted.rst.txt: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | Installation 5 | ------------ 6 | 7 | There are (atleast) two ways to install this module. 8 | 9 | 1. Install using pip in your current Python environment 10 | 2. Download the repository and put `pyvib` folder in your project 11 | 12 | Pip install 13 | ^^^^^^^^^^^ 14 | 15 | Within your current Python environment (either global, through `virtualenv`, or `Anaconda`), run the following code to install from the git repository: 16 | 17 | .. code-block:: 18 | 19 | pip install git+https://github.com/andrek10/bearing-vibration-diagnostics-toolbox.git@master 20 | 21 | This will install PyVib to your activated environment, including all the dependencies. 22 | 23 | Project install 24 | ^^^^^^^^^^^^^^^ 25 | 26 | The repository can be cloned or downloaded, and then put the `pyvib` folder in your project folder. 27 | 28 | To install the required packages, you may use `pip` to install them. 29 | Navigate to the `pyvib` folder and run the following command: 30 | 31 | .. code-block:: 32 | 33 | pip install -r requirements.txt 34 | 35 | Example 36 | ------- 37 | 38 | Here follows an example on running some of the code in the repository. 39 | 40 | For this example, we will use the bearing simulator to make some signals, and perform a diagnosis. 41 | 42 | .. literalinclude:: ../examples/diagnosis.py 43 | :language: python 44 | -------------------------------------------------------------------------------- /.github/workflows/default.yml: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/marketplace/actions/sphinx-build 2 | # https://github.com/ammaraskar/sphinx-action-test/blob/master/.github/workflows/default.yml 3 | name: CI 4 | 5 | on: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v1 17 | # Standard drop-in approach that should work for most people. 18 | - uses: ammaraskar/sphinx-action@master 19 | with: 20 | docs-folder: "docs/" 21 | # Publish built docs to gh-pages branch. 22 | # - name: Commit documentation changes 23 | # run: | 24 | # git clone https://github.com/ammaraskar/sphinx-action-test.git --branch gh-pages --single-branch gh-pages 25 | # cp -r docs/html/* gh-pages/ 26 | # cd gh-pages 27 | # touch .nojekyll 28 | # git config --local user.email "action@github.com" 29 | # git config --local user.name "GitHub Action" 30 | # git add . 31 | # git commit -m "Update documentation" -a || true 32 | # # The above command will fail if no changes were present, so we ignore 33 | # # that. 34 | # - name: Push changes 35 | # uses: ad-m/github-push-action@master 36 | # with: 37 | # branch: gh-pages 38 | # directory: gh-pages 39 | # github_token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /examples/diagnosis.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pyvib 4 | 5 | def main(): 6 | # Simulate some vibration data 7 | fs = 250.0 / 60.0 # Reference shaft frequency [Hz] 8 | Fs = 51200.0 # Vibration sampling rate [Hz] 9 | Fs_s = Fs # Position sampling rate [Hz] 10 | t = np.arange(0, 60.0, step=1/Fs) # Time array [s] 11 | v = fs + fs*0.1*np.sin(t) # Make shaft velocity with disturbance [rev/s] 12 | s = np.cumsum(v)/Fs # Integrate shaft velocity [rev] 13 | fo = 5.12 # Fault order (number of bearing fault impacts per revolution) [orders] 14 | vib = pyvib.bearing.bearingsimulation(t, s, fo) # Simulated vibration signal in [m/s2] 15 | 16 | # Perform order tracking to remove frequency variations 17 | ds = 1.0/(np.round(Fs/fs)) # Desired delta shaft position between each order tracked vibration sample 18 | s_ot, vib_ot = pyvib.signal.ordertrack(t, vib, t, s, ds) 19 | 20 | # Remove synchronous components with time synchronous average 21 | vib_ot = pyvib.signal.tsaremove(vib_ot, Fs, fs) 22 | 23 | # Make an envelope of the signal 24 | env = pyvib.signal.envelope(vib_ot) 25 | 26 | # FFT of the envelope 27 | Y, df = pyvib.fft.fft(env, Fs) 28 | 29 | # Plot the envelope FFT as a function of orders (1 is shaft speed, 2 is 2x shaft speed etc.)' 30 | # In the envelope spectrum, you can see harmonics of the bearing fault order 31 | do = df/fs 32 | fig, ax = plt.subplots() 33 | pyvib.plt.plotfft(Y, do, ax, xlim=[0, 30]) 34 | ax.set_xlabel('Shaft orders') 35 | ax.set_ylabel('Envelope FFT Amplitude') 36 | ax.set_title('Envelope Spectrum') 37 | fig.tight_layout() 38 | 39 | if __name__ == '__main__': 40 | main() 41 | plt.show() 42 | -------------------------------------------------------------------------------- /pyvib/linalg.py: -------------------------------------------------------------------------------- 1 | """ 2 | Linear algebra 3 | """ 4 | 5 | import numpy as np 6 | from numba import njit 7 | from scipy.linalg import hankel 8 | from sklearn.utils.extmath import randomized_svd 9 | 10 | 11 | def SVD(x, m): 12 | """ 13 | Produce the singular value decomposition of a signal 14 | Uses the scikit learn.randomized SVD function 15 | 16 | Parameters 17 | ---------- 18 | x : array 1D float 19 | The signal to decompose 20 | m : int 21 | Number of singular values wanted 22 | 23 | Returns 24 | ------- 25 | U : array 2D float 26 | sigma : array 1D float 27 | The singular values ranging from highest to lowest energy 28 | V : array 2D float 29 | 30 | See also 31 | -------- 32 | get_SVDxi - Use get to recombine a singular value back to time-domain 33 | """ 34 | 35 | A = hankel(x[0:m], x[m-1:]) 36 | U, sigma, V = randomized_svd(A, m) 37 | return U, sigma, V 38 | 39 | def get_SVDxi(U, sigma, V, i): 40 | """ 41 | Estimate the i'th singular value composition using the diagonal mean sum 42 | 43 | Parameters 44 | ---------- 45 | U : array 2D float 46 | sigma : array 1D float 47 | The singular values 48 | V : array 2D float 49 | i : int 50 | The i'th singular value to recompose. 0 < i < Sigma.size 51 | """ 52 | 53 | Ai = sigma[i]*(U[:,i][:,None].dot(V[i,:][None,:])) 54 | m = Ai.shape[0] 55 | N = Ai.shape[1] 56 | xi = np.zeros(m+N-1) 57 | _get_SVDxiJit(Ai, xi, m, N) 58 | return xi 59 | 60 | @njit(cache=True) 61 | def _get_SVDxiJit(Ai, xi, m, N): 62 | """ 63 | JIT worker for get_SVDxi 64 | """ 65 | for i in range(0, xi.size): 66 | kstart = N - xi.size + i - 1 67 | elems = 0 68 | for j in range(m - 1, -1, -1): 69 | k = kstart + m - j 70 | if k > -1 and k < N: 71 | xi[i] += Ai[j, k] 72 | elems += 1 73 | xi[i] /= elems 74 | -------------------------------------------------------------------------------- /docs/html/_static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /pyvib/fft.py: -------------------------------------------------------------------------------- 1 | """ 2 | FFT related functions 3 | """ 4 | 5 | import numpy as np 6 | import pyfftw 7 | from psutil import cpu_count 8 | from scipy.signal import detrend as scipy_detrend 9 | 10 | nthreads = cpu_count(logical=False) 11 | pyfftw.interfaces.cache.enable() 12 | 13 | def fft(y, Fs, detrend='constant', hann=True, cons=True, debug=False): 14 | """ 15 | The scaled amplitude frequency spectrum 16 | 17 | Parameters 18 | ---------- 19 | y : float 1D array 20 | Signal you want FFT of. 21 | Fs : float 22 | Sampling frequency 23 | Detrend : string, optional 24 | Detrends the signal using scipy.signal.detrend 25 | - 'constant' to remove mean value 26 | - 'linear' to remove least squares fit 27 | - 'none' to do nothing 28 | hann : bool, optional 29 | Add a hanning window if true. 30 | cons : bool, optional 31 | Whether conservative part of the spectrum should be returned: 32 | 33 | - True returns Fs/2.56 34 | - False returns Fs/2.0 35 | 36 | Returns 37 | ------- 38 | Y : float 1D array 39 | FFT amplitude 40 | df : float 41 | Delta frequency 42 | """ 43 | 44 | # Copy input array 45 | y = np.array(y) 46 | 47 | # Set variables 48 | n = y.size 49 | T = n/Fs 50 | 51 | # Check if conservative output is desired 52 | if cons: 53 | Fmax = Fs/2.56 54 | else: 55 | Fmax = Fs/2.0 56 | 57 | # Get number of lines 58 | LOR = int(T*Fmax) 59 | 60 | # Remove mean if desired 61 | if detrend != 'none': 62 | y = scipy_detrend(y, type=detrend) 63 | 64 | # Apply hanning window 65 | if hann is True: 66 | y = np.hanning(y.size)*y 67 | 68 | # Perform DFT 69 | Y = rawfft(y) 70 | df = 1.0/T 71 | return np.abs(Y[0:LOR])*2.0/n, df 72 | 73 | def rawfft(y): 74 | """ 75 | Raw FFT of the signal. 76 | 77 | Parameters 78 | ---------- 79 | y : float 1D array 80 | Signal to get spectrum of 81 | Fs : float 82 | Sampling frequency in Hz 83 | 84 | Returns 85 | ------- 86 | Y : float 1D array 87 | Spectrum values 88 | df : float 89 | Delta frequency in Hz 90 | """ 91 | 92 | y = np.array(y, copy=True) 93 | Y_obj = pyfftw.builders.fft(y, auto_align_input=True, auto_contiguous=True, planner_effort='FFTW_ESTIMATE', threads=nthreads, overwrite_input=True) 94 | return Y_obj() 95 | 96 | def rawifft(Y): 97 | """ 98 | Raw inverse FFT 99 | 100 | Parameters 101 | ---------- 102 | Y : float 1D array 103 | Spectrum to inverse 104 | 105 | Returns 106 | ------- 107 | y : float 1D array 108 | Time domain signal 109 | """ 110 | 111 | Y_obj = pyfftw.builders.ifft(Y, planner_effort='FFTW_ESTIMATE') 112 | return Y_obj() 113 | -------------------------------------------------------------------------------- /docs/html/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} -------------------------------------------------------------------------------- /docs/html/_static/js/html5shiv-printshiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/html/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t 2 | 3 | 4 | 5 | 6 | Search — Bearing Vibration Diagnostics Toolbox 1.0 documentation 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 65 | 66 |
70 | 71 |
72 |
73 |
74 |
    75 |
  • »
  • 76 |
  • Search
  • 77 |
  • 78 |
  • 79 |
80 |
81 |
82 |
83 |
84 | 85 | 92 | 93 | 94 |
95 | 96 |
97 | 98 |
99 |
100 |
101 | 102 |
103 | 104 |
105 |

© Copyright 2021, Andreas Klausen.

106 |
107 | 108 | Built with Sphinx using a 109 | theme 110 | provided by Read the Docs. 111 | 112 | 113 |
114 |
115 |
116 |
117 |
118 | 123 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | import os 16 | import sys 17 | import sphinx_rtd_theme 18 | sys.path.insert(0, os.path.abspath('..')) 19 | 20 | 21 | # -- Project information ----------------------------------------------------- 22 | 23 | project = 'Bearing Vibration Diagnostics Toolbox' 24 | copyright = '2021, Andreas Klausen' 25 | author = 'Andreas Klausen' 26 | 27 | html_favicon = 'bearing.svg' 28 | 29 | # The short X.Y version 30 | version = '1.0' 31 | # The full version, including alpha/beta/rc tags 32 | release = '1.0' 33 | 34 | 35 | # -- General configuration --------------------------------------------------- 36 | 37 | # If your documentation needs a minimal Sphinx version, state it here. 38 | # 39 | # needs_sphinx = '1.0' 40 | 41 | # Add any Sphinx extension module names here, as strings. They can be 42 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 43 | # ones. 44 | extensions = [ 45 | "sphinx.ext.napoleon", 46 | "sphinx.ext.autodoc", 47 | "sphinx.ext.coverage", 48 | "sphinx.ext.githubpages", 49 | "sphinx_rtd_theme", 50 | "sphinx_autodoc_typehints", 51 | "numpydoc", 52 | "sphinxcontrib.bibtex" 53 | ] 54 | 55 | bibtex_bibfiles = ['publications.bib', 'datasets.bib', 'thesis.bib'] 56 | bibtex_default_style = 'unsrt' 57 | 58 | # Add any paths that contain templates here, relative to this directory. 59 | templates_path = ['_templates'] 60 | 61 | # The suffix(es) of source filenames. 62 | # You can specify multiple suffix as a list of string: 63 | # 64 | # source_suffix = ['.rst', '.md'] 65 | source_suffix = '.rst' 66 | 67 | # The master toctree document. 68 | master_doc = 'index' 69 | 70 | # The language for content autogenerated by Sphinx. Refer to documentation 71 | # for a list of supported languages. 72 | # 73 | # This is also used if you do content translation via gettext catalogs. 74 | # Usually you set "language" from the command line for these cases. 75 | language = None 76 | 77 | # List of patterns, relative to source directory, that match files and 78 | # directories to ignore when looking for source files. 79 | # This pattern also affects html_static_path and html_extra_path. 80 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 81 | 82 | # The name of the Pygments (syntax highlighting) style to use. 83 | pygments_style = None 84 | 85 | 86 | # -- Options for HTML output ------------------------------------------------- 87 | 88 | # The theme to use for HTML and HTML Help pages. See the documentation for 89 | # a list of builtin themes. 90 | # 91 | html_theme = 'sphinx_rtd_theme' 92 | 93 | # Theme options are theme-specific and customize the look and feel of a theme 94 | # further. For a list of options available for each theme, see the 95 | # documentation. 96 | # 97 | # html_theme_options = {} 98 | 99 | # Add any paths that contain custom static files (such as style sheets) here, 100 | # relative to this directory. They are copied after the builtin static files, 101 | # so a file named "default.css" will overwrite the builtin "default.css". 102 | html_static_path = ['_static'] 103 | 104 | # Custom sidebar templates, must be a dictionary that maps document names 105 | # to template names. 106 | # 107 | # The default sidebars (for documents that don't match any pattern) are 108 | # defined by theme itself. Builtin themes are using these templates by 109 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 110 | # 'searchbox.html']``. 111 | # 112 | # html_sidebars = {} 113 | 114 | 115 | # -- Options for HTMLHelp output --------------------------------------------- 116 | 117 | # Output file base name for HTML help builder. 118 | htmlhelp_basename = 'VibPydoc' 119 | 120 | 121 | # -- Options for LaTeX output ------------------------------------------------ 122 | 123 | latex_elements = { 124 | # The paper size ('letterpaper' or 'a4paper'). 125 | # 126 | # 'papersize': 'letterpaper', 127 | 128 | # The font size ('10pt', '11pt' or '12pt'). 129 | # 130 | # 'pointsize': '10pt', 131 | 132 | # Additional stuff for the LaTeX preamble. 133 | # 134 | # 'preamble': '', 135 | 136 | # Latex figure (float) alignment 137 | # 138 | # 'figure_align': 'htbp', 139 | } 140 | 141 | # Grouping the document tree into LaTeX files. List of tuples 142 | # (source start file, target name, title, 143 | # author, documentclass [howto, manual, or own class]). 144 | latex_documents = [ 145 | (master_doc, 'VibPy.tex', 'VibPy Documentation', 146 | 'Andreas Klausen', 'manual'), 147 | ] 148 | 149 | 150 | # -- Options for manual page output ------------------------------------------ 151 | 152 | # One entry per manual page. List of tuples 153 | # (source start file, name, description, authors, manual section). 154 | man_pages = [ 155 | (master_doc, 'vibpy', 'VibPy Documentation', 156 | [author], 1) 157 | ] 158 | 159 | 160 | # -- Options for Texinfo output ---------------------------------------------- 161 | 162 | # Grouping the document tree into Texinfo files. List of tuples 163 | # (source start file, target name, title, author, 164 | # dir menu entry, description, category) 165 | texinfo_documents = [ 166 | (master_doc, 'VibPy', 'VibPy Documentation', 167 | author, 'VibPy', 'One line description of project.', 168 | 'Miscellaneous'), 169 | ] 170 | 171 | 172 | # -- Options for Epub output ------------------------------------------------- 173 | 174 | # Bibliographic Dublin Core info. 175 | epub_title = project 176 | 177 | # The unique identifier of the text. This can be a ISBN number 178 | # or the project homepage. 179 | # 180 | # epub_identifier = '' 181 | 182 | # A unique identification for the text. 183 | # 184 | # epub_uid = '' 185 | 186 | # A list of files that should not be packed into the epub file. 187 | epub_exclude_files = ['search.html'] 188 | 189 | 190 | # -- Extension configuration ------------------------------------------------- 191 | -------------------------------------------------------------------------------- /docs/html/misc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Miscellaneous — Bearing Vibration Diagnostics Toolbox 1.0 documentation 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 65 | 66 |
70 | 71 |
72 |
73 |
74 | 81 |
82 |
83 |
84 |
85 | 86 |
87 |

Miscellaneous

88 |

misc

89 |
90 |
91 | pyvib.misc.primefactors(number)
92 |

Determine all prime factors for the input number

93 |
94 |
Parameters
95 |

number (int) – Number to get prime factors of

96 |
97 |
Returns
98 |

factors – Prime factors

99 |
100 |
Return type
101 |

1D array of int

102 |
103 |
104 |
105 | 106 |
107 | 108 | 109 |
110 |
111 |
115 | 116 |
117 | 118 |
119 |

© Copyright 2021, Andreas Klausen.

120 |
121 | 122 | Built with Sphinx using a 123 | theme 124 | provided by Read the Docs. 125 | 126 | 127 |
128 |
129 |
130 |
131 |
132 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /docs/html/acoustics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Acoustics functions — Bearing Vibration Diagnostics Toolbox 1.0 documentation 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 65 | 66 |
70 | 71 |
72 |
73 |
74 | 81 |
82 |
83 |
84 |
85 | 86 |
87 |

Acoustics functions

88 |

Functions related to acoustics

89 |
90 |
91 | pyvib.acoustics.cepstrum(x)
92 |
93 |

Get cepstrum of a signal

94 |
95 |
96 |
Parameters
97 |
98 |
xfloat 1D array

Signal

99 |
100 |
101 |
102 |
Returns
103 |
104 |
xcfloat 1D array

Cepstrum of the signal

105 |
106 |
107 |
108 |
109 |
110 | 111 |
112 | 113 | 114 |
115 |
116 |
120 | 121 |
122 | 123 |
124 |

© Copyright 2021, Andreas Klausen.

125 |
126 | 127 | Built with Sphinx using a 128 | theme 129 | provided by Read the Docs. 130 | 131 | 132 |
133 |
134 |
135 |
136 |
137 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /pyvib/bearing.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions related to i.e. bearing lifetime and geometry 3 | """ 4 | 5 | import numpy as np 6 | 7 | from .misc import _XOR 8 | 9 | 10 | class bearinglifetimemodel(object): 11 | """ 12 | Makes a Weibull lifetime distribution for a bearing 13 | 14 | Parameters 15 | ---------- 16 | L10 : float 17 | Lifetime of bearing in million revolutions 18 | 19 | Methods 20 | ------- 21 | life: 22 | Get lifetime in million revs based on probability of survival 23 | survival: 24 | Get survival probability based on number of revs 25 | 26 | Returns 27 | ------- 28 | bearing : class object 29 | Bearing object 30 | """ 31 | 32 | def __init__(self, L10): 33 | self.L10 = L10 34 | self.L1 = self.L10/4.0 35 | self.s1 = np.log(np.log(1.0/0.99)) 36 | self.s10 = np.log(np.log(1.0/0.9)) 37 | 38 | self.a = self.L1**(self.s10/self.s1) 39 | self.b = 1.0/(self.s10/self.s1 - 1.0) 40 | self.Lbeta = (self.a/self.L10)**self.b 41 | self.e = self.s1/np.log(self.L1/self.Lbeta) 42 | 43 | def life(self, S): 44 | """ 45 | Get lifetime in million revs based on probability of survival 46 | 47 | Parameters 48 | ---------- 49 | S : float 50 | Probability of survival. <0.0, 1.0> 51 | 52 | Returns 53 | ------- 54 | L : float 55 | Lifetime in million revolutions 56 | """ 57 | 58 | s = np.log(np.log(1.0/S)) 59 | L = self.Lbeta*np.exp(s/self.e) 60 | return L 61 | 62 | def survival(self, L): 63 | """ 64 | Get survival probability based on number of revs 65 | 66 | Parameters 67 | ---------- 68 | L : float 69 | Lifetime in million revs 70 | 71 | Returns 72 | ------- 73 | S : float 74 | Survival probability <0.0, 1.0> 75 | """ 76 | 77 | a = self.e*np.log(L/self.Lbeta) 78 | S = 1.0/(np.exp(np.exp(a))) 79 | return S 80 | 81 | def bearingcharfrequencies(D, d, n, theta=0.0): 82 | """ 83 | Calculate bearing characteristic orders from dimensions. 84 | Equations taken from: 85 | Rolling element bearing diagnostics-A tutorial by R Randall and J Antoni 86 | 87 | Parameters 88 | ---------- 89 | D : float 90 | Pitch diameter 91 | d : float 92 | roller diameter 93 | n : int 94 | Number of rollers 95 | theta : float, optional 96 | Contact angle in degrees 97 | 98 | Returns 99 | ------- 100 | bearing : array_like 101 | Bearing fault orders (inner, roller, cage, outer) 102 | """ 103 | 104 | theta = theta*np.pi/180.0 105 | FTF = 1.0/2.0 * (1.0 - d/D*np.cos(theta)) 106 | BPFO = n*FTF 107 | BPFI = n/2.0 * (1.0 + d/D*np.cos(theta)) 108 | BSF = D/(2.0*d) * (1.0 - (d/D * np.cos(theta))**2.0) 109 | return np.array([BPFI, 2*BSF, FTF, BPFO]) 110 | 111 | def bearingsimulation(t, s, fo, beta=1000.0, omr=10400.0, taustd=0.02, initpause=0.0, amp=1.0, debug=False, seed=None): 112 | """ 113 | Make a simulation of a bearing with an outer race fault 114 | 115 | Parameters 116 | ---------- 117 | t : float 1D array 118 | Time signal 119 | s : float 1D array 120 | Shaft angle position in number of rounds 121 | fo : float 122 | Outer race fault frequency 123 | beta : float, optional 124 | The damping ratio 125 | omr : float, optional 126 | Resonance frequency in Hz 127 | taustd : float, optional 128 | Standard deviation change in percentage between each impact 129 | initpause : float, optional 130 | Initial pause in rounds before first impact 131 | amp : float or function, optional 132 | The impact amplitude vibration: 133 | - If float, the same amplitude all the time 134 | - If function, the instantaneous velocity is used as input and 135 | the amplitude is returned 136 | debug : boolean, optional 137 | Whether debug information is returned 138 | If true, the percentage finished is printed 139 | seed : int or None, optional 140 | Choose a seed number for numpy, must be positive. 141 | 142 | Returns 143 | ------- 144 | x : float 1D array 145 | The bearing vibration 146 | """ 147 | 148 | if seed is not None: 149 | np.random.seed(seed) 150 | omr = omr*2*np.pi 151 | dt = t[1] - t[0] 152 | Fs = 1.0/dt 153 | Thp = 1.0/fo 154 | M = int(s[-1]/Thp) 155 | #L = np.ones(M, dtype=float)*0.5 156 | tau = np.random.randn(M)*Thp*taustd 157 | #impactperiod = np.ones(M)*-1.0 158 | if callable(amp): 159 | v = np.diff(s)/dt 160 | x = np.zeros(t.size) 161 | impulselength = int(-np.log(0.005)/beta*Fs) 162 | per = 0.05 163 | for m in range(0, M, 1): 164 | sumtau = np.sum(tau[0:m]) 165 | I = np.where(s > (m+initpause)*Thp + sumtau)[0] 166 | I = I[0:impulselength] 167 | impacttime = t[I[0]] 168 | if callable(amp): 169 | L = amp(v[I[0]-1]) 170 | else: 171 | L = amp 172 | p1 = L*np.exp(-beta*(t[I] - impacttime)) 173 | p2 = np.sin(omr*(t[I] - impacttime)) 174 | x[I] += p1*p2 175 | if float(m)/float(M) > per and debug is True: 176 | print('Finished %i percent' % (per*100)) 177 | per += 0.05 178 | return x 179 | 180 | def bearingsimulation_ahc(t, s, fo, amp, beta=1000.0, omr=3543.0, initpause=0.5): 181 | """ 182 | Make a simulation of a bearing with an outer race fault 183 | where the shaft speed can pass 0 rpm speed. 184 | 185 | Parameters 186 | ---------- 187 | t : float 1D array 188 | Time signal 189 | s : float 1D array 190 | Shaft angle position in number of rounds 191 | fo : float 192 | Outer race fault frequency 193 | amp : function(v) 194 | Amplitude function with shaft velocity as input. 195 | Must return scalar amplitude only 196 | beta : float, optional 197 | The damping ratio 198 | omr : float, optional 199 | Resonance frequency in Hz 200 | initpause : float, optional 201 | Initial pause in fault-periods before first impact 202 | 203 | Returns 204 | ------- 205 | x : float 1D array 206 | The bearing vibration 207 | """ 208 | 209 | # Make parameters 210 | omr = omr*2*np.pi 211 | dt = t[1] - t[0] 212 | Fs = 1.0/dt 213 | Thp = 1.0/fo 214 | v = np.diff(s)*Fs 215 | 216 | # Initialize vibratin 217 | x = np.zeros(t.size) 218 | 219 | # Check how long an impulse calculation should be 220 | impulselength = int(-np.log(0.0025)/beta*Fs) 221 | 222 | # Check when fault is struck 223 | sfault = np.array((s)*fo - initpause + 1, dtype=int) # -0.25 is cast to 0. +1 is added to cast 0.75 to 0 and 1.0 to 1 224 | XORresults = _XOR(sfault) 225 | Iimpact = np.where(XORresults==1)[0] 226 | 227 | # Make impulses on impact 228 | for j in range(0, len(Iimpact)): 229 | i = Iimpact[j] 230 | L = amp(v[i-1]) 231 | p1 = L*np.exp(-beta*(t[i:i+impulselength] - t[i])) 232 | p2 = np.sin(omr*(t[i:i+impulselength] - t[i])) 233 | x[i:i+impulselength] += p1*p2 234 | 235 | return x 236 | -------------------------------------------------------------------------------- /docs/html/linalg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Linear algebra — Bearing Vibration Diagnostics Toolbox 1.0 documentation 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 65 | 66 |
70 | 71 |
72 |
73 |
74 | 81 |
82 |
83 |
84 |
85 | 86 |
87 |

Linear algebra

88 |

Linear algebra

89 |
90 |
91 | pyvib.linalg.get_SVDxi(U, sigma, V, i)
92 |

Estimate the i’th singular value composition using the diagonal mean sum

93 |
94 |
Parameters
95 |
    96 |
  • U (array 2D float) –

  • 97 |
  • sigma (array 1D float) – The singular values

  • 98 |
  • V (array 2D float) –

  • 99 |
  • i (int) – The i’th singular value to recompose. 0 < i < Sigma.size

  • 100 |
101 |
102 |
103 |
104 | 105 |
106 | 107 | 108 |
109 |
110 |
114 | 115 |
116 | 117 |
118 |

© Copyright 2021, Andreas Klausen.

119 |
120 | 121 | Built with Sphinx using a 122 | theme 123 | provided by Read the Docs. 124 | 125 | 126 |
127 |
128 |
129 |
130 |
131 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /docs/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Welcome to VibPy’s documentation! — Bearing Vibration Diagnostics Toolbox 1.0 documentation 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 64 | 65 |
69 | 70 |
71 |
72 |
73 |
    74 |
  • »
  • 75 |
  • Welcome to VibPy’s documentation!
  • 76 |
  • 77 | View page source 78 |
  • 79 |
80 |
81 |
82 |
83 |
84 | 85 |
86 |

Welcome to VibPy’s documentation!

87 |
88 |
89 |

Indices and tables

90 | 95 | 114 |
115 | 116 | 117 |
118 |
119 |
122 | 123 |
124 | 125 |
126 |

© Copyright 2021, Andreas Klausen.

127 |
128 | 129 | Built with Sphinx using a 130 | theme 131 | provided by Read the Docs. 132 | 133 | 134 |
135 |
136 |
137 |
138 |
139 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /docs/html/py-modindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Python Module Index — Bearing Vibration Diagnostics Toolbox 1.0 documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 65 | 66 |
70 | 71 |
72 |
73 |
74 |
    75 |
  • »
  • 76 |
  • Python Module Index
  • 77 |
  • 78 |
  • 79 |
80 |
81 |
82 |
83 |
84 | 85 | 86 |

Python Module Index

87 | 88 |
89 | p 90 |
91 | 92 | 93 | 94 | 96 | 97 | 99 | 102 | 103 | 104 | 107 | 108 | 109 | 112 | 113 | 114 | 117 | 118 | 119 | 122 | 123 | 124 | 127 | 128 | 129 | 132 | 133 | 134 | 137 | 138 | 139 | 142 | 143 | 144 | 147 | 148 | 149 | 152 | 153 | 154 | 157 | 158 | 159 | 162 |
 
95 | p
100 | pyvib 101 |
    105 | pyvib.acoustics 106 |
    110 | pyvib.bearing 111 |
    115 | pyvib.diagnose 116 |
    120 | pyvib.features 121 |
    125 | pyvib.fft 126 |
    130 | pyvib.filter 131 |
    135 | pyvib.linalg 136 |
    140 | pyvib.misc 141 |
    145 | pyvib.ParticleFilter 146 |
    150 | pyvib.plt 151 |
    155 | pyvib.signal 156 |
    160 | pyvib.stats 161 |
163 | 164 | 165 |
166 |
167 |
168 | 169 |
170 | 171 |
172 |

© Copyright 2021, Andreas Klausen.

173 |
174 | 175 | Built with Sphinx using a 176 | theme 177 | provided by Read the Docs. 178 | 179 | 180 |
181 |
182 |
183 |
184 |
185 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /docs/html/fft.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Fourier transforms — Bearing Vibration Diagnostics Toolbox 1.0 documentation 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 65 | 66 |
70 | 71 |
72 |
73 |
74 | 81 |
82 |
83 |
84 |
85 | 86 |
87 |

Fourier transforms

88 |

FFT related functions

89 |
90 |
91 | pyvib.fft.fft(y, Fs, detrend='constant', hann=True, cons=True, debug=False)
92 |

The scaled amplitude frequency spectrum

93 |
94 |
Parameters
95 |
    96 |
  • y (float 1D array) – Signal you want FFT of.

  • 97 |
  • Fs (float) – Sampling frequency

  • 98 |
  • Detrend (string, optional) – Detrends the signal using scipy.signal.detrend 99 | - ‘constant’ to remove mean value 100 | - ‘linear’ to remove least squares fit 101 | - ‘none’ to do nothing

  • 102 |
  • hann (bool, optional) – Add a hanning window if true.

  • 103 |
  • cons (bool, optional) –

    Whether conservative part of the spectrum should be returned:

    104 |
      105 |
    • True returns Fs/2.56

    • 106 |
    • False returns Fs/2.0

    • 107 |
    108 |

  • 109 |
110 |
111 |
Returns
112 |

    113 |
  • Y (float 1D array) – FFT amplitude

  • 114 |
  • df (float) – Delta frequency

  • 115 |
116 |

117 |
118 |
119 |
120 | 121 |
122 |
123 | pyvib.fft.rawfft(y)
124 |

Raw FFT of the signal.

125 |
126 |
Parameters
127 |
    128 |
  • y (float 1D array) – Signal to get spectrum of

  • 129 |
  • Fs (float) – Sampling frequency in Hz

  • 130 |
131 |
132 |
Returns
133 |

    134 |
  • Y (float 1D array) – Spectrum values

  • 135 |
  • df (float) – Delta frequency in Hz

  • 136 |
137 |

138 |
139 |
140 |
141 | 142 |
143 |
144 | pyvib.fft.rawifft(Y)
145 |

Raw inverse FFT

146 |
147 |
Parameters
148 |

Y (float 1D array) – Spectrum to inverse

149 |
150 |
Returns
151 |

y – Time domain signal

152 |
153 |
Return type
154 |

float 1D array

155 |
156 |
157 |
158 | 159 |
160 | 161 | 162 |
163 |
164 |
168 | 169 |
170 | 171 |
172 |

© Copyright 2021, Andreas Klausen.

173 |
174 | 175 | Built with Sphinx using a 176 | theme 177 | provided by Read the Docs. 178 | 179 | 180 |
181 |
182 |
183 |
184 |
185 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /docs/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | * 33 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL 34 | */ 35 | jQuery.urldecode = function(x) { 36 | if (!x) { 37 | return x 38 | } 39 | return decodeURIComponent(x.replace(/\+/g, ' ')); 40 | }; 41 | 42 | /** 43 | * small helper function to urlencode strings 44 | */ 45 | jQuery.urlencode = encodeURIComponent; 46 | 47 | /** 48 | * This function returns the parsed url parameters of the 49 | * current request. Multiple values per key are supported, 50 | * it will always return arrays of strings for the value parts. 51 | */ 52 | jQuery.getQueryParameters = function(s) { 53 | if (typeof s === 'undefined') 54 | s = document.location.search; 55 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 56 | var result = {}; 57 | for (var i = 0; i < parts.length; i++) { 58 | var tmp = parts[i].split('=', 2); 59 | var key = jQuery.urldecode(tmp[0]); 60 | var value = jQuery.urldecode(tmp[1]); 61 | if (key in result) 62 | result[key].push(value); 63 | else 64 | result[key] = [value]; 65 | } 66 | return result; 67 | }; 68 | 69 | /** 70 | * highlight a given string on a jquery object by wrapping it in 71 | * span elements with the given class name. 72 | */ 73 | jQuery.fn.highlightText = function(text, className) { 74 | function highlight(node, addItems) { 75 | if (node.nodeType === 3) { 76 | var val = node.nodeValue; 77 | var pos = val.toLowerCase().indexOf(text); 78 | if (pos >= 0 && 79 | !jQuery(node.parentNode).hasClass(className) && 80 | !jQuery(node.parentNode).hasClass("nohighlight")) { 81 | var span; 82 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 83 | if (isInSVG) { 84 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 85 | } else { 86 | span = document.createElement("span"); 87 | span.className = className; 88 | } 89 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 90 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 91 | document.createTextNode(val.substr(pos + text.length)), 92 | node.nextSibling)); 93 | node.nodeValue = val.substr(0, pos); 94 | if (isInSVG) { 95 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 96 | var bbox = node.parentElement.getBBox(); 97 | rect.x.baseVal.value = bbox.x; 98 | rect.y.baseVal.value = bbox.y; 99 | rect.width.baseVal.value = bbox.width; 100 | rect.height.baseVal.value = bbox.height; 101 | rect.setAttribute('class', className); 102 | addItems.push({ 103 | "parent": node.parentNode, 104 | "target": rect}); 105 | } 106 | } 107 | } 108 | else if (!jQuery(node).is("button, select, textarea")) { 109 | jQuery.each(node.childNodes, function() { 110 | highlight(this, addItems); 111 | }); 112 | } 113 | } 114 | var addItems = []; 115 | var result = this.each(function() { 116 | highlight(this, addItems); 117 | }); 118 | for (var i = 0; i < addItems.length; ++i) { 119 | jQuery(addItems[i].parent).before(addItems[i].target); 120 | } 121 | return result; 122 | }; 123 | 124 | /* 125 | * backward compatibility for jQuery.browser 126 | * This will be supported until firefox bug is fixed. 127 | */ 128 | if (!jQuery.browser) { 129 | jQuery.uaMatch = function(ua) { 130 | ua = ua.toLowerCase(); 131 | 132 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 133 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 134 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 135 | /(msie) ([\w.]+)/.exec(ua) || 136 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 137 | []; 138 | 139 | return { 140 | browser: match[ 1 ] || "", 141 | version: match[ 2 ] || "0" 142 | }; 143 | }; 144 | jQuery.browser = {}; 145 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 146 | } 147 | 148 | /** 149 | * Small JavaScript module for the documentation. 150 | */ 151 | var Documentation = { 152 | 153 | init : function() { 154 | this.fixFirefoxAnchorBug(); 155 | this.highlightSearchWords(); 156 | this.initIndexTable(); 157 | if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { 158 | this.initOnKeyListeners(); 159 | } 160 | }, 161 | 162 | /** 163 | * i18n support 164 | */ 165 | TRANSLATIONS : {}, 166 | PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, 167 | LOCALE : 'unknown', 168 | 169 | // gettext and ngettext don't access this so that the functions 170 | // can safely bound to a different name (_ = Documentation.gettext) 171 | gettext : function(string) { 172 | var translated = Documentation.TRANSLATIONS[string]; 173 | if (typeof translated === 'undefined') 174 | return string; 175 | return (typeof translated === 'string') ? translated : translated[0]; 176 | }, 177 | 178 | ngettext : function(singular, plural, n) { 179 | var translated = Documentation.TRANSLATIONS[singular]; 180 | if (typeof translated === 'undefined') 181 | return (n == 1) ? singular : plural; 182 | return translated[Documentation.PLURALEXPR(n)]; 183 | }, 184 | 185 | addTranslations : function(catalog) { 186 | for (var key in catalog.messages) 187 | this.TRANSLATIONS[key] = catalog.messages[key]; 188 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 189 | this.LOCALE = catalog.locale; 190 | }, 191 | 192 | /** 193 | * add context elements like header anchor links 194 | */ 195 | addContextElements : function() { 196 | $('div[id] > :header:first').each(function() { 197 | $('\u00B6'). 198 | attr('href', '#' + this.id). 199 | attr('title', _('Permalink to this headline')). 200 | appendTo(this); 201 | }); 202 | $('dt[id]').each(function() { 203 | $('\u00B6'). 204 | attr('href', '#' + this.id). 205 | attr('title', _('Permalink to this definition')). 206 | appendTo(this); 207 | }); 208 | }, 209 | 210 | /** 211 | * workaround a firefox stupidity 212 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 213 | */ 214 | fixFirefoxAnchorBug : function() { 215 | if (document.location.hash && $.browser.mozilla) 216 | window.setTimeout(function() { 217 | document.location.href += ''; 218 | }, 10); 219 | }, 220 | 221 | /** 222 | * highlight the search words provided in the url in the text 223 | */ 224 | highlightSearchWords : function() { 225 | var params = $.getQueryParameters(); 226 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 227 | if (terms.length) { 228 | var body = $('div.body'); 229 | if (!body.length) { 230 | body = $('body'); 231 | } 232 | window.setTimeout(function() { 233 | $.each(terms, function() { 234 | body.highlightText(this.toLowerCase(), 'highlighted'); 235 | }); 236 | }, 10); 237 | $('') 239 | .appendTo($('#searchbox')); 240 | } 241 | }, 242 | 243 | /** 244 | * init the domain index toggle buttons 245 | */ 246 | initIndexTable : function() { 247 | var togglers = $('img.toggler').click(function() { 248 | var src = $(this).attr('src'); 249 | var idnum = $(this).attr('id').substr(7); 250 | $('tr.cg-' + idnum).toggle(); 251 | if (src.substr(-9) === 'minus.png') 252 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 253 | else 254 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 255 | }).css('display', ''); 256 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 257 | togglers.click(); 258 | } 259 | }, 260 | 261 | /** 262 | * helper function to hide the search marks again 263 | */ 264 | hideSearchWords : function() { 265 | $('#searchbox .highlight-link').fadeOut(300); 266 | $('span.highlighted').removeClass('highlighted'); 267 | }, 268 | 269 | /** 270 | * make the url absolute 271 | */ 272 | makeURL : function(relativeURL) { 273 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 274 | }, 275 | 276 | /** 277 | * get the current relative url 278 | */ 279 | getCurrentURL : function() { 280 | var path = document.location.pathname; 281 | var parts = path.split(/\//); 282 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 283 | if (this === '..') 284 | parts.pop(); 285 | }); 286 | var url = parts.join('/'); 287 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 288 | }, 289 | 290 | initOnKeyListeners: function() { 291 | $(document).keydown(function(event) { 292 | var activeElementType = document.activeElement.tagName; 293 | // don't navigate when in search box, textarea, dropdown or button 294 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' 295 | && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey 296 | && !event.shiftKey) { 297 | switch (event.keyCode) { 298 | case 37: // left 299 | var prevHref = $('link[rel="prev"]').prop('href'); 300 | if (prevHref) { 301 | window.location.href = prevHref; 302 | return false; 303 | } 304 | break; 305 | case 39: // right 306 | var nextHref = $('link[rel="next"]').prop('href'); 307 | if (nextHref) { 308 | window.location.href = nextHref; 309 | return false; 310 | } 311 | break; 312 | } 313 | } 314 | }); 315 | } 316 | }; 317 | 318 | // quick alias for translations 319 | _ = Documentation.gettext; 320 | 321 | $(document).ready(function() { 322 | Documentation.init(); 323 | }); 324 | -------------------------------------------------------------------------------- /pyvib/stats.py: -------------------------------------------------------------------------------- 1 | """ 2 | Statistical functions 3 | """ 4 | 5 | from copy import deepcopy 6 | from math import log 7 | 8 | import matplotlib.pyplot as plt 9 | import numpy as np 10 | from numba import jit, njit 11 | 12 | from .misc import pretty 13 | from .signal import envelope, fftwconvolve 14 | 15 | 16 | def arresidual(t, y, a): 17 | """ 18 | Returns the residual of the autoregressive model with coefficients a 19 | 20 | Parameters 21 | ---------- 22 | t : float 1D array 23 | Time signal 24 | y : float 1D array 25 | Signal to filter 26 | a : float 1D array 27 | AR model coeffs 28 | 29 | Returns 30 | ------- 31 | t : float 1D array 32 | New time signal 33 | y : float 1D array 34 | Filtered signal 35 | """ 36 | 37 | y = fftwconvolve(y, a, 'valid') 38 | N = a.size - 1 39 | t = t[N:] 40 | return t, y 41 | 42 | def arresponse(t, y, a): 43 | """ 44 | Returns the predicted response of the autoregressive model with coeffs a 45 | 46 | Parameters 47 | ---------- 48 | t : float 1D array 49 | Time signal 50 | y : float 1D array 51 | Signal 52 | a : float 1D array 53 | AR model coeffs 54 | 55 | Returns 56 | ------- 57 | t : float 1D array 58 | New time signal 59 | y : float 1D array 60 | Filtered signal 61 | """ 62 | 63 | ynoise = fftwconvolve(y, a, 'valid') 64 | N = a.size - 1 65 | y = y[N:] - ynoise 66 | t = t[N:] 67 | return t, y 68 | 69 | def armodel(y, p, Crit=0, debug=False): 70 | """ 71 | This function tries to remove stationary signals by estimating an 72 | autoregressive model on the vibration signal. Afterwards this estiamte can 73 | be subtracted from the original signal using arresidual() 74 | 75 | Parameters 76 | ---------- 77 | y : float 1D array 78 | Vibration data. 79 | p : int 80 | Maximum number of filter coefficients 81 | Crit : int, optional 82 | Criterion for choosing optimal p: 83 | 84 | - 0 uses Akaike Information Criterium (AICc) 85 | - 1 uses Bayesian Information Criterium (BIC) 86 | 87 | debug : boolean, optional 88 | Choose if debug information should be returned 89 | 90 | Returns 91 | ------- 92 | aopt : float 1D array 93 | Optimal AR model parameters 94 | popt : int 95 | Optimal model order 96 | 97 | See also 98 | -------- 99 | arresidual() - Return the residual (random) signal 100 | arresponse() - Returns the autoregressive model response 101 | """ 102 | 103 | y = y - y.mean() 104 | RR = fftwconvolve(np.flipud(y), y) 105 | RR_start = RR.size // 2 106 | 107 | # Actual AR model 108 | abest, sigma, AIC, apmax, popt = _autoRegressiveFilter_v4(RR[RR_start:RR_start+p+1], p, Crit, y.size) 109 | aopt = np.concatenate((np.ones(1), abest[0:popt])) 110 | apmax = np.concatenate((np.ones(1), apmax)) 111 | 112 | if debug is True: 113 | return aopt, popt, apmax, AIC, sigma 114 | else: 115 | return aopt, popt 116 | 117 | def EHNR(x, Fs = 1.0, debug=False): 118 | """ 119 | Get Envelope Harmonic-to-noise ratio 120 | Based on: 121 | Xu, X., Zhao, M., Lin, J., & Lei, Y. (2016). 122 | Envelope harmonic-to-noise ratio for periodic impulses 123 | detection and its application to bearing diagnosis. 124 | Measurement, 91, 385-397. 125 | 126 | Parameters 127 | ---------- 128 | x : float 1D array 129 | Signal 130 | Fs : float, optional 131 | Sampling frequency 132 | debug : boolean, optional 133 | Whether debug information is returned 134 | 135 | Returns 136 | ------- 137 | EHNR : float 138 | The EHNR value 139 | """ 140 | 141 | if x.size % 2 == 1: 142 | x = x[0:-1] 143 | Env_prime = envelope(x) 144 | Env = Env_prime - np.mean(Env_prime) 145 | 146 | t_i = x.size/2 - 1 147 | dt = 1.0/Fs 148 | temp = Env[:t_i] 149 | r_Env = fftwconvolve(Env[:2*t_i], temp[::-1], 'valid')*dt 150 | i_rem = int(t_i/20.0) 151 | for i in range(i_rem, t_i): 152 | der = r_Env[i] - r_Env[i-1] 153 | if der > 0.0: 154 | i_rem = i 155 | break 156 | if i == t_i - 1: 157 | return 0.0 158 | tauMax_i = np.argmax(r_Env[i_rem:]) + i_rem 159 | r_EnvMax = r_Env[tauMax_i] 160 | if debug is True: 161 | plt.figure() 162 | axes = plt.gca() 163 | plt.plot(r_Env) 164 | plt.title('i_rem = %i' % (i_rem)) 165 | ylim = axes.get_ylim() 166 | plt.plot([i_rem, i_rem], [ylim[0], ylim[1]], '-g') 167 | plt.plot([tauMax_i, tauMax_i], [ylim[0], ylim[1]], '--r') 168 | plt.show() 169 | EHNR = r_EnvMax/(r_Env[0] - r_EnvMax) 170 | return EHNR 171 | 172 | @jit(nopython=True, cache=True) 173 | def _autoRegressiveFilter_v4(RR, pmax, Crit, signalSize): 174 | """ 175 | Solves the Yule-Walker equations for autoregressie model 176 | """ 177 | # Make necessary arrays for calculation 178 | abest = np.zeros(pmax) 179 | aold = np.zeros(pmax) 180 | anew = np.zeros(pmax) 181 | sigma = np.zeros(pmax) 182 | AIC = np.zeros(pmax) 183 | AIC_min = 1e10 184 | popt = 0 185 | 186 | # Initialize AR solver 187 | anew[0] = -RR[1]/RR[0] 188 | sigma[0] = (1.0 - anew[0]**2)*RR[0] 189 | 190 | # Initialzie best AR model 191 | AIC[0] = signalSize*(log(sigma[0] / signalSize) + 1.0) + 2.0*(0 + 1 + 1)*signalSize/(signalSize - (0+1) - 2) 192 | AIC_min = AIC[0] 193 | abest[0] = anew[0] 194 | popt = 1 195 | 196 | # Recurrsively iterate through 197 | for k in range(1, pmax): 198 | for j in range(0, k): 199 | aold[j] = anew[j] 200 | temp = 0.0 201 | for j in range(0, k): 202 | temp += aold[j]*RR[k-j] 203 | anew[k] = -(RR[k+1] + temp)/sigma[k-1] 204 | for i in range(0, k): 205 | anew[i] = aold[i] + anew[k]*aold[k-i-1] 206 | sigma[k] = (1 - anew[k]*anew[k])*sigma[k-1] 207 | AIC[k] = signalSize*(log(sigma[k] / signalSize) + 1) + 2*(k+1 + 1)*signalSize/(signalSize - k+1-2) 208 | if AIC[k] < AIC_min: 209 | AIC_min = AIC[k] 210 | popt = k + 1 211 | for j in range(0, k+1): 212 | abest[j] = anew[j] 213 | 214 | # Return 215 | return abest, sigma, AIC, anew, popt 216 | 217 | def _checkOccurences(rho, tol, printOccurences, skipSignals): 218 | """ 219 | Checks occurences of co-variances being over a thrshold 220 | """ 221 | 222 | occurences = np.zeros(n) 223 | for i in range(0, n): 224 | for j in range(i+1, n): 225 | if np.abs(rho[i, j]) >= tol: 226 | occurences[i] += 1 227 | occurences[j] += 1 228 | if printOccurences is True: 229 | print('W: Linear dependency between signals %i-%i' % (i, j)) 230 | if printOccurences is True: 231 | print('Occurences:') 232 | pretty(occurences[:,None]) 233 | 234 | return occurences 235 | 236 | def covariance(A, printSingular=False, tol=0.9, skipSignals=[]): 237 | """ 238 | Compute the covariance of columns in matrix A 239 | 240 | Parameters 241 | ---------- 242 | A : array 243 | [m,n] array with m observatios and n signals. 244 | printSingular : bool, optional 245 | Print list of singular signals 246 | 247 | Returns 248 | ------- 249 | rho : array 250 | Covariance matrix 251 | occurences : array 252 | How many other signals each signal is 253 | similar to. 254 | """ 255 | 256 | n = A.shape[1] 257 | rho = np.zeros((n, n)) 258 | for i in range(0, n): 259 | if i in skipSignals: next 260 | for j in range(0, n): 261 | if j in skipSignals: next 262 | temp = pearsonr(A[:, i], A[:, j]) 263 | rho[i, j] = temp[0] 264 | 265 | occurences = _checkOccurences(rho, tol, printSingular, skipSignals) 266 | 267 | return rho, occurences 268 | 269 | def maximizeUncorrelatedSignals(A, tol=0.9): 270 | """ 271 | Maximize number of signals such that all are uncorrelated 272 | according to the tolerance. 273 | 274 | A : array, or list of arrays 275 | [m,n] array with m observatios and n signals. 276 | If list, n must be equal on all arrays 277 | tol : float, optional 278 | Tolerance for covariance 279 | """ 280 | 281 | # Initialize 282 | if type(A) is np.ndarray: 283 | operation = 0 284 | A = np.array(A) 285 | ABest = np.array(A) 286 | N = A.shape[1] 287 | elif type(A) is list: 288 | operation = 1 289 | A = deepcopy(A) 290 | ABest = deepcopy(A) 291 | N = A[0].shape[1] 292 | else: 293 | print('Wrong input A') 294 | return None 295 | nMax = 0 296 | 297 | skipSignals = [] 298 | for j in range(N, 1): 299 | if operation == 0: 300 | rho, occurences = covariance(A, printSingular=False, tol=tol, skipSignals=skipSignals) 301 | n = np.sum(occurences == 0.0) 302 | if n > nMax: 303 | nMax = n 304 | ABest = np.array(A) 305 | I = np.ones(j, bool) 306 | temp = np.argsort(occurences) 307 | 308 | I[temp[-1]] = False 309 | 310 | 311 | elif operation == 1: 312 | rho = np.empty((j, j)) 313 | for k in range(0, len(A)): 314 | rhoTemp, occurencesTemp = covariance(A, printSingular=False, tol=tol) 315 | rho += rhoTemp 316 | rho /= len(A) 317 | occurences = _checkOccurences(rho, tol, printOccurences=False) 318 | n = np.sum(occurences == 0.0) 319 | if n > nMax: 320 | nMax = n 321 | ABest = deepcopy(A) 322 | 323 | @njit(cache=True) 324 | def _spearmanWorker(temp1, temp2): 325 | """ 326 | Calculate the spearman coefficient 327 | """ 328 | return np.sum((temp1)*(temp2)) / np.sqrt(np.sum((temp1)**2)*np.sum((temp2)**2)) 329 | 330 | def spearman(x1): 331 | """ 332 | Computes the spearman coefficient of input x1 np.array 333 | Assumes the comparison vector is linearly increasing. 334 | 335 | Parameters 336 | ---------- 337 | x1 : float 1D array 338 | The signal to calculate Spearman coefficient of 339 | 340 | Returns 341 | ------- 342 | spearman : float 343 | Spearman coefficient 344 | """ 345 | 346 | x1rankMean = float(x1.size - 1)/2.0 347 | temp1 = np.argsort(x1)[::-1] - x1rankMean 348 | temp2 = np.arange(x1.size-1, -1, -1) - x1rankMean 349 | return _spearmanWorker(temp1, temp2) 350 | 351 | 352 | @njit(cache=True) 353 | def _percentileWorker(x, y, yp): 354 | """ 355 | Calculates the percentile 356 | """ 357 | if yp <= y[0]: 358 | return x[0] 359 | x1 = x[0] 360 | y1 = y[0] 361 | for i in range(1, x.size): 362 | x2 = x[i] 363 | y2 = y[i] 364 | if y1 <= yp <= y2: 365 | xp = (yp - y1)*(x2 - x1)/(y2 - y1) + x1 366 | break 367 | y1 = y2 368 | x1 = x2 369 | return xp 370 | 371 | def percentile(v, p, w=None): 372 | """ 373 | Gets the p percentile of a PDF with weights w and values v 374 | 0.0 <= p <= w.sum 375 | if w is None, w.sum == 1.0 376 | 377 | Parameters 378 | ---------- 379 | v : float 1D array 380 | Value samples 381 | p : float 382 | Percentile 383 | w : float 1D array, optional 384 | Weights of the value samples 385 | 386 | Returns 387 | ------- 388 | percentile : float 389 | The percentile 390 | """ 391 | assert 0.0 <= p <= 1.0 392 | if w is None: 393 | w = np.ones(v.size)/v.size 394 | else: 395 | assert w.size == v.size 396 | I = np.argsort(v) 397 | return _percentileWorker(v[I], np.cumsum(w[I]), p) 398 | -------------------------------------------------------------------------------- /docs/html/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * language_data.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * This script contains the language-specific data used by searchtools.js, 6 | * namely the list of stopwords, stemmer, scorer and splitter. 7 | * 8 | * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"]; 14 | 15 | 16 | /* Non-minified version is copied as a separate JS file, is available */ 17 | 18 | /** 19 | * Porter Stemmer 20 | */ 21 | var Stemmer = function() { 22 | 23 | var step2list = { 24 | ational: 'ate', 25 | tional: 'tion', 26 | enci: 'ence', 27 | anci: 'ance', 28 | izer: 'ize', 29 | bli: 'ble', 30 | alli: 'al', 31 | entli: 'ent', 32 | eli: 'e', 33 | ousli: 'ous', 34 | ization: 'ize', 35 | ation: 'ate', 36 | ator: 'ate', 37 | alism: 'al', 38 | iveness: 'ive', 39 | fulness: 'ful', 40 | ousness: 'ous', 41 | aliti: 'al', 42 | iviti: 'ive', 43 | biliti: 'ble', 44 | logi: 'log' 45 | }; 46 | 47 | var step3list = { 48 | icate: 'ic', 49 | ative: '', 50 | alize: 'al', 51 | iciti: 'ic', 52 | ical: 'ic', 53 | ful: '', 54 | ness: '' 55 | }; 56 | 57 | var c = "[^aeiou]"; // consonant 58 | var v = "[aeiouy]"; // vowel 59 | var C = c + "[^aeiouy]*"; // consonant sequence 60 | var V = v + "[aeiou]*"; // vowel sequence 61 | 62 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | 201 | 202 | 203 | var splitChars = (function() { 204 | var result = {}; 205 | var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, 206 | 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, 207 | 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, 208 | 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, 209 | 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, 210 | 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, 211 | 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, 212 | 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, 213 | 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, 214 | 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; 215 | var i, j, start, end; 216 | for (i = 0; i < singles.length; i++) { 217 | result[singles[i]] = true; 218 | } 219 | var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], 220 | [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], 221 | [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], 222 | [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], 223 | [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], 224 | [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], 225 | [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], 226 | [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], 227 | [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], 228 | [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], 229 | [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], 230 | [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], 231 | [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], 232 | [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], 233 | [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], 234 | [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], 235 | [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], 236 | [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], 237 | [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], 238 | [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], 239 | [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], 240 | [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], 241 | [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], 242 | [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], 243 | [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], 244 | [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], 245 | [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], 246 | [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], 247 | [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], 248 | [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], 249 | [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], 250 | [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], 251 | [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], 252 | [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], 253 | [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], 254 | [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], 255 | [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], 256 | [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], 257 | [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], 258 | [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], 259 | [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], 260 | [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], 261 | [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], 262 | [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], 263 | [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], 264 | [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], 265 | [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], 266 | [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], 267 | [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; 268 | for (i = 0; i < ranges.length; i++) { 269 | start = ranges[i][0]; 270 | end = ranges[i][1]; 271 | for (j = start; j <= end; j++) { 272 | result[j] = true; 273 | } 274 | } 275 | return result; 276 | })(); 277 | 278 | function splitQuery(query) { 279 | var result = []; 280 | var start = -1; 281 | for (var i = 0; i < query.length; i++) { 282 | if (splitChars[query.charCodeAt(i)]) { 283 | if (start !== -1) { 284 | result.push(query.slice(start, i)); 285 | start = -1; 286 | } 287 | } else if (start === -1) { 288 | start = i; 289 | } 290 | } 291 | if (start !== -1) { 292 | result.push(query.slice(start)); 293 | } 294 | return result; 295 | } 296 | 297 | 298 | -------------------------------------------------------------------------------- /docs/html/publications.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Publications — Bearing Vibration Diagnostics Toolbox 1.0 documentation 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 69 | 70 |
74 | 75 |
76 |
77 |
78 | 85 |
86 |
87 |
88 |
89 | 90 |
91 |

Publications

92 |
93 |

Thesis

94 |

The pyvib module was built during my PhD education at University of Agder, Grimstad, Norway. 95 | Because of the open-source community, I was able to produce most of my thesis with LaTeX, Inkscape and most importantly Python. 96 | In addition, the PhD thesis was funded by the public: Ministry of Education in Norway. 97 | In this spirit, I release the pyvib module to the public.

98 |

Here is a link to download the Thesis at the University of Agder AURA.

99 |

100 |
1
101 |

Andreas Klausen. Condition monitoring of rolling element bearings during low and variable speed conditions. Ph. D. thesis, University of Agder, 2019.

102 |
103 |
104 |

105 |
106 |
107 |

Relevant Papers

108 |

Here is a list of relevant papers written during my PhD.

109 |

110 |
1
111 |

Surya Teja Kandukuri, Andreas Klausen, Hamid Reza Karimi, and Kjell Gunnar Robbersmyr. A review of diagnostics and prognostics of low-speed machinery towards wind turbine farm-level health management. Renewable and Sustainable Energy Reviews, 53:697–708, 2016.

112 |
113 |
2
114 |

Andreas Klausen, Roy Werner Folgerø, Kjell G Robbersmyr, and Hamid Reza Karimi. Accelerated bearing life-time test rig development for low speed data acquisition. Modeling, Identification and Control, 38(3):143–156, 2017.

115 |
116 |
3
117 |

Andreas Klausen and Kjell G Robbersmyr. Cross-correlation of whitened vibration signals for low-speed bearing diagnostics. Mechanical Systems and Signal Processing, 118:226–244, 2019.

118 |
119 |
4
120 |

Martin Hemmer, Andreas Klausen, Khang Huynh, Kjell Gunnar Robbersmyr, and Tor Inge Waag. Simulation-driven deep classification of bearing faults from raw vibration data. International Journal of Prognostics and Health Management, 2019.

121 |
122 |
5
123 |

Andreas Klausen, Huynh V Khang, and Kjell G Robbersmyr. Multi-band identification for enhancing bearing fault detection in variable speed conditions. Mechanical Systems and Signal Processing, 139:106422, 2020.

124 |
125 |
6
126 |

Martin Hemmer, Andreas Klausen, Huynh Van Khang, Kjell G Robbersmyr, and Tor I Waag. Health indicator for low-speed axial bearings using variational autoencoders. IEEE Access, 8:35842–35852, 2020.

127 |
128 |
7
129 |

Andreas Klausen, Kjell G Robbersmyr, and Hamid R Karimi. Autonomous bearing fault diagnosis method based on envelope spectrum. IFAC-PapersOnLine, 50(1):13378–13383, 2017.

130 |
131 |
8
132 |

Surya Teja Kandukuri, Andreas Klausen, Van Khang Huynh, and Kjell G Robbersmyr. Fault diagnostics of wind turbine electric pitch systems using sensor fusion approach. Journal of Physics: Conference Series, 1037(3):032036, 2018.

133 |
134 |
9
135 |

Andreas Klausen, Huynh Van Khang, and Kjell G Robbersmyr. Novel threshold calculations for remaining useful lifetime estimation of rolling element bearings. XIII International Conference on Electrical Machines (ICEM), pages 1912–1918, 2018.

136 |
137 |
10
138 |

Mohamed A. A Ismail and Andreas Klausen. Multiple defect size estimation of rolling bearings using autonomous diagnosis and vibrational jerk. 7th World Conference on Structural Control and Monitoring, 2018.

139 |
140 |
11
141 |

Arild Bergesen Husebø, Surya Teja Kandukuri, Andreas Klausen, and Kjell Gunnar Robbersmyr. Rapid diagnosis of induction motor electrical faults using convolutional autoencoder feature extraction. PHM Society European Conference, 5(1):10, 2020.

142 |
143 |
12
144 |

Andreas Klausen, Johannes Kalaoja, Surya Teja Kandukuri, and Kjell G Robbersmyr. Sensitivity analysis of online oil quality monitoring for early detection of water ingress in marine propulsion systems. PHM Society European Conference, 5(1):10, 2020.

145 |
146 |
147 |

148 |
149 |
150 |

Datasets

151 |

Some of the datasets that were created using a custom test-rig are available here

152 |

153 |
1
154 |

Andreas Klausen. UiA Accelerated Life Time Bearing Test Rig – Test 3, Variable speed around 50rpm. DataverseNO, 2021. URL: https://doi.org/10.18710/NZJOIS, doi:10.18710/NZJOIS.

155 |
156 |
2
157 |

Andreas Klausen. UiA Accelerated Life Time Bearing Test Rig – Test 1, 250 rpm. DataverseNO, 2020. URL: https://doi.org/10.18710/BG1QNG, doi:10.18710/BG1QNG.

158 |
159 |
160 |

161 |
162 |
163 | 164 | 165 |
166 |
167 |
170 | 171 |
172 | 173 |
174 |

© Copyright 2021, Andreas Klausen.

175 |
176 | 177 | Built with Sphinx using a 178 | theme 179 | provided by Read the Docs. 180 | 181 | 182 |
183 |
184 |
185 |
186 |
187 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /pyvib/diagnose.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions related to bearing diagnosis 3 | """ 4 | 5 | import numpy as np 6 | 7 | from .fft import fft 8 | from .linalg import get_SVDxi 9 | from .signal import envelope, fftwconvolve 10 | 11 | 12 | def R_SVD(U, sigma, V, time, f_fault, tolerance = 0.02, PMItreshold = 1.0, estimate_xi_func=get_SVDxi, estimate_xi_func_params=None): 13 | """ 14 | Get PMI and weights for each fault frequency in f_fault 15 | Based on the R-SVD algorithm in paper: 16 | "A novel strategy for signal denoising using reweighted SVD and 17 | its applications to weak fault feature enhancement of rotating 18 | machinery" 19 | 20 | Parameters 21 | ---------- 22 | U : array 2D float 23 | U array from SVD 24 | sigma : array 1D float 25 | The singular values 26 | V : array 2D float 27 | V array from SVD 28 | time : array 1D float 29 | The time of the signal before SVD is computed 30 | f_fault : list or array_like 31 | Frequency of each fault type 32 | tolerance : float, optional 33 | Tolerance around f_fault that the peak is searched for. 34 | PMIthreshold : float, optional 35 | The treshold for which a PMI becomes a weight 36 | estimate_xi_func : function, optional 37 | Which function to use to estimate the X_i before autocorr and env. 38 | Deafult is based on an SVD in the time/angular domain. 39 | Must have (U, sigma, V, i) as input, and return x_i 40 | estimate_xi_func_params : list or None, optional 41 | List of extra variables to use as input 42 | None for no extra input 43 | 44 | Returns 45 | ------- 46 | PMI : list of 1D array 47 | PMI for each fault period 48 | W : list of 1D array 49 | Wights for each fault period 50 | """ 51 | 52 | # Get the search region 53 | m = sigma.size 54 | f_fault = np.asanyarray(f_fault) 55 | dt = time[1] - time[0] 56 | T0 = np.zeros(f_fault.size, dtype=int) 57 | T1 = np.zeros(f_fault.size, dtype=int) 58 | PMI = [] 59 | W = [] 60 | for i in range(0, f_fault.size): 61 | T0[i] = int( np.floor( (1.0/(f_fault[i]*(1.0 + tolerance)))/dt ) ) 62 | T1[i] = int( np.ceil( (1.0/(f_fault[i]*(1.0 - tolerance)))/dt ) ) 63 | if T1[i] == T0[i]: 64 | T1[i] += 1 65 | PMI.append(np.zeros(m)) 66 | W.append(np.zeros(m)) 67 | 68 | # Calculate PMI for each fault type 69 | for i in range(0, m): 70 | if estimate_xi_func_params is None: 71 | a_i = estimate_xi_func(U, sigma, V, i) 72 | else: 73 | a_i = estimate_xi_func(U, sigma, V, i, estimate_xi_func_params) 74 | a_i = envelope(a_i) 75 | a_i -= a_i.mean() 76 | R_a = fftwconvolve(np.flipud(a_i), a_i) 77 | # Keep positive part 78 | R_a = R_a[a_i.size-1:] 79 | # Scale by dividing by number of elements 80 | R_a = R_a / np.arange(R_a.size, 0, -1) 81 | # Get T_0 82 | R_0 = R_a[0] 83 | # Calculate R and PMI for each fault type 84 | for k in range(0, f_fault.size): 85 | # print('T0[%i] = %f, T1[%i] = %f, R_a.size = %i' % (k, T0[k], k, T1[k], R_a.size)) 86 | # print(R_a[481:501]) 87 | R_T = np.max(R_a[T0[k]:T1[k]]) 88 | PMI[k][i] = R_T/(R_0 - R_T) 89 | 90 | # Calculate weights 91 | for k in range(0, f_fault.size): 92 | temp = np.sum(PMI[k]) 93 | for i in range(0, m): 94 | if PMI[k][i] > PMItreshold: 95 | W[k][i] = PMI[k][i]/temp 96 | 97 | # Return data 98 | return PMI, W 99 | 100 | def ES_SVD(U, sigma, V, time, f_fault, f_side, PMItreshold, estimate_xi_func=get_SVDxi, estimate_xi_func_params=None): 101 | """ 102 | Envelope Score - SVD 103 | Get PMI and weights for each fault frequency in f_fault 104 | Based on envelope FFT score 105 | 106 | Parameters 107 | ---------- 108 | U : array 2D float 109 | U array from SVD 110 | sigma : array 1D float 111 | The singular values 112 | V : array 2D float 113 | V array from SVD 114 | time : array 1D float 115 | The time of the signal before SVD is computed 116 | f_fault : list or array_like 117 | Frequency of each fault type 118 | f_side : list or array_like 119 | Side-band frequency for each fault type. 120 | Use 0.0 for entries without side-bands 121 | PMIthreshold : float, optional 122 | The treshold for which a PMI becomes a weight 123 | estimate_xi_func : function, optional 124 | Which function to use to estimate the X_i before env. 125 | Deafult is based on an SVD in the time/angular domain. 126 | Must have (U, sigma, V, i) as input, and return x_i 127 | estimate_xi_func_params : list or None, optional 128 | List of extra variables to use as input 129 | None for no extra input 130 | 131 | Returns 132 | ------- 133 | PMI : list of 1D array 134 | PMI for each fault period 135 | W : list of 1D array 136 | Wights for each fault period 137 | """ 138 | 139 | # Get the search region 140 | m = sigma.size 141 | f_fault = np.asanyarray(f_fault) 142 | f_side = np.asanyarray(f_side) 143 | dt = time[1] - time[0] 144 | Fs = 1.0/dt 145 | PMI = [] #PMI is here the envelope score 146 | W = [] 147 | for i in range(0, f_fault.size): 148 | PMI.append(np.zeros(m)) 149 | W.append(np.zeros(m)) 150 | 151 | # Calculate PMI for each fault type 152 | for i in range(0, m): 153 | if estimate_xi_func_params is None: 154 | a_i = estimate_xi_func(U, sigma, V, i) 155 | else: 156 | a_i = estimate_xi_func(U, sigma, V, i, estimate_xi_func_params) 157 | a_i = envelope(a_i) 158 | Y, df = fft(a_i, Fs) 159 | # Calculate PMI for each fault type 160 | for k in range(0, f_fault.size): 161 | PMI[k][i] = diagnosefft(Y, df, f_fault[k], 1.0, f_side[k]) 162 | 163 | # Calculate weights 164 | for k in range(0, f_fault.size): 165 | temp = 0.0 166 | for i in range(0, m): 167 | if PMI[k][i] > PMItreshold: 168 | temp += PMI[k][i] 169 | for i in range(0, m): 170 | if PMI[k][i] > PMItreshold: 171 | W[k][i] = PMI[k][i]/temp 172 | 173 | # Return data 174 | return PMI, W 175 | 176 | def diagnosefft(Y, df, charf, X, subband, debug=False, version=2, harmthreshold=3.0, subthreshold=3.0): 177 | """ 178 | Diagnose a spectrum for bearing faults. Returns a score 179 | 180 | Parameters 181 | ---------- 182 | Y : float 1D array 183 | Spectrum values 184 | df : float 185 | Delta frequency in Hz 186 | charf : float 187 | Harmonic frequency 188 | X : float 189 | Shaft speed in Hz 190 | xubband : float 191 | Sideband frequency 192 | debug : boolean, optional 193 | Whether debug information is returned 194 | version : int, optional 195 | Which version of this script to run. Default 2 with new noise estimator 196 | 197 | Returns 198 | ------- 199 | score : float 200 | Score for fault being present 201 | """ 202 | 203 | #Rescale fft 204 | df /= X 205 | nHarm = 1 206 | score = 0.0 207 | if debug is True: 208 | harmonics = [] 209 | subbandsNeg = [] 210 | subbandsPos = [] 211 | noises = [] 212 | scores = [] 213 | while True: 214 | #if second harmonic, reduce the tolerance for finding peak 215 | if nHarm == 1: 216 | per = 0.02 217 | else: 218 | per = 0.01 219 | #Detect the charf harmonic 220 | j1 = int((nHarm*charf-per*charf)/df) 221 | j2 = int((nHarm*charf+per*charf)/df) 222 | jh = np.argmax(Y[j1:j2]) + j1 223 | harm = Y[jh] 224 | #Reclaibrate characteristic frequency 225 | charf = df*jh/nHarm 226 | #Detect the noise level for the harmonic 227 | if version == 1: 228 | j1n = int((nHarm*charf-0.02*charf)/df) 229 | j2n = int((nHarm*charf+0.02*charf)/df) 230 | if jh - j1n == 0: 231 | noise = np.mean(Y[jh+1:j2n]) 232 | elif j2n - (jh + 1) == 0: 233 | noise = np.mean(Y[j1n:jh]) 234 | else: 235 | noise = (np.mean(Y[j1n:jh]) + np.mean(Y[jh+1:j2n]))/2.0 236 | elif version == 2: 237 | # Find left bottom of harmonic 238 | for i in range(jh, 0, -1): 239 | if Y[i-1] > Y[i]: 240 | jhl = i 241 | break 242 | # Find right bottom of harmonic 243 | for i in range(jh, Y.size, 1): 244 | if Y[i+1] > Y[i]: 245 | jhr = i 246 | break 247 | # j1n = int((nHarm*charf-charf)/df) 248 | # j2n = int((nHarm*charf+charf)/df) 249 | noise = (np.mean(Y[jhl-2:jhl+1]) + np.mean(Y[jhr:jhr+3]))/2.0 250 | # print('j1=%i, j2=%i, jh=%i, harm=%f, jhl=%i, jhr=%i, noise=%f' % (j1,j2,jh,harm,jhl,jhr,noise)) 251 | #If there should be subbands, detect them aswell 252 | if subband > 0.01: 253 | #Check for negative subband first 254 | j1 = int((nHarm*charf - subband - 0.05)/df) 255 | j2 = int((nHarm*charf - subband + 0.05)/df) 256 | jsn = np.argmax(Y[j1:j2]) + j1 257 | negSubBand = Y[jsn] 258 | #Check for position subband 259 | j1 = int((nHarm*charf + subband - 0.05)/df) 260 | j2 = int((nHarm*charf + subband + 0.05)/df) 261 | jsp = np.argmax(Y[j1:j2]) + j1 262 | posSubBand = Y[jsp] 263 | #Make the final score! 264 | #If subband should exist: 265 | if subband > 0.01: 266 | if harm >= noise*harmthreshold and (negSubBand > noise*subthreshold or posSubBand > noise*subthreshold): 267 | score += harm/(noise*3.0)*nHarm**2.0 268 | nHarm += 1 269 | if debug is True: 270 | subbandsNeg.append(jsn) 271 | subbandsPos.append(jsp) 272 | harmonics.append(jh) 273 | noises.append(noise) 274 | scores.append(score) 275 | else: 276 | if debug is True: 277 | return score, subbandsNeg, subbandsPos, harmonics, noises, scores 278 | else: 279 | return score 280 | #if subband should not exist 281 | else: 282 | if harm >= noise*harmthreshold: 283 | score += harm/(noise*harmthreshold)*(nHarm+1.0)**2.0 284 | nHarm += 1 285 | if debug is True: 286 | harmonics.append(jh) 287 | noises.append(noise) 288 | scores.append(score) 289 | else: 290 | if debug is True: 291 | return score, subbandsNeg, subbandsPos, negSubBand, posSubBand, harmonics, noises, scores 292 | else: 293 | return score 294 | 295 | #Check if FFT is too short. If so, return what is done! 296 | test1 = int((nHarm*charf+0.02*charf)/df) 297 | test2 = int((nHarm*charf + subband + 0.05)/df) 298 | if test1 > Y.size or test2 > Y.size: 299 | if debug is True: 300 | return score, subbandsNeg, subbandsPos, negSubBand, posSubBand, harmonics, noises, scores 301 | else: 302 | return score 303 | 304 | def diagnosevibrationfft(Y, df, X, bearing, radial=True): 305 | """ 306 | Diagnose a spectrum for all three fault types 307 | 308 | Parameters 309 | ---------- 310 | Y : float 1D array 311 | Spectrum amplitude 312 | df : float 313 | Delta frequency in Hz 314 | X : float 315 | Shaft speed in Hz 316 | bearing : array_like of floats 317 | Bearing characteristic orders (inner,roller,cage,outer) 318 | radial : boolean, optional 319 | Whether radial forces exists. If so, sidebands exists 320 | 321 | Returns 322 | ------- 323 | scores : float 1D array 324 | Score for [inner,roller,outer] 325 | """ 326 | score = np.zeros(3) 327 | f_c = np.zeros(3) 328 | f_sb = np.zeros(3) 329 | for i in range(0, 3): 330 | if i == 0: #Inner 331 | f_c[i] = bearing[0] 332 | if radial: 333 | f_sb[i] = 1.0 334 | else: 335 | f_sb[i] = 0.0 336 | elif i == 1: #Roller 337 | f_c[i] = bearing[1] 338 | if radial: 339 | f_sb[i] = bearing[2] 340 | else: 341 | f_sb[i] = 0.0 342 | # Cage 343 | 344 | elif i == 2: #Outer ring 345 | f_c[i] = bearing[3] 346 | f_sb[i] = 0.0 347 | 348 | for j in range(0, 3): 349 | tempScore = diagnosefft(Y, df, f_c[j], X, f_sb[j]) 350 | score[j] += tempScore 351 | 352 | return score 353 | -------------------------------------------------------------------------------- /pyvib/ParticleFilter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Particle filter class 3 | """ 4 | 5 | import matplotlib.pyplot as plt 6 | import numpy as np 7 | from numba import njit, prange 8 | 9 | from .stats import percentile 10 | 11 | 12 | def _weightedMean(particles, weights): 13 | """ 14 | Weighted mean of particles 15 | """ 16 | 17 | temp = 0.0 18 | temp2 = 0.0 19 | for i in prange(particles.shape[0]): 20 | temp += particles[i]*weights[i] 21 | temp2 += weights[i] 22 | temp2 += 1.e-300 # avoid round-off to zero 23 | if temp2 >= 1e-300: 24 | return temp/temp2 25 | else: 26 | return 0.0 27 | 28 | @njit(cache=True) 29 | def _weightedVar(mean, particles, weights): 30 | """ 31 | Weighted variance of particles 32 | """ 33 | 34 | temp = 0.0 35 | temp2 = 0.0 36 | for i in prange(particles.shape[0]): 37 | temp += (particles[i] - mean)**2*weights[i] 38 | temp2 += weights[i] 39 | temp2 += 1.e-300 # avoid round-off to zero 40 | if temp2 >= 1e-300: 41 | return temp/temp2 42 | else: 43 | return 0.0 44 | 45 | @njit(cache=True) 46 | def _systematicResample(weights, indexes, randomnumber): 47 | """ 48 | Performs the systemic resampling algorithm used by particle filters. 49 | 50 | This algorithm separates the sample space into N divisions. A single random 51 | offset is used to to choose where to sample from for all divisions. This 52 | guarantees that every sample is exactly 1/N apart. 53 | 54 | Parameters 55 | ---------- 56 | weights : list-like of float 57 | list of weights as floats 58 | """ 59 | 60 | N = weights.shape[0] 61 | # make N subdivisions, and choose positions with a consistent random offset 62 | positions = (randomnumber + np.arange(N)) / N 63 | 64 | cumulative_sum = np.cumsum(weights) 65 | i, j = 0, 0 66 | while i < N: 67 | if positions[i] < cumulative_sum[j]: 68 | indexes[i] = j 69 | i += 1 70 | else: 71 | j += 1 72 | 73 | def _normPdf(mu, var, z): 74 | """ 75 | Gaussian normal PDF 76 | """ 77 | 78 | return 1.0/((2*np.pi*var)**0.5)*np.exp(-(z - mu)**2/(2*var)) 79 | 80 | class ParticleFilter(): 81 | """ 82 | A particle filter class 83 | 84 | Parameters 85 | ---------- 86 | N : int 87 | Number of particles 88 | R : float or array_like 89 | Variance of measured states 90 | len(R) == len(measuredStates) 91 | Q : float or array_like 92 | Variance of actuation error 93 | Part of model 94 | model : function(u, states, parameters, Q) 95 | Model that generates next step of states 96 | using previous states, parameters and Q 97 | statesDerivative can be used as a placeholder for the derivative 98 | Example: 99 | 100 | def model(u, states, parameters, statesDerivative, Q): 101 | m = parameters[:, 0] 102 | k = parameters[:, 1] 103 | c = parameters[:, 2] 104 | dt = 1.0 105 | statesDerivative[:, 0] = states[:, 1] 106 | statesDerivative[:, 1] = 1.0/m*(-k*states[:, 0] - c*states[:, 1] + (u + randn(states.shape[0])*np.sqrt(Q)) 107 | states[:, 0] += statesDerivative[:, 0]*dt 108 | states[:, 1] += statesDerivative[:, 1]*dt 109 | 110 | nStates : int 111 | Number of states in the system 112 | nParameters : int 113 | Number of parameters in the system 114 | measuredStates : int or array_like 115 | Which state number are measured 116 | Could be a single number or multiple in a list. 117 | Observation (z) must have the same length. 118 | """ 119 | 120 | def __init__(self, N, R, Q, model, nStates, nParameters, measuredStates, resampleAlways=False, resampleDebug=False): 121 | self.N = N 122 | if not type(R) == list and not type(R) == np.ndarray: 123 | self.R = [R] 124 | else: 125 | self.R = R 126 | if not type(Q) == list and not type(Q) == np.ndarray: 127 | self.Q = [Q] 128 | else: 129 | self.Q = Q 130 | self.model = model 131 | self.nStates = nStates 132 | self.nParameters = nParameters 133 | self.particles = np.empty((self.N, self.nParameters + self.nStates)) 134 | self.weights = np.ones(self.N, float)/self.N 135 | self.indexes = np.zeros(self.N, 'i') 136 | self.mean = np.zeros(self.particles.shape[1]) 137 | self.var = np.zeros(self.particles.shape[1]) 138 | if type(measuredStates) is not list or type(measuredStates) is not np.ndarray: 139 | self.measuredStates = [measuredStates] 140 | else: 141 | self.measuredStates = measuredStates 142 | self.meanList = [] 143 | self.varList = [] 144 | self.iter = 0 145 | self.statesDerivative = np.empty((self.N, self.nStates)) 146 | self.resampleIterations = [] 147 | self.resampleAlways = resampleAlways 148 | self.resampleDebug = resampleDebug 149 | self.converged = False 150 | 151 | def createUniformParticles(self, ranges): 152 | """ 153 | Create uniformly distributed particles 154 | 155 | Parameters 156 | ---------- 157 | ranges : 2D numpy.ndarray 158 | The uniform range of starting guess 159 | Shaped as [nStates + nParamteres, 2] 160 | """ 161 | 162 | self.createParticleParameters = ['uniform', ranges] 163 | for i in range(0, self.nParameters + self.nStates): 164 | self.particles[:, i] = np.random.uniform(ranges[i, 0], ranges[i, 1], size=self.N) 165 | self.weights = np.ones(self.N, float)/self.N 166 | self.converged = False 167 | 168 | def createGaussianParticles(self, mean, var): 169 | """ 170 | Create gaussian distributed particles 171 | 172 | Parameters 173 | ---------- 174 | mean : array_like 175 | Mean value of gaussian distributed guess 176 | len(mean) = nStates + nParameters 177 | std : array_like 178 | Variation of gaussian distributed guess 179 | len(var) = nStates + nParameters 180 | """ 181 | 182 | self.createParticleParameters = ['gaussian', mean ,var] 183 | for i in range(0, self.nParameters + self.nStates): 184 | self.particles[:, i] = mean[i] + np.random.randn(self.N)*np.sqrt(var[i]) 185 | self.weights = np.ones(self.N, float)/self.N 186 | self.converged = False 187 | 188 | def predict(self, u): 189 | """ 190 | Predict state of next time step using control input 191 | 192 | Parameters 193 | ---------- 194 | u : float or array_like 195 | The control input. 196 | Must follow rules of model function 197 | """ 198 | 199 | self.iter += 1 200 | states = self.get_states() 201 | parameters = self.get_parameters() 202 | # update states 203 | self.model(u, states, parameters, self.Q, self.statesDerivative) 204 | 205 | def get_states(self): 206 | """ 207 | Return the states of particles 208 | 209 | Returns 210 | ------- 211 | states : float 2D array 212 | States of particles 213 | """ 214 | 215 | return self.particles[:, 0:self.nStates] 216 | 217 | def get_parameters(self): 218 | """ 219 | Return the parameters of particles 220 | 221 | Returns 222 | ------- 223 | parameters : float 2D array 224 | Parameters of particles 225 | """ 226 | 227 | return self.particles[:, self.nStates:self.nStates + self.nParameters] 228 | 229 | def update(self, z, debug=False): 230 | """ 231 | Update the weights based on measurements and observation noise 232 | 233 | z : float or array_like: 234 | The observation 235 | len(z) == len(measuredStates) 236 | """ 237 | 238 | if not type(z) == list or type(z) == np.ndarray: 239 | z = [z] 240 | # self.weights .fill(1.) 241 | n = 0 242 | for i in self.measuredStates: 243 | self.weights *= _normPdf(self.particles[:, i], self.R[n], z[n]) 244 | n += 1 245 | if self.weights.sum() < 1e-10: 246 | self.converged = True 247 | 248 | self.weights += 1.e-300 # avoid round-off to zero 249 | temp = self.weights.sum() 250 | if temp >= 1e-300: 251 | self.weights /= temp # normalize 252 | else: 253 | print('pf.update: weight sum is zero') 254 | 255 | def resample(self, thrScale=0.5): 256 | """ 257 | Resamples particles IF necessary 258 | 259 | Parameters 260 | ---------- 261 | thrScale : float, optional 262 | Thresholds for resampling scaled by number of particles 263 | """ 264 | 265 | if not np.isnan(self.weights).any(): 266 | if self._neff() < self.N*thrScale or self.resampleAlways is True: 267 | if self.resampleDebug: 268 | print('Resamples at iter %i' % (self.iter)) 269 | _systematicResample(self.weights, self.indexes, np.random.rand()) 270 | self._resampleFromIndex() 271 | self.resampleIterations.append(self.iter) 272 | self.weights = np.ones(self.N)/self.N 273 | 274 | def estimate(self): 275 | """ 276 | Estimates true value and variance of states and parameters 277 | Results are saved in ParticleFilter.meanList and -.varList 278 | """ 279 | 280 | for i in range(0, self.particles.shape[1]): 281 | self.mean[i] = _weightedMean(self.particles[:, i], self.weights) 282 | self.var[i] = _weightedVar(self.mean[i], self.particles[:, i], self.weights) 283 | self.meanList.append(np.array(self.mean)) 284 | self.varList.append(np.array(self.var)) 285 | 286 | def getMeanAndVariance(self): 287 | """ 288 | Get meanlist and varlist 289 | Mean and var 290 | 291 | Returns 292 | ------- 293 | meanList : list of float 1D array 294 | Mean of each state for each time step 295 | varList : list of float 1D array 296 | Variance of each state for each time step 297 | """ 298 | 299 | return np.array(self.meanList), np.array(self.varList) 300 | 301 | def getPercentile(self, per): 302 | """ 303 | Get the percentile of values 304 | 305 | Parameters 306 | ---------- 307 | per : float 308 | Percentile <0.0, 1.0> 309 | 310 | Returns 311 | ------- 312 | percentile : float 1D array 313 | Percentiles 314 | """ 315 | return np.array([np.percentile(np.array(self.meanList)[row,:], per) for row in range(0, len(self.meanList))]) 316 | 317 | def plotHistogram(self, column): 318 | """ 319 | Plot histogram of a state or parameter 320 | 321 | Parameters 322 | ---------- 323 | column : int 324 | Which column of self.particles should be pltted 325 | """ 326 | 327 | fig, ax = plt.subplots() 328 | ax.hist(self.particles[:, column], 100) 329 | 330 | def simulateTrend(self, iterations, u, percs=[0.5,]): 331 | """ 332 | Simulate the trend moving forward using the particles parameters and state 333 | 334 | Parameters 335 | ---------- 336 | iterations : int 337 | Number of iterations to simulate 338 | u : Data type defined by user in self.model 339 | Control input 340 | percs : list of floats, optional 341 | Which percentile of parameters and states to simulate as true values 342 | 343 | Returns 344 | ------- 345 | output : float 3D array 346 | Simulation results 347 | output[i, j, k] gives the following 348 | i - the iteration 349 | j - the percentile 350 | k - the state number 351 | """ 352 | 353 | particles = np.array(self.particles) 354 | statesDerivative = np.array(self.statesDerivative) 355 | parameters = particles[:, self.nStates:self.nStates + self.nParameters] 356 | output = np.empty((iterations, len(percs), self.nStates)) 357 | weights = np.array(self.weights) 358 | for i in range(0, iterations): 359 | states = particles[:, 0:self.nStates] 360 | if type(u) == list or type(u) == np.ndarray: 361 | self.model(u[i], states, parameters, self.Q, statesDerivative) 362 | else: 363 | self.model(u, states, parameters, self.Q, statesDerivative) 364 | for k in range(0, self.nStates): 365 | # states[:, k] += np.random.randn(states[:, k].size)*sqrt(self.R[k]) 366 | for j in range(0, len(percs)): 367 | output[i, j, k] = percentile(states[:, k], percs[j], weights) 368 | return output 369 | 370 | def _neff(self): 371 | """ 372 | The check to determine whether to re-sample 373 | 374 | Returns 375 | ------- 376 | neff : float 377 | """ 378 | 379 | return 1.0 / np.sum(np.square(self.weights)) 380 | 381 | def _resampleFromIndex(self): 382 | """ 383 | Performs resampling based on new indexes 384 | """ 385 | 386 | self.particles[:] = self.particles[self.indexes] 387 | self.weights[:] = self.weights[self.indexes] 388 | self.weights.fill (1.0 / self.weights.shape[0]) 389 | -------------------------------------------------------------------------------- /pyvib/filter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Filer design 3 | """ 4 | 5 | import numpy as np 6 | from scipy.linalg import solve_toeplitz 7 | from scipy.signal import decimate as scipydecimate 8 | from scipy.signal import firwin 9 | 10 | from .fft import rawfft, rawifft 11 | from .misc import primefactors 12 | from .plt import figure 13 | from .signal import fftwconvolve 14 | 15 | 16 | def get_filterbankfilters(N, fc=0.25): 17 | """ 18 | Make filters for filterbank decomposition and recomposition 19 | These are even order FIR filters 20 | 21 | Parameters 22 | ---------- 23 | N : int 24 | The filter length. Must be even number 25 | fc : float 26 | Normalized cutoff frequency <0.0, 0.5> 27 | 28 | Returns 29 | ------- 30 | f0, f1, h0, h1 : arrays of float 31 | The filter kernels 32 | """ 33 | 34 | # N must to even to make the best filter! 35 | assert N % 2 == 0 36 | 37 | def spectral_flip(h): 38 | g = np.copy(h) 39 | g[0::2] = -g[0::2] 40 | return g 41 | 42 | h0 = firwin(N+1, fc, nyq=0.5) 43 | h1 = spectral_flip(h0) 44 | f0 = spectral_flip(h1)*2 45 | f1 = - spectral_flip(h0)*2 46 | return h0, h1, f0, f1 47 | 48 | def get_filterbankfilters_kurtogram(N=16): 49 | """ 50 | Acquire the filterbank filters used in: 51 | Antoni, Jerome. "Fast computation of the kurtogram for the detection of transient faults." 52 | Mechanical Systems and Signal Processing 21.1 (2007): 108-124. 53 | 54 | Parameters 55 | ---------- 56 | N : int 57 | Number of filterbank coefficients 58 | 59 | Returns 60 | ------- 61 | h : float 1D array 62 | Lowpass filter 63 | g : float 1D array 64 | Highpass filter 65 | """ 66 | 67 | fc = 0.4 68 | h = firwin(N+1,fc)*np.exp(2*1j*np.pi*np.arange(0, N+1)*0.125) 69 | n = np.arange(2, N+2, 1) 70 | g = np.flipud(h)[1:]*(-1.0)**(1-n) 71 | return h, g 72 | 73 | def _fbdecompose(x, h0, h1): 74 | x0 = fftwconvolve(x, h0, 'same') 75 | x1 = fftwconvolve(x, h1, 'same') 76 | v0 = np.copy(x0[0::2]) 77 | v1 = np.copy(x1[1::2]) 78 | xsize = x0.size 79 | return v0, v1, xsize 80 | 81 | def _fbcompose(x0, x1, f0, f1, xsize): 82 | c0 = np.zeros(xsize) 83 | c0[0::2] = np.copy(x0) 84 | c1 = np.zeros(xsize) 85 | c1[1::2] = np.copy(x1) 86 | y0 = fftwconvolve(c0, f0, 'same') 87 | y1 = fftwconvolve(c1, f1, 'same') 88 | return y0+y1 89 | 90 | def filterbank_decompose(x, h0, h1, level): 91 | """ 92 | Decompose a signal using supplied filters for a certain numebr of levels 93 | 94 | Parameters 95 | ---------- 96 | x : float 1D array 97 | Signal 98 | h0, h1 : float 1D arrays 99 | Filter kernels 100 | h0 is low-pass, h1 is highpass 101 | level : int 102 | The filter decomposition level 103 | 104 | Returns 105 | ------- 106 | xbank : list of float 1D arrays 107 | The filter-bank coefficients ranging from lowest frequency to highest frequency 108 | xsizes : list of lists of integers 109 | The sizes of signals before decomposing. 110 | Only needed for recomposing using filterbank_compose() 111 | 112 | See also 113 | -------- 114 | get_filterbankfilters() : Makes the h0 and h1 filter kernel 115 | filterbank_compose() : Re-combposes an xbank into a signal 116 | """ 117 | 118 | xbank = [x,] 119 | xsizes = [] 120 | for i in range(0, level): 121 | xnew = [] 122 | xsizes.append([]) 123 | for j in range(0, len(xbank)): 124 | v0, v1, xsize = _fbdecompose(xbank[j], h0, h1) 125 | xnew.append(v0) 126 | xnew.append(v1) 127 | xsizes[i].append(xsize) 128 | xbank = xnew 129 | return xbank, xsizes 130 | 131 | def filterbank_compose(xbank, f0, f1, xsizes): 132 | """ 133 | Recompose the filter bank to a single signal 134 | 135 | Parameters 136 | ---------- 137 | xbank : float 1D array 138 | The filterbank 139 | f0, f1 : float 1D arrays 140 | The filter kernels 141 | xsizes : list of list of ints 142 | The sizes of signals before decomposing 143 | 144 | Returns 145 | ------- 146 | x_hat : float array 147 | The recomposed signal. Should be close to the original 148 | signal x after applying the lag 149 | lag : int 150 | The lag of the recomposed signal 151 | Should ideally use x_hat[lag:-lag] after recomposition 152 | x_hat[lag:-lag] approximates x[0:-lag*2] 153 | """ 154 | 155 | level = int(np.log2(len(xbank))) 156 | for i in range(0, level): 157 | xbank_new = [] 158 | for j in range(0, len(xbank), 2): 159 | xsize = xsizes[len(xsizes)-i-1][j//2] 160 | y = _fbcompose(xbank[j], xbank[j+1], f0, f1, xsize) 161 | xbank_new.append(y) 162 | xbank = xbank_new 163 | lag = int(2**level - 1) 164 | return xbank[0], lag 165 | 166 | def waveletfilter(f0, sigma, Fs, N): 167 | """ 168 | Constructs the frequency transformed wavelet filter. Can be used to 169 | filter a frequency transformed signal by taking Y*Ksi. 170 | 171 | Parameters 172 | ---------- 173 | f0 : float 174 | The center frequency for the bandpass filter in Hz 175 | sigma : float 176 | The width of the filter in Hz 177 | Fs : float 178 | The sampling frequency of the signal in Hz 179 | N : int 180 | The number of samples in the signal in Hz 181 | 182 | Returns 183 | ------- 184 | Ksi : float 1D array 185 | Filter in the frequency domain. 186 | """ 187 | 188 | dt = 1.0/Fs 189 | T = dt*float(N) 190 | df = 1.0/T 191 | f = np.arange(0, Fs/2.0, df) 192 | Ksi = np.exp(-(np.pi**2.0/sigma**2.0)*(f - f0)**2.0) 193 | Ksi = np.concatenate((Ksi, np.zeros(N - Ksi.size))) 194 | return Ksi 195 | 196 | def blinddeconvolution(z, L, part=1.0, k=4.0, maxIter=1000, maxMu=2.0, stopCrit=0.01, debug=False): 197 | """ 198 | Iteratively identifies a filter g that deconvolves the filter h 199 | originally applied to z to return the deconvolved signal x. 200 | The iterator tries to maximize the kurtosis (impulsivity) of the 201 | deconvolved signal. 202 | The deconvolution is afterwards performed using: 203 | x = pyvib.signal.fftwconvolve(z, gNew, 'valid') 204 | 205 | Parameters 206 | ---------- 207 | z : float 1D array 208 | Signal to deconvolve 209 | L : int 210 | Length of filter 211 | part : float, optional 212 | Percentage of the data to train the filter on. 213 | Must be within <0, 1> 214 | k - float, optional 215 | Exponent of the objective. 4 gives kurtosis 216 | maxIter : int, optional 217 | Maximum number of iterations to run 218 | maxMu : float, optional 219 | Maximum training coefficient 220 | stopCrit : float, optional 221 | Stopping criterion 222 | debug : boolean, optional 223 | Print progression if true 224 | 225 | Returns 226 | ------- 227 | gNew : float 1D array 228 | Filter kernel that deconvolves the signal 229 | """ 230 | 231 | temp = np.ones(L) 232 | temp[::2] *= -1.0 233 | gNew = temp*np.random.rand(L) 234 | g = np.copy(gNew) 235 | assert 0.0 < part <= 1.0 236 | i1 = 0 237 | i2 = i1 + int(part*z.size) 238 | N = i2 - i1 239 | Rxx = fftwconvolve(np.flipud(z[i1:i2]), z[i1:i2]) 240 | cc = Rxx[N-1:N+L-1] 241 | mu = 1.0 242 | eOld = 1e15 243 | for i in range(0, maxIter): 244 | g = g + mu*(gNew - g) 245 | y = fftwconvolve(z[i1:i2], g, 'valid') 246 | alpha = np.sum(y**2.0)/np.sum(y**k) 247 | if y.size > (i2-i1): 248 | temp = np.flipud(fftwconvolve(np.flipud(y**(k-1)), z[i1:i2], 'valid')) 249 | else: 250 | temp = np.flipud(fftwconvolve(np.flipud(z[i1:i2]), y**(k-1), 'valid')) 251 | b = alpha*temp 252 | gNew = solve_toeplitz(cc, b) 253 | e = np.sqrt(np.sum((g - gNew)**2.0)) 254 | if e > eOld: 255 | mu = 1.0 256 | else: 257 | mu *= 1.1 258 | if mu >= maxMu: 259 | mu = maxMu 260 | eOld = e 261 | if debug is True: 262 | print('i = %i, e = %.3f, mu = %.3f' % (i, e, mu)) 263 | if e < stopCrit: 264 | break 265 | 266 | return gNew 267 | 268 | def filterdesign(Yh, M, plot=True): 269 | """ 270 | Design a FIR filter that matches a frequency response 271 | 272 | Parameters 273 | ---------- 274 | Yh : float 1D array 275 | The amplitude specrum to match 276 | M : int 277 | Number of coefficients to use in the filter 278 | plot : boolean, optional 279 | Whether the resulting filter should be plotted 280 | 281 | Resturns 282 | -------- 283 | h : float 1D array 284 | The designed FIR filter kernel 285 | """ 286 | 287 | if M % 2 != 0: 288 | M += 1 289 | if M > Yh.size: 290 | M = Yh.size - 2 291 | if M % 2 != 0: 292 | M -= 1 293 | 294 | y = rawifft(Yh) 295 | f = np.fft.fftfreq(y.size) 296 | 297 | h = np.zeros(M+1, dtype='complex') 298 | h[0:M//2] = y[-M//2:] 299 | h[M//2:M+1] = y[0:M+1-M//2] 300 | Hamm = np.hamming(M+1) 301 | h[0:M+1] *= Hamm 302 | 303 | if plot is True: 304 | hplot = np.zeros(y.size, dtype='complex') 305 | hplot[0:h.size] = h 306 | Yh2 = rawfft(hplot) 307 | fig, ax = figure(1, 1, width=500.0, height=300.0) 308 | ax.plot(f, Yh/Yh.max(), 'b', label='Input') 309 | ax.plot(f, np.abs(Yh2)/Yh2.max(), '--r', label='Returned') 310 | 311 | return h 312 | 313 | def decimate(y, decimatelist): 314 | """ 315 | Apply decimation of a signal y with time t by applying an IIR filter 316 | with the decimation factor given by all items in decimatelist 317 | 318 | Parameters 319 | ---------- 320 | y : float 1D array 321 | The signal 322 | decimatelist : int, array_like 323 | Acquire this list using get_decimatelist() 324 | 325 | Returns 326 | ------- 327 | yd : float 1D array 328 | Decimated signal 329 | 330 | See Also 331 | -------- 332 | get_decimatelist() 333 | """ 334 | 335 | decimatelist = np.asarray(decimatelist, dtype=int) 336 | for i in range(0, decimatelist.size): 337 | y = scipydecimate(y, int(decimatelist[i]), zero_phase=False) 338 | return y 339 | 340 | def get_decimatelist(desireddecimation, maxdec=12, step=-1): 341 | """ 342 | Generate a decimation list for using the decimate function 343 | 344 | Parameters 345 | ---------- 346 | desireddecimation : int 347 | Desired decimation factor in total 348 | Going from a sample frequency of 50 kHz to 10 kHz is a factor of 5 349 | maxdec : int, optional 350 | Maximum decimation per iteration 351 | Defaults to 12 352 | direction : int, optional 353 | Step to make if a decimation factor is not suitable 354 | 355 | Returns 356 | ------- 357 | decimatelist : list 358 | Decimation factors to follow per decimation iteration. 359 | 360 | See Also 361 | -------- 362 | decimate 363 | """ 364 | 365 | desireddecimation = int(desireddecimation) 366 | if desireddecimation <= maxdec: 367 | return np.array([desireddecimation,], dtype=int) 368 | dec = desireddecimation 369 | while True: 370 | decL = primefactors(dec) 371 | if np.greater(decL, maxdec).any(): 372 | dec += step 373 | else: 374 | break 375 | if dec <= maxdec: 376 | decL = np.array([dec,], dtype=int) 377 | return decL 378 | 379 | def cpw(x): 380 | """ 381 | Removes synchronous parts of a signal using Cepstrum Pre-Whitening 382 | 383 | Parameters 384 | ---------- 385 | x : float 1D array 386 | Signal 387 | 388 | Returns 389 | ------- 390 | xPW : float 1D array 391 | Whitened signal 392 | """ 393 | 394 | spec = rawfft(x - np.mean(x)) 395 | spec[np.less(spec, 1e-15)] = 1e-15 396 | temp = spec/np.abs(spec) 397 | if np.sum(np.isnan(temp)) > 0: 398 | print('CPW Warning: NaN found in FFT of x!') 399 | print(spec[np.isnan(temp)]) 400 | temp[np.isnan(temp)] = 0.0 401 | temp2 = rawifft(temp) 402 | xPW = np.real(temp2) 403 | return xPW 404 | 405 | 406 | class IIRFilter(): 407 | """ 408 | An IIR filter object that can update per sample. 409 | 410 | Parameters 411 | ---------- 412 | b : float 1D array 413 | Filter coefficients 414 | a : float 1D array 415 | Filter coefficients 416 | 417 | Object functions 418 | ---------------- 419 | update() : Filter the next sample 420 | 421 | See also 422 | -------- 423 | scipy.signal.butter() - Create an IIR filter 424 | """ 425 | 426 | def __init__(self, b, a): 427 | self._M = a.size 428 | self._N = b.size 429 | self._a = a 430 | self._b = b 431 | self._x = np.zeros(self._M) 432 | self._y = np.zeros(self._M) 433 | self._iters = 0 434 | def update(self, xin): 435 | """ 436 | Updates the filter with a new sample 437 | 438 | Parameters 439 | ---------- 440 | xin : float 441 | The new sample 442 | 443 | Returns 444 | ------- 445 | xout : float 446 | The filtered sample 447 | """ 448 | 449 | if self._iters < self._M: 450 | # Initialize 451 | i = self._iters 452 | self._x[i] = xin 453 | for j in range(0, i+1, 1): 454 | self._y[i] += self._x[i-j]*self._b[j] 455 | for j in range(1, i+1, 1): 456 | self._y[i] -= self._y[i-j]*self._a[j] 457 | self._y[i] /= self._a[0] 458 | else: 459 | # Update 460 | self._y[0:self._M-1] = self._y[1:self._M] 461 | self._y[-1] = 0.0 462 | self._x[0:self._M-1] = self._x[1:self._M] 463 | self._x[-1] = xin 464 | i = self._M - 1 465 | for j in range(0, self._N): 466 | self._y[-1] += self._x[i-j]*self._b[j] 467 | for j in range(1, self._M): 468 | self._y[-1] -= self._y[i-j]*self._a[j] 469 | self._y[-1] /= self._a[0] 470 | 471 | self._iters += 1 472 | return self._y[i] 473 | --------------------------------------------------------------------------------