mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-02-08 10:18:02 +08:00
ggwave : various improvements
- faster FFT implementation - built-in Direct Sequence Spread option - remove <map> dependency from implementation - update arduino-rx example
This commit is contained in:
779
src/fft.h
Normal file
779
src/fft.h
Normal file
@@ -0,0 +1,779 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
|
||||
The FFT routines below are taken from:
|
||||
|
||||
https://www.kurims.kyoto-u.ac.jp/~ooura/fft.html
|
||||
|
||||
License
|
||||
Copyright Takuya OOURA, 1996-2001
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Fast Fourier/Cosine/Sine Transform
|
||||
dimension :one
|
||||
data length :power of 2
|
||||
decimation :frequency
|
||||
radix :4, 2
|
||||
data :inplace
|
||||
table :use
|
||||
functions
|
||||
rdft: Real Discrete Fourier Transform
|
||||
function prototypes
|
||||
void rdft(int, int, float *, int *, float *);
|
||||
|
||||
|
||||
-------- Real DFT / Inverse of Real DFT --------
|
||||
[definition]
|
||||
<case1> RDFT
|
||||
R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2
|
||||
I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0<k<n/2
|
||||
<case2> IRDFT (excluding scale)
|
||||
a[k] = (R[0] + R[n/2]*cos(pi*k))/2 +
|
||||
sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) +
|
||||
sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k<n
|
||||
[usage]
|
||||
<case1>
|
||||
ip[0] = 0; // first time only
|
||||
rdft(n, 1, a, ip, w);
|
||||
<case2>
|
||||
ip[0] = 0; // first time only
|
||||
rdft(n, -1, a, ip, w);
|
||||
[parameters]
|
||||
n :data length (int)
|
||||
n >= 2, n = power of 2
|
||||
a[0...n-1] :input/output data (float *)
|
||||
<case1>
|
||||
output data
|
||||
a[2*k] = R[k], 0<=k<n/2
|
||||
a[2*k+1] = I[k], 0<k<n/2
|
||||
a[1] = R[n/2]
|
||||
<case2>
|
||||
input data
|
||||
a[2*j] = R[j], 0<=j<n/2
|
||||
a[2*j+1] = I[j], 0<j<n/2
|
||||
a[1] = R[n/2]
|
||||
ip[0...*] :work area for bit reversal (int *)
|
||||
length of ip >= 2+sqrt(n/2)
|
||||
strictly,
|
||||
length of ip >=
|
||||
2+(1<<(int)(log(n/2+0.5)/log(2))/2).
|
||||
ip[0],ip[1] are pointers of the cos/sin table.
|
||||
w[0...n/2-1] :cos/sin table (float *)
|
||||
w[],ip[] are initialized if ip[0] == 0.
|
||||
[remark]
|
||||
Inverse of
|
||||
rdft(n, 1, a, ip, w);
|
||||
is
|
||||
rdft(n, -1, a, ip, w);
|
||||
for (j = 0; j <= n - 1; j++) {
|
||||
a[j] *= 2.0 / n;
|
||||
}
|
||||
.
|
||||
|
||||
|
||||
Appendix :
|
||||
The cos/sin table is recalculated when the larger table required.
|
||||
w[] and ip[] are compatible with all routines.
|
||||
*/
|
||||
|
||||
void rdft(int n, int isgn, float *a, int *ip, float *w)
|
||||
{
|
||||
void makewt(int nw, int *ip, float *w);
|
||||
void makect(int nc, int *ip, float *c);
|
||||
void bitrv2(int n, int *ip, float *a);
|
||||
void cftfsub(int n, float *a, float *w);
|
||||
void cftbsub(int n, float *a, float *w);
|
||||
void rftfsub(int n, float *a, int nc, float *c);
|
||||
void rftbsub(int n, float *a, int nc, float *c);
|
||||
int nw, nc;
|
||||
float xi;
|
||||
|
||||
nw = ip[0];
|
||||
if (n > (nw << 2)) {
|
||||
nw = n >> 2;
|
||||
makewt(nw, ip, w);
|
||||
}
|
||||
nc = ip[1];
|
||||
if (n > (nc << 2)) {
|
||||
nc = n >> 2;
|
||||
makect(nc, ip, w + nw);
|
||||
}
|
||||
if (isgn >= 0) {
|
||||
if (n > 4) {
|
||||
bitrv2(n, ip + 2, a);
|
||||
cftfsub(n, a, w);
|
||||
rftfsub(n, a, nc, w + nw);
|
||||
} else if (n == 4) {
|
||||
cftfsub(n, a, w);
|
||||
}
|
||||
xi = a[0] - a[1];
|
||||
a[0] += a[1];
|
||||
a[1] = xi;
|
||||
} else {
|
||||
a[1] = 0.5 * (a[0] - a[1]);
|
||||
a[0] -= a[1];
|
||||
if (n > 4) {
|
||||
rftbsub(n, a, nc, w + nw);
|
||||
bitrv2(n, ip + 2, a);
|
||||
cftbsub(n, a, w);
|
||||
} else if (n == 4) {
|
||||
cftfsub(n, a, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------- initializing routines -------- */
|
||||
|
||||
#include <math.h>
|
||||
|
||||
void makewt(int nw, int *ip, float *w)
|
||||
{
|
||||
void bitrv2(int n, int *ip, float *a);
|
||||
int j, nwh;
|
||||
float delta, x, y;
|
||||
|
||||
ip[0] = nw;
|
||||
ip[1] = 1;
|
||||
if (nw > 2) {
|
||||
nwh = nw >> 1;
|
||||
delta = atan(1.0) / nwh;
|
||||
w[0] = 1;
|
||||
w[1] = 0;
|
||||
w[nwh] = cos(delta * nwh);
|
||||
w[nwh + 1] = w[nwh];
|
||||
if (nwh > 2) {
|
||||
for (j = 2; j < nwh; j += 2) {
|
||||
x = cos(delta * j);
|
||||
y = sin(delta * j);
|
||||
w[j] = x;
|
||||
w[j + 1] = y;
|
||||
w[nw - j] = y;
|
||||
w[nw - j + 1] = x;
|
||||
}
|
||||
bitrv2(nw, ip + 2, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void makect(int nc, int *ip, float *c)
|
||||
{
|
||||
int j, nch;
|
||||
float delta;
|
||||
|
||||
ip[1] = nc;
|
||||
if (nc > 1) {
|
||||
nch = nc >> 1;
|
||||
delta = atan(1.0) / nch;
|
||||
c[0] = cos(delta * nch);
|
||||
c[nch] = 0.5 * c[0];
|
||||
for (j = 1; j < nch; j++) {
|
||||
c[j] = 0.5 * cos(delta * j);
|
||||
c[nc - j] = 0.5 * sin(delta * j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -------- child routines -------- */
|
||||
|
||||
|
||||
void bitrv2(int n, int *ip, float *a)
|
||||
{
|
||||
int j, j1, k, k1, l, m, m2;
|
||||
float xr, xi, yr, yi;
|
||||
|
||||
ip[0] = 0;
|
||||
l = n;
|
||||
m = 1;
|
||||
while ((m << 3) < l) {
|
||||
l >>= 1;
|
||||
for (j = 0; j < m; j++) {
|
||||
ip[m + j] = ip[j] + l;
|
||||
}
|
||||
m <<= 1;
|
||||
}
|
||||
m2 = 2 * m;
|
||||
if ((m << 3) == l) {
|
||||
for (k = 0; k < m; k++) {
|
||||
for (j = 0; j < k; j++) {
|
||||
j1 = 2 * j + ip[k];
|
||||
k1 = 2 * k + ip[j];
|
||||
xr = a[j1];
|
||||
xi = a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
j1 += m2;
|
||||
k1 += 2 * m2;
|
||||
xr = a[j1];
|
||||
xi = a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
j1 += m2;
|
||||
k1 -= m2;
|
||||
xr = a[j1];
|
||||
xi = a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
j1 += m2;
|
||||
k1 += 2 * m2;
|
||||
xr = a[j1];
|
||||
xi = a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
}
|
||||
j1 = 2 * k + m2 + ip[k];
|
||||
k1 = j1 + m2;
|
||||
xr = a[j1];
|
||||
xi = a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
}
|
||||
} else {
|
||||
for (k = 1; k < m; k++) {
|
||||
for (j = 0; j < k; j++) {
|
||||
j1 = 2 * j + ip[k];
|
||||
k1 = 2 * k + ip[j];
|
||||
xr = a[j1];
|
||||
xi = a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
j1 += m2;
|
||||
k1 += m2;
|
||||
xr = a[j1];
|
||||
xi = a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bitrv2conj(int n, int *ip, float *a)
|
||||
{
|
||||
int j, j1, k, k1, l, m, m2;
|
||||
float xr, xi, yr, yi;
|
||||
|
||||
ip[0] = 0;
|
||||
l = n;
|
||||
m = 1;
|
||||
while ((m << 3) < l) {
|
||||
l >>= 1;
|
||||
for (j = 0; j < m; j++) {
|
||||
ip[m + j] = ip[j] + l;
|
||||
}
|
||||
m <<= 1;
|
||||
}
|
||||
m2 = 2 * m;
|
||||
if ((m << 3) == l) {
|
||||
for (k = 0; k < m; k++) {
|
||||
for (j = 0; j < k; j++) {
|
||||
j1 = 2 * j + ip[k];
|
||||
k1 = 2 * k + ip[j];
|
||||
xr = a[j1];
|
||||
xi = -a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = -a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
j1 += m2;
|
||||
k1 += 2 * m2;
|
||||
xr = a[j1];
|
||||
xi = -a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = -a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
j1 += m2;
|
||||
k1 -= m2;
|
||||
xr = a[j1];
|
||||
xi = -a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = -a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
j1 += m2;
|
||||
k1 += 2 * m2;
|
||||
xr = a[j1];
|
||||
xi = -a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = -a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
}
|
||||
k1 = 2 * k + ip[k];
|
||||
a[k1 + 1] = -a[k1 + 1];
|
||||
j1 = k1 + m2;
|
||||
k1 = j1 + m2;
|
||||
xr = a[j1];
|
||||
xi = -a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = -a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
k1 += m2;
|
||||
a[k1 + 1] = -a[k1 + 1];
|
||||
}
|
||||
} else {
|
||||
a[1] = -a[1];
|
||||
a[m2 + 1] = -a[m2 + 1];
|
||||
for (k = 1; k < m; k++) {
|
||||
for (j = 0; j < k; j++) {
|
||||
j1 = 2 * j + ip[k];
|
||||
k1 = 2 * k + ip[j];
|
||||
xr = a[j1];
|
||||
xi = -a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = -a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
j1 += m2;
|
||||
k1 += m2;
|
||||
xr = a[j1];
|
||||
xi = -a[j1 + 1];
|
||||
yr = a[k1];
|
||||
yi = -a[k1 + 1];
|
||||
a[j1] = yr;
|
||||
a[j1 + 1] = yi;
|
||||
a[k1] = xr;
|
||||
a[k1 + 1] = xi;
|
||||
}
|
||||
k1 = 2 * k + ip[k];
|
||||
a[k1 + 1] = -a[k1 + 1];
|
||||
a[k1 + m2 + 1] = -a[k1 + m2 + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cftfsub(int n, float *a, float *w)
|
||||
{
|
||||
void cft1st(int n, float *a, float *w);
|
||||
void cftmdl(int n, int l, float *a, float *w);
|
||||
int j, j1, j2, j3, l;
|
||||
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
|
||||
|
||||
l = 2;
|
||||
if (n > 8) {
|
||||
cft1st(n, a, w);
|
||||
l = 8;
|
||||
while ((l << 2) < n) {
|
||||
cftmdl(n, l, a, w);
|
||||
l <<= 2;
|
||||
}
|
||||
}
|
||||
if ((l << 2) == n) {
|
||||
for (j = 0; j < l; j += 2) {
|
||||
j1 = j + l;
|
||||
j2 = j1 + l;
|
||||
j3 = j2 + l;
|
||||
x0r = a[j] + a[j1];
|
||||
x0i = a[j + 1] + a[j1 + 1];
|
||||
x1r = a[j] - a[j1];
|
||||
x1i = a[j + 1] - a[j1 + 1];
|
||||
x2r = a[j2] + a[j3];
|
||||
x2i = a[j2 + 1] + a[j3 + 1];
|
||||
x3r = a[j2] - a[j3];
|
||||
x3i = a[j2 + 1] - a[j3 + 1];
|
||||
a[j] = x0r + x2r;
|
||||
a[j + 1] = x0i + x2i;
|
||||
a[j2] = x0r - x2r;
|
||||
a[j2 + 1] = x0i - x2i;
|
||||
a[j1] = x1r - x3i;
|
||||
a[j1 + 1] = x1i + x3r;
|
||||
a[j3] = x1r + x3i;
|
||||
a[j3 + 1] = x1i - x3r;
|
||||
}
|
||||
} else {
|
||||
for (j = 0; j < l; j += 2) {
|
||||
j1 = j + l;
|
||||
x0r = a[j] - a[j1];
|
||||
x0i = a[j + 1] - a[j1 + 1];
|
||||
a[j] += a[j1];
|
||||
a[j + 1] += a[j1 + 1];
|
||||
a[j1] = x0r;
|
||||
a[j1 + 1] = x0i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cftbsub(int n, float *a, float *w)
|
||||
{
|
||||
void cft1st(int n, float *a, float *w);
|
||||
void cftmdl(int n, int l, float *a, float *w);
|
||||
int j, j1, j2, j3, l;
|
||||
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
|
||||
|
||||
l = 2;
|
||||
if (n > 8) {
|
||||
cft1st(n, a, w);
|
||||
l = 8;
|
||||
while ((l << 2) < n) {
|
||||
cftmdl(n, l, a, w);
|
||||
l <<= 2;
|
||||
}
|
||||
}
|
||||
if ((l << 2) == n) {
|
||||
for (j = 0; j < l; j += 2) {
|
||||
j1 = j + l;
|
||||
j2 = j1 + l;
|
||||
j3 = j2 + l;
|
||||
x0r = a[j] + a[j1];
|
||||
x0i = -a[j + 1] - a[j1 + 1];
|
||||
x1r = a[j] - a[j1];
|
||||
x1i = -a[j + 1] + a[j1 + 1];
|
||||
x2r = a[j2] + a[j3];
|
||||
x2i = a[j2 + 1] + a[j3 + 1];
|
||||
x3r = a[j2] - a[j3];
|
||||
x3i = a[j2 + 1] - a[j3 + 1];
|
||||
a[j] = x0r + x2r;
|
||||
a[j + 1] = x0i - x2i;
|
||||
a[j2] = x0r - x2r;
|
||||
a[j2 + 1] = x0i + x2i;
|
||||
a[j1] = x1r - x3i;
|
||||
a[j1 + 1] = x1i - x3r;
|
||||
a[j3] = x1r + x3i;
|
||||
a[j3 + 1] = x1i + x3r;
|
||||
}
|
||||
} else {
|
||||
for (j = 0; j < l; j += 2) {
|
||||
j1 = j + l;
|
||||
x0r = a[j] - a[j1];
|
||||
x0i = -a[j + 1] + a[j1 + 1];
|
||||
a[j] += a[j1];
|
||||
a[j + 1] = -a[j + 1] - a[j1 + 1];
|
||||
a[j1] = x0r;
|
||||
a[j1 + 1] = x0i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cft1st(int n, float *a, float *w)
|
||||
{
|
||||
int j, k1, k2;
|
||||
float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
|
||||
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
|
||||
|
||||
x0r = a[0] + a[2];
|
||||
x0i = a[1] + a[3];
|
||||
x1r = a[0] - a[2];
|
||||
x1i = a[1] - a[3];
|
||||
x2r = a[4] + a[6];
|
||||
x2i = a[5] + a[7];
|
||||
x3r = a[4] - a[6];
|
||||
x3i = a[5] - a[7];
|
||||
a[0] = x0r + x2r;
|
||||
a[1] = x0i + x2i;
|
||||
a[4] = x0r - x2r;
|
||||
a[5] = x0i - x2i;
|
||||
a[2] = x1r - x3i;
|
||||
a[3] = x1i + x3r;
|
||||
a[6] = x1r + x3i;
|
||||
a[7] = x1i - x3r;
|
||||
wk1r = w[2];
|
||||
x0r = a[8] + a[10];
|
||||
x0i = a[9] + a[11];
|
||||
x1r = a[8] - a[10];
|
||||
x1i = a[9] - a[11];
|
||||
x2r = a[12] + a[14];
|
||||
x2i = a[13] + a[15];
|
||||
x3r = a[12] - a[14];
|
||||
x3i = a[13] - a[15];
|
||||
a[8] = x0r + x2r;
|
||||
a[9] = x0i + x2i;
|
||||
a[12] = x2i - x0i;
|
||||
a[13] = x0r - x2r;
|
||||
x0r = x1r - x3i;
|
||||
x0i = x1i + x3r;
|
||||
a[10] = wk1r * (x0r - x0i);
|
||||
a[11] = wk1r * (x0r + x0i);
|
||||
x0r = x3i + x1r;
|
||||
x0i = x3r - x1i;
|
||||
a[14] = wk1r * (x0i - x0r);
|
||||
a[15] = wk1r * (x0i + x0r);
|
||||
k1 = 0;
|
||||
for (j = 16; j < n; j += 16) {
|
||||
k1 += 2;
|
||||
k2 = 2 * k1;
|
||||
wk2r = w[k1];
|
||||
wk2i = w[k1 + 1];
|
||||
wk1r = w[k2];
|
||||
wk1i = w[k2 + 1];
|
||||
wk3r = wk1r - 2 * wk2i * wk1i;
|
||||
wk3i = 2 * wk2i * wk1r - wk1i;
|
||||
x0r = a[j] + a[j + 2];
|
||||
x0i = a[j + 1] + a[j + 3];
|
||||
x1r = a[j] - a[j + 2];
|
||||
x1i = a[j + 1] - a[j + 3];
|
||||
x2r = a[j + 4] + a[j + 6];
|
||||
x2i = a[j + 5] + a[j + 7];
|
||||
x3r = a[j + 4] - a[j + 6];
|
||||
x3i = a[j + 5] - a[j + 7];
|
||||
a[j] = x0r + x2r;
|
||||
a[j + 1] = x0i + x2i;
|
||||
x0r -= x2r;
|
||||
x0i -= x2i;
|
||||
a[j + 4] = wk2r * x0r - wk2i * x0i;
|
||||
a[j + 5] = wk2r * x0i + wk2i * x0r;
|
||||
x0r = x1r - x3i;
|
||||
x0i = x1i + x3r;
|
||||
a[j + 2] = wk1r * x0r - wk1i * x0i;
|
||||
a[j + 3] = wk1r * x0i + wk1i * x0r;
|
||||
x0r = x1r + x3i;
|
||||
x0i = x1i - x3r;
|
||||
a[j + 6] = wk3r * x0r - wk3i * x0i;
|
||||
a[j + 7] = wk3r * x0i + wk3i * x0r;
|
||||
wk1r = w[k2 + 2];
|
||||
wk1i = w[k2 + 3];
|
||||
wk3r = wk1r - 2 * wk2r * wk1i;
|
||||
wk3i = 2 * wk2r * wk1r - wk1i;
|
||||
x0r = a[j + 8] + a[j + 10];
|
||||
x0i = a[j + 9] + a[j + 11];
|
||||
x1r = a[j + 8] - a[j + 10];
|
||||
x1i = a[j + 9] - a[j + 11];
|
||||
x2r = a[j + 12] + a[j + 14];
|
||||
x2i = a[j + 13] + a[j + 15];
|
||||
x3r = a[j + 12] - a[j + 14];
|
||||
x3i = a[j + 13] - a[j + 15];
|
||||
a[j + 8] = x0r + x2r;
|
||||
a[j + 9] = x0i + x2i;
|
||||
x0r -= x2r;
|
||||
x0i -= x2i;
|
||||
a[j + 12] = -wk2i * x0r - wk2r * x0i;
|
||||
a[j + 13] = -wk2i * x0i + wk2r * x0r;
|
||||
x0r = x1r - x3i;
|
||||
x0i = x1i + x3r;
|
||||
a[j + 10] = wk1r * x0r - wk1i * x0i;
|
||||
a[j + 11] = wk1r * x0i + wk1i * x0r;
|
||||
x0r = x1r + x3i;
|
||||
x0i = x1i - x3r;
|
||||
a[j + 14] = wk3r * x0r - wk3i * x0i;
|
||||
a[j + 15] = wk3r * x0i + wk3i * x0r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cftmdl(int n, int l, float *a, float *w)
|
||||
{
|
||||
int j, j1, j2, j3, k, k1, k2, m, m2;
|
||||
float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
|
||||
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
|
||||
|
||||
m = l << 2;
|
||||
for (j = 0; j < l; j += 2) {
|
||||
j1 = j + l;
|
||||
j2 = j1 + l;
|
||||
j3 = j2 + l;
|
||||
x0r = a[j] + a[j1];
|
||||
x0i = a[j + 1] + a[j1 + 1];
|
||||
x1r = a[j] - a[j1];
|
||||
x1i = a[j + 1] - a[j1 + 1];
|
||||
x2r = a[j2] + a[j3];
|
||||
x2i = a[j2 + 1] + a[j3 + 1];
|
||||
x3r = a[j2] - a[j3];
|
||||
x3i = a[j2 + 1] - a[j3 + 1];
|
||||
a[j] = x0r + x2r;
|
||||
a[j + 1] = x0i + x2i;
|
||||
a[j2] = x0r - x2r;
|
||||
a[j2 + 1] = x0i - x2i;
|
||||
a[j1] = x1r - x3i;
|
||||
a[j1 + 1] = x1i + x3r;
|
||||
a[j3] = x1r + x3i;
|
||||
a[j3 + 1] = x1i - x3r;
|
||||
}
|
||||
wk1r = w[2];
|
||||
for (j = m; j < l + m; j += 2) {
|
||||
j1 = j + l;
|
||||
j2 = j1 + l;
|
||||
j3 = j2 + l;
|
||||
x0r = a[j] + a[j1];
|
||||
x0i = a[j + 1] + a[j1 + 1];
|
||||
x1r = a[j] - a[j1];
|
||||
x1i = a[j + 1] - a[j1 + 1];
|
||||
x2r = a[j2] + a[j3];
|
||||
x2i = a[j2 + 1] + a[j3 + 1];
|
||||
x3r = a[j2] - a[j3];
|
||||
x3i = a[j2 + 1] - a[j3 + 1];
|
||||
a[j] = x0r + x2r;
|
||||
a[j + 1] = x0i + x2i;
|
||||
a[j2] = x2i - x0i;
|
||||
a[j2 + 1] = x0r - x2r;
|
||||
x0r = x1r - x3i;
|
||||
x0i = x1i + x3r;
|
||||
a[j1] = wk1r * (x0r - x0i);
|
||||
a[j1 + 1] = wk1r * (x0r + x0i);
|
||||
x0r = x3i + x1r;
|
||||
x0i = x3r - x1i;
|
||||
a[j3] = wk1r * (x0i - x0r);
|
||||
a[j3 + 1] = wk1r * (x0i + x0r);
|
||||
}
|
||||
k1 = 0;
|
||||
m2 = 2 * m;
|
||||
for (k = m2; k < n; k += m2) {
|
||||
k1 += 2;
|
||||
k2 = 2 * k1;
|
||||
wk2r = w[k1];
|
||||
wk2i = w[k1 + 1];
|
||||
wk1r = w[k2];
|
||||
wk1i = w[k2 + 1];
|
||||
wk3r = wk1r - 2 * wk2i * wk1i;
|
||||
wk3i = 2 * wk2i * wk1r - wk1i;
|
||||
for (j = k; j < l + k; j += 2) {
|
||||
j1 = j + l;
|
||||
j2 = j1 + l;
|
||||
j3 = j2 + l;
|
||||
x0r = a[j] + a[j1];
|
||||
x0i = a[j + 1] + a[j1 + 1];
|
||||
x1r = a[j] - a[j1];
|
||||
x1i = a[j + 1] - a[j1 + 1];
|
||||
x2r = a[j2] + a[j3];
|
||||
x2i = a[j2 + 1] + a[j3 + 1];
|
||||
x3r = a[j2] - a[j3];
|
||||
x3i = a[j2 + 1] - a[j3 + 1];
|
||||
a[j] = x0r + x2r;
|
||||
a[j + 1] = x0i + x2i;
|
||||
x0r -= x2r;
|
||||
x0i -= x2i;
|
||||
a[j2] = wk2r * x0r - wk2i * x0i;
|
||||
a[j2 + 1] = wk2r * x0i + wk2i * x0r;
|
||||
x0r = x1r - x3i;
|
||||
x0i = x1i + x3r;
|
||||
a[j1] = wk1r * x0r - wk1i * x0i;
|
||||
a[j1 + 1] = wk1r * x0i + wk1i * x0r;
|
||||
x0r = x1r + x3i;
|
||||
x0i = x1i - x3r;
|
||||
a[j3] = wk3r * x0r - wk3i * x0i;
|
||||
a[j3 + 1] = wk3r * x0i + wk3i * x0r;
|
||||
}
|
||||
wk1r = w[k2 + 2];
|
||||
wk1i = w[k2 + 3];
|
||||
wk3r = wk1r - 2 * wk2r * wk1i;
|
||||
wk3i = 2 * wk2r * wk1r - wk1i;
|
||||
for (j = k + m; j < l + (k + m); j += 2) {
|
||||
j1 = j + l;
|
||||
j2 = j1 + l;
|
||||
j3 = j2 + l;
|
||||
x0r = a[j] + a[j1];
|
||||
x0i = a[j + 1] + a[j1 + 1];
|
||||
x1r = a[j] - a[j1];
|
||||
x1i = a[j + 1] - a[j1 + 1];
|
||||
x2r = a[j2] + a[j3];
|
||||
x2i = a[j2 + 1] + a[j3 + 1];
|
||||
x3r = a[j2] - a[j3];
|
||||
x3i = a[j2 + 1] - a[j3 + 1];
|
||||
a[j] = x0r + x2r;
|
||||
a[j + 1] = x0i + x2i;
|
||||
x0r -= x2r;
|
||||
x0i -= x2i;
|
||||
a[j2] = -wk2i * x0r - wk2r * x0i;
|
||||
a[j2 + 1] = -wk2i * x0i + wk2r * x0r;
|
||||
x0r = x1r - x3i;
|
||||
x0i = x1i + x3r;
|
||||
a[j1] = wk1r * x0r - wk1i * x0i;
|
||||
a[j1 + 1] = wk1r * x0i + wk1i * x0r;
|
||||
x0r = x1r + x3i;
|
||||
x0i = x1i - x3r;
|
||||
a[j3] = wk3r * x0r - wk3i * x0i;
|
||||
a[j3 + 1] = wk3r * x0i + wk3i * x0r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rftfsub(int n, float *a, int nc, float *c)
|
||||
{
|
||||
int j, k, kk, ks, m;
|
||||
float wkr, wki, xr, xi, yr, yi;
|
||||
|
||||
m = n >> 1;
|
||||
ks = 2 * nc / m;
|
||||
kk = 0;
|
||||
for (j = 2; j < m; j += 2) {
|
||||
k = n - j;
|
||||
kk += ks;
|
||||
wkr = 0.5 - c[nc - kk];
|
||||
wki = c[kk];
|
||||
xr = a[j] - a[k];
|
||||
xi = a[j + 1] + a[k + 1];
|
||||
yr = wkr * xr - wki * xi;
|
||||
yi = wkr * xi + wki * xr;
|
||||
a[j] -= yr;
|
||||
a[j + 1] -= yi;
|
||||
a[k] += yr;
|
||||
a[k + 1] -= yi;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rftbsub(int n, float *a, int nc, float *c)
|
||||
{
|
||||
int j, k, kk, ks, m;
|
||||
float wkr, wki, xr, xi, yr, yi;
|
||||
|
||||
a[1] = -a[1];
|
||||
m = n >> 1;
|
||||
ks = 2 * nc / m;
|
||||
kk = 0;
|
||||
for (j = 2; j < m; j += 2) {
|
||||
k = n - j;
|
||||
kk += ks;
|
||||
wkr = 0.5 - c[nc - kk];
|
||||
wki = c[kk];
|
||||
xr = a[j] - a[k];
|
||||
xi = a[j + 1] + a[k + 1];
|
||||
yr = wkr * xr + wki * xi;
|
||||
yi = wkr * xi - wki * xr;
|
||||
a[j] -= yr;
|
||||
a[j + 1] = yi - a[j + 1];
|
||||
a[k] += yr;
|
||||
a[k + 1] = yi - a[k + 1];
|
||||
}
|
||||
a[m + 1] = -a[m + 1];
|
||||
}
|
||||
202
src/ggwave.cpp
202
src/ggwave.cpp
@@ -1,10 +1,10 @@
|
||||
#include "ggwave/ggwave.h"
|
||||
|
||||
#include "fft.h"
|
||||
#include "reed-solomon/rs.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <ctime>
|
||||
//#include <random>
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
namespace {
|
||||
|
||||
FILE * g_fptr = stderr;
|
||||
std::map<ggwave_Instance, GGWave *> g_instances;
|
||||
std::map<ggwave_Instance, GGWave::RxProtocols> g_rxProtocols;
|
||||
std::vector<GGWave *> g_instances;
|
||||
std::vector<GGWave::RxProtocols> g_rxProtocols;
|
||||
|
||||
double linear_interp(double first_number, double second_number, double fraction) {
|
||||
return (first_number + ((second_number - first_number)*fraction));
|
||||
@@ -45,6 +45,11 @@ extern "C"
|
||||
ggwave_Instance ggwave_init(const ggwave_Parameters parameters) {
|
||||
static ggwave_Instance curId = 0;
|
||||
|
||||
if ((int) g_instances.size() < curId + 1) {
|
||||
g_instances.resize(curId + 1, nullptr);
|
||||
g_rxProtocols.resize(curId + 1, GGWave::getTxProtocols());
|
||||
}
|
||||
|
||||
g_instances[curId] = new GGWave({
|
||||
parameters.payloadLength,
|
||||
parameters.sampleRateInp,
|
||||
@@ -61,8 +66,10 @@ ggwave_Instance ggwave_init(const ggwave_Parameters parameters) {
|
||||
|
||||
extern "C"
|
||||
void ggwave_free(ggwave_Instance instance) {
|
||||
delete (GGWave *) g_instances[instance];
|
||||
g_instances.erase(instance);
|
||||
if ((int) g_instances.size() > instance && g_instances[instance]) {
|
||||
delete (GGWave *) g_instances[instance];
|
||||
g_instances[instance] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@@ -170,11 +177,6 @@ void ggwave_toggleRxProtocol(
|
||||
ggwave_Instance instance,
|
||||
ggwave_TxProtocolId rxProtocolId,
|
||||
int state) {
|
||||
// if never called - initialize with all available protocols
|
||||
if (g_rxProtocols.find(instance) == g_rxProtocols.end()) {
|
||||
g_rxProtocols[instance] = GGWave::getTxProtocols();
|
||||
}
|
||||
|
||||
if (state == 0) {
|
||||
// disable Rx protocol
|
||||
g_rxProtocols[instance][rxProtocolId].enabled = false;
|
||||
@@ -192,90 +194,14 @@ void ggwave_toggleRxProtocol(
|
||||
|
||||
namespace {
|
||||
|
||||
// FFT routines taken from https://stackoverflow.com/a/37729648/4039976
|
||||
|
||||
int log2(int N) {
|
||||
int k = N, i = 0;
|
||||
while(k) {
|
||||
k >>= 1;
|
||||
i++;
|
||||
}
|
||||
return i - 1;
|
||||
void FFT(float * f, int N, int * ip, float * w) {
|
||||
rdft(N, 1, f, ip, w);
|
||||
}
|
||||
|
||||
int reverse(int N, int n) {
|
||||
int j, p = 0;
|
||||
for(j = 1; j <= log2(N); j++) {
|
||||
if(n & (1 << (log2(N) - j)))
|
||||
p |= 1 << (j - 1);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
void FFT(const float * src, float * dst, int N, int * ip, float * w) {
|
||||
std::copy(src, src + N, dst);
|
||||
|
||||
void ordina(float * f1, int N) {
|
||||
static thread_local float f2[2*GGWave::kMaxSamplesPerFrame];
|
||||
for (int i = 0; i < N; i++) {
|
||||
int ir = reverse(N, i);
|
||||
f2[2*i + 0] = f1[2*ir + 0];
|
||||
f2[2*i + 1] = f1[2*ir + 1];
|
||||
}
|
||||
for (int j = 0; j < N; j++) {
|
||||
f1[2*j + 0] = f2[2*j + 0];
|
||||
f1[2*j + 1] = f2[2*j + 1];
|
||||
}
|
||||
}
|
||||
|
||||
void transform(float * f, int N) {
|
||||
ordina(f, N); //first: reverse order
|
||||
float * W;
|
||||
W = (float *)malloc(N*sizeof(float));
|
||||
W[2*1 + 0] = cos(-2.*M_PI/N);
|
||||
W[2*1 + 1] = sin(-2.*M_PI/N);
|
||||
W[2*0 + 0] = 1;
|
||||
W[2*0 + 1] = 0;
|
||||
for (int i = 2; i < N / 2; i++) {
|
||||
W[2*i + 0] = cos(-2.*i*M_PI/N);
|
||||
W[2*i + 1] = sin(-2.*i*M_PI/N);
|
||||
}
|
||||
int n = 1;
|
||||
int a = N / 2;
|
||||
for(int j = 0; j < log2(N); j++) {
|
||||
for(int i = 0; i < N; i++) {
|
||||
if(!(i & n)) {
|
||||
int wi = (i * a) % (n * a);
|
||||
int fi = i + n;
|
||||
float a = W[2*wi + 0];
|
||||
float b = W[2*wi + 1];
|
||||
float c = f[2*fi + 0];
|
||||
float d = f[2*fi + 1];
|
||||
float temp[2] = { f[2*i + 0], f[2*i + 1] };
|
||||
float Temp[2] = { a*c - b*d, b*c + a*d };
|
||||
f[2*i + 0] = temp[0] + Temp[0];
|
||||
f[2*i + 1] = temp[1] + Temp[1];
|
||||
f[2*fi + 0] = temp[0] - Temp[0];
|
||||
f[2*fi + 1] = temp[1] - Temp[1];
|
||||
}
|
||||
}
|
||||
n *= 2;
|
||||
a = a / 2;
|
||||
}
|
||||
free(W);
|
||||
}
|
||||
|
||||
void FFT(float * f, int N, float d) {
|
||||
transform(f, N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
f[2*i + 0] *= d;
|
||||
f[2*i + 1] *= d;
|
||||
}
|
||||
}
|
||||
|
||||
void FFT(const float * src, float * dst, int N, float d) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
dst[2*i + 0] = src[i];
|
||||
dst[2*i + 1] = 0.0f;
|
||||
}
|
||||
FFT(dst, N, d);
|
||||
FFT(dst, N, ip, w);
|
||||
}
|
||||
|
||||
inline void addAmplitudeSmooth(
|
||||
@@ -336,8 +262,9 @@ struct GGWave::Rx {
|
||||
int framesToRecord = 0;
|
||||
int samplesNeeded = 0;
|
||||
|
||||
std::vector<float> fftInp; // real
|
||||
std::vector<float> fftOut; // complex
|
||||
std::vector<int> m_fftWorkI;
|
||||
std::vector<float> m_fftWorkF;
|
||||
|
||||
bool hasNewSpectrum = false;
|
||||
bool hasNewAmplitude = false;
|
||||
@@ -439,6 +366,7 @@ GGWave::GGWave(const Parameters & parameters) :
|
||||
m_isTxEnabled (parameters.operatingMode & GGWAVE_OPERATING_MODE_TX),
|
||||
m_needResampling (m_sampleRateInp != m_sampleRate || m_sampleRateOut != m_sampleRate),
|
||||
m_txOnlyTones (parameters.operatingMode & GGWAVE_OPERATING_MODE_TX_ONLY_TONES),
|
||||
m_isDSSEnabled (parameters.operatingMode & GGWAVE_OPERATING_MODE_USE_DSS),
|
||||
|
||||
// common
|
||||
m_dataEncoded (kMaxDataSize),
|
||||
@@ -477,8 +405,10 @@ GGWave::GGWave(const Parameters & parameters) :
|
||||
|
||||
m_rx->samplesNeeded = m_samplesPerFrame;
|
||||
|
||||
m_rx->fftInp.resize(m_samplesPerFrame);
|
||||
m_rx->fftOut.resize(2*m_samplesPerFrame);
|
||||
m_rx->m_fftWorkI.resize(3 + sqrt(m_samplesPerFrame/2));
|
||||
m_rx->m_fftWorkF.resize(m_samplesPerFrame/2);
|
||||
m_rx->m_fftWorkI[0] = 0;
|
||||
|
||||
m_rx->sampleSpectrum.resize(m_samplesPerFrame);
|
||||
m_rx->sampleAmplitude.resize(m_needResampling ? m_samplesPerFrame + 128 : m_samplesPerFrame); // small extra space because sometimes resampling needs a few more samples
|
||||
@@ -563,6 +493,15 @@ GGWave::GGWave(const Parameters & parameters) :
|
||||
m_resampler = new Resampler();
|
||||
}
|
||||
|
||||
if (m_isDSSEnabled) {
|
||||
m_dssMagic = {
|
||||
0x96, 0x9f, 0xb4, 0xaf, 0x1b, 0x91, 0xde, 0xc5, 0x45, 0x75, 0xe8, 0x2e, 0x0f, 0x32, 0x4a, 0x5f,
|
||||
0xb4, 0x56, 0x95, 0xcb, 0x7f, 0x6a, 0x54, 0x6a, 0x48, 0xf2, 0x0b, 0x7b, 0xcd, 0xfb, 0x93, 0x6d,
|
||||
0x3c, 0x77, 0x5e, 0xc3, 0x33, 0x47, 0xc0, 0xf1, 0x71, 0x32, 0x33, 0x27, 0x35, 0x68, 0x47, 0x1f,
|
||||
0x4e, 0xac, 0x23, 0x42, 0x5f, 0x00, 0x37, 0xa4, 0x50, 0x6d, 0x48, 0x24, 0x91, 0x7c, 0xa1, 0x4e,
|
||||
};
|
||||
}
|
||||
|
||||
init("", getDefaultTxProtocol(), 0);
|
||||
}
|
||||
|
||||
@@ -620,23 +559,24 @@ bool GGWave::init(int dataSize, const char * dataBuffer, const TxProtocol & txPr
|
||||
}
|
||||
|
||||
m_tx->txProtocol = txProtocol;
|
||||
m_tx->txDataLength = dataSize;
|
||||
m_tx->txDataLength = m_isFixedPayloadLength ? m_payloadLength : dataSize;
|
||||
m_tx->sendVolume = ((double)(volume))/100.0f;
|
||||
|
||||
m_tx->hasNewTxData = false;
|
||||
std::fill(m_tx->txData.begin(), m_tx->txData.end(), 0);
|
||||
std::fill(m_dataEncoded.begin(), m_dataEncoded.end(), 0);
|
||||
|
||||
if (m_tx->txDataLength > 0) {
|
||||
if (dataSize > 0) {
|
||||
m_tx->txData[0] = m_tx->txDataLength;
|
||||
for (int i = 0; i < m_tx->txDataLength; ++i) m_tx->txData[i + 1] = dataBuffer[i];
|
||||
for (int i = 0; i < m_tx->txDataLength; ++i) {
|
||||
m_tx->txData[i + 1] = i < dataSize ? dataBuffer[i] : 0;
|
||||
if (m_isDSSEnabled) {
|
||||
m_tx->txData[i + 1] ^= m_dssMagic[i%m_dssMagic.size()];
|
||||
}
|
||||
}
|
||||
|
||||
m_tx->hasNewTxData = true;
|
||||
}
|
||||
|
||||
if (m_isFixedPayloadLength) {
|
||||
m_tx->txDataLength = m_payloadLength;
|
||||
}
|
||||
} else {
|
||||
if (dataSize > 0) {
|
||||
ggprintf("Tx is disabled - cannot transmit data with this ggwave instance\n");
|
||||
@@ -661,11 +601,6 @@ bool GGWave::init(int dataSize, const char * dataBuffer, const TxProtocol & txPr
|
||||
|
||||
std::fill(m_rx->rxData.begin(), m_rx->rxData.end(), 0);
|
||||
|
||||
for (int i = 0; i < m_samplesPerFrame; ++i) {
|
||||
m_rx->fftOut[2*i + 0] = 0.0f;
|
||||
m_rx->fftOut[2*i + 1] = 0.0f;
|
||||
}
|
||||
|
||||
for (auto & s : m_rx->spectrumHistoryFixed) {
|
||||
std::fill(s.begin(), s.end(), 0);
|
||||
}
|
||||
@@ -1148,6 +1083,7 @@ bool GGWave::decode(const void * data, uint32_t nBytes) {
|
||||
//
|
||||
|
||||
bool GGWave::hasTxData() const { return m_tx && m_tx->hasNewTxData; }
|
||||
bool GGWave::isDSSEnabled() const { return m_isDSSEnabled; }
|
||||
|
||||
int GGWave::getSamplesPerFrame() const { return m_samplesPerFrame; }
|
||||
int GGWave::getSampleSizeBytesInp() const { return m_sampleSizeBytesInp; }
|
||||
@@ -1183,6 +1119,7 @@ bool GGWave::takeTxAmplitudeI16(AmplitudeDataI16 & dst) {
|
||||
bool GGWave::isReceiving() const { return m_rx->receivingData; }
|
||||
bool GGWave::isAnalyzing() const { return m_rx->analyzingData; }
|
||||
|
||||
int GGWave::getSamplesNeeded() const { return m_rx->samplesNeeded; }
|
||||
int GGWave::getFramesToRecord() const { return m_rx->framesToRecord; }
|
||||
int GGWave::getFramesLeftToRecord() const { return m_rx->framesLeftToRecord; }
|
||||
int GGWave::getFramesToAnalyze() const { return m_rx->framesToAnalyze; }
|
||||
@@ -1238,13 +1175,13 @@ bool GGWave::takeRxAmplitude(AmplitudeData & dst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GGWave::computeFFTR(const float * src, float * dst, int N, float d) {
|
||||
if (N > kMaxSamplesPerFrame) {
|
||||
ggprintf("computeFFTR: N (%d) must be <= %d\n", N, GGWave::kMaxSamplesPerFrame);
|
||||
bool GGWave::computeFFTR(const float * src, float * dst, int N) {
|
||||
if (N != m_samplesPerFrame) {
|
||||
ggprintf("computeFFTR: N (%d) must be equal to 'samplesPerFrame' %d\n", N, m_samplesPerFrame);
|
||||
return false;
|
||||
}
|
||||
|
||||
FFT(src, dst, N, d);
|
||||
FFT(src, dst, N, m_rx->m_fftWorkI.data(), m_rx->m_fftWorkF.data());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1426,7 +1363,7 @@ void GGWave::decode_variable() {
|
||||
}
|
||||
|
||||
// calculate spectrum
|
||||
FFT(m_rx->sampleAmplitudeAverage.data(), m_rx->fftOut.data(), m_samplesPerFrame, 1.0);
|
||||
FFT(m_rx->sampleAmplitudeAverage.data(), m_rx->fftOut.data(), m_samplesPerFrame, m_rx->m_fftWorkI.data(), m_rx->m_fftWorkF.data());
|
||||
|
||||
for (int i = 0; i < m_samplesPerFrame; ++i) {
|
||||
m_rx->sampleSpectrum[i] = (m_rx->fftOut[2*i + 0]*m_rx->fftOut[2*i + 0] + m_rx->fftOut[2*i + 1]*m_rx->fftOut[2*i + 1]);
|
||||
@@ -1488,16 +1425,16 @@ void GGWave::decode_variable() {
|
||||
|
||||
std::copy(
|
||||
m_rx->recordedAmplitude.begin() + offsetTx*step,
|
||||
m_rx->recordedAmplitude.begin() + offsetTx*step + m_samplesPerFrame, m_rx->fftInp.data());
|
||||
m_rx->recordedAmplitude.begin() + offsetTx*step + m_samplesPerFrame, m_rx->fftOut.data());
|
||||
|
||||
// note : should we skip the first and last frame here as they are amplitude-smoothed?
|
||||
for (int k = 1; k < rxProtocol.framesPerTx; ++k) {
|
||||
for (int i = 0; i < m_samplesPerFrame; ++i) {
|
||||
m_rx->fftInp[i] += m_rx->recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i];
|
||||
m_rx->fftOut[i] += m_rx->recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i];
|
||||
}
|
||||
}
|
||||
|
||||
FFT(m_rx->fftInp.data(), m_rx->fftOut.data(), m_samplesPerFrame, 1.0);
|
||||
FFT(m_rx->fftOut.data(), m_samplesPerFrame, m_rx->m_fftWorkI.data(), m_rx->m_fftWorkF.data());
|
||||
|
||||
for (int i = 0; i < m_samplesPerFrame; ++i) {
|
||||
m_rx->sampleSpectrum[i] = (m_rx->fftOut[2*i + 0]*m_rx->fftOut[2*i + 0] + m_rx->fftOut[2*i + 1]*m_rx->fftOut[2*i + 1]);
|
||||
@@ -1559,7 +1496,13 @@ void GGWave::decode_variable() {
|
||||
RS::ReedSolomon rsData(decodedLength, ::getECCBytesForLength(decodedLength), m_workRSData.data());
|
||||
|
||||
if (rsData.Decode(m_dataEncoded.data() + m_encodedDataOffset, m_rx->rxData.data()) == 0) {
|
||||
if (m_rx->rxData[0] != 0) {
|
||||
if (decodedLength > 0) {
|
||||
if (m_isDSSEnabled) {
|
||||
for (int i = 0; i < decodedLength; ++i) {
|
||||
m_rx->rxData[i] = m_rx->rxData[i] ^ m_dssMagic[i%m_dssMagic.size()];
|
||||
}
|
||||
}
|
||||
|
||||
ggprintf("Decoded length = %d, protocol = '%s' (%d)\n", decodedLength, rxProtocol.name, rxProtocolId);
|
||||
ggprintf("Received sound data successfully: '%s'\n", m_rx->rxData.data());
|
||||
|
||||
@@ -1705,7 +1648,7 @@ void GGWave::decode_fixed() {
|
||||
m_rx->hasNewSpectrum = true;
|
||||
|
||||
// calculate spectrum
|
||||
FFT(m_rx->sampleAmplitude.data(), m_rx->fftOut.data(), m_samplesPerFrame, 1.0);
|
||||
FFT(m_rx->sampleAmplitude.data(), m_rx->fftOut.data(), m_samplesPerFrame, m_rx->m_fftWorkI.data(), m_rx->m_fftWorkF.data());
|
||||
|
||||
float amax = 0.0f;
|
||||
for (int i = 0; i < m_samplesPerFrame; ++i) {
|
||||
@@ -1716,20 +1659,23 @@ void GGWave::decode_fixed() {
|
||||
amax = std::max(amax, m_rx->sampleSpectrum[i]);
|
||||
}
|
||||
|
||||
|
||||
// original, floating-point version
|
||||
//m_rx->spectrumHistoryFixed[m_rx->historyIdFixed] = m_rx->sampleSpectrum;
|
||||
|
||||
// in theory, using uint8_t should work alsmost the same and save 4 times the memory, but for some resone
|
||||
// the result are not as good as with the floating-point version
|
||||
// float -> uint8_t
|
||||
//amax = 255.0f/(amax == 0.0f ? 1.0f : amax);
|
||||
//for (int i = 0; i < m_samplesPerFrame; ++i) {
|
||||
// m_rx->spectrumHistoryFixed[m_rx->historyIdFixed][i] = std::min(255.0, std::max(0.0, round(m_rx->sampleSpectrum[i]/amax*255.0f)));
|
||||
// m_rx->spectrumHistoryFixed[m_rx->historyIdFixed][i] = std::min(255.0f, std::max(0.0f, round(m_rx->sampleSpectrum[i]*amax)));
|
||||
//}
|
||||
|
||||
// hence we opt for the uint16_t version, saving 2 times the memory and getting similar results as the floating-point version
|
||||
// float -> uint16_t
|
||||
amax = 65535.0f/(amax == 0.0f ? 1.0f : amax);
|
||||
for (int i = 0; i < m_samplesPerFrame; ++i) {
|
||||
m_rx->spectrumHistoryFixed[m_rx->historyIdFixed][i] = std::min(65535.0, std::max(0.0, round(m_rx->sampleSpectrum[i]/amax*65535.0f)));
|
||||
m_rx->spectrumHistoryFixed[m_rx->historyIdFixed][i] = std::min(65535.0f, std::max(0.0f, round(m_rx->sampleSpectrum[i]*amax)));
|
||||
}
|
||||
|
||||
if (++m_rx->historyIdFixed >= (int) m_rx->spectrumHistoryFixed.size()) {
|
||||
@@ -1844,16 +1790,20 @@ void GGWave::decode_fixed() {
|
||||
}
|
||||
|
||||
if (rsData.Decode(m_dataEncoded.data(), m_rx->rxData.data()) == 0) {
|
||||
if (m_rx->rxData[0] != 0) {
|
||||
ggprintf("Decoded length = %d, protocol = '%s' (%d)\n", m_rx->rxData[0], rxProtocol.name, rxProtocolId);
|
||||
ggprintf("Received sound data successfully: '%s'\n", m_rx->rxData.data());
|
||||
|
||||
isValid = true;
|
||||
m_rx->hasNewRxData = true;
|
||||
m_rx->lastRxDataLength = m_payloadLength;
|
||||
m_rx->rxProtocol = rxProtocol;
|
||||
m_rx->rxProtocolId = TxProtocolId(rxProtocolId);
|
||||
if (m_isDSSEnabled) {
|
||||
for (int i = 0; i < m_payloadLength; ++i) {
|
||||
m_rx->rxData[i] = m_rx->rxData[i] ^ m_dssMagic[i%m_dssMagic.size()];
|
||||
}
|
||||
}
|
||||
|
||||
ggprintf("Decoded length = %d, protocol = '%s' (%d)\n", m_payloadLength, rxProtocol.name, rxProtocolId);
|
||||
ggprintf("Received sound data successfully: '%s'\n", m_rx->rxData.data());
|
||||
|
||||
isValid = true;
|
||||
m_rx->hasNewRxData = true;
|
||||
m_rx->lastRxDataLength = m_payloadLength;
|
||||
m_rx->rxProtocol = rxProtocol;
|
||||
m_rx->rxProtocolId = TxProtocolId(rxProtocolId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user