Compare commits
76 Commits
c8ace2330d
...
main
Author | SHA1 | Date | |
---|---|---|---|
c5dc320989 | |||
452dddc19c | |||
3c02a4b388 | |||
339dbe255e | |||
81143a72c4 | |||
f021780971 | |||
6f947a28fa | |||
581131f1e0 | |||
adf6e40752 | |||
411f96dd87 | |||
b4e4b689ea | |||
2012c37ccb | |||
5184c05bb5 | |||
8d7f87c9e6 | |||
776c9cc491 | |||
994080e574 | |||
b6cb1ecde7 | |||
383fe3ceea | |||
9912e318a8 | |||
d8b1a56c99 | |||
505b374e8f | |||
c73533b156 | |||
594c72f2bf | |||
43d9486edf | |||
27f7277c45 | |||
1a76c4e7ab | |||
67ddbb0f90 | |||
97497e640e | |||
e78cf5595a | |||
086f348084 | |||
8f61cd85d0 | |||
80fd25e7e4 | |||
30fb1190bb | |||
fa80af8447 | |||
926a6abf1f | |||
2285bb78c1 | |||
894d980a64 | |||
9a922762fa | |||
26682f1741 | |||
f1f6c0d45b | |||
e0c42766bf | |||
0202163f08 | |||
18a1b2faa3 | |||
c034cdcb95 | |||
0c152c337e | |||
22a33c2b84 | |||
3369bb290a | |||
322b5ebc9b | |||
014c742e0a | |||
6d920c6809 | |||
2f7b9fc1eb | |||
134f5b45d7 | |||
f35d9a85a6 | |||
5650e7b03b | |||
76886011bb | |||
528de3b4cc | |||
1256c71fac | |||
d637fb73ee | |||
5c4f7e5c97 | |||
919101b339 | |||
67a1864837 | |||
eca933ccef | |||
a5b391e757 | |||
34a4532a02 | |||
98f40179e1 | |||
5a7bc985b4 | |||
f4c099c1d2 | |||
78d0034e34 | |||
a56b2e30e2 | |||
ec010af947 | |||
7e0df9e643 | |||
a20217967f | |||
958d1f96d1 | |||
48d559f084 | |||
167a0b7aef | |||
2c9e9b0eb2 |
8
.github/workflows/python_publish.yml
vendored
8
.github/workflows/python_publish.yml
vendored
@ -54,13 +54,7 @@ jobs:
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Publish distribution 📦 to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
uses: pypa/gh-action-pypi-publish@0ab0b79471669eb3a4d647e625009c62f9f3b241
|
||||
with:
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
verbose: true
|
||||
print-hash: true
|
3
Pipfile
3
Pipfile
@ -9,6 +9,9 @@ charon-vna = {file = ".", editable = true}
|
||||
[dev-packages]
|
||||
ipykernel = "*"
|
||||
ipywidgets = "*"
|
||||
black = "*"
|
||||
flake8 = "*"
|
||||
isort = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.10"
|
||||
|
633
Pipfile.lock
generated
633
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "8ca47bc9e64405da418625530ed3861fbf72ff2f615aa4053f0da928dd09a494"
|
||||
"sha256": "470252a74774400955ef60d73f7e697760609be00b7733aa7a51b2f192e37fe5"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -36,74 +36,63 @@
|
||||
},
|
||||
"contourpy": {
|
||||
"hashes": [
|
||||
"sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0",
|
||||
"sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639",
|
||||
"sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd",
|
||||
"sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad",
|
||||
"sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843",
|
||||
"sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8",
|
||||
"sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4",
|
||||
"sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1",
|
||||
"sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294",
|
||||
"sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84",
|
||||
"sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927",
|
||||
"sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8",
|
||||
"sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09",
|
||||
"sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7",
|
||||
"sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f",
|
||||
"sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab",
|
||||
"sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b",
|
||||
"sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3",
|
||||
"sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223",
|
||||
"sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973",
|
||||
"sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087",
|
||||
"sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081",
|
||||
"sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc",
|
||||
"sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18",
|
||||
"sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f",
|
||||
"sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d",
|
||||
"sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2",
|
||||
"sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41",
|
||||
"sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67",
|
||||
"sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6",
|
||||
"sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b",
|
||||
"sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2",
|
||||
"sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c",
|
||||
"sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42",
|
||||
"sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d",
|
||||
"sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4",
|
||||
"sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5",
|
||||
"sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49",
|
||||
"sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b",
|
||||
"sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7",
|
||||
"sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102",
|
||||
"sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb",
|
||||
"sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7",
|
||||
"sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e",
|
||||
"sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c",
|
||||
"sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8",
|
||||
"sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35",
|
||||
"sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b",
|
||||
"sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14",
|
||||
"sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb",
|
||||
"sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589",
|
||||
"sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c",
|
||||
"sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0",
|
||||
"sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da",
|
||||
"sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800",
|
||||
"sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6",
|
||||
"sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66",
|
||||
"sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca",
|
||||
"sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb",
|
||||
"sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c",
|
||||
"sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06",
|
||||
"sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779",
|
||||
"sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8",
|
||||
"sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f",
|
||||
"sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"
|
||||
"sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1",
|
||||
"sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda",
|
||||
"sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d",
|
||||
"sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509",
|
||||
"sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6",
|
||||
"sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f",
|
||||
"sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e",
|
||||
"sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751",
|
||||
"sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86",
|
||||
"sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b",
|
||||
"sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc",
|
||||
"sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546",
|
||||
"sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec",
|
||||
"sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f",
|
||||
"sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82",
|
||||
"sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c",
|
||||
"sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b",
|
||||
"sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c",
|
||||
"sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c",
|
||||
"sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53",
|
||||
"sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80",
|
||||
"sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242",
|
||||
"sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85",
|
||||
"sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124",
|
||||
"sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5",
|
||||
"sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2",
|
||||
"sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3",
|
||||
"sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d",
|
||||
"sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc",
|
||||
"sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342",
|
||||
"sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1",
|
||||
"sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1",
|
||||
"sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595",
|
||||
"sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30",
|
||||
"sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab",
|
||||
"sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3",
|
||||
"sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2",
|
||||
"sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd",
|
||||
"sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7",
|
||||
"sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277",
|
||||
"sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453",
|
||||
"sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697",
|
||||
"sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b",
|
||||
"sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454",
|
||||
"sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9",
|
||||
"sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1",
|
||||
"sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6",
|
||||
"sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291",
|
||||
"sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750",
|
||||
"sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699",
|
||||
"sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e",
|
||||
"sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81",
|
||||
"sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9",
|
||||
"sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==1.3.0"
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==1.3.1"
|
||||
},
|
||||
"cycler": {
|
||||
"hashes": [
|
||||
@ -123,57 +112,59 @@
|
||||
},
|
||||
"fonttools": {
|
||||
"hashes": [
|
||||
"sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6",
|
||||
"sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263",
|
||||
"sha256:0d1d353ef198c422515a3e974a1e8d5b304cd54a4c2eebcae708e37cd9eeffb1",
|
||||
"sha256:0e88e3018ac809b9662615072dcd6b84dca4c2d991c6d66e1970a112503bba7e",
|
||||
"sha256:1d152d1be65652fc65e695e5619e0aa0982295a95a9b29b52b85775243c06556",
|
||||
"sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d",
|
||||
"sha256:278913a168f90d53378c20c23b80f4e599dca62fbffae4cc620c8eed476b723e",
|
||||
"sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2",
|
||||
"sha256:31c32d7d4b0958600eac75eaf524b7b7cb68d3a8c196635252b7a2c30d80e986",
|
||||
"sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb",
|
||||
"sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd",
|
||||
"sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882",
|
||||
"sha256:4aa4817f0031206e637d1e685251ac61be64d1adef111060df84fdcbc6ab6c44",
|
||||
"sha256:4e10d2e0a12e18f4e2dd031e1bf7c3d7017be5c8dbe524d07706179f355c5dac",
|
||||
"sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20",
|
||||
"sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d",
|
||||
"sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a",
|
||||
"sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c",
|
||||
"sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d",
|
||||
"sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff",
|
||||
"sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7",
|
||||
"sha256:7965af9b67dd546e52afcf2e38641b5be956d68c425bef2158e95af11d229f10",
|
||||
"sha256:7e3b7d44e18c085fd8c16dcc6f1ad6c61b71ff463636fcb13df7b1b818bd0c02",
|
||||
"sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2",
|
||||
"sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07",
|
||||
"sha256:8583e563df41fdecef31b793b4dd3af8a9caa03397be648945ad32717a92885b",
|
||||
"sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08",
|
||||
"sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab",
|
||||
"sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285",
|
||||
"sha256:9dc080e5a1c3b2656caff2ac2633d009b3a9ff7b5e93d0452f40cd76d3da3b3c",
|
||||
"sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58",
|
||||
"sha256:a7a310c6e0471602fe3bf8efaf193d396ea561486aeaa7adc1f132e02d30c4b9",
|
||||
"sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc",
|
||||
"sha256:ada215fd079e23e060157aab12eba0d66704316547f334eee9ff26f8c0d7b8ab",
|
||||
"sha256:c39287f5c8f4a0c5a55daf9eaf9ccd223ea59eed3f6d467133cc727d7b943a55",
|
||||
"sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714",
|
||||
"sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8",
|
||||
"sha256:d3b659d1029946f4ff9b6183984578041b520ce0f8fb7078bb37ec7445806b33",
|
||||
"sha256:dd9cc95b8d6e27d01e1e1f1fae8559ef3c02c76317da650a19047f249acd519d",
|
||||
"sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e",
|
||||
"sha256:e7d82b9e56716ed32574ee106cabca80992e6bbdcf25a88d97d21f73a0aae664",
|
||||
"sha256:e8a4b261c1ef91e7188a30571be6ad98d1c6d9fa2427244c545e2fa0a2494dd7",
|
||||
"sha256:e96bc94c8cda58f577277d4a71f51c8e2129b8b36fd05adece6320dd3d57de8a",
|
||||
"sha256:ed2f80ca07025551636c555dec2b755dd005e2ea8fbeb99fc5cdff319b70b23b",
|
||||
"sha256:f5b8a096e649768c2f4233f947cf9737f8dbf8728b90e2771e2497c6e3d21d13",
|
||||
"sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a",
|
||||
"sha256:fda582236fee135d4daeca056c8c88ec5f6f6d88a004a79b84a02547c8f57386",
|
||||
"sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac"
|
||||
"sha256:07f8288aacf0a38d174445fc78377a97fb0b83cfe352a90c9d9c1400571963c7",
|
||||
"sha256:11e5de1ee0d95af4ae23c1a138b184b7f06e0b6abacabf1d0db41c90b03d834b",
|
||||
"sha256:1bc7ad24ff98846282eef1cbeac05d013c2154f977a79886bb943015d2b1b261",
|
||||
"sha256:1dcc07934a2165ccdc3a5a608db56fb3c24b609658a5b340aee4ecf3ba679dc0",
|
||||
"sha256:22f38464daa6cdb7b6aebd14ab06609328fe1e9705bb0fcc7d1e69de7109ee02",
|
||||
"sha256:27e4ae3592e62eba83cd2c4ccd9462dcfa603ff78e09110680a5444c6925d841",
|
||||
"sha256:3983313c2a04d6cc1fe9251f8fc647754cf49a61dac6cb1e7249ae67afaafc45",
|
||||
"sha256:529cef2ce91dc44f8e407cc567fae6e49a1786f2fefefa73a294704c415322a4",
|
||||
"sha256:5323a22eabddf4b24f66d26894f1229261021dacd9d29e89f7872dd8c63f0b8b",
|
||||
"sha256:54153c49913f45065c8d9e6d0c101396725c5621c8aee744719300f79771d75a",
|
||||
"sha256:546565028e244a701f73df6d8dd6be489d01617863ec0c6a42fa25bf45d43048",
|
||||
"sha256:5480673f599ad410695ca2ddef2dfefe9df779a9a5cda89503881e503c9c7d90",
|
||||
"sha256:5e8d657cd7326eeaba27de2740e847c6b39dde2f8d7cd7cc56f6aad404ddf0bd",
|
||||
"sha256:62d65a3022c35e404d19ca14f291c89cc5890032ff04f6c17af0bd1927299674",
|
||||
"sha256:6314bf82c54c53c71805318fcf6786d986461622dd926d92a465199ff54b1b72",
|
||||
"sha256:7a8aa2c5e5b8b3bcb2e4538d929f6589a5c6bdb84fd16e2ed92649fb5454f11c",
|
||||
"sha256:827e95fdbbd3e51f8b459af5ea10ecb4e30af50221ca103bea68218e9615de07",
|
||||
"sha256:859c358ebf41db18fb72342d3080bce67c02b39e86b9fbcf1610cca14984841b",
|
||||
"sha256:86721fbc389ef5cc1e2f477019e5069e8e4421e8d9576e9c26f840dbb04678de",
|
||||
"sha256:89bdc5d88bdeec1b15af790810e267e8332d92561dce4f0748c2b95c9bdf3926",
|
||||
"sha256:8c4491699bad88efe95772543cd49870cf756b019ad56294f6498982408ab03e",
|
||||
"sha256:8c5ec45428edaa7022f1c949a632a6f298edc7b481312fc7dc258921e9399628",
|
||||
"sha256:8e75f12c82127486fac2d8bfbf5bf058202f54bf4f158d367e41647b972342ca",
|
||||
"sha256:a430178ad3e650e695167cb53242dae3477b35c95bef6525b074d87493c4bf29",
|
||||
"sha256:a8c2794ded89399cc2169c4d0bf7941247b8d5932b2659e09834adfbb01589aa",
|
||||
"sha256:aca318b77f23523309eec4475d1fbbb00a6b133eb766a8bdc401faba91261abe",
|
||||
"sha256:ae3b6600565b2d80b7c05acb8e24d2b26ac407b27a3f2e078229721ba5698427",
|
||||
"sha256:aedbeb1db64496d098e6be92b2e63b5fac4e53b1b92032dfc6988e1ea9134a4d",
|
||||
"sha256:aee3b57643827e237ff6ec6d28d9ff9766bd8b21e08cd13bff479e13d4b14765",
|
||||
"sha256:b54baf65c52952db65df39fcd4820668d0ef4766c0ccdf32879b77f7c804d5c5",
|
||||
"sha256:b586ab5b15b6097f2fb71cafa3c98edfd0dba1ad8027229e7b1e204a58b0e09d",
|
||||
"sha256:b8d5e8916c0970fbc0f6f1bece0063363bb5857a7f170121a4493e31c3db3314",
|
||||
"sha256:bc5dbb4685e51235ef487e4bd501ddfc49be5aede5e40f4cefcccabc6e60fb4b",
|
||||
"sha256:bdcc9f04b36c6c20978d3f060e5323a43f6222accc4e7fcbef3f428e216d96af",
|
||||
"sha256:c3ca99e0d460eff46e033cd3992a969658c3169ffcd533e0a39c63a38beb6831",
|
||||
"sha256:caf8230f3e10f8f5d7593eb6d252a37caf58c480b19a17e250a63dad63834cf3",
|
||||
"sha256:cd70de1a52a8ee2d1877b6293af8a2484ac82514f10b1c67c1c5762d38073e56",
|
||||
"sha256:cf4fe7c124aa3f4e4c1940880156e13f2f4d98170d35c749e6b4f119a872551e",
|
||||
"sha256:d342e88764fb201286d185093781bf6628bbe380a913c24adf772d901baa8276",
|
||||
"sha256:da9da6d65cd7aa6b0f806556f4985bcbf603bf0c5c590e61b43aa3e5a0f822d0",
|
||||
"sha256:dc5294a3d5c84226e3dbba1b6f61d7ad813a8c0238fceea4e09aa04848c3d851",
|
||||
"sha256:dd68c87a2bfe37c5b33bcda0fba39b65a353876d3b9006fde3adae31f97b3ef5",
|
||||
"sha256:e6e8766eeeb2de759e862004aa11a9ea3d6f6d5ec710551a88b476192b64fd54",
|
||||
"sha256:e894b5bd60d9f473bed7a8f506515549cc194de08064d829464088d23097331b",
|
||||
"sha256:eb6ca911c4c17eb51853143624d8dc87cdcdf12a711fc38bf5bd21521e79715f",
|
||||
"sha256:ed63959d00b61959b035c7d47f9313c2c1ece090ff63afea702fe86de00dbed4",
|
||||
"sha256:f412604ccbeee81b091b420272841e5ec5ef68967a9790e80bffd0e30b8e2977",
|
||||
"sha256:f7d66c15ba875432a2d2fb419523f5d3d347f91f48f57b8b08a2dfc3c39b8a3f",
|
||||
"sha256:f9e736f60f4911061235603a6119e72053073a12c6d7904011df2d8fad2c0e35",
|
||||
"sha256:fb594b5a99943042c702c550d5494bdd7577f6ef19b0bc73877c948a63184a32"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==4.54.1"
|
||||
"version": "==4.55.3"
|
||||
},
|
||||
"kiwisolver": {
|
||||
"hashes": [
|
||||
@ -297,49 +288,43 @@
|
||||
},
|
||||
"matplotlib": {
|
||||
"hashes": [
|
||||
"sha256:039082812cacd6c6bec8e17a9c1e6baca230d4116d522e81e1f63a74d01d2e21",
|
||||
"sha256:03ba9c1299c920964e8d3857ba27173b4dbb51ca4bab47ffc2c2ba0eb5e2cbc5",
|
||||
"sha256:050598c2b29e0b9832cde72bcf97627bf00262adbc4a54e2b856426bb2ef0697",
|
||||
"sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9",
|
||||
"sha256:1cd93b91ab47a3616b4d3c42b52f8363b88ca021e340804c6ab2536344fad9ca",
|
||||
"sha256:1d94ff717eb2bd0b58fe66380bd8b14ac35f48a98e7c6765117fe67fb7684e64",
|
||||
"sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e",
|
||||
"sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03",
|
||||
"sha256:3fd595f34aa8a55b7fc8bf9ebea8aa665a84c82d275190a61118d33fbc82ccae",
|
||||
"sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa",
|
||||
"sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3",
|
||||
"sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e",
|
||||
"sha256:65aacf95b62272d568044531e41de26285d54aec8cb859031f511f84bd8b495a",
|
||||
"sha256:6758baae2ed64f2331d4fd19be38b7b4eae3ecec210049a26b6a4f3ae1c85dcc",
|
||||
"sha256:6d1ce5ed2aefcdce11904fc5bbea7d9c21fff3d5f543841edf3dea84451a09ea",
|
||||
"sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b",
|
||||
"sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e",
|
||||
"sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447",
|
||||
"sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b",
|
||||
"sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92",
|
||||
"sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb",
|
||||
"sha256:ab68d50c06938ef28681073327795c5db99bb4666214d2d5f880ed11aeaded66",
|
||||
"sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9",
|
||||
"sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7",
|
||||
"sha256:b2696efdc08648536efd4e1601b5fd491fd47f4db97a5fbfd175549a7365c1b2",
|
||||
"sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30",
|
||||
"sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d",
|
||||
"sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7",
|
||||
"sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4",
|
||||
"sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41",
|
||||
"sha256:cef2a73d06601437be399908cf13aee74e86932a5ccc6ccdf173408ebc5f6bb2",
|
||||
"sha256:d52a3b618cb1cbb769ce2ee1dcdb333c3ab6e823944e9a2d36e37253815f9556",
|
||||
"sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f",
|
||||
"sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772",
|
||||
"sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c",
|
||||
"sha256:e0830e188029c14e891fadd99702fd90d317df294c3298aad682739c5533721a",
|
||||
"sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51",
|
||||
"sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49",
|
||||
"sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c",
|
||||
"sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"
|
||||
"sha256:01d2b19f13aeec2e759414d3bfe19ddfb16b13a1250add08d46d5ff6f9be83c6",
|
||||
"sha256:12eaf48463b472c3c0f8dbacdbf906e573013df81a0ab82f0616ea4b11281908",
|
||||
"sha256:2c5829a5a1dd5a71f0e31e6e8bb449bc0ee9dbfb05ad28fc0c6b55101b3a4be6",
|
||||
"sha256:2fbbabc82fde51391c4da5006f965e36d86d95f6ee83fb594b279564a4c5d0d2",
|
||||
"sha256:3547d153d70233a8496859097ef0312212e2689cdf8d7ed764441c77604095ae",
|
||||
"sha256:359f87baedb1f836ce307f0e850d12bb5f1936f70d035561f90d41d305fdacea",
|
||||
"sha256:3b427392354d10975c1d0f4ee18aa5844640b512d5311ef32efd4dd7db106ede",
|
||||
"sha256:4659665bc7c9b58f8c00317c3c2a299f7f258eeae5a5d56b4c64226fca2f7c59",
|
||||
"sha256:4673ff67a36152c48ddeaf1135e74ce0d4bce1bbf836ae40ed39c29edf7e2765",
|
||||
"sha256:503feb23bd8c8acc75541548a1d709c059b7184cde26314896e10a9f14df5f12",
|
||||
"sha256:5439f4c5a3e2e8eab18e2f8c3ef929772fd5641876db71f08127eed95ab64683",
|
||||
"sha256:5cdbaf909887373c3e094b0318d7ff230b2ad9dcb64da7ade654182872ab2593",
|
||||
"sha256:5e6c6461e1fc63df30bf6f80f0b93f5b6784299f721bc28530477acd51bfc3d1",
|
||||
"sha256:5fd41b0ec7ee45cd960a8e71aea7c946a28a0b8a4dcee47d2856b2af051f334c",
|
||||
"sha256:607b16c8a73943df110f99ee2e940b8a1cbf9714b65307c040d422558397dac5",
|
||||
"sha256:7e8632baebb058555ac0cde75db885c61f1212e47723d63921879806b40bec6a",
|
||||
"sha256:81713dd0d103b379de4516b861d964b1d789a144103277769238c732229d7f03",
|
||||
"sha256:845d96568ec873be63f25fa80e9e7fae4be854a66a7e2f0c8ccc99e94a8bd4ef",
|
||||
"sha256:95b710fea129c76d30be72c3b38f330269363fbc6e570a5dd43580487380b5ff",
|
||||
"sha256:96f2886f5c1e466f21cc41b70c5a0cd47bfa0015eb2d5793c88ebce658600e25",
|
||||
"sha256:994c07b9d9fe8d25951e3202a68c17900679274dadfc1248738dcfa1bd40d7f3",
|
||||
"sha256:9ade1003376731a971e398cc4ef38bb83ee8caf0aee46ac6daa4b0506db1fd06",
|
||||
"sha256:9b0558bae37f154fffda54d779a592bc97ca8b4701f1c710055b609a3bac44c8",
|
||||
"sha256:a2a43cbefe22d653ab34bb55d42384ed30f611bcbdea1f8d7f431011a2e1c62e",
|
||||
"sha256:a994f29e968ca002b50982b27168addfd65f0105610b6be7fa515ca4b5307c95",
|
||||
"sha256:ad2e15300530c1a94c63cfa546e3b7864bd18ea2901317bae8bbf06a5ade6dcf",
|
||||
"sha256:ae80dc3a4add4665cf2faa90138384a7ffe2a4e37c58d83e115b54287c4f06ef",
|
||||
"sha256:b886d02a581b96704c9d1ffe55709e49b4d2d52709ccebc4be42db856e511278",
|
||||
"sha256:c40ba2eb08b3f5de88152c2333c58cee7edcead0a2a0d60fcafa116b17117adc",
|
||||
"sha256:c55b20591ced744aa04e8c3e4b7543ea4d650b6c3c4b208c08a05b4010e8b442",
|
||||
"sha256:c58a9622d5dbeb668f407f35f4e6bfac34bb9ecdcc81680c04d0258169747997",
|
||||
"sha256:d44cb942af1693cced2604c33a9abcef6205601c445f6d0dc531d813af8a2f5a",
|
||||
"sha256:d907fddb39f923d011875452ff1eca29a9e7f21722b873e90db32e5d8ddff12e",
|
||||
"sha256:fd44fc75522f58612ec4a33958a7e5552562b7705b42ef1b4f8c0818e304a363"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==3.9.2"
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==3.10.0"
|
||||
},
|
||||
"numcodecs": {
|
||||
"hashes": [
|
||||
@ -366,11 +351,64 @@
|
||||
},
|
||||
"numpy": {
|
||||
"hashes": [
|
||||
"sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098",
|
||||
"sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"
|
||||
"sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608",
|
||||
"sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef",
|
||||
"sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90",
|
||||
"sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae",
|
||||
"sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83",
|
||||
"sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0",
|
||||
"sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73",
|
||||
"sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671",
|
||||
"sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69",
|
||||
"sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa",
|
||||
"sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066",
|
||||
"sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da",
|
||||
"sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9",
|
||||
"sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e",
|
||||
"sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3",
|
||||
"sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a",
|
||||
"sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74",
|
||||
"sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3",
|
||||
"sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410",
|
||||
"sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72",
|
||||
"sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d",
|
||||
"sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4",
|
||||
"sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038",
|
||||
"sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e",
|
||||
"sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13",
|
||||
"sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d",
|
||||
"sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95",
|
||||
"sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31",
|
||||
"sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3",
|
||||
"sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03",
|
||||
"sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6",
|
||||
"sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2",
|
||||
"sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b",
|
||||
"sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7",
|
||||
"sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab",
|
||||
"sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219",
|
||||
"sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571",
|
||||
"sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d",
|
||||
"sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1",
|
||||
"sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca",
|
||||
"sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661",
|
||||
"sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e",
|
||||
"sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e",
|
||||
"sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e",
|
||||
"sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a",
|
||||
"sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3",
|
||||
"sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881",
|
||||
"sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221",
|
||||
"sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742",
|
||||
"sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773",
|
||||
"sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e",
|
||||
"sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529",
|
||||
"sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67",
|
||||
"sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c",
|
||||
"sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"
|
||||
],
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==2.1.3"
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
@ -532,6 +570,36 @@
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==3.2.0"
|
||||
},
|
||||
"pyside6": {
|
||||
"hashes": [
|
||||
"sha256:6d1fd95651cdbdea741af21e155350986eca31ff015fc4c721ce01c2a110a4cc",
|
||||
"sha256:7d6adc5d53313249bbe02edb673877c1d437e215d71e88da78412520653f5c9f",
|
||||
"sha256:866eeaca3ffead6b9d30fa3ed395d5624da0246d7586c8b8207e77ac65d82458",
|
||||
"sha256:ddeeaeca8ebd0ddb1ded30dd33e9240a40f330cc91832de346ba6c9d0cd1253e"
|
||||
],
|
||||
"markers": "python_version < '3.14' and python_version >= '3.9'",
|
||||
"version": "==6.8.1"
|
||||
},
|
||||
"pyside6-addons": {
|
||||
"hashes": [
|
||||
"sha256:570a25016d80046274f454ed0bb06734f478ce6c21be5dec62b624773fc7504e",
|
||||
"sha256:879c12346b4b76f5d5ee6499d8ca53b5666c0c998b8fdf8780f08f69ea95d6f9",
|
||||
"sha256:d7c8c1e89ee0db84631d5b8fdb9129d9d2a0ffb3b4cb2f5192dc8367dd980db4",
|
||||
"sha256:f80cc03c1ac54132c6f800aa461dced64acd7d1646898db164ccb56fe3c23dd4"
|
||||
],
|
||||
"markers": "python_version < '3.14' and python_version >= '3.9'",
|
||||
"version": "==6.8.1"
|
||||
},
|
||||
"pyside6-essentials": {
|
||||
"hashes": [
|
||||
"sha256:2f600b149e65b57acd6a444edb17615adc42cc2491548ae443ccb574036d86b1",
|
||||
"sha256:bd05155245e3cd1572e68d72772e78fadfd713575bbfdd2c5e060d5278e390e9",
|
||||
"sha256:bf8a3c9ee0b997eb18fb00cb09aacaa28b8a51ce3c295a252cc594c5530aba56",
|
||||
"sha256:d5ed4ddb149f36d65bc49ae4260b2d213ee88b2d9a309012ae27f38158c2d1b6"
|
||||
],
|
||||
"markers": "python_version < '3.14' and python_version >= '3.9'",
|
||||
"version": "==6.8.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
|
||||
@ -549,11 +617,11 @@
|
||||
},
|
||||
"scikit-rf": {
|
||||
"hashes": [
|
||||
"sha256:722def5442bb7033d608e8d3f717b38a3315846496925e85e8015cf006c36bb7",
|
||||
"sha256:d7e1de31842d8922d1c741de7cc09aecf083a94ddc408ee2fe3f36a284858a09"
|
||||
"sha256:796758dcd79650d8c66585583ac8ef13afde0d65d2da324ec60a4557eae52a9a",
|
||||
"sha256:9864653abf0761049ba95deb37741581fff5cf8d43f4acf6e46576e34e04ad43"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==1.4.1"
|
||||
"version": "==1.5.0"
|
||||
},
|
||||
"scipy": {
|
||||
"hashes": [
|
||||
@ -594,13 +662,23 @@
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==1.14.1"
|
||||
},
|
||||
"shiboken6": {
|
||||
"hashes": [
|
||||
"sha256:1dc4c1976809b0e68872bb98474cccd590455bdcd015f0e0639907e94af27b6a",
|
||||
"sha256:3ea127fd72be113b73cacd70e06687ad6f83c1c888047833c7dcdd5cf8e7f586",
|
||||
"sha256:9a2f51d1ddd3b6d193a0f0fdc09f8d41f2092bc664723c9b9efc1056660d0608",
|
||||
"sha256:ab5b60602ca6227103138aae89c4f5df3b1b8e249cbc8ec9e6e2a57f20ad9a91"
|
||||
],
|
||||
"markers": "python_version < '3.14' and python_version >= '3.9'",
|
||||
"version": "==6.8.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
|
||||
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
"version": "==1.17.0"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
@ -620,11 +698,11 @@
|
||||
},
|
||||
"xarray": {
|
||||
"hashes": [
|
||||
"sha256:ae1d38cb44a0324dfb61e492394158ae22389bf7de9f3c174309c17376df63a0",
|
||||
"sha256:e369e2bac430e418c2448e5b96f07da4635f98c1319aa23cfeb3fbcb9a01d2e0"
|
||||
"sha256:1ccace44573ddb862e210ad3ec204210654d2c750bec11bbe7d842dfc298591f",
|
||||
"sha256:6ee94f63ddcbdd0cf3909d1177f78cdac756640279c0e32ae36819a89cdaba37"
|
||||
],
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==2024.10.0"
|
||||
"version": "==2024.11.0"
|
||||
},
|
||||
"zarr": {
|
||||
"hashes": [
|
||||
@ -638,10 +716,48 @@
|
||||
"develop": {
|
||||
"asttokens": {
|
||||
"hashes": [
|
||||
"sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24",
|
||||
"sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"
|
||||
"sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7",
|
||||
"sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"
|
||||
],
|
||||
"version": "==2.4.1"
|
||||
"markers": "python_version >= '3.8'",
|
||||
"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": {
|
||||
"hashes": [
|
||||
@ -653,12 +769,35 @@
|
||||
},
|
||||
"debugpy": {
|
||||
"hashes": [
|
||||
"sha256:a6531d952b565b7cb2fbd1ef5df3d333cf160b44f37547a4e7cf73666aca5d8d",
|
||||
"sha256:e6355385db85cbd666be703a96ab7351bc9e6c61d694893206f8001e22aee091",
|
||||
"sha256:ec684553aba5b4066d4de510859922419febc710df7bba04fe9e7ef3de15d34f"
|
||||
"sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920",
|
||||
"sha256:116bf8342062246ca749013df4f6ea106f23bc159305843491f64672a55af2e5",
|
||||
"sha256:189058d03a40103a57144752652b3ab08ff02b7595d0ce1f651b9acc3a3a35a0",
|
||||
"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'",
|
||||
"version": "==1.8.8"
|
||||
"version": "==1.8.11"
|
||||
},
|
||||
"decorator": {
|
||||
"hashes": [
|
||||
@ -684,6 +823,15 @@
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.1.0"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38",
|
||||
"sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_full_version >= '3.8.1'",
|
||||
"version": "==7.1.1"
|
||||
},
|
||||
"ipykernel": {
|
||||
"hashes": [
|
||||
"sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5",
|
||||
@ -695,11 +843,11 @@
|
||||
},
|
||||
"ipython": {
|
||||
"hashes": [
|
||||
"sha256:0188a1bd83267192123ccea7f4a8ed0a78910535dbaa3f37671dca76ebd429c8",
|
||||
"sha256:40b60e15b22591450eef73e40a027cf77bd652e757523eebc5bd7c7c498290eb"
|
||||
"sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321",
|
||||
"sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e"
|
||||
],
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==8.29.0"
|
||||
"version": "==8.30.0"
|
||||
},
|
||||
"ipywidgets": {
|
||||
"hashes": [
|
||||
@ -710,13 +858,22 @@
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.5"
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109",
|
||||
"sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_full_version >= '3.8.0'",
|
||||
"version": "==5.13.2"
|
||||
},
|
||||
"jedi": {
|
||||
"hashes": [
|
||||
"sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd",
|
||||
"sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"
|
||||
"sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0",
|
||||
"sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.19.1"
|
||||
"version": "==0.19.2"
|
||||
},
|
||||
"jupyter-client": {
|
||||
"hashes": [
|
||||
@ -750,6 +907,22 @@
|
||||
"markers": "python_version >= '3.8'",
|
||||
"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": {
|
||||
"hashes": [
|
||||
"sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe",
|
||||
@ -774,6 +947,14 @@
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.8.4"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08",
|
||||
"sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==0.12.1"
|
||||
},
|
||||
"pexpect": {
|
||||
"hashes": [
|
||||
"sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523",
|
||||
@ -835,6 +1016,22 @@
|
||||
],
|
||||
"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": {
|
||||
"hashes": [
|
||||
"sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199",
|
||||
@ -968,11 +1165,11 @@
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
|
||||
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
"version": "==1.17.0"
|
||||
},
|
||||
"stack-data": {
|
||||
"hashes": [
|
||||
@ -981,22 +1178,60 @@
|
||||
],
|
||||
"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": {
|
||||
"hashes": [
|
||||
"sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8",
|
||||
"sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f",
|
||||
"sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4",
|
||||
"sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3",
|
||||
"sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14",
|
||||
"sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842",
|
||||
"sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9",
|
||||
"sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698",
|
||||
"sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7",
|
||||
"sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d",
|
||||
"sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"
|
||||
"sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803",
|
||||
"sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec",
|
||||
"sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482",
|
||||
"sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634",
|
||||
"sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38",
|
||||
"sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b",
|
||||
"sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c",
|
||||
"sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf",
|
||||
"sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946",
|
||||
"sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73",
|
||||
"sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==6.4.1"
|
||||
"version": "==6.4.2"
|
||||
},
|
||||
"traitlets": {
|
||||
"hashes": [
|
||||
|
64
README.md
64
README.md
@ -1,13 +1,48 @@
|
||||
# Charon
|
||||
# Charon VNA
|
||||
|
||||
Named after [Pluto's moon](https://en.wikipedia.org/wiki/Charon_(moon)), Charon uses the [ADI Pluto SDR]() as a vector network analyzer. The basic usage is as a 1 port VNA but this can be extended to arbitrarily many ports with the addition of a couple RF switches.
|
||||
<!--  -->
|
||||
<!--  -->
|
||||
<!--  -->
|
||||
|
||||
Named after [Pluto's moon](https://en.wikipedia.org/wiki/Charon_(moon)), Charon uses the [ADI Pluto SDR](https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/adalm-pluto.html) as a vector network analyzer. The basic usage is as a 1 port VNA but this can be extended to arbitrarily many ports with the addition of a couple RF switches.
|
||||
|
||||
## Installation
|
||||
|
||||
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`
|
||||
|
||||
2. `pip install charon-vna`
|
||||
2. Charon releases are published on [PyPi](https://pypi.org/project/charon-vna/). Install using pip:
|
||||
`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)
|
||||
- [Cerberus RF switch](https://git.brendanhaines.com/brendanhaines/cerberus_sp4t) + [Pluto IO Shield](https://git.brendanhaines.com/brendanhaines/pluto_io_shield)
|
||||
- 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
|
||||
|
||||
@ -19,6 +54,7 @@ It will also be accessible over a socket to enable test automation with external
|
||||
TBD
|
||||
|
||||
### Power Calibration
|
||||
|
||||
I include a default output power lookup table. This is derived from two TX channels of two Pluto SDRs and does not include any of the loss of a coupler or Charon switch board.
|
||||
|
||||
Absolute output power is generally not well calibrated for VNAs anyway and has negligible impact on most measurements so this is probably sufficient for most users. If you're trying to run a power sweep this may be insufficient.
|
||||
@ -27,20 +63,16 @@ 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.
|
||||
|
||||
## Hardware
|
||||
## References
|
||||
|
||||
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)
|
||||
- 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 Default Connection Settings
|
||||
|
||||
### Pluto Modification
|
||||
- user: `root`
|
||||
- password: `analog`
|
||||
- ip: `192.168.2.1`
|
||||
|
||||
We need two receive channels on the SDR. If you have a Pluto+ that should already be configured and you can skip this step.
|
||||
## Alternatives
|
||||
|
||||
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.
|
||||
- [NanoVNA](https://nanovna.com/). 2-ports. 50 kHz - 2.7 GHz. Degraded performance above 1.5 GHz. S11 and S21 only.
|
||||
- [pluto-network-analyzer](https://github.com/fromconcepttocircuit/pluto-network-analyzer). 2-ports. 100 MHz - 3 GHz. S11 and S21 only. Uses a [wideband RF bridge](https://www.60dbm.com/product/rf-bridge-1-3000-mhz/) instead of a coupler
|
||||
- [LibreVNA](https://github.com/jankae/LibreVNA). 2-ports. 100 KHz - 6 GHz. I've never used this but it is almost certainly faster than Charon. Not sure how the performance compares. $700 on [AliExpress](https://www.aliexpress.us/item/3256802242049773.html?gatewayAdapt=glo2usa4itemAdapt)
|
26
charon_vna/cli.py
Normal file
26
charon_vna/cli.py
Normal file
@ -0,0 +1,26 @@
|
||||
# %% 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()
|
505
charon_vna/config_default.json
Normal file
505
charon_vna/config_default.json
Normal file
@ -0,0 +1,505 @@
|
||||
{
|
||||
"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
|
||||
}
|
25
charon_vna/config_default.py
Normal file
25
charon_vna/config_default.py
Normal file
@ -0,0 +1,25 @@
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
import numpy as np
|
||||
|
||||
from charon_vna.gui import PATH_CONFIG_DEFAULT
|
||||
|
||||
config = dict(
|
||||
frequency=np.linspace(80e6, 500e6, 500).tolist(),
|
||||
power=-5,
|
||||
)
|
||||
|
||||
with open(PATH_CONFIG_DEFAULT, "w") as f:
|
||||
json.dump(config, f)
|
||||
|
||||
# autoformat
|
||||
subprocess.run(
|
||||
[
|
||||
"python",
|
||||
"-m",
|
||||
"json.tool",
|
||||
PATH_CONFIG_DEFAULT.resolve().as_posix(),
|
||||
PATH_CONFIG_DEFAULT.resolve().as_posix(),
|
||||
]
|
||||
)
|
293
charon_vna/gui.py
Normal file
293
charon_vna/gui.py
Normal file
@ -0,0 +1,293 @@
|
||||
# %% imports
|
||||
import json
|
||||
import pickle
|
||||
import re
|
||||
import sys
|
||||
import webbrowser
|
||||
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,
|
||||
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
|
||||
|
||||
# %%
|
||||
PATH_CONFIG_DEFAULT = 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 = PATH_CONFIG_DEFAULT
|
||||
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)
|
||||
|
||||
self.active_port = 0
|
||||
self.vna.set_switches(a=self.active_port, b=self.active_port)
|
||||
|
||||
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)
|
||||
action_p0 = QAction("Switch &Port", self)
|
||||
action_p0.triggered.connect(self.toggle_port)
|
||||
menu_stimulus.addAction(action_p0)
|
||||
|
||||
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)
|
||||
|
||||
menu_help = QMenu("&Help")
|
||||
menubar.addMenu(menu_help)
|
||||
action_open_homepage = QAction("&Documentation", self)
|
||||
action_open_homepage.triggered.connect(
|
||||
lambda: webbrowser.open("https://git.brendanhaines.com/brendanhaines/charon_vna")
|
||||
)
|
||||
menu_help.addAction(action_open_homepage)
|
||||
action_report_issue = QAction("&Report an Issue", self)
|
||||
action_report_issue.triggered.connect(
|
||||
lambda: webbrowser.open("https://git.brendanhaines.com/brendanhaines/charon_vna/issues")
|
||||
)
|
||||
menu_help.addAction(action_report_issue)
|
||||
|
||||
# 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 toggle_port(self):
|
||||
self.active_port = int(not self.active_port)
|
||||
print(f"Activating port {self.active_port}")
|
||||
self.vna.set_switches(a=self.active_port, b=self.active_port)
|
||||
|
||||
def saveas_config(self) -> None:
|
||||
print("Prompting for save path...")
|
||||
dialog = QFileDialog(self)
|
||||
dialog.setNameFilter(f"*{CONFIG_SUFFIX}")
|
||||
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 == PATH_CONFIG_DEFAULT:
|
||||
raise ValueError(f"Cannot overwrite default configuration file at {PATH_CONFIG_DEFAULT}")
|
||||
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 == PATH_CONFIG_DEFAULT:
|
||||
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,
|
||||
Charon.DEFAULT_IP,
|
||||
)
|
||||
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()
|
90
charon_vna/gui_helpers.py
Normal file
90
charon_vna/gui_helpers.py
Normal file
@ -0,0 +1,90 @@
|
||||
from PySide6.QtCore import QMargins, QPoint, QRect, QSize, Qt
|
||||
from PySide6.QtWidgets import QLayout, QSizePolicy
|
||||
|
||||
|
||||
class FlowLayout(QLayout):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
if parent is not None:
|
||||
self.setContentsMargins(QMargins(0, 0, 0, 0))
|
||||
|
||||
self._item_list = []
|
||||
|
||||
def __del__(self):
|
||||
item = self.takeAt(0)
|
||||
while item:
|
||||
item = self.takeAt(0)
|
||||
|
||||
def addItem(self, item):
|
||||
self._item_list.append(item)
|
||||
|
||||
def count(self):
|
||||
return len(self._item_list)
|
||||
|
||||
def itemAt(self, index):
|
||||
if 0 <= index < len(self._item_list):
|
||||
return self._item_list[index]
|
||||
|
||||
return None
|
||||
|
||||
def takeAt(self, index):
|
||||
if 0 <= index < len(self._item_list):
|
||||
return self._item_list.pop(index)
|
||||
|
||||
return None
|
||||
|
||||
def expandingDirections(self):
|
||||
return Qt.Orientation(0)
|
||||
|
||||
def hasHeightForWidth(self):
|
||||
return True
|
||||
|
||||
def heightForWidth(self, width):
|
||||
height = self._do_layout(QRect(0, 0, width, 0), True)
|
||||
return height
|
||||
|
||||
def setGeometry(self, rect):
|
||||
super(FlowLayout, self).setGeometry(rect)
|
||||
self._do_layout(rect, False)
|
||||
|
||||
def sizeHint(self):
|
||||
return self.minimumSize()
|
||||
|
||||
def minimumSize(self):
|
||||
size = QSize()
|
||||
|
||||
for item in self._item_list:
|
||||
size = size.expandedTo(item.minimumSize())
|
||||
|
||||
size += QSize(2 * self.contentsMargins().top(), 2 * self.contentsMargins().top())
|
||||
return size
|
||||
|
||||
def _do_layout(self, rect, test_only):
|
||||
x = rect.x()
|
||||
y = rect.y()
|
||||
line_height = 0
|
||||
spacing = self.spacing()
|
||||
|
||||
for item in self._item_list:
|
||||
style = item.widget().style()
|
||||
layout_spacing_x = style.layoutSpacing(
|
||||
QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Orientation.Horizontal
|
||||
)
|
||||
layout_spacing_y = style.layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)
|
||||
space_x = spacing + layout_spacing_x
|
||||
space_y = spacing + layout_spacing_y
|
||||
next_x = x + item.sizeHint().width() + space_x
|
||||
if next_x - space_x > rect.right() and line_height > 0:
|
||||
x = rect.x()
|
||||
y = y + line_height + space_y
|
||||
next_x = x + item.sizeHint().width() + space_x
|
||||
line_height = 0
|
||||
|
||||
if not test_only:
|
||||
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
|
||||
|
||||
x = next_x
|
||||
line_height = max(line_height, item.sizeHint().height())
|
||||
|
||||
return y + line_height - rect.y()
|
94
charon_vna/io_.py
Normal file
94
charon_vna/io_.py
Normal file
@ -0,0 +1,94 @@
|
||||
import json
|
||||
import shutil
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import List
|
||||
|
||||
import skrf as rf
|
||||
|
||||
|
||||
# scikit-rf has no way to save Calibration objects aside from pickle
|
||||
def cal2zip(cal: rf.calibration.Calibration, path: Path | str) -> None:
|
||||
path = Path(path)
|
||||
cal_type = cal.__class__
|
||||
measured: List[rf.network.Network] = cal.measured
|
||||
ideals: List[rf.network.Network] = cal.ideals
|
||||
|
||||
if cal_type not in [rf.calibration.OnePort]:
|
||||
raise NotImplementedError(f"Calibration {cal_type.__name__} serialization not implemented")
|
||||
|
||||
assert len(ideals) == len(measured) # this should have already been asserted when cal was instantiated
|
||||
|
||||
with TemporaryDirectory() as temp:
|
||||
dir_temp = Path(temp)
|
||||
|
||||
with zipfile.ZipFile(dir_temp / "archive.zip", "w") as archive:
|
||||
# create a configuration file
|
||||
filename_config = dir_temp / "config.json"
|
||||
with open(filename_config, "w") as f:
|
||||
json.dump(
|
||||
dict(
|
||||
cal_type=cal_type.__name__,
|
||||
num_standards=len(measured),
|
||||
),
|
||||
f,
|
||||
)
|
||||
archive.write(filename_config, str(filename_config.relative_to(dir_temp)))
|
||||
|
||||
# add standard data
|
||||
dir_ideals = dir_temp / "ideals"
|
||||
dir_ideals.mkdir()
|
||||
for ii, ideal in enumerate(ideals):
|
||||
filename = dir_ideals / f"{ii}.s2p"
|
||||
ideal.write_touchstone(filename)
|
||||
archive.write(filename, str(filename.relative_to(dir_temp)))
|
||||
|
||||
# add test data
|
||||
dir_measured = dir_temp / "measured"
|
||||
dir_measured.mkdir()
|
||||
for ii, meas in enumerate(measured):
|
||||
filename = dir_measured / f"{ii}.s2p"
|
||||
meas.write_touchstone(filename)
|
||||
archive.write(filename, str(filename.relative_to(dir_temp)))
|
||||
|
||||
print("Wrote calibration to file")
|
||||
archive.printdir()
|
||||
|
||||
shutil.move(dir_temp / "archive.zip", path)
|
||||
|
||||
|
||||
def zip2cal(path: Path | str):
|
||||
path = Path(path)
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"Calibration file {path} does not exist")
|
||||
|
||||
with zipfile.ZipFile(path) as archive:
|
||||
archive.printdir()
|
||||
|
||||
config = json.loads(archive.read("config.json"))
|
||||
print(config)
|
||||
|
||||
ideals = list()
|
||||
measured = list()
|
||||
|
||||
with TemporaryDirectory() as temp:
|
||||
dir_temp = Path(temp)
|
||||
|
||||
for ii in range(config["num_standards"]):
|
||||
with open(dir_temp / f"{ii}.s2p", "wb") as f:
|
||||
f.write(archive.read(f"ideals/{ii}.s2p"))
|
||||
ideals.append(rf.network.Network(dir_temp / f"{ii}.s2p"))
|
||||
|
||||
with open(dir_temp / f"{ii}.s2p", "wb") as f:
|
||||
f.write(archive.read(f"measured/{ii}.s2p"))
|
||||
measured.append(rf.network.Network(dir_temp / f"{ii}.s2p"))
|
||||
|
||||
cal_type = config["cal_type"]
|
||||
CalClass = getattr(rf.calibration, cal_type)
|
||||
if not issubclass(CalClass, rf.calibration.Calibration):
|
||||
raise ValueError()
|
||||
|
||||
calibration = CalClass(measured=measured, ideals=ideals)
|
||||
|
||||
return calibration
|
129
charon_vna/plots.py
Normal file
129
charon_vna/plots.py
Normal file
@ -0,0 +1,129 @@
|
||||
# %% 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()
|
84
charon_vna/util.py
Normal file
84
charon_vna/util.py
Normal file
@ -0,0 +1,84 @@
|
||||
import numpy as np
|
||||
import skrf as rf
|
||||
import xarray as xr
|
||||
from numpy import typing as npt
|
||||
|
||||
HAM_BANDS = [
|
||||
[135.7e3, 137.8e3],
|
||||
[472e3, 479e3],
|
||||
[1.8e6, 2e6],
|
||||
[3.5e6, 4e6],
|
||||
[5332e3, 5405e3],
|
||||
[7e6, 7.3e6],
|
||||
[10.1e6, 10.15e6],
|
||||
[14e6, 14.35e6],
|
||||
[18.068e6, 18.168e6],
|
||||
[21e6, 21.45e6],
|
||||
[24.89e6, 24.99e6],
|
||||
[28e6, 29.7e6],
|
||||
[50e6, 54e6],
|
||||
[144e6, 148e6],
|
||||
[219e6, 220e6],
|
||||
[222e6, 225e6],
|
||||
[420e6, 450e6],
|
||||
[902e6, 928e6],
|
||||
[1240e6, 1300e6],
|
||||
[2300e6, 2310e6],
|
||||
[2390e6, 2450e6],
|
||||
[3400e6, 3450e6],
|
||||
[5650e6, 5925e6],
|
||||
[10e9, 10.5e9],
|
||||
[24e9, 24.25e9],
|
||||
[47e9, 47.2e9],
|
||||
[76e9, 81e9],
|
||||
[122.25e9, 123e9],
|
||||
[134e9, 141e9],
|
||||
[241e9, 250e9],
|
||||
[275e9, np.inf],
|
||||
]
|
||||
|
||||
|
||||
def db10(p: npt.ArrayLike) -> 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
|
@ -1,128 +1,472 @@
|
||||
# %% imports
|
||||
import copy
|
||||
import time
|
||||
import pickle
|
||||
from enum import IntEnum, unique
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Any, Callable, Dict, List, Literal, Tuple
|
||||
|
||||
import adi
|
||||
import iio
|
||||
import matplotlib as mpl
|
||||
import numpy as np
|
||||
import skrf as rf
|
||||
import xarray as xr
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib.gridspec import GridSpec
|
||||
from matplotlib.patches import Circle
|
||||
from matplotlib.ticker import EngFormatter
|
||||
from numpy import typing as npt
|
||||
from scipy import signal
|
||||
|
||||
from charon_vna.util import HAM_BANDS, db20, net2s, s2net
|
||||
|
||||
dir_ = Path(__file__).parent
|
||||
|
||||
|
||||
# https://wiki.analog.com/resources/tools-software/linux-drivers/iio-transceiver/ad9361-customization
|
||||
# %% connection
|
||||
|
||||
|
||||
# %% 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
|
||||
|
||||
|
||||
def db10(p):
|
||||
return 10 * np.log10(np.abs(p))
|
||||
|
||||
|
||||
def db20(p):
|
||||
return 20 * np.log10(np.abs(p))
|
||||
|
||||
|
||||
def minmax(x):
|
||||
return (np.min(x), np.max(x))
|
||||
|
||||
|
||||
def generate_tone(f: float, N: int = 1024, fs: Optional[float] = None):
|
||||
if fs is None:
|
||||
fs = sdr.sample_rate
|
||||
def generate_tone(f: float, fs: float, N: int = 1024, scale: int = 2**14):
|
||||
fs = int(fs)
|
||||
fc = int(f / (fs / N)) * (fs / N)
|
||||
ts = 1 / float(fs)
|
||||
t = np.arange(0, N * ts, ts)
|
||||
i = np.cos(2 * np.pi * t * fc) * 2**14
|
||||
q = np.sin(2 * np.pi * t * fc) * 2**14
|
||||
i = np.cos(2 * np.pi * t * fc) * scale
|
||||
q = np.sin(2 * np.pi * t * fc) * scale
|
||||
iq = i + 1j * q
|
||||
|
||||
return iq
|
||||
|
||||
|
||||
# %% connection
|
||||
sdr = adi.ad9361(uri="ip:192.168.3.1")
|
||||
@unique
|
||||
class AD9361Register(IntEnum):
|
||||
AUXDAC1_WORD = 0x018
|
||||
AUXDAC2_WORD = 0x019
|
||||
AUXDAC1_CONFIG = 0x01A
|
||||
AUXDAC2_CONFIG = 0x01B
|
||||
AUXADC_CLOCK_DIVIDER = 0x01C
|
||||
AUXADC_CONFIG = 0x01D
|
||||
AUXADC_WORD_MSB = 0x01E
|
||||
AUXADC_WORD_LSB = 0x01F
|
||||
AUTO_GPIO = 0x020
|
||||
AGC_GAIN_LOCK_DELAY = 0x021
|
||||
AGC_ATTACK_DELAY = 0x022
|
||||
AUXDAC_ENABLE_CONTROL = 0x023
|
||||
RX_LOAD_SYNTH_DELAY = 0x024
|
||||
TX_LOAD_SYNTH_DELAY = 0x025
|
||||
EXTERNAL_LNA_CONTROL = 0x026
|
||||
GPO_FORCE_AND_INIT = 0x027
|
||||
GPO0_RX_DELAY = 0x028
|
||||
GPO1_RX_DELAY = 0x029
|
||||
GPO2_RX_DELAY = 0x02A
|
||||
GPO3_RX_DELAY = 0x02B
|
||||
GPO0_TX_DELAY = 0x02C
|
||||
GPO1_TX_DELAY = 0x02D
|
||||
GPO2_TX_DELAY = 0x02E
|
||||
GPO3_TX_DELAY = 0x02F
|
||||
AUXDAC1_RX_DELAY = 0x030
|
||||
AUXDAC1_TX_DELAY = 0x031
|
||||
AUXDAC2_RX_DELAY = 0x032
|
||||
AUXDAC2_TX_DELAY = 0x033
|
||||
|
||||
# %% verify device configuration
|
||||
mode_2r2t = bool(sdr._get_iio_debug_attr("adi,2rx-2tx-mode-enable"))
|
||||
if not mode_2r2t:
|
||||
raise ValueError("'adi,2rx-2tx-mode-enable' is not set in pluto. See README.md for instructions for changing this")
|
||||
# TODO: it might be possible to change this on the fly. I think we'll actually just fail in __init__ for sdr
|
||||
|
||||
# %% switch control outputs
|
||||
# NOTE: this doesn't appear to work
|
||||
sdr._set_iio_debug_attr_str("adi,gpo-manual-mode-enable", "1")
|
||||
sdr._get_iio_debug_attr_str("adi,gpo-manual-mode-enable-mask")
|
||||
# but direct register access does
|
||||
@unique
|
||||
class AD9361DacVref(IntEnum):
|
||||
VREF_1V0 = 0b00
|
||||
VREF_1V5 = 0b01
|
||||
VREF_2V0 = 0b10
|
||||
VREF_2V5 = 0b11
|
||||
|
||||
|
||||
@unique
|
||||
class AD9361DacStepFactor(IntEnum):
|
||||
FACTOR_2 = 0b0
|
||||
FACTOR_1 = 0b1
|
||||
|
||||
|
||||
class Charon:
|
||||
FREQUENCY_OFFSET = 1e6
|
||||
DEFAULT_IP = "192.168.3.1"
|
||||
|
||||
calibration: rf.calibration.Calibration | None = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ip: str = DEFAULT_IP,
|
||||
frequency: npt.ArrayLike = np.linspace(1e9, 2e9, 3),
|
||||
ports: Tuple[int] | int = 1,
|
||||
):
|
||||
if isinstance(ports, int):
|
||||
ports = (np.arange(ports) + 1).tolist()
|
||||
ports = tuple(ports)
|
||||
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
|
||||
ctx = iio.Context("ip:192.168.3.1")
|
||||
ctrl = ctx.find_device("ad9361-phy")
|
||||
# https://www.analog.com/media/cn/technical-documentation/user-guides/ad9364_register_map_reference_manual_ug-672.pdf
|
||||
ctrl.reg_write(0x26, 0x90) # bit 7: AuxDAC Manual, bit 4: GPO Manual
|
||||
ctrl.reg_write(0x27, 0x10) # bits 7-4: GPO3-0
|
||||
# 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)
|
||||
|
||||
# initialize switch control outputs
|
||||
self._set_gpo(0b0000)
|
||||
self._set_dac_code(value=0, channel=1)
|
||||
self._set_dac_code(value=0, channel=2)
|
||||
|
||||
# set default switch state
|
||||
self.set_switches(a=self.ports[0] - 1, b=self.ports[0] - 1)
|
||||
|
||||
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 _get_dac_code(self, channel: Literal[1, 2]) -> Tuple[float, AD9361DacVref, AD9361DacStepFactor]:
|
||||
word = self.ctrl.reg_read(AD9361Register.__getitem__(f"AUXDAC{channel}_WORD"))
|
||||
config = self.ctrl.reg_read(AD9361Register.__getitem__(f"AUXDAC{channel}_CONFIG"))
|
||||
|
||||
value = (word << 2) + (config & 0x3)
|
||||
vref = AD9361DacVref((config >> 2) & 0x3)
|
||||
step_factor = AD9361DacStepFactor((config >> 4) & 0x1)
|
||||
|
||||
return (value, vref, step_factor)
|
||||
|
||||
def _set_dac_code(
|
||||
self,
|
||||
value: int,
|
||||
channel: Literal[1, 2],
|
||||
vref: AD9361DacVref = AD9361DacVref.VREF_1V0,
|
||||
step_factor: AD9361DacStepFactor = AD9361DacStepFactor.FACTOR_2,
|
||||
) -> None:
|
||||
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)")
|
||||
|
||||
vref = AD9361DacVref(vref)
|
||||
step_factor = AD9361DacStepFactor(step_factor)
|
||||
|
||||
# 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.value << 2) | (step_factor << 4),
|
||||
)
|
||||
|
||||
def set_switches(self, b: int, a: int, excitation: int | None = None):
|
||||
if excitation is None:
|
||||
excitation = a
|
||||
|
||||
val = 0
|
||||
|
||||
val |= int(bool(excitation)) << 0 # exc = GPO0
|
||||
val |= int(bool(a)) << 2 # a = GPO2
|
||||
val |= int(bool(b)) << 1 # b = GPO1
|
||||
|
||||
self._set_gpo(val)
|
||||
|
||||
def set_output_power(self, power: float):
|
||||
pout = xr.DataArray(
|
||||
[-15, -10, -5, 0, 5],
|
||||
dims=["tx_gain"],
|
||||
coords=dict(
|
||||
# TODO: correct over frequency
|
||||
frequency=1e9, # FIXME: I'm not sure at what frequency I generated this table
|
||||
tx_channel=0,
|
||||
tx_gain=[-22, -17, -12, -7, -1],
|
||||
),
|
||||
)
|
||||
|
||||
tx_gain_idx = np.abs(pout - power).argmin(dim="tx_gain")
|
||||
tx_gain = pout.coords["tx_gain"][tx_gain_idx]
|
||||
|
||||
self.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()
|
||||
self.set_output_power(power)
|
||||
self.sdr.tx_lo = int(frequency - self.FREQUENCY_OFFSET)
|
||||
self.sdr.tx_cyclic_buffer = True
|
||||
# For some reason the pluto's DDS has truly horrendous phase noise to the point where it looks modulated
|
||||
self.sdr.tx(generate_tone(f=self.FREQUENCY_OFFSET, fs=self.sdr.sample_rate))
|
||||
# self.sdr.dds_single_tone(self.FREQUENCY_OFFSET, scale=0.9, channel=0)
|
||||
|
||||
def _rx(self, count: int = 1, fc: float | None = None) -> npt.ArrayLike:
|
||||
if count < 1:
|
||||
raise ValueError
|
||||
|
||||
self.sdr.rx_destroy_buffer()
|
||||
if fc is not None:
|
||||
self.sdr.rx_lo = int(fc)
|
||||
self.sdr.rx_enabled_channels = [0, 1]
|
||||
self.sdr.gain_control_mode_chan0 = "manual"
|
||||
self.sdr.gain_control_mode_chan1 = "manual"
|
||||
self.sdr.rx_hardwaregain_chan0 = 30
|
||||
self.sdr.rx_hardwaregain_chan1 = 30
|
||||
return np.concatenate([np.array(self.sdr.rx()) for _ in range(count)], axis=-1)
|
||||
|
||||
def get_b_over_a(self, frequency: float):
|
||||
self.set_output(frequency=frequency, power=-5)
|
||||
|
||||
data = self._rx(1, fc=frequency - self.FREQUENCY_OFFSET)
|
||||
ddc_tone = generate_tone(f=-self.FREQUENCY_OFFSET, fs=self.sdr.sample_rate, scale=1)
|
||||
ddc_data = data * ddc_tone
|
||||
|
||||
ddc_rel = ddc_data[1] / ddc_data[0]
|
||||
|
||||
# plt.figure()
|
||||
# plt.plot(
|
||||
# np.fft.fftshift(np.fft.fftfreq(ddc_data.shape[-1], 1 / self.sdr.sample_rate)),
|
||||
# np.abs(np.fft.fftshift(np.fft.fft(ddc_data, axis=-1))).T,
|
||||
# )
|
||||
# plt.show()
|
||||
|
||||
# TODO: calculate sos only once
|
||||
n, wn = signal.buttord(
|
||||
wp=0.3 * sdr.FREQUENCY_OFFSET,
|
||||
ws=0.8 * sdr.FREQUENCY_OFFSET,
|
||||
gpass=1,
|
||||
gstop=40,
|
||||
analog=False,
|
||||
fs=self.sdr.sample_rate,
|
||||
)
|
||||
sos = signal.butter(n, wn, "lowpass", analog=False, output="sos", fs=self.sdr.sample_rate)
|
||||
# TODO: figure out why filt sucks. Introduces SO much phase noise (out to several MHz)
|
||||
filt_data = signal.sosfiltfilt(sos, ddc_data, axis=-1)
|
||||
|
||||
filt_rel = filt_data[1] / filt_data[0]
|
||||
|
||||
return np.mean(data[1] / data[0])
|
||||
|
||||
def capture(
|
||||
self,
|
||||
callback: Callable[int, int] | None = None,
|
||||
*,
|
||||
measurements: List[Tuple[int, int]] = None,
|
||||
):
|
||||
if measurements is None:
|
||||
measurements = [(m, n) for n in self.ports for m in self.ports]
|
||||
measurements = list(measurements)
|
||||
|
||||
s = xr.DataArray(
|
||||
np.zeros(
|
||||
[len(self.frequency), len(self.ports), len(self.ports)],
|
||||
dtype=np.complex128,
|
||||
),
|
||||
dims=["frequency", "m", "n"],
|
||||
coords=dict(
|
||||
frequency=self.frequency,
|
||||
m=list(self.ports),
|
||||
n=list(self.ports),
|
||||
),
|
||||
)
|
||||
|
||||
total_count = len(measurements) * len(s.frequency)
|
||||
count = 0
|
||||
|
||||
for m in s.m.data:
|
||||
for n in s.n.data:
|
||||
if (m, n) in measurements:
|
||||
self.set_switches(b=m - 1, a=n - 1)
|
||||
|
||||
for ff, freq in enumerate(s.frequency.data):
|
||||
if callback is not None:
|
||||
# report progress during sweep
|
||||
callback(count, total_count)
|
||||
|
||||
self.set_output(frequency=freq, power=-5)
|
||||
self.sdr.rx_destroy_buffer()
|
||||
self.sdr.rx_lo = int(freq)
|
||||
self.sdr.rx_enabled_channels = [0, 1]
|
||||
self.sdr.gain_control_mode_chan0 = "manual"
|
||||
self.sdr.gain_control_mode_chan1 = "manual"
|
||||
self.sdr.rx_hardwaregain_chan0 = 40
|
||||
self.sdr.rx_hardwaregain_chan1 = 40
|
||||
rx = self.sdr.rx()
|
||||
s.loc[dict(frequency=freq, m=m, n=n)] = np.mean(rx[1] / rx[0])
|
||||
|
||||
count += 1
|
||||
|
||||
if callback is not None:
|
||||
# mark capture as complete
|
||||
callback(total_count, total_count)
|
||||
|
||||
return s
|
||||
|
||||
def calibrate_sol(self, prompt: Callable[[str], None] | None = None, **kwargs) -> None:
|
||||
if len(self.ports) != 1:
|
||||
raise ValueError(
|
||||
f"SOL calibration needs only one port but {len(self.ports)} ports are enabled. "
|
||||
"Did you mean to use SOLT?"
|
||||
)
|
||||
|
||||
if prompt is None:
|
||||
prompt = lambda s: input(f"{s}\nENTER to continue...")
|
||||
|
||||
ideal = rf.media.DefinedGammaZ0(frequency=rf.media.Frequency.from_f(self.frequency, unit="Hz"))
|
||||
ideals = [ideal.short(), ideal.open(), ideal.load(0)]
|
||||
|
||||
names = ["short", "open", "load"]
|
||||
|
||||
measured = list()
|
||||
for name in names:
|
||||
prompt(f"Connect standard {name} to port {self.ports[0]}")
|
||||
measured.append(self.capture(**kwargs))
|
||||
|
||||
cal = rf.OnePort(measured=[s2net(m) for m in measured], ideals=ideals)
|
||||
|
||||
self.calibration = cal
|
||||
|
||||
def calibrate_solt(self, prompt: Callable[[str], None] | None = None, **kwargs) -> None:
|
||||
if len(self.ports) < 2:
|
||||
raise ValueError(
|
||||
f"SOLT calibration needs at least two ports but {len(self.ports)} ports are enabled. "
|
||||
"Did you mean to use SOL?"
|
||||
)
|
||||
|
||||
if len(self.ports) > 2:
|
||||
raise NotImplementedError("SOLT calibration with more than two ports not yet supported")
|
||||
|
||||
if prompt is None:
|
||||
prompt = lambda s: input(f"{s}\nENTER to continue...")
|
||||
|
||||
ideal = rf.media.DefinedGammaZ0(frequency=rf.media.Frequency.from_f(self.frequency, unit="Hz"))
|
||||
ideals = [ideal.short(), ideal.open(), ideal.load(0)]
|
||||
ideals = [rf.two_port_reflect(id, id) for id in ideals]
|
||||
|
||||
thru = np.zeros((len(self.frequency), 2, 2), dtype=np.complex128)
|
||||
thru[:, 0, 1] = 1
|
||||
thru[:, 1, 0] = 1
|
||||
thru = rf.Network(frequency=self.frequency, f_unit="Hz", s=thru)
|
||||
|
||||
ideals.append(thru)
|
||||
|
||||
names_1p = ["short", "open", "load"]
|
||||
names_2p = ["thru"]
|
||||
|
||||
measured = list()
|
||||
for name in names_1p:
|
||||
measured_param = list()
|
||||
for port in self.ports:
|
||||
prompt(f"Connect standard {name} to port {port}")
|
||||
measured_param.append(self.capture(measurements=[(port, port)], **kwargs).sel(m=port, n=port))
|
||||
measured.append(rf.two_port_reflect(*[s2net(m) for m in measured_param]))
|
||||
|
||||
for name in names_2p:
|
||||
prompt(f"Connect standard {name} between ports {self.ports[0]} and {self.ports[1]}")
|
||||
measured.append(s2net(self.capture(**kwargs)))
|
||||
|
||||
cal = rf.SOLT(measured=measured, ideals=ideals)
|
||||
|
||||
self.calibration = cal
|
||||
|
||||
def save_calibration(self, path: Path | str):
|
||||
path = Path(path)
|
||||
if path.suffix.lower() == ".pkl":
|
||||
with open(str(path), "wb") as f:
|
||||
pickle.dump(self.calibration, f)
|
||||
else:
|
||||
raise NotImplementedError(f"Unknown calibration file extension: {path.suffix}")
|
||||
|
||||
def load_calibration(self, path: Path | str):
|
||||
path = Path(path)
|
||||
if path.suffix.lower() == ".pkl":
|
||||
with open(str(path), "rb") as f:
|
||||
cal = pickle.load(f)
|
||||
if not isinstance(cal, rf.calibration.Calibration):
|
||||
raise ValueError(f"Expected {rf.calibration.Calibration}, got {type(cal)}")
|
||||
self.calibration = cal
|
||||
else:
|
||||
raise NotImplementedError(f"Unknown calibration file extension: {path.suffix}")
|
||||
|
||||
|
||||
# %%
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
# %%
|
||||
sdr = Charon("ip:192.168.3.1", frequency=np.linspace(1e9, 1.1e9, 11))
|
||||
|
||||
# %% initialization
|
||||
sdr.rx_lo = int(2.0e9)
|
||||
sdr.tx_lo = int(2.0e9)
|
||||
sdr.sample_rate = 30e6
|
||||
sdr.rx_rf_bandwidth = int(4e6)
|
||||
sdr.tx_rf_bandwidth = int(4e6)
|
||||
sdr.rx_destroy_buffer()
|
||||
sdr.tx_destroy_buffer()
|
||||
sdr.rx_enabled_channels = [0, 1]
|
||||
sdr.tx_enabled_channels = [0]
|
||||
sdr.loopback = 0
|
||||
sdr.gain_control_mode_chan0 = "manual"
|
||||
sdr.gain_control_mode_chan1 = "manual"
|
||||
sdr.rx_hardwaregain_chan0 = 40
|
||||
sdr.rx_hardwaregain_chan1 = 40
|
||||
sdr.tx_hardwaregain_chan0 = -10
|
||||
|
||||
config = get_config(sdr)
|
||||
config = sdr.get_config()
|
||||
# print(sdr.ctrl.debug_attrs["adi,rx-rf-port-input-select"].value)
|
||||
# print(sdr.ctrl.debug_attrs["adi,tx-rf-port-input-select"].value)
|
||||
config
|
||||
|
||||
# %%
|
||||
sdr.tx_destroy_buffer() # must destroy buffer before changing cyclicity
|
||||
sdr.tx_cyclic_buffer = True
|
||||
sdr.tx(generate_tone(1e6))
|
||||
# %% generate tone
|
||||
fc = 1e9
|
||||
sdr.set_output(frequency=fc + sdr.FREQUENCY_OFFSET, power=-5)
|
||||
|
||||
# %%
|
||||
sdr.rx_destroy_buffer()
|
||||
data = sdr.rx()
|
||||
# %% capture data
|
||||
data = sdr._rx(1, fc=fc)
|
||||
|
||||
# %% Plot in time
|
||||
fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True)
|
||||
@ -130,70 +474,45 @@ 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")
|
||||
axs[0].grid(True)
|
||||
axs[1].grid(True)
|
||||
axs[-1].set_xlabel("Sample")
|
||||
axs[-1].set_xlim(0, data.shape[-1])
|
||||
fig.show()
|
||||
|
||||
# %%
|
||||
fig, ax = plt.subplots(1, 1, tight_layout=True)
|
||||
ax.plot(np.real(data).T, np.imag(data).T)
|
||||
ax.grid(True)
|
||||
ax.set_aspect("equal")
|
||||
ax.set_xlabel("Real")
|
||||
ax.set_ylabel("Imag")
|
||||
ax.set_xlim(np.array([-1, 1]) * (2 ** (12 - 1) - 1))
|
||||
ax.set_ylim(ax.get_xlim())
|
||||
fig.show()
|
||||
|
||||
# %% Plot in frequency
|
||||
f, Pxx_den = signal.periodogram(data, sdr.sample_rate, axis=-1, return_onesided=False)
|
||||
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.rx_enabled_channels):
|
||||
plt.semilogy(f, Pxx_den[cc], label=f"Channel {chan}")
|
||||
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([1e-7, 1e2])
|
||||
plt.xlabel("frequency [Hz]")
|
||||
plt.ylabel("PSD [V**2/Hz]")
|
||||
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()
|
||||
|
||||
|
||||
# %% TX helper functions
|
||||
def set_output_power(power: float):
|
||||
if power == -5:
|
||||
# FIXME: this is a hack because I don't want to go through re-calibration
|
||||
tx_gain = -8
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
# # TODO: correct over frequency
|
||||
# tx_gain_idx = np.abs(pout.sel(tx_channel=0) - power).argmin(dim="tx_gain")
|
||||
# tx_gain = pout.coords["tx_gain"][tx_gain_idx]
|
||||
sdr.tx_hardwaregain_chan0 = float(tx_gain)
|
||||
|
||||
|
||||
def set_output(frequency: float, power: float, offset_frequency: float = 1e6):
|
||||
sdr.tx_destroy_buffer()
|
||||
set_output_power(power)
|
||||
sdr.tx_lo = int(frequency - offset_frequency)
|
||||
offset_frequency = frequency - sdr.tx_lo
|
||||
sdr.tx_cyclic_buffer = True
|
||||
sdr.tx(generate_tone(offset_frequency))
|
||||
|
||||
|
||||
# %%
|
||||
def vna_capture(frequency: npt.ArrayLike):
|
||||
s = xr.DataArray(
|
||||
np.empty(len(frequency), dtype=np.complex128),
|
||||
dims=["frequency"],
|
||||
coords=dict(
|
||||
frequency=frequency,
|
||||
),
|
||||
)
|
||||
for freq in s.frequency.data:
|
||||
set_output(frequency=freq, power=-5)
|
||||
sdr.rx_destroy_buffer()
|
||||
sdr.rx_lo = int(freq)
|
||||
sdr.rx_enabled_channels = [0, 1]
|
||||
sdr.gain_control_mode_chan0 = "manual"
|
||||
sdr.gain_control_mode_chan1 = "manual"
|
||||
sdr.rx_hardwaregain_chan0 = 40
|
||||
sdr.rx_hardwaregain_chan1 = 40
|
||||
rx = sdr.rx()
|
||||
s.loc[dict(frequency=freq)] = np.mean(rx[1] / rx[0])
|
||||
|
||||
return s
|
||||
|
||||
|
||||
# %%
|
||||
s = vna_capture(frequency=np.linspace(70e6, 200e6, 101))
|
||||
s = sdr.vna_capture(frequency=np.linspace(70e6, 200e6, 101))
|
||||
|
||||
# %% Plot Logmag
|
||||
fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True)
|
||||
@ -217,7 +536,7 @@ reference_sparams = None
|
||||
reference_sparams = dir_ / "RBP-135+_Plus25degC.s2p"
|
||||
if reference_sparams is not None:
|
||||
ref = rf.Network(reference_sparams)
|
||||
rbp135 = xr.DataArray(ref.s, dims=["frequency", "m", "n"], coords=dict(frequency=ref.f, m=[1, 2], n=[1, 2]))
|
||||
rbp135 = net2s(ref)
|
||||
|
||||
axs[0].plot(rbp135.frequency, db20(rbp135.sel(m=1, n=1)), label="Datasheet")
|
||||
axs[1].plot(rbp135.frequency, np.rad2deg(np.angle(rbp135.sel(m=2, n=1))), label="Datasheet")
|
||||
@ -226,23 +545,15 @@ if reference_sparams is not None:
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
# %%
|
||||
def s2net(s: xr.DataArray) -> rf.Network:
|
||||
net = rf.Network(frequency=s.frequency)
|
||||
net.s = s.data
|
||||
return net
|
||||
|
||||
|
||||
# %% SOL calibration
|
||||
cal_frequency = np.linspace(70e6, 600e6, 2001)
|
||||
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 = vna_capture(frequency=cal_frequency)
|
||||
short = sdr.vna_capture(frequency=cal_frequency)
|
||||
input("Connect OPEN and press ENTER...")
|
||||
open = vna_capture(frequency=cal_frequency)
|
||||
open = sdr.vna_capture(frequency=cal_frequency)
|
||||
input("Connect LOAD and press ENTER...")
|
||||
load = vna_capture(frequency=cal_frequency)
|
||||
load = sdr.vna_capture(frequency=cal_frequency)
|
||||
|
||||
short_net = s2net(short)
|
||||
open_net = s2net(open)
|
||||
@ -254,44 +565,8 @@ calibration = rf.calibration.OnePort(
|
||||
[cal_ideal.short(), cal_ideal.open(), cal_ideal.load(0)],
|
||||
)
|
||||
|
||||
|
||||
# %%
|
||||
s = vna_capture(frequency=cal_frequency)
|
||||
|
||||
# %%
|
||||
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],
|
||||
]
|
||||
s = sdr.vna_capture(frequency=cal_frequency)
|
||||
|
||||
# %%
|
||||
s_calibrated = calibration.apply_cal(s2net(s))
|
||||
@ -302,7 +577,7 @@ s_calibrated.plot_s_smith()
|
||||
plt.show()
|
||||
|
||||
plt.figure()
|
||||
for start, stop in ham_bands:
|
||||
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)
|
||||
@ -312,7 +587,7 @@ plt.xlim(s_calibrated.f[0], s_calibrated.f[-1])
|
||||
plt.show()
|
||||
|
||||
plt.figure()
|
||||
for start, stop in ham_bands:
|
||||
for start, stop in HAM_BANDS:
|
||||
plt.axvspan(start, stop, alpha=0.1, color="k")
|
||||
# s_calibrated.plot_s_vswr()
|
||||
# drop invalid points
|
||||
|
71
charon_vna/vna_dev.py
Normal file
71
charon_vna/vna_dev.py
Normal file
@ -0,0 +1,71 @@
|
||||
# %% imports
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
from charon_vna.util import db20, net2s, s2net
|
||||
from charon_vna.vna import Charon
|
||||
|
||||
# %%
|
||||
frequency = np.linspace(80e6, 280e6, 301)
|
||||
|
||||
# %%
|
||||
vna = Charon(frequency=frequency, ports=2)
|
||||
|
||||
# %%
|
||||
s = vna.capture()
|
||||
|
||||
# %%
|
||||
for m in s.m.data:
|
||||
for n in s.n.data:
|
||||
plt.plot(s.frequency, db20(s.sel(m=m, n=n)), label="$S_{" + str(m) + str(n) + "}$")
|
||||
plt.grid(True)
|
||||
plt.legend()
|
||||
plt.show()
|
||||
|
||||
|
||||
# %%
|
||||
vna.calibrate_sol()
|
||||
|
||||
# %%
|
||||
vna.calibrate_solt()
|
||||
|
||||
# %%
|
||||
vna.save_calibration("./calibration.pkl")
|
||||
|
||||
# %%
|
||||
vna.load_calibration("./calibration.pkl")
|
||||
|
||||
# %%
|
||||
s2 = net2s(vna.calibration.apply_cal(s2net(s)))
|
||||
# s2.coords["m"] = s.m
|
||||
# s2.coords["n"] = s.n
|
||||
|
||||
for m in s.m.data:
|
||||
for n in s.n.data:
|
||||
plt.plot(s.frequency, db20(s.sel(m=m, n=n)), label="$S_{" + str(m) + str(n) + "}$ (uncalibrated)")
|
||||
plt.plot(s2.frequency, db20(s2.sel(m=m, n=n)), label="$S_{" + str(m) + str(n) + "}$ (calibrated)")
|
||||
plt.grid(True)
|
||||
plt.legend()
|
||||
plt.ylabel("Magnitude [dB]")
|
||||
# plt.ylim(-30, 5)
|
||||
plt.show()
|
||||
|
||||
for m in s.m.data:
|
||||
for n in s.n.data:
|
||||
if m != n:
|
||||
plt.plot(
|
||||
s.frequency,
|
||||
np.angle(s.sel(m=m, n=n), deg=True),
|
||||
label="$S_{" + str(m) + str(n) + "}$ (uncalibrated)",
|
||||
)
|
||||
plt.plot(
|
||||
s2.frequency,
|
||||
np.angle(s2.sel(m=m, n=n), deg=True),
|
||||
label="$S_{" + str(m) + str(n) + "}$ (calibrated)",
|
||||
)
|
||||
plt.grid(True)
|
||||
plt.legend()
|
||||
plt.ylabel("Phase [deg]")
|
||||
plt.show()
|
||||
|
||||
# %%
|
BIN
img/calibration_standard.jpg
Normal file
BIN
img/calibration_standard.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 766 KiB |
@ -21,14 +21,17 @@ dependencies = [
|
||||
"pyadi-iio",
|
||||
"click",
|
||||
"matplotlib",
|
||||
"pyside6",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://git.brendanhaines.com/brendanhaines/charon"
|
||||
homepage = "https://pypi.org/project/charon-vna/"
|
||||
repository = "https://git.brendanhaines.com/brendanhaines/charon_vna"
|
||||
|
||||
[project.scripts]
|
||||
# charon = "charon.charon:main"
|
||||
charon-cli = "charon_vna.cli:main"
|
||||
charon-gui = "charon_vna.gui:main"
|
||||
|
||||
[tool.setuptools_scm]
|
||||
version_file = "charon_vna/_version.py"
|
||||
|
Reference in New Issue
Block a user