Updates and improvements for low-bandwidth input usage

This commit is contained in:
Charles J. Cliffe 2015-12-29 20:52:49 -05:00
parent 2867f90977
commit 7d6a387a77
14 changed files with 393 additions and 225 deletions

View File

@ -1322,3 +1322,9 @@ void AppFrame::updateModemProperties(ModemArgInfoList args) {
modemPropertiesUpdated.store(true); modemPropertiesUpdated.store(true);
} }
void AppFrame::setMainWaterfallFFTSize(int fftSize) {
wxGetApp().getSpectrumProcessor()->setFFTSize(fftSize);
spectrumCanvas->setFFTSize(fftSize);
waterfallDataThread->getProcessor()->setFFTSize(fftSize);
waterfallCanvas->setFFTSize(fftSize);
}

View File

@ -69,6 +69,7 @@ public:
FFTVisualDataThread *getWaterfallDataThread(); FFTVisualDataThread *getWaterfallDataThread();
void updateModemProperties(ModemArgInfoList args); void updateModemProperties(ModemArgInfoList args);
void setMainWaterfallFFTSize(int fftSize);
private: private:
void OnMenu(wxCommandEvent& event); void OnMenu(wxCommandEvent& event);

View File

@ -441,6 +441,16 @@ void CubicSDR::setSampleRate(long long rate_in) {
sampleRate = rate_in; sampleRate = rate_in;
sdrThread->setSampleRate(sampleRate); sdrThread->setSampleRate(sampleRate);
setFrequency(frequency); setFrequency(frequency);
if (rate_in <= CHANNELIZER_RATE_MAX) {
appframe->setMainWaterfallFFTSize(1024);
appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false);
spectrumVisualThread->getProcessor()->setHideDC(false);
} else if (rate_in > CHANNELIZER_RATE_MAX) {
appframe->setMainWaterfallFFTSize(2048);
appframe->getWaterfallDataThread()->getProcessor()->setHideDC(true);
spectrumVisualThread->getProcessor()->setHideDC(true);
}
} }
void CubicSDR::setDevice(SDRDeviceInfo *dev) { void CubicSDR::setDevice(SDRDeviceInfo *dev) {

View File

@ -208,7 +208,7 @@ void AudioThread::enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs) {
std::cout << "Audio Device #" << i << " " << info.name << std::endl; std::cout << "Audio Device #" << i << " " << info.name << std::endl;
std::cout << "\tDefault Output? " << (info.isDefaultOutput ? "Yes" : "No") << std::endl; std::cout << "\tDefault Output? " << (info.isDefaultOutput ? "Yes" : "No") << std::endl;
std::cout << "\tDefault Input? " << (info.isDefaultOutput ? "Yes" : "No") << std::endl; std::cout << "\tDefault Input? " << (info.isDefaultInput ? "Yes" : "No") << std::endl;
std::cout << "\tInput channels: " << info.inputChannels << std::endl; std::cout << "\tInput channels: " << info.inputChannels << std::endl;
std::cout << "\tOutput channels: " << info.outputChannels << std::endl; std::cout << "\tOutput channels: " << info.outputChannels << std::endl;
std::cout << "\tDuplex channels: " << info.duplexChannels << std::endl; std::cout << "\tDuplex channels: " << info.duplexChannels << std::endl;

View File

@ -16,14 +16,6 @@ void WaterfallPanel::setup(int fft_size_in, int num_waterfall_lines_in) {
points.resize(fft_size); points.resize(fft_size);
} }
for (int i = 0; i < 2; i++) {
if (waterfall[i]) {
glDeleteTextures(1, &waterfall[i]);
waterfall[i] = 0;
}
waterfall_ofs[i] = waterfall_lines - 1;
}
texInitialized.store(false); texInitialized.store(false);
bufferInitialized.store(false); bufferInitialized.store(false);
} }
@ -65,7 +57,7 @@ void WaterfallPanel::step() {
return; return;
} }
if (points.size()) { if (points.size() && points.size() == fft_size) {
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
for (int i = 0, iMax = half_fft_size; i < iMax; i++) { for (int i = 0, iMax = half_fft_size; i < iMax; i++) {
float v = points[j * half_fft_size + i]; float v = points[j * half_fft_size + i];
@ -94,6 +86,15 @@ void WaterfallPanel::update() {
} }
if (!texInitialized.load()) { if (!texInitialized.load()) {
for (int i = 0; i < 2; i++) {
if (waterfall[i]) {
glDeleteTextures(1, &waterfall[i]);
waterfall[i] = 0;
}
waterfall_ofs[i] = waterfall_lines - 1;
}
glGenTextures(2, waterfall); glGenTextures(2, waterfall);
unsigned char *waterfall_tex; unsigned char *waterfall_tex;
@ -110,12 +111,13 @@ void WaterfallPanel::update() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, waterfall[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, half_fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, half_fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex);
} }
delete[] waterfall_tex; delete[] waterfall_tex;
refreshTheme();
texInitialized.store(true); texInitialized.store(true);
} }

View File

@ -18,6 +18,8 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last
desiredInputSize.store(0); desiredInputSize.store(0);
fft_average_rate = 0.65; fft_average_rate = 0.65;
scaleFactor.store(1.0); scaleFactor.store(1.0);
fftSizeChanged.store(false);
newFFTSize.store(0);
lastView = false; lastView = false;
} }
@ -83,24 +85,34 @@ void SpectrumVisualProcessor::setup(int fftSize_in) {
fftSize = fftSize_in; fftSize = fftSize_in;
fftSizeInternal = fftSize_in * SPECTRUM_VZM; fftSizeInternal = fftSize_in * SPECTRUM_VZM;
desiredInputSize.store(fftSizeInternal); lastDataSize = 0;
int memSize = sizeof(fftwf_complex) * fftSizeInternal;
if (fftwInput) { if (fftwInput) {
free(fftwInput); free(fftwInput);
} }
fftwInput = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSizeInternal); fftwInput = (fftwf_complex*) fftwf_malloc(memSize);
memset(fftwInput,0,memSize);
if (fftInData) { if (fftInData) {
free(fftInData); free(fftInData);
} }
fftInData = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSizeInternal); fftInData = (fftwf_complex*) fftwf_malloc(memSize);
memset(fftwInput,0,memSize);
if (fftLastData) { if (fftLastData) {
free(fftLastData); free(fftLastData);
} }
fftLastData = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSizeInternal); fftLastData = (fftwf_complex*) fftwf_malloc(memSize);
memset(fftwInput,0,memSize);
if (fftwOutput) { if (fftwOutput) {
free(fftwOutput); free(fftwOutput);
} }
fftwOutput = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSizeInternal); fftwOutput = (fftwf_complex*) fftwf_malloc(memSize);
memset(fftwInput,0,memSize);
if (fftw_plan) { if (fftw_plan) {
fftwf_destroy_plan(fftw_plan); fftwf_destroy_plan(fftw_plan);
} }
@ -108,6 +120,14 @@ void SpectrumVisualProcessor::setup(int fftSize_in) {
busy_run.unlock(); busy_run.unlock();
} }
void SpectrumVisualProcessor::setFFTSize(int fftSize_in) {
if (fftSize_in == fftSize) {
return;
}
newFFTSize = fftSize_in;
fftSizeChanged.store(true);
}
void SpectrumVisualProcessor::setHideDC(bool hideDC) { void SpectrumVisualProcessor::setHideDC(bool hideDC) {
this->hideDC.store(hideDC); this->hideDC.store(hideDC);
} }
@ -121,6 +141,11 @@ void SpectrumVisualProcessor::process() {
return; return;
} }
if (fftSizeChanged.load()) {
setup(newFFTSize);
fftSizeChanged.store(false);
}
DemodulatorThreadIQData *iqData; DemodulatorThreadIQData *iqData;
input->pop(iqData); input->pop(iqData);
@ -135,24 +160,11 @@ void SpectrumVisualProcessor::process() {
std::vector<liquid_float_complex> *data = &iqData->data; std::vector<liquid_float_complex> *data = &iqData->data;
if (data && data->size()) { if (data && data->size()) {
SpectrumVisualData *output = outputBuffers.getBuffer();
if (output->spectrum_points.size() < fftSize * 2) {
output->spectrum_points.resize(fftSize * 2);
}
unsigned int num_written; unsigned int num_written;
long resampleBw = iqData->sampleRate; long resampleBw = iqData->sampleRate;
bool newResampler = false; bool newResampler = false;
int bwDiff; int bwDiff;
// if (bandwidth > resampleBw) {
// iqData->decRefCount();
// iqData->busy_rw.unlock();
// busy_run.unlock();
// return;
// }
if (is_view.load()) { if (is_view.load()) {
if (!iqData->frequency || !iqData->sampleRate) { if (!iqData->frequency || !iqData->sampleRate) {
iqData->decRefCount(); iqData->decRefCount();
@ -161,7 +173,6 @@ void SpectrumVisualProcessor::process() {
return; return;
} }
// resamplerRatio = (double) (bandwidth) / (double) iqData->sampleRate;
while (resampleBw / SPECTRUM_VZM >= bandwidth) { while (resampleBw / SPECTRUM_VZM >= bandwidth) {
resampleBw /= SPECTRUM_VZM; resampleBw /= SPECTRUM_VZM;
} }
@ -219,7 +230,7 @@ void SpectrumVisualProcessor::process() {
nco_crcf_mix_block_down(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size); nco_crcf_mix_block_down(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size);
} }
} else { } else {
shiftBuffer.assign(iqData->data.begin(), iqData->data.end()); shiftBuffer.assign(iqData->data.begin(), iqData->data.begin()+desired_input_size);
} }
if (!resampler || resampleBw != lastBandwidth || lastInputBandwidth != iqData->sampleRate) { if (!resampler || resampleBw != lastBandwidth || lastInputBandwidth != iqData->sampleRate) {
@ -247,11 +258,8 @@ void SpectrumVisualProcessor::process() {
resampleBuffer.resize(out_size); resampleBuffer.resize(out_size);
} }
msresamp_crcf_execute(resampler, &shiftBuffer[0], desired_input_size, &resampleBuffer[0], &num_written); msresamp_crcf_execute(resampler, &shiftBuffer[0], desired_input_size, &resampleBuffer[0], &num_written);
resampleBuffer.resize(fftSizeInternal);
if (num_written < fftSizeInternal) { if (num_written < fftSizeInternal) {
for (int i = 0; i < num_written; i++) { for (int i = 0; i < num_written; i++) {
fftInData[i][0] = resampleBuffer[i].real; fftInData[i][0] = resampleBuffer[i].real;
@ -268,6 +276,8 @@ void SpectrumVisualProcessor::process() {
} }
} }
} else { } else {
this->desiredInputSize.store(fftSizeInternal);
num_written = data->size(); num_written = data->size();
if (data->size() < fftSizeInternal) { if (data->size() < fftSizeInternal) {
for (int i = 0, iMax = data->size(); i < iMax; i++) { for (int i = 0, iMax = data->size(); i < iMax; i++) {
@ -311,11 +321,22 @@ void SpectrumVisualProcessor::process() {
} }
if (execute) { if (execute) {
SpectrumVisualData *output = outputBuffers.getBuffer();
if (output->spectrum_points.size() != fftSize * 2) {
output->spectrum_points.resize(fftSize * 2);
}
fftwf_execute(fftw_plan); fftwf_execute(fftw_plan);
float fft_ceil = 0, fft_floor = 1; float fft_ceil = 0, fft_floor = 1;
if (fft_result.size() < fftSizeInternal) { if (fft_result.size() != fftSizeInternal) {
if (fft_result.capacity() < fftSizeInternal) {
fft_result.reserve(fftSizeInternal);
fft_result_ma.reserve(fftSizeInternal);
fft_result_maa.reserve(fftSizeInternal);
}
fft_result.resize(fftSizeInternal); fft_result.resize(fftSizeInternal);
fft_result_ma.resize(fftSizeInternal); fft_result_ma.resize(fftSizeInternal);
fft_result_maa.resize(fftSizeInternal); fft_result_maa.resize(fftSizeInternal);
@ -376,30 +397,31 @@ void SpectrumVisualProcessor::process() {
} }
for (int i = 0, iMax = fftSizeInternal; i < iMax; i++) { for (int i = 0, iMax = fftSizeInternal; i < iMax; i++) {
if (fft_result_maa[i] != fft_result_maa[i]) fft_result_maa[i] = fft_result[i];
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate;
if (fft_result_ma[i] != fft_result_ma[i]) fft_result_ma[i] = fft_result[i];
fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate; fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate;
if (fft_result_maa[i] > fft_ceil) { if (fft_result_maa[i] > fft_ceil || fft_ceil != fft_ceil) {
fft_ceil = fft_result_maa[i]; fft_ceil = fft_result_maa[i];
} }
if (fft_result_maa[i] < fft_floor) { if (fft_result_maa[i] < fft_floor || fft_floor != fft_floor) {
fft_floor = fft_result_maa[i]; fft_floor = fft_result_maa[i];
} }
} }
if (fft_ceil_ma != fft_ceil_ma) fft_ceil_ma = fft_ceil;
fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05;
if (fft_ceil_maa != fft_ceil_maa) fft_ceil_maa = fft_ceil;
fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05;
if (fft_floor_ma != fft_floor_ma) fft_floor_ma = fft_floor;
fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05;
if (fft_floor_maa != fft_floor_maa) fft_floor_maa = fft_floor;
fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05;
float sf = scaleFactor.load(); float sf = scaleFactor.load();
// for (int i = 0, iMax = fftSize; i < iMax; i++) {
// float v = (log10(fft_result_maa[i*SPECTRUM_VZM]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75)));
// output->spectrum_points[i * 2] = ((float) i / (float) iMax);
// output->spectrum_points[i * 2 + 1] = v*sf;
// }
double visualRatio = (double(bandwidth) / double(resampleBw)); double visualRatio = (double(bandwidth) / double(resampleBw));
double visualStart = (double(fftSizeInternal) / 2.0) - (double(fftSizeInternal) * (visualRatio / 2.0)); double visualStart = (double(fftSizeInternal) / 2.0) - (double(fftSizeInternal) * (visualRatio / 2.0));
double visualAccum = 0; double visualAccum = 0;
@ -408,13 +430,6 @@ void SpectrumVisualProcessor::process() {
for (int x = 0, xMax = output->spectrum_points.size() / 2; x < xMax; x++) { for (int x = 0, xMax = output->spectrum_points.size() / 2; x < xMax; x++) {
visualAccum += visualRatio * double(SPECTRUM_VZM); visualAccum += visualRatio * double(SPECTRUM_VZM);
// while (visualAccum >= 1.0) {
// visualAccum -= 1.0;
// i++;
// }
// acc = (log10(fft_result_maa[visualStart+i]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75)));
// output->spectrum_points[x * 2] = (float(x) / float(xMax));
// output->spectrum_points[x * 2 + 1] = acc*sf;
while (visualAccum >= 1.0) { while (visualAccum >= 1.0) {
int idx = round(visualStart+i); int idx = round(visualStart+i);
@ -429,8 +444,9 @@ void SpectrumVisualProcessor::process() {
visualAccum -= 1.0; visualAccum -= 1.0;
i++; i++;
} }
output->spectrum_points[x * 2] = ((float) x / (float) xMax);
if (accCount) { if (accCount) {
output->spectrum_points[x * 2] = ((float) x / (float) xMax);
output->spectrum_points[x * 2 + 1] = ((log10((acc/accCount)+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))))*sf; output->spectrum_points[x * 2 + 1] = ((log10((acc/accCount)+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))))*sf;
acc = 0.0; acc = 0.0;
accCount = 0.0; accCount = 0.0;
@ -475,11 +491,12 @@ void SpectrumVisualProcessor::process() {
output->fft_ceiling = fft_ceil_maa/sf; output->fft_ceiling = fft_ceil_maa/sf;
output->fft_floor = fft_floor_maa; output->fft_floor = fft_floor_maa;
}
output->centerFreq = centerFreq; output->centerFreq = centerFreq;
output->bandwidth = bandwidth; output->bandwidth = bandwidth;
distribute(output);
distribute(output);
}
} }
iqData->decRefCount(); iqData->decRefCount();

View File

@ -38,6 +38,7 @@ public:
int getDesiredInputSize(); int getDesiredInputSize();
void setup(int fftSize); void setup(int fftSize);
void setFFTSize(int fftSize);
void setHideDC(bool hideDC); void setHideDC(bool hideDC);
void setScaleFactor(float sf); void setScaleFactor(float sf);
@ -48,7 +49,7 @@ protected:
ReBuffer<SpectrumVisualData> outputBuffers; ReBuffer<SpectrumVisualData> outputBuffers;
std::atomic_bool is_view; std::atomic_bool is_view;
std::atomic_int fftSize; std::atomic_int fftSize, newFFTSize;
std::atomic_int fftSizeInternal; std::atomic_int fftSizeInternal;
std::atomic_llong centerFreq; std::atomic_llong centerFreq;
std::atomic_long bandwidth; std::atomic_long bandwidth;
@ -82,4 +83,5 @@ private:
std::mutex busy_run; std::mutex busy_run;
std::atomic_bool hideDC; std::atomic_bool hideDC;
std::atomic<float> scaleFactor; std::atomic<float> scaleFactor;
std::atomic_bool fftSizeChanged;
}; };

View File

@ -157,185 +157,46 @@ void SDRPostThread::run() {
iqDataInQueue->set_max_num_items(0); iqDataInQueue->set_max_num_items(0);
std::vector<liquid_float_complex> dcBuf;
while (!terminated) { while (!terminated) {
SDRThreadIQData *data_in; SDRThreadIQData *data_in;
iqDataInQueue->pop(data_in); iqDataInQueue->pop(data_in);
// std::lock_guard < std::mutex > lock(data_in->m_mutex); // std::lock_guard < std::mutex > lock(data_in->m_mutex);
if (data_in && data_in->data.size() && data_in->numChannels) { busy_demod.lock();
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) {
numChannels = data_in->numChannels; if (data_in && data_in->data.size()) {
sampleRate = data_in->sampleRate; if(data_in->numChannels > 1) {
initPFBChannelizer(); runPFBCH(data_in);
doRefresh.store(true); } else {
runSingleCH(data_in);
} }
int dataSize = data_in->data.size();
int outSize = data_in->data.size();
if (outSize > dataOut.capacity()) {
dataOut.reserve(outSize);
}
if (outSize != dataOut.size()) {
dataOut.resize(outSize);
}
int activeVisChannel = -1;
if (iqDataOutQueue != NULL && !iqDataOutQueue->full() && activeVisChannel < 0) {
DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer();
bool doVis = false;
if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
doVis = true;
}
iqDataOut->setRefCount(1 + (doVis?1:0));
iqDataOut->frequency = data_in->frequency;
iqDataOut->sampleRate = data_in->sampleRate;
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
iqDataOutQueue->push(iqDataOut);
if (doVis) {
iqVisualQueue->push(iqDataOut);
}
}
busy_demod.lock();
if (frequency != data_in->frequency) {
frequency = data_in->frequency;
doRefresh.store(true);
}
if (doRefresh.load()) {
updateActiveDemodulators();
updateChannels();
doRefresh.store(false);
}
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
int activeDemodChannel = -1;
// Find active demodulators
if (nRunDemods || (activeVisChannel >= 0)) {
// channelize data
// firpfbch output rate is (input rate / channels)
for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels) {
firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]);
}
for (int i = 0, iMax = numChannels+1; i < iMax; i++) {
demodChannelActive[i] = 0;
}
// Find nearest channel for each demodulator
for (int i = 0; i < nRunDemods; i++) {
DemodulatorInstance *demod = runDemods[i];
demodChannel[i] = getChannelAt(demod->getFrequency());
if (demod == activeDemod) {
activeDemodChannel = demodChannel[i];
}
}
for (int i = 0; i < nRunDemods; i++) {
// cache channel usage refcounts
if (demodChannel[i] >= 0) {
demodChannelActive[demodChannel[i]]++;
}
}
// Run channels
for (int i = 0; i < numChannels+1; i++) {
int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full())?1:0;
if (!doDemodVis && demodChannelActive[i] == 0) {
continue;
}
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
demodDataOut->setRefCount(demodChannelActive[i] + doDemodVis);
demodDataOut->frequency = chanCenters[i];
demodDataOut->sampleRate = chanBw;
// Calculate channel buffer size
int chanDataSize = (outSize/numChannels);
if (demodDataOut->data.size() != chanDataSize) {
if (demodDataOut->data.capacity() < chanDataSize) {
demodDataOut->data.reserve(chanDataSize);
}
demodDataOut->data.resize(chanDataSize);
}
int idx = i;
// Extra channel wraps lower side band of lowest channel
// to fix frequency gap on upper side of spectrum
if (i == numChannels) {
idx = (numChannels/2);
}
// prepare channel data buffer
if (i == 0) { // Channel 0 requires DC correction
if (dcBuf.size() != chanDataSize) {
dcBuf.resize(chanDataSize);
}
for (int j = 0; j < chanDataSize; j++) {
dcBuf[j] = dataOut[idx];
idx += numChannels;
}
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]);
} else {
for (int j = 0; j < chanDataSize; j++) {
demodDataOut->data[j] = dataOut[idx];
idx += numChannels;
}
}
if (doDemodVis) {
iqActiveDemodVisualQueue->push(demodDataOut);
}
for (int j = 0; j < nRunDemods; j++) {
if (demodChannel[j] == i) {
DemodulatorInstance *demod = runDemods[j];
demod->getIQInputDataPipe()->push(demodDataOut);
}
}
}
}
bool doUpdate = false;
for (int j = 0; j < nRunDemods; j++) {
DemodulatorInstance *demod = runDemods[j];
if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) {
doUpdate = true;
}
}
if (doUpdate) {
updateActiveDemodulators();
}
busy_demod.unlock();
} }
data_in->decRefCount();
}
// buffers.purge(); data_in->decRefCount();
bool doUpdate = false;
for (int j = 0; j < nRunDemods; j++) {
DemodulatorInstance *demod = runDemods[j];
if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) {
doUpdate = true;
}
}
if (doUpdate) {
updateActiveDemodulators();
}
busy_demod.unlock();
}
if (iqVisualQueue && !iqVisualQueue->empty()) { if (iqVisualQueue && !iqVisualQueue->empty()) {
DemodulatorThreadIQData *visualDataDummy; DemodulatorThreadIQData *visualDataDummy;
iqVisualQueue->pop(visualDataDummy); iqVisualQueue->pop(visualDataDummy);
} }
// visualDataBuffers.purge(); // buffers.purge();
// visualDataBuffers.purge();
std::cout << "SDR post-processing thread done." << std::endl; std::cout << "SDR post-processing thread done." << std::endl;
} }
@ -345,3 +206,242 @@ void SDRPostThread::terminate() {
SDRThreadIQData *dummy = new SDRThreadIQData; SDRThreadIQData *dummy = new SDRThreadIQData;
iqDataInQueue->push(dummy); iqDataInQueue->push(dummy);
} }
void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) {
if (sampleRate != data_in->sampleRate) {
sampleRate = data_in->sampleRate;
doRefresh.store(true);
}
int dataSize = data_in->data.size();
int outSize = data_in->data.size();
if (outSize > dataOut.capacity()) {
dataOut.reserve(outSize);
}
if (outSize != dataOut.size()) {
dataOut.resize(outSize);
}
if (frequency != data_in->frequency) {
frequency = data_in->frequency;
doRefresh.store(true);
}
if (doRefresh.load()) {
updateActiveDemodulators();
doRefresh.store(false);
}
// if (iqDataOutQueue != NULL && !iqDataOutQueue->full()) {
// DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer();
//
// bool doVis = false;
//
// if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
// doVis = true;
// }
//
// iqDataOut->setRefCount(1 + (doVis?1:0));
//
// iqDataOut->frequency = data_in->frequency;
// iqDataOut->sampleRate = data_in->sampleRate;
// iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
//
// iqDataOutQueue->push(iqDataOut);
// if (doVis) {
// iqVisualQueue->push(iqDataOut);
// }
// }
int refCount = nRunDemods;
bool doIQDataOut = (iqDataOutQueue != NULL && !iqDataOutQueue->full());
bool doDemodVisOut = (nRunDemods && iqActiveDemodVisualQueue != NULL && !iqActiveDemodVisualQueue->full());
bool doVisOut = (iqVisualQueue != NULL && !iqVisualQueue->full());
if (doIQDataOut) {
refCount++;
}
if (doDemodVisOut) {
refCount++;
}
if (doVisOut) {
refCount++;
}
if (refCount) {
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
demodDataOut->setRefCount(refCount);
demodDataOut->frequency = frequency;
demodDataOut->sampleRate = sampleRate;
if (demodDataOut->data.size() != dataSize) {
if (demodDataOut->data.capacity() < dataSize) {
demodDataOut->data.reserve(dataSize);
}
demodDataOut->data.resize(dataSize);
}
iirfilt_crcf_execute_block(dcFilter, &data_in->data[0], dataSize, &demodDataOut->data[0]);
if (doDemodVisOut) {
iqActiveDemodVisualQueue->push(demodDataOut);
}
if (doIQDataOut) {
iqDataOutQueue->push(demodDataOut);
}
if (doVisOut) {
iqVisualQueue->push(demodDataOut);
}
for (int i = 0; i < nRunDemods; i++) {
runDemods[i]->getIQInputDataPipe()->push(demodDataOut);
}
}
}
void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) {
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) {
numChannels = data_in->numChannels;
sampleRate = data_in->sampleRate;
initPFBChannelizer();
doRefresh.store(true);
}
int dataSize = data_in->data.size();
int outSize = data_in->data.size();
if (outSize > dataOut.capacity()) {
dataOut.reserve(outSize);
}
if (outSize != dataOut.size()) {
dataOut.resize(outSize);
}
if (iqDataOutQueue != NULL && !iqDataOutQueue->full()) {
DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer();
bool doVis = false;
if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
doVis = true;
}
iqDataOut->setRefCount(1 + (doVis?1:0));
iqDataOut->frequency = data_in->frequency;
iqDataOut->sampleRate = data_in->sampleRate;
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
iqDataOutQueue->push(iqDataOut);
if (doVis) {
iqVisualQueue->push(iqDataOut);
}
}
if (frequency != data_in->frequency) {
frequency = data_in->frequency;
doRefresh.store(true);
}
if (doRefresh.load()) {
updateActiveDemodulators();
updateChannels();
doRefresh.store(false);
}
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
int activeDemodChannel = -1;
// Find active demodulators
if (nRunDemods) {
// channelize data
// firpfbch output rate is (input rate / channels)
for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels) {
firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]);
}
for (int i = 0, iMax = numChannels+1; i < iMax; i++) {
demodChannelActive[i] = 0;
}
// Find nearest channel for each demodulator
for (int i = 0; i < nRunDemods; i++) {
DemodulatorInstance *demod = runDemods[i];
demodChannel[i] = getChannelAt(demod->getFrequency());
if (demod == activeDemod) {
activeDemodChannel = demodChannel[i];
}
}
for (int i = 0; i < nRunDemods; i++) {
// cache channel usage refcounts
if (demodChannel[i] >= 0) {
demodChannelActive[demodChannel[i]]++;
}
}
// Run channels
for (int i = 0; i < numChannels+1; i++) {
int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full())?1:0;
if (!doDemodVis && demodChannelActive[i] == 0) {
continue;
}
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
demodDataOut->setRefCount(demodChannelActive[i] + doDemodVis);
demodDataOut->frequency = chanCenters[i];
demodDataOut->sampleRate = chanBw;
// Calculate channel buffer size
int chanDataSize = (outSize/numChannels);
if (demodDataOut->data.size() != chanDataSize) {
if (demodDataOut->data.capacity() < chanDataSize) {
demodDataOut->data.reserve(chanDataSize);
}
demodDataOut->data.resize(chanDataSize);
}
int idx = i;
// Extra channel wraps lower side band of lowest channel
// to fix frequency gap on upper side of spectrum
if (i == numChannels) {
idx = (numChannels/2);
}
// prepare channel data buffer
if (i == 0) { // Channel 0 requires DC correction
if (dcBuf.size() != chanDataSize) {
dcBuf.resize(chanDataSize);
}
for (int j = 0; j < chanDataSize; j++) {
dcBuf[j] = dataOut[idx];
idx += numChannels;
}
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]);
} else {
for (int j = 0; j < chanDataSize; j++) {
demodDataOut->data[j] = dataOut[idx];
idx += numChannels;
}
}
if (doDemodVis) {
iqActiveDemodVisualQueue->push(demodDataOut);
}
for (int j = 0; j < nRunDemods; j++) {
if (demodChannel[j] == i) {
DemodulatorInstance *demod = runDemods[j];
demod->getIQInputDataPipe()->push(demodDataOut);
}
}
}
}
}

