SDRPlusPlus/src/main_window.cpp

382 lines
12 KiB
C++
Raw Normal View History

2020-06-10 04:13:56 +02:00
#include <main_window.h>
std::thread worker;
std::mutex fft_mtx;
ImGui::WaterFall wtf;
2020-07-19 15:59:44 +02:00
FrequencySelect fSel;
2020-06-10 18:52:07 +02:00
fftwf_complex *fft_in, *fft_out;
fftwf_plan p;
2020-06-15 15:53:45 +02:00
float* tempData;
2020-07-19 21:26:37 +02:00
float* uiGains;
char buf[1024];
2020-06-10 18:52:07 +02:00
2020-06-15 15:53:45 +02:00
int fftSize = 8192 * 8;
2020-06-10 04:13:56 +02:00
2020-06-22 16:45:57 +02:00
io::SoapyWrapper soapy;
2020-06-15 15:53:45 +02:00
SignalPath sigPath;
std::vector<float> _data;
std::vector<float> fftTaps;
2020-06-22 16:45:57 +02:00
void fftHandler(dsp::complex_t* samples) {
2020-06-15 15:53:45 +02:00
fftwf_execute(p);
int half = fftSize / 2;
2020-06-10 04:13:56 +02:00
2020-06-15 15:53:45 +02:00
for (int i = 0; i < half; i++) {
_data.push_back(log10(std::abs(std::complex<float>(fft_out[half + i][0], fft_out[half + i][1])) / (float)fftSize) * 10.0f);
}
for (int i = 0; i < half; i++) {
_data.push_back(log10(std::abs(std::complex<float>(fft_out[i][0], fft_out[i][1])) / (float)fftSize) * 10.0f);
}
2020-06-10 04:13:56 +02:00
2020-06-15 15:53:45 +02:00
for (int i = 5; i < fftSize; i++) {
_data[i] = (_data[i - 4] + _data[i - 3] + _data[i - 2] + _data[i - 1] + _data[i]) / 5.0f;
}
2020-06-10 04:13:56 +02:00
2020-06-15 15:53:45 +02:00
wtf.pushFFT(_data, fftSize);
_data.clear();
}
2020-06-10 04:13:56 +02:00
2020-08-11 18:33:42 +02:00
dsp::NullSink sink;
2020-06-15 15:53:45 +02:00
void windowInit() {
2020-07-09 16:02:58 +02:00
int sampleRate = 8000000;
2020-07-11 21:15:10 +02:00
wtf.setBandwidth(sampleRate);
wtf.setCenterFrequency(90500000);
2020-07-19 15:59:44 +02:00
fSel.init();
fSel.setFrequency(90500000);
2020-06-15 15:53:45 +02:00
2020-07-20 15:26:59 +02:00
fft_in = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize);
fft_out = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize);
2020-06-15 15:53:45 +02:00
p = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE);
2020-06-10 04:13:56 +02:00
2020-06-22 16:45:57 +02:00
sigPath.init(sampleRate, 20, fftSize, &soapy.output, (dsp::complex_t*)fft_in, fftHandler);
2020-06-15 15:53:45 +02:00
sigPath.start();
2020-07-19 21:26:37 +02:00
2020-08-11 18:33:42 +02:00
vfoman::init(&wtf, &sigPath);
2020-07-19 21:26:37 +02:00
uiGains = new float[1];
2020-08-12 16:43:44 +02:00
spdlog::info("Loading modules");
mod::initAPI(&wtf);
mod::loadFromList("module_list.json");
2020-06-10 04:13:56 +02:00
}
2020-08-04 21:34:56 +02:00
watcher<int> devId(0, true);
watcher<int> srId(0, true);
2020-08-05 21:13:53 +02:00
watcher<int> bandplanId(0, true);
2020-08-04 21:34:56 +02:00
watcher<long> freq(90500000L);
2020-07-19 21:26:37 +02:00
int demod = 1;
2020-08-04 21:34:56 +02:00
watcher<float> vfoFreq(92000000.0f);
2020-08-12 16:43:44 +02:00
float dummyVolume = 1.0f;
float* volume = &dummyVolume;
2020-07-11 21:15:10 +02:00
float fftMin = -70.0f;
float fftMax = 0.0f;
2020-08-04 21:34:56 +02:00
watcher<float> offset(0.0f, true);
watcher<float> bw(8000000.0f, true);
2020-07-19 15:59:44 +02:00
int sampleRate = 1000000;
bool playing = false;
2020-08-04 21:34:56 +02:00
watcher<bool> dcbias(false, false);
2020-08-05 21:13:53 +02:00
watcher<bool> bandPlanEnabled(true, false);
2020-07-19 18:23:00 +02:00
2020-07-19 15:59:44 +02:00
void setVFO(float freq) {
2020-08-10 02:30:25 +02:00
ImGui::WaterfallVFO* vfo = wtf.vfos[wtf.selectedVFO];
float currentOff = vfo->centerOffset;
float currentTune = wtf.getCenterFrequency() + vfo->generalOffset;
2020-07-19 15:59:44 +02:00
float delta = freq - currentTune;
float newVFO = currentOff + delta;
2020-08-10 02:30:25 +02:00
float vfoBW = vfo->bandwidth;
2020-07-19 15:59:44 +02:00
float vfoBottom = newVFO - (vfoBW / 2.0f);
float vfoTop = newVFO + (vfoBW / 2.0f);
float view = wtf.getViewOffset();
float viewBW = wtf.getViewBandwidth();
float viewBottom = view - (viewBW / 2.0f);
float viewTop = view + (viewBW / 2.0f);
float wholeFreq = wtf.getCenterFrequency();
float BW = wtf.getBandwidth();
float bottom = -(BW / 2.0f);
float top = (BW / 2.0f);
// VFO still fints in the view
if (vfoBottom > viewBottom && vfoTop < viewTop) {
2020-08-11 18:33:42 +02:00
vfoman::setCenterOffset(wtf.selectedVFO, newVFO);
2020-07-19 15:59:44 +02:00
return;
}
// VFO too low for current SDR tuning
if (vfoBottom < bottom) {
wtf.setViewOffset((BW / 2.0f) - (viewBW / 2.0f));
float newVFOOffset = (BW / 2.0f) - (vfoBW / 2.0f) - (viewBW / 10.0f);
2020-08-11 18:33:42 +02:00
vfoman::setCenterOffset(wtf.selectedVFO, newVFOOffset);
2020-07-19 15:59:44 +02:00
wtf.setCenterFrequency(freq - newVFOOffset);
soapy.setFrequency(freq - newVFOOffset);
return;
}
// VFO too high for current SDR tuning
if (vfoTop > top) {
wtf.setViewOffset((viewBW / 2.0f) - (BW / 2.0f));
float newVFOOffset = (vfoBW / 2.0f) - (BW / 2.0f) + (viewBW / 10.0f);
2020-08-11 18:33:42 +02:00
vfoman::setCenterOffset(wtf.selectedVFO, newVFOOffset);
2020-07-19 15:59:44 +02:00
wtf.setCenterFrequency(freq - newVFOOffset);
soapy.setFrequency(freq - newVFOOffset);
return;
}
// VFO is still without the SDR's bandwidth
if (delta < 0) {
float newViewOff = vfoTop - (viewBW / 2.0f) + (viewBW / 10.0f);
float newViewBottom = newViewOff - (viewBW / 2.0f);
float newViewTop = newViewOff + (viewBW / 2.0f);
if (newViewBottom > bottom) {
wtf.setViewOffset(newViewOff);
2020-08-11 18:33:42 +02:00
vfoman::setCenterOffset(wtf.selectedVFO, newVFO);
2020-07-19 15:59:44 +02:00
return;
}
wtf.setViewOffset((BW / 2.0f) - (viewBW / 2.0f));
float newVFOOffset = (BW / 2.0f) - (vfoBW / 2.0f) - (viewBW / 10.0f);
2020-08-11 18:33:42 +02:00
vfoman::setCenterOffset(wtf.selectedVFO, newVFOOffset);
2020-07-19 15:59:44 +02:00
wtf.setCenterFrequency(freq - newVFOOffset);
soapy.setFrequency(freq - newVFOOffset);
}
else {
float newViewOff = vfoBottom + (viewBW / 2.0f) - (viewBW / 10.0f);
float newViewBottom = newViewOff - (viewBW / 2.0f);
float newViewTop = newViewOff + (viewBW / 2.0f);
if (newViewTop < top) {
wtf.setViewOffset(newViewOff);
2020-08-11 18:33:42 +02:00
vfoman::setCenterOffset(wtf.selectedVFO, newVFO);
2020-07-19 15:59:44 +02:00
return;
}
wtf.setViewOffset((viewBW / 2.0f) - (BW / 2.0f));
float newVFOOffset = (vfoBW / 2.0f) - (BW / 2.0f) + (viewBW / 10.0f);
2020-08-11 18:33:42 +02:00
vfoman::setCenterOffset(wtf.selectedVFO, newVFOOffset);
2020-07-19 15:59:44 +02:00
wtf.setCenterFrequency(freq - newVFOOffset);
soapy.setFrequency(freq - newVFOOffset);
}
}
2020-07-11 21:15:10 +02:00
2020-06-10 04:13:56 +02:00
void drawWindow() {
2020-08-11 18:33:42 +02:00
if (wtf.selectedVFO == "" && wtf.vfos.size() > 0) {
wtf.selectFirstVFO();
}
2020-08-10 02:30:25 +02:00
ImGui::WaterfallVFO* vfo = wtf.vfos[wtf.selectedVFO];
2020-08-11 18:33:42 +02:00
if (vfo->centerOffsetChanged) {
fSel.setFrequency(wtf.getCenterFrequency() + vfo->generalOffset);
}
vfoman::updateFromWaterfall();
if (wtf.selectedVFOChanged) {
wtf.selectedVFOChanged = false;
2020-08-10 02:30:25 +02:00
fSel.setFrequency(vfo->generalOffset + wtf.getCenterFrequency());
2020-08-12 16:43:44 +02:00
mod::broadcastEvent(mod::EVENT_SELECTED_VFO_CHANGED);
2020-08-10 02:30:25 +02:00
}
2020-07-19 15:59:44 +02:00
if (fSel.frequencyChanged) {
fSel.frequencyChanged = false;
setVFO(fSel.frequency);
2020-08-11 18:33:42 +02:00
vfo->centerOffsetChanged = false;
vfo->lowerOffsetChanged = false;
vfo->upperOffsetChanged = false;
2020-06-10 04:13:56 +02:00
}
2020-07-19 15:59:44 +02:00
if (wtf.centerFreqMoved) {
wtf.centerFreqMoved = false;
soapy.setFrequency(wtf.getCenterFrequency());
2020-08-10 02:30:25 +02:00
fSel.setFrequency(wtf.getCenterFrequency() + vfo->generalOffset);
2020-06-15 15:53:45 +02:00
}
2020-08-04 21:34:56 +02:00
if (devId.changed() && soapy.devList.size() > 0) {
spdlog::info("Changed input device: {0}", devId.val);
soapy.setDevice(soapy.devList[devId.val]);
srId.markAsChanged();
2020-07-19 21:26:37 +02:00
if (soapy.gainList.size() == 0) {
return;
}
delete[] uiGains;
uiGains = new float[soapy.gainList.size()];
for (int i = 0; i < soapy.gainList.size(); i++) {
uiGains[i] = soapy.currentGains[i];
}
2020-06-22 16:45:57 +02:00
}
2020-08-04 21:34:56 +02:00
if (srId.changed() && soapy.devList.size() > 0) {
spdlog::info("Changed sample rate: {0}", srId.val);
sampleRate = soapy.sampleRates[srId.val];
2020-07-19 15:59:44 +02:00
soapy.setSampleRate(sampleRate);
wtf.setBandwidth(sampleRate);
wtf.setViewBandwidth(sampleRate);
sigPath.setSampleRate(sampleRate);
2020-08-04 21:34:56 +02:00
bw.val = sampleRate;
}
if (dcbias.changed()) {
sigPath.setDCBiasCorrection(dcbias.val);
2020-06-22 16:45:57 +02:00
}
2020-08-05 21:13:53 +02:00
if (bandplanId.changed() && bandPlanEnabled.val) {
wtf.bandplan = &bandplan::bandplans[bandplan::bandplanNames[bandplanId.val]];
}
if (bandPlanEnabled.changed()) {
wtf.bandplan = bandPlanEnabled.val ? &bandplan::bandplans[bandplan::bandplanNames[bandplanId.val]] : NULL;
2020-07-19 18:23:00 +02:00
}
2020-06-10 04:13:56 +02:00
ImVec2 vMin = ImGui::GetWindowContentRegionMin();
ImVec2 vMax = ImGui::GetWindowContentRegionMax();
int width = vMax.x - vMin.x;
int height = vMax.y - vMin.y;
2020-08-12 16:43:44 +02:00
int modCount = mod::moduleNames.size();
mod::Module_t mod;
for (int i = 0; i < modCount; i++) {
mod = mod::modules[mod::moduleNames[i]];
mod._NEW_FRAME_(mod.ctx);
}
2020-07-19 15:59:44 +02:00
// To Bar
if (playing) {
if (ImGui::ImageButton(icons::STOP_RAW, ImVec2(30, 30))) {
soapy.stop();
playing = false;
}
}
else {
if (ImGui::ImageButton(icons::PLAY_RAW, ImVec2(30, 30)) && soapy.devList.size() > 0) {
2020-07-19 15:59:44 +02:00
soapy.start();
soapy.setFrequency(wtf.getCenterFrequency());
playing = true;
}
}
ImGui::SameLine();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 8);
ImGui::SetNextItemWidth(200);
2020-08-12 16:43:44 +02:00
ImGui::SliderFloat("##_2_", volume, 0.0f, 1.0f, "");
2020-07-19 15:59:44 +02:00
ImGui::SameLine();
fSel.draw();
ImGui::Columns(3, "WindowColumns", false);
ImVec2 winSize = ImGui::GetWindowSize();
2020-06-10 04:13:56 +02:00
ImGui::SetColumnWidth(0, 300);
2020-07-19 18:09:59 +02:00
ImGui::SetColumnWidth(1, winSize.x - 300 - 60);
ImGui::SetColumnWidth(2, 60);
2020-06-10 04:13:56 +02:00
// Left Column
ImGui::BeginChild("Left Column");
if (ImGui::CollapsingHeader("Source")) {
2020-06-22 16:45:57 +02:00
ImGui::PushItemWidth(ImGui::GetWindowSize().x);
2020-08-04 21:34:56 +02:00
ImGui::Combo("##_0_", &devId.val, soapy.txtDevList.c_str());
2020-07-19 21:26:37 +02:00
ImGui::PopItemWidth();
2020-08-04 21:34:56 +02:00
2020-07-19 18:09:59 +02:00
if (!playing) {
2020-08-04 21:34:56 +02:00
ImGui::Combo("##_1_", &srId.val, soapy.txtSampleRateList.c_str());
2020-07-19 18:09:59 +02:00
}
else {
2020-08-04 21:34:56 +02:00
ImGui::Text("%.0f Samples/s", soapy.sampleRates[srId.val]);
2020-07-19 18:09:59 +02:00
}
2020-06-22 16:45:57 +02:00
2020-07-19 21:26:37 +02:00
ImGui::SameLine();
2020-06-22 16:45:57 +02:00
if (ImGui::Button("Refresh")) {
soapy.refresh();
2020-06-15 15:53:45 +02:00
}
2020-07-19 21:26:37 +02:00
for (int i = 0; i < soapy.gainList.size(); i++) {
ImGui::Text("%s gain", soapy.gainList[i].c_str());
ImGui::SameLine();
sprintf(buf, "##_gain_slide_%d_", i);
ImGui::SliderFloat(buf, &uiGains[i], soapy.gainRanges[i].minimum(), soapy.gainRanges[i].maximum());
if (uiGains[i] != soapy.currentGains[i]) {
soapy.setGain(i, uiGains[i]);
}
}
2020-06-10 04:13:56 +02:00
}
2020-08-07 14:29:06 +02:00
for (int i = 0; i < modCount; i++) {
if (ImGui::CollapsingHeader(mod::moduleNames[i].c_str())) {
mod = mod::modules[mod::moduleNames[i]];
mod._DRAW_MENU_(mod.ctx);
}
2020-06-10 04:13:56 +02:00
}
ImGui::CollapsingHeader("Audio");
2020-08-04 21:34:56 +02:00
if (ImGui::CollapsingHeader("Band Plan")) {
ImGui::PushItemWidth(ImGui::GetWindowSize().x);
ImGui::Combo("##_4_", &bandplanId.val, bandplan::bandplanNameTxt.c_str());
ImGui::PopItemWidth();
2020-08-05 21:13:53 +02:00
ImGui::Checkbox("Enabled", &bandPlanEnabled.val);
bandplan::BandPlan_t plan = bandplan::bandplans[bandplan::bandplanNames[bandplanId.val]];
ImGui::Text("Country: %s (%s)", plan.countryName, plan.countryCode);
ImGui::Text("Author: %s", plan.authorName);
2020-08-04 21:34:56 +02:00
}
2020-06-10 04:13:56 +02:00
ImGui::CollapsingHeader("Display");
ImGui::CollapsingHeader("Recording");
if(ImGui::CollapsingHeader("Debug")) {
ImGui::Text("Frame time: %.3f ms/frame", 1000.0f / ImGui::GetIO().Framerate);
ImGui::Text("Framerate: %.1f FPS", ImGui::GetIO().Framerate);
2020-08-10 02:30:25 +02:00
ImGui::Text("Center Frequency: %.1f FPS", wtf.getCenterFrequency());
2020-06-10 04:13:56 +02:00
}
ImGui::EndChild();
// Right Column
ImGui::NextColumn();
ImGui::BeginChild("Waterfall");
2020-07-11 21:15:10 +02:00
wtf.draw();
2020-06-10 04:13:56 +02:00
ImGui::EndChild();
2020-07-19 15:59:44 +02:00
ImGui::NextColumn();
ImGui::Text("Zoom");
2020-08-04 21:34:56 +02:00
ImGui::VSliderFloat("##_7_", ImVec2(20.0f, 150.0f), &bw.val, sampleRate, 1000.0f, "");
2020-07-19 15:59:44 +02:00
ImGui::NewLine();
2020-07-19 18:09:59 +02:00
ImGui::Text("Max");
2020-07-19 22:00:01 +02:00
ImGui::VSliderFloat("##_8_", ImVec2(20.0f, 150.0f), &fftMax, 0.0f, -100.0f, "");
2020-07-19 15:59:44 +02:00
ImGui::NewLine();
2020-07-19 18:09:59 +02:00
ImGui::Text("Min");
2020-07-19 22:00:01 +02:00
ImGui::VSliderFloat("##_9_", ImVec2(20.0f, 150.0f), &fftMin, 0.0f, -100.0f, "");
2020-07-19 15:59:44 +02:00
2020-08-04 21:34:56 +02:00
if (bw.changed()) {
wtf.setViewBandwidth(bw.val);
2020-08-10 02:30:25 +02:00
wtf.setViewOffset(vfo->centerOffset);
2020-07-19 15:59:44 +02:00
}
wtf.setFFTMin(fftMin);
wtf.setFFTMax(fftMax);
wtf.setWaterfallMin(fftMin);
wtf.setWaterfallMax(fftMax);
2020-08-12 16:43:44 +02:00
}
void bindVolumeVariable(float* vol) {
volume = vol;
}
void unbindVolumeVariable() {
volume = &dummyVolume;
2020-06-10 04:13:56 +02:00
}