Merge branch 'master' into feature/pounder-timestamping
This commit is contained in:
commit
29a89637f8
@ -5,7 +5,7 @@ rustflags = [
|
||||
# The target (below) defaults to cortex-m4
|
||||
# There currently are two different options to go beyond that:
|
||||
# 1. cortex-m7 has the right flags and instructions (FPU) but no instruction schedule yet
|
||||
"-C", "target-cpu=cortex-m7",
|
||||
# "-C", "target-cpu=cortex-m7",
|
||||
# 2. cortex-m4 with the additional fpv5 instructions and a potentially
|
||||
# better-than-nothing instruction schedule
|
||||
"-C", "target-feature=+fp-armv8d16",
|
||||
|
49
.github/workflows/ci.yml
vendored
49
.github/workflows/ci.yml
vendored
@ -18,28 +18,18 @@ jobs:
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
target: thumbv7em-none-eabihf
|
||||
override: true
|
||||
components: rustfmt
|
||||
components: rustfmt, clippy
|
||||
- name: cargo fmt --check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
target: thumbv7em-none-eabihf
|
||||
override: true
|
||||
components: clippy
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
continue-on-error: true
|
||||
with:
|
||||
toolchain: stable
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
compile:
|
||||
@ -57,6 +47,7 @@ jobs:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
target: thumbv7em-none-eabihf
|
||||
override: true
|
||||
components: llvm-tools-preview
|
||||
- name: cargo check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@ -71,11 +62,28 @@ jobs:
|
||||
with:
|
||||
command: build
|
||||
args: --release
|
||||
- name: cargo build release+semihosting
|
||||
- name: cargo-binutils
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release --features semihosting
|
||||
command: install
|
||||
args: cargo-binutils
|
||||
- name: cargo size
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: size
|
||||
args: --release
|
||||
- name: cargo objcopy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: objcopy
|
||||
args: --release --verbose -- -O binary stabilizer-release.bin
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ matrix.toolchain == 'stable' }}
|
||||
with:
|
||||
name: stabilizer_${{ github.sha }}
|
||||
path: |
|
||||
target/*/release/stabilizer
|
||||
stabilizer-release.bin
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@ -95,6 +103,11 @@ jobs:
|
||||
with:
|
||||
command: test
|
||||
args: --package dsp --target=x86_64-unknown-linux-gnu
|
||||
- name: cargo bench
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: bench
|
||||
args: --package dsp --target=x86_64-unknown-linux-gnu
|
||||
|
||||
# Tell bors about it
|
||||
# https://github.com/rtic-rs/cortex-m-rtic/blob/8a4f9c6b8ae91bebeea0791680f89375a78bffc6/.github/workflows/build.yml#L566-L603
|
||||
@ -103,8 +116,8 @@ jobs:
|
||||
if: github.event_name == 'push' && success()
|
||||
needs:
|
||||
- style
|
||||
- clippy
|
||||
- compile
|
||||
- test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mark the job as a success
|
||||
@ -114,8 +127,8 @@ jobs:
|
||||
if: github.event_name == 'push' && !success()
|
||||
needs:
|
||||
- style
|
||||
- clippy
|
||||
- compile
|
||||
- test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mark the job as a failure
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1 @@
|
||||
/target
|
||||
/dsp/target
|
||||
.gdb_history
|
||||
|
494
Cargo.lock
generated
494
Cargo.lock
generated
@ -40,6 +40,17 @@ dependencies = [
|
||||
"embedded-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
@ -85,6 +96,24 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
@ -106,6 +135,29 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
version = "0.6.4"
|
||||
@ -185,14 +237,125 @@ dependencies = [
|
||||
"cortex-m",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70daa7ceec6cf143990669a04c7df13391d55fb27bd4079d252fca774ba244d8"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"cast",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"csv",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"const_fn",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d58633299b24b515ac72a3f869f8b91306a3cec616a602843a383acd6f9e97"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dsp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"libm",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-dma"
|
||||
version = "0.1.2"
|
||||
@ -260,6 +423,12 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.1.1"
|
||||
@ -288,6 +457,15 @@ dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.6.0"
|
||||
@ -298,6 +476,42 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.1"
|
||||
@ -310,7 +524,7 @@ version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -327,6 +541,21 @@ dependencies = [
|
||||
"embedded-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.3"
|
||||
@ -342,6 +571,31 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "panic-halt"
|
||||
version = "0.2.0"
|
||||
@ -360,9 +614,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7151b083b0664ed58ed669fcdd92f01c3d2fdbf10af4931a301474950b52bfa9"
|
||||
checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d1685fbe7beba33de0330629da9d955ac75bd54f33d7b79f9a895590124f6bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
@ -388,6 +654,55 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
|
||||
dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
|
||||
|
||||
[[package]]
|
||||
name = "rtic-core"
|
||||
version = "0.3.1"
|
||||
@ -414,6 +729,27 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
@ -448,6 +784,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622"
|
||||
dependencies = [
|
||||
"half",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.118"
|
||||
@ -459,6 +805,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.6.0"
|
||||
@ -543,12 +900,37 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
@ -581,3 +963,109 @@ checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
|
||||
dependencies = [
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
@ -24,6 +24,9 @@ maintenance = { status = "experimental" }
|
||||
features = []
|
||||
default-target = "thumbv7em-none-eabihf"
|
||||
|
||||
[workspace]
|
||||
members = ["ad9959", "dsp"]
|
||||
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.6", features = ["const-fn"] }
|
||||
cortex-m-rt = { version = "0.6", features = ["device"] }
|
||||
|
@ -1 +1 @@
|
||||
"05b1xcr9jachnih0d6i63cfjcb88xrddmr2kf4h3vfwpjf8y9w10"
|
||||
"0mrnm74wd5c1cl3av8iqndg6xrm07vs862882m59pjnrgy3z2zqj"
|
||||
|
70
dsp/Cargo.lock
generated
70
dsp/Cargo.lock
generated
@ -1,70 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "dsp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libm",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "443b4178719c5a851e1bde36ce12da21d74a0e60b4d982ec3385a933c812f0f6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
@ -8,5 +8,12 @@ edition = "2018"
|
||||
libm = "0.2.1"
|
||||
serde = { version = "1.0", features = ["derive"], default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "trig"
|
||||
harness = false
|
||||
|
||||
[features]
|
||||
nightly = []
|
||||
|
28
dsp/benches/trig.rs
Normal file
28
dsp/benches/trig.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use core::f32::consts::PI;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use dsp::trig::{atan2, cossin};
|
||||
|
||||
fn atan2_bench(c: &mut Criterion) {
|
||||
let xi = (10 << 16) as i32;
|
||||
let xf = xi as f32 / i32::MAX as f32;
|
||||
|
||||
let yi = (-26_328 << 16) as i32;
|
||||
let yf = yi as f32 / i32::MAX as f32;
|
||||
|
||||
c.bench_function("atan2(y, x)", |b| {
|
||||
b.iter(|| atan2(black_box(yi), black_box(xi)))
|
||||
});
|
||||
c.bench_function("y.atan2(x)", |b| {
|
||||
b.iter(|| black_box(yf).atan2(black_box(xf)))
|
||||
});
|
||||
}
|
||||
|
||||
fn cossin_bench(c: &mut Criterion) {
|
||||
let zi = -0x7304_2531_i32;
|
||||
let zf = zi as f32 / i32::MAX as f32 * PI;
|
||||
c.bench_function("cossin(zi)", |b| b.iter(|| cossin(black_box(zi))));
|
||||
c.bench_function("zf.sin_cos()", |b| b.iter(|| black_box(zf).sin_cos()));
|
||||
}
|
||||
|
||||
criterion_group!(benches, atan2_bench, cossin_bench);
|
||||
criterion_main!(benches);
|
46
dsp/build.rs
Normal file
46
dsp/build.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::env;
|
||||
use std::f64::consts::PI;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
|
||||
fn write_cossin_table() {
|
||||
const DEPTH: usize = 7;
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("cossin_table.rs");
|
||||
let mut file = File::create(dest_path).unwrap();
|
||||
|
||||
writeln!(file, "pub(crate) const COSSIN_DEPTH: usize = {};", DEPTH)
|
||||
.unwrap();
|
||||
write!(
|
||||
file,
|
||||
"pub(crate) const COSSIN: [(u16, u16); 1 << COSSIN_DEPTH] = ["
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Treat sin and cos as unsigned values since the sign will always be
|
||||
// positive in the range [0, pi/4).
|
||||
// No headroom for interpolation rounding error (this is needed for
|
||||
// DEPTH = 6 for example).
|
||||
const AMPLITUDE: f64 = u16::MAX as f64;
|
||||
|
||||
for i in 0..(1 << DEPTH) {
|
||||
// use midpoint samples to save one entry in the LUT
|
||||
let phase = (PI / 4. / (1 << DEPTH) as f64) * (i as f64 + 0.5);
|
||||
// add one bit accuracy to cos due to 0.5 < cos(z) <= 1 for |z| < pi/4
|
||||
let cos = ((phase.cos() - 0.5) * 2. * AMPLITUDE).round() as u16;
|
||||
let sin = (phase.sin() * AMPLITUDE).round() as u16;
|
||||
if i % 4 == 0 {
|
||||
write!(file, "\n ").unwrap();
|
||||
}
|
||||
write!(file, " ({}, {}),", cos, sin).unwrap();
|
||||
}
|
||||
writeln!(file, "\n];").unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
write_cossin_table();
|
||||
}
|
@ -1,85 +1,8 @@
|
||||
use core::ops::{Add, Mul, Neg};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{abs, copysign, macc, max, min};
|
||||
use core::f32;
|
||||
|
||||
// These are implemented here because core::f32 doesn't have them (yet).
|
||||
// They are naive and don't handle inf/nan.
|
||||
// `compiler-intrinsics`/llvm should have better (robust, universal, and
|
||||
// faster) implementations.
|
||||
|
||||
fn abs<T>(x: T) -> T
|
||||
where
|
||||
T: PartialOrd + Default + Neg<Output = T>,
|
||||
{
|
||||
if x >= T::default() {
|
||||
x
|
||||
} else {
|
||||
-x
|
||||
}
|
||||
}
|
||||
|
||||
fn copysign<T>(x: T, y: T) -> T
|
||||
where
|
||||
T: PartialOrd + Default + Neg<Output = T>,
|
||||
{
|
||||
if (x >= T::default() && y >= T::default())
|
||||
|| (x <= T::default() && y <= T::default())
|
||||
{
|
||||
x
|
||||
} else {
|
||||
-x
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
fn max<T>(x: T, y: T) -> T
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
if x > y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
fn min<T>(x: T, y: T) -> T
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
if x < y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
fn max(x: f32, y: f32) -> f32 {
|
||||
core::intrinsics::maxnumf32(x, y)
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
fn min(x: f32, y: f32) -> f32 {
|
||||
core::intrinsics::minnumf32(x, y)
|
||||
}
|
||||
|
||||
// Multiply-accumulate vectors `x` and `a`.
|
||||
//
|
||||
// A.k.a. dot product.
|
||||
// Rust/LLVM optimize this nicely.
|
||||
fn macc<T>(y0: T, x: &[T], a: &[T]) -> T
|
||||
where
|
||||
T: Add<Output = T> + Mul<Output = T> + Copy,
|
||||
{
|
||||
x.iter()
|
||||
.zip(a)
|
||||
.map(|(x, a)| *x * *a)
|
||||
.fold(y0, |y, xa| y + xa)
|
||||
}
|
||||
|
||||
/// IIR state and coefficients type.
|
||||
///
|
||||
/// To represent the IIR state (input and output memory) during the filter update
|
||||
|
58
dsp/src/iir_int.rs
Normal file
58
dsp/src/iir_int.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub type IIRState = [i32; 5];
|
||||
|
||||
fn macc(y0: i32, x: &[i32], a: &[i32], shift: u32) -> i32 {
|
||||
// Rounding bias, half up
|
||||
let y0 = ((y0 as i64) << shift) + (1 << (shift - 1));
|
||||
let y = x
|
||||
.iter()
|
||||
.zip(a)
|
||||
.map(|(x, a)| *x as i64 * *a as i64)
|
||||
.fold(y0, |y, xa| y + xa);
|
||||
(y >> shift) as i32
|
||||
}
|
||||
|
||||
/// Integer biquad IIR
|
||||
///
|
||||
/// See `dsp::iir::IIR` for general implementation details.
|
||||
/// Offset and limiting disabled to suit lowpass applications.
|
||||
/// Coefficient scaling fixed and optimized.
|
||||
#[derive(Copy, Clone, Deserialize, Serialize)]
|
||||
pub struct IIR {
|
||||
pub ba: IIRState,
|
||||
// pub y_offset: i32,
|
||||
// pub y_min: i32,
|
||||
// pub y_max: i32,
|
||||
}
|
||||
|
||||
impl IIR {
|
||||
/// Coefficient fixed point: signed Q2.30.
|
||||
/// Tailored to low-passes PI, II etc.
|
||||
const SHIFT: u32 = 30;
|
||||
|
||||
/// Feed a new input value into the filter, update the filter state, and
|
||||
/// return the new output. Only the state `xy` is modified.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `xy` - Current filter state.
|
||||
/// * `x0` - New input.
|
||||
pub fn update(&self, xy: &mut IIRState, x0: i32) -> i32 {
|
||||
let n = self.ba.len();
|
||||
debug_assert!(xy.len() == n);
|
||||
// `xy` contains x0 x1 y0 y1 y2
|
||||
// Increment time x1 x2 y1 y2 y3
|
||||
// Shift x1 x1 x2 y1 y2
|
||||
// This unrolls better than xy.rotate_right(1)
|
||||
xy.copy_within(0..n - 1, 1);
|
||||
// Store x0 x0 x1 x2 y1 y2
|
||||
xy[0] = x0;
|
||||
// Compute y0 by multiply-accumulate
|
||||
let y0 = macc(0, xy, &self.ba, IIR::SHIFT);
|
||||
// Limit y0
|
||||
// let y0 = y0.max(self.y_min).min(self.y_max);
|
||||
// Store y0 x0 x1 y0 y1 y2
|
||||
xy[n / 2] = y0;
|
||||
y0
|
||||
}
|
||||
}
|
@ -1,10 +1,107 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(asm, core_intrinsics))]
|
||||
|
||||
use core::ops::{Add, Mul, Neg};
|
||||
|
||||
pub type Complex<T> = (T, T);
|
||||
|
||||
/// Round up half.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// `x` - Value to shift and round.
|
||||
/// `shift` - Number of bits to right shift `x`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Shifted and rounded value.
|
||||
#[inline(always)]
|
||||
pub fn shift_round(x: i32, shift: usize) -> i32 {
|
||||
(x + (1 << (shift - 1))) >> shift
|
||||
}
|
||||
|
||||
fn abs<T>(x: T) -> T
|
||||
where
|
||||
T: PartialOrd + Default + Neg<Output = T>,
|
||||
{
|
||||
if x >= T::default() {
|
||||
x
|
||||
} else {
|
||||
-x
|
||||
}
|
||||
}
|
||||
|
||||
// These are implemented here because core::f32 doesn't have them (yet).
|
||||
// They are naive and don't handle inf/nan.
|
||||
// `compiler-intrinsics`/llvm should have better (robust, universal, and
|
||||
// faster) implementations.
|
||||
|
||||
fn copysign<T>(x: T, y: T) -> T
|
||||
where
|
||||
T: PartialOrd + Default + Neg<Output = T>,
|
||||
{
|
||||
if (x >= T::default() && y >= T::default())
|
||||
|| (x <= T::default() && y <= T::default())
|
||||
{
|
||||
x
|
||||
} else {
|
||||
-x
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
fn max<T>(x: T, y: T) -> T
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
if x > y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
fn min<T>(x: T, y: T) -> T
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
if x < y {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
fn max(x: f32, y: f32) -> f32 {
|
||||
core::intrinsics::maxnumf32(x, y)
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
fn min(x: f32, y: f32) -> f32 {
|
||||
core::intrinsics::minnumf32(x, y)
|
||||
}
|
||||
|
||||
// Multiply-accumulate vectors `x` and `a`.
|
||||
//
|
||||
// A.k.a. dot product.
|
||||
// Rust/LLVM optimize this nicely.
|
||||
fn macc<T>(y0: T, x: &[T], a: &[T]) -> T
|
||||
where
|
||||
T: Add<Output = T> + Mul<Output = T> + Copy,
|
||||
{
|
||||
x.iter()
|
||||
.zip(a)
|
||||
.map(|(x, a)| *x * *a)
|
||||
.fold(y0, |y, xa| y + xa)
|
||||
}
|
||||
|
||||
pub mod iir;
|
||||
pub mod iir_int;
|
||||
pub mod lockin;
|
||||
pub mod pll;
|
||||
pub mod trig;
|
||||
pub mod unwrap;
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -10,12 +10,14 @@ use serde::{Deserialize, Serialize};
|
||||
/// stable for any gain (1 <= shift <= 30). It has a single parameter that determines the loop
|
||||
/// bandwidth in octave steps. The gain can be changed freely between updates.
|
||||
///
|
||||
/// The frequency and phase settling time constants for an (any) frequency jump are `1 << shift`
|
||||
/// The frequency and phase settling time constants for a frequency/phase jump are `1 << shift`
|
||||
/// update cycles. The loop bandwidth is about `1/(2*pi*(1 << shift))` in units of the sample rate.
|
||||
/// While the phase is being settled within one turn, there is a typically very small frequency
|
||||
/// overshoot.
|
||||
///
|
||||
/// All math is naturally wrapping 32 bit integer. Phase and frequency are understood modulo that
|
||||
/// overflow in the first Nyquist zone. Expressing the IIR equations in other ways (e.g. single
|
||||
/// (T)-DF-{I,II} biquad/IIR) would break on overflow.
|
||||
/// (T)-DF-{I,II} biquad/IIR) would break on overflow (i.e. every cycle).
|
||||
///
|
||||
/// There are no floating point rounding errors here. But there is integer quantization/truncation
|
||||
/// error of the `shift` lowest bits leading to a phase offset for very low gains. Truncation
|
||||
@ -23,8 +25,8 @@ use serde::{Deserialize, Serialize};
|
||||
/// efficiently by dithering.
|
||||
///
|
||||
/// This PLL does not unwrap phase slips during lock acquisition. This can and should be
|
||||
/// implemented elsewhere by (down) scaling and then unwrapping the input phase and (up) scaling
|
||||
/// and wrapping output phase and frequency. This affects dynamic range accordingly.
|
||||
/// implemented elsewhere by unwrapping and scaling the input phase and un-scaling
|
||||
/// and wrapping output phase and frequency. This affects dynamic range, gain, and noise accordingly.
|
||||
///
|
||||
/// The extension to I^3,I^2,I behavior to track chirps phase-accurately or to i64 data to
|
||||
/// increase resolution for extremely narrowband applications is obvious.
|
||||
@ -39,25 +41,39 @@ pub struct PLL {
|
||||
}
|
||||
|
||||
impl PLL {
|
||||
/// Update the PLL with a new phase sample.
|
||||
/// Update the PLL with a new phase sample. This needs to be called (sampled) periodically.
|
||||
/// The signal's phase/frequency is reconstructed relative to the sampling period.
|
||||
///
|
||||
/// Args:
|
||||
/// * `input`: New input phase sample.
|
||||
/// * `shift`: Error scaling. The frequency gain per update is `1/(1 << shift)`. The phase gain
|
||||
/// is always twice the frequency gain.
|
||||
/// * `x`: New input phase sample.
|
||||
/// * `shift_frequency`: Frequency error scaling. The frequency gain per update is
|
||||
/// `1/(1 << shift_frequency)`.
|
||||
/// * `shift_phase`: Phase error scaling. The phase gain is `1/(1 << shift_phase)`
|
||||
/// per update. A good value is typically `shift_frequency - 1`.
|
||||
///
|
||||
/// Returns:
|
||||
/// A tuple of instantaneous phase and frequency (the current phase increment).
|
||||
pub fn update(&mut self, x: i32, shift: u8) -> (i32, i32) {
|
||||
debug_assert!((1..=30).contains(&shift));
|
||||
let bias = 1i32 << shift;
|
||||
pub fn update(
|
||||
&mut self,
|
||||
x: i32,
|
||||
shift_frequency: u8,
|
||||
shift_phase: u8,
|
||||
) -> (i32, i32) {
|
||||
debug_assert!((1..=30).contains(&shift_frequency));
|
||||
debug_assert!((1..=30).contains(&shift_phase));
|
||||
let e = x.wrapping_sub(self.f);
|
||||
self.f = self.f.wrapping_add(
|
||||
(bias >> 1).wrapping_add(e).wrapping_sub(self.x) >> shift,
|
||||
(1i32 << (shift_frequency - 1))
|
||||
.wrapping_add(e)
|
||||
.wrapping_sub(self.x)
|
||||
>> shift_frequency,
|
||||
);
|
||||
self.x = x;
|
||||
let f = self.f.wrapping_add(
|
||||
bias.wrapping_add(e).wrapping_sub(self.y) >> (shift - 1),
|
||||
(1i32 << (shift_phase - 1))
|
||||
.wrapping_add(e)
|
||||
.wrapping_sub(self.y)
|
||||
>> shift_phase,
|
||||
);
|
||||
self.y = self.y.wrapping_add(f);
|
||||
(self.y, f)
|
||||
@ -70,8 +86,8 @@ mod tests {
|
||||
#[test]
|
||||
fn mini() {
|
||||
let mut p = PLL::default();
|
||||
let (y, f) = p.update(0x10000, 10);
|
||||
assert_eq!(y, 0xc2);
|
||||
let (y, f) = p.update(0x10000, 8, 4);
|
||||
assert_eq!(y, 0x1100);
|
||||
assert_eq!(f, y);
|
||||
}
|
||||
|
||||
@ -79,17 +95,17 @@ mod tests {
|
||||
fn converge() {
|
||||
let mut p = PLL::default();
|
||||
let f0 = 0x71f63049_i32;
|
||||
let shift = 10;
|
||||
let n = 31 << shift + 2;
|
||||
let shift = (10, 9);
|
||||
let n = 31 << shift.0 + 2;
|
||||
let mut x = 0i32;
|
||||
for i in 0..n {
|
||||
x = x.wrapping_add(f0);
|
||||
let (y, f) = p.update(x, shift);
|
||||
let (y, f) = p.update(x, shift.0, shift.1);
|
||||
if i > n / 4 {
|
||||
assert_eq!(f.wrapping_sub(f0).abs() <= 1, true);
|
||||
}
|
||||
if i > n / 2 {
|
||||
// The remaining error is removed by dithering.
|
||||
// The remaining error would be removed by dithering.
|
||||
assert_eq!(y.wrapping_sub(x).abs() < 1 << 18, true);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
#![allow(dead_code)]
|
||||
use super::Complex;
|
||||
|
||||
pub fn isclose(a: f32, b: f32, rtol: f32, atol: f32) -> bool {
|
||||
pub fn isclose(a: f64, b: f64, rtol: f64, atol: f64) -> bool {
|
||||
(a - b).abs() <= a.abs().max(b.abs()) * rtol + atol
|
||||
}
|
||||
|
||||
pub fn isclosef(a: f32, b: f32, rtol: f32, atol: f32) -> bool {
|
||||
(a - b).abs() <= a.abs().max(b.abs()) * rtol + atol
|
||||
}
|
||||
|
||||
@ -10,7 +15,7 @@ pub fn complex_isclose(
|
||||
rtol: f32,
|
||||
atol: f32,
|
||||
) -> bool {
|
||||
isclose(a.0, b.0, rtol, atol) && isclose(a.1, b.1, rtol, atol)
|
||||
isclosef(a.0, b.0, rtol, atol) && isclosef(a.1, b.1, rtol, atol)
|
||||
}
|
||||
|
||||
pub fn complex_allclose(
|
||||
@ -19,9 +24,7 @@ pub fn complex_allclose(
|
||||
rtol: f32,
|
||||
atol: f32,
|
||||
) -> bool {
|
||||
let mut result: bool = true;
|
||||
a.iter().zip(b.iter()).for_each(|(i, j)| {
|
||||
result &= complex_isclose(*i, *j, rtol, atol);
|
||||
});
|
||||
result
|
||||
a.iter()
|
||||
.zip(b)
|
||||
.all(|(&i, &j)| complex_isclose(i, j, rtol, atol))
|
||||
}
|
||||
|
278
dsp/src/trig.rs
Normal file
278
dsp/src/trig.rs
Normal file
@ -0,0 +1,278 @@
|
||||
use super::Complex;
|
||||
use core::f64::consts::PI;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/cossin_table.rs"));
|
||||
|
||||
/// 2-argument arctangent function.
|
||||
///
|
||||
/// This implementation uses all integer arithmetic for fast
|
||||
/// computation. It is designed to have high accuracy near the axes
|
||||
/// and lower away from the axes. It is additionally designed so that
|
||||
/// the error changes slowly with respect to the angle.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `y` - Y-axis component.
|
||||
/// * `x` - X-axis component.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The angle between the x-axis and the ray to the point (x,y). The
|
||||
/// result range is from i32::MIN to i32::MAX, where i32::MIN
|
||||
/// represents -pi and, equivalently, +pi. i32::MAX represents one
|
||||
/// count less than +pi.
|
||||
pub fn atan2(y: i32, x: i32) -> i32 {
|
||||
let sign = (x < 0, y < 0);
|
||||
|
||||
let mut y = y.wrapping_abs() as u32;
|
||||
let mut x = x.wrapping_abs() as u32;
|
||||
|
||||
let y_greater = y > x;
|
||||
if y_greater {
|
||||
core::mem::swap(&mut y, &mut x);
|
||||
}
|
||||
|
||||
let z = (16 - y.leading_zeros() as i32).max(0);
|
||||
|
||||
x >>= z;
|
||||
if x == 0 {
|
||||
return 0;
|
||||
}
|
||||
y >>= z;
|
||||
let r = (y << 16) / x;
|
||||
debug_assert!(r <= 1 << 16);
|
||||
|
||||
// Uses the general procedure described in the following
|
||||
// Mathematics stack exchange answer:
|
||||
//
|
||||
// https://math.stackexchange.com/a/1105038/583981
|
||||
//
|
||||
// The atan approximation method has been modified to be cheaper
|
||||
// to compute and to be more compatible with integer
|
||||
// arithmetic. The approximation technique used here is
|
||||
//
|
||||
// pi / 4 * r + C * r * (1 - abs(r))
|
||||
//
|
||||
// which is taken from Rajan 2006: Efficient Approximations for
|
||||
// the Arctangent Function.
|
||||
//
|
||||
// The least mean squared error solution is C = 0.279 (no the 0.285 that
|
||||
// Rajan uses). K = C*4/pi.
|
||||
// Q5 for K provides sufficient correction accuracy while preserving
|
||||
// as much smoothness of the quadratic correction as possible.
|
||||
const FP_K: usize = 5;
|
||||
const K: u32 = (0.35489 * (1 << FP_K) as f64) as u32;
|
||||
// debug_assert!(K == 11);
|
||||
|
||||
// `r` is unsigned Q16.16 and <= 1
|
||||
// `angle` is signed Q1.31 with 1 << 31 == +- pi
|
||||
// Since K < 0.5 and r*(1 - r) <= 0.25 the correction product can use
|
||||
// 4 bits for K, and 15 bits for r and 1-r to remain within the u32 range.
|
||||
let mut angle = ((r << 13)
|
||||
+ ((K * (r >> 1) * ((1 << 15) - (r >> 1))) >> (FP_K + 1)))
|
||||
as i32;
|
||||
|
||||
if y_greater {
|
||||
angle = (1 << 30) - angle;
|
||||
}
|
||||
|
||||
if sign.0 {
|
||||
angle = i32::MAX - angle;
|
||||
}
|
||||
|
||||
if sign.1 {
|
||||
angle = angle.wrapping_neg();
|
||||
}
|
||||
|
||||
angle
|
||||
}
|
||||
|
||||
/// Compute the cosine and sine of an angle.
|
||||
/// This is ported from the MiSoC cossin core.
|
||||
/// (https://github.com/m-labs/misoc/blob/master/misoc/cores/cossin.py)
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `phase` - 32-bit phase.
|
||||
///
|
||||
/// # Returns
|
||||
/// The cos and sin values of the provided phase as a `Complex<i32>`
|
||||
/// value. With a 7-bit deep LUT there is 1e-5 max and 6e-8 RMS error
|
||||
/// in each quadrature over 20 bit phase.
|
||||
pub fn cossin(phase: i32) -> Complex<i32> {
|
||||
// Phase bits excluding the three highes MSB
|
||||
const OCTANT_BITS: usize = 32 - 3;
|
||||
|
||||
// This is a slightly more compact way to compute the four flags for
|
||||
// octant mapping/unmapping used below.
|
||||
let mut octant = (phase as u32) >> OCTANT_BITS;
|
||||
octant ^= octant << 1;
|
||||
|
||||
// Mask off octant bits. This leaves the angle in the range [0, pi/4).
|
||||
let mut phase = phase & ((1 << OCTANT_BITS) - 1);
|
||||
|
||||
if octant & 1 != 0 {
|
||||
// phase = pi/4 - phase
|
||||
phase = (1 << OCTANT_BITS) - 1 - phase;
|
||||
}
|
||||
|
||||
let lookup = COSSIN[(phase >> (OCTANT_BITS - COSSIN_DEPTH)) as usize];
|
||||
// 1/2 < cos(0 <= x <= pi/4) <= 1: Shift the cos
|
||||
// values and scale the sine values as encoded in the LUT.
|
||||
let mut cos = lookup.0 as i32 + u16::MAX as i32;
|
||||
let mut sin = (lookup.1 as i32) << 1;
|
||||
|
||||
// 16 + 1 bits for cos/sin and 15 for dphi to saturate the i32 range.
|
||||
const ALIGN_MSB: usize = 32 - 16 - 1;
|
||||
phase >>= OCTANT_BITS - COSSIN_DEPTH - ALIGN_MSB;
|
||||
phase &= (1 << ALIGN_MSB) - 1;
|
||||
// The phase values used for the LUT are at midpoint for the truncated phase.
|
||||
// Interpolate relative to the LUT entry midpoint.
|
||||
phase -= (1 << (ALIGN_MSB - 1)) - (octant & 1) as i32;
|
||||
// Fixed point pi/4.
|
||||
const PI4: i32 = (PI / 4. * (1 << (32 - ALIGN_MSB)) as f64) as i32;
|
||||
// No rounding bias necessary here since we keep enough low bits.
|
||||
let dphi = (phase * PI4) >> (32 - ALIGN_MSB);
|
||||
|
||||
// Make room for the sign bit.
|
||||
let dcos = (sin * dphi) >> (COSSIN_DEPTH + 1);
|
||||
let dsin = (cos * dphi) >> (COSSIN_DEPTH + 1);
|
||||
|
||||
cos = (cos << (ALIGN_MSB - 1)) - dcos;
|
||||
sin = (sin << (ALIGN_MSB - 1)) + dsin;
|
||||
|
||||
// Unmap using octant bits.
|
||||
if octant & 2 != 0 {
|
||||
core::mem::swap(&mut sin, &mut cos);
|
||||
}
|
||||
|
||||
if octant & 4 != 0 {
|
||||
cos *= -1;
|
||||
}
|
||||
|
||||
if octant & 8 != 0 {
|
||||
sin *= -1;
|
||||
}
|
||||
|
||||
(cos, sin)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use core::f64::consts::PI;
|
||||
|
||||
fn angle_to_axis(angle: f64) -> f64 {
|
||||
let angle = angle % (PI / 2.);
|
||||
(PI / 2. - angle).min(angle)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atan2_absolute_error() {
|
||||
const N: usize = 321;
|
||||
let mut test_vals = [0i32; N + 4];
|
||||
let scale = (1i64 << 31) as f64;
|
||||
for i in 0..N {
|
||||
test_vals[i] = (scale * (-1. + 2. * i as f64 / N as f64)) as i32;
|
||||
}
|
||||
|
||||
assert!(test_vals.contains(&i32::MIN));
|
||||
test_vals[N] = i32::MAX;
|
||||
test_vals[N + 1] = 0;
|
||||
test_vals[N + 2] = -1;
|
||||
test_vals[N + 3] = 1;
|
||||
|
||||
let mut rms_err = 0f64;
|
||||
let mut abs_err = 0f64;
|
||||
let mut rel_err = 0f64;
|
||||
|
||||
for &x in test_vals.iter() {
|
||||
for &y in test_vals.iter() {
|
||||
let want = (y as f64 / scale).atan2(x as f64 / scale);
|
||||
let have = atan2(y, x) as f64 * PI / scale;
|
||||
|
||||
let err = (have - want).abs();
|
||||
abs_err = abs_err.max(err);
|
||||
rms_err += err * err;
|
||||
if err > 3e-5 {
|
||||
rel_err = rel_err.max(err / angle_to_axis(want));
|
||||
}
|
||||
}
|
||||
}
|
||||
rms_err = rms_err.sqrt() / test_vals.len() as f64;
|
||||
println!("max abs err: {:.2e}", abs_err);
|
||||
println!("rms abs err: {:.2e}", rms_err);
|
||||
println!("max rel err: {:.2e}", rel_err);
|
||||
assert!(abs_err < 5e-3);
|
||||
assert!(rms_err < 3e-3);
|
||||
assert!(rel_err < 0.6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cossin_error_max_rms_all_phase() {
|
||||
// Constant amplitude error due to LUT data range.
|
||||
const AMPLITUDE: f64 = ((1i64 << 31) - (1i64 << 15)) as f64;
|
||||
const MAX_PHASE: f64 = (1i64 << 32) as f64;
|
||||
let mut rms_err: Complex<f64> = (0., 0.);
|
||||
let mut sum_err: Complex<f64> = (0., 0.);
|
||||
let mut max_err: Complex<f64> = (0., 0.);
|
||||
let mut sum: Complex<f64> = (0., 0.);
|
||||
let mut demod: Complex<f64> = (0., 0.);
|
||||
|
||||
// use std::{fs::File, io::{BufWriter, prelude::*}, path::Path};
|
||||
// let mut file = BufWriter::new(File::create(Path::new("data.bin")).unwrap());
|
||||
|
||||
const PHASE_DEPTH: usize = 20;
|
||||
|
||||
for phase in 0..(1 << PHASE_DEPTH) {
|
||||
let phase = (phase << (32 - PHASE_DEPTH)) as i32;
|
||||
let have = cossin(phase);
|
||||
// file.write(&have.0.to_le_bytes()).unwrap();
|
||||
// file.write(&have.1.to_le_bytes()).unwrap();
|
||||
|
||||
let have = (have.0 as f64 / AMPLITUDE, have.1 as f64 / AMPLITUDE);
|
||||
|
||||
let radian_phase = 2. * PI * phase as f64 / MAX_PHASE;
|
||||
let want = (radian_phase.cos(), radian_phase.sin());
|
||||
|
||||
sum.0 += have.0;
|
||||
sum.1 += have.1;
|
||||
|
||||
demod.0 += have.0 * want.0 - have.1 * want.1;
|
||||
demod.1 += have.1 * want.0 + have.0 * want.1;
|
||||
|
||||
let err = (have.0 - want.0, have.1 - want.1);
|
||||
|
||||
sum_err.0 += err.0;
|
||||
sum_err.1 += err.1;
|
||||
|
||||
rms_err.0 += err.0 * err.0;
|
||||
rms_err.1 += err.1 * err.1;
|
||||
|
||||
max_err.0 = max_err.0.max(err.0.abs());
|
||||
max_err.1 = max_err.1.max(err.1.abs());
|
||||
}
|
||||
rms_err.0 /= MAX_PHASE;
|
||||
rms_err.1 /= MAX_PHASE;
|
||||
|
||||
println!("sum: {:.2e} {:.2e}", sum.0, sum.1);
|
||||
println!("demod: {:.2e} {:.2e}", demod.0, demod.1);
|
||||
println!("sum_err: {:.2e} {:.2e}", sum_err.0, sum_err.1);
|
||||
println!("rms: {:.2e} {:.2e}", rms_err.0.sqrt(), rms_err.1.sqrt());
|
||||
println!("max: {:.2e} {:.2e}", max_err.0, max_err.1);
|
||||
|
||||
assert!(sum.0.abs() < 4e-10);
|
||||
assert!(sum.1.abs() < 4e-10);
|
||||
|
||||
assert!(demod.0.abs() < 4e-10);
|
||||
assert!(demod.1.abs() < 4e-10);
|
||||
|
||||
assert!(sum_err.0.abs() < 4e-10);
|
||||
assert!(sum_err.1.abs() < 4e-10);
|
||||
|
||||
assert!(rms_err.0.sqrt() < 6e-8);
|
||||
assert!(rms_err.1.sqrt() < 6e-8);
|
||||
|
||||
assert!(max_err.0 < 1.1e-5);
|
||||
assert!(max_err.1 < 1.1e-5);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user