Squashing manual changes
|
@ -83,3 +83,29 @@ 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-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
|
||||||
|
args: --dir target/thumbv7em-none-eabihf/doc
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
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
|
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
docs/_site/
|
||||||
|
|
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
|
|
@ -70,7 +70,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"
|
||||||
|
|
75
README.md
|
@ -13,79 +13,6 @@
|
||||||
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.
|
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).
|
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.
|
An application can compose and configure these hardware and software components to implement different use cases.
|
||||||
Several applications are provides by default:
|
|
||||||
|
|
||||||
### Dual-IIR
|
Check out the [Documentation](https://quartiq.de/stabilizer) for more information on usage.
|
||||||
|
|
||||||
![Flow diagram](stabilizer_pid.svg)
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
gem "just-the-docs"
|
||||||
|
gem "jekyll-remote-theme"
|
|
@ -0,0 +1,81 @@
|
||||||
|
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)
|
||||||
|
eventmachine (1.2.7-x64-mingw32)
|
||||||
|
ffi (1.15.3-x64-mingw32)
|
||||||
|
forwardable-extended (2.6.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)
|
||||||
|
pathutil (0.16.2)
|
||||||
|
forwardable-extended (~> 2.6)
|
||||||
|
public_suffix (4.0.6)
|
||||||
|
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)
|
||||||
|
unicode-display_width (1.7.0)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
x64-mingw32
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
jekyll-remote-theme
|
||||||
|
just-the-docs
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.2.22
|
|
@ -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` 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 --bins
|
||||||
|
cp -r target/thumbv7em-none-eabihf/doc docs/firmware
|
||||||
|
```
|
|
@ -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
|
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,55 @@
|
||||||
|
---
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
The Stabilizer hardware is managed via a [separate repository](https://github.com/sinara-hw/Stabilizer).
|
||||||
|
|
||||||
|
[![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, 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 | Documentation | Application Description |
|
||||||
|
| :---: | :--: | :---- |
|
||||||
|
| `dual-iir` | [Link]({{site.baseurl}}/firmware/dual_iir/index.html) | Two channel biquad IIR filter
|
||||||
|
| `lockin` | [Link]({{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).
|
|
@ -0,0 +1,162 @@
|
||||||
|
---
|
||||||
|
title: Getting Started
|
||||||
|
layout: default
|
||||||
|
permalink: /getting-started
|
||||||
|
nav_order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
{: .no_toc .text-delta }
|
||||||
|
|
||||||
|
1. TOC
|
||||||
|
{:toc}
|
||||||
|
---
|
||||||
|
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
Getting started requires a few steps:
|
||||||
|
There are a number of steps that must be completed when first getting started with Stabilizer.
|
||||||
|
1. Configure the firmware
|
||||||
|
* This requires updating any parameters, such as static IP addresses and
|
||||||
|
sampling rate.
|
||||||
|
1. Build the application
|
||||||
|
* This requires compiling the code after configuration parameters have been
|
||||||
|
updated.
|
||||||
|
1. Upload the application
|
||||||
|
* Once fimrware has been built, it needs to be programmed onto the device.
|
||||||
|
1. Set up MQTT
|
||||||
|
* Stabilizer utilizes MQTT for telemetry and configuration.
|
||||||
|
|
||||||
|
The following sections will walk you through completing each of these steps.
|
||||||
|
|
||||||
|
# Program 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:
|
||||||
|
* Sample frequency
|
||||||
|
* Sample batch size
|
||||||
|
* MQTT Broker IP address
|
||||||
|
|
||||||
|
Parameters are configured by editing `src/configuration.rs`.
|
||||||
|
|
||||||
|
Refer to the [documentation]({{site.baseurl}}/firmware/stabilizer/configuration.rs) 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.
|
|
@ -0,0 +1,63 @@
|
||||||
|
---
|
||||||
|
title: Networking
|
||||||
|
layout: default
|
||||||
|
nav_order: 4
|
||||||
|
permalink: /networking/
|
||||||
|
has_children: true
|
||||||
|
has_toc: false
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
Refer to the [documentation](run-time-settings) for information on how to configure run-time
|
||||||
|
settings.
|
||||||
|
|
||||||
|
## 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](/#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](/#applications) for more information.
|
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
title: Run-Time Settings
|
||||||
|
layout: default
|
||||||
|
permalink: /networking/run-time-settings
|
||||||
|
parent: Networking
|
||||||
|
---
|
||||||
|
|
||||||
|
# Settings Configuration
|
||||||
|
|
||||||
|
Stabilizer allows for run-time settings configurations using the `miniconf.py` utility script. This
|
||||||
|
script is in the root of the Stabilizer github repository.
|
||||||
|
|
||||||
|
## 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 localhost dt/sinara/dual-iir/00-11-22-33-44-55 stream_target='{"ip": [192, 168, 0, 1], "port": 4000}'
|
||||||
|
```
|
||||||
|
|
||||||
|
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](/#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.
|
|
@ -12,7 +12,7 @@ miniconf = "0.1"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
easybench = "1.0"
|
easybench = "1.0"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
ndarray = "0.15"
|
ndarray = { default-features = false, version = "0.15" }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "micro"
|
name = "micro"
|
||||||
|
|
|
@ -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,25 @@ 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": y0, "y_min": ym, "y_max": yM, "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>
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ## Notes
|
||||||
|
/// The units of the IIR utilize 16-bit signed integers for full-scale. saturation and offset
|
||||||
|
/// parameter are given in this scale, where full-scale represents an output amplitude of 10.24
|
||||||
|
/// V.
|
||||||
#[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]
|
||||||
|
@ -34,11 +61,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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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},
|
||||||
|
@ -38,35 +65,118 @@ const DAC_SEQUENCE: [i16; design_parameters::SAMPLE_BUFFER_SIZE] =
|
||||||
|
|
||||||
#[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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,8 +238,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.
|
||||||
|
@ -204,8 +314,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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -213,7 +322,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),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 100MHz.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// With a value of 7, this corresponds to 2^7 = 128 ticks. Each tick of the 100MHz timer requires
|
||||||
|
/// 10ns.
|
||||||
|
///
|
||||||
|
/// Sampling Period = 10ns * 128 = 1.28 us
|
||||||
|
/// Sampling Frequency = 781.25KHz
|
||||||
|
///
|
||||||
|
/// Or more succinctly:
|
||||||
|
/// `F_s = 100MHz / (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,8 +48,13 @@ pub struct TelemetryBuffer {
|
||||||
/// overhead.
|
/// overhead.
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct Telemetry {
|
pub struct Telemetry {
|
||||||
|
/// Most recent input voltage measurement.
|
||||||
adcs: [f32; 2],
|
adcs: [f32; 2],
|
||||||
|
|
||||||
|
/// Most recent output voltage.
|
||||||
dacs: [f32; 2],
|
dacs: [f32; 2],
|
||||||
|
|
||||||
|
/// Most recent digital input assertion state.
|
||||||
digital_inputs: [bool; 2],
|
digital_inputs: [bool; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 130 B |