diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index b783f32..c55e61c 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -133,6 +133,7 @@ AppFrame::AppFrame() : // menu->Append(wxID_NEW); menu->Append(wxID_SET_FREQ_OFFSET, "Set Frequency Offset"); menu->Append(wxID_SET_PPM, "Set Device PPM"); + menu->AppendSeparator(); menu->Append(wxID_OPEN, "&Open Session"); menu->Append(wxID_SAVE, "&Save Session"); menu->Append(wxID_SAVEAS, "Save Session &As.."); @@ -188,6 +189,7 @@ AppFrame::AppFrame() : sampleRateMenuItems[wxID_BANDWIDTH_1000M] = menu->AppendRadioItem(wxID_BANDWIDTH_1000M, "1.0M"); sampleRateMenuItems[wxID_BANDWIDTH_1500M] = menu->AppendRadioItem(wxID_BANDWIDTH_1500M, "1.5M"); sampleRateMenuItems[wxID_BANDWIDTH_2000M] = menu->AppendRadioItem(wxID_BANDWIDTH_2000M, "2.0M"); + sampleRateMenuItems[wxID_BANDWIDTH_2160M] = menu->AppendRadioItem(wxID_BANDWIDTH_2160M, "2.16M"); sampleRateMenuItems[wxID_BANDWIDTH_2500M] = menu->AppendRadioItem(wxID_BANDWIDTH_2500M, "2.5M"); sampleRateMenuItems[wxID_BANDWIDTH_2880M] = menu->AppendRadioItem(wxID_BANDWIDTH_2880M, "2.88M"); sampleRateMenuItems[wxID_BANDWIDTH_3200M] = menu->AppendRadioItem(wxID_BANDWIDTH_3200M, "3.2M"); @@ -380,6 +382,9 @@ void AppFrame::OnMenu(wxCommandEvent& event) { case wxID_BANDWIDTH_2000M: wxGetApp().setSampleRate(2000000); break; + case wxID_BANDWIDTH_2160M: + wxGetApp().setSampleRate(2160000); + break; case wxID_BANDWIDTH_2500M: wxGetApp().setSampleRate(2500000); break; diff --git a/src/AppFrame.h b/src/AppFrame.h index 5a7967f..d5bb09a 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -28,8 +28,9 @@ #define wxID_BANDWIDTH_1000M 2152 #define wxID_BANDWIDTH_1500M 2153 #define wxID_BANDWIDTH_2000M 2154 -#define wxID_BANDWIDTH_2500M 2155 -#define wxID_BANDWIDTH_2880M 2156 +#define wxID_BANDWIDTH_2160M 2155 +#define wxID_BANDWIDTH_2500M 2156 +#define wxID_BANDWIDTH_2880M 2157 #define wxID_BANDWIDTH_3200M 2158 #define wxID_DEVICE_ID 3500 diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 7d5fb54..fce7a96 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -277,3 +277,13 @@ void CubicSDR::showFrequencyInput() { fdialog.ShowModal(); } +void CubicSDR::setFrequencySnap(int snap) { + if (snap > 1000000) { + snap = 1000000; + } + this->snap = snap; +} + +int CubicSDR::getFrequencySnap() { + return snap; +} diff --git a/src/CubicSDR.h b/src/CubicSDR.h index dfc7e33..2d84af1 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -22,7 +22,7 @@ class CubicSDR: public wxApp { public: CubicSDR() : - m_glContext(NULL), frequency(DEFAULT_FREQ), sdrThread(NULL), sdrPostThread(NULL), threadCmdQueueSDR(NULL), iqVisualQueue(NULL), iqPostDataQueue(NULL), audioVisualQueue(NULL), t_SDR(NULL), t_PostSDR(NULL), sampleRate(DEFAULT_SAMPLE_RATE), offset(0) { + m_glContext(NULL), frequency(DEFAULT_FREQ), sdrThread(NULL), sdrPostThread(NULL), threadCmdQueueSDR(NULL), iqVisualQueue(NULL), iqPostDataQueue(NULL), audioVisualQueue(NULL), t_SDR(NULL), t_PostSDR(NULL), sampleRate(DEFAULT_SAMPLE_RATE), offset(0), snap(1) { } @@ -51,6 +51,9 @@ public: void bindDemodulator(DemodulatorInstance *demod); void removeDemodulator(DemodulatorInstance *demod); + void setFrequencySnap(int snap); + int getFrequencySnap(); + AppConfig *getConfig(); void saveConfig(); @@ -69,7 +72,7 @@ private: long long frequency; long long offset; - int ppm; + int ppm, snap; long long sampleRate; SDRThread *sdrThread; diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp index e6322d7..375c2d5 100644 --- a/src/visual/TuningCanvas.cpp +++ b/src/visual/TuningCanvas.cpp @@ -20,6 +20,9 @@ EVT_IDLE(TuningCanvas::OnIdle) EVT_MOTION(TuningCanvas::OnMouseMoved) EVT_LEFT_DOWN(TuningCanvas::OnMouseDown) EVT_LEFT_UP(TuningCanvas::OnMouseReleased) +EVT_RIGHT_DOWN(TuningCanvas::OnMouseRightDown) +EVT_RIGHT_UP(TuningCanvas::OnMouseRightReleased) + EVT_LEAVE_WINDOW(TuningCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(TuningCanvas::OnMouseEnterWindow) EVT_MOUSEWHEEL(TuningCanvas::OnMouseWheelMoved) @@ -124,6 +127,10 @@ void TuningCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glContext->DrawTuner(currentPPM, 11, freqDP, freqW); } else { glContext->DrawTuner(freq, 11, freqDP, freqW); + int snap = wxGetApp().getFrequencySnap(); + if (snap != 1) { + glContext->DrawTunerDigitBox((int)log10(snap), 11, freqDP, freqW, RGBColor(1.0,0.0,0.0)); + } } glContext->DrawTuner(bw, 7, bwDP, bwW); glContext->DrawTuner(center, 11, centerDP, centerW); @@ -271,7 +278,7 @@ void TuningCanvas::OnMouseMoved(wxMouseEvent& event) { } else { switch (hoverState) { case TUNING_HOVER_FREQ: - setStatusText("Click, wheel or drag a digit to change frequency; SPACE for direct input. Hold ALT to change PPM. Hold SHIFT to disable carry."); + setStatusText("Click, wheel or drag a digit to change frequency; SPACE for direct input. Right click to set/clear snap. Hold ALT to change PPM. Hold SHIFT to disable carry."); break; case TUNING_HOVER_BW: setStatusText("Click, wheel or drag a digit to change bandwidth. Hold SHIFT to disable carry."); @@ -341,6 +348,27 @@ void TuningCanvas::OnMouseReleased(wxMouseEvent& event) { SetCursor(wxCURSOR_ARROW); } +void TuningCanvas::OnMouseRightDown(wxMouseEvent& event) { + InteractiveCanvas::OnMouseRightDown(event); +} + +void TuningCanvas::OnMouseRightReleased(wxMouseEvent& event) { + InteractiveCanvas::OnMouseRightReleased(event); + + if (hoverState == TUNING_HOVER_FREQ) { + if (hoverIndex == 1) { + wxGetApp().setFrequencySnap(1); + } else if (hoverIndex > 1 && hoverIndex < 8) { + int exp = pow(10, hoverIndex-1); + if (wxGetApp().getFrequencySnap() == exp) { + wxGetApp().setFrequencySnap(1); + } else { + wxGetApp().setFrequencySnap(exp); + } + } + } +} + void TuningCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); SetCursor(wxCURSOR_CROSS); diff --git a/src/visual/TuningCanvas.h b/src/visual/TuningCanvas.h index d6e2cee..1fea2e2 100644 --- a/src/visual/TuningCanvas.h +++ b/src/visual/TuningCanvas.h @@ -35,6 +35,8 @@ private: void OnMouseLeftWindow(wxMouseEvent& event); void OnKeyDown(wxKeyEvent& event); void OnKeyUp(wxKeyEvent& event); + void OnMouseRightDown(wxMouseEvent& event); + void OnMouseRightReleased(wxMouseEvent& event); void StepTuner(ActiveState state, int factor, bool up = true); diff --git a/src/visual/TuningContext.cpp b/src/visual/TuningContext.cpp index 9a4dd4c..16d49ac 100644 --- a/src/visual/TuningContext.cpp +++ b/src/visual/TuningContext.cpp @@ -112,6 +112,34 @@ void TuningContext::DrawTuner(long long freq, int count, float displayPos, float glDisable(GL_BLEND); } + +void TuningContext::DrawTunerDigitBox(int index, int count, float displayPos, float displayWidth, RGBColor c) { + GLint vp[4]; + glGetIntegerv( GL_VIEWPORT, vp); + + float viewHeight = (float) vp[3]; + float viewWidth = (float) vp[2]; + + float pixelHeight = 2.0/viewHeight; + + glColor4f(1.0, 0,0,1); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + float xpos = displayPos + (displayWidth / (float) count) * (float) (count-index); + float xpos2 = displayPos + (displayWidth / (float) count) * (float) ((count-1)-index); + glBegin(GL_LINE_STRIP); + glVertex2f(xpos, 1.0-pixelHeight); + glVertex2f(xpos, -1.0+pixelHeight); + glVertex2f(xpos2, -1.0+pixelHeight); + glVertex2f(xpos2, 1.0-pixelHeight); + glVertex2f(xpos, 1.0-pixelHeight); + glEnd(); + glDisable(GL_BLEND); +} + + + + int TuningContext::GetTunerDigitIndex(float mPos, int count, float displayPos, float displayWidth) { mPos -= 0.5; mPos *= 2.0; diff --git a/src/visual/TuningContext.h b/src/visual/TuningContext.h index dd4c77b..436a1f1 100644 --- a/src/visual/TuningContext.h +++ b/src/visual/TuningContext.h @@ -14,6 +14,7 @@ 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); + void DrawTunerDigitBox(int index, int count, float displayPos, float displayWidth, RGBColor c); 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); diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index b00fb84..c504dcf 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -19,7 +19,8 @@ #define MIN_BANDWIDTH 1500 -wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) EVT_PAINT(WaterfallCanvas::OnPaint) +wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) +EVT_PAINT(WaterfallCanvas::OnPaint) EVT_KEY_DOWN(WaterfallCanvas::OnKeyDown) EVT_KEY_UP(WaterfallCanvas::OnKeyUp) EVT_IDLE(WaterfallCanvas::OnIdle) @@ -36,7 +37,7 @@ wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : InteractiveCanvas(parent, attribList), spectrumCanvas(NULL), dragState(WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), waterfall_lines( 0), plan( - NULL), in(NULL), out(NULL), resampler(NULL), resamplerRatio(0), lastInputBandwidth(0), zoom(1), mouseZoom(1), otherWaterfallCanvas(NULL), polling(true), last_data_size(0), fft_in_data(NULL), fft_last_data(NULL), hoverAlpha(1.0) { + NULL), in(NULL), out(NULL), resampler(NULL), resamplerRatio(0), lastInputBandwidth(0), zoom(1), mouseZoom(1), otherWaterfallCanvas(NULL), polling(true), last_data_size(0), fft_in_data(NULL), fft_last_data(NULL), hoverAlpha(1.0), dragOfs(0) { glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); @@ -633,12 +634,20 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { } if (dragState == WF_DRAG_FREQUENCY) { - long long bwDiff = (long long) (mouseTracker.getDeltaMouseX() * (float) getBandwidth()); + long long bwTarget = getFrequencyAt(mouseTracker.getMouseX()) - dragOfs; long long currentFreq = demod->getFrequency(); + long long bwDiff = bwTarget - currentFreq; + int snap = wxGetApp().getFrequencySnap(); - demod->setFrequency(currentFreq + bwDiff); - currentFreq = demod->getFrequency(); - demod->updateLabel(currentFreq); + if (snap > 1) { + bwDiff = roundf((float)bwDiff/(float)snap)*snap; + } + + if (bwDiff) { + demod->setFrequency(currentFreq + bwDiff); + currentFreq = demod->getFrequency(); + demod->updateLabel(currentFreq); + } setStatusText("Set demodulator frequency: %s", demod->getFrequency()); } @@ -670,20 +679,23 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { DemodulatorInstance *demod = (*demodsHover)[i]; long long freqDiff = demod->getFrequency() - freqPos; long halfBw = (demod->getBandwidth() / 2); - + long long currentBw = getBandwidth(); + long long globalBw = wxGetApp().getSampleRate(); long dist = abs(freqDiff); + double bufferBw = 10000.0 * ((double)currentBw / (double)globalBw); + double maxDist = ((double)halfBw + bufferBw); - if (dist < near_dist) { - activeDemodulator = demod; - near_dist = dist; - } - - - if (dist <= halfBw && dist >= (int)((float) halfBw / 1.5)) { - if ((freqDiff > 0 && activeDemodulator->getDemodulatorType() == DEMOD_TYPE_USB) || - (freqDiff < 0 && activeDemodulator->getDemodulatorType() == DEMOD_TYPE_LSB)) { + if ((double)dist <= maxDist) { + if ((freqDiff > 0 && demod->getDemodulatorType() == DEMOD_TYPE_USB) || + (freqDiff < 0 && demod->getDemodulatorType() == DEMOD_TYPE_LSB)) { continue; } + + if (dist < near_dist) { + activeDemodulator = demod; + near_dist = dist; + } + long edge_dist = abs(halfBw - dist); if (edge_dist < near_dist) { activeDemodulator = demod; @@ -693,6 +705,8 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { } if (activeDemodulator == NULL) { + nextDragState = WF_DRAG_NONE; + SetCursor(wxCURSOR_CROSS); return; } @@ -701,15 +715,16 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { long long freqDiff = activeDemodulator->getFrequency() - freqPos; if (abs(freqDiff) > (activeDemodulator->getBandwidth() / 3)) { - SetCursor(wxCURSOR_SIZEWE); if (freqDiff > 0) { if (activeDemodulator->getDemodulatorType() != DEMOD_TYPE_USB) { nextDragState = WF_DRAG_BANDWIDTH_LEFT; + SetCursor(wxCURSOR_SIZEWE); } } else { if (activeDemodulator->getDemodulatorType() != DEMOD_TYPE_LSB) { nextDragState = WF_DRAG_BANDWIDTH_RIGHT; + SetCursor(wxCURSOR_SIZEWE); } } @@ -745,6 +760,10 @@ void WaterfallCanvas::OnMouseDown(wxMouseEvent& event) { dragState = nextDragState; if (dragState && dragState != WF_DRAG_RANGE) { + DemodulatorInstance *demod = wxGetApp().getDemodMgr().getActiveDemodulator(); + if (demod) { + dragOfs = (long long) (mouseTracker.getMouseX() * (float) getBandwidth()) + getCenterFrequency() - (getBandwidth() / 2) - demod->getFrequency(); + } wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false); } } @@ -765,13 +784,32 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { mouseTracker.setVertDragLock(false); mouseTracker.setHorizDragLock(false); - DemodulatorInstance *demod; + DemodulatorInstance *demod = isNew?NULL:wxGetApp().getDemodMgr().getLastActiveDemodulator(); + DemodulatorInstance *activeDemod = isNew?NULL:wxGetApp().getDemodMgr().getActiveDemodulator(); + DemodulatorMgr *mgr = &wxGetApp().getDemodMgr(); if (mouseTracker.getOriginDeltaMouseX() == 0 && mouseTracker.getOriginDeltaMouseY() == 0) { float pos = mouseTracker.getMouseX(); long long input_center_freq = getCenterFrequency(); - long long freq = input_center_freq - (long long) (0.5 * (float) getBandwidth()) + (long long) ((float) pos * (float) getBandwidth()); + long long freqTarget = input_center_freq - (long long) (0.5 * (float) getBandwidth()) + (long long) ((float) pos * (float) getBandwidth()); + long long demodFreq = demod?demod->getFrequency():freqTarget; + long long bwDiff = freqTarget - demodFreq; + long long freq = demodFreq; + + int snap = wxGetApp().getFrequencySnap(); + + if (snap > 1) { + if (demod) { + bwDiff = roundf((double)bwDiff/(double)snap)*snap; + freq += bwDiff; + } else { + freq = roundl((long double)freq/(double)snap)*snap; + } + } else { + freq += bwDiff; + } + if (dragState == WF_DRAG_NONE) { if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) { @@ -791,10 +829,9 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { demod->run(); wxGetApp().bindDemodulator(demod); - wxGetApp().getDemodMgr().setActiveDemodulator(demod, false); } - if (demod == NULL) { + if (!demod) { dragState = WF_DRAG_NONE; return; } @@ -808,15 +845,19 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { setStatusText("Moved demodulator to frequency: %s", freq); } - wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false); + wxGetApp().getDemodMgr().setActiveDemodulator(demod, false); SetCursor(wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(false); } else { - wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false); - wxGetApp().getDemodMgr().getActiveDemodulator()->setTracking(true); - nextDragState = WF_DRAG_FREQUENCY; + if (activeDemod) { + wxGetApp().getDemodMgr().setActiveDemodulator(activeDemod, false); + activeDemod->setTracking(true); + nextDragState = WF_DRAG_FREQUENCY; + } else { + nextDragState = WF_DRAG_NONE; + } } } else if (dragState == WF_DRAG_RANGE) { float width = mouseTracker.getOriginDeltaMouseX(); @@ -853,6 +894,13 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { return; } + int snap = wxGetApp().getFrequencySnap(); + + if (snap > 1) { + freq = roundl((long double)freq/(double)snap)*snap; + } + + if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) { demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); } else { @@ -869,7 +917,6 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { demod->run(); wxGetApp().bindDemodulator(demod); - wxGetApp().getDemodMgr().setActiveDemodulator(demod, false); } if (demod == NULL) { @@ -879,10 +926,10 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { setStatusText("New demodulator at frequency: %s", freq); - wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false); demod->updateLabel(freq); demod->setFrequency(freq); demod->setBandwidth(bw); + wxGetApp().getDemodMgr().setActiveDemodulator(demod, false); } dragState = WF_DRAG_NONE; diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index de1389b..7c06271 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -74,6 +74,7 @@ private: int fft_size; int waterfall_lines; + int dragOfs; msresamp_crcf resampler; double resamplerRatio;