Merge branch 'master' into feature/scan-mode
1
.github/bors.toml
vendored
@ -5,5 +5,6 @@ status = [
|
||||
"style",
|
||||
"test (stable)",
|
||||
"compile (stable)",
|
||||
"doc",
|
||||
"HITL Run Status"
|
||||
]
|
||||
|
54
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
||||
|
36
.github/workflows/release-docs.yml
vendored
Normal file
@ -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
|
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/target
|
||||
docs/_site/
|
||||
|
52
Cargo.lock
generated
@ -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",
|
||||
|
@ -1,6 +1,7 @@
|
||||
[package]
|
||||
name = "stabilizer"
|
||||
version = "0.5.0"
|
||||
resolver = "2"
|
||||
authors = ["Robert Jördens <rj@quartiq.de>"]
|
||||
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"
|
||||
|
85
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)
|
||||
|
4
docs/Gemfile
Normal file
@ -0,0 +1,4 @@
|
||||
source 'https://rubygems.org'
|
||||
gem "just-the-docs"
|
||||
gem "jekyll-remote-theme"
|
||||
gem "html-proofer"
|
102
docs/Gemfile.lock
Normal file
@ -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
|
20
docs/README.md
Normal file
@ -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
|
||||
```
|
35
docs/Rakefile
Normal file
@ -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
|
18
docs/_config.yml
Normal file
@ -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
|
BIN
docs/assets/mqtt-explorer.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
docs/assets/stabilizer-jtag.jpg
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
docs/assets/stabilizer-logo.png
Normal file
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
BIN
docs/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
57
docs/index.md
Normal file
@ -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).
|
157
docs/pages/getting-started.md
Normal file
@ -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.
|
97
docs/pages/usage.md
Normal file
@ -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/<app>/<mac-address>`, where `<app>` is the name of the application and
|
||||
`<mac-address>` 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.
|
@ -1,6 +1,7 @@
|
||||
[package]
|
||||
name = "dsp"
|
||||
version = "0.1.0"
|
||||
resolver = "2"
|
||||
authors = ["Robert Jördens <rj@quartiq.de>"]
|
||||
edition = "2018"
|
||||
|
||||
|
@ -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)
|
||||
/// <https://github.com/m-labs/misoc/blob/master/misoc/cores/cossin.py>
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `phase` - 32-bit phase.
|
||||
|
@ -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 <https://arxiv.org/abs/1508.06319>
|
||||
#[derive(Copy, Clone, Debug, Default, Deserialize, MiniconfAtomic)]
|
||||
pub struct IIR {
|
||||
pub ba: Vec5,
|
||||
|
@ -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/<n>`
|
||||
///
|
||||
/// * <n> specifies which channel to configure. <n> := [0, 1]
|
||||
///
|
||||
/// # Value
|
||||
/// Any of the variants of [Gain] enclosed in double quotes.
|
||||
afe: [Gain; 2],
|
||||
|
||||
/// Configure the IIR filter parameters.
|
||||
///
|
||||
/// # Path
|
||||
/// `iir_ch/<n>/<m>`
|
||||
///
|
||||
/// * <n> specifies which channel to configure. <n> := [0, 1]
|
||||
/// * <m> specifies which cascade to configure. <m> := [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],
|
||||
}
|
||||
|
@ -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/<n>`
|
||||
///
|
||||
/// * <n> specifies which channel to configure. <n> := [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/<n>`
|
||||
///
|
||||
/// * <n> specifies which channel to configure. <n> := [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/<n>`
|
||||
///
|
||||
/// * <n> specifies which channel to configure. <n> := [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),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
41
src/configuration.rs
Normal file
@ -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;
|
@ -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];
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
||||
|
||||
pub mod configuration;
|
||||
pub mod hardware;
|
||||
pub mod net;
|
||||
|
@ -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": <addr>, "port": <port>}`
|
||||
///
|
||||
/// * `<addr>` is an array of 4 bytes. E.g. `[192, 168, 0, 1]`
|
||||
/// * `<port>` 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) <N>
|
||||
/// [3..4]: Batch Size (u8) <BS>
|
||||
/// 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:
|
||||
/// [<BS>*0..<BS>*2]: ADC0
|
||||
/// [<BS>*2..<BS>*4]: ADC1
|
||||
/// [<BS>*4..<BS>*6]: DAC0
|
||||
/// [<BS>*6..<BS>*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
|
||||
/// <Header>,<Batch 1>,[<Batch 2>, ...<Batch N>]
|
||||
///
|
||||
/// # The header takes the following form
|
||||
/// <Header> = <Starting ID (2)>,<Number blocks [N] (1)>,<Batch size [BS] (1)>
|
||||
///
|
||||
/// # Each batch takes the following form
|
||||
/// <Batch N> = <ADC0>,<ADC1>,<DAC0>,<DAC1>
|
||||
///
|
||||
/// # Where
|
||||
/// <ADCx/DACx> = <Sample 1 (2)>, ...<Sample BS (2)>
|
||||
/// ```
|
||||
///
|
||||
/// ### 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>,<Batch 1>,[<Batch 2>, ...<Batch N>]
|
||||
/// ```
|
||||
///
|
||||
/// ### 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)
|
||||
///
|
||||
/// ```
|
||||
/// <Starting ID (2)>,<N blocks (1)>,<Batch size (1)>
|
||||
/// ```
|
||||
///
|
||||
/// ### Data Blocks
|
||||
/// Following the header, each block is sequentially serialized. Each block takes the following form:
|
||||
/// ```
|
||||
/// <ADC0 samples>,<ADC1 samples>,<DAC0 samples>,<DAC1 samples>
|
||||
/// ```
|
||||
///
|
||||
/// Where `<XXX samples>` 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<u16>,
|
||||
|
@ -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<S>
|
||||
|
@ -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<T: Serialize> {
|
||||
@ -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 {
|
||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 130 B |