2020-09-20 00:19:39 +02:00
|
|
|
#include <gui/main_window.h>
|
|
|
|
#include <gui/gui.h>
|
2020-06-10 04:13:56 +02:00
|
|
|
|
|
|
|
std::thread worker;
|
|
|
|
std::mutex fft_mtx;
|
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-08-16 03:39:05 +02:00
|
|
|
ImFont* bigFont;
|
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
|
|
|
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-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.pushFFT(_data, fftSize);
|
2020-06-15 15:53:45 +02:00
|
|
|
_data.clear();
|
|
|
|
}
|
2020-06-10 04:13:56 +02:00
|
|
|
|
2020-08-11 18:33:42 +02:00
|
|
|
dsp::NullSink sink;
|
2020-08-16 03:39:05 +02:00
|
|
|
int devId = 0;
|
|
|
|
int srId = 0;
|
|
|
|
watcher<int> bandplanId(0, true);
|
2020-08-21 15:38:27 +02:00
|
|
|
watcher<uint64_t> freq((uint64_t)90500000);
|
2020-08-16 03:39:05 +02:00
|
|
|
int demod = 1;
|
|
|
|
watcher<float> vfoFreq(92000000.0f);
|
|
|
|
float dummyVolume = 1.0f;
|
|
|
|
float* volume = &dummyVolume;
|
|
|
|
float fftMin = -70.0f;
|
|
|
|
float fftMax = 0.0f;
|
|
|
|
watcher<float> offset(0.0f, true);
|
|
|
|
watcher<float> bw(8000000.0f, true);
|
|
|
|
int sampleRate = 8000000;
|
|
|
|
bool playing = false;
|
|
|
|
watcher<bool> dcbias(false, false);
|
|
|
|
watcher<bool> bandPlanEnabled(true, false);
|
|
|
|
bool showCredits = false;
|
|
|
|
std::string audioStreamName = "";
|
|
|
|
std::string sourceName = "";
|
2020-08-20 18:29:23 +02:00
|
|
|
int menuWidth = 300;
|
|
|
|
bool grabbingMenu = false;
|
|
|
|
int newWidth = 300;
|
|
|
|
bool showWaterfall = true;
|
|
|
|
int fftHeight = 300;
|
2020-08-21 15:34:50 +02:00
|
|
|
bool showMenu = true;
|
2020-08-16 03:39:05 +02:00
|
|
|
|
|
|
|
void saveCurrentSource() {
|
|
|
|
int i = 0;
|
|
|
|
for (std::string gainName : soapy.gainList) {
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.conf["sourceSettings"][sourceName]["gains"][gainName] = uiGains[i];
|
2020-08-16 03:39:05 +02:00
|
|
|
i++;
|
|
|
|
}
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.conf["sourceSettings"][sourceName]["sampleRate"] = soapy.sampleRates[srId];
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
2020-08-11 18:33:42 +02:00
|
|
|
|
2020-08-16 03:39:05 +02:00
|
|
|
void loadSourceConfig(std::string name) {
|
2020-09-24 19:36:57 +02:00
|
|
|
json sourceSettings = core::configManager.conf["sourceSettings"][name];
|
2020-08-16 03:39:05 +02:00
|
|
|
|
|
|
|
sampleRate = sourceSettings["sampleRate"];
|
2020-08-16 17:36:48 +02:00
|
|
|
|
|
|
|
auto _srIt = std::find(soapy.sampleRates.begin(), soapy.sampleRates.end(), sampleRate);
|
|
|
|
// If the sample rate isn't valid, set to minimum
|
|
|
|
if (_srIt == soapy.sampleRates.end()) {
|
|
|
|
srId = 0;
|
|
|
|
sampleRate = soapy.sampleRates[0];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
srId = std::distance(soapy.sampleRates.begin(), _srIt);
|
|
|
|
}
|
2020-09-20 00:19:39 +02:00
|
|
|
sigpath::signalPath.setSampleRate(sampleRate);
|
2020-08-16 03:39:05 +02:00
|
|
|
soapy.setSampleRate(sampleRate);
|
|
|
|
|
|
|
|
// Set gains
|
2020-08-16 18:18:10 +02:00
|
|
|
delete[] uiGains;
|
2020-08-16 03:39:05 +02:00
|
|
|
uiGains = new float[soapy.gainList.size()];
|
|
|
|
int i = 0;
|
|
|
|
for (std::string gainName : soapy.gainList) {
|
2020-08-16 17:36:48 +02:00
|
|
|
// If gain doesn't exist in config, set it to the minimum value
|
|
|
|
if (!sourceSettings["gains"].contains(gainName)) {
|
|
|
|
uiGains[i] = soapy.gainRanges[i].minimum();
|
|
|
|
soapy.setGain(i, uiGains[i]);
|
|
|
|
continue;
|
|
|
|
}
|
2020-08-16 03:39:05 +02:00
|
|
|
uiGains[i] = sourceSettings["gains"][gainName];
|
|
|
|
soapy.setGain(i, uiGains[i]);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update GUI
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setBandwidth(sampleRate);
|
|
|
|
gui::waterfall.setViewBandwidth(sampleRate);
|
2020-08-16 03:39:05 +02:00
|
|
|
bw.val = sampleRate;
|
|
|
|
}
|
2020-07-19 15:59:44 +02:00
|
|
|
|
2020-08-16 17:36:48 +02:00
|
|
|
void loadAudioConfig(std::string name) {
|
2020-09-24 19:36:57 +02:00
|
|
|
json audioSettings = core::configManager.conf["audio"][name];
|
2020-08-16 17:36:48 +02:00
|
|
|
std::string devName = audioSettings["device"];
|
|
|
|
auto _devIt = std::find(audio::streams[name]->audio->deviceNames.begin(), audio::streams[name]->audio->deviceNames.end(), devName);
|
|
|
|
|
|
|
|
// If audio device doesn't exist anymore
|
|
|
|
if (_devIt == audio::streams[name]->audio->deviceNames.end()) {
|
|
|
|
audio::streams[name]->audio->setToDefault();
|
|
|
|
int deviceId = audio::streams[name]->audio->getDeviceId();
|
|
|
|
audio::setAudioDevice(name, deviceId, audio::streams[name]->audio->devices[deviceId].sampleRates[0]);
|
|
|
|
audio::streams[name]->sampleRateId = 0;
|
|
|
|
audio::streams[name]->volume = audioSettings["volume"];
|
|
|
|
audio::streams[name]->audio->setVolume(audio::streams[name]->volume);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int deviceId = std::distance(audio::streams[name]->audio->deviceNames.begin(), _devIt);
|
|
|
|
float sr = audioSettings["sampleRate"];
|
|
|
|
auto _srIt = std::find(audio::streams[name]->audio->devices[deviceId].sampleRates.begin(), audio::streams[name]->audio->devices[deviceId].sampleRates.end(), sr);
|
|
|
|
|
|
|
|
// If sample rate doesn't exist anymore
|
|
|
|
if (_srIt == audio::streams[name]->audio->devices[deviceId].sampleRates.end()) {
|
|
|
|
audio::streams[name]->sampleRateId = 0;
|
|
|
|
audio::setAudioDevice(name, deviceId, audio::streams[name]->audio->devices[deviceId].sampleRates[0]);
|
|
|
|
audio::streams[name]->volume = audioSettings["volume"];
|
|
|
|
audio::streams[name]->audio->setVolume(audio::streams[name]->volume);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int samplerateId = std::distance(audio::streams[name]->audio->devices[deviceId].sampleRates.begin(), _srIt);
|
|
|
|
audio::streams[name]->sampleRateId = samplerateId;
|
|
|
|
audio::setAudioDevice(name, deviceId, audio::streams[name]->audio->devices[deviceId].sampleRates[samplerateId]);
|
|
|
|
audio::streams[name]->deviceId = deviceId;
|
|
|
|
audio::streams[name]->volume = audioSettings["volume"];
|
|
|
|
audio::streams[name]->audio->setVolume(audio::streams[name]->volume);
|
|
|
|
}
|
|
|
|
|
2020-08-21 17:11:12 +02:00
|
|
|
void saveAudioConfig(std::string name) {
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.conf["audio"][name]["device"] = audio::streams[name]->audio->deviceNames[audio::streams[name]->deviceId];
|
|
|
|
core::configManager.conf["audio"][name]["volume"] = audio::streams[name]->volume;
|
|
|
|
core::configManager.conf["audio"][name]["sampleRate"] = audio::streams[name]->sampleRate;
|
2020-08-21 17:11:12 +02:00
|
|
|
}
|
|
|
|
|
2020-09-24 19:36:57 +02:00
|
|
|
void setVFO(float freq);
|
|
|
|
|
|
|
|
// =======================================================
|
|
|
|
|
|
|
|
void sourceMenu(void* ctx) {
|
|
|
|
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
|
|
|
|
if (playing) { style::beginDisabled(); };
|
|
|
|
|
|
|
|
ImGui::PushItemWidth(menuColumnWidth);
|
|
|
|
if (ImGui::Combo("##_0_", &devId, soapy.txtDevList.c_str())) {
|
|
|
|
spdlog::info("Changed input device: {0}", devId);
|
|
|
|
sourceName = soapy.devNameList[devId];
|
|
|
|
soapy.setDevice(soapy.devList[devId]);
|
|
|
|
|
|
|
|
core::configManager.aquire();
|
|
|
|
if (core::configManager.conf["sourceSettings"].contains(sourceName)) {
|
|
|
|
loadSourceConfig(sourceName);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
srId = 0;
|
|
|
|
sampleRate = soapy.getSampleRate();
|
|
|
|
bw.val = sampleRate;
|
|
|
|
gui::waterfall.setBandwidth(sampleRate);
|
|
|
|
gui::waterfall.setViewBandwidth(sampleRate);
|
|
|
|
sigpath::signalPath.setSampleRate(sampleRate);
|
|
|
|
|
|
|
|
if (soapy.gainList.size() >= 0) {
|
|
|
|
delete[] uiGains;
|
|
|
|
uiGains = new float[soapy.gainList.size()];
|
|
|
|
for (int i = 0; i < soapy.gainList.size(); i++) {
|
|
|
|
uiGains[i] = soapy.currentGains[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setVFO(gui::freqSelect.frequency);
|
|
|
|
core::configManager.conf["source"] = sourceName;
|
|
|
|
core::configManager.release(true);
|
|
|
|
}
|
|
|
|
ImGui::PopItemWidth();
|
|
|
|
|
|
|
|
if (ImGui::Combo("##_1_", &srId, soapy.txtSampleRateList.c_str())) {
|
|
|
|
spdlog::info("Changed sample rate: {0}", srId);
|
|
|
|
sampleRate = soapy.sampleRates[srId];
|
|
|
|
soapy.setSampleRate(sampleRate);
|
|
|
|
gui::waterfall.setBandwidth(sampleRate);
|
|
|
|
gui::waterfall.setViewBandwidth(sampleRate);
|
|
|
|
sigpath::signalPath.setSampleRate(sampleRate);
|
|
|
|
bw.val = sampleRate;
|
|
|
|
|
|
|
|
core::configManager.aquire();
|
|
|
|
if (!core::configManager.conf["sourceSettings"].contains(sourceName)) {
|
|
|
|
saveCurrentSource();
|
|
|
|
}
|
|
|
|
core::configManager.conf["sourceSettings"][sourceName]["sampleRate"] = sampleRate;
|
|
|
|
core::configManager.release(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
bool noDevice = (soapy.devList.size() == 0);
|
|
|
|
if (ImGui::Button("Refresh", ImVec2(menuColumnWidth - ImGui::GetCursorPosX(), 0.0f))) {
|
|
|
|
soapy.refresh();
|
|
|
|
if (noDevice && soapy.devList.size() > 0) {
|
|
|
|
sourceName = soapy.devNameList[0];
|
|
|
|
soapy.setDevice(soapy.devList[0]);
|
|
|
|
if (core::configManager.conf["sourceSettings"][sourceName]) {
|
|
|
|
loadSourceConfig(sourceName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (playing) { style::endDisabled(); };
|
|
|
|
|
|
|
|
float maxTextLength = 0;
|
|
|
|
float txtLen = 0;
|
|
|
|
char buf[100];
|
|
|
|
|
|
|
|
// Calculate the spacing
|
|
|
|
for (int i = 0; i < soapy.gainList.size(); i++) {
|
|
|
|
sprintf(buf, "%s gain", soapy.gainList[i].c_str());
|
|
|
|
txtLen = ImGui::CalcTextSize(buf).x;
|
|
|
|
if (txtLen > maxTextLength) {
|
|
|
|
maxTextLength = txtLen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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::SetCursorPosX(maxTextLength + 5);
|
|
|
|
ImGui::PushItemWidth(menuColumnWidth - (maxTextLength + 5));
|
|
|
|
if (ImGui::SliderFloat(buf, &uiGains[i], soapy.gainRanges[i].minimum(), soapy.gainRanges[i].maximum())) {
|
|
|
|
soapy.setGain(i, uiGains[i]);
|
|
|
|
core::configManager.aquire();
|
|
|
|
if (!core::configManager.conf["sourceSettings"].contains(sourceName)) {
|
|
|
|
saveCurrentSource();
|
|
|
|
}
|
|
|
|
core::configManager.conf["sourceSettings"][sourceName]["gains"][soapy.gainList[i]] = uiGains[i];
|
|
|
|
core::configManager.release(true);
|
|
|
|
}
|
|
|
|
ImGui::PopItemWidth();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Spacing();
|
|
|
|
}
|
|
|
|
|
|
|
|
// =======================================================
|
|
|
|
|
2020-08-16 03:39:05 +02:00
|
|
|
void windowInit() {
|
2020-09-19 12:48:34 +02:00
|
|
|
spdlog::info("Initializing SoapySDR");
|
|
|
|
soapy.init();
|
2020-09-24 19:36:57 +02:00
|
|
|
|
|
|
|
gui::menu.registerEntry("Source", sourceMenu, NULL);
|
2020-09-19 12:48:34 +02:00
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::freqSelect.init();
|
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-09-20 00:19:39 +02:00
|
|
|
sigpath::signalPath.init(sampleRate, 20, fftSize, &soapy.output, (dsp::complex_t*)fft_in, fftHandler);
|
|
|
|
sigpath::signalPath.start();
|
2020-08-11 18:33:42 +02:00
|
|
|
|
2020-08-16 17:56:12 +02:00
|
|
|
uiGains = new float[soapy.gainList.size()];
|
2020-08-12 16:43:44 +02:00
|
|
|
|
|
|
|
spdlog::info("Loading modules");
|
2020-09-20 00:19:39 +02:00
|
|
|
mod::initAPI(&gui::waterfall);
|
2020-09-24 19:36:57 +02:00
|
|
|
mod::loadFromList(ROOT_DIR "/module_list.json");
|
2020-06-10 04:13:56 +02:00
|
|
|
|
2020-09-24 19:36:57 +02:00
|
|
|
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
|
2020-08-16 03:39:05 +02:00
|
|
|
|
|
|
|
// Load last source configuration
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
uint64_t frequency = core::configManager.conf["frequency"];
|
|
|
|
sourceName = core::configManager.conf["source"];
|
2020-08-16 03:39:05 +02:00
|
|
|
auto _sourceIt = std::find(soapy.devNameList.begin(), soapy.devNameList.end(), sourceName);
|
2020-09-24 19:36:57 +02:00
|
|
|
if (_sourceIt != soapy.devNameList.end() && core::configManager.conf["sourceSettings"].contains(sourceName)) {
|
|
|
|
json sourceSettings = core::configManager.conf["sourceSettings"][sourceName];
|
2020-08-16 03:39:05 +02:00
|
|
|
devId = std::distance(soapy.devNameList.begin(), _sourceIt);
|
|
|
|
soapy.setDevice(soapy.devList[devId]);
|
|
|
|
loadSourceConfig(sourceName);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int i = 0;
|
|
|
|
bool settingsFound = false;
|
|
|
|
for (std::string devName : soapy.devNameList) {
|
2020-09-24 19:36:57 +02:00
|
|
|
if (core::configManager.conf["sourceSettings"].contains(devName)) {
|
2020-08-16 03:39:05 +02:00
|
|
|
sourceName = devName;
|
|
|
|
settingsFound = true;
|
|
|
|
devId = i;
|
|
|
|
soapy.setDevice(soapy.devList[i]);
|
|
|
|
loadSourceConfig(sourceName);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (!settingsFound) {
|
2020-08-21 17:11:12 +02:00
|
|
|
if (soapy.devNameList.size() > 0) {
|
|
|
|
sourceName = soapy.devNameList[0];
|
|
|
|
}
|
2020-08-16 03:39:05 +02:00
|
|
|
sampleRate = soapy.getSampleRate();
|
2020-09-20 00:19:39 +02:00
|
|
|
sigpath::signalPath.setSampleRate(sampleRate);
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
|
|
|
// Search for the first source in the list to have a config
|
|
|
|
// If no pre-defined source, selected default device
|
|
|
|
}
|
|
|
|
|
2020-08-16 14:26:22 +02:00
|
|
|
// Also add a loading screen
|
2020-08-20 18:29:23 +02:00
|
|
|
// Adjustable "snap to grid" for each VFO
|
|
|
|
// Finish the recorder module
|
2020-08-16 14:26:22 +02:00
|
|
|
// Add squelsh
|
|
|
|
// Bandwidth ajustment
|
2020-09-06 15:39:09 +02:00
|
|
|
// CW and RAW modes;
|
2020-08-18 00:56:51 +02:00
|
|
|
// Bring VFO to a visible place when changing sample rate if it's smaller
|
2020-09-18 00:23:03 +02:00
|
|
|
// Add save config for modules
|
|
|
|
// Do VFO in two steps: First sample rate conversion, then filtering
|
2020-08-21 17:11:12 +02:00
|
|
|
|
2020-08-20 18:29:23 +02:00
|
|
|
// And a module add/remove/change order menu
|
|
|
|
// get rid of watchers and use if() instead
|
2020-08-18 00:56:51 +02:00
|
|
|
// Switch to double for all frequecies and bandwidth
|
2020-08-16 03:39:05 +02:00
|
|
|
|
|
|
|
// Update UI settings
|
2020-09-24 19:36:57 +02:00
|
|
|
fftMin = core::configManager.conf["min"];
|
|
|
|
fftMax = core::configManager.conf["max"];
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setFFTMin(fftMin);
|
|
|
|
gui::waterfall.setWaterfallMin(fftMin);
|
|
|
|
gui::waterfall.setFFTMax(fftMax);
|
|
|
|
gui::waterfall.setWaterfallMax(fftMax);
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-09-24 19:36:57 +02:00
|
|
|
bandPlanEnabled.val = core::configManager.conf["bandPlanEnabled"];
|
2020-08-16 03:39:05 +02:00
|
|
|
bandPlanEnabled.markAsChanged();
|
|
|
|
|
2020-09-24 19:36:57 +02:00
|
|
|
std::string bandPlanName = core::configManager.conf["bandPlan"];
|
2020-08-16 03:39:05 +02:00
|
|
|
auto _bandplanIt = bandplan::bandplans.find(bandPlanName);
|
|
|
|
if (_bandplanIt != bandplan::bandplans.end()) {
|
|
|
|
bandplanId.val = std::distance(bandplan::bandplans.begin(), bandplan::bandplans.find(bandPlanName));
|
|
|
|
|
|
|
|
if (bandPlanEnabled.val) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.bandplan = &bandplan::bandplans[bandPlanName];
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
|
|
|
else {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.bandplan = NULL;
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bandplanId.val = 0;
|
|
|
|
}
|
|
|
|
bandplanId.markAsChanged();
|
|
|
|
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::freqSelect.setFrequency(frequency);
|
|
|
|
gui::freqSelect.frequencyChanged = false;
|
2020-08-16 03:39:05 +02:00
|
|
|
soapy.setFrequency(frequency);
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setCenterFrequency(frequency);
|
|
|
|
gui::waterfall.setBandwidth(sampleRate);
|
|
|
|
gui::waterfall.setViewBandwidth(sampleRate);
|
2020-08-16 03:39:05 +02:00
|
|
|
bw.val = sampleRate;
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.vfoFreqChanged = false;
|
|
|
|
gui::waterfall.centerFreqMoved = false;
|
|
|
|
gui::waterfall.selectFirstVFO();
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-08-16 17:36:48 +02:00
|
|
|
for (auto [name, stream] : audio::streams) {
|
2020-09-24 19:36:57 +02:00
|
|
|
if (core::configManager.conf["audio"].contains(name)) {
|
2020-08-16 17:36:48 +02:00
|
|
|
bool running = audio::streams[name]->running;
|
|
|
|
audio::stopStream(name);
|
|
|
|
loadAudioConfig(name);
|
|
|
|
if (running) {
|
|
|
|
audio::startStream(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
audioStreamName = audio::getNameFromVFO(gui::waterfall.selectedVFO);
|
2020-08-16 03:39:05 +02:00
|
|
|
if (audioStreamName != "") {
|
|
|
|
volume = &audio::streams[audioStreamName]->volume;
|
|
|
|
}
|
2020-08-20 18:29:23 +02:00
|
|
|
|
2020-09-24 19:36:57 +02:00
|
|
|
menuWidth = core::configManager.conf["menuWidth"];
|
2020-08-20 18:29:23 +02:00
|
|
|
newWidth = menuWidth;
|
|
|
|
|
2020-09-24 19:36:57 +02:00
|
|
|
showWaterfall = core::configManager.conf["showWaterfall"];
|
2020-08-20 18:29:23 +02:00
|
|
|
if (!showWaterfall) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.hideWaterfall();
|
2020-08-20 18:29:23 +02:00
|
|
|
}
|
|
|
|
|
2020-09-24 19:36:57 +02:00
|
|
|
fftHeight = core::configManager.conf["fftHeight"];
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setFFTHeight(fftHeight);
|
2020-09-24 19:36:57 +02:00
|
|
|
|
|
|
|
core::configManager.release();
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
2020-07-19 18:23:00 +02:00
|
|
|
|
2020-07-19 15:59:44 +02:00
|
|
|
void setVFO(float freq) {
|
2020-09-20 00:19:39 +02:00
|
|
|
ImGui::WaterfallVFO* vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO];
|
2020-08-10 02:30:25 +02:00
|
|
|
|
|
|
|
float currentOff = vfo->centerOffset;
|
2020-09-20 00:19:39 +02:00
|
|
|
float currentTune = gui::waterfall.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);
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
float view = gui::waterfall.getViewOffset();
|
|
|
|
float viewBW = gui::waterfall.getViewBandwidth();
|
2020-07-19 15:59:44 +02:00
|
|
|
float viewBottom = view - (viewBW / 2.0f);
|
|
|
|
float viewTop = view + (viewBW / 2.0f);
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
float wholeFreq = gui::waterfall.getCenterFrequency();
|
|
|
|
float BW = gui::waterfall.getBandwidth();
|
2020-07-19 15:59:44 +02:00
|
|
|
float bottom = -(BW / 2.0f);
|
|
|
|
float top = (BW / 2.0f);
|
|
|
|
|
|
|
|
// VFO still fints in the view
|
|
|
|
if (vfoBottom > viewBottom && vfoTop < viewTop) {
|
2020-09-20 00:19:39 +02:00
|
|
|
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO);
|
2020-07-19 15:59:44 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// VFO too low for current SDR tuning
|
|
|
|
if (vfoBottom < bottom) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setViewOffset((BW / 2.0f) - (viewBW / 2.0f));
|
2020-07-19 15:59:44 +02:00
|
|
|
float newVFOOffset = (BW / 2.0f) - (vfoBW / 2.0f) - (viewBW / 10.0f);
|
2020-09-20 00:19:39 +02:00
|
|
|
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
|
|
|
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
2020-07-19 15:59:44 +02:00
|
|
|
soapy.setFrequency(freq - newVFOOffset);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// VFO too high for current SDR tuning
|
|
|
|
if (vfoTop > top) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setViewOffset((viewBW / 2.0f) - (BW / 2.0f));
|
2020-07-19 15:59:44 +02:00
|
|
|
float newVFOOffset = (vfoBW / 2.0f) - (BW / 2.0f) + (viewBW / 10.0f);
|
2020-09-20 00:19:39 +02:00
|
|
|
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
|
|
|
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
2020-07-19 15:59:44 +02:00
|
|
|
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) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setViewOffset(newViewOff);
|
|
|
|
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO);
|
2020-07-19 15:59:44 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setViewOffset((BW / 2.0f) - (viewBW / 2.0f));
|
2020-07-19 15:59:44 +02:00
|
|
|
float newVFOOffset = (BW / 2.0f) - (vfoBW / 2.0f) - (viewBW / 10.0f);
|
2020-09-20 00:19:39 +02:00
|
|
|
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
|
|
|
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
2020-07-19 15:59:44 +02:00
|
|
|
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) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setViewOffset(newViewOff);
|
|
|
|
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO);
|
2020-07-19 15:59:44 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setViewOffset((viewBW / 2.0f) - (BW / 2.0f));
|
2020-07-19 15:59:44 +02:00
|
|
|
float newVFOOffset = (vfoBW / 2.0f) - (BW / 2.0f) + (viewBW / 10.0f);
|
2020-09-20 00:19:39 +02:00
|
|
|
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
|
|
|
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
2020-07-19 15:59:44 +02:00
|
|
|
soapy.setFrequency(freq - newVFOOffset);
|
|
|
|
}
|
|
|
|
}
|
2020-07-11 21:15:10 +02:00
|
|
|
|
2020-06-10 04:13:56 +02:00
|
|
|
void drawWindow() {
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::Begin("Main", NULL, WINDOW_FLAGS);
|
2020-08-11 18:33:42 +02:00
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
ImGui::WaterfallVFO* vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO];
|
2020-08-10 02:30:25 +02:00
|
|
|
|
2020-08-11 18:33:42 +02:00
|
|
|
if (vfo->centerOffsetChanged) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset);
|
|
|
|
gui::freqSelect.frequencyChanged = false;
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
|
|
|
|
core::configManager.release(true);
|
2020-08-11 18:33:42 +02:00
|
|
|
}
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
sigpath::vfoManager.updateFromWaterfall(&gui::waterfall);
|
2020-08-11 18:33:42 +02:00
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
if (gui::waterfall.selectedVFOChanged) {
|
|
|
|
gui::waterfall.selectedVFOChanged = false;
|
|
|
|
gui::freqSelect.setFrequency(vfo->generalOffset + gui::waterfall.getCenterFrequency());
|
|
|
|
gui::freqSelect.frequencyChanged = false;
|
2020-08-12 16:43:44 +02:00
|
|
|
mod::broadcastEvent(mod::EVENT_SELECTED_VFO_CHANGED);
|
2020-09-20 00:19:39 +02:00
|
|
|
audioStreamName = audio::getNameFromVFO(gui::waterfall.selectedVFO);
|
2020-08-16 03:39:05 +02:00
|
|
|
if (audioStreamName != "") {
|
|
|
|
volume = &audio::streams[audioStreamName]->volume;
|
|
|
|
}
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
|
|
|
|
core::configManager.release(true);
|
2020-08-10 02:30:25 +02:00
|
|
|
}
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
if (gui::freqSelect.frequencyChanged) {
|
|
|
|
gui::freqSelect.frequencyChanged = false;
|
|
|
|
setVFO(gui::freqSelect.frequency);
|
2020-08-11 18:33:42 +02:00
|
|
|
vfo->centerOffsetChanged = false;
|
|
|
|
vfo->lowerOffsetChanged = false;
|
|
|
|
vfo->upperOffsetChanged = false;
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
|
|
|
|
core::configManager.release(true);
|
2020-06-10 04:13:56 +02:00
|
|
|
}
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
if (gui::waterfall.centerFreqMoved) {
|
|
|
|
gui::waterfall.centerFreqMoved = false;
|
|
|
|
soapy.setFrequency(gui::waterfall.getCenterFrequency());
|
|
|
|
gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset);
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
|
|
|
|
core::configManager.release(true);
|
2020-08-04 21:34:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dcbias.changed()) {
|
2020-09-20 00:19:39 +02:00
|
|
|
sigpath::signalPath.setDCBiasCorrection(dcbias.val);
|
2020-06-22 16:45:57 +02:00
|
|
|
}
|
|
|
|
|
2020-08-05 21:13:53 +02:00
|
|
|
if (bandplanId.changed() && bandPlanEnabled.val) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.bandplan = &bandplan::bandplans[bandplan::bandplanNames[bandplanId.val]];
|
2020-08-05 21:13:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bandPlanEnabled.changed()) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.bandplan = bandPlanEnabled.val ? &bandplan::bandplans[bandplan::bandplanNames[bandplanId.val]] : NULL;
|
2020-07-19 18:23:00 +02:00
|
|
|
}
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
int _fftHeight = gui::waterfall.getFFTHeight();
|
2020-08-20 18:29:23 +02:00
|
|
|
if (fftHeight != _fftHeight) {
|
|
|
|
fftHeight = _fftHeight;
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["fftHeight"] = fftHeight;
|
|
|
|
core::configManager.release(true);
|
2020-08-20 18:29:23 +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
|
2020-08-21 15:34:50 +02:00
|
|
|
if (ImGui::ImageButton(icons::MENU, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0)) {
|
|
|
|
showMenu = !showMenu;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
2020-07-19 15:59:44 +02:00
|
|
|
if (playing) {
|
2020-08-16 03:39:05 +02:00
|
|
|
if (ImGui::ImageButton(icons::STOP, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0)) {
|
2020-07-19 15:59:44 +02:00
|
|
|
soapy.stop();
|
|
|
|
playing = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-08-16 03:39:05 +02:00
|
|
|
if (ImGui::ImageButton(icons::PLAY, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0) && soapy.devList.size() > 0) {
|
2020-07-19 15:59:44 +02:00
|
|
|
soapy.start();
|
2020-09-20 00:19:39 +02:00
|
|
|
soapy.setFrequency(gui::waterfall.getCenterFrequency());
|
2020-07-19 15:59:44 +02:00
|
|
|
playing = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
|
|
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 8);
|
|
|
|
ImGui::SetNextItemWidth(200);
|
2020-08-16 03:39:05 +02:00
|
|
|
if (ImGui::SliderFloat("##_2_", volume, 0.0f, 1.0f, "")) {
|
|
|
|
if (audioStreamName != "") {
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
if (!core::configManager.conf["audio"].contains(audioStreamName)) {
|
2020-08-21 17:11:12 +02:00
|
|
|
saveAudioConfig(audioStreamName);
|
|
|
|
}
|
2020-08-16 03:39:05 +02:00
|
|
|
audio::streams[audioStreamName]->audio->setVolume(*volume);
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.conf["audio"][audioStreamName]["volume"] = *volume;
|
|
|
|
core::configManager.release(true);
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-19 15:59:44 +02:00
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::freqSelect.draw();
|
2020-07-19 15:59:44 +02:00
|
|
|
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::SameLine();
|
|
|
|
|
|
|
|
// Logo button
|
|
|
|
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 48);
|
|
|
|
ImGui::SetCursorPosY(10);
|
|
|
|
if (ImGui::ImageButton(icons::LOGO, ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), 0)) {
|
|
|
|
showCredits = true;
|
|
|
|
}
|
|
|
|
if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
|
|
|
|
showCredits = false;
|
|
|
|
}
|
|
|
|
if (ImGui::IsKeyPressed(GLFW_KEY_ESCAPE)) {
|
|
|
|
showCredits = false;
|
|
|
|
}
|
|
|
|
|
2020-08-20 18:29:23 +02:00
|
|
|
// Handle menu resize
|
|
|
|
float curY = ImGui::GetCursorPosY();
|
2020-07-19 15:59:44 +02:00
|
|
|
ImVec2 winSize = ImGui::GetWindowSize();
|
2020-08-20 18:29:23 +02:00
|
|
|
ImVec2 mousePos = ImGui::GetMousePos();
|
|
|
|
bool click = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
|
|
|
bool down = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
|
|
|
if (grabbingMenu) {
|
|
|
|
newWidth = mousePos.x;
|
|
|
|
newWidth = std::clamp<float>(newWidth, 250, winSize.x - 250);
|
|
|
|
ImGui::GetForegroundDrawList()->AddLine(ImVec2(newWidth, curY), ImVec2(newWidth, winSize.y - 10), ImGui::GetColorU32(ImGuiCol_SeparatorActive));
|
|
|
|
}
|
|
|
|
if (mousePos.x >= newWidth - 2 && mousePos.x <= newWidth + 2 && mousePos.y > curY) {
|
|
|
|
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
|
|
|
if (click) {
|
|
|
|
grabbingMenu = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
|
|
|
|
}
|
|
|
|
if(!down && grabbingMenu) {
|
|
|
|
grabbingMenu = false;
|
|
|
|
menuWidth = newWidth;
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["menuWidth"] = menuWidth;
|
|
|
|
core::configManager.release(true);
|
2020-08-20 18:29:23 +02:00
|
|
|
}
|
|
|
|
|
2020-06-10 04:13:56 +02:00
|
|
|
// Left Column
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
if (showMenu) {
|
|
|
|
ImGui::Columns(3, "WindowColumns", false);
|
|
|
|
ImGui::SetColumnWidth(0, menuWidth);
|
|
|
|
ImGui::SetColumnWidth(1, winSize.x - menuWidth - 60);
|
|
|
|
ImGui::SetColumnWidth(2, 60);
|
|
|
|
ImGui::BeginChild("Left Column");
|
|
|
|
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-09-24 19:36:57 +02:00
|
|
|
gui::menu.draw();
|
2020-06-10 04:13:56 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
for (int i = 0; i < modCount; i++) {
|
|
|
|
if (ImGui::CollapsingHeader(mod::moduleNames[i].c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
mod = mod::modules[mod::moduleNames[i]];
|
|
|
|
mod._DRAW_MENU_(mod.ctx);
|
|
|
|
ImGui::Spacing();
|
|
|
|
}
|
|
|
|
}
|
2020-06-10 04:13:56 +02:00
|
|
|
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
if (ImGui::CollapsingHeader("Audio", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
int count = 0;
|
|
|
|
int maxCount = audio::streams.size();
|
|
|
|
for (auto const& [name, stream] : audio::streams) {
|
|
|
|
int deviceId;
|
|
|
|
float vol = 1.0f;
|
|
|
|
deviceId = stream->audio->getDeviceId();
|
|
|
|
|
|
|
|
ImGui::SetCursorPosX((menuColumnWidth / 2.0f) - (ImGui::CalcTextSize(name.c_str()).x / 2.0f));
|
|
|
|
ImGui::Text(name.c_str());
|
|
|
|
|
|
|
|
ImGui::PushItemWidth(menuColumnWidth);
|
|
|
|
bool running = stream->running;
|
|
|
|
if (ImGui::Combo(("##_audio_dev_0_"+ name).c_str(), &stream->deviceId, stream->audio->devTxtList.c_str())) {
|
|
|
|
audio::stopStream(name);
|
|
|
|
audio::setAudioDevice(name, stream->deviceId, stream->audio->devices[deviceId].sampleRates[0]);
|
|
|
|
if (running) {
|
|
|
|
audio::startStream(name);
|
|
|
|
}
|
|
|
|
stream->sampleRateId = 0;
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
// Create config if it doesn't exist
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
if (!core::configManager.conf["audio"].contains(name)) {
|
2020-08-21 17:11:12 +02:00
|
|
|
saveAudioConfig(name);
|
2020-08-21 15:34:50 +02:00
|
|
|
}
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.conf["audio"][name]["device"] = stream->audio->deviceNames[stream->deviceId];
|
|
|
|
core::configManager.conf["audio"][name]["sampleRate"] = stream->audio->devices[stream->deviceId].sampleRates[0];
|
|
|
|
core::configManager.release(true);
|
2020-08-16 17:36:48 +02:00
|
|
|
}
|
2020-08-21 15:34:50 +02:00
|
|
|
if (ImGui::Combo(("##_audio_sr_0_" + name).c_str(), &stream->sampleRateId, stream->audio->devices[deviceId].txtSampleRates.c_str())) {
|
|
|
|
audio::stopStream(name);
|
|
|
|
audio::setSampleRate(name, stream->audio->devices[deviceId].sampleRates[stream->sampleRateId]);
|
|
|
|
if (running) {
|
|
|
|
audio::startStream(name);
|
|
|
|
}
|
2020-08-16 17:36:48 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
// Create config if it doesn't exist
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
if (!core::configManager.conf["audio"].contains(name)) {
|
2020-08-21 17:11:12 +02:00
|
|
|
saveAudioConfig(name);
|
2020-08-21 15:34:50 +02:00
|
|
|
}
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.conf["audio"][name]["sampleRate"] = stream->audio->devices[deviceId].sampleRates[stream->sampleRateId];
|
|
|
|
core::configManager.release(true);
|
2020-08-16 17:36:48 +02:00
|
|
|
}
|
2020-08-21 15:34:50 +02:00
|
|
|
if (ImGui::SliderFloat(("##_audio_vol_0_" + name).c_str(), &stream->volume, 0.0f, 1.0f, "")) {
|
|
|
|
stream->audio->setVolume(stream->volume);
|
2020-08-16 17:36:48 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
// Create config if it doesn't exist
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
if (!core::configManager.conf["audio"].contains(name)) {
|
2020-08-21 17:11:12 +02:00
|
|
|
saveAudioConfig(name);
|
2020-08-21 15:34:50 +02:00
|
|
|
}
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.conf["audio"][name]["volume"] = stream->volume;
|
|
|
|
core::configManager.release(true);
|
2020-08-16 17:36:48 +02:00
|
|
|
}
|
2020-08-21 15:34:50 +02:00
|
|
|
ImGui::PopItemWidth();
|
|
|
|
count++;
|
|
|
|
if (count < maxCount) {
|
|
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Separator();
|
|
|
|
}
|
|
|
|
ImGui::Spacing();
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
2020-08-21 15:34:50 +02:00
|
|
|
ImGui::Spacing();
|
|
|
|
}
|
2020-08-16 17:36:48 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
if (ImGui::CollapsingHeader("Band Plan", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
ImGui::PushItemWidth(menuColumnWidth);
|
|
|
|
if (ImGui::Combo("##_4_", &bandplanId.val, bandplan::bandplanNameTxt.c_str())) {
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["bandPlan"] = bandplan::bandplanNames[bandplanId.val];
|
|
|
|
core::configManager.release(true);
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
|
|
|
ImGui::PopItemWidth();
|
2020-08-21 15:34:50 +02:00
|
|
|
if (ImGui::Checkbox("Enabled", &bandPlanEnabled.val)) {
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["bandPlanEnabled"] = bandPlanEnabled.val;
|
|
|
|
core::configManager.release(true);
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
2020-08-21 15:34:50 +02:00
|
|
|
bandplan::BandPlan_t plan = bandplan::bandplans[bandplan::bandplanNames[bandplanId.val]];
|
2020-08-21 17:12:48 +02:00
|
|
|
ImGui::Text("Country: %s (%s)", plan.countryName.c_str(), plan.countryCode.c_str());
|
|
|
|
ImGui::Text("Author: %s", plan.authorName.c_str());
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::Spacing();
|
2020-08-21 15:34:50 +02:00
|
|
|
}
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
if (ImGui::CollapsingHeader("Display", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
if (ImGui::Checkbox("Show waterfall", &showWaterfall)) {
|
2020-09-20 00:19:39 +02:00
|
|
|
showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall();
|
2020-08-21 15:34:50 +02:00
|
|
|
}
|
|
|
|
ImGui::Spacing();
|
2020-08-20 18:29:23 +02:00
|
|
|
}
|
2020-06-10 04:13:56 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
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-09-20 00:19:39 +02:00
|
|
|
ImGui::Text("Center Frequency: %.0f Hz", gui::waterfall.getCenterFrequency());
|
2020-08-21 17:11:12 +02:00
|
|
|
ImGui::Text("Source name: %s", sourceName.c_str());
|
2020-08-21 15:34:50 +02:00
|
|
|
ImGui::Spacing();
|
|
|
|
}
|
2020-06-10 04:13:56 +02:00
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
ImGui::EndChild();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// When hiding the menu bar
|
|
|
|
ImGui::Columns(3, "WindowColumns", false);
|
|
|
|
ImGui::SetColumnWidth(0, 8);
|
|
|
|
ImGui::SetColumnWidth(1, winSize.x - 8 - 60);
|
|
|
|
ImGui::SetColumnWidth(2, 60);
|
|
|
|
}
|
2020-06-10 04:13:56 +02:00
|
|
|
|
|
|
|
// Right Column
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
2020-06-10 04:13:56 +02:00
|
|
|
ImGui::NextColumn();
|
2020-08-17 02:39:56 +02:00
|
|
|
ImGui::PopStyleVar();
|
|
|
|
|
2020-06-10 04:13:56 +02:00
|
|
|
ImGui::BeginChild("Waterfall");
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.draw();
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-06-10 04:13:56 +02:00
|
|
|
ImGui::EndChild();
|
2020-07-19 15:59:44 +02:00
|
|
|
|
2020-08-20 18:29:23 +02:00
|
|
|
|
2020-07-19 15:59:44 +02:00
|
|
|
ImGui::NextColumn();
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::BeginChild("WaterfallControls");
|
2020-07-19 15:59:44 +02:00
|
|
|
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - (ImGui::CalcTextSize("Zoom").x / 2.0f));
|
2020-07-19 15:59:44 +02:00
|
|
|
ImGui::Text("Zoom");
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - 10);
|
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
|
|
|
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - (ImGui::CalcTextSize("Max").x / 2.0f));
|
2020-07-19 18:09:59 +02:00
|
|
|
ImGui::Text("Max");
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - 10);
|
|
|
|
if (ImGui::VSliderFloat("##_8_", ImVec2(20.0f, 150.0f), &fftMax, 0.0f, -100.0f, "")) {
|
2020-08-17 02:39:56 +02:00
|
|
|
fftMax = std::max<float>(fftMax, fftMin + 10);
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["max"] = fftMax;
|
|
|
|
core::configManager.release(true);
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
2020-07-19 15:59:44 +02:00
|
|
|
|
|
|
|
ImGui::NewLine();
|
2020-07-19 18:09:59 +02:00
|
|
|
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - (ImGui::CalcTextSize("Min").x / 2.0f));
|
2020-07-19 18:09:59 +02:00
|
|
|
ImGui::Text("Min");
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - 10);
|
|
|
|
if (ImGui::VSliderFloat("##_9_", ImVec2(20.0f, 150.0f), &fftMin, 0.0f, -100.0f, "")) {
|
2020-08-17 02:39:56 +02:00
|
|
|
fftMin = std::min<float>(fftMax - 10, fftMin);
|
2020-09-24 19:36:57 +02:00
|
|
|
core::configManager.aquire();
|
|
|
|
core::configManager.conf["min"] = fftMin;
|
|
|
|
core::configManager.release(true);
|
2020-08-16 03:39:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndChild();
|
2020-07-19 15:59:44 +02:00
|
|
|
|
2020-08-04 21:34:56 +02:00
|
|
|
if (bw.changed()) {
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setViewBandwidth(bw.val);
|
|
|
|
gui::waterfall.setViewOffset(vfo->centerOffset);
|
2020-07-19 15:59:44 +02:00
|
|
|
}
|
|
|
|
|
2020-09-20 00:19:39 +02:00
|
|
|
gui::waterfall.setFFTMin(fftMin);
|
|
|
|
gui::waterfall.setFFTMax(fftMax);
|
|
|
|
gui::waterfall.setWaterfallMin(fftMin);
|
|
|
|
gui::waterfall.setWaterfallMax(fftMax);
|
2020-08-16 03:39:05 +02:00
|
|
|
|
2020-08-20 18:29:23 +02:00
|
|
|
|
2020-08-16 03:39:05 +02:00
|
|
|
ImGui::End();
|
|
|
|
|
|
|
|
if (showCredits) {
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 20.0f));
|
|
|
|
ImGui::OpenPopup("Credits");
|
|
|
|
ImGui::BeginPopupModal("Credits", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
|
|
|
|
|
|
|
|
ImGui::PushFont(bigFont);
|
|
|
|
ImGui::Text("SDR++ ");
|
|
|
|
ImGui::PopFont();
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Image(icons::LOGO, ImVec2(128, 128));
|
|
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Spacing();
|
|
|
|
|
|
|
|
ImGui::Text("This software is brought to you by\n\n");
|
|
|
|
|
|
|
|
ImGui::Columns(3, "CreditColumns", true);
|
|
|
|
|
|
|
|
// Contributors
|
|
|
|
ImGui::Text("Contributors");
|
|
|
|
ImGui::BulletText("Ryzerth (Creator)");
|
|
|
|
ImGui::BulletText("aosync");
|
|
|
|
ImGui::BulletText("Benjamin Kyd");
|
|
|
|
ImGui::BulletText("Tobias Mädel");
|
|
|
|
ImGui::BulletText("Raov");
|
2020-09-06 16:31:50 +02:00
|
|
|
ImGui::BulletText("Howard0su");
|
2020-08-16 03:39:05 +02:00
|
|
|
|
|
|
|
// Libraries
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("Libraries");
|
|
|
|
ImGui::BulletText("SoapySDR (PothosWare)");
|
|
|
|
ImGui::BulletText("Dear ImGui (ocornut)");
|
|
|
|
ImGui::BulletText("spdlog (gabime)");
|
|
|
|
ImGui::BulletText("json (nlohmann)");
|
|
|
|
ImGui::BulletText("portaudio (PA Comm.)");
|
|
|
|
|
|
|
|
// Patrons
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("Patrons");
|
|
|
|
ImGui::BulletText("SignalsEverywhere");
|
|
|
|
|
|
|
|
ImGui::EndPopup();
|
|
|
|
ImGui::PopStyleVar(1);
|
|
|
|
}
|
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
|
|
|
}
|