diff --git a/CHANGELOG.txt b/CHANGELOG.txt index aa43cb3..3776700 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,13 @@ +Quisk Version 4.2.37 August 2024 +================================= +Quisk now has a new option "Fixed tune offset" on the Config/radio/Options screen. This is meant for +Softrock radios that need a constant audio frequency to simplify amplitude and phase corrections. +The value zero means no change to Quisk tuning. Otherwise enter the fixed frequency offset from center. +If you are using the obsolete "mouse_tune_method = 1" please delete it and use this new feature. +If you use softrock_tune_vfo.py as your hardware file, you should replace it with softrock/hardware_usb.py. + +Quisk now works on Python 3.12. And I fixed a bug in the small screen graph width. + Quisk Version 4.2.36 August 2024 ================================= I added two new color schemes by Michael, DK1MI. The On/Off button color and text color can diff --git a/__init__.py b/__init__.py index 86f7764..cc010b3 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,2 @@ -#Quisk version 4.2.36 +#Quisk version 4.2.37 from .quisk import main diff --git a/configure.py b/configure.py index 68608d2..fa631f2 100644 --- a/configure.py +++ b/configure.py @@ -328,7 +328,7 @@ class Configuration: path = self.NormPath(path) if not os.path.isfile(path): dlg = wx.MessageDialog(None, - "Failure for hardware file %s!" % path, + "Can not find the hardware file %s!" % path, 'Hardware File', wx.OK|wx.ICON_ERROR) ret = dlg.ShowModal() dlg.Destroy() @@ -399,11 +399,12 @@ class Configuration: notebk.AddPage(self.radio_page, "Radios") self.radios_page_start = notebk.GetPageCount() notebk.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanging, notebk) + if Settings[1] in Settings[2]: + page = RadioNotebook(notebk, Settings[1]) + notebk.AddPage(page, "*%s*" % Settings[1]) for name in Settings[2]: - page = RadioNotebook(notebk, name) - if name == Settings[1]: - notebk.AddPage(page, "*%s*" % name) - else: + if name != Settings[1]: + page = RadioNotebook(notebk, name) notebk.AddPage(page, name) def GuessType(self): udp = conf.use_rx_udp @@ -477,7 +478,7 @@ class Configuration: for rxname, data in self.receiver_data: if rxname == receiver_name: return data - return None + return () def GetReceiverDatum(self, receiver_name, item_name): for rxname, data in self.receiver_data: if rxname == receiver_name: @@ -563,6 +564,8 @@ class Configuration: # self.format4name with the items that Configuration understands. # Dicts and lists are Python objects. All other items are text, not Python objects. # + # This parses the quisk_conf_defaults.py file and user-defined radios in ./*pkg/quisk_hardware.py. + # # Sections start with 16 #, section name # self.sections is a list of [section_name, section_data] # section_data is a list of [data_name, text, fmt, help_text, values] @@ -595,7 +598,7 @@ class Configuration: except: traceback.print_exc() def _ParserConf(self, filename): - re_AeqB = re.compile("^#?(\w+)\s*=\s*([^#]+)#*(.*)") # item values "a = b" + re_AeqB = re.compile(r"^#?(\w+)\s*=\s*([^#]+)#*(.*)") # item values "a = b" section = None data_name = None multi_line = False @@ -2227,7 +2230,8 @@ class ControlMixin: if self.radio_name == Settings[1]: # changed for current radio if name in ('hot_key_ptt_toggle', 'hot_key_ptt_if_hidden', 'keyupDelay', 'cwTone', 'pulse_audio_verbose_output', 'start_cw_delay', 'start_ssb_delay', 'maximum_tx_secs', 'quisk_serial_cts', 'quisk_serial_dsr', - 'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec', 'hermes_lite2_enable'): + 'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec', 'hermes_lite2_enable', + 'fixed_tune_offset'): setattr(conf, name, x) application.ImmediateChange(name) elif name[0:4] in ('lin_', 'win_'): @@ -2258,6 +2262,9 @@ class ControlMixin: elif hasattr(application.Hardware, "ImmediateChange"): setattr(conf, name, x) application.Hardware.ImmediateChange(name) + elif name[0:6] == 'keyer_': + setattr(conf, name, x) + application.ImmediateChange(name) def FormatOK(self, value, fmt4): # Check formats integer, number, boolean ok, v = self.EvalItem(value, fmt4) return ok @@ -2660,6 +2667,7 @@ class Radios(BaseWindow): # The "Radios" first-level page choices = [] for name, data in local_conf.receiver_data: choices.append(name) + choices.sort() self.add_type = self.AddComboCtrl(2, '', choices=choices, no_edit=True) self.add_type.SetSelection(0) item = self.AddTextL(3, "and name the new radio") @@ -2743,11 +2751,9 @@ class Radios(BaseWindow): # The "Radios" first-level page choices = Settings[2][:] # can rename any available radio self.rename_old.SetItems(choices) self.rename_old.SetSelection(0) - if "ConfigFileRadio" in choices: + if "ConfigFileRadio" in choices: # can not delete ConfigFileRadio choices.remove("ConfigFileRadio") - if Settings[1] in choices: - choices.remove(Settings[1]) - self.delete_name.SetItems(choices) # can not delete ConfigFileRadio nor the current radio + self.delete_name.SetItems(choices) self.delete_name.SetSelection(0) choices = Settings[2] + ["Ask me"] if "ConfigFileRadio" not in choices: @@ -2892,12 +2898,6 @@ class RadioSection(BaseWindow): # The pages for each section in the second-leve hlp = "To delete a row, select it and press this button." self.AddTextButtonHelp(2, "Delete Midi row", "Delete", self.OnMidiDelete, hlp) self.NextRow() - if self.section == "Timing and CW": - if col != 1: - col = 1 - self.NextRow() - self.NextRow() - self.AddTextL(1, 'Midi CW key moved to "Keys"', span=4) if not Keys: self.AddColSpacer(2, 20) self.AddColSpacer(5, 20) @@ -3688,8 +3688,10 @@ class RadioSound(BaseWindow): # The Sound page in the second-level notebook for self.NextRow() choices = (("48000", "96000", "192000"), ("0", "1"), ("0", "1"), (" ", "0", "1")) r = 0 - if "SoftRock" in self.radio_dict['hardware_file_type']: # Samples come from sound card - softrock = True + if "SoftRock" in self.radio_dict['hardware_file_type']: + softrock = True # Samples come from sound card + elif hasattr(application.Hardware, "use_softrock"): + softrock = application.Hardware.use_softrock else: softrock = False last_row = 8 diff --git a/docs.html b/docs.html index f8a7a46..16b4df2 100644 --- a/docs.html +++ b/docs.html @@ -194,7 +194,7 @@ Quisk is free open source software written in Python and C. It is hosted on the

