diff --git a/CMakeLists.txt b/CMakeLists.txt index eb3c5b5..2eb316e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 2.8) SET(CUBICSDR_VERSION_MAJOR "0") SET(CUBICSDR_VERSION_MINOR "1") -SET(CUBICSDR_VERSION_PATCH "1") +SET(CUBICSDR_VERSION_PATCH "2") SET(CUBICSDR_VERSION_REL "beta") SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}-${CUBICSDR_VERSION_REL}") diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index d9c4f20..925e7a3 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -38,7 +38,7 @@ EVT_IDLE(AppFrame::OnIdle) wxEND_EVENT_TABLE() AppFrame::AppFrame() : -wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { + wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { #ifdef __linux__ SetIcon(wxICON(cubicsdr)); @@ -50,13 +50,7 @@ wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL); - int attribList[] = { - WX_GL_RGBA, - WX_GL_STENCIL_SIZE, 8, - WX_GL_BUFFER_SIZE, 24, - WX_GL_DOUBLEBUFFER, - 0 - }; + int attribList[] = { WX_GL_RGBA, WX_GL_STENCIL_SIZE, 8, WX_GL_BUFFER_SIZE, 24, WX_GL_DOUBLEBUFFER, 0 }; demodModeSelector = new ModeSelectorCanvas(this, attribList); demodModeSelector->addChoice(DEMOD_TYPE_FM, "FM"); @@ -164,7 +158,6 @@ wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { i++; } - for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE + mdevices_i->first, mdevices_i->second.name, wxT("Description?")); itm->SetId(wxID_RT_AUDIO_DEVICE + mdevices_i->first); @@ -232,31 +225,30 @@ wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { menuBar->Append(menu, wxT("Input &Device")); } - menu = new wxMenu; - - #define NUM_RATES_DEFAULT 4 +#define NUM_RATES_DEFAULT 4 int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 }; for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { - int desired_rate = 0; - int desired_rank = NUM_RATES_DEFAULT+1; + int desired_rate = 0; + int desired_rank = NUM_RATES_DEFAULT + 1; - for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); srate++) { - for (i = 0; i < NUM_RATES_DEFAULT; i++) { - if (desired_rates[i] == (*srate)) { - if (desired_rank > i) { - desired_rank = i; - desired_rate = (*srate); - } - } - } + for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); + srate++) { + for (i = 0; i < NUM_RATES_DEFAULT; i++) { + if (desired_rates[i] == (*srate)) { + if (desired_rank > i) { + desired_rank = i; + desired_rate = (*srate); + } + } + } } - if (desired_rank > NUM_RATES_DEFAULT) { - desired_rate = mdevices_i->second.sampleRates.back(); - } + if (desired_rank > NUM_RATES_DEFAULT) { + desired_rate = mdevices_i->second.sampleRates.back(); + } AudioThread::deviceSampleRate[mdevices_i->first] = desired_rate; } @@ -264,18 +256,19 @@ wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { new wxMenu; int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first; wxMenu *subMenu = new wxMenu; - menu->AppendSubMenu(subMenu,mdevices_i->second.name, wxT("Description?")); + menu->AppendSubMenu(subMenu, mdevices_i->second.name, wxT("Description?")); int j = 0; - for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); srate++) { + for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); + srate++) { std::stringstream srateName; - srateName << ((float)(*srate)/1000.0f) << "kHz"; - wxMenuItem *itm = subMenu->AppendRadioItem(menu_id+j, srateName.str(), wxT("Description?")); + srateName << ((float) (*srate) / 1000.0f) << "kHz"; + wxMenuItem *itm = subMenu->AppendRadioItem(menu_id + j, srateName.str(), wxT("Description?")); if ((*srate) == AudioThread::deviceSampleRate[mdevices_i->first]) { itm->Check(true); } - audioSampleRateMenuItems[menu_id+j] = itm; + audioSampleRateMenuItems[menu_id + j] = itm; j++; } @@ -283,7 +276,6 @@ wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { menuBar->Append(menu, wxT("Audio &Bandwidth")); - SetMenuBar(menuBar); CreateStatusBar(); @@ -291,7 +283,6 @@ wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { Centre(); Show(); - #ifdef _WIN32 SetIcon(wxICON(frame_icon)); #endif @@ -398,7 +389,6 @@ void AppFrame::OnMenu(wxCommandEvent& event) { wxGetApp().setDevice(event.GetId() - wxID_DEVICE_ID); } - if (event.GetId() >= wxID_AUDIO_BANDWIDTH_BASE) { int evId = event.GetId(); std::vector::iterator devices_i; @@ -406,21 +396,22 @@ void AppFrame::OnMenu(wxCommandEvent& event) { int i = 0; for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { - int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first; + int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first; - int j = 0; - for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); srate++) { + int j = 0; + for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); + srate++) { - if (evId == menu_id + j) { - //audioSampleRateMenuItems[menu_id+j]; - //std::cout << "Would set audio sample rate on device " << mdevices_i->second.name << " (" << mdevices_i->first << ") to " << (*srate) << "Hz" << std::endl; - AudioThread::setDeviceSampleRate(mdevices_i->first, *srate); - } + if (evId == menu_id + j) { + //audioSampleRateMenuItems[menu_id+j]; + //std::cout << "Would set audio sample rate on device " << mdevices_i->second.name << " (" << mdevices_i->first << ") to " << (*srate) << "Hz" << std::endl; + AudioThread::setDeviceSampleRate(mdevices_i->first, *srate); + } - j++; - } - i++; - } + j++; + } + i++; + } } } @@ -463,12 +454,12 @@ void AppFrame::OnIdle(wxIdleEvent& event) { if (demod->getDemodulatorType() == DEMOD_TYPE_USB) { demodBw /= 2; - centerFreq += demod->getBandwidth()/4; + centerFreq += demod->getBandwidth() / 4; } if (demod->getDemodulatorType() == DEMOD_TYPE_LSB) { demodBw /= 2; - centerFreq -= demod->getBandwidth()/4; + centerFreq -= demod->getBandwidth() / 4; } if (demodBw > wxGetApp().getSampleRate() / 2) { @@ -520,6 +511,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) { demodWaterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); demodSpectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); } + if (spectrumCanvas->getViewState() && abs(wxGetApp().getFrequency()-spectrumCanvas->getCenterFrequency()) > (wxGetApp().getSampleRate()/2)) { + spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); + waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); + } } if (!waterfallCanvas->HasFocus()) { diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index de171dc..e06a4f3 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -1,6 +1,6 @@ #pragma once -#define CUBICSDR_TITLE "CubicSDR " CUBICSDR_VERSION " by Charles J. Cliffe (@ccliffe)" +#define CUBICSDR_TITLE "CubicSDR v" CUBICSDR_VERSION " by Charles J. Cliffe (@ccliffe) :: www.cubicsdr.com" #ifndef __BYTE_ORDER #ifdef _WIN32 diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index 12a27fb..52b4047 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -236,9 +236,9 @@ int DemodulatorInstance::getOutputDevice() { } void DemodulatorInstance::checkBandwidth() { - if ((currentDemodType == DEMOD_TYPE_USB || currentDemodType == DEMOD_TYPE_LSB) && (getBandwidth() % 2)) { - setBandwidth(getBandwidth()+1); - } +// if ((currentDemodType == DEMOD_TYPE_USB || currentDemodType == DEMOD_TYPE_LSB) && (getBandwidth() % 2)) { +// setBandwidth(getBandwidth()+1); +// } } void DemodulatorInstance::setDemodulatorType(int demod_type_in) { @@ -335,3 +335,11 @@ void DemodulatorInstance::setGain(float gain_in) { float DemodulatorInstance::getGain() { return audioThread->getGain(); } + +bool DemodulatorInstance::isFollow() { + return follow; +} + +void DemodulatorInstance::setFollow(bool follow) { + this->follow = follow; +} diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index 15743b6..5f53a38 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -77,6 +77,8 @@ public: void setAudioSampleRate(int sampleRate); int getAudioSampleRate(); + bool isFollow(); + void setFollow(bool follow); private: @@ -96,4 +98,5 @@ private: int currentDemodType; int currentOutputDevice; int currentAudioSampleRate; + bool follow; }; diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp index 3d5345d..1dad193 100644 --- a/src/demod/DemodulatorPreThread.cpp +++ b/src/demod/DemodulatorPreThread.cpp @@ -151,12 +151,10 @@ void DemodulatorPreThread::threadMain() { } // Requested frequency is not center, shift it into the center! - if (inp->frequency != params.frequency) { - if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) { - shiftFrequency = params.frequency - inp->frequency; - if (abs(shiftFrequency) <= (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) { - nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) wxGetApp().getSampleRate()))); - } + if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) { + shiftFrequency = params.frequency - inp->frequency; + if (abs(shiftFrequency) <= (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) { + nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) wxGetApp().getSampleRate()))); } } diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 34b8942..f07b2b2 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -185,15 +185,18 @@ void SDRPostThread::threadMain() { DemodulatorInstance *demod = *i; DemodulatorThreadInputQueue *demodQueue = demod->threadQueueDemod; - if (demod->getFrequency() != data_in->frequency - && abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) { - if (demod->isActive()) { + if (abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) { + if (demod->isActive() && !demod->isFollow()) { demod->setActive(false); DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData; dummyDataOut->frequency = data_in->frequency; dummyDataOut->sampleRate = data_in->sampleRate; demodQueue->push(dummyDataOut); } + + if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) { + wxGetApp().setFrequency(demod->getFrequency()); + } } else if (!demod->isActive()) { demod->setActive(true); if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) { @@ -204,6 +207,9 @@ void SDRPostThread::threadMain() { if (!demod->isActive()) { continue; } + if (demod->isFollow()) { + demod->setFollow(false); + } demodQueue->push(demodDataOut); pushedData = true; diff --git a/src/visual/ColorTheme.cpp b/src/visual/ColorTheme.cpp index bbb438c..65b05b5 100644 --- a/src/visual/ColorTheme.cpp +++ b/src/visual/ColorTheme.cpp @@ -48,7 +48,10 @@ DefaultColorTheme::DefaultColorTheme() { fftLine = RGBColor(0.9, 0.9, 0.9); fftHighlight = RGBColor(1, 1, 1); scopeLine = RGBColor(0.9, 0.9, 0.9); - tuningBar = RGBColor(0.2, 0.2, 0.9); + tuningBarLight = RGBColor(0.2, 0.2, 0.9); + tuningBarDark = RGBColor(0.0, 0.0, 0.35); + tuningBarUp = RGBColor(1.0, 139.0/255.0, 96.0/255.0); + tuningBarDown = RGBColor(148.0/255.0, 148.0/255.0, 1.0); meterLevel = RGBColor(0.1, 0.75, 0.1); meterValue = RGBColor(0.75, 0.1, 0.1); text = RGBColor(1, 1, 1); @@ -76,7 +79,10 @@ RadarColorTheme::RadarColorTheme() { fftLine = RGBColor(0.8, 1.0, 0.8); fftHighlight = RGBColor(1, 1, 1); scopeLine = RGBColor(0.8, 1.0, 0.8); - tuningBar = RGBColor(0.2, 0.9, 0.2); + tuningBarLight = RGBColor(0.0, 0.45, 0.0); + tuningBarDark = RGBColor(0.0, 0.1, 0.0); + tuningBarUp = RGBColor(1.0, 139.0/255.0, 96.0/255.0); + tuningBarDown = RGBColor(148.0/255.0, 0.0, 0.0); meterLevel = RGBColor(0, 0.5, 0); meterValue = RGBColor(0, 0.5, 0); text = RGBColor(0.8, 1.0, 0.8); @@ -102,7 +108,10 @@ BlackAndWhiteColorTheme::BlackAndWhiteColorTheme() { fftLine = RGBColor(0.9, 0.9, 0.9); fftHighlight = RGBColor(1, 1, 0.9); scopeLine = RGBColor(0.9, 0.9, 0.9); - tuningBar = RGBColor(0.4, 0.4, 0.4); + tuningBarLight = RGBColor(0.4, 0.4, 0.4); + tuningBarDark = RGBColor(0.1, 0.1, 0.1); + tuningBarUp = RGBColor(0.8, 0.8, 0.8); + tuningBarDown = RGBColor(0.4, 0.4, 0.4); meterLevel = RGBColor(0.5, 0.5, 0.5); meterValue = RGBColor(0.5, 0.5, 0.5); text = RGBColor(1, 1, 1); @@ -137,7 +146,10 @@ SharpColorTheme::SharpColorTheme() { fftLine = RGBColor(0.9, 0.9, 1.0); fftHighlight = RGBColor(0.9, 0.9, 1.0); scopeLine = RGBColor(0.85, 0.85, 1.0); - tuningBar = RGBColor(0.2, 0.2, 0.9); + tuningBarLight = RGBColor(28.0 / 255.0, 106.0 / 255.0, 179.0 / 255.0); + tuningBarDark = RGBColor(14.0 / 255.0, 53.0 / 255.0, 89.5 / 255.0); + tuningBarUp = RGBColor(0.7, 0.7, 0.7); + tuningBarDown = RGBColor(1.0, 0.0, 0.0); meterLevel = RGBColor(28.0 / 255.0, 106.0 / 255.0, 179.0 / 255.0); meterValue = RGBColor(190.0 / 255.0, 190.0 / 255.0, 60.0 / 255.0); text = RGBColor(0.9, 0.9, 1); @@ -165,7 +177,10 @@ RadColorTheme::RadColorTheme() { fftLine = RGBColor(1.0, 0.9, 0.9); fftHighlight = RGBColor(1, 1, 1); scopeLine = RGBColor(1.0, 0.9, 0.9); - tuningBar = RGBColor(0.2, 0.2, 0.9); + tuningBarLight = RGBColor(0.0, 0.45, 0.0); + tuningBarDark = RGBColor(0.0, 0.1, 0.0); + tuningBarUp = RGBColor(1.0, 0.0, 0.0); + tuningBarDown = RGBColor(0.0, 0.5, 1.0); meterLevel = RGBColor(0, 0.5, 0); meterValue = RGBColor(0.5, 0, 0); text = RGBColor(1, 1, 1); @@ -196,7 +211,10 @@ TouchColorTheme::TouchColorTheme() { fftLine = RGBColor(234.0 / 255.0, 232.0 / 255.0, 247.0 / 255.0); fftHighlight = RGBColor(1.0, 1.0, 1.0); scopeLine = RGBColor(234.0 / 255.0, 232.0 / 255.0, 247.0 / 255.0); - tuningBar = RGBColor(61.0 / 255.0, 57.0 / 255.0, 88.0 / 255.0); + tuningBarLight = RGBColor(0.2, 0.2, 0.7); + tuningBarDark = RGBColor(0.1, 0.1, 0.45); + tuningBarUp = RGBColor(0.5, 139.0/255.0, 96.0/255.0); + tuningBarDown = RGBColor(0.6, 108.0/255.0, 1.0); meterLevel = RGBColor(61.0 / 255.0, 57.0 / 255.0, 88.0 / 255.0); meterValue = RGBColor(61.0 / 255.0, 57.0 / 255.0, 88.0 / 255.0); text = RGBColor(1, 1, 1); @@ -228,7 +246,10 @@ HDColorTheme::HDColorTheme() { fftLine = RGBColor(0.9, 0.9, 0.9); fftHighlight = RGBColor(1, 1, 1); scopeLine = RGBColor(0.9, 0.9, 0.9); - tuningBar = RGBColor(0, 0.7, 0.7); + tuningBarLight = RGBColor(0.4, 0.4, 1.0); + tuningBarDark = RGBColor(0.1, 0.1, 0.45); + tuningBarUp = RGBColor(1.0, 139.0/255.0, 96.0/255.0); + tuningBarDown = RGBColor(148.0/255.0, 148.0/255.0, 1.0); meterLevel = RGBColor(0, 0.5, 0); meterValue = RGBColor(0.0, 0.0, 1.0); text = RGBColor(1, 1, 1); diff --git a/src/visual/ColorTheme.h b/src/visual/ColorTheme.h index 398dd3f..f7d44e0 100644 --- a/src/visual/ColorTheme.h +++ b/src/visual/ColorTheme.h @@ -47,7 +47,10 @@ public: RGBColor fftLine; RGBColor fftHighlight; RGBColor scopeLine; - RGBColor tuningBar; + RGBColor tuningBarLight; + RGBColor tuningBarDark; + RGBColor tuningBarUp; + RGBColor tuningBarDown; RGBColor meterLevel; RGBColor meterValue; RGBColor text; diff --git a/src/visual/InteractiveCanvas.cpp b/src/visual/InteractiveCanvas.cpp index 226d8f5..9a24165 100644 --- a/src/visual/InteractiveCanvas.cpp +++ b/src/visual/InteractiveCanvas.cpp @@ -41,6 +41,10 @@ void InteractiveCanvas::disableView() { lastBandwidth = 0; } +bool InteractiveCanvas::getViewState() { + return isView; +} + long long InteractiveCanvas::getFrequencyAt(float x) { long long iqCenterFreq = getCenterFrequency(); long long iqBandwidth = getBandwidth(); @@ -73,6 +77,10 @@ unsigned int InteractiveCanvas::getBandwidth() { } } +MouseTracker *InteractiveCanvas::getMouseTracker() { + return &mouseTracker; +} + void InteractiveCanvas::OnKeyUp(wxKeyEvent& event) { shiftDown = event.ShiftDown(); altDown = event.AltDown(); diff --git a/src/visual/InteractiveCanvas.h b/src/visual/InteractiveCanvas.h index 6699d49..055dcc9 100644 --- a/src/visual/InteractiveCanvas.h +++ b/src/visual/InteractiveCanvas.h @@ -15,6 +15,7 @@ public: void setView(long long center_freq_in, int bandwidth_in); void disableView(); + bool getViewState(); void setCenterFrequency(long long center_freq_in); long long getCenterFrequency(); @@ -22,6 +23,8 @@ public: void setBandwidth(unsigned int bandwidth_in); unsigned int getBandwidth(); + MouseTracker *getMouseTracker(); + protected: void OnKeyDown(wxKeyEvent& event); void OnKeyUp(wxKeyEvent& event); diff --git a/src/visual/ScopeContext.cpp b/src/visual/ScopeContext.cpp index 9c5bf2e..6b72c4a 100644 --- a/src/visual/ScopeContext.cpp +++ b/src/visual/ScopeContext.cpp @@ -73,6 +73,19 @@ void ScopeContext::Plot(std::vector &points, bool stereo) { } glLineWidth(1.0); + + GLint vp[4]; + glGetIntegerv(GL_VIEWPORT, vp); + float viewHeight = (float) vp[3]; + float hPos = (float) (13) / viewHeight; + + glColor3f(0.65, 0.65, 0.65); + + getFont(PrimaryGLContext::GLFONT_SIZE12).drawString("Frequency", -0.66, -1.0+hPos, 12, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); + getFont(PrimaryGLContext::GLFONT_SIZE12).drawString("Bandwidth", 0.0, -1.0+hPos, 12, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); + getFont(PrimaryGLContext::GLFONT_SIZE12).drawString("Center Frequency", 0.66, -1.0+hPos, 12, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); + + if (stereo) { glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r, ThemeMgr::mgr.currentTheme->scopeLine.g, ThemeMgr::mgr.currentTheme->scopeLine.b); glBegin (GL_LINES); diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp index 2b16016..b3f950a 100644 --- a/src/visual/TuningCanvas.cpp +++ b/src/visual/TuningCanvas.cpp @@ -25,9 +25,24 @@ EVT_ENTER_WINDOW(TuningCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() TuningCanvas::TuningCanvas(wxWindow *parent, int *attribList) : - InteractiveCanvas(parent, attribList), dragAccum(0) { + InteractiveCanvas(parent, attribList), dragAccum(0), top(false), bottom(false), uxDown(0) { glContext = new TuningContext(this, &wxGetApp().GetContext(this)); + + hoverIndex = 0; + downIndex = 0; + hoverState = TUNING_HOVER_NONE; + downState = TUNING_HOVER_NONE; + dragging = false; + + freqDP = -1.0; + freqW = (1.0 / 3.0) * 2.0; + + bwDP = -1.0 + (2.25 / 3.0); + bwW = (1.0 / 4.0) * 2.0; + + centerDP = -1.0 + (2.0 / 3.0) * 2.0; + centerW = (1.0 / 3.0) * 2.0; } TuningCanvas::~TuningCanvas() { @@ -49,59 +64,139 @@ void TuningCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); + long long freq = 0; if (activeDemod != NULL) { - glContext->DrawDemodFreqBw(activeDemod->getFrequency(), activeDemod->getBandwidth(), wxGetApp().getFrequency()); - } else { - glContext->DrawDemodFreqBw(0, wxGetApp().getDemodMgr().getLastBandwidth(), wxGetApp().getFrequency()); + freq = activeDemod->getFrequency(); } + long long bw = wxGetApp().getDemodMgr().getLastBandwidth(); + long long center = wxGetApp().getFrequency(); if (mouseTracker.mouseDown()) { - glContext->Draw(ThemeMgr::mgr.currentTheme->tuningBar.r, ThemeMgr::mgr.currentTheme->tuningBar.g, ThemeMgr::mgr.currentTheme->tuningBar.b, - 0.6, mouseTracker.getOriginMouseX(), mouseTracker.getMouseX()); + glContext->Draw(ThemeMgr::mgr.currentTheme->tuningBarDark.r, ThemeMgr::mgr.currentTheme->tuningBarDark.g, ThemeMgr::mgr.currentTheme->tuningBarDark.b, + 0.75, mouseTracker.getOriginMouseX(), mouseTracker.getMouseX()); } + RGBColor clr = top ? ThemeMgr::mgr.currentTheme->tuningBarUp : ThemeMgr::mgr.currentTheme->tuningBarDown; + + RGBColor clrDark = ThemeMgr::mgr.currentTheme->tuningBarDark; + RGBColor clrMid = ThemeMgr::mgr.currentTheme->tuningBarLight; + + glContext->DrawTunerBarIndexed(1, 3, 11, freqDP, freqW, clrMid, 0.25, true, true); // freq + glContext->DrawTunerBarIndexed(4, 6, 11, freqDP, freqW, clrDark, 0.25, true, true); + glContext->DrawTunerBarIndexed(7, 9, 11, freqDP, freqW, clrMid, 0.25, true, true); + glContext->DrawTunerBarIndexed(10, 11, 11, freqDP, freqW, clrDark, 0.25, true, true); + + glContext->DrawTunerBarIndexed(1, 3, 7, bwDP, bwW, clrMid, 0.25, true, true); // bw + glContext->DrawTunerBarIndexed(4, 6, 7, bwDP, bwW, clrDark, 0.25, true, true); + glContext->DrawTunerBarIndexed(7, 7, 7, bwDP, bwW, clrMid, 0.25, true, true); + + glContext->DrawTunerBarIndexed(1, 3, 11, centerDP, centerW, clrMid, 0.25, true, true); // freq + glContext->DrawTunerBarIndexed(4, 6, 11, centerDP, centerW, clrDark, 0.25, true, true); + glContext->DrawTunerBarIndexed(7, 9, 11, centerDP, centerW, clrMid, 0.25, true, true); + glContext->DrawTunerBarIndexed(10, 11, 11, centerDP, centerW, clrDark, 0.25, true, true); + + if (hoverIndex > 0 && !mouseTracker.mouseDown()) { + switch (hoverState) { + + case TUNING_HOVER_FREQ: + glContext->DrawTunerBarIndexed(hoverIndex, hoverIndex, 11, freqDP, freqW, clr, 0.25, top, bottom); // freq + break; + case TUNING_HOVER_BW: + glContext->DrawTunerBarIndexed(hoverIndex, hoverIndex, 7, bwDP, bwW, clr, 0.25, top, bottom); // bw + break; + case TUNING_HOVER_CENTER: + glContext->DrawTunerBarIndexed(hoverIndex, hoverIndex, 11, centerDP, centerW, clr, 0.25, top, bottom); // center + break; + case TUNING_HOVER_NONE: + break; + } + } + + glContext->DrawTuner(freq, 11, freqDP, freqW); + glContext->DrawTuner(bw, 7, bwDP, bwW); + glContext->DrawTuner(center, 11, centerDP, centerW); + glContext->DrawEnd(); SwapBuffers(); } -void TuningCanvas::OnIdle(wxIdleEvent &event) { - if (mouseTracker.mouseDown()) { - DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); +void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) { + double exp = pow(10, exponent); + long long amount = up?exp:-exp; - float dragDelta = mouseTracker.getMouseX() - mouseTracker.getOriginMouseX(); + DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); + if (state == TUNING_HOVER_FREQ && activeDemod) { + long long freq = activeDemod->getFrequency(); + long long diff = abs(wxGetApp().getFrequency() - freq); - dragAccum += dragDelta; - - float moveVal = dragAccum * 10.0; - - if (uxDown > 0.275) { - wxGetApp().setFrequency( - wxGetApp().getFrequency() - + (int) (dragAccum * fabs(dragAccum * 10.0) * fabs(dragAccum * 10.0) * (float) wxGetApp().getSampleRate())); - } else if (fabs(moveVal) >= 1.0) { - if (uxDown < -0.275) { - if (activeDemod != NULL) { - long long freq = activeDemod->getFrequency() + (int) (moveVal * fabs(moveVal) * fabs(moveVal) * fabs(moveVal)); - activeDemod->setFrequency(freq); - activeDemod->updateLabel(freq); - } - } else { - int amt = (int) (moveVal * fabs(moveVal) * fabs(moveVal) * fabs(moveVal)); - if (activeDemod != NULL) { - activeDemod->setBandwidth(activeDemod->getBandwidth() + amt); - } else { - wxGetApp().getDemodMgr().setLastBandwidth(wxGetApp().getDemodMgr().getLastBandwidth() + amt); - } - } + if (shiftDown) { + bool carried = (long long)((freq) / (exp * 10)) != (long long)((freq + amount) / (exp * 10)) || (bottom && freq < exp); + freq += carried?(9*-amount):amount; + } else { + freq += amount; } - while (fabs(dragAccum * 10.0) >= 1.0) { - if (dragAccum > 0) { - dragAccum -= 1.0 / 10.0; - } else { - dragAccum += 1.0 / 10.0; + if (wxGetApp().getSampleRate() / 2 < diff) { + wxGetApp().setFrequency(freq); + } + + activeDemod->setFrequency(freq); + activeDemod->updateLabel(freq); + activeDemod->setFollow(true); + } + + if (state == TUNING_HOVER_BW) { + long bw = wxGetApp().getDemodMgr().getLastBandwidth(); + + if (shiftDown) { + bool carried = (long)((bw) / (exp * 10)) != (long)((bw + amount) / (exp * 10)) || (bottom && bw < exp); + bw += carried?(9*-amount):amount; + } else { + bw += amount; + } + + if (bw > wxGetApp().getSampleRate()) { + bw = wxGetApp().getSampleRate(); + } + + wxGetApp().getDemodMgr().setLastBandwidth(bw); + + if (activeDemod) { + activeDemod->setBandwidth(wxGetApp().getDemodMgr().getLastBandwidth()); + } + } + + if (state == TUNING_HOVER_CENTER) { + long long ctr = wxGetApp().getFrequency(); + if (shiftDown) { + bool carried = (long long)((ctr) / (exp * 10)) != (long long)((ctr + amount) / (exp * 10)) || (bottom && ctr < exp); + ctr += carried?(9*-amount):amount; + } else { + ctr += amount; + } + + wxGetApp().setFrequency(ctr); + } +} + +void TuningCanvas::OnIdle(wxIdleEvent &event) { + if (mouseTracker.mouseDown()) { + if (downState != TUNING_HOVER_NONE) { + dragAccum += 5.0*mouseTracker.getOriginDeltaMouseX(); + while (dragAccum > 1.0) { + StepTuner(downState, downIndex-1, true); + dragAccum -= 1.0; + dragging = true; } + while (dragAccum < -1.0) { + StepTuner(downState, downIndex-1, false); + dragAccum += 1.0; + dragging = true; + } + } else { + dragAccum = 0; + dragging = false; } } @@ -110,16 +205,63 @@ void TuningCanvas::OnIdle(wxIdleEvent &event) { void TuningCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); + + int index = 0; + + top = mouseTracker.getMouseY() >= 0.5; + bottom = mouseTracker.getMouseY() <= 0.5; + + index = glContext->GetTunerDigitIndex(mouseTracker.getMouseX(), 11, freqDP, freqW); // freq + if (index > 0) { + hoverIndex = index; + hoverState = TUNING_HOVER_FREQ; + } + + if (!index) { + index = glContext->GetTunerDigitIndex(mouseTracker.getMouseX(), 7, bwDP, bwW); // bw + if (index > 0) { + hoverIndex = index; + hoverState = TUNING_HOVER_BW; + } + } + + if (!index) { + index = glContext->GetTunerDigitIndex(mouseTracker.getMouseX(), 11, centerDP, centerW); // center + if (index > 0) { + hoverIndex = index; + hoverState = TUNING_HOVER_CENTER; + } + } + + if (!index) { + hoverIndex = 0; + hoverState = TUNING_HOVER_NONE; + } else { + switch (hoverState) { + case TUNING_HOVER_FREQ: + setStatusText("Click or drag a digit to change frequency. Hold shift to disable carry."); + break; + case TUNING_HOVER_BW: + setStatusText("Click or drag a digit to change bandwidth. Hold shift to disable carry."); + break; + case TUNING_HOVER_CENTER: + setStatusText("Click or drag a digit to change center frequency. Hold shift to disable carry."); + break; + } + } + + } void TuningCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); - mouseTracker.setVertDragLock(true); uxDown = 2.0 * (mouseTracker.getMouseX() - 0.5); - dragAccum = 0; - SetCursor(wxCURSOR_IBEAM); + + mouseTracker.setVertDragLock(true); + downIndex = hoverIndex; + downState = hoverState; } void TuningCanvas::OnMouseWheelMoved(wxMouseEvent& event) { @@ -127,19 +269,38 @@ void TuningCanvas::OnMouseWheelMoved(wxMouseEvent& event) { } void TuningCanvas::OnMouseReleased(wxMouseEvent& event) { + GLint vp[4]; + glGetIntegerv( GL_VIEWPORT, vp); + + float viewHeight = (float) vp[3]; + float viewWidth = (float) vp[2]; + InteractiveCanvas::OnMouseReleased(event); + + int hExponent = hoverIndex - 1; + + if (hoverState != TUNING_HOVER_NONE && !dragging && (downState == hoverState) && (downIndex == hoverIndex)) { + StepTuner(hoverState, hExponent, top); + } + mouseTracker.setVertDragLock(false); - SetCursor(wxCURSOR_SIZEWE); + + dragging = false; + SetCursor(wxCURSOR_ARROW); } void TuningCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); SetCursor(wxCURSOR_CROSS); + hoverIndex = 0; + hoverState = TUNING_HOVER_NONE; } void TuningCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event); - SetCursor(wxCURSOR_SIZEWE); + SetCursor(wxCURSOR_ARROW); + hoverIndex = 0; + hoverState = TUNING_HOVER_NONE; } void TuningCanvas::setHelpTip(std::string tip) { diff --git a/src/visual/TuningCanvas.h b/src/visual/TuningCanvas.h index 1bd23ee..21b22c5 100644 --- a/src/visual/TuningCanvas.h +++ b/src/visual/TuningCanvas.h @@ -15,6 +15,9 @@ class TuningCanvas: public InteractiveCanvas { public: + enum ActiveState { + TUNING_HOVER_NONE, TUNING_HOVER_FREQ, TUNING_HOVER_BW, TUNING_HOVER_CENTER + }; TuningCanvas(wxWindow *parent, int *attribList = NULL); ~TuningCanvas(); @@ -30,12 +33,31 @@ private: void OnMouseReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); + void StepTuner(ActiveState state, int factor, bool up = true); TuningContext *glContext; std::string helpTip; float dragAccum; float uxDown; + ActiveState hoverState; + ActiveState downState; + int hoverIndex; + int downIndex; + bool dragging; + + float freqDP; + float freqW; + + float bwDP; + float bwW; + + float centerDP; + float centerW; + + bool top; + bool bottom; + // wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/TuningContext.cpp b/src/visual/TuningContext.cpp index f348c70..9a4dd4c 100644 --- a/src/visual/TuningContext.cpp +++ b/src/visual/TuningContext.cpp @@ -24,11 +24,12 @@ TuningContext::TuningContext(TuningCanvas *canvas, wxGLContext *sharedContext) : glLoadIdentity(); comma_locale = std::locale(std::locale(), new comma_numpunct()); - freqStr.imbue(comma_locale); + freqStrFormatted.imbue(comma_locale); } void TuningContext::DrawBegin() { - glClearColor(ThemeMgr::mgr.currentTheme->generalBackground.r, ThemeMgr::mgr.currentTheme->generalBackground.g, ThemeMgr::mgr.currentTheme->generalBackground.b, 1.0); + glClearColor(ThemeMgr::mgr.currentTheme->generalBackground.r, ThemeMgr::mgr.currentTheme->generalBackground.g, + ThemeMgr::mgr.currentTheme->generalBackground.b, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); @@ -41,18 +42,18 @@ void TuningContext::Draw(float r, float g, float b, float a, float p1, float p2) glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); glBegin(GL_QUADS); - glColor4f(r*0.5, g*0.5, b*0.5, a); - glVertex2f(-1.0+p2*2.0, 1.0); - glVertex2f(-1.0+p1*2.0, 1.0); + glColor4f(r * 0.5, g * 0.5, b * 0.5, a); + glVertex2f(-1.0 + p2 * 2.0, 1.0); + glVertex2f(-1.0 + p1 * 2.0, 1.0); glColor4f(r, g, b, a); - glVertex2f(-1.0+p1*2.0, 0.0); - glVertex2f(-1.0+p2*2.0, 0.0); + glVertex2f(-1.0 + p1 * 2.0, 0.0); + glVertex2f(-1.0 + p2 * 2.0, 0.0); - glVertex2f(-1.0+p2*2.0, 0.0); - glVertex2f(-1.0+p1*2.0, 0.0); - glColor4f(r*0.5, g*0.5, b*0.5, a); - glVertex2f(-1.0+p1*2.0, -1.0); - glVertex2f(-1.0+p2*2.0, -1.0); + glVertex2f(-1.0 + p2 * 2.0, 0.0); + glVertex2f(-1.0 + p1 * 2.0, 0.0); + glColor4f(r * 0.5, g * 0.5, b * 0.5, a); + glVertex2f(-1.0 + p1 * 2.0, -1.0); + glVertex2f(-1.0 + p2 * 2.0, -1.0); glEnd(); glDisable(GL_BLEND); } @@ -63,6 +64,100 @@ void TuningContext::DrawEnd() { CheckGLError(); } +void TuningContext::DrawTuner(long long freq, int count, float displayPos, float displayWidth) { + GLint vp[4]; + glGetIntegerv( GL_VIEWPORT, vp); + + float viewHeight = (float) vp[3]; + float viewWidth = (float) vp[2]; + + freqStr.str(""); + freqStr << freq; + std::string freqChars = freqStr.str(); + + PrimaryGLContext::GLFontSize fontSize = GLFONT_SIZE24; + int fontHeight = 24; + + if (viewHeight < 28) { + fontSize = GLFONT_SIZE18; + fontHeight = 18; + } + if (viewHeight < 24) { + fontSize = GLFONT_SIZE16; + fontHeight = 16; + } + if (viewHeight < 18) { + fontSize = GLFONT_SIZE12; + fontHeight = 12; + } + + glColor3f(ThemeMgr::mgr.currentTheme->text.r, ThemeMgr::mgr.currentTheme->text.g, ThemeMgr::mgr.currentTheme->text.b); + int numChars = freqChars.length(); + int ofs = count - numChars; + for (int i = ofs; i < count; i++) { + float xpos = displayPos + (displayWidth / (float) count) * (float) i + ((displayWidth / 2.0) / (float) count); + getFont(fontSize).drawString(freqStr.str().substr(i - ofs, 1), xpos, 0, fontHeight, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); + } + + glColor4f(0.65, 0.65, 0.65, 0.25); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBegin(GL_LINES); + for (int i = count; i >= 0; i--) { + float xpos = displayPos + (displayWidth / (float) count) * (float) i; + glVertex2f(xpos, -1.0); + glVertex2f(xpos, 1.0); + } + glEnd(); + glDisable(GL_BLEND); +} + +int TuningContext::GetTunerDigitIndex(float mPos, int count, float displayPos, float displayWidth) { + mPos -= 0.5; + mPos *= 2.0; + + float delta = mPos - displayPos; + + if (delta < 0 || delta > displayWidth) { + return 0; + } + + int index = floor((delta / displayWidth) * (count)); + + return count - index; +} + +void TuningContext::DrawTunerBarIndexed(int start, int end, int count, float displayPos, float displayWidth, RGBColor color, float alpha, bool top, +bool bottom) { + float ofs = (displayWidth / (float) count); + float p2 = displayPos + ofs * (float) (count - start + 1); + float p1 = displayPos + ofs * (float) (count - end); + + float r = color.r, g = color.g, b = color.b, a = 0.6; + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glBegin(GL_QUADS); + if (top) { + glColor4f(r * 0.5, g * 0.5, b * 0.5, a); + glVertex2f(p2, 1.0); + glVertex2f(p1, 1.0); + glColor4f(r, g, b, a); + glVertex2f(p1, 0.0); + glVertex2f(p2, 0.0); + } + if (bottom) { + glColor4f(r, g, b, a); + glVertex2f(p2, 0.0); + glVertex2f(p1, 0.0); + glColor4f(r * 0.5, g * 0.5, b * 0.5, a); + glVertex2f(p1, -1.0); + glVertex2f(p2, -1.0); + } + glEnd(); + glDisable(GL_BLEND); +} + void TuningContext::DrawDemodFreqBw(long long freq, unsigned int bw, long long center) { GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); @@ -70,54 +165,8 @@ void TuningContext::DrawDemodFreqBw(long long freq, unsigned int bw, long long c float viewHeight = (float) vp[3]; float viewWidth = (float) vp[2]; - PrimaryGLContext::GLFontSize fontSize = GLFONT_SIZE16; - - int fontHeight = 16; - - if (viewWidth < 400) { - fontSize = GLFONT_SIZE12; - fontHeight = 12; - } - - glColor3f(ThemeMgr::mgr.currentTheme->text.r, ThemeMgr::mgr.currentTheme->text.g, ThemeMgr::mgr.currentTheme->text.b); - - getFont(fontSize).drawString("Freq: ", -0.75, 0, fontHeight, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER); - if (freq) { - freqStr.str(""); - freqStr << std::fixed << freq << " Hz"; - } else { - freqStr.str("---"); - } - getFont(fontSize).drawString(freqStr.str(), -0.75, 0, fontHeight, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER); - - - getFont(fontSize).drawString("BW: ", -0.10, 0, fontHeight, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER); - if (bw) { - freqStr.str(""); - freqStr << std::fixed << bw << " Hz"; - } else { - freqStr.str("---"); - } - getFont(fontSize).drawString(freqStr.str(), -0.10, 0, fontHeight, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER); - - - getFont(fontSize).drawString("CTR: ", 0.50, 0, fontHeight, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER); - if (center) { - freqStr.str(""); - freqStr << std::fixed << center << " Hz"; - } else { - freqStr.str("---"); - } - getFont(fontSize).drawString(freqStr.str(), 0.50, 0, fontHeight, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER); - - - glColor3f(0.65, 0.65, 0.65); - glBegin(GL_LINES); - glVertex2f(-0.275, -1.0); - glVertex2f(-0.275, 1.0); - glVertex2f(0.275, -1.0); - glVertex2f(0.275, 1.0); - glEnd(); - + DrawTuner(freq, 11, -1.0, (1.0 / 3.0) * 2.0); + DrawTuner(bw, 7, -1.0 + (2.25 / 3.0), (1.0 / 4.0) * 2.0); + DrawTuner(center, 11, -1.0 + (2.0 / 3.0) * 2.0, (1.0 / 3.0) * 2.0); } diff --git a/src/visual/TuningContext.h b/src/visual/TuningContext.h index f64a351..dd4c77b 100644 --- a/src/visual/TuningContext.h +++ b/src/visual/TuningContext.h @@ -13,10 +13,15 @@ public: void DrawBegin(); void Draw(float r, float g, float b, float a, float p1, float p2); + void DrawTuner(long long freq, int count, float displayPos, float displayWidth); + int GetTunerDigitIndex(float mPos, int count, float displayPos, float displayWidth); + void DrawTunerBarIndexed(int start, int end, int count, float displayPos, float displayWidth, RGBColor color, float alpha, bool top, bool bottom); + void DrawDemodFreqBw(long long freq, unsigned int bw, long long center); void DrawEnd(); private: std::locale comma_locale; std::stringstream freqStr; + std::stringstream freqStrFormatted; };