Compare commits

..

No commits in common. "main" and "v0.1.0" have entirely different histories.
main ... v0.1.0

14 changed files with 377 additions and 2044 deletions

View File

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

View File

@ -9,9 +9,6 @@ charon-vna = {file = ".", editable = true}
[dev-packages] [dev-packages]
ipykernel = "*" ipykernel = "*"
ipywidgets = "*" ipywidgets = "*"
black = "*"
flake8 = "*"
isort = "*"
[requires] [requires]
python_version = "3.10" python_version = "3.10"

633
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "470252a74774400955ef60d73f7e697760609be00b7733aa7a51b2f192e37fe5" "sha256": "8ca47bc9e64405da418625530ed3861fbf72ff2f615aa4053f0da928dd09a494"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -36,63 +36,74 @@
}, },
"contourpy": { "contourpy": {
"hashes": [ "hashes": [
"sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1", "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0",
"sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda", "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639",
"sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d", "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd",
"sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509", "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad",
"sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6", "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843",
"sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f", "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8",
"sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e", "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4",
"sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751", "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1",
"sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86", "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294",
"sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b", "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84",
"sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc", "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927",
"sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8",
"sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec", "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09",
"sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f", "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7",
"sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82", "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f",
"sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c", "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab",
"sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b", "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b",
"sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c", "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3",
"sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c", "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223",
"sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53", "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973",
"sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80", "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087",
"sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242", "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081",
"sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85", "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc",
"sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124", "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18",
"sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5", "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f",
"sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2", "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d",
"sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3", "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2",
"sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d", "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41",
"sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc", "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67",
"sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342", "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6",
"sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b",
"sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2",
"sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595", "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c",
"sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30", "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42",
"sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab", "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d",
"sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3", "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4",
"sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2", "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5",
"sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd", "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49",
"sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7", "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b",
"sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277", "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7",
"sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453", "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102",
"sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697", "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb",
"sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b", "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7",
"sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454", "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e",
"sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9", "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c",
"sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1", "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8",
"sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6", "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35",
"sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291", "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b",
"sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750", "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14",
"sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb",
"sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e", "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589",
"sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81", "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c",
"sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9", "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0",
"sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375" "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da",
"sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800",
"sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6",
"sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66",
"sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca",
"sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb",
"sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c",
"sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06",
"sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779",
"sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8",
"sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f",
"sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"
], ],
"markers": "python_version >= '3.10'", "markers": "python_version >= '3.9'",
"version": "==1.3.1" "version": "==1.3.0"
}, },
"cycler": { "cycler": {
"hashes": [ "hashes": [
@ -112,59 +123,57 @@
}, },
"fonttools": { "fonttools": {
"hashes": [ "hashes": [
"sha256:07f8288aacf0a38d174445fc78377a97fb0b83cfe352a90c9d9c1400571963c7", "sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6",
"sha256:11e5de1ee0d95af4ae23c1a138b184b7f06e0b6abacabf1d0db41c90b03d834b", "sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263",
"sha256:1bc7ad24ff98846282eef1cbeac05d013c2154f977a79886bb943015d2b1b261", "sha256:0d1d353ef198c422515a3e974a1e8d5b304cd54a4c2eebcae708e37cd9eeffb1",
"sha256:1dcc07934a2165ccdc3a5a608db56fb3c24b609658a5b340aee4ecf3ba679dc0", "sha256:0e88e3018ac809b9662615072dcd6b84dca4c2d991c6d66e1970a112503bba7e",
"sha256:22f38464daa6cdb7b6aebd14ab06609328fe1e9705bb0fcc7d1e69de7109ee02", "sha256:1d152d1be65652fc65e695e5619e0aa0982295a95a9b29b52b85775243c06556",
"sha256:27e4ae3592e62eba83cd2c4ccd9462dcfa603ff78e09110680a5444c6925d841", "sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d",
"sha256:3983313c2a04d6cc1fe9251f8fc647754cf49a61dac6cb1e7249ae67afaafc45", "sha256:278913a168f90d53378c20c23b80f4e599dca62fbffae4cc620c8eed476b723e",
"sha256:529cef2ce91dc44f8e407cc567fae6e49a1786f2fefefa73a294704c415322a4", "sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2",
"sha256:5323a22eabddf4b24f66d26894f1229261021dacd9d29e89f7872dd8c63f0b8b", "sha256:31c32d7d4b0958600eac75eaf524b7b7cb68d3a8c196635252b7a2c30d80e986",
"sha256:54153c49913f45065c8d9e6d0c101396725c5621c8aee744719300f79771d75a", "sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb",
"sha256:546565028e244a701f73df6d8dd6be489d01617863ec0c6a42fa25bf45d43048", "sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd",
"sha256:5480673f599ad410695ca2ddef2dfefe9df779a9a5cda89503881e503c9c7d90", "sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882",
"sha256:5e8d657cd7326eeaba27de2740e847c6b39dde2f8d7cd7cc56f6aad404ddf0bd", "sha256:4aa4817f0031206e637d1e685251ac61be64d1adef111060df84fdcbc6ab6c44",
"sha256:62d65a3022c35e404d19ca14f291c89cc5890032ff04f6c17af0bd1927299674", "sha256:4e10d2e0a12e18f4e2dd031e1bf7c3d7017be5c8dbe524d07706179f355c5dac",
"sha256:6314bf82c54c53c71805318fcf6786d986461622dd926d92a465199ff54b1b72", "sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20",
"sha256:7a8aa2c5e5b8b3bcb2e4538d929f6589a5c6bdb84fd16e2ed92649fb5454f11c", "sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d",
"sha256:827e95fdbbd3e51f8b459af5ea10ecb4e30af50221ca103bea68218e9615de07", "sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a",
"sha256:859c358ebf41db18fb72342d3080bce67c02b39e86b9fbcf1610cca14984841b", "sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c",
"sha256:86721fbc389ef5cc1e2f477019e5069e8e4421e8d9576e9c26f840dbb04678de", "sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d",
"sha256:89bdc5d88bdeec1b15af790810e267e8332d92561dce4f0748c2b95c9bdf3926", "sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff",
"sha256:8c4491699bad88efe95772543cd49870cf756b019ad56294f6498982408ab03e", "sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7",
"sha256:8c5ec45428edaa7022f1c949a632a6f298edc7b481312fc7dc258921e9399628", "sha256:7965af9b67dd546e52afcf2e38641b5be956d68c425bef2158e95af11d229f10",
"sha256:8e75f12c82127486fac2d8bfbf5bf058202f54bf4f158d367e41647b972342ca", "sha256:7e3b7d44e18c085fd8c16dcc6f1ad6c61b71ff463636fcb13df7b1b818bd0c02",
"sha256:a430178ad3e650e695167cb53242dae3477b35c95bef6525b074d87493c4bf29", "sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2",
"sha256:a8c2794ded89399cc2169c4d0bf7941247b8d5932b2659e09834adfbb01589aa", "sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07",
"sha256:aca318b77f23523309eec4475d1fbbb00a6b133eb766a8bdc401faba91261abe", "sha256:8583e563df41fdecef31b793b4dd3af8a9caa03397be648945ad32717a92885b",
"sha256:ae3b6600565b2d80b7c05acb8e24d2b26ac407b27a3f2e078229721ba5698427", "sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08",
"sha256:aedbeb1db64496d098e6be92b2e63b5fac4e53b1b92032dfc6988e1ea9134a4d", "sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab",
"sha256:aee3b57643827e237ff6ec6d28d9ff9766bd8b21e08cd13bff479e13d4b14765", "sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285",
"sha256:b54baf65c52952db65df39fcd4820668d0ef4766c0ccdf32879b77f7c804d5c5", "sha256:9dc080e5a1c3b2656caff2ac2633d009b3a9ff7b5e93d0452f40cd76d3da3b3c",
"sha256:b586ab5b15b6097f2fb71cafa3c98edfd0dba1ad8027229e7b1e204a58b0e09d", "sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58",
"sha256:b8d5e8916c0970fbc0f6f1bece0063363bb5857a7f170121a4493e31c3db3314", "sha256:a7a310c6e0471602fe3bf8efaf193d396ea561486aeaa7adc1f132e02d30c4b9",
"sha256:bc5dbb4685e51235ef487e4bd501ddfc49be5aede5e40f4cefcccabc6e60fb4b", "sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc",
"sha256:bdcc9f04b36c6c20978d3f060e5323a43f6222accc4e7fcbef3f428e216d96af", "sha256:ada215fd079e23e060157aab12eba0d66704316547f334eee9ff26f8c0d7b8ab",
"sha256:c3ca99e0d460eff46e033cd3992a969658c3169ffcd533e0a39c63a38beb6831", "sha256:c39287f5c8f4a0c5a55daf9eaf9ccd223ea59eed3f6d467133cc727d7b943a55",
"sha256:caf8230f3e10f8f5d7593eb6d252a37caf58c480b19a17e250a63dad63834cf3", "sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714",
"sha256:cd70de1a52a8ee2d1877b6293af8a2484ac82514f10b1c67c1c5762d38073e56", "sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8",
"sha256:cf4fe7c124aa3f4e4c1940880156e13f2f4d98170d35c749e6b4f119a872551e", "sha256:d3b659d1029946f4ff9b6183984578041b520ce0f8fb7078bb37ec7445806b33",
"sha256:d342e88764fb201286d185093781bf6628bbe380a913c24adf772d901baa8276", "sha256:dd9cc95b8d6e27d01e1e1f1fae8559ef3c02c76317da650a19047f249acd519d",
"sha256:da9da6d65cd7aa6b0f806556f4985bcbf603bf0c5c590e61b43aa3e5a0f822d0", "sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e",
"sha256:dc5294a3d5c84226e3dbba1b6f61d7ad813a8c0238fceea4e09aa04848c3d851", "sha256:e7d82b9e56716ed32574ee106cabca80992e6bbdcf25a88d97d21f73a0aae664",
"sha256:dd68c87a2bfe37c5b33bcda0fba39b65a353876d3b9006fde3adae31f97b3ef5", "sha256:e8a4b261c1ef91e7188a30571be6ad98d1c6d9fa2427244c545e2fa0a2494dd7",
"sha256:e6e8766eeeb2de759e862004aa11a9ea3d6f6d5ec710551a88b476192b64fd54", "sha256:e96bc94c8cda58f577277d4a71f51c8e2129b8b36fd05adece6320dd3d57de8a",
"sha256:e894b5bd60d9f473bed7a8f506515549cc194de08064d829464088d23097331b", "sha256:ed2f80ca07025551636c555dec2b755dd005e2ea8fbeb99fc5cdff319b70b23b",
"sha256:eb6ca911c4c17eb51853143624d8dc87cdcdf12a711fc38bf5bd21521e79715f", "sha256:f5b8a096e649768c2f4233f947cf9737f8dbf8728b90e2771e2497c6e3d21d13",
"sha256:ed63959d00b61959b035c7d47f9313c2c1ece090ff63afea702fe86de00dbed4", "sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a",
"sha256:f412604ccbeee81b091b420272841e5ec5ef68967a9790e80bffd0e30b8e2977", "sha256:fda582236fee135d4daeca056c8c88ec5f6f6d88a004a79b84a02547c8f57386",
"sha256:f7d66c15ba875432a2d2fb419523f5d3d347f91f48f57b8b08a2dfc3c39b8a3f", "sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac"
"sha256:f9e736f60f4911061235603a6119e72053073a12c6d7904011df2d8fad2c0e35",
"sha256:fb594b5a99943042c702c550d5494bdd7577f6ef19b0bc73877c948a63184a32"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.8'",
"version": "==4.55.3" "version": "==4.54.1"
}, },
"kiwisolver": { "kiwisolver": {
"hashes": [ "hashes": [
@ -288,43 +297,49 @@
}, },
"matplotlib": { "matplotlib": {
"hashes": [ "hashes": [
"sha256:01d2b19f13aeec2e759414d3bfe19ddfb16b13a1250add08d46d5ff6f9be83c6", "sha256:039082812cacd6c6bec8e17a9c1e6baca230d4116d522e81e1f63a74d01d2e21",
"sha256:12eaf48463b472c3c0f8dbacdbf906e573013df81a0ab82f0616ea4b11281908", "sha256:03ba9c1299c920964e8d3857ba27173b4dbb51ca4bab47ffc2c2ba0eb5e2cbc5",
"sha256:2c5829a5a1dd5a71f0e31e6e8bb449bc0ee9dbfb05ad28fc0c6b55101b3a4be6", "sha256:050598c2b29e0b9832cde72bcf97627bf00262adbc4a54e2b856426bb2ef0697",
"sha256:2fbbabc82fde51391c4da5006f965e36d86d95f6ee83fb594b279564a4c5d0d2", "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9",
"sha256:3547d153d70233a8496859097ef0312212e2689cdf8d7ed764441c77604095ae", "sha256:1cd93b91ab47a3616b4d3c42b52f8363b88ca021e340804c6ab2536344fad9ca",
"sha256:359f87baedb1f836ce307f0e850d12bb5f1936f70d035561f90d41d305fdacea", "sha256:1d94ff717eb2bd0b58fe66380bd8b14ac35f48a98e7c6765117fe67fb7684e64",
"sha256:3b427392354d10975c1d0f4ee18aa5844640b512d5311ef32efd4dd7db106ede", "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e",
"sha256:4659665bc7c9b58f8c00317c3c2a299f7f258eeae5a5d56b4c64226fca2f7c59", "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03",
"sha256:4673ff67a36152c48ddeaf1135e74ce0d4bce1bbf836ae40ed39c29edf7e2765", "sha256:3fd595f34aa8a55b7fc8bf9ebea8aa665a84c82d275190a61118d33fbc82ccae",
"sha256:503feb23bd8c8acc75541548a1d709c059b7184cde26314896e10a9f14df5f12", "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa",
"sha256:5439f4c5a3e2e8eab18e2f8c3ef929772fd5641876db71f08127eed95ab64683", "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3",
"sha256:5cdbaf909887373c3e094b0318d7ff230b2ad9dcb64da7ade654182872ab2593", "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e",
"sha256:5e6c6461e1fc63df30bf6f80f0b93f5b6784299f721bc28530477acd51bfc3d1", "sha256:65aacf95b62272d568044531e41de26285d54aec8cb859031f511f84bd8b495a",
"sha256:5fd41b0ec7ee45cd960a8e71aea7c946a28a0b8a4dcee47d2856b2af051f334c", "sha256:6758baae2ed64f2331d4fd19be38b7b4eae3ecec210049a26b6a4f3ae1c85dcc",
"sha256:607b16c8a73943df110f99ee2e940b8a1cbf9714b65307c040d422558397dac5", "sha256:6d1ce5ed2aefcdce11904fc5bbea7d9c21fff3d5f543841edf3dea84451a09ea",
"sha256:7e8632baebb058555ac0cde75db885c61f1212e47723d63921879806b40bec6a", "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b",
"sha256:81713dd0d103b379de4516b861d964b1d789a144103277769238c732229d7f03", "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e",
"sha256:845d96568ec873be63f25fa80e9e7fae4be854a66a7e2f0c8ccc99e94a8bd4ef", "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447",
"sha256:95b710fea129c76d30be72c3b38f330269363fbc6e570a5dd43580487380b5ff", "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b",
"sha256:96f2886f5c1e466f21cc41b70c5a0cd47bfa0015eb2d5793c88ebce658600e25", "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92",
"sha256:994c07b9d9fe8d25951e3202a68c17900679274dadfc1248738dcfa1bd40d7f3", "sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb",
"sha256:9ade1003376731a971e398cc4ef38bb83ee8caf0aee46ac6daa4b0506db1fd06", "sha256:ab68d50c06938ef28681073327795c5db99bb4666214d2d5f880ed11aeaded66",
"sha256:9b0558bae37f154fffda54d779a592bc97ca8b4701f1c710055b609a3bac44c8", "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9",
"sha256:a2a43cbefe22d653ab34bb55d42384ed30f611bcbdea1f8d7f431011a2e1c62e", "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7",
"sha256:a994f29e968ca002b50982b27168addfd65f0105610b6be7fa515ca4b5307c95", "sha256:b2696efdc08648536efd4e1601b5fd491fd47f4db97a5fbfd175549a7365c1b2",
"sha256:ad2e15300530c1a94c63cfa546e3b7864bd18ea2901317bae8bbf06a5ade6dcf", "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30",
"sha256:ae80dc3a4add4665cf2faa90138384a7ffe2a4e37c58d83e115b54287c4f06ef", "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d",
"sha256:b886d02a581b96704c9d1ffe55709e49b4d2d52709ccebc4be42db856e511278", "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7",
"sha256:c40ba2eb08b3f5de88152c2333c58cee7edcead0a2a0d60fcafa116b17117adc", "sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4",
"sha256:c55b20591ced744aa04e8c3e4b7543ea4d650b6c3c4b208c08a05b4010e8b442", "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41",
"sha256:c58a9622d5dbeb668f407f35f4e6bfac34bb9ecdcc81680c04d0258169747997", "sha256:cef2a73d06601437be399908cf13aee74e86932a5ccc6ccdf173408ebc5f6bb2",
"sha256:d44cb942af1693cced2604c33a9abcef6205601c445f6d0dc531d813af8a2f5a", "sha256:d52a3b618cb1cbb769ce2ee1dcdb333c3ab6e823944e9a2d36e37253815f9556",
"sha256:d907fddb39f923d011875452ff1eca29a9e7f21722b873e90db32e5d8ddff12e", "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f",
"sha256:fd44fc75522f58612ec4a33958a7e5552562b7705b42ef1b4f8c0818e304a363" "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772",
"sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c",
"sha256:e0830e188029c14e891fadd99702fd90d317df294c3298aad682739c5533721a",
"sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51",
"sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49",
"sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c",
"sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"
], ],
"markers": "python_version >= '3.10'", "markers": "python_version >= '3.9'",
"version": "==3.10.0" "version": "==3.9.2"
}, },
"numcodecs": { "numcodecs": {
"hashes": [ "hashes": [
@ -351,64 +366,11 @@
}, },
"numpy": { "numpy": {
"hashes": [ "hashes": [
"sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608", "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098",
"sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef", "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"
"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'", "markers": "python_version >= '3.10'",
"version": "==2.2.0" "version": "==2.1.3"
}, },
"packaging": { "packaging": {
"hashes": [ "hashes": [
@ -570,36 +532,6 @@
"markers": "python_version >= '3.9'", "markers": "python_version >= '3.9'",
"version": "==3.2.0" "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": { "python-dateutil": {
"hashes": [ "hashes": [
"sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
@ -617,11 +549,11 @@
}, },
"scikit-rf": { "scikit-rf": {
"hashes": [ "hashes": [
"sha256:796758dcd79650d8c66585583ac8ef13afde0d65d2da324ec60a4557eae52a9a", "sha256:722def5442bb7033d608e8d3f717b38a3315846496925e85e8015cf006c36bb7",
"sha256:9864653abf0761049ba95deb37741581fff5cf8d43f4acf6e46576e34e04ad43" "sha256:d7e1de31842d8922d1c741de7cc09aecf083a94ddc408ee2fe3f36a284858a09"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.8'",
"version": "==1.5.0" "version": "==1.4.1"
}, },
"scipy": { "scipy": {
"hashes": [ "hashes": [
@ -662,23 +594,13 @@
"markers": "python_version >= '3.10'", "markers": "python_version >= '3.10'",
"version": "==1.14.1" "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": { "six": {
"hashes": [ "hashes": [
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.17.0" "version": "==1.16.0"
}, },
"typing-extensions": { "typing-extensions": {
"hashes": [ "hashes": [
@ -698,11 +620,11 @@
}, },
"xarray": { "xarray": {
"hashes": [ "hashes": [
"sha256:1ccace44573ddb862e210ad3ec204210654d2c750bec11bbe7d842dfc298591f", "sha256:ae1d38cb44a0324dfb61e492394158ae22389bf7de9f3c174309c17376df63a0",
"sha256:6ee94f63ddcbdd0cf3909d1177f78cdac756640279c0e32ae36819a89cdaba37" "sha256:e369e2bac430e418c2448e5b96f07da4635f98c1319aa23cfeb3fbcb9a01d2e0"
], ],
"markers": "python_version >= '3.10'", "markers": "python_version >= '3.10'",
"version": "==2024.11.0" "version": "==2024.10.0"
}, },
"zarr": { "zarr": {
"hashes": [ "hashes": [
@ -716,48 +638,10 @@
"develop": { "develop": {
"asttokens": { "asttokens": {
"hashes": [ "hashes": [
"sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24",
"sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2" "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"
], ],
"markers": "python_version >= '3.8'", "version": "==2.4.1"
"version": "==3.0.0"
},
"black": {
"hashes": [
"sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f",
"sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd",
"sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea",
"sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981",
"sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b",
"sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7",
"sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8",
"sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175",
"sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d",
"sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392",
"sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad",
"sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f",
"sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f",
"sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b",
"sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875",
"sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3",
"sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800",
"sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65",
"sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2",
"sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812",
"sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50",
"sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"
],
"index": "pypi",
"markers": "python_version >= '3.9'",
"version": "==24.10.0"
},
"click": {
"hashes": [
"sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
"sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"
],
"markers": "python_version >= '3.7'",
"version": "==8.1.7"
}, },
"comm": { "comm": {
"hashes": [ "hashes": [
@ -769,35 +653,12 @@
}, },
"debugpy": { "debugpy": {
"hashes": [ "hashes": [
"sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920", "sha256:a6531d952b565b7cb2fbd1ef5df3d333cf160b44f37547a4e7cf73666aca5d8d",
"sha256:116bf8342062246ca749013df4f6ea106f23bc159305843491f64672a55af2e5", "sha256:e6355385db85cbd666be703a96ab7351bc9e6c61d694893206f8001e22aee091",
"sha256:189058d03a40103a57144752652b3ab08ff02b7595d0ce1f651b9acc3a3a35a0", "sha256:ec684553aba5b4066d4de510859922419febc710df7bba04fe9e7ef3de15d34f"
"sha256:23dc34c5e03b0212fa3c49a874df2b8b1b8fda95160bd79c01eb3ab51ea8d851",
"sha256:28e45b3f827d3bf2592f3cf7ae63282e859f3259db44ed2b129093ca0ac7940b",
"sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd",
"sha256:32db46ba45849daed7ccf3f2e26f7a386867b077f39b2a974bb5c4c2c3b0a280",
"sha256:40499a9979c55f72f4eb2fc38695419546b62594f8af194b879d2a18439c97a9",
"sha256:44b1b8e6253bceada11f714acf4309ffb98bfa9ac55e4fce14f9e5d4484287a1",
"sha256:52c3cf9ecda273a19cc092961ee34eb9ba8687d67ba34cc7b79a521c1c64c4c0",
"sha256:52d8a3166c9f2815bfae05f386114b0b2d274456980d41f320299a8d9a5615a7",
"sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f",
"sha256:654130ca6ad5de73d978057eaf9e582244ff72d4574b3e106fb8d3d2a0d32458",
"sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57",
"sha256:6c1f6a173d1140e557347419767d2b14ac1c9cd847e0b4c5444c7f3144697e4e",
"sha256:84e511a7545d11683d32cdb8f809ef63fc17ea2a00455cc62d0a4dbb4ed1c308",
"sha256:85de8474ad53ad546ff1c7c7c89230db215b9b8a02754d41cb5a76f70d0be296",
"sha256:8988f7163e4381b0da7696f37eec7aca19deb02e500245df68a7159739bbd0d3",
"sha256:8da1db4ca4f22583e834dcabdc7832e56fe16275253ee53ba66627b86e304da1",
"sha256:8ffc382e4afa4aee367bf413f55ed17bd91b191dcaf979890af239dda435f2a1",
"sha256:987bce16e86efa86f747d5151c54e91b3c1e36acc03ce1ddb50f9d09d16ded0e",
"sha256:ad7efe588c8f5cf940f40c3de0cd683cc5b76819446abaa50dc0829a30c094db",
"sha256:bb3b15e25891f38da3ca0740271e63ab9db61f41d4d8541745cfc1824252cb28",
"sha256:c928bbf47f65288574b78518449edaa46c82572d340e2750889bbf8cd92f3737",
"sha256:ce291a5aca4985d82875d6779f61375e959208cdf09fcec40001e65fb0a54768",
"sha256:d8768edcbeb34da9e11bcb8b5c2e0958d25218df7a6e56adf415ef262cd7b6d1"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.8'",
"version": "==1.8.11" "version": "==1.8.8"
}, },
"decorator": { "decorator": {
"hashes": [ "hashes": [
@ -823,15 +684,6 @@
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.8'",
"version": "==2.1.0" "version": "==2.1.0"
}, },
"flake8": {
"hashes": [
"sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38",
"sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"
],
"index": "pypi",
"markers": "python_full_version >= '3.8.1'",
"version": "==7.1.1"
},
"ipykernel": { "ipykernel": {
"hashes": [ "hashes": [
"sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5", "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5",
@ -843,11 +695,11 @@
}, },
"ipython": { "ipython": {
"hashes": [ "hashes": [
"sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321", "sha256:0188a1bd83267192123ccea7f4a8ed0a78910535dbaa3f37671dca76ebd429c8",
"sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e" "sha256:40b60e15b22591450eef73e40a027cf77bd652e757523eebc5bd7c7c498290eb"
], ],
"markers": "python_version >= '3.10'", "markers": "python_version >= '3.10'",
"version": "==8.30.0" "version": "==8.29.0"
}, },
"ipywidgets": { "ipywidgets": {
"hashes": [ "hashes": [
@ -858,22 +710,13 @@
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==8.1.5" "version": "==8.1.5"
}, },
"isort": {
"hashes": [
"sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109",
"sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"
],
"index": "pypi",
"markers": "python_full_version >= '3.8.0'",
"version": "==5.13.2"
},
"jedi": { "jedi": {
"hashes": [ "hashes": [
"sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd",
"sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9" "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==0.19.2" "version": "==0.19.1"
}, },
"jupyter-client": { "jupyter-client": {
"hashes": [ "hashes": [
@ -907,22 +750,6 @@
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.8'",
"version": "==0.1.7" "version": "==0.1.7"
}, },
"mccabe": {
"hashes": [
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
],
"markers": "python_version >= '3.6'",
"version": "==0.7.0"
},
"mypy-extensions": {
"hashes": [
"sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d",
"sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"
],
"markers": "python_version >= '3.5'",
"version": "==1.0.0"
},
"nest-asyncio": { "nest-asyncio": {
"hashes": [ "hashes": [
"sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe",
@ -947,14 +774,6 @@
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==0.8.4" "version": "==0.8.4"
}, },
"pathspec": {
"hashes": [
"sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08",
"sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"
],
"markers": "python_version >= '3.8'",
"version": "==0.12.1"
},
"pexpect": { "pexpect": {
"hashes": [ "hashes": [
"sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523",
@ -1016,22 +835,6 @@
], ],
"version": "==0.2.3" "version": "==0.2.3"
}, },
"pycodestyle": {
"hashes": [
"sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3",
"sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"
],
"markers": "python_version >= '3.8'",
"version": "==2.12.1"
},
"pyflakes": {
"hashes": [
"sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f",
"sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"
],
"markers": "python_version >= '3.8'",
"version": "==3.2.0"
},
"pygments": { "pygments": {
"hashes": [ "hashes": [
"sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199",
@ -1165,11 +968,11 @@
}, },
"six": { "six": {
"hashes": [ "hashes": [
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.17.0" "version": "==1.16.0"
}, },
"stack-data": { "stack-data": {
"hashes": [ "hashes": [
@ -1178,60 +981,22 @@
], ],
"version": "==0.6.3" "version": "==0.6.3"
}, },
"tomli": {
"hashes": [
"sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6",
"sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd",
"sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c",
"sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b",
"sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8",
"sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6",
"sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77",
"sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff",
"sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea",
"sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192",
"sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249",
"sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee",
"sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4",
"sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98",
"sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8",
"sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4",
"sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281",
"sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744",
"sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69",
"sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13",
"sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140",
"sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e",
"sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e",
"sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc",
"sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff",
"sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec",
"sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2",
"sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222",
"sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106",
"sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272",
"sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a",
"sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"
],
"markers": "python_version < '3.11'",
"version": "==2.2.1"
},
"tornado": { "tornado": {
"hashes": [ "hashes": [
"sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8",
"sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f",
"sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4",
"sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3",
"sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14",
"sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842",
"sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9",
"sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698",
"sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7",
"sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d",
"sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1" "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.8'",
"version": "==6.4.2" "version": "==6.4.1"
}, },
"traitlets": { "traitlets": {
"hashes": [ "hashes": [

View File

@ -1,43 +1,13 @@
# Charon VNA # Charon
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. 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.
## Installation ## Installation
1. Install LibIIO. This is a dependency of [PyADI-IIO](https://wiki.analog.com/resources/tools-software/linux-software/pyadi-iio). 1. Install LibIIO. This is a dependency of [PyADI-IIO](https://wiki.analog.com/resources/tools-software/linux-software/pyadi-iio).
On Ubuntu 22.04 just run `sudo apt-get install -y libiio-dev` On Ubuntu 22.04 just run `sudo apt-get install -y libiio-dev`
2. Charon releases are published on [PyPi](https://pypi.org/project/charon-vna/). Install using pip: 2. `pip install charon-vna`
`pip install charon-vna`
## Hardware Setup
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) however I have only tested with the basic flavor
- Note that you _must_ have two receive ports which means revision C or later of the basic Pluto
- 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.
- Optional. Without this you'll be limited to S11 and uncalibrated S21 measurements (with required re-cabling)
- There's nothing special about this particular board, if you want more than 4 ports you can make your own pretty easily. You just need 3 SPxT switches. Note that these switches will see tons of cycles so avoid mechanical switches
- SMA cables
- Calibration standard
- Ideally something with s-parameters measured on a better VNA
- I have used a basic SMA load and two modified SMA jacks with decent results
### Pluto Configuration
Most of my testing is with Pluto firmware [v0.39](https://github.com/analogdevicesinc/plutosdr-fw/releases/tag/v0.39) though this may work with other firmware versions. I had issues with the Pluto sometimes seeing no signal which resolved when I upgraded from v0.35. Instructions for upgrading firmware are on the [Analog Devices wiki](https://wiki.analog.com/university/tools/pluto/users/firmware).
We need two receive channels on the SDR. If you have a Pluto+ that should already be configured and you can skip this step.
Analog devices has a [guide](https://wiki.analog.com/university/tools/pluto/users/customizing#updating_to_the_ad9364) for enabling the second channel. Ideally this should be set as `ad9361` to enable a wider band of operation in addition to the second channel, however the critical setting is enabling 2r2t. SSH into the Pluto and run the following:
```bash
fw_setenv attr_name compatible
fw_setenv attr_val ad9361
fw_setenv mode 2r2t
```
## Usage ## Usage
@ -57,9 +27,20 @@ If you have an RF power meter you can generate your own power calibration.
Note that unlike the main calibration, power calibration frequencies do not need to match the measurement frequencies. Values are interpolated. Note that unlike the main calibration, power calibration frequencies do not need to match the measurement frequencies. Values are interpolated.
## References ## Hardware
#### Pluto Default Connection Settings You need a few things:
user: `root` - [Analog Devices Pluto SDR](https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/adalm-pluto.html).
password: `analog` Any variant of the Pluto *should* work too such as the [Pluto+](https://github.com/plutoplus/plutoplus?tab=readme-ov-file)
ip: `192.168.2.1` - 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.
Without this, you'll be limited to S11 and uncalibrated S21 measurements (with required re-cabling).
There's nothing special about this particular board, if you want more than 4 ports you can make your own pretty easily. You just need 3 SPxT switches. Note that these switches will see tons of cycles so avoid mechanical switches.
- SMA cables
### Pluto Modification
We need two receive channels on the SDR. If you have a Pluto+ that should already be configured and you can skip this step.
Analog devices has a [guide](https://wiki.analog.com/university/tools/pluto/users/customizing#updating_to_the_ad9364) for enabling the second channel. Ideally this should be set as `ad9361` to enable a wider band of operation in addition to the second channel, however the critical setting is enabling 2r2t.

View File

@ -1,26 +0,0 @@
# %% imports
from pathlib import Path
import click
# from charon_vna.vna import Charon
@click.command()
@click.argument("start", type=float)
@click.argument("stop", type=float)
@click.argument("pts", type=int)
@click.option("--ports", "-n", type=int, default=1, help="Number of ports.")
@click.option("--power", "-p", type=float, default=-5, help="Port output power [dBm].")
@click.option("--snp", "-o", type=click.Path(), help="Path for output Touchstone file.")
def capture(start: float, stop: float, pts: int, power: float, snp: Path, ports: int):
raise NotImplementedError
# %%
def main():
capture()
if __name__ == "__main__":
main()

View File

@ -1,505 +0,0 @@
{
"frequency": [
80000000.0,
80841683.36673346,
81683366.73346694,
82525050.1002004,
83366733.46693386,
84208416.83366734,
85050100.2004008,
85891783.56713426,
86733466.93386774,
87575150.3006012,
88416833.66733468,
89258517.03406814,
90100200.4008016,
90941883.76753508,
91783567.13426854,
92625250.501002,
93466933.86773548,
94308617.23446894,
95150300.6012024,
95991983.96793588,
96833667.33466934,
97675350.70140281,
98517034.06813627,
99358717.43486974,
100200400.8016032,
101042084.16833667,
101883767.53507014,
102725450.90180361,
103567134.26853707,
104408817.63527054,
105250501.00200401,
106092184.36873747,
106933867.73547095,
107775551.10220441,
108617234.46893787,
109458917.83567134,
110300601.20240481,
111142284.56913827,
111983967.93587175,
112825651.30260521,
113667334.66933867,
114509018.03607213,
115350701.40280561,
116192384.76953909,
117034068.13627255,
117875751.50300601,
118717434.86973947,
119559118.23647295,
120400801.60320641,
121242484.96993989,
122084168.33667335,
122925851.70340681,
123767535.07014027,
124609218.43687375,
125450901.80360723,
126292585.17034069,
127134268.53707415,
127975951.90380761,
128817635.27054109,
129659318.63727455,
130501002.00400802,
131342685.37074149,
132184368.73747495,
133026052.10420841,
133867735.47094189,
134709418.83767536,
135551102.20440882,
136392785.5711423,
137234468.93787575,
138076152.30460924,
138917835.67134267,
139759519.03807616,
140601202.40480962,
141442885.7715431,
142284569.13827655,
143126252.50501,
143967935.8717435,
144809619.23847696,
145651302.60521042,
146492985.97194389,
147334669.33867735,
148176352.70541084,
149018036.07214427,
149859719.43887776,
150701402.80561122,
151543086.17234468,
152384769.53907818,
153226452.9058116,
154068136.2725451,
154909819.63927856,
155751503.00601202,
156593186.3727455,
157434869.73947895,
158276553.10621244,
159118236.4729459,
159959919.83967936,
160801603.20641282,
161643286.57314628,
162484969.93987978,
163326653.30661324,
164168336.6733467,
165010020.04008016,
165851703.40681362,
166693386.7735471,
167535070.14028054,
168376753.50701404,
169218436.8737475,
170060120.24048096,
170901803.60721445,
171743486.97394788,
172585170.34068137,
173426853.70741484,
174268537.0741483,
175110220.4408818,
175951903.80761522,
176793587.1743487,
177635270.54108217,
178476953.90781564,
179318637.2745491,
180160320.64128256,
181002004.00801605,
181843687.37474948,
182685370.74148297,
183527054.10821643,
184368737.4749499,
185210420.8416834,
186052104.20841682,
186893787.5751503,
187735470.94188377,
188577154.30861723,
189418837.67535073,
190260521.04208416,
191102204.40881765,
191943887.7755511,
192785571.14228457,
193627254.50901806,
194468937.8757515,
195310621.242485,
196152304.60921845,
196993987.9759519,
197835671.34268537,
198677354.70941883,
199519038.07615232,
200360721.44288576,
201202404.80961925,
202044088.1763527,
202885771.54308617,
203727454.90981966,
204569138.2765531,
205410821.6432866,
206252505.01002005,
207094188.3767535,
207935871.743487,
208777555.11022043,
209619238.47695392,
210460921.8436874,
211302605.21042085,
212144288.5771543,
212985971.94388777,
213827655.31062126,
214669338.67735472,
215511022.04408818,
216352705.41082165,
217194388.7775551,
218036072.14428857,
218877755.51102206,
219719438.87775552,
220561122.24448898,
221402805.61122245,
222244488.9779559,
223086172.3446894,
223927855.71142286,
224769539.07815632,
225611222.44488978,
226452905.81162325,
227294589.1783567,
228136272.5450902,
228977955.91182366,
229819639.27855712,
230661322.64529058,
231503006.01202404,
232344689.37875754,
233186372.745491,
234028056.11222446,
234869739.47895792,
235711422.84569138,
236553106.21242484,
237394789.57915834,
238236472.9458918,
239078156.31262526,
239919839.67935872,
240761523.04609218,
241603206.41282564,
242444889.77955914,
243286573.1462926,
244128256.51302606,
244969939.87975952,
245811623.24649298,
246653306.61322647,
247494989.97995993,
248336673.3466934,
249178356.71342686,
250020040.08016032,
250861723.44689378,
251703406.81362727,
252545090.18036073,
253386773.5470942,
254228456.91382766,
255070140.28056112,
255911823.6472946,
256753507.01402807,
257595190.38076153,
258436873.747495,
259278557.11422846,
260120240.48096192,
260961923.8476954,
261803607.21442887,
262645290.58116233,
263486973.9478958,
264328657.31462926,
265170340.68136275,
266012024.0480962,
266853707.41482967,
267695390.78156313,
268537074.1482966,
269378757.51503,
270220440.8817636,
271062124.248497,
271903807.61523044,
272745490.98196393,
273587174.3486974,
274428857.71543086,
275270541.08216435,
276112224.44889784,
276953907.8156313,
277795591.1823647,
278637274.5490982,
279478957.9158317,
280320641.2825651,
281162324.6492986,
282004008.0160321,
282845691.38276553,
283687374.74949896,
284529058.1162325,
285370741.48296595,
286212424.8496994,
287054108.21643287,
287895791.58316636,
288737474.9498998,
289579158.3166333,
290420841.6833668,
291262525.0501002,
292104208.41683364,
292945891.78356713,
293787575.1503006,
294629258.51703405,
295470941.88376755,
296312625.25050104,
297154308.61723447,
297995991.98396796,
298837675.35070145,
299679358.7174349,
300521042.0841683,
301362725.4509018,
302204408.8176353,
303046092.1843687,
303887775.5511022,
304729458.9178357,
305571142.28456914,
306412825.6513026,
307254509.0180361,
308096192.38476956,
308937875.751503,
309779559.1182365,
310621242.48497,
311462925.8517034,
312304609.2184369,
313146292.5851704,
313987975.9519038,
314829659.31863725,
315671342.68537074,
316513026.05210423,
317354709.41883767,
318196392.78557116,
319038076.15230465,
319879759.5190381,
320721442.8857715,
321563126.25250506,
322404809.6192385,
323246492.9859719,
324088176.3527054,
324929859.7194389,
325771543.08617234,
326613226.45290583,
327454909.8196393,
328296593.18637276,
329138276.5531062,
329979959.9198397,
330821643.2865732,
331663326.6533066,
332505010.0200401,
333346693.3867736,
334188376.753507,
335030060.12024045,
335871743.486974,
336713426.85370743,
337555110.22044086,
338396793.58717436,
339238476.95390785,
340080160.3206413,
340921843.6873748,
341763527.05410826,
342605210.4208417,
343446893.7875751,
344288577.1543086,
345130260.5210421,
345971943.88777554,
346813627.25450903,
347655310.6212425,
348496993.98797596,
349338677.35470945,
350180360.7214429,
351022044.08817637,
351863727.45490986,
352705410.8216433,
353547094.1883768,
354388777.5551102,
355230460.9218437,
356072144.28857714,
356913827.65531063,
357755511.0220441,
358597194.38877755,
359438877.75551105,
360280561.1222445,
361122244.48897797,
361963927.85571146,
362805611.2224449,
363647294.5891784,
364488977.9559118,
365330661.3226453,
366172344.6893788,
367014028.05611223,
367855711.4228457,
368697394.78957915,
369539078.15631264,
370380761.5230461,
371222444.88977957,
372064128.25651306,
372905811.6232465,
373747494.98998,
374589178.3567134,
375430861.7234469,
376272545.0901804,
377114228.4569138,
377955911.8236473,
378797595.19038075,
379639278.55711424,
380480961.92384773,
381322645.29058117,
382164328.65731466,
383006012.0240481,
383847695.3907816,
384689378.7575151,
385531062.1242485,
386372745.490982,
387214428.8577154,
388056112.2244489,
388897795.59118235,
389739478.95791584,
390581162.32464933,
391422845.69138277,
392264529.05811626,
393106212.4248497,
393947895.7915832,
394789579.1583167,
395631262.5250501,
396472945.8917836,
397314629.258517,
398156312.6252505,
398997995.991984,
399839679.35871744,
400681362.72545093,
401523046.09218436,
402364729.45891786,
403206412.8256513,
404048096.1923848,
404889779.5591183,
405731462.9258517,
406573146.2925852,
407414829.6593186,
408256513.0260521,
409098196.3927856,
409939879.75951904,
410781563.12625253,
411623246.49298596,
412464929.85971946,
413306613.22645295,
414148296.5931864,
414989979.95991987,
415831663.3266533,
416673346.6933868,
417515030.0601203,
418356713.4268537,
419198396.7935872,
420040080.16032064,
420881763.52705413,
421723446.89378756,
422565130.26052105,
423406813.62725455,
424248496.993988,
425090180.36072147,
425931863.7274549,
426773547.0941884,
427615230.4609219,
428456913.8276553,
429298597.1943888,
430140280.56112224,
430981963.92785573,
431823647.2945892,
432665330.66132265,
433507014.02805614,
434348697.3947896,
435190380.76152307,
436032064.12825656,
436873747.49499,
437715430.8617235,
438557114.2284569,
439398797.5951904,
440240480.96192384,
441082164.3286573,
441923847.6953908,
442765531.06212425,
443607214.42885774,
444448897.7955912,
445290581.16232467,
446132264.52905816,
446973947.8957916,
447815631.2625251,
448657314.6292585,
449498997.995992,
450340681.3627255,
451182364.7294589,
452024048.0961924,
452865731.46292585,
453707414.82965934,
454549098.1963928,
455390781.56312627,
456232464.92985976,
457074148.2965932,
457915831.6633267,
458757515.0300601,
459599198.3967936,
460440881.7635271,
461282565.1302605,
462124248.496994,
462965931.86372745,
463807615.23046094,
464649298.59719443,
465490981.96392787,
466332665.33066136,
467174348.6973948,
468016032.0641283,
468857715.4308618,
469699398.7975952,
470541082.1643287,
471382765.5310621,
472224448.8977956,
473066132.26452905,
473907815.63126254,
474749498.99799603,
475591182.36472946,
476432865.73146296,
477274549.0981964,
478116232.4649299,
478957915.83166337,
479799599.1983968,
480641282.5651303,
481482965.9318637,
482324649.2985972,
483166332.6653307,
484008016.03206414,
484849699.39879763,
485691382.76553106,
486533066.13226455,
487374749.498998,
488216432.8657315,
489058116.23246497,
489899799.5991984,
490741482.9659319,
491583166.3326653,
492424849.6993988,
493266533.0661323,
494108216.43286574,
494949899.79959923,
495791583.16633266,
496633266.53306615,
497474949.89979964,
498316633.2665331,
499158316.63326657,
500000000.0
],
"power": -5
}

View File

@ -1,25 +0,0 @@
import json
import subprocess
import numpy as np
from charon_vna.gui import DEFAULT_CONFIG
config = dict(
frequency=np.linspace(80e6, 500e6, 500).tolist(),
power=-5,
)
with open(DEFAULT_CONFIG, "w") as f:
json.dump(config, f)
# autoformat
subprocess.run(
[
"python",
"-m",
"json.tool",
DEFAULT_CONFIG.resolve().as_posix(),
DEFAULT_CONFIG.resolve().as_posix(),
]
)

View File

@ -1,268 +0,0 @@
# %% imports
import json
import pickle
import re
import sys
from pathlib import Path
from typing import List
import matplotlib as mpl
import numpy as np
import skrf as rf
import xarray as xr
from numpy import typing as npt
from PySide6.QtGui import QAction, QKeySequence
from PySide6.QtWidgets import (
QApplication,
QDialogButtonBox,
QFileDialog,
QInputDialog,
QLineEdit,
QMainWindow,
QMenu,
QProgressBar,
QTabWidget,
QVBoxLayout,
QWidget,
)
from charon_vna.plots import PlotWidget
from charon_vna.util import net2s, s2net
from charon_vna.vna import Charon
# %%
DEFAULT_CONFIG = Path(__file__).parent / "config_default.json"
CONFIG_SUFFIX = ".json"
class MainWindow(QMainWindow):
config_path: Path | None
# device: Charon
plots: List[PlotWidget]
def __init__(self, ip: str | None = None):
super().__init__()
self.config_path = DEFAULT_CONFIG
with open(self.config_path, "r") as f:
config = json.load(f)
self._frequency = config["frequency"]
vna_kwargs = dict(
frequency=self._frequency,
)
if ip is not None:
vna_kwargs["ip"] = ip
self.vna = Charon(**vna_kwargs)
mpl.use("QtAgg")
self.setWindowTitle("Charon VNA")
# Menu
menubar = self.menuBar()
menu_file = QMenu("&File")
menubar.addMenu(menu_file)
action_open_config = QAction("&Open Configuration", self)
menu_file.addAction(action_open_config)
action_open_config.triggered.connect(self.open_config)
action_open_config.setShortcut(QKeySequence("Ctrl+O"))
action_save_config = QAction("&Save Configuration", self)
menu_file.addAction(action_save_config)
action_save_config.triggered.connect(self.save_config)
action_save_config.setShortcut(QKeySequence("Ctrl+S"))
action_saveas_config = QAction("Save Configuration &As", self)
menu_file.addAction(action_saveas_config)
action_saveas_config.triggered.connect(self.saveas_config)
action_saveas_config.setShortcut(QKeySequence("Ctrl+Shift+S"))
menu_stimulus = QMenu("&Stimulus")
menubar.addMenu(menu_stimulus)
action_set_frequency = QAction("&Frequency", self)
menu_stimulus.addAction(action_set_frequency)
action_set_frequency.triggered.connect(self.set_frequency)
action_set_power = QAction("&Power", self)
menu_stimulus.addAction(action_set_power)
# action_set_power.triggered.connect(self.set_power)
action_trigger = QAction("&Trigger", self)
action_trigger.triggered.connect(self.capture)
action_trigger.setShortcut("Ctrl+T")
menu_stimulus.addAction(action_trigger)
menu_calibration = QMenu("&Calibration")
menubar.addMenu(menu_calibration)
action_cal_solt = QAction("&SOLT", self)
action_cal_solt.triggered.connect(self.calibrate_solt)
menu_calibration.addAction(action_cal_solt)
# Content
window_layout = QVBoxLayout()
prog_sweep = QProgressBar()
prog_sweep.setMinimum(0)
prog_sweep.setMaximum(100)
prog_sweep.setFormat("%v / %m")
# prog_sweep.setTextVisible(False)
prog_sweep.setValue(0)
window_layout.addWidget(prog_sweep)
self.prog_sweep = prog_sweep
plot_widget = QTabWidget()
self.plots = []
for type_ in [
"logmag",
"phase",
"vswr",
"smith",
]:
self.plots.append(PlotWidget(type_=type_))
plot_widget.addTab(self.plots[-1], type_)
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...")
dialog = QFileDialog(self)
dialog.setDefaultSuffix(CONFIG_SUFFIX)
dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
if dialog.exec():
config_path = Path(dialog.selectedFiles()[0])
if config_path.suffix != CONFIG_SUFFIX:
raise ValueError(
f"{config_path.name} is not a valid configuration file. Must have extension {CONFIG_SUFFIX}"
)
if config_path == DEFAULT_CONFIG:
raise ValueError(f"Cannot overwrite default configuration file at {DEFAULT_CONFIG}")
self.config_path = config_path
print(f"Config path is now {self.config_path.resolve()}")
self.save_config()
def open_config(self) -> None:
print("Prompting for load path...")
dialog = QFileDialog(self)
dialog.setNameFilter(f"*{CONFIG_SUFFIX}")
dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
if dialog.exec():
config_path = Path(dialog.selectedFiles()[0])
print(config_path)
if config_path.suffix != CONFIG_SUFFIX:
raise ValueError(
f"{config_path.name} is not a valid configuration file. Must have extension {CONFIG_SUFFIX}"
)
self.config_path = config_path
print(f"Config path is now {self.config_path.resolve()}")
self.load_config(self.config_path)
def save_config(self) -> None:
if self.config_path == DEFAULT_CONFIG:
self.saveas_config()
else:
print(f"Saving config to {self.config_path.resolve()}")
# TODO: save config
def load_config(self, path: Path) -> None:
print(f"Loading config from {path}...")
# TODO: load config
def progress_callback(self, done: int, total: int):
self.prog_sweep.setMaximum(total)
self.prog_sweep.setValue(done)
def capture(self) -> None:
s = self.vna.vna_capture(self._frequency, self.progress_callback)
if self.vna.calibration is not None:
s_calibrated = self.vna.calibration.apply_cal(s2net(s))
data = net2s(s_calibrated)
else:
data = xr.DataArray(
[[s]],
dims=["m", "n", "frequency"],
coords=dict(
frequency=s.coords["frequency"],
m=[1],
n=[1],
),
)
for plot in self.plots:
plot.update_plot(data)
def set_frequency(self, *, frequency: npt.ArrayLike | None = None):
print(frequency)
if frequency is None:
start, ok = QInputDialog.getDouble(
self, "Start Frequency", "Start Frequency", minValue=30e6, maxValue=6e9, value=1e9
)
stop, ok = QInputDialog.getDouble(
self, "Stop Frequency", "Stop Frequency", minValue=30e6, maxValue=6e9, value=2e9
)
points, ok = QInputDialog.getInt(self, "Points", "Points", minValue=2, value=101)
frequency = np.linspace(start, stop, points)
# Currently does not support zero span
self._frequency = frequency
def calibrate_solt(self):
if len(self.vna.ports) > 1:
raise NotImplementedError
calfile = Path(__file__).parent / "cal.pkl"
if calfile.exists():
# don't re-cal while debugging because that's slooooooow
with open(calfile, "rb") as f:
calibration = pickle.load(f)
else:
s = dict()
for net in ["short", "open", "load"]:
input(f"Connect {net} standard and press ENTER...")
s[net] = self.vna.vna_capture(self._frequency, self.progress_callback)
ideal = rf.media.DefinedGammaZ0(frequency=rf.media.Frequency.from_f(self._frequency, unit="Hz"))
calibration = rf.calibration.OnePort(
[s2net(s["short"]), s2net(s["open"]), s2net(s["load"])],
[ideal.short(), ideal.open(), ideal.load(0)],
)
# TODO: don't use pickles for calibration. They're fragile
with open(calfile, "wb") as f:
pickle.dump(calibration, f)
self.vna.calibration = calibration
def main() -> None:
app = QApplication(sys.argv)
try:
window = MainWindow()
except Exception as e:
if e.args[0] == "No device found":
dialog = QInputDialog()
text, ok = dialog.getText(
None,
"Pluto IP Address",
"Enter Pluto IP Address",
QLineEdit.Normal,
"192.168.2.1",
)
match = re.match(r"(\d{1,3}\.){3}\d{1,3}", text)
if not match:
raise ValueError(f"Invalid IP address: {text}")
window = MainWindow(ip=text)
else:
raise e
window.show()
app.exec()
# %%
if __name__ == "__main__":
main()

View File

@ -1,90 +0,0 @@
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()

View File

@ -1,11 +0,0 @@
from pathlib import Path
import skrf as rf
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)

View File

@ -1,129 +0,0 @@
# %% imports
from typing import Callable, List, Literal, Tuple
import numpy as np
import xarray as xr
from matplotlib import pyplot as plt
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.lines import Line2D
from matplotlib.ticker import EngFormatter
from numpy import typing as npt
from PySide6.QtWidgets import QVBoxLayout, QWidget
from skrf import plotting as rf_plt
from charon_vna.util import db20, s2vswr
__all__ = ("PlotWidget",)
# %%
class PlotWidget(QWidget):
traces: List[Tuple[int | str]]
lines: List[Line2D]
def __init__(self, type_: str = "logmag"):
super().__init__()
self.traces = [(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.set_plot_type(type_)
self.lines = [
self.ax.plot([np.nan], [np.nan], label="$S_{" + str(m) + str(n) + "}$")[0] for m, n in self.traces
]
self.ax.legend(loc="upper right")
canvas = FigureCanvasQTAgg(self.fig)
layout.addWidget(canvas)
# toolbar = QToolBar("Toolbar")
# toolbar.addAction("blah")
# self.addToolBar(toolbar)
def set_plot_type(
self,
type_: Literal["logmag", "phase", "vswr", "smith"],
sweep_type: Literal["frequency", "time"] = "frequency",
) -> None:
if sweep_type != "frequency":
raise NotImplementedError("Only frequency sweeps are currently supported")
if type_ == "logmag":
self.setup_logmag()
elif type_ == "phase":
self.setup_phase()
elif type_ == "vswr":
self.setup_vswr()
elif type_ == "smith":
self.setup_smith()
else:
raise ValueError(f"Unknown plot type: {type_}")
self._plot_type = type_
def update_plot(self, data: xr.DataArray):
if self._plot_type == "logmag":
self.update_logmag(data)
elif self._plot_type == "phase":
self.update_phase(data)
elif self._plot_type == "vswr":
self.update_vswr(data)
elif self._plot_type == "smith":
self.update_smith(data)
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], npt.ArrayLike]) -> None:
self.ax.set_xlim(data["frequency"].min().data, data["frequency"].max().data)
for ii, (m, n) in enumerate(self.traces):
self.lines[ii].set_xdata(data["frequency"])
self.lines[ii].set_ydata(func(data.sel(m=m, n=n)))
self.fig.canvas.draw()
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:
for ii, (m, n) in enumerate(self.traces):
sel = data.sel(m=m, n=n)
self.lines[ii].set_xdata(sel.real)
self.lines[ii].set_ydata(sel.imag)
self.fig.canvas.draw()

View File

@ -1,84 +0,0 @@
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) -> npt.ArrayLike:
return 10 * np.log10(np.abs(p))
def db20(v: npt.ArrayLike) -> npt.ArrayLike:
return 20 * np.log10(np.abs(v))
def s2vswr(s: npt.ArrayLike) -> npt.ArrayLike:
return np.abs((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,286 +1,147 @@
# %% imports # %% imports
import copy import time
from enum import IntEnum, unique from typing import Optional
from pathlib import Path
from typing import Any, Callable, Dict, Literal, Tuple
import adi import adi
import iio import matplotlib as mpl
import numpy as np import numpy as np
import skrf as rf
import xarray as xr import xarray as xr
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
from matplotlib.gridspec import GridSpec
from matplotlib.patches import Circle
from matplotlib.ticker import EngFormatter from matplotlib.ticker import EngFormatter
from numpy import typing as npt from numpy import typing as npt
from scipy import signal from scipy import signal
from charon_vna.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 generate_tone(f: float, fs: float, N: int = 1024, scale: int = 2**14): 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
fs = int(fs) fs = int(fs)
fc = int(f / (fs / N)) * (fs / N) fc = int(f / (fs / N)) * (fs / N)
ts = 1 / float(fs) ts = 1 / float(fs)
t = np.arange(0, N * ts, ts) t = np.arange(0, N * ts, ts)
i = np.cos(2 * np.pi * t * fc) * scale i = np.cos(2 * np.pi * t * fc) * 2**14
q = np.sin(2 * np.pi * t * fc) * scale q = np.sin(2 * np.pi * t * fc) * 2**14
iq = i + 1j * q iq = i + 1j * q
return iq return iq
@unique # %% connection
class AD9361Register(IntEnum): sdr = adi.ad9361(uri="ip:192.168.3.1")
AUXDAC1_WORD = 0x018
AUXDAC2_WORD = 0x019 # %% initialization
AUXDAC1_CONFIG = 0x01A sdr.rx_lo = int(2.0e9)
AUXDAC2_CONFIG = 0x01B sdr.tx_lo = int(2.0e9)
AUXADC_CLOCK_DIVIDER = 0x01C sdr.sample_rate = 30e6
AUXADC_CONFIG = 0x01D sdr.rx_rf_bandwidth = int(4e6)
AUXADC_WORD_MSB = 0x01E sdr.tx_rf_bandwidth = int(4e6)
AUXADC_WORD_LSB = 0x01F sdr.rx_destroy_buffer()
AUTO_GPIO = 0x020 sdr.tx_destroy_buffer()
AGC_GAIN_LOCK_DELAY = 0x021 sdr.rx_enabled_channels = [0, 1]
AGC_ATTACK_DELAY = 0x022 sdr.tx_enabled_channels = [0]
AUXDAC_ENABLE_CONTROL = 0x023 sdr.loopback = 0
RX_LOAD_SYNTH_DELAY = 0x024 sdr.gain_control_mode_chan0 = "manual"
TX_LOAD_SYNTH_DELAY = 0x025 sdr.gain_control_mode_chan1 = "manual"
EXTERNAL_LNA_CONTROL = 0x026 sdr.rx_hardwaregain_chan0 = 10
GPO_FORCE_AND_INIT = 0x027 sdr.rx_hardwaregain_chan1 = 10
GPO0_RX_DELAY = 0x028 sdr.tx_hardwaregain_chan0 = -10
GPO1_RX_DELAY = 0x029
GPO2_RX_DELAY = 0x02A config = get_config(sdr)
GPO3_RX_DELAY = 0x02B config
GPO0_TX_DELAY = 0x02C
GPO1_TX_DELAY = 0x02D # %%
GPO2_TX_DELAY = 0x02E sdr.tx_destroy_buffer() # must destroy buffer before changing cyclicity
GPO3_TX_DELAY = 0x02F sdr.tx_cyclic_buffer = True
AUXDAC1_RX_DELAY = 0x030 sdr.tx(generate_tone(1e6))
AUXDAC1_TX_DELAY = 0x031
AUXDAC2_RX_DELAY = 0x032 # %%
AUXDAC2_TX_DELAY = 0x033 sdr.rx_destroy_buffer()
data = sdr.rx()
# %% 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()
# %% 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()
class Charon: # %% TX helper functions
FREQUENCY_OFFSET = 1e6 def set_output_power(power: float):
if power == -5:
calibration: rf.calibration.Calibration | None = None
def __init__(
self,
ip: str = "192.168.2.1",
frequency: npt.ArrayLike = np.linspace(1e9, 2e9, 3),
ports: Tuple[int] = (1,),
):
self.ports = ports
self.frequency = frequency
# everything RF
uri = f"ip:{ip}"
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
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
# # 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 # noqa: E501
self.ctrl.reg_write(AD9361Register.EXTERNAL_LNA_CONTROL, 0x90) # bit 7: AuxDAC Manual, bit 4: GPO Manual
self.ctrl.reg_write(AD9361Register.AUXDAC_ENABLE_CONTROL, 0x3F)
self._set_gpo(0b0000)
self._set_dac(value=0, channel=1)
self._set_dac(value=0, channel=2)
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}")
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
return config
def _get_gpo(self) -> int:
return (self.ctrl.reg_read(AD9361Register.GPO_FORCE_AND_INIT) >> 4) & 0x0F
def _set_gpo(self, value: int) -> None:
self.ctrl.reg_write(AD9361Register.GPO_FORCE_AND_INIT, (value & 0x0F) << 4) # bits 7-4: GPO3-0
def _set_dac(self, value: int, channel: Literal[1, 2]):
if channel not in [1, 2]:
raise ValueError(f"Invalid channel {channel}. Must be 1 or 2")
if value > 0x3FF or value < 0:
raise ValueError("Invalid value for 10 bit DAC. Must be between 0 and 0x3FF (inclusive)")
@unique
class Vref(IntEnum):
VREF_1V0 = 0b00
VREF_1V5 = 0b01
VREF_2V0 = 0b10
VREF_2V5 = 0b11
@unique
class StepFactor(IntEnum):
FACTOR_2 = 0b0
FACTOR_1 = 0b1
# https://www.analog.com/media/cn/technical-documentation/user-guides/ad9364_register_map_reference_manual_ug-672.pdf
# page 13
# vout = 0.97 * vref + (0.000738 + 9e-6 * (vref * 1.6 - 2)) * auxdac_word[9:0] * step_factor - 0.3572 * step_factor + 0.05
# vout ~= (vref - 0.3572 * step_factor) + 0.000738 * auxdac_word[9:0] * step_factor
# which gives a 1.5V swing with step_factor == 2 and 0.75V swing with step_factor == 1
# vref basically just changes the minimum voltage with negligible impact on output scaling
self.ctrl.reg_write(
AD9361Register.__getitem__(f"AUXDAC{channel}_WORD"),
(value >> 2) & 0xFF,
)
self.ctrl.reg_write(
AD9361Register.__getitem__(f"AUXDAC{channel}_CONFIG"),
(value & 0x3) | (Vref.VREF_1V0.value << 2) | (StepFactor.FACTOR_2 << 4),
)
def set_output_power(self, power: float):
# FIXME: this is a hack because I don't want to go through re-calibration # FIXME: this is a hack because I don't want to go through re-calibration
if power == 5: tx_gain = -8
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: else:
raise NotImplementedError() raise NotImplementedError()
# # TODO: correct over frequency # # TODO: correct over frequency
# tx_gain_idx = np.abs(pout.sel(tx_channel=0) - power).argmin(dim="tx_gain") # tx_gain_idx = np.abs(pout.sel(tx_channel=0) - power).argmin(dim="tx_gain")
# tx_gain = pout.coords["tx_gain"][tx_gain_idx] # tx_gain = pout.coords["tx_gain"][tx_gain_idx]
self.sdr.tx_hardwaregain_chan0 = float(tx_gain) sdr.tx_hardwaregain_chan0 = float(tx_gain)
def set_output(self, frequency: float, power: float):
# TODO: switch to DDS in Pluto
self.sdr.tx_destroy_buffer() def set_output(frequency: float, power: float, offset_frequency: float = 1e6):
self.set_output_power(power) sdr.tx_destroy_buffer()
self.sdr.tx_lo = int(frequency - self.FREQUENCY_OFFSET) set_output_power(power)
self.sdr.tx_cyclic_buffer = True sdr.tx_lo = int(frequency - offset_frequency)
# For some reason the pluto's DDS has truly horrendous phase noise to the point where it looks modulated offset_frequency = frequency - sdr.tx_lo
self.sdr.tx(generate_tone(f=self.FREQUENCY_OFFSET, fs=self.sdr.sample_rate)) sdr.tx_cyclic_buffer = True
# self.sdr.dds_single_tone(self.FREQUENCY_OFFSET, scale=0.9, channel=0) sdr.tx(generate_tone(offset_frequency))
def _rx(self, count: int = 1, fc: float | None = None) -> npt.ArrayLike:
if count < 1:
raise ValueError
self.sdr.rx_destroy_buffer() # %%
if fc is not None: def vna_capture(frequency: npt.ArrayLike):
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, callback: Callable[int, int] | None):
s = xr.DataArray( s = xr.DataArray(
np.empty(len(frequency), dtype=np.complex128), np.empty(len(frequency), dtype=np.complex128),
dims=["frequency"], dims=["frequency"],
@ -288,179 +149,52 @@ class Charon:
frequency=frequency, frequency=frequency,
), ),
) )
for ff, freq in enumerate(s.frequency.data): for freq in s.frequency.data:
if callback is not None: set_output(frequency=freq, power=-5)
callback(ff, len(s.frequency)) sdr.rx_destroy_buffer()
self.set_output(frequency=freq, power=-5) sdr.rx_lo = int(freq)
self.sdr.rx_destroy_buffer() sdr.rx_enabled_channels = [0, 1]
self.sdr.rx_lo = int(freq) sdr.gain_control_mode_chan0 = "manual"
self.sdr.rx_enabled_channels = [0, 1] sdr.gain_control_mode_chan1 = "manual"
self.sdr.gain_control_mode_chan0 = "manual" sdr.rx_hardwaregain_chan0 = 40
self.sdr.gain_control_mode_chan1 = "manual" sdr.rx_hardwaregain_chan1 = 40
self.sdr.rx_hardwaregain_chan0 = 40 rx = sdr.rx()
self.sdr.rx_hardwaregain_chan1 = 40
rx = self.sdr.rx()
s.loc[dict(frequency=freq)] = np.mean(rx[1] / rx[0]) s.loc[dict(frequency=freq)] = np.mean(rx[1] / rx[0])
if callback is not None:
callback(len(s.frequency), len(s.frequency))
return s return s
# %% # %%
if __name__ == "__main__": s = vna_capture(frequency=np.linspace(70e6, 200e6, 101))
pass
# %% # %% Plot Logmag
sdr = Charon("ip:192.168.3.1", frequency=np.linspace(1e9, 1.1e9, 11)) fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True)
# %% initialization axs[0].plot(s.frequency, db20(s), label="Measured")
config = sdr.get_config() axs[1].plot(s.frequency, np.rad2deg(np.angle((s))), label="Measured")
# 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 axs[0].grid(True)
fc = 1e9 axs[1].grid(True)
sdr.set_output(frequency=fc + sdr.FREQUENCY_OFFSET, power=-5)
# %% capture data axs[0].set_ylim(-80, 0)
data = sdr._rx(1, fc=fc) axs[1].set_ylim(-200, 200)
axs[1].set_xlim(np.min(s.frequency), np.max(s.frequency))
axs[1].xaxis.set_major_formatter(EngFormatter(places=1))
axs[1].set_xlabel("Frequency")
# %% Plot in time axs[0].set_ylabel("|S11| [dB]")
fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True) axs[1].set_ylabel("∠S11 [deg]")
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()
# %% reference_sparams = "/home/brendan/Documents/projects/bh_instruments/rbp135.npz"
fig, ax = plt.subplots(1, 1, tight_layout=True) if reference_sparams is not None:
ax.plot(np.real(data).T, np.imag(data).T) rbp135 = np.load(reference_sparams)
ax.grid(True) rbp135 = xr.DataArray(
ax.set_aspect("equal") rbp135["s"], dims=["frequency", "m", "n"], coords=dict(frequency=rbp135["frequency"], m=[1, 2], n=[1, 2])
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)
axs[0].plot(s.frequency, db20(s), label="Measured")
axs[1].plot(s.frequency, np.rad2deg(np.angle((s))), label="Measured")
axs[0].grid(True)
axs[1].grid(True)
axs[0].set_ylim(-80, 0)
axs[1].set_ylim(-200, 200)
axs[1].set_xlim(np.min(s.frequency), np.max(s.frequency))
axs[1].xaxis.set_major_formatter(EngFormatter(places=1))
axs[1].set_xlabel("Frequency")
axs[0].set_ylabel("|S11| [dB]")
axs[1].set_ylabel("∠S11 [deg]")
reference_sparams = None
reference_sparams = dir_ / "RBP-135+_Plus25degC.s2p"
if reference_sparams is not None:
ref = rf.Network(reference_sparams)
rbp135 = net2s(ref)
axs[0].plot(rbp135.frequency, db20(rbp135.sel(m=1, n=1)), label="Datasheet") 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") axs[1].plot(rbp135.frequency, np.rad2deg(np.angle(rbp135.sel(m=2, n=1))), label="Datasheet")
axs[0].legend() axs[0].legend()
axs[1].legend() axs[1].legend()
plt.show() 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,17 +21,14 @@ dependencies = [
"pyadi-iio", "pyadi-iio",
"click", "click",
"matplotlib", "matplotlib",
"pyside6",
] ]
dynamic = ["version"] dynamic = ["version"]
[project.urls] [project.urls]
homepage = "https://pypi.org/project/charon-vna/" homepage = "https://git.brendanhaines.com/brendanhaines/charon"
repository = "https://git.brendanhaines.com/brendanhaines/charon_vna"
[project.scripts] [project.scripts]
charon-cli = "charon_vna.cli:main" # charon = "charon.charon:main"
charon-gui = "charon_vna.gui:main"
[tool.setuptools_scm] [tool.setuptools_scm]
version_file = "charon_vna/_version.py" version_file = "charon_vna/_version.py"
@ -53,6 +50,3 @@ exclude = '''
| dist | dist
)/ )/
''' '''
[tool.isort]
profile = "black"