diff --git a/CHANGELOG.txt b/CHANGELOG.txt index aff43d3..d3158b4 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,15 @@ +Quisk Version 4.2.47 November 2025 +=================================== +To improve the performance of Quisk with the Anan 10-E and other OpenHPSDR radios, I added +a new type of radio "OpenHPSDR". Use this type for the Anan 10-E and similar radios. It removes +all the protocol extensions added by the Hermes-Lite2 project. I removed the configuration +option "Hermes Lite2 protocol enable". This is now automatic. + +I added buttons for antennas 0, 1, 2 and 3 for OpenHPSDR radios that support more antennas. +I added controls for the OpenHPSDR preamplifier and attenuator. + +I do not have any Apache Labs radios, so I must depend on my users to report problems and solutions. + Quisk Version 4.2.46 October 2025 ================================== I fixed a bug in Hamlib rig 2 which happens with the new Pure Signal code. The Hamlib "f" command diff --git a/__init__.py b/__init__.py index 39bb04f..4fa9bd9 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,2 @@ -#Quisk version 4.2.46 +#Quisk version 4.2.47 from .quisk import main diff --git a/configure.py b/configure.py index d2253d8..b208188 100644 --- a/configure.py +++ b/configure.py @@ -88,6 +88,7 @@ class Configuration: platform_ignore = 'win_' self.sections = [] self.receiver_data = [] + self.receiver_explain = [] self.StatePath = conf.settings_file_path if not self.StatePath: self.StatePath = os.path.join(app.QuiskFilesDir, "quisk_settings.json") @@ -601,6 +602,7 @@ class Configuration: # Receiver sections start with 16 #, "Receivers ", receiver name, explain # self.receiver_data is a list of [receiver_name, receiver_data] # receiver_data is a list of [data_name, text, fmt, help_text, values] + # self.receiver_explain is a list of the text in Receivers: the name, a comma and the explanation. # Variable names start with ## variable_name variable_text, format # The format is integer, number, text, boolean, integer choice, text choice, rfile @@ -638,6 +640,7 @@ class Configuration: continue if line[0:27] == '################ Receivers ': section = 'Receivers' + self.receiver_explain.append(line[27:].strip()) args = line[27:].split(',', 1) rxname = args[0].strip() section_data = [] @@ -2298,7 +2301,7 @@ 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', 'fixed_tune_offset'): setattr(conf, name, x) application.ImmediateChange(name) @@ -2737,8 +2740,8 @@ class Radios(BaseWindow): # The "Radios" first-level page self.NextRow() rb = self.AddRadioButton(1, "Add a new radio with the general type", self.NewTop) choices = [] - for name, data in local_conf.receiver_data: - choices.append(name) + for explain in local_conf.receiver_explain: + choices.append(explain) choices.sort() self.add_type = self.AddComboCtrl(2, '', choices=choices, no_edit=True) self.add_type.SetSelection(0) @@ -2810,7 +2813,7 @@ class Radios(BaseWindow): # The "Radios" first-level page if not name or self.DuplicateName(name): return self.add_name.SetValue('') - typ = self.add_type.GetValue().strip() + typ = self.add_type.GetValue().split(',')[0].strip() if local_conf.AddRadio(name, typ): self.NewRadioNames() local_conf.settings_changed = True diff --git a/hermes/hpsdr_widgets.py b/hermes/hpsdr_widgets.py new file mode 100644 index 0000000..4466330 --- /dev/null +++ b/hermes/hpsdr_widgets.py @@ -0,0 +1,31 @@ +# Please do not change this widgets module for Quisk. Instead copy +# it to your own quisk_widgets.py and make changes there. +# +# This module is used to add extra widgets to the QUISK screen. + +import wx + +class BottomWidgets: # Add extra widgets to the bottom of the screen + def __init__(self, app, hardware, conf, frame, gbs, vertBox): + self.config = conf + self.hardware = hardware + self.application = app + self.start_row = app.widget_row # The first available row + self.start_col = app.button_start_col # The start of the button columns + self.num_rows_added = 1 + self.btnPreamp = app.QuiskCheckbutton(frame, self.OnBtnPreamp, text='Preamp') + gbs.Add(self.btnPreamp, (self.start_row, self.start_col), flag=wx.EXPAND) + init = app.hermes_atten_dB + self.sliderAtten = app.SliderBoxHH(frame, 'Atten %d dB', init, 0, 31, self.OnAtten, True) + self.sliderAtten.idName = "RfAtten" + app.midiControls["RfAtten"] = (self.sliderAtten, self.OnAtten) + hardware.ChangeAtten(init) + gbs.Add(self.sliderAtten, (self.start_row, self.start_col + 2), (1, 6), flag=wx.EXPAND) + def OnBtnPreamp(self, event): + self.hardware.ChangePreamp(self.btnPreamp.GetValue()) + def OnAtten(self, event): + value = self.sliderAtten.GetValue() + self.hardware.ChangeAtten(value) + self.application.hermes_atten_dB = value + def UpdateText(self): # Called at intervals + pass diff --git a/hermes/quisk_hardware.py b/hermes/quisk_hardware.py index 3108924..52ba171 100644 --- a/hermes/quisk_hardware.py +++ b/hermes/quisk_hardware.py @@ -48,6 +48,8 @@ class IOBoard: def HeartBeat(self): # Called at 10 Hz for housekeeping tasks if not QS.get_params('rx_udp_started'): return + if self.hardware.hermes_board_id != 6: + return if self.have_IO_Board is None: resp = self.hardware.ReadI2C(0x7d, 0x41, 0) # Check for the N2ADR HL2 IO Board if resp and resp[1] == 0xF1: @@ -166,7 +168,10 @@ class Hardware(BaseHardware): self.vna_count = 0 self.vna_started = False self.repeater_freq = None # original repeater output frequency - self.antenna_labels = ('Ant 0', 'Ant 1') # labels for the antenna button + if conf.hardware_file_type == "Red Pitaya": + self.antenna_labels = ('Ant 0', 'Ant 1') # labels for the antenna button + else: + self.antenna_labels = ('Ant 0', 'Ant 1', 'Ant 2', 'Ant 3') self.antenna_index = 0 # index of antenna in use self.delay_config = True # Delay sending message to HL2 until after sound starts self.TFRC_counter = 0 # Call for power etc. at intervals @@ -529,7 +534,9 @@ class Hardware(BaseHardware): def ChangeBandFilters(self): if not hasattr(self.application, "multi_rx_screen"): return # Needed for the VNA program - self.SetControlBit(0x00, 13, self.antenna_index) + self.SetControlBit(0x00, 13, self.antenna_index & 0x01) + self.SetControlBit(0x00, 14, self.antenna_index & 0x02) + self.SetControlBit(0x00, 15, self.antenna_index > 0) highest = self.band freq = self.conf.BandEdge.get(highest, (0, 0))[0] for pane in self.application.multi_rx_screen.receiver_list: @@ -606,12 +613,7 @@ class Hardware(BaseHardware): return (48000, 384000) ## Hardware AGC is no longer supported in HL2 identifying as version >=40 def ChangeAGC(self, value): - if value: - self.pc2hermes[2] |= 0x10 # C0 index == 0, C3[4]: AGC enable - else: - self.pc2hermes[2] &= ~0x10 - QS.pc_to_hermes(self.pc2hermes) - if DEBUG: print ("Change AGC to", value) + pass ## Simpler LNA setting for HL2 identifying as version >=40, see HL2 wiki for details def ChangeLNA(self, value): # LNA for Rx # value is -12 to +48 @@ -624,7 +626,7 @@ class Hardware(BaseHardware): value = 51 - value else: value = ((value+12) & 0x3f) | 0x40 - self.pc2hermes[4 * 10 + 3] = value # C0 index == 0x1010, C4[4:0] LNA 0-32 dB gain + self.pc2hermes[4 * 10 + 3] = value # C0 index == 0b1010, C4[4:0] LNA 0-32 dB gain QS.pc_to_hermes(self.pc2hermes) if DEBUG: print ("Change LNA to", value) def ChangeTxLNA(self, value): # LNA for Tx @@ -634,9 +636,17 @@ class Hardware(BaseHardware): elif value > 48: value = 48 value = ((value+12) & 0x3f) | 0x40 | 0x80 - self.SetControlByte(0x0e, 3, value, False) # C0 index == 0x0e, C3 + self.SetControlByte(0x0e, 3, value, False) # C0 index == 0b1110 == 0x0e, C3 QS.pc_to_hermes(self.pc2hermes) if DEBUG: print ("Change Tx LNA to", value) + def ChangeAtten(self, value): + # value is 0 to 31 + self.pc2hermes[4 * 10 + 3] = value & 0x1F # C0 index == 0b1010, C4[4:0] attenuator, 0 to 31 dB + self.SetControlBit(0b1010, 5, value) # enable attenuator + QS.pc_to_hermes(self.pc2hermes) + if DEBUG: print ("Change attenuator to", value) + def ChangePreamp(self, value): + self.SetControlBit(0b0000, 10, value) # enable preamp def SetTxLevel(self): try: tx_level = self.conf.tx_level[self.band] @@ -653,7 +663,7 @@ class Hardware(BaseHardware): tx_level = 0 elif tx_level > 255: tx_level = 255 - self.pc2hermes[4 * 9] = tx_level # C0 index == 0x1001, C1[7:0] Tx level + self.pc2hermes[4 * 9] = tx_level # C0 index == 0b1001, C1[7:0] Tx level QS.pc_to_hermes(self.pc2hermes) if DEBUG: print("Change tx_level to", tx_level) def MultiRxCount(self, count): # count == number of additional receivers besides the Tx/Rx receiver: 1, 2, 3 @@ -876,6 +886,8 @@ class Hardware(BaseHardware): QS.set_hermeslite_writepointer(0) return False def WriteQueue(self, wait=False): + if self.hermes_board_id != 6: + return False self._wait_queue() # Send next write self.old_writequeue = self.pc2hermeslitewritequeue[:] diff --git a/n2adr/hl2_hardware.py b/n2adr/hl2_hardware.py index 1f6e2b3..bb928a1 100644 --- a/n2adr/hl2_hardware.py +++ b/n2adr/hl2_hardware.py @@ -26,12 +26,11 @@ class Hardware(BaseHw): return BaseHw.close(self) def ChangeFilterFrequency(self, tx_freq): if tx_freq and tx_freq > 0: + self.v2filter.SetTxFreq(tx_freq) if self.GUI: self.GUI.SetTxFreq(tx_freq) - self.GUI.freq_entry.ChangeValue("%.3f" % (tx_freq * 1E-6)) else: self.anttuner.SetTxFreq(tx_freq) - self.v2filter.SetTxFreq(tx_freq) def ChangeFrequency(self, tx_freq, vfo_freq, source='', band='', event=None): self.ChangeFilterFrequency(tx_freq) return BaseHw.ChangeFrequency(self, tx_freq, vfo_freq, source, band, event) diff --git a/n2adr/station_hardware.py b/n2adr/station_hardware.py index 79a55b5..c197c08 100644 --- a/n2adr/station_hardware.py +++ b/n2adr/station_hardware.py @@ -1,9 +1,5 @@ # This file supports various hardware boxes at my shack -from __future__ import print_function -from __future__ import absolute_import -from __future__ import division - import sys, struct, math, socket, select, time, traceback, os import wx, wx.lib.buttons @@ -410,19 +406,16 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole self.set_C = 0 self.set_HighZ = 0 self.antnum = 0 # Antenna number 0 or 1 - self.dipole2 = None #AntennaControl(app, conf) # Control the KI8BV dipole - if False and conf.use_rx_udp == 10: # Hermes UDP protocol for Hermes-Lite2 - path = 'TunerLCZ_HL2.txt' - else: - path = 'TunerLCZ.txt' + self.dipole2 = AntennaControl(app, conf) # Control the KI8BV dipole + path = 'TunerLCZ.txt' if sys.platform == "win32": - path = 'C:/pub/' + path + self.path = 'C:/pub/' + path else: - path = '/home/jim/pub/' + path - fp = open(path, "r") + self.path = '/home/jim/pub/' + path + fp = open(self.path, "r") lines = fp.readlines() fp.close() - self.TunerLCZ = [(0, 0, 0, 0), (999111000, 0, 0, 0)] # Add dummy first and last entry. + self.TunerLCZ = [] for line in lines: freq, antL, antC, hilo = line.split() freq = int(freq) @@ -451,7 +444,12 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole pass def ChangeBand(self, band): pass - def FindLCZ(self, tx_freq): + def SaveLCZ(self): + fp = open(self.path, 'w') + for tup in self.TunerLCZ: + fp.write("%12d %2d %2d %d\n" % tup) + fp.close() + def FindIndexTup(self, tx_freq): i1 = 0 i2 = len(self.TunerLCZ) - 1 while 1: # binary partition @@ -462,20 +460,14 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole i2 = i if i2 - i1 <= 1: break - # The correct setting is between i1 and i2. + # The correct setting is between i1 and i2, or below i1 or above i2. # Choose the index that is closest in frequency. - delta1 = tx_freq - self.TunerLCZ[i1][0] - delta2 = self.TunerLCZ[i2][0] - tx_freq - if i1 == 0: # below the first frequency + if abs(tx_freq - self.TunerLCZ[i1][0]) < abs(tx_freq - self.TunerLCZ[i2][0]): index = i1 - elif i2 == len(self.TunerLCZ) - 1: # above the last frequency + else: index = i2 - elif delta2 < delta1: # i2 is closer - index = i2 - else: # i1 is closer - index = i1 - if DEBUG: print ("AntennaTuner FindLCZ", tx_freq, i1, i2, len(self.TunerLCZ), self.TunerLCZ[index]) - return self.TunerLCZ[index] + if DEBUG: print ("StatuinControlGUI FindIndexTup", tx_freq, i1, i2, len(self.TunerLCZ), index, self.TunerLCZ[index]) + return index, self.TunerLCZ[index] def SetTxFreq(self, tx_freq, no_tune=False): #if DEBUG: print("AntennaTuner SetTxFreq", tx_freq) self.tx_freq = tx_freq @@ -496,7 +488,7 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole if abs(self.tune_freq - tx_freq) < 5000: # ignore small changes return self.tune_freq = tx_freq - f, newL, newC, newH = self.FindLCZ(tx_freq) + index, (f, newL, newC, newH) = self.FindIndexTup(tx_freq) if newH != self.set_HighZ or self.set_C != newC or self.set_L != newL: self.set_HighZ = newH self.set_C = newC @@ -540,26 +532,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for self.conf = conf self.data_saving = False self.tx_freq = 0 - if sys.platform == "win32": - self.filename = 'C:/pub/TunerLCZ.tmp' - else: - self.filename = '/home/jim/pub/TunerLCZ.tmp' - try: - fp = open(self.filename, "r") - except FileNotFoundError: - lines = () - else: - lines = fp.readlines() - fp.close() - self.TunerLCZ = [(0, 0, 0, 0), (999111000, 0, 0, 0)] # Add dummy first and last entry. - for line in lines: - freq, antL, antC, hilo = line.split() - freq = int(freq) - antL = int(antL) - antC = int(antC) - hilo = int(hilo) - self.TunerLCZ.append((freq, antL, antC, hilo)) - self.TunerLCZ.sort() + self.anttuner = self.hware.anttuner self.SetBackgroundColour('light steel blue') self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown) sizer = wx.GridBagSizer(hgap=5, vgap=3) @@ -569,6 +542,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for self.bands = [] for band in ('160', '80', '60', '40', '30', '20', '17', '15', '12', '10'): b = wx.lib.buttons.GenToggleButton(self, -1, band) + b.Enable(False) b.SetUseFocusIndicator(False) b.SetBezelWidth(4) self.bands.append(b) @@ -605,6 +579,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for szr = wx.BoxSizer(wx.HORIZONTAL) szr.AddSpacer(20) b = wx.lib.buttons.GenToggleButton(self, -1, "Key Down") + b.Enable(False) b.SetFont(font) b.SetUseFocusIndicator(False) b.SetBezelWidth(4) @@ -612,6 +587,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for szr.Add(b) szr.AddSpacer(10) b = wx.lib.buttons.GenToggleButton(self, -1, "FilterV2 Preamp") + b.Enable(False) b.SetFont(font) b.SetUseFocusIndicator(False) b.SetBezelWidth(4) @@ -623,16 +599,31 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for b.SetFont(font) szr.Add(b, flag=wx.ALIGN_CENTER_VERTICAL) szr.AddSpacer(10) - self.freq_entry = wx.TextCtrl(self, value='0000.000', style=wx.TE_PROCESS_ENTER) + self.freq_entry = wx.StaticText(self, -1, "0000.000") + #self.freq_entry = wx.TextCtrl(self, value='0000.000', style=wx.TE_PROCESS_ENTER) self.freq_entry.SetFont(font) - self.Bind(wx.EVT_TEXT_ENTER, self.OnFreqEntry, self.freq_entry) - szr.Add(self.freq_entry) + #self.Bind(wx.EVT_TEXT_ENTER, self.OnFreqEntry, self.freq_entry) + szr.Add(self.freq_entry, flag=wx.ALIGN_CENTER_VERTICAL) szr.AddSpacer(10) - b = wx.lib.buttons.GenButton(self, -1, "Search Freq") + b = wx.lib.buttons.GenButton(self, -1, "Freq-") b.SetUseFocusIndicator(False) b.SetBezelWidth(4) b.SetFont(font) - self.Bind(wx.EVT_BUTTON, self.OnButtonSearch, b) + self.Bind(wx.EVT_BUTTON, self.OnButtonDown, b) + szr.Add(b) + szr.AddSpacer(10) + b = wx.lib.buttons.GenButton(self, -1, "Freq+") + b.SetUseFocusIndicator(False) + b.SetBezelWidth(4) + b.SetFont(font) + self.Bind(wx.EVT_BUTTON, self.OnButtonUp, b) + szr.Add(b) + szr.AddSpacer(10) + b = wx.lib.buttons.GenButton(self, -1, "Delete") + b.SetUseFocusIndicator(False) + b.SetBezelWidth(4) + b.SetFont(font) + self.Bind(wx.EVT_BUTTON, self.OnButtonDelete, b) szr.Add(b) sizer.Add(szr, pos=(row, 0), span=(1, 3)) if frame is None: @@ -648,13 +639,13 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for self.Bind(wx.EVT_TIMER, self.HeartBeat) self.timer.Start(100) def SetTxFreq(self, tx_freq): - #print("StationControlGUI SetTxFreq", tx_freq) + tx_freq = ((tx_freq + 500) // 1000) * 1000 self.tx_freq = tx_freq - index, tup = self.FindIndexTup(tx_freq) + anttuner = self.anttuner + index, tup = anttuner.FindIndexTup(tx_freq) self.sliderL.SetValue(tup[1]) self.sliderC.SetValue(tup[2]) self.btnHiLo.SetValue(tup[3]) - anttuner = self.hware.anttuner fff, anttuner.set_L, anttuner.set_C, anttuner.set_HighZ = tup if tx_freq < 17000000: anttuner.antnum = 0 @@ -663,31 +654,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for if anttuner.antnum == 1 and anttuner.dipole2: anttuner.dipole2.SetTxFreq(tx_freq) anttuner.WantData() - def FindIndexTup(self, tx_freq): - i1 = 0 - i2 = len(self.TunerLCZ) - 1 - while 1: # binary partition - i = (i1 + i2) // 2 - if self.TunerLCZ[i][0] < tx_freq: - i1 = i - else: - i2 = i - if i2 - i1 <= 1: - break - # The correct setting is between i1 and i2. - # Choose the index that is closest in frequency. - delta1 = tx_freq - self.TunerLCZ[i1][0] - delta2 = self.TunerLCZ[i2][0] - tx_freq - if i1 == 0: # below the first frequency - index = i1 - elif i2 == len(self.TunerLCZ) - 1: # above the last frequency - index = i2 - elif delta2 < delta1: # i2 is closer - index = i2 - else: # i1 is closer - index = i1 - if DEBUG: print ("StatuinControlGUI FindLCZ", tx_freq, i1, i2, len(self.TunerLCZ), index, self.TunerLCZ[index]) - return index, self.TunerLCZ[index] + self.freq_entry.SetLabel("%.3f" % (tx_freq * 1E-6)) def OnBtnClose(self, event): self.hware.anttuner.close() self.hware.controlbox.close() @@ -731,16 +698,15 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for f = ((f + 500) // 1000) * 1000 if self.hware.v2filter: self.hware.v2filter.SetTxFreq(f) - self.hware.anttuner.SetTxFreq(f, no_tune=True) + self.anttuner.SetTxFreq(f, no_tune=True) self.freq_entry.ChangeValue("%.3f" % (f * 1E-6)) self.SetTxFreq(f) def OnButtonHiLo(self, event): - anttuner = self.hware.anttuner if self.btnHiLo.GetValue(): - anttuner.set_HighZ = 1 + self.anttuner.set_HighZ = 1 else: - anttuner.set_HighZ = 0 - anttuner.WantData() + self.anttuner.set_HighZ = 0 + self.anttuner.WantData() def OnFreqEntry(self, event=None): freq = self.freq_entry.GetValue() freq = float(freq) * 1E6 @@ -752,11 +718,27 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for freq = int(freq + 0.1) self.tx_freq = freq self.SetTxFreq(self.tx_freq) + def OnButtonDown(self, event): + index, tup = self.anttuner.FindIndexTup(self.tx_freq) + index -= 1 + if index >= 0: + self.SetTxFreq(self.anttuner.TunerLCZ[index][0]) + def OnButtonUp(self, event): + index, tup = self.anttuner.FindIndexTup(self.tx_freq) + index += 1 + if index < len(self.anttuner.TunerLCZ): + self.SetTxFreq(self.anttuner.TunerLCZ[index][0]) + def OnButtonDelete(self, event): + index, tup = self.anttuner.FindIndexTup(self.tx_freq) + if self.tx_freq == self.anttuner.TunerLCZ[index][0]: + del self.anttuner.TunerLCZ[index] + if index > 0: + self.SetTxFreq(self.anttuner.TunerLCZ[index - 1][0]) + elif len(self.anttuner.TunerLCZ) > 0: + self.SetTxFreq(self.anttuner.TunerLCZ[0][0]) + else: + self.SetTxFreq(0) def OnButtonSave(self, event): - freq = self.freq_entry.GetValue() - freq = float(freq) * 1E6 - freq = int(freq + 0.1) - self.tx_freq = freq L = self.sliderL.GetValue() C = self.sliderC.GetValue() if self.btnHiLo.GetValue(): @@ -764,22 +746,19 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for else: hilo = 0 tup = (self.tx_freq, L, C, hilo) - index, t = self.FindIndexTup(self.tx_freq) - if self.tx_freq == self.TunerLCZ[index][0]: - self.TunerLCZ[index] = tup + index, t = self.anttuner.FindIndexTup(self.tx_freq) + if self.tx_freq == self.anttuner.TunerLCZ[index][0]: + self.anttuner.TunerLCZ[index] = tup else: - self.TunerLCZ.append(tup) - self.TunerLCZ.sort() - fp = open(self.filename, 'w') - for tup in self.TunerLCZ[1:-1]: # Throw away dummy entries - fp.write("%12d %2d %2d %d\n" % tup) - fp.close() + self.anttuner.TunerLCZ.append(tup) + self.anttuner.TunerLCZ.sort() + self.anttuner.SaveLCZ() def OnSliderL(self, event=None): - self.hware.anttuner.set_L = self.sliderL.GetValue() - self.hware.anttuner.WantData() + self.anttuner.set_L = self.sliderL.GetValue() + self.anttuner.WantData() def OnSliderC(self, event=None): - self.hware.anttuner.set_C = self.sliderC.GetValue() - self.hware.anttuner.WantData() + self.anttuner.set_C = self.sliderC.GetValue() + self.anttuner.WantData() def OnBtnV2Preamp(self, event): if self.hware.v2filter: self.hware.v2filter.SetPreamp(event.GetEventObject().GetValue()) @@ -1073,21 +1052,3 @@ class AT200PC: # Control an AT-200PC autotuner made by LDG self.set_C += 1 elif text == 'C-': self.set_C -= 1 - -class App(wx.App): - def OnInit(self): - if sys.path[0] != "'.'": # Make sure the current working directory is on path - sys.path.insert(0, '.') - import quisk_conf_defaults as conf - import quisk_hardware_model as hardware - self.bottom_widgets = None - hardware.anttuner = AntennaTuner(self, conf) # Control the antenna tuner - hardware.v2filter = FilterBoxV2(self, conf) # Control V2 filter box - hardware.controlbox = ControlBox(self, conf) # Control my Station Control Box - self.main_frame = frame = StationControlGUI (None, hardware, self, conf) - frame.Open() - frame.Show() - return True - -if __name__ == '__main__': - App().MainLoop() diff --git a/perseuspkg/quisk_hardware.py b/perseuspkg/quisk_hardware.py index 0301342..3ad119c 100644 --- a/perseuspkg/quisk_hardware.py +++ b/perseuspkg/quisk_hardware.py @@ -18,7 +18,7 @@ from quisk_hardware_model import Hardware as BaseHardware DEBUG = 1 # Define the name of the hardware and the items on the hardware screen (see quisk_conf_defaults.py): -################ Receivers PerseusSDR, The PerseusSDR interface to multiple hardware SDRs. +################ Receivers PerseusSDR, The PerseusSDR interface to multiple SDRs. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'perseuspkg/quisk_hardware.py' diff --git a/quisk.py b/quisk.py index d8e397d..4a2ed9f 100644 --- a/quisk.py +++ b/quisk.py @@ -3718,7 +3718,7 @@ class App(wx.App): 'wfallScaleZ', 'wfallGrScaleZ', 'graphScaleZ', 'split_rxtx_play', 'modeFilter', 'file_name_rec_audio', 'file_name_rec_samples', 'file_name_rec_mic', 'file_name_rec_tmp', 'file_name_play_audio', 'file_name_play_samples', 'file_name_play_cq', - 'file_play_source', 'hermes_LNA_dB'] + 'file_play_source', 'hermes_LNA_dB', 'hermes_atten_dB'] def __init__(self): global application QS.AppStatus(1) @@ -3939,6 +3939,7 @@ class App(wx.App): self.serial_ptt_active = False self.vox_ptt_active = False self.hermes_LNA_dB = 20 + self.hermes_atten_dB = 0 if hasattr(Hardware, "OnChangeRxTx"): self.want_RxTx = True else: @@ -6798,7 +6799,7 @@ class App(wx.App): if self.timer - self.heart_time0 > 0.10: # call hardware to perform background tasks: self.heart_time0 = self.timer Hardware.HeartBeat() - if conf.hermes_lite2_enable: + if conf.use_rx_udp == 10 and Hardware.hermes_board_id == 6: self.tx_inhibit = QS.get_params('quisk_tx_inhibit') else: self.tx_inhibit = 0 diff --git a/quisk_conf_defaults.py b/quisk_conf_defaults.py index 67d7086..e80c491 100644 --- a/quisk_conf_defaults.py +++ b/quisk_conf_defaults.py @@ -48,7 +48,7 @@ quisk_widgets = None -################ Receivers SoftRock USB, Devices controlled by USB that capture samples from a sound card, and (for Tx) play samples to a sound card +################ Receivers SoftRock USB, Sound card radios controlled by USB. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'softrock/hardware_usb.py' @@ -133,7 +133,7 @@ digital_tx_level = 100 -################ Receivers SoftRock Fixed, Fixed frequency devices that capture samples from a sound card, and (for Tx) play samples to a sound card +################ Receivers SoftRock Fixed, Fixed frequency sound card radios. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'quisk_hardware_fixed.py' @@ -174,7 +174,7 @@ digital_tx_level = 100 -################ Receivers HiQSDR, The original N2ADR hardware and the improved HiQSDR using UDP +################ Receivers HiQSDR, The original N2ADR and HiQSDR radios. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'hiqsdr/quisk_hardware.py' @@ -258,7 +258,7 @@ sndp_active = True -################ Receivers Hermes, The Hermes-Lite Project and possibly other hardware with the Hermes FPGA code. +################ Receivers Hermes, The Hermes-Lite project by Steve Haynal. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'hermes/quisk_hardware.py' @@ -275,7 +275,7 @@ sndp_active = True # You can set these options: ## use_rx_udp Hardware type, integer choice -# This is the type of UDP hardware. Use 10 for the Hermes protocol. +# This is the type of UDP hardware. Use 10 for the Hermes Lite2 protocol. #use_rx_udp = 10 ## udp_rx_ip Hermes known IP, text @@ -373,6 +373,7 @@ hermes_disable_sync = False # be sure to write it down. To use this address you must set "Eeprom IP Usage". # And you may want to set "Hermes known IP" too. Make sure the address does not conflict # with your DHCP server. +# Power cycle the HL2 after changing this item. Hware_Hl2_EepromIP = '192.168.1.6' #Hware_Hl2_EepromIP = '192.168.1.241' @@ -380,6 +381,7 @@ Hware_Hl2_EepromIP = '192.168.1.6' # This is the way the EEPROM IP address is used at power on. # "Ignore" means it is not used at all. "Set address" means DHCP is not used and the EEPROM IP address is used. # "Use DHCP first" means the EEPROM IP address is only used if DHCP fails. +# Power cycle the HL2 after changing this item. Hware_Hl2_EepromIPUse = 'Ignore' #Hware_Hl2_EepromIPUse = 'Use DHCP first' #Hware_Hl2_EepromIPUse = 'Set address' @@ -390,6 +392,7 @@ Hware_Hl2_EepromIPUse = 'Ignore' # at power on instead of the factory supplied MAC address 0:1c:c0:a2:13:dd. It is only used at power on, # and it is only used if it is marked valid by setting "Eeprom MAC usage" to "Set address". # If you have two HL2's, you must have a unique MAC address for each. +# Power cycle the HL2 after changing this item. Hware_Hl2_EepromMAC = '0xA1 0x6B' #Hware_Hl2_EepromMAC = '0x4C 0x33' @@ -398,6 +401,7 @@ Hware_Hl2_EepromMAC = '0xA1 0x6B' # the HL2 will use the last two MAC bytes from "Eeprom MAC address"; otherwise the default MAC is used. # If you change this item, the bit is written to the HL2. # The HL2 only uses these bits at power on. +# Power cycle the HL2 after changing this item. Hware_Hl2_EepromMACUse = 'Ignore' #Hware_Hl2_EepromMACUse = 'Set address' @@ -474,13 +478,6 @@ hermes_iob_rxin = 'J10 available' #hermes_iob_rxin = 'HL2 Rx to J9' #hermes_iob_rxin = 'Use J9 and J10' -## hermes_lite2_enable Hermes Lite2 protocol, boolean -# This turns on some of the protocol extensions for the Hermes Lite2 that are not part -# of the official Hermes Protocol 1. Currently this only enables Tx Inhibit (CN8). -# Changes are immediate (no need to restart). -hermes_lite2_enable = True -#hermes_lite2_enable = False - # These are known power meter calibration tables. This table is not present in the JSON settings file. power_meter_std_calibrations = {} @@ -508,17 +505,17 @@ Hermes_BandDictEnTx = False #Hermes_BandDictEnTx = True -################ Receivers Red Pitaya, The Red Pitaya Project by Pavel Demin. This uses the Hermes FPGA code. +################ Receivers Red Pitaya, The Red Pitaya project by Pavel Demin. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'hermes/quisk_hardware.py' ## widgets_file_name Widget file path, rfile # This optional file adds additional controls for the radio. -#widgets_file_name = '' +#widgets_file_name = 'hermes/hpsdr_widgets.py' ## use_rx_udp Hardware type, integer choice -# This is the type of UDP hardware. Use 10 for the Hermes protocol. +# This is the type of UDP hardware. Use 10 for the Red Pitaya using the OpenHPSDR protocol. #use_rx_udp = 10 ## rx_udp_ip IP change, text @@ -586,7 +583,87 @@ Hermes_BandDictEnTx = False #Hermes_BandDictEnTx = True -################ Receivers SoapySDR, The SoapySDR interface to multiple hardware SDRs. +################ Receivers OpenHPSDR, Radios using the HPSDR Protocol 1. +## hardware_file_name Hardware file path, rfile +# This is the file that contains the control logic for each radio. +#hardware_file_name = 'hermes/quisk_hardware.py' + +## widgets_file_name Widget file path, rfile +# This optional file adds additional controls for the radio. +#widgets_file_name = 'hermes/hpsdr_widgets.py' + +## use_rx_udp Hardware type, integer choice +# This is the type of UDP hardware. Use 10 for hardware using the OpenHPSDR protocol 1. +#use_rx_udp = 10 + +## rx_udp_ip IP change, text +# This item should be left blank. It is used to change the IP address of the hardware to a different +# IP once the hardware is found. Not all Hermes firmware supports changing the IP address. +#rx_udp_ip = "" + +## rx_udp_port Hardware UDP port, integer +# This is the UDP port number of your hardware. +#rx_udp_port = 1024 + +## rx_udp_ip_netmask Network netmask, text +# This is the netmask for the network. +#rx_udp_ip_netmask = '255.255.255.0' + +## tx_ip Transmit IP, text +# Leave this blank to use the same IP address as the receive hardware. Otherwise, enter "disable" +# to disable sending transmit I/Q samples, or enter the actual IP address. You must enter "disable" +# if you have multiple hardwares on the network, and only one should transmit. This item is normally blank. +tx_ip = "" +#tx_ip = "disable" + +## tx_audio_port Tx audio UDP port, integer +# This is the UDP port for transmit audio I/Q samples. Enter zero to calculate this from the +# base hardware port. Otherwise enter the special custom port. +tx_audio_port = 0 + +## rx_udp_clock Clock frequency Hertz, integer +# This is the clock frequency of the hardware in Hertz. +#rx_udp_clock = 122880000 +#rx_udp_clock = 76800000 +#rx_udp_clock = 125000000 + +## tx_level Tx Level, dict +# This sets the transmit level 0 to 255 for each band. +# The config screen has a slider 0 to 100% so you can reduce the transmit power. +# The hardware only supports a limited adjustment range, so zero is still a small amount of power. +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 + +## hermes_code_version Hermes code version, integer +# There can be multiple Hermes devices on a network, but Quisk can only use one of these. If you have multiple +# hermes devices, you can use this to specify a unique device. Or use -1 to accept any board. +hermes_code_version = -1 + +## hermes_board_id Hermes board ID, integer +# There can be multiple Hermes devices on a network, but Quisk can only use one of these. If you have multiple +# hermes devices, you can use this to specify a unique device. Or use -1 to accept any board. +hermes_board_id = -1 + +## Hermes_BandDict Hermes Bus, dict +# The Hermes_BandDict sets the 7 bits on the J16 connector. +Hermes_BandDict = { + '160':0b0000001, '80':0b0000010, '60':0b0000100, '40':0b0001000, '30':0b0010000, '20':0b0100000, '15':0b1000000} + +## Hermes_BandDictTx Tx IO Bus, dict +# The Hermes_BandDictTx sets the 7 bits on the J16 connector for Tx if enabled. +Hermes_BandDictTx = {'160':0, '80':0, '60':0, '40':0, '30':0, '20':0, '17':0, '15':0, '12':0, '10':0} + +## Hermes_BandDictEnTx Enable Tx Filt, boolean +# Enable the separate Rx and Tx settings for the J16 connector. +Hermes_BandDictEnTx = False +#Hermes_BandDictEnTx = True + + +################ Receivers SoapySDR, The SoapySDR interface to multiple SDRs. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'soapypkg/quisk_hardware.py' @@ -602,7 +679,7 @@ Hermes_BandDictEnTx = False # Further items are present in the radio dictionary with names soapy_* -################ Receivers SdrIQ, The SDR-IQ receiver by RfSpace +################ Receivers SdrIQ, The SDR-IQ receiver by RfSpace. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. hardware_file_name = 'quisk_hardware_sdriq.py' @@ -633,7 +710,7 @@ sdriq_decimation = 1250 -################ Receivers Odyssey, The Odyssey project using a UDP protocol similar to the HiQSDR +################ Receivers Odyssey, The Odyssey project. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'hiqsdr/quisk_hardware.py' @@ -744,7 +821,7 @@ sndp_active = True #radio_sound_mic_boost = True -################ Receivers Odyssey2, The Odyssey-2 project using the HPSDR Hermes protocol +################ Receivers Odyssey2, The Odyssey-2 project. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'hermes/quisk_hardware.py' @@ -759,7 +836,7 @@ sndp_active = True # You can set these options: ## use_rx_udp Hardware type, integer choice -# This is the type of UDP hardware. Use 10 for the Hermes protocol. +# This is the type of UDP hardware. Use 10 for the Odyssey-2. #use_rx_udp = 10 ## rx_udp_ip IP change, text @@ -828,7 +905,7 @@ Hermes_BandDictEnTx = False #Hermes_BandDictEnTx = True -################ Receivers Afedri, The Afedri SDR receiver with the Ethernet interface. +################ Receivers Afedri, The Afedri SDR receiver with Ethernet. ## hardware_file_name Hardware file path, rfile # This is the file that contains the control logic for each radio. #hardware_file_name = 'afedrinet/quisk_hardware.py' @@ -860,7 +937,7 @@ Hermes_BandDictEnTx = False #default_rf_gain = 11 -################ Receivers Control Head, Use Quisk to control a remote radio. No real hardware is used here. +################ Receivers Control Head, Use Quisk to control a remote Quisk radio. ## hardware_file_name Hardware file path, rfile # Quisk can be used as a control head to control a real radio located remotely. # This file contains the control logic for remote radios of type Hermes, SoftRock or HiQSDR. diff --git a/setup.py b/setup.py index d47ee29..b1474b3 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.46' +Version = '4.2.47' fp = open("__init__.py", "w") # write title string fp.write("#Quisk version %s\n" % Version)