-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathfft_mt_r2iq_impl.hpp
More file actions
177 lines (158 loc) · 5.99 KB
/
fft_mt_r2iq_impl.hpp
File metadata and controls
177 lines (158 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
{
const int decimate = this->mdecimation;
const int mfft = this->mfftdim[decimate]; // = halfFft / 2^mdecimation
const fftwf_complex* filter = filterHw[decimate];
const bool lsb = this->getSideband();
const auto filter2 = &filter[halfFft - mfft / 2];
plan_f2t_c2c = &plans_f2t_c2c[decimate];
fftwf_complex* pout = nullptr;
int decimate_count = 0;
#ifdef _SOFT_TONE_DEBUG
init_burst(); // if DEBUG_LENGTH
#endif
while (r2iqOn) {
const int16_t *dataADC; // pointer to input data
const int16_t *endloop; // pointer to end data to be copied to beginning
const int _mtunebin = this->mtunebin; // Update LO tune is possible during run
{
std::unique_lock<std::mutex> lk(mutexR2iqControl);
dataADC = inputbuffer->getReadPtr();
if (!r2iqOn)
return 0;
this->bufIdx = (this->bufIdx + 1) % QUEUE_SIZE;
endloop = inputbuffer->peekReadPtr(-1) + transferSamples - halfFft;
}
auto inloop = th->ADCinTime;
// @todo: move the following int16_t conversion to (32-bit) float
// directly inside the following loop (for "k < fftPerBuf")
// just before the forward fft "fftwf_execute_dft_r2c" is called
// idea: this should improve cache/memory locality
#if PRINT_INPUT_RANGE
std::pair<int16_t, int16_t> blockMinMax = std::make_pair<int16_t, int16_t>(0, 0);
#endif
if (!this->getRand()) // plain samples no ADC rand set
{
convert_float<false>(endloop, inloop, halfFft);
#if PRINT_INPUT_RANGE
auto minmax = std::minmax_element(dataADC, dataADC + transferSamples);
blockMinMax.first = *minmax.first;
blockMinMax.second = *minmax.second;
#endif
convert_float<false>(dataADC, inloop + halfFft, transferSamples);
}
else
{
#ifdef _SOFT_TONE_DEBUG
generate_float(inloop, transferSamples + halfFft);
#else
convert_float<true>(endloop, inloop, halfFft);
convert_float<true>(dataADC, inloop + halfFft, transferSamples);
#endif
}
#if PRINT_INPUT_RANGE
th->MinValue = std::min(blockMinMax.first, th->MinValue);
th->MaxValue = std::max(blockMinMax.second, th->MaxValue);
++th->MinMaxBlockCount;
if (th->MinMaxBlockCount * processor_count / 3 >= DEFAULT_TRANSFERS_PER_SEC )
{
float minBits = (th->MinValue < 0) ? (log10f((float)(-th->MinValue)) / log10f(2.0f)) : -1.0f;
float maxBits = (th->MaxValue > 0) ? (log10f((float)(th->MaxValue)) / log10f(2.0f)) : -1.0f;
printf("r2iq: min = %d (%.1f bits) %.2f%%, max = %d (%.1f bits) %.2f%%\n",
(int)th->MinValue, minBits, th->MinValue *-100.0f / 32768.0f,
(int)th->MaxValue, maxBits, th->MaxValue * 100.0f / 32768.0f);
th->MinValue = 0;
th->MaxValue = 0;
th->MinMaxBlockCount = 0;
}
#endif
dataADC = nullptr;
inputbuffer->ReadDone();
// decimate in frequency plus tuning
if (decimate_count == 0)
pout = (fftwf_complex*)outputbuffer->getWritePtr();
decimate_count = (decimate_count + 1) & ((1 << decimate) - 1);
// Calculate the parameters for the first half
const auto count = std::min(mfft/2, halfFft - _mtunebin);
const auto source = &th->ADCinFreq[_mtunebin];
// Calculate the parameters for the second half
const auto start = std::max(0, mfft / 2 - _mtunebin);
const auto source2 = &th->ADCinFreq[_mtunebin - mfft / 2];
const auto dest = &th->inFreqTmp[mfft / 2];
for (int k = 0; k < fftPerBuf; k++)
{
// core of fast convolution including filter and decimation
// main part is 'overlap-scrap' (IMHO better name for 'overlap-save'), see
// https://en.wikipedia.org/wiki/Overlap%E2%80%93save_method
{
// FFT first stage: time to frequency, real to complex
// 'full' transformation size: 2 * halfFft
fftwf_execute_dft_r2c(plan_t2f_r2c, th->ADCinTime + (3 * halfFft / 2) * k, th->ADCinFreq);
// result now in th->ADCinFreq[]
// circular shift (mixing in full bins) and low/bandpass filtering (complex multiplication)
{
// circular shift tune fs/2 first half array into th->inFreqTmp[]
shift_freq(th->inFreqTmp, source, filter, 0, count);
if (mfft / 2 != count)
memset(th->inFreqTmp[count], 0, sizeof(float) * 2 * (mfft / 2 - count));
// circular shift tune fs/2 second half array
shift_freq(dest, source2, filter2, start, mfft/2);
if (start != 0)
memset(th->inFreqTmp[mfft / 2], 0, sizeof(float) * 2 * start);
}
// result now in th->inFreqTmp[]
// 'shorter' inverse FFT transform (decimation); frequency (back) to COMPLEX time domain
// transform size: mfft = mfftdim[k] = halfFft / 2^k with k = mdecimation
fftwf_execute_dft(*plan_f2t_c2c, th->inFreqTmp, th->inFreqTmp); // c2c decimation
// result now in th->inFreqTmp[]
}
// postprocessing
// @todo: is it possible to ..
// 1)
// let inverse FFT produce/save it's result directly
// in "this->obuffers[modx] + offset" (pout)
// ( obuffers[] would need to have additional space ..;
// need to move 'scrap' of 'ovelap-scrap'? )
// at least FFTW would allow so,
// see http://www.fftw.org/fftw3_doc/New_002darray-Execute-Functions.html
// attention: multithreading!
// 2)
// could mirroring (lower sideband) get calculated together
// with fine mixer - modifying the mixer frequency? (fs - fc)/fs
// (this would reduce one memory pass)
if (lsb) // lower sideband
{
// mirror just by negating the imaginary Q of complex I/Q
if (k == 0)
{
copy<true>(pout, &th->inFreqTmp[mfft / 4], mfft/2);
}
else
{
copy<true>(pout + mfft / 2 + (3 * mfft / 4) * (k - 1), &th->inFreqTmp[0], (3 * mfft / 4));
}
}
else // upper sideband
{
if (k == 0)
{
copy<false>(pout, &th->inFreqTmp[mfft / 4], mfft/2);
}
else
{
copy<false>(pout + mfft / 2 + (3 * mfft / 4) * (k - 1), &th->inFreqTmp[0], (3 * mfft / 4));
}
}
// result now in this->obuffers[]
}
if (decimate_count == 0) {
outputbuffer->WriteDone();
pout = nullptr;
}
else
{
pout += mfft / 2 + (3 * mfft / 4) * (fftPerBuf - 1);
}
} // while(run)
// DbgPrintf((char *) "r2iqThreadf idx %d pthread_exit %u\n",(int)th->t, pthread_self());
return 0;
}