If you have Python 3 installed on your computer, just continue to use that version. -On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11. +On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11 or 3.12. If you want to install a more recent version of Python, uninstall the old version first. Running multiple versions of Python is possible but can be confusing and is not necessary. A Quisk installation is needed for each version of Python you have. @@ -204,7 +204,7 @@ A Quisk installation is needed for each version of Python you have.

Windows does not include Python, so you must first install a recent version of 64-bit Python 3. If you already have 64-bit Python 3 installed, just keep using it. -On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11. +On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11 or 3.12. There may be newer versions of Python, but until support is added, use the versions listed above. Python is available from http://www.python.org. There is an option to Add Python to PATH. You MUST select @@ -239,13 +239,19 @@ python -m pip install --upgrade pyserial

python -m pip install --upgrade quisk

-You should then be able to start Quisk with the command "quisk". -You can also start Quisk with "python -m quisk". To create a Quisk shortcut on your desktop, right-click an empty space and select "New" and "Shortcut". -Use "quisk.exe" as the command and "Quisk" as the name. -The "quisk.exe" program is in your_install_directory\Scripts. +Enter this as the location of the item: +

+
+
+pythonw -m quisk
+
+
+

+and then name the shortcut, perhaps "Quisk". +If you check the properties you will see that Windows expanded the location and start directory to the full path. If you are curious, Quisk and its Python source files are installed in "your_install_directory\Lib\site-packages\quisk". -If you change to that directory you can run Quisk with "python quisk.py". +If you change to that directory you can run Quisk with "pythonw quisk.py".


