2019-08-19 02:08:41 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
|
|
|
|
|
// //
|
|
|
|
|
// 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 as 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 General Public License V3 for more details. //
|
|
|
|
|
// //
|
|
|
|
|
// You should have received a copy of the GNU General Public License //
|
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
#include "dsp/fftengine.h"
|
|
|
|
|
#include "interferometercorr.h"
|
|
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
Sample sAdd(const Sample& a, const Sample& b) { //!< Sample addition
|
|
|
|
|
return Sample{a.real() + b.real(), a.imag() + b.imag()};
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
Sample sMulConj(const Sample& a, const Sample& b) { //!< Sample multiply with conjugate
|
2019-09-12 18:20:25 +02:00
|
|
|
Sample s;
|
|
|
|
|
s.setReal((a.real()*b.real() + a.imag()*b.imag()) / (1<<(SDR_RX_SAMP_SZ - 16 + 1)));
|
|
|
|
|
s.setImag((a.imag()*b.real() - a.real()*b.imag()) / (1<<(SDR_RX_SAMP_SZ - 16 + 1)));
|
|
|
|
|
return s;
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
Sample cf2s(const std::complex<float>& a) { //!< Complex float to Sample
|
|
|
|
|
Sample s;
|
|
|
|
|
s.setReal(a.real()*SDR_RX_SCALEF);
|
|
|
|
|
s.setImag(a.imag()*SDR_RX_SCALEF);
|
|
|
|
|
return s;
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-12 18:20:25 +02:00
|
|
|
Sample invfft2s(const std::complex<float>& a) { //!< Complex float to Sample
|
|
|
|
|
Sample s;
|
|
|
|
|
s.setReal(a.real());
|
|
|
|
|
s.setImag(a.imag());
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-19 02:08:41 +02:00
|
|
|
InterferometerCorrelator::InterferometerCorrelator(int fftSize) :
|
|
|
|
|
m_corrType(InterferometerSettings::CorrelationAdd),
|
|
|
|
|
m_fftSize(fftSize)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
|
|
|
{
|
|
|
|
|
m_fft[i] = FFTEngine::create();
|
|
|
|
|
m_fft[i]->configure(2*fftSize, false); // internally twice the data FFT size
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_invFFT = FFTEngine::create();
|
|
|
|
|
m_invFFT->configure(2*fftSize, true);
|
|
|
|
|
|
|
|
|
|
m_dataj = new std::complex<float>[2*fftSize]; // receives actual FFT result hence twice the data FFT size
|
2019-09-02 00:41:41 +02:00
|
|
|
m_scorr.resize(2*fftSize);
|
|
|
|
|
m_tcorr.resize(2*fftSize);
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InterferometerCorrelator::~InterferometerCorrelator()
|
|
|
|
|
{
|
|
|
|
|
delete[] m_dataj;
|
2019-09-12 18:20:25 +02:00
|
|
|
delete m_invFFT;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
|
delete m_fft[i];
|
|
|
|
|
}
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-12 18:20:25 +02:00
|
|
|
bool InterferometerCorrelator::performCorr(
|
|
|
|
|
const SampleVector& data0,
|
|
|
|
|
int size0,
|
|
|
|
|
const SampleVector& data1,
|
|
|
|
|
int size1
|
|
|
|
|
)
|
2019-08-19 02:08:41 +02:00
|
|
|
{
|
2019-09-12 18:20:25 +02:00
|
|
|
bool results = false;
|
|
|
|
|
|
2019-08-19 02:08:41 +02:00
|
|
|
switch (m_corrType)
|
|
|
|
|
{
|
|
|
|
|
case InterferometerSettings::CorrelationAdd:
|
2019-09-12 18:20:25 +02:00
|
|
|
results = performOpCorr(data0, size0, data1, size1, sAdd);
|
2019-08-19 02:08:41 +02:00
|
|
|
break;
|
|
|
|
|
case InterferometerSettings::CorrelationMultiply:
|
2019-09-12 18:20:25 +02:00
|
|
|
results = performOpCorr(data0, size0, data1, size1, sMulConj);
|
2019-08-19 02:08:41 +02:00
|
|
|
break;
|
2019-09-12 18:20:25 +02:00
|
|
|
case InterferometerSettings::CorrelationFFT:
|
|
|
|
|
results = performFFTCorr(data0, size0, data1, size1);
|
2019-08-19 02:08:41 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-09-12 18:20:25 +02:00
|
|
|
|
|
|
|
|
return results;
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-12 18:20:25 +02:00
|
|
|
bool InterferometerCorrelator::performOpCorr(
|
|
|
|
|
const SampleVector& data0,
|
|
|
|
|
int size0,
|
|
|
|
|
const SampleVector& data1,
|
|
|
|
|
int size1,
|
|
|
|
|
Sample sampleOp(const Sample& a, const Sample& b)
|
|
|
|
|
)
|
2019-08-19 02:08:41 +02:00
|
|
|
{
|
2019-09-12 18:20:25 +02:00
|
|
|
unsigned int size = std::min(size0, size1);
|
2019-09-24 02:46:14 +02:00
|
|
|
// if (size0 != size1) {
|
|
|
|
|
// qDebug("InterferometerCorrelator::performOpCorr: size0: %d, size1: %d", size0, size1);
|
|
|
|
|
// }
|
2019-09-02 00:41:41 +02:00
|
|
|
adjustTCorrSize(size);
|
|
|
|
|
|
|
|
|
|
std::transform(
|
|
|
|
|
data0.begin(),
|
|
|
|
|
data0.begin() + size,
|
|
|
|
|
data1.begin(),
|
|
|
|
|
m_tcorr.begin(),
|
|
|
|
|
sampleOp
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
m_processed = size;
|
2019-09-12 18:20:25 +02:00
|
|
|
m_remaining[0] = size0 - size;
|
|
|
|
|
m_remaining[1] = size1 - size;
|
|
|
|
|
return true;
|
2019-09-02 00:41:41 +02:00
|
|
|
}
|
2019-08-19 02:08:41 +02:00
|
|
|
|
2019-09-12 18:20:25 +02:00
|
|
|
bool InterferometerCorrelator::performFFTCorr(
|
|
|
|
|
const SampleVector& data0,
|
|
|
|
|
int size0,
|
|
|
|
|
const SampleVector& data1,
|
|
|
|
|
int size1
|
|
|
|
|
)
|
2019-09-02 00:41:41 +02:00
|
|
|
{
|
2019-09-12 18:20:25 +02:00
|
|
|
unsigned int size = std::min(size0, size1);
|
|
|
|
|
int nfft = 0;
|
2019-09-02 00:41:41 +02:00
|
|
|
SampleVector::const_iterator begin0 = data0.begin();
|
|
|
|
|
SampleVector::const_iterator begin1 = data1.begin();
|
|
|
|
|
adjustSCorrSize(size);
|
|
|
|
|
adjustTCorrSize(size);
|
2019-08-19 02:08:41 +02:00
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
while (size > m_fftSize)
|
2019-08-19 02:08:41 +02:00
|
|
|
{
|
2019-09-02 00:41:41 +02:00
|
|
|
// FFT[0]
|
2019-08-19 02:08:41 +02:00
|
|
|
std::transform(
|
2019-09-02 00:41:41 +02:00
|
|
|
begin0,
|
|
|
|
|
begin0 + m_fftSize,
|
|
|
|
|
m_fft[0]->in(),
|
|
|
|
|
[](const Sample& s) -> std::complex<float> {
|
|
|
|
|
return std::complex<float>{s.real() / SDR_RX_SCALEF, s.imag() / SDR_RX_SCALEF};
|
|
|
|
|
}
|
2019-08-19 02:08:41 +02:00
|
|
|
);
|
2019-09-02 00:41:41 +02:00
|
|
|
std::fill(m_fft[0]->in() + m_fftSize, m_fft[0]->in() + 2*m_fftSize, std::complex<float>{0, 0});
|
|
|
|
|
m_fft[0]->transform();
|
2019-08-19 02:08:41 +02:00
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
// FFT[1]
|
|
|
|
|
std::transform(
|
|
|
|
|
begin1,
|
|
|
|
|
begin1 + m_fftSize,
|
|
|
|
|
m_fft[1]->in(),
|
|
|
|
|
[](const Sample& s) -> std::complex<float> {
|
|
|
|
|
return std::complex<float>{s.real() / SDR_RX_SCALEF, s.imag() / SDR_RX_SCALEF};
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
std::fill(m_fft[1]->in() + m_fftSize, m_fft[1]->in() + 2*m_fftSize, std::complex<float>{0, 0});
|
|
|
|
|
m_fft[1]->transform();
|
2019-08-19 02:08:41 +02:00
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
// conjugate FFT[1]
|
|
|
|
|
std::transform(
|
|
|
|
|
m_fft[1]->out(),
|
|
|
|
|
m_fft[1]->out()+2*m_fftSize,
|
|
|
|
|
m_dataj,
|
|
|
|
|
[](const std::complex<float>& c) -> std::complex<float> {
|
|
|
|
|
return std::conj(c);
|
|
|
|
|
}
|
|
|
|
|
);
|
2019-08-19 02:08:41 +02:00
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
// product of FFT[1]* with FFT[0] and store in inverse FFT input
|
|
|
|
|
std::transform(
|
|
|
|
|
m_fft[0]->out(),
|
|
|
|
|
m_fft[0]->out()+2*m_fftSize,
|
|
|
|
|
m_dataj,
|
|
|
|
|
m_invFFT->in(),
|
|
|
|
|
[](std::complex<float>& a, const std::complex<float>& b) -> std::complex<float> {
|
|
|
|
|
return a*b;
|
|
|
|
|
}
|
|
|
|
|
);
|
2019-08-19 02:08:41 +02:00
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
// copy product to correlation spectrum
|
|
|
|
|
std::transform(
|
|
|
|
|
m_invFFT->in(),
|
|
|
|
|
m_invFFT->in() + 2*m_fftSize,
|
|
|
|
|
m_scorr.begin(),
|
|
|
|
|
cf2s
|
|
|
|
|
);
|
2019-08-19 02:08:41 +02:00
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
// do the inverse FFT to get time correlation
|
|
|
|
|
m_invFFT->transform();
|
|
|
|
|
std::transform(
|
|
|
|
|
m_invFFT->out(),
|
|
|
|
|
m_invFFT->out() + 2*m_fftSize,
|
|
|
|
|
m_tcorr.begin(),
|
2019-09-12 18:20:25 +02:00
|
|
|
invfft2s
|
2019-09-02 00:41:41 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
size -= m_fftSize;
|
|
|
|
|
begin0 += m_fftSize;
|
|
|
|
|
begin1 += m_fftSize;
|
2019-09-12 18:20:25 +02:00
|
|
|
nfft++;
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
2019-09-02 00:41:41 +02:00
|
|
|
|
|
|
|
|
// update the samples counters
|
2019-09-12 18:20:25 +02:00
|
|
|
m_processed = nfft*m_fftSize;
|
|
|
|
|
m_remaining[0] = size0 - nfft*m_fftSize;
|
|
|
|
|
m_remaining[1] = size1 - nfft*m_fftSize;
|
|
|
|
|
|
|
|
|
|
return nfft > 0;
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
void InterferometerCorrelator::adjustSCorrSize(int size)
|
2019-08-19 02:08:41 +02:00
|
|
|
{
|
2019-09-02 00:41:41 +02:00
|
|
|
if (size > m_scorrSize)
|
2019-08-19 02:08:41 +02:00
|
|
|
{
|
2019-09-02 00:41:41 +02:00
|
|
|
m_scorr.resize(size);
|
|
|
|
|
m_scorrSize = size;
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
2019-09-02 00:41:41 +02:00
|
|
|
}
|
2019-08-19 02:08:41 +02:00
|
|
|
|
2019-09-02 00:41:41 +02:00
|
|
|
void InterferometerCorrelator::adjustTCorrSize(int size)
|
|
|
|
|
{
|
|
|
|
|
if (size > m_tcorrSize)
|
|
|
|
|
{
|
|
|
|
|
m_tcorr.resize(size);
|
|
|
|
|
m_tcorrSize = size;
|
2019-08-19 02:08:41 +02:00
|
|
|
}
|
2019-09-02 00:41:41 +02:00
|
|
|
}
|