diff --git a/Cargo.lock b/Cargo.lock index cb3441b..d5ebc04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,17 @@ 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" @@ -85,6 +96,24 @@ 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" @@ -106,6 +135,23 @@ 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" @@ -199,33 +245,127 @@ dependencies = [ ] [[package]] -name = "derive_stringset" -version = "0.1.0" -source = "git+https://github.com/quartiq/miniconf.git?branch=rs/issue-21/terminal-array-elements#b93924e81ad860efd9dcda8df2ea901a579958e2" +name = "criterion" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" dependencies = [ - "proc-macro2", - "quote", - "syn", + "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", ] [[package]] name = "dsp" version = "0.1.0" dependencies = [ - "easybench", + "criterion", "generic-array 0.14.4", "libm", - "miniconf", "ndarray", + "num", "rand", "serde", ] [[package]] -name = "easybench" -version = "1.1.0" +name = "either" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355215cf95ddc4db0459d5313c9b146bedd43453305e3f0f1c4e8fde7f8d3884" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "embedded-dma" @@ -246,17 +386,6 @@ 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" @@ -316,6 +445,12 @@ 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" @@ -344,6 +479,15 @@ 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" @@ -354,6 +498,45 @@ 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" @@ -399,28 +582,18 @@ dependencies = [ ] [[package]] -name = "miniconf" -version = "0.1.0" -source = "git+https://github.com/quartiq/miniconf.git?branch=rs/issue-21/terminal-array-elements#b93924e81ad860efd9dcda8df2ea901a579958e2" -dependencies = [ - "derive_stringset", - "heapless", - "minimq", - "serde", - "serde-json-core", -] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] -name = "minimq" -version = "0.2.0" +name = "memoffset" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5e626690b6f62e15710cf9815e5ca25ee54084899298c100a14b2504c80a46" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" dependencies = [ - "bit_field", - "embedded-nal", - "enum-iterator", - "generic-array 0.14.4", - "heapless", + "autocfg", ] [[package]] @@ -452,10 +625,17 @@ dependencies = [ ] [[package]] -name = "no-std-net" -version = "0.4.0" +name = "num" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2178127478ae4ee9be7180bc9c3bffb6354dd7238400db567102f98c413a9f35" +checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] [[package]] name = "num-complex" @@ -476,6 +656,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.14" @@ -485,6 +687,22 @@ 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" @@ -507,6 +725,34 @@ 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" @@ -583,6 +829,55 @@ 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" @@ -609,6 +904,27 @@ 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" @@ -643,6 +959,16 @@ dependencies = [ "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" @@ -654,6 +980,17 @@ 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" @@ -665,17 +1002,6 @@ 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" @@ -692,13 +1018,13 @@ dependencies = [ "heapless", "log", "mcp23017", - "miniconf", "nb 1.0.0", "panic-halt", "panic-semihosting", "paste", "serde", - "smoltcp-nal", + "serde-json-core", + "smoltcp", "stm32h7xx-hal", ] @@ -749,12 +1075,37 @@ 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" @@ -788,8 +1139,114 @@ 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/dsp/Cargo.toml b/dsp/Cargo.toml index 1fefdd7..516b6e2 100644 --- a/dsp/Cargo.toml +++ b/dsp/Cargo.toml @@ -8,10 +8,10 @@ edition = "2018" libm = "0.2.1" serde = { version = "1.0", features = ["derive"], default-features = false } generic-array = "0.14" -miniconf = "0.1" +num = { version = "0.3.1", default-features = false } [dev-dependencies] -easybench = "1.0" +criterion = "0.3" rand = "0.8" ndarray = "0.14" diff --git a/dsp/benches/micro.rs b/dsp/benches/micro.rs index 8c34256..6ecc090 100644 --- a/dsp/benches/micro.rs +++ b/dsp/benches/micro.rs @@ -1,80 +1,68 @@ use core::f32::consts::PI; -use dsp::{atan2, cossin}; -use dsp::{iir, iir_int}; -use dsp::{pll::PLL, rpll::RPLL}; -use easybench::bench_env; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use dsp::{atan2, cossin, iir, iir_int, PLL, RPLL}; -fn atan2_bench() { +fn atan2_bench(c: &mut Criterion) { 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; - 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)) - ); + 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))) + }); } -fn cossin_bench() { +fn cossin_bench(c: &mut Criterion) { let zi = -0x7304_2531_i32; let zf = zi as f32 / i32::MAX as f32 * PI; - println!("cossin(zi): {}", bench_env(zi, |zi| cossin(*zi))); - println!("zf.sin_cos(): {}", bench_env(zf, |zf| zf.sin_cos())); + 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())); } -fn rpll_bench() { +fn rpll_bench(c: &mut Criterion) { let mut dut = RPLL::new(8); - 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)) - ); + 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)) + }) + }); } -fn pll_bench() { +fn pll_bench(c: &mut Criterion) { let mut dut = PLL::default(); - 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)) - ); + 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))) + }); } -fn iir_int_bench() { +fn iir_int_bench(c: &mut Criterion) { let dut = iir_int::IIR::default(); let mut xy = iir_int::Vec5::default(); - println!( - "int_iir::IIR::update(s, x): {}", - bench_env(0x2832, |x| dut.update(&mut xy, *x)) - ); + c.bench_function("int_iir::IIR::update(s, x)", |b| { + b.iter(|| dut.update(&mut xy, black_box(0x2832))) + }); } -fn iir_bench() { +fn iir_bench(c: &mut Criterion) { let dut = iir::IIR::default(); let mut xy = iir::Vec5::default(); - println!( - "int::IIR::update(s, x): {}", - bench_env(0.32241, |x| dut.update(&mut xy, *x)) - ); + c.bench_function("int::IIR::update(s, x)", |b| { + b.iter(|| dut.update(&mut xy, black_box(0.32241))) + }); } -fn main() { - atan2_bench(); - cossin_bench(); - rpll_bench(); - pll_bench(); - iir_int_bench(); - iir_bench(); -} +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); diff --git a/dsp/src/complex.rs b/dsp/src/complex.rs index 6353f9a..3349460 100644 --- a/dsp/src/complex.rs +++ b/dsp/src/complex.rs @@ -1,33 +1,29 @@ -use core::ops::Mul; +pub use num::Complex; use super::{atan2, cossin}; -#[derive(Copy, Clone, Default, PartialEq, Debug)] -pub struct Complex(pub T, pub T); - -impl Complex { - pub fn map(&self, func: F) -> Self - where - F: Fn(T) -> T, - { - Complex(func(self.0), func(self.1)) - } +/// Complex extension trait offering DSP (fast, good accuracy) functionality. +pub trait ComplexExt { + fn from_angle(angle: T) -> Self; + fn abs_sqr(&self) -> U; + fn log2(&self) -> T; + fn arg(&self) -> T; } -impl Complex { +impl ComplexExt for Complex { /// Return a Complex on the unit circle given an angle. /// /// Example: /// /// ``` - /// use dsp::Complex; + /// use dsp::{Complex, ComplexExt}; /// Complex::::from_angle(0); /// Complex::::from_angle(1 << 30); // pi/2 /// Complex::::from_angle(-1 << 30); // -pi/2 /// ``` - pub fn from_angle(angle: i32) -> Self { + fn from_angle(angle: i32) -> Self { let (c, s) = cossin(angle); - Self(c, s) + Self { re: c, im: s } } /// Return the absolute square (the squared magnitude). @@ -39,13 +35,13 @@ impl Complex { /// Example: /// /// ``` - /// use dsp::Complex; - /// assert_eq!(Complex(i32::MIN, 0).abs_sqr(), 1 << 31); - /// assert_eq!(Complex(i32::MAX, i32::MAX).abs_sqr(), u32::MAX - 3); + /// use dsp::{Complex, ComplexExt}; + /// assert_eq!(Complex::new(i32::MIN, 0).abs_sqr(), 1 << 31); + /// assert_eq!(Complex::new(i32::MAX, i32::MAX).abs_sqr(), u32::MAX - 3); /// ``` - pub fn abs_sqr(&self) -> u32 { - (((self.0 as i64) * (self.0 as i64) - + (self.1 as i64) * (self.1 as i64)) + fn abs_sqr(&self) -> u32 { + (((self.re as i64) * (self.re as i64) + + (self.im as i64) * (self.im as i64)) >> 31) as u32 } @@ -59,15 +55,15 @@ impl Complex { /// Example: /// /// ``` - /// use dsp::Complex; - /// assert_eq!(Complex(i32::MAX, i32::MAX).log2(), -1); - /// assert_eq!(Complex(i32::MAX, 0).log2(), -2); - /// assert_eq!(Complex(1, 0).log2(), -63); - /// assert_eq!(Complex(0, 0).log2(), -64); + /// use dsp::{Complex, ComplexExt}; + /// assert_eq!(Complex::new(i32::MAX, i32::MAX).log2(), -1); + /// assert_eq!(Complex::new(i32::MAX, 0).log2(), -2); + /// assert_eq!(Complex::new(1, 0).log2(), -63); + /// assert_eq!(Complex::new(0, 0).log2(), -64); /// ``` - pub fn log2(&self) -> i32 { - let a = (self.0 as i64) * (self.0 as i64) - + (self.1 as i64) * (self.1 as i64); + fn log2(&self) -> i32 { + let a = (self.re as i64) * (self.re as i64) + + (self.im as i64) * (self.im as i64); -(a.leading_zeros() as i32) } @@ -78,52 +74,51 @@ impl Complex { /// Example: /// /// ``` - /// use dsp::Complex; - /// assert_eq!(Complex(1, 0).arg(), 0); - /// assert_eq!(Complex(-i32::MAX, 1).arg(), i32::MAX); - /// assert_eq!(Complex(-i32::MAX, -1).arg(), -i32::MAX); - /// assert_eq!(Complex(0, -1).arg(), -i32::MAX >> 1); - /// assert_eq!(Complex(0, 1).arg(), (i32::MAX >> 1) + 1); - /// assert_eq!(Complex(1, 1).arg(), (i32::MAX >> 2) + 1); + /// use dsp::{Complex, ComplexExt}; + /// assert_eq!(Complex::new(1, 0).arg(), 0); + /// assert_eq!(Complex::new(-i32::MAX, 1).arg(), i32::MAX); + /// assert_eq!(Complex::new(-i32::MAX, -1).arg(), -i32::MAX); + /// assert_eq!(Complex::new(0, -1).arg(), -i32::MAX >> 1); + /// assert_eq!(Complex::new(0, 1).arg(), (i32::MAX >> 1) + 1); + /// assert_eq!(Complex::new(1, 1).arg(), (i32::MAX >> 2) + 1); /// ``` - pub fn arg(&self) -> i32 { - atan2(self.1, self.0) + fn arg(&self) -> i32 { + atan2(self.im, self.re) } } -impl Mul for Complex { - type Output = Self; +/// Full scale fixed point multiplication. +pub trait MulScaled { + fn mul_scaled(self, other: T) -> Self; +} - fn mul(self, other: Self) -> Self { - let a = self.0 as i64; - let b = self.1 as i64; - let c = other.0 as i64; - let d = other.1 as i64; - Complex( - ((a * c - b * d + (1 << 31)) >> 32) as i32, - ((b * c + a * d + (1 << 31)) >> 32) as i32, - ) +impl MulScaled> for Complex { + fn mul_scaled(self, other: Self) -> Self { + let a = self.re as i64; + let b = self.im as i64; + let c = other.re as i64; + let d = other.im as i64; + Complex { + re: ((a * c - b * d + (1 << 30)) >> 31) as i32, + im: ((b * c + a * d + (1 << 30)) >> 31) as i32, + } } } -impl Mul for Complex { - type Output = Self; - - fn mul(self, other: i32) -> Self { - Complex( - ((other as i64 * self.0 as i64 + (1 << 31)) >> 32) as i32, - ((other as i64 * self.1 as i64 + (1 << 31)) >> 32) as i32, - ) +impl MulScaled for Complex { + fn mul_scaled(self, other: i32) -> Self { + Complex { + re: ((other as i64 * self.re as i64 + (1 << 30)) >> 31) as i32, + im: ((other as i64 * self.im as i64 + (1 << 30)) >> 31) as i32, + } } } -impl Mul for Complex { - type Output = Self; - - fn mul(self, other: i16) -> Self { - Complex( - (other as i32 * (self.0 >> 16) + (1 << 15)) >> 16, - (other as i32 * (self.1 >> 16) + (1 << 15)) >> 16, - ) +impl MulScaled for Complex { + fn mul_scaled(self, other: i16) -> Self { + Complex { + re: (other as i32 * (self.re >> 16) + (1 << 14)) >> 15, + im: (other as i32 * (self.im >> 16) + (1 << 14)) >> 15, + } } } diff --git a/dsp/src/cossin.rs b/dsp/src/cossin.rs index e5746a3..4a5cccd 100644 --- a/dsp/src/cossin.rs +++ b/dsp/src/cossin.rs @@ -74,7 +74,6 @@ pub fn cossin(phase: i32) -> (i32, i32) { #[cfg(test)] mod tests { use super::*; - use crate::Complex; use core::f64::consts::PI; #[test] @@ -82,11 +81,11 @@ mod tests { // Constant amplitude error due to LUT data range. const AMPLITUDE: f64 = ((1i64 << 31) - (1i64 << 15)) as _; const MAX_PHASE: f64 = (1i64 << 32) as _; - let mut rms_err = Complex(0f64, 0f64); - let mut sum_err = Complex(0f64, 0f64); - let mut max_err = Complex(0f64, 0f64); - let mut sum = Complex(0f64, 0f64); - let mut demod = Complex(0f64, 0f64); + let mut rms_err = (0f64, 0f64); + let mut sum_err = (0f64, 0f64); + let mut max_err = (0f64, 0f64); + let mut sum = (0f64, 0f64); + let mut demod = (0f64, 0f64); // use std::{fs::File, io::{BufWriter, prelude::*}, path::Path}; // let mut file = BufWriter::new(File::create(Path::new("data.bin")).unwrap()); diff --git a/dsp/src/lib.rs b/dsp/src/lib.rs index 31ff680..ac91124 100644 --- a/dsp/src/lib.rs +++ b/dsp/src/lib.rs @@ -1,101 +1,28 @@ #![cfg_attr(not(test), no_std)] #![cfg_attr(feature = "nightly", feature(asm, core_intrinsics))] -use core::ops::{Add, Mul, Neg}; - -fn abs(x: T) -> T -where - T: PartialOrd + Default + Neg, -{ - if x >= T::default() { - x - } else { - -x - } -} - -// These are implemented here because core::f32 doesn't have them (yet). -// They are naive and don't handle inf/nan. -// `compiler-intrinsics`/llvm should have better (robust, universal, and -// faster) implementations. - -fn copysign(x: T, y: T) -> T -where - T: PartialOrd + Default + Neg, -{ - if (x >= T::default() && y >= T::default()) - || (x <= T::default() && y <= T::default()) - { - x - } else { - -x - } -} - -#[cfg(not(feature = "nightly"))] -fn max(x: T, y: T) -> T -where - T: PartialOrd, -{ - if x > y { - x - } else { - y - } -} - -#[cfg(not(feature = "nightly"))] -fn min(x: T, y: T) -> T -where - T: PartialOrd, -{ - if x < y { - x - } else { - y - } -} - -#[cfg(feature = "nightly")] -fn max(x: f32, y: f32) -> f32 { - core::intrinsics::maxnumf32(x, y) -} - -#[cfg(feature = "nightly")] -fn min(x: f32, y: f32) -> f32 { - core::intrinsics::minnumf32(x, y) -} - -// Multiply-accumulate vectors `x` and `a`. -// -// A.k.a. dot product. -// Rust/LLVM optimize this nicely. -fn macc(y0: T, x: &[T], a: &[T]) -> T -where - T: Add + Mul + Copy, -{ - x.iter() - .zip(a) - .map(|(x, a)| *x * *a) - .fold(y0, |y, xa| y + xa) -} - -pub mod accu; +mod tools; +pub use tools::*; mod atan2; +pub use atan2::*; +mod accu; +pub use accu::*; mod complex; +pub use complex::*; mod cossin; +pub use cossin::*; pub mod iir; pub mod iir_int; -pub mod lockin; -pub mod lowpass; -pub mod pll; -pub mod rpll; -pub mod unwrap; - -pub use accu::Accu; -pub use atan2::atan2; -pub use complex::Complex; -pub use cossin::cossin; +mod lockin; +pub use lockin::*; +mod lowpass; +pub use lowpass::*; +mod pll; +pub use pll::*; +mod rpll; +pub use rpll::*; +mod unwrap; +pub use unwrap::*; #[cfg(test)] pub mod testing; diff --git a/dsp/src/lockin.rs b/dsp/src/lockin.rs index 6ebf4cb..6a977b4 100644 --- a/dsp/src/lockin.rs +++ b/dsp/src/lockin.rs @@ -1,4 +1,4 @@ -use super::{lowpass::Lowpass, Complex}; +use super::{Complex, ComplexExt, Lowpass, MulScaled}; use generic_array::typenum::U2; #[derive(Clone, Default)] @@ -8,19 +8,15 @@ pub struct Lockin { impl Lockin { /// Update the lockin with a sample taken at a given phase. - /// The lowpass has a gain of `1 << k`. - pub fn update(&mut self, sample: i16, phase: i32, k: u8) -> Complex { - // Get the LO signal for demodulation. - let lo = Complex::from_angle(phase); - - // Mix with the LO signal - let mix = lo * sample; + pub fn update(&mut self, sample: i32, phase: i32, k: u8) -> Complex { + // Get the LO signal for demodulation and mix the sample; + let mix = Complex::from_angle(phase).mul_scaled(sample); // Filter with the IIR lowpass, // return IQ (in-phase and quadrature) data. - Complex( - self.state[0].update(mix.0, k), - self.state[1].update(mix.1, k), - ) + Complex { + re: self.state[0].update(mix.re, k), + im: self.state[1].update(mix.im, k), + } } } diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs index 91fae2f..5ab803d 100644 --- a/dsp/src/lowpass.rs +++ b/dsp/src/lowpass.rs @@ -14,19 +14,20 @@ impl> Lowpass { /// Update the filter with a new sample. /// /// # Args - /// * `x`: Input data, needs `k` bits headroom. - /// * `k`: Log2 time constant, 0..31. + /// * `x`: Input data. Needs 1 bit headroom but will saturate cleanly beyond that. + /// * `k`: Log2 time constant, 1..=31. /// /// # Return - /// Filtered output y, with gain of `1 << k`. + /// Filtered output y. pub fn update(&mut self, x: i32, k: u8) -> i32 { debug_assert!(k & 31 == k); + debug_assert!((k - 1) & 31 == k - 1); // This is an unrolled and optimized first-order IIR loop // that works for all possible time constants. - // Note DF-II and the zeros at Nyquist. - let mut x = x << k; + // Note T-DF-I and the zeros at Nyquist. + let mut x = x; for y in self.y.iter_mut() { - let dy = (x - *y + (1 << (k - 1))) >> k; + let dy = x.saturating_sub(*y).saturating_add(1 << (k - 1)) >> k; *y += dy; x = *y - (dy >> 1); } diff --git a/dsp/src/testing.rs b/dsp/src/testing.rs index f8e753d..1654b62 100644 --- a/dsp/src/testing.rs +++ b/dsp/src/testing.rs @@ -31,7 +31,7 @@ pub fn complex_isclose( rtol: f32, atol: f32, ) -> bool { - isclosef(a.0, b.0, rtol, atol) && isclosef(a.1, b.1, rtol, atol) + isclosef(a.re, b.re, rtol, atol) && isclosef(a.im, b.im, rtol, atol) } pub fn complex_allclose( diff --git a/dsp/src/tools.rs b/dsp/src/tools.rs new file mode 100644 index 0000000..378734f --- /dev/null +++ b/dsp/src/tools.rs @@ -0,0 +1,95 @@ +use core::ops::{Add, Mul, Neg}; + +pub fn abs(x: T) -> T +where + T: PartialOrd + Default + Neg, +{ + if x >= T::default() { + x + } else { + -x + } +} + +// These are implemented here because core::f32 doesn't have them (yet). +// They are naive and don't handle inf/nan. +// `compiler-intrinsics`/llvm should have better (robust, universal, and +// faster) implementations. + +pub fn copysign(x: T, y: T) -> T +where + T: PartialOrd + Default + Neg, +{ + if (x >= T::default() && y >= T::default()) + || (x <= T::default() && y <= T::default()) + { + x + } else { + -x + } +} + +#[cfg(not(feature = "nightly"))] +pub fn max(x: T, y: T) -> T +where + T: PartialOrd, +{ + if x > y { + x + } else { + y + } +} + +#[cfg(not(feature = "nightly"))] +pub fn min(x: T, y: T) -> T +where + T: PartialOrd, +{ + if x < y { + x + } else { + y + } +} + +#[cfg(feature = "nightly")] +pub fn max(x: f32, y: f32) -> f32 { + core::intrinsics::maxnumf32(x, y) +} + +#[cfg(feature = "nightly")] +pub fn min(x: f32, y: f32) -> f32 { + core::intrinsics::minnumf32(x, y) +} + +// Multiply-accumulate vectors `x` and `a`. +// +// A.k.a. dot product. +// Rust/LLVM optimize this nicely. +pub fn macc(y0: T, x: &[T], a: &[T]) -> T +where + T: Add + Mul + Copy, +{ + x.iter() + .zip(a) + .map(|(x, a)| *x * *a) + .fold(y0, |y, xa| y + xa) +} + +/// Combine high and low i32 into a single downscaled i32, saturating the type. +pub fn saturating_scale(lo: i32, hi: i32, shift: u32) -> i32 { + debug_assert!(shift & 31 == shift); + + let shift_hi = 31 - shift; + debug_assert!(shift_hi & 31 == shift_hi); + + let over = hi >> shift; + if over < -1 { + i32::MIN + } else if over > 0 { + i32::MAX + } else { + (lo >> shift) + (hi << shift_hi) + } +} diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index ae15111..3b7c6a8 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -6,7 +6,7 @@ use stm32h7xx_hal as hal; use stabilizer::{hardware, hardware::design_parameters}; -use dsp::{lockin::Lockin, rpll::RPLL, Accu}; +use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL}; use hardware::{ Adc0Input, Adc1Input, Dac0Output, Dac1Output, InputStamper, AFE0, AFE1, }; @@ -110,22 +110,33 @@ const APP: () = { let sample_phase = phase_offset.wrapping_add(pll_phase.wrapping_mul(harmonic)); - let output = adc_samples[0] + let output: Complex = adc_samples[0] .iter() + // Zip in the LO phase. .zip(Accu::new(sample_phase, sample_frequency)) - // Convert to signed, MSB align the ADC sample. + // Convert to signed, MSB align the ADC sample, update the Lockin (demodulate, filter) .map(|(&sample, phase)| { - lockin.update(sample as i16, phase, time_constant) + let s = (sample as i16 as i32) << 16; + lockin.update(s, phase, time_constant) }) + // Decimate .last() - .unwrap(); + .unwrap() + * 2; // Full scale assuming the 2f component is gone. - let conf = "frequency_discriminator"; + #[allow(dead_code)] + enum Conf { + PowerPhase, + FrequencyDiscriminator, + Quadrature, + } + + let conf = Conf::FrequencyDiscriminator; // TODO: expose let output = match conf { // Convert from IQ to power and phase. - "power_phase" => [(output.log2() << 24) as _, output.arg()], - "frequency_discriminator" => [pll_frequency as _, output.arg()], - _ => [output.0, output.1], + Conf::PowerPhase => [(output.log2() << 24) as _, output.arg()], + Conf::FrequencyDiscriminator => [pll_frequency as _, output.arg()], + Conf::Quadrature => [output.re, output.im], }; // Convert to DAC data. diff --git a/src/bin/lockin-internal.rs b/src/bin/lockin-internal.rs index c4d5bd6..1f9df6e 100644 --- a/src/bin/lockin-internal.rs +++ b/src/bin/lockin-internal.rs @@ -2,7 +2,7 @@ #![no_std] #![no_main] -use dsp::{lockin::Lockin, Accu}; +use dsp::{Accu, Complex, ComplexExt, Lockin}; use hardware::{Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1}; use stabilizer::{hardware, hardware::design_parameters}; @@ -95,17 +95,19 @@ const APP: () = { let sample_phase = phase_offset .wrapping_add((pll_phase as i32).wrapping_mul(harmonic)); - let output = adc_samples + let output: Complex = adc_samples .iter() // Zip in the LO phase. .zip(Accu::new(sample_phase, sample_frequency)) // Convert to signed, MSB align the ADC sample, update the Lockin (demodulate, filter) .map(|(&sample, phase)| { - lockin.update(sample as i16, phase, time_constant) + let s = (sample as i16 as i32) << 16; + lockin.update(s, phase, time_constant) }) // Decimate .last() - .unwrap(); + .unwrap() + * 2; // Full scale assuming the 2f component is gone. for value in dac_samples[1].iter_mut() { *value = (output.arg() >> 16) as u16 ^ 0x8000;