#pragma once #include #include #include namespace dsp { class FMStereoDemuxPilotFilter : public generic_block { public: FMStereoDemuxPilotFilter() {} FMStereoDemuxPilotFilter(stream* in, dsp::filter_window::generic_complex_window* window) { init(in, window); } ~FMStereoDemuxPilotFilter() { if (!generic_block::_block_init) { return; } generic_block::stop(); volk_free(buffer); volk_free(taps); generic_block::_block_init = false; } void init(stream* in, dsp::filter_window::generic_complex_window* window) { _in = in; tapCount = window->getTapCount(); taps = (complex_t*)volk_malloc(tapCount * sizeof(complex_t), volk_get_alignment()); window->createTaps(taps, tapCount); buffer = (complex_t*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(complex_t) * 2, volk_get_alignment()); bufStart = &buffer[tapCount]; generic_block::registerInput(_in); generic_block::registerOutput(&dataOut); generic_block::registerOutput(&pilotOut); generic_block::_block_init = true; } void setInput(stream* in) { assert(generic_block::_block_init); std::lock_guard lck(generic_block::ctrlMtx); generic_block::tempStop(); generic_block::unregisterInput(_in); _in = in; generic_block::registerInput(_in); generic_block::tempStart(); } void updateWindow(dsp::filter_window::generic_complex_window* window) { assert(generic_block::_block_init); std::lock_guard lck(generic_block::ctrlMtx); _window = window; volk_free(taps); tapCount = window->getTapCount(); taps = (complex_t*)volk_malloc(tapCount * sizeof(complex_t), volk_get_alignment()); bufStart = &buffer[tapCount]; window->createTaps(taps, tapCount); } int run() { int count = _in->read(); if (count < 0) { return -1; } generic_block::ctrlMtx.lock(); memcpy(bufStart, _in->readBuf, count * sizeof(complex_t)); _in->flush(); for (int i = 0; i < count; i++) { volk_32fc_x2_dot_prod_32fc((lv_32fc_t*)&pilotOut.writeBuf[i], (lv_32fc_t*)&buffer[i+1], (lv_32fc_t*)taps, tapCount); } memcpy(dataOut.writeBuf, &buffer[tapCount - ((tapCount-1)/2)], count * sizeof(complex_t)); if (!dataOut.swap(count)) { return -1; } if (!pilotOut.swap(count)) { return -1; } memmove(buffer, &buffer[count], tapCount * sizeof(complex_t)); generic_block::ctrlMtx.unlock(); return count; } stream dataOut; stream pilotOut; private: stream* _in; dsp::filter_window::generic_complex_window* _window; complex_t* bufStart; complex_t* buffer; int tapCount; complex_t* taps; }; class FMStereoDemux: public generic_block { public: FMStereoDemux() {} FMStereoDemux(stream* data, stream* pilot, float loopBandwidth) { init(data, pilot, loopBandwidth); } void init(stream* data, stream* pilot, float loopBandwidth) { _data = data; _pilot = pilot; lastVCO.re = 1.0f; lastVCO.im = 0.0f; _loopBandwidth = loopBandwidth; float dampningFactor = sqrtf(2.0f) / 2.0f; float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth); _alpha = (4 * dampningFactor * _loopBandwidth) / denominator; _beta = (4 * _loopBandwidth * _loopBandwidth) / denominator; generic_block::registerInput(_data); generic_block::registerInput(_pilot); generic_block::registerOutput(&AplusBOut); generic_block::registerOutput(&AminusBOut); generic_block::_block_init = true; } void setInput(stream* data, stream* pilot) { assert(generic_block::_block_init); generic_block::tempStop(); generic_block::unregisterInput(_data); generic_block::unregisterInput(_pilot); _data = data; _pilot = pilot; generic_block::registerInput(_data); generic_block::registerInput(_pilot); generic_block::tempStart(); } void setLoopBandwidth(float loopBandwidth) { assert(generic_block::_block_init); generic_block::tempStop(); _loopBandwidth = loopBandwidth; float dampningFactor = sqrtf(2.0f) / 2.0f; float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth); _alpha = (4 * dampningFactor * _loopBandwidth) / denominator; _beta = (4 * _loopBandwidth * _loopBandwidth) / denominator; generic_block::tempStart(); } int run() { int count = _data->read(); if (count < 0) { return -1; } int pCount = _pilot->read(); if (pCount < 0) { return -1; } complex_t doubledVCO; float error; volk_32fc_deinterleave_real_32f(AplusBOut.writeBuf, (lv_32fc_t*)_data->readBuf, count); for (int i = 0; i < count; i++) { // Double the VCO, then mix it with the input data. doubledVCO = lastVCO*lastVCO; AminusBOut.writeBuf[i] = (_data->readBuf[i].re * doubledVCO.re) + (_data->readBuf[i].im * doubledVCO.im); // Calculate the phase error estimation error = _pilot->readBuf[i].phase() - vcoPhase; if (error > 3.1415926535f) { error -= 2.0f * 3.1415926535f; } else if (error <= -3.1415926535f) { error += 2.0f * 3.1415926535f; } // Integrate frequency and clamp it vcoFrequency += _beta * error; if (vcoFrequency > upperLimit) { vcoFrequency = upperLimit; } else if (vcoFrequency < lowerLimit) { vcoFrequency = lowerLimit; } // Calculate new phase and wrap it vcoPhase += vcoFrequency + (_alpha * error); while (vcoPhase > (2.0f * FL_M_PI)) { vcoPhase -= (2.0f * FL_M_PI); } while (vcoPhase < (-2.0f * FL_M_PI)) { vcoPhase += (2.0f * FL_M_PI); } // Calculate output lastVCO.re = cosf(vcoPhase); lastVCO.im = sinf(vcoPhase); } _data->flush(); _pilot->flush(); if (!AplusBOut.swap(count)) { return -1; } if (!AminusBOut.swap(count)) { return -1; } return count; } stream AplusBOut; stream AminusBOut; private: float _loopBandwidth = 0.01f; const float expectedFreq = ((19000.0f / 250000.0f) * 2.0f * FL_M_PI); const float upperLimit = ((19200.0f / 250000.0f) * 2.0f * FL_M_PI); const float lowerLimit = ((18800.0f / 250000.0f) * 2.0f * FL_M_PI); float _alpha; // Integral coefficient float _beta; // Proportional coefficient float vcoFrequency = expectedFreq; float vcoPhase = 0.0f; complex_t lastVCO; stream* _data; stream* _pilot; }; class FMStereoReconstruct : public generic_block { public: FMStereoReconstruct() {} FMStereoReconstruct(stream* a, stream* b) { init(a, b); } ~FMStereoReconstruct() { generic_block::stop(); delete[] leftBuf; delete[] rightBuf; } void init(stream* aplusb, stream* aminusb) { _aplusb = aplusb; _aminusb = aminusb; leftBuf = new float[STREAM_BUFFER_SIZE]; rightBuf = new float[STREAM_BUFFER_SIZE]; generic_block::registerInput(aplusb); generic_block::registerInput(aminusb); generic_block::registerOutput(&out); generic_block::_block_init = true; } void setInputs(stream* aplusb, stream* aminusb) { assert(generic_block::_block_init); std::lock_guard lck(generic_block::ctrlMtx); generic_block::tempStop(); generic_block::unregisterInput(_aplusb); generic_block::unregisterInput(_aminusb); _aplusb = aplusb; _aminusb = aminusb; generic_block::registerInput(_aplusb); generic_block::registerInput(_aminusb); generic_block::tempStart(); } int run() { int a_count = _aplusb->read(); if (a_count < 0) { return -1; } int b_count = _aminusb->read(); if (b_count < 0) { return -1; } if (a_count != b_count) { _aplusb->flush(); _aminusb->flush(); return 0; } volk_32f_x2_add_32f(leftBuf, _aplusb->readBuf, _aminusb->readBuf, a_count); volk_32f_x2_subtract_32f(rightBuf, _aplusb->readBuf, _aminusb->readBuf, a_count); _aplusb->flush(); _aminusb->flush(); volk_32f_x2_interleave_32fc((lv_32fc_t*)out.writeBuf, leftBuf, rightBuf, a_count); if (!out.swap(a_count)) { return -1; } return a_count; } stream out; private: stream* _aplusb; stream* _aminusb; float* leftBuf; float* rightBuf; }; }