/* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ #ifndef __DSP_DECIMATE_H__ #define __DSP_DECIMATE_H__ #include #include #include #include #include "dsp_types.hpp" #include "simd.hpp" #include "utility.hpp" namespace dsp { namespace decimate { /* "Saturating" scalars used by decimators to scale either * 8 or 16 bit complex values into 32 bit complex values. * Some of the decimators accept a scale factor as part of * configuration, which is then passed to scale_round_and_pack. */ // c8_to_c32_sat_scalar == 2^25. 2^25 * 2^7 (signed C8 Max) == 2^32. constexpr int32_t c8_to_c32_sat_scalar = 0x2000000; // c16_to_c32_sat_scalar == 2^17. 2^17 * 2^15 (signed C16 Max) == 2^32. constexpr int32_t c16_to_c32_sat_scalar = 0x20000; class Complex8DecimateBy2CIC3 { public: buffer_c16_t execute( const buffer_c8_t& src, const buffer_c16_t& dst); private: uint32_t _i1_i0{0}; uint32_t _q1_q0{0}; }; class TranslateByFSOver4AndDecimateBy2CIC3 { public: buffer_c16_t execute( const buffer_c8_t& src, const buffer_c16_t& dst); private: uint32_t _q1_i0{0}; uint32_t _q0_i1{0}; }; class DecimateBy2CIC3 { public: buffer_c16_t execute( const buffer_c16_t& src, const buffer_c16_t& dst); private: uint32_t _iq0{0}; uint32_t _iq1{0}; }; class FIR64AndDecimateBy2Real { public: static constexpr size_t taps_count = 64; void configure( const std::array& taps); buffer_s16_t execute( const buffer_s16_t& src, const buffer_s16_t& dst); private: std::array z{}; std::array taps{}; }; class FIRC8xR16x24FS4Decim4 { public: static constexpr size_t taps_count = 24; static constexpr size_t decimation_factor = 4; using sample_t = complex8_t; using tap_t = int16_t; enum class Shift : bool { Down = true, Up = false }; void configure( const std::array& taps, const int32_t scale = c8_to_c32_sat_scalar, const Shift shift = Shift::Down); buffer_c16_t execute( const buffer_c8_t& src, const buffer_c16_t& dst); private: std::array z_{}; std::array taps_{}; int32_t output_scale = 0; }; class FIRC8xR16x24FS4Decim8 { public: static constexpr size_t taps_count = 24; static constexpr size_t decimation_factor = 8; using sample_t = complex8_t; using tap_t = int16_t; enum class Shift : bool { Down = true, Up = false }; void configure( const std::array& taps, const int32_t scale = c8_to_c32_sat_scalar, const Shift shift = Shift::Down); buffer_c16_t execute( const buffer_c8_t& src, const buffer_c16_t& dst); private: std::array z_{}; std::array taps_{}; int32_t output_scale = 0; }; class FIRC16xR16x16Decim2 { public: static constexpr size_t taps_count = 16; static constexpr size_t decimation_factor = 2; using sample_t = complex16_t; using tap_t = int16_t; void configure( const std::array& taps, const int32_t scale = c16_to_c32_sat_scalar); buffer_c16_t execute( const buffer_c16_t& src, const buffer_c16_t& dst); private: std::array z_{}; std::array taps_{}; int32_t output_scale = 0; }; class FIRC16xR16x32Decim8 { public: static constexpr size_t taps_count = 32; static constexpr size_t decimation_factor = 8; using sample_t = complex16_t; using tap_t = int16_t; void configure( const std::array& taps, const int32_t scale = c16_to_c32_sat_scalar); buffer_c16_t execute( const buffer_c16_t& src, const buffer_c16_t& dst); private: std::array z_{}; std::array taps_{}; int32_t output_scale = 0; }; class FIRAndDecimateComplex { public: using sample_t = complex16_t; using tap_t = complex16_t; using taps_t = tap_t[]; /* NOTE! Current code makes an assumption that block of samples to be * processed will be a multiple of the taps_count. */ template void configure( const T& taps, const size_t decimation_factor) { configure(taps.data(), taps.size(), decimation_factor); } buffer_c16_t execute( const buffer_c16_t& src, const buffer_c16_t& dst); private: using samples_t = sample_t[]; std::unique_ptr samples_{}; std::unique_ptr taps_reversed_{}; size_t taps_count_{0}; size_t decimation_factor_{1}; template void configure( const T* const taps, const size_t taps_count, const size_t decimation_factor) { configure_common(taps_count, decimation_factor); std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]); } void configure_common( const size_t taps_count, const size_t decimation_factor); }; class DecimateBy2CIC4Real { public: buffer_s16_t execute( const buffer_s16_t& src, const buffer_s16_t& dst); private: int16_t z[5]{}; int16_t _dummy{}; // TODO: Addresses GCC bug when constructing a class that's not sizeof() % 4 == 0? }; } /* namespace decimate */ } /* namespace dsp */ #endif /*__DSP_DECIMATE_H__*/