117 lines
4.4 KiB
C
Raw Normal View History

2021-03-20 21:53:44 +01:00
#pragma once
#include <dsp/block.h>
#include <dsp/interpolation_taps.h>
2021-03-29 21:53:43 +02:00
#include <math.h>
#include <dsp/utils/macros.h>
2021-03-20 21:53:44 +01:00
namespace dsp {
2021-03-29 21:53:43 +02:00
template <int ORDER>
class CostasLoop: public generic_block<CostasLoop<ORDER>> {
2021-03-20 21:53:44 +01:00
public:
2021-03-29 21:53:43 +02:00
CostasLoop() {}
CostasLoop(stream<complex_t>* in, float loopBandwidth) { init(in, loopBandwidth); }
2021-03-20 21:53:44 +01:00
2021-03-29 21:53:43 +02:00
void init(stream<complex_t>* in, float loopBandwidth) {
2021-03-20 21:53:44 +01:00
_in = in;
2021-03-29 21:53:43 +02:00
lastVCO.re = 1.0f;
lastVCO.im = 0.0f;
_loopBandwidth = loopBandwidth;
2021-03-20 21:53:44 +01:00
2021-03-29 21:53:43 +02:00
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;
2021-03-20 21:53:44 +01:00
2021-03-29 21:53:43 +02:00
generic_block<CostasLoop<ORDER>>::registerInput(_in);
generic_block<CostasLoop<ORDER>>::registerOutput(&out);
2021-03-20 21:53:44 +01:00
}
2021-03-29 21:53:43 +02:00
void setInput(stream<complex_t>* in) {
generic_block<CostasLoop<ORDER>>::tempStop();
generic_block<CostasLoop<ORDER>>::unregisterInput(_in);
2021-03-20 21:53:44 +01:00
_in = in;
2021-03-29 21:53:43 +02:00
generic_block<CostasLoop<ORDER>>::registerInput(_in);
generic_block<CostasLoop<ORDER>>::tempStart();
}
2021-03-20 21:53:44 +01:00
2021-03-29 21:53:43 +02:00
void setLoopBandwidth(float loopBandwidth) {
generic_block<CostasLoop<ORDER>>::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<CostasLoop<ORDER>>::tempStart();
2021-03-20 21:53:44 +01:00
}
int run() {
2021-03-29 21:53:43 +02:00
int count = _in->read();
2021-03-20 21:53:44 +01:00
if (count < 0) { return -1; }
2021-03-29 21:53:43 +02:00
complex_t outVal;
float error;
2021-03-20 21:53:44 +01:00
2021-03-29 21:53:43 +02:00
for (int i = 0; i < count; i++) {
2021-03-20 21:53:44 +01:00
2021-03-29 21:53:43 +02:00
// Mix the VFO with the input to create the output value
outVal = lastVCO * _in->readBuf[i];
out.writeBuf[i] = outVal;
2021-03-20 21:53:44 +01:00
2021-03-29 21:53:43 +02:00
// Calculate the phase error estimation
if constexpr (ORDER == 2) {
error = outVal.re * outVal.im;
}
if constexpr (ORDER == 4) {
error = (DSP_STEP(outVal.re) * outVal.im) - (DSP_STEP(outVal.im) * outVal.re);
}
if constexpr (ORDER == 8) {
// This is taken from GR, I have no idea how it works but it does...
const float K = (sqrtf(2.0) - 1);
if (fabsf(outVal.re) >= fabsf(outVal.im)) {
error = ((outVal.re > 0.0f ? 1.0f : -1.0f) * outVal.im -
(outVal.im > 0.0f ? 1.0f : -1.0f) * outVal.re * K);
} else {
error = ((outVal.re > 0.0f ? 1.0f : -1.0f) * outVal.im * K -
(outVal.im > 0.0f ? 1.0f : -1.0f) * outVal.re);
}
}
if (error > 1.0f) { error = 1.0f; }
if (error < -1.0f) { error = -1.0f; }
// Integrate frequency and clamp it
vcoFrequency += _beta * error;
if (vcoFrequency > 1.0f) { vcoFrequency = 1.0f; }
if (vcoFrequency < -1.0f) { vcoFrequency = -1.0f; }
// 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);
2021-03-20 21:53:44 +01:00
}
_in->flush();
2021-03-29 21:53:43 +02:00
if (!out.swap(count)) { return -1; }
2021-03-20 21:53:44 +01:00
return count;
}
2021-03-29 21:53:43 +02:00
stream<complex_t> out;
2021-03-20 21:53:44 +01:00
private:
2021-03-29 21:53:43 +02:00
float _loopBandwidth = 1.0f;
2021-03-20 21:53:44 +01:00
2021-03-29 21:53:43 +02:00
float _alpha; // Integral coefficient
float _beta; // Proportional coefficient
float vcoFrequency = 0.0f;
float vcoPhase = 0.0f;
complex_t lastVCO;
2021-03-20 21:53:44 +01:00
2021-03-29 21:53:43 +02:00
stream<complex_t>* _in;
2021-03-20 21:53:44 +01:00
};
}