diff --git a/Cargo.lock b/Cargo.lock index d5ebc04..7fa03f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,17 +40,6 @@ dependencies = [ "embedded-hal", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.0.1" @@ -96,24 +85,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d" -[[package]] -name = "bstr" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" - [[package]] name = "byteorder" version = "1.4.2" @@ -135,23 +106,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "bitflags", - "textwrap", - "unicode-width", -] - -[[package]] -name = "const_fn" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" - [[package]] name = "cortex-m" version = "0.6.7" @@ -245,116 +199,23 @@ dependencies = [ ] [[package]] -name = "criterion" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" +name = "derive_stringset" +version = "0.1.0" +source = "git+https://github.com/quartiq/miniconf.git?branch=develop#97ace3d8268075235cb67a2a8740d200bea1fe30" dependencies = [ - "atty", - "cast", - "clap", - "criterion-plot", - "csv", - "itertools 0.10.0", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" -dependencies = [ - "cast", - "itertools 0.9.0", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" -dependencies = [ - "cfg-if", - "const_fn", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" -dependencies = [ - "autocfg", - "cfg-if", - "lazy_static", -] - -[[package]] -name = "csv" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d58633299b24b515ac72a3f869f8b91306a3cec616a602843a383acd6f9e97" -dependencies = [ - "bstr", - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "dsp" version = "0.1.0" dependencies = [ - "criterion", + "easybench", "generic-array 0.14.4", "libm", + "miniconf", "ndarray", "num", "rand", @@ -362,10 +223,10 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.6.1" +name = "easybench" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "355215cf95ddc4db0459d5313c9b146bedd43453305e3f0f1c4e8fde7f8d3884" [[package]] name = "embedded-dma" @@ -386,6 +247,17 @@ dependencies = [ "void", ] +[[package]] +name = "embedded-nal" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae46eb1b02de5a76d9d0ea21d657ff5b0ad2cc47f3a7723608227b1dd1b3eb18" +dependencies = [ + "heapless", + "nb 1.0.0", + "no-std-net", +] + [[package]] name = "enum-iterator" version = "0.6.0" @@ -445,12 +317,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "half" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" - [[package]] name = "hash32" version = "0.1.1" @@ -479,15 +345,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "hermit-abi" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" -dependencies = [ - "libc", -] - [[package]] name = "indexmap" version = "1.6.1" @@ -498,45 +355,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - -[[package]] -name = "js-sys" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.86" @@ -582,18 +400,28 @@ dependencies = [ ] [[package]] -name = "memchr" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +name = "miniconf" +version = "0.1.0" +source = "git+https://github.com/quartiq/miniconf.git?branch=develop#97ace3d8268075235cb67a2a8740d200bea1fe30" +dependencies = [ + "derive_stringset", + "heapless", + "minimq", + "serde", + "serde-json-core", +] [[package]] -name = "memoffset" -version = "0.6.1" +name = "minimq" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +checksum = "9c5e626690b6f62e15710cf9815e5ca25ee54084899298c100a14b2504c80a46" dependencies = [ - "autocfg", + "bit_field", + "embedded-nal", + "enum-iterator", + "generic-array 0.14.4", + "heapless", ] [[package]] @@ -624,6 +452,12 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "no-std-net" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2178127478ae4ee9be7180bc9c3bffb6354dd7238400db567102f98c413a9f35" + [[package]] name = "num" version = "0.3.1" @@ -687,22 +521,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - [[package]] name = "panic-halt" version = "0.2.0" @@ -725,34 +543,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" -[[package]] -name = "plotters" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" - -[[package]] -name = "plotters-svg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" -dependencies = [ - "plotters-backend", -] - [[package]] name = "ppv-lite86" version = "0.2.10" @@ -829,55 +619,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" -[[package]] -name = "rayon" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", -] - -[[package]] -name = "regex" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -dependencies = [ - "byteorder", -] - -[[package]] -name = "regex-syntax" -version = "0.6.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" - [[package]] name = "rtic-core" version = "0.3.1" @@ -904,27 +645,6 @@ dependencies = [ "semver", ] -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "semver" version = "0.9.0" @@ -952,23 +672,12 @@ dependencies = [ [[package]] name = "serde-json-core" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89fd6016a00149b485f66da701f76d909210d319040c97b6eff300f6e2ba2153" +source = "git+https://github.com/rust-embedded-community/serde-json-core.git?branch=master#ee06ac91bc43b72450a92198a00d9e5c5b9946d2" dependencies = [ "heapless", "serde", ] -[[package]] -name = "serde_cbor" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_derive" version = "1.0.123" @@ -980,17 +689,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "smoltcp" version = "0.7.0" @@ -1002,6 +700,17 @@ dependencies = [ "managed", ] +[[package]] +name = "smoltcp-nal" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e5aeb4818706fd74c35917692008d29a5314483c8180300a582253718ce57a" +dependencies = [ + "embedded-nal", + "heapless", + "smoltcp", +] + [[package]] name = "stabilizer" version = "0.4.1" @@ -1018,13 +727,13 @@ dependencies = [ "heapless", "log", "mcp23017", + "miniconf", "nb 1.0.0", "panic-halt", "panic-semihosting", "paste", "serde", - "serde-json-core", - "smoltcp", + "smoltcp-nal", "stm32h7xx-hal", ] @@ -1075,37 +784,12 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "tinytemplate" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ada8616fad06a2d0c455adc530de4ef57605a8120cc65da9653e0e9623ca74" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - [[package]] name = "unicode-xid" version = "0.2.1" @@ -1139,114 +823,8 @@ dependencies = [ "vcell", ] -[[package]] -name = "walkdir" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "wasm-bindgen" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" - -[[package]] -name = "web-sys" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 0bf2c38..599e595 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ panic-semihosting = { version = "0.5", optional = true } panic-halt = "0.2" serde = { version = "1.0", features = ["derive"], default-features = false } heapless = { version = "0.5", features = ["serde"] } -serde-json-core = "0.2" cortex-m-rtic = "0.5.5" embedded-hal = "0.2.4" nb = "1.0.0" @@ -45,15 +44,20 @@ enum-iterator = "0.6.0" paste = "1" dsp = { path = "dsp" } ad9959 = { path = "ad9959" } +smoltcp-nal = "0.1.0" +miniconf = "0.1" + +[patch.crates-io.miniconf] +git = "https://github.com/quartiq/miniconf.git" +branch = "develop" + +[patch.crates-io.serde-json-core] +git = "https://github.com/rust-embedded-community/serde-json-core.git" +branch = "master" [dependencies.mcp23017] git = "https://github.com/mrd0ll4r/mcp23017.git" -[dependencies.smoltcp] -version = "0.7" -features = ["ethernet", "proto-ipv4", "socket-tcp", "proto-ipv6"] -default-features = false - [dependencies.stm32h7xx-hal] features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"] git = "https://github.com/stm32-rs/stm32h7xx-hal" diff --git a/README.md b/README.md index 7f0967b..017ee7e 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,5 @@ See https://github.com/sinara-hw/Stabilizer ## Protocol -Stabilizer can be configured via newline-delimited JSON over TCP. -It listens on port 1235. [stabilizer.py](stabilizer.py) contains a reference -implementation of the protocol. +Stabilizer can be configured via MQTT under the topic `stabilizer/settings/`. Refer to +[`miniconf`](https://github.com/quartiq/miniconf) for more information about topics. diff --git a/dsp/Cargo.toml b/dsp/Cargo.toml index 516b6e2..4c675ff 100644 --- a/dsp/Cargo.toml +++ b/dsp/Cargo.toml @@ -9,9 +9,10 @@ libm = "0.2.1" serde = { version = "1.0", features = ["derive"], default-features = false } generic-array = "0.14" num = { version = "0.3.1", default-features = false } +miniconf = "0.1" [dev-dependencies] -criterion = "0.3" +easybench = "1.0" rand = "0.8" ndarray = "0.14" diff --git a/dsp/benches/micro.rs b/dsp/benches/micro.rs index 6ecc090..1f2cc1f 100644 --- a/dsp/benches/micro.rs +++ b/dsp/benches/micro.rs @@ -1,68 +1,80 @@ use core::f32::consts::PI; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use dsp::{atan2, cossin, iir, iir_int, PLL, RPLL}; +use dsp::{atan2, cossin}; +use dsp::{iir, iir_int}; +use dsp::{PLL, RPLL}; +use easybench::bench_env; -fn atan2_bench(c: &mut Criterion) { +fn atan2_bench() { let xi = (10 << 16) as i32; let xf = xi as f32 / i32::MAX as f32; let yi = (-26_328 << 16) as i32; let yf = yi as f32 / i32::MAX as f32; - c.bench_function("atan2(y, x)", |b| { - b.iter(|| atan2(black_box(yi), black_box(xi))) - }); - c.bench_function("y.atan2(x)", |b| { - b.iter(|| black_box(yf).atan2(black_box(xf))) - }); + println!( + "atan2(yi, xi): {}", + bench_env((yi, xi), |(yi, xi)| atan2(*yi, *xi)) + ); + println!( + "yf.atan2(xf): {}", + bench_env((yf, xf), |(yf, xf)| yf.atan2(*xf)) + ); } -fn cossin_bench(c: &mut Criterion) { +fn cossin_bench() { let zi = -0x7304_2531_i32; let zf = zi as f32 / i32::MAX as f32 * PI; - c.bench_function("cossin(zi)", |b| b.iter(|| cossin(black_box(zi)))); - c.bench_function("zf.sin_cos()", |b| b.iter(|| black_box(zf).sin_cos())); + println!("cossin(zi): {}", bench_env(zi, |zi| cossin(*zi))); + println!("zf.sin_cos(): {}", bench_env(zf, |zf| zf.sin_cos())); } -fn rpll_bench(c: &mut Criterion) { +fn rpll_bench() { let mut dut = RPLL::new(8); - c.bench_function("RPLL::update(Some(t), 21, 20)", |b| { - b.iter(|| dut.update(black_box(Some(0x241)), 21, 20)) - }); - c.bench_function("RPLL::update(Some(t), sf, sp)", |b| { - b.iter(|| { - dut.update(black_box(Some(0x241)), black_box(21), black_box(20)) - }) - }); + println!( + "RPLL::update(Some(t), 21, 20): {}", + bench_env(Some(0x241), |x| dut.update(*x, 21, 20)) + ); + println!( + "RPLL::update(Some(t), sf, sp): {}", + bench_env((Some(0x241), 21, 20), |(x, p, q)| dut.update(*x, *p, *q)) + ); } -fn pll_bench(c: &mut Criterion) { +fn pll_bench() { let mut dut = PLL::default(); - c.bench_function("PLL::update(t, 12, 11)", |b| { - b.iter(|| dut.update(black_box(0x1234), 12, 1)) - }); - c.bench_function("PLL::update(t, sf, sp)", |b| { - b.iter(|| dut.update(black_box(0x241), black_box(21), black_box(20))) - }); + println!( + "PLL::update(t, 12, 12): {}", + bench_env(0x241, |x| dut.update(*x, 12, 12)) + ); + println!( + "PLL::update(t, sf, sp): {}", + bench_env((0x241, 21, 20), |(x, p, q)| dut.update(*x, *p, *q)) + ); } -fn iir_int_bench(c: &mut Criterion) { +fn iir_int_bench() { let dut = iir_int::IIR::default(); let mut xy = iir_int::Vec5::default(); - c.bench_function("int_iir::IIR::update(s, x)", |b| { - b.iter(|| dut.update(&mut xy, black_box(0x2832))) - }); + println!( + "int_iir::IIR::update(s, x): {}", + bench_env(0x2832, |x| dut.update(&mut xy, *x)) + ); } -fn iir_bench(c: &mut Criterion) { +fn iir_bench() { let dut = iir::IIR::default(); let mut xy = iir::Vec5::default(); - c.bench_function("int::IIR::update(s, x)", |b| { - b.iter(|| dut.update(&mut xy, black_box(0.32241))) - }); + println!( + "int::IIR::update(s, x): {}", + bench_env(0.32241, |x| dut.update(&mut xy, *x)) + ); } -criterion_group!(trig, atan2_bench, cossin_bench); -criterion_group!(pll, rpll_bench, pll_bench); -criterion_group!(iir, iir_int_bench, iir_bench); -criterion_main!(trig, pll, iir); +fn main() { + atan2_bench(); + cossin_bench(); + rpll_bench(); + pll_bench(); + iir_int_bench(); + iir_bench(); +} diff --git a/dsp/src/iir.rs b/dsp/src/iir.rs index 31c9db1..d323a6c 100644 --- a/dsp/src/iir.rs +++ b/dsp/src/iir.rs @@ -1,4 +1,5 @@ -use serde::{Deserialize, Serialize}; +use miniconf::StringSet; +use serde::Deserialize; use super::{abs, copysign, macc, max, min}; use core::f32; @@ -38,7 +39,7 @@ pub type Vec5 = [f32; 5]; /// Therefore it can trivially implement bump-less transfer. /// * Cascading multiple IIR filters allows stable and robust /// implementation of transfer functions beyond bequadratic terms. -#[derive(Copy, Clone, Default, Deserialize, Serialize)] +#[derive(Copy, Clone, Debug, Default, Deserialize, StringSet)] pub struct IIR { pub ba: Vec5, pub y_offset: f32, diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index d037bfb..a2279d8 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -4,33 +4,49 @@ use stm32h7xx_hal as hal; -#[macro_use] -extern crate log; +use stabilizer::hardware; -use rtic::cyccnt::{Instant, U32Ext}; - -use heapless::{consts::*, String}; - -use stabilizer::{hardware, server}; +use miniconf::{ + embedded_nal::{IpAddr, Ipv4Addr}, + minimq, MqttInterface, StringSet, +}; +use serde::Deserialize; use dsp::iir; -use hardware::{Adc0Input, Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1}; +use hardware::{ + Adc0Input, Adc1Input, AfeGain, CycleCounter, Dac0Output, Dac1Output, + NetworkStack, AFE0, AFE1, +}; const SCALE: f32 = i16::MAX as _; -const TCP_RX_BUFFER_SIZE: usize = 8192; -const TCP_TX_BUFFER_SIZE: usize = 8192; - // The number of cascaded IIR biquads per channel. Select 1 or 2! const IIR_CASCADE_LENGTH: usize = 1; +#[derive(Debug, Deserialize, StringSet)] +pub struct Settings { + afe: [AfeGain; 2], + iir_ch: [[iir::IIR; IIR_CASCADE_LENGTH]; 2], +} + +impl Default for Settings { + fn default() -> Self { + Self { + afe: [AfeGain::G1, AfeGain::G1], + iir_ch: [[iir::IIR::new(1., -SCALE, SCALE); IIR_CASCADE_LENGTH]; 2], + } + } +} + #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] const APP: () = { struct Resources { afes: (AFE0, AFE1), adcs: (Adc0Input, Adc1Input), dacs: (Dac0Output, Dac1Output), - net_interface: hardware::Ethernet, + mqtt_interface: + MqttInterface, + clock: CycleCounter, // Format: iir_state[ch][cascade-no][coeff] #[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])] @@ -44,6 +60,21 @@ const APP: () = { // Configure the microcontroller let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device); + let mqtt_interface = { + let mqtt_client = { + let broker = IpAddr::V4(Ipv4Addr::new(10, 34, 16, 1)); + minimq::MqttClient::new( + broker, + "stabilizer", + stabilizer.net.stack, + ) + .unwrap() + }; + + MqttInterface::new(mqtt_client, "stabilizer", Settings::default()) + .unwrap() + }; + // Enable ADC/DAC events stabilizer.adcs.0.start(); stabilizer.adcs.1.start(); @@ -54,10 +85,11 @@ const APP: () = { stabilizer.adc_dac_timer.start(); init::LateResources { + mqtt_interface, afes: stabilizer.afes, adcs: stabilizer.adcs, dacs: stabilizer.dacs, - net_interface: stabilizer.net.interface, + clock: stabilizer.cycle_counter, } } @@ -106,159 +138,44 @@ const APP: () = { } } - #[idle(resources=[net_interface, iir_state, iir_ch, afes])] + #[idle(resources=[mqtt_interface, clock], spawn=[settings_update])] fn idle(mut c: idle::Context) -> ! { - let mut socket_set_entries: [_; 8] = Default::default(); - let mut sockets = - smoltcp::socket::SocketSet::new(&mut socket_set_entries[..]); - - let mut rx_storage = [0; TCP_RX_BUFFER_SIZE]; - let mut tx_storage = [0; TCP_TX_BUFFER_SIZE]; - let tcp_handle = { - let tcp_rx_buffer = - smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]); - let tcp_tx_buffer = - smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]); - let tcp_socket = - smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); - sockets.add(tcp_socket) - }; - - let mut server = server::Server::new(); - - let mut time = 0u32; - let mut next_ms = Instant::now(); - - // TODO: Replace with reference to CPU clock from CCDR. - next_ms += 400_000.cycles(); + let clock = c.resources.clock; loop { - let tick = Instant::now() > next_ms; - - if tick { - next_ms += 400_000.cycles(); - time += 1; - } + let sleep = c.resources.mqtt_interface.lock(|interface| { + !interface.network_stack().poll(clock.current_ms()) + }); + match c + .resources + .mqtt_interface + .lock(|interface| interface.update().unwrap()) { - let socket = - &mut *sockets.get::(tcp_handle); - if socket.state() == smoltcp::socket::TcpState::CloseWait { - socket.close(); - } else if !(socket.is_open() || socket.is_listening()) { - socket - .listen(1235) - .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); - } else { - server.poll(socket, |req| { - info!("Got request: {:?}", req); - stabilizer::route_request!(req, - readable_attributes: [ - "stabilizer/iir/state": (|| { - let state = c.resources.iir_state.lock(|iir_state| - server::Status { - t: time, - x0: iir_state[0][0][0], - y0: iir_state[0][0][2], - x1: iir_state[1][0][0], - y1: iir_state[1][0][2], - }); - - Ok::(state) - }), - // "_b" means cascades 2nd IIR - "stabilizer/iir_b/state": (|| { let state = c.resources.iir_state.lock(|iir_state| - server::Status { - t: time, - x0: iir_state[0][IIR_CASCADE_LENGTH-1][0], - y0: iir_state[0][IIR_CASCADE_LENGTH-1][2], - x1: iir_state[1][IIR_CASCADE_LENGTH-1][0], - y1: iir_state[1][IIR_CASCADE_LENGTH-1][2], - }); - - Ok::(state) - }), - "stabilizer/afe0/gain": (|| c.resources.afes.0.get_gain()), - "stabilizer/afe1/gain": (|| c.resources.afes.1.get_gain()) - ], - - modifiable_attributes: [ - "stabilizer/iir0/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][0] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir1/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][0] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir_b0/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir_b1/state": server::IirRequest,(|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/afe0/gain": hardware::AfeGain, (|gain| { - c.resources.afes.0.set_gain(gain); - Ok::<(), ()>(()) - }), - "stabilizer/afe1/gain": hardware::AfeGain, (|gain| { - c.resources.afes.1.set_gain(gain); - Ok::<(), ()>(()) - }) - ] - ) - }); + miniconf::Action::Continue => { + if sleep { + cortex_m::asm::wfi(); + } } - } - - let sleep = match c.resources.net_interface.poll( - &mut sockets, - smoltcp::time::Instant::from_millis(time as i64), - ) { - Ok(changed) => !changed, - Err(smoltcp::Error::Unrecognized) => true, - Err(e) => { - info!("iface poll error: {:?}", e); - true + miniconf::Action::CommitSettings => { + c.spawn.settings_update().unwrap() } - }; - - if sleep { - cortex_m::asm::wfi(); } } } + #[task(priority = 1, resources=[mqtt_interface, afes, iir_ch])] + fn settings_update(mut c: settings_update::Context) { + let settings = &c.resources.mqtt_interface.settings; + + // Update the IIR channels. + c.resources.iir_ch.lock(|iir| *iir = settings.iir_ch); + + // Update AFEs + c.resources.afes.0.set_gain(settings.afe[0]); + c.resources.afes.1.set_gain(settings.afe[1]); + } + #[task(binds = ETH, priority = 1)] fn eth(_: eth::Context) { unsafe { hal::ethernet::interrupt_handler() } diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index 3d7ff0b..3b7c6a8 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -17,7 +17,6 @@ const APP: () = { afes: (AFE0, AFE1), adcs: (Adc0Input, Adc1Input), dacs: (Dac0Output, Dac1Output), - net_interface: hardware::Ethernet, timestamper: InputStamper, pll: RPLL, @@ -53,7 +52,6 @@ const APP: () = { afes: stabilizer.afes, adcs: stabilizer.adcs, dacs: stabilizer.dacs, - net_interface: stabilizer.net.interface, timestamper: stabilizer.timestamper, pll, diff --git a/src/hardware/afe.rs b/src/hardware/afe.rs index 5dd725a..8ce1bd1 100644 --- a/src/hardware/afe.rs +++ b/src/hardware/afe.rs @@ -1,9 +1,12 @@ +use miniconf::StringSet; use serde::{Deserialize, Serialize}; use core::convert::TryFrom; use enum_iterator::IntoEnumIterator; -#[derive(Copy, Clone, Debug, Serialize, Deserialize, IntoEnumIterator)] +#[derive( + Copy, Clone, Debug, Serialize, Deserialize, IntoEnumIterator, StringSet, +)] pub enum Gain { G1 = 0b00, G2 = 0b01, diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index 8ba24fe..d3d10fe 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -1,32 +1,36 @@ ///! Stabilizer hardware configuration ///! ///! This file contains all of the hardware-specific configuration of Stabilizer. -use smoltcp::{iface::Routes, wire::Ipv4Address}; - use stm32h7xx_hal::{ self as hal, ethernet::{self, PHY}, prelude::*, }; +use smoltcp_nal::smoltcp; + use embedded_hal::digital::v2::{InputPin, OutputPin}; use super::{ - adc, afe, dac, design_parameters, digital_input_stamper, eeprom, pounder, - timers, DdsOutput, Ethernet, AFE0, AFE1, + adc, afe, cycle_counter::CycleCounter, dac, design_parameters, + digital_input_stamper, eeprom, pounder, timers, DdsOutput, NetworkStack, + AFE0, AFE1, }; -// Network storage definition for the ethernet interface. -struct NetStorage { - ip_addrs: [smoltcp::wire::IpCidr; 1], - neighbor_cache: +pub struct NetStorage { + pub ip_addrs: [smoltcp::wire::IpCidr; 1], + pub sockets: [Option>; 1], + pub neighbor_cache: [Option<(smoltcp::wire::IpAddress, smoltcp::iface::Neighbor)>; 8], - routes_storage: [Option<(smoltcp::wire::IpCidr, smoltcp::iface::Route)>; 1], + pub routes_cache: + [Option<(smoltcp::wire::IpCidr, smoltcp::iface::Route)>; 8], + pub tx_storage: [u8; 4096], + pub rx_storage: [u8; 4096], } /// The available networking devices on Stabilizer. pub struct NetworkDevices { - pub interface: Ethernet, + pub stack: NetworkStack, pub phy: ethernet::phy::LAN8742A, } @@ -39,6 +43,7 @@ pub struct StabilizerDevices { pub adc_dac_timer: timers::SamplingTimer, pub timestamp_timer: timers::TimestampTimer, pub net: NetworkDevices, + pub cycle_counter: CycleCounter, } /// The available Pounder-specific hardware interfaces. @@ -63,7 +68,11 @@ static mut NET_STORE: NetStorage = NetStorage { smoltcp::wire::Ipv6Cidr::SOLICITED_NODE_PREFIX, )], neighbor_cache: [None; 8], - routes_storage: [None; 1], + routes_cache: [None; 8], + sockets: [None; 1], + + tx_storage: [0; 4096], + rx_storage: [0; 4096], }; /// Configure the stabilizer hardware for operation. @@ -514,8 +523,9 @@ pub fn setup( 24, ); - let default_v4_gw = Ipv4Address::new(10, 34, 16, 1); - let mut routes = Routes::new(&mut store.routes_storage[..]); + let default_v4_gw = smoltcp::wire::Ipv4Address::new(10, 34, 16, 1); + let mut routes = + smoltcp::iface::Routes::new(&mut store.routes_cache[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); let neighbor_cache = @@ -528,8 +538,36 @@ pub fn setup( .routes(routes) .finalize(); + let sockets = { + // Note(unsafe): Configuration is only called once, so we only access the global + // storage a single time. + let socket_storage = unsafe { &mut NET_STORE.sockets[..] }; + let mut sockets = smoltcp::socket::SocketSet::new(socket_storage); + + let tcp_socket = { + let rx_buffer = { + // Note(unsafe): Configuration is only called once, so we only access the global + // storage a single time. + let storage = unsafe { &mut NET_STORE.rx_storage[..] }; + smoltcp::socket::TcpSocketBuffer::new(storage) + }; + + let tx_buffer = { + // Note(unsafe): Configuration is only called once, so we only access the global + // storage a single time. + let storage = unsafe { &mut NET_STORE.tx_storage[..] }; + smoltcp::socket::TcpSocketBuffer::new(storage) + }; + + smoltcp::socket::TcpSocket::new(rx_buffer, tx_buffer) + }; + + sockets.add(tcp_socket); + sockets + }; + NetworkDevices { - interface, + stack: smoltcp_nal::NetworkStack::new(interface, sockets), phy: lan8742a, } }; @@ -795,6 +833,7 @@ pub fn setup( net: network_devices, adc_dac_timer: sampling_timer, timestamp_timer, + cycle_counter: CycleCounter::new(core.DWT, ccdr.clocks.c_ck()), }; // info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap()); @@ -804,8 +843,5 @@ pub fn setup( // Enable the instruction cache. core.SCB.enable_icache(); - // Utilize the cycle counter for RTIC scheduling. - core.DWT.enable_cycle_counter(); - (stabilizer, pounder) } diff --git a/src/hardware/cycle_counter.rs b/src/hardware/cycle_counter.rs new file mode 100644 index 0000000..da915c8 --- /dev/null +++ b/src/hardware/cycle_counter.rs @@ -0,0 +1,66 @@ +use rtic::cyccnt::{Duration, Instant, U32Ext}; + +use stm32h7xx_hal::time::Hertz; + +/// A simple clock for counting elapsed milliseconds. +pub struct CycleCounter { + // The time of the next millisecond in the system. + next_tick: Option, + + // The number of elapsed milliseconds recorded. + ticks: u32, + + // The increment amount of clock cycles for each elapsed millisecond. + increment: Duration, +} + +impl CycleCounter { + /// Construct the cycle counting clock. + /// + /// # Args + /// * `dwt` - The debug watch and trace unit of the CPU core. + /// * `cpu_frequency` - The frequency that the cycle counter counts at. + /// + /// # Returns + /// A clock that can be used for measuring elapsed milliseconds. + pub fn new( + mut dwt: cortex_m::peripheral::DWT, + cpu_frequency: impl Into, + ) -> Self { + dwt.enable_cycle_counter(); + let increment = + ((cpu_frequency.into().0 as f32 / 1000.0) as u32).cycles(); + + Self { + increment, + ticks: 0, + next_tick: None, + } + } + + /// Get the current number of milliseconds elapsed in the system. + /// + /// # Note + /// This function must be called more often than once per 10 seconds to prevent internal + /// wrapping of the cycle counter. + /// + /// The internal millisecond accumulator will overflow just shy of every 50 days. + /// + /// This function does not start counting milliseconds until the very first invocation. + /// + /// # Returns + /// The number of elapsed milliseconds since the system started. + pub fn current_ms(&mut self) -> u32 { + if self.next_tick.is_none() { + self.next_tick = Some(Instant::now() + self.increment); + } + + let now = Instant::now(); + while now > self.next_tick.unwrap() { + *self.next_tick.as_mut().unwrap() += self.increment; + self.ticks += 1; + } + + self.ticks + } +} diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index 41bca1b..0fa6d65 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -10,6 +10,7 @@ use panic_halt as _; mod adc; mod afe; mod configuration; +mod cycle_counter; mod dac; pub mod design_parameters; mod digital_input_stamper; @@ -19,6 +20,7 @@ mod timers; pub use adc::{Adc0Input, Adc1Input}; pub use afe::Gain as AfeGain; +pub use cycle_counter::CycleCounter; pub use dac::{Dac0Output, Dac1Output}; pub use digital_input_stamper::InputStamper; pub use pounder::DdsOutput; @@ -35,8 +37,8 @@ pub type AFE1 = afe::ProgrammableGainAmplifier< hal::gpio::gpiod::PD15>, >; -// Type alias for the ethernet interface on Stabilizer. -pub type Ethernet = smoltcp::iface::EthernetInterface< +pub type NetworkStack = smoltcp_nal::NetworkStack< + 'static, 'static, hal::ethernet::EthernetDMA<'static>, >; diff --git a/src/lib.rs b/src/lib.rs index c252f37..0c9bf2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,4 +5,3 @@ extern crate log; pub mod hardware; -pub mod server; diff --git a/src/server.rs b/src/server.rs deleted file mode 100644 index cd9f5ac..0000000 --- a/src/server.rs +++ /dev/null @@ -1,278 +0,0 @@ -use core::fmt::Write; -use heapless::{consts::*, String, Vec}; -use serde::{Deserialize, Serialize}; -use serde_json_core::{de::from_slice, ser::to_string}; -use smoltcp as net; - -use dsp::iir; - -#[macro_export] -macro_rules! route_request { - ($request:ident, - readable_attributes: [$($read_attribute:tt: $getter:tt),*], - modifiable_attributes: [$($write_attribute:tt: $TYPE:ty, $setter:tt),*]) => { - match $request.req { - server::AccessRequest::Read => { - match $request.attribute { - $( - $read_attribute => { - #[allow(clippy::redundant_closure_call)] - let value = match $getter() { - Ok(data) => data, - Err(_) => return server::Response::error($request.attribute, - "Failed to read attribute"), - }; - - let encoded_data: String = match serde_json_core::to_string(&value) { - Ok(data) => data, - Err(_) => return server::Response::error($request.attribute, - "Failed to encode attribute value"), - }; - - server::Response::success($request.attribute, &encoded_data) - }, - )* - _ => server::Response::error($request.attribute, "Unknown attribute") - } - }, - server::AccessRequest::Write => { - match $request.attribute { - $( - $write_attribute => { - let new_value = match serde_json_core::from_str::<$TYPE>(&$request.value) { - Ok((data, _)) => data, - Err(_) => return server::Response::error($request.attribute, - "Failed to decode value"), - }; - - #[allow(clippy::redundant_closure_call)] - match $setter(new_value) { - Ok(_) => server::Response::success($request.attribute, &$request.value), - Err(_) => server::Response::error($request.attribute, - "Failed to set attribute"), - } - } - )* - _ => server::Response::error($request.attribute, "Unknown attribute") - } - } - } - } -} - -#[derive(Deserialize, Serialize, Debug)] -pub enum AccessRequest { - Read, - Write, -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct Request<'a> { - pub req: AccessRequest, - pub attribute: &'a str, - pub value: String, -} - -#[derive(Serialize, Deserialize)] -pub struct IirRequest { - pub channel: u8, - pub iir: iir::IIR, -} - -#[derive(Serialize)] -pub struct Response { - code: i32, - attribute: String, - value: String, -} - -impl<'a> Request<'a> { - pub fn restore_value(&mut self) { - let mut new_value: String = String::new(); - for byte in self.value.as_str().chars() { - if byte == '\'' { - new_value.push('"').unwrap(); - } else { - new_value.push(byte).unwrap(); - } - } - - self.value = new_value; - } -} - -impl Response { - /// Remove all double quotation marks from the `value` field of a response. - fn sanitize_value(&mut self) { - let mut new_value: String = String::new(); - for byte in self.value.as_str().chars() { - if byte == '"' { - new_value.push('\'').unwrap(); - } else { - new_value.push(byte).unwrap(); - } - } - - self.value = new_value; - } - - /// Remove all double quotation marks from the `value` field of a response and wrap it in single - /// quotes. - fn wrap_and_sanitize_value(&mut self) { - let mut new_value: String = String::new(); - new_value.push('\'').unwrap(); - for byte in self.value.as_str().chars() { - if byte == '"' { - new_value.push('\'').unwrap(); - } else { - new_value.push(byte).unwrap(); - } - } - new_value.push('\'').unwrap(); - - self.value = new_value; - } - - /// Construct a successful reply. - /// - /// Note: `value` will be sanitized to convert all single quotes to double quotes. - /// - /// Args: - /// * `attrbute` - The attribute of the success. - /// * `value` - The value of the attribute. - pub fn success(attribute: &str, value: &str) -> Self { - let mut res = Self { - code: 200, - attribute: String::from(attribute), - value: String::from(value), - }; - res.sanitize_value(); - res - } - - /// Construct an error reply. - /// - /// Note: `message` will be sanitized to convert all single quotes to double quotes. - /// - /// Args: - /// * `attrbute` - The attribute of the success. - /// * `message` - The message denoting the error. - pub fn error(attribute: &str, message: &str) -> Self { - let mut res = Self { - code: 400, - attribute: String::from(attribute), - value: String::from(message), - }; - res.wrap_and_sanitize_value(); - res - } - - /// Construct a custom reply. - /// - /// Note: `message` will be sanitized to convert all single quotes to double quotes. - /// - /// Args: - /// * `attrbute` - The attribute of the success. - /// * `message` - The message denoting the status. - pub fn custom(code: i32, message: &str) -> Self { - let mut res = Self { - code, - attribute: String::from(""), - value: String::from(message), - }; - res.wrap_and_sanitize_value(); - res - } -} - -#[derive(Serialize)] -pub struct Status { - pub t: u32, - pub x0: f32, - pub y0: f32, - pub x1: f32, - pub y1: f32, -} - -pub fn json_reply(socket: &mut net::socket::TcpSocket, msg: &T) { - let mut u: String = to_string(msg).unwrap(); - u.push('\n').unwrap(); - socket.write_str(&u).unwrap(); -} - -pub struct Server { - data: Vec, - discard: bool, -} - -impl Server { - /// Construct a new server object for managing requests. - pub fn new() -> Self { - Self { - data: Vec::new(), - discard: false, - } - } - - /// Poll the server for potential data updates. - /// - /// Args: - /// * `socket` - The socket to check contents from. - /// * `f` - A closure that can be called if a request has been received on the server. - pub fn poll(&mut self, socket: &mut net::socket::TcpSocket, mut f: F) - where - F: FnMut(&Request) -> Response, - { - while socket.can_recv() { - let found = socket - .recv(|buf| { - let (len, found) = - match buf.iter().position(|&c| c as char == '\n') { - Some(end) => (end + 1, true), - None => (buf.len(), false), - }; - if self.data.len() + len >= self.data.capacity() { - self.discard = true; - self.data.clear(); - } else if !self.discard && len > 0 { - self.data.extend_from_slice(&buf[..len]).unwrap(); - } - (len, found) - }) - .unwrap(); - - if found { - if self.discard { - self.discard = false; - json_reply( - socket, - &Response::custom(520, "command buffer overflow"), - ); - } else { - let r = from_slice::( - &self.data[..self.data.len() - 1], - ); - match r { - Ok((mut res, _)) => { - // Note that serde_json_core doesn't escape quotations within a string. - // To account for this, we manually translate all single quotes to - // double quotes. This occurs because we doubly-serialize this field in - // some cases. - res.restore_value(); - let response = f(&res); - json_reply(socket, &response); - } - Err(err) => { - warn!("parse error {:?}", err); - json_reply( - socket, - &Response::custom(550, "parse error"), - ); - } - } - } - self.data.clear(); - } - } - } -} diff --git a/stabilizer.py b/stabilizer.py deleted file mode 100644 index 0f1e206..0000000 --- a/stabilizer.py +++ /dev/null @@ -1,112 +0,0 @@ -import json -import asyncio -from collections import OrderedDict as OD -import logging - -import numpy as np - -logger = logging.getLogger() - - -class StabilizerError(Exception): - pass - - -class StabilizerConfig: - async def connect(self, host, port=1235): - self.reader, self.writer = await asyncio.open_connection(host, port) - - async def set(self, channel, iir): - value = OD([("channel", channel), ("iir", iir.as_dict())]) - request = { - "req": "Write", - "attribute": "stabilizer/iir{}/state".format(channel), - "value": json.dumps(value, separators=[',', ':']).replace('"', "'"), - } - s = json.dumps(request, separators=[',', ':']) - assert "\n" not in s - logger.debug("send %s", s) - self.writer.write(s.encode("ascii") + b"\n") - r = (await self.reader.readline()).decode() - logger.debug("recv %s", r) - ret = json.loads(r, object_pairs_hook=OD) - if ret["code"] != 200: - raise StabilizerError(ret) - return ret - - -class IIR: - t_update = 2e-6 - full_scale = float((1 << 15) - 1) - - def __init__(self): - self.ba = np.zeros(5, np.float32) - self.y_offset = 0. - self.y_min = -self.full_scale - 1 - self.y_max = self.full_scale - - def as_dict(self): - iir = OD() - iir["ba"] = [float(_) for _ in self.ba] - iir["y_offset"] = self.y_offset - iir["y_min"] = self.y_min - iir["y_max"] = self.y_max - return iir - - def configure_pi(self, kp, ki, g=0.): - ki = np.copysign(ki, kp)*self.t_update*2 - g = np.copysign(g, kp) - eps = np.finfo(np.float32).eps - if abs(ki) < eps: - a1, b0, b1 = 0., kp, 0. - else: - if abs(g) < eps: - c = 1. - else: - c = 1./(1. + ki/g) - a1 = 2*c - 1. - b0 = ki*c + kp - b1 = ki*c - a1*kp - if abs(b0 + b1) < eps: - raise ValueError("low integrator gain and/or gain limit") - self.ba[0] = b0 - self.ba[1] = b1 - self.ba[2] = 0. - self.ba[3] = a1 - self.ba[4] = 0. - - def set_x_offset(self, o): - b = self.ba[:3].sum()*self.full_scale - self.y_offset = b*o - - -if __name__ == "__main__": - import argparse - p = argparse.ArgumentParser() - p.add_argument("-s", "--stabilizer", default="10.0.16.99") - p.add_argument("-c", "--channel", default=0, type=int, - help="Stabilizer channel to configure") - p.add_argument("-o", "--offset", default=0., type=float, - help="input offset, in units of full scale") - p.add_argument("-p", "--proportional-gain", default=1., type=float, - help="Proportional gain, in units of 1") - p.add_argument("-i", "--integral-gain", default=0., type=float, - help="Integral gain, in units of Hz, " - "sign taken from proportional-gain") - - args = p.parse_args() - - loop = asyncio.get_event_loop() - # loop.set_debug(True) - logging.basicConfig(level=logging.DEBUG) - - async def main(): - i = IIR() - i.configure_pi(args.proportional_gain, args.integral_gain) - i.set_x_offset(args.offset) - s = StabilizerConfig() - await s.connect(args.stabilizer) - assert args.channel in range(2) - r = await s.set(args.channel, i) - - loop.run_until_complete(main())