From 06efb9ad7059552a32e4c51425194ab6e8e280a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 7 Apr 2026 17:06:35 +0000 Subject: [PATCH 1/3] deps: bump --- Cargo.lock | 211 ++++++++++++++++++++++++----------------------------- Cargo.toml | 5 +- 2 files changed, 101 insertions(+), 115 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30f262a7b8..551ed3a9e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,9 +141,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.57" +version = "1.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" dependencies = [ "find-msvc-tools", "jobserver", @@ -205,38 +205,14 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", -] - [[package]] name = "darling" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "darling_core 0.23.0", - "darling_macro 0.23.0", -] - -[[package]] -name = "darling_core" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", + "darling_core", + "darling_macro", ] [[package]] @@ -252,24 +228,13 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core 0.21.3", - "quote", - "syn 2.0.117", -] - [[package]] name = "darling_macro" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core 0.23.0", + "darling_core", "quote", "syn 2.0.117", ] @@ -287,8 +252,8 @@ dependencies = [ [[package]] name = "dsp-fixedpoint" -version = "0.1.0" -source = "git+https://github.com/quartiq/idsp.git#d3f21570d8fd9a5047d527b355472c4689f4068d" +version = "0.1.1" +source = "git+https://github.com/quartiq/idsp.git#3fe244523f48bd5db76693cf585a060c43125aa0" dependencies = [ "num-traits", "serde", @@ -297,7 +262,7 @@ dependencies = [ [[package]] name = "dsp-process" version = "0.2.0" -source = "git+https://github.com/quartiq/idsp.git#d3f21570d8fd9a5047d527b355472c4689f4068d" +source = "git+https://github.com/quartiq/idsp.git#3fe244523f48bd5db76693cf585a060c43125aa0" [[package]] name = "dyn-clone" @@ -372,6 +337,12 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "embedded-io" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7" + [[package]] name = "embedded-nal" version = "0.9.0" @@ -434,12 +405,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -589,12 +554,13 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -602,9 +568,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -615,9 +581,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -629,15 +595,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -649,15 +615,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -698,7 +664,7 @@ dependencies = [ [[package]] name = "idsp" version = "0.21.0" -source = "git+https://github.com/quartiq/idsp.git#d3f21570d8fd9a5047d527b355472c4689f4068d" +source = "git+https://github.com/quartiq/idsp.git#3fe244523f48bd5db76693cf585a060c43125aa0" dependencies = [ "bytemuck", "dsp-fixedpoint", @@ -707,15 +673,15 @@ dependencies = [ "num-complex 0.4.6", "num-traits", "serde", - "strum", + "strum 0.27.2", "thiserror 2.0.18", ] [[package]] name = "indexmap" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" dependencies = [ "equivalent", "hashbrown", @@ -739,9 +705,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.183" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "libgit2-sys" @@ -763,9 +729,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libz-sys" -version = "1.1.25" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" +checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" dependencies = [ "cc", "libc", @@ -775,9 +741,9 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "lm75" @@ -833,15 +799,14 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb20200e7bbede2f1f10ad3aac9ae0f4755233795801dbb42a241c19713ec875" dependencies = [ - "embedded-io", + "embedded-io 0.6.1", "rustversion", ] [[package]] name = "miniconf" version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c2f61ae3c5e0361464f6ae2d5a68d95f8f0701004eab98812ee94c0d9ea180" +source = "git+https://github.com/quartiq/miniconf.git#10d20671239630815a18d1b5b06bad129a21d243" dependencies = [ "heapless 0.8.0", "heapless 0.9.2", @@ -859,10 +824,9 @@ dependencies = [ [[package]] name = "miniconf_derive" version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9826a0808d011e3217d93fd84ac670d75782d1f5962438b53a3f580f4dc14016" +source = "git+https://github.com/quartiq/miniconf.git#10d20671239630815a18d1b5b06bad129a21d243" dependencies = [ - "darling 0.21.3", + "darling", "proc-macro2", "quote", "syn 2.0.117", @@ -871,17 +835,15 @@ dependencies = [ [[package]] name = "miniconf_mqtt" version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72fed0fffafa2c18fd87abd2d9fff85843eb70dece96ebce5dbe74e4984cfca1" +source = "git+https://github.com/quartiq/miniconf.git#10d20671239630815a18d1b5b06bad129a21d243" dependencies = [ - "embedded-io", - "heapless 0.8.0", + "embedded-io 0.7.1", + "heapless 0.9.2", "log", "miniconf", "minimq", - "serde-json-core", "smlang", - "strum", + "strum 0.28.0", ] [[package]] @@ -1071,7 +1033,7 @@ dependencies = [ "cortex-m", "embassy-futures", "embedded-hal 0.2.7", - "embedded-io", + "embedded-io 0.6.1", "embedded-storage", "embedded-storage-async", "heapless 0.8.0", @@ -1105,9 +1067,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -1298,7 +1260,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.27", + "semver 1.0.28", ] [[package]] @@ -1342,9 +1304,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "semver-parser" @@ -1446,7 +1408,7 @@ version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ - "darling 0.23.0", + "darling", "proc-macro2", "quote", "syn 2.0.117", @@ -1456,7 +1418,7 @@ dependencies = [ name = "serial_settings" version = "0.2.0" dependencies = [ - "embedded-io", + "embedded-io 0.6.1", "heapless 0.8.0", "log", "menu", @@ -1614,7 +1576,7 @@ dependencies = [ "spin 0.10.0", "stm32h7xx-hal", "stream", - "strum", + "strum 0.28.0", "tca9539", "thiserror 2.0.18", "urukul", @@ -1692,7 +1654,16 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros", + "strum_macros 0.27.2", +] + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros 0.28.0", ] [[package]] @@ -1707,6 +1678,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "syn" version = "1.0.109" @@ -1805,9 +1788,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -1876,7 +1859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065e4eaf93db81d5adac82d9cef8f8da314cb640fa7f89534b972383f1cf80fc" dependencies = [ "embedded-hal 0.2.7", - "embedded-io", + "embedded-io 0.6.1", "nb 1.1.0", "usb-device", ] @@ -1937,9 +1920,9 @@ checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "yafnv" @@ -1952,9 +1935,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -1963,9 +1946,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -1975,18 +1958,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -1996,9 +1979,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -2007,9 +1990,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -2018,9 +2001,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index c4d13a67bb..15047dc9fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,7 @@ num-complex = { version = "0.4", features = [ "serde", ], default-features = false } num-traits = { version = "0.2.19", default-features = false } -strum = { version = "0.27.1", default-features = false, features = ["derive"] } +strum = { version = "0.28", default-features = false, features = ["derive"] } grounded = "0.2.0" signal_generator = { version = "0.1", path = "signal_generator" } stream = { version = "0.1", path = "stream" } @@ -176,3 +176,6 @@ lto = true idsp = { git = "https://github.com/quartiq/idsp.git" } dsp-process = { git = "https://github.com/quartiq/idsp.git" } dsp-fixedpoint = { git = "https://github.com/quartiq/idsp.git" } +miniconf = { git = "https://github.com/quartiq/miniconf.git" } +miniconf_mqtt = { git = "https://github.com/quartiq/miniconf.git" } +miniconf_derive = { git = "https://github.com/quartiq/miniconf.git" } From 50f86ee4f8c70db95d6c41e7d05a5f6dcca98b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 7 Apr 2026 23:49:29 +0000 Subject: [PATCH 2/3] miniconf: bump, split stabilizer_dsp crate --- Cargo.lock | 22 +++- Cargo.toml | 4 +- ad9959/src/lib.rs | 6 +- platform/src/settings.rs | 8 +- serial_settings/src/lib.rs | 98 ++++++++++------- signal_generator/src/lib.rs | 4 +- src/bin/dual-iir.rs | 113 +++---------------- src/bin/fls.rs | 3 +- src/bin/lockin.rs | 171 ++++++++--------------------- src/bin/mpll.rs | 3 +- src/hardware/pounder/dds_output.rs | 3 +- src/lib.rs | 4 - src/statistics.rs | 1 + 13 files changed, 156 insertions(+), 284 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 551ed3a9e9..48f144872b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -806,7 +806,7 @@ dependencies = [ [[package]] name = "miniconf" version = "0.20.1" -source = "git+https://github.com/quartiq/miniconf.git#10d20671239630815a18d1b5b06bad129a21d243" +source = "git+https://github.com/quartiq/miniconf.git#696455b8a58f4ff454fe0326bedd2446a20a2c9b" dependencies = [ "heapless 0.8.0", "heapless 0.9.2", @@ -824,7 +824,7 @@ dependencies = [ [[package]] name = "miniconf_derive" version = "0.20.0" -source = "git+https://github.com/quartiq/miniconf.git#10d20671239630815a18d1b5b06bad129a21d243" +source = "git+https://github.com/quartiq/miniconf.git#696455b8a58f4ff454fe0326bedd2446a20a2c9b" dependencies = [ "darling", "proc-macro2", @@ -835,7 +835,7 @@ dependencies = [ [[package]] name = "miniconf_mqtt" version = "0.20.0" -source = "git+https://github.com/quartiq/miniconf.git#10d20671239630815a18d1b5b06bad129a21d243" +source = "git+https://github.com/quartiq/miniconf.git#696455b8a58f4ff454fe0326bedd2446a20a2c9b" dependencies = [ "embedded-io 0.7.1", "heapless 0.9.2", @@ -1574,6 +1574,7 @@ dependencies = [ "signal_generator", "smoltcp-nal", "spin 0.10.0", + "stabilizer_dsp", "stm32h7xx-hal", "stream", "strum 0.28.0", @@ -1584,6 +1585,21 @@ dependencies = [ "usbd-serial", ] +[[package]] +name = "stabilizer_dsp" +version = "0.1.0" +dependencies = [ + "arbitrary-int", + "bytemuck", + "dsp-fixedpoint", + "dsp-process", + "idsp", + "miniconf", + "num-traits", + "serde", + "signal_generator", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index 15047dc9fa..b73f42a5e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "ad9912", "ad9959", "encoded_pin", + "stabilizer_dsp", "serial_settings", "urukul", "signal_generator", @@ -74,6 +75,7 @@ idsp.workspace = true dsp-process.workspace = true dsp-fixedpoint.workspace = true ad9959 = { path = "ad9959", version = "0.3.0" } +stabilizer_dsp = { version = "0.1.0", path = "stabilizer_dsp" } serial_settings = { version = "0.2", path = "serial_settings" } mcp230xx = "1.0" fugit = "0.3" @@ -163,7 +165,7 @@ opt-level = 3 [profile.release.package.stream] opt-level = 3 -[profile.release.package.stabilizer] +[profile.release.package.stabilizer_dsp] opt-level = 3 [profile.release] diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 1b9dddadc7..0235f7f899 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -517,7 +517,7 @@ impl ProfileSerializer { /// * `ftw` - If provided, indicates a frequency tuning word for the channels. /// * `pow` - If provided, indicates a phase offset word for the channels. /// * `acr` - If provided, indicates the amplitude control register for the channels. - #[inline] + #[inline(always)] pub fn push( &mut self, channels: Channel, @@ -545,7 +545,7 @@ impl ProfileSerializer { } /// Add a register write to the serialization data. - #[inline] + #[inline(always)] fn push_write(&mut self, register: Address, value: &[u8]) { let data = &mut self.data[self.index..]; data[0] = register as u8; @@ -561,7 +561,7 @@ impl ProfileSerializer { /// /// # Returns /// A slice of `u32` words representing the serialized profile. - #[inline] + #[inline(always)] pub fn finalize(&mut self) -> &[u32] { // Pad the buffer to 32-bit (4 byte) alignment by adding dummy writes to CSR and LSRR. // In the case of 1 byte padding, this instead pads with 5 bytes as there is no diff --git a/platform/src/settings.rs b/platform/src/settings.rs index 140f7253e5..8ad94feacf 100644 --- a/platform/src/settings.rs +++ b/platform/src/settings.rs @@ -83,7 +83,7 @@ where // Loop over flash and read settings let mut buffer = [0u8; 512]; for path in C::SCHEMA - .nodes::, '/'>, { serial_settings::MAX_DEPTH }>() + .nodes::>, { serial_settings::MAX_DEPTH }>() { let path = path.unwrap(); @@ -98,7 +98,7 @@ where Err(e) => { log::warn!( "Failed to fetch `{}` from flash: {e:?}", - path.0.as_str() + path.path.as_str() ); continue; } @@ -112,13 +112,13 @@ where continue; } - log::info!("Loading initial `{}` from flash", path.0.as_str()); + log::info!("Loading initial `{}` from flash", path.path.as_str()); let flavor = ::postcard::de_flavors::Slice::new(value); if let Err(e) = postcard::set_by_key(structure, &path, flavor) { log::warn!( "Failed to deserialize `{}` from flash: {e:?}", - path.0.as_str() + path.path.as_str() ); } } diff --git a/serial_settings/src/lib.rs b/serial_settings/src/lib.rs index 10b638556c..7e0ee37c82 100644 --- a/serial_settings/src/lib.rs +++ b/serial_settings/src/lib.rs @@ -11,6 +11,8 @@ use miniconf::{ mod interface; pub use interface::BestEffortInterface; +const SEPARATOR: char = '/'; + /// Specifies the API required for objects that are used as settings with the serial terminal /// interface. pub trait Settings: @@ -86,16 +88,14 @@ impl<'a, P: Platform> Interface<'a, P> { settings: &mut P::Settings, mut func: F, ) where - F: FnMut( - Path<&str, '/'>, - &mut Self, - &mut P::Settings, - &mut P::Settings, - ), + F: FnMut(Path<&str>, &mut Self, &mut P::Settings, &mut P::Settings), { let iter = if let Some(key) = key { - match NodeIter::with_root(P::Settings::SCHEMA, Path::<_, '/'>(key)) - { + match NodeIter::with_root( + P::Settings::SCHEMA, + Path::new(key, SEPARATOR), + SEPARATOR, + ) { Ok(it) => it, Err(e) => { writeln!(interface, "Failed to locate `{key}`: {e}") @@ -104,8 +104,11 @@ impl<'a, P: Platform> Interface<'a, P> { } } } else { - NodeIter::, '/'>, MAX_DEPTH>::new( + NodeIter::>, MAX_DEPTH>::new( P::Settings::SCHEMA, + Default::default(), + 0, + SEPARATOR, ) }; @@ -114,7 +117,7 @@ impl<'a, P: Platform> Interface<'a, P> { for key in iter { match key { Ok(key) => func( - Path(key.0.as_str()), + Path::new(key.path.as_str(), SEPARATOR), interface, settings, &mut defaults, @@ -153,15 +156,19 @@ impl<'a, P: Platform> Interface<'a, P> { return; } Err(e) => { - writeln!(interface, "Failed to get `{}`: {e}", key.0) - .unwrap(); + writeln!( + interface, + "Failed to get `{}`: {e}", + key.path + ) + .unwrap(); return; } Ok(len) => { write!( interface.platform.interface_mut(), "{}: {}", - key.0, + key.path, core::str::from_utf8(&interface.buffer[..len]) .unwrap() ) @@ -198,7 +205,7 @@ impl<'a, P: Platform> Interface<'a, P> { // Get stored and compare match interface .platform - .fetch(interface.buffer, key.0.as_bytes()) + .fetch(interface.buffer, key.path.as_bytes()) { Err(e) => write!(interface, " [fetch error: {e:?}]"), Ok(None) => write!(interface, " [not stored]"), @@ -267,8 +274,12 @@ impl<'a, P: Platform> Interface<'a, P> { return; } Err(e) => { - writeln!(interface, "Failed to get {}: {e:?}", key.0) - .unwrap(); + writeln!( + interface, + "Failed to get {}: {e:?}", + key.path + ) + .unwrap(); return; } Ok(slic) => yafnv::fnv1a::(slic), @@ -281,7 +292,7 @@ impl<'a, P: Platform> Interface<'a, P> { Err(SerdeError::Value(ValueError::Absent)) => { log::warn!( "Can't clear. Default is absent: `{}`", - key.0 + key.path ); None } @@ -289,7 +300,7 @@ impl<'a, P: Platform> Interface<'a, P> { writeln!( interface, "Failed to get default `{}`: {e}", - key.0 + key.path ) .unwrap(); return; @@ -314,15 +325,19 @@ impl<'a, P: Platform> Interface<'a, P> { writeln!( interface, "Failed to set {}: {e:?}", - key.0 + key.path ) .unwrap(); return; } Ok(_rest) => { interface.updated = true; - writeln!(interface, "Cleared current `{}`", key.0) - .unwrap() + writeln!( + interface, + "Cleared current `{}`", + key.path + ) + .unwrap() } } } @@ -330,13 +345,13 @@ impl<'a, P: Platform> Interface<'a, P> { // Check for stored match interface .platform - .fetch(interface.buffer, key.0.as_bytes()) + .fetch(interface.buffer, key.path.as_bytes()) { Err(e) => { writeln!( interface, "Failed to fetch `{}`: {e:?}", - key.0 + key.path ) .unwrap(); } @@ -344,16 +359,16 @@ impl<'a, P: Platform> Interface<'a, P> { // Clear stored Ok(Some(_stored)) => match interface .platform - .clear(interface.buffer, key.0.as_bytes()) + .clear(interface.buffer, key.path.as_bytes()) { Ok(()) => { - writeln!(interface, "Clear stored `{}`", key.0) + writeln!(interface, "Clear stored `{}`", key.path) } Err(e) => { writeln!( interface, "Failed to clear `{}` from storage: {e:?}", - key.0 + key.path ) } } @@ -390,14 +405,14 @@ impl<'a, P: Platform> Interface<'a, P> { // Could also serialize directly into the hasher for all these checksum calcs Ok(slic) => yafnv::fnv1a::(slic), Err(SerdeError::Value(ValueError::Absent)) => { - log::warn!("Default absent: `{}`", key.0); + log::warn!("Default absent: `{}`", key.path); return; } Err(e) => { writeln!( interface, "Failed to get `{}` default: {e:?}", - key.0 + key.path ) .unwrap(); return; @@ -407,7 +422,7 @@ impl<'a, P: Platform> Interface<'a, P> { // Get stored value checksum match interface .platform - .fetch(interface.buffer, key.0.as_bytes()) + .fetch(interface.buffer, key.path.as_bytes()) { Ok(None) => {} Ok(Some(stored)) => { @@ -415,10 +430,13 @@ impl<'a, P: Platform> Interface<'a, P> { if stored != check { log::debug!( "Stored differs from default: `{}`", - key.0 + key.path ); } else { - log::debug!("Stored matches default: `{}`", key.0); + log::debug!( + "Stored matches default: `{}`", + key.path + ); } check = stored; } @@ -426,7 +444,7 @@ impl<'a, P: Platform> Interface<'a, P> { writeln!( interface, "Failed to fetch `{}`: {e:?}", - key.0 + key.path ) .unwrap(); } @@ -441,8 +459,12 @@ impl<'a, P: Platform> Interface<'a, P> { return; } Err(e) => { - writeln!(interface, "Could not get `{}`: {e}", key.0) - .unwrap(); + writeln!( + interface, + "Could not get `{}`: {e}", + key.path + ) + .unwrap(); return; } }; @@ -451,7 +473,7 @@ impl<'a, P: Platform> Interface<'a, P> { if yafnv::fnv1a::(value) == check && !force { log::debug!( "Not saving matching default/stored `{}`", - key.0 + key.path ); return; } @@ -459,13 +481,13 @@ impl<'a, P: Platform> Interface<'a, P> { let (value, rest) = interface.buffer.split_at_mut(len); // Store - match interface.platform.store(rest, key.0.as_bytes(), value) { - Ok(_) => writeln!(interface, "`{}` stored", key.0), + match interface.platform.store(rest, key.path.as_bytes(), value) { + Ok(_) => writeln!(interface, "`{}` stored", key.path), Err(e) => { writeln!( interface, "Failed to store `{}`: {e:?}", - key.0 + key.path ) } } diff --git a/signal_generator/src/lib.rs b/signal_generator/src/lib.rs index 00746f36cc..4600e5b695 100644 --- a/signal_generator/src/lib.rs +++ b/signal_generator/src/lib.rs @@ -19,7 +19,7 @@ pub enum Signal { } impl Signal { - #[inline] + #[inline(always)] fn map(&self, x: i32) -> i32 { match self { Self::Cosine => idsp::cossin(x).0, @@ -159,7 +159,7 @@ pub enum Source { impl Iterator for Source { type Item = i32; - #[inline] + #[inline(always)] fn next(&mut self) -> Option { let (s, a) = match self { Self::SweptSine { sweep, amp } => { diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index 394daec313..a330ee5174 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -30,32 +30,14 @@ use miniconf::Tree; -use dsp_process::SplitProcess; -use idsp::iir::{self, pid::Units}; - use platform::{AppSettings, NetSettings}; -use serde::{Deserialize, Serialize}; -use signal_generator::{self, Source}; use stabilizer::convert::{AdcCode, DacCode, Gain}; +use stabilizer_dsp::dual_iir::{self, Active, BiquadRepr, Run}; -// The number of cascaded IIR biquads per channel. Select 1 or 2! -const IIR_CASCADE_LENGTH: usize = 1; - -// The number of samples in each batch process -const BATCH_SIZE: usize = 8; - -// The logarithm of the number of 100MHz timer ticks between each sample. With a value of 2^7 = -// 128, there is 1.28uS per sample, corresponding to a sampling frequency of 781.25 KHz. -const SAMPLE_TICKS_LOG2: u8 = 7; -const SAMPLE_TICKS: u32 = 1 << SAMPLE_TICKS_LOG2; -const SAMPLE_PERIOD: f32 = - SAMPLE_TICKS as f32 * stabilizer::design_parameters::TIMER_PERIOD; - -const UNITS: Units = Units { - t: SAMPLE_PERIOD, - x: AdcCode::VOLT_PER_LSB, - y: DacCode::VOLT_PER_LSB, -}; +const BATCH_SIZE: usize = dual_iir::BATCH_SIZE; +const SAMPLE_TICKS: u32 = dual_iir::SAMPLE_TICKS; +const SAMPLE_PERIOD: f32 = dual_iir::SAMPLE_PERIOD; +const IIR_CASCADE_LENGTH: usize = dual_iir::IIR_CASCADE_LENGTH; #[derive(Clone, Debug, Tree, Default)] #[tree(meta(doc, typename))] @@ -86,48 +68,6 @@ impl serial_settings::Settings for Settings { } } -#[derive(Clone, Debug, Tree)] -#[tree(meta(doc, typename = "BiquadReprTree"))] -pub struct BiquadRepr { - /// Biquad parameters - #[tree(rename="typ", typ="&str", with=miniconf::str_leaf, defer=self.repr)] - _typ: (), - repr: iir::repr::BiquadRepr, -} - -impl Default for BiquadRepr { - fn default() -> Self { - let mut i = iir::BiquadClamp::from(iir::Biquad::IDENTITY); - i.min = -i16::MAX as _; - i.max = i16::MAX as _; - Self { - _typ: (), - repr: iir::repr::BiquadRepr::Raw(i), - } - } -} - -#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)] -pub enum Run { - #[default] - /// Run - Run, - /// Hold - Hold, - /// Hold controlled by corresponding digital input - External, -} - -impl Run { - fn run(&self, di: bool) -> bool { - match self { - Self::Run => true, - Self::Hold => false, - Self::External => di, - } - } -} - /// A ADC-DAC channel #[derive(Clone, Debug, Tree, Default)] #[tree(meta(doc, typename))] @@ -149,14 +89,14 @@ impl Channel { Ok(Active { source: self .source - .build(SAMPLE_PERIOD, DacCode::FULL_SCALE.recip()) + .build(SAMPLE_PERIOD, dual_iir::DAC_FULL_SCALE.recip()) .unwrap(), state: Default::default(), run: self.run, biquad: self .biquad .each_ref() - .map(|biquad| biquad.repr.build(&UNITS)), + .map(|biquad| biquad.repr.build(&dual_iir::UNITS)), }) } } @@ -190,14 +130,6 @@ impl Default for DualIir { } } -#[derive(Clone, Debug)] -pub struct Active { - run: Run, - biquad: [iir::BiquadClamp; IIR_CASCADE_LENGTH], - state: [iir::DirectForm1; IIR_CASCADE_LENGTH], - source: Source, -} - #[cfg(not(target_os = "none"))] fn main() { use miniconf::{json::to_json_value, json_schema::TreeJsonSchema}; @@ -381,27 +313,7 @@ mod app { .zip(telemetry.digital_inputs) .zip(source.iter()) { - for ((adc, dac), source) in - adc.iter().zip(dac.iter_mut()).zip(source) - { - let x = f32::from(*adc as i16); - let y = active - .biquad - .iter() - .zip(active.state.iter_mut()) - .fold(x, |y, (ch, state)| { - if active.run.run(di) { - ch.process(state, y) - } else { - iir::Biquad::::HOLD.process(state, y) - } - }); - - // Note(unsafe): The filter limits must ensure that the value is in range. - // The truncation introduces 1/2 LSB distortion. - let y: i16 = unsafe { y.to_int_unchecked() }; - *dac = DacCode::from(y.saturating_add(*source)).0; - } + active.process(di, adc, dac, source); } telemetry.adcs = [AdcCode(adc[0][0]), AdcCode(adc[1][0])]; telemetry.dacs = [DacCode(dac[0][0]), DacCode(dac[1][0])]; @@ -462,7 +374,7 @@ mod app { let s = settings.dual_iir.ch.each_ref().map(|ch| { let s = ch .source - .build(SAMPLE_PERIOD, DacCode::FULL_SCALE.recip()); + .build(SAMPLE_PERIOD, dual_iir::DAC_FULL_SCALE.recip()); if let Err(err) = &s { log::error!("Failed to update source: {:?}", err); } @@ -477,7 +389,12 @@ mod app { }); } let b = settings.dual_iir.ch.each_ref().map(|ch| { - (ch.run, ch.biquad.each_ref().map(|b| b.repr.build(&UNITS))) + ( + ch.run, + ch.biquad + .each_ref() + .map(|b| b.repr.build(&dual_iir::UNITS)), + ) }); c.shared.active.lock(|active| { for (a, b) in active.iter_mut().zip(b) { diff --git a/src/bin/fls.rs b/src/bin/fls.rs index 70f1832500..d16efb8b7f 100644 --- a/src/bin/fls.rs +++ b/src/bin/fls.rs @@ -89,7 +89,8 @@ use serde::Serialize; use ad9959::Acr; use platform::{AppSettings, NetSettings}; -use stabilizer::{convert::Gain, fls, statistics}; +use stabilizer::{convert::Gain, statistics}; +use stabilizer_dsp::fls; #[derive(Clone, Debug, Tree, Default)] #[tree(meta(doc, typename))] diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index d053b720e8..62524f6ba6 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -29,32 +29,24 @@ use core::{ iter, - mem::MaybeUninit, num::Wrapping, sync::atomic::{Ordering, fence}, }; -use dsp_process::SplitProcess; use fugit::ExtU32; -use idsp::{Accu, Complex, Lowpass, RPLL, RPLLConfig}; +use idsp::{Lowpass, RPLL, RPLLConfig}; use miniconf::{Leaf, Tree}; use rtic_monotonics::Monotonic; -use serde::{Deserialize, Serialize}; use stabilizer::convert::{AdcCode, DacCode, Gain}; +use stabilizer_dsp::lockin::{self, ActiveSettings, Conf, LockinMode}; use platform::{AppSettings, NetSettings}; -// The logarithm of the number of samples in each batch process. This corresponds with 2^3 samples -// per batch = 8 samples -const BATCH_SIZE_LOG2: u32 = 3; -const BATCH_SIZE: usize = 1 << BATCH_SIZE_LOG2; - -// The logarithm of the number of 100MHz timer ticks between each sample. This corresponds with a -// sampling period of 2^7 = 128 ticks. At 100MHz, 10ns per tick, this corresponds to a sampling -// period of 1.28 uS or 781.25 KHz. -const SAMPLE_TICKS_LOG2: u32 = 7; -const SAMPLE_TICKS: u32 = 1 << SAMPLE_TICKS_LOG2; +const BATCH_SIZE_LOG2: u32 = lockin::BATCH_SIZE_LOG2; +const BATCH_SIZE: usize = lockin::BATCH_SIZE; +const SAMPLE_TICKS_LOG2: u32 = lockin::SAMPLE_TICKS_LOG2; +const SAMPLE_TICKS: u32 = lockin::SAMPLE_TICKS; #[derive(Clone, Debug, Tree, Default)] #[tree(meta(doc, typename))] @@ -85,32 +77,6 @@ impl serial_settings::Settings for Settings { } } -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] -enum Conf { - /// Output the lockin magnitude. - Magnitude, - /// Output the phase of the lockin - Phase, - /// Output the lockin reference frequency as a sinusoid - ReferenceFrequency, - /// Output the logarithmic power of the lockin - LogPower, - /// Output the in-phase component of the lockin signal. - InPhase, - /// Output the quadrature component of the lockin signal. - Quadrature, - /// Output the lockin internal modulation frequency as a sinusoid - Modulation, -} - -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -enum LockinMode { - /// Utilize an internally generated reference for demodulation - Internal, - /// Utilize an external modulation signal supplied to DI0 - External, -} - #[derive(Clone, Debug, Tree)] #[tree(meta(doc, typename))] pub struct Lockin { @@ -181,6 +147,19 @@ impl Default for Lockin { } } +impl Lockin { + fn build(&self) -> ActiveSettings { + ActiveSettings { + lockin_mode: self.lockin_mode, + pll_tc: self.pll_tc.clone(), + lockin_k: self.lockin_k.clone(), + lockin_harmonic: self.lockin_harmonic, + lockin_phase: self.lockin_phase, + output_conf: self.output_conf.map(|conf| conf.0), + } + } +} + #[cfg(not(target_os = "none"))] fn main() { use miniconf::{json::to_json_value, json_schema::TreeJsonSchema}; @@ -200,7 +179,6 @@ fn main() { #[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, dispatchers=[DCMI, JPEG, SDMMC])] mod app { use super::*; - use core::num::Wrapping; use idsp::LowpassState; use stabilizer::{ hardware::{ @@ -222,7 +200,7 @@ mod app { usb: UsbDevice, network: NetworkUsers, settings: Settings, - active_settings: Lockin, + active_settings: ActiveSettings, telemetry: TelemetryBuffer, } @@ -271,7 +249,7 @@ mod app { network, usb: stabilizer.usb, telemetry: TelemetryBuffer::default(), - active_settings: stabilizer.settings.lockin.clone(), + active_settings: stabilizer.settings.lockin.build(), settings: stabilizer.settings, }; @@ -351,100 +329,37 @@ mod app { } = c.local; (active_settings, telemetry).lock(|settings, telemetry| { - let (reference_phase, reference_frequency) = match settings - .lockin_mode - { - LockinMode::External => { - let timestamp = - timestamper.latest_timestamp().unwrap_or(None); // Ignore data from timer capture overflows. - let accu = settings - .pll_tc - .process(pll, timestamp.map(|t| Wrapping(t as i32))); - (accu.state, (accu.step >> BATCH_SIZE_LOG2 as usize)) - } - LockinMode::Internal => { - // Reference phase and frequency are known. - ( - Wrapping(1i32 << 30), - Wrapping(1i32 << (32 - BATCH_SIZE_LOG2)), - ) - } - }; - - let sample_frequency = - reference_frequency * settings.lockin_harmonic; - let sample_phase = settings.lockin_phase - + reference_phase * settings.lockin_harmonic; - (adc0, adc1, dac0, dac1).lock(|adc0, adc1, dac0, dac1| { - let adc_samples = [adc0, adc1]; - let mut dac_samples = [dac0, dac1]; - // Preserve instruction and data ordering w.r.t. DMA flag access. fence(Ordering::SeqCst); - 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, update the Lockin (demodulate, filter) - .map(|(&sample, phase)| { - let s = (sample as i16 as i32) << 16; - settings.lockin_k.process(lockin, (s, phase)) - }) - // Decimate - .last() - .unwrap() - * 2; // Full scale assuming the 2f component is gone. - - // Convert to DAC data. - for (channel, samples) in dac_samples.iter_mut().enumerate() { - for sample in samples.iter_mut() { - let value = match *settings.output_conf[channel] { - Conf::Magnitude => { - output.norm_sqr().inner as i32 >> 16 - } - Conf::Phase => output.arg().0 >> 16, - Conf::LogPower => output.log2() << 8, - Conf::ReferenceFrequency => { - reference_frequency.0 >> 16 - } - Conf::InPhase => output.re() >> 16, - Conf::Quadrature => output.im() >> 16, - - Conf::Modulation => source.next().unwrap().re(), - }; - - *sample = DacCode::from(value as i16).0; - } - } + let adc_samples: [&[u16; BATCH_SIZE]; 2] = [ + (**adc0).try_into().unwrap(), + (**adc1).try_into().unwrap(), + ]; + let mut dac_samples: [&mut [u16; BATCH_SIZE]; 2] = + [(*dac0).try_into().unwrap(), (*dac1).try_into().unwrap()]; + let telemetry_update = settings.process( + pll, + lockin, + source, + timestamper.latest_timestamp().unwrap_or(None), + adc_samples, + &mut dac_samples, + ); // Stream the data. - const N: usize = BATCH_SIZE * size_of::() - / size_of::>(); generator.add(|buf| { - for (data, buf) in adc_samples - .iter() - .chain(dac_samples.iter()) - .zip(buf.chunks_exact_mut(N)) - { - let data = unsafe { - core::slice::from_raw_parts( - data.as_ptr() as *const MaybeUninit, - N, - ) - }; - buf.copy_from_slice(data) - } - N * 4 + lockin::ActiveSettings::stream( + adc_samples, + [dac_samples[0], dac_samples[1]], + buf, + ) }); // Update telemetry measurements. - telemetry.adcs = - [AdcCode(adc_samples[0][0]), AdcCode(adc_samples[1][0])]; - - telemetry.dacs = - [DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])]; + telemetry.adcs = telemetry_update.adcs.map(AdcCode); + telemetry.dacs = telemetry_update.dacs.map(DacCode); // Preserve instruction and data ordering w.r.t. DMA flag access. fence(Ordering::SeqCst); @@ -487,7 +402,7 @@ mod app { c.shared .active_settings - .lock(|current| *current = settings.lockin.clone()); + .lock(|current| *current = settings.lockin.build()); }); } diff --git a/src/bin/mpll.rs b/src/bin/mpll.rs index 7880ff6fa3..f3512720a9 100644 --- a/src/bin/mpll.rs +++ b/src/bin/mpll.rs @@ -8,7 +8,8 @@ use miniconf::{Leaf, Tree}; use rtic_monotonics::Monotonic; use serde::Serialize; -use stabilizer::{convert::Gain, mpll::*, statistics}; +use stabilizer::{convert::Gain, statistics}; +use stabilizer_dsp::mpll::*; use platform::{AppSettings, NetSettings}; diff --git a/src/hardware/pounder/dds_output.rs b/src/hardware/pounder/dds_output.rs index 89247e5502..fc15b63277 100644 --- a/src/hardware/pounder/dds_output.rs +++ b/src/hardware/pounder/dds_output.rs @@ -90,6 +90,7 @@ impl DdsOutput { } /// Get a builder for serializing a Pounder DDS profile. + #[inline(always)] pub fn builder(&mut self) -> ProfileSerializer { ProfileSerializer::new(self.mode) } @@ -103,7 +104,7 @@ impl DdsOutput { /// /// # Args /// * `profile` - The serialized DDS profile to write. - #[inline] + #[inline(always)] pub fn write(&mut self, mut profile: ProfileSerializer) { // Note(unsafe): We own the QSPI interface, so it is safe to access the registers in a raw // fashion. diff --git a/src/lib.rs b/src/lib.rs index dc39103fbd..db32d99b09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,3 @@ pub mod telemetry; pub mod convert; pub mod statistics; - -pub mod mpll; - -pub mod fls; diff --git a/src/statistics.rs b/src/statistics.rs index f40a195262..09fe7a1db3 100644 --- a/src/statistics.rs +++ b/src/statistics.rs @@ -31,6 +31,7 @@ impl Default for State { } impl State { + #[inline(always)] pub fn update(&mut self, x: i32) { if self.count == 0 { self.x0 = x; From c82e16a2f743290a12e2fe6e8fed74f60fcb7588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 8 Apr 2026 00:28:25 +0000 Subject: [PATCH 3/3] undo dsp split --- Cargo.lock | 16 --- Cargo.toml | 4 +- ad9959/src/lib.rs | 6 +- signal_generator/src/lib.rs | 4 +- src/bin/dual-iir.rs | 113 ++++++++++++++++--- src/bin/fls.rs | 3 +- src/bin/lockin.rs | 171 +++++++++++++++++++++-------- src/bin/mpll.rs | 3 +- src/hardware/pounder/dds_output.rs | 3 +- src/lib.rs | 4 + src/statistics.rs | 1 - 11 files changed, 239 insertions(+), 89 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 48f144872b..1b89c2d5fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1574,7 +1574,6 @@ dependencies = [ "signal_generator", "smoltcp-nal", "spin 0.10.0", - "stabilizer_dsp", "stm32h7xx-hal", "stream", "strum 0.28.0", @@ -1585,21 +1584,6 @@ dependencies = [ "usbd-serial", ] -[[package]] -name = "stabilizer_dsp" -version = "0.1.0" -dependencies = [ - "arbitrary-int", - "bytemuck", - "dsp-fixedpoint", - "dsp-process", - "idsp", - "miniconf", - "num-traits", - "serde", - "signal_generator", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index b73f42a5e3..15047dc9fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,6 @@ members = [ "ad9912", "ad9959", "encoded_pin", - "stabilizer_dsp", "serial_settings", "urukul", "signal_generator", @@ -75,7 +74,6 @@ idsp.workspace = true dsp-process.workspace = true dsp-fixedpoint.workspace = true ad9959 = { path = "ad9959", version = "0.3.0" } -stabilizer_dsp = { version = "0.1.0", path = "stabilizer_dsp" } serial_settings = { version = "0.2", path = "serial_settings" } mcp230xx = "1.0" fugit = "0.3" @@ -165,7 +163,7 @@ opt-level = 3 [profile.release.package.stream] opt-level = 3 -[profile.release.package.stabilizer_dsp] +[profile.release.package.stabilizer] opt-level = 3 [profile.release] diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 0235f7f899..1b9dddadc7 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -517,7 +517,7 @@ impl ProfileSerializer { /// * `ftw` - If provided, indicates a frequency tuning word for the channels. /// * `pow` - If provided, indicates a phase offset word for the channels. /// * `acr` - If provided, indicates the amplitude control register for the channels. - #[inline(always)] + #[inline] pub fn push( &mut self, channels: Channel, @@ -545,7 +545,7 @@ impl ProfileSerializer { } /// Add a register write to the serialization data. - #[inline(always)] + #[inline] fn push_write(&mut self, register: Address, value: &[u8]) { let data = &mut self.data[self.index..]; data[0] = register as u8; @@ -561,7 +561,7 @@ impl ProfileSerializer { /// /// # Returns /// A slice of `u32` words representing the serialized profile. - #[inline(always)] + #[inline] pub fn finalize(&mut self) -> &[u32] { // Pad the buffer to 32-bit (4 byte) alignment by adding dummy writes to CSR and LSRR. // In the case of 1 byte padding, this instead pads with 5 bytes as there is no diff --git a/signal_generator/src/lib.rs b/signal_generator/src/lib.rs index 4600e5b695..00746f36cc 100644 --- a/signal_generator/src/lib.rs +++ b/signal_generator/src/lib.rs @@ -19,7 +19,7 @@ pub enum Signal { } impl Signal { - #[inline(always)] + #[inline] fn map(&self, x: i32) -> i32 { match self { Self::Cosine => idsp::cossin(x).0, @@ -159,7 +159,7 @@ pub enum Source { impl Iterator for Source { type Item = i32; - #[inline(always)] + #[inline] fn next(&mut self) -> Option { let (s, a) = match self { Self::SweptSine { sweep, amp } => { diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index a330ee5174..394daec313 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -30,14 +30,32 @@ use miniconf::Tree; +use dsp_process::SplitProcess; +use idsp::iir::{self, pid::Units}; + use platform::{AppSettings, NetSettings}; +use serde::{Deserialize, Serialize}; +use signal_generator::{self, Source}; use stabilizer::convert::{AdcCode, DacCode, Gain}; -use stabilizer_dsp::dual_iir::{self, Active, BiquadRepr, Run}; -const BATCH_SIZE: usize = dual_iir::BATCH_SIZE; -const SAMPLE_TICKS: u32 = dual_iir::SAMPLE_TICKS; -const SAMPLE_PERIOD: f32 = dual_iir::SAMPLE_PERIOD; -const IIR_CASCADE_LENGTH: usize = dual_iir::IIR_CASCADE_LENGTH; +// The number of cascaded IIR biquads per channel. Select 1 or 2! +const IIR_CASCADE_LENGTH: usize = 1; + +// The number of samples in each batch process +const BATCH_SIZE: usize = 8; + +// The logarithm of the number of 100MHz timer ticks between each sample. With a value of 2^7 = +// 128, there is 1.28uS per sample, corresponding to a sampling frequency of 781.25 KHz. +const SAMPLE_TICKS_LOG2: u8 = 7; +const SAMPLE_TICKS: u32 = 1 << SAMPLE_TICKS_LOG2; +const SAMPLE_PERIOD: f32 = + SAMPLE_TICKS as f32 * stabilizer::design_parameters::TIMER_PERIOD; + +const UNITS: Units = Units { + t: SAMPLE_PERIOD, + x: AdcCode::VOLT_PER_LSB, + y: DacCode::VOLT_PER_LSB, +}; #[derive(Clone, Debug, Tree, Default)] #[tree(meta(doc, typename))] @@ -68,6 +86,48 @@ impl serial_settings::Settings for Settings { } } +#[derive(Clone, Debug, Tree)] +#[tree(meta(doc, typename = "BiquadReprTree"))] +pub struct BiquadRepr { + /// Biquad parameters + #[tree(rename="typ", typ="&str", with=miniconf::str_leaf, defer=self.repr)] + _typ: (), + repr: iir::repr::BiquadRepr, +} + +impl Default for BiquadRepr { + fn default() -> Self { + let mut i = iir::BiquadClamp::from(iir::Biquad::IDENTITY); + i.min = -i16::MAX as _; + i.max = i16::MAX as _; + Self { + _typ: (), + repr: iir::repr::BiquadRepr::Raw(i), + } + } +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)] +pub enum Run { + #[default] + /// Run + Run, + /// Hold + Hold, + /// Hold controlled by corresponding digital input + External, +} + +impl Run { + fn run(&self, di: bool) -> bool { + match self { + Self::Run => true, + Self::Hold => false, + Self::External => di, + } + } +} + /// A ADC-DAC channel #[derive(Clone, Debug, Tree, Default)] #[tree(meta(doc, typename))] @@ -89,14 +149,14 @@ impl Channel { Ok(Active { source: self .source - .build(SAMPLE_PERIOD, dual_iir::DAC_FULL_SCALE.recip()) + .build(SAMPLE_PERIOD, DacCode::FULL_SCALE.recip()) .unwrap(), state: Default::default(), run: self.run, biquad: self .biquad .each_ref() - .map(|biquad| biquad.repr.build(&dual_iir::UNITS)), + .map(|biquad| biquad.repr.build(&UNITS)), }) } } @@ -130,6 +190,14 @@ impl Default for DualIir { } } +#[derive(Clone, Debug)] +pub struct Active { + run: Run, + biquad: [iir::BiquadClamp; IIR_CASCADE_LENGTH], + state: [iir::DirectForm1; IIR_CASCADE_LENGTH], + source: Source, +} + #[cfg(not(target_os = "none"))] fn main() { use miniconf::{json::to_json_value, json_schema::TreeJsonSchema}; @@ -313,7 +381,27 @@ mod app { .zip(telemetry.digital_inputs) .zip(source.iter()) { - active.process(di, adc, dac, source); + for ((adc, dac), source) in + adc.iter().zip(dac.iter_mut()).zip(source) + { + let x = f32::from(*adc as i16); + let y = active + .biquad + .iter() + .zip(active.state.iter_mut()) + .fold(x, |y, (ch, state)| { + if active.run.run(di) { + ch.process(state, y) + } else { + iir::Biquad::::HOLD.process(state, y) + } + }); + + // Note(unsafe): The filter limits must ensure that the value is in range. + // The truncation introduces 1/2 LSB distortion. + let y: i16 = unsafe { y.to_int_unchecked() }; + *dac = DacCode::from(y.saturating_add(*source)).0; + } } telemetry.adcs = [AdcCode(adc[0][0]), AdcCode(adc[1][0])]; telemetry.dacs = [DacCode(dac[0][0]), DacCode(dac[1][0])]; @@ -374,7 +462,7 @@ mod app { let s = settings.dual_iir.ch.each_ref().map(|ch| { let s = ch .source - .build(SAMPLE_PERIOD, dual_iir::DAC_FULL_SCALE.recip()); + .build(SAMPLE_PERIOD, DacCode::FULL_SCALE.recip()); if let Err(err) = &s { log::error!("Failed to update source: {:?}", err); } @@ -389,12 +477,7 @@ mod app { }); } let b = settings.dual_iir.ch.each_ref().map(|ch| { - ( - ch.run, - ch.biquad - .each_ref() - .map(|b| b.repr.build(&dual_iir::UNITS)), - ) + (ch.run, ch.biquad.each_ref().map(|b| b.repr.build(&UNITS))) }); c.shared.active.lock(|active| { for (a, b) in active.iter_mut().zip(b) { diff --git a/src/bin/fls.rs b/src/bin/fls.rs index d16efb8b7f..70f1832500 100644 --- a/src/bin/fls.rs +++ b/src/bin/fls.rs @@ -89,8 +89,7 @@ use serde::Serialize; use ad9959::Acr; use platform::{AppSettings, NetSettings}; -use stabilizer::{convert::Gain, statistics}; -use stabilizer_dsp::fls; +use stabilizer::{convert::Gain, fls, statistics}; #[derive(Clone, Debug, Tree, Default)] #[tree(meta(doc, typename))] diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index 62524f6ba6..d053b720e8 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -29,24 +29,32 @@ use core::{ iter, + mem::MaybeUninit, num::Wrapping, sync::atomic::{Ordering, fence}, }; +use dsp_process::SplitProcess; use fugit::ExtU32; -use idsp::{Lowpass, RPLL, RPLLConfig}; +use idsp::{Accu, Complex, Lowpass, RPLL, RPLLConfig}; use miniconf::{Leaf, Tree}; use rtic_monotonics::Monotonic; +use serde::{Deserialize, Serialize}; use stabilizer::convert::{AdcCode, DacCode, Gain}; -use stabilizer_dsp::lockin::{self, ActiveSettings, Conf, LockinMode}; use platform::{AppSettings, NetSettings}; -const BATCH_SIZE_LOG2: u32 = lockin::BATCH_SIZE_LOG2; -const BATCH_SIZE: usize = lockin::BATCH_SIZE; -const SAMPLE_TICKS_LOG2: u32 = lockin::SAMPLE_TICKS_LOG2; -const SAMPLE_TICKS: u32 = lockin::SAMPLE_TICKS; +// The logarithm of the number of samples in each batch process. This corresponds with 2^3 samples +// per batch = 8 samples +const BATCH_SIZE_LOG2: u32 = 3; +const BATCH_SIZE: usize = 1 << BATCH_SIZE_LOG2; + +// The logarithm of the number of 100MHz timer ticks between each sample. This corresponds with a +// sampling period of 2^7 = 128 ticks. At 100MHz, 10ns per tick, this corresponds to a sampling +// period of 1.28 uS or 781.25 KHz. +const SAMPLE_TICKS_LOG2: u32 = 7; +const SAMPLE_TICKS: u32 = 1 << SAMPLE_TICKS_LOG2; #[derive(Clone, Debug, Tree, Default)] #[tree(meta(doc, typename))] @@ -77,6 +85,32 @@ impl serial_settings::Settings for Settings { } } +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +enum Conf { + /// Output the lockin magnitude. + Magnitude, + /// Output the phase of the lockin + Phase, + /// Output the lockin reference frequency as a sinusoid + ReferenceFrequency, + /// Output the logarithmic power of the lockin + LogPower, + /// Output the in-phase component of the lockin signal. + InPhase, + /// Output the quadrature component of the lockin signal. + Quadrature, + /// Output the lockin internal modulation frequency as a sinusoid + Modulation, +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +enum LockinMode { + /// Utilize an internally generated reference for demodulation + Internal, + /// Utilize an external modulation signal supplied to DI0 + External, +} + #[derive(Clone, Debug, Tree)] #[tree(meta(doc, typename))] pub struct Lockin { @@ -147,19 +181,6 @@ impl Default for Lockin { } } -impl Lockin { - fn build(&self) -> ActiveSettings { - ActiveSettings { - lockin_mode: self.lockin_mode, - pll_tc: self.pll_tc.clone(), - lockin_k: self.lockin_k.clone(), - lockin_harmonic: self.lockin_harmonic, - lockin_phase: self.lockin_phase, - output_conf: self.output_conf.map(|conf| conf.0), - } - } -} - #[cfg(not(target_os = "none"))] fn main() { use miniconf::{json::to_json_value, json_schema::TreeJsonSchema}; @@ -179,6 +200,7 @@ fn main() { #[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, dispatchers=[DCMI, JPEG, SDMMC])] mod app { use super::*; + use core::num::Wrapping; use idsp::LowpassState; use stabilizer::{ hardware::{ @@ -200,7 +222,7 @@ mod app { usb: UsbDevice, network: NetworkUsers, settings: Settings, - active_settings: ActiveSettings, + active_settings: Lockin, telemetry: TelemetryBuffer, } @@ -249,7 +271,7 @@ mod app { network, usb: stabilizer.usb, telemetry: TelemetryBuffer::default(), - active_settings: stabilizer.settings.lockin.build(), + active_settings: stabilizer.settings.lockin.clone(), settings: stabilizer.settings, }; @@ -329,37 +351,100 @@ mod app { } = c.local; (active_settings, telemetry).lock(|settings, telemetry| { + let (reference_phase, reference_frequency) = match settings + .lockin_mode + { + LockinMode::External => { + let timestamp = + timestamper.latest_timestamp().unwrap_or(None); // Ignore data from timer capture overflows. + let accu = settings + .pll_tc + .process(pll, timestamp.map(|t| Wrapping(t as i32))); + (accu.state, (accu.step >> BATCH_SIZE_LOG2 as usize)) + } + LockinMode::Internal => { + // Reference phase and frequency are known. + ( + Wrapping(1i32 << 30), + Wrapping(1i32 << (32 - BATCH_SIZE_LOG2)), + ) + } + }; + + let sample_frequency = + reference_frequency * settings.lockin_harmonic; + let sample_phase = settings.lockin_phase + + reference_phase * settings.lockin_harmonic; + (adc0, adc1, dac0, dac1).lock(|adc0, adc1, dac0, dac1| { + let adc_samples = [adc0, adc1]; + let mut dac_samples = [dac0, dac1]; + // Preserve instruction and data ordering w.r.t. DMA flag access. fence(Ordering::SeqCst); - let adc_samples: [&[u16; BATCH_SIZE]; 2] = [ - (**adc0).try_into().unwrap(), - (**adc1).try_into().unwrap(), - ]; - let mut dac_samples: [&mut [u16; BATCH_SIZE]; 2] = - [(*dac0).try_into().unwrap(), (*dac1).try_into().unwrap()]; - let telemetry_update = settings.process( - pll, - lockin, - source, - timestamper.latest_timestamp().unwrap_or(None), - adc_samples, - &mut dac_samples, - ); + 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, update the Lockin (demodulate, filter) + .map(|(&sample, phase)| { + let s = (sample as i16 as i32) << 16; + settings.lockin_k.process(lockin, (s, phase)) + }) + // Decimate + .last() + .unwrap() + * 2; // Full scale assuming the 2f component is gone. + + // Convert to DAC data. + for (channel, samples) in dac_samples.iter_mut().enumerate() { + for sample in samples.iter_mut() { + let value = match *settings.output_conf[channel] { + Conf::Magnitude => { + output.norm_sqr().inner as i32 >> 16 + } + Conf::Phase => output.arg().0 >> 16, + Conf::LogPower => output.log2() << 8, + Conf::ReferenceFrequency => { + reference_frequency.0 >> 16 + } + Conf::InPhase => output.re() >> 16, + Conf::Quadrature => output.im() >> 16, + + Conf::Modulation => source.next().unwrap().re(), + }; + + *sample = DacCode::from(value as i16).0; + } + } // Stream the data. + const N: usize = BATCH_SIZE * size_of::() + / size_of::>(); generator.add(|buf| { - lockin::ActiveSettings::stream( - adc_samples, - [dac_samples[0], dac_samples[1]], - buf, - ) + for (data, buf) in adc_samples + .iter() + .chain(dac_samples.iter()) + .zip(buf.chunks_exact_mut(N)) + { + let data = unsafe { + core::slice::from_raw_parts( + data.as_ptr() as *const MaybeUninit, + N, + ) + }; + buf.copy_from_slice(data) + } + N * 4 }); // Update telemetry measurements. - telemetry.adcs = telemetry_update.adcs.map(AdcCode); - telemetry.dacs = telemetry_update.dacs.map(DacCode); + telemetry.adcs = + [AdcCode(adc_samples[0][0]), AdcCode(adc_samples[1][0])]; + + telemetry.dacs = + [DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])]; // Preserve instruction and data ordering w.r.t. DMA flag access. fence(Ordering::SeqCst); @@ -402,7 +487,7 @@ mod app { c.shared .active_settings - .lock(|current| *current = settings.lockin.build()); + .lock(|current| *current = settings.lockin.clone()); }); } diff --git a/src/bin/mpll.rs b/src/bin/mpll.rs index f3512720a9..7880ff6fa3 100644 --- a/src/bin/mpll.rs +++ b/src/bin/mpll.rs @@ -8,8 +8,7 @@ use miniconf::{Leaf, Tree}; use rtic_monotonics::Monotonic; use serde::Serialize; -use stabilizer::{convert::Gain, statistics}; -use stabilizer_dsp::mpll::*; +use stabilizer::{convert::Gain, mpll::*, statistics}; use platform::{AppSettings, NetSettings}; diff --git a/src/hardware/pounder/dds_output.rs b/src/hardware/pounder/dds_output.rs index fc15b63277..89247e5502 100644 --- a/src/hardware/pounder/dds_output.rs +++ b/src/hardware/pounder/dds_output.rs @@ -90,7 +90,6 @@ impl DdsOutput { } /// Get a builder for serializing a Pounder DDS profile. - #[inline(always)] pub fn builder(&mut self) -> ProfileSerializer { ProfileSerializer::new(self.mode) } @@ -104,7 +103,7 @@ impl DdsOutput { /// /// # Args /// * `profile` - The serialized DDS profile to write. - #[inline(always)] + #[inline] pub fn write(&mut self, mut profile: ProfileSerializer) { // Note(unsafe): We own the QSPI interface, so it is safe to access the registers in a raw // fashion. diff --git a/src/lib.rs b/src/lib.rs index db32d99b09..dc39103fbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,3 +11,7 @@ pub mod telemetry; pub mod convert; pub mod statistics; + +pub mod mpll; + +pub mod fls; diff --git a/src/statistics.rs b/src/statistics.rs index 09fe7a1db3..f40a195262 100644 --- a/src/statistics.rs +++ b/src/statistics.rs @@ -31,7 +31,6 @@ impl Default for State { } impl State { - #[inline(always)] pub fn update(&mut self, x: i32) { if self.count == 0 { self.x0 = x;