diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e9d144..af942cf 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 "13") +SET(CUBICSDR_VERSION_PATCH "14") SET(CUBICSDR_VERSION_REL "alpha") SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}-${CUBICSDR_VERSION_REL}") @@ -237,6 +237,7 @@ SET (cubicsdr_sources src/visual/ScopeContext.cpp src/visual/SpectrumCanvas.cpp src/visual/WaterfallCanvas.cpp + src/visual/GainCanvas.cpp src/process/VisualProcessor.cpp src/process/ScopeVisualProcessor.cpp src/process/SpectrumVisualProcessor.cpp @@ -296,6 +297,7 @@ SET (cubicsdr_headers src/visual/ScopeContext.h src/visual/SpectrumCanvas.h src/visual/WaterfallCanvas.h + src/visual/GainCanvas.h src/process/VisualProcessor.h src/process/ScopeVisualProcessor.h src/process/SpectrumVisualProcessor.h diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 019795e..f918d78 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -48,7 +48,7 @@ AppFrame::AppFrame() : wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL); - wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); + demodTray = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL); int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 }; @@ -59,6 +59,13 @@ AppFrame::AppFrame() : wxPanel *demodPanel = new wxPanel(mainSplitter, wxID_ANY); + gainCanvas = new GainCanvas(demodPanel, attribList); + + gainSizerItem = demodTray->Add(gainCanvas, 0, wxEXPAND | wxALL, 0); + gainSizerItem->Show(false); + gainSpacerItem = demodTray->AddSpacer(1); + gainSpacerItem->Show(false); + demodModeSelector = new ModeSelectorCanvas(demodPanel, attribList); demodModeSelector->addChoice(DEMOD_TYPE_FM, "FM"); demodModeSelector->addChoice(DEMOD_TYPE_AM, "AM"); @@ -252,6 +259,9 @@ AppFrame::AppFrame() : menu->AppendSubMenu(dsMenu, "Direct Sampling"); + agcMenuItem = menu->AppendCheckItem(wxID_AGC_CONTROL, "Automatic Gain"); + agcMenuItem->Check(wxGetApp().getAGCMode()); + menuBar->Append(menu, wxT("&Settings")); menu = new wxMenu; @@ -451,6 +461,22 @@ void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) { if (!checked) { sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true); } + + if (!wxGetApp().getAGCMode()) { + gainSpacerItem->Show(true); + gainSizerItem->Show(true); + gainSizerItem->SetMinSize(devInfo->getRxChannel()->getGains().size()*50,0); + demodTray->Layout(); + gainCanvas->updateGainUI(); + gainCanvas->Refresh(); + gainCanvas->Refresh(); + } else { + gainSpacerItem->Show(false); + gainSizerItem->Show(false); + demodTray->Layout(); + } + + agcMenuItem->Check(wxGetApp().getAGCMode()); } @@ -481,6 +507,22 @@ void AppFrame::OnMenu(wxCommandEvent& event) { wxGetApp().setSwapIQ(swap_state); wxGetApp().saveConfig(); iqSwapMenuItem->Check(swap_state); + } else if (event.GetId() == wxID_AGC_CONTROL) { + if (!wxGetApp().getAGCMode()) { + wxGetApp().setAGCMode(true); + gainSpacerItem->Show(false); + gainSizerItem->Show(false); + demodTray->Layout(); + } else { + wxGetApp().setAGCMode(false); + gainSpacerItem->Show(true); + gainSizerItem->Show(true); + gainSizerItem->SetMinSize(wxGetApp().getDevice()->getRxChannel()->getGains().size()*40,0); + demodTray->Layout(); + gainCanvas->updateGainUI(); + gainCanvas->Refresh(); + gainCanvas->Refresh(); + } } else if (event.GetId() == wxID_SDR_DEVICES) { wxGetApp().deviceSelector(); } else if (event.GetId() == wxID_SET_PPM) { @@ -554,6 +596,9 @@ void AppFrame::OnMenu(wxCommandEvent& event) { if (event.GetId() >= wxID_THEME_DEFAULT && event.GetId() <= wxID_THEME_RADAR) { demodTuner->Refresh(); demodModeSelector->Refresh(); + waterfallSpeedMeter->Refresh(); + spectrumAvgMeter->Refresh(); + gainCanvas->setThemeColors(); } switch (event.GetId()) { diff --git a/src/AppFrame.h b/src/AppFrame.h index dba4308..28e5589 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "PrimaryGLContext.h" @@ -12,6 +13,7 @@ #include "MeterCanvas.h" #include "TuningCanvas.h" #include "ModeSelectorCanvas.h" +#include "GainCanvas.h" #include "FFTVisualDataThread.h" #include "SDRDeviceInfo.h" //#include "UITestCanvas.h" @@ -27,6 +29,7 @@ #define wxID_SET_DS_Q 2006 #define wxID_SET_SWAP_IQ 2007 #define wxID_SDR_DEVICES 2008 +#define wxID_AGC_CONTROL 2009 #define wxID_MAIN_SPLITTER 2050 #define wxID_VIS_SPLITTER 2051 @@ -83,7 +86,10 @@ private: MeterCanvas *spectrumAvgMeter; MeterCanvas *waterfallSpeedMeter; ModeSelectorCanvas *demodMuteButton; + GainCanvas *gainCanvas; + wxSizerItem *gainSizerItem, *gainSpacerItem; wxSplitterWindow *mainVisSplitter, *mainSplitter; + wxBoxSizer *demodTray; DemodulatorInstance *activeDemodulator; @@ -96,6 +102,7 @@ private: std::map directSamplingMenuItems; wxMenuItem *iqSwapMenuItem; wxMenu *sampleRateMenu; + wxMenuItem *agcMenuItem; std::vector sampleRates; std::string currentSessionFile; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 0bc8903..b7d471e 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -115,6 +115,7 @@ long long strToFrequency(std::string freqStr) { CubicSDR::CubicSDR() : appframe(NULL), m_glContext(NULL), frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), directSamplingMode(0), sdrThread(NULL), sdrPostThread(NULL), spectrumVisualThread(NULL), demodVisualThread(NULL), pipeSDRIQData(NULL), pipeIQVisualData(NULL), pipeAudioVisualData(NULL), t_SDR(NULL), t_PostSDR(NULL) { sampleRateInitialized.store(false); + agcMode.store(true); } @@ -597,3 +598,22 @@ void CubicSDR::setDeviceSelectorClosed() { bool CubicSDR::isDeviceSelectorOpen() { return deviceSelectorOpen.load(); } + +void CubicSDR::setAGCMode(bool mode) { + agcMode.store(mode); + sdrThread->setAGCMode(mode); +} + +bool CubicSDR::getAGCMode() { + return agcMode.load(); +} + + +void CubicSDR::setGain(std::string name, float gain_in) { + sdrThread->setGain(name,gain_in); +} + +float CubicSDR::getGain(std::string name) { + return sdrThread->getGain(name); +} + diff --git a/src/CubicSDR.h b/src/CubicSDR.h index 856e526..bbacf87 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -108,6 +108,12 @@ public: void setDeviceSelectorClosed(); bool isDeviceSelectorOpen(); + void setAGCMode(bool mode); + bool getAGCMode(); + + void setGain(std::string name, float gain_in); + float getGain(std::string name); + private: AppFrame *appframe; AppConfig config; @@ -121,6 +127,7 @@ private: int ppm, snap; long long sampleRate; int directSamplingMode; + std::atomic_bool agcMode; SDRThread *sdrThread; SDREnumerator *sdrEnum; diff --git a/src/process/FFTDataDistributor.cpp b/src/process/FFTDataDistributor.cpp index 3733edf..a2e10db 100644 --- a/src/process/FFTDataDistributor.cpp +++ b/src/process/FFTDataDistributor.cpp @@ -1,6 +1,7 @@ #include "FFTDataDistributor.h" FFTDataDistributor::FFTDataDistributor() : linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0), fftSize(DEFAULT_FFT_SIZE) { + bufferedItems = 0; } void FFTDataDistributor::setFFTSize(int fftSize) { diff --git a/src/sdr/SDRDeviceInfo.cpp b/src/sdr/SDRDeviceInfo.cpp index f059cd9..7896cfa 100644 --- a/src/sdr/SDRDeviceInfo.cpp +++ b/src/sdr/SDRDeviceInfo.cpp @@ -11,6 +11,10 @@ SDRDeviceRange::SDRDeviceRange(double low, double high) { this->high = high; } +SDRDeviceRange::SDRDeviceRange(std::string name, double low, double high) : SDRDeviceRange(low, high) { + this->name = name; +} + double SDRDeviceRange::getLow() { return low; } @@ -24,6 +28,14 @@ void SDRDeviceRange::setHigh(double high) { this->high = high; } +std::string SDRDeviceRange::getName() { + return this->name; +} + +void SDRDeviceRange::setName(std::string name) { + this->name = name; +} + SDRDeviceChannel::SDRDeviceChannel() { hardwareDC = false; hasCorr = false; @@ -81,6 +93,18 @@ SDRDeviceRange &SDRDeviceChannel::getRFRange() { return rangeRF; } +void SDRDeviceChannel::addGain(SDRDeviceRange range) { + gainInfo.push_back(range); +} + +std::vector &SDRDeviceChannel::getGains() { + return gainInfo; +} + +void SDRDeviceChannel::addGain(std::string name, SoapySDR::Range range) { + gainInfo.push_back(SDRDeviceRange(name,range.minimum(),range.maximum())); +} + std::vector &SDRDeviceChannel::getSampleRates() { return sampleRates; } diff --git a/src/sdr/SDRDeviceInfo.h b/src/sdr/SDRDeviceInfo.h index ebcbb8c..ccec06c 100644 --- a/src/sdr/SDRDeviceInfo.h +++ b/src/sdr/SDRDeviceInfo.h @@ -36,13 +36,17 @@ class SDRDeviceRange { public: SDRDeviceRange(); SDRDeviceRange(double low, double high); + SDRDeviceRange(std::string name, double low, double high); double getLow(); void setLow(double low); double getHigh(); void setHigh(double high); + std::string getName(); + void setName(std::string name); private: + std::string name; double low, high; }; @@ -63,6 +67,10 @@ public: bool isRx(); void setRx(bool rx); + void addGain(SDRDeviceRange range); + void addGain(std::string name, SoapySDR::Range range); + std::vector &getGains(); + SDRDeviceRange &getGain(); SDRDeviceRange &getLNAGain(); SDRDeviceRange &getFreqRange(); @@ -89,6 +97,7 @@ private: std::vector sampleRates; std::vector filterBandwidths; SoapySDR::ArgInfoList streamArgInfo; + std::vector gainInfo; }; diff --git a/src/sdr/SDREnumerator.cpp b/src/sdr/SDREnumerator.cpp index a2ed22a..c81a6e1 100644 --- a/src/sdr/SDREnumerator.cpp +++ b/src/sdr/SDREnumerator.cpp @@ -167,6 +167,12 @@ std::vector *SDREnumerator::enumerate_devices(std::string remot chan->setStreamArgsInfo(device->getStreamArgsInfo(SOAPY_SDR_RX, i)); + std::vector gainNames = device->listGains(SOAPY_SDR_RX, i); + + for (std::vector::iterator gname = gainNames.begin(); gname!= gainNames.end(); gname++) { + chan->addGain((*gname),device->getGainRange(SOAPY_SDR_RX, i, (*gname))); + } + dev->addChannel(chan); } diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index e4eefaa..766c814 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -332,6 +332,18 @@ void SDRPostThread::run() { } } + bool doUpdate = false; + for (int j = 0; j < nRunDemods; j++) { + DemodulatorInstance *demod = runDemods[j]; + if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) { + doUpdate = true; + } + } + + if (doUpdate) { + updateActiveDemodulators(); + } + busy_demod.unlock(); } data_in->decRefCount(); diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index c36563d..a775042 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -33,6 +33,10 @@ SDRThread::SDRThread() : IOThread() { numChannels.store(8); hasDirectSampling.store(false); hasIQSwap.store(false); + + agc_mode.store(true); + agc_mode_changed.store(false); + gain_value_changed.store(false); } SDRThread::~SDRThread() { @@ -90,7 +94,7 @@ void SDRThread::init() { hasIQSwap.store(true); } - device->setGainMode(SOAPY_SDR_RX,0,true); + device->setGainMode(SOAPY_SDR_RX,0,agc_mode.load()); numChannels.store(getOptimalChannelCount(sampleRate.load())); numElems.store(getOptimalElementCount(sampleRate.load(), 30)); @@ -152,6 +156,8 @@ void SDRThread::readLoop() { return; } + updateGains(); + while (!terminated.load()) { if (offset_changed.load()) { if (!freq_changed.load()) { @@ -186,12 +192,48 @@ void SDRThread::readLoop() { device->writeSetting("iq_swap", iq_swap.load()?"true":"false"); iq_swap_changed.store(false); } + if (agc_mode_changed.load()) { + SDRDeviceInfo *devInfo = deviceInfo.load(); + + device->setGainMode(SOAPY_SDR_RX,devInfo->getRxChannel()->getChannel(),agc_mode.load()); + agc_mode_changed.store(false); + if (!agc_mode.load()) { + updateGains(); + } + } + if (gain_value_changed.load() && !agc_mode.load()) { + SDRDeviceInfo *devInfo = deviceInfo.load(); + + for (std::map::iterator gci = gainChanged.begin(); gci != gainChanged.end(); gci++) { + if (gci->second.load()) { + device->setGain(SOAPY_SDR_RX, devInfo->getRxChannel()->getChannel(), gci->first, gainValues[gci->first]); + gainChanged[gci->first] = false; + } + } + + gain_value_changed.store(false); + } readStream(iqDataOutQueue); } buffers.purge(); } +void SDRThread::updateGains() { + SDRDeviceInfo *devInfo = deviceInfo.load(); + + gainValues.erase(gainValues.begin(),gainValues.end()); + gainChanged.erase(gainChanged.begin(),gainChanged.end()); + + std::vector gains = devInfo->getRxChannel()->getGains(); + for (std::vector::iterator gi = gains.begin(); gi != gains.end(); gi++) { + gainValues[(*gi).getName()] = device->getGain(SOAPY_SDR_RX, devInfo->getRxChannel()->getChannel(), (*gi).getName()); + gainChanged[(*gi).getName()] = false; + } + + gain_value_changed.store(false); +} + void SDRThread::run() { //#ifdef __APPLE__ @@ -320,3 +362,22 @@ void SDRThread::setIQSwap(bool iqSwap) { bool SDRThread::getIQSwap() { return iq_swap.load(); } + +void SDRThread::setAGCMode(bool mode) { + agc_mode.store(mode); + agc_mode_changed.store(true); +} + +bool SDRThread::getAGCMode() { + return agc_mode.load(); +} + +void SDRThread::setGain(std::string name, float value) { + gainValues[name].store(value); + gainChanged[name].store(true); + gain_value_changed.store(true); +} + +float SDRThread::getGain(std::string name) { + return gainValues[name].load(); +} diff --git a/src/sdr/SoapySDRThread.h b/src/sdr/SoapySDRThread.h index 3f94864..e573d05 100644 --- a/src/sdr/SoapySDRThread.h +++ b/src/sdr/SoapySDRThread.h @@ -74,8 +74,16 @@ public: void setIQSwap(bool iqSwap); bool getIQSwap(); - + + void setAGCMode(bool mode); + bool getAGCMode(); + + void setGain(std::string name, float value); + float getGain(std::string name); + protected: + void updateGains(); + SoapySDR::Stream *stream; SoapySDR::Device *device; void *buffs[1]; @@ -88,6 +96,8 @@ protected: std::atomic_llong frequency, offset; std::atomic_int ppm, direct_sampling_mode, numElems, numChannels; std::atomic_bool hasPPM, hasHardwareDC, hasDirectSampling, hasIQSwap; - std::atomic_bool iq_swap, rate_changed, freq_changed, offset_changed, - ppm_changed, direct_sampling_changed, device_changed, iq_swap_changed; + std::atomic_bool iq_swap, agc_mode, rate_changed, freq_changed, offset_changed, + ppm_changed, direct_sampling_changed, device_changed, iq_swap_changed, agc_mode_changed, gain_value_changed; + std::map > gainValues; + std::map gainChanged; }; diff --git a/src/ui/GLPanel.cpp b/src/ui/GLPanel.cpp index 7d06c81..c61d626 100644 --- a/src/ui/GLPanel.cpp +++ b/src/ui/GLPanel.cpp @@ -5,7 +5,7 @@ using namespace CubicVR; -GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), transform(mat4::identity()) { +GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), visible(true), transform(mat4::identity()) { pos[0] = 0.0f; pos[1] = 0.0f; rot[0] = 0.0f; @@ -19,6 +19,8 @@ GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), transf setCoordinateSystem(GLPANEL_Y_UP); setMarginPx(0); setBorderPx(0); + srcBlend = GL_SRC_ALPHA; + dstBlend = GL_ONE_MINUS_SRC_ALPHA; } void GLPanel::genArrays() { @@ -174,6 +176,19 @@ void GLPanel::setCoordinateSystem(GLPanelCoordinateSystem coord_in) { genArrays(); } +bool GLPanel::hitTest(CubicVR::vec2 pos, CubicVR::vec2 &result) { + CubicVR::vec4 hitPos = CubicVR::mat4::vec4_multiply(CubicVR::vec4(pos.x, pos.y, 0.0, 1.0), transformInverse); + + if (hitPos.x >= -1.0 && hitPos.x <= 1.0 && hitPos.y >= -1.0 && hitPos.y <= 1.0) { + result.x = hitPos.x; + result.y = hitPos.y; + return true; + } + + return false; +} + + void GLPanel::setFill(GLPanelFillType fill_mode) { fillType = fill_mode; genArrays(); @@ -210,6 +225,11 @@ void GLPanel::setBorderPx(float bordl, float bordr, float bordt, float bordb) { borderPx.bottom = bordb; } +void GLPanel::setBlend(GLuint src, GLuint dst) { + srcBlend = src; + dstBlend = dst; +} + void GLPanel::addChild(GLPanel *childPanel) { std::vector::iterator i = std::find(children.begin(), children.end(), childPanel); @@ -278,6 +298,8 @@ void GLPanel::calcTransform(mat4 transform_in) { if (marginPx) { transform *= mat4::scale(1.0 - marginPx * 2.0 * pvec.x / size[0], 1.0 - marginPx * 2.0 * pvec.y / size[1], 1); } + + transformInverse = CubicVR::mat4::inverse(transform); } void GLPanel::draw() { @@ -285,9 +307,9 @@ void GLPanel::draw() { glLoadMatrixf(transform); - if (fillType != GLPANEL_FILL_NONE) { + if (fillType != GLPANEL_FILL_NONE && visible) { glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFunc(srcBlend, dstBlend); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &glPoints[0]); diff --git a/src/ui/GLPanel.h b/src/ui/GLPanel.h index 0f3e3b6..ed329fe 100644 --- a/src/ui/GLPanel.h +++ b/src/ui/GLPanel.h @@ -45,8 +45,8 @@ public: GLPanelEdges borderPx; RGBA4f fill[2]; RGBA4f borderColor; - bool contentsVisible; - CubicVR::mat4 transform; + bool contentsVisible, visible; + CubicVR::mat4 transform, transformInverse; CubicVR::mat4 localTransform; float min, mid, max; // screen dimensions @@ -55,7 +55,8 @@ public: CubicVR::vec2 umin, umax, ucenter; // pixel dimensions CubicVR::vec2 pdim, pvec; - + GLuint srcBlend, dstBlend; + std::vector children; GLPanel(); @@ -68,6 +69,8 @@ public: float getHeightPx(); void setCoordinateSystem(GLPanelCoordinateSystem coord); + bool hitTest(CubicVR::vec2 pos, CubicVR::vec2 &result); + void setFill(GLPanelFillType fill_mode); void setFillColor(RGBA4f color1); void setFillColor(RGBA4f color1, RGBA4f color2); @@ -77,6 +80,8 @@ public: void setBorderPx(float bord); void setBorderPx(float bordl, float bordr, float bordt, float bordb); + void setBlend(GLuint src, GLuint dst); + void addChild(GLPanel *childPanel); void removeChild(GLPanel *childPanel); diff --git a/src/visual/GainCanvas.cpp b/src/visual/GainCanvas.cpp new file mode 100644 index 0000000..a098d63 --- /dev/null +++ b/src/visual/GainCanvas.cpp @@ -0,0 +1,305 @@ +#include "GainCanvas.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#if !wxUSE_GLCANVAS +#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" +#endif + +#include "CubicSDR.h" +#include "CubicSDRDefs.h" +#include "AppFrame.h" +#include + +wxBEGIN_EVENT_TABLE(GainCanvas, wxGLCanvas) EVT_PAINT(GainCanvas::OnPaint) +EVT_IDLE(GainCanvas::OnIdle) +EVT_MOTION(GainCanvas::OnMouseMoved) +EVT_LEFT_DOWN(GainCanvas::OnMouseDown) +EVT_LEFT_UP(GainCanvas::OnMouseReleased) +EVT_LEAVE_WINDOW(GainCanvas::OnMouseLeftWindow) +EVT_ENTER_WINDOW(GainCanvas::OnMouseEnterWindow) +wxEND_EVENT_TABLE() + +GainCanvas::GainCanvas(wxWindow *parent, int *attribList) : + InteractiveCanvas(parent, attribList) { + + glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this)); + bgPanel.setCoordinateSystem(GLPanel::GLPANEL_Y_UP); + bgPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_X); + + numGains = 1; + spacing = 2.0/numGains; + barWidth = (1.0/numGains)*0.8; + startPos = spacing/2.0; + barHeight = 0.8; +} + +GainCanvas::~GainCanvas() { + +} + +void GainCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { + wxPaintDC dc(this); + const wxSize ClientSize = GetClientSize(); + + glContext->SetCurrent(*this); + initGLExtensions(); + + glViewport(0, 0, ClientSize.x, ClientSize.y); + + float i = 0; + for (std::vector::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + GainInfo *gInfo = (*gi); + float midPos = -1.0+startPos+spacing*i; + + gInfo->labelPanel.setSize(spacing/2.0,(15.0/float(ClientSize.y))); + gInfo->labelPanel.setPosition(midPos, -barHeight-(20.0/float(ClientSize.y))); + + gInfo->valuePanel.setSize(spacing/2.0,(15.0/float(ClientSize.y))); + gInfo->valuePanel.setPosition(midPos, barHeight+(20.0/float(ClientSize.y))); + + i+=1.0; + } + + bgPanel.draw(); + + SwapBuffers(); +} + +void GainCanvas::OnIdle(wxIdleEvent &event) { + if (mouseTracker.mouseInView()) { + Refresh(); + } else { + event.Skip(); + } + + for (std::vector::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + GainInfo *gInfo = (*gi); + if (gInfo->changed) { + wxGetApp().setGain(gInfo->name, gInfo->current); + gInfo->changed = false; + } + } +} + +int GainCanvas::GetPanelHit(CubicVR::vec2 &result) { + std::vector::iterator gi; + + int i = 0; + for (gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + GainInfo *gInfo = (*gi); + + CubicVR::vec2 hitResult; + if (gInfo->panel.hitTest(CubicVR::vec2((mouseTracker.getMouseX()-0.5)*2.0, (mouseTracker.getMouseY()-0.5)*2.0), hitResult)) { +// std::cout << "Hit #" << i << " result: " << hitResult << std::endl; + result = (hitResult + CubicVR::vec2(1.0,1.0)) * 0.5; + return i; + } + i++; + } + return -1; +} + + +void GainCanvas::SetLevel() { + CubicVR::vec2 hitResult; + int panelHit = GetPanelHit(hitResult); + + if (panelHit >= 0) { + gainInfo[panelHit]->levelPanel.setSize(1.0, hitResult.y); + gainInfo[panelHit]->levelPanel.setPosition(0.0, (-1.0+(hitResult.y))); + gainInfo[panelHit]->current = gainInfo[panelHit]->low+(hitResult.y * (gainInfo[panelHit]->high-gainInfo[panelHit]->low)); + gainInfo[panelHit]->changed = true; + gainInfo[panelHit]->valuePanel.setText(std::to_string(int(gainInfo[panelHit]->current))); + } +} + +void GainCanvas::OnMouseMoved(wxMouseEvent& event) { + InteractiveCanvas::OnMouseMoved(event); + + CubicVR::vec2 hitResult; + int panelHit = GetPanelHit(hitResult); + + if (panelHit >= 0) { + gainInfo[panelHit]->highlightPanel.setSize(1.0, hitResult.y); + gainInfo[panelHit]->highlightPanel.setPosition(0.0, (-1.0+(hitResult.y))); + } + + int i = 0; + for (std::vector::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + (*gi)->highlightPanel.visible = (i==panelHit); + i++; + } + + if (mouseTracker.mouseDown()) { + SetLevel(); + } +} + +void GainCanvas::OnMouseDown(wxMouseEvent& event) { + InteractiveCanvas::OnMouseDown(event); + SetLevel(); +} + +void GainCanvas::OnMouseWheelMoved(wxMouseEvent& event) { + InteractiveCanvas::OnMouseWheelMoved(event); +// Refresh(); +} + +void GainCanvas::OnMouseReleased(wxMouseEvent& event) { + InteractiveCanvas::OnMouseReleased(event); +// Refresh(); +} + +void GainCanvas::OnMouseLeftWindow(wxMouseEvent& event) { + InteractiveCanvas::OnMouseLeftWindow(event); + SetCursor(wxCURSOR_CROSS); + + int i = 0; + for (std::vector::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + (*gi)->highlightPanel.visible = false; + i++; + } + Refresh(); +} + +void GainCanvas::OnMouseEnterWindow(wxMouseEvent& event) { + InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event); + SetCursor(wxCURSOR_CROSS); +// Refresh(); +} + + + +void GainCanvas::setHelpTip(std::string tip) { + helpTip = tip; +} + +void GainCanvas::updateGainUI() { + const wxSize ClientSize = GetClientSize(); + + SDRDeviceInfo *devInfo = wxGetApp().getDevice(); + + std::vector &gains = devInfo->getRxChannel()->getGains(); + std::vector::iterator gi; + + numGains = gains.size(); + float i = 0; + + if (!numGains) { + return; + } + + spacing = 2.0/numGains; + barWidth = (1.0/numGains)*0.7; + startPos = spacing/2.0; + barHeight = 0.8; + + RGBA4f c1, c2; + + while (gainInfo.size()) { + GainInfo *giDel; + giDel = gainInfo.back(); + gainInfo.pop_back(); + + giDel->panel.removeChild(&giDel->levelPanel); + bgPanel.removeChild(&(giDel->labelPanel)); + bgPanel.removeChild(&(giDel->valuePanel)); + bgPanel.removeChild(&(giDel->panel)); + delete giDel; + } + + for (gi = gains.begin(); gi != gains.end(); gi++) { + GainInfo *gInfo = new GainInfo; + float midPos = -1.0+startPos+spacing*i; + + gInfo->name = (*gi).getName(); + gInfo->low = (*gi).getLow(); + gInfo->high = (*gi).getHigh(); + gInfo->current = wxGetApp().getGain(gInfo->name); + + gInfo->panel.setBorderPx(1); + gInfo->panel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); + gInfo->panel.setPosition(midPos, 0); + gInfo->panel.setSize(barWidth, barHeight); + gInfo->panel.setBlend(GL_ONE, GL_ONE); + + gInfo->levelPanel.setBorderPx(0); + gInfo->levelPanel.setMarginPx(1); + gInfo->levelPanel.setSize(1.0,0.8); + float levelVal = float(gInfo->current-gInfo->low)/float(gInfo->high-gInfo->low); + gInfo->levelPanel.setSize(1.0, levelVal); + gInfo->levelPanel.setPosition(0.0, (-1.0+(levelVal))); + gInfo->levelPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); + gInfo->levelPanel.setBlend(GL_ONE, GL_ONE); + + gInfo->panel.addChild(&gInfo->levelPanel); + + gInfo->highlightPanel.setBorderPx(0); + gInfo->highlightPanel.setMarginPx(1); + gInfo->highlightPanel.setSize(1.0,0.8); + gInfo->highlightPanel.setPosition(0.0,-0.2); + gInfo->highlightPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); + gInfo->highlightPanel.setBlend(GL_ONE, GL_ONE); + gInfo->highlightPanel.visible = false; + + gInfo->panel.addChild(&gInfo->highlightPanel); + + gInfo->labelPanel.setSize(spacing/2.0,(15.0/float(ClientSize.y))); + gInfo->labelPanel.setPosition(midPos, -barHeight-(20.0/float(ClientSize.y))); + gInfo->labelPanel.setText((*gi).getName()); + gInfo->labelPanel.setFill(GLPanel::GLPANEL_FILL_NONE); + + bgPanel.addChild(&(gInfo->labelPanel)); + + gInfo->valuePanel.setSize(spacing/2.0,(15.0/float(ClientSize.y))); + gInfo->valuePanel.setPosition(midPos, barHeight+(20.0/float(ClientSize.y))); + gInfo->valuePanel.setText(std::to_string(int(gInfo->current))); + gInfo->valuePanel.setFill(GLPanel::GLPANEL_FILL_NONE); + + bgPanel.addChild(&(gInfo->valuePanel)); + + bgPanel.addChild(&(gInfo->panel)); + gainInfo.push_back(gInfo); + i++; + } + + setThemeColors(); +} + +void GainCanvas::setThemeColors() { + std::vector::iterator gi; + + RGBA4f c1, c2; + + c1 = ThemeMgr::mgr.currentTheme->generalBackground; + c2 = ThemeMgr::mgr.currentTheme->generalBackground * 0.5; + + bgPanel.setFillColor(c1, c2); + + for (gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + GainInfo *gInfo = (*gi); + + c1 = ThemeMgr::mgr.currentTheme->generalBackground; + c2 = ThemeMgr::mgr.currentTheme->generalBackground * 0.5; + c1.a = 1.0; + c2.a = 1.0; + gInfo->panel.setFillColor(c1, c2); + + c1 = ThemeMgr::mgr.currentTheme->meterLevel * 0.5; + c2 = ThemeMgr::mgr.currentTheme->meterLevel; + c1.a = 1.0; + c2.a = 1.0; + gInfo->levelPanel.setFillColor(c1, c2); + + c1 = RGBA4f(0.3,0.3,0.3,1.0); + c2 = RGBA4f(0.65,0.65,0.65,1.0);; + gInfo->highlightPanel.setFillColor(c1, c2); + } + Refresh(); +} + diff --git a/src/visual/GainCanvas.h b/src/visual/GainCanvas.h new file mode 100644 index 0000000..d1eb50d --- /dev/null +++ b/src/visual/GainCanvas.h @@ -0,0 +1,63 @@ +#pragma once + +#include "wx/glcanvas.h" +#include "wx/timer.h" + +#include +#include + +#include "InteractiveCanvas.h" +#include "MouseTracker.h" +#include "GLPanel.h" +#include "PrimaryGLContext.h" + +#include "fftw3.h" +#include "Timer.h" + +class GainInfo { +public: + std::string name; + float low, high, current; + bool changed; + GLPanel panel; + GLPanel levelPanel; + GLPanel highlightPanel; + GLTextPanel labelPanel; + GLTextPanel valuePanel; +}; + +class GainCanvas: public InteractiveCanvas { +public: + GainCanvas(wxWindow *parent, int *attribList = NULL); + ~GainCanvas(); + + void setHelpTip(std::string tip); + void updateGainUI(); + void setThemeColors(); + +private: + void OnPaint(wxPaintEvent& event); + void OnIdle(wxIdleEvent &event); + + int GetPanelHit(CubicVR::vec2 &result); + void SetLevel(); + + void OnShow(wxShowEvent& event); + void OnMouseMoved(wxMouseEvent& event); + void OnMouseDown(wxMouseEvent& event); + void OnMouseWheelMoved(wxMouseEvent& event); + void OnMouseReleased(wxMouseEvent& event); + void OnMouseEnterWindow(wxMouseEvent& event); + void OnMouseLeftWindow(wxMouseEvent& event); + + PrimaryGLContext *glContext; + std::string helpTip; + std::vector gainInfo; + GLPanel bgPanel; + + float spacing, barWidth, startPos, barHeight, numGains; + wxSize clientSize; + // +wxDECLARE_EVENT_TABLE(); +}; +