Merge branch 'master' into feature/scan-mode
1
.github/bors.toml
vendored
@ -5,5 +5,6 @@ status = [
|
|||||||
"style",
|
"style",
|
||||||
"test (stable)",
|
"test (stable)",
|
||||||
"compile (stable)",
|
"compile (stable)",
|
||||||
|
"doc",
|
||||||
"HITL Run Status"
|
"HITL Run Status"
|
||||||
]
|
]
|
||||||
|
54
.github/workflows/ci.yml
vendored
@ -83,3 +83,57 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: bench
|
command: bench
|
||||||
args: --package dsp --target=x86_64-unknown-linux-gnu
|
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
|
/target
|
||||||
|
docs/_site/
|
||||||
|
52
Cargo.lock
generated
@ -38,7 +38,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b9a69a963b70ddacfcd382524f72a4576f359af9334b3bf48a79566590bb8bfa"
|
checksum = "b9a69a963b70ddacfcd382524f72a4576f359af9334b3bf48a79566590bb8bfa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitrate",
|
"bitrate",
|
||||||
"cortex-m 0.7.2",
|
"cortex-m 0.7.3",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30302dda7a66f8c55932ebf208f7def840743ff64d495e9ceffcd97c18f11d39"
|
checksum = "30302dda7a66f8c55932ebf208f7def840743ff64d495e9ceffcd97c18f11d39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m 0.7.2",
|
"cortex-m 0.7.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -126,15 +126,15 @@ dependencies = [
|
|||||||
"aligned",
|
"aligned",
|
||||||
"bare-metal 0.2.5",
|
"bare-metal 0.2.5",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
"cortex-m 0.7.2",
|
"cortex-m 0.7.3",
|
||||||
"volatile-register",
|
"volatile-register",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m"
|
name = "cortex-m"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "643a210c1bdc23d0db511e2a576082f4ff4dcae9d0c37f50b431b8f8439d6d6b"
|
checksum = "2ac919ef424449ec8c08d515590ce15d9262c0ca5f0da5b0c901e971a3b783b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 0.2.5",
|
"bare-metal 0.2.5",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
@ -162,9 +162,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m-rtic"
|
name = "cortex-m-rtic"
|
||||||
version = "0.5.6"
|
version = "0.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa43f63284b363ac64f9ce5221a0f593b54f73258aba8e1a88c1feed8efdb664"
|
checksum = "9845c4c7f7af19e216a2d00345f7f1507b8907b85cd551e403d68baeec342bb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m 0.6.7",
|
"cortex-m 0.6.7",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
@ -176,9 +176,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m-rtic-macros"
|
name = "cortex-m-rtic-macros"
|
||||||
version = "0.5.2"
|
version = "0.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a1a6a4c9550373038c0e21a78d44d529bd697c25bbf6b8004bddc6e63b119c7"
|
checksum = "cc874eda99515b15e67f03562726a530388f454431096d30131051b52b840559"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -200,7 +200,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_miniconf"
|
name = "derive_miniconf"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -250,7 +250,7 @@ version = "0.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db9efecb57ab54fa918730f2874d7d37647169c50fa1357fecb81abee840b113"
|
checksum = "db9efecb57ab54fa918730f2874d7d37647169c50fa1357fecb81abee840b113"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heapless 0.7.2",
|
"heapless 0.7.3",
|
||||||
"nb 1.0.0",
|
"nb 1.0.0",
|
||||||
"no-std-net",
|
"no-std-net",
|
||||||
]
|
]
|
||||||
@ -352,9 +352,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heapless"
|
name = "heapless"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e44a9fba8877c568fc30457f5639d0aa7f57e494e4cdbc464150740a66c6613a"
|
checksum = "34e26526e7168021f34243a3c8faac4dc4f938cde75a0f9b8e373cca5eb4e7ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-polyfill",
|
"atomic-polyfill",
|
||||||
"hash32 0.2.1",
|
"hash32 0.2.1",
|
||||||
@ -414,7 +414,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "miniconf"
|
name = "miniconf"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"derive_miniconf",
|
"derive_miniconf",
|
||||||
"serde",
|
"serde",
|
||||||
@ -429,7 +429,7 @@ dependencies = [
|
|||||||
"bit_field",
|
"bit_field",
|
||||||
"embedded-nal",
|
"embedded-nal",
|
||||||
"enum-iterator",
|
"enum-iterator",
|
||||||
"heapless 0.7.2",
|
"heapless 0.7.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -509,9 +509,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_enum"
|
name = "num_enum"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066"
|
checksum = "e5adf0198d427ee515335639f275e806ca01acf9f07d7cf14bb36a10532a6169"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derivative",
|
"derivative",
|
||||||
"num_enum_derive",
|
"num_enum_derive",
|
||||||
@ -519,9 +519,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_enum_derive"
|
name = "num_enum_derive"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e"
|
checksum = "b1def5a3f69d4707d8a040b12785b98029a39e8c610ae685c7f6265669767482"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -645,7 +645,7 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "065d6058bb1204f51a562a67209e1817cf714759d5cf845aa45c75fa7b0b9d9b"
|
checksum = "065d6058bb1204f51a562a67209e1817cf714759d5cf845aa45c75fa7b0b9d9b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m 0.7.2",
|
"cortex-m 0.7.3",
|
||||||
"ufmt-write",
|
"ufmt-write",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -721,7 +721,7 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8014aeea272bca0f0779778d43253f2f3375b414185b30e6ecc4d3e4a9994781"
|
checksum = "8014aeea272bca0f0779778d43253f2f3375b414185b30e6ecc4d3e4a9994781"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heapless 0.7.2",
|
"heapless 0.7.3",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@ -763,7 +763,7 @@ version = "0.1.0"
|
|||||||
source = "git+https://github.com/quartiq/smoltcp-nal.git?rev=5baf55f#5baf55fafbfe2c08d9fe56c836171e9d2fb468e8"
|
source = "git+https://github.com/quartiq/smoltcp-nal.git?rev=5baf55f#5baf55fafbfe2c08d9fe56c836171e9d2fb468e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-nal",
|
"embedded-nal",
|
||||||
"heapless 0.7.2",
|
"heapless 0.7.3",
|
||||||
"nanorand",
|
"nanorand",
|
||||||
"smoltcp",
|
"smoltcp",
|
||||||
]
|
]
|
||||||
@ -774,12 +774,12 @@ version = "0.5.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ad9959",
|
"ad9959",
|
||||||
"asm-delay",
|
"asm-delay",
|
||||||
"cortex-m 0.7.2",
|
"cortex-m 0.7.3",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"cortex-m-rtic",
|
"cortex-m-rtic",
|
||||||
"dsp",
|
"dsp",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
"heapless 0.7.2",
|
"heapless 0.7.3",
|
||||||
"log",
|
"log",
|
||||||
"mcp23017",
|
"mcp23017",
|
||||||
"miniconf",
|
"miniconf",
|
||||||
@ -810,7 +810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8b672c837e0ee8158ecc7fce0f9a948dd0693a9c588338e728d14b73307a0b7d"
|
checksum = "8b672c837e0ee8158ecc7fce0f9a948dd0693a9c588338e728d14b73307a0b7d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 0.2.5",
|
"bare-metal 0.2.5",
|
||||||
"cortex-m 0.7.2",
|
"cortex-m 0.7.3",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"vcell",
|
"vcell",
|
||||||
]
|
]
|
||||||
@ -822,7 +822,7 @@ source = "git+https://github.com/quartiq/stm32h7xx-hal.git?rev=33aa67d#33aa67d74
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 1.0.0",
|
"bare-metal 1.0.0",
|
||||||
"cast",
|
"cast",
|
||||||
"cortex-m 0.7.2",
|
"cortex-m 0.7.3",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"embedded-dma",
|
"embedded-dma",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "stabilizer"
|
name = "stabilizer"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
resolver = "2"
|
||||||
authors = ["Robert Jördens <rj@quartiq.de>"]
|
authors = ["Robert Jördens <rj@quartiq.de>"]
|
||||||
description = "Firmware for the Sinara Stabilizer board (stm32h743, eth, poe, 2 adc, 2 dac)"
|
description = "Firmware for the Sinara Stabilizer board (stm32h743, eth, poe, 2 adc, 2 dac)"
|
||||||
categories = ["embedded", "no-std", "hardware-support", "science"]
|
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"] }
|
rtt-target = { version = "0.3", features = ["cortex-m"] }
|
||||||
serde = { version = "1.0", features = ["derive"], default-features = false }
|
serde = { version = "1.0", features = ["derive"], default-features = false }
|
||||||
heapless = { version = "0.7", features = ["serde"] }
|
heapless = { version = "0.7", features = ["serde"] }
|
||||||
cortex-m-rtic = "0.5.6"
|
cortex-m-rtic = "0.5.7"
|
||||||
embedded-hal = "0.2.5"
|
embedded-hal = "0.2.5"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
asm-delay = "0.9.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"
|
paste = "1"
|
||||||
dsp = { path = "dsp" }
|
dsp = { path = "dsp" }
|
||||||
ad9959 = { path = "ad9959" }
|
ad9959 = { path = "ad9959" }
|
||||||
@ -70,7 +71,7 @@ rev = "a2e3ad5"
|
|||||||
|
|
||||||
[patch.crates-io.miniconf]
|
[patch.crates-io.miniconf]
|
||||||
git = "https://github.com/quartiq/miniconf.git"
|
git = "https://github.com/quartiq/miniconf.git"
|
||||||
rev = "2750533"
|
rev = "9c826f8"
|
||||||
|
|
||||||
[dependencies.smoltcp-nal]
|
[dependencies.smoltcp-nal]
|
||||||
git = "https://github.com/quartiq/smoltcp-nal.git"
|
git = "https://github.com/quartiq/smoltcp-nal.git"
|
||||||
|
85
README.md
@ -4,88 +4,13 @@
|
|||||||
|
|
||||||
# Stabilizer Firmware
|
# Stabilizer Firmware
|
||||||
|
|
||||||
## Hardware
|
|
||||||
|
|
||||||
[![Hardware](https://github.com/sinara-hw/Stabilizer/wiki/Stabilizer_v1.0_top_small.jpg)](https://github.com/sinara-hw/Stabilizer)
|
|
||||||
|
|
||||||
## Applications
|
## 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.
|
Check out the [Documentation](https://quartiq.de/stabilizer) for more information on usage,
|
||||||
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).
|
configuration, and development.
|
||||||
An application can compose and configure these hardware and software components to implement different use cases.
|
|
||||||
Several applications are provides by default:
|
|
||||||
|
|
||||||
### 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
|
[![Pounder](https://user-images.githubusercontent.com/1338946/125936814-3664aa2d-a530-4c85-9393-999a7173424e.png)](https://github.com/sinara-hw/Pounder/wiki)
|
||||||
* 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.
|
|
||||||
|
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]
|
[package]
|
||||||
name = "dsp"
|
name = "dsp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
resolver = "2"
|
||||||
authors = ["Robert Jördens <rj@quartiq.de>"]
|
authors = ["Robert Jördens <rj@quartiq.de>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ include!(concat!(env!("OUT_DIR"), "/cossin_table.rs"));
|
|||||||
|
|
||||||
/// Compute the cosine and sine of an angle.
|
/// Compute the cosine and sine of an angle.
|
||||||
/// This is ported from the MiSoC cossin core.
|
/// 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
|
/// # Arguments
|
||||||
/// * `phase` - 32-bit phase.
|
/// * `phase` - 32-bit phase.
|
||||||
|
@ -39,6 +39,19 @@ pub type Vec5 = [f32; 5];
|
|||||||
/// Therefore it can trivially implement bump-less transfer.
|
/// Therefore it can trivially implement bump-less transfer.
|
||||||
/// * Cascading multiple IIR filters allows stable and robust
|
/// * Cascading multiple IIR filters allows stable and robust
|
||||||
/// implementation of transfer functions beyond bequadratic terms.
|
/// 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)]
|
#[derive(Copy, Clone, Debug, Default, Deserialize, MiniconfAtomic)]
|
||||||
pub struct IIR {
|
pub struct IIR {
|
||||||
pub ba: Vec5,
|
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)]
|
#![deny(warnings)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
@ -35,11 +62,63 @@ const IIR_CASCADE_LENGTH: usize = 1;
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Miniconf)]
|
#[derive(Clone, Copy, Debug, Deserialize, Miniconf)]
|
||||||
pub struct Settings {
|
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],
|
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],
|
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,
|
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,
|
force_hold: bool,
|
||||||
|
|
||||||
|
/// Specifies the telemetry output period in seconds.
|
||||||
|
///
|
||||||
|
/// # Path
|
||||||
|
/// `telemetry_period`
|
||||||
|
///
|
||||||
|
/// # Value
|
||||||
|
/// Any non-zero value less than 65536.
|
||||||
telemetry_period: u16,
|
telemetry_period: u16,
|
||||||
|
|
||||||
|
/// Specifies the target for data livestreaming.
|
||||||
|
///
|
||||||
|
/// # Path
|
||||||
|
/// `stream_target`
|
||||||
|
///
|
||||||
|
/// # Value
|
||||||
|
/// See [StreamTarget#miniconf]
|
||||||
stream_target: StreamTarget,
|
stream_target: StreamTarget,
|
||||||
signal_generator: [signal_generator::BasicConfig; 2],
|
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)]
|
#![deny(warnings)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
@ -8,6 +34,7 @@ use mutex_trait::prelude::*;
|
|||||||
|
|
||||||
use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL};
|
use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL};
|
||||||
use stabilizer::{
|
use stabilizer::{
|
||||||
|
configuration,
|
||||||
hardware::{
|
hardware::{
|
||||||
self,
|
self,
|
||||||
adc::{Adc0Input, Adc1Input, AdcCode},
|
adc::{Adc0Input, Adc1Input, AdcCode},
|
||||||
@ -32,35 +59,118 @@ use stabilizer::{
|
|||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, Miniconf)]
|
#[derive(Copy, Clone, Debug, Deserialize, Miniconf)]
|
||||||
enum Conf {
|
enum Conf {
|
||||||
|
/// Output the lockin magnitude.
|
||||||
Magnitude,
|
Magnitude,
|
||||||
|
/// Output the phase of the lockin
|
||||||
Phase,
|
Phase,
|
||||||
|
/// Output the lockin reference frequency as a sinusoid
|
||||||
ReferenceFrequency,
|
ReferenceFrequency,
|
||||||
|
/// Output the logarithmic power of the lockin
|
||||||
LogPower,
|
LogPower,
|
||||||
|
/// Output the in-phase component of the lockin signal.
|
||||||
InPhase,
|
InPhase,
|
||||||
|
/// Output the quadrature component of the lockin signal.
|
||||||
Quadrature,
|
Quadrature,
|
||||||
|
/// Output the lockin internal modulation frequency as a sinusoid
|
||||||
Modulation,
|
Modulation,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Miniconf, Deserialize, PartialEq)]
|
#[derive(Copy, Clone, Debug, Miniconf, Deserialize, PartialEq)]
|
||||||
enum LockinMode {
|
enum LockinMode {
|
||||||
|
/// Utilize an internally generated reference for demodulation
|
||||||
Internal,
|
Internal,
|
||||||
|
/// Utilize an external modulation signal supplied to DI0
|
||||||
External,
|
External,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, Miniconf)]
|
#[derive(Copy, Clone, Debug, Deserialize, Miniconf)]
|
||||||
pub struct Settings {
|
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],
|
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,
|
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],
|
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,
|
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,
|
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,
|
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],
|
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,
|
telemetry_period: u16,
|
||||||
|
|
||||||
|
/// Specifies the target for data livestreaming.
|
||||||
|
///
|
||||||
|
/// # Path
|
||||||
|
/// `stream_target`
|
||||||
|
///
|
||||||
|
/// # Value
|
||||||
|
/// See [StreamTarget#miniconf]
|
||||||
stream_target: StreamTarget,
|
stream_target: StreamTarget,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,8 +233,8 @@ const APP: () = {
|
|||||||
let settings = Settings::default();
|
let settings = Settings::default();
|
||||||
|
|
||||||
let pll = RPLL::new(
|
let pll = RPLL::new(
|
||||||
design_parameters::ADC_SAMPLE_TICKS_LOG2
|
configuration::ADC_SAMPLE_TICKS_LOG2
|
||||||
+ design_parameters::SAMPLE_BUFFER_SIZE_LOG2,
|
+ configuration::SAMPLE_BUFFER_SIZE_LOG2,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Spawn a settings and telemetry update for default settings.
|
// Spawn a settings and telemetry update for default settings.
|
||||||
@ -213,8 +323,7 @@ const APP: () = {
|
|||||||
);
|
);
|
||||||
(
|
(
|
||||||
pll_phase,
|
pll_phase,
|
||||||
(pll_frequency
|
(pll_frequency >> configuration::SAMPLE_BUFFER_SIZE_LOG2)
|
||||||
>> design_parameters::SAMPLE_BUFFER_SIZE_LOG2)
|
|
||||||
as i32,
|
as i32,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -222,7 +331,7 @@ const APP: () = {
|
|||||||
// Reference phase and frequency are known.
|
// Reference phase and frequency are known.
|
||||||
(
|
(
|
||||||
1i32 << 30,
|
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
|
// The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is
|
||||||
// equal to 10ns per tick.
|
// equal to 10ns per tick.
|
||||||
// Currently, the sample rate is equal to: Fsample = 100/128 MHz ~ 800 KHz
|
pub const ADC_SAMPLE_TICKS: u16 =
|
||||||
pub const ADC_SAMPLE_TICKS_LOG2: u8 = 7;
|
1 << crate::configuration::ADC_SAMPLE_TICKS_LOG2;
|
||||||
pub const ADC_SAMPLE_TICKS: u16 = 1 << ADC_SAMPLE_TICKS_LOG2;
|
|
||||||
|
|
||||||
// The desired ADC sample processing buffer size.
|
// The desired ADC sample processing buffer size.
|
||||||
pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3;
|
pub const SAMPLE_BUFFER_SIZE: usize =
|
||||||
pub const SAMPLE_BUFFER_SIZE: usize = 1 << SAMPLE_BUFFER_SIZE_LOG2;
|
1 << crate::configuration::SAMPLE_BUFFER_SIZE_LOG2;
|
||||||
|
|
||||||
pub type SampleBuffer = [u16; SAMPLE_BUFFER_SIZE];
|
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
|
///! 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
|
///! 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.
|
///! 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 core::convert::TryFrom;
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
@ -58,10 +58,8 @@ impl Timestamper {
|
|||||||
|
|
||||||
// Capture at the batch period.
|
// Capture at the batch period.
|
||||||
input_capture.configure_prescaler(
|
input_capture.configure_prescaler(
|
||||||
timers::Prescaler::try_from(
|
timers::Prescaler::try_from(configuration::SAMPLE_BUFFER_SIZE_LOG2)
|
||||||
design_parameters::SAMPLE_BUFFER_SIZE_LOG2,
|
.unwrap(),
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -45,10 +45,11 @@ impl SystemTimer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl rtic::Monotonic for SystemTimer {
|
impl rtic::Monotonic for SystemTimer {
|
||||||
// Instants are stored in 32-bit signed integers. With a 10KHz tick rate, this means an
|
/// 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.
|
/// instant can store up to ~59 hours of time before overflowing.
|
||||||
type Instant = i32;
|
type Instant = i32;
|
||||||
|
|
||||||
|
/// The ratio of the CPU clock to the system timer.
|
||||||
fn ratio() -> rtic::Fraction {
|
fn ratio() -> rtic::Fraction {
|
||||||
rtic::Fraction {
|
rtic::Fraction {
|
||||||
// At 10KHz with a 400MHz CPU clock, the CPU clock runs 40,000 times faster than
|
// At 10KHz with a 400MHz CPU clock, the CPU clock runs 40,000 times faster than
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
||||||
|
|
||||||
|
pub mod configuration;
|
||||||
pub mod hardware;
|
pub mod hardware;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
|
@ -1,20 +1,15 @@
|
|||||||
///! Stabilizer data stream capabilities
|
//! Stabilizer data stream capabilities
|
||||||
///!
|
//!
|
||||||
///! # Design
|
//! # Design
|
||||||
///! Stabilizer data streamining utilizes UDP packets to send live data streams at high throughput.
|
//! 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
|
//! 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.
|
//! an identifier that can be used to detect dropped data.
|
||||||
///!
|
//!
|
||||||
///! The current implementation utilizes an single-producer, single-consumer queue to send data
|
//! Refer to [DataPacket] for information about the serialization format of each UDP packet.
|
||||||
///! between a high priority task and the UDP transmitter.
|
//!
|
||||||
///!
|
//! # Example
|
||||||
///! A "batch" of data is defined to be a single item in the SPSC queue sent to the UDP transmitter
|
//! A sample Python script is available in `scripts/stream_throughput.py` to demonstrate reception
|
||||||
///! thread. The transmitter thread then serializes as many sequential "batches" into a single UDP
|
//! of livestreamed data.
|
||||||
///! 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).
|
|
||||||
use heapless::spsc::{Consumer, Producer, Queue};
|
use heapless::spsc::{Consumer, Producer, Queue};
|
||||||
use miniconf::MiniconfAtomic;
|
use miniconf::MiniconfAtomic;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -30,6 +25,15 @@ const BLOCK_BUFFER_SIZE: usize = 30;
|
|||||||
const SUBSAMPLE_RATE: usize = 1;
|
const SUBSAMPLE_RATE: usize = 1;
|
||||||
|
|
||||||
/// Represents the destination for the UDP stream to send data to.
|
/// 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)]
|
#[derive(Copy, Clone, Debug, MiniconfAtomic, Deserialize, Default)]
|
||||||
pub struct StreamTarget {
|
pub struct StreamTarget {
|
||||||
pub ip: [u8; 4],
|
pub ip: [u8; 4],
|
||||||
@ -125,22 +129,74 @@ impl BlockGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Stream Packet
|
||||||
/// Represents a single UDP packet sent by the stream.
|
/// Represents a single UDP packet sent by the stream.
|
||||||
///
|
///
|
||||||
/// # Packet Format
|
/// A "batch" of data is defined to be the data collected for a single invocation of the DSP
|
||||||
/// All data is sent in network-endian format. The format is as follows
|
/// routine. A packet is composed of as many sequential batches as can fit.
|
||||||
///
|
///
|
||||||
/// Header:
|
/// The packet is given a header indicating the starting batch sequence number and the number of
|
||||||
/// [0..2]: Start block ID (u16)
|
/// batches present. If the UDP transmitter encounters a non-sequential batch, it does not enqueue
|
||||||
/// [2..3]: Num Blocks present (u8) <N>
|
/// it into the packet and instead transmits any staged data. The non-sequential batch is then
|
||||||
/// [3..4]: Batch Size (u8) <BS>
|
/// 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:
|
/// ## Data Format
|
||||||
/// [<BS>*0..<BS>*2]: ADC0
|
///
|
||||||
/// [<BS>*2..<BS>*4]: ADC1
|
/// Data sent via UDP is sent in "blocks". Each block is a single batch of ADC/DAC codes from an
|
||||||
/// [<BS>*4..<BS>*6]: DAC0
|
/// individual DSP processing routine. Each block is assigned a unique 16-bit identifier. The identifier
|
||||||
/// [<BS>*6..<BS>*8]: DAC1
|
/// increments by one for each block and rolls over. All blocks in a single packet are guaranteed to
|
||||||
struct DataPacket<'a> {
|
/// 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],
|
buf: &'a mut [u8],
|
||||||
subsample_rate: usize,
|
subsample_rate: usize,
|
||||||
start_id: Option<u16>,
|
start_id: Option<u16>,
|
||||||
|
@ -14,7 +14,7 @@ use heapless::String;
|
|||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use super::{MqttMessage, NetworkReference, SettingsResponse, UpdateState};
|
use super::{MqttMessage, NetworkReference, SettingsResponse, UpdateState};
|
||||||
use crate::hardware::design_parameters::MQTT_BROKER;
|
use crate::configuration::MQTT_BROKER;
|
||||||
|
|
||||||
/// MQTT settings interface.
|
/// MQTT settings interface.
|
||||||
pub struct MiniconfClient<S>
|
pub struct MiniconfClient<S>
|
||||||
|
@ -15,9 +15,8 @@ use minimq::QoS;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::NetworkReference;
|
use super::NetworkReference;
|
||||||
use crate::hardware::{
|
use crate::configuration::MQTT_BROKER;
|
||||||
adc::AdcCode, afe::Gain, dac::DacCode, design_parameters::MQTT_BROKER,
|
use crate::hardware::{adc::AdcCode, afe::Gain, dac::DacCode};
|
||||||
};
|
|
||||||
|
|
||||||
/// The telemetry client for reporting telemetry data over MQTT.
|
/// The telemetry client for reporting telemetry data over MQTT.
|
||||||
pub struct TelemetryClient<T: Serialize> {
|
pub struct TelemetryClient<T: Serialize> {
|
||||||
@ -49,9 +48,14 @@ pub struct TelemetryBuffer {
|
|||||||
/// overhead.
|
/// overhead.
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct Telemetry {
|
pub struct Telemetry {
|
||||||
adcs: [f32; 2],
|
/// Most recent input voltage measurement.
|
||||||
dacs: [f32; 2],
|
pub adcs: [f32; 2],
|
||||||
digital_inputs: [bool; 2],
|
|
||||||
|
/// Most recent output voltage.
|
||||||
|
pub dacs: [f32; 2],
|
||||||
|
|
||||||
|
/// Most recent digital input assertion state.
|
||||||
|
pub digital_inputs: [bool; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TelemetryBuffer {
|
impl Default for TelemetryBuffer {
|
||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 130 B |