View File

@ -18,6 +18,8 @@ public:
void run(); void run();
void terminate(); void terminate();
void runSingleCH(SDRThreadIQData *data_in);
void runPFBCH(SDRThreadIQData *data_in);
void setIQVisualRange(long long frequency, int bandwidth); void setIQVisualRange(long long frequency, int bandwidth);
protected: protected:
@ -54,4 +56,5 @@ private:
long long frequency; long long frequency;
firpfbch_crcf channelizer; firpfbch_crcf channelizer;
iirfilt_crcf dcFilter; iirfilt_crcf dcFilter;
std::vector<liquid_float_complex> dcBuf;
}; };

View File

@ -333,6 +333,10 @@ int SDRThread::getOptimalElementCount(long long sampleRate, int fps) {
} }
int SDRThread::getOptimalChannelCount(long long sampleRate) { int SDRThread::getOptimalChannelCount(long long sampleRate) {
if (sampleRate <= CHANNELIZER_RATE_MAX) {
return 1;
}
int optimal_rate = CHANNELIZER_RATE_MAX; int optimal_rate = CHANNELIZER_RATE_MAX;
int optimal_count = int(ceil(double(sampleRate)/double(optimal_rate))); int optimal_count = int(ceil(double(sampleRate)/double(optimal_rate)));

View File

@ -169,6 +169,9 @@ void SpectrumCanvas::setScaleFactorEnabled(bool en) {
scaleFactorEnabled = en; scaleFactorEnabled = en;
} }
void SpectrumCanvas::setFFTSize(int fftSize) {
spectrumPanel.setFFTSize(fftSize);
}
void SpectrumCanvas::updateScaleFactor(float factor) { void SpectrumCanvas::updateScaleFactor(float factor) {
SpectrumVisualProcessor *sp = wxGetApp().getSpectrumProcessor(); SpectrumVisualProcessor *sp = wxGetApp().getSpectrumProcessor();

View File

@ -26,6 +26,7 @@ public:
void disableView(); void disableView();
void setScaleFactorEnabled(bool en); void setScaleFactorEnabled(bool en);
void setFFTSize(int fftSize);
SpectrumVisualDataQueue *getVisualDataQueue(); SpectrumVisualDataQueue *getVisualDataQueue();

View File

@ -33,7 +33,7 @@ EVT_MOUSEWHEEL(WaterfallCanvas::OnMouseWheelMoved)
wxEND_EVENT_TABLE() wxEND_EVENT_TABLE()
WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
InteractiveCanvas(parent, attribList), dragState(WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), waterfall_lines(0), InteractiveCanvas(parent, attribList), dragState(WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), new_fft_size(0), waterfall_lines(0),
dragOfs(0), mouseZoom(1), zoom(1), freqMoving(false), freqMove(0.0), hoverAlpha(1.0) { dragOfs(0), mouseZoom(1), zoom(1), freqMoving(false), freqMove(0.0), hoverAlpha(1.0) {
glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this)); glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
@ -43,6 +43,7 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
SetCursor(wxCURSOR_CROSS); SetCursor(wxCURSOR_CROSS);
scaleMove = 0; scaleMove = 0;
minBandwidth = 30000; minBandwidth = 30000;
fft_size_changed.store(false);
} }
WaterfallCanvas::~WaterfallCanvas() { WaterfallCanvas::~WaterfallCanvas() {
@ -59,6 +60,14 @@ void WaterfallCanvas::setup(int fft_size_in, int waterfall_lines_in) {
gTimer.start(); gTimer.start();
} }
void WaterfallCanvas::setFFTSize(int fft_size_in) {
if (fft_size_in == fft_size) {
return;
}
new_fft_size = fft_size_in;
fft_size_changed.store(true);
}
WaterfallCanvas::DragState WaterfallCanvas::getDragState() { WaterfallCanvas::DragState WaterfallCanvas::getDragState() {
return dragState; return dragState;
} }
@ -88,7 +97,9 @@ void WaterfallCanvas::processInputQueue() {
visualDataQueue.pop(vData); visualDataQueue.pop(vData);
if (vData) { if (vData) {
waterfallPanel.setPoints(vData->spectrum_points); if (vData->spectrum_points.size() == fft_size * 2) {
waterfallPanel.setPoints(vData->spectrum_points);
}
waterfallPanel.step(); waterfallPanel.step();
vData->decRefCount(); vData->decRefCount();
updated = true; updated = true;
@ -236,6 +247,12 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
initGLExtensions(); initGLExtensions();
glViewport(0, 0, ClientSize.x, ClientSize.y); glViewport(0, 0, ClientSize.x, ClientSize.y);
if (fft_size_changed.load()) {
fft_size = new_fft_size;
waterfallPanel.setup(fft_size, waterfall_lines);
fft_size_changed.store(false);
}
glContext->BeginDraw(0,0,0); glContext->BeginDraw(0,0,0);
waterfallPanel.calcTransform(CubicVR::mat4::identity()); waterfallPanel.calcTransform(CubicVR::mat4::identity());

View File

@ -20,6 +20,7 @@ public:
WaterfallCanvas(wxWindow *parent, int *attribList = NULL); WaterfallCanvas(wxWindow *parent, int *attribList = NULL);
void setup(int fft_size_in, int waterfall_lines_in); void setup(int fft_size_in, int waterfall_lines_in);
void setFFTSize(int fft_size_in);
~WaterfallCanvas(); ~WaterfallCanvas();
DragState getDragState(); DragState getDragState();
@ -59,7 +60,7 @@ private:
DragState dragState; DragState dragState;
DragState nextDragState; DragState nextDragState;
int fft_size; int fft_size, new_fft_size;
int waterfall_lines; int waterfall_lines;
int dragOfs; int dragOfs;
@ -77,6 +78,7 @@ private:
bool preBuf; bool preBuf;
std::mutex tex_update; std::mutex tex_update;
int minBandwidth; int minBandwidth;
std::atomic_bool fft_size_changed;
// event table // event table
wxDECLARE_EVENT_TABLE(); wxDECLARE_EVENT_TABLE();
}; };