diff --git a/quisk.py b/quisk.py index 24d2ee5..644e187 100644 --- a/quisk.py +++ b/quisk.py @@ -39,6 +39,7 @@ from filters import Filters import dxcluster import configure import quisk_conf_defaults as conf +import quisk_utils import quisk_wdsp for name in configure.name2format: # Add defaults. These may be overwritten by a user config file. @@ -2584,7 +2585,7 @@ class GraphScreen(wx.Window): else: self.mouse_is_rx = False if self.mouse_is_rx: - freq = application.rxFreq + self.VFO + wm * event.GetWheelRotation() // event.GetWheelDelta() + freq = application.rxFreq + self.VFO + quisk_utils.wheel_step(event.GetWheelRotation(), event.GetWheelDelta(), wm) if conf.freq_spacing: freq = self.FreqRound(freq, 0) elif freq >= 0: @@ -2594,7 +2595,7 @@ class GraphScreen(wx.Window): tune = freq - self.VFO self.ChangeHwFrequency(self.txFreq, self.VFO, 'MouseWheel', event=event, rx_freq=tune) else: - freq = self.txFreq + self.VFO + wm * event.GetWheelRotation() // event.GetWheelDelta() + freq = self.txFreq + self.VFO + quisk_utils.wheel_step(event.GetWheelRotation(), event.GetWheelDelta(), wm) if conf.freq_spacing: freq = self.FreqRound(freq, 0) elif freq >= 0: diff --git a/quisk_utils.py b/quisk_utils.py index 4abe8a3..cbf70a0 100644 --- a/quisk_utils.py +++ b/quisk_utils.py @@ -1,6 +1,29 @@ from __future__ import print_function from __future__ import division + +def wheel_step(rotation, delta, step): + """Convert signed wheel rotation into configured frequency steps. + + A measured downward event reported rotation=-180 with delta=120. Scaling + that by a 50 Hz step gives -75 Hz; flooring the resulting absolute frequency + made downward tuning jump -100 Hz while upward tuning moved only +50 Hz. + Round the wheel units symmetrically first: +/-180 now gives +/-50 Hz, while + +/-240 still gives +/-100 Hz rather than being collapsed to one step. + """ + if delta == 0: + return 0 + rotation = int(rotation) + delta = int(delta) + step = int(step) + direction = -1 if rotation * delta < 0 else 1 + whole_steps, remainder = divmod(abs(rotation), abs(delta)) + if remainder * 2 > abs(delta): + whole_steps += 1 + + return direction * whole_steps * step + + class SplineInterpolator: # From Numerical Recipes in C """Interpolate a table of [x, y] values.""" def __init__(self, table, Xmin=None, Xmax=None): diff --git a/quisk_vna.py b/quisk_vna.py index cd23f57..b004e7b 100644 --- a/quisk_vna.py +++ b/quisk_vna.py @@ -26,6 +26,7 @@ import threading, webbrowser import _quisk as QS from quisk_widgets import * import configure +import quisk_utils DEBUG = 0 @@ -630,7 +631,7 @@ class GraphScreen(wx.Window): mouse_x, mouse_y = self.GetMousePosition(event) self.SetTxFreq(index=mouse_x - self.originX) def OnWheel(self, event): - tune = self.display.tune_tx + event.GetWheelRotation() // event.GetWheelDelta() + tune = self.display.tune_tx + quisk_utils.wheel_step(event.GetWheelRotation(), event.GetWheelDelta(), 1) self.SetTxFreq(index=tune) class HelpScreen(wx.html.HtmlWindow): diff --git a/test_wheel_steps.py b/test_wheel_steps.py new file mode 100644 index 0000000..db80470 --- /dev/null +++ b/test_wheel_steps.py @@ -0,0 +1,26 @@ +import quisk_utils + + +def test_wheel_step_rounds_half_steps_toward_zero(): + assert quisk_utils.wheel_step(120, 120, 50) == 50 + assert quisk_utils.wheel_step(-120, 120, 50) == -50 + assert quisk_utils.wheel_step(180, 120, 50) == 50 + assert quisk_utils.wheel_step(-180, 120, 50) == -50 + + +def test_wheel_step_preserves_whole_multiple_steps(): + assert quisk_utils.wheel_step(240, 120, 50) == 100 + assert quisk_utils.wheel_step(-240, 120, 50) == -100 + assert quisk_utils.wheel_step(180, 120, 1) == 1 + assert quisk_utils.wheel_step(-180, 120, 1) == -1 + + +def test_wheel_step_handles_zero_delta(): + assert quisk_utils.wheel_step(180, 0, 50) == 0 + + +if __name__ == '__main__': + test_wheel_step_rounds_half_steps_toward_zero() + test_wheel_step_preserves_whole_multiple_steps() + test_wheel_step_handles_zero_delta() + print('wheel_step ok') \ No newline at end of file