/* * Polyphase channelizer * * Copyright (C) 2012-2014 Tom Tsou * Copyright (C) 2015 Ettus Research LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include #include #include #include #include #include "Logger.h" #include "ChannelizerBase.h" extern "C" { #include "common/fft.h" } static float sinc(float x) { if (x == 0.0f) return 0.999999999999f; return sin(M_PI * x) / (M_PI * x); } /* * There are more efficient reversal algorithms, but we only reverse at * initialization so we don't care. */ static void reverse(float *buf, size_t len) { float tmp[2 * len]; memcpy(tmp, buf, 2 * len * sizeof(float)); for (size_t i = 0; i < len; i++) { buf[2 * i + 0] = tmp[2 * (len - 1 - i) + 0]; buf[2 * i + 1] = tmp[2 * (len - 1 - i) + 1]; } } /* * Create polyphase filterbank * * Implementation based material found in, * * "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ, * Prentice Hall, 2006." */ bool ChannelizerBase::initFilters() { size_t protoLen = m * hLen; float *proto; float sum = 0.0f, scale = 0.0f; float midpt = (float) (protoLen - 1.0) / 2.0; /* * Allocate 'M' partition filters and the temporary prototype * filter. Coefficients are real only and must be 16-byte memory * aligned for SSE usage. */ proto = new float[protoLen]; if (!proto) return false; subFilters = (float **) malloc(sizeof(float *) * m); if (!subFilters) { delete[] proto; return false; } for (size_t i = 0; i < m; i++) { subFilters[i] = (float *) memalign(16, hLen * 2 * sizeof(float)); } /* * Generate the prototype filter with a Blackman-harris window. * Scale coefficients with DC filter gain set to unity divided * by the number of channels. */ float a0 = 0.35875; float a1 = 0.48829; float a2 = 0.14128; float a3 = 0.01168; for (size_t i = 0; i < protoLen; i++) { proto[i] = sinc(((float) i - midpt) / (float) m); proto[i] *= a0 - a1 * cos(2 * M_PI * i / (protoLen - 1)) + a2 * cos(4 * M_PI * i / (protoLen - 1)) - a3 * cos(6 * M_PI * i / (protoLen - 1)); sum += proto[i]; } scale = (float) m / sum; /* * Populate partition filters and reverse the coefficients per * convolution requirements. */ for (size_t i = 0; i < hLen; i++) { for (size_t n = 0; n < m; n++) { subFilters[n][2 * i + 0] = proto[i * m + n] * scale; subFilters[n][2 * i + 1] = 0.0f; } } for (size_t i = 0; i < m; i++) reverse(subFilters[i], hLen); delete[] proto; return true; } bool ChannelizerBase::initFFT() { size_t size; if (fftInput || fftOutput || fftHandle) return false; size = blockLen * m * 2 * sizeof(float); fftInput = (float *) fft_malloc(size); memset(fftInput, 0, size); size = (blockLen + hLen) * m * 2 * sizeof(float); fftOutput = (float *) fft_malloc(size); memset(fftOutput, 0, size); if (!fftInput | !fftOutput) { LOG(ALERT) << "Memory allocation error"; return false; } fftHandle = init_fft(0, m, blockLen, blockLen + hLen, fftInput, fftOutput, hLen); return true; } bool ChannelizerBase::mapBuffers() { if (!fftHandle) { LOG(ALERT) << "FFT buffers not initialized"; return false; } hInputs = (float **) malloc(sizeof(float *) * m); hOutputs = (float **) malloc(sizeof(float *) * m); if (!hInputs | !hOutputs) return false; for (size_t i = 0; i < m; i++) { hInputs[i] = &fftOutput[2 * (i * (blockLen + hLen) + hLen)]; hOutputs[i] = &fftInput[2 * (i * blockLen)]; } return true; } /* * Setup filterbank internals */ bool ChannelizerBase::init() { /* * Filterbank coefficients, fft plan, history, and output sample * rate conversion blocks */ if (!initFilters()) { LOG(ALERT) << "Failed to initialize channelizing filter"; return false; } hist = (float **) malloc(sizeof(float *) * m); for (size_t i = 0; i < m; i++) { hist[i] = new float[2 * hLen]; memset(hist[i], 0, 2 * hLen * sizeof(float)); } if (!initFFT()) { LOG(ALERT) << "Failed to initialize FFT"; return false; } mapBuffers(); return true; } /* Check vector length validity */ bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen) { if (outerLen != innerLen * m) { LOG(ALERT) << "Invalid outer length " << innerLen << " is not multiple of " << blockLen; return false; } if (innerLen != blockLen) { LOG(ALERT) << "Invalid inner length " << outerLen << " does not equal " << blockLen; return false; } return true; } /* * Setup channelizer paramaters */ ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen) : fftInput(NULL), fftOutput(NULL), fftHandle(NULL) { this->m = m; this->hLen = hLen; this->blockLen = blockLen; } ChannelizerBase::~ChannelizerBase() { free_fft(fftHandle); for (size_t i = 0; i < m; i++) { free(subFilters[i]); delete hist[i]; } fft_free(fftInput); fft_free(fftOutput); free(hInputs); free(hOutputs); free(hist); }