diff --git a/.github/bors.toml b/.github/bors.toml index 1733a86..4ba0fd5 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -5,5 +5,6 @@ status = [ "style", "test (stable)", "compile (stable)", + "doc", "HITL Run Status" ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf68b3f..7f00033 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,3 +83,57 @@ jobs: with: command: bench args: --package dsp --target=x86_64-unknown-linux-gnu + + doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-ruby@v1 + with: + ruby-version: 2.7.x + + - name: Setup Jekyll + working-directory: docs + run: | + gem update + gem update bundler + gem install jekyll bundler + bundler install + + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: thumbv7em-none-eabihf + override: true + - name: Install Deadlinks + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-deadlinks + + - name: cargo doc + uses: actions-rs/cargo@v1 + with: + command: doc + args: --no-deps -p miniconf -p dsp -p ad9959 -p stabilizer + + - name: cargo deadlinks + uses: actions-rs/cargo@v1 + with: + command: deadlinks + # We intentionally ignore fragments, as RTIC may generate fragments for various + # auto-generated code. + args: --dir target/thumbv7em-none-eabihf/doc --ignore-fragments --check-http --check-intra-doc-links + + - name: Move firmware documents + run: | + mkdir -p docs/_site/stabilizer + + - name: Test User Manual + working-directory: docs + run: | + rake build + mv ../target/thumbv7em-none-eabihf/doc _site/stabilizer/firmware + rake test + diff --git a/.github/workflows/release-docs.yml b/.github/workflows/release-docs.yml new file mode 100644 index 0000000..0816dad --- /dev/null +++ b/.github/workflows/release-docs.yml @@ -0,0 +1,36 @@ +name: Release Documentation + +on: + workflow_dispatch: + push: + branches: + - master + +jobs: + release-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: thumbv7em-none-eabihf + override: true + + - uses: actions-rs/cargo@v1 + with: + command: doc + args: --no-deps -p miniconf -p ad9959 -p stabilizer -p dsp + + - run: mv target/thumbv7em-none-eabihf/doc docs/firmware + + - uses: peaceiris/actions-gh-pages@v3.8.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs + enable_jekyll: true + publish_branch: pages + force_orphan: true diff --git a/.gitignore b/.gitignore index ea8c4bf..a6eef6e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +docs/_site/ diff --git a/Cargo.lock b/Cargo.lock index db78e72..7a8d540 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9a69a963b70ddacfcd382524f72a4576f359af9334b3bf48a79566590bb8bfa" dependencies = [ "bitrate", - "cortex-m 0.7.2", + "cortex-m 0.7.3", "embedded-hal", ] @@ -48,7 +48,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30302dda7a66f8c55932ebf208f7def840743ff64d495e9ceffcd97c18f11d39" dependencies = [ - "cortex-m 0.7.2", + "cortex-m 0.7.3", ] [[package]] @@ -126,15 +126,15 @@ dependencies = [ "aligned", "bare-metal 0.2.5", "bitfield", - "cortex-m 0.7.2", + "cortex-m 0.7.3", "volatile-register", ] [[package]] name = "cortex-m" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643a210c1bdc23d0db511e2a576082f4ff4dcae9d0c37f50b431b8f8439d6d6b" +checksum = "2ac919ef424449ec8c08d515590ce15d9262c0ca5f0da5b0c901e971a3b783b3" dependencies = [ "bare-metal 0.2.5", "bitfield", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "cortex-m-rtic" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa43f63284b363ac64f9ce5221a0f593b54f73258aba8e1a88c1feed8efdb664" +checksum = "9845c4c7f7af19e216a2d00345f7f1507b8907b85cd551e403d68baeec342bb3" dependencies = [ "cortex-m 0.6.7", "cortex-m-rt", @@ -176,9 +176,9 @@ dependencies = [ [[package]] name = "cortex-m-rtic-macros" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1a6a4c9550373038c0e21a78d44d529bd697c25bbf6b8004bddc6e63b119c7" +checksum = "cc874eda99515b15e67f03562726a530388f454431096d30131051b52b840559" dependencies = [ "proc-macro2", "quote", @@ -200,7 +200,7 @@ dependencies = [ [[package]] name = "derive_miniconf" version = "0.1.0" -source = "git+https://github.com/quartiq/miniconf.git?rev=2750533#275053396f0334e9efefa1ab2aae4c19b95a9a53" +source = "git+https://github.com/quartiq/miniconf.git?rev=9c826f8#9c826f8de8d0dd1a59e1ce7bf124ac0311994b46" dependencies = [ "proc-macro2", "quote", @@ -250,7 +250,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db9efecb57ab54fa918730f2874d7d37647169c50fa1357fecb81abee840b113" dependencies = [ - "heapless 0.7.2", + "heapless 0.7.3", "nb 1.0.0", "no-std-net", ] @@ -352,9 +352,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44a9fba8877c568fc30457f5639d0aa7f57e494e4cdbc464150740a66c6613a" +checksum = "34e26526e7168021f34243a3c8faac4dc4f938cde75a0f9b8e373cca5eb4e7ce" dependencies = [ "atomic-polyfill", "hash32 0.2.1", @@ -414,7 +414,7 @@ dependencies = [ [[package]] name = "miniconf" version = "0.1.0" -source = "git+https://github.com/quartiq/miniconf.git?rev=2750533#275053396f0334e9efefa1ab2aae4c19b95a9a53" +source = "git+https://github.com/quartiq/miniconf.git?rev=9c826f8#9c826f8de8d0dd1a59e1ce7bf124ac0311994b46" dependencies = [ "derive_miniconf", "serde", @@ -429,7 +429,7 @@ dependencies = [ "bit_field", "embedded-nal", "enum-iterator", - "heapless 0.7.2", + "heapless 0.7.3", ] [[package]] @@ -509,9 +509,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" +checksum = "e5adf0198d427ee515335639f275e806ca01acf9f07d7cf14bb36a10532a6169" dependencies = [ "derivative", "num_enum_derive", @@ -519,9 +519,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" +checksum = "b1def5a3f69d4707d8a040b12785b98029a39e8c610ae685c7f6265669767482" dependencies = [ "proc-macro2", "quote", @@ -645,7 +645,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065d6058bb1204f51a562a67209e1817cf714759d5cf845aa45c75fa7b0b9d9b" dependencies = [ - "cortex-m 0.7.2", + "cortex-m 0.7.3", "ufmt-write", ] @@ -721,7 +721,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8014aeea272bca0f0779778d43253f2f3375b414185b30e6ecc4d3e4a9994781" dependencies = [ - "heapless 0.7.2", + "heapless 0.7.3", "ryu", "serde", ] @@ -763,7 +763,7 @@ version = "0.1.0" source = "git+https://github.com/quartiq/smoltcp-nal.git?rev=5baf55f#5baf55fafbfe2c08d9fe56c836171e9d2fb468e8" dependencies = [ "embedded-nal", - "heapless 0.7.2", + "heapless 0.7.3", "nanorand", "smoltcp", ] @@ -774,12 +774,12 @@ version = "0.5.0" dependencies = [ "ad9959", "asm-delay", - "cortex-m 0.7.2", + "cortex-m 0.7.3", "cortex-m-rt", "cortex-m-rtic", "dsp", "embedded-hal", - "heapless 0.7.2", + "heapless 0.7.3", "log", "mcp23017", "miniconf", @@ -810,7 +810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b672c837e0ee8158ecc7fce0f9a948dd0693a9c588338e728d14b73307a0b7d" dependencies = [ "bare-metal 0.2.5", - "cortex-m 0.7.2", + "cortex-m 0.7.3", "cortex-m-rt", "vcell", ] @@ -822,7 +822,7 @@ source = "git+https://github.com/quartiq/stm32h7xx-hal.git?rev=33aa67d#33aa67d74 dependencies = [ "bare-metal 1.0.0", "cast", - "cortex-m 0.7.2", + "cortex-m 0.7.3", "cortex-m-rt", "embedded-dma", "embedded-hal", diff --git a/Cargo.toml b/Cargo.toml index 6f52210..cce9eeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "stabilizer" version = "0.5.0" +resolver = "2" authors = ["Robert Jördens "] description = "Firmware for the Sinara Stabilizer board (stm32h743, eth, poe, 2 adc, 2 dac)" categories = ["embedded", "no-std", "hardware-support", "science"] @@ -34,11 +35,11 @@ log = { version = "0.4", features = ["max_level_trace", "release_max_level_info" rtt-target = { version = "0.3", features = ["cortex-m"] } serde = { version = "1.0", features = ["derive"], default-features = false } heapless = { version = "0.7", features = ["serde"] } -cortex-m-rtic = "0.5.6" +cortex-m-rtic = "0.5.7" embedded-hal = "0.2.5" nb = "1.0.0" asm-delay = "0.9.0" -num_enum = { version = "0.5.1", default-features = false } +num_enum = { version = "0.5.2", default-features = false } paste = "1" dsp = { path = "dsp" } ad9959 = { path = "ad9959" } @@ -70,7 +71,7 @@ rev = "a2e3ad5" [patch.crates-io.miniconf] git = "https://github.com/quartiq/miniconf.git" -rev = "2750533" +rev = "9c826f8" [dependencies.smoltcp-nal] git = "https://github.com/quartiq/smoltcp-nal.git" diff --git a/README.md b/README.md index d34d971..75249db 100644 --- a/README.md +++ b/README.md @@ -4,88 +4,13 @@ # Stabilizer Firmware -## Hardware - -[![Hardware](https://github.com/sinara-hw/Stabilizer/wiki/Stabilizer_v1.0_top_small.jpg)](https://github.com/sinara-hw/Stabilizer) - ## Applications -This firmware offers a library of hardware and software functionality targeting the use of the Stabilizer hardware in various digital signal processing applications commonly occurring in Quantum Technology. -It provides abstractions over the fast analog inputs and outputs, time stamping, Pounder DDS interfaces and a collection of tailored and optimized digital signal processing algorithms (IIR, FIR, Lockin, PLL, reciprocal PLL, Unwrapper, Lowpass, Cosine-Sine, Atan2). -An application can compose and configure these hardware and software components to implement different use cases. -Several applications are provides by default: +Check out the [Documentation](https://quartiq.de/stabilizer) for more information on usage, +configuration, and development. -### Dual-IIR +## Hardware -![Flow diagram](stabilizer_pid.svg) +[![Stabilizer](https://github.com/sinara-hw/Stabilizer/wiki/Stabilizer_v1.0_top_small.jpg)](https://github.com/sinara-hw/Stabilizer) -* dual channel -* SPI ADC -* SPI DAC -* up to 800 kHz rate, timed sampling -* down to 2 µs latency -* f32 IIR math -* generic biquad (second order) IIR filter -* anti-windup -* derivative kick avoidance - -### Lockin - -* Up to 800 kHz sampling -* Up to 400 kHz modulation frequency -* Reciprocal PLL for external reference -* Internal reference -* Adjustable PLL and locking time constants -* Adjustable phase offset and harmonic index -* Different output modes (in-phase, quadrature, magnitude, log2 power, phase, frequency) - -## Minimal bootstrapping documentation - -* Clone or download this -* Get [rustup](https://rustup.rs/) -* Minimum supported Rust version (MSRV) is 1.52.0 -* Install target support: `rustup target add thumbv7em-none-eabihf` -* Install `probe-run`: `cargo install probe-run` -* `cargo run --release --bin dual-iir` -* When using debug (non `--release`) mode, increase the sample interval significantly. - The added error checking code and missing optimizations may lead to the code - missing deadlines and panicing. - -## Alternative flashing tools - -### Cargo-embed - -* Install `cargo-embed`: `cargo install cargo-embed` -* Program the device: `cargo embed --bin dual-iir --release` - -### GDB/OpenOCD - -* Get a recent openocd, a JTAG adapter ("st-link" or some clone) and - everything connected and permissions setup. Most - [Nucleo](https://www.digikey.de/short/p41h4v) boards have a - detachable ST-Link v2 and are cheap.[^swd] -* Get a multiarch `gdb` (or a cross arm gdb and edit `.cargo/config` accordingly) -* `openocd -f stabilizer.cfg` and leave it running -* `cargo run --release` - -### USB-DFU - -* Get [cargo-binutils](https://github.com/rust-embedded/cargo-binutils/) -* `cargo objcopy --release --bin dual-iir -- -O binary dual-iir.bin` or `arm-none-eabi-objcopy -O binary target/thumbv7em-none-eabihf/release/dual-iir dual-iir.bin` -* Install the DFU USB tool (`dfu-util`) -* Connect to the Micro USB connector below the RJ45 -* Short JC2/BOOT -* `dfu-util -a 0 -s 0x08000000:leave -D dual-iir.bin` - -### ST-Link virtual mass storage - -* Prepare `dual-iir.bin` like above -* Connect the ST-Link debugger -* Copy `dual-iir.bin` to the `NODE_H743ZI` virtual mass storage device - -## Protocol - -Stabilizer can be configured via MQTT. Refer to -[`miniconf`](https://github.com/quartiq/miniconf) for more information about topics. -A basic command line interface is available in [`miniconf.py`](miniconf.py). -Telemetry is published via MQTT as well. +[![Pounder](https://user-images.githubusercontent.com/1338946/125936814-3664aa2d-a530-4c85-9393-999a7173424e.png)](https://github.com/sinara-hw/Pounder/wiki) diff --git a/doc/.keep-me b/doc/.keep-me deleted file mode 100644 index e69de29..0000000 diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 0000000..e18d92a --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' +gem "just-the-docs" +gem "jekyll-remote-theme" +gem "html-proofer" diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock new file mode 100644 index 0000000..b8495b8 --- /dev/null +++ b/docs/Gemfile.lock @@ -0,0 +1,102 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + colorator (1.1.0) + concurrent-ruby (1.1.9) + em-websocket (0.5.2) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0.6.0) + ethon (0.14.0) + ffi (>= 1.15.0) + eventmachine (1.2.7-x64-mingw32) + ffi (1.15.3-x64-mingw32) + forwardable-extended (2.6.0) + html-proofer (3.19.2) + addressable (~> 2.3) + mercenary (~> 0.3) + nokogumbo (~> 2.0) + parallel (~> 1.3) + rainbow (~> 3.0) + typhoeus (~> 1.3) + yell (~> 2.0) + http_parser.rb (0.6.0) + i18n (1.8.10) + concurrent-ruby (~> 1.0) + jekyll (4.2.0) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 1.0) + jekyll-sass-converter (~> 2.0) + jekyll-watch (~> 2.0) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.0) + liquid (~> 4.0) + mercenary (~> 0.4.0) + pathutil (~> 0.9) + rouge (~> 3.0) + safe_yaml (~> 1.0) + terminal-table (~> 2.0) + jekyll-remote-theme (0.4.3) + addressable (~> 2.0) + jekyll (>= 3.5, < 5.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) + jekyll-sass-converter (2.1.0) + sassc (> 2.0.1, < 3.0) + jekyll-seo-tag (2.7.1) + jekyll (>= 3.8, < 5.0) + jekyll-watch (2.2.1) + listen (~> 3.0) + just-the-docs (0.3.3) + jekyll (>= 3.8.5) + jekyll-seo-tag (~> 2.0) + rake (>= 12.3.1, < 13.1.0) + kramdown (2.3.1) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.3) + listen (3.5.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.4.0) + nokogiri (1.11.7-x64-mingw32) + racc (~> 1.4) + nokogumbo (2.0.5) + nokogiri (~> 1.8, >= 1.8.4) + parallel (1.20.1) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (4.0.6) + racc (1.5.2) + rainbow (3.0.0) + rake (13.0.6) + rb-fsevent (0.11.0) + rb-inotify (0.10.1) + ffi (~> 1.0) + rexml (3.2.5) + rouge (3.26.0) + rubyzip (2.3.2) + safe_yaml (1.0.5) + sassc (2.4.0-x64-mingw32) + ffi (~> 1.9) + terminal-table (2.0.0) + unicode-display_width (~> 1.1, >= 1.1.1) + typhoeus (1.4.0) + ethon (>= 0.9.0) + unicode-display_width (1.7.0) + yell (2.2.2) + +PLATFORMS + x64-mingw32 + +DEPENDENCIES + html-proofer + jekyll-remote-theme + just-the-docs + +BUNDLED WITH + 2.2.22 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..0d5ddec --- /dev/null +++ b/docs/README.md @@ -0,0 +1,20 @@ +This folder represents the Github Pages site that is used to host Stabilizer's user guide. + +The site is hosted with Jekyll and utilizes the "Just the Docs" theme. + +To run locally: +1. Install Ruby +1. Install [Jekyll](https://jekyllrb.com/) +1. Install [Bundler](https://bundler.io/) +1. From this directory: +``` +bundle install +bundle exec jekyll serve +``` +1. Navigate to `localhost:4000/stabilizer/` in a web browser + +Note: Some of the links in the docs rely on Cargo's documentation. To make all links work locally, run: +``` +cargo doc --no-deps -p dsp -p miniconf -p stabilizer -p ad9959 +cp -r target/thumbv7em-none-eabihf/doc docs/firmware +``` diff --git a/docs/Rakefile b/docs/Rakefile new file mode 100644 index 0000000..dc5ab2f --- /dev/null +++ b/docs/Rakefile @@ -0,0 +1,35 @@ +# Rakefile taken from: +# https://seankilleen.com/2019/09/how-to-check-your-jekyll-based-blog-for-dead-links/ + +# Ensures we have the html-proofer library available to use +require 'html-proofer' + +# The function that will run the proofer, so that we can re-use it between our two rake tasks +def run_htmlproofer() + options = { + # Assumes html file extensions + assume_extension: true, + + file_ignore: [ /stabilizer\/firmware\/.*/ ], + url_ignore: [ /quartiq.de\/stabilizer/ ], + + # The options for the curl library that's used. + :typhoeus => { + # This will stop you from getting errors when certs can't be parsed, which doesn't matter in this case. + :ssl_verifypeer => false + }, + # Won't fail for local links + allow_hash_href: true, + } + + # Calls html-proofer and uses the Jekyll _site folder + HTMLProofer.check_directory("./_site", options).run +end + +task :test do + run_htmlproofer() +end + +task :build do + sh "bundle exec jekyll build -d _site/stabilizer" +end diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..a7901db --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,18 @@ +remote_theme: pmarsceill/just-the-docs +title: Stabilizer +description: "User Manual" +logo: "/assets/stabilizer-logo.png" + +url: "https://quartiq.de" +baseurl: "/stabilizer" + +exclude: ['README.md'] + +plugins: + - jekyll-remote-theme + +# Enable an auxilary link in top right with a new tab open +aux_links: + "Stabilizer on Github": + - "//github.com/quartiq/stabilizer" +aux_links_new_tab: true diff --git a/docs/assets/mqtt-explorer.png b/docs/assets/mqtt-explorer.png new file mode 100644 index 0000000..9ffe74c Binary files /dev/null and b/docs/assets/mqtt-explorer.png differ diff --git a/docs/assets/stabilizer-jtag.jpg b/docs/assets/stabilizer-jtag.jpg new file mode 100644 index 0000000..6535fda Binary files /dev/null and b/docs/assets/stabilizer-jtag.jpg differ diff --git a/docs/assets/stabilizer-logo.png b/docs/assets/stabilizer-logo.png new file mode 100644 index 0000000..a5cfb76 Binary files /dev/null and b/docs/assets/stabilizer-logo.png differ diff --git a/stabilizer_pid.svg b/docs/assets/stabilizer_pid.svg similarity index 100% rename from stabilizer_pid.svg rename to docs/assets/stabilizer_pid.svg diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000..a6cbb9e Binary files /dev/null and b/docs/favicon.ico differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..6aaa6ad --- /dev/null +++ b/docs/index.md @@ -0,0 +1,57 @@ +--- +title: Home +layout: default +nav_order: 1 +permalink: / +--- + +# Stabilizer +{: .no_toc } + +## Table of Contents +{: .no_toc .text-delta } + +1. TOC +{:toc} + +## Overview + +Stabilizer is a flexible tool designed for quantum physics experiments. Fundamentally, Stabilizer +samples up two two analog input signals, performs digital signal processing internally, and then +generates up to two output signals. + +Stabilizer firmware supports run-time configuration of the internal signal processing algorithms, +which allows for a wide variety of experimental uses, such as digital filter design or +implementation of digital lockin schemes. + +This documentation is intended to bring a user up to speed on using Stabilizer and the firmware +provided by QUARTIQ and contributors. + +## Hardware + +The Stabilizer hardware is managed via a [separate repository](https://github.com/sinara-hw/Stabilizer). +Some information about the hardware is gathered in the [Stabilizer wiki](https://github.com/sinara-hw/Stabilizer/wiki). More detailed data, measurements, discussions, and tests have been posted in the [Stabilizer issue tracker](https://github.com/sinara-hw/Stabilizer/issues?q=is%3Aissue). + +[![Hardware](https://github.com/sinara-hw/Stabilizer/wiki/Stabilizer_v1.0_top_small.jpg)](https://github.com/sinara-hw/Stabilizer) + +Stabilizer can be extended and coupled with a mezzanine board. One such mezzanine is the DDS upconversion/downconversion frontend Pounder. The Pounder hardware is managed via a [separate repository](https://github.com/sinara-hw/Pounder), again with [wiki](https://github.com/sinara-hw/Pounder/wiki) and [issue tracker](https://github.com/sinara-hw/Pounder/issues?q=is%3Aissue). +## Applications + +This firmware offers a library of hardware and software functionality targeting the use of the Stabilizer hardware in various digital signal processing applications commonly occurring in Quantum Technology. +It provides abstractions over the fast analog inputs and outputs, time stamping, Pounder DDS interfaces and a collection of tailored and optimized digital signal processing algorithms (IIR, FIR, Lockin, PLL, reciprocal PLL, Unwrapper, Lowpass, Cosine-Sine, Atan2) in the [DSP crate]({{site.baseurl}}/firmware/dsp/index.html). +An application, which is the compiled firmware running on the device, can compose and configure these hardware and software components to implement different use cases. +Several applications are provided by default. + +The following documentation links contain the application-specific settings and telemetry +information. + +| Application | Description | +| :---: | :---- | +| [`dual-iir`]({{site.baseurl}}/firmware/dual_iir/index.html) | Two channel biquad IIR filter | +| [`lockin`]({{site.baseurl}}/firmware/lockin/index.html) | Lockin amplifier support various various reference sources | + +### Library Documentation +The Stabilizer library docs contain documentation for common components used in all Stabilizer +applications. + +The Stabilizer library documentation is available [here]({{site.baseurl}}/firmware/stabilizer/index.html). diff --git a/docs/pages/getting-started.md b/docs/pages/getting-started.md new file mode 100644 index 0000000..3fefa77 --- /dev/null +++ b/docs/pages/getting-started.md @@ -0,0 +1,157 @@ +--- +title: Getting Started +layout: default +permalink: /getting-started +nav_order: 2 +--- + +## Table of Contents +{: .no_toc .text-delta } + +1. TOC +{:toc} +--- + +# Getting Started +{: .no_toc } + +There are a number of steps that must be completed when first getting started with Stabilizer. +1. Update the Stabilizer Application + 1. Set parameters in the firmware source code, such as IP addresses and sampling rate. + 1. Build the application by compiling the source code. + 1. Upload the application and programming it onto the device. +1. Set up MQTT for telemetry and configuration. + +The following sections will walk you through completing each of these steps. + +# Update Stabilizer + +Firmware is compiled and loaded onto Stabilizer to program a specific application. + +After receiving the Stabilizer hardware, you will need to flash firmware onto the device to use your +desired application. + +## Configuring Firmware + +Stabilizer firmware contains compile-time parameters that may need to be changed based on +application requirements. Some examples of parameters that may require configuraton: +* Sampling interval. +* Batch size. +* MQTT Broker IP address + +Parameters are configured by editing the file `src/configuration.rs`. + +Refer to the [documentation]({{site.baseurl}}/firmware/stabilizer/configuration/index.html) for more +information on parameters. + +When these parameters are updated, firmware must be built and flashed onto Stabilizer before they +take effect. + + +## Building Firmware + +1. Clone or download [stabilizer](https://github.com/quartiq/stabilizer) +```bash +git clone https://github.com/quartiq/stabilizer +``` +1. Get [rustup](https://rustup.rs/) + * The minimum supported Rust version (MSRV) is 1.52.0 +1. Install target support +```bash +rustup target add thumbv7em-none-eabihf +``` +1. Build Firmware +```bash +cargo build --release +``` + +## Uploading Firmware + +Firmware is loaded onto stabilizer utilizing an ST-Link (V2-1 or greater) JTAG programmer. +* If a programmer is not available, please see the [alternative method](#alternative-using-usb) + +Ensure the ST-Link is connected to Stabilizer as shown below. + +![JTAG Connection]({{site.baseurl}}/assets/stabilizer-jtag.jpg) + +All of the instructions below assume you have properly [`built the firmware`](#building-firmware). + +Substitute `dual-iir` below with the application name you are flashing. + +1. Install [cargo-binutils](https://github.com/rust-embedded/cargo-binutils/) +```bash +cargo install cargo-binutils +rustup component add llvm-tools-preview +``` + +1. Generate the binary file +```bash +cargo objcopy --release --bin dual-iir -- -O binary dual-iir.bin` +``` + +1. Copy `dual-iir.bin` into the ST-Link drive on your computer. + + +### Alternative: Using USB + +If an ST-Link V2-1 or above is not available, you can upload firmware using a micro USB cable +plugged in to the front of Stabilizer. + +1. Install the DFU USB tool [`dfu-util`](http://dfu-util.sourceforge.net) +1. Connect to the Micro USB connector below the RJ45 +1. Short JC2/BOOT +1. Perform the Device Firmware Upgrade (DFU) +```bash +dfu-util -a 0 -s 0x08000000:leave -D dual-iir.bin +``` + +### Alternative: Firmware Development / Debug + +The below instructions are useful for debugging or developing firmware + +For an interactive flash experience with live logging, utilize `probe-run` as follows. + +1. Install `probe-run` +```bash +cargo install probe-run +``` +1. Build and run firmware on the device +```bash +cargo run --release --bin dual-iir +``` +1. When using debug (non `--release`) mode, decrease the sampling frequency significantly. + The added error checking code and missing optimizations may lead to the code + missing deadlines and panicing. + +# Set up and test MQTT + +## Set up the Broker +Stabilizer requires an MQTT broker that supports MQTTv5. [Mosquitto](https://mosquitto.org/) has +been used during development, but any MQTTv5 broker is supported. + +> _Note_: Mosquitto version 1 only supports MQTTv3.1. If using Mosquitto, ensure version 2.0.0 or +> later is used. + +Stabilizer utilizes a static IP address for broker configuration. Ensure the IP address was +[configured](#configuring-firmware) properly to point to your broker before continuing. + +## Test the Connection +Once your broker is running, test that Stabilizer is properly connected to it. + +To do this, we will check that Stabilizer is reporting telemetry on the following topic: +``` +dt/sinara/dual-iir/00-11-22-33-44-55/telemetry +``` +> **Note**: The telemetry topic will be different based on the programmed application and the MAC address + of the device. + +Download [MQTT-Explorer](http://mqtt-explorer.com/) to observe which topics have been posted to the +Broker. + +![MQTT Explorer Configuration]({{site.baseurl}}/assets/mqtt-explorer.png) + +> **Note**: Use the same broker address that you defined in the firmware for MQTT explorer. + +Telemetry messages should come in approximately every 10 seconds when Stabilizer has connected to +the broker. Once you observe incoming telemetry, Stabilizer has been properly configured and is +operational. diff --git a/docs/pages/usage.md b/docs/pages/usage.md new file mode 100644 index 0000000..3e1807d --- /dev/null +++ b/docs/pages/usage.md @@ -0,0 +1,97 @@ +--- +title: Usage +layout: default +nav_order: 4 +permalink: /usage +--- + +## Table of Contents +{: .no_toc .text-delta } + +1. TOC +{:toc} + +## Miniconf Run-time Settings +Stabilizer supports run-time settings configuration using MQTT. + +Settings can be stored in the MQTT broker so that they are automatically applied whenever +Stabilizer reboots and connects. This is referred to as "retained" settings. Broker implementations +may optionally store these retained settings as well such that they will be reapplied between +restarts of the MQTT broker. + +Settings are specific to a device. Any settings configured for one Stabilizer will not be applied +to another. Disambiguation of devices is done by using Stabilizer's MAC address. + +Settings are specific to an application. If two identical settings exist for two different +applications, each application maintains its own independent value. + +### Setup + +In order to use `miniconf.py`, run the following command: +``` +python -m pip install gmqtt +``` + +### Usage +The `miniconf.py` script utilizes a unique "device prefix". The device prefix is always of the +form `dt/sinara//`, where `` is the name of the application and +`` is the MAC address of the device, formatted with delimiting dashes. + +Settings have a `path` and a `value` being configured. The `value` parameter is JSON-encoded data +and the `path` value is a path-like string. + +As an example, for configuring `dual-iir`'s `stream_target`, the following information would be +used: +* `path` = `stream_target` +* `value` = `{"ip": [192, 168, 0, 1], "port": 4000}` + +``` +python miniconf.py --broker 10.34.16.10 dt/sinara/dual-iir/00-11-22-33-44-55 stream_target='{"ip": [10, 34, 16, 123], "port": 4000}' + +Where `10.34.16.10` is the MQTT broker address that matches the one configured in the source code and `10.34.16.123` and `4000` are the desire stream target IP and port. +``` + +The prefix can be found for a specific device by looking at the topic on which telemetry that is +being published. + +Refer to the [application documentation]({{site.baseurl}}/#applications) for the exact settings and values exposed +for each application. + +The rules for constructing `path` values are documented in [`miniconf`'s +documentation](https://github.com/quartiq/miniconf#settings-paths) + +Refer to the documentation for [Miniconf]({{site.baseurl}}/firmware/miniconf/enum.Error.html) for a +description of the possible error codes that `miniconf.py` may return if the settings update was +unsuccessful. + +## Telemetry + +Stabilizer applications publish telemetry utilizes MQTT for managing run-time settings configurations as well as live telemetry +reporting. + +Telemetry is defined as low rate, general health information. It is not intended for high throughput +or efficiency. Telemetry is generally used to determine that the device is functioning nominally. + +Stabilizer applications publish telemetry over MQTT at a set rate. Telemetry data units are defined +by the application. All telemetry is reported using standard JSON format. + +Telemetry is intended for low-bandwidth monitoring. It is not intended to transfer large amounts of +data and uses a minimal amount of bandwidth. Telemetry is published using "best effort" semantics - +individual messages may be dropped or Stabilizer may fail to publish telemetry due to internal +buffering requirements. + +In its most basic form, telemetry publishes the latest ADC input voltages, DAC output voltages, and +digital input states. + +Refer to the respective [application documentation]({{site.baseurl}}/#applications) for more information on telemetry. + +## Livestream + +Stabilizer supports livestream capabilities for streaming real-time data over UDP. The livestream is +intended to be a high-bandwidth mechanism to transfer large amounts of data from Stabilizer to a +host computer for further analysis. + +Livestreamed data is sent with "best effort" - it's possible that data may be lost either due to +network congestion or by Stabilizer. + +Refer to the the respective [application documentation]({{site.baseurl}}/#applications) for more information. diff --git a/dsp/Cargo.toml b/dsp/Cargo.toml index e3be45b..972a0f6 100644 --- a/dsp/Cargo.toml +++ b/dsp/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "dsp" version = "0.1.0" +resolver = "2" authors = ["Robert Jördens "] edition = "2018" diff --git a/dsp/src/cossin.rs b/dsp/src/cossin.rs index 5dae7c3..abca821 100644 --- a/dsp/src/cossin.rs +++ b/dsp/src/cossin.rs @@ -4,7 +4,7 @@ include!(concat!(env!("OUT_DIR"), "/cossin_table.rs")); /// Compute the cosine and sine of an angle. /// This is ported from the MiSoC cossin core. -/// (https://github.com/m-labs/misoc/blob/master/misoc/cores/cossin.py) +/// /// /// # Arguments /// * `phase` - 32-bit phase. diff --git a/dsp/src/iir.rs b/dsp/src/iir.rs index a6af191..c53af74 100644 --- a/dsp/src/iir.rs +++ b/dsp/src/iir.rs @@ -39,6 +39,19 @@ pub type Vec5 = [f32; 5]; /// Therefore it can trivially implement bump-less transfer. /// * Cascading multiple IIR filters allows stable and robust /// implementation of transfer functions beyond bequadratic terms. +/// +/// # Miniconf +/// +/// `{"y_offset": y_offset, "y_min": y_min, "y_max": y_max, "ba": [b0, b1, b2, a1, a2]}` +/// +/// * `y0` is the output offset code +/// * `ym` is the lower saturation limit +/// * `yM` is the upper saturation limit +/// +/// IIR filter tap gains (`ba`) are an array `[b0, b1, b2, a1, a2]` such that the +/// new output is computed as `y0 = a1*y1 + a2*y2 + b0*x0 + b1*x1 + b2*x2`. +/// The IIR coefficients can be mapped to other transfer function +/// representations, for example as described in #[derive(Copy, Clone, Debug, Default, Deserialize, MiniconfAtomic)] pub struct IIR { pub ba: Vec5, diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index 2e45be6..56cd7bb 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -1,3 +1,30 @@ +//! # Dual IIR +//! +//! The Dual IIR application exposes two configurable channels. Stabilizer samples input at a fixed +//! rate, digitally filters the data, and then generates filtered output signals on the respective +//! channel outputs. +//! +//! ## Features +//! * Two indpenendent channels +//! * up to 800 kHz rate, timed sampling +//! * Run-time filter configuration +//! * Input/Output data streaming +//! * Down to 2 µs latency +//! * f32 IIR math +//! * Generic biquad (second order) IIR filter +//! * Anti-windup +//! * Derivative kick avoidance +//! +//! ## Settings +//! Refer to the [Settings] structure for documentation of run-time configurable settings for this +//! application. +//! +//! ## Telemetry +//! Refer to [Telemetry] for information about telemetry reported by this application. +//! +//! ## Livestreaming +//! This application streams raw ADC and DAC data over UDP. Refer to +//! [stabilizer::net::data_stream](../stabilizer/net/data_stream/index.html) for more information. #![deny(warnings)] #![no_std] #![no_main] @@ -35,11 +62,63 @@ const IIR_CASCADE_LENGTH: usize = 1; #[derive(Clone, Copy, Debug, Deserialize, Miniconf)] pub struct Settings { + /// Configure the Analog Front End (AFE) gain. + /// + /// # Path + /// `afe/` + /// + /// * specifies which channel to configure. := [0, 1] + /// + /// # Value + /// Any of the variants of [Gain] enclosed in double quotes. afe: [Gain; 2], + + /// Configure the IIR filter parameters. + /// + /// # Path + /// `iir_ch//` + /// + /// * specifies which channel to configure. := [0, 1] + /// * specifies which cascade to configure. := [0, 1], depending on [IIR_CASCADE_LENGTH] + /// + /// # Value + /// See [iir::IIR#miniconf] iir_ch: [[iir::IIR; IIR_CASCADE_LENGTH]; 2], + + /// Specified true if DI1 should be used as a "hold" input. + /// + /// # Path + /// `allow_hold` + /// + /// # Value + /// "true" or "false" allow_hold: bool, + + /// Specified true if "hold" should be forced regardless of DI1 state and hold allowance. + /// + /// # Path + /// `force_hold` + /// + /// # Value + /// "true" or "false" force_hold: bool, + + /// Specifies the telemetry output period in seconds. + /// + /// # Path + /// `telemetry_period` + /// + /// # Value + /// Any non-zero value less than 65536. telemetry_period: u16, + + /// Specifies the target for data livestreaming. + /// + /// # Path + /// `stream_target` + /// + /// # Value + /// See [StreamTarget#miniconf] stream_target: StreamTarget, signal_generator: [signal_generator::BasicConfig; 2], } diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index ae2d611..3b07341 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -1,3 +1,29 @@ +//! # Lockin +//! +//! THe `lockin` application implements a lock-in amplifier using either an external or internally +//! generated +//! +//! ## Features +//! * Up to 800 kHz sampling +//! * Up to 400 kHz modulation frequency +//! * Supports internal and external reference sources: +//! 1. Internal: Generate reference internally and output on one of the channel outputs +//! 2. External: Reciprocal PLL, reference input applied to DI0. +//! * Adjustable PLL and locking time constants +//! * Adjustable phase offset and harmonic index +//! * Run-time configurable output modes (in-phase, quadrature, magnitude, log2 power, phase, frequency) +//! * Input/output data streamng via UDP +//! +//! ## Settings +//! Refer to the [Settings] structure for documentation of run-time configurable settings for this +//! application. +//! +//! ## Telemetry +//! Refer to [Telemetry] for information about telemetry reported by this application. +//! +//! ## Livestreaming +//! This application streams raw ADC and DAC data over UDP. Refer to +//! [stabilizer::net::data_stream](../stabilizer/net/data_stream/index.html) for more information. #![deny(warnings)] #![no_std] #![no_main] @@ -8,6 +34,7 @@ use mutex_trait::prelude::*; use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL}; use stabilizer::{ + configuration, hardware::{ self, adc::{Adc0Input, Adc1Input, AdcCode}, @@ -32,35 +59,118 @@ use stabilizer::{ #[derive(Copy, Clone, Debug, Deserialize, Miniconf)] 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, Miniconf, Deserialize, PartialEq)] enum LockinMode { + /// Utilize an internally generated reference for demodulation Internal, + /// Utilize an external modulation signal supplied to DI0 External, } #[derive(Copy, Clone, Debug, Deserialize, Miniconf)] pub struct Settings { + /// Configure the Analog Front End (AFE) gain. + /// + /// # Path + /// `afe/` + /// + /// * specifies which channel to configure. := [0, 1] + /// + /// # Value + /// Any of the variants of [Gain] enclosed in double quotes. afe: [Gain; 2], + + /// Specifies the operational mode of the lockin. + /// + /// # Path + /// `lockin_mode` + /// + /// # Value + /// One of the variants of [LockinMode] enclosed in double quotes. lockin_mode: LockinMode, + /// Specifis the PLL time constant. + /// + /// # Path + /// `pll_tc/` + /// + /// * specifies which channel to configure. := [0, 1] + /// + /// # Value + /// The PLL time constant as an unsigned byte (0-255). pll_tc: [u8; 2], + /// Specifies the lockin time constant. + /// + /// # Path + /// `lockin_tc` + /// + /// # Value + /// The lockin low-pass time constant as an unsigned byte (0-255). lockin_tc: u8, + + /// Specifies which harmonic to use for the lockin. + /// + /// # Path + /// `lockin_harmonic` + /// + /// # Value + /// Harmonic index of the LO. -1 to _de_modulate the fundamental (complex conjugate) lockin_harmonic: i32, + + /// Specifies the LO phase offset. + /// + /// # Path + /// `lockin_phase` + /// + /// # Value + /// Demodulation LO phase offset. Units are in terms of i32, where [i32::MIN] is equivalent to + /// -pi and [i32::MAX] is equivalent to +pi. lockin_phase: i32, + /// Specifies DAC output mode. + /// + /// # Path + /// `output_conf/` + /// + /// * specifies which channel to configure. := [0, 1] + /// + /// # Value + /// One of the variants of [Conf] enclosed in double quotes. output_conf: [Conf; 2], + + /// Specifies the telemetry output period in seconds. + /// + /// # Path + /// `telemetry_period` + /// + /// # Value + /// Any non-zero value less than 65536. telemetry_period: u16, + /// Specifies the target for data livestreaming. + /// + /// # Path + /// `stream_target` + /// + /// # Value + /// See [StreamTarget#miniconf] stream_target: StreamTarget, } @@ -123,8 +233,8 @@ const APP: () = { let settings = Settings::default(); let pll = RPLL::new( - design_parameters::ADC_SAMPLE_TICKS_LOG2 - + design_parameters::SAMPLE_BUFFER_SIZE_LOG2, + configuration::ADC_SAMPLE_TICKS_LOG2 + + configuration::SAMPLE_BUFFER_SIZE_LOG2, ); // Spawn a settings and telemetry update for default settings. @@ -213,8 +323,7 @@ const APP: () = { ); ( pll_phase, - (pll_frequency - >> design_parameters::SAMPLE_BUFFER_SIZE_LOG2) + (pll_frequency >> configuration::SAMPLE_BUFFER_SIZE_LOG2) as i32, ) } @@ -222,7 +331,7 @@ const APP: () = { // Reference phase and frequency are known. ( 1i32 << 30, - 1i32 << (32 - design_parameters::SAMPLE_BUFFER_SIZE_LOG2), + 1i32 << (32 - configuration::SAMPLE_BUFFER_SIZE_LOG2), ) } }; diff --git a/src/configuration.rs b/src/configuration.rs new file mode 100644 index 0000000..01e410a --- /dev/null +++ b/src/configuration.rs @@ -0,0 +1,41 @@ +//! This module contains any compile-time configuration parameters for the Stabilizer firmware. + +/// MQTT broker IPv4 address +/// +/// In the default configuration, the IP address is defined as 10.34.16.10. +pub const MQTT_BROKER: [u8; 4] = [10, 34, 16, 10]; + +/// Sampling Frequency +/// +/// Define the frequency at which ADCs (and DACs) are sampled at. +/// +/// # Units +/// The units of this parameter are specified as a logarithmic number of ticks of the internal +/// timer, which runs at 100 MHz. +/// +/// ## Example +/// With a value of 7, this corresponds to 2^7 = 128 ticks. Each tick of the 100 MHz timer requires +/// 10ns. +/// +/// Sampling Period = 10ns * 128 = 1.28 us +/// Sampling Frequency = 781.25 KHz +/// +/// Or more succinctly: +/// `F_s = 100 MHz / (2 ^ ADC_SAMPLE_TICKS_LOG2)` +pub const ADC_SAMPLE_TICKS_LOG2: u8 = 7; + +/// Sample Batch Sizing +/// +/// The sample batch size defines how many samples are collected before the DSP routines are +/// executed. +/// +/// # Note +/// Smaller batch sizes result in less input -> output latency, but come at the cost of reduced +/// maximum sampling frequency. +/// +/// # Units +/// The units of the batch size are specified logarithmically. +/// +/// ## Example +/// With a value of 3, the number of samples per batch is 2^3 = 8. +pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3; diff --git a/src/hardware/design_parameters.rs b/src/hardware/design_parameters.rs index d04cc75..4ac28e3 100644 --- a/src/hardware/design_parameters.rs +++ b/src/hardware/design_parameters.rs @@ -42,15 +42,11 @@ pub const DDS_SYNC_CLK_DIV: u8 = 4; // The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is // equal to 10ns per tick. -// Currently, the sample rate is equal to: Fsample = 100/128 MHz ~ 800 KHz -pub const ADC_SAMPLE_TICKS_LOG2: u8 = 7; -pub const ADC_SAMPLE_TICKS: u16 = 1 << ADC_SAMPLE_TICKS_LOG2; +pub const ADC_SAMPLE_TICKS: u16 = + 1 << crate::configuration::ADC_SAMPLE_TICKS_LOG2; // The desired ADC sample processing buffer size. -pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3; -pub const SAMPLE_BUFFER_SIZE: usize = 1 << SAMPLE_BUFFER_SIZE_LOG2; +pub const SAMPLE_BUFFER_SIZE: usize = + 1 << crate::configuration::SAMPLE_BUFFER_SIZE_LOG2; pub type SampleBuffer = [u16; SAMPLE_BUFFER_SIZE]; - -// The MQTT broker IPv4 address -pub const MQTT_BROKER: [u8; 4] = [10, 34, 16, 10]; diff --git a/src/hardware/pounder/timestamp.rs b/src/hardware/pounder/timestamp.rs index 7f8241d..262af9b 100644 --- a/src/hardware/pounder/timestamp.rs +++ b/src/hardware/pounder/timestamp.rs @@ -16,7 +16,7 @@ ///! capture is simultaneously triggered. That trigger is prescaled (its rate is divided) by the ///! batch size. This results in the input capture triggering identically to when the ADC samples ///! the last sample of the batch. That sample is then available for processing by the user. -use crate::hardware::{design_parameters, timers}; +use crate::{configuration, hardware::timers}; use core::convert::TryFrom; use stm32h7xx_hal as hal; @@ -58,10 +58,8 @@ impl Timestamper { // Capture at the batch period. input_capture.configure_prescaler( - timers::Prescaler::try_from( - design_parameters::SAMPLE_BUFFER_SIZE_LOG2, - ) - .unwrap(), + timers::Prescaler::try_from(configuration::SAMPLE_BUFFER_SIZE_LOG2) + .unwrap(), ); Self { diff --git a/src/hardware/system_timer.rs b/src/hardware/system_timer.rs index 7b445c4..a26d9e9 100644 --- a/src/hardware/system_timer.rs +++ b/src/hardware/system_timer.rs @@ -45,10 +45,11 @@ impl SystemTimer { } impl rtic::Monotonic for SystemTimer { - // Instants are stored in 32-bit signed integers. With a 10KHz tick rate, this means an - // instant can store up to ~59 hours of time before overflowing. + /// Instants are stored in 32-bit signed integers. With a 10KHz tick rate, this means an + /// instant can store up to ~59 hours of time before overflowing. type Instant = i32; + /// The ratio of the CPU clock to the system timer. fn ratio() -> rtic::Fraction { rtic::Fraction { // At 10KHz with a 400MHz CPU clock, the CPU clock runs 40,000 times faster than diff --git a/src/lib.rs b/src/lib.rs index 85964a7..58c85af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![cfg_attr(feature = "nightly", feature(core_intrinsics))] +pub mod configuration; pub mod hardware; pub mod net; diff --git a/src/net/data_stream.rs b/src/net/data_stream.rs index 4b926ed..e5b69ce 100644 --- a/src/net/data_stream.rs +++ b/src/net/data_stream.rs @@ -1,20 +1,15 @@ -///! Stabilizer data stream capabilities -///! -///! # Design -///! Stabilizer data streamining utilizes UDP packets to send live data streams at high throughput. -///! Packets are always sent in a best-effort fashion, and data may be dropped. Each packet contains -///! an identifier that can be used to detect any dropped data. -///! -///! The current implementation utilizes an single-producer, single-consumer queue to send data -///! between a high priority task and the UDP transmitter. -///! -///! A "batch" of data is defined to be a single item in the SPSC queue sent to the UDP transmitter -///! thread. The transmitter thread then serializes as many sequential "batches" into a single UDP -///! packet as possible. The UDP packet is also given a header indicating the starting batch -///! sequence number and the number of batches present. If the UDP transmitter encounters a -///! non-sequential batch, it does not enqueue it into the packet and instead transmits any staged -///! data. The non-sequential batch is then transmitted in a new UDP packet. This method allows a -///! receiver to detect dropped batches (e.g. due to processing overhead). +//! Stabilizer data stream capabilities +//! +//! # Design +//! Data streamining utilizes UDP packets to send live data streams at high throughput. +//! Packets are always sent in a best-effort fashion, and data may be dropped. Each packet contains +//! an identifier that can be used to detect dropped data. +//! +//! Refer to [DataPacket] for information about the serialization format of each UDP packet. +//! +//! # Example +//! A sample Python script is available in `scripts/stream_throughput.py` to demonstrate reception +//! of livestreamed data. use heapless::spsc::{Consumer, Producer, Queue}; use miniconf::MiniconfAtomic; use serde::Deserialize; @@ -30,6 +25,15 @@ const BLOCK_BUFFER_SIZE: usize = 30; const SUBSAMPLE_RATE: usize = 1; /// Represents the destination for the UDP stream to send data to. +/// +/// # Miniconf +/// `{"ip": , "port": }` +/// +/// * `` is an array of 4 bytes. E.g. `[192, 168, 0, 1]` +/// * `` is any unsigned 16-bit value. +/// +/// ## Example +/// `{"ip": [192, 168,0, 1], "port": 1111}` #[derive(Copy, Clone, Debug, MiniconfAtomic, Deserialize, Default)] pub struct StreamTarget { pub ip: [u8; 4], @@ -125,22 +129,74 @@ impl BlockGenerator { } } +/// # Stream Packet /// Represents a single UDP packet sent by the stream. /// -/// # Packet Format -/// All data is sent in network-endian format. The format is as follows +/// A "batch" of data is defined to be the data collected for a single invocation of the DSP +/// routine. A packet is composed of as many sequential batches as can fit. /// -/// Header: -/// [0..2]: Start block ID (u16) -/// [2..3]: Num Blocks present (u8) -/// [3..4]: Batch Size (u8) +/// The packet is given a header indicating the starting batch sequence number and the number of +/// batches present. If the UDP transmitter encounters a non-sequential batch, it does not enqueue +/// it into the packet and instead transmits any staged data. The non-sequential batch is then +/// transmitted in a new UDP packet. This method allows a receiver to detect dropped batches (e.g. +/// due to processing overhead). /// -/// Following the header, batches are added sequentially. Each batch takes the form of: -/// [*0..*2]: ADC0 -/// [*2..*4]: ADC1 -/// [*4..*6]: DAC0 -/// [*6..*8]: DAC1 -struct DataPacket<'a> { +/// ## Data Format +/// +/// Data sent via UDP is sent in "blocks". Each block is a single batch of ADC/DAC codes from an +/// individual DSP processing routine. Each block is assigned a unique 16-bit identifier. The identifier +/// increments by one for each block and rolls over. All blocks in a single packet are guaranteed to +/// contain sequential identifiers. +/// +/// All data is transmitted in network-endian (big-endian) format. +/// +/// ### Quick Reference +/// +/// In the reference below, any values enclosed in parentheses represents the number of bytes used for +/// that value. E.g. "Batch size (1)" indicates 1 byte is used to represent the batch size. +/// ``` +/// # UDP packets take the following form +///
,,[, ...] +/// +/// # The header takes the following form +///
= ,, +/// +/// # Each batch takes the following form +/// = ,,, +/// +/// # Where +/// = , ... +/// ``` +/// +/// ### Packet Format +/// Multiple blocks are sent in a single UDP packet simultaneously. Each UDP packet transmitted +/// contains a header followed by the serialized data blocks. +/// ``` +///
,,[, ...] +/// ``` +/// +/// ### Header +/// A header takes the following form: +/// * The starting block ID (2 bytes) +/// * The number of blocks present in the packet (1 byte) +/// * The size of each bach in samples (1 byte) +/// +/// ``` +/// ,, +/// ``` +/// +/// ### Data Blocks +/// Following the header, each block is sequentially serialized. Each block takes the following form: +/// ``` +/// ,,, +/// ``` +/// +/// Where `` is an array of N 16-bit ADC/DAC samples. The number of samples is provided in the +/// header. +/// +/// ADC and DAC codes are transmitted in raw machine-code format. Please refer to the datasheet for the +/// ADC and DAC if you need to convert these to voltages. +pub struct DataPacket<'a> { buf: &'a mut [u8], subsample_rate: usize, start_id: Option, diff --git a/src/net/miniconf_client.rs b/src/net/miniconf_client.rs index 4bdd2b1..0caf8d0 100644 --- a/src/net/miniconf_client.rs +++ b/src/net/miniconf_client.rs @@ -14,7 +14,7 @@ use heapless::String; use log::info; use super::{MqttMessage, NetworkReference, SettingsResponse, UpdateState}; -use crate::hardware::design_parameters::MQTT_BROKER; +use crate::configuration::MQTT_BROKER; /// MQTT settings interface. pub struct MiniconfClient diff --git a/src/net/telemetry.rs b/src/net/telemetry.rs index 40a5ce3..ffb0d58 100644 --- a/src/net/telemetry.rs +++ b/src/net/telemetry.rs @@ -15,9 +15,8 @@ use minimq::QoS; use serde::Serialize; use super::NetworkReference; -use crate::hardware::{ - adc::AdcCode, afe::Gain, dac::DacCode, design_parameters::MQTT_BROKER, -}; +use crate::configuration::MQTT_BROKER; +use crate::hardware::{adc::AdcCode, afe::Gain, dac::DacCode}; /// The telemetry client for reporting telemetry data over MQTT. pub struct TelemetryClient { @@ -49,9 +48,14 @@ pub struct TelemetryBuffer { /// overhead. #[derive(Serialize)] pub struct Telemetry { - adcs: [f32; 2], - dacs: [f32; 2], - digital_inputs: [bool; 2], + /// Most recent input voltage measurement. + pub adcs: [f32; 2], + + /// Most recent output voltage. + pub dacs: [f32; 2], + + /// Most recent digital input assertion state. + pub digital_inputs: [bool; 2], } impl Default for TelemetryBuffer { diff --git a/stabilizer_pid.png b/stabilizer_pid.png index 981112f..186e09b 100644 Binary files a/stabilizer_pid.png and b/stabilizer_pid.png differ