├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── examples ├── CMakeLists.txt ├── filter.cpp └── filter_fixed_point.cpp ├── include └── embedded │ ├── circular_buffer_adapter.h │ ├── config.h │ ├── constexpr_math.h │ ├── constexpr_math │ ├── complex.h │ ├── constants.h │ ├── convolve.h │ ├── functions.h │ ├── poly.h │ └── vector.h │ ├── detail │ └── circular_buffer_adapter.h │ ├── function.h │ ├── move_wrapper.h │ ├── mutex.h │ ├── ostream_ops.h │ ├── preprocessor.h │ ├── signal │ ├── bessel.h │ ├── butterworth.h │ ├── chebyshev.h │ ├── debug_filter_coefs.h │ ├── detail │ │ ├── bessel_poles.h │ │ ├── debug_filter_coefs.h │ │ └── filter.h │ ├── filter.h │ ├── poly.h │ └── sos.h │ ├── type_traits.h │ ├── type_traits │ ├── conjunction.h │ └── is_invocable.h │ ├── typelist.h │ ├── utility │ └── integer_sequence.h │ ├── variant.h │ └── varint.h ├── scripts ├── gen-bessel-poles.py ├── gen-signal-tests.py ├── parse-filter-coefs.py └── soak.sh └── tests ├── CMakeLists.txt ├── CMakeLists.txt.gtest ├── circular_buffer_adapter.cpp ├── constexpr_complex.cpp ├── constexpr_convolve.cpp ├── constexpr_vector.cpp ├── function.cpp ├── integer_sequence.cpp ├── lock_guard.cpp ├── move_wrapper.cpp ├── signal.cpp ├── signal_bessel_double.cpp ├── signal_bessel_float.cpp ├── signal_butter_double.cpp ├── signal_butter_float.cpp ├── signal_cheby1_double.cpp ├── signal_cheby1_float.cpp ├── signal_cheby2_double.cpp ├── signal_cheby2_float.cpp ├── test_util.h ├── typelist.cpp └── varint.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AlwaysBreakTemplateDeclarations: 'true' 3 | AccessModifierOffset: -1 4 | BreakConstructorInitializersBeforeComma: 'true' 5 | ColumnLimit: '80' 6 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 7 | ConstructorInitializerIndentWidth: '4' 8 | Cpp11BracedListStyle: 'true' 9 | IndentWidth: '2' 10 | Language: Cpp 11 | MaxEmptyLinesToKeep: '1' 12 | NamespaceIndentation: None 13 | PenaltyReturnTypeOnItsOwnLine: '0' 14 | PointerAlignment: Left 15 | Standard: Cpp11 16 | UseTab: Never 17 | 18 | ... 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /@* 2 | /tmp/ 3 | /build*/ 4 | *.log 5 | /.gdb_history 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "variant"] 2 | path = variant 3 | url = https://github.com/mpark/variant 4 | [submodule "gcem"] 5 | path = gcem 6 | url = https://github.com/kthohr/gcem 7 | [submodule "fpm"] 8 | path = fpm 9 | url = https://github.com/MikeLankamp/fpm 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) Marcus Holland-Moritz 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a 5 | # copy of this software and associated documentation files (the "Software"), 6 | # to deal in the Software without restriction, including without limitation 7 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | # and/or sell copies of the Software, and to permit persons to whom the 9 | # Software is furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included 12 | # in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | # DEALINGS IN THE SOFTWARE. 21 | # 22 | 23 | project(libembedded) 24 | 25 | cmake_minimum_required(VERSION 3.13.4) 26 | 27 | option(WITH_EXAMPLES "build examples" OFF) 28 | 29 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 30 | add_compile_options(-fdiagnostics-color=always) 31 | elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 32 | add_compile_options(-fcolor-diagnostics) 33 | endif() 34 | 35 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 36 | add_compile_options(-Wall -Wextra -pedantic -Werror) 37 | endif() 38 | 39 | if(DEFINED ENV{SOAKING}) 40 | message("\n======= ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} [${CMAKE_BUILD_TYPE}] =======\n") 41 | endif() 42 | 43 | include_directories(include fpm/include gcem/include variant/include) 44 | 45 | enable_testing() 46 | 47 | if(WITH_EXAMPLES) 48 | add_subdirectory(examples) 49 | endif() 50 | 51 | add_subdirectory(tests) 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Marcus Holland-Moritz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | `include/embedded/mpark/variant.hpp` is licensed under the Boost Software License 24 | and available [here](https://github.com/mpark/variant/tree/single-header). 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/5e1f58ea5d434b09bfba571413a02811)](https://www.codacy.com/gh/mhx/libembedded/dashboard?utm_source=github.com&utm_medium=referral&utm_content=mhx/libembedded&utm_campaign=Badge_Grade) 2 | 3 | # libembedded - Modern C++ for Embedded Systems 4 | 5 | This library will hopefully, over time, become a collection of useful, 6 | modern C++ abstractions, with a particular focus on being useful in 7 | the context of embedded systems. 8 | 9 | The goal for this library is to provide abstractions that 10 | 11 | - work nicely without exceptions and RTTI enabled, 12 | 13 | - work nicely without dynamic memory allocation and 14 | 15 | - require only C++11 compliant compilers. 16 | 17 | # What's inside 18 | 19 | ## A polymorphic function wrapper with in-place storage 20 | 21 | `embedded::function` is loosely modeled after `std::function` and inspired 22 | by both SG14's `inplace_function` and `folly::Function`. It requires no 23 | dynamic memory allocation, but, contrary to `etl::delegate`, is able to 24 | wrap lambdas and other function objects as long as they fit into the 25 | in-place storage. 26 | 27 | ## A circular buffer adapter 28 | 29 | The `circular_buffer_adapter` template allows the construction of circular 30 | buffers on top of a arbitrary, and possibly persistent, memory. This means 31 | that you can easily build circular buffers on top of EEPROM sections. 32 | 33 | ## Variable length integers 34 | 35 | `embedded::varint` implements encoding and decoding of interger values to 36 | and from byte streams, using as few bytes as possible, and independent of 37 | byte order. 38 | 39 | ## Type traits 40 | 41 | A few type traits have been back-ported from later C++ standards. More 42 | will be added on-demand. 43 | 44 | ## An experimental compile-time IIR filter design library 45 | 46 | This library (in `embedded/signal`) can be used to directly design IIR 47 | filters in C++ code without any run-time overhead. This is currently in 48 | experimental state as the interface isn't yet settled and there's quite 49 | a few things that are still missing. However, you can do things like: 50 | 51 | ``` 52 | constexpr auto design = 53 | iirfilter<>(1000.0).lowpass(chebyshev1<8>(3.0), 40.0).sos(); 54 | 55 | auto filter = design.instance(); 56 | 57 | for (;;) { 58 | output(filter(input())); 59 | } 60 | ``` 61 | 62 | This will design an 8th-order Chebyshev Type I low-pass filter with 63 | 3 dB of passband ripple, a sample rate of 1 kHz and a cutoff frequency 64 | of 40 Hz. All filter coefficients will be determined at compile-time 65 | and the run-time filter code is extremely efficient. For example, this 66 | is the full ARM assembly for the above code, but using a second-order 67 | filter: 68 | 69 | ``` 70 | process(): 71 | push {r3, lr} 72 | vpush.64 {d8, d9, d10} 73 | vldr.32 s17, .L5 74 | vldr.32 s20, .L5+4 75 | vldr.32 s19, .L5+8 76 | vmov.f32 s16, s17 77 | vldr.32 s18, .L5+12 78 | .L2: 79 | bl input() 80 | vmul.f32 s13, s0, s20 81 | vmov.f32 s15, s0 82 | vldr.32 s12, .L5+16 83 | vadd.f32 s14, s16, s13 84 | vmul.f32 s16, s14, s18 85 | vmls.f32 s13, s14, s12 86 | vmov.f32 s0, s14 87 | vnmls.f32 s16, s15, s19 88 | vadd.f32 s16, s16, s17 89 | vmov.f32 s17, s13 90 | bl output(float) 91 | b .L2 92 | .L5: 93 | .word 0 94 | .word 1005574271 95 | .word 1013962879 96 | .word -1075339548 97 | .word 1062851613 98 | ``` 99 | 100 | The library is also able to build fixed-point arithmetic filters 101 | if a suitable fixed-point implementation is provided 102 | (e.g. [fpm](https://github.com/MikeLankamp/fpm)). 103 | 104 | You can find examples in the `examples` directory of the repo. 105 | 106 | ## Experimental compile-time math library 107 | 108 | This is primarily used by the compile-time filter design library and 109 | currently only contains the minimum functionality needed to implement 110 | the filter design. It does contain things like `constexpr` functions 111 | (provided either via `libstdc++`'s non-standard builtins or via the 112 | [gcem library](https://github.com/kthohr/gcem)), `constexpr` complex 113 | numbers and `constexpr` vectors. 114 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) Marcus Holland-Moritz 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a 5 | # copy of this software and associated documentation files (the "Software"), 6 | # to deal in the Software without restriction, including without limitation 7 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | # and/or sell copies of the Software, and to permit persons to whom the 9 | # Software is furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included 12 | # in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | # DEALINGS IN THE SOFTWARE. 21 | # 22 | 23 | add_executable(filter filter.cpp) 24 | add_executable(filter_fixed_point filter_fixed_point.cpp) 25 | -------------------------------------------------------------------------------- /examples/filter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "embedded/signal/chebyshev.h" 7 | #include "embedded/signal/filter.h" 8 | 9 | int main() { 10 | using namespace embedded::signal; 11 | 12 | // Repeat N times for more accurate benchmark 13 | constexpr size_t Repeat = 20; 14 | 15 | // Design the IIR filter: 16 | // - 10th-order Chebyshev Type-I lowpass filter 17 | // - Using double-precision for filter design 18 | // - Using single-precision for filter implementation 19 | // - Using a Second Order System (SOS) implementation 20 | // 21 | // The design is fully determined at compile time. 22 | constexpr double fs{1000.0}; // sample rate 23 | constexpr double fc{40.0}; // cutoff frequency 24 | constexpr double rp{3.0}; // passband ripple 25 | constexpr auto design = 26 | iirfilter(fs).lowpass(chebyshev1<10>(rp), fc).sos(); 27 | 28 | // Set up input and output signal vectors 29 | std::vector vec; 30 | std::vector out; 31 | 32 | // Read signal from stdin 33 | { 34 | float x; 35 | while (std::fread(&x, sizeof(x), 1, stdin)) { 36 | vec.push_back(x); 37 | } 38 | } 39 | 40 | out.resize(vec.size()); 41 | 42 | auto t1 = std::chrono::steady_clock::now(); 43 | 44 | for (size_t i = 0; i < Repeat; ++i) { 45 | // Create a new instance of the filter design 46 | auto filter = design.instance(); 47 | 48 | // Filter the input signal 49 | for (size_t k = 0; k < vec.size(); ++k) { 50 | out[k] = filter(vec[k]); 51 | } 52 | } 53 | 54 | auto t2 = std::chrono::steady_clock::now(); 55 | 56 | std::cerr << 1e9 * std::chrono::duration(t2 - t1).count() / 57 | (Repeat * vec.size()) 58 | << " ns/sample\n"; 59 | 60 | // Write output signal to stdout 61 | std::fwrite(out.data(), sizeof(float), out.size(), stdout); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /examples/filter_fixed_point.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "embedded/ostream_ops.h" 10 | #include "embedded/signal/butterworth.h" 11 | #include "embedded/signal/filter.h" 12 | 13 | int main() { 14 | using namespace embedded::signal; 15 | 16 | // Repeat N times for more accurate benchmark 17 | constexpr size_t Repeat = 1; 18 | 19 | constexpr unsigned FractionBits = 28; 20 | constexpr int ScaleFactor = 1 << (FractionBits - 8 * sizeof(int16_t)); 21 | 22 | // Define our fixed-point arithmetic type 23 | using value_type = fpm::fixed; 24 | 25 | // Design the IIR filter: 26 | // - 20th-order Butterworth highpass filter 27 | // - Using double-precision for filter design 28 | // - Using fixed-point for filter implementation 29 | // - Using a Second Order System (SOS) implementation 30 | // 31 | // The design is fully determined at compile time. 32 | constexpr double fs{1000.0}; // sample rate 33 | constexpr double fc{40.0}; // cutoff frequency 34 | constexpr auto design = iirfilter(fs) 35 | .highpass(butterworth<20>(), fc) 36 | .sos(sos_gain::distribute); 37 | 38 | // Set up input and output signal vectors 39 | std::vector vec; 40 | std::vector out; 41 | 42 | // Read signal from stdin 43 | { 44 | int16_t x; 45 | while (std::fread(&x, sizeof(x), 1, stdin)) { 46 | vec.push_back(x); 47 | } 48 | } 49 | 50 | // Reserve space for output signal and individual SOS sections 51 | out.resize(vec.size() * (1 + design.sos().size())); 52 | 53 | auto t1 = std::chrono::steady_clock::now(); 54 | 55 | for (size_t i = 0; i < Repeat; ++i) { 56 | // Create a new instance of the filter design 57 | auto filter = design.instance(); 58 | 59 | // Filter the input signal 60 | for (size_t k = 0; k < vec.size(); ++k) { 61 | out[k] = 62 | filter(value_type::from_raw_value(ScaleFactor * vec[k])).raw_value() / 63 | ScaleFactor; 64 | } 65 | } 66 | 67 | auto t2 = std::chrono::steady_clock::now(); 68 | 69 | std::cerr << 1e9 * std::chrono::duration(t2 - t1).count() / 70 | (Repeat * vec.size()) 71 | << " ns/sample\n"; 72 | 73 | // Filter the signal through each SOS section individually 74 | for (size_t i = 0; i < design.sos().size(); ++i) { 75 | auto const& section = design.sos()[i]; 76 | std::decay::type::state_type state; 77 | 78 | for (size_t k = 0; k < vec.size(); ++k) { 79 | out[(i + 1) * vec.size() + k] = 80 | section 81 | .filter(state, value_type::from_raw_value(ScaleFactor * vec[k])) 82 | .raw_value() / 83 | ScaleFactor; 84 | } 85 | } 86 | 87 | // Write output and SOS decomposition signals to stdout 88 | std::fwrite(out.data(), sizeof(out[0]), out.size(), stdout); 89 | 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /include/embedded/circular_buffer_adapter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "config.h" 29 | 30 | #if LIBEMB_HAS_EXCEPTIONS 31 | #include 32 | #endif 33 | 34 | #include "detail/circular_buffer_adapter.h" 35 | 36 | namespace embedded { 37 | 38 | /** 39 | * An adapter to use arbitrary memory as a circular buffer 40 | * 41 | * This adapter allows you to access an arbitrary piece of memory, for example 42 | * a section of a memory-mapped EEPROM, as a circular buffer. 43 | * 44 | * The adapter interface is mostly compatible with std::deque, although it 45 | * currenly lacks operations such as `insert` and `erase`. 46 | * 47 | * While the adapter can be used to implement a circular buffer for non-trivial 48 | * types, care must be taken to ensure object lifetime is handled properly. 49 | * If the adapter is initialized to a non-empty circular buffer (i.e. non-zero 50 | * `item_count`) of non-trivial types, it assumes that the memory passed in 51 | * contains valid instances in the right slots. Similarly, the adapter won't 52 | * dispose of any instances that still exist in the managed memory when it is 53 | * itself destroyed. It will, however, properly manage objects of non-trivial 54 | * type during its lifetime. 55 | */ 56 | template 57 | class circular_buffer_adapter { 58 | public: 59 | using value_type = typename std::remove_const::type; 60 | using reference = T&; 61 | using const_reference = value_type const&; 62 | using pointer = T*; 63 | using const_pointer = value_type const*; 64 | using size_type = std::size_t; 65 | using difference_type = std::ptrdiff_t; 66 | 67 | using iterator = 68 | detail::cba_iterator>; 69 | using const_iterator = 70 | detail::cba_iterator>; 71 | 72 | using reverse_iterator = std::reverse_iterator; 73 | using const_reverse_iterator = std::reverse_iterator; 74 | 75 | friend iterator; 76 | friend const_iterator; 77 | 78 | circular_buffer_adapter() = default; 79 | 80 | circular_buffer_adapter(circular_buffer_adapter&&) = default; 81 | circular_buffer_adapter& operator=(circular_buffer_adapter&&) = default; 82 | 83 | circular_buffer_adapter(pointer data, size_type capacity) 84 | : circular_buffer_adapter(data, capacity, 0, 0) {} 85 | 86 | circular_buffer_adapter(pointer data, size_type capacity, 87 | size_type first_index, size_type item_count) 88 | : circular_buffer_adapter(data, data + capacity, data + first_index, 89 | item_count) {} 90 | 91 | circular_buffer_adapter(pointer begin, pointer end) 92 | : circular_buffer_adapter(begin, end, begin, 0) {} 93 | 94 | circular_buffer_adapter(pointer begin, pointer end, pointer first, 95 | size_type item_count) 96 | : begin_{begin} 97 | , end_{end} 98 | , first_{first} 99 | , last_{add(first, item_count)} 100 | , size_{item_count} { 101 | assert(begin <= first); 102 | assert(first <= end); 103 | assert(size_ <= capacity()); 104 | } 105 | 106 | iterator begin() const { return iterator(this, first_iter()); } 107 | iterator end() const { return iterator(this, nullptr); } 108 | 109 | const_iterator cbegin() const { return const_iterator(this, first_iter()); } 110 | const_iterator cend() const { return const_iterator(this, nullptr); } 111 | 112 | reverse_iterator rbegin() const { return reverse_iterator(end()); } 113 | reverse_iterator rend() const { return reverse_iterator(begin()); } 114 | 115 | const_reverse_iterator crbegin() const { 116 | return const_reverse_iterator(cend()); 117 | } 118 | const_reverse_iterator crend() const { 119 | return const_reverse_iterator(cbegin()); 120 | } 121 | 122 | void clear() { 123 | destroy(first_, size_); 124 | first_ = last_ = begin_; 125 | size_ = 0; 126 | } 127 | 128 | bool empty() const { return size_ == 0; } 129 | bool full() const { return size_ == capacity(); } 130 | 131 | size_type capacity() const { return std::distance(begin_, end_); } 132 | size_type size() const { return size_; } 133 | size_type remaining() const { return capacity() - size_; } 134 | 135 | reference front() const { 136 | assert(!empty()); 137 | return *first_; 138 | } 139 | 140 | reference back() const { 141 | assert(!empty()); 142 | return *((last_ != begin_ ? last_ : end_) - 1); 143 | } 144 | 145 | reference operator[](size_type pos) const { 146 | assert(pos < size()); 147 | return *add(first_, pos); 148 | } 149 | 150 | reference at(size_type pos) const { 151 | #if LIBEMB_HAS_EXCEPTIONS 152 | if (pos >= size()) { 153 | throw std::out_of_range("circular_buffer_adapter"); 154 | } 155 | #endif 156 | return (*this)[pos]; 157 | } 158 | 159 | void push_front(const value_type& value) { 160 | assert(!full()); 161 | new (prev(first_)) value_type(value); 162 | dec(first_); 163 | ++size_; 164 | } 165 | 166 | void push_front(value_type&& value) { 167 | assert(!full()); 168 | new (prev(first_)) value_type(std::move(value)); 169 | dec(first_); 170 | ++size_; 171 | } 172 | 173 | template 174 | reference emplace_front(Args&&... args) { 175 | assert(!full()); 176 | auto p = new (prev(first_)) value_type(std::forward(args)...); 177 | dec(first_); 178 | ++size_; 179 | return *p; 180 | } 181 | 182 | void pop_front() { 183 | assert(!empty()); 184 | destroy(first_); 185 | inc(first_); 186 | --size_; 187 | } 188 | 189 | void pop_front(size_type count) { 190 | assert(count <= size()); 191 | destroy(first_, count); 192 | first_ = add(first_, count); 193 | size_ -= count; 194 | } 195 | 196 | void push_back(const value_type& value) { 197 | assert(!full()); 198 | new (last_) value_type(value); 199 | inc(last_); 200 | ++size_; 201 | } 202 | 203 | void push_back(value_type&& value) { 204 | assert(!full()); 205 | new (last_) value_type(std::move(value)); 206 | inc(last_); 207 | ++size_; 208 | } 209 | 210 | template 211 | reference emplace_back(Args&&... args) { 212 | assert(!full()); 213 | auto p = new (last_) value_type(std::forward(args)...); 214 | inc(last_); 215 | ++size_; 216 | return *p; 217 | } 218 | 219 | void pop_back() { 220 | assert(!empty()); 221 | dec(last_); 222 | destroy(last_); 223 | --size_; 224 | } 225 | 226 | void pop_back(size_type count) { 227 | assert(count <= size()); 228 | last_ = sub(last_, count); 229 | destroy(last_, count); 230 | size_ -= count; 231 | } 232 | 233 | template 234 | size_type raw_index(detail::cba_iterator const& it) const { 235 | return std::distance(begin_, it.realiter()); 236 | } 237 | 238 | template ::value && 240 | std::is_same::value, 241 | bool>::type = true> 242 | void copy_in_front(U const* data, size_type count) { 243 | assert(count <= remaining()); 244 | auto new_first = sub(first_, count); 245 | copy_in(new_first, data, count); 246 | size_ += count; 247 | first_ = new_first; 248 | } 249 | 250 | template ::value && 252 | std::is_same::value, 253 | bool>::type = true> 254 | void copy_in_back(U const* data, size_type count) { 255 | assert(count <= remaining()); 256 | copy_in(last_, data, count); 257 | last_ = add(last_, count); 258 | size_ += count; 259 | } 260 | 261 | template ::value && 263 | std::is_same::value, 264 | bool>::type = true> 265 | void copy_out_front(U* data, size_type count) { 266 | assert(count <= size()); 267 | copy_out(data, first_, count); 268 | first_ = add(first_, count); 269 | size_ -= count; 270 | } 271 | 272 | template ::value && 274 | std::is_same::value, 275 | bool>::type = true> 276 | void copy_out_back(U* data, size_type count) { 277 | assert(count <= size()); 278 | auto new_last = sub(last_, count); 279 | copy_out(data, new_last, count); 280 | size_ -= count; 281 | last_ = new_last; 282 | } 283 | 284 | template ::value && 286 | std::is_same::value, 287 | bool>::type = true> 288 | void copy_in(iterator first, iterator last, U const* data) { 289 | assert(begin() <= first); 290 | assert(first <= last); 291 | assert(last <= end()); 292 | copy_in(first.it_, data, last - first); 293 | } 294 | 295 | template ::value && 297 | std::is_same::value, 298 | bool>::type = true> 299 | void copy_out(const_iterator first, const_iterator last, U* data) const { 300 | assert(begin() <= first); 301 | assert(first <= last); 302 | assert(last <= end()); 303 | copy_out(data, first.it_, last - first); 304 | } 305 | 306 | private: 307 | template 308 | void copy_in_impl(pointer dest, value_type const* src, size_type count, 309 | Func const& copy_fn) { 310 | if (dest + count <= end_) { 311 | copy_fn(dest, src, count); 312 | } else { 313 | size_type const count_a = std::distance(dest, end_); 314 | size_type const count_b = count - count_a; 315 | copy_fn(dest, src, count_a); 316 | copy_fn(begin_, src + count_a, count_b); 317 | } 318 | } 319 | 320 | template 321 | void copy_out_impl(value_type* dest, const_pointer src, size_type count, 322 | Func const& copy_fn) const { 323 | if (src + count <= end_) { 324 | copy_fn(dest, src, count); 325 | } else { 326 | size_type const count_a = 327 | std::distance(src, const_cast(end_)); 328 | size_type const count_b = count - count_a; 329 | copy_fn(dest, src, count_a); 330 | copy_fn(dest + count_a, begin_, count_b); 331 | } 332 | } 333 | 334 | void copy_in(pointer dest, value_type const* src, size_type count) { 335 | copy_in_impl(dest, src, count, 336 | [](pointer dest, value_type const* src, size_type count) { 337 | std::memcpy(dest, src, sizeof(*dest) * count); 338 | }); 339 | } 340 | 341 | void copy_out(value_type* dest, const_pointer src, size_type count) const { 342 | copy_out_impl(dest, src, count, 343 | [](value_type* dest, const_pointer src, size_type count) { 344 | std::memcpy(dest, src, sizeof(*dest) * count); 345 | }); 346 | } 347 | 348 | template ::value, 349 | bool>::type = true> 350 | void destroy(pointer, size_type) {} 351 | 352 | template ::value, 353 | bool>::type = true> 354 | void destroy(pointer first, size_type count) { 355 | while (count > 0) { 356 | destroy(first); 357 | inc(first); 358 | --count; 359 | } 360 | } 361 | 362 | void destroy(value_type* p) const { p->~value_type(); } 363 | 364 | pointer first_iter() const { return size_ != 0 ? first_ : 0; } 365 | 366 | void inc(pointer& p) const { 367 | assert(p != nullptr); 368 | if (++p == end_) { 369 | p = begin_; 370 | } 371 | } 372 | 373 | void dec(pointer& p) const { 374 | assert(p != nullptr); 375 | if (p == begin_) { 376 | p = end_; 377 | } 378 | --p; 379 | } 380 | 381 | pointer prev(pointer p) const { 382 | dec(p); 383 | return p; 384 | } 385 | 386 | difference_type wrap_around(difference_type n) const { 387 | return n - static_cast(capacity()); 388 | } 389 | 390 | pointer add(pointer p, difference_type n) const { 391 | assert(p != nullptr); 392 | assert(n >= 0); 393 | return p + (n < std::distance(p, end_) ? n : wrap_around(n)); 394 | } 395 | 396 | pointer sub(pointer p, difference_type n) const { 397 | assert(p != nullptr); 398 | assert(n >= 0); 399 | return p - (n <= std::distance(begin_, p) ? n : wrap_around(n)); 400 | } 401 | 402 | difference_type index(const_pointer cp) const { 403 | assert(cp != nullptr); 404 | auto p = const_cast(cp); 405 | return p < first_ ? std::distance(first_, end_) + std::distance(begin_, p) 406 | : std::distance(first_, p); 407 | } 408 | 409 | pointer begin_{nullptr}; 410 | pointer end_{nullptr}; 411 | pointer first_{nullptr}; 412 | pointer last_{nullptr}; 413 | size_type size_{0}; 414 | }; 415 | 416 | } // namespace embedded 417 | -------------------------------------------------------------------------------- /include/embedded/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #if defined(__cpp_exceptions) 26 | #define LIBEMB_HAS_EXCEPTIONS 1 27 | #else 28 | #define LIBEMB_HAS_EXCEPTIONS 0 29 | #endif 30 | 31 | #if defined(__cpp_rtti) 32 | #define LIBEMB_HAS_RTTI 1 33 | #else 34 | #define LIBEMB_HAS_RTTI 0 35 | #endif 36 | -------------------------------------------------------------------------------- /include/embedded/constexpr_math.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include "constexpr_math/complex.h" 26 | #include "constexpr_math/constants.h" 27 | #include "constexpr_math/convolve.h" 28 | #include "constexpr_math/functions.h" 29 | #include "constexpr_math/poly.h" 30 | #include "constexpr_math/vector.h" 31 | -------------------------------------------------------------------------------- /include/embedded/constexpr_math/complex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include "functions.h" 28 | 29 | // clang-format off 30 | #ifdef __IAR_SYSTEMS_ICC__ 31 | #pragma diag_suppress=Pe540 32 | #endif 33 | // clang-format on 34 | 35 | namespace embedded { 36 | namespace cmath { 37 | 38 | template 39 | class vector; 40 | 41 | template 42 | class complex { 43 | public: 44 | static_assert(std::is_floating_point::value, 45 | "floating point type required for complex numbers"); 46 | 47 | constexpr complex() noexcept = default; 48 | 49 | constexpr complex(T const& real) noexcept 50 | : real_{real} {} 51 | 52 | constexpr complex(T const& real, T const& imag) noexcept 53 | : real_{real} 54 | , imag_{imag} {} 55 | 56 | constexpr T real() const noexcept { return real_; } 57 | 58 | constexpr T imag() const noexcept { return imag_; } 59 | 60 | constexpr T norm() const noexcept { return real_ * real_ + imag_ * imag_; } 61 | 62 | constexpr T abs() const noexcept { return cmath::sqrt(norm()); } 63 | 64 | constexpr T distance(complex const& z) const noexcept { 65 | return (*this - z).abs(); 66 | } 67 | 68 | constexpr bool is_real() const noexcept { return imag_ == T{}; } 69 | 70 | constexpr auto conj() const noexcept -> complex { 71 | return complex{real_, -imag_}; 72 | } 73 | 74 | constexpr bool operator==(complex const& z) const noexcept { 75 | return real_ == z.real_ && imag_ == z.imag_; 76 | } 77 | 78 | private: 79 | T real_{}, imag_{}; 80 | }; 81 | 82 | namespace detail { 83 | 84 | class real_transformer { 85 | public: 86 | template 87 | constexpr auto operator()(cmath::complex const& v) const noexcept -> T { 88 | return v.real(); 89 | } 90 | }; 91 | 92 | class imag_transformer { 93 | public: 94 | template 95 | constexpr auto operator()(cmath::complex const& v) const noexcept -> T { 96 | return v.imag(); 97 | } 98 | }; 99 | 100 | class norm_transformer { 101 | public: 102 | template 103 | constexpr auto operator()(cmath::complex const& v) const noexcept -> T { 104 | return v.norm(); 105 | } 106 | }; 107 | 108 | class abs_transformer { 109 | public: 110 | template 111 | constexpr auto operator()(cmath::complex const& v) const noexcept -> T { 112 | return v.abs(); 113 | } 114 | }; 115 | 116 | } // namespace detail 117 | 118 | template 119 | constexpr auto operator-(complex const& z) noexcept -> complex { 120 | return complex(-z.real(), -z.imag()); 121 | } 122 | 123 | template 124 | constexpr auto 125 | operator-(complex const& z1, complex const& z2) noexcept -> complex { 126 | return complex(z1.real() - z2.real(), z1.imag() - z2.imag()); 127 | } 128 | 129 | template 130 | constexpr auto 131 | operator+(complex const& z1, complex const& z2) noexcept -> complex { 132 | return complex(z1.real() + z2.real(), z1.imag() + z2.imag()); 133 | } 134 | 135 | template 136 | constexpr auto operator-(T v, complex const& z) noexcept -> complex { 137 | return complex(v) - z; 138 | } 139 | 140 | template 141 | constexpr auto operator+(T v, complex const& z) noexcept -> complex { 142 | return complex(v) + z; 143 | } 144 | 145 | template 146 | constexpr auto 147 | operator*(complex const& z1, complex const& z2) noexcept -> complex { 148 | return complex(z1.real() * z2.real() - z1.imag() * z2.imag(), 149 | z1.real() * z2.imag() + z2.real() * z1.imag()); 150 | } 151 | 152 | template 153 | constexpr auto 154 | operator/(complex const& z1, complex const& z2) noexcept -> complex { 155 | return (T{1} / z2.norm()) * 156 | complex(z1.real() * z2.real() + z1.imag() * z2.imag(), 157 | z1.imag() * z2.real() - z1.real() * z2.imag()); 158 | } 159 | 160 | template 161 | constexpr auto 162 | operator/(T const& x, complex const& z) noexcept -> complex { 163 | return complex(x) / z; 164 | } 165 | 166 | template 167 | constexpr auto 168 | operator/(complex const& z, T const& x) noexcept -> complex { 169 | return z / complex(x); 170 | } 171 | 172 | template 173 | constexpr auto 174 | operator*(T const& x, complex const& z) noexcept -> complex { 175 | return complex(x) * z; 176 | } 177 | 178 | template 179 | constexpr auto exp(complex const& z) noexcept -> complex { 180 | return cmath::exp(z.real()) * 181 | complex(cmath::cos(z.imag()), cmath::sin(z.imag())); 182 | } 183 | 184 | template 185 | constexpr auto 186 | real(vector, Size> const& a) noexcept -> vector { 187 | return a.template transform(detail::real_transformer()); 188 | } 189 | 190 | template 191 | constexpr auto 192 | imag(vector, Size> const& a) noexcept -> vector { 193 | return a.template transform(detail::imag_transformer()); 194 | } 195 | 196 | template 197 | constexpr auto 198 | norm(vector, Size> const& a) noexcept -> vector { 199 | return a.template transform(detail::norm_transformer()); 200 | } 201 | 202 | template 203 | constexpr auto 204 | abs(vector, Size> const& a) noexcept -> vector { 205 | return a.template transform(detail::abs_transformer()); 206 | } 207 | 208 | } // namespace cmath 209 | } // namespace embedded 210 | 211 | // clang-format off 212 | #ifdef __IAR_SYSTEMS_ICC__ 213 | #pragma diag_default=Pe540 214 | #endif 215 | // clang-format on 216 | -------------------------------------------------------------------------------- /include/embedded/constexpr_math/constants.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | // clang-format off 26 | #ifdef __IAR_SYSTEMS_ICC__ 27 | #pragma diag_suppress=Pe540 28 | #endif 29 | // clang-format on 30 | 31 | namespace embedded { 32 | namespace cmath { 33 | 34 | template 35 | constexpr T pi() noexcept { 36 | return T{3.1415926535897932384626433832795028841971l}; 37 | } 38 | 39 | } // namespace cmath 40 | } // namespace embedded 41 | 42 | // clang-format off 43 | #ifdef __IAR_SYSTEMS_ICC__ 44 | #pragma diag_default=Pe540 45 | #endif 46 | // clang-format on 47 | -------------------------------------------------------------------------------- /include/embedded/constexpr_math/convolve.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include "../utility/integer_sequence.h" 28 | #include "vector.h" 29 | 30 | // clang-format off 31 | #ifdef __IAR_SYSTEMS_ICC__ 32 | #pragma diag_suppress=Pe540 33 | #endif 34 | // clang-format on 35 | 36 | namespace embedded { 37 | namespace cmath { 38 | 39 | namespace detail { 40 | 41 | template 42 | constexpr auto convolve_single(vector const& a, vector const& b, 43 | I n, I m = 0) noexcept -> T { 44 | return (m < I(a.size()) ? a[m] : T{}) * 45 | (n >= m && n < I(b.size()) + m ? b[n - m] : T{}) + 46 | (m < n ? convolve_single(a, b, n, m + 1) : T{}); 47 | } 48 | 49 | template 50 | constexpr auto 51 | convolve_full_impl(vector const& a, vector const& b, 52 | index_sequence) noexcept -> vector { 53 | return vector{convolve_single(a, b, Ints)...}; 54 | } 55 | 56 | } // namespace detail 57 | 58 | template 59 | constexpr auto 60 | convolve_full(vector const& a, vector const& b) noexcept 61 | -> vector { 62 | return detail::convolve_full_impl(a, b, make_index_sequence{}); 63 | } 64 | 65 | } // namespace cmath 66 | } // namespace embedded 67 | 68 | // clang-format off 69 | #ifdef __IAR_SYSTEMS_ICC__ 70 | #pragma diag_default=Pe540 71 | #endif 72 | // clang-format on 73 | -------------------------------------------------------------------------------- /include/embedded/constexpr_math/functions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | // TODO: this is probably a poor check 26 | #if !defined(LIBEMB_DISABLE_GLIBCXX_CONSTEXPR) && \ 27 | !defined(__INTEL_COMPILER) && !defined(__clang__) && defined(__GNUG__) && \ 28 | defined(_GLIBCXX_CONSTEXPR) 29 | 30 | #include 31 | #include 32 | 33 | namespace embedded { 34 | namespace cmath { 35 | 36 | using ::std::abs; 37 | using ::std::acos; 38 | using ::std::acosh; 39 | using ::std::asin; 40 | using ::std::asinh; 41 | using ::std::atan; 42 | using ::std::atan2; 43 | using ::std::atanh; 44 | using ::std::ceil; 45 | using ::std::copysign; 46 | using ::std::cos; 47 | using ::std::cosh; 48 | using ::std::erf; 49 | using ::std::exp; 50 | using ::std::expm1; 51 | using ::std::floor; 52 | using ::std::fmod; 53 | using ::std::lgamma; 54 | using ::std::log; 55 | using ::std::log10; 56 | using ::std::log1p; 57 | using ::std::log2; 58 | 59 | #if __cplusplus >= 201402L 60 | using ::std::max; 61 | using ::std::min; 62 | #else 63 | template 64 | static constexpr const T& min(const T& a, const T& b) { 65 | return a < b ? a : b; 66 | } 67 | 68 | template 69 | static constexpr const T& max(const T& a, const T& b) { 70 | return a < b ? b : a; 71 | } 72 | #endif 73 | 74 | using ::std::pow; 75 | using ::std::round; 76 | using ::std::signbit; 77 | using ::std::sin; 78 | using ::std::sinh; 79 | using ::std::sqrt; 80 | using ::std::tan; 81 | using ::std::tanh; 82 | using ::std::tgamma; 83 | using ::std::trunc; 84 | 85 | } // namespace cmath 86 | } // namespace embedded 87 | 88 | #else 89 | 90 | // #define GCEM_ERF_MAX_ITER (2*60) 91 | // #define GCEM_ERF_INV_MAX_ITER (2*55) 92 | // #define GCEM_EXP_MAX_ITER_SMALL (2*25) 93 | // #define GCEM_LOG_MAX_ITER_SMALL (2*25) 94 | // #define GCEM_LOG_MAX_ITER_BIG (2*255) 95 | // #define GCEM_INCML_BETA_TOL 1E-16 96 | // #define GCEM_INCML_BETA_MAX_ITER (2*205) 97 | // #define GCEM_INCML_BETA_INV_MAX_ITER (2*35) 98 | // #define GCEM_INCML_GAMMA_MAX_ITER (2*55) 99 | // #define GCEM_INCML_GAMMA_INV_MAX_ITER (2*35) 100 | // #define GCEM_SQRT_MAX_ITER (2*100) 101 | // #define GCEM_TAN_MAX_ITER (2*35) 102 | // #define GCEM_TANH_MAX_ITER (2*35) 103 | 104 | // clang-format off 105 | #ifdef __IAR_SYSTEMS_ICC__ 106 | #pragma diag_suppress=Pe540 107 | #endif 108 | // clang-format on 109 | 110 | #include 111 | 112 | // clang-format off 113 | #ifdef __IAR_SYSTEMS_ICC__ 114 | #pragma diag_default=Pe540 115 | #endif 116 | // clang-format on 117 | 118 | namespace embedded { 119 | namespace cmath { 120 | 121 | using ::gcem::abs; 122 | using ::gcem::acos; 123 | using ::gcem::acosh; 124 | using ::gcem::asin; 125 | using ::gcem::asinh; 126 | using ::gcem::atan; 127 | using ::gcem::atan2; 128 | using ::gcem::atanh; 129 | using ::gcem::ceil; 130 | using ::gcem::copysign; 131 | using ::gcem::cos; 132 | using ::gcem::cosh; 133 | using ::gcem::erf; 134 | using ::gcem::exp; 135 | using ::gcem::expm1; 136 | using ::gcem::floor; 137 | using ::gcem::fmod; 138 | using ::gcem::lgamma; 139 | using ::gcem::lmgamma; 140 | using ::gcem::log; 141 | using ::gcem::log10; 142 | using ::gcem::log1p; 143 | using ::gcem::log2; 144 | using ::gcem::max; 145 | using ::gcem::min; 146 | using ::gcem::pow; 147 | using ::gcem::round; 148 | using ::gcem::signbit; 149 | using ::gcem::sin; 150 | using ::gcem::sinh; 151 | using ::gcem::sqrt; 152 | using ::gcem::tan; 153 | using ::gcem::tanh; 154 | using ::gcem::tgamma; 155 | using ::gcem::trunc; 156 | 157 | } // namespace cmath 158 | } // namespace embedded 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /include/embedded/constexpr_math/poly.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include "convolve.h" 28 | 29 | // clang-format off 30 | #ifdef __IAR_SYSTEMS_ICC__ 31 | #pragma diag_suppress=Pe540 32 | #endif 33 | // clang-format on 34 | 35 | namespace embedded { 36 | namespace cmath { 37 | 38 | namespace detail { 39 | 40 | template 41 | struct poly_rec { 42 | template 43 | constexpr auto 44 | operator()(vector const& a, vector const& zeros) const noexcept 45 | -> vector { 46 | return convolve_full(poly_rec()(a, zeros), 47 | vector{T{1}, -zeros[N - 1]}); 48 | } 49 | }; 50 | 51 | template <> 52 | struct poly_rec<0> { 53 | template 54 | constexpr auto 55 | operator()(vector const& a, 56 | vector const& /*zeros*/) const noexcept -> vector { 57 | return a; 58 | } 59 | }; 60 | 61 | } // namespace detail 62 | 63 | template 64 | constexpr auto poly(vector const& zeros) noexcept -> vector { 65 | return detail::poly_rec()(vector{T{1}}, zeros); 66 | } 67 | 68 | } // namespace cmath 69 | } // namespace embedded 70 | 71 | // clang-format off 72 | #ifdef __IAR_SYSTEMS_ICC__ 73 | #pragma diag_default=Pe540 74 | #endif 75 | // clang-format on 76 | -------------------------------------------------------------------------------- /include/embedded/constexpr_math/vector.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "../type_traits/conjunction.h" 29 | #include "../utility/integer_sequence.h" 30 | 31 | // clang-format off 32 | #ifdef __IAR_SYSTEMS_ICC__ 33 | #pragma diag_suppress=Pe540 34 | #endif 35 | // clang-format on 36 | 37 | namespace embedded { 38 | namespace cmath { 39 | 40 | template 41 | class vector; 42 | 43 | namespace detail { 44 | 45 | template 46 | class multiply { 47 | public: 48 | constexpr multiply(F x) 49 | : x_{x} {} 50 | 51 | template 52 | constexpr auto operator()(T const& v) const noexcept -> T { 53 | return x_ * v; 54 | } 55 | 56 | private: 57 | F x_; 58 | }; 59 | 60 | template 61 | class add { 62 | public: 63 | constexpr add(F x) 64 | : x_{x} {} 65 | 66 | template 67 | constexpr auto operator()(T const& v) const noexcept -> T { 68 | return x_ + v; 69 | } 70 | 71 | private: 72 | F x_; 73 | }; 74 | 75 | class negate { 76 | public: 77 | template 78 | constexpr auto operator()(T const& v) const noexcept -> T { 79 | return -v; 80 | } 81 | }; 82 | 83 | template 84 | class divide { 85 | public: 86 | constexpr divide(F x) 87 | : x_{x} {} 88 | 89 | template 90 | constexpr auto operator()(T const& v) const noexcept -> T { 91 | return x_ / v; 92 | } 93 | 94 | private: 95 | F x_; 96 | }; 97 | 98 | template 99 | class divide_by { 100 | public: 101 | constexpr divide_by(F x) 102 | : x_{x} {} 103 | 104 | template 105 | constexpr auto operator()(T const& v) const noexcept -> T { 106 | return v / x_; 107 | } 108 | 109 | private: 110 | F x_; 111 | }; 112 | 113 | class exp_functor { 114 | public: 115 | template 116 | constexpr auto operator()(T const& v) const noexcept -> T { 117 | return exp(v); 118 | } 119 | }; 120 | 121 | class prod_reducer { 122 | public: 123 | template 124 | constexpr auto operator()(F const& a, F const& v) const noexcept -> F { 125 | return a * v; 126 | } 127 | }; 128 | 129 | template 130 | struct compare { 131 | constexpr int operator()(T const& a, T const& b) const noexcept { 132 | return a[I] < b[I] ? -1 : a[I] > b[I] ? 1 : compare()(a, b); 133 | } 134 | }; 135 | 136 | template 137 | struct compare { 138 | constexpr int operator()(T const&, T const&) const noexcept { return 0; } 139 | }; 140 | 141 | template 142 | struct argmin { 143 | constexpr std::size_t operator()(T const& a, Pred const& pred, 144 | std::size_t min_index = 0) const noexcept { 145 | return argmin()( 146 | a, pred, pred(a[I], a[min_index]) ? I : min_index); 147 | } 148 | }; 149 | 150 | template 151 | struct argmin { 152 | constexpr std::size_t 153 | operator()(T const&, Pred const&, std::size_t min_index = 0) const noexcept { 154 | return min_index; 155 | } 156 | }; 157 | 158 | template 159 | struct count { 160 | constexpr std::size_t 161 | operator()(T const& a, Pred const& pred) const noexcept { 162 | return pred(a[I]) + count()(a, pred); 163 | } 164 | }; 165 | 166 | template 167 | struct count { 168 | constexpr std::size_t operator()(T const&, Pred const&) const noexcept { 169 | return 0; 170 | } 171 | }; 172 | 173 | template 174 | struct sort { 175 | constexpr auto 176 | operator()(vector const& a, Pred const& pred) const noexcept 177 | -> vector { 178 | return sort_min(a, pred, a.argmin(pred)); 179 | } 180 | 181 | private: 182 | constexpr auto 183 | sort_min(vector const& a, Pred const& pred, 184 | std::size_t min) const noexcept -> vector { 185 | return vector{a[min]}.append( 186 | sort{}(a.swappop(min), pred)); 187 | } 188 | }; 189 | 190 | template 191 | struct sort { 192 | constexpr auto operator()(vector const& a, Pred const&) const noexcept 193 | -> vector { 194 | return a; 195 | } 196 | }; 197 | 198 | template 199 | class constant { 200 | public: 201 | constexpr constant(T c = T{}) 202 | : c_{c} {} 203 | 204 | constexpr auto operator()(int) const noexcept -> T { return c_; } 205 | 206 | private: 207 | T c_; 208 | }; 209 | 210 | } // namespace detail 211 | 212 | template 213 | struct vector_traits { 214 | using underlying_type = T[Size]; 215 | 216 | static constexpr T const* 217 | pointer(underlying_type const& x, std::size_t i) noexcept { 218 | return &x[i]; 219 | } 220 | }; 221 | 222 | template 223 | struct vector_traits { 224 | struct underlying_type {}; 225 | 226 | static constexpr T const* 227 | pointer(underlying_type const&, std::size_t) noexcept { 228 | return nullptr; 229 | } 230 | }; 231 | 232 | template 233 | class vector { 234 | public: 235 | using value_type = T; 236 | using size_type = std::size_t; 237 | using const_reference = T const&; 238 | using traits_type = vector_traits; 239 | 240 | template 241 | friend class vector; 242 | 243 | template ...>::value>::type> 246 | constexpr vector(Args&&... args) 247 | : items_{args...} {} 248 | 249 | template ::value>::type> 251 | constexpr vector(vector const& other) 252 | : vector{other, make_index_sequence{}} {} 253 | 254 | template 255 | static constexpr auto create(Fn const& fn) noexcept -> vector { 256 | return create_impl(fn, make_index_sequence{}); 257 | } 258 | 259 | static constexpr auto full(T const& value) noexcept -> vector { 260 | return create(detail::constant{value}); 261 | } 262 | 263 | static constexpr auto ones() noexcept -> vector { return full(T{1}); } 264 | 265 | static constexpr auto zeros() noexcept -> vector { return full(T{0}); } 266 | 267 | constexpr const_reference operator[](size_type i) const noexcept { 268 | return *traits_type::pointer(items_, i); 269 | } 270 | 271 | static constexpr size_type size() noexcept { return Size; } 272 | 273 | template 274 | constexpr auto transform(Fn const& fn) const noexcept -> vector { 275 | return transform_impl(fn, make_index_sequence{}); 276 | } 277 | 278 | template 279 | constexpr auto 280 | reduce(Fn const& fn, value_type initial = value_type{1}) const noexcept 281 | -> value_type { 282 | return Size > 0 ? reduce_impl(initial, fn, Size - 1) : initial; 283 | } 284 | 285 | template 286 | constexpr auto append(vector const& other) const noexcept 287 | -> vector { 288 | return append_impl(other, make_index_sequence{}, 289 | make_index_sequence{}); 290 | } 291 | 292 | template 293 | constexpr auto subvector() const noexcept -> vector { 294 | static_assert(Pos + Count <= Size, "invalid subvector range"); 295 | return subvector_impl(make_index_range{}); 296 | } 297 | 298 | constexpr bool operator==(vector const& other) const noexcept { 299 | return detail::compare()(*this, other) == 0; 300 | } 301 | 302 | template 303 | constexpr std::size_t argmin(Pred const& pred) const noexcept { 304 | return detail::argmin()(*this, pred); 305 | } 306 | 307 | template 308 | constexpr std::size_t count(Pred const& pred) const noexcept { 309 | return detail::count()(*this, pred); 310 | } 311 | 312 | template 313 | constexpr auto erase() const noexcept -> vector { 314 | return subvector<0, Pos>().append( 315 | subvector()); 316 | } 317 | 318 | constexpr auto swap(std::size_t a, std::size_t b) const noexcept -> vector { 319 | return swap_impl(a, b, make_index_sequence{}); 320 | } 321 | 322 | constexpr auto 323 | swappop(std::size_t i) const noexcept -> vector { 324 | return swap(i, 0).template erase<0>(); 325 | } 326 | 327 | template 328 | constexpr auto sort(Pred const& pred) const noexcept -> vector { 329 | return detail::sort{}(*this, pred); 330 | } 331 | 332 | private: 333 | template ::value>::type> 336 | constexpr vector(vector const& other, 337 | index_sequence) noexcept 338 | : items_{static_cast(other[Ints])...} {} 339 | 340 | constexpr std::size_t 341 | swap_idx(std::size_t a, std::size_t b, std::size_t i) const noexcept { 342 | return i == a ? b : i == b ? a : i; 343 | } 344 | 345 | template 346 | constexpr auto swap_impl(std::size_t a, std::size_t b, 347 | index_sequence) const noexcept -> vector { 348 | return vector{(*this)[swap_idx(a, b, Ints)]...}; 349 | } 350 | 351 | template 352 | constexpr auto subvector_impl(index_sequence) const noexcept 353 | -> vector { 354 | return vector{(*this)[Ints]...}; 355 | } 356 | 357 | template 358 | static constexpr auto 359 | create_impl(Fn const& fn, index_sequence) noexcept -> vector { 360 | return vector{fn(Ints)...}; 361 | } 362 | 363 | template 364 | constexpr auto 365 | append_impl(vector const& other, index_sequence, 366 | index_sequence) const noexcept 367 | -> vector { 368 | return vector{(*this)[Ints1]..., other[Ints2]...}; 369 | } 370 | 371 | template 372 | constexpr auto 373 | reduce_impl(value_type value, Fn const& fn, std::size_t i) const noexcept 374 | -> value_type { 375 | return i > 0 ? reduce_impl(fn(value, (*this)[i]), fn, i - 1) 376 | : fn(value, (*this)[i]); 377 | } 378 | 379 | template 380 | constexpr auto 381 | transform_impl(Fn const& fn, index_sequence) const noexcept 382 | -> vector { 383 | return vector{fn((*this)[Ints])...}; 384 | } 385 | 386 | typename traits_type::underlying_type items_; 387 | }; 388 | 389 | template 390 | constexpr auto make_vector(Fn const& fn, index_sequence) noexcept 391 | -> vector { 392 | return vector{fn(Ints)...}; 393 | } 394 | 395 | template 396 | constexpr auto operator-(vector const& a) noexcept -> vector { 397 | return a.transform(detail::negate()); 398 | } 399 | 400 | template 401 | constexpr auto 402 | operator*(T1 const& x, vector const& a) noexcept -> vector { 403 | return a.transform(detail::multiply(x)); 404 | } 405 | 406 | template 407 | constexpr auto 408 | operator+(T1 const& x, vector const& a) noexcept -> vector { 409 | return a.transform(detail::add(x)); 410 | } 411 | 412 | template 413 | constexpr auto 414 | operator/(T1 const& x, vector const& a) noexcept -> vector { 415 | return a.transform(detail::divide(x)); 416 | } 417 | 418 | template 419 | constexpr auto 420 | operator/(vector const& a, T2 const& x) noexcept -> vector { 421 | return a.transform(detail::divide_by(x)); 422 | } 423 | 424 | template 425 | constexpr auto exp(vector const& a) noexcept -> vector { 426 | return a.transform(detail::exp_functor()); 427 | } 428 | 429 | template 430 | constexpr auto prod(cmath::vector const& a) noexcept -> F { 431 | return a.reduce(detail::prod_reducer()); 432 | } 433 | 434 | } // namespace cmath 435 | } // namespace embedded 436 | 437 | // clang-format off 438 | #ifdef __IAR_SYSTEMS_ICC__ 439 | #pragma diag_default=Pe540 440 | #endif 441 | // clang-format on 442 | -------------------------------------------------------------------------------- /include/embedded/detail/circular_buffer_adapter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace embedded { 30 | 31 | template 32 | class circular_buffer_adapter; 33 | 34 | namespace detail { 35 | 36 | template 37 | struct cba_const_iterator_traits { 38 | using value_type = typename std::remove_const::type; 39 | using pointer = T const*; 40 | using reference = T const&; 41 | using size_type = std::size_t; 42 | using difference_type = std::ptrdiff_t; 43 | }; 44 | 45 | template 46 | struct cba_mutable_iterator_traits { 47 | using value_type = typename std::remove_const::type; 48 | using pointer = T*; 49 | using reference = T&; 50 | using size_type = std::size_t; 51 | using difference_type = std::ptrdiff_t; 52 | }; 53 | 54 | template 55 | class cba_iterator { 56 | public: 57 | friend class circular_buffer_adapter; 58 | friend class cba_iterator>; 59 | friend class cba_iterator>; 60 | 61 | using iterator_category = std::random_access_iterator_tag; 62 | using value_type = typename Traits::value_type; 63 | using reference = typename Traits::reference; 64 | using pointer = typename Traits::pointer; 65 | using size_type = typename Traits::size_type; 66 | using difference_type = typename Traits::difference_type; 67 | 68 | cba_iterator() = default; 69 | 70 | cba_iterator(cba_iterator> const& other) 71 | : adapter_{other.adapter_} 72 | , it_{other.it_} {} 73 | 74 | cba_iterator(cba_iterator&&) = default; 75 | cba_iterator& operator=(cba_iterator const&) = default; 76 | cba_iterator& operator=(cba_iterator&&) = default; 77 | 78 | pointer operator->() const { return it_; } 79 | 80 | reference operator*() const { 81 | assert(it_ != nullptr); 82 | return *it_; 83 | } 84 | 85 | reference operator[](difference_type n) const { 86 | assert(index() + n < static_cast(adapter_->size())); 87 | return *(*this + n); 88 | } 89 | 90 | template 91 | bool operator==(const cba_iterator& other) const { 92 | return it_ == other.it_; 93 | } 94 | 95 | template 96 | bool operator!=(const cba_iterator& other) const { 97 | return !(*this == other); 98 | } 99 | 100 | template 101 | bool operator<(const cba_iterator& other) const { 102 | return index() < other.index(); 103 | } 104 | 105 | template 106 | bool operator>(const cba_iterator& other) const { 107 | return other < *this; 108 | } 109 | 110 | template 111 | bool operator<=(const cba_iterator& other) const { 112 | return !(other < *this); 113 | } 114 | 115 | template 116 | bool operator>=(const cba_iterator& other) const { 117 | return !(*this < other); 118 | } 119 | 120 | cba_iterator& operator++() { 121 | assert(index() < static_cast(adapter_->size())); 122 | adapter_->inc(it_); 123 | if (it_ == adapter_->last_) { 124 | it_ = nullptr; 125 | } 126 | return *this; 127 | } 128 | 129 | cba_iterator& operator--() { 130 | assert(index() > 0); 131 | if (it_ == 0) { 132 | it_ = adapter_->last_; 133 | } 134 | adapter_->dec(it_); 135 | return *this; 136 | } 137 | 138 | cba_iterator operator++(int) { 139 | auto tmp = *this; 140 | ++*this; 141 | return tmp; 142 | } 143 | 144 | cba_iterator operator--(int) { 145 | auto tmp = *this; 146 | --*this; 147 | return tmp; 148 | } 149 | 150 | cba_iterator& operator+=(difference_type n) { 151 | if (n > 0) { 152 | assert(index() + n <= static_cast(adapter_->size())); 153 | it_ = adapter_->add(it_, n); 154 | if (it_ == adapter_->last_) { 155 | it_ = nullptr; 156 | } 157 | } else if (n < 0) { 158 | *this -= -n; 159 | } 160 | return *this; 161 | } 162 | 163 | cba_iterator& operator-=(difference_type n) { 164 | if (n > 0) { 165 | assert(n <= index()); 166 | it_ = adapter_->sub(realiter(), n); 167 | } else if (n < 0) { 168 | *this += -n; 169 | } 170 | return *this; 171 | } 172 | 173 | cba_iterator operator+(difference_type n) const { 174 | return cba_iterator(*this) += n; 175 | } 176 | 177 | cba_iterator operator-(difference_type n) const { 178 | return cba_iterator(*this) -= n; 179 | } 180 | 181 | difference_type operator-(cba_iterator const& other) const { 182 | return index() - other.index(); 183 | } 184 | 185 | private: 186 | cba_iterator(circular_buffer_adapter const* adapter, pointer it) 187 | : adapter_{adapter} 188 | , it_{it} {} 189 | 190 | difference_type index() const { 191 | return it_ ? adapter_->index(it_) : adapter_->size(); 192 | } 193 | 194 | pointer realiter() const { return it_ ? it_ : adapter_->last_; } 195 | 196 | circular_buffer_adapter const* adapter_{nullptr}; 197 | pointer it_{nullptr}; 198 | }; 199 | 200 | template 201 | cba_iterator operator+(typename Traits::difference_type n, 202 | cba_iterator const& iter) { 203 | return iter + n; 204 | } 205 | 206 | } // namespace detail 207 | 208 | } // namespace embedded 209 | -------------------------------------------------------------------------------- /include/embedded/function.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "config.h" 33 | 34 | #if LIBEMB_HAS_EXCEPTIONS 35 | #include 36 | #endif 37 | 38 | #include "type_traits.h" 39 | 40 | namespace embedded { 41 | 42 | template 44 | class function; 45 | 46 | namespace detail { 47 | 48 | template 49 | struct wrap { 50 | using type = T; 51 | }; 52 | 53 | enum class oper { move, destroy }; 54 | 55 | template 56 | class vtbl; 57 | 58 | template 59 | class vtbl { 60 | public: 61 | using storage_t = void*; 62 | 63 | constexpr vtbl() noexcept {} 64 | 65 | template 66 | explicit constexpr vtbl(wrap) noexcept 67 | : call{&vt_call} 68 | , proc{&vt_proc} {} 69 | 70 | private: 71 | static Return empty_call(storage_t, Args&&...) { 72 | #if LIBEMB_HAS_EXCEPTIONS 73 | throw std::bad_function_call(); 74 | #else 75 | std::terminate(); 76 | #endif 77 | } 78 | 79 | static void empty_proc(oper, storage_t, storage_t) noexcept {} 80 | 81 | template 82 | static Return vt_call(storage_t s, Args&&... args) { 83 | return (*static_cast(s))(static_cast(args)...); 84 | } 85 | 86 | template 87 | static void vt_proc(oper op, storage_t src, storage_t dst) noexcept { 88 | switch (op) { 89 | case oper::move: 90 | ::new (dst) T{std::move(*static_cast(src))}; 91 | // fallthrough 92 | case oper::destroy: 93 | static_cast(src)->~T(); 94 | break; 95 | } 96 | } 97 | 98 | public: 99 | decltype(&empty_call) const call{&empty_call}; 100 | decltype(&empty_proc) const proc{&empty_proc}; 101 | }; 102 | 103 | template 104 | struct empty_vtbl { 105 | static vtbl constexpr value{}; 106 | }; 107 | 108 | template 109 | constexpr vtbl empty_vtbl::value; 110 | 111 | template 112 | struct typed_vtbl { 113 | static vtbl constexpr value{wrap{}}; 114 | }; 115 | 116 | template 117 | constexpr vtbl typed_vtbl::value; 118 | 119 | template 120 | struct is_function : std::false_type {}; 121 | template 122 | struct is_function> : std::true_type {}; 123 | 124 | template 125 | struct function_traits; 126 | 127 | template 128 | struct function_traits { 129 | template 130 | using is_invocable = is_invocable_r; 131 | using signature = Return(Args...); 132 | 133 | Return operator()(Args... args) { 134 | auto fn = static_cast*>(this); 135 | return fn->vtbl_->call(std::addressof(fn->storage_), 136 | std::forward(args)...); 137 | } 138 | }; 139 | 140 | template 141 | struct function_traits { 142 | template 143 | using is_invocable = is_invocable_r; 144 | using signature = Return(Args...); 145 | 146 | Return operator()(Args... args) const { 147 | auto fn = static_cast const*>(this); 148 | return fn->vtbl_->call(std::addressof(fn->storage_), 149 | std::forward(args)...); 150 | } 151 | }; 152 | 153 | } // namespace detail 154 | 155 | /** 156 | * A const-correct, move-only function wrapper with in-place storage 157 | * 158 | * This function wrapper is largely inspired by `folly::Function` [1], 159 | * which is another const-correct, move-only function wrapper. I had a 160 | * few discussions about folly::Function with the author during its 161 | * inception and have used it extensively in the past. However, it's 162 | * unsuitable as-is for embedded systems without dynamic memory 163 | * allocation, and it requires at least C++14 to build. 164 | * 165 | * The code also incorporates ideas from SG14's `inplace_function` [2], 166 | * which is a proposed function wrapper with only in-place storage. 167 | * 168 | * The implementation only requires C++11 to build. Also, by default, 169 | * objects are a lot smaller than `folly::Function` objects, and they 170 | * only use a single pointer internally instead of two, at the cost of 171 | * another level of indirection. 172 | * 173 | * [1] https://github.com/facebook/folly/blob/master/folly/docs/Function.md 174 | * [2] 175 | * https://github.com/WG21-SG14/SG14/blob/master/Docs/Proposals/NonAllocatingStandardFunction.pdf 176 | */ 177 | template 178 | class function final : private detail::function_traits { 179 | public: 180 | using traits = detail::function_traits; 181 | using signature = typename traits::signature; 182 | using vtbl_t = detail::vtbl; 183 | using empty_vtbl = detail::empty_vtbl; 184 | template 185 | using typed_vtbl = detail::typed_vtbl; 186 | 187 | friend traits; 188 | 189 | function() noexcept 190 | : vtbl_{&empty_vtbl::value} {} 191 | 192 | function(std::nullptr_t) noexcept 193 | : function() {} 194 | 195 | function(function const&) = delete; 196 | function& operator=(function const&) = delete; 197 | 198 | ~function() { 199 | vtbl_->proc(detail::oper::destroy, std::addressof(storage_), nullptr); 200 | } 201 | 202 | template ::type, 203 | typename = typename std::enable_if< 204 | !detail::is_function::value && 205 | traits::template is_invocable::value>::type> 206 | function(T&& fun) noexcept 207 | : vtbl_{&typed_vtbl::value} { 208 | static_assert( 209 | std::is_nothrow_move_constructible::value, 210 | "function<> can only be used with nothrow move-constructible types"); 211 | static_assert(sizeof(C) <= Capacity, 212 | "function<> storage too small for this type"); 213 | static_assert(Alignment % alignof(C) == 0, 214 | "function<> alignment too small for this type"); 215 | 216 | ::new (std::addressof(storage_)) C{std::forward(fun)}; 217 | } 218 | 219 | function(function&& other) noexcept 220 | : vtbl_(other.vtbl_) { 221 | other.vtbl_ = &empty_vtbl::value; 222 | vtbl_->proc(detail::oper::move, std::addressof(other.storage_), 223 | std::addressof(storage_)); 224 | } 225 | 226 | function& operator=(function&& other) noexcept { 227 | this->~function(); 228 | ::new (this) function(std::move(other)); 229 | return *this; 230 | } 231 | 232 | function& operator=(std::nullptr_t) noexcept { 233 | *this = function(); 234 | return *this; 235 | } 236 | 237 | template (nullptr)))> 239 | function(M C::*pm) noexcept 240 | : function() { 241 | if (pm) { 242 | *this = std::mem_fn(pm); 243 | } 244 | } 245 | 246 | template 247 | auto operator=(M C::*pm) noexcept -> decltype(operator=(std::mem_fn(pm))) { 248 | if (pm) { 249 | *this = std::mem_fn(pm); 250 | } else { 251 | *this = function(); 252 | } 253 | return *this; 254 | } 255 | 256 | explicit operator bool() const noexcept { 257 | return vtbl_ != &empty_vtbl::value; 258 | } 259 | 260 | using traits::operator(); 261 | 262 | private: 263 | vtbl_t const* vtbl_; 264 | alignas(Alignment) char mutable storage_[Capacity]; 265 | }; 266 | 267 | } // namespace embedded 268 | -------------------------------------------------------------------------------- /include/embedded/move_wrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | namespace embedded { 29 | 30 | template 31 | class move_wrapper { 32 | public: 33 | explicit move_wrapper(T&& t) noexcept(std::is_nothrow_constructible::value) 34 | : val_{std::forward(t)} {} 35 | 36 | move_wrapper(move_wrapper const& other) noexcept( 37 | std::is_nothrow_move_constructible::value) 38 | : val_{std::move(const_cast(other).val_)} {} 39 | move_wrapper(move_wrapper&& other) noexcept( 40 | std::is_nothrow_move_constructible::value) 41 | : val_{std::move(other.val_)} {} 42 | 43 | T const& operator*() const { return val_; } 44 | T& operator*() { return val_; } 45 | 46 | T const* operator->() const { return &val_; } 47 | T* operator->() { return &val_; } 48 | 49 | private: 50 | T val_; 51 | }; 52 | 53 | } // namespace embedded 54 | -------------------------------------------------------------------------------- /include/embedded/mutex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | namespace embedded { 26 | 27 | template 28 | class lock_guard { 29 | public: 30 | explicit lock_guard(Mutex& m) 31 | : m_(m) { 32 | m_.lock(); 33 | } 34 | ~lock_guard() { m_.unlock(); } 35 | lock_guard(lock_guard const&) = delete; 36 | lock_guard& operator=(lock_guard const&) = delete; 37 | 38 | private: 39 | Mutex& m_; 40 | }; 41 | 42 | } // namespace embedded 43 | -------------------------------------------------------------------------------- /include/embedded/ostream_ops.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "constexpr_math/complex.h" 29 | #include "constexpr_math/vector.h" 30 | #include "signal/detail/filter.h" 31 | 32 | namespace fpm { 33 | 34 | template 35 | std::ostream& operator<<(std::ostream& os, fixed const& f) { 36 | os << static_cast(f) << " (" << f.raw_value() << ")"; 37 | return os; 38 | } 39 | 40 | } // namespace fpm 41 | 42 | namespace embedded { 43 | 44 | namespace signal { 45 | namespace detail { 46 | 47 | template 48 | std::ostream& operator<<(std::ostream& os, zpk_value const& zpk) { 49 | os << "{zeros=" << zpk.zeros() << ", poles=" << zpk.poles() 50 | << ", gain=" << zpk.gain() << "}"; 51 | return os; 52 | } 53 | 54 | template 55 | std::ostream& 56 | operator<<(std::ostream& os, embedded::signal::sos_section const& s) { 57 | os << "{gain=" << s.gain_ << ", b=" << s.b() << ", a=" << s.a() << "}"; 58 | return os; 59 | } 60 | 61 | } // namespace detail 62 | } // namespace signal 63 | 64 | namespace cmath { 65 | 66 | template 67 | std::ostream& operator<<(std::ostream& os, complex const& z) { 68 | os << z.real(); 69 | if (z.imag() >= 0) { 70 | os << "+"; 71 | } 72 | os << z.imag() << "j"; 73 | return os; 74 | } 75 | 76 | template 77 | std::ostream& operator<<(std::ostream& os, vector const& a) { 78 | os << "["; 79 | for (std::size_t i = 0; i < Size; ++i) { 80 | if (i > 0) { 81 | os << ", "; 82 | } 83 | os << a[i]; 84 | } 85 | os << "]"; 86 | return os; 87 | } 88 | 89 | } // namespace cmath 90 | } // namespace embedded 91 | 92 | namespace std { 93 | 94 | template 95 | std::ostream& operator<<(std::ostream& os, array const& a) { 96 | os << "["; 97 | for (std::size_t i = 0; i < Size; ++i) { 98 | if (i > 0) { 99 | os << ", "; 100 | } 101 | os << a[i]; 102 | } 103 | os << "]"; 104 | return os; 105 | } 106 | 107 | } // namespace std 108 | -------------------------------------------------------------------------------- /include/embedded/preprocessor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #define LIBEMB_STRINGIFY(x) LIBEMB_STRINGIFY_IMPL(x) 26 | #define LIBEMB_STRINGIFY_IMPL(x) #x 27 | 28 | #define LIBEMB_CONCAT_IMPL(x, y) x##y 29 | #define LIBEMB_CONCAT(x, y) LIBEMB_CONCAT_IMPL(x, y) 30 | 31 | #ifdef __COUNTER__ 32 | #define LIBEMB_UNIQUE_NAME(prefix) LIBEMB_CONCAT(prefix, __COUNTER__) 33 | #else 34 | #define LIBEMB_UNIQUE_NAME(prefix) LIBEMB_CONCAT(prefix, __LINE__) 35 | #endif 36 | -------------------------------------------------------------------------------- /include/embedded/signal/bessel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include "detail/bessel_poles.h" 28 | #include "detail/filter.h" 29 | 30 | // clang-format off 31 | #ifdef __IAR_SYSTEMS_ICC__ 32 | #pragma diag_suppress=Pe540 33 | #endif 34 | // clang-format on 35 | 36 | namespace embedded { 37 | namespace signal { 38 | 39 | template 40 | class bessel { 41 | public: 42 | static_assert(Order > 0, "Filter order must be non-zero"); 43 | 44 | static constexpr auto order() noexcept -> std::size_t { return Order; } 45 | 46 | constexpr bessel() noexcept = default; 47 | 48 | template 49 | class specification { 50 | public: 51 | using value_type = F; 52 | 53 | template 54 | using carray = cmath::vector, N>; 55 | 56 | constexpr auto zeros() const noexcept -> carray<0> { return carray<0>{}; } 57 | 58 | constexpr auto poles() const noexcept -> carray { 59 | return detail::bessel_poles::value; 60 | } 61 | 62 | constexpr auto gain() const noexcept -> value_type { return value_type{1}; } 63 | 64 | constexpr auto 65 | zpk() const noexcept -> detail::zpk_value<0, Order, value_type> { 66 | return detail::zpk_value<0, Order, value_type>(zeros(), poles(), gain()); 67 | } 68 | }; 69 | 70 | template 71 | constexpr auto spec() const noexcept -> specification { 72 | return specification(); 73 | } 74 | }; 75 | 76 | } // namespace signal 77 | } // namespace embedded 78 | 79 | // clang-format off 80 | #ifdef __IAR_SYSTEMS_ICC__ 81 | #pragma diag_default=Pe540 82 | #endif 83 | // clang-format on 84 | -------------------------------------------------------------------------------- /include/embedded/signal/butterworth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include "detail/filter.h" 28 | 29 | // clang-format off 30 | #ifdef __IAR_SYSTEMS_ICC__ 31 | #pragma diag_suppress=Pe540 32 | #endif 33 | // clang-format on 34 | 35 | namespace embedded { 36 | namespace signal { 37 | 38 | template 39 | class butterworth { 40 | public: 41 | static_assert(Order > 0, "Filter order must be non-zero"); 42 | 43 | static constexpr auto order() noexcept -> std::size_t { return Order; } 44 | 45 | constexpr butterworth() noexcept = default; 46 | 47 | template 48 | class specification { 49 | public: 50 | using value_type = F; 51 | 52 | template 53 | using carray = cmath::vector, N>; 54 | 55 | constexpr auto zeros() const noexcept -> carray<0> { return carray<0>{}; } 56 | 57 | constexpr auto poles() const noexcept -> carray { 58 | return detail::butterworth_poles(); 59 | } 60 | 61 | constexpr auto gain() const noexcept -> value_type { return value_type{1}; } 62 | 63 | constexpr auto 64 | zpk() const noexcept -> detail::zpk_value<0, Order, value_type> { 65 | return detail::zpk_value<0, Order, value_type>(zeros(), poles(), gain()); 66 | } 67 | }; 68 | 69 | template 70 | constexpr auto spec() const noexcept -> specification { 71 | return specification(); 72 | } 73 | }; 74 | 75 | } // namespace signal 76 | } // namespace embedded 77 | 78 | // clang-format off 79 | #ifdef __IAR_SYSTEMS_ICC__ 80 | #pragma diag_default=Pe540 81 | #endif 82 | // clang-format on 83 | -------------------------------------------------------------------------------- /include/embedded/signal/chebyshev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include "detail/filter.h" 28 | 29 | // clang-format off 30 | #ifdef __IAR_SYSTEMS_ICC__ 31 | #pragma diag_suppress=Pe540 32 | #endif 33 | // clang-format on 34 | 35 | namespace embedded { 36 | namespace signal { 37 | 38 | template 39 | class chebyshev1 { 40 | public: 41 | static_assert(Order > 0, "Filter order must be non-zero"); 42 | 43 | static constexpr auto order() noexcept -> std::size_t { return Order; } 44 | 45 | constexpr chebyshev1(PF ripple) noexcept 46 | : ripple_{ripple} {} 47 | 48 | template 49 | class specification { 50 | public: 51 | using value_type = F; 52 | 53 | constexpr specification(PF ripple) noexcept 54 | : rf_{cmath::sqrt(cmath::pow(PF{10}, PF{0.1} * ripple) - PF{1})} {} 55 | 56 | template 57 | using carray = cmath::vector, N>; 58 | 59 | constexpr auto zeros() const noexcept -> carray<0> { return carray<0>{}; } 60 | 61 | constexpr auto poles() const noexcept -> carray { 62 | return (mu() + detail::theta()()) 63 | .transform(detail::minus_sinh()); 64 | } 65 | 66 | constexpr auto gain() const noexcept -> value_type { 67 | return prod(-poles()).real() / 68 | (Order % 2 == 0 ? cmath::sqrt(value_type{1} + rf_ * rf_) 69 | : value_type{1}); 70 | } 71 | 72 | constexpr auto 73 | zpk() const noexcept -> detail::zpk_value<0, Order, value_type> { 74 | return detail::zpk_value<0, Order, value_type>(zeros(), poles(), gain()); 75 | } 76 | 77 | constexpr auto rf() const noexcept -> value_type { return rf_; } 78 | 79 | private: 80 | constexpr value_type mu() const noexcept { 81 | return value_type{1} / value_type{Order} * 82 | cmath::asinh(value_type{1} / rf_); 83 | } 84 | 85 | value_type rf_; 86 | }; 87 | 88 | template 89 | constexpr auto spec() const noexcept -> specification { 90 | return specification(ripple_); 91 | } 92 | 93 | private: 94 | PF const ripple_; 95 | }; 96 | 97 | template 98 | class chebyshev2 { 99 | public: 100 | static_assert(Order > 0, "Filter order must be non-zero"); 101 | 102 | static constexpr auto order() noexcept -> std::size_t { return Order; } 103 | 104 | constexpr chebyshev2(PF ripple) noexcept 105 | : ripple_{ripple} {} 106 | 107 | template 108 | class specification { 109 | public: 110 | using value_type = F; 111 | static constexpr std::size_t Zn = Order - Order % 2; 112 | 113 | class warp_poles { 114 | public: 115 | constexpr warp_poles(value_type mu) noexcept 116 | : sinh_mu_{cmath::sinh(mu)} 117 | , cosh_mu_{cmath::cosh(mu)} {} 118 | 119 | constexpr auto 120 | operator()(cmath::complex const& v) const noexcept 121 | -> cmath::complex { 122 | return cmath::complex{1} / 123 | cmath::complex{sinh_mu_ * v.real(), 124 | cosh_mu_ * v.imag()}; 125 | } 126 | 127 | private: 128 | value_type const sinh_mu_; 129 | value_type const cosh_mu_; 130 | }; 131 | 132 | constexpr specification(PF ripple) noexcept 133 | : rf_{PF{1} / 134 | cmath::sqrt(cmath::pow(PF{10}, PF{0.1} * ripple) - PF{1})} {} 135 | 136 | template 137 | using carray = cmath::vector, N>; 138 | 139 | constexpr auto zeros() const noexcept -> carray { 140 | return value_type{1} / 141 | detail::theta()().transform( 142 | detail::minus_sinh()); 143 | } 144 | 145 | constexpr auto poles() const noexcept -> carray { 146 | return detail::butterworth_poles().transform( 147 | warp_poles(mu())); 148 | } 149 | 150 | constexpr auto gain() const noexcept -> value_type { 151 | return (prod(-poles()) / prod(-zeros())).real(); 152 | } 153 | 154 | constexpr auto 155 | zpk() const noexcept -> detail::zpk_value { 156 | return detail::zpk_value(zeros(), poles(), gain()); 157 | } 158 | 159 | constexpr auto rf() const noexcept -> value_type { return rf_; } 160 | 161 | private: 162 | constexpr value_type mu() const noexcept { 163 | return cmath::asinh(value_type{1} / rf_) / value_type{Order}; 164 | } 165 | 166 | value_type rf_; 167 | }; 168 | 169 | template 170 | constexpr auto spec() const noexcept -> specification { 171 | return specification(ripple_); 172 | } 173 | 174 | private: 175 | PF const ripple_; 176 | }; 177 | 178 | } // namespace signal 179 | } // namespace embedded 180 | 181 | // clang-format off 182 | #ifdef __IAR_SYSTEMS_ICC__ 183 | #pragma diag_default=Pe540 184 | #endif 185 | // clang-format on 186 | -------------------------------------------------------------------------------- /include/embedded/signal/debug_filter_coefs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include "detail/debug_filter_coefs.h" 26 | 27 | #ifndef LIBEMB_DEBUG_FILTER_COEFS_SECTION 28 | #define LIBEMB_DEBUG_FILTER_COEFS_SECTION ".libemb_filter_coefs" 29 | #endif 30 | 31 | #define LIBEMB_DEBUG_FILTER_COEFS_NAME(design, name) \ 32 | extern "C" ::embedded::signal::detail::filter_design_debug< \ 33 | std::decay::type> const \ 34 | __attribute__((section(LIBEMB_DEBUG_FILTER_COEFS_SECTION))) \ 35 | LIBEMB_UNIQUE_NAME(design) { \ 36 | design, name \ 37 | } 38 | 39 | #define LIBEMB_DEBUG_FILTER_COEFS(design) \ 40 | LIBEMB_DEBUG_FILTER_COEFS_NAME( \ 41 | design, #design " (" __FILE__ ":" LIBEMB_STRINGIFY(__LINE__) ")") 42 | -------------------------------------------------------------------------------- /include/embedded/signal/detail/debug_filter_coefs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "../../preprocessor.h" 29 | #include "../../utility/integer_sequence.h" 30 | #include "../poly.h" 31 | #include "../sos.h" 32 | 33 | // clang-format off 34 | #ifdef __IAR_SYSTEMS_ICC__ 35 | #pragma diag_suppress=Pe540 36 | #endif 37 | // clang-format on 38 | 39 | namespace embedded { 40 | namespace signal { 41 | namespace detail { 42 | 43 | enum class filter_debug_structure : uint8_t { 44 | SOS = 0, 45 | POLY = 1, 46 | }; 47 | 48 | enum class filter_debug_value_type : uint8_t { 49 | FLOAT = 0, 50 | DOUBLE = 1, 51 | LONG_DOUBLE = 2, 52 | }; 53 | 54 | struct filter_debug_header { 55 | uint32_t magic{0x544C4946}; 56 | uint16_t length; 57 | uint8_t version{0}; 58 | filter_debug_structure structure; 59 | filter_debug_value_type valtype; 60 | char name[119]; 61 | 62 | template 63 | constexpr filter_debug_header(uint16_t length, 64 | filter_debug_structure structure, 65 | filter_debug_value_type valtype, 66 | char const (&name)[S]) noexcept 67 | : filter_debug_header( 68 | length, structure, valtype, name, 69 | make_index_sequence{}) {} 70 | 71 | template 72 | constexpr filter_debug_header(uint16_t length_, 73 | filter_debug_structure structure_, 74 | filter_debug_value_type valtype_, 75 | char const (&name_)[S], 76 | index_sequence) noexcept 77 | : length{length_} 78 | , structure{structure_} 79 | , valtype{valtype_} 80 | , name{name_[Ints]...} {} 81 | }; 82 | 83 | static_assert(sizeof(filter_debug_header) % 8 == 0, 84 | "unexpected filter_debug_header size"); 85 | 86 | template 87 | struct filter_debug_value_type_of; 88 | 89 | template <> 90 | struct filter_debug_value_type_of { 91 | static constexpr auto value{filter_debug_value_type::FLOAT}; 92 | }; 93 | 94 | template <> 95 | struct filter_debug_value_type_of { 96 | static constexpr auto value{filter_debug_value_type::DOUBLE}; 97 | }; 98 | 99 | template <> 100 | struct filter_debug_value_type_of { 101 | static constexpr auto value{filter_debug_value_type::LONG_DOUBLE}; 102 | }; 103 | 104 | template 105 | struct filter_design_debug; 106 | 107 | template 108 | struct filter_design_debug<::embedded::signal::sos_design> { 109 | using Design = ::embedded::signal::sos_design; 110 | static constexpr uint16_t size = 111 | sizeof(filter_debug_header) + sizeof(typename Design::sos_array); 112 | 113 | filter_debug_header header; 114 | typename Design::sos_array coef; 115 | 116 | template 117 | constexpr filter_design_debug(Design const& d, char const (&name)[S]) noexcept 118 | : header{size, filter_debug_structure::SOS, 119 | filter_debug_value_type_of::value, name} 120 | , coef{d.sos()} {} 121 | }; 122 | 123 | template 124 | struct filter_design_debug<::embedded::signal::poly_design> { 125 | using Design = ::embedded::signal::poly_design; 126 | static constexpr uint16_t size = 127 | sizeof(filter_debug_header) + 2 * sizeof(typename Design::parray); 128 | 129 | filter_debug_header header; 130 | typename Design::parray b; 131 | typename Design::parray a; 132 | 133 | template 134 | constexpr filter_design_debug(Design const& d, char const (&name)[S]) noexcept 135 | : header{size, filter_debug_structure::POLY, 136 | filter_debug_value_type_of::value, name} 137 | , b{d.b()} 138 | , a{d.a()} {} 139 | }; 140 | 141 | } // namespace detail 142 | } // namespace signal 143 | } // namespace embedded 144 | 145 | // clang-format off 146 | #ifdef __IAR_SYSTEMS_ICC__ 147 | #pragma diag_default=Pe540 148 | #endif 149 | // clang-format on 150 | -------------------------------------------------------------------------------- /include/embedded/signal/detail/filter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "../../constexpr_math.h" 29 | #include "../../utility/integer_sequence.h" 30 | 31 | // clang-format off 32 | #ifdef __IAR_SYSTEMS_ICC__ 33 | #pragma diag_suppress=Pe540 34 | #endif 35 | // clang-format on 36 | 37 | namespace embedded { 38 | namespace signal { 39 | 40 | template 41 | struct sos_state; 42 | 43 | template 44 | class sos_section; 45 | 46 | namespace detail { 47 | 48 | template 49 | class zpk_value { 50 | public: 51 | using value_type = F; 52 | 53 | template 54 | using carray = cmath::vector, N>; 55 | 56 | constexpr zpk_value(carray const& z, carray const& p, value_type k) 57 | : z_{z} 58 | , p_{p} 59 | , k_{k} {} 60 | 61 | constexpr auto zeros() const noexcept -> carray const& { return z_; } 62 | constexpr auto poles() const noexcept -> carray const& { return p_; } 63 | constexpr auto gain() const noexcept -> value_type { return k_; } 64 | 65 | constexpr auto 66 | even() const noexcept -> zpk_value { 67 | return zpk_value{ 68 | z_.append(carray::zeros()), p_.append(carray::zeros()), 69 | k_}; 70 | } 71 | 72 | private: 73 | carray const z_; 74 | carray const p_; 75 | value_type const k_; 76 | }; 77 | 78 | template 79 | class theta { 80 | public: 81 | static constexpr bool RemoveZero = !IncludeZero && N % 2 != 0; 82 | static constexpr std::size_t M = N - static_cast(RemoveZero); 83 | 84 | constexpr auto 85 | operator()() const noexcept -> cmath::vector, M> { 86 | return cmath::make_vector>(single_pole, 87 | make_index_sequence{}); 88 | } 89 | 90 | private: 91 | static constexpr int index(std::size_t i) noexcept { 92 | return 2 * (int(i) + int(RemoveZero && int(i) >= int(N) / 2)) + 1 - int(N); 93 | } 94 | 95 | static constexpr auto 96 | single_pole(std::size_t i) noexcept -> cmath::complex { 97 | return cmath::complex(F{0}, F{cmath::pi() * index(i) / (2 * int(N))}); 98 | } 99 | }; 100 | 101 | struct minus_sinh { 102 | template 103 | constexpr auto 104 | operator()(cmath::complex const& v) const noexcept -> cmath::complex { 105 | return -(exp(v) - exp(-v)) / F{2}; 106 | } 107 | }; 108 | 109 | template 110 | constexpr auto warp_frequency(F freq, F fs) noexcept -> F { 111 | return F{2} * fs * cmath::tan(cmath::pi() * freq / fs); 112 | } 113 | 114 | template 115 | constexpr auto lowpass_zpk(zpk_value const& zpk, F f) noexcept 116 | -> zpk_value { 117 | return zpk_value(f * zpk.zeros(), f * zpk.poles(), 118 | zpk.gain() * cmath::pow(f, Pn - Zn)); 119 | } 120 | 121 | template 122 | class highpass_gain { 123 | public: 124 | constexpr auto 125 | operator()(cmath::complex const& a, 126 | cmath::complex const& v) const noexcept -> cmath::complex { 127 | return a * (-v); 128 | } 129 | }; 130 | 131 | template 132 | constexpr auto highpass_zpk(zpk_value const& zpk, F f) noexcept 133 | -> zpk_value { 134 | return zpk_value( 135 | (f / zpk.zeros()) 136 | .append(cmath::vector, Pn - Zn>::zeros()), 137 | f / zpk.poles(), 138 | zpk.gain() * (zpk.zeros().reduce(highpass_gain{}) / 139 | zpk.poles().reduce(highpass_gain{})) 140 | .real()); 141 | } 142 | 143 | template 144 | class bilinear_zp { 145 | public: 146 | constexpr bilinear_zp(F fs) 147 | : fs2_{F{2} * fs} {} 148 | 149 | constexpr auto 150 | operator()(cmath::complex const& v) const noexcept -> cmath::complex { 151 | return (fs2_ + v) / (fs2_ - v); 152 | } 153 | 154 | private: 155 | F fs2_; 156 | }; 157 | 158 | template 159 | class bilinear_gain { 160 | public: 161 | constexpr bilinear_gain(F fs) 162 | : fs2_{F{2} * fs} {} 163 | 164 | constexpr auto 165 | operator()(cmath::complex const& a, 166 | cmath::complex const& v) const noexcept -> cmath::complex { 167 | return a * (fs2_ - v); 168 | } 169 | 170 | private: 171 | F fs2_; 172 | }; 173 | 174 | template 175 | constexpr auto 176 | bilinear_zpk(zpk_value const& zpk, F fs = F{2}) noexcept 177 | -> zpk_value { 178 | return zpk_value( 179 | zpk.zeros() 180 | .transform(bilinear_zp{fs}) 181 | .append(cmath::vector, Pn - Zn>::full(-1)), 182 | zpk.poles().transform(bilinear_zp{fs}), 183 | zpk.gain() * (zpk.zeros().reduce(bilinear_gain{fs}) / 184 | zpk.poles().reduce(bilinear_gain{fs})) 185 | .real()); 186 | } 187 | 188 | template 189 | class poly_df2t_update { 190 | public: 191 | using parray = cmath::vector; 192 | static constexpr std::size_t I = N - C - 1; 193 | 194 | void operator()(std::array& state, parray const& b, parray const& a, 195 | F x, F y) const { 196 | state[I] = b[I + 1] * x - a[I + 1] * y + state[I + 1], 197 | poly_df2t_update{}(state, b, a, x, y); 198 | } 199 | }; 200 | 201 | template 202 | class poly_df2t_update { 203 | public: 204 | using parray = cmath::vector; 205 | 206 | void operator()(std::array&, parray const&, parray const&, F, F) const { 207 | } 208 | }; 209 | 210 | class is_real { 211 | public: 212 | template 213 | constexpr bool operator()(cmath::complex const& x) const noexcept { 214 | return x.is_real(); 215 | } 216 | }; 217 | 218 | template 219 | static constexpr F unit_distance(cmath::complex const& z) noexcept { 220 | // norm() is cheaper than abs() 221 | return cmath::abs(F{1} - z.norm()); 222 | } 223 | 224 | struct unit_circle_distance_less { 225 | public: 226 | template 227 | constexpr bool operator()(cmath::complex const& a, 228 | cmath::complex const& b) const noexcept { 229 | return unit_distance(a) < unit_distance(b); 230 | } 231 | }; 232 | 233 | struct unit_circle_distance_real_less { 234 | public: 235 | template 236 | constexpr bool operator()(cmath::complex const& a, 237 | cmath::complex const& b) const noexcept { 238 | return a.is_real() < b.is_real() || 239 | (a.is_real() == a.is_real() && unit_distance(a) < unit_distance(b)); 240 | } 241 | }; 242 | 243 | template 244 | class distance_less { 245 | public: 246 | constexpr distance_less(cmath::complex const& z) noexcept 247 | : z_{z} {} 248 | 249 | constexpr bool operator()(cmath::complex const& a, 250 | cmath::complex const& b) const noexcept { 251 | return (a - z_).norm() < (b - z_).norm(); 252 | } 253 | 254 | private: 255 | cmath::complex const z_; 256 | }; 257 | 258 | template 259 | class distance_real_less { 260 | public: 261 | constexpr distance_real_less(cmath::complex const& z) noexcept 262 | : z_{z} {} 263 | 264 | constexpr bool operator()(cmath::complex const& a, 265 | cmath::complex const& b) const noexcept { 266 | return a.is_real() > b.is_real() || 267 | (a.is_real() == b.is_real() && (a - z_).norm() < (b - z_).norm()); 268 | } 269 | 270 | private: 271 | cmath::complex const z_; 272 | }; 273 | 274 | template 275 | class distance_complex_less { 276 | public: 277 | constexpr distance_complex_less(cmath::complex const& z) noexcept 278 | : z_{z} {} 279 | 280 | constexpr bool operator()(cmath::complex const& a, 281 | cmath::complex const& b) const noexcept { 282 | return a.is_real() < b.is_real() || 283 | (a.is_real() == b.is_real() && (a - z_).norm() < (b - z_).norm()); 284 | } 285 | 286 | private: 287 | cmath::complex const z_; 288 | }; 289 | 290 | template 291 | class zpk_to_sos { 292 | public: 293 | using value_type = F; 294 | using output_type = FO; 295 | 296 | using cnum = cmath::complex; 297 | 298 | template 299 | using carray = cmath::vector; 300 | 301 | template 302 | using sos_array = cmath::vector, N>; 303 | 304 | constexpr auto 305 | operator()(carray<2 * Stages> const& z, carray<2 * Stages> const& p, 306 | value_type gain, bool distribute_gain) const noexcept 307 | -> sos_array { 308 | return step0(next_pole_index(p), z, p, check_gain(gain), distribute_gain); 309 | } 310 | 311 | private: 312 | static constexpr value_type check_gain(value_type gain) noexcept { 313 | // Protect against zero gain by forcing division-by-zero. 314 | // This can happen with buggy gcem when trying to take the log() of a 315 | // very small number. We can't use static_assert() here as the gain 316 | // *could* be passed in as a run-time argument. If this is evaluated 317 | // at compile time, the compile will typically fail. 318 | return gain != value_type{0} ? gain : value_type{1} / gain; 319 | } 320 | 321 | constexpr auto 322 | step0(std::size_t p1, carray<2 * Stages> const& z, 323 | carray<2 * Stages> const& p, value_type gain, 324 | bool distribute_gain) const noexcept -> sos_array { 325 | return step1(p[p1], z, p.swappop(p1), gain, distribute_gain); 326 | } 327 | 328 | constexpr auto 329 | step1(cnum p1, carray<2 * Stages> const& z, carray<2 * Stages - 1> const& p, 330 | value_type gain, bool distribute_gain) const noexcept 331 | -> sos_array { 332 | return !p1.is_real() && z.count(is_real{}) == 1 && p.count(is_real{}) == 1 333 | ? step2a(nearest_complex_index(z, p1), p1, z, p, gain, 334 | distribute_gain) 335 | : step2b(p1, 336 | p1.is_real() ? next_real_pole_index(p) 337 | : conjugate_index(p, p1), 338 | z, p, gain, distribute_gain); 339 | } 340 | 341 | // We have a complex zero and a complex pole. We need to find and add 342 | // the complex conjugates. 343 | constexpr auto 344 | step2a(std::size_t z1, cnum p1, carray<2 * Stages> const& z, 345 | carray<2 * Stages - 1> const& p, value_type gain, 346 | bool distribute_gain) const noexcept -> sos_array { 347 | return step3a(z[z1], p1, z.swappop(z1), p, gain, distribute_gain); 348 | } 349 | 350 | constexpr auto 351 | step3a(cnum z1, cnum p1, carray<2 * Stages - 1> const& z, 352 | carray<2 * Stages - 1> const& p, value_type gain, 353 | bool distribute_gain) const noexcept -> sos_array { 354 | return step4a(z1, p1, conjugate_index(z, z1), conjugate_index(p, p1), z, p, 355 | gain, distribute_gain); 356 | } 357 | 358 | constexpr auto 359 | step4a(cnum z1, cnum p1, std::size_t z2, std::size_t p2, 360 | carray<2 * Stages - 1> const& z, carray<2 * Stages - 1> const& p, 361 | value_type gain, bool distribute_gain) const noexcept 362 | -> sos_array { 363 | return finish(z1, p1, z[z2], p[p2], z.swappop(z2), p.swappop(p2), gain, 364 | distribute_gain); 365 | } 366 | 367 | // We have two poles, either a conjugate pair or two real poles. We 368 | // need to find two zeros. 369 | constexpr auto 370 | step2b(cnum p1, std::size_t p2, carray<2 * Stages> const& z, 371 | carray<2 * Stages - 1> const& p, value_type gain, 372 | bool distribute_gain) const noexcept -> sos_array { 373 | return step3b(p1, p[p2], z, p.swappop(p2), gain, distribute_gain); 374 | } 375 | 376 | constexpr auto 377 | step3b(cnum p1, cnum p2, carray<2 * Stages> const& z, 378 | carray<2 * Stages - 2> const& p, value_type gain, 379 | bool distribute_gain) const noexcept -> sos_array { 380 | return step4b(p1, p2, nearest_index(z, p1), z, p, gain, distribute_gain); 381 | } 382 | 383 | constexpr auto 384 | step4b(cnum p1, cnum p2, std::size_t z1, carray<2 * Stages> const& z, 385 | carray<2 * Stages - 2> const& p, value_type gain, 386 | bool distribute_gain) const noexcept -> sos_array { 387 | return step5b(p1, p2, z[z1], z.swappop(z1), p, gain, distribute_gain); 388 | } 389 | 390 | constexpr auto 391 | step5b(cnum p1, cnum p2, cnum z1, carray<2 * Stages - 1> const& z, 392 | carray<2 * Stages - 2> const& p, value_type gain, 393 | bool distribute_gain) const noexcept -> sos_array { 394 | return step6b(p1, p2, z1, 395 | z1.is_real() ? nearest_real_index(z, p1) 396 | : conjugate_index(z, z1), 397 | z, p, gain, distribute_gain); 398 | } 399 | 400 | constexpr auto 401 | step6b(cnum p1, cnum p2, cnum z1, std::size_t z2, 402 | carray<2 * Stages - 1> const& z, carray<2 * Stages - 2> const& p, 403 | value_type gain, bool distribute_gain) const noexcept 404 | -> sos_array { 405 | return finish(z1, p1, z[z2], p2, z.swappop(z2), p, gain, distribute_gain); 406 | } 407 | 408 | constexpr auto 409 | finish(cnum z1, cnum p1, cnum z2, cnum p2, carray<2 * Stages - 2> const& z, 410 | carray<2 * Stages - 2> const& p, value_type gain, 411 | bool distribute_gain) const noexcept -> sos_array { 412 | return zpk_to_sos{}(z, p, gain, 413 | distribute_gain) 414 | .append(sos_array<1>{sos_section{ 415 | carray<2>{z1, z2}, carray<2>{p1, p2}, 416 | Stages == 1 || distribute_gain ? gain : value_type{1}}}); 417 | } 418 | 419 | template 420 | constexpr std::size_t next_pole_index(carray const& p) const noexcept { 421 | return p.argmin(unit_circle_distance_less{}); 422 | } 423 | 424 | template 425 | constexpr std::size_t 426 | next_real_pole_index(carray const& p) const noexcept { 427 | return p.argmin(unit_circle_distance_real_less{}); 428 | } 429 | 430 | template 431 | constexpr std::size_t 432 | nearest_index(carray const& a, 433 | cmath::complex const& z) const noexcept { 434 | return a.argmin(distance_less{z}); 435 | } 436 | 437 | template 438 | constexpr std::size_t 439 | nearest_real_index(carray const& a, 440 | cmath::complex const& z) const noexcept { 441 | return a.argmin(distance_real_less{z}); 442 | } 443 | 444 | template 445 | constexpr std::size_t 446 | nearest_complex_index(carray const& a, 447 | cmath::complex const& z) const noexcept { 448 | return a.argmin(distance_complex_less{z}); 449 | } 450 | 451 | template 452 | constexpr std::size_t 453 | conjugate_index(carray const& a, 454 | cmath::complex const& z) const noexcept { 455 | return nearest_index(a, z.conj()); 456 | } 457 | }; 458 | 459 | template 460 | class zpk_to_sos { 461 | public: 462 | using value_type = F; 463 | using output_type = FO; 464 | using carray = cmath::vector, 0>; 465 | using sos_array = cmath::vector, 0>; 466 | 467 | constexpr auto operator()(carray const&, carray const&, value_type, 468 | bool) const noexcept -> sos_array { 469 | return sos_array{}; 470 | } 471 | }; 472 | 473 | template 474 | struct filter_chain { 475 | F operator()(cmath::vector, N> const& sos, 476 | std::array, N>& state, F x) const { 477 | return filter_chain{}(sos, state, sos[I].filter(state[I], x)); 478 | } 479 | }; 480 | 481 | template 482 | struct filter_chain { 483 | F operator()(cmath::vector, N> const&, 484 | std::array, N>&, F x) const { 485 | return x; 486 | } 487 | }; 488 | 489 | template 490 | constexpr auto 491 | butterworth_poles() noexcept -> cmath::vector, Order> { 492 | return -cmath::exp(detail::theta()()); 493 | } 494 | 495 | } // namespace detail 496 | } // namespace signal 497 | } // namespace embedded 498 | 499 | // clang-format off 500 | #ifdef __IAR_SYSTEMS_ICC__ 501 | #pragma diag_default=Pe540 502 | #endif 503 | // clang-format on 504 | -------------------------------------------------------------------------------- /include/embedded/signal/filter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "detail/filter.h" 29 | #include "poly.h" 30 | #include "sos.h" 31 | 32 | // TODO: 33 | // - cleanup 34 | // - return more properties from filter spec (frequency, sample rate, ...) 35 | // - documentation :-) 36 | // - bandpass, bandstop 37 | // - elliptic and bessel filters 38 | // - filter combinations (likely by simply joining poles/zeros) 39 | // - SOS gain optimisation for fixed-point arithmetic 40 | 41 | // clang-format off 42 | #ifdef __IAR_SYSTEMS_ICC__ 43 | #pragma diag_suppress=Pe540 44 | #endif 45 | // clang-format on 46 | 47 | namespace embedded { 48 | namespace signal { 49 | 50 | template 51 | class iirfilter { 52 | public: 53 | using value_type = T; 54 | 55 | constexpr iirfilter(value_type fs) noexcept 56 | : fs_{fs} {} 57 | 58 | template 59 | class design { 60 | public: 61 | using zpk_value = detail::zpk_value; 62 | 63 | constexpr design(zpk_value const& zpk) noexcept 64 | : zpk_{zpk} {} 65 | 66 | template 67 | constexpr auto poly() const noexcept -> poly_design { 68 | return poly_design(zpk_); 69 | } 70 | 71 | template 72 | constexpr auto sos(sos_gain mode = sos_gain::first_section) const noexcept 73 | -> sos_design { 74 | return sos_design(zpk_, mode); 75 | } 76 | 77 | private: 78 | zpk_value const zpk_; 79 | }; 80 | 81 | template 82 | constexpr auto 83 | lowpass(C const& c, value_type f) const noexcept -> design { 84 | return design(bilinear_zpk( 85 | lowpass_zpk(c.template spec().zpk(), warp(f)))); 86 | } 87 | 88 | template 89 | constexpr auto 90 | highpass(C const& c, value_type f) const noexcept -> design { 91 | return design(bilinear_zpk( 92 | highpass_zpk(c.template spec().zpk(), warp(f)))); 93 | } 94 | 95 | private: 96 | constexpr value_type warp(value_type f) const noexcept { 97 | return detail::warp_frequency(2.0 * f / fs_, 2.0); 98 | } 99 | 100 | value_type fs_; 101 | }; 102 | 103 | } // namespace signal 104 | } // namespace embedded 105 | 106 | // clang-format off 107 | #ifdef __IAR_SYSTEMS_ICC__ 108 | #pragma diag_default=Pe540 109 | #endif 110 | // clang-format on 111 | -------------------------------------------------------------------------------- /include/embedded/signal/poly.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "detail/filter.h" 29 | 30 | // clang-format off 31 | #ifdef __IAR_SYSTEMS_ICC__ 32 | #pragma diag_suppress=Pe540 33 | #endif 34 | // clang-format on 35 | 36 | namespace embedded { 37 | namespace signal { 38 | 39 | template 40 | class poly_instance; 41 | 42 | template 43 | class poly_design { 44 | public: 45 | using value_type = F; 46 | using parray = cmath::vector; 47 | 48 | template 49 | constexpr poly_design(Z const& zpk) noexcept 50 | : b_{real(zpk.gain() * poly(zpk.zeros()))} 51 | , a_{real(poly(zpk.poles()))} {} 52 | 53 | static constexpr std::size_t order() noexcept { return N; } 54 | 55 | constexpr auto b() const noexcept -> parray { return b_; } 56 | constexpr auto a() const noexcept -> parray { return a_; } 57 | 58 | F filter(std::array& state, F x) const { 59 | F const y = b_[0] * x + state[0]; 60 | detail::poly_df2t_update{}(state, b_, a_, x, y); 61 | state[N - 1] = b_[N] * x - a_[N] * y; 62 | return y; 63 | } 64 | 65 | constexpr auto instance() const noexcept -> poly_instance { 66 | return poly_instance(this); 67 | } 68 | 69 | private: 70 | parray const b_; 71 | parray const a_; 72 | }; 73 | 74 | template 75 | class poly_instance { 76 | public: 77 | using value_type = F; 78 | 79 | poly_instance(poly_design const* i) noexcept 80 | : impl_{i} {} 81 | 82 | value_type operator()(value_type x) { return impl_->filter(y_, x); } 83 | 84 | private: 85 | poly_design const* impl_; 86 | std::array y_{}; 87 | }; 88 | 89 | } // namespace signal 90 | } // namespace embedded 91 | 92 | // clang-format off 93 | #ifdef __IAR_SYSTEMS_ICC__ 94 | #pragma diag_default=Pe540 95 | #endif 96 | // clang-format on 97 | -------------------------------------------------------------------------------- /include/embedded/signal/sos.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "detail/filter.h" 29 | 30 | // clang-format off 31 | #ifdef __IAR_SYSTEMS_ICC__ 32 | #pragma diag_suppress=Pe540 33 | #endif 34 | // clang-format on 35 | 36 | namespace embedded { 37 | namespace signal { 38 | 39 | enum class sos_gain { 40 | first_section, 41 | distribute, 42 | }; 43 | 44 | template 45 | struct sos_state { 46 | F y1{}, y2{}; 47 | }; 48 | 49 | template 50 | class sos_section { 51 | public: 52 | using value_type = F; 53 | using state_type = sos_state; 54 | 55 | template 56 | constexpr sos_section(cmath::vector, 2> const& zeros, 57 | cmath::vector, 2> const& poles, 58 | F2 gain) noexcept 59 | : sos_section(real(gain * poly(zeros)), real(poly(poles))) {} 60 | 61 | value_type filter(state_type& state, value_type x) const { 62 | value_type const y = b0_ * x + state.y1; 63 | state.y1 = b1_ * x - a1_ * y + state.y2; 64 | state.y2 = b2_ * x - a2_ * y; 65 | return y; 66 | } 67 | 68 | constexpr auto b() const noexcept -> cmath::vector { 69 | return cmath::vector{b0_, b1_, b2_}; 70 | } 71 | 72 | constexpr auto a() const noexcept -> cmath::vector { 73 | return cmath::vector{value_type{1}, a1_, a2_}; 74 | } 75 | 76 | private: 77 | template 78 | constexpr sos_section(cmath::vector const& b, 79 | cmath::vector const& a) noexcept 80 | : b0_{static_cast(b[0])} 81 | , b1_{static_cast(b[1])} 82 | , b2_{static_cast(b[2])} 83 | , a1_{static_cast(a[1])} 84 | , a2_{static_cast(a[2])} {} 85 | 86 | value_type const b0_, b1_, b2_; 87 | value_type const a1_, a2_; 88 | }; 89 | 90 | template 91 | class sos_instance; 92 | 93 | template 94 | class sos_design { 95 | public: 96 | static constexpr std::size_t sos_count{(N + 1) / 2}; 97 | using sos_array = cmath::vector, sos_count>; 98 | 99 | template 100 | static constexpr auto 101 | build_sos(detail::zpk_value<2 * sos_count, 2 * sos_count, F2> const& zpk, 102 | sos_gain mode) noexcept -> sos_array { 103 | return detail::zpk_to_sos{}( 104 | zpk.zeros(), zpk.poles(), 105 | mode == sos_gain::distribute ? cmath::pow(zpk.gain(), F2{1} / sos_count) 106 | : zpk.gain(), 107 | mode == sos_gain::distribute); 108 | } 109 | 110 | template 111 | constexpr sos_design(detail::zpk_value const& zpk, 112 | sos_gain mode) noexcept 113 | : sos_{build_sos(zpk.even(), mode)} {} 114 | 115 | static constexpr std::size_t order() noexcept { return N; } 116 | static constexpr std::size_t size() noexcept { return sos_count; } 117 | 118 | constexpr auto sos() const noexcept -> sos_array const& { return sos_; } 119 | 120 | constexpr auto instance() const noexcept -> sos_instance { 121 | return sos_instance{this}; 122 | } 123 | 124 | private: 125 | sos_array const sos_; 126 | }; 127 | 128 | template 129 | class sos_instance { 130 | public: 131 | static constexpr std::size_t sos_count{(N + 1) / 2}; 132 | using value_type = F; 133 | 134 | sos_instance(sos_design const* i) noexcept 135 | : impl_{i} {} 136 | 137 | value_type operator()(value_type x) { 138 | return detail::filter_chain{}(impl_->sos(), state_, 139 | x); 140 | } 141 | 142 | auto state() const -> std::array, sos_count> const& { 143 | return state_; 144 | } 145 | 146 | private: 147 | sos_design const* impl_; 148 | std::array, sos_count> state_{}; 149 | }; 150 | 151 | } // namespace signal 152 | } // namespace embedded 153 | 154 | // clang-format off 155 | #ifdef __IAR_SYSTEMS_ICC__ 156 | #pragma diag_default=Pe540 157 | #endif 158 | // clang-format on 159 | -------------------------------------------------------------------------------- /include/embedded/type_traits.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include "type_traits/conjunction.h" 26 | #include "type_traits/is_invocable.h" 27 | -------------------------------------------------------------------------------- /include/embedded/type_traits/conjunction.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | namespace embedded { 28 | 29 | template 30 | struct conjunction : std::true_type {}; 31 | 32 | template 33 | struct conjunction : T {}; 34 | 35 | template 36 | struct conjunction 37 | : std::conditional, T1>::type {}; 38 | 39 | } // namespace embedded 40 | -------------------------------------------------------------------------------- /include/embedded/type_traits/is_invocable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | namespace embedded { 29 | 30 | #ifdef __cpp_lib_is_invocable 31 | 32 | template 33 | using is_invocable_r = std::is_invocable_r; 34 | 35 | #else 36 | 37 | namespace detail { 38 | 39 | template 40 | void accept(R); 41 | 42 | template 43 | struct is_invocable_helper : std::false_type {}; 44 | 45 | template 46 | struct is_invocable_helper()(std::declval()...), 47 | void()), 48 | void, Fn, Args...> : std::true_type {}; 49 | 50 | template 51 | struct is_invocable_helper()(std::declval()...), 52 | void()), 53 | const void, Fn, Args...> : std::true_type {}; 54 | 55 | template 56 | struct is_invocable_helper( 57 | std::declval()(std::declval()...))), 58 | R, Fn, Args...> : std::true_type {}; 59 | 60 | } // namespace detail 61 | 62 | template 63 | using is_invocable_r = detail::is_invocable_helper; 64 | 65 | #endif 66 | 67 | } // namespace embedded 68 | -------------------------------------------------------------------------------- /include/embedded/typelist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Marcus Holland-Moritz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | namespace embedded { 26 | 27 | /** 28 | * Simple Type List 29 | * 30 | * This is currently mainly useful for building lists of types to pass 31 | * to (variadic) templates as parameters. 32 | */ 33 | template 34 | class typelist { 35 | public: 36 | template 37 | using append = typelist; 38 | 39 | template