@@ -824,8 +830,14 @@ SoftRock radios use an analog mixer to change the RF signal to stereo audio, and A high quality sound card is advisable. The analog mixer is not perfect, and it will be necessary to adjust the I and Q signals to equal amplitude and 90 degrees phase difference. Use the Config/Config screen buttons to bring up an adjustment screen. Adjustments must be made for each band, and separately for transmit and receive. -The adjustment depends on both the VFO frequency and the tuning offset from the VFO. See my paper +The adjustment depends on both the VFO frequency and the tuning offset from the VFO. +Quisk has an option to fix the tuning offset to a constant and to tune with the hardware. +The tuning offset then has a constant correction and it is only necessary to correct for the VFO, +a much less sensitive correction. +See my paper http://james.ahlstrom.name/phase_corr.html for more information. +
+
A good strategy is to pick a VFO near the band center, and record corrections at an Rx frequency of -15000, -1000, 1000 and 15000 Hertz. If the corrections are sensitive to VFO, record the corrections for these same Rx frequencies at VFOs equal to or slightly outside the upper and lower band edges. @@ -834,6 +846,10 @@ You can add as many correction points as desired. The corrections are saved in t by hand if you are a Python expert. Otherwise you can just read the values.

+If you fix the tuning offset to a constant, you can create a correction for VFO at the band center, or +the center and both edges. +
+
To create a Receive correction point for a given VFO and frequency, attach a signal generator to the SoftRock through an attenuator, and look at the image on the graph screen. If the signal is 3500 Hertz above the VFO, the image is 3500 Hertz below it. Choose "Measure" to measure the corrections and then "Add Cell". diff --git a/multuspkg/__init__.py b/multuspkg/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/multuspkg/__init__.py @@ -0,0 +1 @@ +# diff --git a/multuspkg/quisk_hardware.py b/multuspkg/quisk_hardware.py new file mode 100644 index 0000000..a27711d --- /dev/null +++ b/multuspkg/quisk_hardware.py @@ -0,0 +1,188 @@ +# This file is part of the Multus project. See https://www.multus-sdr.com/. +# It controls what items appear on the Hardware configuration screen and +# provides logic for each radio. + + +################ Receivers Multus CW, A transceiver from the Multus project. +## hardware_file_name Hardware file path, rfile +# This is the file that contains the control logic for each radio. +#hardware_file_name = "multuspkg/quisk_hardware.py" + +## widgets_file_name Widget file path, rfile +# This optional file adds additional controls for the radio. +#widgets_file_name = "" + +## rx_max_amplitude_correct Max ampl correct, number +# If you get your I/Q samples from a sound card, you will need to correct the +# amplitude and phase for inaccuracies in the analog hardware. The correction is +# entered using the controls from the "Rx Phase" button on the config screen. +# You must enter a positive number. This controls the range of the control. +#rx_max_amplitude_correct = 0.2 + +## rx_max_phase_correct Max phase correct, number +# If you get your I/Q samples from a sound card, you will need to correct the +# amplitude and phase for inaccuracies in the analog hardware. The correction is +# entered using the controls from the "Rx Phase" button on the config screen. +# You must enter a positive number. This controls the range of the control in degrees. +#rx_max_phase_correct = 10.0 + +## tx_level Tx Level, dict +# This is the level of the Tx audio sent to SoftRock hardware after all processing as a percentage +# number from 0 to 100. +# The level should be below 100 to allow headroom for amplitude and phase adjustments. +# Changes are immediate (no need to restart). +#tx_level = {} + +## digital_tx_level Digital Tx power %, integer +# Digital modes reduce power by the percentage on the config screen. +# This is the maximum value of the slider. +#digital_tx_level = 100 + +## keyer_speed Keyer speed WPM, integer +# This sets the keyer speed in words per minute. +#keyer_speed = 8 +#keyer_speed = 13 +#keyer_speed = 18 +#keyer_speed = 20 +#keyer_speed = 30 +#keyer_speed = 40 + +## keyer_type Type of keyer, text choice +# Use "Straight" mode for a straight key or a bug. Connect the tip of the key to the transceiver. +# Otherwise, select Iambic-A or Iambic-B mode of operation. +#keyer_type = "Straight" +#keyer_type = "Iambic-A" +#keyer_type = "Iambic-B" + +## keyer_space Keyer space, text choice +# This sets the type of spacing between elements or letters. +# "Element" attempts to provide proper spacing between the elements of a character. +# (The keyer always provides proper spacing between elements). +# "Letter" turns on letter spacing. This attempts to provide proper spacing between characters. +#keyer_space = "Element" +#keyer_space = "Letter" + +## keyer_weight Keyer weight, integer choice +# A weight of 50% means that the dit time equals the inter-element time, which is standard. +# Reducing the weight increases the inter-element time, increasing the weight reduces it. +#keyer_weight = 50 +#keyer_weight = 25 +#keyer_weight = 75 + +## keyer_paddle Keyer paddle, text choice +# "Normal" means the left paddle is DIT. "Reverse" means the left paddle is DAH. +#keyer_paddle = "Normal" +#keyer_paddle = "Reverse" + +import configure +import softrock +from softrock.hardware_usb import Hardware as BaseHardware +from softrock.hardware_usb import IN, OUT, UBYTE2, UBYTE4 +import _quisk as QS + +DEBUG = 0 + +class Hardware(BaseHardware): + def __init__(self, app, conf): + # Set constant parameters here: + conf.usb_vendor_id = 0x16c0 + conf.usb_product_id = 0x05dc + conf.si570_i2c_address = 0x55 + conf.si570_direct_control = False + conf.si570_xtal_freq = 114285000 + conf.repeater_delay = 0.25 + BaseHardware.__init__(self, app, conf) + QS.set_sparams(multus_cw_samples=1) + self.use_softrock = True + if DEBUG: + print ("The name of this radio is", configure.Settings[1]) + print ("The hardware file type is", conf.hardware_file_type) + def TransferOut(self, address, message): # message is a bytes array or an integer 0 to 255 + if isinstance(message, int): + message = message.to_bytes(1, 'big') + if self.usb_dev: + self.usb_dev.ctrl_transfer(OUT, address, self.si570_i2c_address + 0x700, 0, message) + if DEBUG: + print ("USB send to 0x%X" % address, message) + def open(self): + text = BaseHardware.open(self) + self.InitKeyer() + return text + def ChangeMode(self, mode): + ret = BaseHardware.ChangeMode(self, mode) + if mode in ("CWL", "CWU"): + cw_mode = b'C' # Adding "b" does not affect the value sent + if DEBUG: + print ("Mode is CW") + else: + cw_mode = b'U' + if DEBUG: + print ("Mode is not CW") + self.TransferOut(0x70, cw_mode) + return ret + def PollCwKey(self): # Called frequently by Quisk to check the CW key status + return # Quisk is always in Rx + def InitKeyer(self): + # Initialize the keyer parameters + conf = self.conf + if not hasattr(conf, "keyer_speed"): + conf.keyer_speed = 18 + if not hasattr(conf, "keyer_type"): + conf.keyer_type = "Straight" + if not hasattr(conf, "keyer_space"): + conf.keyer_space = "Element" + if not hasattr(conf, "keyer_weight"): + conf.keyer_weight = 50 + if not hasattr(conf, "keyer_paddle"): + conf.keyer_paddle = "Normal" + # We need to initialize in case the persistent hardware values differ from the Quisk values. + for name in ("keyer_speed", "keyer_type", "keyer_space", "keyer_weight", "keyer_paddle", "cwTone"): + self.ImmediateChange(name) + def ImmediateChange(self, name): + BaseHardware.ImmediateChange(self, name) + value = getattr(self.conf, name) + if DEBUG: + print ("ImmediateChange", name, value) + if name == "keyer_speed": + self.TransferOut(0x7B, value) + elif name == "keyer_type": + if value == "Straight": + mode = 0 + elif value == "Iambic-A": + mode = 1 + elif value == "Iambic-B": + mode = 2 + else: + mode = 0 + self.TransferOut(0x71, mode) + elif name == "keyer_space": + if value == "Element": + spacing = 0 + elif value == "Letter": + spacing = 1 + else: + spacing = 0 + self.TransferOut(0x75, spacing) + elif name == "keyer_weight": + weight = int(value) + self.TransferOut(0x77, weight) + elif name == "keyer_paddle": + if value == "Normal": + paddle = 0 + elif value == "Reverse": + paddle = 1 + else: + paddle = 0 + self.TransferOut(0x73, paddle) + elif name == "cwTone": + if value < 500: + tone_index = 0 # 400 Hz + elif 500 <= value < 700: + tone_index = 1 # 600 Hz + elif 700 <= value < 900: + tone_index = 2 # 800 Hz + else: + tone_index = 3 # 1000 Hz + self.TransferOut(0x7F, tone_index) + + diff --git a/quisk.py b/quisk.py index 1e2b646..2aad0b4 100644 --- a/quisk.py +++ b/quisk.py @@ -836,6 +836,8 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532 """Send text back to the client.""" if isinstance(text, Q3StringTypes): text = text.encode('utf-8', errors='ignore') + if HAMLIB_DEBUG: + print ("Rig2 send", text) try: self.sock.sendall(text) except socket.error: @@ -942,6 +944,8 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532 else: return 1 self.input = self.input.strip() # Here is our command line + if HAMLIB_DEBUG: + print ("Rig2 received", self.input) while self.input: # Parse the commands and call the appropriate handlers self.params = '' @@ -992,8 +996,12 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532 if rx: rx = rx[0] self.Reply('Frequency', rx.txFreq + rx.VFO, 0) + if HAMLIB_DEBUG: + print ("GetFreq rx", rx.txFreq, rx.VFO) else: self.Reply('Frequency', self.app.rxFreq + self.app.VFO, 0) + if HAMLIB_DEBUG: + print ("GetFreq app", self.app.rxFreq, self.app.txFreq, self.app.VFO) def SetFreq(self): # The Rx frequency freq = self.GetParamNumber() try: @@ -2218,7 +2226,8 @@ class GraphScreen(wx.Window): self.ChangeHwFrequency(self.txFreq, self.VFO, 'MouseBtn1', event=event, rx_freq=freq) else: self.ChangeHwFrequency(freq, self.VFO, 'MouseBtn1', event=event) - self.CaptureMouse() + if not self.HasCapture(): + self.CaptureMouse() def OnLeftUp(self, event): if self.HasCapture(): self.ReleaseMouse() @@ -2231,7 +2240,7 @@ class GraphScreen(wx.Window): mouse_x, mouse_y = self.GetMousePosition(event) if wx.GetKeyState(wx.WXK_SHIFT): mouse_x -= self.filter_center * self.data_width / sample_rate - if conf.mouse_tune_method: # Mouse motion changes the VFO frequency + if False: #conf.mouse_tune_method: # Mouse motion changes the VFO frequency x = (mouse_x - self.mouse_x) # Thanks to VK6JBL self.mouse_x = mouse_x freq = float(x) * sample_rate / self.data_width @@ -3821,16 +3830,15 @@ class App(wx.App): self.std_out_err.Show(False) else: self.std_out_err.Show(True) + wClient, hClient = frame.GetClientSize() #w, h = frame.GetSize().Get() - #ww, hh = frame.GetClientSizeTuple() - #print ('Main frame: size', w, h, 'client', ww, hh) + #print ('Main frame: size', w, h, 'client', wClient, hClient) # Find the data width from a list of preferred sizes; it is the width of returned graph data. # The graph_width is the width of data_width that is displayed. + # The total width of the graph is graph_width + self.graph_borders if conf.window_width > 0: - #wFrame, h = frame.GetClientSize().Get() # client window width - wFrame = conf.window_width - 15 # Thanks to Nick Abramenko, RA3PKJ graph = GraphScreen(frame, self.width//2, self.width//2, None) # make a GraphScreen to calculate borders - self.graph_width = wFrame - (graph.width - graph.graph_width) # less graph borders equals actual graph_width + self.graph_width = wClient - (graph.width - graph.graph_width) # less graph borders equals actual graph_width graph.Destroy() del graph if self.graph_width % 2 == 1: # Both data_width and graph_width are even numbers @@ -4706,7 +4714,7 @@ class App(wx.App): b.char_shortcut = 'l' self.MakeAccel(b) self.splitButton = WrapMenu(b, self.split_menu) - if conf.mouse_tune_method: # Mouse motion changes the VFO frequency + if False: #conf.mouse_tune_method: # Mouse motion changes the VFO frequency self.splitButton.Enable(False) left_row3.append(self.splitButton) b = QuiskCheckbutton(frame, self.OnBtnFDX, 'FDX', color=conf.color_test) @@ -5873,16 +5881,31 @@ class App(wx.App): The hardware will reply with the updated frequencies which may be different from those requested; use and display the returned tune and vfo. """ + #print ("ChangeHW", source, tune, vfo, vfo + tune) if not self.split_rxtx: self.rxFreq = tune # rxFreq must be correct before the call to Hardware.ChangeFrequency() elif rx_freq is not None: self.rxFreq = rx_freq + rx_offset = self.rxFreq - tune if self.screen == self.bandscope_screen: freq = vfo + tune tune = freq % 10000 vfo = freq - tune - tune, vfo = Hardware.ChangeFrequency(vfo + tune, vfo, source, band, event) - self.ChangeDisplayFrequency(tune - vfo, vfo, rx_freq is not None) + tune, vfo = Hardware.ChangeFrequency(vfo + tune, vfo, source, band, event) + self.ChangeDisplayFrequency(tune - vfo, vfo, rx_freq is not None) + elif conf.fixed_tune_offset: + # Tune with the VFO and keep a constant audio offset from the center. Tx tune is always conf.fixed_tune_offset. + tune_freq = vfo + tune + self.rxFreq = conf.fixed_tune_offset + rx_offset + if source == "BtnUpDown": + tune_freq = vfo + conf.fixed_tune_offset + else: + vfo = tune_freq - conf.fixed_tune_offset + tune_freq, vfo = Hardware.ChangeFrequency(tune_freq, vfo, source, band, event) + self.ChangeDisplayFrequency(tune_freq - vfo, vfo, True) + else: + tune, vfo = Hardware.ChangeFrequency(vfo + tune, vfo, source, band, event) + self.ChangeDisplayFrequency(tune - vfo, vfo, rx_freq is not None) def ChangeDisplayFrequency(self, tune, vfo, new_rxfreq=False): 'Change the frequency displayed by Quisk' change = 0 @@ -6379,7 +6402,7 @@ class App(wx.App): for client in self.hamlib_clients: # Service existing clients if not client.Process(): # False return indicates a closed connection; remove the handler for this client self.hamlib_clients.remove(client) - # print 'Remove', client.address + # print ('Remove', client.address) break def OnKeyHook(self, event): event.Skip() diff --git a/quisk_conf_defaults.py b/quisk_conf_defaults.py index a178355..643572f 100644 --- a/quisk_conf_defaults.py +++ b/quisk_conf_defaults.py @@ -1287,6 +1287,18 @@ dc_remove_bw = 100 #dc_remove_bw = 200 #dc_remove_bw = 400 +## fixed_tune_offset Fixed tune offset, integer +# If this is zero, Quisk tunes as usual. The hardware frequency (the VFO or center frequency) +# is fixed and Quisk tunes +# within the spectrum returned by the hardware. Otherwise enter a fixed offset between the +# center frequency and the tuning frequency. Then tuning is done by the hardware. +# This is useful with Softrock hardware as +# it simplifies the corrections for amplitude and phase imbalance. +# Changes are immediate (no need to restart). +fixed_tune_offset = 0 +#fixed_tune_offset = 5000 +#fixed_tune_offset = 10000 + diff --git a/setup.py b/setup.py index 510ae38..9a8eefd 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ import os # You must define the version here. A title string including # the version will be written to __init__.py and read by quisk.py. -Version = '4.2.36' +Version = '4.2.37' fp = open("__init__.py", "w") # write title string fp.write("#Quisk version %s\n" % Version) @@ -85,7 +85,7 @@ N1MM+ and software that uses Hamlib. url = 'http://james.ahlstrom.name/quisk/', packages = ['quisk', 'quisk.n2adr', 'quisk.softrock', 'quisk.freedvpkg', 'quisk.hermes', 'quisk.hiqsdr', 'quisk.afedrinet', 'quisk.soapypkg', - 'quisk.sdrmicronpkg', 'quisk.perseuspkg', 'quisk.ac2yd'], + 'quisk.sdrmicronpkg', 'quisk.perseuspkg', 'quisk.ac2yd', 'quisk.multuspkg'], package_dir = {'quisk' : '.'}, package_data = {'' : ['*.txt', '*.html', '*.so', '*.dll']}, entry_points = {'gui_scripts' : ['quisk = quisk.quisk:main', 'quisk_vna = quisk.quisk_vna:main']}, diff --git a/softrock/hardware_usb.py b/softrock/hardware_usb.py index c231f18..dc8deb5 100644 --- a/softrock/hardware_usb.py +++ b/softrock/hardware_usb.py @@ -178,6 +178,8 @@ class Hardware(BaseHardware): vfo = self.vfo + int(offset * 1000) # Convert kHz to Hz self.ChangeFrequency(vfo, vfo, 'repeater') return False + def ImmediateChange(self, name): + return def OnSpot(self, level): pass def HeartBeat(self): # Called at about 10 Hz by the main