19 Commits

Author SHA1 Message Date
67a1864837 util improvements
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -41s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-18 23:38:45 -07:00
eca933ccef update readme 2024-12-18 23:34:56 -07:00
a5b391e757 untested: add some plot update stuff
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -38s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-18 23:31:34 -07:00
34a4532a02 more gui stuff
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -42s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-18 22:52:12 -07:00
98f40179e1 menu stuff
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -41s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-18 22:35:16 -07:00
5a7bc985b4 start making gui
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -31s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-18 21:37:57 -07:00
f4c099c1d2 update README.md
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -14s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-08 13:53:31 -07:00
78d0034e34 use an old release of pypa/gh-action-pypi-publish that actually works
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -16s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-08 13:48:24 -07:00
a56b2e30e2 remove unrelated stuff from python_publish 2024-12-08 13:35:58 -07:00
ec010af947 calibrated measurements look a little funky
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -15s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-06 23:12:11 -07:00
7e0df9e643 vna_capture() working through class 2024-12-06 23:03:43 -07:00
a20217967f sweep_b_over_a working
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -11s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-04 19:26:44 -07:00
958d1f96d1 filtering stuff
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -15s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-02 22:55:39 -07:00
48d559f084 I'm not sure why exp creates such a bad tone 2024-12-02 22:38:56 -07:00
167a0b7aef start turning this into a class 2024-12-02 19:29:23 -07:00
2c9e9b0eb2 move some helpers to util.py 2024-12-02 18:56:43 -07:00
c8ace2330d vna calibration
All checks were successful
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in -10s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been skipped
2024-12-02 18:07:40 -07:00
60ef43e66e Update .github/workflows/python_publish.yml
Some checks failed
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Failing after 1m1s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in 1m6s
2024-11-14 13:43:52 -07:00
a2044ba7de Update .github/workflows/python_publish.yml
Some checks failed
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Successful in 1m6s
Publish Python 🐍 distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Failing after 1m0s
2024-11-14 13:42:43 -07:00
9 changed files with 1019 additions and 330 deletions

View File

@ -55,6 +55,6 @@ jobs:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
uses: pypa/gh-action-pypi-publish@0ab0b79471669eb3a4d647e625009c62f9f3b241
with:
password: ${{ secrets.PYPI_API_TOKEN }}

503
Pipfile.lock generated
View File

