1 #include "fftviewer_frFFTviewer.h"
2 #include <wx/timer.h>
3 #include <vector>
4 #include "OpenGLGraph.h"
5 #include <LMSBoards.h>
6 #include "kiss_fft.h"
7 #include "IConnection.h"
8 #include "dataTypes.h"
9 #include "LMS7002M.h"
10 #include "windowFunction.h"
11 #include <fstream>
12 #include "lms7suiteEvents.h"
13 #include "lms7_device.h"
14 #include "Logger.h"
15
16 using namespace std;
17 using namespace lime;
18
Initialize(lms_device_t * pDataPort)19 void fftviewer_frFFTviewer::Initialize(lms_device_t* pDataPort)
20 {
21 StopStreaming();
22 lmsControl = pDataPort;
23 lmsIndex = 0;
24 for (unsigned i =0; i < this->cMaxChCount ; i++)
25 {
26 this->rxStreams[i].handle = 0;
27 this->txStreams[i].handle = 0;
28 }
29 cmbStreamType->Clear();
30 const int num_ch = LMS_GetNumChannels(lmsControl, false);
31 if (num_ch>2)
32 {
33 cmbStreamType->Append(_T("LMS1 SISO"));
34 cmbStreamType->Append(_T("LMS1 MIMO"));
35 cmbStreamType->Append(_T("LMS2 SISO"));
36 cmbStreamType->Append(_T("LMS2 MIMO"));
37 cmbStreamType->Append(_T("Ext. ADC/DAC"));
38 }
39 else if (num_ch == 2)
40 {
41 cmbStreamType->Append(_T("LMS SISO"));
42 cmbStreamType->Append(_T("LMS MIMO"));
43 }
44 else
45 {
46 cmbStreamType->Append(_T("LMS SISO"));
47 }
48 cmbStreamType->SetSelection(0);
49 SetNyquistFrequency();
50
51 }
52
fftviewer_frFFTviewer(wxWindow * parent)53 fftviewer_frFFTviewer::fftviewer_frFFTviewer( wxWindow* parent )
54 :
55 frFFTviewer(parent),
56 mStreamRunning(false),
57 lmsControl(nullptr)
58 {
59 captureSamples.store(false);
60 averageCount.store(50);
61 spinAvgCount->SetValue(averageCount);
62 updateGUI.store(true);
63 #ifndef __unix__
64 SetIcon(wxIcon(_("aaaaAPPicon")));
65 #endif
66 SetSize(1000, 700);
67 mFFTpanel->settings.useVBO = true;
68 mFFTpanel->AddSerie(new cDataSerie());
69 mFFTpanel->AddSerie(new cDataSerie());
70 mFFTpanel->series[0]->color = 0xFF0000FF;
71 mFFTpanel->series[1]->color = 0x0000FFFF;
72 mFFTpanel->SetDrawingMode(GLG_LINE);
73 mFFTpanel->settings.gridXlines = 15;
74 mFFTpanel->SetInitialDisplayArea(-16000000, 16000000, -115, 0);
75
76 mFFTpanel->settings.title = "FFT";
77 mFFTpanel->settings.titleXaxis = "Frequency(MHz)";
78 mFFTpanel->settings.titleYaxis = "Amplitude(dBFS)";
79 mFFTpanel->settings.xUnits = "";
80 mFFTpanel->settings.gridXprec = 3;
81 //mFFTpanel->settings.yUnits = "dB";
82 mFFTpanel->settings.markersEnabled = true;
83
84 mFFTpanel->settings.marginLeft = 40;
85 mFFTpanel->settings.staticGrid = true;
86
87 mTimeDomainPanel->settings.useVBO = true;
88 mTimeDomainPanel->AddSerie(new cDataSerie());
89 mTimeDomainPanel->AddSerie(new cDataSerie());
90 mTimeDomainPanel->AddSerie(new cDataSerie());
91 mTimeDomainPanel->AddSerie(new cDataSerie());
92 mTimeDomainPanel->SetInitialDisplayArea(0, 1024, -2050, 2050);
93 mTimeDomainPanel->settings.title = "IQ samples";
94 mTimeDomainPanel->series[0]->color = 0xFF0000FF;
95 mTimeDomainPanel->series[1]->color = 0x0000FFFF;
96 mTimeDomainPanel->series[2]->color = 0xFF00FFFF;
97 mTimeDomainPanel->series[3]->color = 0x00FFFFFF;
98 mTimeDomainPanel->settings.marginLeft = 48;
99 mConstelationPanel->settings.useVBO = true;
100 mConstelationPanel->AddSerie(new cDataSerie());
101 mConstelationPanel->AddSerie(new cDataSerie());
102 mConstelationPanel->series[0]->color = 0xFF0000FF;
103 mConstelationPanel->series[1]->color = 0x0000FFFF;
104 mConstelationPanel->SetInitialDisplayArea(-2050, 2050, -2050, 2050);
105 mConstelationPanel->SetDrawingMode(GLG_POINTS);
106 mConstelationPanel->settings.title = "I versus Q";
107 mConstelationPanel->settings.titleXaxis = "I";
108 mConstelationPanel->settings.titleYaxis = "Q";
109 mConstelationPanel->settings.gridXlines = 8;
110 mConstelationPanel->settings.gridYlines = 8;
111 mConstelationPanel->settings.marginLeft = 48;
112
113 mGUIupdater = new wxTimer(this, wxID_ANY); //timer for updating plots
114 Connect(wxEVT_THREAD, wxThreadEventHandler(fftviewer_frFFTviewer::OnUpdatePlots), NULL, this);
115 Connect(wxEVT_TIMER, wxTimerEventHandler(fftviewer_frFFTviewer::OnUpdateStats), NULL, this);
116
117 wxCommandEvent evt;
118 //show only A channel at startup
119 evt.SetInt(0);
120 OnChannelVisibilityChange(evt);
121 }
122
~fftviewer_frFFTviewer()123 fftviewer_frFFTviewer::~fftviewer_frFFTviewer()
124 {
125 if (mStreamRunning == true)
126 StopStreaming();
127 }
128
OnWindowFunctionChanged(wxCommandEvent & event)129 void fftviewer_frFFTviewer::OnWindowFunctionChanged( wxCommandEvent& event )
130 {
131 windowFunctionID.store(cmbWindowFunc->GetSelection());
132 }
133
OnbtnStartStop(wxCommandEvent & event)134 void fftviewer_frFFTviewer::OnbtnStartStop( wxCommandEvent& event )
135 {
136 if (threadProcessing.joinable() == false)
137 StartStreaming();
138 else
139 StopStreaming();
140 }
141
StartStreaming()142 void fftviewer_frFFTviewer::StartStreaming()
143 {
144 auto conn = ((LMS7_Device*)lmsControl)->GetConnection();
145 if (!conn || !conn->IsOpen())
146 {
147 wxMessageBox(_("FFTviewer: Connection not initialized"), _("ERROR"));
148 return;
149 }
150
151 txtNyquistFreqMHz->Disable();
152 cmbStreamType->Disable();
153 spinFFTsize->Disable();
154 chkEnSync->Disable();
155
156 stopProcessing.store(false);
157 updateGUI.store(true);
158
159 const int fftSize = spinFFTsize->GetValue();
160 fftFreqAxis.resize(fftSize);
161 double nyquistMHz;
162 txtNyquistFreqMHz->GetValue().ToDouble(&nyquistMHz);
163 const float step = 2*nyquistMHz / fftSize;
164 for (int i = 0; i < fftSize; ++i)
165 fftFreqAxis[i] = 1e6*(-nyquistMHz + (i+1)*step);
166 timeXAxis.resize(fftSize);
167 for (int i = 0; i < fftSize; ++i)
168 timeXAxis[i] = i;
169
170 if(chkCaptureToFile->GetValue() == true)
171 {
172 captureSamples.store(true);
173 wxFileDialog dlg(this, _("Save samples file"), "", "", "Text (*.txt)|*.txt", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
174 if (dlg.ShowModal() == wxID_CANCEL)
175 captureSamples.store(false);
176 else
177 captureFilename = dlg.GetPath().To8BitData();
178 }
179 else
180 captureSamples.store(false);
181 chkCaptureToFile->Disable();
182 spinCaptureCount->Disable();
183 cmbFmt->Disable();
184 chkEnTx->Disable();
185 lmsIndex = cmbStreamType->GetSelection()/2;
186 if (mStreamRunning.load() == true)
187 return;
188 switch (cmbStreamType->GetSelection()%2)
189 {
190 case 0: //SISO
191 if (cmbChannelVisibility->GetSelection() > 1)
192 cmbChannelVisibility->SetSelection(0);
193 cmbChannelVisibility->Disable();
194 threadProcessing = std::thread(StreamingLoop, this, fftSize, 1, 0);
195 break;
196 case 1: //MIMO
197 threadProcessing = std::thread(StreamingLoop, this, fftSize, 2, 0);
198 break;
199 }
200
201 btnStartStop->SetLabel(_("STOP"));
202 mGUIupdater->Start(500);
203 }
204
StopStreaming()205 void fftviewer_frFFTviewer::StopStreaming()
206 {
207 txtNyquistFreqMHz->Enable();
208 mGUIupdater->Stop();
209 if (mStreamRunning.load() == false)
210 return;
211 stopProcessing.store(true);
212 threadProcessing.join();
213 btnStartStop->SetLabel(_("START"));
214 cmbStreamType->Enable();
215 spinFFTsize->Enable();
216 chkCaptureToFile->Enable();
217 spinCaptureCount->Enable();
218 chkEnSync->Enable();
219 cmbFmt->Enable();
220 cmbChannelVisibility->Enable();
221 chkEnTx->Enable();
222 }
223
OnUpdateStats(wxTimerEvent & event)224 void fftviewer_frFFTviewer::OnUpdateStats(wxTimerEvent& event)
225 {
226 if (mStreamRunning.load() == false)
227 return;
228 if(rxStreams[0].handle != 0)
229 {
230 lms_stream_status_t rxStats;
231 LMS_GetStreamStatus(&rxStreams[0],&rxStats);
232 float RxFilled = 100*(float)rxStats.fifoFilledCount/rxStats.fifoSize;
233 gaugeRxBuffer->SetValue((int)RxFilled);
234 lblRxDataRate->SetLabel(printDataRate(rxStats.linkRate));
235 }
236 else
237 {
238 gaugeRxBuffer->SetValue(0);
239 lblRxDataRate->SetLabel(printDataRate(0));
240 }
241 if(txStreams[0].handle != 0)
242 {
243 lms_stream_status_t txStats;
244 LMS_GetStreamStatus(&txStreams[0],&txStats);
245 float TxFilled = 100*(float)txStats.fifoFilledCount/txStats.fifoSize;
246 gaugeTxBuffer->SetValue((int)TxFilled);
247 lblTxDataRate->SetLabel(printDataRate(txStats.linkRate));
248 }
249 else
250 {
251 gaugeTxBuffer->SetValue(0);
252 lblTxDataRate->SetLabel(printDataRate(0));
253 }
254 }
255
OnUpdatePlots(wxThreadEvent & event)256 void fftviewer_frFFTviewer::OnUpdatePlots(wxThreadEvent& event)
257 {
258 double dbOffset = cmbFmt->GetSelection() == 1 ? 93.319 : 69.2369;
259 if (mStreamRunning.load() == false)
260 return;
261
262 const int fftSize = streamData.fftBins[0].size();
263
264 if (chkEnPwr->GetValue())
265 {
266 float chPwr[2] = { 0, 0 };
267 double cFreq[2] = { 0, 0 };
268 txtCenterOffset1->GetValue().ToDouble(&cFreq[0]);
269 txtCenterOffset2->GetValue().ToDouble(&cFreq[1]);
270 double bw[2] = {1, 1};
271 txtBW1->GetValue().ToDouble(&bw[0]);
272 txtBW2->GetValue().ToDouble(&bw[1]);
273
274 for (int c = 0; c<2; ++c)
275 {
276 float f0 = (cFreq[c] - bw[c]/2) * 1e6;
277 float fn = (cFreq[c] + bw[c]/2) * 1e6;
278 float sum = 0;
279 int bins = 0;
280 const int lmsch = mFFTpanel->series[0]->visible ? 0 : 1;
281 for (int i = 0; i<fftSize; ++i)
282 if (f0 <= fftFreqAxis[i] && fftFreqAxis[i] <= fn)
283 {
284 sum += streamData.fftBins[lmsch][i];
285 ++bins;
286 }
287 chPwr[c] = sum;
288 }
289
290 float pwr1 = (chPwr[0] != 0 ? (10 * log10(chPwr[0])) - dbOffset : -300);
291 lblPower1->SetLabel(wxString::Format("%.3f", pwr1));
292 float pwr2 = (chPwr[1] != 0 ? (10 * log10(chPwr[1])) - dbOffset : -300);
293 lblPower2->SetLabel(wxString::Format("%.3f", pwr2));
294 lbldBc->SetLabel(wxString::Format("%.3f", pwr2-pwr1));
295 }
296
297 if (fftSize > 0)
298 {
299 if (chkFreezeTimeDomain->IsChecked() == false)
300 {
301 mTimeDomainPanel->series[0]->AssignValues(&timeXAxis[0], &streamData.samplesI[0][0], streamData.samplesI[0].size());
302 mTimeDomainPanel->series[1]->AssignValues(&timeXAxis[0], &streamData.samplesQ[0][0], streamData.samplesQ[0].size());
303 mTimeDomainPanel->series[2]->AssignValues(&timeXAxis[0], &streamData.samplesI[1][0], streamData.samplesI[1].size());
304 mTimeDomainPanel->series[3]->AssignValues(&timeXAxis[0], &streamData.samplesQ[1][0], streamData.samplesQ[1].size());
305 }
306 if (chkFreezeConstellation->IsChecked() == false)
307 {
308 mConstelationPanel->series[0]->AssignValues(&streamData.samplesI[0][0], &streamData.samplesQ[0][0], streamData.samplesQ[0].size());
309 mConstelationPanel->series[1]->AssignValues(&streamData.samplesI[1][0], &streamData.samplesQ[1][0], streamData.samplesQ[1].size());
310 }
311 if (chkFreezeFFT->IsChecked() == false)
312 {
313 for (int ch = 0; ch<2; ++ch)
314 {
315 for (int s = 0; s < fftSize; ++s)
316 {
317 streamData.fftBins[ch][s] = (streamData.fftBins[ch][s] != 0 ? (10 * log10(streamData.fftBins[ch][s])) - dbOffset : -300);
318 }
319 }
320 mFFTpanel->series[0]->AssignValues(&fftFreqAxis[0], &streamData.fftBins[0][0], fftSize);
321 mFFTpanel->series[1]->AssignValues(&fftFreqAxis[0], &streamData.fftBins[1][0], fftSize);
322 }
323 }
324
325 if (chkFreezeTimeDomain->IsChecked() == false)
326 {
327 mTimeDomainPanel->Refresh();
328 mTimeDomainPanel->Draw();
329 }
330
331 if (chkFreezeConstellation->IsChecked() == false)
332 {
333 mConstelationPanel->Refresh();
334 mConstelationPanel->Draw();
335 }
336
337 if (chkFreezeFFT->IsChecked() == false)
338 {
339 mFFTpanel->Refresh();
340 mFFTpanel->Draw();
341 enableFFT.store(true);
342 }
343 else enableFFT.store(false);
344
345 updateGUI.store(true);
346 }
347
StreamingLoop(fftviewer_frFFTviewer * pthis,const unsigned int fftSize,const int channelsCount,const uint32_t format)348 void fftviewer_frFFTviewer::StreamingLoop(fftviewer_frFFTviewer* pthis, const unsigned int fftSize, const int channelsCount, const uint32_t format)
349 {
350 const bool runTx = pthis->chkEnTx->GetValue();
351 const int fifoSize = fftSize*512;
352 int avgCount = pthis->spinAvgCount->GetValue();
353 int wndFunction = pthis->windowFunctionID.load();
354 bool fftEnabled = true;
355 int ch_offset = 0;
356 bool syncTx = pthis->chkEnSync->GetValue();
357 if (channelsCount == 1)
358 {
359 if (pthis->cmbChannelVisibility->GetSelection() == 1)
360 ch_offset = 1;
361 }
362 vector<float> wndCoef;
363 GenerateWindowCoefficients(wndFunction, fftSize, wndCoef, 1);
364
365 lime::complex16_t** buffers;
366
367 DataToGUI localDataResults;
368 localDataResults.nyquist_Hz = 7.68e6;
369 for (unsigned i = 0; i < cMaxChCount; i++)
370 {
371 localDataResults.samplesI[i].resize(fftSize, 0);
372 localDataResults.samplesQ[i].resize(fftSize, 0);
373 localDataResults.fftBins[i].resize(fftSize, 0);
374 pthis->streamData.samplesI[i].resize(fftSize);
375 pthis->streamData.samplesQ[i].resize(fftSize);
376 pthis->streamData.fftBins[i].resize(fftSize);
377 }
378 buffers = new lime::complex16_t*[channelsCount];
379 for (int i = 0; i < channelsCount; ++i)
380 buffers[i] = new complex16_t[fftSize];
381
382 vector<complex16_t> captureBuffer[cMaxChCount];
383 uint32_t samplesToCapture[cMaxChCount];
384 uint32_t samplesCaptured[cMaxChCount];
385 if(pthis->captureSamples.load() == true)
386 for(int ch=0; ch<channelsCount; ++ch)
387 {
388 samplesToCapture[ch] = pthis->spinCaptureCount->GetValue();
389 captureBuffer[ch].resize(samplesToCapture[ch]);
390 samplesCaptured[ch] = 0;
391 }
392
393 if (LMS_GetNumChannels(pthis->lmsControl, false)>2)
394 ch_offset += 2*pthis->lmsIndex;
395
396 auto fmt = pthis->cmbFmt->GetSelection() == 1 ? lms_stream_t::LMS_FMT_I16 : lms_stream_t::LMS_FMT_I12;
397 for(int i=0; i<channelsCount; ++i)
398 {
399 pthis->rxStreams[i].channel = i + ch_offset;
400 pthis->rxStreams[i].fifoSize = fifoSize;
401 pthis->rxStreams[i].isTx = false;
402 pthis->rxStreams[i].dataFmt = fmt;
403 pthis->rxStreams[i].throughputVsLatency = 0.8;
404 LMS_SetupStream(pthis->lmsControl, &pthis->rxStreams[i]);
405
406 pthis->txStreams[i].handle = 0;
407 pthis->txStreams[i].channel = i + ch_offset;
408 pthis->txStreams[i].fifoSize = fifoSize;
409 pthis->txStreams[i].isTx = true;
410 pthis->txStreams[i].dataFmt = fmt;
411 pthis->txStreams[i].throughputVsLatency = 0.8;
412 if(runTx)
413 LMS_SetupStream(pthis->lmsControl, &pthis->txStreams[i]);
414 }
415
416 kiss_fft_cfg m_fftCalcPlan = kiss_fft_alloc(fftSize, 0, 0, 0);
417 kiss_fft_cpx* m_fftCalcIn = new kiss_fft_cpx[fftSize];
418 kiss_fft_cpx* m_fftCalcOut = new kiss_fft_cpx[fftSize];
419
420 for(int i=0; i<channelsCount; ++i)
421 {
422 LMS_StartStream(&pthis->rxStreams[i]);
423 if(runTx)
424 LMS_StartStream(&pthis->txStreams[i]);
425 }
426
427 uint16_t regVal = 0;
428 if (LMS_ReadFPGAReg(pthis->lmsControl, 0x0008, ®Val) == 0)
429 {
430 wxCommandEvent* e = new wxCommandEvent(wxEVT_COMMAND_CHOICE_SELECTED);
431 e->SetInt((regVal&2) ? 0 : 1);
432 wxQueueEvent(pthis->cmbFmt, e);
433 }
434
435 pthis->mStreamRunning.store(true);
436 lms_stream_meta_t meta;
437 meta.waitForTimestamp = syncTx;
438 meta.flushPartialPacket = false;
439 int fftCounter = 0;
440
441 while (pthis->stopProcessing.load() == false)
442 {
443 do
444 {
445 uint32_t samplesPopped[cMaxChCount];
446 uint64_t ts[cMaxChCount];
447 for(int i=0; i<channelsCount; ++i)
448 {
449 samplesPopped[i] = LMS_RecvStream(&pthis->rxStreams[i], &buffers[i][0], fftSize, &meta, 1000);
450 ts[i] = meta.timestamp + fifoSize/4;
451 }
452
453 for(int i=0; runTx && i<channelsCount; ++i)
454 {
455 meta.timestamp = ts[i];
456 meta.waitForTimestamp = syncTx;
457 LMS_SendStream(&pthis->txStreams[i], &buffers[i][0], fftSize, &meta, 1000);
458 }
459
460 if(pthis->captureSamples.load())
461 {
462 for(int ch=0; ch<channelsCount; ++ch)
463 {
464 uint32_t samplesToCopy = min(samplesPopped[ch], samplesToCapture[ch]);
465 if(samplesToCopy <= 0)
466 break;
467 memcpy((captureBuffer[ch].data() + samplesCaptured[ch]), buffers[ch], samplesToCopy*sizeof(complex16_t));
468 samplesToCapture[ch] -= samplesToCopy;
469 samplesCaptured[ch] += samplesToCopy;
470 }
471 }
472
473 for (int ch = 0; ch < channelsCount; ++ch)
474 {
475 //take only first buffer for time domain display
476 //reset fftBins for accumulation
477 for (unsigned i = 0; fftCounter==0 && i < fftSize; ++i)
478 {
479 if (fftEnabled)
480 localDataResults.fftBins[ch][i] = 0;
481 localDataResults.samplesI[ch][i] = buffers[ch][i].i;
482 localDataResults.samplesQ[ch][i] = buffers[ch][i].q;
483 }
484 if (fftEnabled)
485 {
486 if (wndFunction == 0)
487 {
488 for (unsigned i = 0; i < fftSize; ++i)
489 {
490 m_fftCalcIn[i].r = buffers[ch][i].i;
491 m_fftCalcIn[i].i = buffers[ch][i].q;
492 }
493 }
494 else
495 {
496 for (unsigned i = 0; i < fftSize; ++i)
497 {
498 m_fftCalcIn[i].r = buffers[ch][i].i * wndCoef[i];
499 m_fftCalcIn[i].i = buffers[ch][i].q * wndCoef[i];
500 }
501 }
502
503 kiss_fft(m_fftCalcPlan, m_fftCalcIn, m_fftCalcOut);
504
505 int output_index = 0;
506 for (unsigned i = fftSize / 2 + 1; i < fftSize; ++i)
507 localDataResults.fftBins[ch][output_index++] += m_fftCalcOut[i].r * m_fftCalcOut[i].r + m_fftCalcOut[i].i * m_fftCalcOut[i].i;
508 for (unsigned i = 0; i < fftSize / 2 + 1; ++i)
509 localDataResults.fftBins[ch][output_index++] += m_fftCalcOut[i].r * m_fftCalcOut[i].r + m_fftCalcOut[i].i * m_fftCalcOut[i].i;
510 }
511 }
512 } while (++fftCounter < avgCount && pthis->stopProcessing.load() == false);
513
514 if (fftCounter >= avgCount && pthis->updateGUI.load() == true)
515 {
516 //shift fft
517 if (fftEnabled)
518 {
519 for(int ch=0; ch<channelsCount; ++ch)
520 {
521 for (unsigned s = 0; s < fftSize; ++s)
522 {
523 const float div = (float)fftCounter*fftSize*fftSize;
524 localDataResults.fftBins[ch][s] /= div;
525 }
526 }
527 }
528 if(pthis->stopProcessing.load() == false)
529 {
530 pthis->streamData = localDataResults;
531 wxThreadEvent* evt = new wxThreadEvent;
532 evt->SetEventObject(pthis);
533 pthis->updateGUI.store(false);
534 pthis->QueueEvent(evt);
535 }
536 fftCounter = 0;
537 fftEnabled = pthis->enableFFT.load();
538 avgCount = pthis->averageCount.load();
539 int wndFunctionSelection = pthis->windowFunctionID.load();
540 if(wndFunctionSelection != wndFunction)
541 {
542 wndFunction = wndFunctionSelection;
543 GenerateWindowCoefficients(wndFunction, fftSize, wndCoef, 1);
544 }
545 }
546 }
547
548 if(pthis->captureSamples.load() == true)
549 {
550 ofstream fout;
551 fout.open(pthis->captureFilename.c_str());
552 fout << "AI\tAQ";
553 if(channelsCount > 1)
554 fout << "\tBI\tBQ";
555 fout << endl;
556
557 int samplesCnt = captureBuffer[0].size();
558 for(int i=0; i<samplesCnt; ++i)
559 {
560 for(int ch=0; ch<channelsCount; ++ch)
561 {
562 fout << captureBuffer[ch][i].i << "\t" << captureBuffer[ch][i].q << "\t";
563 }
564 fout << endl;
565 }
566 fout.close();
567
568 string filename = pthis->captureFilename+".absdbfs";
569 fout.open(filename.c_str());
570 fout << "AI\tAQ";
571 if(channelsCount > 1)
572 fout << "\tBI\tBQ";
573 fout << endl;
574
575 for(int i=0; i<samplesCnt; ++i)
576 {
577 for(int ch=0; ch<channelsCount; ++ch)
578 {
579 fout
580 << (captureBuffer[ch][i].i == 0 ? -67 : 20*log10(abs(captureBuffer[ch][i].i)/2048))<< "\t"
581 << (captureBuffer[ch][i].q == 0 ? -67 : 20*log10(abs(captureBuffer[ch][i].q)/2048))<< "\t";
582 }
583 fout << endl;
584 }
585 fout.close();
586 }
587
588 kiss_fft_free(m_fftCalcPlan);
589 pthis->stopProcessing.store(true);
590 pthis->mStreamRunning.store(false);
591 for(int i=0; i<channelsCount; ++i)
592 {
593 if(runTx)
594 LMS_StopStream(&pthis->txStreams[i]);
595 LMS_StopStream(&pthis->rxStreams[i]);
596 }
597 for(int i=0; i<channelsCount; ++i)
598 {
599 if(runTx)
600 LMS_DestroyStream(pthis->lmsControl, &pthis->txStreams[i]);
601 LMS_DestroyStream(pthis->lmsControl, &pthis->rxStreams[i]);
602 }
603 for (int i = 0; i < channelsCount; ++i)
604 delete [] buffers[i];
605 delete [] buffers;
606 delete [] m_fftCalcIn;
607 delete [] m_fftCalcOut;
608 }
609
printDataRate(float dataRate)610 wxString fftviewer_frFFTviewer::printDataRate(float dataRate)
611 {
612 if (dataRate > 1000000)
613 return wxString::Format(_("%.3f MB/s"), dataRate / 1000000.0);
614 else if (dataRate > 1000)
615 return wxString::Format(_("%.3f KB/s"), dataRate / 1000.0);
616 else
617 return wxString::Format(_("%.0f B/s"), dataRate / 1000.0);
618 }
619
SetNyquistFrequency()620 void fftviewer_frFFTviewer::SetNyquistFrequency()
621 {
622 double freqHz;
623 LMS_GetSampleRate(lmsControl,LMS_CH_RX,cmbStreamType->GetSelection()/2*2,&freqHz,nullptr);
624 txtNyquistFreqMHz->SetValue(wxString::Format(_("%2.5f"), freqHz / 2e6));
625 mFFTpanel->SetInitialDisplayArea(-freqHz/2, freqHz/2, -115, 0);
626 }
627
OnStreamChange(wxCommandEvent & event)628 void fftviewer_frFFTviewer::OnStreamChange(wxCommandEvent& event)
629 {
630 SetNyquistFrequency();
631
632
633 int tmp = cmbChannelVisibility->GetSelection();
634 cmbChannelVisibility->Clear();
635 cmbChannelVisibility->Append(_T("A"));
636 cmbChannelVisibility->Append(_T("B"));
637 if (cmbStreamType->GetSelection()%2==1)
638 cmbChannelVisibility->Append(_T("A&B"));
639 else if (tmp > 1)
640 tmp = 0;
641 cmbChannelVisibility->SetSelection(tmp);
642 }
643
OnFmtChange(wxCommandEvent & event)644 void fftviewer_frFFTviewer::OnFmtChange(wxCommandEvent& event)
645 {
646 int val = event.GetInt();
647 int max = val == 1 ? 32800 : 2050;
648 if (val != cmbFmt->GetSelection())
649 cmbFmt->SetSelection(val);
650 mTimeDomainPanel->SetInitialDisplayArea(0, 1024, -max, max);
651 mConstelationPanel->SetInitialDisplayArea(-max, max, -max, max);
652 }
653
OnEnTx(wxCommandEvent & event)654 void fftviewer_frFFTviewer::OnEnTx(wxCommandEvent& event)
655 {
656 chkEnSync->Enable(event.GetInt());
657 }
658
OnEnPwr(wxCommandEvent & event)659 void fftviewer_frFFTviewer::OnEnPwr(wxCommandEvent& event)
660 {
661 bool en = event.GetInt();
662 txtCenterOffset1->Enable(en);
663 txtCenterOffset2->Enable(en);
664 txtBW1->Enable(en);
665 txtBW2->Enable(en);
666 }
667
668
OnChannelVisibilityChange(wxCommandEvent & event)669 void fftviewer_frFFTviewer::OnChannelVisibilityChange(wxCommandEvent& event)
670 {
671 bool visibilities[cMaxChCount];
672
673 if (cmbStreamType->GetSelection()%2)
674 {
675 switch (event.GetInt())
676 {
677 case 0:
678 visibilities[0] = true;
679 visibilities[1] = false;
680 break;
681 case 1:
682 visibilities[0] = false;
683 visibilities[1] = true;
684 break;
685 case 2:
686 visibilities[0] = true;
687 visibilities[1] = true;
688 break;
689 default:
690 visibilities[0] = false;
691 visibilities[1] = false;
692 break;
693 }
694 }
695 else
696 {
697 visibilities[0] = true;
698 visibilities[1] = false;
699 }
700 mTimeDomainPanel->series[0]->visible = visibilities[0];
701 mTimeDomainPanel->series[1]->visible = visibilities[0];
702 mTimeDomainPanel->series[2]->visible = visibilities[1];
703 mTimeDomainPanel->series[3]->visible = visibilities[1];
704 mConstelationPanel->series[0]->visible = visibilities[0];
705 mConstelationPanel->series[1]->visible = visibilities[1];
706 mFFTpanel->series[0]->visible = visibilities[0];
707 mFFTpanel->series[1]->visible = visibilities[1];
708 }
709
OnAvgChange(wxSpinEvent & event)710 void fftviewer_frFFTviewer::OnAvgChange(wxSpinEvent& event)
711 {
712 averageCount.store(spinAvgCount->GetValue());
713 }
714
OnAvgChangeEnter(wxCommandEvent & event)715 void fftviewer_frFFTviewer::OnAvgChangeEnter(wxCommandEvent& event)
716 {
717 averageCount.store(spinAvgCount->GetValue());
718 }
719
OnWindowFunctionChange(wxCommandEvent & event)720 void fftviewer_frFFTviewer::OnWindowFunctionChange(wxCommandEvent& event)
721 {
722 windowFunctionID.store(cmbWindowFunc->GetSelection());
723 }
724