Add basic preprocessing and visualisation
This commit is contained in:
commit
841da23c47
176
.gitignore
vendored
Normal file
176
.gitignore
vendored
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/python
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
||||||
|
|
||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
### Python Patch ###
|
||||||
|
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||||
|
poetry.toml
|
||||||
|
|
||||||
|
# ruff
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# LSP config files
|
||||||
|
pyrightconfig.json
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/python
|
||||||
14
Pipfile
Normal file
14
Pipfile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
flask = "*"
|
||||||
|
numpy = "*"
|
||||||
|
flask-sock = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.12"
|
||||||
232
Pipfile.lock
generated
Normal file
232
Pipfile.lock
generated
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "7a239bffead5df2e0a422f7b84d0ee970f85f9d94ab29b1d56d004f67144422c"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.12"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"blinker": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf",
|
||||||
|
"sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==1.9.0"
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
|
||||||
|
"sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==8.1.7"
|
||||||
|
},
|
||||||
|
"flask": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac",
|
||||||
|
"sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==3.1.0"
|
||||||
|
},
|
||||||
|
"flask-sock": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:caac4d679392aaf010d02fabcf73d52019f5bdaf1c9c131ec5a428cb3491204a",
|
||||||
|
"sha256:e023b578284195a443b8d8bdb4469e6a6acf694b89aeb51315b1a34fcf427b7d"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==0.7.0"
|
||||||
|
},
|
||||||
|
"h11": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
|
||||||
|
"sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==0.14.0"
|
||||||
|
},
|
||||||
|
"itsdangerous": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef",
|
||||||
|
"sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==2.2.0"
|
||||||
|
},
|
||||||
|
"jinja2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369",
|
||||||
|
"sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==3.1.4"
|
||||||
|
},
|
||||||
|
"markupsafe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4",
|
||||||
|
"sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30",
|
||||||
|
"sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0",
|
||||||
|
"sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9",
|
||||||
|
"sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396",
|
||||||
|
"sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13",
|
||||||
|
"sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028",
|
||||||
|
"sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca",
|
||||||
|
"sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557",
|
||||||
|
"sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832",
|
||||||
|
"sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0",
|
||||||
|
"sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b",
|
||||||
|
"sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579",
|
||||||
|
"sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a",
|
||||||
|
"sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c",
|
||||||
|
"sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff",
|
||||||
|
"sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c",
|
||||||
|
"sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22",
|
||||||
|
"sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094",
|
||||||
|
"sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb",
|
||||||
|
"sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e",
|
||||||
|
"sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5",
|
||||||
|
"sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a",
|
||||||
|
"sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d",
|
||||||
|
"sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a",
|
||||||
|
"sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b",
|
||||||
|
"sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8",
|
||||||
|
"sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225",
|
||||||
|
"sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c",
|
||||||
|
"sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144",
|
||||||
|
"sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f",
|
||||||
|
"sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87",
|
||||||
|
"sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d",
|
||||||
|
"sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93",
|
||||||
|
"sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf",
|
||||||
|
"sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158",
|
||||||
|
"sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84",
|
||||||
|
"sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb",
|
||||||
|
"sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48",
|
||||||
|
"sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171",
|
||||||
|
"sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c",
|
||||||
|
"sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6",
|
||||||
|
"sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd",
|
||||||
|
"sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d",
|
||||||
|
"sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1",
|
||||||
|
"sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d",
|
||||||
|
"sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca",
|
||||||
|
"sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a",
|
||||||
|
"sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29",
|
||||||
|
"sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe",
|
||||||
|
"sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798",
|
||||||
|
"sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c",
|
||||||
|
"sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8",
|
||||||
|
"sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f",
|
||||||
|
"sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f",
|
||||||
|
"sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a",
|
||||||
|
"sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178",
|
||||||
|
"sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0",
|
||||||
|
"sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79",
|
||||||
|
"sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430",
|
||||||
|
"sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==3.0.2"
|
||||||
|
},
|
||||||
|
"numpy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe",
|
||||||
|
"sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0",
|
||||||
|
"sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48",
|
||||||
|
"sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a",
|
||||||
|
"sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564",
|
||||||
|
"sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958",
|
||||||
|
"sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17",
|
||||||
|
"sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0",
|
||||||
|
"sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee",
|
||||||
|
"sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b",
|
||||||
|
"sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4",
|
||||||
|
"sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4",
|
||||||
|
"sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6",
|
||||||
|
"sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4",
|
||||||
|
"sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d",
|
||||||
|
"sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f",
|
||||||
|
"sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f",
|
||||||
|
"sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f",
|
||||||
|
"sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56",
|
||||||
|
"sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9",
|
||||||
|
"sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd",
|
||||||
|
"sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23",
|
||||||
|
"sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed",
|
||||||
|
"sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a",
|
||||||
|
"sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098",
|
||||||
|
"sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1",
|
||||||
|
"sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512",
|
||||||
|
"sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f",
|
||||||
|
"sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09",
|
||||||
|
"sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f",
|
||||||
|
"sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc",
|
||||||
|
"sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8",
|
||||||
|
"sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0",
|
||||||
|
"sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761",
|
||||||
|
"sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef",
|
||||||
|
"sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5",
|
||||||
|
"sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e",
|
||||||
|
"sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b",
|
||||||
|
"sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d",
|
||||||
|
"sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43",
|
||||||
|
"sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c",
|
||||||
|
"sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41",
|
||||||
|
"sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff",
|
||||||
|
"sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408",
|
||||||
|
"sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2",
|
||||||
|
"sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9",
|
||||||
|
"sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57",
|
||||||
|
"sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb",
|
||||||
|
"sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9",
|
||||||
|
"sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3",
|
||||||
|
"sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a",
|
||||||
|
"sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0",
|
||||||
|
"sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e",
|
||||||
|
"sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598",
|
||||||
|
"sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.10'",
|
||||||
|
"version": "==2.1.3"
|
||||||
|
},
|
||||||
|
"simple-websocket": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c",
|
||||||
|
"sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==1.1.0"
|
||||||
|
},
|
||||||
|
"werkzeug": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e",
|
||||||
|
"sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==3.1.3"
|
||||||
|
},
|
||||||
|
"wsproto": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065",
|
||||||
|
"sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"
|
||||||
|
],
|
||||||
|
"markers": "python_full_version >= '3.7.0'",
|
||||||
|
"version": "==1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
||||||
119
src/csi.py
Normal file
119
src/csi.py
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import struct
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
RATE_MCS_MOD_TYPE_POS = 8
|
||||||
|
RATE_MCS_MOD_TYPE_MSK = 0x7 << RATE_MCS_MOD_TYPE_POS
|
||||||
|
RATE_MCS_CCK_MSK = 0 << RATE_MCS_MOD_TYPE_POS
|
||||||
|
RATE_MCS_LEGACY_OFDM_MSK = 1 << RATE_MCS_MOD_TYPE_POS
|
||||||
|
RATE_MCS_HT_MSK = 2 << RATE_MCS_MOD_TYPE_POS
|
||||||
|
RATE_MCS_VHT_MSK = 3 << RATE_MCS_MOD_TYPE_POS
|
||||||
|
RATE_MCS_HE_MSK = 4 << RATE_MCS_MOD_TYPE_POS
|
||||||
|
RATE_MCS_EHT_MSK = 5 << RATE_MCS_MOD_TYPE_POS
|
||||||
|
RATE_MCS_CHAN_WIDTH_POS = 11
|
||||||
|
RATE_MCS_CHAN_WIDTH_MSK = 0x7 << RATE_MCS_CHAN_WIDTH_POS
|
||||||
|
RATE_MCS_CHAN_WIDTH_20_VAL = 0
|
||||||
|
RATE_MCS_CHAN_WIDTH_20 = RATE_MCS_CHAN_WIDTH_20_VAL << RATE_MCS_CHAN_WIDTH_POS
|
||||||
|
RATE_MCS_CHAN_WIDTH_40_VAL = 1
|
||||||
|
RATE_MCS_CHAN_WIDTH_40 = RATE_MCS_CHAN_WIDTH_40_VAL << RATE_MCS_CHAN_WIDTH_POS
|
||||||
|
RATE_MCS_CHAN_WIDTH_80_VAL = 2
|
||||||
|
RATE_MCS_CHAN_WIDTH_80 = RATE_MCS_CHAN_WIDTH_80_VAL << RATE_MCS_CHAN_WIDTH_POS
|
||||||
|
RATE_MCS_CHAN_WIDTH_160_VAL = 3
|
||||||
|
RATE_MCS_CHAN_WIDTH_160 = RATE_MCS_CHAN_WIDTH_160_VAL << RATE_MCS_CHAN_WIDTH_POS
|
||||||
|
RATE_MCS_CHAN_WIDTH_320_VAL = 4
|
||||||
|
RATE_MCS_CHAN_WIDTH_320 = RATE_MCS_CHAN_WIDTH_320_VAL << RATE_MCS_CHAN_WIDTH_POS
|
||||||
|
RATE_HT_MCS_CODE_MSK = 7
|
||||||
|
RATE_MCS_ANT_A_POS = 14
|
||||||
|
RATE_MCS_ANT_A_MSK = 1 << RATE_MCS_ANT_A_POS
|
||||||
|
RATE_MCS_ANT_B_POS = 15
|
||||||
|
RATE_MCS_ANT_B_MSK = 1 << RATE_MCS_ANT_B_POS
|
||||||
|
RATE_MCS_LDPC_POS = 16
|
||||||
|
RATE_MCS_LDPC_MSK = 1 << RATE_MCS_LDPC_POS
|
||||||
|
RATE_MCS_SS_POS = 16
|
||||||
|
RATE_MCS_SS_MSK = 1 << RATE_MCS_SS_POS
|
||||||
|
RATE_MCS_BEAMF_POS = 16
|
||||||
|
RATE_MCS_BEAMF_MSK = 1 << RATE_MCS_BEAMF_POS
|
||||||
|
|
||||||
|
|
||||||
|
class CSIHeader:
|
||||||
|
def __init__(self, data: bytes):
|
||||||
|
self.csi_size = struct.unpack("I", data[0:4])[0]
|
||||||
|
self.ftm_clock = struct.unpack("I", data[8:12])[0]
|
||||||
|
self.num_rx = data[46]
|
||||||
|
self.num_tx = data[47]
|
||||||
|
self.num_subcarriers = struct.unpack("I", data[52:56])[0]
|
||||||
|
self.rssi1 = struct.unpack("I", data[60:64])[0]
|
||||||
|
self.rssi2 = struct.unpack("I", data[64:68])[0]
|
||||||
|
self.source_mac = struct.unpack("BBBBBB", data[68:74])
|
||||||
|
self.source_mac_string = "%02x:%02x:%02x:%02x:%02x:%02x" % struct.unpack(
|
||||||
|
"BBBBBB", data[68:74]
|
||||||
|
)
|
||||||
|
self.rate_flags = struct.unpack("I", data[92:96])[0]
|
||||||
|
|
||||||
|
rate_format = self.rate_flags & RATE_MCS_MOD_TYPE_MSK
|
||||||
|
if rate_format == RATE_MCS_CCK_MSK:
|
||||||
|
rate_format = "CCK"
|
||||||
|
elif rate_format == RATE_MCS_LEGACY_OFDM_MSK:
|
||||||
|
rate_format = "LEGACY_OFDM"
|
||||||
|
elif rate_format == RATE_MCS_VHT_MSK:
|
||||||
|
rate_format = "VHT"
|
||||||
|
elif rate_format == RATE_MCS_HT_MSK:
|
||||||
|
rate_format = "HT"
|
||||||
|
elif rate_format == RATE_MCS_HE_MSK:
|
||||||
|
rate_format = "HE"
|
||||||
|
elif rate_format == RATE_MCS_EHT_MSK:
|
||||||
|
rate_format = "EHT"
|
||||||
|
else:
|
||||||
|
rate_format = "unknown"
|
||||||
|
self.rate_format = rate_format
|
||||||
|
|
||||||
|
channel_width = self.rate_flags & RATE_MCS_CHAN_WIDTH_MSK
|
||||||
|
if channel_width == RATE_MCS_CHAN_WIDTH_20:
|
||||||
|
channel_width = "20"
|
||||||
|
elif channel_width == RATE_MCS_CHAN_WIDTH_40:
|
||||||
|
channel_width = "40"
|
||||||
|
elif channel_width == RATE_MCS_CHAN_WIDTH_80:
|
||||||
|
channel_width = "80"
|
||||||
|
elif channel_width == RATE_MCS_CHAN_WIDTH_160:
|
||||||
|
channel_width = "160"
|
||||||
|
elif channel_width == RATE_MCS_CHAN_WIDTH_320:
|
||||||
|
channel_width = "320"
|
||||||
|
else:
|
||||||
|
channel_width = "unknown"
|
||||||
|
self.channel_width = channel_width
|
||||||
|
|
||||||
|
self.mcs = self.rate_flags & RATE_HT_MCS_CODE_MSK
|
||||||
|
self.antenna_a = True if self.rate_flags & RATE_MCS_ANT_A_MSK else False
|
||||||
|
self.antenna_b = True if self.rate_flags & RATE_MCS_ANT_B_MSK else False
|
||||||
|
self.ldpc = True if self.rate_flags & RATE_MCS_LDPC_MSK else False
|
||||||
|
self.ss = 2 if self.rate_flags & RATE_MCS_SS_MSK else 1
|
||||||
|
self.beamforming = True if self.rate_flags & RATE_MCS_BEAMF_MSK else False
|
||||||
|
|
||||||
|
|
||||||
|
class CSI:
|
||||||
|
@staticmethod
|
||||||
|
def parseCsiData(data: bytes, header: CSIHeader):
|
||||||
|
csi_matrix = np.zeros(
|
||||||
|
(
|
||||||
|
header.num_subcarriers,
|
||||||
|
header.num_rx,
|
||||||
|
header.num_tx,
|
||||||
|
),
|
||||||
|
dtype=complex,
|
||||||
|
)
|
||||||
|
pos = 0
|
||||||
|
for j in range(header.num_rx):
|
||||||
|
for k in range(header.num_tx):
|
||||||
|
for n in range(header.num_subcarriers):
|
||||||
|
real = struct.unpack("h", data[pos : pos + 2])[0]
|
||||||
|
imag = struct.unpack("h", data[pos + 2 : pos + 4])[0]
|
||||||
|
pos += 4
|
||||||
|
csi_matrix[n, j, k] = complex(real, imag)
|
||||||
|
|
||||||
|
return csi_matrix
|
||||||
|
|
||||||
|
def __init__(self, data: bytes):
|
||||||
|
self.header = CSIHeader(data[:272])
|
||||||
|
self.matrix = self.parseCsiData(
|
||||||
|
data[272 : 272 + self.header.csi_size], self.header
|
||||||
|
)
|
||||||
59
src/main.py
Normal file
59
src/main.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
from csi import CSI
|
||||||
|
from preprocess import Preprocessor
|
||||||
|
import visualise
|
||||||
|
import threading
|
||||||
|
import struct
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s %(levelname)-8s %(message)s",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FeitServer:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
ip: str,
|
||||||
|
frequency: int = 5260,
|
||||||
|
channel_width: int = 80,
|
||||||
|
frame_format: str = "VHT",
|
||||||
|
):
|
||||||
|
self.ip = ip
|
||||||
|
self.port = 8008
|
||||||
|
self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
self.server.connect((self.ip, self.port))
|
||||||
|
self.start_string = (
|
||||||
|
f"feitcsi --frequency {frequency} "
|
||||||
|
f"--channel-width {channel_width} --format {frame_format} "
|
||||||
|
f"--mode measure"
|
||||||
|
)
|
||||||
|
self.server.send(b"stop\n")
|
||||||
|
self.server.send(self.start_string.encode())
|
||||||
|
|
||||||
|
def listen(self, callback):
|
||||||
|
while True:
|
||||||
|
data = self.server.recv(65535)
|
||||||
|
try:
|
||||||
|
csidata = CSI(data)
|
||||||
|
callback(csidata)
|
||||||
|
except struct.error:
|
||||||
|
logging.error("Failed to parse CSI data")
|
||||||
|
|
||||||
|
|
||||||
|
def process_data(data):
|
||||||
|
logging.debug("Got CSI frame")
|
||||||
|
processed = preprocess.preprocess(data)
|
||||||
|
visualise.add_data(processed)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
server = FeitServer("10.0.12.62")
|
||||||
|
preprocess = Preprocessor()
|
||||||
|
|
||||||
|
# Start webapp in background thread
|
||||||
|
webapp = threading.Thread(target=visualise.start)
|
||||||
|
webapp.start()
|
||||||
|
|
||||||
|
server.listen(process_data)
|
||||||
41
src/preprocess.py
Normal file
41
src/preprocess.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import numpy as np
|
||||||
|
from csi import CSI
|
||||||
|
import logging
|
||||||
|
from queue import Queue
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
class Preprocessor:
|
||||||
|
NUM_SHORT_TERM_ENTRIES = 10
|
||||||
|
LONG_TERM_ALPHA = 0.1
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.prev_entries = Queue(maxsize=100)
|
||||||
|
self.short_term_avg = np.zeros((1,), dtype=complex)
|
||||||
|
self.long_term_avg = np.zeros((1,), dtype=complex)
|
||||||
|
|
||||||
|
def preprocess(self, csi: CSI):
|
||||||
|
h = csi.matrix
|
||||||
|
h_hat = np.multiply(h, h.conj() / abs(h.conj()))
|
||||||
|
|
||||||
|
# Assume that all csi matrices will have the same shape
|
||||||
|
if self.short_term_avg.shape != h_hat.shape:
|
||||||
|
self.short_term_avg = np.zeros(h_hat.shape, dtype=complex)
|
||||||
|
self.long_term_avg = np.zeros(h_hat.shape, dtype=complex)
|
||||||
|
self.prev_entries = Queue(maxsize=100)
|
||||||
|
|
||||||
|
while self.prev_entries.full():
|
||||||
|
old_value = self.prev_entries.get()
|
||||||
|
self.short_term_avg -= old_value / self.NUM_SHORT_TERM_ENTRIES
|
||||||
|
self.prev_entries.put(h_hat)
|
||||||
|
self.short_term_avg += h_hat / self.NUM_SHORT_TERM_ENTRIES
|
||||||
|
self.long_term_avg = (
|
||||||
|
self.long_term_avg * (1 - self.LONG_TERM_ALPHA)
|
||||||
|
+ self.short_term_avg * self.LONG_TERM_ALPHA
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove static components
|
||||||
|
current_measurement = self.short_term_avg - self.long_term_avg
|
||||||
|
return current_measurement
|
||||||
53
src/visualise/__init__.py
Normal file
53
src/visualise/__init__.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
from flask import Flask, render_template, request
|
||||||
|
from flask_sock import Sock
|
||||||
|
import numpy as np
|
||||||
|
import numpy.typing as npt
|
||||||
|
import json
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
sock = Sock(app)
|
||||||
|
|
||||||
|
|
||||||
|
data: npt.NDArray[np.complex128] = np.array([], dtype=complex)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/preprocessed")
|
||||||
|
def preprocessed():
|
||||||
|
return render_template("preprocessed.html")
|
||||||
|
|
||||||
|
|
||||||
|
subscribers = {}
|
||||||
|
|
||||||
|
|
||||||
|
@sock.route("/data")
|
||||||
|
def get_data(sock):
|
||||||
|
while True:
|
||||||
|
msg = sock.receive()
|
||||||
|
subscribers[sock] = list(map(int, msg.split()))
|
||||||
|
|
||||||
|
|
||||||
|
def add_data(new_data):
|
||||||
|
global data
|
||||||
|
if data.size == 0:
|
||||||
|
data = np.expand_dims(new_data, axis=0)
|
||||||
|
else:
|
||||||
|
data = np.concat([data, np.expand_dims(new_data, axis=0)], axis=0)
|
||||||
|
|
||||||
|
# Only keep latest 100 entries
|
||||||
|
if data.shape[0] > 100:
|
||||||
|
data = data[-100:]
|
||||||
|
to_remove = []
|
||||||
|
for subscriber in subscribers:
|
||||||
|
try:
|
||||||
|
subcarrier, rx, tx = subscribers[subscriber]
|
||||||
|
subscriber.send(new_data.real[subcarrier, rx, tx])
|
||||||
|
except Exception as e:
|
||||||
|
to_remove.append(subscriber)
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
for subscriber in to_remove:
|
||||||
|
del subscribers[subscriber]
|
||||||
|
|
||||||
|
|
||||||
|
def start():
|
||||||
|
app.run(debug=True, use_reloader=False)
|
||||||
42
src/visualise/static/plot.js
Normal file
42
src/visualise/static/plot.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const plotElement = document.getElementById("plot");
|
||||||
|
|
||||||
|
const layout = {
|
||||||
|
title: "Real-Time Complex Data Visualization",
|
||||||
|
xaxis: { title: "Time (ms)" },
|
||||||
|
yaxis: { title: "Value" },
|
||||||
|
};
|
||||||
|
|
||||||
|
Plotly.newPlot(
|
||||||
|
plotElement,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
x: [],
|
||||||
|
y: [],
|
||||||
|
type: "scatter", // Line chart
|
||||||
|
mode: "lines+markers", // Line + markers
|
||||||
|
},
|
||||||
|
],
|
||||||
|
layout,
|
||||||
|
);
|
||||||
|
|
||||||
|
const socket = new WebSocket("ws://" + location.host + "/data");
|
||||||
|
socket.onopen = function () {
|
||||||
|
console.log("Connected to the server");
|
||||||
|
socket.send("0 0 0");
|
||||||
|
};
|
||||||
|
socket.addEventListener("message", function (msg) {
|
||||||
|
Plotly.extendTraces(
|
||||||
|
plotElement,
|
||||||
|
{
|
||||||
|
x: [[msg.timeStamp]],
|
||||||
|
y: [[msg.data]],
|
||||||
|
},
|
||||||
|
[0],
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function changeSubcarrier() {
|
||||||
|
const subcarrier = document.getElementById("subcarrier").value;
|
||||||
|
socket.send(subcarrier + " 0 0");
|
||||||
|
}
|
||||||
15
src/visualise/templates/preprocessed.html
Normal file
15
src/visualise/templates/preprocessed.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.plot.ly/plotly-2.35.2.min.js" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Preprocessing</h1>
|
||||||
|
<div id="plot"></div>
|
||||||
|
<script src="{{url_for('static', filename='plot.js')}}"></script>
|
||||||
|
<input type="number" id="subcarrier" class="form-control" placeholder="Choose subcarrier" onchange="changeSubcarrier()" value="0">
|
||||||
|
</div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
Loading…
Reference in New Issue
Block a user