1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-25 09:18:54 -05:00
sdrangel/sdrbase/dsp/projector.cpp
2024-07-10 23:06:38 +02:00

417 lines
13 KiB
C++

///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018-2019, 2021 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
// //
// 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 <cmath>
#include "projector.h"
Projector::Projector(ProjectionType projectionType) :
m_projectionType(projectionType),
m_prevArg(0.0f),
m_cache(nullptr),
m_cacheMaster(true)
{
}
Projector::~Projector()
{
}
Real Projector::run(const Sample& s)
{
Real v;
if ((m_cache) && !m_cacheMaster) {
return m_cache[(int) m_projectionType];
}
else
{
switch (m_projectionType)
{
case ProjectionImag:
v = s.m_imag / SDR_RX_SCALEF;
break;
case ProjectionMagLin:
{
Real re = s.m_real / SDR_RX_SCALEF;
Real im = s.m_imag / SDR_RX_SCALEF;
Real magsq = re*re + im*im;
v = std::sqrt(magsq);
}
break;
case ProjectionMagSq:
{
Real re = s.m_real / SDR_RX_SCALEF;
Real im = s.m_imag / SDR_RX_SCALEF;
v = re*re + im*im;
}
break;
case ProjectionDMagSq:
{
Real re = s.m_real / SDR_RX_SCALEF;
Real im = s.m_imag / SDR_RX_SCALEF;
Real curMagSq = re*re + im*im;
v = curMagSq - m_prevVal;
m_prevVal = curMagSq;
}
break;
case ProjectionMagDB:
{
Real re = s.m_real / SDR_RX_SCALEF;
Real im = s.m_imag / SDR_RX_SCALEF;
Real magsq = re*re + im*im;
v = log10f(magsq) * 10.0f;
}
break;
case ProjectionPhase:
v = std::atan2((float) s.m_imag, (float) s.m_real) / M_PI; // normalize
break;
case ProjectionDOAP:
{
// calculate phase. Assume phase difference between two sources at half wavelength distance with sources axis as reference (positive side)
// cos(theta) = phi / 2*pi*k
Real p = std::atan2((float) s.m_imag, (float) s.m_real); // do not normalize phi (phi in -pi..+pi)
v = acos(p/M_PI) / M_PI; // normalize theta
}
break;
case ProjectionDOAN:
{
// calculate phase. Assume phase difference between two sources at half wavelength distance with sources axis as reference (negative source)
Real p = std::atan2((float) s.m_imag, (float) s.m_real); // do not normalize phi (phi in -pi..+pi)
v = -acos(p/M_PI) / M_PI; // normalize theta
}
break;
case ProjectionDPhase:
{
Real curArg = std::atan2((float) s.m_imag, (float) s.m_real);
Real dPhi = (curArg - m_prevArg) / M_PI;
m_prevArg = curArg;
if (dPhi < -1.0f) {
dPhi += 2.0f;
} else if (dPhi > 1.0f) {
dPhi -= 2.0f;
}
v = dPhi;
}
break;
case ProjectionBPSK:
{
Real arg = std::atan2((float) s.m_imag, (float) s.m_real);
v = normalizeAngle(2*arg) / (2.0*M_PI); // generic estimation around 0
// mapping on 2 symbols
if (arg < -M_PI/2) {
v -= 1.0/2;
} else if (arg < M_PI/2) {
v += 1.0/2;
} else if (arg < M_PI) {
v -= 1.0/2;
}
}
break;
case ProjectionQPSK:
{
Real arg = std::atan2((float) s.m_imag, (float) s.m_real);
v = normalizeAngle(4*arg) / (4.0*M_PI); // generic estimation around 0
// mapping on 4 symbols
if (arg < -3*M_PI/4) {
v -= 3.0/4;
} else if (arg < -M_PI/4) {
v -= 1.0/4;
} else if (arg < M_PI/4) {
v += 1.0/4;
} else if (arg < 3*M_PI/4) {
v += 3.0/4;
} else if (arg < M_PI) {
v -= 3.0/4;
}
}
break;
case Projection8PSK:
{
Real arg = std::atan2((float) s.m_imag, (float) s.m_real);
v = normalizeAngle(8*arg) / (8.0*M_PI); // generic estimation around 0
// mapping on 8 symbols
if (arg < -7*M_PI/8) {
v -= 7.0/8;
} else if (arg < -5*M_PI/8) {
v -= 5.0/8;
} else if (arg < -3*M_PI/8) {
v -= 3.0/8;
} else if (arg < -M_PI/8) {
v -= 1.0/8;
} else if (arg < M_PI/8) {
v += 1.0/8;
} else if (arg < 3*M_PI/8) {
v += 3.0/8;
} else if (arg < 5*M_PI/8) {
v += 5.0/8;
} else if (arg < 7*M_PI/8) {
v += 7.0/8;
} else if (arg < M_PI) {
v -= 7.0/8;
}
}
break;
case Projection16PSK:
{
Real arg = std::atan2((float) s.m_imag, (float) s.m_real);
v = normalizeAngle(16*arg) / (16.0*M_PI); // generic estimation around 0
// mapping on 16 symbols
if (arg < -15*M_PI/16) {
v -= 15.0/16;
} else if (arg < -13*M_PI/16) {
v -= 13.0/6;
} else if (arg < -11*M_PI/16) {
v -= 11.0/16;
} else if (arg < -9*M_PI/16) {
v -= 9.0/16;
} else if (arg < -7*M_PI/16) {
v -= 7.0/16;
} else if (arg < -5*M_PI/16) {
v -= 5.0/16;
} else if (arg < -3*M_PI/16) {
v -= 3.0/16;
} else if (arg < -M_PI/16) {
v -= 1.0/16;
} else if (arg < M_PI/16) {
v += 1.0/16;
} else if (arg < 3.0*M_PI/16) {
v += 3.0/16;
} else if (arg < 5.0*M_PI/16) {
v += 5.0/16;
} else if (arg < 7.0*M_PI/16) {
v += 7.0/16;
} else if (arg < 9.0*M_PI/16) {
v += 9.0/16;
} else if (arg < 11.0*M_PI/16) {
v += 11.0/16;
} else if (arg < 13.0*M_PI/16) {
v += 13.0/16;
} else if (arg < 15.0*M_PI/16) {
v += 15.0/16;
} else if (arg < M_PI) {
v -= 15.0/16;
}
}
break;
case ProjectionReal:
default:
v = s.m_real / SDR_RX_SCALEF;
break;
}
if (m_cache) {
m_cache[(int) m_projectionType] = v;
}
return v;
}
}
Real Projector::run(const std::complex<float>& s)
{
Real v;
if ((m_cache) && !m_cacheMaster) {
return m_cache[(int) m_projectionType];
}
else
{
switch (m_projectionType)
{
case ProjectionImag:
v = s.imag();
break;
case ProjectionMagLin:
v = std::abs(s);
break;
case ProjectionMagSq:
v = std::norm(s);
break;
case ProjectionDMagSq:
{
Real curMagSq = std::norm(s);
v = curMagSq - m_prevVal;
m_prevVal = curMagSq;
}
break;
case ProjectionMagDB:
{
Real magsq = std::norm(s);
v = log10f(magsq) * 10.0f;
}
break;
case ProjectionPhase:
v = std::arg(s) / M_PI; // normalize
break;
case ProjectionDOAP:
{
// calculate phase. Assume phase difference between two sources at half wavelength distance with sources axis as reference (positive side)
// cos(theta) = phi / 2*pi*k
Real p = std::arg(s); // do not normalize phi (phi in -pi..+pi)
v = acos(p/M_PI) / M_PI; // normalize theta
}
break;
case ProjectionDOAN:
{
// calculate phase. Assume phase difference between two sources at half wavelength distance with sources axis as reference (negative source)
Real p = std::arg(s); // do not normalize phi (phi in -pi..+pi)
v = -acos(p/M_PI) / M_PI; // normalize theta
}
break;
case ProjectionDPhase:
{
Real curArg = std::arg(s);
Real dPhi = (curArg - m_prevArg) / M_PI;
m_prevArg = curArg;
if (dPhi < -1.0f) {
dPhi += 2.0f;
} else if (dPhi > 1.0f) {
dPhi -= 2.0f;
}
v = dPhi;
}
break;
case ProjectionBPSK:
{
Real arg = std::arg(s);
v = normalizeAngle(2*arg) / (2.0*M_PI); // generic estimation around 0
// mapping on 2 symbols
if (arg < -M_PI/2) {
v -= 1.0/2;
} else if (arg < M_PI/2) {
v += 1.0/2;
} else if (arg < M_PI) {
v -= 1.0/2;
}
}
break;
case ProjectionQPSK:
{
Real arg = std::arg(s);
v = normalizeAngle(4*arg) / (4.0*M_PI); // generic estimation around 0
// mapping on 4 symbols
if (arg < -3*M_PI/4) {
v -= 3.0/4;
} else if (arg < -M_PI/4) {
v -= 1.0/4;
} else if (arg < M_PI/4) {
v += 1.0/4;
} else if (arg < 3*M_PI/4) {
v += 3.0/4;
} else if (arg < M_PI) {
v -= 3.0/4;
}
}
break;
case Projection8PSK:
{
Real arg = std::arg(s);
v = normalizeAngle(8*arg) / (8.0*M_PI); // generic estimation around 0
// mapping on 8 symbols
if (arg < -7*M_PI/8) {
v -= 7.0/8;
} else if (arg < -5*M_PI/8) {
v -= 5.0/8;
} else if (arg < -3*M_PI/8) {
v -= 3.0/8;
} else if (arg < -M_PI/8) {
v -= 1.0/8;
} else if (arg < M_PI/8) {
v += 1.0/8;
} else if (arg < 3*M_PI/8) {
v += 3.0/8;
} else if (arg < 5*M_PI/8) {
v += 5.0/8;
} else if (arg < 7*M_PI/8) {
v += 7.0/8;
} else if (arg < M_PI) {
v -= 7.0/8;
}
}
break;
case Projection16PSK:
{
Real arg = std::arg(s);
v = normalizeAngle(16*arg) / (16.0*M_PI); // generic estimation around 0
// mapping on 16 symbols
if (arg < -15*M_PI/16) {
v -= 15.0/16;
} else if (arg < -13*M_PI/16) {
v -= 13.0/6;
} else if (arg < -11*M_PI/16) {
v -= 11.0/16;
} else if (arg < -9*M_PI/16) {
v -= 9.0/16;
} else if (arg < -7*M_PI/16) {
v -= 7.0/16;
} else if (arg < -5*M_PI/16) {
v -= 5.0/16;
} else if (arg < -3*M_PI/16) {
v -= 3.0/16;
} else if (arg < -M_PI/16) {
v -= 1.0/16;
} else if (arg < M_PI/16) {
v += 1.0/16;
} else if (arg < 3.0*M_PI/16) {
v += 3.0/16;
} else if (arg < 5.0*M_PI/16) {
v += 5.0/16;
} else if (arg < 7.0*M_PI/16) {
v += 7.0/16;
} else if (arg < 9.0*M_PI/16) {
v += 9.0/16;
} else if (arg < 11.0*M_PI/16) {
v += 11.0/16;
} else if (arg < 13.0*M_PI/16) {
v += 13.0/16;
} else if (arg < 15.0*M_PI/16) {
v += 15.0/16;
} else if (arg < M_PI) {
v -= 15.0/16;
}
}
break;
case ProjectionReal:
default:
v = s.real();
break;
}
if (m_cache) {
m_cache[(int) m_projectionType] = v;
}
return v;
}
}
Real Projector::normalizeAngle(Real angle)
{
while (angle <= -M_PI) {
angle += 2.0*M_PI;
}
while (angle > M_PI) {
angle -= 2.0*M_PI;
}
return angle;
}