Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/SciNim/impulse
Impulse will be a collection of primitives for signal processing (FFT, Convolutions, ...)
https://github.com/SciNim/impulse
Last synced: about 2 months ago
JSON representation
Impulse will be a collection of primitives for signal processing (FFT, Convolutions, ...)
- Host: GitHub
- URL: https://github.com/SciNim/impulse
- Owner: SciNim
- License: other
- Created: 2020-05-30T10:22:57.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2024-03-04T17:12:38.000Z (10 months ago)
- Last Synced: 2024-03-05T11:33:19.417Z (10 months ago)
- Language: C++
- Size: 139 KB
- Stars: 12
- Watchers: 9
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: changelog.org
- License: LICENSE-APACHEv2
Awesome Lists containing this project
- awesome-nim - impulse - Impulse will be a collection of primitives for signal processing (FFT, Convolutions). (Algorithms / Math)
README
# Impulse
Impulse will be a collection of signal processing primitives for Nim.
## FFT
The FFT part of this library wraps
[PocketFFT](https://gitlab.mpcdf.mpg.de/mtr/pocketfft).For the C++ backend an optimized version of PocketFFT is used, in the
form of a header-only version,https://github.com/mreineck/pocketfft
For the C backend we use the PocketFFT directly (the C library linked
before).Note that the API differs slightly between the two. See the two
examples below.### C example
The `pocketfft` submodule can to be imported manually using
`import impulse/fft/pocketfft` or one can simply import the `fft`
submodule as shown below.```nim
import impulse/ffttemplate isClose(a1, a2, eps: untyped): untyped =
for i in 0 ..< a1.len:
doAssert abs(a1[i] - a2[i]) < eps, "Is: " & $a1[i] & " | " & $a2[i]block Array:
let dIn = [1.0, 2.0, 1.0, -1.0, 1.5]
let dOut = fft(dIn, forward = true) # forward or backwards?
echo dIn
echo dOut
isClose(dIn, fft(dOut, forward = false), eps = 1e-10)
# [1.0, 2.0, 1.0, -1.0, 1.5]
# @[4.5, 2.081559480312316, -1.651098762732523, -1.831559480312316, 1.608220406444071]
block Seq:
let dIn = @[1.0, 2.0, 1.0, -1.0, 1.5]
let dOut = fft(dIn, forward = true) # forward or backwards?
echo dIn
echo dOut
isClose(dIn, fft(dOut, forward = false), eps = 1e-10)
# @[1.0, 2.0, 1.0, -1.0, 1.5]
# @[4.5, 2.081559480312316, -1.651098762732523, -1.831559480312316, 1.608220406444071]
block Tensor:
let dIn = @[1.0, 2.0, 1.0, -1.0, 1.5].toTensor
let dOut = fft(dIn, forward = true) # forward or backwards?
echo dIn
echo dOut
isClose(dIn, fft(dOut, forward = false), eps = 1e-10)
# Tensor[system.float] of shape "[5]" on backend "Cpu"
# 1 2 1 -1 1.5
# Tensor[system.float] of shape "[5]" on backend "Cpu"
# 4.5 2.08156 -1.6511 -1.83156 1.60822
import std / complex
block Complex:
let dIn = [complex(1.0), complex(2.0), complex(1.0), complex(-1.0), complex(1.5)]
let dOut = fft(dIn, forward = true) # forward or backwards?
echo dIn
echo dOut
isClose(dIn, fft(dOut, forward = false), eps = 1e-10)
# [(1.0, 0.0), (2.0, 0.0), (1.0, 0.0), (-1.0, 0.0), (1.5, 0.0)]
# @[(4.5, 0.0), (2.081559480312316, -1.651098762732523), (-1.831559480312316, 1.608220406444071), (-1.831559480312316, -1.608220406444071), (2.081559480312316, 1.651098762732523)]
```### C++ example
When compiling on the C++ backend, the API is a bit different:
```nim
import impulse/fft
import std / complexlet dIn = @[1.0, 2.0, 1.0, -1.0, 1.5]
var dOut = newSeq[Complex[float64]](dIn.len)let dInDesc = DataDesc[float64].init(
dIn[0].unsafeAddr, [dIn.len]
)
var dOutDesc = DataDesc[Complex[float64]].init(
dOut[0].addr, [dOut.len]
)let fft = FFTDesc[float64].init(
axes = [0],
forward = true
)fft.apply(dOutDesc, dInDesc)
echo dIn
echo dOut
# @[1.0, 2.0, 1.0, -1.0, 1.5]
# @[(4.5, 0.0), (2.081559480312316, -1.651098762732523), (-1.831559480312316, 1.608220406444071), (0.0, 0.0), (0.0, 0.0)]
```## LFSR
LFSR module which implements a Linear Feedback Shift Register that can be
used to generate pseudo-random boolean sequences. It supports both Fibonacci
and Galois LFSRs.LFSRs used in many digital communication systems (including, for example LTE
and 5GNR). For more information see:https://simple.wikipedia.org/wiki/Linear-feedback_shift_register
Note that this module uses Arraymancer under the hood, so it depends on it.
Also note that this code is heavily based on Nikesh Bajaj's pylfsr, which can
be found in https://pylfsr.github.io### LFSR examples
The following example creates a Fibonacci-style LFSR with polynomial
`x^5 + x^3 + 1` and uses it to generate a pseudo-random sequence of 31 values.
Note how the `taps` argument is an integer tensor with values `[5, 3]`,
corresponding to the exponents of the coefficients of the polynomial, in
descending order. Exponent 0 was skipped because it is implicitly included
(if it is included it will be ignored). If the exponents are not in
descending order a ValueError exception will be raised.```nim
import impulse/lfsr
import arraymancervar fibonacci_lfsr = initLFSR(
taps = [5, 3], # descending order and 0 can be omitted
# The following 2 lines can be skipped in this case since they are the defaults
# state = single_true,
# conf = fibonacci
)let sequence1 = fibonacci_lfsr.generate(31)
# Print the first few elements
# Note that sequence1 will be a Tensor[bool] but it can be easily converted to
# Tensor[int] for more concise printing
echo sequence1.asType(int)[_..11]
# Tensor[system.int] of shape "[12]" on backend "Cpu"
# 1 0 0 0 0 1 0 0 1 0 1 1# The generator can be reset to start over
fibonacci_lfsr.reset()
let sequence2 = fibonacci_lfsr.generate(31)
doAssert sequence1 == sequence2
```Galois style LFSRs are also supported and it is also possible to set a custom
start state as a Tensor[bool]:```nim
var galois_lfsr = initLFSR(
# note how the 0 exponent can be included and taps can be a Tensor as well
taps = [5, 3, 0].toTensor,
state = [true, true, true, true, true].toTensor, # this is equivalent to `all_true`
conf = galois
)# Generate the first 8 values
let sequence3a = galois_lfsr.generate(8)
echo sequence3a.asType(int)
# Tensor[system.int] of shape "[8]" on backend "Cpu"
# 1 1 1 1 0 0 0 1# Generate a few more values
let sequence3b = galois_lfsr.generate(10)
echo sequence3b.asType(int)
# Tensor[system.int] of shape "[10]" on backend "Cpu"
# 1 0 1 1 1 0 1 0 1 0galois_lfsr.reset()
echo galois_lfsr.generate(18)
# Tensor[system.int] of shape "[18]" on backend "Cpu"
# 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0
```### Maximal LFSR tap examples
As a convenience, a `tap_examples` function is provided. This function takes a
`size` and returns one example (out of many) sequence of taps that generates a
"maximal" LSFR sequence.### LFSR efficiency
The LFSR module implementation is favors simplicity over speed. As of 2024, it
is able to generate 2^24 values in less than 1 minute on a mid-range laptop.## Signal
The Signal module implements several signal processing and related functions, such as a Kaiser window and a firls FIR filter design function. See the documentation of those functions for more details.
## License
Licensed and distributed under either of
* MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
or
* Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0)
at your option. These files may not be copied, modified, or distributed except according to those terms.