From 85df6218de80efe89af087060b7b0c8b51ae603c Mon Sep 17 00:00:00 2001
From: f4exb <f4exb06@gmail.com>
Date: Tue, 22 May 2018 13:54:01 +0200
Subject: [PATCH] Channel analyzer NG and Projector: PSK symbol mapping
 projection

---
 sdrbase/dsp/projector.cpp   | 113 ++++++++++++++++++++++++++++++++++++
 sdrbase/dsp/projector.h     |   5 ++
 sdrgui/gui/glscopenggui.cpp |   4 ++
 3 files changed, 122 insertions(+)

diff --git a/sdrbase/dsp/projector.cpp b/sdrbase/dsp/projector.cpp
index a678409f7..1d9a66c86 100644
--- a/sdrbase/dsp/projector.cpp
+++ b/sdrbase/dsp/projector.cpp
@@ -15,6 +15,7 @@
 // along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 ///////////////////////////////////////////////////////////////////////////////////
 
+#include <math.h>
 #include "projector.h"
 
 Projector::Projector(ProjectionType projectionType) :
@@ -77,6 +78,106 @@ Real Projector::run(const Sample& s)
             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;
@@ -91,3 +192,15 @@ Real Projector::run(const Sample& s)
     }
 }
 
+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;
+}
+
+
diff --git a/sdrbase/dsp/projector.h b/sdrbase/dsp/projector.h
index b0cdf88af..094c8c592 100644
--- a/sdrbase/dsp/projector.h
+++ b/sdrbase/dsp/projector.h
@@ -28,6 +28,10 @@ public:
         ProjectionMagDB,    //!< Calculate logarithmic (dB) of squared magnitude
         ProjectionPhase,    //!< Calculate phase
         ProjectionDPhase,   //!< Calculate phase derivative i.e. instantaneous frequency scaled to sample rate
+        ProjectionBPSK,     //!< Phase comparator BPSK evaluation
+        ProjectionQPSK,     //!< Phase comparator QPSK evaluation
+        Projection8PSK,     //!< Phase comparator 8-PSK evaluation
+        Projection16PSK,    //!< Phase comparator 16-PSK evaluation
 		nbProjectionTypes   //!< Gives the number of projections in the enum
     };
 
@@ -42,6 +46,7 @@ public:
     Real run(const Sample& s);
 
 private:
+    static Real normalizeAngle(Real angle);
     ProjectionType m_projectionType;
     Real m_prevArg;
     Real *m_cache;
diff --git a/sdrgui/gui/glscopenggui.cpp b/sdrgui/gui/glscopenggui.cpp
index 55da3719b..32087f0dc 100644
--- a/sdrgui/gui/glscopenggui.cpp
+++ b/sdrgui/gui/glscopenggui.cpp
@@ -1187,6 +1187,10 @@ void GLScopeNGGUI::fillProjectionCombo(QComboBox* comboBox)
     comboBox->addItem("MagdB", Projector::ProjectionMagDB);
     comboBox->addItem("Phi", Projector::ProjectionPhase);
     comboBox->addItem("dPhi", Projector::ProjectionDPhase);
+    comboBox->addItem("BPSK", Projector::ProjectionBPSK);
+    comboBox->addItem("QPSK", Projector::ProjectionQPSK);
+    comboBox->addItem("8PSK", Projector::Projection8PSK);
+    comboBox->addItem("16PSK", Projector::Projection16PSK);
 }
 
 void GLScopeNGGUI::disableLiveMode(bool disable)