From 89d9836b3fb0b88bbf195c292cb5dedb6ad1d9b0 Mon Sep 17 00:00:00 2001 From: ltindall Date: Tue, 3 Sep 2024 19:17:18 -0700 Subject: [PATCH 1/3] Add wifi experiments --- poetry.lock | 227 ++++++++++++++--- pyproject.toml | 3 +- rfml/experiment.py | 6 +- rfml/models.py | 6 +- rfml/train_iq.py | 127 +++++++--- rfml/wifi_experiments.py | 536 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 824 insertions(+), 81 deletions(-) create mode 100644 rfml/wifi_experiments.py diff --git a/poetry.lock b/poetry.lock index 89d4cfc..82920ea 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,16 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "absl-py" +version = "2.1.0" +description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." +optional = false +python-versions = ">=3.7" +files = [ + {file = "absl-py-2.1.0.tar.gz", hash = "sha256:7820790efbb316739cde8b4e19357243fc3608a152024288513dd968d7d959ff"}, + {file = "absl_py-2.1.0-py3-none-any.whl", hash = "sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308"}, +] + [[package]] name = "aiohappyeyeballs" version = "2.4.0" @@ -1187,6 +1198,64 @@ tqdm = "*" [package.extras] test = ["build", "mypy", "pytest", "pytest-xdist", "ruff", "twine", "types-requests", "types-setuptools"] +[[package]] +name = "grpcio" +version = "1.66.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"}, + {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"}, + {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"}, + {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"}, + {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"}, + {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"}, + {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"}, + {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"}, + {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"}, + {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"}, + {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"}, + {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"}, + {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"}, + {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"}, + {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"}, + {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"}, + {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"}, + {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"}, + {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"}, + {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"}, + {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.66.1)"] + [[package]] name = "h11" version = "0.14.0" @@ -2045,6 +2114,21 @@ files = [ {file = "lmdb-1.5.1.tar.gz", hash = "sha256:717c255827d331e02f7242b44051aa06466c90f6d732ecb07b31edfb1e06c67a"}, ] +[[package]] +name = "markdown" +version = "3.7" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, + {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, +] + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -2901,6 +2985,26 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "protobuf" +version = "5.28.0" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-5.28.0-cp310-abi3-win32.whl", hash = "sha256:66c3edeedb774a3508ae70d87b3a19786445fe9a068dd3585e0cefa8a77b83d0"}, + {file = "protobuf-5.28.0-cp310-abi3-win_amd64.whl", hash = "sha256:6d7cc9e60f976cf3e873acb9a40fed04afb5d224608ed5c1a105db4a3f09c5b6"}, + {file = "protobuf-5.28.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:532627e8fdd825cf8767a2d2b94d77e874d5ddb0adefb04b237f7cc296748681"}, + {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:018db9056b9d75eb93d12a9d35120f97a84d9a919bcab11ed56ad2d399d6e8dd"}, + {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:6206afcb2d90181ae8722798dcb56dc76675ab67458ac24c0dd7d75d632ac9bd"}, + {file = "protobuf-5.28.0-cp38-cp38-win32.whl", hash = "sha256:eef7a8a2f4318e2cb2dee8666d26e58eaf437c14788f3a2911d0c3da40405ae8"}, + {file = "protobuf-5.28.0-cp38-cp38-win_amd64.whl", hash = "sha256:d001a73c8bc2bf5b5c1360d59dd7573744e163b3607fa92788b7f3d5fefbd9a5"}, + {file = "protobuf-5.28.0-cp39-cp39-win32.whl", hash = "sha256:dde9fcaa24e7a9654f4baf2a55250b13a5ea701493d904c54069776b99a8216b"}, + {file = "protobuf-5.28.0-cp39-cp39-win_amd64.whl", hash = "sha256:853db610214e77ee817ecf0514e0d1d052dff7f63a0c157aa6eabae98db8a8de"}, + {file = "protobuf-5.28.0-py3-none-any.whl", hash = "sha256:510ed78cd0980f6d3218099e874714cdf0d8a95582e7b059b06cabad855ed0a0"}, + {file = "protobuf-5.28.0.tar.gz", hash = "sha256:dde74af0fa774fa98892209992295adbfb91da3fa98c8f67a88afe8f5a349add"}, +] + [[package]] name = "psutil" version = "6.0.0" @@ -3868,53 +3972,45 @@ tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc ( [[package]] name = "scipy" -version = "1.14.1" +version = "1.13.0" description = "Fundamental algorithms for scientific computing in Python" optional = false -python-versions = ">=3.10" +python-versions = ">=3.9" files = [ - {file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"}, - {file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"}, - {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"}, - {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"}, - {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"}, - {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"}, - {file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"}, - {file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"}, - {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"}, - {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"}, - {file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"}, - {file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"}, - {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"}, - {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"}, - {file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"}, - {file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"}, - {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"}, - {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"}, - {file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"}, - {file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"}, - {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, + {file = "scipy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba419578ab343a4e0a77c0ef82f088238a93eef141b2b8017e46149776dfad4d"}, + {file = "scipy-1.13.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:22789b56a999265431c417d462e5b7f2b487e831ca7bef5edeb56efe4c93f86e"}, + {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f1432ba070e90d42d7fd836462c50bf98bd08bed0aa616c359eed8a04e3922"}, + {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8434f6f3fa49f631fae84afee424e2483289dfc30a47755b4b4e6b07b2633a4"}, + {file = "scipy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dcbb9ea49b0167de4167c40eeee6e167caeef11effb0670b554d10b1e693a8b9"}, + {file = "scipy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1d2f7bb14c178f8b13ebae93f67e42b0a6b0fc50eba1cd8021c9b6e08e8fb1cd"}, + {file = "scipy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fbcf8abaf5aa2dc8d6400566c1a727aed338b5fe880cde64907596a89d576fa"}, + {file = "scipy-1.13.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5e4a756355522eb60fcd61f8372ac2549073c8788f6114449b37e9e8104f15a5"}, + {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5acd8e1dbd8dbe38d0004b1497019b2dbbc3d70691e65d69615f8a7292865d7"}, + {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ff7dad5d24a8045d836671e082a490848e8639cabb3dbdacb29f943a678683d"}, + {file = "scipy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4dca18c3ffee287ddd3bc8f1dabaf45f5305c5afc9f8ab9cbfab855e70b2df5c"}, + {file = "scipy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:a2f471de4d01200718b2b8927f7d76b5d9bde18047ea0fa8bd15c5ba3f26a1d6"}, + {file = "scipy-1.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0de696f589681c2802f9090fff730c218f7c51ff49bf252b6a97ec4a5d19e8b"}, + {file = "scipy-1.13.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:b2a3ff461ec4756b7e8e42e1c681077349a038f0686132d623fa404c0bee2551"}, + {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf9fe63e7a4bf01d3645b13ff2aa6dea023d38993f42aaac81a18b1bda7a82a"}, + {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e7626dfd91cdea5714f343ce1176b6c4745155d234f1033584154f60ef1ff42"}, + {file = "scipy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:109d391d720fcebf2fbe008621952b08e52907cf4c8c7efc7376822151820820"}, + {file = "scipy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8930ae3ea371d6b91c203b1032b9600d69c568e537b7988a3073dfe4d4774f21"}, + {file = "scipy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5407708195cb38d70fd2d6bb04b1b9dd5c92297d86e9f9daae1576bd9e06f602"}, + {file = "scipy-1.13.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ac38c4c92951ac0f729c4c48c9e13eb3675d9986cc0c83943784d7390d540c78"}, + {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c74543c4fbeb67af6ce457f6a6a28e5d3739a87f62412e4a16e46f164f0ae5"}, + {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28e286bf9ac422d6beb559bc61312c348ca9b0f0dae0d7c5afde7f722d6ea13d"}, + {file = "scipy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33fde20efc380bd23a78a4d26d59fc8704e9b5fd9b08841693eb46716ba13d86"}, + {file = "scipy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:45c08bec71d3546d606989ba6e7daa6f0992918171e2a6f7fbedfa7361c2de1e"}, + {file = "scipy-1.13.0.tar.gz", hash = "sha256:58569af537ea29d3f78e5abd18398459f195546bb3be23d16677fb26616cc11e"}, ] [package.dependencies] -numpy = ">=1.23.5,<2.3" +numpy = ">=1.22.4,<2.3" [package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"] -test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] +test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] name = "seaborn" @@ -4068,6 +4164,40 @@ mpmath = ">=1.1.0,<1.4" [package.extras] dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] +[[package]] +name = "tensorboard" +version = "2.17.1" +description = "TensorBoard lets you watch Tensors Flow" +optional = false +python-versions = ">=3.9" +files = [ + {file = "tensorboard-2.17.1-py3-none-any.whl", hash = "sha256:253701a224000eeca01eee6f7e978aea7b408f60b91eb0babdb04e78947b773e"}, +] + +[package.dependencies] +absl-py = ">=0.4" +grpcio = ">=1.48.2" +markdown = ">=2.6.8" +numpy = ">=1.12.0" +packaging = "*" +protobuf = ">=3.19.6,<4.24.0 || >4.24.0" +setuptools = ">=41.0.0" +six = ">1.9" +tensorboard-data-server = ">=0.7.0,<0.8.0" +werkzeug = ">=1.0.1" + +[[package]] +name = "tensorboard-data-server" +version = "0.7.2" +description = "Fast data loading for TensorBoard" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tensorboard_data_server-0.7.2-py3-none-any.whl", hash = "sha256:7e0610d205889588983836ec05dc098e80f97b7e7bbff7e994ebb78f578d0ddb"}, + {file = "tensorboard_data_server-0.7.2-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:9fe5d24221b29625dbc7328b0436ca7fc1c23de4acf4d272f1180856e32f9f60"}, + {file = "tensorboard_data_server-0.7.2-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:ef687163c24185ae9754ed5650eb5bc4d84ff257aabdc33f0cc6f74d8ba54530"}, +] + [[package]] name = "terminado" version = "0.18.1" @@ -4559,6 +4689,23 @@ docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] optional = ["python-socks", "wsaccel"] test = ["websockets"] +[[package]] +name = "werkzeug" +version = "3.0.4" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.4-py3-none-any.whl", hash = "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c"}, + {file = "werkzeug-3.0.4.tar.gz", hash = "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + [[package]] name = "widgetsnbextension" version = "4.0.13" @@ -4788,4 +4935,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = ">=3.10, <3.12" -content-hash = "da0071578e41ba79ff218296b51fcc3e5f2276ad022678eab366a10696f109e4" +content-hash = "f0c4d3fe091bb6849b86f778797a5bb577995cdac6a4a001da5d7975fe1599e9" diff --git a/pyproject.toml b/pyproject.toml index 4305f67..6338acb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ scikit-image = "^0.24.0" matplotlib = "^3.8.3" numpy = "^1.26.4" opencv-python = "^4.8.0.74" -scipy = "1.14.1" +scipy = "1.13" zstandard = "^0.23.0" pyyaml = "^6.0.1" pillow = "^10.2.0" @@ -29,6 +29,7 @@ ultralytics = "^8.2.79" jupyter = "^1.0.0" ipykernel = "^6.29.3" black = "24.8.0" +tensorboard = "^2.17.1" [build-system] requires = ["poetry-core"] diff --git a/rfml/experiment.py b/rfml/experiment.py index fcd563a..f5103d8 100644 --- a/rfml/experiment.py +++ b/rfml/experiment.py @@ -12,10 +12,11 @@ def __init__( train_dir, val_dir=None, test_dir=None, - iq_num_samples=1024, + iq_num_samples=800,#1024, iq_only_start_of_burst=True, iq_epochs=40, - iq_batch_size=180, + iq_batch_size=128, + iq_learning_rate=0.0001, spec_n_fft=1024, spec_time_dim=512, spec_epochs=40, @@ -34,6 +35,7 @@ def __init__( self.iq_only_start_of_burst = iq_only_start_of_burst self.iq_epochs = iq_epochs self.iq_batch_size = iq_batch_size + self.iq_learning_rate = iq_learning_rate self.spec_n_fft = spec_n_fft self.spec_time_dim = spec_time_dim self.spec_n_samples = spec_n_fft * spec_time_dim diff --git a/rfml/models.py b/rfml/models.py index dd23d28..ff57bc4 100644 --- a/rfml/models.py +++ b/rfml/models.py @@ -20,6 +20,7 @@ def __init__( num_classes=None, extra_metrics=True, logs_dir=None, + learning_rate=None, ): super(ExampleNetwork, self).__init__() self.mdl = model @@ -27,7 +28,7 @@ def __init__( self.val_data_loader = val_data_loader # Hyperparameters - self.lr = 0.001 + self.lr = learning_rate if learning_rate else 0.001 self.batch_size = data_loader.batch_size self.num_classes = num_classes @@ -59,7 +60,8 @@ def predict(self, x): return out def configure_optimizers(self): - return optim.Adam(self.parameters(), lr=self.lr) + # return optim.Adam(self.parameters(), lr=self.lr) + return optim.AdamW(self.parameters(), lr=self.lr) def train_dataloader(self): return self.data_loader diff --git a/rfml/train_iq.py b/rfml/train_iq.py index 15b0c8c..239a256 100644 --- a/rfml/train_iq.py +++ b/rfml/train_iq.py @@ -10,6 +10,8 @@ from torchsig.utils.dataset import SignalDataset from torchsig.datasets.sig53 import Sig53 from torch.utils.data import DataLoader +import matplotlib +matplotlib.use('Agg') from matplotlib import pyplot as plt from typing import List from tqdm import tqdm @@ -17,14 +19,16 @@ import numpy as np import os from pathlib import Path -import torchmetrics -from torchsig.models.iq_models.efficientnet.efficientnet import efficientnet_b4 +from torchsig.models.iq_models.efficientnet.efficientnet import efficientnet_b0, efficientnet_b4 # from lightning.pytorch.callbacks import DeviceStatsMonitor from torchsig.utils.cm_plotter import plot_confusion_matrix from pytorch_lightning.callbacks import ModelCheckpoint, DeviceStatsMonitor +from pytorch_lightning.callbacks.early_stopping import EarlyStopping +from pytorch_lightning.loggers import TensorBoardLogger from pytorch_lightning import Trainer +from scipy import signal as sp from sklearn.metrics import classification_report from torchsig.datasets.sig53 import Sig53 @@ -81,6 +85,8 @@ def train_iq( class_list=None, logs_dir=None, output_dir=None, + learning_rate=None, + experiment_name=None, ): print(f"\n\nSTARTING I/Q TRAINING\n\n") if logs_dir is None: @@ -91,10 +97,7 @@ def train_iq( logs_dir = Path(output_dir, logs_dir) logs_dir.mkdir(parents=True, exist_ok=True) - visualize_dataset( - train_dataset_path, num_iq_samples, logs_dir, class_list=class_list - ) - + # # SigMF based Model Training eb_no = False @@ -128,50 +131,83 @@ def train_iq( # ### Load the SigMF File dataset # and generate the class list - # transform = ST.Compose([ - # # ST.RandomPhaseShift(phase_offset=(-1, 1)), - # ST.Normalize(norm=np.inf), - # ST.ComplexTo2D(), - # ]) + # changes, + # 1) augmentations + # 2) pretrained weights + # 3) optimizers + # 4) learning rate + # 5) batch size + + basic_transform = ST.Compose([ + # ST.RandomPhaseShift(phase_offset=(-1, 1)), + # ST.AddNoise(), + # ST.AutomaticGainControl(), + ST.Normalize(norm=2), + # ST.Normalize(norm=np.inf), + ST.ComplexTo2D(), + ]) val_transform = ST.Compose( [ - ST.Normalize(norm=np.inf), + # ST.AutomaticGainControl(), + ST.Normalize(norm=2), + # ST.Normalize(norm=np.inf), ST.ComplexTo2D(), ] ) - train_transform = level2 + visualize_transform = ST.Compose( + [ + # ST.AddNoise(), + ST.AutomaticGainControl() + ] + ) + # train_transform = level2 + train_transform = basic_transform + + + visualize_dataset( + train_dataset_path, num_iq_samples, logs_dir, class_list=class_list, transform=visualize_transform + ) + + train_limit = 0.5 + + ### if val_dataset_path: - train_dataset = SigMFDataset( + original_train_dataset = SigMFDataset( root=train_dataset_path, sample_count=num_iq_samples, transform=train_transform, only_first_samples=only_use_start_of_burst, class_list=class_list, ) - val_dataset = SigMFDataset( + original_val_dataset = SigMFDataset( root=val_dataset_path, sample_count=num_iq_samples, transform=val_transform, only_first_samples=only_use_start_of_burst, class_list=class_list, ) - sampler = train_dataset.get_weighted_sampler() - train_class_counts = train_dataset.get_class_counts() + train_dataset, _ = torch.utils.data.random_split(original_train_dataset, [train_limit, 1-train_limit]) + val_dataset, _ = torch.utils.data.random_split(original_val_dataset, [train_limit, 1-train_limit]) + + sampler = original_train_dataset.get_weighted_sampler(indices=train_dataset.indices) + + train_class_counts = original_train_dataset.get_class_counts(indices=train_dataset.indices) train_class_counts = { - train_dataset.class_list[k]: v for k, v in train_class_counts.items() + original_train_dataset.class_list[k]: v for k, v in train_class_counts.items() } - val_class_counts = val_dataset.get_class_counts() + val_class_counts = original_val_dataset.get_class_counts(indices=val_dataset.indices) val_class_counts = { - val_dataset.class_list[k]: v for k, v in val_class_counts.items() + original_val_dataset.class_list[k]: v for k, v in val_class_counts.items() } - class_list = class_list if class_list else train_dataset.class_list + class_list = class_list if class_list else original_train_dataset.class_list ### else: + print("\n\n\ntrain_limit\n\n\n") dataset = SigMFDataset( root=train_dataset_path, sample_count=num_iq_samples, @@ -179,8 +215,8 @@ def train_iq( only_first_samples=only_use_start_of_burst, class_list=class_list, ) - - train_dataset, val_dataset = torch.utils.data.random_split(dataset, [0.8, 0.2]) + train_dataset, val_dataset, _ = torch.utils.data.random_split(dataset, [train_limit*0.8, train_limit*0.2, 1-train_limit]) + # train_dataset, val_dataset = torch.utils.data.random_split(dataset, [0.8, 0.2]) sampler = dataset.get_weighted_sampler(indices=train_dataset.indices) train_class_counts = dataset.get_class_counts(indices=train_dataset.indices) @@ -194,13 +230,13 @@ def train_iq( class_list = class_list if class_list else dataset.class_list - print(f"{len(train_dataset)=}, {train_class_counts=}") - print(f"{len(val_dataset)=}, {val_class_counts=}") + print(f"\n{len(train_dataset)=}, {train_class_counts=}") + print(f"{len(val_dataset)=}, {val_class_counts=}\n") train_dataloader = DataLoader( dataset=train_dataset, batch_size=batch_size, - num_workers=16, + num_workers=24, sampler=sampler, # shuffle=True, drop_last=True, @@ -208,16 +244,25 @@ def train_iq( val_dataloader = DataLoader( dataset=val_dataset, batch_size=batch_size, - num_workers=16, + num_workers=24, shuffle=False, drop_last=True, ) - model = efficientnet_b4( - pretrained=True, - path="efficientnet_b4.pt", + model = efficientnet_b0( + pretrained=False,#True, + path="efficientnet_b0.pt", num_classes=len(class_list), + drop_path_rate=0.2, + drop_rate=0.2, ) + # model = efficientnet_b4( + # pretrained=True, + # path="efficientnet_b4.pt", + # num_classes=len(class_list), + # drop_path_rate=0.2, + # drop_rate=0.6, + # ) # model.classifier = torch.nn.Linear(in_features=model.classifier.in_features, out_features=len(class_list), bias=True) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") @@ -229,6 +274,7 @@ def train_iq( val_dataloader, num_classes=len(class_list), logs_dir=logs_dir, + learning_rate=learning_rate, ) # Setup checkpoint callbacks @@ -240,13 +286,15 @@ def train_iq( mode="min", ) # Create and fit trainer - + experiment_name = experiment_name if experiment_name else 1 + logger = TensorBoardLogger(save_dir=os.getcwd(), version=experiment_name, name="lightning_logs") trainer = Trainer( max_epochs=epochs, - callbacks=[DeviceStatsMonitor(), checkpoint_callback], + callbacks=[EarlyStopping(monitor="val_loss", mode="min", patience=10, verbose=True), checkpoint_callback], accelerator="gpu", devices=1, - profiler="simple", + logger=logger, + # profiler="simple", ) trainer.fit(example_model) @@ -317,13 +365,14 @@ def train_iq( print(f"Best Model Checkpoint: {checkpoint_callback.best_model_path}") -def visualize_dataset(dataset_path, num_iq_samples, logs_dir, class_list): +def visualize_dataset(dataset_path, num_iq_samples, logs_dir, class_list, transform=None): print("\nVisualizing Dataset\n") dataset = SigMFDataset( root=dataset_path, sample_count=num_iq_samples, allowed_filetypes=[".sigmf-data"], class_list=class_list, + transform=transform, ) dataset_class_counts = {class_name: 0 for class_name in dataset.class_list} for data, label in dataset: @@ -333,7 +382,7 @@ def visualize_dataset(dataset_path, num_iq_samples, logs_dir, class_list): data_loader = DataLoader( dataset=dataset, - batch_size=100, + batch_size=36, shuffle=True, ) @@ -342,9 +391,15 @@ def visualize_dataset(dataset_path, num_iq_samples, logs_dir, class_list): for figure in iter(visualizer): figure.set_size_inches(16, 16) plt.show() - plt.savefig(Path(logs_dir, "dataset.png")) + plt.savefig(Path(logs_dir, "iq_dataset.png")) break + spec_visualizer = SpectrogramVisualizer(data_loader=data_loader, sample_rate=20e6, window=sp.windows.blackmanharris(32), nperseg=32, nfft=32 ) + for figure in iter(spec_visualizer): + figure.set_size_inches(16, 16) + plt.show() + plt.savefig(Path(logs_dir, "spec_dataset.png")) + break def argument_parser(): parser = ArgumentParser() diff --git a/rfml/wifi_experiments.py b/rfml/wifi_experiments.py new file mode 100644 index 0000000..7fb99b3 --- /dev/null +++ b/rfml/wifi_experiments.py @@ -0,0 +1,536 @@ +from pathlib import Path + +from rfml.experiment import * +from rfml.train_iq import * +from rfml.train_spec import * + + +# Ensure that data directories have sigmf-meta files with annotations +# Annotations can be generated using scripts in label_scripts directory or notebooks/Label_WiFi.ipynb and notebooks/Label_DJI.ipynb + +experiments = { + "experiment_test": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": ["data/gamutrf/gamutrf-sd-gr-ieee-wifi/test_offline"], + "iq_epochs": 10, + "spec_epochs": 0, + "notes": "TESTING", + }, + "experiment_nz_wifi_ettus": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "data/gamutrf/gamutrf-nz-anon-wifi", + "data/gamutrf/gamutrf-nz-nonanon-wifi", + ], + "iq_epochs": 40, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, Ettus B200Mini, anarkiwi collect", + }, + "experiment_nz_wifi_ettus_blade": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "data/gamutrf/gamutrf-nz-anon-wifi", + "data/gamutrf/gamutrf-nz-nonanon-wifi", + ], + "val_dir": ["data/gamutrf/gamutrf-wifi-and-anom-bladerf"], + "iq_epochs": 40, + "spec_epochs": 0, + "spec_skip_export": True, # USE WITH CAUTION (but speeds up large directories significantly): skip after first run if using separate train/val directories + "notes": "Wi-Fi vs anomalous Wi-Fi, train on Ettus B200Mini RX/TX, validate on BladeRF TX & Ettus B200Mini RX, anarkiwi collect", + }, + "experiment_nz_wifi_ettus_ap": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "data/gamutrf/gamutrf-nz-anon-wifi", + "data/gamutrf/gamutrf-nz-nonanon-wifi", + ], + "val_dir": ["data/gamutrf/gamutrf-nz-wifi"], + "iq_epochs": 40, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train on Ettus B200Mini RX/TX, validate on real Wi-Fi AP TX & Ettus B200Mini RX, anarkiwi collect", + }, + "experiment_emair": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "data/gamutrf/wifi-data-03082024/20msps/normal/train", + "data/gamutrf/wifi-data-03082024/20msps/normal/test", + "data/gamutrf/wifi-data-03082024/20msps/normal/inference", + "data/gamutrf/wifi-data-03082024/20msps/mod/train", + "data/gamutrf/wifi-data-03082024/20msps/mod/test", + "data/gamutrf/wifi-data-03082024/20msps/mod/inference", + ], + "val_dir": [ + "data/gamutrf/wifi-data-03082024/20msps/normal/validate", + "data/gamutrf/wifi-data-03082024/20msps/mod/validate", + ], + "iq_num_samples": 16 * 25, + "iq_epochs": 10, + "iq_batch_size": 16, + "spec_batch_size": 32, + "spec_epochs": 40, + "spec_n_fft": 16, + "spec_time_dim": 25, + "notes": "Ettus B200Mini RX, emair collect", + }, + "experiment_nz_wifi_blade": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": ["data/gamutrf/gamutrf-wifi-and-anom-bladerf"], + "iq_epochs": 40, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, BladeRF, anarkiwi collect", + }, + "experiment_nz_wifi_blade_ettus": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": ["data/gamutrf/gamutrf-wifi-and-anom-bladerf"], + "val_dir": [ + "data/gamutrf/gamutrf-nz-anon-wifi", + "data/gamutrf/gamutrf-nz-nonanon-wifi", + ], + "iq_epochs": 40, + "spec_epochs": 0, + "spec_skip_export": True, # USE WITH CAUTION (but speeds up large directories significantly): skip after first run if using separate train/val directories + "notes": "Wi-Fi vs anomalous Wi-Fi, validate on BladeRF TX & Ettus B200Mini RX, train on Ettus B200Mini RX/TX, anarkiwi collect", + }, + + "experiment_train_blade_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + ], + "iq_epochs": 40, + "spec_epochs": 0, + "spec_batch_size": -1, + "spec_skip_export": True, # USE WITH CAUTION (but speeds up large directories significantly): skip after first run if using separate train/val directories + "notes": "Wi-Fi vs anomalous Wi-Fi, train on BladeRF TX & Ettus B200Mini RX, validate on Ettus B200Mini RX/TX, anarkiwi collect 2", + }, + "experiment_train_ettus_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "iq_epochs": 40, + "spec_epochs": 0, + "spec_batch_size": -1, + "spec_skip_export": True, # USE WITH CAUTION (but speeds up large directories significantly): skip after first run if using separate train/val directories + "notes": "Wi-Fi vs anomalous Wi-Fi, validate on BladeRF TX & Ettus B200Mini RX, train on Ettus B200Mini RX/TX, anarkiwi collect 2", + }, + "experiment_train_blade_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": ["/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf"], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "iq_epochs": 40, + "spec_epochs": 0, + "spec_batch_size": -1, + "spec_skip_export": True, # USE WITH CAUTION (but speeds up large directories significantly): skip after first run if using separate train/val directories + "notes": "Wi-Fi vs anomalous Wi-Fi, train on BladeRF TX & Ettus B200Mini RX, validate on Ettus B200Mini RX/TX, anarkiwi collect 1", + }, + "experiment_train_ettus_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "val_dir": ["/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf"], + "iq_epochs": 40, + "spec_epochs": 0, + "spec_batch_size": -1, + "spec_skip_export": True, # USE WITH CAUTION (but speeds up large directories significantly): skip after first run if using separate train/val directories + "notes": "Wi-Fi vs anomalous Wi-Fi, validate on BladeRF TX & Ettus B200Mini RX, train on Ettus B200Mini RX/TX, anarkiwi collect 1", + }, + "experiment_ettus_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train and validate on Ettus B200Mini RX/TX, anarkiwi collect 1", + }, + "experiment_blade_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": ["/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf"], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train and validate on BladeRF TX & Ettus B200Mini RX, anarkiwi collect 1", + }, + "experiment_blade_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train and validate on BladeRF TX & Ettus B200Mini RX, anarkiwi collect 2", + }, + "experiment_ettus_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + ], + "iq_epochs": 200, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train and validate on Ettus B200Mini RX/TX, anarkiwi collect 2", + }, + "experiment_ettus_1_to_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_2_to_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 2, validate Ettus 1", + }, + "experiment_blade_1_to_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": ["/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf"], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Blade 1, validate Blade 2", + }, + "experiment_blade_2_to_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "val_dir": ["/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf"], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Blade 2, validate Blade 1", + }, + "experiment_ettus_1_blade_1_to_blade_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf" + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_1_blade_2_to_blade_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "val_dir": ["/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf"], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_1_blade_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf" + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_1_to_blade_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf", + ], + "iq_epochs": 150, + "iq_learning_rate": 0.0000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_blade_1_to_ettus_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "iq_epochs": 150, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_1_blade_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_2_blade_1_blade_2_to_ettus_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.0000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_1_blade_1_blade_2_to_ettus_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.0000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_1_ettus_2_blade_1_to_blade_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf", + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_1_ettus_2_blade_2_to_blade_1": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.0000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_1_to_blade_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-anon-wifi", + "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "iq_epochs": 100, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_ettus_2_to_blade_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "iq_epochs": 150, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + "experiment_blade_2_to_ettus_2": { + "class_list": ["wifi", "anom_wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", + "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", + ], + "iq_epochs": 150, + "iq_learning_rate": 0.000001, + "spec_epochs": 0, + "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", + }, + # ettus1, blade1, blade2 + +} + + +if __name__ == "__main__": + + experiments_to_run = [ + # "experiment_test", + # "experiment_nz_wifi_ettus", + # "experiment_nz_wifi_ettus_blade", + # "experiment_nz_wifi_ettus_ap", + # "experiment_emair", + # "experiment_nz_wifi_blade", + # "experiment_nz_wifi_blade_ettus", + # "experiment_train_blade_1", + # "experiment_train_ettus_1", + # "experiment_train_blade_2", + # "experiment_train_ettus_2", + # "experiment_ettus_1", + # "experiment_blade_1", + # "experiment_ettus_2", + # "experiment_blade_2", + # "experiment_ettus_1_to_2", + # "experiment_ettus_2_to_1", + # "experiment_blade_1_to_2", + # "experiment_blade_2_to_1", + # "experiment_ettus_1_blade_1_to_blade_2", + # "experiment_ettus_1_blade_2_to_blade_1", + # "experiment_ettus_1_blade_1", + # "experiment_ettus_1_blade_2", + # "experiment_ettus_2_blade_1_blade_2_to_ettus_1", + # "experiment_ettus_1_blade_1_blade_2_to_ettus_2", + "experiment_ettus_1_ettus_2_blade_1_to_blade_2", + # "experiment_ettus_1_ettus_2_blade_2_to_blade_1", + # "experiment_ettus_1_to_blade_2", + "experiment_blade_2_to_ettus_2", + "experiment_ettus_2_to_blade_2", + "experiment_ettus_1_to_blade_1", + "experiment_blade_1_to_ettus_1", + + ] + + for experiment_name in experiments_to_run: + print(f"Running {experiment_name}") + try: + exp = Experiment( + experiment_name=experiment_name, **experiments[experiment_name] + ) + + logs_timestamp = datetime.now().strftime("%m_%d_%Y_%H_%M_%S") + + if exp.iq_epochs > 0: + train_iq( + train_dataset_path=exp.train_dir, + val_dataset_path=exp.val_dir, + num_iq_samples=exp.iq_num_samples, + only_use_start_of_burst=exp.iq_only_start_of_burst, + epochs=exp.iq_epochs, + batch_size=exp.iq_batch_size, + class_list=exp.class_list, + output_dir=Path("experiment_logs", exp.experiment_name), + logs_dir=Path("iq_logs", logs_timestamp), + learning_rate=exp.iq_learning_rate, + experiment_name=exp.experiment_name, + ) + else: + print("Skipping IQ training") + + if exp.spec_epochs > 0: + train_spec( + train_dataset_path=exp.train_dir, + val_dataset_path=exp.val_dir, + n_fft=exp.spec_n_fft, + time_dim=exp.spec_time_dim, + epochs=exp.spec_epochs, + batch_size=exp.spec_batch_size, + class_list=exp.class_list, + yolo_augment=exp.spec_yolo_augment, + skip_export=exp.spec_skip_export, + force_yolo_label_larger=exp.spec_force_yolo_label_larger, + output_dir=Path("experiment_logs", exp.experiment_name), + logs_dir=Path("spec_logs", logs_timestamp), + ) + else: + print("Skipping spectrogram training") + + except Exception as error: + print(f"Error: {error}") From 063d842e70625374ddaf2e0a32eca1fddec89527 Mon Sep 17 00:00:00 2001 From: ltindall Date: Sun, 8 Sep 2024 17:51:48 -0700 Subject: [PATCH 2/3] Start label refactor --- label_scripts/label_gamutrf_nz_wifi.py | 34 + label_scripts/label_mavic3_lab.py | 68 ++ label_scripts/label_mini2_fieldday.py | 70 ++ label_scripts/label_mini2_lab.py | 67 ++ poetry.lock | 2 +- pyproject.toml | 1 + rfml/annotation_utils.py | 1302 ++++++++++++++++++------ rfml/data.py | 32 +- rfml/utils.py | 66 ++ 9 files changed, 1342 insertions(+), 300 deletions(-) create mode 100644 label_scripts/label_gamutrf_nz_wifi.py create mode 100644 label_scripts/label_mavic3_lab.py create mode 100644 label_scripts/label_mini2_fieldday.py create mode 100644 label_scripts/label_mini2_lab.py create mode 100644 rfml/utils.py diff --git a/label_scripts/label_gamutrf_nz_wifi.py b/label_scripts/label_gamutrf_nz_wifi.py new file mode 100644 index 0000000..b424e81 --- /dev/null +++ b/label_scripts/label_gamutrf_nz_wifi.py @@ -0,0 +1,34 @@ +import glob + +from pathlib import Path +from tqdm import tqdm + +import rfml.annotation_utils as annotation_utils +import rfml.data as data_class + +data_globs = [ + "/data/s3_gamutrf/gamutrf-nz-wifi/gamutrf_ax_gain10_2430000000Hz_20480000sps.raw.zst" +] + + +for file_glob in data_globs: + for f in tqdm(glob.glob(str(Path(file_glob)))): + + data_obj = data_class.Data(f) + annotation_utils.reset_annotations(data_obj) + annotation_utils.annotate( + data_obj, + avg_window_len=256, + avg_duration=0.25, + debug=False, + verbose=False, + bandwidth_estimation=0.99, + overwrite=False, + labels = { + "wifi": { + "bandwidth_limits": (10e6, None), + "annotation_length": (10000, None), + "annotation_seconds": (0.001, None), + } + } + ) \ No newline at end of file diff --git a/label_scripts/label_mavic3_lab.py b/label_scripts/label_mavic3_lab.py new file mode 100644 index 0000000..f1594c8 --- /dev/null +++ b/label_scripts/label_mavic3_lab.py @@ -0,0 +1,68 @@ +import glob + +from pathlib import Path +from tqdm import tqdm + +import rfml.annotation_utils as annotation_utils +import rfml.data as data_class + + +data_globs = [ + "/data/s3_gamutrf/gamutrf-arl/01_30_23/mavic3/*.zst", + # "/data/s3_gamutrf/gamutrf-arl/01_30_23/mavic3/gamutrf_recording_ettus__gain40_1675089393_5735500000Hz_20480000sps.s16.sigmf-meta" +] + + +for file_glob in data_globs: + for f in tqdm(glob.glob(str(Path(file_glob)))): + + data_obj = data_class.Data(f) + annotation_utils.reset_annotations(data_obj) + annotation_utils.annotate( + data_obj, + # label="mini2_video", + avg_window_len=256, + avg_duration=0.25, + debug=False, + spectral_energy_threshold=True, + # force_threshold_db=-60, + overwrite=False, + # min_bandwidth=16e6, + # min_annotation_length=10000, + # max_annotations=500, + # dc_block=True, + # time_start_stop=(1,3.5), + # necessary={ + # "annotation_seconds": (0.001, -1) + # }, + labels = { + "mavic3_video": { + "bandwidth_limits": (16e6, None), + "annotation_length": (10000, None), + "annotation_seconds": (0.001, None), + "set_bandwidth": (-9e6, 9e6) + }, + "mavic3_telem": { + "bandwidth_limits": (None, 16e6), + "annotation_length": (10000, None), + "annotation_seconds": (None, 0.001), + } + } + ) + # annotation_utils.annotate( + # data_obj, + # label="mini2_telem", + # avg_window_len=256, + # avg_duration=0.25, + # debug=False, + # spectral_energy_threshold=True, + # # force_threshold_db=-58, + # overwrite=False, + # max_bandwidth=16e6, + # min_annotation_length=10000, + # # max_annotations=500, + # # dc_block=True, + # necessary={ + # "annotation_seconds": (0, 0.001) + # }, + # ) diff --git a/label_scripts/label_mini2_fieldday.py b/label_scripts/label_mini2_fieldday.py new file mode 100644 index 0000000..b071f53 --- /dev/null +++ b/label_scripts/label_mini2_fieldday.py @@ -0,0 +1,70 @@ +import glob + +from pathlib import Path +from tqdm import tqdm + +import rfml.annotation_utils as annotation_utils +import rfml.data as data_class + + +data_globs = [ + # "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings/dji-mini2-0to100m-0deg-5735mhz-lp-50-gain_20p5Msps_craft_flying-1.sigmf-meta" + # "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings/dji-mini2-0to100m-0deg-5735mhz-lp-60-gain_20Msps_craft_flying-1.sigmf-meta" + "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings/*.sigmf-meta", + # "/data/s3_gamutrf/gamutrf-birdseye-field-days/leesburg_field_day_2022_06_15/iq_recordings/*.sigmf-meta", +] + + +for file_glob in data_globs: + for f in tqdm(glob.glob(str(Path(file_glob)))): + + data_obj = data_class.Data(f) + annotation_utils.reset_annotations(data_obj) + annotation_utils.annotate( + data_obj, + # label="mini2_video", + avg_window_len=256, + avg_duration=0.25, + debug=False, + bandwidth_estimation=True, + # force_threshold_db=-60, + overwrite=False, + # min_bandwidth=16e6, + # min_annotation_length=10000, + # max_annotations=500, + # dc_block=True, + # time_start_stop=(1,3.5), + # necessary={ + # "annotation_seconds": (0.001, -1) + # }, + labels = { + "mini2_video": { + "bandwidth_limits": (16e6, None), + "annotation_length": (10000, None), + "annotation_seconds": (0.001, None), + "set_bandwidth": (-8.5e6, 9.5e6) + }, + "mini2_telem": { + "bandwidth_limits": (None, 16e6), + "annotation_length": (10000, None), + "annotation_seconds": (None, 0.001), + } + } + ) + # annotation_utils.annotate( + # data_obj, + # label="mini2_telem", + # avg_window_len=256, + # avg_duration=0.25, + # debug=False, + # spectral_energy_threshold=True, + # # force_threshold_db=-58, + # overwrite=False, + # max_bandwidth=16e6, + # min_annotation_length=10000, + # # max_annotations=500, + # # dc_block=True, + # necessary={ + # "annotation_seconds": (0, 0.001) + # }, + # ) diff --git a/label_scripts/label_mini2_lab.py b/label_scripts/label_mini2_lab.py new file mode 100644 index 0000000..b05ceb3 --- /dev/null +++ b/label_scripts/label_mini2_lab.py @@ -0,0 +1,67 @@ +import glob + +from pathlib import Path +from tqdm import tqdm + +import rfml.annotation_utils as annotation_utils +import rfml.data as data_class + + +data_globs = [ + "/data/s3_gamutrf/gamutrf-arl/01_30_23/mini2/*.zst", +] + + +for file_glob in data_globs: + for f in tqdm(glob.glob(str(Path(file_glob)))): + + data_obj = data_class.Data(f) + annotation_utils.reset_annotations(data_obj) + annotation_utils.annotate( + data_obj, + # label="mini2_video", + avg_window_len=256, + avg_duration=0.25, + debug=False, + spectral_energy_threshold=True, + # force_threshold_db=-60, + overwrite=False, + # min_bandwidth=16e6, + # min_annotation_length=10000, + # max_annotations=500, + # dc_block=True, + # time_start_stop=(1,3.5), + # necessary={ + # "annotation_seconds": (0.001, -1) + # }, + labels = { + "mini2_video": { + "bandwidth_limits": (16e6, None), + "annotation_length": (10000, None), + "annotation_seconds": (0.001, None), + "set_bandwidth": (-9e6, 9e6) + }, + "mini2_telem": { + "bandwidth_limits": (None, 16e6), + "annotation_length": (10000, None), + "annotation_seconds": (None, 0.001), + } + } + ) + # annotation_utils.annotate( + # data_obj, + # label="mini2_telem", + # avg_window_len=256, + # avg_duration=0.25, + # debug=False, + # spectral_energy_threshold=True, + # # force_threshold_db=-58, + # overwrite=False, + # max_bandwidth=16e6, + # min_annotation_length=10000, + # # max_annotations=500, + # # dc_block=True, + # necessary={ + # "annotation_seconds": (0, 0.001) + # }, + # ) diff --git a/poetry.lock b/poetry.lock index 11dc648..8724f1d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4895,4 +4895,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = ">=3.10, <3.12" -content-hash = "f0c4d3fe091bb6849b86f778797a5bb577995cdac6a4a001da5d7975fe1599e9" +content-hash = "7236b29077d47b3a18a0a7ee9eae541a9f1a4f491b46bf1cb69475b3ceb7ad60" diff --git a/pyproject.toml b/pyproject.toml index 6338acb..f271fc3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ jupyter = "^1.0.0" ipykernel = "^6.29.3" black = "24.8.0" tensorboard = "^2.17.1" +seaborn = "^0.13.2" [build-system] requires = ["poetry-core"] diff --git a/rfml/annotation_utils.py b/rfml/annotation_utils.py index ab40d99..665bfcc 100644 --- a/rfml/annotation_utils.py +++ b/rfml/annotation_utils.py @@ -1,9 +1,12 @@ # Tools for annotating RF data - +import seaborn as sns +import time from collections.abc import Iterable import cupy from cupyx.scipy.signal import spectrogram as cupyx_spectrogram from cupyx.scipy.ndimage import gaussian_filter as cupyx_gaussian_filter +import cupyx.scipy.signal +import scipy.signal from rfml.spectrogram import * @@ -13,6 +16,8 @@ from pathlib import Path from tqdm import tqdm +from sklearn import mixture +import warnings def moving_average(complex_iq, avg_window_len): @@ -20,6 +25,10 @@ def moving_average(complex_iq, avg_window_len): np.convolve(np.abs(complex_iq) ** 2, np.ones(avg_window_len), "valid") / avg_window_len ) + # return ( + # np.abs(np.convolve(complex_iq, np.ones(avg_window_len), "valid") + # / avg_window_len) ** 2 + # ) def power_squelch(iq_samples, threshold, avg_window_len): @@ -42,128 +51,254 @@ def reset_annotations(data_obj): print(f"Resetting annotations in {data_obj.sigmf_meta_filename}") -def annotate_power_squelch( +# def annotate_power_squelch( +# data_obj, +# threshold, +# avg_window_len, +# label=None, +# skip_validate=False, +# spectral_energy_threshold=False, +# dry_run=False, +# min_annotation_length=400, +# min_bandwidth=None, +# max_bandwidth=None, +# overwrite=True, +# max_annotations=None, +# dc_block=False, +# verbose=False, +# n_seek_samples=None, +# n_samples=None, +# set_bandwidth=None, +# ): +# # get I/Q samples +# iq_samples = data_obj.get_samples( +# n_seek_samples=n_seek_samples, n_samples=n_samples +# ) + +# # apply power squelch to I/Q samples using dB threshold +# idx = power_squelch(iq_samples, threshold=threshold, avg_window_len=avg_window_len) + +# # if overwrite, delete existing annotations +# if overwrite: +# data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY] = [] + +# if isinstance(spectral_energy_threshold, bool) and spectral_energy_threshold: +# spectral_energy_threshold = 0.94 + +# for start, stop in tqdm(idx[:max_annotations]): +# start, stop = int(start), int(stop) + +# # skip if proposed annotation length is less than min_annotation_length +# if min_annotation_length and (stop - start < min_annotation_length): +# continue + +# freq_edges = get_bandwidth(data_obj, iq_samples, start, stop, set_bandwidth, spectral_energy_threshold, dc_block, verbose, min_bandwidth, max_bandwidth, label) + +# if freq_edges is None: +# continue + +# freq_lower_edge, freq_upper_edge = freq_edges + +# metadata = { +# "core:freq_lower_edge": freq_lower_edge, +# "core:freq_upper_edge": freq_upper_edge, +# } +# if label: +# metadata["core:label"] = label + +# data_obj.sigmf_obj.add_annotation( +# n_seek_samples + start, length=stop - start, metadata=metadata +# ) + +# if not dry_run: +# data_obj.sigmf_obj.tofile( +# data_obj.sigmf_meta_filename, skip_validate=skip_validate +# ) +# print( +# f"Writing {len(data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY])} annotations to {data_obj.sigmf_meta_filename}" +# ) + + +# MAD estimator +def median_absolute_deviation(series): + mad = 1.4826 * np.median(np.abs(series - np.median(series))) + # sci_mad = scipy.stats.median_abs_deviation(series, scale="normal") + return np.median(series) + 6 * mad + + +def debug_plot( + avg_pwr_db, + mad, + threshold_db, + avg_duration, data_obj, - threshold, - avg_window_len, - label=None, - skip_validate=False, - spectral_energy_threshold=False, - dry_run=False, - min_annotation_length=400, - min_bandwidth=None, - max_bandwidth=None, - overwrite=True, - max_annotations=None, - dc_block=False, - verbose=False, - n_seek_samples=None, - n_samples=None, - set_bandwidth=None, + guess_threshold_old, + force_threshold_db, + n_components=None, ): - iq_samples = data_obj.get_samples( - n_seek_samples=n_seek_samples, n_samples=n_samples + n_components = n_components if n_components else 3 + + print(f"{np.max(avg_pwr_db)=}") + print(f"{np.mean(avg_pwr_db)=}") + print(f"median absolute deviation threshold = {mad}") + print(f"using threshold = {threshold_db}") + # print(f"{len(avg_pwr_db)=}") + # print(f"{len(avg_pwr_db)=}") + # print(f'{int(avg_duration * data_obj.metadata["global"]["core:sample_rate"])=}') + + #### + # Figure 1 + ### + plt.figure() + db_plot = avg_pwr_db[ + int(0 * data_obj.metadata["global"]["core:sample_rate"]) : int( + avg_duration * data_obj.metadata["global"]["core:sample_rate"] + ) + ] + # db_plot = avg_pwr_db + plt.plot( + np.arange(len(db_plot)) / data_obj.metadata["global"]["core:sample_rate"], + db_plot, ) - idx = power_squelch(iq_samples, threshold=threshold, avg_window_len=avg_window_len) - - if overwrite: - data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY] = [] - for start, stop in tqdm(idx[:max_annotations]): - # print(f"{start=}, {stop=} {max_annotations=}") - start, stop = int(start), int(stop) - if min_annotation_length and (stop - start < min_annotation_length): - continue - - if isinstance(spectral_energy_threshold, bool) and spectral_energy_threshold: - spectral_energy_threshold = 0.94 - - if set_bandwidth: - freq_lower_edge = ( - data_obj.metadata["captures"][0]["core:frequency"] - set_bandwidth / 2 - ) - freq_upper_edge = ( - data_obj.metadata["captures"][0]["core:frequency"] + set_bandwidth / 2 - ) - - elif isinstance(spectral_energy_threshold, float): - freq_lower_edge, freq_upper_edge = get_occupied_bandwidth( - iq_samples[start:stop], - data_obj.metadata["global"]["core:sample_rate"], - data_obj.metadata["captures"][0]["core:frequency"], - spectral_energy_threshold=spectral_energy_threshold, - dc_block=dc_block, - verbose=verbose, - ) - bandwidth = freq_upper_edge - freq_lower_edge - if min_bandwidth and bandwidth < min_bandwidth: - if verbose: - print( - f"min_bandwidth - Skipping, {label}, {start=}, {stop=}, {bandwidth=}, {freq_upper_edge=}, {freq_lower_edge=}" - ) - # print(f"Skipping, {label}, {start=}, {stop=}, {bandwidth=}, {freq_upper_edge=}, {freq_lower_edge=}") - continue - if max_bandwidth and bandwidth > max_bandwidth: - if verbose: - print( - f"max_bandwidth - Skipping, {label}, {start=}, {stop=}, {bandwidth=}, {freq_upper_edge=}, {freq_lower_edge=}" - ) - continue - - else: - freq_lower_edge = ( - data_obj.metadata["captures"][0]["core:frequency"] - - data_obj.metadata["global"]["core:sample_rate"] / 2 - ) - freq_upper_edge = ( - data_obj.metadata["captures"][0]["core:frequency"] - + data_obj.metadata["global"]["core:sample_rate"] / 2 - ) - metadata = { - "core:freq_lower_edge": freq_lower_edge, - "core:freq_upper_edge": freq_upper_edge, - } - if label: - metadata["core:label"] = label - - data_obj.sigmf_obj.add_annotation( - n_seek_samples + start, length=stop - start, metadata=metadata + plt.axhline(y=guess_threshold_old, color="g", linestyle="-", label="old threshold") + plt.axhline(y=np.mean(avg_pwr_db), color="r", linestyle="-", label="average") + plt.axhline( + y=mad, + color="b", + linestyle="-", + label="median absolute deviation threshold", + ) + if force_threshold_db: + plt.axhline( + y=force_threshold_db, + color="yellow", + linestyle="-", + label="force threshold db", ) + plt.legend(loc="center left", bbox_to_anchor=(1, 0.5)) + plt.ylabel("dB") + plt.xlabel("time (seconds)") + plt.title("Signal Power") + plt.show() - # print(f"{data_obj.sigmf_obj=}") - - if not dry_run: - data_obj.sigmf_obj.tofile( - data_obj.sigmf_meta_filename, skip_validate=skip_validate + ### + # Figure 2 + ### + db_plot = avg_pwr_db[ + int(0 * data_obj.metadata["global"]["core:sample_rate"]) : int( + avg_duration * data_obj.metadata["global"]["core:sample_rate"] ) - print( - f"Writing {len(data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY])} annotations to {data_obj.sigmf_meta_filename}" + ] + start_time = time.time() + plt.figure() + sns.histplot(db_plot, kde=True) + plt.xlabel("dB") + plt.title(f"Signal Power Histogram & Density ({avg_duration} seconds)") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + # fit a Gaussian Mixture Model with two components + start_time = time.time() + clf = mixture.GaussianMixture(n_components=n_components) + clf.fit(db_plot.reshape(-1, 1)) + print(f"Gaussian mixture model time = {time.time()-start_time}") + print(f"{clf.weights_=}") + print(f"{clf.means_=}") + print(f"{clf.covariances_=}") + print(f"{clf.converged_=}") + + ### + # Figure 3 + ### + db_plot = avg_pwr_db + start_time = time.time() + plt.figure() + sns.histplot(db_plot, kde=True) + plt.xlabel("dB") + plt.title(f"Signal Power Histogram & Density") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + # fit a Gaussian Mixture Model with two components + start_time = time.time() + clf = mixture.GaussianMixture(n_components=n_components) + clf.fit(db_plot.reshape(-1, 1)) + print(f"Gaussian mixture model time = {time.time()-start_time}") + print(f"{clf.weights_=}") + print(f"{clf.means_=}") + print(f"{clf.covariances_=}") + print(f"{clf.converged_=}") + + ### + # Figure 4 + ### + plt.figure() + db_plot = avg_pwr_db[ + int(0 * data_obj.metadata["global"]["core:sample_rate"]) : int( + avg_duration * data_obj.metadata["global"]["core:sample_rate"] ) + ] + # db_plot = avg_pwr_db + plt.plot( + np.arange(len(db_plot)) / data_obj.metadata["global"]["core:sample_rate"], + db_plot, + ) + plt.axhline(y=guess_threshold_old, color="g", linestyle="-", label="old threshold") + plt.axhline(y=np.mean(avg_pwr_db), color="r", linestyle="-", label="average") + plt.axhline( + y=mad, + color="b", + linestyle="-", + label="median absolute deviation threshold", + ) + plt.axhline( + y=np.min(clf.means_) + + 3 * np.sqrt(clf.covariances_[np.argmin(clf.means_)].squeeze()), + color="yellow", + linestyle="-", + label="gaussian mixture model estimate", + ) + if force_threshold_db: + plt.axhline( + y=force_threshold_db, + color="yellow", + linestyle="-", + label="force threshold db", + ) + plt.legend(loc="center left", bbox_to_anchor=(1, 0.5)) + plt.ylabel("dB") + plt.xlabel("time (seconds)") + plt.title("Signal Power") + plt.show() def annotate( - filename, - label, + data_obj, + # label, avg_window_len, avg_duration=-1, debug=False, dry_run=False, - min_annotation_length=400, - spectral_energy_threshold=True, + # min_annotation_length=400, + bandwidth_estimation=True, + # spectral_energy_threshold=True, force_threshold_db=None, overwrite=True, - min_bandwidth=None, - max_bandwidth=None, + # min_bandwidth=None, + # max_bandwidth=None, max_annotations=None, dc_block=None, verbose=False, time_start_stop=None, set_bandwidth=None, + labels=None, ): - data_obj = data_class.Data(filename) + time_chunk = 1 # only process n seconds of I/Q samples at a time sample_rate = data_obj.metadata["global"]["core:sample_rate"] + # set n_seek_samples (skip n samples at start) and n_samples (process n samples) if isinstance(time_start_stop, int) and time_start_stop > 0: n_seek_samples = int(sample_rate * time_start_stop) n_samples = -1 @@ -177,121 +312,379 @@ def annotate( n_seek_samples = 0 n_samples = -1 - if force_threshold_db: - threshold_db = force_threshold_db + if n_samples > -1: + sample_idxs = np.arange( + n_seek_samples, n_seek_samples + n_samples, sample_rate * time_chunk + ) else: - # use a seconds worth of data to calculate threshold - if avg_duration > -1: - iq_samples = data_obj.get_samples( - n_seek_samples=n_seek_samples, n_samples=int(sample_rate * avg_duration) + sample_idxs = np.arange( + n_seek_samples, data_obj.sigmf_obj.sample_count, sample_rate * time_chunk + ) + + # if overwrite, delete existing annotations + if overwrite: + data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY] = [] + + # if isinstance(spectral_energy_threshold, bool) and spectral_energy_threshold: + # spectral_energy_threshold = 0.94 + + n_annotations = 0 + # i = 0 + for sample_idx in tqdm(sample_idxs): + # i += 1 + # if i >= 2: + # break + + if n_samples > -1: + get_n_samples = min( + sample_rate * time_chunk, n_samples - (sample_idx - n_seek_samples) ) - if iq_samples is None: - iq_samples = data_obj.get_samples( - n_seek_samples=n_seek_samples, n_samples=n_samples - ) else: - iq_samples = data_obj.get_samples( - n_seek_samples=n_seek_samples, n_samples=n_samples - ) + get_n_samples = sample_rate * time_chunk - avg_pwr = moving_average(iq_samples, avg_window_len) - avg_pwr_db = 10 * np.log10(avg_pwr) - del avg_pwr - del iq_samples - - # current threshold in custom_handler - guess_threshold_old = (np.max(avg_pwr_db) + np.mean(avg_pwr_db)) / 2 - - # MAD estimator - def median_absolute_deviation(series): - mad = 1.4826 * np.median(np.abs(series - np.median(series))) - # sci_mad = scipy.stats.median_abs_deviation(series, scale="normal") - return np.median(series) + 6 * mad - - mad = median_absolute_deviation(avg_pwr_db) - - threshold_db = mad - - if debug: - print(f"{np.max(avg_pwr_db)=}") - print(f"{np.mean(avg_pwr_db)=}") - print(f"median absolute deviation threshold = {mad}") - print(f"using threshold = {threshold_db}") - # print(f"{len(avg_pwr_db)=}") - - plt.figure() - db_plot = avg_pwr_db[int(0 * 20.48e6) : int(avg_duration * 20.48e6)] - plt.plot( - np.arange(len(db_plot)) - / data_obj.metadata["global"]["core:sample_rate"], - db_plot, - ) - plt.axhline( - y=guess_threshold_old, color="g", linestyle="-", label="old threshold" - ) - plt.axhline( - y=np.mean(avg_pwr_db), color="r", linestyle="-", label="average" - ) - plt.axhline( - y=mad, - color="b", - linestyle="-", - label="median absolute deviation threshold", + iq_samples = data_obj.get_samples( + n_seek_samples=sample_idx, n_samples=get_n_samples + ) + + if iq_samples is None: + break + + iq_samples = scipy.signal.detrend( + iq_samples, type="linear", bp=np.arange(0, len(iq_samples), 1024) + ) + # iq_samples = cupyx.scipy.signal.detrend( + # cupy.asarray(iq_samples), type="linear", bp=np.arange(0, len(iq_samples), 1024) + # ) + # iq_samples = cupy.asnumpy(iq_samples) + + # set dB threshold (1. manually set, 2. calculate using median absolute deviation) + if force_threshold_db: + threshold_db = force_threshold_db + else: + avg_pwr = moving_average(iq_samples, avg_window_len) + avg_pwr_db = 10 * np.log10(avg_pwr) + del avg_pwr + + # current threshold in custom_handler + guess_threshold_old = (np.max(avg_pwr_db) + np.mean(avg_pwr_db)) / 2 + + mad = median_absolute_deviation(avg_pwr_db) + + tqdm.write(f"Estimating noise floor for signal detection (may take a while)...") + n_components = len(labels)+1 if labels else 3 + clf = mixture.GaussianMixture(n_components=n_components) + clf.fit(avg_pwr_db.reshape(-1, 1)) + # TODO: add standard deviation parameter (was 2 *) + gaussian_mixture_model_estimate = np.min(clf.means_) + 3 * np.sqrt( + clf.covariances_[np.argmin(clf.means_)].squeeze() ) - if force_threshold_db: - plt.axhline( - y=force_threshold_db, - color="yellow", - linestyle="-", - label="force threshold db", + + threshold_db = gaussian_mixture_model_estimate # mad + + if debug: + print(f"debug") + debug_plot( + avg_pwr_db, + mad, + threshold_db, + avg_duration, + data_obj, + guess_threshold_old, + force_threshold_db, + n_components=n_components, ) - plt.legend(loc="upper left") - plt.ylabel("dB") - plt.xlabel("time (seconds)") - plt.title("Signal Power") - plt.show() - print(f"Using dB threshold = {threshold_db} for detecting signals to annotate") - annotate_power_squelch( - data_obj, - threshold_db, - avg_window_len, - label=label, - skip_validate=True, - spectral_energy_threshold=spectral_energy_threshold, - min_bandwidth=min_bandwidth, - max_bandwidth=max_bandwidth, - dry_run=dry_run, - min_annotation_length=min_annotation_length, - overwrite=overwrite, - max_annotations=max_annotations, - dc_block=dc_block, - verbose=verbose, - n_seek_samples=n_seek_samples, - n_samples=n_samples, - set_bandwidth=set_bandwidth, + + # print(f"Using dB threshold = {threshold_db} for detecting signals to annotate") + tqdm.write( + f"Using dB threshold = {threshold_db} for detecting signals to annotate" + ) + + # apply power squelch to I/Q samples using dB threshold + idx = power_squelch( + iq_samples, threshold=threshold_db, avg_window_len=avg_window_len + ) + + + # j = 0 + for start, stop in tqdm(idx[:max_annotations]): + + candidate_labels = list(labels.keys()) + + start, stop = int(start), int(stop) + + annotation_n_samples = stop - start + annotation_seconds = annotation_n_samples / sample_rate + + for label in candidate_labels[:]: + if "annotation_seconds" in labels[label]: + min_annotation_seconds, max_annotation_seconds = labels[label]["annotation_seconds"] + if min_annotation_seconds and (annotation_seconds < min_annotation_seconds): + candidate_labels.remove(label) + # if verbose: + # print( + # f"min_annotation_seconds not satisfied for {label}: {annotation_seconds} < {min_annotation_seconds}" + # ) + continue + if max_annotation_seconds and (annotation_seconds > max_annotation_seconds): + candidate_labels.remove(label) + if verbose: + print( + f"max_annotation_seconds not satisfied for {label}: {annotation_seconds} > {max_annotation_seconds}" + ) + continue + + if "annotation_length" in labels[label]: + min_annotation_length, max_annotation_length = labels[label]["annotation_length"] + # skip if proposed annotation length is less than min_annotation_length + if min_annotation_length and (annotation_n_samples < min_annotation_length): + candidate_labels.remove(label) + if verbose: + print( + f"min_annotation_length not satisfied for {label}: {annotation_n_samples} < {min_annotation_length}" + ) + continue + if max_annotation_length and (annotation_n_samples > max_annotation_length): + candidate_labels.remove(label) + if verbose: + print( + f"max_annotation_length not satisfied for {label}: {annotation_n_samples} > {max_annotation_length}" + ) + continue + + if len(candidate_labels) == 0: + continue + + freq_edges = None + + # if any candidate labels manually set bandwidth, then skip get_bandwidth + for label in candidate_labels[:]: + if "set_bandwidth" in labels[label]: + freq_edges = [ + data_obj.metadata["captures"][0]["core:frequency"] + labels[label]["set_bandwidth"][0], + data_obj.metadata["captures"][0]["core:frequency"] + labels[label]["set_bandwidth"][1] + ] + candidate_labels = [label] + break + + if freq_edges is None: + freq_edges = get_bandwidth( + data_obj, + iq_samples, + start, + stop, + # set_bandwidth, + bandwidth_estimation, + # spectral_energy_threshold, + dc_block, + verbose, + # min_bandwidth, + # max_bandwidth, + # label, + ) + # if freq_edges is None: + # continue + + freq_lower_edge, freq_upper_edge = freq_edges + + bandwidth = freq_upper_edge - freq_lower_edge + + for label in candidate_labels[:]: + if "bandwidth_limits" in labels[label]: + min_bandwidth, max_bandwidth = labels[label]["bandwidth_limits"] + if min_bandwidth and bandwidth < min_bandwidth: + candidate_labels.remove(label) + if verbose: + print( + f"min_bandwidth not satisfied for {label}, {bandwidth} < {min_bandwidth}, ({freq_lower_edge=}, {freq_upper_edge=})" + ) + continue + if max_bandwidth and bandwidth > max_bandwidth: + candidate_labels.remove(label) + if verbose: + print( + f"max_bandwidth not satisfied for {label}, {bandwidth} > {max_bandwidth}, ({freq_lower_edge=}, {freq_upper_edge=})" + ) + continue + + if len(candidate_labels) == 0: + continue + elif len(candidate_labels) > 1: + warnings.warn(f"Multiple labels are possible {candidate_labels}. Using first label {candidate_labels[0]}.") + + metadata = { + "core:freq_lower_edge": freq_lower_edge, + "core:freq_upper_edge": freq_upper_edge, + } + # if label: + # metadata["core:label"] = label + metadata["core:label"] = candidate_labels[0] + + data_obj.sigmf_obj.add_annotation( + int(sample_idx) + start, length=stop - start, metadata=metadata + ) + n_annotations += 1 + + # j += 1 + + # if j > 15: + # break + + if not dry_run and n_annotations: + data_obj.sigmf_obj.tofile(data_obj.sigmf_meta_filename, skip_validate=True) + print( + f"Writing {len(data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY])} annotations to {data_obj.sigmf_meta_filename}" + ) + + +def get_bandwidth( + data_obj, + iq_samples, + start, + stop, + # set_bandwidth, + bandwidth_estimation, + # spectral_energy_threshold, + dc_block, + verbose, + # min_bandwidth, + # max_bandwidth, + # label, +): + # set bandwidth using user supplied set_bandwidth + + # if set_bandwidth: + # freq_lower_edge = ( + # data_obj.metadata["captures"][0]["core:frequency"] - set_bandwidth / 2 + # ) + # freq_upper_edge = ( + # data_obj.metadata["captures"][0]["core:frequency"] + set_bandwidth / 2 + # ) + # estimate bandwidth using spectral energy thresholding + # if isinstance(spectral_energy_threshold, float): + if isinstance(bandwidth_estimation, bool) and bandwidth_estimation: + freq_lower_edge, freq_upper_edge = get_occupied_bandwidth_gmm( + iq_samples[start:stop], + data_obj.metadata["global"]["core:sample_rate"], + data_obj.metadata["captures"][0]["core:frequency"], + # spectral_energy_threshold=spectral_energy_threshold, + dc_block=dc_block, + verbose=verbose, + ) + # bandwidth = freq_upper_edge - freq_lower_edge + # if min_bandwidth and bandwidth < min_bandwidth: + # if verbose: + # print( + # f"min_bandwidth - Skipping, {start=}, {stop=}, {bandwidth=}, {freq_upper_edge=}, {freq_lower_edge=}" + # ) + # # print(f"Skipping, {label}, {start=}, {stop=}, {bandwidth=}, {freq_upper_edge=}, {freq_lower_edge=}") + # return None + # if max_bandwidth and bandwidth > max_bandwidth: + # if verbose: + # print( + # f"max_bandwidth - Skipping, {start=}, {stop=}, {bandwidth=}, {freq_upper_edge=}, {freq_lower_edge=}" + # ) + # return None + elif isinstance(bandwidth_estimation, float): + freq_lower_edge, freq_upper_edge = get_occupied_bandwidth_spectral_threshold( + iq_samples[start:stop], + data_obj.metadata["global"]["core:sample_rate"], + data_obj.metadata["captures"][0]["core:frequency"], + spectral_energy_threshold=bandwidth_estimation, + + ) + # set bandwidth as full capture bandwidth + else: + freq_lower_edge = ( + data_obj.metadata["captures"][0]["core:frequency"] + - data_obj.metadata["global"]["core:sample_rate"] / 2 + ) + freq_upper_edge = ( + data_obj.metadata["captures"][0]["core:frequency"] + + data_obj.metadata["global"]["core:sample_rate"] / 2 + ) + + return [freq_lower_edge, freq_upper_edge] + +def get_occupied_bandwidth_spectral_threshold( + samples, + sample_rate, + center_frequency, + spectral_energy_threshold, +): + f, t, Sxx = cupyx_spectrogram( + samples, + fs=sample_rate, + return_onesided=False, + scaling="spectrum", + # mode="complex", + detrend=False, + window=cupyx.scipy.signal.windows.boxcar(256), ) + freq_power = cupy.median(cupy.fft.fftshift(Sxx, axes=0), axis=1) + + freq_power_normalized = freq_power / freq_power.sum(axis=0) + + lower_idx = 0 + upper_idx = freq_power_normalized.shape[0] + + while True: + if ( + freq_power_normalized[lower_idx : upper_idx].sum() + <= spectral_energy_threshold + ): + break + + if freq_power_normalized[lower_idx] < freq_power_normalized[upper_idx-1]: + lower_idx += 1 + else: + upper_idx -= 1 + + freq_upper_edge = ( + center_frequency + - (freq_power.shape[0] / 2 - upper_idx) / freq_power.shape[0] * sample_rate + ) + freq_lower_edge = ( + center_frequency + - (freq_power.shape[0] / 2 - lower_idx) / freq_power.shape[0] * sample_rate + ) -def get_occupied_bandwidth( + return freq_lower_edge, freq_upper_edge + + + + +def get_occupied_bandwidth_gmm( samples, sample_rate, center_frequency, - spectral_energy_threshold=None, + # spectral_energy_threshold=None, dc_block=False, verbose=False, ): - if not spectral_energy_threshold: - spectral_energy_threshold = 0.94 + # if not spectral_energy_threshold: + # spectral_energy_threshold = 0.94 f, t, Sxx = cupyx_spectrogram( - samples, fs=sample_rate, return_onesided=False, scaling="spectrum" + samples, + fs=sample_rate, + return_onesided=False, + scaling="spectrum", + # mode="complex", + detrend=False, + window=cupyx.scipy.signal.windows.boxcar(256), ) + # cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=1) + # Sxx = np.abs(Sxx)**2 + # freq_power = cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0)) freq_power = cupy.median(cupy.fft.fftshift(Sxx, axes=0), axis=1) + # freq_power = cupy.median(cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=2, mode="reflect"), axis=1) + # lessen DC if dc_block: dc_start = int(len(freq_power) / 2) - 1 @@ -300,6 +693,208 @@ def get_occupied_bandwidth( freq_power_normalized = freq_power / freq_power.sum(axis=0) + ##### + start_time = time.time() + clf = mixture.GaussianMixture(n_components=2) + predictions = clf.fit_predict( + cupy.asnumpy(10 * cupy.log10(freq_power_normalized)).reshape(-1, 1) + ) + signal_predictions = np.zeros(len(predictions)) + signal_predictions[np.where(predictions == np.argmax(clf.means_))] = 1 + + signal_predictions_idx = ( + np.ediff1d(np.r_[0, signal_predictions == 1, 0]).nonzero()[0].reshape(-1, 2) + ) # gets indices where signal power above threshold + + freq_bounds = signal_predictions_idx[ + np.argmax(np.abs(signal_predictions_idx[:, 0] - signal_predictions_idx[:, 1])) + ] + lower_idx = freq_bounds[0] + upper_idx = freq_bounds[1] + + # plt.figure() + # plt.imshow(cupy.asnumpy(10*cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) + # plt.axhline(y=freq_bounds[0], color="r", linestyle="-") + # plt.axhline(y=freq_bounds[1], color="r", linestyle="-") + # plt.show() + + freq_upper_edge = ( + center_frequency + - (freq_power.shape[0] / 2 - upper_idx) / freq_power.shape[0] * sample_rate + ) + freq_lower_edge = ( + center_frequency + - (freq_power.shape[0] / 2 - lower_idx) / freq_power.shape[0] * sample_rate + ) + + if verbose: + max_power_idx = int(cupy.asnumpy(freq_power_normalized.argmax(axis=0))) + + print(f"\n{lower_idx=}, {upper_idx=}\n") + ### + # Figure 1 + ### + # print(f"{freq_power_normalized[lower_idx]=}") + # print(f"{freq_power_normalized[upper_idx]=}") + # print(f"{freq_power_normalized=}") + fig, axs = plt.subplots(1, 3) + axs[0].imshow(cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) + axs[0].axhline(y=upper_idx, color="r", linestyle="-") + axs[0].axhline(y=lower_idx, color="g", linestyle="-") + # axs[0].pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) + # plt.ylabel('Frequency [Hz]') + # plt.xlabel('Time [sec]') + axs[1].imshow( + np.tile( + np.expand_dims( + cupy.asnumpy(cupy.median(cupy.fft.fftshift(Sxx, axes=0), axis=1)), 1 + ), + 25, + ) + ) + # axs[1].axhline(y = upper_idx, color = 'r', linestyle = '-') + # axs[1].axhline(y = lower_idx, color = 'g', linestyle = '-') + + axs[2].imshow( + np.tile(np.expand_dims(cupy.asnumpy(freq_power_normalized), 1), 25) + ) + axs[2].axhline(y=max_power_idx, color="pink", linestyle="-") + axs[2].axhline(y=upper_idx, color="r", linestyle="-") + axs[2].axhline(y=lower_idx, color="g", linestyle="-") + plt.show() + + ### + # Figure 2 + ### + start_time = time.time() + plt.figure() + sns.histplot(cupy.asnumpy(freq_power), kde=True) + plt.xlabel("power") + plt.title(f"Occupied Bandwidth Signal Power Histogram & Density") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 3 + ### + start_time = time.time() + plt.figure() + sns.histplot(cupy.asnumpy(freq_power_normalized), kde=True) + plt.xlabel("power") + plt.title(f"Normalized Occupied Bandwidth Signal Power Histogram & Density") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 4 + ### + start_time = time.time() + plt.figure() + sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power)), kde=True) + plt.xlabel("dB") + plt.title(f"10*cupy.log10(freq_power)") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 5 + ### + start_time = time.time() + plt.figure() + sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized)), kde=True) + plt.xlabel("dB") + plt.title(f"10*cupy.log10(freq_power_normalized)") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 6 + ### + start_time = time.time() + plt.figure() + sns.histplot( + cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0))).flatten(), + kde=True, + ) + plt.xlabel("dB") + plt.title(f"10*cupy.log10(cupy.fft.fftshift(Sxx, axes=0))") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 7 + ### + start_time = time.time() + plt.figure() + plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power))) + plt.xlabel("frequency") + plt.ylabel("power") + plt.title(f"10*cupy.log10(freq_power)") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 8 + ### + start_time = time.time() + plt.figure() + plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized))) + plt.xlabel("frequency") + plt.ylabel("power") + plt.title(f"10*cupy.log10(freq_power_normalized)") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + # fit a Gaussian Mixture Model with two components + start_time = time.time() + clf = mixture.GaussianMixture(n_components=2) + predictions = clf.fit_predict( + cupy.asnumpy(10 * cupy.log10(freq_power_normalized)).reshape(-1, 1) + ) + # predictions = clf.fit_predict(cupy.asnumpy(freq_power_normalized).reshape(-1, 1)) + print(f"Gaussian mixture model time = {time.time()-start_time}") + print(f"{clf.weights_=}") + print(f"{clf.means_=}") + print(f"{clf.covariances_=}") + print(f"{clf.converged_=}") + + ### + # Figure 9 + ### + start_time = time.time() + plt.figure() + plt.plot(predictions) + plt.xlabel("") + plt.ylabel("gaussian mixture labels") + plt.title(f"") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + #### + #### + signal_predictions = np.zeros(len(predictions)) + signal_predictions[np.where(predictions == np.argmax(clf.means_))] = 1 + + signal_predictions_idx = ( + np.ediff1d(np.r_[0, signal_predictions == 1, 0]).nonzero()[0].reshape(-1, 2) + ) # gets indices where signal power above threshold + + freq_bounds = signal_predictions_idx[ + np.argmax( + np.abs(signal_predictions_idx[:, 0] - signal_predictions_idx[:, 1]) + ) + ] + print(f"{signal_predictions_idx.shape=}") + print(f"{signal_predictions_idx=}") + plt.figure() + plt.imshow(cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) + plt.axhline(y=freq_bounds[0], color="r", linestyle="-") + plt.axhline(y=freq_bounds[1], color="r", linestyle="-") + plt.show() + + return freq_lower_edge, freq_upper_edge + ##### + max_power_idx = int(cupy.asnumpy(freq_power_normalized.argmax(axis=0))) lower_idx = max_power_idx upper_idx = max_power_idx @@ -339,11 +934,16 @@ def get_occupied_bandwidth( ) if verbose: + + print(f"\n{lower_idx=}, {upper_idx=}\n") + ### + # Figure 1 + ### # print(f"{freq_power_normalized[lower_idx]=}") # print(f"{freq_power_normalized[upper_idx]=}") # print(f"{freq_power_normalized=}") fig, axs = plt.subplots(1, 3) - axs[0].imshow(cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) + axs[0].imshow(cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) axs[0].axhline(y=upper_idx, color="r", linestyle="-") axs[0].axhline(y=lower_idx, color="g", linestyle="-") # axs[0].pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) @@ -363,114 +963,244 @@ def get_occupied_bandwidth( axs[2].imshow( np.tile(np.expand_dims(cupy.asnumpy(freq_power_normalized), 1), 25) ) - axs[2].axhline(y=max_power_idx, color="orange", linestyle="-") + axs[2].axhline(y=max_power_idx, color="pink", linestyle="-") axs[2].axhline(y=upper_idx, color="r", linestyle="-") axs[2].axhline(y=lower_idx, color="g", linestyle="-") plt.show() - # exit() - return freq_lower_edge, freq_upper_edge - - -def get_occupied_bandwidth_backup(samples, sample_rate, center_frequency): - - # spectrogram_data, spectrogram_raw = spectrogram( - # samples, - # sample_rate, - # 256, - # 0, - # ) - # spectrogram_color = spectrogram_cmap(spectrogram_data, plt.get_cmap("viridis")) - - # plt.figure() - # plt.imshow(spectrogram_color) - # plt.show() - - # print(f"{samples.shape=}") - # print(f"{samples=}") - - f, t, Sxx = cupyx_spectrogram( - samples, fs=sample_rate, return_onesided=False, scaling="spectrum" - ) - - freq_power = cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0)) - # print(f"{freq_power.shape=}") - # print(f"{freq_power.argmax(axis=0).shape=}") - # print(f"{freq_power.argmax(axis=0)=}") + ### + # Figure 2 + ### + start_time = time.time() + plt.figure() + sns.histplot(cupy.asnumpy(freq_power), kde=True) + plt.xlabel("power") + plt.title(f"Occupied Bandwidth Signal Power Histogram & Density") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 3 + ### + start_time = time.time() + plt.figure() + sns.histplot(cupy.asnumpy(freq_power_normalized), kde=True) + plt.xlabel("power") + plt.title(f"Normalized Occupied Bandwidth Signal Power Histogram & Density") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 4 + ### + start_time = time.time() + plt.figure() + sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power)), kde=True) + plt.xlabel("dB") + plt.title(f"10*cupy.log10(freq_power)") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 5 + ### + start_time = time.time() + plt.figure() + sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized)), kde=True) + plt.xlabel("dB") + plt.title(f"10*cupy.log10(freq_power_normalized)") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 6 + ### + start_time = time.time() + plt.figure() + sns.histplot( + cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0))).flatten(), + kde=True, + ) + plt.xlabel("dB") + plt.title(f"10*cupy.log10(cupy.fft.fftshift(Sxx, axes=0))") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 7 + ### + start_time = time.time() + plt.figure() + plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power))) + plt.xlabel("frequency") + plt.ylabel("power") + plt.title(f"10*cupy.log10(freq_power)") + plt.show() + print(f"Plot time = {time.time()-start_time}") + + ### + # Figure 8 + ### + start_time = time.time() + plt.figure() + plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized))) + plt.xlabel("frequency") + plt.ylabel("power") + plt.title(f"10*cupy.log10(freq_power_normalized)") + plt.show() + print(f"Plot time = {time.time()-start_time}") - # freq_power = cupy.asnumpy(cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=1)) - freq_power = cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=1) + # fit a Gaussian Mixture Model with two components + start_time = time.time() + clf = mixture.GaussianMixture(n_components=2) + predictions = clf.fit_predict( + cupy.asnumpy(10 * cupy.log10(freq_power_normalized)).reshape(-1, 1) + ) + # predictions = clf.fit_predict(cupy.asnumpy(freq_power_normalized).reshape(-1, 1)) + print(f"Gaussian mixture model time = {time.time()-start_time}") + print(f"{clf.weights_=}") + print(f"{clf.means_=}") + print(f"{clf.covariances_=}") + print(f"{clf.converged_=}") + + ### + # Figure 9 + ### + start_time = time.time() + plt.figure() + plt.plot(predictions) + plt.xlabel("") + plt.ylabel("gaussian mixture labels") + plt.title(f"") + plt.show() + print(f"Plot time = {time.time()-start_time}") - # plt.figure() - # plt.pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) - # plt.ylabel('Frequency [Hz]') - # plt.xlabel('Time [sec]') - # plt.show() - # plt.figure() - # plt.pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(freq_power)) - # plt.ylabel('Frequency [Hz]') - # plt.xlabel('Time [sec]') - # plt.show() + #### + #### + signal_predictions = np.zeros(len(predictions)) + signal_predictions[np.where(predictions == np.argmax(clf.means_))] = 1 - freq_power_normalized = freq_power / freq_power.sum(axis=0) + signal_predictions_idx = ( + np.ediff1d(np.r_[0, signal_predictions == 1, 0]).nonzero()[0].reshape(-1, 2) + ) # gets indices where signal power above threshold - # print(f"{freq_power_normalized.shape=}") - # print(f"{freq_power_normalized.argmax(axis=0).shape=}") - # print(f"{freq_power_normalized.argmax(axis=0)=}") - bounds = [] - for i, max_power_idx in enumerate(freq_power_normalized.argmax(axis=0)): - max_power_idx = int(cupy.asnumpy(max_power_idx)) - # print(f"{i=}, {max_power_idx=}") - lower_idx = max_power_idx - upper_idx = max_power_idx - while True: - - if upper_idx == freq_power_normalized.shape[0] - 1: - lower_idx -= 1 - elif lower_idx == 0: - upper_idx += 1 - elif ( - freq_power_normalized[lower_idx, i] - > freq_power_normalized[upper_idx, i] - ): - lower_idx -= 1 - else: - upper_idx += 1 - - # print(f"{lower_idx=}, {upper_idx=}") - # print(f"{freq_power_normalized[lower_idx:upper_idx, i].sum()=}") - if freq_power_normalized[lower_idx:upper_idx, i].sum() >= 0.94: - break - - bounds.append([lower_idx, upper_idx]) - bounds = np.array(bounds) + freq_bounds = signal_predictions_idx[ + np.argmax( + np.abs(signal_predictions_idx[:, 0] - signal_predictions_idx[:, 1]) + ) + ] + print(f"{signal_predictions_idx.shape=}") + print(f"{signal_predictions_idx=}") + plt.figure() + plt.imshow(cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) + plt.axhline(y=freq_bounds[0], color="r", linestyle="-") + plt.axhline(y=freq_bounds[1], color="r", linestyle="-") + plt.show() - plt.figure() - plt.imshow(cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) - plt.plot(cupy.asnumpy(freq_power.argmax(axis=0))) - plt.plot(bounds[:, 0]) - plt.plot(bounds[:, 1]) - plt.axhline(y=np.median(bounds[:, 0]), color="r", linestyle="-") - plt.axhline(y=np.median(bounds[:, 1]), color="b", linestyle="-") - plt.show() + # exit() + return freq_lower_edge, freq_upper_edge - freq_lower_edge = ( - center_frequency - + (freq_power.shape[0] / 2 - np.median(bounds[:, 1])) - / freq_power.shape[0] - * sample_rate - ) - freq_upper_edge = ( - center_frequency - + (freq_power.shape[0] / 2 - np.median(bounds[:, 0])) - / freq_power.shape[0] - * sample_rate - ) - # print(f"{freq_lower_edge=}") - # print(f"{freq_upper_edge=}") - print(f"estimated bandwidth = {freq_upper_edge-freq_lower_edge}") - return freq_lower_edge, freq_upper_edge +# def get_occupied_bandwidth_backup(samples, sample_rate, center_frequency): + +# # spectrogram_data, spectrogram_raw = spectrogram( +# # samples, +# # sample_rate, +# # 256, +# # 0, +# # ) +# # spectrogram_color = spectrogram_cmap(spectrogram_data, plt.get_cmap("viridis")) + +# # plt.figure() +# # plt.imshow(spectrogram_color) +# # plt.show() + +# # print(f"{samples.shape=}") +# # print(f"{samples=}") + +# f, t, Sxx = cupyx_spectrogram( +# samples, fs=sample_rate, return_onesided=False, scaling="spectrum" +# ) + +# freq_power = cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0)) +# # print(f"{freq_power.shape=}") + +# # print(f"{freq_power.argmax(axis=0).shape=}") +# # print(f"{freq_power.argmax(axis=0)=}") + +# # freq_power = cupy.asnumpy(cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=1)) +# freq_power = cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=1) + +# # plt.figure() +# # plt.pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) +# # plt.ylabel('Frequency [Hz]') +# # plt.xlabel('Time [sec]') +# # plt.show() +# # plt.figure() +# # plt.pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(freq_power)) +# # plt.ylabel('Frequency [Hz]') +# # plt.xlabel('Time [sec]') +# # plt.show() + +# freq_power_normalized = freq_power / freq_power.sum(axis=0) + +# # print(f"{freq_power_normalized.shape=}") +# # print(f"{freq_power_normalized.argmax(axis=0).shape=}") +# # print(f"{freq_power_normalized.argmax(axis=0)=}") +# bounds = [] +# for i, max_power_idx in enumerate(freq_power_normalized.argmax(axis=0)): +# max_power_idx = int(cupy.asnumpy(max_power_idx)) +# # print(f"{i=}, {max_power_idx=}") +# lower_idx = max_power_idx +# upper_idx = max_power_idx +# while True: + +# if upper_idx == freq_power_normalized.shape[0] - 1: +# lower_idx -= 1 +# elif lower_idx == 0: +# upper_idx += 1 +# elif ( +# freq_power_normalized[lower_idx, i] +# > freq_power_normalized[upper_idx, i] +# ): +# lower_idx -= 1 +# else: +# upper_idx += 1 + +# # print(f"{lower_idx=}, {upper_idx=}") +# # print(f"{freq_power_normalized[lower_idx:upper_idx, i].sum()=}") +# if freq_power_normalized[lower_idx:upper_idx, i].sum() >= 0.94: +# break + +# bounds.append([lower_idx, upper_idx]) +# bounds = np.array(bounds) + +# plt.figure() +# plt.imshow(cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) +# plt.plot(cupy.asnumpy(freq_power.argmax(axis=0))) +# plt.plot(bounds[:, 0]) +# plt.plot(bounds[:, 1]) +# plt.axhline(y=np.median(bounds[:, 0]), color="r", linestyle="-") +# plt.axhline(y=np.median(bounds[:, 1]), color="b", linestyle="-") +# plt.show() + +# freq_lower_edge = ( +# center_frequency +# + (freq_power.shape[0] / 2 - np.median(bounds[:, 1])) +# / freq_power.shape[0] +# * sample_rate +# ) +# freq_upper_edge = ( +# center_frequency +# + (freq_power.shape[0] / 2 - np.median(bounds[:, 0])) +# / freq_power.shape[0] +# * sample_rate +# ) + +# # print(f"{freq_lower_edge=}") +# # print(f"{freq_upper_edge=}") +# print(f"estimated bandwidth = {freq_upper_edge-freq_lower_edge}") +# return freq_lower_edge, freq_upper_edge def reset_predictions_sigmf(dataset): diff --git a/rfml/data.py b/rfml/data.py index 4ddeb46..e9bfab0 100644 --- a/rfml/data.py +++ b/rfml/data.py @@ -161,6 +161,7 @@ def __init__(self, filename, force_sigmf_data=True): ) if not self.data_filename or not os.path.isfile(self.data_filename): raise ValueError(f"File: {self.data_filename} is not a valid file.") + elif self.filename.lower().endswith(".sigmf-data"): self.data_filename = self.filename self.sigmf_meta_filename = ( @@ -171,12 +172,16 @@ def __init__(self, filename, force_sigmf_data=True): f"File: {self.sigmf_meta_filename} is not a valid vile." ) elif self.filename.lower().endswith(".zst"): + self.data_filename = self.filename + possible_sigmf_meta_filenames = [ f"{os.path.splitext(self.data_filename)[0]}.sigmf-meta", f"{self.data_filename}.sigmf-meta", ] + self.sigmf_meta_filename = f"{os.path.splitext(self.data_filename)[0]}.sigmf-meta" + for possible_sigmf in possible_sigmf_meta_filenames: if os.path.isfile(possible_sigmf): self.sigmf_meta_filename = possible_sigmf @@ -185,7 +190,8 @@ def __init__(self, filename, force_sigmf_data=True): self.zst_to_sigmf_meta() if force_sigmf_data: - self.export_sigmf_data(output_path=self.data_filename + ".sigmf-data") + self.export_sigmf_data(output_path=f"{os.path.splitext(self.data_filename)[0]}.sigmf-data") + elif self.filename.lower().endswith(".raw"): self.data_filename = self.filename self.sigmf_meta_filename = ( @@ -290,15 +296,15 @@ def get_samples(self, n_seek_samples=0, n_samples=-1): np.array: Complex vector of I/Q samples. """ - if self.sigmf_obj: - try: - return self.sigmf_obj.read_samples( - start_index=n_seek_samples, count=n_samples - ) - except OSError as e: - print(f"Error: {e}") - # reached end of file - return None + # if self.sigmf_obj: + # try: + # return self.sigmf_obj.read_samples( + # start_index=n_seek_samples, count=n_samples + # ) + # except OSError as e: + # print(f"Error: {e}") + # # reached end of file + # return None reader = self.get_sample_reader() @@ -327,9 +333,9 @@ def get_samples(self, n_seek_samples=0, n_samples=-1): return None if n_samples > -1 and n_buffered_samples != n_samples: warnings.warn( - f"Could only read {n_buffered_samples}/{n_samples} samples from {self.data_filename}." + f"Could only read {n_buffered_samples} samples from {self.data_filename}, but requested {n_samples}." ) - return None + # return None x1d = np.frombuffer( sample_buffer, dtype=sample_dtype, count=n_buffered_samples ) @@ -1538,7 +1544,7 @@ def get_custom_metadata(filename, metadata_directory): sample_filename = metadata["sample_file"]["filename"] return spectrogram_metadata, sample_filename - + if __name__ == "__main__": # /Users/ltindall/data/gamutrf/gamutrf-arl/01_30_23/mini2/snr_noise_floor/ diff --git a/rfml/utils.py b/rfml/utils.py new file mode 100644 index 0000000..4cefb85 --- /dev/null +++ b/rfml/utils.py @@ -0,0 +1,66 @@ +import copy +import glob +import json + +from datetime import datetime +from pathlib import Path + +import rfml.data + + +def manual_to_sigmf(file, datatype, sample_rate, frequency, iso_date_string): + # change to .sigmf-data + if file.suffix in [".raw"]: + file = file.rename(file.with_suffix(".sigmf-data")) + else: + raise NotImplementedError + + sigmf_meta = copy.deepcopy(rfml.data.SIGMF_META_DEFAULT) + sigmf_meta["global"]["core:dataset"] = str(file) + sigmf_meta["global"]["core:datatype"] = datatype + sigmf_meta["global"]["core:sample_rate"] = sample_rate + sigmf_meta["captures"][0]["core:frequency"] = frequency + sigmf_meta["captures"][0]["core:datetime"] = ( + datetime.fromisoformat(iso_date_string) + .isoformat(timespec="milliseconds") + .replace("+00:00", "Z") + ) + + with open(file.with_suffix(".sigmf-meta"), "w") as outfile: + print(f"Saving {file.with_suffix('.sigmf-meta')}\n") + outfile.write(json.dumps(sigmf_meta, indent=4)) + + +if __name__ == "__main__": + + data_globs = [ + ( + "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings/*.raw", + { + "datatype": "cf32_le", + "sample_rate": 20500000, + "frequency": 5735000000, + "iso_date_string": "2022-05-26", + }, + ), + ( + "/data/s3_gamutrf/gamutrf-birdseye-field-days/leesburg_field_day_2022_06_15/iq_recordings/*.raw", + { + "datatype": "cf32_le", + "sample_rate": 20500000, + "frequency": 5735000000, + "iso_date_string": "2022-06-15", + } + ) + ] + for file_glob, metadata in data_globs: + files = glob.glob(str(Path(file_glob))) + for f in files: + f = Path(f) + manual_to_sigmf( + f, + metadata["datatype"], + metadata["sample_rate"], + metadata["frequency"], + metadata["iso_date_string"], + ) From 12b265bd4a4683425938a7b240ccc4924f1a63f0 Mon Sep 17 00:00:00 2001 From: ltindall Date: Fri, 13 Sep 2024 11:21:57 -0700 Subject: [PATCH 3/3] Refactor annotation_utils and other changes. --- README.md | 61 +- experiments/dji_mini2_wifi_experiments.py | 139 + .../mixed_experiments.py | 50 +- experiments/siggen_experiments.py | 37 + {rfml => experiments}/wifi_experiments.py | 85 +- label_scripts/label-lucas-collect.py | 66 + label_scripts/label_bladerf_wifi.py | 3 +- label_scripts/label_gamutrf_nz_wifi.py | 15 +- label_scripts/label_lab_collect.py | 42 + label_scripts/label_mavic3_lab.py | 10 +- label_scripts/label_mini2_fieldday.py | 42 +- label_scripts/label_mini2_lab.py | 10 +- notebooks/manual_to_sigmf.ipynb | 104 + notebooks/signal_power.ipynb | 2637 +++++++++++++++++ rfml/annotation_utils.py | 1244 +++----- rfml/convert_model.py | 50 - rfml/data.py | 32 +- rfml/experiment.py | 68 +- rfml/export_model.py | 109 + rfml/models.py | 98 +- rfml/sigmf_pytorch_dataset.py | 50 +- rfml/train_iq.py | 192 +- rfml/utils.py | 4 +- 23 files changed, 3946 insertions(+), 1202 deletions(-) create mode 100644 experiments/dji_mini2_wifi_experiments.py rename rfml/run_experiments.py => experiments/mixed_experiments.py (86%) create mode 100644 experiments/siggen_experiments.py rename {rfml => experiments}/wifi_experiments.py (88%) create mode 100644 label_scripts/label-lucas-collect.py create mode 100644 label_scripts/label_lab_collect.py create mode 100644 notebooks/manual_to_sigmf.ipynb create mode 100644 notebooks/signal_power.ipynb delete mode 100644 rfml/convert_model.py create mode 100644 rfml/export_model.py diff --git a/README.md b/README.md index ce8a53c..1619d60 100644 --- a/README.md +++ b/README.md @@ -95,21 +95,31 @@ In the labeling scripts, the settings for autolabeling need to be tuned for the ```python annotation_utils.annotate( - f, - label="mavic3_video", # This is the label that is applied to all of the matching annotations - avg_window_len=256, # The number of samples over which to average signal power - avg_duration=0.25, # The number of seconds, from the start of the recording to use to automatically calculate the SNR threshold, if it is None then all of the samples will be used - debug=False, - set_bandwidth=10000000, # Manually set the bandwidth of the signals in Hz, if this parameter is set, then spectral_energy_threshold is ignored - spectral_energy_threshold=0.95, # Percentage used to determine the upper and lower frequency bounds for an annotation - force_threshold_db=-58, # Used to manually set the threshold used for detecting a signal and creating an annotation. If None, then the automatic threshold calculation will be used instead. - overwrite=False, # If True, any existing annotations in the .sigmf-meta file will be removed - min_bandwidth=16e6, # The minimum bandwidth (in Hz) of a signal to annotate - max_bandwidth=None, # The maximum bandwidth (in Hz) of a signal to annotate - min_annotation_length=10000, # The minimum numbers of samples in length a signal needs to be in order for it to be annotated. This is directly related to the sample rate a signal was captured at and does not take into account bandwidth. So 10000 samples at 20,000,000 samples per second, would mean a minimum transmission length of 0.0005 seconds - # max_annotations=500, # The maximum number of annotations to automatically add - dc_block=True # De-emphasize the DC spike when trying to calculate the frequencies for a signal - ) + rfml.data.Data(filename), + avg_window_len=256, # The window size to use when averaging signal power + power_estimate_duration=0.1, # Process the file in chunks of power_estimate_duration seconds + debug_duration=0.25, # If debug==True, then plot debug_duration seconds of data in debug plots + debug=False, # Set True to enable debugging plots + verbose=False, # Set True to eanble verbose messages + dry_run=False, # Set True to disable annotations being written to SigMF-Meta file. + bandwidth_estimation=True, # If set to True, will estimate signal bandwidth using Gaussian Mixture Models. If set to a float will estimate signal bandwidth using spectral thresholding. + force_threshold_db=None, # Used to manually set the threshold used for detecting a signal and creating an annotation. If None, then the automatic threshold calculation will be used instead. + overwrite=True, # If True, any existing annotations in the .sigmf-meta file will be removed + max_annotations=None, # If set, limits the number of annotations to add. + dc_block=None, # De-emphasize the DC spike when trying to calculate the frequencies for a signal + time_start_stop=None, # Sets the start/stop time for annotating the recording (must be tuple or list of length 2). + n_components = None, # Sets the number of mixture components to use when calculating signal detection threshold. If not set, then automatically calculated from labels. + n_init=1, # Number of initializations to use in Gaussian Mixture Method. Increasing this number can significantly increase run time. + fft_len=256, # FFT length used in calculating bandwidth + labels = { # The labels dictionary defines the annotations that the script will attempt to find. + "mavic3_video": { # The dictionary keys define the annotation labels. Only a key is necessary. + "bandwidth_limits": (8e6, None), # Optional. Set min/max bandwidth limit for a signal. If None, no min/max limit. + "annotation_length": (10000, None), # Optional. Set min/max annoation length in number of samples. If None, no min/max limit. + "annotation_seconds": (0.0001, 0.0025), # Optional. Set min/max annotation length in seconds. If None, no min/max limit. + "set_bandwidth": (-8.5e6, 9.5e6) # Optional. Ignore bandwidth estimation, set bandwidth manually. Limits are in relation to center frequency. + } + } +) ``` ### Tips for Tuning Autolabeling @@ -138,7 +148,7 @@ After you have finished labeling your data, the next step is to train a model on ### Configure -This repo provides an automated script for training and evaluating models. To do this, configure the [run_experiments.py](rfml/run_experiments.py) file to point to the data you want to use and set the training parameters: +This repo provides an automated script for training and evaluating models. To do this, configure the [mixed_experiments.py](rfml/mixed_experiments.py) file or create your own to point to the data you want to use and set the training parameters: ```python "experiment_0": { # A name to refer to the experiment @@ -150,10 +160,10 @@ This repo provides an automated script for training and evaluating models. To do } ``` -Once you have the **run_experiments.py** file configured, run it: +Once you have the **mixed_experiments.py** file configured, run it: ```bash -python3 run_experiments.py +python3 mixed_experiments.py ``` Once the training has completed, it will print out the logs location, model accuracy, and the location of the best checkpoint: @@ -170,18 +180,15 @@ Best Model Checkpoint: lightning_logs/version_5/checkpoints/experiment_logs/expe ### Convert & Export IQ Models -Once you have a trained model, you need to convert it into a portable format that can easily be served by TorchServe. To do this, use **convert_model.py**: +Once you have a trained model, you need to convert it into a portable format that can easily be served by TorchServe. To do this, use **export_model.py**: ```bash -python3 convert_model.py --model_name=drone_detect --checkpoint=lightning_logs/version_5/checkpoints/experiment_logs/experiment_1/iq_checkpoints/checkpoint.ckpt +python3 rfml/export_model.py --model_name=drone_detect --checkpoint=lightning_logs/version_5/checkpoints/experiment_logs/experiment_1/iq_checkpoints/checkpoint.ckpt ``` -This will export a **_torchscript.pt** file. +This will create a **_torchscript.pt** and **_torchserve.pt** file in the weights folder. -```bash -torch-model-archiver --force --model-name drone_detect --version 1.0 --serialized-file weights/drone_detect_torchscript.pt --handler custom_handlers/iq_custom_handler.py --export-path models/ -r custom_handler/requirements.txt -``` +A **.mar** file will also be created in the [models/](./models/) folder. [GamutRF](https://github.com/IQTLabs/gamutRF) can run this model and use it to classify signals. -This will generate a **.mar** file in the [models/](./models/) folder. [GamutRF](https://github.com/IQTLabs/gamutRF) can run this model and use it to classify signals. ## Files @@ -194,9 +201,11 @@ This will generate a **.mar** file in the [models/](./models/) folder. [GamutRF] [experiment.py](rfml/experiment.py) - Class to manage experiments +[export_model.py](rfml/export_model.py) - Convert and export model checkpoints to Torchscript/Torchserve/MAR format. + [models.py](rfml/models.py) - Class for I/Q models (based on TorchSig) -[run_experiments.py](rfml/run_experiments.py) - Experiment configurations and run script +[experiments/](experiments/) - Experiment configurations and run script [sigmf_pytorch_dataset.py](rfml/sigmf_pytorch_dataset.py) - PyTorch style dataset class for SigMF data (based on TorchSig) diff --git a/experiments/dji_mini2_wifi_experiments.py b/experiments/dji_mini2_wifi_experiments.py new file mode 100644 index 0000000..2b3f97b --- /dev/null +++ b/experiments/dji_mini2_wifi_experiments.py @@ -0,0 +1,139 @@ +from rfml.experiment import * + +# Ensure that data directories have sigmf-meta files with annotations +# Annotations can be generated using scripts in label_scripts directory or notebooks/Label_WiFi.ipynb and notebooks/Label_DJI.ipynb + +spec_epochs = 0 +iq_epochs = 10 +iq_only_start_of_burst = False +iq_num_samples = 4000 +iq_early_stop = 3 +iq_train_limit = 0.01 +iq_val_limit = 0.1 + +experiments = { + "experiment_nz_wifi_arl_mini2_pdx_mini2_to_leesburg_mini2": { + "class_list": ["mini2_video", "mini2_telem", "wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-arl/01_30_23/mini2", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/leesburg_field_day_2022_06_15/iq_recordings", + ], + "iq_epochs": iq_epochs, + "spec_epochs": spec_epochs, + "iq_only_start_of_burst": iq_only_start_of_burst, + "iq_early_stop": iq_early_stop, + "iq_train_limit": iq_train_limit, + "iq_val_limit": iq_val_limit, + "notes": "", + }, + "experiment_nz_wifi_arl_mini2_to_leesburg_mini2": { + "class_list": ["mini2_video", "mini2_telem", "wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-arl/01_30_23/mini2", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/leesburg_field_day_2022_06_15/iq_recordings", + ], + "iq_epochs": iq_epochs, + "spec_epochs": spec_epochs, + "iq_only_start_of_burst": iq_only_start_of_burst, + "iq_early_stop": iq_early_stop, + "iq_train_limit": iq_train_limit, + "iq_val_limit": iq_val_limit, + "notes": "", + }, + "experiment_nz_wifi_pdx_mini2_to_leesburg_mini2": { + "class_list": ["mini2_video", "mini2_telem", "wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/leesburg_field_day_2022_06_15/iq_recordings", + ], + "iq_epochs": iq_epochs, + "spec_epochs": spec_epochs, + "iq_only_start_of_burst": iq_only_start_of_burst, + "iq_early_stop": iq_early_stop, + "iq_train_limit": iq_train_limit, + "iq_val_limit": iq_val_limit, + "notes": "", + }, + "experiment_nz_wifi_arl_mini2_to_pdx_mini2": { + "class_list": ["mini2_video", "mini2_telem", "wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-arl/01_30_23/mini2", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings", + ], + "iq_epochs": iq_epochs, + "spec_epochs": spec_epochs, + "iq_only_start_of_burst": iq_only_start_of_burst, + "iq_early_stop": iq_early_stop, + "iq_train_limit": iq_train_limit, + "iq_val_limit": iq_val_limit, + "notes": "", + }, + "experiment_nz_wifi_leesburg_mini2_to_pdx_mini2": { + "class_list": ["mini2_video", "mini2_telem", "wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/leesburg_field_day_2022_06_15/iq_recordings", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings", + ], + "iq_epochs": iq_epochs, + "spec_epochs": spec_epochs, + "iq_only_start_of_burst": iq_only_start_of_burst, + "iq_early_stop": iq_early_stop, + "iq_train_limit": iq_train_limit, + "iq_val_limit": iq_val_limit, + "notes": "", + }, + "experiment_nz_wifi_leesburg_mini2_pdx_mini2_to_arl_mini2": { + "class_list": ["mini2_video", "mini2_telem", "wifi"], + "train_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/leesburg_field_day_2022_06_15/iq_recordings", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings", + ], + "val_dir": [ + "/data/s3_gamutrf/gamutrf-nz-wifi", + "/data/s3_gamutrf/gamutrf-arl/01_30_23/mini2", + ], + "iq_epochs": iq_epochs, + "spec_epochs": spec_epochs, + "iq_only_start_of_burst": iq_only_start_of_burst, + "iq_early_stop": iq_early_stop, + "iq_train_limit": iq_train_limit, + "iq_val_limit": iq_val_limit, + "notes": "", + }, +} + + +if __name__ == "__main__": + + experiments_to_run = [ + # "experiment_nz_wifi_arl_mini2_pdx_mini2_to_leesburg_mini2", + # "experiment_nz_wifi_arl_mini2_to_leesburg_mini2", + # "experiment_nz_wifi_pdx_mini2_to_leesburg_mini2", + # "experiment_nz_wifi_arl_mini2_to_pdx_mini2", + # "experiment_nz_wifi_leesburg_mini2_to_pdx_mini2", + "experiment_nz_wifi_leesburg_mini2_pdx_mini2_to_arl_mini2" + ] + + train({name: experiments[name] for name in experiments_to_run}) diff --git a/rfml/run_experiments.py b/experiments/mixed_experiments.py similarity index 86% rename from rfml/run_experiments.py rename to experiments/mixed_experiments.py index 79f7ea1..d08e593 100644 --- a/rfml/run_experiments.py +++ b/experiments/mixed_experiments.py @@ -1,9 +1,4 @@ -from pathlib import Path - from rfml.experiment import * -from rfml.train_iq import * -from rfml.train_spec import * - # Ensure that data directories have sigmf-meta files with annotations # Annotations can be generated using scripts in label_scripts directory or notebooks/Label_WiFi.ipynb and notebooks/Label_DJI.ipynb @@ -262,47 +257,4 @@ # "experiment_mavic3", ] - for experiment_name in experiments_to_run: - print(f"Running {experiment_name}") - try: - exp = Experiment( - experiment_name=experiment_name, **experiments[experiment_name] - ) - - logs_timestamp = datetime.now().strftime("%m_%d_%Y_%H_%M_%S") - - if exp.iq_epochs > 0: - train_iq( - train_dataset_path=exp.train_dir, - val_dataset_path=exp.val_dir, - num_iq_samples=exp.iq_num_samples, - only_use_start_of_burst=exp.iq_only_start_of_burst, - epochs=exp.iq_epochs, - batch_size=exp.iq_batch_size, - class_list=exp.class_list, - output_dir=Path("experiment_logs", exp.experiment_name), - logs_dir=Path("iq_logs", logs_timestamp), - ) - else: - print("Skipping IQ training") - - if exp.spec_epochs > 0: - train_spec( - train_dataset_path=exp.train_dir, - val_dataset_path=exp.val_dir, - n_fft=exp.spec_n_fft, - time_dim=exp.spec_time_dim, - epochs=exp.spec_epochs, - batch_size=exp.spec_batch_size, - class_list=exp.class_list, - yolo_augment=exp.spec_yolo_augment, - skip_export=exp.spec_skip_export, - force_yolo_label_larger=exp.spec_force_yolo_label_larger, - output_dir=Path("experiment_logs", exp.experiment_name), - logs_dir=Path("spec_logs", logs_timestamp), - ) - else: - print("Skipping spectrogram training") - - except Exception as error: - print(f"Error: {error}") + train({name: experiments[name] for name in experiments_to_run}) diff --git a/experiments/siggen_experiments.py b/experiments/siggen_experiments.py new file mode 100644 index 0000000..50ebe58 --- /dev/null +++ b/experiments/siggen_experiments.py @@ -0,0 +1,37 @@ +import torch + +torch.set_float32_matmul_precision("medium") +from rfml.experiment import * + +# +# python rfml/siggen_experiments.py +# python convert_model.py --model_name siggen_model --checkpoint /home/ltindall/iqt/rfml/lightning_logs/siggen_experiment/checkpoints/experiment_logs/siggen_experiment/iq_checkpoints/checkpoint-v3.ckpt +# torch-model-archiver --force --model-name siggen_model --version 1.0 --serialized-file rfml/weights/siggen_model_torchscript.pt --handler custom_handlers/iq_custom_handler.py --export-path models/ -r custom_handlers/requirements.txt +# cp models/siggen_model.mar ~/iqt/gamutrf-deploy/docker_rundir/model_store/ +# sudo chmod -R 777 /home/ltindall/iqt/gamutrf-deploy/docker_rundir/ +# + + +experiments = { + "siggen_experiment": { + "class_list": ["am", "fm"], + "train_dir": [ + "/data/siggen/fm.sigmf-meta", + "/data/siggen/am.sigmf-meta", + ], + "val_dir": [ + "/data/siggen/fm.sigmf-meta", + "/data/siggen/am.sigmf-meta", + ], + "iq_epochs": 10, + "iq_train_limit": 0.5, + "iq_only_start_of_burst": False, + "iq_num_samples": 1024, + "spec_epochs": 0, + } +} + + +if __name__ == "__main__": + + train(experiments) diff --git a/rfml/wifi_experiments.py b/experiments/wifi_experiments.py similarity index 88% rename from rfml/wifi_experiments.py rename to experiments/wifi_experiments.py index 7fb99b3..2a5242c 100644 --- a/rfml/wifi_experiments.py +++ b/experiments/wifi_experiments.py @@ -1,9 +1,4 @@ -from pathlib import Path - from rfml.experiment import * -from rfml.train_iq import * -from rfml.train_spec import * - # Ensure that data directories have sigmf-meta files with annotations # Annotations can be generated using scripts in label_scripts directory or notebooks/Label_WiFi.ipynb and notebooks/Label_DJI.ipynb @@ -91,7 +86,6 @@ "spec_skip_export": True, # USE WITH CAUTION (but speeds up large directories significantly): skip after first run if using separate train/val directories "notes": "Wi-Fi vs anomalous Wi-Fi, validate on BladeRF TX & Ettus B200Mini RX, train on Ettus B200Mini RX/TX, anarkiwi collect", }, - "experiment_train_blade_2": { "class_list": ["wifi", "anom_wifi"], "train_dir": [ @@ -229,7 +223,7 @@ "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", ], "iq_epochs": 100, - "iq_learning_rate": 0.000001, + "iq_learning_rate": 0.001, # 0.000001, "spec_epochs": 0, "notes": "Wi-Fi vs anomalous Wi-Fi, train Blade 1, validate Blade 2", }, @@ -250,7 +244,7 @@ "train_dir": [ "/data/s3_gamutrf/gamutrf-nz-anon-wifi", "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", - "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf" + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf", ], "val_dir": [ "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", @@ -271,7 +265,7 @@ ], "val_dir": ["/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf"], "iq_epochs": 100, - "iq_learning_rate": 0.000001, + "iq_learning_rate": 0.001, # 0.000001, "spec_epochs": 0, "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", }, @@ -280,7 +274,7 @@ "train_dir": [ "/data/s3_gamutrf/gamutrf-nz-anon-wifi", "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", - "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf" + "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf", ], "iq_epochs": 100, "iq_learning_rate": 0.000001, @@ -297,7 +291,7 @@ "/data/s3_gamutrf/gamutrf-wifi-and-anom-bladerf", ], "iq_epochs": 150, - "iq_learning_rate": 0.0000001, + "iq_learning_rate": 0.001, # 0.0000001, "spec_epochs": 0, "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", }, @@ -311,7 +305,7 @@ "/data/s3_gamutrf/gamutrf-nz-nonanon-wifi", ], "iq_epochs": 150, - "iq_learning_rate": 0.000001, + "iq_learning_rate": 0.001, # 0.000001, "spec_epochs": 0, "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", }, @@ -411,7 +405,7 @@ "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", ], "iq_epochs": 100, - "iq_learning_rate": 0.000001, + "iq_learning_rate": 0.001, # 0.000001, "spec_epochs": 0, "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", }, @@ -425,8 +419,8 @@ "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/blade/", "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/blade/", ], - "iq_epochs": 150, - "iq_learning_rate": 0.000001, + "iq_epochs": 200, + "iq_learning_rate": 0.000001, # 0.000001, "spec_epochs": 0, "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", }, @@ -440,13 +434,12 @@ "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx/ettus/", "/data/s3_gamutrf/gamutrf-anom-wifi2/collect/wifi_tx_mod/ettus/", ], - "iq_epochs": 150, + "iq_epochs": 100, "iq_learning_rate": 0.000001, "spec_epochs": 0, "notes": "Wi-Fi vs anomalous Wi-Fi, train Ettus 1, validate Ettus 2", }, # ettus1, blade1, blade2 - } @@ -470,7 +463,6 @@ # "experiment_blade_2", # "experiment_ettus_1_to_2", # "experiment_ettus_2_to_1", - # "experiment_blade_1_to_2", # "experiment_blade_2_to_1", # "experiment_ettus_1_blade_1_to_blade_2", # "experiment_ettus_1_blade_2_to_blade_1", @@ -478,59 +470,14 @@ # "experiment_ettus_1_blade_2", # "experiment_ettus_2_blade_1_blade_2_to_ettus_1", # "experiment_ettus_1_blade_1_blade_2_to_ettus_2", - "experiment_ettus_1_ettus_2_blade_1_to_blade_2", # "experiment_ettus_1_ettus_2_blade_2_to_blade_1", # "experiment_ettus_1_to_blade_2", - "experiment_blade_2_to_ettus_2", + # "experiment_ettus_1_to_blade_1", + # "experiment_blade_1_to_ettus_1", + # "experiment_blade_1_to_2", "experiment_ettus_2_to_blade_2", - "experiment_ettus_1_to_blade_1", - "experiment_blade_1_to_ettus_1", - + "experiment_ettus_1_ettus_2_blade_1_to_blade_2", + # "experiment_blade_2_to_ettus_2", ] - for experiment_name in experiments_to_run: - print(f"Running {experiment_name}") - try: - exp = Experiment( - experiment_name=experiment_name, **experiments[experiment_name] - ) - - logs_timestamp = datetime.now().strftime("%m_%d_%Y_%H_%M_%S") - - if exp.iq_epochs > 0: - train_iq( - train_dataset_path=exp.train_dir, - val_dataset_path=exp.val_dir, - num_iq_samples=exp.iq_num_samples, - only_use_start_of_burst=exp.iq_only_start_of_burst, - epochs=exp.iq_epochs, - batch_size=exp.iq_batch_size, - class_list=exp.class_list, - output_dir=Path("experiment_logs", exp.experiment_name), - logs_dir=Path("iq_logs", logs_timestamp), - learning_rate=exp.iq_learning_rate, - experiment_name=exp.experiment_name, - ) - else: - print("Skipping IQ training") - - if exp.spec_epochs > 0: - train_spec( - train_dataset_path=exp.train_dir, - val_dataset_path=exp.val_dir, - n_fft=exp.spec_n_fft, - time_dim=exp.spec_time_dim, - epochs=exp.spec_epochs, - batch_size=exp.spec_batch_size, - class_list=exp.class_list, - yolo_augment=exp.spec_yolo_augment, - skip_export=exp.spec_skip_export, - force_yolo_label_larger=exp.spec_force_yolo_label_larger, - output_dir=Path("experiment_logs", exp.experiment_name), - logs_dir=Path("spec_logs", logs_timestamp), - ) - else: - print("Skipping spectrogram training") - - except Exception as error: - print(f"Error: {error}") + train({name: experiments[name] for name in experiments_to_run}) diff --git a/label_scripts/label-lucas-collect.py b/label_scripts/label-lucas-collect.py new file mode 100644 index 0000000..cefa6de --- /dev/null +++ b/label_scripts/label-lucas-collect.py @@ -0,0 +1,66 @@ +import glob + +from pathlib import Path +from tqdm import tqdm + +import rfml.annotation_utils as annotation_utils +import rfml.data as data_class + +data_globs = ["/data/s3_gamutrf/gamutrf-lucas-collect/mini2/*.zst"] + +for file_glob in data_globs: + for f in tqdm(glob.glob(str(Path(file_glob)))): + data_obj = data_class.Data(f) + annotation_utils.reset_annotations(data_obj) + annotation_utils.annotate( + data_obj, + avg_window_len=256, + debug=False, + bandwidth_estimation=0.99, # True, + overwrite=False, + # power_estimate_duration = 0.1, + # n_components=3, + # n_init=2, + # dry_run=True, + # time_start_stop=(1,None), + labels={ + "mini2_video": { + "bandwidth_limits": (16e6, None), + "annotation_length": (10000, None), + "annotation_seconds": (0.001, None), + # "set_bandwidth": (-8.5e6, 9.5e6) + }, + # "mini2_telem": { + # "bandwidth_limits": (None, 16e6), + # "annotation_length": (10000, None), + # "annotation_seconds": (None, 0.001), + # } + }, + ) + + +# data_globs = [ +# "/data/s3_gamutrf/gamutrf-lucas-collect/environment/*.zst" +# ] + + +# for file_glob in data_globs: +# for f in tqdm(glob.glob(str(Path(file_glob)))): +# data_obj = data_class.Data(f) +# annotation_utils.reset_annotations(data_obj) +# annotation_utils.annotate( +# data_obj, +# avg_window_len=1024, +# debug=False, +# bandwidth_estimation=0.99,#True, +# overwrite=False, +# # power_estimate_duration = 0.1, +# # n_components=3, +# # n_init=2, +# # dry_run=True, +# labels = { +# "environment": { +# "annotation_length": (2048, None), +# }, +# } +# ) diff --git a/label_scripts/label_bladerf_wifi.py b/label_scripts/label_bladerf_wifi.py index 2ff5626..fb78043 100644 --- a/label_scripts/label_bladerf_wifi.py +++ b/label_scripts/label_bladerf_wifi.py @@ -4,6 +4,7 @@ from tqdm import tqdm import rfml.annotation_utils as annotation_utils +import rfml.data as data_class s3_data = { "anom_wifi": [ @@ -21,5 +22,5 @@ for data_glob in s3_data[label]: for f in tqdm(glob.glob(str(Path(data_glob)))): annotation_utils.annotate( - f, label=label, avg_window_len=256, avg_duration=3, debug=False + data_class.Data(f), labels={label: {}}, avg_window_len=256, debug=False ) diff --git a/label_scripts/label_gamutrf_nz_wifi.py b/label_scripts/label_gamutrf_nz_wifi.py index b424e81..8bc1c1f 100644 --- a/label_scripts/label_gamutrf_nz_wifi.py +++ b/label_scripts/label_gamutrf_nz_wifi.py @@ -7,7 +7,8 @@ import rfml.data as data_class data_globs = [ - "/data/s3_gamutrf/gamutrf-nz-wifi/gamutrf_ax_gain10_2430000000Hz_20480000sps.raw.zst" + # "/data/s3_gamutrf/gamutrf-nz-wifi/gamutrf_ax_gain10_2430000000Hz_20480000sps.raw.zst", + "/data/s3_gamutrf/gamutrf-nz-wifi/*.zst" ] @@ -24,11 +25,11 @@ verbose=False, bandwidth_estimation=0.99, overwrite=False, - labels = { + labels={ "wifi": { - "bandwidth_limits": (10e6, None), - "annotation_length": (10000, None), - "annotation_seconds": (0.001, None), + "bandwidth_limits": (5e6, None), + # "annotation_length": (10000, None), + "annotation_seconds": (0.0005, None), } - } - ) \ No newline at end of file + }, + ) diff --git a/label_scripts/label_lab_collect.py b/label_scripts/label_lab_collect.py new file mode 100644 index 0000000..de1c0c5 --- /dev/null +++ b/label_scripts/label_lab_collect.py @@ -0,0 +1,42 @@ +import glob + +from pathlib import Path +from tqdm import tqdm + +import rfml.annotation_utils as annotation_utils +import rfml.data as data_class + + +mavic_globs = [ + "/data/s3_gamutrf/gamutrf-lab-collect/mavic-30db/*.sigmf-meta", + # "/data/s3_gamutrf/gamutrf-lab-collect/mavic-0db/*.sigmf-meta", + # "/data/s3_gamutrf/gamutrf-drone-detection/drone.sigmf-meta", +] + +for file_glob in mavic_globs: + for f in tqdm(glob.glob(str(Path(file_glob)))): + data_obj = data_class.Data(f) + annotation_utils.reset_annotations(data_obj) + annotation_utils.annotate( + data_obj, + avg_window_len=256, + power_estimate_duration=0.1, + # debug_duration=0.25, + # debug=True, + # verbose=True, + bandwidth_estimation=True, + overwrite=False, + labels={ + "mavic3_video": { + "bandwidth_limits": (8e6, None), + "annotation_length": (10000, None), + "annotation_seconds": (0.0001, 0.0025), + # "set_bandwidth": (-8.5e6, 9.5e6) + }, + "mavic3_telem": { + "bandwidth_limits": (None, 5e6), + # "annotation_length": (10000, None), + "annotation_seconds": (0.0003, 0.001), + }, + }, + ) diff --git a/label_scripts/label_mavic3_lab.py b/label_scripts/label_mavic3_lab.py index f1594c8..0578b71 100644 --- a/label_scripts/label_mavic3_lab.py +++ b/label_scripts/label_mavic3_lab.py @@ -31,23 +31,23 @@ # min_annotation_length=10000, # max_annotations=500, # dc_block=True, - # time_start_stop=(1,3.5), + # time_start_stop=(1,3.5), # necessary={ # "annotation_seconds": (0.001, -1) # }, - labels = { + labels={ "mavic3_video": { "bandwidth_limits": (16e6, None), "annotation_length": (10000, None), "annotation_seconds": (0.001, None), - "set_bandwidth": (-9e6, 9e6) + "set_bandwidth": (-9e6, 9e6), }, "mavic3_telem": { "bandwidth_limits": (None, 16e6), "annotation_length": (10000, None), "annotation_seconds": (None, 0.001), - } - } + }, + }, ) # annotation_utils.annotate( # data_obj, diff --git a/label_scripts/label_mini2_fieldday.py b/label_scripts/label_mini2_fieldday.py index b071f53..51784c1 100644 --- a/label_scripts/label_mini2_fieldday.py +++ b/label_scripts/label_mini2_fieldday.py @@ -10,8 +10,9 @@ data_globs = [ # "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings/dji-mini2-0to100m-0deg-5735mhz-lp-50-gain_20p5Msps_craft_flying-1.sigmf-meta" # "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings/dji-mini2-0to100m-0deg-5735mhz-lp-60-gain_20Msps_craft_flying-1.sigmf-meta" - "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings/*.sigmf-meta", + # "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings/*.sigmf-meta", # "/data/s3_gamutrf/gamutrf-birdseye-field-days/leesburg_field_day_2022_06_15/iq_recordings/*.sigmf-meta", + "/data/s3_gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/iq_recordings/dji-mini2-0to100m-0deg-5735mhz-lp-50-gain_20p5Msps_craft_flying-1.sigmf-meta" ] @@ -22,49 +23,24 @@ annotation_utils.reset_annotations(data_obj) annotation_utils.annotate( data_obj, - # label="mini2_video", avg_window_len=256, - avg_duration=0.25, debug=False, bandwidth_estimation=True, - # force_threshold_db=-60, overwrite=False, - # min_bandwidth=16e6, - # min_annotation_length=10000, - # max_annotations=500, - # dc_block=True, - # time_start_stop=(1,3.5), - # necessary={ - # "annotation_seconds": (0.001, -1) - # }, - labels = { + power_estimate_duration=0.1, + n_components=4, + n_init=2, + labels={ "mini2_video": { "bandwidth_limits": (16e6, None), "annotation_length": (10000, None), "annotation_seconds": (0.001, None), - "set_bandwidth": (-8.5e6, 9.5e6) + "set_bandwidth": (-8.5e6, 9.5e6), }, "mini2_telem": { "bandwidth_limits": (None, 16e6), "annotation_length": (10000, None), "annotation_seconds": (None, 0.001), - } - } + }, + }, ) - # annotation_utils.annotate( - # data_obj, - # label="mini2_telem", - # avg_window_len=256, - # avg_duration=0.25, - # debug=False, - # spectral_energy_threshold=True, - # # force_threshold_db=-58, - # overwrite=False, - # max_bandwidth=16e6, - # min_annotation_length=10000, - # # max_annotations=500, - # # dc_block=True, - # necessary={ - # "annotation_seconds": (0, 0.001) - # }, - # ) diff --git a/label_scripts/label_mini2_lab.py b/label_scripts/label_mini2_lab.py index b05ceb3..f901e43 100644 --- a/label_scripts/label_mini2_lab.py +++ b/label_scripts/label_mini2_lab.py @@ -30,23 +30,23 @@ # min_annotation_length=10000, # max_annotations=500, # dc_block=True, - # time_start_stop=(1,3.5), + # time_start_stop=(1,3.5), # necessary={ # "annotation_seconds": (0.001, -1) # }, - labels = { + labels={ "mini2_video": { "bandwidth_limits": (16e6, None), "annotation_length": (10000, None), "annotation_seconds": (0.001, None), - "set_bandwidth": (-9e6, 9e6) + "set_bandwidth": (-9e6, 9e6), }, "mini2_telem": { "bandwidth_limits": (None, 16e6), "annotation_length": (10000, None), "annotation_seconds": (None, 0.001), - } - } + }, + }, ) # annotation_utils.annotate( # data_obj, diff --git a/notebooks/manual_to_sigmf.ipynb b/notebooks/manual_to_sigmf.ipynb new file mode 100644 index 0000000..ab04aff --- /dev/null +++ b/notebooks/manual_to_sigmf.ipynb @@ -0,0 +1,104 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "import rfml.data\n", + "import copy\n", + "import glob\n", + "import json\n", + "from pathlib import Path\n", + "from datetime import datetime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saving /data/rfml-dev/rfml-dev/data/gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/testing/dji-mini2-0to100m-0deg-5735mhz-lp-45-gain_20p5Msps_craft_flying-1.sigmf-meta\n", + "\n" + ] + } + ], + "source": [ + "\n", + "def manual_to_sigmf(file, datatype, sample_rate, frequency, iso_date_string):\n", + " # change to .sigmf-data\n", + " if file.suffix in [\".raw\"]:\n", + " file = file.rename(file.with_suffix(\".sigmf-data\"))\n", + " else: \n", + " raise NotImplementedError\n", + " \n", + " sigmf_meta = copy.deepcopy(rfml.data.SIGMF_META_DEFAULT)\n", + " sigmf_meta[\"global\"][\"core:dataset\"] = str(file)\n", + " sigmf_meta[\"global\"][\"core:datatype\"] = datatype\n", + " sigmf_meta[\"global\"][\"core:sample_rate\"] = sample_rate\n", + " sigmf_meta[\"captures\"][0][\"core:frequency\"] = frequency\n", + " sigmf_meta[\"captures\"][0][\"core:datetime\"] = (\n", + " datetime.fromisoformat(iso_date_string)\n", + " .isoformat(timespec=\"milliseconds\")\n", + " .replace(\"+00:00\", \"Z\")\n", + " )\n", + "\n", + " with open(file.with_suffix(\".sigmf-meta\"), \"w\") as outfile:\n", + " print(f\"Saving {file.with_suffix('.sigmf-meta')}\\n\")\n", + " outfile.write(json.dumps(sigmf_meta, indent=4))\n", + "\n", + "data_globs = [\n", + " (\n", + " \"/data/rfml-dev/rfml-dev/data/gamutrf/gamutrf-birdseye-field-days/pdx_field_day_2022_05_26/testing/*.raw\",\n", + " {\n", + " \"datatype\": \"cf32_le\",\n", + " \"sample_rate\": 20500000,\n", + " \"frequency\": 5735500000,\n", + " \"iso_date_string\": \"2022-05-26\",\n", + " }\n", + " )\n", + "]\n", + "for file_glob, metadata in data_globs:\n", + " files = glob.glob(str(Path(file_glob)))\n", + " for f in files:\n", + " f = Path(f)\n", + " manual_to_sigmf(f, metadata[\"datatype\"], metadata[\"sample_rate\"], metadata[\"frequency\"], metadata[\"iso_date_string\"])\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "rfml", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/signal_power.ipynb b/notebooks/signal_power.ipynb new file mode 100644 index 0000000..0ad9f32 --- /dev/null +++ b/notebooks/signal_power.ipynb @@ -0,0 +1,2637 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import glob\n", + "import seaborn as sns\n", + "from pathlib import Path\n", + "from tqdm import tqdm\n", + "import numpy as np \n", + "import matplotlib.pyplot as plt\n", + "import scipy.fft\n", + "import scipy.signal\n", + "import rfml.data as rfml_data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/5 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Method 1, scipy.spectrogram(scaling=spectrum, mode=psd)\n", + "\n", + "Avg power spectrogram = -16.407647132873535\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaUAAAGzCAYAAACPXQgoAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC+YklEQVR4nOy9ebxcVZW3/+x9zqmqO+ZmDoEQQkARZHpBMSqCgIyKKC2v7QQ2CtoBm4Z2oJtGBF7zCrZNO7++bwsO0D8bu9WWViYjoIITigMIRgiDQAYy3dyhqs45e/3+2PucOqfuDQm5SQXL/Xw+ldRwhl117z2r1lrftZYSEcHj8Xg8nucBelcvwOPxeDyeDG+UPB6Px/O8wRslj8fj8Txv8EbJ4/F4PM8bvFHyeDwez/MGb5Q8Ho/H87zBGyWPx+PxPG/wRsnj8Xg8zxu8UfJ4PB7P84Y/GaN03XXXoZTi5z//+U4/19FHH83RRx+908/zfOCss85ir732Kj2nlOKyyy7bJevxeAD22msvzjrrrF29jD8ZOnnNuuOOO1BKcccdd+yU4z8no5QZBqUUP/zhDye8LiIsWLAApRSvfe1rt2tBn/3sZ7nuuuu2a99OMjY2xmc+8xmOP/54dtttNwYGBjj00EP53Oc+R5qmu3p5nl3ARz/6Ub75zW/u6mV4PDz11FNcdtll3Hfffbt6Kc+Z7fKUarUaN9xww4Tn77zzTv74xz9SrVa3e0F/KkbpkUce4fzzz0dEuPDCC/n4xz/OokWL+Ou//mv+6q/+alcvb0qMj49zySWX7Opl/MnhjZJnV3Hrrbdy66235o+feuopPvKRj/z5GKWTTz6ZG2+8kSRJSs/fcMMNHHbYYcybN2+HLO75zLx58/jNb37Dbbfdxvvf/37OPfdc/vM//5N3vvOdfPnLX+YPf/jDrl7idlOr1QjDcFcvo6sZHR3d1UvwdBGVSoVKpbKrl7FD2C6j9Jd/+ZesW7eO2267LX+u2Wzy9a9/nbe85S2T7mOM4ZprruGAAw6gVqsxd+5czj33XDZs2JBvs9dee3H//fdz55135mHC9jhpo9HgwgsvZPbs2fT19fGGN7yBtWvXTjjfZz/7WQ444ACq1Srz589n6dKlbNy4ccJ2X/jCF1i8eDE9PT289KUv5Qc/+ME2fQazZs3igAMOmPD8G97wBgB+97vfbdNx2lmxYgWnn3468+bNo1arsccee/DmN7+ZTZs2lbb76le/yktf+lJ6e3uZPn06r3rVq0rflL71rW9xyimnMH/+fKrVKosXL+aKK67YptBie07psssuQynFH/7wB8466yyGhoaYNm0a73znOxkbGyvtOz4+zvve9z5mzZrFwMAAp556Kk8++eQOy1OtWrWKd77zneyxxx5Uq1V22203Xv/61/Poo4/m2+y111689rWv5dZbb+WQQw6hVqux//7785//+Z8Tjrdx40YuuOACFixYQLVaZZ999uFjH/sYxpjSdsYY/uVf/oUDDzyQWq3G7NmzOfHEE/Mcp1KK0dFRvvSlL+W/u1lOJPv8HnjgAd7ylrcwffp0XvnKVwJbzgW05/oeffRRlFJ8/OMf5zOf+Qx77703vb29HH/88TzxxBOICFdccQV77LEHPT09vP71r2f9+vXb9RlnOYN///d/5yMf+Qi77747AwMD/MVf/AWbNm2i0WhwwQUXMGfOHPr7+3nnO99Jo9EoHSNJEq644goWL15MtVplr7324u///u8nbCciXHnlleyxxx709vby6le/mvvvv3/SdW3rz2pXvedrr72WY445hjlz5lCtVtl///353Oc+N+Fcxhguu+wy5s+fn7/nBx54YEIeLUuX/OhHP9rqNa/4e3THHXfwkpe8BIB3vvOd+e9jFoHaUr5ust/FP/7xj5x22mn09fUxZ84c/vZv/3bC+874yU9+woknnsi0adPo7e3lqKOO4kc/+tGzfOqTs11fh/faay+WLFnCv/3bv3HSSScB8N3vfpdNmzbx5je/mU9+8pMT9jn33HO57rrreOc738n73vc+Vq5cyac//Wl++ctf8qMf/Ygoirjmmms4//zz6e/v5x/+4R8AmDt3buk4559/PtOnT+fDH/4wjz76KNdccw3nnXceX/va1/JtLrvsMj7ykY9w3HHH8d73vpeHHnqIz33uc/zsZz/LzwXwr//6r5x77rm8/OUv54ILLuCRRx7h1FNPZcaMGSxYsGB7PhpWrVoFWKP1XGk2m5xwwgk0Gg3OP/985s2bx5NPPslNN93Exo0bmTZtGgAf+chHuOyyy3j5y1/O5ZdfTqVS4Sc/+QnLly/n+OOPB+wvdH9/PxdeeCH9/f0sX76cSy+9lOHhYa6++urtem9nnHEGixYtYtmyZfziF7/g//2//8ecOXP42Mc+lm9z1lln8e///u+8/e1v52Uvexl33nknp5xyyoRjxXE8wdBuiRkzZqC1/f50+umnc//993P++eez1157sWbNGm677TYef/zx0kV8xYoV/M//+T95z3vew5lnnsm1117Lm970Jm6++WZe85rXADYveNRRR/Hkk09y7rnnsueee3L33Xdz8cUX8/TTT3PNNdfkxzv77LO57rrrOOmkk3jXu95FkiT84Ac/4Mc//jGHH344X/nKV3jXu97FS1/6Us455xwAFi9eXHofb3rTm9h333356Ec/yvZOjLn++utpNpucf/75rF+/nquuuoozzjiDY445hjvuuIMPfvCD/OEPf+BTn/oUf/d3f8cXv/jF7ToPwLJly+jp6eFDH/pQfswoitBas2HDBi677DJ+/OMfc91117Fo0SIuvfTSfN93vetdfOlLX+Iv/uIvuOiii/jJT37CsmXL+N3vfsc3vvGNfLtLL72UK6+8kpNPPpmTTz6ZX/ziFxx//PE0m83SWp7Lz2rDhg3b9OWrt7eX3t7eHfaeP/e5z3HAAQdw6qmnEoYh3/72t/nrv/5rjDEsXbo03+7iiy/mqquu4nWvex0nnHACv/rVrzjhhBOo1+uTrnNbrnlFXvSiF3H55Zdz6aWXcs4553DkkUcC8PKXv3yrn0mR8fFxjj32WB5//HHe9773MX/+fL7yla+wfPnyCdsuX76ck046icMOO4wPf/jDaK1zI/2DH/yAl770pdt+YnkOXHvttQLIz372M/n0pz8tAwMDMjY2JiIib3rTm+TVr361iIgsXLhQTjnllHy/H/zgBwLI9ddfXzrezTffPOH5Aw44QI466qgtnvu4444TY0z+/N/+7d9KEASyceNGERFZs2aNVCoVOf744yVN03y7T3/60wLIF7/4RRERaTabMmfOHDnkkEOk0Wjk233hC18QYNI1bI1GoyH777+/LFq0SOI4fs77//KXvxRAbrzxxi1us2LFCtFayxve8IbS+xOR0ueS/VyKnHvuudLb2yv1ej1/7swzz5SFCxeWtgPkwx/+cP74wx/+sADyV3/1V6Xt3vCGN8jMmTPzx/fee68AcsEFF5S2O+ussyYc8/vf/74A23RbuXKliIhs2LBBALn66qu3+PmI2N8/QP7jP/4jf27Tpk2y2267yaGHHpo/d8UVV0hfX5/8/ve/L+3/oQ99SIIgkMcff1xERJYvXy6AvO9975twruJn3tfXJ2eeeeaEbbLP7y//8i8nvHbUUUdN+rvW/nNZuXKlADJ79uz8d11E5OKLLxZADj744NLv3F/+5V9KpVIp/ay3lexn8+IXv1iazWbpmEopOemkk0rbL1mypLTW++67TwB517veVdru7/7u7wSQ5cuXi0jrb/WUU04pfY5///d/L0Dps9zWn5VI6+e/tdtkv4/b+55FJv+bO+GEE2TvvffOH69atUrCMJTTTjuttN1ll1024T1v6zVPZOLv0c9+9jMB5Nprr52wpoULF076e9p+jGuuuUYA+fd///f8udHRUdlnn30EkO9///siYv8G9t13XznhhBMmXIMWLVokr3nNayac69nYbkn4GWecwfj4ODfddBObN2/mpptu2mLo7sYbb2TatGm85jWv4Zlnnslvhx12GP39/Xz/+9/f5vOec845KKXyx0ceeSRpmvLYY48BcPvtt9NsNrngggvyb9cA7373uxkcHOS///u/Afj5z3/OmjVreM973lOKxZ511lm5R/JcOe+883jggQf49Kc/vV05mey8t9xyy4SwWMY3v/lNjDFceumlpfcHlD6Xnp6e/P7mzZt55plnOPLIIxkbG+PBBx98zmsDeM973lN6fOSRR7Ju3TqGh4cBuPnmmwH467/+69J2559//oRjHXzwwdx2223bdMtylD09PVQqFe64445S2Hcy5s+fn4dSAQYHB3nHO97BL3/5y9ybvfHGGznyyCOZPn166ffyuOOOI01T7rrrLgD+4z/+A6UUH/7whyecp/iZb432z297eNOb3lT6/TziiCMAeNvb3lb6nTviiCNoNps8+eST232ud7zjHXlUITumiEwQ8hxxxBE88cQTeY75O9/5DgAXXnhhabuLLroIIP8bzP5Wzz///NLneMEFF0xYy7b+rMB6k9vye/WOd7xjh71nKP/Nbdq0iWeeeYajjjqKRx55JI8KfO973yNJkm36G8nY2jVvZ/Gd73yH3Xbbjb/4i7/In+vt7c0jARn33XcfK1as4C1veQvr1q3Lfzajo6Mce+yx3HXXXc8pxLrd2ezZs2dz3HHHccMNNzA2NkaapqXFF1mxYgWbNm1izpw5k76+Zs2abT7vnnvuWXo8ffp0gPwilf2gXvjCF5a2q1Qq7L333vnr2f/77rtvabsoith77723eT0ZV199Nf/3//5frrjiCk4++eTnvD/AokWLuPDCC/nEJz7B9ddfz5FHHsmpp57K2972tvxC9PDDD6O1Zv/993/WY91///1ccsklLF++PDcaGdsaNmvn2T77wcFBHnvsMbTWLFq0qLTdPvvsM+FY06dP57jjjntO569Wq3zsYx/joosuYu7cubzsZS/jta99Le94xzsmiGv22WefCQbjBS94AWDzM/PmzWPFihX8+te/Zvbs2ZOeL/u9fPjhh5k/fz4zZsx4Tuttp/1z2R7afwbZ70V7uDl7fmvGe0edyxjDpk2bmDlzZv570P5znzdvHkNDQ1v9G5w9e3b+u5WxrT8rgFe84hXb+hYnsL3vGeBHP/oRH/7wh7nnnnsmfKnctGkT06ZNy99z+2czY8aMCe95S2tqv+btLB577LFJ/47ar60rVqwA4Mwzz9zisTZt2rTF99fOlCRWb3nLW3j3u9/NqlWrOOmkkxgaGpp0O2MMc+bM4frrr5/09S39ok1GEASTPi+7cKr7ddddxwc/+EHe8573TFlK/U//9E+cddZZfOtb3+LWW2/lfe97H8uWLePHP/4xe+yxxzYdY+PGjRx11FEMDg5y+eWXs3jxYmq1Gr/4xS/44Ac/uN2J4R352TebzW1OxM+ePTs/9wUXXMDrXvc6vvnNb3LLLbfwj//4jyxbtozly5dz6KGHPqc1GGN4zWtewwc+8IFJX8+M2I6i+E06Qyk16ee3pZzIln4GO+PvYqrnei5e5NZ4Lj+rtWvXblNOqb+/n/7+/tJz2/ueH374YY499lj2228/PvGJT7BgwQIqlQrf+c53+Od//ucpiTF29M92Sz+XNE23eK5nI3tvV199NYcccsik27R/zs/GlIzSG97wBs4991x+/OMfbzHpBjbhe/vtt/OKV7xi0j/MIlP9RV64cCEADz30UMnjaTabrFy5Mv92nm23YsUKjjnmmHy7OI5ZuXIlBx988Dad71vf+hbvete7eOMb38hnPvOZKa0948ADD+TAAw/kkksu4e677+YVr3gFn//857nyyitZvHgxxhgeeOCBLf4C3HHHHaxbt47//M//5FWvelX+/MqVK3fI+rbEwoULMcawcuXK0rffyeTxd999N69+9au36bgrV64siRgWL17MRRddxEUXXcSKFSs45JBD+Kd/+ie++tWvls4pIqXfp9///vcA+bEWL17MyMjIVj22xYsXc8stt7B+/fpn9Za253d3+vTpPPLIIxOe39mhmZ1J9nuwYsUKXvSiF+XPr169mo0bN+Z/e8W/weLf6tq1ayd4Adv6swJ4yUtesk2f34c//OEd1rnk29/+No1Gg//6r/8qeTbtqYnsPf/hD38oec7r1q3boZ7Ps/0uTp8+fVIl8mOPPVb6OSxcuJDf/va3E/6OHnroodJ+maBncHDwOUc/JmNKbYb6+/v53Oc+x2WXXcbrXve6LW53xhlnkKYpV1xxxYTXkiQpfUB9fX2TfmDbynHHHUelUuGTn/xk6ZvEv/7rv7Jp06ZcCXb44Ycze/ZsPv/5z5eUPtddd902n/+uu+7izW9+M6961au4/vrrJ+R4nivDw8MTar8OPPBAtNa5DPO0005Da83ll18+4dtX9n6zbzvF999sNvnsZz87pfVtjRNOOAFgwnk+9alPTdh2e3JKY2NjExRKixcvZmBgYIJM9amnniqpvIaHh/nyl7/MIYcckh/vjDPO4J577uGWW26ZsL6NGzfmP4vTTz8dEeEjH/nIhO2Kn/H2/O4uXryYBx98sCTx/dWvfrVdUtrnC1n4uqiIA/jEJz4BkP8NHnfccURRxKc+9anS59i+H2z7zwqmllPaXib7m9u0aRPXXnttabtjjz2WMAwnSMU//elP77C1gP1dBCb9fVy8eDE//vGPS9e9m266iSeeeKK03cknn8xTTz3F17/+9fy5sbExvvCFL5S2O+yww1i8eDEf//jHGRkZmXC+yUp2no0pV0g+Wxwx46ijjuLcc89l2bJl3HfffRx//PFEUcSKFSu48cYb+Zd/+Zc8H3XYYYfxuc99jiuvvJJ99tmHOXPmlDyZrTF79mwuvvhiPvKRj3DiiSdy6qmn8tBDD/HZz36Wl7zkJbztbW8DbO7oyiuv5Nxzz+WYY47hf/7P/8nKlSu59tprtymn9Nhjj3HqqaeilOIv/uIvuPHGG0uvH3TQQRx00EH54+zbebGepp3ly5dz3nnn8aY3vYkXvOAFJEnCV77yFYIg4PTTTwdsLPof/uEfuOKKKzjyyCN54xvfSLVa5Wc/+xnz589n2bJlvPzlL2f69OmceeaZvO9970MpxVe+8pWdHuI87LDDOP3007nmmmtYt25dLgnPPJTit63tySn9/ve/59hjj+WMM85g//33JwxDvvGNb7B69Wre/OY3l7Z9wQtewNlnn83PfvYz5s6dyxe/+EVWr15duki8//3v57/+67947Wtfy1lnncVhhx3G6Ogov/nNb/j617/Oo48+yqxZs3j1q1/N29/+dj75yU+yYsUKTjzxRIwx/OAHP+DVr3415513Xv7+b7/9dj7xiU8wf/58Fi1alAsRtsRf/dVf8YlPfIITTjiBs88+mzVr1vD5z3+eAw44YEIucKpkJRnXXnvtTu0rd/DBB3PmmWfyhS98IQ8l//SnP+VLX/oSp512Wu4hz549m7/7u79j2bJlvPa1r+Xkk0/ml7/8Jd/97ncnlFRs688KppZT2l6OP/54KpUKr3vd6zj33HMZGRnh//7f/8ucOXN4+umn8+3mzp3L3/zN3/BP//RPnHrqqZx44on86le/yt/zjgp5Ll68mKGhIT7/+c8zMDBAX18fRxxxBIsWLeJd73oXX//61znxxBM544wzePjhh/nqV786oYTh3e9+N5/+9Kd5xzvewb333stuu+3GV77ylQkyeq01/+///T9OOukkDjjgAN75zney++678+STT/L973+fwcFBvv3tb2/74p+LVK8oCX822iXhGV/4whfksMMOk56eHhkYGJADDzxQPvCBD8hTTz2Vb7Nq1So55ZRTZGBgoCTN3tK5MylnJk/M+PSnPy377befRFEkc+fOlfe+972yYcOGCWv67Gc/K4sWLZJqtSqHH3643HXXXVuU6U523i3dinJTEZFZs2bJy172smc95iOPPCJ/9Vd/JYsXL5ZarSYzZsyQV7/61XL77bdP2PaLX/yiHHrooVKtVmX69Oly1FFHyW233Za//qMf/Uhe9rKXSU9Pj8yfP18+8IEPyC233DLhs3oukvC1a9eWtst+JplkW8RKRpcuXSozZsyQ/v5+Oe200+Shhx4SQP73//7fz/r+t8YzzzwjS5culf3220/6+vpk2rRpcsQRR5QkqyKt379bbrlFDjroIKlWq7LffvtNKrXfvHmzXHzxxbLPPvtIpVKRWbNmyctf/nL5+Mc/XpIGJ0kiV199tey3335SqVRk9uzZctJJJ8m9996bb/Pggw/Kq171Kunp6SnJe7f0+WV89atflb333lsqlYoccsghcsstt2xREt4uh89+D9vf22R/L5/61KcEkJtvvvlZP+fncswt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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Method 2, scipy.spectrogram(scaling=spectrum, mode=magnitude)\n", + "\n", + "Avg power spectrogram = -16.40764832496643\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZEAAAGzCAYAAADuRSpqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC8WElEQVR4nOy9e7wcZX34/36emdndc83JPQRCgHCTi4SCQlAEhHJXsCDFK1QUpEFLwQtURAQKVay1qHj5toJW6E+tVVuqQDAKVlAURQUEAwRBIPfLSc45uzszz+f3x/PM7MyekxByyIFtnvfrtcnu7DPPPGfPnuczn7sSEcHj8Xg8nq1Av9QL8Hg8Hk/n4oWIx+PxeLYaL0Q8Ho/Hs9V4IeLxeDyercYLEY/H4/FsNV6IeDwej2er8ULE4/F4PFuNFyIej8fj2Wq8EPF4PB7PVtMxQuSmm25CKcUvf/nLbX6tI488kiOPPHKbX+flwNlnn80uu+xSOqaU4oorrnhJ1uPxAOyyyy6cffbZL/UyXvY8+eSTKKW46aabXrI1vCAhkm3kSin+93//d9T7IsKcOXNQSnHyySdv1YJuuOGGl/QDeSFcc801HHrooUyfPp1arcYee+zBhRdeyMqVK1/qpXleAq655hq++93vvtTL8HgmlHBrTqrVatxyyy289rWvLR2/6667+NOf/kS1Wt3qBd1www1MmzatI+5C7r//fubPn8+ZZ55JX18fv//97/l//+//8T//8z888MAD9PT0vNRL3CpGRkYIw636amzXXHPNNZx++umceuqpL/VSPJ4JY6t2ihNPPJFvfetbXH/99aXN5pZbbuGggw5i1apVL9oCX858+9vfHnVswYIFnH766fz3f/83Z5555kuwqvFTq9Ve6iX8n2doaKhjbzI8niJb5RN5y1vewurVq1m0aFF+rNls8h//8R+89a1vHfMcYwyf+cxn2HfffanVasycOZPzzjuPtWvX5mN22WUXHnroIe66667cbNbum2g0Glx00UVMnz6dnp4e3vSmN41pPrrhhhvYd999qVarzJ49m4ULF7Ju3bpR47785S8zb948urq6ePWrX81PfvKTrflISj8DMOa1toQlS5Zw2mmnMWvWLGq1GjvttBNnnnkm69evL437+te/zqtf/Wq6u7uZPHkyr3vd67jjjjvy97/3ve9x0kknMXv2bKrVKvPmzeOqq64iTdPnXUO7T+SKK65AKcVjjz3G2WefzcDAAJMmTeKv/uqvGB4eLp07MjLC+9//fqZNm0ZfXx9vfOMbeeaZZ140P8uyZcv4q7/6K3baaSeq1So77LADp5xyCk8++WQ+ZpddduHkk0/mjjvuYP78+dRqNfbZZx/+8z//c9R869at48ILL2TOnDlUq1V23313PvGJT2CMKY0zxvDP//zP7L///tRqNaZPn87xxx+f++iUUgwNDfHVr341/+5m2nT2+T388MO89a1vZfLkybkWvyn/W7uvKrN9f+pTn+Lzn/88u+22G93d3Rx77LE8/fTTiAhXXXUVO+20E11dXZxyyimsWbNmqz7jH//4xyil+OY3v8nHP/5xdtxxR/r6+jj99NNZv349jUaDCy+8kBkzZtDb28tf/dVf0Wg0SnMkScJVV13FvHnzqFar7LLLLvzd3/3dqHEiwtVXX81OO+1Ed3c3Rx11FA899NCY69rS39XWUq/XueKKK9hzzz2p1WrssMMO/MVf/AWPP/54PmZoaIiLL744X8Nee+3Fpz71KdqLoSuluOCCC/jWt77FPvvsQ1dXFwsWLOB3v/sdAF/60pfYfffdqdVqHHnkkaXvL9jvxX777cf999/PYYcdRldXF7vuuitf/OIXt+hneeSRRzj99NOZMmUKtVqNgw8+mP/6r//K31+xYgXTp0/nyCOPLK39scceo6enh7/8y7/c4s9tqzSRXXbZhQULFvDv//7vnHDCCQD84Ac/YP369Zx55plcf/31o84577zzuOmmm/irv/or3v/+97N06VI+97nP8etf/5qf/vSnRFHEZz7zGd73vvfR29vLRz7yEQBmzpxZmud973sfkydP5mMf+xhPPvkkn/nMZ7jgggv4xje+kY+54oor+PjHP84xxxzD+eefz6OPPsoXvvAFfvGLX+TXAvjXf/1XzjvvPA477DAuvPBCnnjiCd74xjcyZcoU5syZs0WfhYiwevVqkiRhyZIlXHLJJQRBsFWO+WazyXHHHUej0eB973sfs2bN4plnnuHWW29l3bp1TJo0CYCPf/zjXHHFFRx22GFceeWVVCoVfv7zn7N48WKOPfZYwPqvent7ueiii+jt7WXx4sVcfvnlDA4Oct11173gtQGcccYZ7Lrrrlx77bX86le/4l/+5V+YMWMGn/jEJ/IxZ599Nt/85jd5xzvewaGHHspdd93FSSedNGquOI5HCcZNMWXKFLS29zunnXYaDz30EO973/vYZZddWLFiBYsWLeKpp54qbbpLlizhL//yL3nve9/LWWedxY033sib3/xmbrvtNv78z/8cgOHhYY444gieeeYZzjvvPHbeeWfuueceLr30Up577jk+85nP5POdc8453HTTTZxwwgm8+93vJkkSfvKTn/Czn/2Mgw8+mH/7t3/j3e9+N69+9as599xzAZg3b17p53jzm9/MHnvswTXXXDNq09lSbr75ZprNJu973/tYs2YNn/zkJznjjDN4/etfz49//GM+/OEP89hjj/HZz36WD3zgA3zlK1/ZqusAXHvttXR1dXHJJZfkc0ZRhNaatWvXcsUVV/Czn/2Mm266iV133ZXLL788P/fd7343X/3qVzn99NO5+OKL+fnPf861117L73//e77zne/k4y6//HKuvvpqTjzxRE488UR+9atfceyxx9JsNktreSG/q7Vr127RzVJ3dzfd3d0ApGnKySefzA9/+EPOPPNM/uZv/oYNGzawaNEiHnzwQebNm4eI8MY3vpEf/ehHnHPOOcyfP5/bb7+dD37wgzzzzDP80z/9U2n+n/zkJ/zXf/0XCxcuzD/Pk08+mQ996EPccMMN/PVf/zVr167lk5/8JO9617tYvHhx6fy1a9dy4okncsYZZ/CWt7yFb37zm5x//vlUKhXe9a53bfLneuihh3jNa17DjjvuyCWXXEJPTw/f/OY3OfXUU/n2t7/Nm970JmbMmMEXvvAF3vzmN/PZz36W97///RhjOPvss+nr6+OGG2543s8vR14AN954owDyi1/8Qj73uc9JX1+fDA8Pi4jIm9/8ZjnqqKNERGTu3Lly0kkn5ef95Cc/EUBuvvnm0ny33XbbqOP77ruvHHHEEZu89jHHHCPGmPz43/7t30oQBLJu3ToREVmxYoVUKhU59thjJU3TfNznPvc5AeQrX/mKiIg0m02ZMWOGzJ8/XxqNRj7uy1/+sgBjrmEsnnvuOQHyx0477STf+MY3tujcdn79618LIN/61rc2OWbJkiWitZY3velNpZ9PREqfS/Z7KXLeeedJd3e31Ov1/NhZZ50lc+fOLY0D5GMf+1j++mMf+5gA8q53vas07k1vepNMnTo1f33//fcLIBdeeGFp3Nlnnz1qzh/96Eelz21zj6VLl4qIyNq1awWQ6667bpOfj4j9/gHy7W9/Oz+2fv162WGHHeTAAw/Mj1111VXS09Mjf/jDH0rnX3LJJRIEgTz11FMiIrJ48WIB5P3vf/+oaxU/856eHjnrrLNGjck+v7e85S2j3jviiCPG/K61/16WLl0qgEyfPj3/rouIXHrppQLIAQccIHEc58ff8pa3SKVSKf2ut5Tsd7PffvtJs9kszamUkhNOOKE0fsGCBaW1PvDAAwLIu9/97tK4D3zgAwLI4sWLRaT1t3rSSSeVPse/+7u/E6D0WW7p70qk9ft/vkfx+/iVr3xFAPn0pz896vPI1vbd735XALn66qtL759++umilJLHHnssPwZItVrNv7siIl/60pcEkFmzZsng4GB+PPsdFsceccQRAsg//uM/5scajYbMnz9fZsyYkf9esu/FjTfemI87+uijZf/99y/97o0xcthhh8kee+xRWvtb3vIW6e7ulj/84Q9y3XXXCSDf/e53R30Gm2OrQ3zPOOMMRkZGuPXWW9mwYQO33nrrJk1Z3/rWt5g0aRJ//ud/zqpVq/LHQQcdRG9vLz/60Y+2+LrnnnsuSqn89eGHH06apvzxj38E4M4776TZbHLhhRfmd68A73nPe+jv7+d//ud/APjlL3/JihUreO9730ulUsnHnX322fkd/5YwZcoUFi1axH//939z5ZVXMm3aNDZu3LjF5xfJrnv77bePMhNlfPe738UYw+WXX176+YDS59LV1ZU/37BhA6tWreLwww9neHiYRx55ZKvW9973vrf0+vDDD2f16tUMDg4CcNtttwHw13/916Vx73vf+0bNdcABB7Bo0aItesyaNSv/mSqVCj/+8Y9LZtCxmD17Nm9605vy1/39/bzzne/k17/+NcuWLQPs9/Lwww9n8uTJpe/lMcccQ5qm3H333YD1fSml+NjHPjbqOsXP/Plo//y2hje/+c2l7+chhxwCwNvf/vaSf/KQQw6h2WzyzDPPbPW13vnOd+ZaezaniIy6Cz7kkEN4+umnSZIEgO9///sAXHTRRaVxF198MUD+N5j9rb7vfe8rfY4XXnjhqLVs6e8KrLa2Jd+rd77znfk53/72t5k2bdqY39Vsbd///vcJgoD3v//9o34uEeEHP/hB6fjRRx9d0o6z39Vpp51GX1/fqONPPPFE6fwwDDnvvPPy15VKhfPOO48VK1Zw//33j1onwJo1a1i8eDFnnHFG/ne/atUqVq9ezXHHHceSJUtK34nPfe5zTJo0idNPP52PfvSjvOMd7+CUU04Zc+5NsdUhONOnT+eYY47hlltuYXh4mDRNOf3008ccu2TJEtavX8+MGTPGfH/FihVbfN2dd9659Hry5MkA+aaSCZO99tqrNK5SqbDbbrvl72f/77HHHqVxURSx2267bfF6KpUKxxxzDAAnn3wyRx99NK95zWuYMWPGCw5z3nXXXbnooov49Kc/zc0338zhhx/OG9/4Rt7+9rfnG8fjjz+O1pp99tlns3M99NBDXHbZZSxevDjf5DO21IzUzuY++/7+fv74xz+itWbXXXctjdt9991HzTV58uT8c9tSqtUqn/jEJ7j44ouZOXMmhx56KCeffDLvfOc7c0FTvGb7Br/nnnsC1r8wa9YslixZwm9/+1umT58+5vWy7+Xjjz/O7NmzmTJlygtabzvtn8vW0P47yL4X7ebX7PjzCdsX61rGGNavX8/UqVPz70H7733WrFkMDAw879/g9OnT8+9Wxpb+rgBe85rXbOmPmPP444+z1157bTYq8Y9//COzZ88uCQCAV7ziFfn7Rcb7u5o9e/ao4Ivid/jQQw8dtcbHHnsMEeGjH/0oH/3oR8f8OVasWMGOO+4I2Jvg66+/nje/+c3MnDlzTFfE8zGuOM63vvWtvOc972HZsmWccMIJDAwMjDnOGMOMGTO4+eabx3x/U1+MsQiCYMzj8jLp8nvYYYexww47cPPNN29Vrsw//uM/cvbZZ/O9732PO+64g/e///1ce+21/OxnP2OnnXbaojnWrVvHEUccQX9/P1deeSXz5s2jVqvxq1/9ig9/+MNb7Yh8MT/7ZrO5xY7f6dOn59e+8MILecMb3sB3v/tdbr/9dj760Y9y7bXXsnjxYg488MAXtAZjDH/+53/Ohz70oTHfz/5gXyyK2mGGUmrMz29TNv1N/Q62xd/FeK/1QrS05+OF/K5Wrly5RT6R3t5eent7X7Q1tjORv6uM7G/7Ax/4AMcdd9yYY9qF++233w5YIfanP/1pk/v4phiXEHnTm97Eeeedx89+9rOSY7udefPmceedd/Ka17xmzD+kIuP94s2dOxeARx99tKRRNJtNli5dmt/9ZuOWLFnC61//+nxcHMcsXbqUAw44YKvXUK/Xt/puH2D//fdn//3357LLLuOee+7hNa95DV/84he5+uqrmTdvHsYYHn74YebPnz/m+T/+8Y9ZvXo1//mf/8nrXve6/PjSpUu3ek1bwty5czHGsHTp0tLd5WOPPTZq7D333MNRRx21RfMuXbq0ZBaYN28eF198MRdffDFLlixh/vz5/OM//iNf//rXS9cUkdL36Q9/+APQiqCbN28eGzdufF6NaN68edx+++2sWbNms9rI1nx3J0+ePMqMAaPvajuJ7HuwZMmS/C4dYPny5axbty7/2yv+DRb/VleuXDnqrnxLf1cAr3rVq7bo8/vYxz6WRwzOmzePn//858RxXDLhtf9cd955Jxs2bChpI5l5OPt5XiyeffbZUaHg7d/hdrLPMYqiLfqsbrvtNv7lX/6FD33oQ9x8882cddZZ/PznP39BeWLjKnvS29vLF77wBa644gre8IY3bHLcGWecQZqmXHXVVaPeS5KkFA7b09Oz1eGxAMcccwyVSoXrr7++JNn/9V//lfXr1+eRQgcffDDTp0/ni1/8YikS5Kabbtqi6w8NDY3pt/j2t7/N2rVrOfjgg1/w2gcHB3O7csb++++P1joPjTz11FPRWnPllVeO0iiynze70yn+/M1m84VFXGwF2Z1P+3U++9nPjhq7NT6R4eFh6vV6aZ558+bR19c3KnT02WefLUUBDQ4O8rWvfY358+fn851xxhnce++9+Z1YkXXr1uW/i9NOOw0R4eMf//ioccXPeGu+u/PmzeORRx4phan/5je/4ac//ekLmuflxIknnghQipgC+PSnPw2Q/w0ec8wxRFHEZz/72dLn2H4ebPnvCrbOJ3LaaaexatUqPve5z42aP1vbiSeeSJqmo8b80z/9E0qpPFL1xSJJEr70pS/lr5vNJl/60peYPn06Bx100JjnzJgxgyOPPJIvfelLPPfcc6PeL37P1q1bl0cUXnPNNfzLv/wLv/rVr7jmmmte0DrHnZZ81llnPe+YI444gvPOO49rr72WBx54gGOPPZYoiliyZAnf+ta3+Od//ufcn3LQQQfxhS98gauvvprdd9+dGTNmlDSF52P69OlceumlfPzjH+f444/njW98I48++ig33HADr3rVq3j7298OWEl99dVXc9555/H617+ev/zLv2Tp0qXceOONW+QTWbJkCccccwx/+Zd/yd57743Wml/+8pd8/etfZ5ddduFv/uZvSuOzO4f2ePAiixcv5oILLuDNb34ze+65J0mS8G//9m8EQcBpp50GWFX0Ix/5CFdddRWHH344f/EXf0G1WuUXv/gFs2fP5tprr+Wwww5j8uTJnHXWWbz//e9HKcW//du/bXOT30EHHcRpp53GZz7zGVavXp2H+GZ3T8U79a3xifzhD3/g6KOP5owzzmCfffYhDEO+853vsHz58lGJnXvuuSfnnHMOv/jFL5g5cyZf+cpXWL58OTfeeGM+5oMf/CD/9V//xcknn8zZZ5/NQQcdxNDQEL/73e/4j//4D5588kmmTZvGUUcdxTve8Q6uv/56lixZwvHHH48xhp/85CccddRRXHDBBfnPf+edd/LpT3+a2bNns+uuu+ZO003xrne9i09/+tMcd9xxnHPOOaxYsYIvfvGL7LvvvqN8WeMlC7G/8cYbt2lFiAMOOICzzjqLL3/5y7lp9b777uOrX/0qp556aq6BTp8+nQ984AN56OuJJ57Ir3/9a37wgx8wbdq00pxb+ruCrfOJvPOd7+RrX/saF110Effddx+HH344Q0ND3Hnnnfz1X/81p5xyCm94wxs46qij+MhHPsKTTz7JAQccwB133MH3vvc9LrzwwlEh3eNl9uzZfOITn+DJJ59kzz335Bvf+AYPPPAAX/7ylzepLQF8/vOf57WvfS37778/73nPe9htt91Yvnw59957L3/605/4zW9+A8Df/M3fsHr1au68806CIOD444/n3e9+N1dffTWnnHLKlltjXkgoVzHEd3O0h/hmfPnLX5aDDjpIurq6pK+vT/bff3/50Ic+JM8++2w+ZtmyZXLSSSdJX19fKdR2U9fOwhF/9KMflY5/7nOfk7333luiKJKZM2fK+eefL2vXrh21phtuuEF23XVXqVarcvDBB8vdd9+9ybDLIitXrpRzzz1X9t57b+np6ZFKpSJ77LGHXHjhhbJy5cpR46dNmyaHHnroZud84okn5F3vepfMmzdParWaTJkyRY466ii58847R439yle+IgceeKBUq1WZPHmyHHHEEbJo0aL8/Z/+9Kdy6KGHSldXl8yePVs+9KEPye233z7qs3ohIb7tP1f2OymGJg4NDcnChQtlypQp0tvbK6eeeqo8+uijAsg//MM/bPbnfz5WrVolCxcuzD/zSZMmySGH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p54qnXPWWWeBiEqvSy65ZCzrM4xDjRkRY95zsGaRzIYHH3wQ69evxyOPPIL77rsPvV4P55xzDvbt21c67+KLL8bzzz8fX9/5znfmfG2GMR+wOhFj3rNmzRqcccYZuPnmmwH4limrVq3C5Zdfjquuumqsa3nxxRexfPlyPPjggzjzzDMBeE/ktNNOw0033TTWtRjGfMA8EWNeI7NIzj777HjstWaRzCUy1+Twww8vHf/Zz36GI488EieffDI2bdqEV199dexrM4xDgbU9MeY1s5lFMlc453DFFVfgAx/4AE4++eR4/HOf+xxWr16NY445Bo8//jiuvPJKPPXUU/jVr3411vUZxqHAjIhhvE7Wr1+PJ554Ag899FDp+Fe+8pX4+ymnnIKjjz4aH/3oR/HMM8/grW9967iXaRhjxcJZxrxmNrNI5oLLLrsMd999Nx544AEce+yxBzx3zZo1AICnn356HEszjEOKGRFjXnOoZ5EwMy677DL8+te/xp/+9Cccd9xxr3nN1q1bAQBHH330HK/OMA49Fs4y5j2HchbJ+vXrceedd+I3v/kNlixZgh07dgDwUwcnJyfxzDPP4M4778THP/5xHHHEEXj88cexYcMGnHnmmTj11FPnfH2Gcagxia9RC26++WZ897vfjbNIfvjDH8aw0VxCREOP33777bjwwguxfft2fOELX8ATTzyBffv2YdWqVfjUpz6Fq6++el6N8TWMucKMiGEYhjFrLCdiGIZhzBozIoZhGMasMSNiGIZhzBozIoZhGMasMSNiGIZhzBozIoZhGMasMSNiGIZhzBozIoZhGMasMSNiGIZhzBozIoZhGMasMSNiGIZhzJr/A/kxaMWEGn0TAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Method 3, scipy.spectrogram(scaling=spectrum, mode=complex)\n", + "\n", + "Avg power spectrogram = -16.40764832496643\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAqcAAAGzCAYAAAASSr4UAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAADUOklEQVR4nOydeZwcZZ3/389TVd099+SahEBIICBnFAzIIZdc4VAWlgU55FAEZEEUd1FZlcMLFXZFlOOHq8AirIJ44oGwICCHKIrIFTkSwhUSckzm6u6qep7fH09VdfUcySSTTLD9vl+vTqar63i6p6fnM9/j81XWWosgCIIgCIIgvAXQG3sBgiAIgiAIgpAi4lQQBEEQBEF4yyDiVBAEQRAEQXjLIOJUEARBEARBeMsg4lQQBEEQBEF4yyDiVBAEQRAEQXjLIOJUEARBEARBeMsg4lQQBEEQBEF4yyDiVBAEQRAEQXjL8HcjTm+44QaUUvzxj3/c4Nfab7/92G+//Tb4dTYks2bN4tRTT93Yy8j47W9/i1KK3/72txt7KQBcfPHFKKXW2/nS5/fDH/5wvZ1zQ5L+PC1cuHBjL2VcufXWW5k4cSK9vb3ZtiiK+OQnP8mMGTPQWnPkkUcC0Nvby4c//GGmTZuGUoqPf/zjG2fR64m32s/gPyp/b98HpRQXX3zxxl6GkGM8f7+P5XfFpz/9aXbbbbd1uu5aidN0kUopfve73w153FrLjBkzUErx3ve+d50WdPXVV3PDDTes07Ebk5UrV9LV1fV3JVCE0fHzn/+cfffdl66uLpqbm9lyyy059thj+fWvf73R1nTLLbdwxRVX1G3bb7/9sp/P1d029i+a9JfzcLfjjjsu2291z+fZZ58d1XPNf6jGccxFF13ERz/6UVpbW7PrfPe73+Wyyy7jX/7lX7jxxhs577zzAPjyl7/MDTfcwFlnncVNN93ESSedxEMPPcTFF1/MypUrx/MlE9aS4X4+hLcef6+/799qPP3001x88cVvuWDDxz/+cf7yl7/ws5/9bK2P9dflgqVSiVtuuYW99tqrbvt9993HK6+8QrFYXJfTAu7NOnny5LdU1G80XHjhhfT392/sZWTMnz8frd86gfF99tmHgYEBCoXCxl7KWnH55Zdz/vnns++++3LBBRfQ3NzM888/z9133833v/99DjnkkI2yrltuuYUnn3yyLpr3mc98hg9/+MPZ/T/84Q9ceeWV/Md//Afbbbddtv3tb387O+ywA8cdd9yYflbHyrnnnsuuu+5at23WrFl19zfbbDMuvfTSIcdusskm3HTTTXXb/vM//5NXXnmFr3/963Xbp0yZArg/MubPn88ZZ5xR9/g999zDpptuOuS4e+65h913352LLroo23b55ZdzySWXcOqpp9LZ2Tmq5ymMP8P9fAhvPf5ef99vbAb/fn/66ae55JJL2G+//YZ8hm5Mpk2bxj/90z9x+eWXc8QRR6zVseskTg877DBuu+02rrzySny/dopbbrmFuXPn8uabb67Laf9uefLJJ7nmmmu48MILufDCCzf2cgA2qugYDq01pVJpYy9jrYiiiC984QscdNBB/OY3vxny+JIlS8Z9TX19fbS0tAz72EEHHVR3v1QqceWVV3LQQQcNW6bied6GWOKo2XvvvfmXf/mX1e7T0dHBBz7wgWEfG7z9+9//PitWrBhx/+uvv553v/vdbLrppnXblyxZMqzQXLJkCdtvv/1q1yf842CMoVqt/t19jgmNx1vt9/vqOPbYYznmmGN48cUX2XLLLUd93DqF1o4//niWLVvGXXfdlW2rVqv88Ic/5IQTThj2GGMMV1xxBTvssAOlUompU6dy5plnsmLFimyfWbNm8dRTT3HfffdlKbnBv1QrlQqf+MQnmDJlCi0tLRx11FEsXbp0yPWuvvpqdthhB4rFItOnT+fss88eNhV33XXXMXv2bJqamnjXu97FAw88sNavx8c+9jGOOuoo9t5777U+djB//OMfmTdvHpMnT6apqYktttiCD33oQ3X7GGP4xje+wZw5cyiVSkyZMoVDDjmkrh53cE1KWpJx//33c+aZZzJp0iTa29s5+eST674Hp5xyCpMnTyYMwyFrO/jgg9lmm22GXff3v/995s6dS1tbG+3t7cyZM4dvfOMb2eMj1VldddVVbLnllnWv/+Ca3/TYW2+9lS996UtsttlmlEolDjjgAJ5//vm68z3wwAMcc8wxbL755hSLRWbMmMF5553HwMDAiK/5SLz55pusWrWKd7/73cM+3tXVNWSbMWaNawS47bbbmDt3Lk1NTUyePJkPfOADvPrqq3X7nHrqqbS2tvLCCy9w2GGH0dbWxoknnsh+++3HL37xC1566aXs52Rt/1oero5o1qxZvPe97+W3v/0tu+yyC01NTcyZMyf7nv3oRz/K3nNz587lz3/+85DzPvvss/zLv/wLEydOpFQqscsuu6xTSmd9Uy6X+fWvf82BBx6YbVu4cCFKKe69916eeuqp7LVM328LFizgF7/4Rbb91FNP5fzzzwdgiy22GFI2sCbS7+eiRYt473vfS2trK5tuuilXXXUVAH/961/Zf//9aWlpYebMmdxyyy1DzvHiiy9yzDHHMHHiRJqbm9l99935xS9+MWS/V155hSOPPJKWlha6uro477zzqFQqw67r97//PYcccggdHR00Nzez77778uCDD47qOY2GZ599lmOPPZYpU6bQ1NTENttsw2c+85m6fV599VU+9KEPMXXqVIrFIjvssAPf/e536/YZ7efAmn4+KpUKF110EVtttVX2GfHJT35yyOujlOKcc87h5ptvzn6XpKU8o1kvrN33YbSs6bMWXJnZeeedx6xZsygWi2y22WacfPLJWeCoWq1y4YUXMnfuXDo6OmhpaWHvvffm3nvvHdUaRvv8V8eaft+P9r2+LqSff7/73e8499xzmTJlCp2dnZx55plUq1VWrlzJySefzIQJE5gwYQKf/OQnsdbWnePyyy9nzz33ZNKkSTQ1NTF37txhS/oGBgY499xzmTx5Mm1tbRxxxBG8+uqrQ0qs0j6I559/PsvMdHR08MEPfnBIVjb/+/2GG27gmGOOAeA973lP3ecYjFwzPFzd6lNPPcX+++9PU1MTm222GV/84hcxxgz7Gv7qV79i7733pqWlhba2Ng4//HCeeuqpIfuln7k//elPhz3PSKxT5HTWrFnsscce/O///i+HHnpottDu7m6OO+44rrzyyiHHnHnmmdxwww188IMf5Nxzz2XBggV861vf4s9//jMPPvggQRBwxRVXZPVg6YfX1KlT687z0Y9+lAkTJnDRRRexcOFCrrjiCs455xx+8IMfZPtcfPHFXHLJJRx44IGcddZZzJ8/n2uuuYY//OEP2bUAvvOd73DmmWey55578vGPf5wXX3yRI444gokTJzJjxoxRvRa33XYbDz30EM8888yY6z2WLFnCwQcfzJQpU/j0pz9NZ2cnCxcu5Ec/+lHdfqeddho33HADhx56KB/+8IeJoogHHniARx55hF122WW11zjnnHPo7Ozk4osvzl6Xl156KfvgP+mkk/if//kf7rzzzrq64cWLF3PPPffUpThT7rrrLo4//ngOOOAAvvrVrwLwzDPP8OCDD/Kxj31sxLVcc801nHPOOey9996cd955LFy4kCOPPJIJEyaw2WabDdn/K1/5Clpr/v3f/53u7m6+9rWvceKJJ/L73/8+2+e2226jv7+fs846i0mTJvHoo4/yzW9+k1deeYXbbrttta/NYLq6umhqauLnP/85H/3oR5k4ceIajxnNGtOfg1133ZVLL72UN954g2984xs8+OCD/PnPf66L4kVRxLx589hrr724/PLLaW5uZtq0aXR3d9elsPM1lGPh+eef54QTTuDMM8/kAx/4AJdffjnve9/7uPbaa/mP//gP/vVf/xWASy+9lGOPPbYuvfTUU09lkclPf/rTtLS0cOutt3LkkUdy++23c9RRR9Vdq6enZ0iWZeLEiXXpqjiOh+xTKpXW+vk+9thjVKtV3vnOd2bbpkyZwk033cSXvvQlent7s/KB7bbbjptuuonzzjuPzTbbjH/7t38DYM6cOVSrVf73f/+Xr3/960yePDk7z2iJ45hDDz2UffbZh6997WvcfPPNnHPOObS0tPCZz3yGE088kX/+53/m2muv5eSTT2aPPfZgiy22AOCNN95gzz33pL+/n3PPPZdJkyZx4403csQRR/DDH/4we30HBgY44IADWLRoEeeeey7Tp0/npptu4p577hmynnvuuYdDDz2UuXPnctFFF6G15vrrr2f//ffngQce4F3vehcAYRjS3d09queY/x4+8cQT7L333gRBwBlnnMGsWbN44YUX+PnPf86XvvSl7HntvvvumRicMmUKv/rVrzjttNNYtWrVkNT8mn7GPvOZz4z482GM4YgjjuB3v/sdZ5xxBttttx1//etf+frXv87f/vY3fvKTnwx5fW699VbOOeccJk+ezKxZs0a93rX5PoyW0XzW9vb2svfee/PMM8/woQ99iHe+8528+eab/OxnP+OVV15h8uTJrFq1iv/+7//m+OOP5/TTT6enp4fvfOc7zJs3j0cffZSddtppxDWs7fdrJFb3+36073Vg1Jnatra2IRHHj370o0ybNo1LLrmERx55hOuuu47Ozk4eeughNt98c7785S/zy1/+kssuu4wdd9yRk08+OTv2G9/4BkcccQQnnngi1WqV73//+xxzzDHccccdHH744dl+p556KrfeeisnnXQSu+++O/fdd1/d44M59thj2WKLLbj00kv505/+xH//93/T1dWVfb8Hs88++3DuuecOKeHKl3KNhsWLF/Oe97yHKIqyz+/rrruOpqamIfvedNNNnHLKKcybN4+vfvWr9Pf3c80117DXXnvx5z//ue6PwY6ODmbPns2DDz6Y1fOPCrsWXH/99Rawf/jDH+y3vvUt29bWZvv7+6211h5zzDH2Pe95j7XW2pkzZ9rDDz88O+6BBx6wgL355pvrzvfrX/96yPYddtjB7rvvviNe+8ADD7TGmGz7eeedZz3PsytXrrTWWrtkyRJbKBTswQcfbOM4zvb71re+ZQH73e9+11prbbVatV1dXXannXaylUol2++6666zwLBrGEx/f7/dfPPN7QUXXGCttfbee++1gL3tttvWeOxw/PjHP85e35G45557LGDPPffcIY/lX5eZM2faU045Jbufvn5z58611Wo12/61r33NAvanP/2ptdbaOI7tZpttZt///vfXnfu//uu/rFLKvvjii0Ou+7GPfcy2t7fbKIpGXHf62tx7773WWmsrlYqdNGmS3XXXXW0Yhtl+N9xww5DXPz12u+22q/tefeMb37CA/etf/5ptS9+PeS699FKrlLIvvfRStu2iiy6yo3n7X3jhhRawLS0t9tBDD7Vf+tKX7GOPPTbi81vTGtP33Y477mgHBgay/e644w4L2AsvvDDbdsopp1jAfvrTnx5yvcMPP9zOnDlztWu/7bbb6l7zPOn7YcGCBdm2mTNnWsA+9NBD2bY777zTArapqanu9ft//+//DTn3AQccYOfMmWPL5XK2zRhj99xzT7v11lsPea2Gu+XXs++++w67T/59PdrX5L//+7+HvFfy19lhhx2GbB/8OWattZdddtmQdY6W9Pv55S9/Odu2YsUK29TUZJVS9vvf/362/dlnn7WAveiii7JtH//4xy1gH3jggWxbT0+P3WKLLeysWbOyz7srrrjCAvbWW2/N9uvr67NbbbVV3ffMGGO33nprO2/evLrPjv7+frvFFlvYgw46KNu2uu/Z6r6H++yzj21ra6t776TXTjnttNPsJptsYt988826fY477jjb0dGR/UyvzefASO+Fm266yWqt615Da6299tprLWAffPDBbBtgtdb2qaeeqtt3tOsd7fdhbRjNZ236mfWjH/1oyGPp6x5FUd1raK17L06dOtV+6EMfqts++H042uc/Gkb6fT/a93q6vtHcrr/++uyY9PNv8Ht/jz32sEop+5GPfCTbFkWR3WyzzYasc/DzrFardscdd7T7779/tu2xxx6zgP34xz9et++pp5465HVNfycNfv2POuooO2nSpLptg3+/r+6zfvB1RjpH+pr//ve/z7YtWbLEdnR01P1c9/T02M7OTnv66afXnW/x4sW2o6NjyHZrrT344IPtdtttN2T76ljnjpljjz2WgYEB7rjjDnp6erjjjjtGTOnfdtttdHR0cNBBB/Hmm29mt7lz59La2jrqVALAGWecUWcBtPfeexPHMS+99BIAd999N9VqlY9//ON1EZjTTz+d9vb2LC3wxz/+kSVLlvCRj3ykrknn1FNPpaOjY1Rr+cpXvkIYhvzHf/zHqNe/OtKI2R133DFsWh3g9ttvRyk1bARzNNZIZ5xxRhY5BjjrrLPwfZ9f/vKXgKsNPfHEE/nZz35GT09Ptt/NN9/MnnvumUVxBq+7r6+vrsxjTfzxj39k2bJlnH766XV1yyeeeCITJkwY9pgPfvCDdd+rtIzixRdfzLbl/8rr6+vjzTffZM8998RaO2waek1ccskl3HLLLey8887ceeedfOYzn2Hu3Lm8853v5JlnnlnrNabvu3/913+tq107/PDD2XbbbYdNW5111llrve51Zfvtt2ePPfbI7qc2IPvvvz+bb775kO3p81q+fDn33HMPxx57bBYRffPNN1m2bBnz5s3jueeeG1K2cOGFF3LXXXfV3aZNm1a3z6xZs4bs88lPfnKtn9eyZcsARnxvjSf5prXOzk622WYbWlpaOPbYY7Pt22yzDZ2dnXXv7V/+8pe8613vqmtEbW1t5YwzzmDhwoU8/fTT2X6bbLJJXT1vc3PzkEawxx9/nOeee44TTjiBZcuWZd+zvr4+DjjgAO6///4spfeOd7xjyPdhpFv6PVy6dCn3338/H/rQh+reO1D7rLLWcvvtt/O+970Pa23d74d58+bR3d3Nn/70p7pjR/M5MBK33XYb2223Hdtuu23dtfbff3+AIb+L9t1337q647VZ72i/D2vDaD5rb7/9dt7xjncMyVRA7XX3PC97DY0xLF++nCiK2GWXXYa83nnW5fu1Loz2vQ6M+n05b968Idc57bTT6n5v7rbbblhrOe2007Jtnuexyy67DHl/5X/XrFixgu7ubvbee++655+WgaQZp5SPfvSjIz73j3zkI3X39957b5YtW8aqVatGPGZ98Mtf/pLdd989y5aAywqdeOKJdfvdddddrFy5kuOPP77u++95Hrvtttuwem7ChAlr3Yu0Tmn9dNEHHnggt9xyC/39/cRxPGJzw3PPPUd3d/ewNXqwdo0lgz/k0l82ad1kKlIH10YWCgW23HLL7PH0/6233rpuvyAIRlW0u3DhQi677DKuuuqq9ZZS3XfffTn66KO55JJL+PrXv85+++3HkUceyQknnJClI1544QWmT58+qhTzcAx+vq2trWyyySZ1JQknn3wyX/3qV/nxj3/MySefzPz583nssce49tprhz3nv/7rv3Lrrbdy6KGHsummm3LwwQdz7LHHrraTPX39t9pqq7rtvu+PWD+5pu89wKJFi7jwwgv52c9+VrcdGHVacjDHH388xx9/PKtWreL3v/89N9xwA7fccgvve9/7ePLJJ+tE5rq+PwG23XbbIRZtvu8PW+KwoRi8/vQPtcFlLun29Hk9//zzWGv53Oc+x+c+97lhz71kyZK6ZqQ5c+bU1YAOR0tLyxr3WRvsoLqx8SatEc/T0dHBZpttNuSPy46Ojrr38EsvvTSsZ2CavnvppZfYcccdeemll9hqq62GnG/we+65554DXJ35SHR3d2d1d2v7fUh/me+4444j7rN06VJWrlzJddddx3XXXTfsPoN/P4zmc2AknnvuOZ555pkRSzEGX2vwH+Nrs97Rfh/WhtF81r7wwgscffTRazzXjTfeyH/+53/y7LPP1gVDhgtApKzL92tdGO17HRjT58PafN4Nfn/dcccdfPGLX+Txxx+vqyPOf79feukltNZDXtPBv/dWt6b8+7u9vX1NT2mdGek1H+lzI/2DbjDDrdFau9a+4ussTgFOOOEETj/9dBYvXsyhhx46orWKMYauri5uvvnmYR9fm5qtkTqMx/uXzoUXXsimm27Kfvvtlwm7xYsXA+4HeOHChWy++eZrZeeUeqQ+8sgj/PznP+fOO+/kQx/6EP/5n//JI488st5E8JrYfvvtmTt3Lt/73vc4+eST+d73vkehUKiL7OTp6uri8ccf58477+RXv/oVv/rVr7j++us5+eSTufHGG9fbutb0vY/jmIMOOojly5fzqU99im233ZaWlhZeffVVTj311BELu0dLe3s7Bx10EAcddBBBEHDjjTfy+9//nn333XfUa1xbisXiuFqCjbT+NT2v9LX993//92EjFLD6D+QNzaRJkwD3AT+eYn8w6/r6bgjS79lll102Yo1h+plTrVZZvnz5qM47ZcqUUTtBpGv4wAc+MKJIfvvb3153fyyvlTGGOXPm8F//9V/DPj5YlAyut1uX9a5P1tdn7fe+9z1OPfVUjjzySM4//3y6urrwPI9LL72UF154YcTjNvbzH4709+6a6OjoGPL9XJufx/z764EHHuCII45gn3324eqrr2aTTTYhCAKuv/76YRsZ14bx+iyI43idjkvfAzfddNOQTBdQlwlNWbFiRVajP1rGJE6POuoozjzzTB555JG6hqTBzJ49m7vvvpt3v/vdwxbX5hnr1J6ZM2cCzgcsHwGtVqssWLAg+ysr3e+5556r+wsgDEMWLFjAO97xjtVeZ9GiRTz//PPDRlnTEP6KFSvWyQtx9913Z/fdd+dLX/oSt9xyCyeeeCLf//73+fCHP8zs2bO58847Wb58+TpFT5977jne8573ZPd7e3t5/fXXOeyww+r2O/nkk/nEJz7B66+/zi233MLhhx++2pRooVDgfe97H+973/swxvCv//qv/L//9//43Oc+N6woSV//559/vm49URSxcOHCdfqA++tf/8rf/vY3brzxxrrC9bUpNxgtu+yyCzfeeCOvv/76Wh2Xf38O/stz/vz52eNrYn1Ot1ofpD8HQRCs10jn+mLbbbcFYMGCBcyZM2edz7MxX/eZM2cyf/78IdufffbZ7PH0/yeffHJItGLwsbNnzwbcH11r+p499NBDdT+nq2PBggXMmjUre088+eSTI+47ZcoU2traiON4vb5vRvo+zZ49m7/85S8ccMAB6/S9XJv1jvb7sLas6bN29uzZq33NAX74wx+y5ZZb8qMf/ahubcOVi+VZ39+vkb4Ho32vg/M8Hg3XX3/9evNTvf322ymVStx55511TVbXX3993X4zZ87EGMOCBQvqspbDObiMhdW9lydMmDDEqaharQ753TVz5swsKppnpM+Nrq6uUb8HRqOpBjOmkExrayvXXHMNF198Me973/tG3O/YY48ljmO+8IUvDHksiqK6F66lpWVM01cOPPBACoUCV155Zd1fGt/5znfo7u7OuuR22WUXpkyZwrXXXku1Ws32u+GGG0Z1/S9+8Yv8+Mc/rrulz++Tn/wkP/7xj0f0oxyJFStWDPnrKI1opGmDo48+Gmstl1xyyZDjR/OX1XXXXVeXwrnmmmuIoihzXUg5/vjjUUrxsY99jBdffDHzjgzDkGeffbbujZ3W86VorTNxOZJtyi677MKkSZP49re/TRRF2fabb755VOm54Uj/4sy/DtbaITYrI7Fo0aLsww+gv7+fhx9+eNh9f/WrXwFrn6LbZZdd6Orq4tprr617bX71q1/xzDPPrLaLM09LS8s6lylsCLq6uthvv/34f//v/w0r2IezextP5s6dS6FQGPP44/RnemNMiDrssMN49NFH696TfX19XHfddcyaNSurjTzssMN47bXX6mxt+vv7h6Rh586dy+zZs7n88svrxrmm5L9n61JzOmXKFPbZZx+++93vsmjRorpzpz+jnudx9NFHc/vttw8rqNb1fTPSz8exxx7Lq6++yre//e0hjw0MDNDX17fa867Nekf7fVgbRvNZe/TRR/OXv/yFH//4x0OOz7/u+fvgLMVG+rxLWd/fr5F+34/2vQ5jqzldVzzPQylVF31cuHDhELeH9JpXX3113fZvfvOb620tsPrPpdmzZ3P//ffXbbvuuuuGRE4PO+wwHnnkER599NFs29KlS4dkvOfNm0d7eztf/vKXh+2NGfwe6O7u5oUXXmDPPfdcq+c0psgprL5eKWXfffflzDPP5NJLL+Xxxx/n4IMPJggCnnvuOW677Ta+8Y1vZPWqc+fO5ZprruGLX/wiW221FV1dXSPWNgzHlClTuOCCC7jkkks45JBDOOKII5g/fz5XX301u+66ayaygiDgi1/8ImeeeSb7778/73//+1mwYAHXX3/9qGpOB0/HglpD06677prN505RSrHvvvuudp7yjTfeyNVXX81RRx3F7Nmz6enp4dvf/jbt7e1ZZPM973kPJ510EldeeSXPPfcchxxyCMYYHnjgAd7znvdwzjnnrHbd1WqVAw44ILMBuvrqq9lrr72GTG9IvVNvu+02Ojs7M9H06quvst1223HKKadkY+c+/OEPs3z5cvbff38222wzXnrpJb75zW+y0047jWhnUSgUuPjii/noRz/K/vvvz7HHHsvChQu54YYbmD179jpFNbbddltmz57Nv//7v/Pqq6/S3t7O7bffPmqxe/LJJ3PfffdlH9j9/f3sueee7L777hxyyCHMmDGDlStX8pOf/IQHHniAI488kp133nmt1hgEAV/96lf54Ac/yL777svxxx+fWUnNmjVr1FYbc+fO5Qc/+AGf+MQn2HXXXWltbV3tH4jjwVVXXcVee+3FnDlzOP3009lyyy154403ePjhh3nllVf4y1/+stHWViqVOPjgg7n77rv5/Oc/v87nmTt3LuDsio477jiCIOB973vfWv8hui58+tOfzuz7zj33XCZOnMiNN97IggULuP3227Pyj9NPP51vfetbnHzyyTz22GPZNK3m5ua682mt+e///m8OPfRQdthhBz74wQ+y6aab8uqrr3LvvffS3t7Oz3/+c4B1qjkFuPLKK9lrr7145zvfyRlnnMEWW2zBwoUL+cUvfsHjjz8OuMbSe++9l912243TTz+d7bffnuXLl/OnP/2Ju+++e9TlBHlG+vk46aSTuPXWW/nIRz7Cvffey7vf/W7iOObZZ5/l1ltv5c4771yjHd9o1zva7wM4D9f3vOc9XHTRRasdLzyaz9rzzz+fH/7whxxzzDF86EMfYu7cuSxfvpyf/exnXHvttbzjHe/gve99Lz/60Y846qijOPzww1mwYAHXXnst22+//bB/qKzL8x8NI/2+H+17HcZWc7quHH744fzXf/0XhxxyCCeccAJLlizhqquuYquttuKJJ56oe35HH300V1xxBcuWLcuspP72t78B6y8Ts9NOO+F5Hl/96lfp7u6mWCyy//7709XVxYc//GE+8pGPcPTRR3PQQQfxl7/8hTvvvHNImv2Tn/wkN910E4cccggf+9jHMiupmTNn1j2n9vZ2rrnmGk466STe+c53ctxxxzFlyhQWLVrEL37xC9797nfzrW99K9v/7rvvxlrLP/3TP63dk1qb1v68ldTqGM6CxVpn0zR37lzb1NRk29ra7Jw5c+wnP/lJ+9prr2X7LF682B5++OG2ra2tzlJopGsPtihK+da3vmW33XZbGwSBnTp1qj3rrLPsihUrhqzp6quvtltssYUtFot2l112sffff7/dd999R2UlNZiRrKR6enosYI877rjVHv+nP/3JHn/88XbzzTe3xWLRdnV12fe+9732j3/8Y91+URTZyy67zG677ba2UCjYKVOm2EMPPbTO4mgkK6n77rvPnnHGGXbChAm2tbXVnnjiiXbZsmXDrufWW2+1gD3jjDOybQsWLBhi5/PDH/7QHnzwwbarq8sWCgW7+eab2zPPPNO+/vrrQ16bwd+nK6+80s6cOdMWi0X7rne9yz744IN27ty59pBDDlnj65quJW8R8vTTT9sDDzzQtra22smTJ9vTTz/d/uUvfxmy33BWUql1UUoYhvbb3/62PfLII7M1Njc325133tledtlldVYsa7NGa639wQ9+YHfeeWdbLBbtxIkT7YknnmhfeeWVun1OOeUU29LSYoejt7fXnnDCCbazs9MCw9rmrIuV1HA/t4A9++yzh31el112Wd32F154wZ588sl22rRpNggCu+mmm9r3vve99oc//GG2z2gt10ayeBqJNdlr/ehHP7JKKbto0aJRXWek1+MLX/iC3XTTTa3Weq1spUb6fq7N9V944QX7L//yL7azs9OWSiX7rne9y95xxx1Djn3ppZfsEUccYZubm+3kyZPtxz72scy6b/D74c9//rP953/+Zztp0iRbLBbtzJkz7bHHHmv/7//+b1TPa008+eST9qijjsrWvM0229jPfe5zdfu88cYb9uyzz7YzZsywQRDYadOm2QMOOMBed9112T5r8zO2up+ParVqv/rVr9oddtjBFotFO2HCBDt37lx7ySWX2O7u7my/4d73a7Nea0f/ffj5z39uAXvttdeu9rUczWettdYuW7bMnnPOOXbTTTe1hULBbrbZZvaUU07J7J+MMfbLX/5y9rm288472zvuuMOecsopQ36GGMaKaLTPf02M9Pve2tG/19eFkfRE+nth6dKldduH+9n9zne+Y7feemtbLBbttttua6+//vphf6/09fXZs88+206cONG2trbaI4880s6fP98C9itf+coarz3SZ/VgS71vf/vbdsstt7Se59W9v+I4tp/61Kfs5MmTbXNzs503b559/vnnhz3HE088Yffdd19bKpXspptuar/whS/Y73znO8N+zt1777123rx5tqOjw5ZKJTt79mx76qmnDtEr73//++1ee+1l15a1EqfCuvGLX/zCKqXsE088sdHWMNo/LPL85Cc/sYC9//77N+DK6onj2E6cONF++MMfHrdrCv8YRFFk3/a2t9nPfvazG3spglDH+eefbzfbbLM6j2Chcfnzn/9sAfu9731vYy9lg/L666/bUqlkf/KTn6z1sePXBvwPzL333stxxx03pkaM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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Method 4, scipy.signal.ShortTimeFFT(fft_mode=centered, scale_to=magnitude)\n", + "\n", + "Avg power spectrogram = -16.40764725853183\n", + "upper_idx=177, lower_idx=106\n", + "Sxx.shape[0]=256\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAqcAAAGzCAYAAAASSr4UAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAADU8ElEQVR4nOydeZwcVbm/n3Oqqrtnn2yTEBISCMgaBQOyyL6FRbkoF2SRRRGQC6LoxeWnsrhxFe4VUYGLVwERroK4gopwQUAWURARkMiSsIeELDOTmenuqjrn98epqq6eJZlkkgm27/P5NExX13J6Sc933uX7KmutRRAEQRAEQRDeBOiNvQBBEARBEARBSBFxKgiCIAiCILxpEHEqCIIgCIIgvGkQcSoIgiAIgiC8aRBxKgiCIAiCILxpEHEqCIIgCIIgvGkQcSoIgiAIgiC8aRBxKgiCIAiCILxpEHEqCIIgCIIgvGn4hxGn1157LUop/vSnP23wa+27777su+++G/w6G5LZs2dzyimnbOxlZPzud79DKcXvfve7jb0UAC688EKUUuvtfOnz+/GPf7zezrkhSf89LVq0aGMvZVy56aabmDhxIqtWrcq2RVHEJz/5SWbOnInWmiOPPBKAVatW8aEPfYhp06ahlOJjH/vYxln0euLN9m/wn5V/tPdBKcWFF164sZch5BjP3+9j+V3x6U9/ml133XWdrrtW4jRdpFKK3//+90Met9Yyc+ZMlFK8613vWqcFXXHFFVx77bXrdOx4s++++2avR/52yCGHbOylCeuRX/7yl+yzzz50dXXR3NzMFltswTHHHMNvfvObjbamG2+8kcsuu6xu20ifx8G3jf2LJv3lPNzt2GOPzfZb3fN5+umnR/Vc81+qcRxzwQUX8JGPfITW1tbsOt/73ve45JJL+Nd//Veuu+46zj33XAC+8pWvcO2113LmmWdy/fXXc+KJJ/LAAw9w4YUXsnLlyvF8yYS1ZLh/H8Kbj3+k3/dvZp566ikuvPDCN12w4WMf+xh/+ctf+MUvfrHWx/rrcsFSqcSNN97InnvuWbf9nnvu4eWXX6ZYLK7LaQH3YZ08efKbKuq3OmbMmMHFF19ct2369OkbaTU1FixYgNZvnsD43nvvzcDAAIVCYWMvZa249NJLOe+889hnn334zGc+Q3NzM88++yx33nknP/zhDzfaHyI33ngjTzzxRF0077Of/Swf+tCHsvt//OMfufzyy/l//+//se2222bb3/rWt7L99ttz7LHHjunf6lg555xz2GWXXeq2zZ49u+7+cP++ADbZZBOuv/76um3/+Z//ycsvv8zXv/71uu1TpkwB3B8ZCxYs4PTTT697/K677mLTTTcdctxdd93FbrvtxgUXXJBtu/TSS7nooos45ZRT6OzsHNXzFMaf4f59CG8+/tF+379ZGPz7/amnnuKiiy5i3333HfIdujGZNm0a//Iv/8Kll17KEUccsVbHrpM4Peyww7j55pu5/PLL8f3aKW688UbmzZvHG2+8sS6n/Yeko6OD97///Rt7GUPYmKJjOLTWlEqljb2MtSKKIr74xS9y0EEH8dvf/nbI40uWLBn3NfX19dHS0jLsYwcddFDd/VKpxOWXX85BBx00bJmK53kbYomjZq+99uJf//VfV7vP6v59Dd7+wx/+kBUrVoy4/zXXXMM73/lONt1007rtS5YsGVZoLlmyhO2222616xP+eTDGUK1W/+G+x4TG4832+311HHPMMRx99NE8//zzbLHFFqM+bp1Ca8cddxzLli3jjjvuyLZVq1V+/OMfc/zxxw97jDGGyy67jO23355SqcTUqVM544wzWLFiRbbP7NmzefLJJ7nnnnuylNzgX6qVSoWPf/zjTJkyhZaWFt7znvewdOnSIde74oor2H777SkWi0yfPp2zzjpr2FTc1VdfzZw5c2hqauId73gH991331q/HlEU1dWwjYU//elPzJ8/n8mTJ9PU1MTmm2/OBz/4wbp9jDF84xvfYO7cuZRKJaZMmcIhhxxSV487uCYlLcm49957OeOMM5g0aRLt7e2cdNJJde/BySefzOTJkwnDcMjaDj74YLbeeuth1/3DH/6QefPm0dbWRnt7O3PnzuUb3/hG9vhIdVbf/va32WKLLepe/8E1v+mxN910E1/+8peZMWMGpVKJAw44gGeffbbufPfddx9HH300m222GcVikZkzZ3LuuecyMDAw4ms+Em+88QY9PT28853vHPbxrq6uIduMMWtcI8DNN9/MvHnzaGpqYvLkybz//e/nlVdeqdvnlFNOobW1leeee47DDjuMtrY2TjjhBPbdd19uu+02Xnjhhezfydr+tTxcHdHs2bN517vexe9+9zt23nlnmpqamDt3bvae/eQnP8k+c/PmzePPf/7zkPM+/fTT/Ou//isTJ06kVCqx8847r1NKZ31TLpf5zW9+w4EHHphtW7RoEUop7r77bp588snstUw/bwsXLuS2227Ltp9yyimcd955AGy++eZDygbWRPp+vvjii7zrXe+itbWVTTfdlG9/+9sA/PWvf2X//fenpaWFWbNmceONNw45x/PPP8/RRx/NxIkTaW5uZrfdduO2224bst/LL7/MkUceSUtLC11dXZx77rlUKpVh1/WHP/yBQw45hI6ODpqbm9lnn324//77R/WcRsPTTz/NMcccw5QpU2hqamLrrbfms5/9bN0+r7zyCh/84AeZOnUqxWKR7bffnu9973t1+4z2e2BN/z4qlQoXXHABW265ZfYd8clPfnLI66OU4uyzz+aGG27IfpekpTyjWS+s3fswWtb0XQuwcuVKzj33XGbPnk2xWGTGjBmcdNJJWeCoWq1y/vnnM2/ePDo6OmhpaWGvvfbi7rvvHtUaRvv8V8eaft+P9rO+LqTff7///e8555xzmDJlCp2dnZxxxhlUq1VWrlzJSSedxIQJE5gwYQKf/OQnsdbWnePSSy9ljz32YNKkSTQ1NTFv3rxhew4GBgY455xzmDx5Mm1tbRxxxBG88sorQ0qs0j6IZ599NsvMdHR08IEPfID+/v4hr136+/3aa6/l6KOPBmC//far+x6DkWuGh6tbffLJJ9l///1pampixowZfOlLX8IYM+xr+Otf/5q99tqLlpYW2traOPzww3nyySeH7Jd+5/785z8f9jwjsU6R09mzZ7P77rvzv//7vxx66KHZQru7uzn22GO5/PLLhxxzxhlncO211/KBD3yAc845h4ULF/Ktb32LP//5z9x///0EQcBll12W1YOlX15Tp06tO89HPvIRJkyYwAUXXMCiRYu47LLLOPvss/nRj36U7XPhhRdy0UUXceCBB3LmmWeyYMECrrzySv74xz9m1wL47ne/yxlnnMEee+zBxz72MZ5//nmOOOIIJk6cyMyZM0f1Wvz973+npaWFarXK1KlTOe200zj//POza6wNS5Ys4eCDD2bKlCl8+tOfprOzk0WLFvGTn/ykbr9TTz2Va6+9lkMPPZQPfehDRFHEfffdx0MPPcTOO++82mucffbZdHZ2cuGFF2avywsvvJB98Z944ol8//vf5/bbb6+rG168eDF33XVXXYoz5Y477uC4447jgAMO4Ktf/SoAf/vb37j//vv56Ec/OuJarrzySs4++2z22msvzj33XBYtWsSRRx7JhAkTmDFjxpD9/+M//gOtNf/+7/9Od3c3X/va1zjhhBP4wx/+kO1z880309/fz5lnnsmkSZN4+OGH+eY3v8nLL7/MzTffvNrXZjBdXV00NTXxy1/+ko985CNMnDhxjceMZo3pv4NddtmFiy++mNdff51vfOMb3H///fz5z3+ui+JFUcT8+fPZc889ufTSS2lubmbatGl0d3fXpbDzNZRj4dlnn+X444/njDPO4P3vfz+XXnop7373u7nqqqv4f//v//Fv//ZvAFx88cUcc8wxdemlJ598MotMfvrTn6alpYWbbrqJI488kltuuYX3vOc9ddfq7e0dkmWZOHFiXboqjuMh+5RKpbV+vo888gjVapW3v/3t2bYpU6Zw/fXX8+Uvf5lVq1Zl5QPbbrst119/Peeeey4zZszgE5/4BABz586lWq3yv//7v3z9619n8uTJ2XlGSxzHHHrooey999587Wtf44YbbuDss8+mpaWFz372s5xwwgm8973v5aqrruKkk05i9913Z/PNNwfg9ddfZ4899qC/v59zzjmHSZMmcd1113HEEUfw4x//OHt9BwYGOOCAA3jxxRc555xzmD59Otdffz133XXXkPXcddddHHroocybN48LLrgArTXXXHMN+++/P/fddx/veMc7AAjDkO7u7lE9x/x7+Pjjj7PXXnsRBAGnn346s2fP5rnnnuOXv/wlX/7yl7Pntdtuu2VicMqUKfz617/m1FNPpaenZ0hqfk3/xj772c+O+O/DGMMRRxzB73//e04//XS23XZb/vrXv/L1r3+dv//97/zsZz8b8vrcdNNNnH322UyePJnZs2ePer1r8z6MltF8165atYq99tqLv/3tb3zwgx/k7W9/O2+88Qa/+MUvePnll5k8eTI9PT38z//8D8cddxynnXYavb29fPe732X+/Pk8/PDD7LjjjiOuYW3fr5FY3e/70X7WgVFnatva2oZEHD/ykY8wbdo0LrroIh566CGuvvpqOjs7eeCBB9hss834yle+wq9+9SsuueQSdthhB0466aTs2G984xscccQRnHDCCVSrVX74wx9y9NFHc+utt3L44Ydn+51yyincdNNNnHjiiey2227cc889dY8P5phjjmHzzTfn4osv5tFHH+V//ud/6Orqyt7vwey9996cc845Q0q48qVco2Hx4sXst99+RFGUfX9fffXVNDU1Ddn3+uuv5+STT2b+/Pl89atfpb+/nyuvvJI999yTP//5z3V/DHZ0dDBnzhzuv//+rJ5/VNi14JprrrGA/eMf/2i/9a1v2ba2Ntvf32+ttfboo4+2++23n7XW2lmzZtnDDz88O+6+++6zgL3hhhvqzveb3/xmyPbtt9/e7rPPPiNe+8ADD7TGmGz7ueeeaz3PsytXrrTWWrtkyRJbKBTswQcfbOM4zvb71re+ZQH7ve99z1prbbVatV1dXXbHHXe0lUol2+/qq6+2wLBrGMwHP/hBe+GFF9pbbrnFfv/737dHHHGEBewxxxyzxmOH46c//Wn2+o7EXXfdZQF7zjnnDHks/7rMmjXLnnzyydn99PWbN2+erVar2favfe1rFrA///nPrbXWxnFsZ8yYYd/3vvfVnfu//uu/rFLKPv/880Ou+9GPftS2t7fbKIpGXPfdd99tAXv33Xdba62tVCp20qRJdpdddrFhGGb7XXvttUNe//TYbbfdtu69+sY3vmEB+9e//jXbln4e81x88cVWKWVfeOGFbNsFF1xgR/PxP//88y1gW1pa7KGHHmq//OUv20ceeWTE57emNaafux122MEODAxk+916660WsOeff3627eSTT7aA/fSnPz3keocffridNWvWatd+8803173medLPw8KFC7Nts2bNsoB94IEHsm233367BWxTU1Pd6/ff//3fQ859wAEH2Llz59pyuZxtM8bYPfbYw2611VZDXqvhbvn17LPPPsPuk/9cj/Y1+Z//+Z8hn5X8dbbffvsh2wd/j1lr7SWXXDJknaMlfT+/8pWvZNtWrFhhm5qarFLK/vCHP8y2P/300xawF1xwQbbtYx/7mAXsfffdl23r7e21m2++uZ09e3b2fXfZZZdZwN50003Zfn19fXbLLbese8+MMXarrbay8+fPr/vu6O/vt5tvvrk96KCDsm2re89W9x7uvffetq2tre6zk1475dRTT7WbbLKJfeONN+r2OfbYY21HR0f2b3ptvgdG+ixcf/31Vmtd9xpaa+1VV11lAXv//fdn2wCrtbZPPvlk3b6jXe9o34e1YTTftel31k9+8pMhj6WvexRFda+hte6zOHXqVPvBD36wbvvgz+Fon/9oGOn3/Wg/6+n6RnO75pprsmPS77/Bn/3dd9/dKqXshz/84WxbFEV2xowZQ9Y5+HlWq1W7ww472P333z/b9sgjj1jAfuxjH6vb95RTThnyuqa/kwa//u95z3vspEmT6rYN/v2+uu/6wdcZ6Rzpa/6HP/wh27ZkyRLb0dFR9++6t7fXdnZ22tNOO63ufIsXL7YdHR1Dtltr7cEHH2y33XbbIdtXxzp3zBxzzDEMDAxw66230tvby6233jpiSv/mm2+mo6ODgw46iDfeeCO7zZs3j9bW1lGnEgBOP/30OgugvfbaiziOeeGFFwC48847qVarfOxjH6uLwJx22mm0t7dnaYE//elPLFmyhA9/+MN1TTqnnHIKHR0do1rLd7/7XS644ALe+973cuKJJ/Lzn/+c0047jZtuuomHHnpo1M8pJY2Y3XrrrcOm1QFuueUWlFLDRjBHY410+umn10V1zzzzTHzf51e/+hXgakNPOOEEfvGLX9Db25vtd8MNN7DHHntkUZzB6+7r66sr81gTf/rTn1i2bBmnnXZaXd3yCSecwIQJE4Y95gMf+EDde7XXXnsBLv2Tkv8rr6+vjzfeeIM99tgDa+2waeg1cdFFF3HjjTey0047cfvtt/PZz36WefPm8fa3v52//e1va73G9HP3b//2b3W1a4cffjjbbLPNsGmrM888c63Xva5st9127L777tn91AZk//33Z7PNNhuyPX1ey5cv56677uKYY47JIqJvvPEGy5YtY/78+TzzzDNDyhbOP/987rjjjrrbtGnT6vaZPXv2kH0++clPrvXzWrZsGcCIn63xJN+01tnZydZbb01LSwvHHHNMtn3rrbems7Oz7rP9q1/9ine84x11jaitra2cfvrpLFq0iKeeeirbb5NNNqmr521ubh7SCPbYY4/xzDPPcPzxx7Ns2bLsPevr6+OAAw7g3nvvzVJ6b3vb24a8DyPd0vdw6dKl3HvvvXzwgx+s++xA7bvKWsstt9zCu9/9bqy1db8f5s+fT3d3N48++mjdsaP5HhiJm2++mW233ZZtttmm7lr7778/wJDfRfvss09d3fHarHe078PaMJrv2ltuuYW3ve1tQzIVUHvdPc/LXkNjDMuXLyeKInbeeechr3eedXm/1oXRftaBUX8u58+fP+Q6p556at3vzV133RVrLaeeemq2zfM8dt555yGfr/zvmhUrVtDd3c1ee+1V9/zTMpA045TykY98ZMTn/uEPf7ju/l577cWyZcvo6ekZ8Zj1wa9+9St22223LFsCLit0wgkn1O13xx13sHLlSo477ri699/zPHbddddh9dyECRPWuhdpndL66aIPPPBAbrzxRvr7+4njeMTmhmeeeYbu7u5ha/Rg7RpLBn/Jpb9s0rrJVKQOro0sFApsscUW2ePp/7faaqu6/YIgWKui3cF84hOf4Dvf+Q533nknu+2221odu88++3DUUUdx0UUX8fWvf519992XI488kuOPPz5LRzz33HNMnz59VCnm4Rj8fFtbW9lkk03q6uZOOukkvvrVr/LTn/6Uk046iQULFvDII49w1VVXDXvOf/u3f+Omm27i0EMPZdNNN+Xggw/mmGOOWW0ne/r6b7nllnXbfd8fsX5yTe89wIsvvsj555/PL37xi7rtwKjTkoM57rjjOO644+jp6eEPf/gD1157LTfeeCPvfve7eeKJJ+pE5rp+PgG22WabIRZtvu8PW+KwoRi8/vQPtcFlLun29Hk9++yzWGv5/Oc/z+c///lhz71kyZK6ZqS5c+fW1YAOR0tLyxr3WRvsoLqx8SatEc/T0dHBjBkzhvxx2dHRUfcZfuGFF4b1DEzTdy+88AI77LADL7zwAltuueWQ8w3+zD3zzDOAqzMfie7u7qzubm3fh/SX+Q477DDiPkuXLmXlypVcffXVXH311cPuM/j3w2i+B0bimWee4W9/+9uIpRiDrzX4j/G1We9o34e1YTTftc899xxHHXXUGs913XXX8Z//+Z88/fTTdcGQ4QIQKevyfq0Lo/2sA2P6flib77vBn69bb72VL33pSzz22GN1dcT59/uFF15Aaz3kNR38e291a8p/vtvb29f0lNaZkV7zkb430j/oBjPcGq21a+0rvs7iFOD444/ntNNOY/HixRx66KEjWqsYY+jq6uKGG24Y9vG1qdkaqcN4Y//SSUk/1MuXL1/rY1MT94ceeohf/vKX3H777Xzwgx/kP//zP3nooYfWW13hmthuu+2YN28eP/jBDzjppJP4wQ9+QKFQqIvs5Onq6uKxxx7j9ttv59e//jW//vWvueaaazjppJO47rrr1tu61vTex3HMQQcdxPLly/nUpz7FNttsQ0tLC6+88gqnnHLKiIXdo6W9vZ2DDjqIgw46iCAIuO666/jDH/7APvvsM+o1ri3FYnFcLcFGWv+anlf62v77v//7sBEKWP0X8oZm0qRJgPuCH0+xP5h1fX03BOl7dskll4xYY5h+51Sr1VF/p02ZMmXUThDpGt7//vePKJLf+ta31t0fy2tljGHu3Ln813/917CPDxYlg+vt1mW965P19V37gx/8gFNOOYUjjzyS8847j66uLjzP4+KLL+a5554b8biN/fyHY/HixaPar6OjY8j7uTb/HvOfr/vuu48jjjiCvffemyuuuIJNNtmEIAi45pprhm1kXBvG67sgjuN1Oi79DFx//fVDMl1AXSY0ZcWKFVmN/mgZkzh9z3vewxlnnMFDDz1U15A0mDlz5nDnnXfyzne+c9ji2jxjndoza9YswPmA5SOg1WqVhQsXZn9lpfs988wzdX8BhGHIwoULedvb3rZO10+jBWsjuAez2267sdtuu/HlL3+ZG2+8kRNOOIEf/vCHfOhDH2LOnDncfvvtLF++fJ2ip8888wz77bdfdn/VqlW89tprHHbYYXX7nXTSSXz84x/ntdde48Ybb+Twww9fbUq0UCjw7ne/m3e/+90YY/i3f/s3/vu//5vPf/7zw4qS9PV/9tln69YTRRGLFi1apy+4v/71r/z973/nuuuuqytcX5tyg9Gy8847c9111/Haa6+t1XH5z+fgvzwXLFiQPb4m1ud0q/VB+m8tCIL1GulcX2yzzTYALFy4kLlz567zeTbm6z5r1iwWLFgwZPvTTz+dPZ7+/4knnhgSrRh87Jw5cwD3R9ea3rMHHnig7t/p6li4cCGzZ8/OPhNPPPHEiPtOmTKFtrY24jher5+bkd6nOXPm8Je//IUDDjhgnd7LtVnvaN+HtWVN37Vz5sxZ7WsO8OMf/5gtttiCn/zkJ3VrG65cLM/6fr9Geg9G+1kH53k8Gq655pr15qd6yy23UCqVuP322+uarK655pq6/WbNmoUxhoULF9ZlLYdzcBkLq/ssT5gwYYhTUbVaHfK7a9asWVlUNM9I3xtdXV2j/gysi6YaU0imtbWVK6+8kgsvvJB3v/vdI+53zDHHEMcxX/ziF4c8FkVR3QvX0tIypukrBx54IIVCgcsvv7zuL43vfve7dHd3Z11yO++8M1OmTOGqq66iWq1m+1177bWjun5PT88QSxBrLV/60pcARowerY4VK1YM+esojWik1zrqqKOw1nLRRRcNOX40f1ldffXVdSmcK6+8kiiKMteFlOOOOw6lFB/96Ed5/vnnM+/IMAx5+umn6z7YaT1fitY6E5cj2absvPPOTJo0ie985ztEUZRtv+GGG0aVnhuO9C/O/OtgrR1iszISL774YvblB9Df38+DDz447L6//vWvgbVP0e288850dXVx1VVX1b02v/71r/nb3/622i7OPC0tLetcprAh6OrqYt999+W///u/hxXsw9m9jSfz5s2jUCiMefxx6jG7MSZEHXbYYTz88MN1n8m+vj6uvvpqZs+endVGHnbYYbz66qt1tjb9/f1D0rDz5s1jzpw5XHrppcNa4eXfs3WpOZ0yZQp777033/ve93jxxRfrzp3+G/U8j6OOOopbbrllWEG1rp+bkf59HHPMMbzyyit85zvfGfLYwMAAfX19qz3v2qx3tO/D2jCa79qjjjqKv/zlL/z0pz8dcnz+dc/fB2cpNtL3Xcr6fr9G+n0/2s86jK3mdF3xPA+lVF30cdGiRUPcHtJrXnHFFXXbv/nNb663tcDqv5fmzJnDvffeW7ft6quvHhI5Peyww3jooYd4+OGHs21Lly4dkvGeP38+7e3tfOUrXxm2N2bwZ6C7u5vnnnuOPfbYY62e05gip7D6eqWUffbZhzPOOIOLL76Yxx57jIMPPpggCHjmmWe4+eab+cY3vpHVq86bN48rr7ySL33pS2y55ZZ0dXWNWNswHFOmTOEzn/kMF110EYcccghHHHEECxYs4IorrmCXXXbJRFYQBHzpS1/ijDPOYP/99+d973sfCxcu5JprrhlVzemjjz6a1SJuueWWDAwM8NOf/pT777+f008/vc6yBtxfNvvss89q5ylfd911XHHFFbznPe9hzpw59Pb28p3vfIf29vYssrnffvtx4okncvnll/PMM89wyCGHYIzhvvvuY7/99uPss89e7bqr1SoHHHBAZgN0xRVXsOeeew6Z3pB6p9588810dnZmoumVV15h22235eSTT87Gzn3oQx9i+fLl7L///syYMYMXXniBb37zm+y4444j2lkUCgUuvPBCPvKRj7D//vtzzDHHsGjRIq699lrmzJmzTlGNbbbZhjlz5vDv//7vvPLKK7S3t3PLLbeMWuyedNJJ3HPPPdkXdn9/P3vssQe77bYbhxxyCDNnzmTlypX87Gc/47777uPII49kp512Wqs1BkHAV7/6VT7wgQ+wzz77cNxxx2VWUrNnzx611ca8efP40Y9+xMc//nF22WUXWltbV/sH4njw7W9/mz333JO5c+dy2mmnscUWW/D666/z4IMP8vLLL/OXv/xlo62tVCpx8MEHc+edd/KFL3xhnc8zb948wNkVHXvssQRBwLvf/e4RByOsTz796U9n9n3nnHMOEydO5LrrrmPhwoXccsstWfnHaaedxre+9S1OOukkHnnkkWyaVnNzc935tNb8z//8D4ceeijbb789H/jAB9h000155ZVXuPvuu2lvb+eXv/wlwDrVnAJcfvnl7Lnnnrz97W/n9NNPZ/PNN2fRokXcdtttPPbYY4Czhrr77rvZddddOe2009huu+1Yvnw5jz76KHfeeec6lUiN9O/jxBNP5KabbuLDH/4wd999N+985zuJ45inn36am266idtvv32NdnyjXe9o3wdwHq777bcfF1xwwWrHC4/mu/a8887jxz/+MUcffTQf/OAHmTdvHsuXL+cXv/gFV111FW9729t417vexU9+8hPe8573cPjhh7Nw4UKuuuoqtttuuzV6dq/P92uk3/ej/azD2GpO15XDDz+c//qv/+KQQw7h+OOPZ8mSJXz7299myy235PHHH697fkcddRSXXXYZy5Yty6yk/v73vwPrLxOz44474nkeX/3qV+nu7qZYLLL//vvT1dXFhz70IT784Q9z1FFHcdBBB/GXv/yF22+/fUia/ZOf/CTXX389hxxyCB/96EczK6lZs2bVPaf29nauvPJKTjzxRN7+9rdz7LHHMmXKFF588UVuu+023vnOd/Ktb30r2//OO+/EWsu//Mu/rN2TWpvW/ryV1OoYzoLFWmfTNG/ePNvU1GTb2trs3Llz7Sc/+Un76quvZvssXrzYHn744batra3OUmikaw+2KEr51re+ZbfZZhsbBIGdOnWqPfPMM+2KFSuGrOmKK66wm2++uS0Wi3bnnXe29957r91nn33WaCX1/PPP26OPPtrOnj3blkol29zcbOfNm2evuuqqOmsKa531AmCPPfbY1Z7z0Ucftccdd5zdbLPNbLFYtF1dXfZd73qX/dOf/lS3XxRF9pJLLrHbbLONLRQKdsqUKfbQQw+tszgayUrqnnvusaeffrqdMGGCbW1ttSeccIJdtmzZsOu56aabLGBPP/30bNvChQuH2Pn8+Mc/tgcffLDt6uqyhULBbrbZZvaMM86wr732WrbPSO/T5ZdfbmfNmmWLxaJ9xzveYe+//347b948e8ghhww59uabb647Nl1L3iLkqaeesgceeKBtbW21kydPtqeddpr9y1/+MmS/4aykUuuilDAM7Xe+8x175JFHZmtsbm62O+20k73kkkvqrFjWZo3WWvujH/3I7rTTTrZYLNqJEyfaE044wb788st1+5x88sm2paXFDseqVavs8ccfbzs7Oy0wrG3OulhJDffvFrBnnXXWsM/rkksuqdv+3HPP2ZNOOslOmzbNBkFgN910U/uud73L/vjHP872Gem1GsxIFk8jsSZ7rZ/85CdWKWVffPHFUV1npNfji1/8ot10002t1nqtbKVGej/X5vrPPfec/dd//Vfb2dlpS6WSfcc73mFvvfXWIce+8MIL9ogjjrDNzc128uTJ9qMf/Whm3Tf48/DnP//Zvve977WTJk2yxWLRzpo1yx5zzDH2//7v/0b1vNbEE088Yd/znvdka956663t5z//+bp9Xn/9dXvWWWfZmTNn2iAI7LRp0+wBBxxgr7766myftfk3trp/H9Vq1X71q1+122+/vS0Wi3bChAl23rx59qKLLrLd3d3ZfsN97tdmvdaO/n345S9/aQF71VVXrfa1HM13rbXWLlu2zJ599tl20003tYVCwc6YMcOefPLJmf2TMcZ+5Stfyb7XdtppJ3vrrbfak08+eci/IYaxIhrt818TI/2+t3b0n/V1YSQ9kf5eWLp0ad324f7tfve737VbbbWVLRaLdptttrHXXHPNsL9X+vr67FlnnWUnTpxoW1tb7ZFHHmkXLFhgAfsf//Efa7z2SN/Vgy31vvOd79gtttjCep5X9/mK49h+6lOfspMnT7bNzc12/vz59tlnnx32HI8//rjdZ599bKlUsptuuqn94he/aL/73e8O+z1399132/nz59uOjg5bKpXsnDlz7CmnnDJEr7zvfe+ze+65p11b1kqcCuvGbbfdZpVS9vHHH99oaxjtHxZ5fvazn1nA3nvvvRtwZfXEcWwnTpxoP/ShD43bNYV/DqIosm95y1vs5z73uY29FEGo47zzzrMzZsyo8wgWGpc///nPFrA/+MEPNvZSNiivvfaaLZVK9mc/+9laHzt+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AHDHHXegLEts2bIl3vvMZz4TT3nKU7Bjx46DrnfllVdi9erV8diwYQMAoCixoDY02DcFMRdrUNHMqqd0fvNdYiUt1woAypGmrjP3ADnIvbEZyr/HVF7AtjkWQUh7FA5CCDkMFKeEkE7YtGkTrr32Wtx44434m7/5G9x33334zd/8TTz22GPYuXMnRqMR1qxZ03hm3bp12Llz50HXvOSSS7B79+54PPDAAwBct75o6L7PIo3WdeYX8z5VL0lYRv/QcG9ez2mduFV/TWqkbnkLn/73j2o6osC1guG+loWUleQelaf0Wy4DUVC3hgCIF7hddsAzrU8I6QKm9QkhnXDeeefFz8997nOxadMmnH766fjMZz6D2dnZx7XmeDzGeDxecL4ap3pSp+pczalmafUgCPMufnciCEI/qjTUkQYb1NzeyQvL8aOKeiwxzR4isoMDQHWCQI2impHUlCW+hrX2uX2bVKkabRrwB7EbSgzygQGK5NdKCCE9gZFTQsgxYc2aNfjFX/xF3HPPPVi/fj2m0ykeffTRxj27du1atEb1cJgayCc5NdLtYUSotKKWgaATWylobVtNaWqKsgOkv6bt+tGwbBDEYf0oMptlAZJNigrvbZQfxN+jea0T2K1PCOkAilNCyDFh7969uPfee3Hqqafi7LPPxnA4xC233BKv33333bj//vuxefPmn3ltM8UC0WmCpylS81NjylK4VqTzKfrqSgTaYlB8ur9cJagX8xsNpvl+QlUQw6aWZof+wUScAuqbt1Q01s26l7voq+0y/xUmRC3lIISQw8C0PiGkE/7oj/4Ir3jFK3D66afjRz/6ES6//HIURYHXvva1WL16Nd785jdj+/btOPHEEzE3N4d3vOMd2Lx580E79Q9FPePT4AZQVUgtKOYBWwB2KLBjBSZY0OkuKq621Ns2mQrJWzR0xgtgvV0URFJdaagzDR6r8PZVojCVwEwVxURgQyd/KVFsRguqVnkBbFa32io/kFpgqm679TkhihDSBRSnhJBO+L//+z+89rWvxU9+8hOccsopeMELXoCvfe1rOOWUUwAAH/nIR2CMwatf/WpMJhNs3boVH//4xx/Xu4JlU+PcwKfBw2jRzIAfQEq1x5rTLIoqiwirLK1ezCvMitRk5V6IKGpVADtKQlbzrv12J37bCSCrj21c9nuX7mxOCSGkEyhOCSGd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xR133IEtW7bEc8YYbNmyBTt27Oh0L7t37wYAnHjiiY3zn/rUp3DyySfj2c9+Ni655BLs37+/030RQsgTAY4vJYT0gocffhh1XS8Yc7pu3Trcddddne3DWot3vvOd+I3f+A08+9nPjud///d/H6effjpOO+00fPvb38Z73/te3H333fiXf/mXzvZGCCFPBChOCSHkKLJt2zZ85zvfwW233dY4/9a3vjV+fs5znoNTTz0VL3nJS3DvvffiF37hF7reJiGEHLcwrU8I6QUnn3wyiqLArl27Gud37dqF9evXd7KHCy+8EDfccAO+9KUv4clPfvIh7920aRMA4J577ulia4QQ8oSB4pQQ0gtGoxHOPvts3HLLLfGctRa33HILNm/evKzvVlVceOGF+Nd//VfceuutOOOMMw77zJ133gkAOPXUU5d1b4QQ8kSDaX1CSG/Yvn07LrjgApxzzjl4/vOfj6uuugr79u3Dm970pmV977Zt23D99dfjc5/7HFatWoWdO3cCAFavXo3Z2Vnce++9uP766/Gyl70MJ510Er797W/joosuwgtf+EI897nPXda9EULIEw1aSRFCesXHPvYx/Pmf/zl27tyJjRs34qMf/WhMoS8XIrLo+U984hN44xvfiAceeACvf/3r8Z3vfAf79u3Dhg0b8Nu//du49NJLMTc3t6x7I4SQJxoUp4QQQggh5LiBNaeEEEIIIeS4geKUEEIIIYQcN1CcEkIIIYSQ4waKU0IIIYQQctxAcUoIIYQQQo4bKE4JIYQQQshxA8UpIYQQQgg5bqA4JYQQQgghxw0Up4QQQggh5LiB4pQQQgghhBw3UJwSQgghhJDjhv8PsPEZ3+8YSPQAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Method 5, scipy.signal.ShortTimeFFT(fft_mode=centered, scale_to=magnitude)\n", + "\n", + "Avg power spectrogram = -16.40764725853183\n", + "signal_power_db=-16.418011027565242, noise_power_db=-42.635493968070875\n", + "total_power=-16.40764725853183\n", + "snr_db=26.217482940505633, snr_linear=418.550913426658, snr2=26.207094380762356\n", + "welch_signal_power_db=-16.41801118850708, welch_noise_power_db=-42.63549327850342\n" + ] + }, + { + "data": { + "image/png": "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mbh27Rp27dqFwMBA3HnnnejevbvUnfbDDz9IoQRo2Pf7yZMn0aFDB1lYrIkt7weRLTgrjaiRPP744xg9ejQMBgMGDhwo/Y/5emazGX5+fli9erXV11u0aGHzOTUajdXtomKMjKMEBQUBAPz9/au95ufnp7h1AihvEfj000/x008/4b///S+++eYbPPXUU5g7dy5++ukneHp6NrjetujcuTMiIiLw0UcfITExER999BHc3NwwZMgQq+X9/Pxw4MABfPPNN/j666/x9ddfY8WKFUhMTMSqVasarV51ffYmkwn3338/cnNz8dJLL6Fjx47w8PDAmTNn8OSTT8JsNis+Z69evVBaWor09HTs2rVLCkC9e/fGrl278Ntvv+HChQuyYNSY3++1uVF+Fuivjy1GRI3kkUcegVqtxk8//VRjNxoAtG3bFpcuXcI999wDvV5f7dG9e3epbENXI27dujWA8laOqkpKSpCVlSW9bvl6/PhxWbnS0lJkZWXVeZ6wsDC4urrizJkz1V47e/Zsg/743X333Xj99dexd+9erF69GocPH8batWsBlL+XZ8+eRW5ubr2Off31FhQU4Ny5c9VaaRITE7Ft2zacO3cOa9asQXx8fI1dXEB5i9xDDz2Ed999FydPnsTYsWPx4YcfVps1ZmF5/69/vaysTNZ6pcTBgwfxv//9D3PnzsVLL72EQYMGQa/XSyG2PiIjI+Hm5oZdu3bJgtG9996LjIwMpKWlSc8tlHy/X69t27Y4duwYB1CTQzEYETUST09PvPfee5g+fToeeuihGssNGTIEJpMJr732WrXXysrKZNOhPTw8rE6rt5Ver4ebmxsWLFgg+5/zsmXLYDQaER8fDwDo2bMnWrRogcWLF6OkpEQqt3LlSpvO37RpUzzwwAPYvXs3fvvtN2n70aNHsXv3btx///2K63758uVq/9sPDw8HAKk77bHHHoMQAjNmzKi2vy0tBUuWLJH90X3vvfdQVlaGgQMHysoNHz4cKpUKEyZMwO+//y7NRistLcVvv/2Gc+fOSWUvXbok21etVqNbt26yel+vZ8+euO222/DBBx+grKxM2r569ep6dwVZWlCqvg9CCNmyAbXJzs6WfZZA+WKmd911Fz7++GNkZ2fLWoyuXbuGBQsWoG3btggMDJT2UfL9fr3HHnsMFy9exMKFC6u9xpYgshd2pRE1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G7nFSBpjxBYjIqdgMCIiqof63ietLpyVRuRcDEZERPVQZFn12oWDr4luJgxGRET1UCx1pTXyGCOpK43T9YmcgcGIiKge7NViZLklSDFbjIicgsGIiKgeiu01xqgiaHHwNZFzMBgREdWDvdYx4k1kiZyLwYiIqB7stY4RF3gkci4GIyKierDbyteclUbkVAxGRET1YLlXmr0WeCzhTWSJnILBiIioHuy1wKMru9KInIrBiIioHirHGLErjehmUq9gtGjRIrRp0wY6nQ5RUVHYs2dPreXXr1+Pjh07QqfTISwsDJs3b5a9LoRASkoKAgMD4e7uDr1ej+PHj8vKvP7664iJiUGTJk3g4+NT7Ry//PILhg8fjuDgYLi7u6NTp06YP3++rMyOHTugUqmqPQwGQ33eBiK6hdnvliCclUbkTIp/otetW4fk5GRMmzYN+/btQ/fu3REXF4fz589bLb97924MHz4co0aNwv79+5GQkICEhAQcOnRIKjN79mwsWLAAixcvRkZGBjw8PBAXF4eioiKpTElJCQYPHoxnnnnG6nkyMzPh5+eHjz76CIcPH8a//vUvTJ48GQsXLqxW9tixYzh37pz08PPzU/o2ENEtzn4LPLIrjciphEKRkZFi3Lhx0nOTySSCgoJEamqq1fJDhgwR8fHxsm1RUVFi7NixQgghzGazCAgIEHPmzJFez8vLE1qtVnz88cfVjrdixQrh7e1tU12fffZZ0bdvX+n59u3bBQBx+fJlm/YXQoiioiJhNBqlxx9//CEACKPRaPMxiOjmM3jxbtH6pY1i4y9nG/W4y3/4XbR+aaMYtzqzUY9LdKszGo02/f1W1GJUUlKCzMxM6PV6aZtarYZer0d6errVfdLT02XlASAuLk4qn5WVBYPBICvj7e2NqKioGo9pK6PRiObNm1fbHh4ejsDAQNx///348ccfaz1GamoqvL29pUdwcHCD6kRENwd7rWMk3SuNXWlETqHoJ/rixYswmUzw9/eXbff3969xnI7BYKi1vOWrkmPaYvfu3Vi3bh3GjBkjbQsMDMTixYvx2Wef4bPPPkNwcDD69OmDffv21XicyZMnw2g0So8//vij3nUiopuHvW4i6yYFI07XJ3IGF2dXwB4OHTqEQYMGYdq0aejfv7+0vUOHDujQoYP0PCYmBidPnsTbb7+N//znP1aPpdVqodVq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v79o0aKFcHV1Fa1btxajR4+22x8fWyi5vokTJ0pl/f39xQMPPCD27dsnO95f9bMTQojffvtNABDffvtttWPdSJ9dTb8LLNeTlJQkYmNjq+0THh4u3NzcxB133CH7e2dR2/vV2FRCOHCeMxEREdENjOsYEREREVVgMCIiIiKqwGBEREREVIHBiIiIiKgCgxERERFRBQYjIiIiogoMRkREREQVGIyIiIjI6Xbu3ImHHnoIQUFBUKlU2LBhg6L9p0+fDpVKVe3h4eGh6DgMRkREROR0hYWF6N69OxYtWlSv/V944QWcO3dO9ujcuTMGDx6s6DgMRkREROR0AwcOxMyZM/HII49Yfb24uBgvvPACWrZsCQ8PD0RFRWHHjh3S656enggICJAeOTk5OHLkCEaNGqWoHgxGREREdMMbP3480tPTsXbtWvz6668YPHgwBgwYgOPHj1stv3TpUrRv3x69e/dWdB4GIyIiIrqhZWdnY8WKFVi/fj169+6Ntm3b4oUXXkCvXr2wYsWKauWLioqwevVqxa1FAODSGBUmIiIispeDBw/CZDKhffv2su3FxcW47bbbqpX/4osvcOXKFSQlJSk+F4MRERER3dAKCgqg0WiQmZkJjUYje83T07Na+aVLl+LBBx+Ev7+/4nMxGBEREdENrUePHjCZTDh//nydY4aysrKwfft2fPXVV/U6F4MREREROV1BQQFOnDghPc/KysKBAwfQvHlztG/fHiNGjEBiYiLmzp2LHj164MKFC0hLS0O3bt0QHx8v7bd8+XIEBgZi4MCB9aqHSgghGnw1RERERA2wY8cO9O3bt9r2pKQkrFy5EqWlpZg5cyY+/PBDnDlzBr6+vrj77rsxY8YMhIWFAQDMZjNat26NxMREvP766/WqB4MRERERUQVO1yciIiKqwGBEREREVIHBiIiIiKgCgxERERFRBQYjIiIiogoMRkREREQVGIyIiIiIKjAYEREREVVgMCIiIiKqwGBEREREVIHBiIiIiKjC/wMS7brcn3HO6AAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Method 6, scipy.signal.welch\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "(-16.418011027565242,\n", + " -42.635493968070875,\n", + " 26.217482940505633,\n", + " 26.207094380762356)" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_power_spectrogram(iq_samples, sample_rate, 256, annotation, tuning_frequency, verbose=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "upper_idx=240, lower_idx=16\n", + "Sxx.shape[0]=256\n", + "i=0, mini2_video, signal_power=-24.38874795953527, noise_power=-47.38528758195419, snr=22.99653962241892, snr2=22.974701172255514\n", + "upper_idx=240, lower_idx=16\n", + "Sxx.shape[0]=256\n", + "i=1, mini2_video, signal_power=-24.363572960206046, noise_power=-47.57630905696584, snr=23.212736096759794, snr2=23.191960712905924\n", + "upper_idx=240, lower_idx=16\n", + "Sxx.shape[0]=256\n", + "i=2, mini2_video, signal_power=-24.409451558470565, noise_power=-47.61442817550022, snr=23.204976617029658, snr2=23.1841639917392\n", + "upper_idx=240, lower_idx=16\n", + "Sxx.shape[0]=256\n", + "i=3, mini2_video, signal_power=-24.475922150779805, noise_power=-47.722086616252206, snr=23.2461644654724, snr2=23.225548758036787\n", + "upper_idx=177, lower_idx=106\n", + "Sxx.shape[0]=256\n", + "i=4, mini2_telem, signal_power=-16.418011027565242, noise_power=-42.635493968070875, snr=26.217482940505633, snr2=26.207094380762356\n", + "upper_idx=240, lower_idx=16\n", + "Sxx.shape[0]=256\n", + "i=5, mini2_video, signal_power=-24.523693030912636, noise_power=-48.25135752127288, snr=23.727664490360244, snr2=23.709216888549655\n", + "upper_idx=149, lower_idx=86\n", + "Sxx.shape[0]=256\n", + "i=6, mini2_telem, signal_power=-16.16917000438684, noise_power=-41.752061587903924, snr=25.582891583517085, snr2=25.57086627354871\n", + "upper_idx=240, lower_idx=16\n", + "Sxx.shape[0]=256\n", + "i=7, mini2_video, signal_power=-24.5325278669922, noise_power=-48.377676696227596, snr=23.845148829235395, snr2=23.827194597319043\n", + "upper_idx=150, lower_idx=82\n", + "Sxx.shape[0]=256\n", + "i=8, mini2_telem, signal_power=-16.235715733566828, noise_power=-41.46590384400939, snr=25.230188110442562, snr2=25.21714390749429\n", + "upper_idx=240, lower_idx=16\n", + "Sxx.shape[0]=256\n", + "i=9, mini2_video, signal_power=-24.728753609743666, noise_power=-48.6677151117491, snr=23.93896150200543, snr2=23.921391720772203\n" + ] + } + ], + "source": [ + "sample_rate = data_obj.metadata[\"global\"][\"core:sample_rate\"]\n", + "tuning_frequency = data_obj.metadata[\"captures\"][0][\"core:frequency\"]\n", + "for i in range(10):\n", + " annotation = data_obj.sigmf_obj.get_annotations()[i]\n", + " iq_samples = get_annotation_samples(data_obj, annotation)\n", + " signal_power, noise_power, snr, snr2 = get_power_spectrogram(iq_samples, sample_rate, 256, annotation, tuning_frequency, verbose=False)\n", + " print(f\"{i=}, {data_obj.sigmf_obj.get_annotations()[i]['core:label']}, {signal_power=}, {noise_power=}, {snr=}, {snr2=}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Avg power spectrogram = -34.85135316848755\n", + "# 1. Avg power spectrogram = -34.85135237006511\n", + "# 2. Avg power spectrogram = -35.267020197850925\n", + "# 3. Avg power spectrogram = -35.267020197850925" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SFT.p_min=0, SFT.p_max(len(iq_samples))=689\n", + "p0=0, p1=687, k_offset=16\n", + "fft_len = 32, power_estimate = -24.383972533977357\n", + "SFT.p_min=0, SFT.p_max(len(iq_samples))=345\n", + "p0=0, p1=343, k_offset=32\n", + "fft_len = 64, power_estimate = -24.377647483001397\n", + "SFT.p_min=0, SFT.p_max(len(iq_samples))=173\n", + "p0=0, p1=171, k_offset=64\n", + "fft_len = 128, power_estimate = -24.365006570802628\n", + "SFT.p_min=0, SFT.p_max(len(iq_samples))=87\n", + "p0=0, p1=85, k_offset=128\n", + "fft_len = 256, power_estimate = -24.367018774568102\n", + "SFT.p_min=0, SFT.p_max(len(iq_samples))=44\n", + "p0=0, p1=42, k_offset=256\n", + "fft_len = 512, power_estimate = -24.372761498502314\n", + "SFT.p_min=0, SFT.p_max(len(iq_samples))=22\n", + "p0=0, p1=21, k_offset=512\n", + "fft_len = 1024, power_estimate = -24.372761498502314\n", + "SFT.p_min=0, SFT.p_max(len(iq_samples))=12\n", + "p0=0, p1=10, k_offset=1024\n", + "fft_len = 2048, power_estimate = -24.396755856152026\n" + ] + } + ], + "source": [ + "fft_lens = [32, 64, 128, 256, 512, 1024, 2048]\n", + "\n", + "powers = []\n", + "for f in fft_lens: \n", + " p = get_power_spectrogram(iq_samples, sample_rate, f, verbose=False)\n", + " powers.append(p)\n", + " print(f\"fft_len = {f}, power_estimate = {p}\")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [], + "source": [ + "# psd\n", + "# (X*X) / N**2\n", + "\n", + "# complex \n", + "# X/N\n", + "\n", + "# magnitude\n", + "# abs(X/N)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "rfml", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/rfml/annotation_utils.py b/rfml/annotation_utils.py index 665bfcc..49a3a71 100644 --- a/rfml/annotation_utils.py +++ b/rfml/annotation_utils.py @@ -20,344 +20,97 @@ import warnings -def moving_average(complex_iq, avg_window_len): - return ( - np.convolve(np.abs(complex_iq) ** 2, np.ones(avg_window_len), "valid") - / avg_window_len - ) - # return ( - # np.abs(np.convolve(complex_iq, np.ones(avg_window_len), "valid") - # / avg_window_len) ** 2 - # ) - - -def power_squelch(iq_samples, threshold, avg_window_len): - avg_pwr = moving_average(iq_samples, avg_window_len) - avg_pwr_db = 10 * np.log10(avg_pwr) - - good_samples = np.zeros(len(iq_samples)) - good_samples[np.where(avg_pwr_db > threshold)] = 1 - - idx = ( - np.ediff1d(np.r_[0, good_samples == 1, 0]).nonzero()[0].reshape(-1, 2) - ) # gets indices where signal power above threshold - - return idx - - -def reset_annotations(data_obj): - data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY] = [] - data_obj.sigmf_obj.tofile(data_obj.sigmf_meta_filename, skip_validate=True) - print(f"Resetting annotations in {data_obj.sigmf_meta_filename}") - - -# def annotate_power_squelch( -# data_obj, -# threshold, -# avg_window_len, -# label=None, -# skip_validate=False, -# spectral_energy_threshold=False, -# dry_run=False, -# min_annotation_length=400, -# min_bandwidth=None, -# max_bandwidth=None, -# overwrite=True, -# max_annotations=None, -# dc_block=False, -# verbose=False, -# n_seek_samples=None, -# n_samples=None, -# set_bandwidth=None, -# ): -# # get I/Q samples -# iq_samples = data_obj.get_samples( -# n_seek_samples=n_seek_samples, n_samples=n_samples -# ) - -# # apply power squelch to I/Q samples using dB threshold -# idx = power_squelch(iq_samples, threshold=threshold, avg_window_len=avg_window_len) - -# # if overwrite, delete existing annotations -# if overwrite: -# data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY] = [] - -# if isinstance(spectral_energy_threshold, bool) and spectral_energy_threshold: -# spectral_energy_threshold = 0.94 - -# for start, stop in tqdm(idx[:max_annotations]): -# start, stop = int(start), int(stop) - -# # skip if proposed annotation length is less than min_annotation_length -# if min_annotation_length and (stop - start < min_annotation_length): -# continue - -# freq_edges = get_bandwidth(data_obj, iq_samples, start, stop, set_bandwidth, spectral_energy_threshold, dc_block, verbose, min_bandwidth, max_bandwidth, label) - -# if freq_edges is None: -# continue - -# freq_lower_edge, freq_upper_edge = freq_edges - -# metadata = { -# "core:freq_lower_edge": freq_lower_edge, -# "core:freq_upper_edge": freq_upper_edge, -# } -# if label: -# metadata["core:label"] = label - -# data_obj.sigmf_obj.add_annotation( -# n_seek_samples + start, length=stop - start, metadata=metadata -# ) - -# if not dry_run: -# data_obj.sigmf_obj.tofile( -# data_obj.sigmf_meta_filename, skip_validate=skip_validate -# ) -# print( -# f"Writing {len(data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY])} annotations to {data_obj.sigmf_meta_filename}" -# ) - - -# MAD estimator -def median_absolute_deviation(series): - mad = 1.4826 * np.median(np.abs(series - np.median(series))) - # sci_mad = scipy.stats.median_abs_deviation(series, scale="normal") - return np.median(series) + 6 * mad - - -def debug_plot( - avg_pwr_db, - mad, - threshold_db, - avg_duration, - data_obj, - guess_threshold_old, - force_threshold_db, - n_components=None, -): - n_components = n_components if n_components else 3 - - print(f"{np.max(avg_pwr_db)=}") - print(f"{np.mean(avg_pwr_db)=}") - print(f"median absolute deviation threshold = {mad}") - print(f"using threshold = {threshold_db}") - # print(f"{len(avg_pwr_db)=}") - # print(f"{len(avg_pwr_db)=}") - # print(f'{int(avg_duration * data_obj.metadata["global"]["core:sample_rate"])=}') - - #### - # Figure 1 - ### - plt.figure() - db_plot = avg_pwr_db[ - int(0 * data_obj.metadata["global"]["core:sample_rate"]) : int( - avg_duration * data_obj.metadata["global"]["core:sample_rate"] - ) - ] - # db_plot = avg_pwr_db - plt.plot( - np.arange(len(db_plot)) / data_obj.metadata["global"]["core:sample_rate"], - db_plot, - ) - plt.axhline(y=guess_threshold_old, color="g", linestyle="-", label="old threshold") - plt.axhline(y=np.mean(avg_pwr_db), color="r", linestyle="-", label="average") - plt.axhline( - y=mad, - color="b", - linestyle="-", - label="median absolute deviation threshold", - ) - if force_threshold_db: - plt.axhline( - y=force_threshold_db, - color="yellow", - linestyle="-", - label="force threshold db", - ) - plt.legend(loc="center left", bbox_to_anchor=(1, 0.5)) - plt.ylabel("dB") - plt.xlabel("time (seconds)") - plt.title("Signal Power") - plt.show() - - ### - # Figure 2 - ### - db_plot = avg_pwr_db[ - int(0 * data_obj.metadata["global"]["core:sample_rate"]) : int( - avg_duration * data_obj.metadata["global"]["core:sample_rate"] - ) - ] - start_time = time.time() - plt.figure() - sns.histplot(db_plot, kde=True) - plt.xlabel("dB") - plt.title(f"Signal Power Histogram & Density ({avg_duration} seconds)") - plt.show() - print(f"Plot time = {time.time()-start_time}") - - # fit a Gaussian Mixture Model with two components - start_time = time.time() - clf = mixture.GaussianMixture(n_components=n_components) - clf.fit(db_plot.reshape(-1, 1)) - print(f"Gaussian mixture model time = {time.time()-start_time}") - print(f"{clf.weights_=}") - print(f"{clf.means_=}") - print(f"{clf.covariances_=}") - print(f"{clf.converged_=}") - - ### - # Figure 3 - ### - db_plot = avg_pwr_db - start_time = time.time() - plt.figure() - sns.histplot(db_plot, kde=True) - plt.xlabel("dB") - plt.title(f"Signal Power Histogram & Density") - plt.show() - print(f"Plot time = {time.time()-start_time}") - - # fit a Gaussian Mixture Model with two components - start_time = time.time() - clf = mixture.GaussianMixture(n_components=n_components) - clf.fit(db_plot.reshape(-1, 1)) - print(f"Gaussian mixture model time = {time.time()-start_time}") - print(f"{clf.weights_=}") - print(f"{clf.means_=}") - print(f"{clf.covariances_=}") - print(f"{clf.converged_=}") - - ### - # Figure 4 - ### - plt.figure() - db_plot = avg_pwr_db[ - int(0 * data_obj.metadata["global"]["core:sample_rate"]) : int( - avg_duration * data_obj.metadata["global"]["core:sample_rate"] - ) - ] - # db_plot = avg_pwr_db - plt.plot( - np.arange(len(db_plot)) / data_obj.metadata["global"]["core:sample_rate"], - db_plot, - ) - plt.axhline(y=guess_threshold_old, color="g", linestyle="-", label="old threshold") - plt.axhline(y=np.mean(avg_pwr_db), color="r", linestyle="-", label="average") - plt.axhline( - y=mad, - color="b", - linestyle="-", - label="median absolute deviation threshold", - ) - plt.axhline( - y=np.min(clf.means_) - + 3 * np.sqrt(clf.covariances_[np.argmin(clf.means_)].squeeze()), - color="yellow", - linestyle="-", - label="gaussian mixture model estimate", - ) - if force_threshold_db: - plt.axhline( - y=force_threshold_db, - color="yellow", - linestyle="-", - label="force threshold db", - ) - plt.legend(loc="center left", bbox_to_anchor=(1, 0.5)) - plt.ylabel("dB") - plt.xlabel("time (seconds)") - plt.title("Signal Power") - plt.show() - - def annotate( data_obj, - # label, - avg_window_len, - avg_duration=-1, + avg_window_len=256, + debug_duration=0.25, debug=False, dry_run=False, - # min_annotation_length=400, bandwidth_estimation=True, - # spectral_energy_threshold=True, force_threshold_db=None, overwrite=True, - # min_bandwidth=None, - # max_bandwidth=None, max_annotations=None, dc_block=None, verbose=False, time_start_stop=None, - set_bandwidth=None, labels=None, + power_estimate_duration=1, # only process n seconds of I/Q samples at a time + n_components=None, + n_init=1, + fft_len=256, ): - time_chunk = 1 # only process n seconds of I/Q samples at a time - sample_rate = data_obj.metadata["global"]["core:sample_rate"] # set n_seek_samples (skip n samples at start) and n_samples (process n samples) - if isinstance(time_start_stop, int) and time_start_stop > 0: - n_seek_samples = int(sample_rate * time_start_stop) - n_samples = -1 - elif isinstance(time_start_stop, Iterable): - if len(time_start_stop) != 2 or time_start_stop[1] < time_start_stop[0]: + # if isinstance(time_start_stop, int) and time_start_stop > 0: + # n_seek_samples = int(sample_rate * time_start_stop) + # n_samples = -1 + if isinstance(time_start_stop, Iterable): + if len(time_start_stop) != 2: # or time_start_stop[1] < time_start_stop[0]: + raise ValueError + + if time_start_stop[0] is None: + time_start_stop = (0, time_start_stop[1]) + + if time_start_stop[1] is None: + n_samples = -1 + elif time_start_stop[1] < time_start_stop[0]: raise ValueError + else: + n_samples = int(sample_rate * (time_start_stop[1] - time_start_stop[0])) n_seek_samples = int(sample_rate * time_start_stop[0]) - n_samples = int(sample_rate * (time_start_stop[1] - time_start_stop[0])) + else: n_seek_samples = 0 n_samples = -1 if n_samples > -1: sample_idxs = np.arange( - n_seek_samples, n_seek_samples + n_samples, sample_rate * time_chunk + n_seek_samples, + n_seek_samples + n_samples, + sample_rate * power_estimate_duration, ) else: sample_idxs = np.arange( - n_seek_samples, data_obj.sigmf_obj.sample_count, sample_rate * time_chunk + n_seek_samples, + data_obj.sigmf_obj.sample_count, + sample_rate * power_estimate_duration, ) # if overwrite, delete existing annotations if overwrite: data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY] = [] - # if isinstance(spectral_energy_threshold, bool) and spectral_energy_threshold: - # spectral_energy_threshold = 0.94 - + if n_components is None: + n_components = len(labels) + 1 if labels else 2 + n_annotations = 0 - # i = 0 for sample_idx in tqdm(sample_idxs): - # i += 1 - # if i >= 2: - # break if n_samples > -1: get_n_samples = min( - sample_rate * time_chunk, n_samples - (sample_idx - n_seek_samples) + sample_rate * power_estimate_duration, + n_samples - (sample_idx - n_seek_samples), ) else: - get_n_samples = sample_rate * time_chunk + get_n_samples = min( + data_obj.sigmf_obj.sample_count - sample_idx, + sample_rate * power_estimate_duration, + ) iq_samples = data_obj.get_samples( - n_seek_samples=sample_idx, n_samples=get_n_samples + n_seek_samples=int(sample_idx), n_samples=int(get_n_samples) ) - + if iq_samples is None: break iq_samples = scipy.signal.detrend( - iq_samples, type="linear", bp=np.arange(0, len(iq_samples), 1024) + iq_samples, type="linear", bp=np.arange(0, len(iq_samples), 1024) # 1024) ) - # iq_samples = cupyx.scipy.signal.detrend( - # cupy.asarray(iq_samples), type="linear", bp=np.arange(0, len(iq_samples), 1024) - # ) - # iq_samples = cupy.asnumpy(iq_samples) - # set dB threshold (1. manually set, 2. calculate using median absolute deviation) if force_threshold_db: threshold_db = force_threshold_db else: @@ -367,34 +120,41 @@ def annotate( # current threshold in custom_handler guess_threshold_old = (np.max(avg_pwr_db) + np.mean(avg_pwr_db)) / 2 - mad = median_absolute_deviation(avg_pwr_db) - tqdm.write(f"Estimating noise floor for signal detection (may take a while)...") - n_components = len(labels)+1 if labels else 3 - clf = mixture.GaussianMixture(n_components=n_components) + tqdm.write( + f"Estimating noise floor for signal detection (may take a while)..." + ) + + clf = mixture.GaussianMixture(n_components=n_components, n_init=n_init) clf.fit(avg_pwr_db.reshape(-1, 1)) # TODO: add standard deviation parameter (was 2 *) - gaussian_mixture_model_estimate = np.min(clf.means_) + 3 * np.sqrt( + gaussian_mixture_model_estimate = np.min(clf.means_) + 2 * np.sqrt( clf.covariances_[np.argmin(clf.means_)].squeeze() ) - threshold_db = gaussian_mixture_model_estimate # mad + if verbose: + print(f"\n{gaussian_mixture_model_estimate=}") + print(f"{clf.weights_=}") + print(f"{clf.means_=}") + print(f"{clf.covariances_=}") + print(f"{clf.converged_=}\n") + + threshold_db = gaussian_mixture_model_estimate if debug: - print(f"debug") + print(f"Debug") debug_plot( avg_pwr_db, mad, threshold_db, - avg_duration, + debug_duration, data_obj, guess_threshold_old, force_threshold_db, n_components=n_components, ) - # print(f"Using dB threshold = {threshold_db} for detecting signals to annotate") tqdm.write( f"Using dB threshold = {threshold_db} for detecting signals to annotate" ) @@ -404,8 +164,6 @@ def annotate( iq_samples, threshold=threshold_db, avg_window_len=avg_window_len ) - - # j = 0 for start, stop in tqdm(idx[:max_annotations]): candidate_labels = list(labels.keys()) @@ -415,42 +173,59 @@ def annotate( annotation_n_samples = stop - start annotation_seconds = annotation_n_samples / sample_rate + if verbose: + print( + f"\nAnnotation start={(int(sample_idx) + start)/sample_rate}, stop={(int(sample_idx) + stop)/sample_rate}" + ) + for label in candidate_labels[:]: if "annotation_seconds" in labels[label]: - min_annotation_seconds, max_annotation_seconds = labels[label]["annotation_seconds"] - if min_annotation_seconds and (annotation_seconds < min_annotation_seconds): + min_annotation_seconds, max_annotation_seconds = labels[label][ + "annotation_seconds" + ] + if min_annotation_seconds and ( + annotation_seconds < min_annotation_seconds + ): candidate_labels.remove(label) - # if verbose: - # print( - # f"min_annotation_seconds not satisfied for {label}: {annotation_seconds} < {min_annotation_seconds}" - # ) + if verbose: + print( + f"min_annotation_seconds not satisfied for {label}: {annotation_seconds} < {min_annotation_seconds}" + ) continue - if max_annotation_seconds and (annotation_seconds > max_annotation_seconds): + if max_annotation_seconds and ( + annotation_seconds > max_annotation_seconds + ): candidate_labels.remove(label) if verbose: print( f"max_annotation_seconds not satisfied for {label}: {annotation_seconds} > {max_annotation_seconds}" ) continue - + if "annotation_length" in labels[label]: - min_annotation_length, max_annotation_length = labels[label]["annotation_length"] + min_annotation_length, max_annotation_length = labels[label][ + "annotation_length" + ] # skip if proposed annotation length is less than min_annotation_length - if min_annotation_length and (annotation_n_samples < min_annotation_length): + if min_annotation_length and ( + annotation_n_samples < min_annotation_length + ): candidate_labels.remove(label) if verbose: print( f"min_annotation_length not satisfied for {label}: {annotation_n_samples} < {min_annotation_length}" ) continue - if max_annotation_length and (annotation_n_samples > max_annotation_length): + if max_annotation_length and ( + annotation_n_samples > max_annotation_length + ): candidate_labels.remove(label) if verbose: print( f"max_annotation_length not satisfied for {label}: {annotation_n_samples} > {max_annotation_length}" ) continue - + if len(candidate_labels) == 0: continue @@ -460,29 +235,32 @@ def annotate( for label in candidate_labels[:]: if "set_bandwidth" in labels[label]: freq_edges = [ - data_obj.metadata["captures"][0]["core:frequency"] + labels[label]["set_bandwidth"][0], - data_obj.metadata["captures"][0]["core:frequency"] + labels[label]["set_bandwidth"][1] + data_obj.metadata["captures"][0]["core:frequency"] + + labels[label]["set_bandwidth"][0], + data_obj.metadata["captures"][0]["core:frequency"] + + labels[label]["set_bandwidth"][1], ] candidate_labels = [label] break if freq_edges is None: + if bandwidth_estimation and annotation_n_samples < fft_len: + if verbose: + print( + f"annotation length smaller than FFT size {annotation_n_samples} < {fft_len}" + ) + continue + freq_edges = get_bandwidth( data_obj, iq_samples, start, stop, - # set_bandwidth, bandwidth_estimation, - # spectral_energy_threshold, dc_block, - verbose, - # min_bandwidth, - # max_bandwidth, - # label, + debug, + fft_len=fft_len, ) - # if freq_edges is None: - # continue freq_lower_edge, freq_upper_edge = freq_edges @@ -508,26 +286,26 @@ def annotate( if len(candidate_labels) == 0: continue - elif len(candidate_labels) > 1: - warnings.warn(f"Multiple labels are possible {candidate_labels}. Using first label {candidate_labels[0]}.") + elif len(candidate_labels) > 1: + warnings.warn( + f"Multiple labels are possible {candidate_labels}. Using first label {candidate_labels[0]}." + ) metadata = { "core:freq_lower_edge": freq_lower_edge, "core:freq_upper_edge": freq_upper_edge, } - # if label: - # metadata["core:label"] = label + metadata["core:label"] = candidate_labels[0] data_obj.sigmf_obj.add_annotation( int(sample_idx) + start, length=stop - start, metadata=metadata ) - n_annotations += 1 - # j += 1 + if verbose: + print(f"Adding annotation {metadata}\n") - # if j > 15: - # break + n_annotations += 1 if not dry_run and n_annotations: data_obj.sigmf_obj.tofile(data_obj.sigmf_meta_filename, skip_validate=True) @@ -541,56 +319,30 @@ def get_bandwidth( iq_samples, start, stop, - # set_bandwidth, bandwidth_estimation, - # spectral_energy_threshold, dc_block, - verbose, - # min_bandwidth, - # max_bandwidth, - # label, + debug, + fft_len=256, ): - # set bandwidth using user supplied set_bandwidth - - # if set_bandwidth: - # freq_lower_edge = ( - # data_obj.metadata["captures"][0]["core:frequency"] - set_bandwidth / 2 - # ) - # freq_upper_edge = ( - # data_obj.metadata["captures"][0]["core:frequency"] + set_bandwidth / 2 - # ) - # estimate bandwidth using spectral energy thresholding - # if isinstance(spectral_energy_threshold, float): + if isinstance(bandwidth_estimation, bool) and bandwidth_estimation: freq_lower_edge, freq_upper_edge = get_occupied_bandwidth_gmm( iq_samples[start:stop], data_obj.metadata["global"]["core:sample_rate"], data_obj.metadata["captures"][0]["core:frequency"], - # spectral_energy_threshold=spectral_energy_threshold, dc_block=dc_block, - verbose=verbose, + debug=debug, + fft_len=fft_len, ) - # bandwidth = freq_upper_edge - freq_lower_edge - # if min_bandwidth and bandwidth < min_bandwidth: - # if verbose: - # print( - # f"min_bandwidth - Skipping, {start=}, {stop=}, {bandwidth=}, {freq_upper_edge=}, {freq_lower_edge=}" - # ) - # # print(f"Skipping, {label}, {start=}, {stop=}, {bandwidth=}, {freq_upper_edge=}, {freq_lower_edge=}") - # return None - # if max_bandwidth and bandwidth > max_bandwidth: - # if verbose: - # print( - # f"max_bandwidth - Skipping, {start=}, {stop=}, {bandwidth=}, {freq_upper_edge=}, {freq_lower_edge=}" - # ) - # return None + elif isinstance(bandwidth_estimation, float): freq_lower_edge, freq_upper_edge = get_occupied_bandwidth_spectral_threshold( iq_samples[start:stop], data_obj.metadata["global"]["core:sample_rate"], data_obj.metadata["captures"][0]["core:frequency"], spectral_energy_threshold=bandwidth_estimation, - + debug=debug, + fft_len=fft_len, ) # set bandwidth as full capture bandwidth else: @@ -605,11 +357,14 @@ def get_bandwidth( return [freq_lower_edge, freq_upper_edge] + def get_occupied_bandwidth_spectral_threshold( samples, sample_rate, center_frequency, spectral_energy_threshold, + debug, + fft_len=256, ): f, t, Sxx = cupyx_spectrogram( samples, @@ -618,7 +373,7 @@ def get_occupied_bandwidth_spectral_threshold( scaling="spectrum", # mode="complex", detrend=False, - window=cupyx.scipy.signal.windows.boxcar(256), + window=cupyx.scipy.signal.windows.boxcar(fft_len), ) freq_power = cupy.median(cupy.fft.fftshift(Sxx, axes=0), axis=1) @@ -630,16 +385,16 @@ def get_occupied_bandwidth_spectral_threshold( while True: if ( - freq_power_normalized[lower_idx : upper_idx].sum() + freq_power_normalized[lower_idx:upper_idx].sum() <= spectral_energy_threshold ): break - if freq_power_normalized[lower_idx] < freq_power_normalized[upper_idx-1]: + if freq_power_normalized[lower_idx] < freq_power_normalized[upper_idx - 1]: lower_idx += 1 - else: - upper_idx -= 1 - + else: + upper_idx -= 1 + freq_upper_edge = ( center_frequency - (freq_power.shape[0] / 2 - upper_idx) / freq_power.shape[0] * sample_rate @@ -649,23 +404,58 @@ def get_occupied_bandwidth_spectral_threshold( - (freq_power.shape[0] / 2 - lower_idx) / freq_power.shape[0] * sample_rate ) - return freq_lower_edge, freq_upper_edge + if debug: + max_power_idx = int(cupy.asnumpy(freq_power_normalized.argmax(axis=0))) + + print( + f"\nEstimated frequency edges {freq_lower_edge=}, {freq_upper_edge=}, {lower_idx=}, {upper_idx=}\n" + ) + ### + # Figure 1 + ### + fig, axs = plt.subplots(1, 3) + axs[0].imshow( + cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0))), + origin="lower", + ) + axs[0].axhline(y=upper_idx, color="r", linestyle="-") + axs[0].axhline(y=lower_idx, color="r", linestyle="-") + # axs[0].pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) + # plt.ylabel('Frequency [Hz]') + # plt.xlabel('Time [sec]') + axs[1].imshow( + np.tile( + np.expand_dims( + cupy.asnumpy(cupy.median(cupy.fft.fftshift(Sxx, axes=0), axis=1)), 1 + ), + 25, + ), + origin="lower", + ) + # axs[1].axhline(y = upper_idx, color = 'r', linestyle = '-') + # axs[1].axhline(y = lower_idx, color = 'g', linestyle = '-') + + axs[2].imshow( + np.tile(np.expand_dims(cupy.asnumpy(freq_power_normalized), 1), 25), + origin="lower", + ) + axs[2].axhline(y=max_power_idx, color="pink", linestyle="-") + axs[2].axhline(y=upper_idx, color="r", linestyle="-") + axs[2].axhline(y=lower_idx, color="r", linestyle="-") + plt.show() + return freq_lower_edge, freq_upper_edge - def get_occupied_bandwidth_gmm( samples, sample_rate, center_frequency, - # spectral_energy_threshold=None, dc_block=False, - verbose=False, + debug=False, + fft_len=256, ): - # if not spectral_energy_threshold: - # spectral_energy_threshold = 0.94 - f, t, Sxx = cupyx_spectrogram( samples, fs=sample_rate, @@ -673,16 +463,13 @@ def get_occupied_bandwidth_gmm( scaling="spectrum", # mode="complex", detrend=False, - window=cupyx.scipy.signal.windows.boxcar(256), + window=cupyx.scipy.signal.windows.boxcar(fft_len), ) - # cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=1) # Sxx = np.abs(Sxx)**2 # freq_power = cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0)) - freq_power = cupy.median(cupy.fft.fftshift(Sxx, axes=0), axis=1) - # freq_power = cupy.median(cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=2, mode="reflect"), axis=1) # lessen DC @@ -712,12 +499,6 @@ def get_occupied_bandwidth_gmm( lower_idx = freq_bounds[0] upper_idx = freq_bounds[1] - # plt.figure() - # plt.imshow(cupy.asnumpy(10*cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) - # plt.axhline(y=freq_bounds[0], color="r", linestyle="-") - # plt.axhline(y=freq_bounds[1], color="r", linestyle="-") - # plt.show() - freq_upper_edge = ( center_frequency - (freq_power.shape[0] / 2 - upper_idx) / freq_power.shape[0] * sample_rate @@ -727,20 +508,23 @@ def get_occupied_bandwidth_gmm( - (freq_power.shape[0] / 2 - lower_idx) / freq_power.shape[0] * sample_rate ) - if verbose: + if debug: max_power_idx = int(cupy.asnumpy(freq_power_normalized.argmax(axis=0))) - print(f"\n{lower_idx=}, {upper_idx=}\n") + print( + f"\nEstimated frequency edges {freq_lower_edge=}, {freq_upper_edge=}, {lower_idx=}, {upper_idx=}\n" + ) ### # Figure 1 ### - # print(f"{freq_power_normalized[lower_idx]=}") - # print(f"{freq_power_normalized[upper_idx]=}") - # print(f"{freq_power_normalized=}") + fig, axs = plt.subplots(1, 3) - axs[0].imshow(cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) + axs[0].imshow( + cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0))), + origin="lower", + ) axs[0].axhline(y=upper_idx, color="r", linestyle="-") - axs[0].axhline(y=lower_idx, color="g", linestyle="-") + axs[0].axhline(y=lower_idx, color="r", linestyle="-") # axs[0].pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) # plt.ylabel('Frequency [Hz]') # plt.xlabel('Time [sec]') @@ -750,102 +534,104 @@ def get_occupied_bandwidth_gmm( cupy.asnumpy(cupy.median(cupy.fft.fftshift(Sxx, axes=0), axis=1)), 1 ), 25, - ) + ), + origin="lower", ) # axs[1].axhline(y = upper_idx, color = 'r', linestyle = '-') # axs[1].axhline(y = lower_idx, color = 'g', linestyle = '-') axs[2].imshow( - np.tile(np.expand_dims(cupy.asnumpy(freq_power_normalized), 1), 25) + np.tile(np.expand_dims(cupy.asnumpy(freq_power_normalized), 1), 25), + origin="lower", ) axs[2].axhline(y=max_power_idx, color="pink", linestyle="-") axs[2].axhline(y=upper_idx, color="r", linestyle="-") - axs[2].axhline(y=lower_idx, color="g", linestyle="-") + axs[2].axhline(y=lower_idx, color="r", linestyle="-") plt.show() - ### - # Figure 2 - ### - start_time = time.time() - plt.figure() - sns.histplot(cupy.asnumpy(freq_power), kde=True) - plt.xlabel("power") - plt.title(f"Occupied Bandwidth Signal Power Histogram & Density") - plt.show() - print(f"Plot time = {time.time()-start_time}") + # ### + # # Figure 2 + # ### + # start_time = time.time() + # plt.figure() + # sns.histplot(cupy.asnumpy(freq_power), kde=True) + # plt.xlabel("power") + # plt.title(f"Occupied Bandwidth Signal Power Histogram & Density") + # plt.show() + # print(f"Plot time = {time.time()-start_time}") + + # ### + # # Figure 3 + # ### + # start_time = time.time() + # plt.figure() + # sns.histplot(cupy.asnumpy(freq_power_normalized), kde=True) + # plt.xlabel("power") + # plt.title(f"Normalized Occupied Bandwidth Signal Power Histogram & Density") + # plt.show() + # print(f"Plot time = {time.time()-start_time}") + + # ### + # # Figure 4 + # ### + # start_time = time.time() + # plt.figure() + # sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power)), kde=True) + # plt.xlabel("dB") + # plt.title(f"10*cupy.log10(freq_power)") + # plt.show() + # print(f"Plot time = {time.time()-start_time}") + + # ### + # # Figure 5 + # ### + # start_time = time.time() + # plt.figure() + # sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized)), kde=True) + # plt.xlabel("dB") + # plt.title(f"10*cupy.log10(freq_power_normalized)") + # plt.show() + # print(f"Plot time = {time.time()-start_time}") + + # ### + # # Figure 6 + # ### + # start_time = time.time() + # plt.figure() + # sns.histplot( + # cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0))).flatten(), + # kde=True, + # ) + # plt.xlabel("dB") + # plt.title(f"10*cupy.log10(cupy.fft.fftshift(Sxx, axes=0))") + # plt.show() + # print(f"Plot time = {time.time()-start_time}") + + # ### + # # Figure 7 + # ### + # start_time = time.time() + # plt.figure() + # plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power))) + # plt.xlabel("frequency") + # plt.ylabel("power") + # plt.title(f"10*cupy.log10(freq_power)") + # plt.show() + # print(f"Plot time = {time.time()-start_time}") ### - # Figure 3 + # Figure 8 ### - start_time = time.time() - plt.figure() - sns.histplot(cupy.asnumpy(freq_power_normalized), kde=True) - plt.xlabel("power") - plt.title(f"Normalized Occupied Bandwidth Signal Power Histogram & Density") - plt.show() - print(f"Plot time = {time.time()-start_time}") + # start_time = time.time() + # plt.figure() + # plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized))) + # plt.xlabel("frequency") + # plt.ylabel("power") + # plt.title(f"10*cupy.log10(freq_power_normalized)") + # plt.show() + # print(f"Plot time = {time.time()-start_time}") - ### - # Figure 4 - ### - start_time = time.time() - plt.figure() - sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power)), kde=True) - plt.xlabel("dB") - plt.title(f"10*cupy.log10(freq_power)") - plt.show() - print(f"Plot time = {time.time()-start_time}") - - ### - # Figure 5 - ### - start_time = time.time() - plt.figure() - sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized)), kde=True) - plt.xlabel("dB") - plt.title(f"10*cupy.log10(freq_power_normalized)") - plt.show() - print(f"Plot time = {time.time()-start_time}") - - ### - # Figure 6 - ### - start_time = time.time() - plt.figure() - sns.histplot( - cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0))).flatten(), - kde=True, - ) - plt.xlabel("dB") - plt.title(f"10*cupy.log10(cupy.fft.fftshift(Sxx, axes=0))") - plt.show() - print(f"Plot time = {time.time()-start_time}") - - ### - # Figure 7 - ### - start_time = time.time() - plt.figure() - plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power))) - plt.xlabel("frequency") - plt.ylabel("power") - plt.title(f"10*cupy.log10(freq_power)") - plt.show() - print(f"Plot time = {time.time()-start_time}") - - ### - # Figure 8 - ### - start_time = time.time() - plt.figure() - plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized))) - plt.xlabel("frequency") - plt.ylabel("power") - plt.title(f"10*cupy.log10(freq_power_normalized)") - plt.show() - print(f"Plot time = {time.time()-start_time}") - - # fit a Gaussian Mixture Model with two components + # fit a Gaussian Mixture Model with two components start_time = time.time() clf = mixture.GaussianMixture(n_components=2) predictions = clf.fit_predict( @@ -872,335 +658,237 @@ def get_occupied_bandwidth_gmm( #### #### - signal_predictions = np.zeros(len(predictions)) - signal_predictions[np.where(predictions == np.argmax(clf.means_))] = 1 - - signal_predictions_idx = ( - np.ediff1d(np.r_[0, signal_predictions == 1, 0]).nonzero()[0].reshape(-1, 2) - ) # gets indices where signal power above threshold - - freq_bounds = signal_predictions_idx[ - np.argmax( - np.abs(signal_predictions_idx[:, 0] - signal_predictions_idx[:, 1]) - ) - ] - print(f"{signal_predictions_idx.shape=}") - print(f"{signal_predictions_idx=}") - plt.figure() - plt.imshow(cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) - plt.axhline(y=freq_bounds[0], color="r", linestyle="-") - plt.axhline(y=freq_bounds[1], color="r", linestyle="-") - plt.show() + # signal_predictions = np.zeros(len(predictions)) + # signal_predictions[np.where(predictions == np.argmax(clf.means_))] = 1 + + # signal_predictions_idx = ( + # np.ediff1d(np.r_[0, signal_predictions == 1, 0]).nonzero()[0].reshape(-1, 2) + # ) # gets indices where signal power above threshold + + # freq_bounds = signal_predictions_idx[ + # np.argmax( + # np.abs(signal_predictions_idx[:, 0] - signal_predictions_idx[:, 1]) + # ) + # ] + # print(f"{signal_predictions_idx.shape=}") + # print(f"{signal_predictions_idx=}") + # plt.figure() + # plt.imshow(cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0))), origin='lower') + # plt.axhline(y=freq_bounds[0], color="r", linestyle="-") + # plt.axhline(y=freq_bounds[1], color="r", linestyle="-") + # plt.show() return freq_lower_edge, freq_upper_edge - ##### - max_power_idx = int(cupy.asnumpy(freq_power_normalized.argmax(axis=0))) - lower_idx = max_power_idx - upper_idx = max_power_idx - # print(f"{max_power_idx=}") - while True: - # print(f"{lower_idx=}, {upper_idx=}, {freq_power_normalized[lower_idx]=}, {freq_power_normalized[upper_idx]=}, {spectral_energy_threshold=}") - if upper_idx == freq_power_normalized.shape[0] - 1: - lower_idx -= 1 - elif lower_idx == 0: - upper_idx += 1 - elif ( - freq_power_normalized[lower_idx - 1] > freq_power_normalized[upper_idx + 1] - ): - lower_idx -= 1 - else: - upper_idx += 1 - if ( - freq_power_normalized[lower_idx : upper_idx + 1].sum() - >= spectral_energy_threshold - ): - break +def moving_average(complex_iq, avg_window_len): + return ( + np.convolve(np.abs(complex_iq) ** 2, np.ones(avg_window_len), "valid") + / avg_window_len + ) - if lower_idx == 0 and upper_idx == freq_power_normalized.shape[0] - 1: - print( - f"Could not find spectral energy threshold - max was: {freq_power_normalized[lower_idx:upper_idx].sum()}" - ) - break - freq_upper_edge = ( - center_frequency - - (freq_power.shape[0] / 2 - upper_idx) / freq_power.shape[0] * sample_rate - ) - freq_lower_edge = ( - center_frequency - - (freq_power.shape[0] / 2 - lower_idx) / freq_power.shape[0] * sample_rate - ) +def power_squelch(iq_samples, threshold, avg_window_len): + avg_pwr = moving_average(iq_samples, avg_window_len) + avg_pwr_db = 10 * np.log10(avg_pwr) - if verbose: + good_samples = np.zeros(len(iq_samples)) + good_samples[np.where(avg_pwr_db > threshold)] = 1 - print(f"\n{lower_idx=}, {upper_idx=}\n") - ### - # Figure 1 - ### - # print(f"{freq_power_normalized[lower_idx]=}") - # print(f"{freq_power_normalized[upper_idx]=}") - # print(f"{freq_power_normalized=}") - fig, axs = plt.subplots(1, 3) - axs[0].imshow(cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) - axs[0].axhline(y=upper_idx, color="r", linestyle="-") - axs[0].axhline(y=lower_idx, color="g", linestyle="-") - # axs[0].pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) - # plt.ylabel('Frequency [Hz]') - # plt.xlabel('Time [sec]') - axs[1].imshow( - np.tile( - np.expand_dims( - cupy.asnumpy(cupy.median(cupy.fft.fftshift(Sxx, axes=0), axis=1)), 1 - ), - 25, - ) - ) - # axs[1].axhline(y = upper_idx, color = 'r', linestyle = '-') - # axs[1].axhline(y = lower_idx, color = 'g', linestyle = '-') + idx = ( + np.ediff1d(np.r_[0, good_samples == 1, 0]).nonzero()[0].reshape(-1, 2) + ) # gets indices where signal power above threshold - axs[2].imshow( - np.tile(np.expand_dims(cupy.asnumpy(freq_power_normalized), 1), 25) - ) - axs[2].axhline(y=max_power_idx, color="pink", linestyle="-") - axs[2].axhline(y=upper_idx, color="r", linestyle="-") - axs[2].axhline(y=lower_idx, color="g", linestyle="-") - plt.show() + return idx - ### - # Figure 2 - ### - start_time = time.time() - plt.figure() - sns.histplot(cupy.asnumpy(freq_power), kde=True) - plt.xlabel("power") - plt.title(f"Occupied Bandwidth Signal Power Histogram & Density") - plt.show() - print(f"Plot time = {time.time()-start_time}") - ### - # Figure 3 - ### - start_time = time.time() - plt.figure() - sns.histplot(cupy.asnumpy(freq_power_normalized), kde=True) - plt.xlabel("power") - plt.title(f"Normalized Occupied Bandwidth Signal Power Histogram & Density") - plt.show() - print(f"Plot time = {time.time()-start_time}") +def reset_annotations(data_obj): + data_obj.sigmf_obj._metadata[data_obj.sigmf_obj.ANNOTATION_KEY] = [] + data_obj.sigmf_obj.tofile(data_obj.sigmf_meta_filename, skip_validate=True) + print(f"Resetting annotations in {data_obj.sigmf_meta_filename}") - ### - # Figure 4 - ### - start_time = time.time() - plt.figure() - sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power)), kde=True) - plt.xlabel("dB") - plt.title(f"10*cupy.log10(freq_power)") - plt.show() - print(f"Plot time = {time.time()-start_time}") - ### - # Figure 5 - ### - start_time = time.time() - plt.figure() - sns.histplot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized)), kde=True) - plt.xlabel("dB") - plt.title(f"10*cupy.log10(freq_power_normalized)") - plt.show() - print(f"Plot time = {time.time()-start_time}") +# MAD estimator +def median_absolute_deviation(series): + mad = 1.4826 * np.median(np.abs(series - np.median(series))) + # sci_mad = scipy.stats.median_abs_deviation(series, scale="normal") + return np.median(series) + 6 * mad - ### - # Figure 6 - ### - start_time = time.time() - plt.figure() - sns.histplot( - cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0))).flatten(), - kde=True, - ) - plt.xlabel("dB") - plt.title(f"10*cupy.log10(cupy.fft.fftshift(Sxx, axes=0))") - plt.show() - print(f"Plot time = {time.time()-start_time}") - ### - # Figure 7 - ### - start_time = time.time() - plt.figure() - plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power))) - plt.xlabel("frequency") - plt.ylabel("power") - plt.title(f"10*cupy.log10(freq_power)") - plt.show() - print(f"Plot time = {time.time()-start_time}") +def debug_plot( + avg_pwr_db, + mad, + threshold_db, + debug_duration, + data_obj, + guess_threshold_old, + force_threshold_db, + n_components=None, +): + n_components = n_components if n_components else 3 - ### - # Figure 8 - ### - start_time = time.time() - plt.figure() - plt.plot(cupy.asnumpy(10 * cupy.log10(freq_power_normalized))) - plt.xlabel("frequency") - plt.ylabel("power") - plt.title(f"10*cupy.log10(freq_power_normalized)") - plt.show() - print(f"Plot time = {time.time()-start_time}") + print(f"Using threshold = {threshold_db} dB") - # fit a Gaussian Mixture Model with two components - start_time = time.time() - clf = mixture.GaussianMixture(n_components=2) - predictions = clf.fit_predict( - cupy.asnumpy(10 * cupy.log10(freq_power_normalized)).reshape(-1, 1) + #### + # Figure 1 + ### + plt.figure() + db_plot = avg_pwr_db[ + int(0 * data_obj.metadata["global"]["core:sample_rate"]) : int( + debug_duration * data_obj.metadata["global"]["core:sample_rate"] ) - # predictions = clf.fit_predict(cupy.asnumpy(freq_power_normalized).reshape(-1, 1)) - print(f"Gaussian mixture model time = {time.time()-start_time}") - print(f"{clf.weights_=}") - print(f"{clf.means_=}") - print(f"{clf.covariances_=}") - print(f"{clf.converged_=}") + ] + plt.plot( + np.arange(len(db_plot)) / data_obj.metadata["global"]["core:sample_rate"], + db_plot, + ) + plt.axhline(y=guess_threshold_old, color="g", linestyle="-", label="old threshold") + plt.axhline(y=np.mean(avg_pwr_db), color="r", linestyle="-", label="average") + # plt.axhline( + # y=mad, + # color="b", + # linestyle="-", + # label="median absolute deviation threshold", + # ) + if force_threshold_db: + plt.axhline( + y=force_threshold_db, + color="yellow", + linestyle="-", + label="force threshold db", + ) + plt.legend(loc="center left", bbox_to_anchor=(1, 0.5)) + plt.ylabel("dB") + plt.xlabel("time (seconds)") + plt.title("Signal Power") + plt.show() - ### - # Figure 9 - ### - start_time = time.time() - plt.figure() - plt.plot(predictions) - plt.xlabel("") - plt.ylabel("gaussian mixture labels") - plt.title(f"") - plt.show() - print(f"Plot time = {time.time()-start_time}") + ### + # Figure 2 + ### + db_plot = avg_pwr_db[ + int(0 * data_obj.metadata["global"]["core:sample_rate"]) : int( + debug_duration * data_obj.metadata["global"]["core:sample_rate"] + ) + ] + start_time = time.time() + plt.figure() + sns.histplot(db_plot, kde=True) + plt.xlabel("dB") + plt.title(f"Signal Power Histogram & Density ({debug_duration} seconds)") + plt.show() + print(f"Plot time = {time.time()-start_time}") - #### - #### - signal_predictions = np.zeros(len(predictions)) - signal_predictions[np.where(predictions == np.argmax(clf.means_))] = 1 + # fit a Gaussian Mixture Model with two components + start_time = time.time() + clf = mixture.GaussianMixture(n_components=n_components) + clf.fit(db_plot.reshape(-1, 1)) + print(f"Gaussian mixture model time = {time.time()-start_time}") + print(f"{clf.weights_=}") + print(f"{clf.means_=}") + print(f"{clf.covariances_=}") + print(f"{clf.converged_=}") - signal_predictions_idx = ( - np.ediff1d(np.r_[0, signal_predictions == 1, 0]).nonzero()[0].reshape(-1, 2) - ) # gets indices where signal power above threshold + ### + # Figure 3 + ### + db_plot = avg_pwr_db + start_time = time.time() + plt.figure() + sns.histplot(db_plot, kde=True) + plt.xlabel("dB") + plt.title(f"Signal Power Histogram & Density") + plt.show() + print(f"Plot time = {time.time()-start_time}") - freq_bounds = signal_predictions_idx[ - np.argmax( - np.abs(signal_predictions_idx[:, 0] - signal_predictions_idx[:, 1]) - ) - ] - print(f"{signal_predictions_idx.shape=}") - print(f"{signal_predictions_idx=}") - plt.figure() - plt.imshow(cupy.asnumpy(10 * cupy.log10(cupy.fft.fftshift(Sxx, axes=0)))) - plt.axhline(y=freq_bounds[0], color="r", linestyle="-") - plt.axhline(y=freq_bounds[1], color="r", linestyle="-") - plt.show() + # fit a Gaussian Mixture Model with two components + start_time = time.time() + clf = mixture.GaussianMixture(n_components=n_components) + clf.fit(db_plot.reshape(-1, 1)) + print(f"Gaussian mixture model time = {time.time()-start_time}") + print(f"{clf.weights_=}") + print(f"{clf.means_=}") + print(f"{clf.covariances_=}") + print(f"{clf.converged_=}") - # exit() - return freq_lower_edge, freq_upper_edge + ### + # Figure 4 + ### + plt.figure() + db_plot = avg_pwr_db[ + int(0 * data_obj.metadata["global"]["core:sample_rate"]) : int( + debug_duration * data_obj.metadata["global"]["core:sample_rate"] + ) + ] + plt.plot( + np.arange(len(db_plot)) / data_obj.metadata["global"]["core:sample_rate"], + db_plot, + ) + plt.axhline(y=guess_threshold_old, color="g", linestyle="-", label="old threshold") + plt.axhline(y=np.mean(avg_pwr_db), color="r", linestyle="-", label="average") + # plt.axhline( + # y=mad, + # color="b", + # linestyle="-", + # label="median absolute deviation threshold", + # ) + plt.axhline( + y=np.min(clf.means_) + + 3 * np.sqrt(clf.covariances_[np.argmin(clf.means_)].squeeze()), + color="yellow", + linestyle="-", + label="gaussian mixture model estimate", + ) + if force_threshold_db: + plt.axhline( + y=force_threshold_db, + color="yellow", + linestyle="-", + label="force threshold db", + ) + plt.legend(loc="center left", bbox_to_anchor=(1, 0.5)) + plt.ylabel("dB") + plt.xlabel("time (seconds)") + plt.title("Signal Power") + plt.show() + ### + # Figure 5 + ### + plt.figure() -# def get_occupied_bandwidth_backup(samples, sample_rate, center_frequency): - -# # spectrogram_data, spectrogram_raw = spectrogram( -# # samples, -# # sample_rate, -# # 256, -# # 0, -# # ) -# # spectrogram_color = spectrogram_cmap(spectrogram_data, plt.get_cmap("viridis")) - -# # plt.figure() -# # plt.imshow(spectrogram_color) -# # plt.show() - -# # print(f"{samples.shape=}") -# # print(f"{samples=}") - -# f, t, Sxx = cupyx_spectrogram( -# samples, fs=sample_rate, return_onesided=False, scaling="spectrum" -# ) - -# freq_power = cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0)) -# # print(f"{freq_power.shape=}") - -# # print(f"{freq_power.argmax(axis=0).shape=}") -# # print(f"{freq_power.argmax(axis=0)=}") - -# # freq_power = cupy.asnumpy(cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=1)) -# freq_power = cupyx_gaussian_filter(cupy.fft.fftshift(Sxx, axes=0), sigma=1) - -# # plt.figure() -# # plt.pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) -# # plt.ylabel('Frequency [Hz]') -# # plt.xlabel('Time [sec]') -# # plt.show() -# # plt.figure() -# # plt.pcolormesh(cupy.asnumpy(t), cupy.asnumpy(cupy.fft.fftshift(f)), cupy.asnumpy(freq_power)) -# # plt.ylabel('Frequency [Hz]') -# # plt.xlabel('Time [sec]') -# # plt.show() - -# freq_power_normalized = freq_power / freq_power.sum(axis=0) - -# # print(f"{freq_power_normalized.shape=}") -# # print(f"{freq_power_normalized.argmax(axis=0).shape=}") -# # print(f"{freq_power_normalized.argmax(axis=0)=}") -# bounds = [] -# for i, max_power_idx in enumerate(freq_power_normalized.argmax(axis=0)): -# max_power_idx = int(cupy.asnumpy(max_power_idx)) -# # print(f"{i=}, {max_power_idx=}") -# lower_idx = max_power_idx -# upper_idx = max_power_idx -# while True: - -# if upper_idx == freq_power_normalized.shape[0] - 1: -# lower_idx -= 1 -# elif lower_idx == 0: -# upper_idx += 1 -# elif ( -# freq_power_normalized[lower_idx, i] -# > freq_power_normalized[upper_idx, i] -# ): -# lower_idx -= 1 -# else: -# upper_idx += 1 - -# # print(f"{lower_idx=}, {upper_idx=}") -# # print(f"{freq_power_normalized[lower_idx:upper_idx, i].sum()=}") -# if freq_power_normalized[lower_idx:upper_idx, i].sum() >= 0.94: -# break - -# bounds.append([lower_idx, upper_idx]) -# bounds = np.array(bounds) - -# plt.figure() -# plt.imshow(cupy.asnumpy(cupy.fft.fftshift(Sxx, axes=0))) -# plt.plot(cupy.asnumpy(freq_power.argmax(axis=0))) -# plt.plot(bounds[:, 0]) -# plt.plot(bounds[:, 1]) -# plt.axhline(y=np.median(bounds[:, 0]), color="r", linestyle="-") -# plt.axhline(y=np.median(bounds[:, 1]), color="b", linestyle="-") -# plt.show() - -# freq_lower_edge = ( -# center_frequency -# + (freq_power.shape[0] / 2 - np.median(bounds[:, 1])) -# / freq_power.shape[0] -# * sample_rate -# ) -# freq_upper_edge = ( -# center_frequency -# + (freq_power.shape[0] / 2 - np.median(bounds[:, 0])) -# / freq_power.shape[0] -# * sample_rate -# ) - -# # print(f"{freq_lower_edge=}") -# # print(f"{freq_upper_edge=}") -# print(f"estimated bandwidth = {freq_upper_edge-freq_lower_edge}") -# return freq_lower_edge, freq_upper_edge + db_plot = avg_pwr_db + plt.plot( + np.arange(len(db_plot)) / data_obj.metadata["global"]["core:sample_rate"], + db_plot, + ) + plt.axhline(y=guess_threshold_old, color="g", linestyle="-", label="old threshold") + plt.axhline(y=np.mean(avg_pwr_db), color="r", linestyle="-", label="average") + # plt.axhline( + # y=mad, + # color="b", + # linestyle="-", + # label="median absolute deviation threshold", + # ) + plt.axhline( + y=np.min(clf.means_) + + 3 * np.sqrt(clf.covariances_[np.argmin(clf.means_)].squeeze()), + color="yellow", + linestyle="-", + label="gaussian mixture model estimate", + ) + if force_threshold_db: + plt.axhline( + y=force_threshold_db, + color="yellow", + linestyle="-", + label="force threshold db", + ) + plt.legend(loc="center left", bbox_to_anchor=(1, 0.5)) + plt.ylabel("dB") + plt.xlabel("time (seconds)") + plt.title("Signal Power") + plt.show() def reset_predictions_sigmf(dataset): diff --git a/rfml/convert_model.py b/rfml/convert_model.py deleted file mode 100644 index 51416b3..0000000 --- a/rfml/convert_model.py +++ /dev/null @@ -1,50 +0,0 @@ -import torch -from collections import OrderedDict -from torchsig.models.iq_models.efficientnet.efficientnet import efficientnet_b4 -import os -import argparse - -model_name = "drone_detection" -checkpoint = "/home/iqt/lberndt/rfml-dev-1/rfml-dev/lightning_logs/version_5/checkpoints/experiment_logs/experiment_1/iq_checkpoints/checkpoint.ckpt" - -parser = argparse.ArgumentParser() -parser.add_argument("--model_name", type=str, help="Name of the model", required=True) -parser.add_argument( - "--checkpoint", type=str, help="Path to the model checkpoint", required=True -) -args = parser.parse_args() - -model_name = args.model_name -checkpoint = args.checkpoint - -model_checkpoint = torch.load(checkpoint) -print(f"Loaded model checkpoint from {checkpoint}") -model_weights = model_checkpoint["state_dict"] -model_weights = OrderedDict( - (k.removeprefix("mdl."), v) for k, v in model_weights.items() -) -num_classes = len(model_weights["classifier.bias"]) -print(f"Model has {num_classes} classes") -if not os.path.exists("weights"): - os.makedirs("weights") - -torch.save(model_weights, f"weights/{model_name}_torchserve.pt") -print(f"Saved model weights to weights/{model_name}_torchserve.pt") - -model = efficientnet_b4(num_classes=num_classes) - -model.load_state_dict(model_weights) - -device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -model = model.to(device) -model.eval() - -x = torch.randn(1, 2, 1024).to(device) -model(x) - -model.eval() -print(f"Model Test: {model(x)}") -jit_net = torch.jit.trace(model, x) -jit_net.save(f"weights/{model_name}_torchscript.pt") # Save - -print(f"Saved torchscript version of model to: weights/{model_name}_torchscript.pt") diff --git a/rfml/data.py b/rfml/data.py index e9bfab0..a23a2c1 100644 --- a/rfml/data.py +++ b/rfml/data.py @@ -161,7 +161,7 @@ def __init__(self, filename, force_sigmf_data=True): ) if not self.data_filename or not os.path.isfile(self.data_filename): raise ValueError(f"File: {self.data_filename} is not a valid file.") - + elif self.filename.lower().endswith(".sigmf-data"): self.data_filename = self.filename self.sigmf_meta_filename = ( @@ -180,7 +180,9 @@ def __init__(self, filename, force_sigmf_data=True): f"{self.data_filename}.sigmf-meta", ] - self.sigmf_meta_filename = f"{os.path.splitext(self.data_filename)[0]}.sigmf-meta" + self.sigmf_meta_filename = ( + f"{os.path.splitext(self.data_filename)[0]}.sigmf-meta" + ) for possible_sigmf in possible_sigmf_meta_filenames: if os.path.isfile(possible_sigmf): @@ -190,7 +192,9 @@ def __init__(self, filename, force_sigmf_data=True): self.zst_to_sigmf_meta() if force_sigmf_data: - self.export_sigmf_data(output_path=f"{os.path.splitext(self.data_filename)[0]}.sigmf-data") + self.export_sigmf_data( + output_path=f"{os.path.splitext(self.data_filename)[0]}.sigmf-data" + ) elif self.filename.lower().endswith(".raw"): self.data_filename = self.filename @@ -296,15 +300,17 @@ def get_samples(self, n_seek_samples=0, n_samples=-1): np.array: Complex vector of I/Q samples. """ - # if self.sigmf_obj: - # try: - # return self.sigmf_obj.read_samples( - # start_index=n_seek_samples, count=n_samples - # ) - # except OSError as e: - # print(f"Error: {e}") - # # reached end of file - # return None + if self.sigmf_obj: + try: + return self.sigmf_obj.read_samples( + start_index=n_seek_samples, count=n_samples + ) + except OSError as e: + print(f"Error: {e}") + # reached end of file + return None + + # TODO: add autoscaling from sigmf library reader = self.get_sample_reader() @@ -1544,7 +1550,7 @@ def get_custom_metadata(filename, metadata_directory): sample_filename = metadata["sample_file"]["filename"] return spectrogram_metadata, sample_filename - + if __name__ == "__main__": # /Users/ltindall/data/gamutrf/gamutrf-arl/01_30_23/mini2/snr_noise_floor/ diff --git a/rfml/experiment.py b/rfml/experiment.py index f5103d8..3ddeeac 100644 --- a/rfml/experiment.py +++ b/rfml/experiment.py @@ -3,6 +3,9 @@ from datetime import datetime from pathlib import Path +from rfml.train_iq import * +from rfml.train_spec import * + class Experiment: def __init__( @@ -12,11 +15,14 @@ def __init__( train_dir, val_dir=None, test_dir=None, - iq_num_samples=800,#1024, - iq_only_start_of_burst=True, + iq_num_samples=1024, + iq_only_start_of_burst=False, iq_epochs=40, iq_batch_size=128, iq_learning_rate=0.0001, + iq_early_stop=10, + iq_train_limit=1, + iq_val_limit=1, spec_n_fft=1024, spec_time_dim=512, spec_epochs=40, @@ -36,6 +42,9 @@ def __init__( self.iq_epochs = iq_epochs self.iq_batch_size = iq_batch_size self.iq_learning_rate = iq_learning_rate + self.iq_early_stop = iq_early_stop + self.iq_train_limit = iq_train_limit + self.iq_val_limit = iq_val_limit self.spec_n_fft = spec_n_fft self.spec_time_dim = spec_time_dim self.spec_n_samples = spec_n_fft * spec_time_dim @@ -54,7 +63,60 @@ def __init__( ) with open(experiment_config_path, "w") as f: f.write(json.dumps(vars(self), indent=4)) - print(f"\n\nFind experiment config file at {experiment_config_path}\n\n") + print(f"\nFind experiment config file at {experiment_config_path}") def __repr__(self): return str(vars(self)) + + +def train(experiment_configs): + + for experiment_name in experiment_configs: + print(f"\nRunning {experiment_name}") + try: + exp = Experiment( + experiment_name=experiment_name, **experiment_configs[experiment_name] + ) + + logs_timestamp = datetime.now().strftime("%m_%d_%Y_%H_%M_%S") + + if exp.iq_epochs > 0: + train_iq( + train_dataset_path=exp.train_dir, + val_dataset_path=exp.val_dir, + num_iq_samples=exp.iq_num_samples, + only_use_start_of_burst=exp.iq_only_start_of_burst, + epochs=exp.iq_epochs, + batch_size=exp.iq_batch_size, + class_list=exp.class_list, + logs_dir=Path("iq_logs", logs_timestamp), + output_dir=Path("experiment_logs", exp.experiment_name), + learning_rate=exp.iq_learning_rate, + experiment_name=exp.experiment_name, + early_stop=exp.iq_early_stop, + train_limit=exp.iq_train_limit, + val_limit=exp.iq_val_limit, + ) + else: + print("Skipping IQ training") + + if exp.spec_epochs > 0: + train_spec( + train_dataset_path=exp.train_dir, + val_dataset_path=exp.val_dir, + n_fft=exp.spec_n_fft, + time_dim=exp.spec_time_dim, + epochs=exp.spec_epochs, + batch_size=exp.spec_batch_size, + class_list=exp.class_list, + yolo_augment=exp.spec_yolo_augment, + skip_export=exp.spec_skip_export, + force_yolo_label_larger=exp.spec_force_yolo_label_larger, + logs_dir=Path("spec_logs", logs_timestamp), + output_dir=Path("experiment_logs", exp.experiment_name), + ) + else: + print("Skipping spectrogram training") + + except Exception as error: + print(f"Error: {error}") diff --git a/rfml/export_model.py b/rfml/export_model.py new file mode 100644 index 0000000..d133c88 --- /dev/null +++ b/rfml/export_model.py @@ -0,0 +1,109 @@ +import torch +from collections import OrderedDict +from torchsig.models.iq_models.efficientnet.efficientnet import efficientnet_b0 +import os +import argparse +import subprocess + + +def argument_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_name", type=str, help="Name of the model", required=True + ) + parser.add_argument( + "--checkpoint", type=str, help="Path to the model checkpoint", required=True + ) + parser.add_argument( + "--mode", + type=str, + choices=["export, convert"], + default="export", + help="Whether to convert model to torchserve/torchscript or export to MAR. 'export' will automatically convert the checkpoint and export to MAR. (default: %(default)s)", + ) + parser.add_argument( + "--custom_handler", + type=str, + default="custom_handlers/iq_custom_handler.py", + help="Custom handler to use when exporting to MAR. Only used if --mode='export'. (default: %(default)s)", + ) + parser.add_argument( + "--export_path", + type=str, + default="models/", + help="Path to export MAR file to. Only used if --mode='export'. (default: %(default)s)", + ) + + return parser + + +def convert_model(model_name, checkpoint): + + model_checkpoint = torch.load(checkpoint) + print(f"Loaded model checkpoint from {checkpoint}") + model_weights = model_checkpoint["state_dict"] + model_weights = OrderedDict( + (k.removeprefix("mdl."), v) for k, v in model_weights.items() + ) + num_classes = len(model_weights["classifier.bias"]) + print(f"Model has {num_classes} classes") + if not os.path.exists("weights"): + os.makedirs("weights") + + torch.save(model_weights, f"weights/{model_name}_torchserve.pt") + print(f"Saved model weights to weights/{model_name}_torchserve.pt") + + model = efficientnet_b0(num_classes=num_classes) + + model.load_state_dict(model_weights) + + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + model = model.to(device) + model.eval() + + x = torch.randn(1, 2, 1024).to(device) + model(x) + + model.eval() + print(f"Model Test: {model(x)}") + jit_net = torch.jit.trace(model, x) + torchscript_file = f"weights/{model_name}_torchscript.pt" + jit_net.save(torchscript_file) # Save + + print(f"Saved torchscript version of model to: {torchscript_file}") + + return torchscript_file + + +def export_model(model_name, torchscript_file, custom_handler, export_path): + + torch_model_archiver_args = [ + "torch-model-archiver", + "--force", + "--model-name", + model_name, + "--version", + "1.0", + "--serialized-file", + torchscript_file, + "--handler", + custom_handler, + "--export-path", + export_path, + "-r", + "custom_handlers/requirements.txt", + ] + + subprocess.run(torch_model_archiver_args) + + +if __name__ == "__main__": + + args = argument_parser().parse_args() + + torchscript_file = convert_model(args.model_name, args.checkpoint) + + if args.mode == "export": + export_model( + args.model_name, torchscript_file, args.custom_handler, args.export_path + ) diff --git a/rfml/models.py b/rfml/models.py index ff57bc4..2c898db 100644 --- a/rfml/models.py +++ b/rfml/models.py @@ -10,6 +10,56 @@ from pytorch_lightning import LightningModule from torch import optim +import torch +import torch.nn as nn +import torch.nn.functional as F + + +class mod_relu(torch.nn.Module): + def __init__( + self, + ): + super().__init__() + self.b = torch.nn.parameter.Parameter(torch.rand(1) * 0.25) + self.b.requiresGrad = True + + def forward(self, x): + # This is probably not correct (specifically torch.abs(self.b)) but it works + return F.relu(torch.abs(x) + torch.abs(self.b)) * torch.exp( + 1.0j * torch.angle(x) + ) + + +def calculate_output_length(length_in, kernel_size, stride=1, padding=0, dilation=1): + return (length_in + 2 * padding - dilation * (kernel_size - 1) - 1) // stride + 1 + + +class SimpleRealNet(nn.Module): + def __init__(self, n_classes, n_input): + super(SimpleRealNet, self).__init__() + self.conv1 = nn.Conv1d(2, 8, 3, 1) + self.conv2 = nn.Conv1d(8, 16, 3, 1) + n_fc = 16 * calculate_output_length(calculate_output_length(n_input, 3), 3) + self.fc1 = nn.Linear(n_fc, 8) + self.fc2 = nn.Linear(8, n_classes) + self.mod_relu = F.relu + + def forward(self, x): + x = self.conv1(x) + x = self.mod_relu(x) + # x = F.tanh(x) + x = self.conv2(x) + x = self.mod_relu(x) + # x = F.tanh(x) + x = torch.flatten(x, 1) + x = self.fc1(x) + x = self.mod_relu(x) + # x = F.tanh(x) + x = self.fc2(x) + x = x.abs() + output = F.log_softmax(x, dim=1) + return output + class ExampleNetwork(LightningModule): def __init__( @@ -21,6 +71,7 @@ def __init__( extra_metrics=True, logs_dir=None, learning_rate=None, + class_list=None, ): super(ExampleNetwork, self).__init__() self.mdl = model @@ -39,6 +90,8 @@ def __init__( self.logs_dir = logs_dir + self.class_list = class_list + # Metrics if self.extra_metrics: self.train_acc = torchmetrics.classification.Accuracy( @@ -102,52 +155,9 @@ def validation_step(self, batch, batch_nb): def on_validation_end(self): if self.extra_metrics: self.confusion_mat.compute() - fig, ax = self.confusion_mat.plot() + fig, ax = self.confusion_mat.plot(labels=self.class_list) fig.savefig( Path(self.logs_dir, f"confusion_matrix_{self.current_epoch}.png") ) # save the figure to file plt.close(fig) self.confusion_mat.reset() - - -# class CustomNetwork(LightningModule): -# def __init__(self, model, data_loader=None, val_data_loader=None): -# super(CustomNetwork, self).__init__() -# self.mdl = model -# self.data_loader = data_loader -# self.val_data_loader = val_data_loader - -# # Hyperparameters -# self.lr = 0.001 -# if data_loader: -# self.batch_size = data_loader.batch_size - -# def forward(self, x): -# return self.mdl(x) - -# def predict(self, x): -# with torch.no_grad(): -# out = self.forward(x) -# return out - -# def configure_optimizers(self): -# return optim.Adam(self.parameters(), lr=self.lr) - -# def train_dataloader(self): -# return self.data_loader - -# def training_step(self, batch, batch_nb): -# x, y = batch -# y = torch.squeeze(y.to(torch.int64)) -# loss = F.cross_entropy(self(x.float()), y) -# return {"loss": loss} - -# def val_dataloader(self): -# return self.val_data_loader - -# def validation_step(self, batch, batch_nb): -# x, y = batch -# y = torch.squeeze(y.to(torch.int64)) -# val_loss = F.cross_entropy(self(x.float()), y) -# self.log("val_loss", val_loss, prog_bar=True) -# return {"val_loss": val_loss} diff --git a/rfml/sigmf_pytorch_dataset.py b/rfml/sigmf_pytorch_dataset.py index 4fa8cc1..39194ad 100644 --- a/rfml/sigmf_pytorch_dataset.py +++ b/rfml/sigmf_pytorch_dataset.py @@ -70,7 +70,7 @@ def __init__( sample_count: int = 2048, # 4096 index_filter: Optional[Callable[[Tuple[Any, SignalCapture]], bool]] = None, class_list: Optional[List[str]] = None, - allowed_filetypes: Optional[List[str]] = [".sigmf-data"], + allowed_filetypes: Optional[List[str]] = [".sigmf-data", ".sigmf-meta"], only_first_samples: bool = True, **kwargs, ): @@ -98,7 +98,6 @@ def get_class_counts(self, indices=None): class_counts = {idx: 0 for idx in range(len(self.class_list))} for label_idx, _ in self.get_indices(indices): class_counts[label_idx] += 1 - # print(f"{class_counts=}") return class_counts @@ -156,46 +155,20 @@ def indexer_from_sigmf_annotations( index = [] for file_type in self.allowed_filetypes: for r in root: - for f in glob.glob( - os.path.join(r, "**", "*" + file_type), recursive=True - ): + + if os.path.isfile(r): + file_list = [f"{os.path.splitext(r)[0]}.sigmf-data"] + elif os.path.isdir(r): + file_list = glob.glob( + os.path.join(r, "**", "*" + file_type), recursive=True + ) + else: + raise ValueError + for f in file_list: if os.path.isfile(f"{os.path.splitext(f)[0]}.sigmf-meta"): signals = self._parse_sigmf_annotations(f) if signals: index = index + signals - # index = index + self._parse_sigmf_annotations(f) - print(f"Class List: {self.class_list}") - - # # go through directories and find files - # non_empty_dirs = [ - # d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d)) - # ] - # non_empty_dirs.append(".") - # print(non_empty_dirs) - # # non_empty_dirs = [d for d in non_empty_dirs if os.listdir(os.path.join(root, d))] - # # print(non_empty_dirs) - # # Identify all files associated with each class - # index = [] - # for dir_idx, dir_name in enumerate(non_empty_dirs): - # data_dir = os.path.join(root, dir_name) - - # # Find files with allowed filetype - # proper_sigmf_files = list( - # filter( - # lambda x: os.path.splitext(x)[1] in self.allowed_filetypes - # and os.path.isfile(os.path.join(data_dir, x)) - # and os.path.isfile( - # os.path.join(data_dir, f"{os.path.splitext(x)[0]}.sigmf-meta") - # ), - # os.listdir(data_dir), - # ) - # ) - - # # Go through each file and create and index - # for f in proper_sigmf_files: - # index = index + self._parse_sigmf_annotations(os.path.join(data_dir, f)) - - # print(f"Class List: {self.class_list}") return index @@ -220,7 +193,6 @@ def _parse_sigmf_annotations(self, absolute_file_path: str) -> List[SignalCaptur """ meta_file_name = f"{os.path.splitext(absolute_file_path)[0]}.sigmf-meta" - # meta_file_name = "{}{}".format(absolute_file_path.split("sigmf-data")[0], "sigmf-meta") meta = json.load(open(meta_file_name, "r")) item_type = indexer.SIGMF_DTYPE_MAP[meta["global"]["core:datatype"]] sample_size = item_type.itemsize * ( diff --git a/rfml/train_iq.py b/rfml/train_iq.py index 239a256..59742c6 100644 --- a/rfml/train_iq.py +++ b/rfml/train_iq.py @@ -11,7 +11,8 @@ from torchsig.datasets.sig53 import Sig53 from torch.utils.data import DataLoader import matplotlib -matplotlib.use('Agg') + +matplotlib.use("Agg") from matplotlib import pyplot as plt from typing import List from tqdm import tqdm @@ -20,7 +21,10 @@ import os from pathlib import Path -from torchsig.models.iq_models.efficientnet.efficientnet import efficientnet_b0, efficientnet_b4 +from torchsig.models.iq_models.efficientnet.efficientnet import ( + efficientnet_b0, + efficientnet_b4, +) # from lightning.pytorch.callbacks import DeviceStatsMonitor from torchsig.utils.cm_plotter import plot_confusion_matrix @@ -43,7 +47,7 @@ import torch import os from rfml.sigmf_pytorch_dataset import SigMFDataset -from rfml.models import ExampleNetwork +from rfml.models import ExampleNetwork, SimpleRealNet from torchsig.transforms import ( Compose, @@ -59,21 +63,6 @@ ComplexTo2D, ) -# # dataset_path = "./dev_data/torchsig_train/" -# dataset_path = "./data/gamutrf/gamutrf-sd-gr-ieee-wifi/v2_host/gain_40/" -# print(f"{dataset_path=}") -# num_iq_samples = 1024 -# only_use_start_of_burst = True - -# logs_dir = datetime.now().strftime('logs/%H_%M_%S_%m_%d_%Y') - -# logs_dir = Path(logs_dir) -# logs_dir.mkdir(parents=True) - -# epochs = 40 -# batch_size = 180 -# class_list = ['anom_wifi','wifi'] - def train_iq( train_dataset_path, @@ -87,8 +76,11 @@ def train_iq( output_dir=None, learning_rate=None, experiment_name=None, + early_stop=10, + train_limit=1, + val_limit=1, ): - print(f"\n\nSTARTING I/Q TRAINING\n\n") + print(f"\nI/Q MODEL TRAINING") if logs_dir is None: logs_dir = datetime.now().strftime("iq_logs/%m_%d_%Y_%H_%M_%S") if output_dir is None: @@ -97,7 +89,6 @@ def train_iq( logs_dir = Path(output_dir, logs_dir) logs_dir.mkdir(parents=True, exist_ok=True) - # # SigMF based Model Training eb_no = False @@ -128,30 +119,26 @@ def train_iq( ] ) - # ### Load the SigMF File dataset - # and generate the class list - - # changes, - # 1) augmentations - # 2) pretrained weights - # 3) optimizers - # 4) learning rate - # 5) batch size - - basic_transform = ST.Compose([ - # ST.RandomPhaseShift(phase_offset=(-1, 1)), - # ST.AddNoise(), - # ST.AutomaticGainControl(), - ST.Normalize(norm=2), - # ST.Normalize(norm=np.inf), - ST.ComplexTo2D(), - ]) + # TODO: add user parameters for + # transforms + # use pretrained weights + + basic_transform = ST.Compose( + [ + # ST.RandomPhaseShift(phase_offset=(-1, 1)), + # ST.AddNoise(), + # ST.AutomaticGainControl(), + # ST.Normalize(norm=2), + ST.Normalize(norm=np.inf), + ST.ComplexTo2D(), + ] + ) val_transform = ST.Compose( [ # ST.AutomaticGainControl(), - ST.Normalize(norm=2), - # ST.Normalize(norm=np.inf), + # ST.Normalize(norm=2), + ST.Normalize(norm=np.inf), ST.ComplexTo2D(), ] ) @@ -159,21 +146,22 @@ def train_iq( visualize_transform = ST.Compose( [ # ST.AddNoise(), - ST.AutomaticGainControl() + # ST.AutomaticGainControl() ] ) + # train_transform = level2 train_transform = basic_transform - visualize_dataset( - train_dataset_path, num_iq_samples, logs_dir, class_list=class_list, transform=visualize_transform + train_dataset_path, + num_iq_samples, + logs_dir, + class_list=class_list, + only_use_start_of_burst=only_use_start_of_burst, + transform=visualize_transform, ) - train_limit = 0.5 - - - ### if val_dataset_path: original_train_dataset = SigMFDataset( root=train_dataset_path, @@ -190,24 +178,33 @@ def train_iq( class_list=class_list, ) - train_dataset, _ = torch.utils.data.random_split(original_train_dataset, [train_limit, 1-train_limit]) - val_dataset, _ = torch.utils.data.random_split(original_val_dataset, [train_limit, 1-train_limit]) - - sampler = original_train_dataset.get_weighted_sampler(indices=train_dataset.indices) + train_dataset, _ = torch.utils.data.random_split( + original_train_dataset, [train_limit, 1 - train_limit] + ) + val_dataset, _ = torch.utils.data.random_split( + original_val_dataset, [val_limit, 1 - val_limit] + ) + + sampler = original_train_dataset.get_weighted_sampler( + indices=train_dataset.indices + ) - train_class_counts = original_train_dataset.get_class_counts(indices=train_dataset.indices) + train_class_counts = original_train_dataset.get_class_counts( + indices=train_dataset.indices + ) train_class_counts = { - original_train_dataset.class_list[k]: v for k, v in train_class_counts.items() + original_train_dataset.class_list[k]: v + for k, v in train_class_counts.items() } - val_class_counts = original_val_dataset.get_class_counts(indices=val_dataset.indices) + val_class_counts = original_val_dataset.get_class_counts( + indices=val_dataset.indices + ) val_class_counts = { original_val_dataset.class_list[k]: v for k, v in val_class_counts.items() } class_list = class_list if class_list else original_train_dataset.class_list - ### else: - print("\n\n\ntrain_limit\n\n\n") dataset = SigMFDataset( root=train_dataset_path, sample_count=num_iq_samples, @@ -215,8 +212,9 @@ def train_iq( only_first_samples=only_use_start_of_burst, class_list=class_list, ) - train_dataset, val_dataset, _ = torch.utils.data.random_split(dataset, [train_limit*0.8, train_limit*0.2, 1-train_limit]) - # train_dataset, val_dataset = torch.utils.data.random_split(dataset, [0.8, 0.2]) + train_dataset, val_dataset, _ = torch.utils.data.random_split( + dataset, [train_limit * 0.8, train_limit * 0.2, 1 - train_limit] + ) sampler = dataset.get_weighted_sampler(indices=train_dataset.indices) train_class_counts = dataset.get_class_counts(indices=train_dataset.indices) @@ -230,8 +228,11 @@ def train_iq( class_list = class_list if class_list else dataset.class_list - print(f"\n{len(train_dataset)=}, {train_class_counts=}") - print(f"{len(val_dataset)=}, {val_class_counts=}\n") + print(f"\nTraining dataset information:") + print(f"{len(train_dataset)=}, {train_class_counts=}") + print(f"\nValidation dataset information:") + print(f"{len(val_dataset)=}, {val_class_counts=}") + print("") train_dataloader = DataLoader( dataset=train_dataset, @@ -249,12 +250,19 @@ def train_iq( drop_last=True, ) + # TODO: add feature to specify model + + # model = SimpleRealNet( + # n_classes=len(class_list), + # n_input=num_iq_samples, + # ) + model = efficientnet_b0( - pretrained=False,#True, + pretrained=True, path="efficientnet_b0.pt", num_classes=len(class_list), - drop_path_rate=0.2, - drop_rate=0.2, + drop_path_rate=0.4, + drop_rate=0.4, ) # model = efficientnet_b4( # pretrained=True, @@ -275,6 +283,7 @@ def train_iq( num_classes=len(class_list), logs_dir=logs_dir, learning_rate=learning_rate, + class_list=class_list, ) # Setup checkpoint callbacks @@ -287,15 +296,23 @@ def train_iq( ) # Create and fit trainer experiment_name = experiment_name if experiment_name else 1 - logger = TensorBoardLogger(save_dir=os.getcwd(), version=experiment_name, name="lightning_logs") + logger = TensorBoardLogger( + save_dir=os.getcwd(), version=experiment_name, name="lightning_logs" + ) trainer = Trainer( max_epochs=epochs, - callbacks=[EarlyStopping(monitor="val_loss", mode="min", patience=10, verbose=True), checkpoint_callback], + callbacks=[ + EarlyStopping( + monitor="val_loss", mode="min", patience=early_stop, verbose=True + ), + checkpoint_callback, + ], accelerator="gpu", devices=1, logger=logger, # profiler="simple", ) + print(f"\nStarting training...") trainer.fit(example_model) # checkpoint_callback.best_model_path @@ -312,15 +329,14 @@ def train_iq( # Infer results over validation set num_test_examples = len(val_dataset) - # num_classes = 5 #len(list(Sig53._idx_to_name_dict.values())) - # y_raw_preds = np.empty((num_test_examples,num_classes)) y_preds = np.zeros((num_test_examples,)) y_true = np.zeros((num_test_examples,)) y_true_list = [] y_preds_list = [] + + print(f"\nStarting final validation...") with torch.no_grad(): example_model.eval() - # for i in tqdm(range(0,num_test_examples)): for data, label in tqdm(val_dataloader): # Retrieve data # idx = i # Use index if evaluating over full dataset @@ -365,20 +381,28 @@ def train_iq( print(f"Best Model Checkpoint: {checkpoint_callback.best_model_path}") -def visualize_dataset(dataset_path, num_iq_samples, logs_dir, class_list, transform=None): - print("\nVisualizing Dataset\n") +def visualize_dataset( + dataset_path, + num_iq_samples, + logs_dir, + class_list, + only_use_start_of_burst, + transform=None, +): + print("\nVisualizing Dataset") + dataset = SigMFDataset( root=dataset_path, sample_count=num_iq_samples, - allowed_filetypes=[".sigmf-data"], class_list=class_list, transform=transform, + only_first_samples=only_use_start_of_burst, ) dataset_class_counts = {class_name: 0 for class_name in dataset.class_list} for data, label in dataset: dataset_class_counts[dataset.class_list[label]] += 1 - print(f"{len(dataset)=}") - print(dataset_class_counts) + print(f"Visualize Dataset: {len(dataset)=}") + print(f"Visualize Dataset: {dataset_class_counts=}") data_loader = DataLoader( dataset=dataset, @@ -390,16 +414,28 @@ def visualize_dataset(dataset_path, num_iq_samples, logs_dir, class_list, transf for figure in iter(visualizer): figure.set_size_inches(16, 16) - plt.show() - plt.savefig(Path(logs_dir, "iq_dataset.png")) + # plt.show() + iq_viz_path = Path(logs_dir, "iq_dataset.png") + print(f"Saving IQ visualization at {iq_viz_path}") + plt.savefig(iq_viz_path) break - spec_visualizer = SpectrogramVisualizer(data_loader=data_loader, sample_rate=20e6, window=sp.windows.blackmanharris(32), nperseg=32, nfft=32 ) + spec_visualizer = SpectrogramVisualizer( + data_loader=data_loader, + sample_rate=20e6, + window=sp.windows.blackmanharris(32), + nperseg=32, + nfft=32, + ) for figure in iter(spec_visualizer): figure.set_size_inches(16, 16) - plt.show() - plt.savefig(Path(logs_dir, "spec_dataset.png")) + # plt.show() + spec_viz_path = Path(logs_dir, "spec_dataset.png") + print(f"Saving spectrogram visualization at {spec_viz_path}") + plt.savefig(spec_viz_path) break + print("") + def argument_parser(): parser = ArgumentParser() diff --git a/rfml/utils.py b/rfml/utils.py index 4cefb85..cd8a9cc 100644 --- a/rfml/utils.py +++ b/rfml/utils.py @@ -50,8 +50,8 @@ def manual_to_sigmf(file, datatype, sample_rate, frequency, iso_date_string): "sample_rate": 20500000, "frequency": 5735000000, "iso_date_string": "2022-06-15", - } - ) + }, + ), ] for file_glob, metadata in data_globs: files = glob.glob(str(Path(file_glob)))