@ -36,74 +36,63 @@
},
"contourpy": {
"hashes": [
"sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0",
"sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639",
"sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd",
"sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad",
"sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843",
"sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8",
"sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4",
"sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1",
"sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294",
"sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84",
"sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927",
"sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8",
"sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09",
"sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7",
"sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f",
"sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab",
"sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b",
"sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3",
"sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223",
"sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973",
"sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087",
"sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081",
"sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc",
"sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18",
"sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f",
"sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d",
"sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2",
"sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41",
"sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67",
"sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6",
"sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b",
"sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2",
"sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c",
"sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42",
"sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d",
"sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4",
"sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5",
"sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49",
"sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b",
"sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7",
"sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102",
"sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb",
"sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7",
"sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e",
"sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c",
"sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8",
"sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35",
"sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b",
"sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14",
"sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb",
"sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589",
"sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c",
"sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0",
"sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da",
"sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800",
"sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6",
"sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66",
"sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca",
"sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb",
"sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c",
"sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06",
"sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779",
"sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8",
"sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f",
"sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"
"sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1",
"sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda",
"sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d",
"sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509",
"sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6",
"sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f",
"sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e",
"sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751",
"sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86",
"sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b",
"sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc",
"sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546",
"sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec",
"sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f",
"sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82",
"sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c",
"sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b",
"sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c",
"sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c",
"sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53",
"sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80",
"sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242",
"sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85",
"sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124",
"sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5",
"sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2",
"sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3",
"sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d",
"sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc",
"sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342",
"sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1",
"sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1",
"sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595",
"sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30",
"sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab",
"sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3",
"sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2",
"sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd",
"sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7",
"sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277",
"sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453",
"sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697",
"sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b",
"sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454",
"sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9",
"sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1",
"sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6",
"sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291",
"sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750",
"sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699",
"sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e",
"sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81",
"sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9",
"sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"
],
"markers": "python_version >= '3.9'",
"version": "==1.3.0"
"markers": "python_version >= '3.10'",
"version": "==1.3.1"
},
"cycler": {
"hashes": [
@ -123,57 +112,59 @@
},
"fonttools": {
"hashes": [
"sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6",
"sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263",
"sha256:0d1d353ef198c422515a3e974a1e8d5b304cd54a4c2eebcae708e37cd9eeffb1",
"sha256:0e88e3018ac809b9662615072dcd6b84dca4c2d991c6d66e1970a112503bba7e",
"sha256:1d152d1be65652fc65e695e5619e0aa0982295a95a9b29b52b85775243c06556",
"sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d",
"sha256:278913a168f90d53378c20c23b80f4e599dca62fbffae4cc620c8eed476b723e",
"sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2",
"sha256:31c32d7d4b0958600eac75eaf524b7b7cb68d3a8c196635252b7a2c30d80e986",
"sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb",
"sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd",
"sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882",
"sha256:4aa4817f0031206e637d1e685251ac61be64d1adef111060df84fdcbc6ab6c44",
"sha256:4e10d2e0a12e18f4e2dd031e1bf7c3d7017be5c8dbe524d07706179f355c5dac",
"sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20",
"sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d",
"sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a",
"sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c",
"sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d",
"sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff",
"sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7",
"sha256:7965af9b67dd546e52afcf2e38641b5be956d68c425bef2158e95af11d229f10",
"sha256:7e3b7d44e18c085fd8c16dcc6f1ad6c61b71ff463636fcb13df7b1b818bd0c02",
"sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2",
"sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07",
"sha256:8583e563df41fdecef31b793b4dd3af8a9caa03397be648945ad32717a92885b",
"sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08",
"sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab",
"sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285",
"sha256:9dc080e5a1c3b2656caff2ac2633d009b3a9ff7b5e93d0452f40cd76d3da3b3c",
"sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58",
"sha256:a7a310c6e0471602fe3bf8efaf193d396ea561486aeaa7adc1f132e02d30c4b9",
"sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc",
"sha256:ada215fd079e23e060157aab12eba0d66704316547f334eee9ff26f8c0d7b8ab",
"sha256:c39287f5c8f4a0c5a55daf9eaf9ccd223ea59eed3f6d467133cc727d7b943a55",
"sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714",
"sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8",
"sha256:d3b659d1029946f4ff9b6183984578041b520ce0f8fb7078bb37ec7445806b33",
"sha256:dd9cc95b8d6e27d01e1e1f1fae8559ef3c02c76317da650a19047f249acd519d",
"sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e",
"sha256:e7d82b9e56716ed32574ee106cabca80992e6bbdcf25a88d97d21f73a0aae664",
"sha256:e8a4b261c1ef91e7188a30571be6ad98d1c6d9fa2427244c545e2fa0a2494dd7",
"sha256:e96bc94c8cda58f577277d4a71f51c8e2129b8b36fd05adece6320dd3d57de8a",
"sha256:ed2f80ca07025551636c555dec2b755dd005e2ea8fbeb99fc5cdff319b70b23b",
"sha256:f5b8a096e649768c2f4233f947cf9737f8dbf8728b90e2771e2497c6e3d21d13",
"sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a",
"sha256:fda582236fee135d4daeca056c8c88ec5f6f6d88a004a79b84a02547c8f57386",
"sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac"
"sha256:09fe922a3eff181fd07dd724cdb441fb6b9fc355fd1c0f1aa79aca60faf1fbdd",
"sha256:0ecd1c2b1c2ec46bb73685bc5473c72e16ed0930ef79bc2919ccadc43a99fb16",
"sha256:10aff204e2edee1d312fa595c06f201adf8d528a3b659cfb34cd47eceaaa6a26",
"sha256:131591ac8d7a47043aaf29581aba755ae151d46e49d2bf49608601efd71e8b4d",
"sha256:18f082445b8fe5e91c53e6184f4c1c73f3f965c8bcc614c6cd6effd573ce6c1a",
"sha256:22ef222740eb89d189bf0612eb98fbae592c61d7efeac51bfbc2a1592d469557",
"sha256:25062b6ca03464dd5179fc2040fb19e03391b7cc49b9cc4f879312e638605c5c",
"sha256:27c0f91adbbd706e8acd1db73e3e510118e62d0ffb651864567dccc5b2339f90",
"sha256:2df61d9fc15199cc86dad29f64dd686874a3a52dda0c2d8597d21f509f95c332",
"sha256:3d8ccce035320d63dba0c35f52499322f5531dbe85bba1514c7cea26297e4c54",
"sha256:3d9bbc1e380fdaf04ad9eabd8e3e6a4301eaf3487940893e9fd98537ea2e283b",
"sha256:42a9afedff07b6f75aa0f39b5e49922ac764580ef3efce035ca30284b2ee65c8",
"sha256:42aca564b575252fd9954ed0d91d97a24de24289a16ce8ff74ed0bdf5ecebf11",
"sha256:44cf2a98aa661dbdeb8c03f5e405b074e2935196780bb729888639f5276067d9",
"sha256:45947e7b3f9673f91df125d375eb57b9a23f2a603f438a1aebf3171bffa7a205",
"sha256:487e1e8b524143a799bda0169c48b44a23a6027c1bb1957d5a172a7d3a1dd704",
"sha256:4c83381c3e3e3d9caa25527c4300543578341f21aae89e4fbbb4debdda8d82a2",
"sha256:508ebb42956a7a931c4092dfa2d9b4ffd4f94cea09b8211199090d2bd082506b",
"sha256:5b1a6e576db0c83c1b91925bf1363478c4bb968dbe8433147332fb5782ce6190",
"sha256:5cfa67414d7414442a5635ff634384101c54f53bb7b0e04aa6a61b013fcce194",
"sha256:616368b15716781bc84df5c2191dc0540137aaef56c2771eb4b89b90933f347a",
"sha256:627cf10d6f5af5bec6324c18a2670f134c29e1b7dce3fb62e8ef88baa6cba7a9",
"sha256:663eba5615d6abaaf616432354eb7ce951d518e43404371bcc2b0694ef21e8d6",
"sha256:6b5917ef79cac8300b88fd6113003fd01bbbbea2ea060a27b95d8f77cb4c65c2",
"sha256:6fc88cfb58b0cd7b48718c3e61dd0d0a3ee8e2c86b973342967ce09fbf1db6d4",
"sha256:7bbae4f3915225c2c37670da68e2bf18a21206060ad31dfb95fec91ef641caa7",
"sha256:803d5cef5fc47f44f5084d154aa3d6f069bb1b60e32390c225f897fa19b0f939",
"sha256:81ccd2b3a420b8050c7d9db3be0555d71662973b3ef2a1d921a2880b58957db8",
"sha256:8b02b10648d69d67a7eb055f4d3eedf4a85deb22fb7a19fbd9acbae7c7538199",
"sha256:8bc5f100de0173cc39102c0399bd6c3bd544bbdf224957933f10ee442d43cddd",
"sha256:8e2d89fbe9b08d96e22c7a81ec04a4e8d8439c31223e2dc6f2f9fc8ff14bdf9f",
"sha256:9008438ad59e5a8e403a62fbefef2b2ff377eb3857d90a3f2a5f4d674ff441b2",
"sha256:93f439ca27e55f585e7aaa04a74990acd983b5f2245e41d6b79f0a8b44e684d8",
"sha256:944228b86d472612d3b48bcc83b31c25c2271e63fdc74539adfcfa7a96d487fb",
"sha256:96e126df9615df214ec7f04bebcf60076297fbc10b75c777ce58b702d7708ffb",
"sha256:9b1726872e09268bbedb14dc02e58b7ea31ecdd1204c6073eda4911746b44797",
"sha256:9f0e55f5da594b85f269cfbecd2f6bd3e07d0abba68870bc3f34854de4fa4678",
"sha256:bbea0ab841113ac8e8edde067e099b7288ffc6ac2dded538b131c2c0595d5f77",
"sha256:bef0f8603834643b1a6419d57902f18e7d950ec1a998fb70410635c598dc1a1e",
"sha256:c1b9de46ef7b683d50400abf9f1578eaceee271ff51c36bf4b7366f2be29f498",
"sha256:c6457f650ebe15baa17fc06e256227f0a47f46f80f27ec5a0b00160de8dc2c13",
"sha256:d0bf24d2b02dbc9376d795a63062632ff73e3e9e60c0229373f500aed7e86dd7",
"sha256:d1100d8e665fe386a79cab59446992de881ea74d0d6c191bb988642692aa2421",
"sha256:d337ec087da8216a828574aa0525d869df0a2ac217a2efc1890974ddd1fbc5b9",
"sha256:d34525e8141286fa976e14806639d32294bfb38d28bbdb5f6be9f46a1cd695a6",
"sha256:d4ff250ed4ff05015dfd9cf2adf7570c7a383ca80f4d9732ac484a5ed0d8453c",
"sha256:d559eb1744c7dcfa90ae60cb1a4b3595e898e48f4198738c321468c01180cd83",
"sha256:dbdc251c5e472e5ae6bc816f9b82718b8e93ff7992e7331d6cf3562b96aa268e",
"sha256:e857fe1859901ad8c5cab32e0eebc920adb09f413d2d73b74b677cf47b28590c",
"sha256:f1c76f423f1a241df08f87614364dff6e0b7ce23c962c1b74bd995ec7c0dad13"
],
"markers": "python_version >= '3.8'",
"version": "==4.54.1"
"version": "==4.55.2"
},
"kiwisolver": {
"hashes": [
@ -297,49 +288,50 @@
},
"matplotlib": {
"hashes": [
"sha256:039082812cacd6c6bec8e17a9c1e6baca230d4116d522e81e1f63a74d01d2e21",
"sha256:03ba9c1299c920964e8d3857ba27173b4dbb51ca4bab47ffc2c2ba0eb5e2cbc5",
"sha256:050598c2b29e0b9832cde72bcf97627bf00262adbc4a54e2b856426bb2ef0697",
"sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9",
"sha256:1cd93b91ab47a3616b4d3c42b52f8363b88ca021e340804c6ab2536344fad9ca",
"sha256:1d94ff717eb2bd0b58fe66380bd8b14ac35f48a98e7c6765117fe67fb7684e64",
"sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e",
"sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03",
"sha256:3fd595f34aa8a55b7fc8bf9ebea8aa665a84c82d275190a61118d33fbc82ccae",
"sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa",
"sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3",
"sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e",
"sha256:65aacf95b62272d568044531e41de26285d54aec8cb859031f511f84bd8b495a",
"sha256:6758baae2ed64f2331d4fd19be38b7b4eae3ecec210049a26b6a4f3ae1c85dcc",
"sha256:6d1ce5ed2aefcdce11904fc5bbea7d9c21fff3d5f543841edf3dea84451a09ea",
"sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b",
"sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e",
"sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447",
"sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b",
"sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92",
"sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb",
"sha256:ab68d50c06938ef28681073327795c5db99bb4666214d2d5f880ed11aeaded66",
"sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9",
"sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7",
"sha256:b2696efdc08648536efd4e1601b5fd491fd47f4db97a5fbfd175549a7365c1b2",
"sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30",
"sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d",
"sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7",
"sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4",
"sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41",
"sha256:cef2a73d06601437be399908cf13aee74e86932a5ccc6ccdf173408ebc5f6bb2",
"sha256:d52a3b618cb1cbb769ce2ee1dcdb333c3ab6e823944e9a2d36e37253815f9556",
"sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f",
"sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772",
"sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c",
"sha256:e0830e188029c14e891fadd99702fd90d317df294c3298aad682739c5533721a",
"sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51",
"sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49",
"sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c",
"sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"
"sha256:026bdf3137ab6022c866efa4813b6bbeddc2ed4c9e7e02f0e323a7bca380dfa0",
"sha256:031b7f5b8e595cc07def77ec5b58464e9bb67dc5760be5d6f26d9da24892481d",
"sha256:0a0a63cb8404d1d1f94968ef35738900038137dab8af836b6c21bb6f03d75465",
"sha256:0a361bd5583bf0bcc08841df3c10269617ee2a36b99ac39d455a767da908bbbc",
"sha256:10d3e5c7a99bd28afb957e1ae661323b0800d75b419f24d041ed1cc5d844a764",
"sha256:1c40c244221a1adbb1256692b1133c6fb89418df27bf759a31a333e7912a4010",
"sha256:203d18df84f5288973b2d56de63d4678cc748250026ca9e1ad8f8a0fd8a75d83",
"sha256:213d6dc25ce686516208d8a3e91120c6a4fdae4a3e06b8505ced5b716b50cc04",
"sha256:3119b2f16de7f7b9212ba76d8fe6a0e9f90b27a1e04683cd89833a991682f639",
"sha256:3fb0b37c896172899a4a93d9442ffdc6f870165f59e05ce2e07c6fded1c15749",
"sha256:41b016e3be4e740b66c79a031a0a6e145728dbc248142e751e8dab4f3188ca1d",
"sha256:4a8d279f78844aad213c4935c18f8292a9432d51af2d88bca99072c903948045",
"sha256:4e6eefae6effa0c35bbbc18c25ee6e0b1da44d2359c3cd526eb0c9e703cf055d",
"sha256:5f2a4ea08e6876206d511365b0bc234edc813d90b930be72c3011bbd7898796f",
"sha256:66d7b171fecf96940ce069923a08ba3df33ef542de82c2ff4fe8caa8346fa95a",
"sha256:687df7ceff57b8f070d02b4db66f75566370e7ae182a0782b6d3d21b0d6917dc",
"sha256:6be0ba61f6ff2e6b68e4270fb63b6813c9e7dec3d15fc3a93f47480444fd72f0",
"sha256:6e9de2b390d253a508dd497e9b5579f3a851f208763ed67fdca5dc0c3ea6849c",
"sha256:760a5e89ebbb172989e8273024a1024b0f084510b9105261b3b00c15e9c9f006",
"sha256:816a966d5d376bf24c92af8f379e78e67278833e4c7cbc9fa41872eec629a060",
"sha256:87ad73763d93add1b6c1f9fcd33af662fd62ed70e620c52fcb79f3ac427cf3a6",
"sha256:896774766fd6be4571a43bc2fcbcb1dcca0807e53cab4a5bf88c4aa861a08e12",
"sha256:8e0143975fc2a6d7136c97e19c637321288371e8f09cff2564ecd73e865ea0b9",
"sha256:90a85a004fefed9e583597478420bf904bb1a065b0b0ee5b9d8d31b04b0f3f70",
"sha256:9b081dac96ab19c54fd8558fac17c9d2c9cb5cc4656e7ed3261ddc927ba3e2c5",
"sha256:9d6b2e8856dec3a6db1ae51aec85c82223e834b228c1d3228aede87eee2b34f9",
"sha256:9f459c8ee2c086455744723628264e43c884be0c7d7b45d84b8cd981310b4815",
"sha256:9fa6e193c14d6944e0685cdb527cb6b38b0e4a518043e7212f214113af7391da",
"sha256:a42b9dc42de2cfe357efa27d9c50c7833fc5ab9b2eb7252ccd5d5f836a84e1e4",
"sha256:b651b0d3642991259109dc0351fc33ad44c624801367bb8307be9bfc35e427ad",
"sha256:b6c12514329ac0d03128cf1dcceb335f4fbf7c11da98bca68dca8dcb983153a9",
"sha256:c52f48eb75fcc119a4fdb68ba83eb5f71656999420375df7c94cc68e0e14686e",
"sha256:c96eeeb8c68b662c7747f91a385688d4b449687d29b691eff7068a4602fe6dc4",
"sha256:cd1077b9a09b16d8c3c7075a8add5ffbfe6a69156a57e290c800ed4d435bef1d",
"sha256:cd5dbbc8e25cad5f706845c4d100e2c8b34691b412b93717ce38d8ae803bcfa5",
"sha256:cf2a60daf6cecff6828bc608df00dbc794380e7234d2411c0ec612811f01969d",
"sha256:d3c93796b44fa111049b88a24105e947f03c01966b5c0cc782e2ee3887b790a3",
"sha256:d796272408f8567ff7eaa00eb2856b3a00524490e47ad505b0b4ca6bb8a7411f",
"sha256:e0fcb7da73fbf67b5f4bdaa57d85bb585a4e913d4a10f3e15b32baea56a67f0a",
"sha256:e14485bb1b83eeb3d55b6878f9560240981e7bbc7a8d4e1e8c38b9bd6ec8d2de",
"sha256:edd14cf733fdc4f6e6fe3f705af97676a7e52859bf0044aa2c84e55be739241c"
],
"markers": "python_version >= '3.9'",
"version": "==3.9.2"
"version": "==3.9.3"
},
"numcodecs": {
"hashes": [
@ -366,11 +358,64 @@
},
"numpy": {
"hashes": [
"sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098",
"sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"
"sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608",
"sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef",
"sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90",
"sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae",
"sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83",
"sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0",
"sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73",
"sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671",
"sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69",
"sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa",
"sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066",
"sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da",
"sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9",
"sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e",
"sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3",
"sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a",
"sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74",
"sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3",
"sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410",
"sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72",
"sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d",
"sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4",
"sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038",
"sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e",
"sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13",
"sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d",
"sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95",
"sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31",
"sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3",
"sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03",
"sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6",
"sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2",
"sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b",
"sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7",
"sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab",
"sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219",
"sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571",
"sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d",
"sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1",
"sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca",
"sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661",
"sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e",
"sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e",
"sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e",
"sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a",
"sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3",
"sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881",
"sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221",
"sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742",
"sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773",
"sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e",
"sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529",
"sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67",
"sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c",
"sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"
],
"markers": "python_version >= '3.10'",
"version": "==2.1.3"
"version": "==2.2.0"
},
"packaging": {
"hashes": [
@ -532,6 +577,36 @@
"markers": "python_version >= '3.9'",
"version": "==3.2.0"
},
"pyside6": {
"hashes": [
"sha256:6d1fd95651cdbdea741af21e155350986eca31ff015fc4c721ce01c2a110a4cc",
"sha256:7d6adc5d53313249bbe02edb673877c1d437e215d71e88da78412520653f5c9f",
"sha256:866eeaca3ffead6b9d30fa3ed395d5624da0246d7586c8b8207e77ac65d82458",
"sha256:ddeeaeca8ebd0ddb1ded30dd33e9240a40f330cc91832de346ba6c9d0cd1253e"
],
"markers": "python_version < '3.14' and python_version >= '3.9'",
"version": "==6.8.1"
},
"pyside6-addons": {
"hashes": [
"sha256:570a25016d80046274f454ed0bb06734f478ce6c21be5dec62b624773fc7504e",
"sha256:879c12346b4b76f5d5ee6499d8ca53b5666c0c998b8fdf8780f08f69ea95d6f9",
"sha256:d7c8c1e89ee0db84631d5b8fdb9129d9d2a0ffb3b4cb2f5192dc8367dd980db4",
"sha256:f80cc03c1ac54132c6f800aa461dced64acd7d1646898db164ccb56fe3c23dd4"
],
"markers": "python_version < '3.14' and python_version >= '3.9'",
"version": "==6.8.1"
},
"pyside6-essentials": {
"hashes": [
"sha256:2f600b149e65b57acd6a444edb17615adc42cc2491548ae443ccb574036d86b1",
"sha256:bd05155245e3cd1572e68d72772e78fadfd713575bbfdd2c5e060d5278e390e9",
"sha256:bf8a3c9ee0b997eb18fb00cb09aacaa28b8a51ce3c295a252cc594c5530aba56",
"sha256:d5ed4ddb149f36d65bc49ae4260b2d213ee88b2d9a309012ae27f38158c2d1b6"
],
"markers": "python_version < '3.14' and python_version >= '3.9'",
"version": "==6.8.1"
},
"python-dateutil": {
"hashes": [
"sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
@ -549,11 +624,11 @@
},
"scikit-rf": {
"hashes": [
"sha256:722def5442bb7033d608e8d3f717b38a3315846496925e85e8015cf006c36bb7",
"sha256:d7e1de31842d8922d1c741de7cc09aecf083a94ddc408ee2fe3f36a284858a09"
"sha256:796758dcd79650d8c66585583ac8ef13afde0d65d2da324ec60a4557eae52a9a",
"sha256:9864653abf0761049ba95deb37741581fff5cf8d43f4acf6e46576e34e04ad43"
],
"markers": "python_version >= '3.8'",
"version": "==1.4.1"
"version": "==1.5.0"
},
"scipy": {
"hashes": [
@ -594,13 +669,23 @@
"markers": "python_version >= '3.10'",
"version": "==1.14.1"
},
"shiboken6": {
"hashes": [
"sha256:1dc4c1976809b0e68872bb98474cccd590455bdcd015f0e0639907e94af27b6a",
"sha256:3ea127fd72be113b73cacd70e06687ad6f83c1c888047833c7dcdd5cf8e7f586",
"sha256:9a2f51d1ddd3b6d193a0f0fdc09f8d41f2092bc664723c9b9efc1056660d0608",
"sha256:ab5b60602ca6227103138aae89c4f5df3b1b8e249cbc8ec9e6e2a57f20ad9a91"
],
"markers": "python_version < '3.14' and python_version >= '3.9'",
"version": "==6.8.1"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
"version": "==1.17.0"
},
"typing-extensions": {
"hashes": [
@ -620,11 +705,11 @@
},
"xarray": {
"hashes": [
"sha256:ae1d38cb44a0324dfb61e492394158ae22389bf7de9f3c174309c17376df63a0",
"sha256:e369e2bac430e418c2448e5b96f07da4635f98c1319aa23cfeb3fbcb9a01d2e0"
"sha256:1ccace44573ddb862e210ad3ec204210654d2c750bec11bbe7d842dfc298591f",
"sha256:6ee94f63ddcbdd0cf3909d1177f78cdac756640279c0e32ae36819a89cdaba37"
],
"markers": "python_version >= '3.10'",
"version": "==2024.10.0"
"version": "==2024.11.0"
},
"zarr": {
"hashes": [
@ -638,10 +723,11 @@
"develop": {
"asttokens": {
"hashes": [
"sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24",
"sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"
"sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7",
"sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"
],
"version": "==2.4.1"
"markers": "python_version >= '3.8'",
"version": "==3.0.0"
},
"comm": {
"hashes": [
@ -653,12 +739,35 @@
},
"debugpy": {
"hashes": [
"sha256:a6531d952b565b7cb2fbd1ef5df3d333cf160b44f37547a4e7cf73666aca5d8d",
"sha256:e6355385db85cbd666be703a96ab7351bc9e6c61d694893206f8001e22aee091",
"sha256:ec684553aba5b4066d4de510859922419febc710df7bba04fe9e7ef3de15d34f"
"sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e",
"sha256:17c5e0297678442511cf00a745c9709e928ea4ca263d764e90d233208889a19e",
"sha256:1efbb3ff61487e2c16b3e033bc8595aea578222c08aaf3c4bf0f93fadbd662ee",
"sha256:365e556a4772d7d0d151d7eb0e77ec4db03bcd95f26b67b15742b88cacff88e9",
"sha256:3d9755e77a2d680ce3d2c5394a444cf42be4a592caaf246dbfbdd100ffcf7ae5",
"sha256:3e59842d6c4569c65ceb3751075ff8d7e6a6ada209ceca6308c9bde932bcef11",
"sha256:472a3994999fe6c0756945ffa359e9e7e2d690fb55d251639d07208dbc37caea",
"sha256:54a7e6d3014c408eb37b0b06021366ee985f1539e12fe49ca2ee0d392d9ceca5",
"sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe",
"sha256:62d22dacdb0e296966d7d74a7141aaab4bec123fa43d1a35ddcb39bf9fd29d70",
"sha256:66eeae42f3137eb428ea3a86d4a55f28da9bd5a4a3d369ba95ecc3a92c1bba53",
"sha256:6953b335b804a41f16a192fa2e7851bdcfd92173cbb2f9f777bb934f49baab65",
"sha256:7c4d65d03bee875bcb211c76c1d8f10f600c305dbd734beaed4077e902606fee",
"sha256:7e646e62d4602bb8956db88b1e72fe63172148c1e25c041e03b103a25f36673c",
"sha256:7e8b079323a56f719977fde9d8115590cb5e7a1cba2fcee0986ef8817116e7c1",
"sha256:8138efff315cd09b8dcd14226a21afda4ca582284bf4215126d87342bba1cc66",
"sha256:8e99c0b1cc7bf86d83fb95d5ccdc4ad0586d4432d489d1f54e4055bcc795f693",
"sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2",
"sha256:957ecffff80d47cafa9b6545de9e016ae8c9547c98a538ee96ab5947115fb3dd",
"sha256:ada7fb65102a4d2c9ab62e8908e9e9f12aed9d76ef44880367bc9308ebe49a0f",
"sha256:b74a49753e21e33e7cf030883a92fa607bddc4ede1aa4145172debc637780040",
"sha256:c36856343cbaa448171cba62a721531e10e7ffb0abff838004701454149bc037",
"sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899",
"sha256:cfe1e6c6ad7178265f74981edf1154ffce97b69005212fbc90ca22ddfe3d017e",
"sha256:e46b420dc1bea64e5bbedd678148be512442bc589b0111bd799367cde051e71a",
"sha256:ff54ef77ad9f5c425398efb150239f6fe8e20c53ae2f68367eba7ece1e96226d"
],
"markers": "python_version >= '3.8'",
"version": "==1.8.8"
"version": "==1.8.9"
},
"decorator": {
"hashes": [
@ -695,11 +804,11 @@
},
"ipython": {
"hashes": [
"sha256:0188a1bd83267192123ccea7f4a8ed0a78910535dbaa3f37671dca76ebd429c8",
"sha256:40b60e15b22591450eef73e40a027cf77bd652e757523eebc5bd7c7c498290eb"
"sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321",
"sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e"
],
"markers": "python_version >= '3.10'",
"version": "==8.29.0"
"version": "==8.30.0"
},
"ipywidgets": {
"hashes": [
@ -712,11 +821,11 @@
},
"jedi": {
"hashes": [
"sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd",
"sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"
"sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0",
"sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"
],
"markers": "python_version >= '3.6'",
"version": "==0.19.1"
"version": "==0.19.2"
},
"jupyter-client": {
"hashes": [
@ -968,11 +1077,11 @@
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
"version": "==1.17.0"
},
"stack-data": {
"hashes": [
@ -983,20 +1092,20 @@
},
"tornado": {
"hashes": [
"sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8",
"sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f",
"sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4",
"sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3",
"sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14",
"sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842",
"sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9",
"sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698",
"sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7",
"sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d",
"sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"
"sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803",
"sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec",
"sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482",
"sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634",
"sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38",
"sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b",
"sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c",
"sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf",
"sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946",
"sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73",
"sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"
],
"markers": "python_version >= '3.8'",
"version": "==6.4.1"
"version": "==6.4.2"
},
"traitlets": {
"hashes": [

View File

@ -1,6 +1,6 @@
# Charon
# Charon VNA
Named after [Pluto's moon](https://en.wikipedia.org/wiki/Charon_(moon)), Charon uses the [ADI Pluto SDR]() as a vector network analyzer. The basic usage is as a 1 port VNA but this can be extended to arbitrarily many ports with the addition of a couple RF switches.
Named after [Pluto's moon](https://en.wikipedia.org/wiki/Charon_(moon)), Charon uses the [ADI Pluto SDR](https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/adalm-pluto.html) as a vector network analyzer. The basic usage is as a 1 port VNA but this can be extended to arbitrarily many ports with the addition of a couple RF switches.
## Installation
@ -31,7 +31,7 @@ Note that unlike the main calibration, power calibration frequencies do not need
You need a few things:
- [Analog Devices Pluto SDR](https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/adalm-pluto.html).
Any variant of the Pluto *should* work too such as the [Pluto+](https://github.com/plutoplus/plutoplus?tab=readme-ov-file)
Any variant of the Pluto *should* work too such as the [Pluto+](https://github.com/plutoplus/plutoplus?tab=readme-ov-file) however I have only tested with the basic flavor.
- Directional couplers (1 per port up to 4 ports).
I have been using [AAMCS-UDC-0.5G-18G-10dB-Sf](http://www.aa-mcs.com/wp-content/uploads/documents/AAMCS-UDC-0.5G-18G-10dB-Sf.pdf)
- Charon switch board - coming soon.

204
charon_vna/gui.py Normal file
View File

@ -0,0 +1,204 @@
import sys
from pathlib import Path
from typing import Callable, List, Tuple
import matplotlib as mpl
import numpy as np
import xarray as xr
from gui_helpers import FlowLayout
from matplotlib import pyplot as plt
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.ticker import EngFormatter
from numpy import typing as npt
from PySide6.QtCore import QSize, Qt
from PySide6.QtGui import QAction, QKeySequence, QShortcut
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QMenu,
QProgressBar,
QPushButton,
QToolBar,
QVBoxLayout,
QWidget,
)
from skrf import plotting as rf_plt
from util import db20, s2vswr
from vna import Charon
DEFAULT_CONFIG = dict(
frequency=np.arange(1e9, 2e9, 11), # Hz
power=-5, # dB
)
class PlotWidget(QWidget):
enabled_ports: List[Tuple[int | str]]
def __init__(self):
super().__init__()
self.enabled_ports = [(1, 1)]
layout = QVBoxLayout()
self.setLayout(layout)
self.fig = plt.Figure(figsize=(5, 4), dpi=100, tight_layout=True)
self.ax = self.fig.add_subplot(111)
self.setup_logmag()
# self.setup_smith()
self.ax.legend(loc="upper right")
canvas = FigureCanvasQTAgg(self.fig)
layout.addWidget(canvas)
# toolbar = QToolBar("Toolbar")
# toolbar.addAction("blah")
# self.addToolBar(toolbar)
def setup_rect(self) -> None:
self.ax.grid(True)
self.ax.xaxis.set_major_formatter(EngFormatter())
self.ax.set_xlabel("Frequency [Hz]")
def update_rect(
self, data: xr.DataArray, func: Callable[[npt.ArrayLike[np.complex128]], npt.ArrayLike[float]]
) -> None:
self.ax.set_xlim(data["frequency"].min().data, data["frequency"].max().data)
# remove old lines
for line in self.ax.lines:
line.remove()
for m, n in self.enabled_ports:
self.ax.plot(data["frequency"], func(data.sel(m=m, n=n)))
def setup_logmag(self, ylim: List[float] = [-30, 30]) -> None:
self.setup_rect()
self.ax.set_ylim(ylim)
self.ax.set_ylabel("Amplitude [dB]")
def update_logmag(self, data: xr.DataArray) -> None:
self.update_rect(data, db20)
def setup_phase(self) -> None:
self.setup_rect()
self.ax.set_ylim(-200, 200)
self.ax.set_ylabel("Phase [deg]")
def update_phase(self, data: xr.DataArray):
self.update_rect(data, lambda s: np.angle(s, deg=True))
def setup_vswr(self) -> None:
self.setup_rect()
self.ax.set_yticks(np.arange(1, 11))
self.ax.set_ylim(1, 10)
self.ax.set_ylabel("VSWR")
def update_vswr(self, data: xr.DataArray) -> None:
self.update_rect(data, s2vswr)
def setup_smith(self) -> None:
self.ax.grid(False)
self.ax.set_xlim(-1, 1)
self.ax.set_ylim(-1, 1)
self.ax.set_aspect("equal")
rf_plt.smith(ax=self.ax, smithR=1, chart_type="z", draw_vswr=None)
def update_smith(self, data: xr.DataArray) -> None:
# remove old lines
for line in self.ax.lines:
line.remove()
for m, n in self.enabled_ports:
sel = data.sel(m=m, n=n)
self.ax.plot(sel.real, sel.imag)
# Subclass QMainWindow to customize your application's main window
class MainWindow(QMainWindow):
config_path: Path | None
device: Charon
def __init__(self):
super().__init__()
self.config_path = None
self.device = Charon("ip:192.168.3.1", frequency=DEFAULT_CONFIG["frequency"])
mpl.use("QtAgg")
self.setWindowTitle("Charon VNA")
# Menu
menubar = self.menuBar()
menu_file = QMenu("&File")
menubar.addMenu(menu_file)
action_load_config = QAction("&Open Configuration", self)
menu_file.addAction(action_load_config)
action_load_config.triggered.connect(self.load_config)
QShortcut(QKeySequence("Ctrl+O"), self).activated.connect(self.load_config)
action_save_config = QAction("&Save Configuration", self)
menu_file.addAction(action_save_config)
action_save_config.triggered.connect(self.save_config)
QShortcut(QKeySequence("Ctrl+S"), self).activated.connect(self.save_config)
action_saveas_config = QAction("Save Configuration &As", self)
menu_file.addAction(action_saveas_config)
action_saveas_config.triggered.connect(self.saveas_config)
QShortcut(QKeySequence("Ctrl+Shift+S"), self).activated.connect(self.saveas_config)
menu_stimulus = QMenu("&Stimulus")
menubar.addMenu(menu_stimulus)
menu_calibration = QMenu("&Calibration")
menubar.addMenu(menu_calibration)
# Content
window_layout = QVBoxLayout()
prog_sweep = QProgressBar()
prog_sweep.setMinimum(0)
prog_sweep.setMaximum(100)
prog_sweep.setTextVisible(False)
prog_sweep.setValue(50)
window_layout.addWidget(prog_sweep)
# window_widget.se
plot_layout = QVBoxLayout()
# TODO: handle plots properly
for i in range(2):
plot0 = PlotWidget()
plot_layout.addWidget(plot0)
plot_widget = QWidget()
plot_widget.setLayout(plot_layout)
window_layout.addWidget(plot_widget)
# Set the central widget of the Window.
widget = QWidget()
widget.setLayout(window_layout)
self.setCentralWidget(widget)
def saveas_config(self) -> None:
print("Prompting for save path...")
# TODO: prompt for config path
self.config_path = Path(__file__).parent / "config.json"
print(f"Config path is now {self.config_path.resolve()}")
self.save_config()
def save_config(self) -> None:
if self.config_path is None:
self.saveas_config()
else:
print(f"saving config to {self.config_path.resolve()}")
# TODO: save config
def load_config(self) -> None:
print("loading config")
# TODO: load config
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

90
charon_vna/gui_helpers.py Normal file
View File

@ -0,0 +1,90 @@
from PySide6.QtCore import QMargins, QPoint, QRect, QSize, Qt
from PySide6.QtWidgets import QLayout, QSizePolicy
class FlowLayout(QLayout):
def __init__(self, parent=None):
super().__init__(parent)
if parent is not None:
self.setContentsMargins(QMargins(0, 0, 0, 0))
self._item_list = []
def __del__(self):
item = self.takeAt(0)
while item:
item = self.takeAt(0)
def addItem(self, item):
self._item_list.append(item)
def count(self):
return len(self._item_list)
def itemAt(self, index):
if 0 <= index < len(self._item_list):
return self._item_list[index]
return None
def takeAt(self, index):
if 0 <= index < len(self._item_list):
return self._item_list.pop(index)
return None
def expandingDirections(self):
return Qt.Orientation(0)
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
height = self._do_layout(QRect(0, 0, width, 0), True)
return height
def setGeometry(self, rect):
super(FlowLayout, self).setGeometry(rect)
self._do_layout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QSize()
for item in self._item_list:
size = size.expandedTo(item.minimumSize())
size += QSize(2 * self.contentsMargins().top(), 2 * self.contentsMargins().top())
return size
def _do_layout(self, rect, test_only):
x = rect.x()
y = rect.y()
line_height = 0
spacing = self.spacing()
for item in self._item_list:
style = item.widget().style()
layout_spacing_x = style.layoutSpacing(
QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Orientation.Horizontal
)
layout_spacing_y = style.layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)
space_x = spacing + layout_spacing_x
space_y = spacing + layout_spacing_y
next_x = x + item.sizeHint().width() + space_x
if next_x - space_x > rect.right() and line_height > 0:
x = rect.x()
y = y + line_height + space_y
next_x = x + item.sizeHint().width() + space_x
line_height = 0
if not test_only:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
x = next_x
line_height = max(line_height, item.sizeHint().height())
return y + line_height - rect.y()

12
charon_vna/io_.py Normal file
View File

@ -0,0 +1,12 @@
from pathlib import Path
import skrf as rf
import xarray as xr
from util import net2s
# scikit-rf has no way to save files aside from touchstone and pickle
def cal2zarr(cal: rf.calibration.Calibration, outpath: Path):
ideals = [net2s(net) for net in cal.ideals]
measured = [net2s(net) for net in cal.measured]
# s.to_zarr(outpath)

84
charon_vna/util.py Normal file
View File

@ -0,0 +1,84 @@
import numpy as np
import skrf as rf
import xarray as xr
from numpy import typing as npt
HAM_BANDS = [
[135.7e3, 137.8e3],
[472e3, 479e3],
[1.8e6, 2e6],
[3.5e6, 4e6],
[5332e3, 5405e3],
[7e6, 7.3e6],
[10.1e6, 10.15e6],
[14e6, 14.35e6],
[18.068e6, 18.168e6],
[21e6, 21.45e6],
[24.89e6, 24.99e6],
[28e6, 29.7e6],
[50e6, 54e6],
[144e6, 148e6],
[219e6, 220e6],
[222e6, 225e6],
[420e6, 450e6],
[902e6, 928e6],
[1240e6, 1300e6],
[2300e6, 2310e6],
[2390e6, 2450e6],
[3400e6, 3450e6],
[5650e6, 5925e6],
[10e9, 10.5e9],
[24e9, 24.25e9],
[47e9, 47.2e9],
[76e9, 81e9],
[122.25e9, 123e9],
[134e9, 141e9],
[241e9, 250e9],
[275e9, np.inf],
]
def db10(p: npt.ArrayLike[np.complex128]) -> npt.ArrayLike[float]:
return 10 * np.log10(np.abs(p))
def db20(v: npt.ArrayLike[np.complex128]) -> npt.ArrayLike[float]:
return 20 * np.log10(np.abs(v))
def s2vswr(s: npt.ArrayLike[np.complex128]) -> npt.ArrayLike[float]:
return (1 + np.abs(s)) / (1 - np.abs(s))
def minmax(x):
return (np.min(x), np.max(x))
def s2net(s: xr.DataArray) -> rf.Network:
net = rf.Network(frequency=s.frequency, f_unit="Hz", s=s)
return net
def net2s(net: rf.Network) -> xr.DataArray:
port_tuples = net.port_tuples
m = list(set(t[0] for t in port_tuples))
m.sort()
m = np.array(m)
m += 1 # skrf uses 0-indexed ports
n = list(set(t[0] for t in port_tuples))
n.sort()
n = np.array(n)
n += 1 # skrf uses 0-indexed ports
s = xr.DataArray(
net.s,
dims=["frequency", "m", "n"],
coords=dict(
frequency=net.f,
m=m,
n=n,
),
)
return s

View File

@ -1,147 +1,211 @@
# %% imports
import time
from typing import Optional
import copy
from pathlib import Path
from typing import Any, Dict, Tuple
import adi
import matplotlib as mpl
import iio
import numpy as np
import skrf as rf
import xarray as xr
from matplotlib import pyplot as plt
from matplotlib.gridspec import GridSpec
from matplotlib.patches import Circle
from matplotlib.ticker import EngFormatter
from numpy import typing as npt
from scipy import signal
from util import HAM_BANDS, db20, net2s, s2net
dir_ = Path(__file__).parent
# %% helper functions
def get_config(sdr: adi.ad9361):
config = dict()
config["rx_lo"] = sdr.rx_lo
config["rx_rf_bandwidth"] = sdr.rx_rf_bandwidth
config["rx_enabled_channels"] = sdr.rx_enabled_channels
for chan in config["rx_enabled_channels"]:
config[f"rx_hardwaregain_chan{chan}"] = getattr(sdr, f"rx_hardwaregain_chan{chan}")
config[f"gain_control_mode_chan{chan}"] = getattr(sdr, f"gain_control_mode_chan{chan}")
config["tx_lo"] = sdr.tx_lo
config["tx_rf_bandwidth"] = sdr.tx_rf_bandwidth
config["tx_cyclic_buffer"] = sdr.tx_cyclic_buffer
config["tx_enabled_channels"] = sdr.tx_enabled_channels
for chan in config["tx_enabled_channels"]:
config[f"tx_hardwaregain_chan{chan}"] = getattr(sdr, f"tx_hardwaregain_chan{chan}")
config["filter"] = sdr.filter
config["sample_rate"] = sdr.sample_rate
config["loopback"] = sdr.loopback
return config
# %% connection
def db10(p):
return 10 * np.log10(np.abs(p))
def db20(p):
return 20 * np.log10(np.abs(p))
def minmax(x):
return (np.min(x), np.max(x))
def generate_tone(f: float, N: int = 1024, fs: Optional[float] = None):
if fs is None:
fs = sdr.sample_rate
def generate_tone(f: float, fs: float, N: int = 1024, scale: int = 2**14):
fs = int(fs)
fc = int(f / (fs / N)) * (fs / N)
ts = 1 / float(fs)
t = np.arange(0, N * ts, ts)
i = np.cos(2 * np.pi * t * fc) * 2**14
q = np.sin(2 * np.pi * t * fc) * 2**14
i = np.cos(2 * np.pi * t * fc) * scale
q = np.sin(2 * np.pi * t * fc) * scale
iq = i + 1j * q
return iq
# %% connection
sdr = adi.ad9361(uri="ip:192.168.3.1")
class Charon:
FREQUENCY_OFFSET = 1e6
# %% initialization
sdr.rx_lo = int(2.0e9)
sdr.tx_lo = int(2.0e9)
sdr.sample_rate = 30e6
sdr.rx_rf_bandwidth = int(4e6)
sdr.tx_rf_bandwidth = int(4e6)
sdr.rx_destroy_buffer()
sdr.tx_destroy_buffer()
sdr.rx_enabled_channels = [0, 1]
sdr.tx_enabled_channels = [0]
sdr.loopback = 0
sdr.gain_control_mode_chan0 = "manual"
sdr.gain_control_mode_chan1 = "manual"
sdr.rx_hardwaregain_chan0 = 10
sdr.rx_hardwaregain_chan1 = 10
sdr.tx_hardwaregain_chan0 = -10
def __init__(
self,
uri: str,
frequency: npt.ArrayLike = np.linspace(1e9, 2e9, 3),
ports: Tuple[int] = (1,),
):
self.ports = ports
self.frequency = frequency
config = get_config(sdr)
config
# everything RF
self.sdr = adi.ad9361(uri=uri)
for attr, expected in [
("adi,2rx-2tx-mode-enable", True),
# ("adi,gpo-manual-mode-enable", True),
]:
# available configuration options:
# https://wiki.analog.com/resources/tools-software/linux-drivers/iio-transceiver/ad9361-customization
if bool(self.sdr._get_iio_debug_attr(attr)) != expected:
raise ValueError(
f"'{attr}' is not set in pluto. "
"See README.md for instructions for one time configuration instructions"
)
# TODO: it might be possible to change this on the fly.
# I think we'll actually just fail in __init__ of ad9361 if 2rx-2tx is wrong
# %%
sdr.tx_destroy_buffer() # must destroy buffer before changing cyclicity
sdr.tx_cyclic_buffer = True
sdr.tx(generate_tone(1e6))
self.sdr.rx_lo = int(self.frequency[0])
self.sdr.tx_lo = int(self.frequency[0])
self.sdr.sample_rate = 30e6
self.sdr.rx_rf_bandwidth = int(4e6)
self.sdr.tx_rf_bandwidth = int(4e6)
self.sdr.rx_destroy_buffer()
self.sdr.tx_destroy_buffer()
self.sdr.rx_enabled_channels = [0, 1]
self.sdr.tx_enabled_channels = [0]
self.sdr.loopback = 0
self.sdr.gain_control_mode_chan0 = "manual"
self.sdr.gain_control_mode_chan1 = "manual"
self.sdr.rx_hardwaregain_chan0 = 10
self.sdr.rx_hardwaregain_chan1 = 10
self.sdr.tx_hardwaregain_chan0 = -10
# %%
sdr.rx_destroy_buffer()
data = sdr.rx()
# # switch control
# ctx = iio.Context(uri)
# self.ctrl = ctx.find_device("ad9361-phy")
# # raw ad9361 register accesss:
# # https://ez.analog.com/linux-software-drivers/f/q-a/120853/control-fmcomms3-s-gpo-with-python
# # https://www.analog.com/media/cn/technical-documentation/user-guides/ad9364_register_map_reference_manual_ug-672.pdf
# self.ctrl.reg_write(0x26, 0x90) # bit 7: AuxDAC Manual, bit 4: GPO Manual
# self._set_gpo(self.ports[0] - 1)
# # TODO: init AuxDAC
# %% Plot in time
fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True)
axs[0].plot(np.real(data).T)
axs[1].plot(np.imag(data).T)
axs[0].set_ylabel("Real")
axs[1].set_ylabel("Imag")
axs[-1].set_xlabel("Time")
fig.show()
def get_config(self) -> Dict[str, Any]:
config = dict()
config["rx_lo"] = self.sdr.rx_lo
config["rx_rf_bandwidth"] = self.sdr.rx_rf_bandwidth
config["rx_enabled_channels"] = self.sdr.rx_enabled_channels
for chan in config["rx_enabled_channels"]:
config[f"rx_hardwaregain_chan{chan}"] = getattr(self.sdr, f"rx_hardwaregain_chan{chan}")
config[f"gain_control_mode_chan{chan}"] = getattr(self.sdr, f"gain_control_mode_chan{chan}")
# %% Plot in frequency
f, Pxx_den = signal.periodogram(data, sdr.sample_rate, axis=-1, return_onesided=False)
plt.figure()
for cc, chan in enumerate(sdr.rx_enabled_channels):
plt.semilogy(f, Pxx_den[cc], label=f"Channel {chan}")
plt.legend()
plt.ylim([1e-7, 1e2])
plt.xlabel("frequency [Hz]")
plt.ylabel("PSD [V**2/Hz]")
plt.grid(True)
plt.show()
config["tx_lo"] = self.sdr.tx_lo
config["tx_rf_bandwidth"] = self.sdr.tx_rf_bandwidth
config["tx_cyclic_buffer"] = self.sdr.tx_cyclic_buffer
config["tx_enabled_channels"] = self.sdr.tx_enabled_channels
for chan in config["tx_enabled_channels"]:
config[f"tx_hardwaregain_chan{chan}"] = getattr(self.sdr, f"tx_hardwaregain_chan{chan}")
config["filter"] = self.sdr.filter
config["sample_rate"] = self.sdr.sample_rate
config["loopback"] = self.sdr.loopback
# %% TX helper functions
def set_output_power(power: float):
if power == -5:
return config
def _get_gpo(self) -> int:
return (self.ctrl.reg_read(0x27) >> 4) & 0x0F
def _set_gpo(self, value: int) -> None:
self.ctrl.reg_write(0x27, (value & 0x0F) << 4) # bits 7-4: GPO3-0
def set_output_power(self, power: float):
# FIXME: this is a hack because I don't want to go through re-calibration
tx_gain = -8
if power == 5:
tx_gain = -1
elif power == 0:
tx_gain = -7
elif power == -5:
tx_gain = -12
elif power == -10:
tx_gain = -17
elif power == -15:
tx_gain = -22
else:
raise NotImplementedError()
# # TODO: correct over frequency
# tx_gain_idx = np.abs(pout.sel(tx_channel=0) - power).argmin(dim="tx_gain")
# tx_gain = pout.coords["tx_gain"][tx_gain_idx]
sdr.tx_hardwaregain_chan0 = float(tx_gain)
self.sdr.tx_hardwaregain_chan0 = float(tx_gain)
def set_output(self, frequency: float, power: float):
# TODO: switch to DDS in Pluto
def set_output(frequency: float, power: float, offset_frequency: float = 1e6):
sdr.tx_destroy_buffer()
set_output_power(power)
sdr.tx_lo = int(frequency - offset_frequency)
offset_frequency = frequency - sdr.tx_lo
sdr.tx_cyclic_buffer = True
sdr.tx(generate_tone(offset_frequency))
self.sdr.tx_destroy_buffer()
self.set_output_power(power)
self.sdr.tx_lo = int(frequency - self.FREQUENCY_OFFSET)
self.sdr.tx_cyclic_buffer = True
# self.sdr.tx(generate_tone(f=self.FREQUENCY_OFFSET, fs=self.sdr.sample_rate))
self.sdr.dds_single_tone(self.FREQUENCY_OFFSET, scale=0.9, channel=0)
def _rx(self, count: int = 1, fc: float | None = None) -> npt.ArrayLike:
if count < 1:
raise ValueError
# %%
def vna_capture(frequency: npt.ArrayLike):
self.sdr.rx_destroy_buffer()
if fc is not None:
self.sdr.rx_lo = int(fc)
self.sdr.rx_enabled_channels = [0, 1]
self.sdr.gain_control_mode_chan0 = "manual"
self.sdr.gain_control_mode_chan1 = "manual"
self.sdr.rx_hardwaregain_chan0 = 30
self.sdr.rx_hardwaregain_chan1 = 30
return np.concatenate([np.array(self.sdr.rx()) for _ in range(count)], axis=-1)
def get_b_over_a(self, frequency: float):
self.set_output(frequency=frequency, power=-5)
data = self._rx(1, fc=frequency - self.FREQUENCY_OFFSET)
ddc_tone = generate_tone(f=-self.FREQUENCY_OFFSET, fs=self.sdr.sample_rate, scale=1)
ddc_data = data * ddc_tone
ddc_rel = ddc_data[1] / ddc_data[0]
# plt.figure()
# plt.plot(
# np.fft.fftshift(np.fft.fftfreq(ddc_data.shape[-1], 1 / self.sdr.sample_rate)),
# np.abs(np.fft.fftshift(np.fft.fft(ddc_data, axis=-1))).T,
# )
# plt.show()
# TODO: calculate sos only once
n, wn = signal.buttord(
wp=0.3 * sdr.FREQUENCY_OFFSET,
ws=0.8 * sdr.FREQUENCY_OFFSET,
gpass=1,
gstop=40,
analog=False,
fs=self.sdr.sample_rate,
)
sos = signal.butter(n, wn, "lowpass", analog=False, output="sos", fs=self.sdr.sample_rate)
# TODO: figure out why filt sucks. Introduces SO much phase noise (out to several MHz)
filt_data = signal.sosfiltfilt(sos, ddc_data, axis=-1)
filt_rel = filt_data[1] / filt_data[0]
return np.mean(data[1] / data[0])
def sweep_b_over_a(self):
s = xr.DataArray(
np.zeros(
len(self.frequency),
dtype=np.complex128,
),
dims=["frequency"],
coords=dict(
frequency=self.frequency,
),
)
for frequency in self.frequency:
s.loc[dict(frequency=frequency)] = self.get_b_over_a(frequency=frequency)
return s
def vna_capture(self, frequency: npt.ArrayLike):
s = xr.DataArray(
np.empty(len(frequency), dtype=np.complex128),
dims=["frequency"],
@ -150,22 +214,82 @@ def vna_capture(frequency: npt.ArrayLike):
),
)
for freq in s.frequency.data:
set_output(frequency=freq, power=-5)
sdr.rx_destroy_buffer()
sdr.rx_lo = int(freq)
sdr.rx_enabled_channels = [0, 1]
sdr.gain_control_mode_chan0 = "manual"
sdr.gain_control_mode_chan1 = "manual"
sdr.rx_hardwaregain_chan0 = 40
sdr.rx_hardwaregain_chan1 = 40
rx = sdr.rx()
self.set_output(frequency=freq, power=-5)
self.sdr.rx_destroy_buffer()
self.sdr.rx_lo = int(freq)
self.sdr.rx_enabled_channels = [0, 1]
self.sdr.gain_control_mode_chan0 = "manual"
self.sdr.gain_control_mode_chan1 = "manual"
self.sdr.rx_hardwaregain_chan0 = 40
self.sdr.rx_hardwaregain_chan1 = 40
rx = self.sdr.rx()
s.loc[dict(frequency=freq)] = np.mean(rx[1] / rx[0])
return s
# %%
s = vna_capture(frequency=np.linspace(70e6, 200e6, 101))
sdr = Charon("ip:192.168.3.1", frequency=np.linspace(1e9, 1.1e9, 11))
# %% initialization
config = sdr.get_config()
# print(sdr.ctrl.debug_attrs["adi,rx-rf-port-input-select"].value)
# print(sdr.ctrl.debug_attrs["adi,tx-rf-port-input-select"].value)
config
# %% generate tone
fc = 1e9
sdr.set_output(frequency=fc + sdr.FREQUENCY_OFFSET, power=-5)
# %% capture data
data = sdr._rx(1, fc=fc)
# %% Plot in time
fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True)
axs[0].plot(np.real(data).T)
axs[1].plot(np.imag(data).T)
axs[0].set_ylabel("Real")
axs[1].set_ylabel("Imag")
axs[0].grid(True)
axs[1].grid(True)
axs[-1].set_xlabel("Sample")
axs[-1].set_xlim(0, data.shape[-1])
fig.show()
# %%
fig, ax = plt.subplots(1, 1, tight_layout=True)
ax.plot(np.real(data).T, np.imag(data).T)
ax.grid(True)
ax.set_aspect("equal")
ax.set_xlabel("Real")
ax.set_ylabel("Imag")
ax.set_xlim(np.array([-1, 1]) * (2 ** (12 - 1) - 1))
ax.set_ylim(ax.get_xlim())
fig.show()
# %% Plot in frequency
f = np.fft.fftfreq(data.shape[-1], 1 / sdr.sdr.sample_rate)
RX_BITS = 12 # for each of i, q (including sign bit)
fft_data = np.fft.fft(data, axis=-1, norm="forward") / (2 ** (RX_BITS - 1))
plt.figure()
for cc, chan in enumerate(sdr.sdr.rx_enabled_channels):
plt.plot(
np.fft.fftshift(f),
db20(np.fft.fftshift(fft_data[cc])),
label=f"Channel {chan}",
)
plt.legend()
plt.ylim(-100, 0)
plt.xlabel("Frequency [Hz]")
plt.ylabel("Power [dBfs]")
plt.title(f"Fc = {sdr.sdr.rx_lo / 1e9} GHz")
plt.gca().xaxis.set_major_formatter(EngFormatter())
plt.grid(True)
plt.show()
# %%
s = sdr.vna_capture(frequency=np.linspace(70e6, 200e6, 101))
# %% Plot Logmag
fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True)
@ -185,12 +309,11 @@ axs[1].set_xlabel("Frequency")
axs[0].set_ylabel("|S11| [dB]")
axs[1].set_ylabel("∠S11 [deg]")
reference_sparams = "/home/brendan/Documents/projects/bh_instruments/rbp135.npz"
reference_sparams = None
reference_sparams = dir_ / "RBP-135+_Plus25degC.s2p"
if reference_sparams is not None:
rbp135 = np.load(reference_sparams)
rbp135 = xr.DataArray(
rbp135["s"], dims=["frequency", "m", "n"], coords=dict(frequency=rbp135["frequency"], m=[1, 2], n=[1, 2])
)
ref = rf.Network(reference_sparams)
rbp135 = net2s(ref)
axs[0].plot(rbp135.frequency, db20(rbp135.sel(m=1, n=1)), label="Datasheet")
axs[1].plot(rbp135.frequency, np.rad2deg(np.angle(rbp135.sel(m=2, n=1))), label="Datasheet")
@ -198,3 +321,66 @@ if reference_sparams is not None:
axs[1].legend()
plt.show()
# %% SOL calibration
cal_frequency = np.linspace(70e6, 600e6, 101)
ideal_cal_frequency = rf.Frequency(np.min(cal_frequency), np.max(cal_frequency), len(cal_frequency))
input("Connect SHORT and press ENTER...")
short = sdr.vna_capture(frequency=cal_frequency)
input("Connect OPEN and press ENTER...")
open = sdr.vna_capture(frequency=cal_frequency)
input("Connect LOAD and press ENTER...")
load = sdr.vna_capture(frequency=cal_frequency)
short_net = s2net(short)
open_net = s2net(open)
load_net = s2net(load)
cal_ideal = rf.media.DefinedGammaZ0(frequency=ideal_cal_frequency)
calibration = rf.calibration.OnePort(
[short_net, open_net, load_net],
[cal_ideal.short(), cal_ideal.open(), cal_ideal.load(0)],
)
# %%
s = sdr.vna_capture(frequency=cal_frequency)
# %%
s_calibrated = calibration.apply_cal(s2net(s))
plt.figure()
s_calibrated.plot_s_smith()
# ref.plot_s_smith(m=1, n=1)
plt.show()
plt.figure()
for start, stop in HAM_BANDS:
plt.axvspan(start, stop, alpha=0.1, color="k")
s_calibrated.plot_s_db()
# ref.plot_s_db(m=1, n=1)
plt.gca().xaxis.set_major_formatter(EngFormatter())
plt.grid(True)
plt.xlim(s_calibrated.f[0], s_calibrated.f[-1])
plt.show()
plt.figure()
for start, stop in HAM_BANDS:
plt.axvspan(start, stop, alpha=0.1, color="k")
# s_calibrated.plot_s_vswr()
# drop invalid points
vswr = copy.deepcopy(s_calibrated.s_vswr[:, 0, 0])
vswr[vswr < 1] = np.nan
plt.plot(s_calibrated.f, vswr)
plt.axhline(1, color="k", linestyle="--")
plt.ylabel("VSWR")
plt.xlabel("Frequency [Hz]")
# ref.plot_s_vswr(m=1, n=1)
plt.gca().xaxis.set_major_formatter(EngFormatter())
plt.grid(True)
plt.ylim(0, 10)
plt.xlim(s_calibrated.f[0], s_calibrated.f[-1])
plt.show()
# %%

View File

@ -21,6 +21,7 @@ dependencies = [
"pyadi-iio",
"click",
"matplotlib",
"pyside6",
]
dynamic = ["version"]
@ -50,3 +51,6 @@ exclude = '''
| dist
)/
'''
[tool.isort]
profile = "black"