Quisk version 4.2.49

This commit is contained in:
James C. Ahlstrom
2025-12-22 15:12:47 -05:00
parent 1111d24333
commit 018fc0e871
10 changed files with 285 additions and 68 deletions
+22
View File
@@ -1,3 +1,25 @@
Quisk Version 4.2.49 December 2025
===================================
This version adds more features for the Anan 10E and other OpenHPSDR radios. Please test.
I added the forward power, SWR, temperature and PA current display to the OpenHPSDR radio. But
I don't have that radio so I need some help. I think the forward and reverse power connections
are correct, but it is still necessary to create a table to convert analog input codes to power.
Go to the Hardware screen and check the "Power meter calibration" setting. You will need to enter
"New" and create a suitable table. To see the ADC values, choose table "Show ADC".
There is no protocol standard to specify which analog input is used for temperature and PA current.
And AFAIK the Anan 10E has no such sensors. I am hoping to use the board ID to find the right input.
The ADC conversion is linear and the two points are specified on the Hardware screen.
I currently assigned the analog inputs as follows:
AIN1 Forward power
AIN2 Reverse power
AIN3 PA current
AIN5 Temperature
If you need to see the ADC codes 0-4095 for each input, go to hermes/quisk_hardware.py and
change DEBUG_SENSORS to one.
Quisk Version 4.2.48 November 2025 Quisk Version 4.2.48 November 2025
=================================== ===================================
This is a quisk fix for a bug in the new OpenHPSDR radios. It fixes use_rx_udp that was reset to zero. This is a quisk fix for a bug in the new OpenHPSDR radios. It fixes use_rx_udp that was reset to zero.
+1 -1
View File
@@ -1,2 +1,2 @@
#Quisk version 4.2.48 #Quisk version 4.2.49
from .quisk import main from .quisk import main
+9 -19
View File
@@ -2302,7 +2302,7 @@ class ControlMixin:
if name in ('hot_key_ptt_toggle', 'hot_key_ptt_if_hidden', 'keyupDelay', 'cwTone', 'pulse_audio_verbose_output', 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', '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', 'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec',
'fixed_tune_offset'): 'fixed_tune_offset', 'calibrate_temp_20', 'calibrate_temp_40', 'calibrate_current_0', 'calibrate_current_1'):
setattr(conf, name, x) setattr(conf, name, x)
application.ImmediateChange(name) application.ImmediateChange(name)
elif name[0:4] in ('lin_', 'win_'): elif name[0:4] in ('lin_', 'win_'):
@@ -2403,6 +2403,7 @@ class BaseWindow(wx.ScrolledWindow, ControlMixin):
def __init__(self, parent): def __init__(self, parent):
wx.ScrolledWindow.__init__(self, parent) wx.ScrolledWindow.__init__(self, parent)
ControlMixin.__init__(self, parent) ControlMixin.__init__(self, parent)
self.SetScrollRate(8, 8)
class ConfigConfig(BaseWindow): class ConfigConfig(BaseWindow):
def __init__(self, parent, width): def __init__(self, parent, width):
@@ -2513,7 +2514,6 @@ The CW message will then repeat. A time of zero means no repeat.'
sl, btn = self.AddTextSliderHelp(4, " Repeat secs %.1f ", 0, 0, 100, self.OnPlayFileRepeat, help_text, span=2, scale=0.1) sl, btn = self.AddTextSliderHelp(4, " Repeat secs %.1f ", 0, 0, 100, self.OnPlayFileRepeat, help_text, span=2, scale=0.1)
self.NextRow() self.NextRow()
self.FitInside() self.FitInside()
self.SetScrollRate(1, 1)
def MakeFileButton(self, text, path, index, name, help_text): def MakeFileButton(self, text, path, index, name, help_text):
if index < 10: # record buttons if index < 10: # record buttons
cb = self.AddCheckBox(4, text, self.OnCheckRecPlay) cb = self.AddCheckBox(4, text, self.OnCheckRecPlay)
@@ -2691,7 +2691,6 @@ class ConfigTxAudio(BaseWindow):
application.CtrlTxAudioPreemph = sld application.CtrlTxAudioPreemph = sld
self.NextRow() self.NextRow()
self.FitInside() self.FitInside()
self.SetScrollRate(1, 1)
def OnTimer(self, event): def OnTimer(self, event):
if not self.tmp_playing: if not self.tmp_playing:
self.timer.Stop() self.timer.Stop()
@@ -2769,7 +2768,6 @@ class Radios(BaseWindow): # The "Radios" first-level page
self.apply_btn = self.AddPushButton(3, "Apply", self.OnBtnApply) self.apply_btn = self.AddPushButton(3, "Apply", self.OnBtnApply)
self.NewRadioNames() self.NewRadioNames()
self.NewTop() self.NewTop()
self.SetScrollRate(1, 1)
def NewTop(self, event=None): def NewTop(self, event=None):
if event: if event:
btn = event.GetEventObject() btn = event.GetEventObject()
@@ -3006,7 +3004,6 @@ class RadioSection(BaseWindow): # The pages for each section in the second-leve
self.AddColSpacer(2, 20) self.AddColSpacer(2, 20)
self.AddColSpacer(5, 20) self.AddColSpacer(5, 20)
self.FitInside() self.FitInside()
self.SetScrollRate(1, 1)
def OnButtonChangeFavorites(self, event): def OnButtonChangeFavorites(self, event):
if isinstance(event, (ChoiceCombo, ComboCtrl)): if isinstance(event, (ChoiceCombo, ComboCtrl)):
path = event.GetValue() path = event.GetValue()
@@ -3131,7 +3128,6 @@ class RadioHardwareBase(BaseWindow): # The Hardware page in the second-level no
self.NextRow(7) self.NextRow(7)
self.AddColSpacer(2, 20) self.AddColSpacer(2, 20)
self.AddColSpacer(5, 20) self.AddColSpacer(5, 20)
self.SetScrollRate(1, 1)
def OnButtonChangeHardware(self, event): def OnButtonChangeHardware(self, event):
if isinstance(event, (ChoiceCombo, ComboCtrl)): if isinstance(event, (ChoiceCombo, ComboCtrl)):
path = event.GetValue() path = event.GetValue()
@@ -3178,25 +3174,24 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level
data_names = local_conf.GetReceiverData(radio_type) data_names = local_conf.GetReceiverData(radio_type)
col = 1 col = 1
border = 2 border = 2
hermes_board_id = 0 try:
if radio_type == "Hermes": is_HermesLite2 = application.Hardware.is_HermesLite2
try: except:
hermes_board_id = application.Hardware.hermes_board_id is_HermesLite2 = False
except:
pass
if radio_name == Settings[1] and hasattr(application.Hardware, "ProgramGateware"): if radio_name == Settings[1] and hasattr(application.Hardware, "ProgramGateware"):
help_text = "Choose an RBF file and program the Gateware (FPGA software) over Ethernet." help_text = "Choose an RBF file and program the Gateware (FPGA software) over Ethernet."
self.AddTextButtonHelp(1, "Gateware Update", "Program from RBF file..", application.Hardware.ProgramGateware, help_text) self.AddTextButtonHelp(1, "Gateware Update", "Program from RBF file..", application.Hardware.ProgramGateware, help_text)
col = 1 col = 1
self.NextRow(self.row + 2) self.NextRow(self.row + 2)
for name, text, fmt, help_text, values in data_names: for name, text, fmt, help_text, values in data_names:
text = text.replace("\\u2103", "\u2103")
if name in ('hardware_file_name', 'widgets_file_name'): if name in ('hardware_file_name', 'widgets_file_name'):
pass pass
elif name[0:4] == platform_ignore: elif name[0:4] == platform_ignore:
pass pass
elif name in ('Hermes_BandDictEnTx', ): elif name in ('Hermes_BandDictEnTx', ):
pass pass
elif 'Hl2_' in name and hermes_board_id != 6: elif 'Hl2_' in name and not is_HermesLite2:
pass pass
elif fmt[0:4] in ('dict', 'list'): elif fmt[0:4] in ('dict', 'list'):
pass pass
@@ -3225,7 +3220,7 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level
col = 1 col = 1
border = 2 border = 2
self.NextRow() self.NextRow()
if hermes_board_id == 6: if is_HermesLite2:
if col == 4: if col == 4:
self.NextRow() self.NextRow()
help_text = ('This controls the bias level for transistors in the final power amplifier. Enter a level from 0 to 255.' help_text = ('This controls the bias level for transistors in the final power amplifier. Enter a level from 0 to 255.'
@@ -3241,7 +3236,6 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level
self.HermesWriteBiasButton = self.AddPushButton(7, "Write", self.OnButtonHermesWriteBias, border=0) self.HermesWriteBiasButton = self.AddPushButton(7, "Write", self.OnButtonHermesWriteBias, border=0)
self.HermesWriteBiasButton.Enable(enbl) self.HermesWriteBiasButton.Enable(enbl)
self.FitInside() self.FitInside()
self.SetScrollRate(1, 1)
def OnHermesChangeBias0(self, event): def OnHermesChangeBias0(self, event):
value = self.HermesBias0.GetValue() value = self.HermesBias0.GetValue()
application.Hardware.ChangeBias0(value) application.Hardware.ChangeBias0(value)
@@ -3855,7 +3849,6 @@ class RadioSound(BaseWindow): # The Sound page in the second-level notebook for
break break
self.gbs = gbs self.gbs = gbs
self.FitInside() self.FitInside()
self.SetScrollRate(1, 1)
def ItemValue(self, row, col): def ItemValue(self, row, col):
data_name = self.sound_names[row][col] data_name = self.sound_names[row][col]
if col == 4: # Device names if col == 4: # Device names
@@ -4083,7 +4076,6 @@ The Tx bits are sent as the "Alex" filters and are sent to the J16 interface.'
self.AddBitField(col + 1, 7, 'Hermes_BandDictTx', band, bus, self.ChangeIO) self.AddBitField(col + 1, 7, 'Hermes_BandDictTx', band, bus, self.ChangeIO)
self.NextRow() self.NextRow()
self.FitInside() self.FitInside()
self.SetScrollRate(1, 1)
def SortCmp(self, item1): def SortCmp(self, item1):
# Numerical conversion of band name to megahertz # Numerical conversion of band name to megahertz
try: try:
@@ -4258,7 +4250,6 @@ class RadioRemote(BaseWindow): # The Remote page in the second-level notebook f
self.AddColSpacer(3, 20) self.AddColSpacer(3, 20)
self.AddColSpacer(6, 20) self.AddColSpacer(6, 20)
self.FitInside() self.FitInside()
self.SetScrollRate(1, 1)
def OnChangePassword(self, event): def OnChangePassword(self, event):
text = local_conf.globals.get('remote_radio_password', '') text = local_conf.globals.get('remote_radio_password', '')
dlg = wx.TextEntryDialog(self, "Please enter a password for remote access", dlg = wx.TextEntryDialog(self, "Please enter a password for remote access",
@@ -4340,7 +4331,6 @@ class xxRadioFilters(BaseWindow): # The Filters page in the second-level notebo
self.NextRow() self.NextRow()
index += 1 index += 1
self.FitInside() self.FitInside()
self.SetScrollRate(1, 1)
def OnChangeFreq(self, event): def OnChangeFreq(self, event):
freq = event.GetString() freq = event.GetString()
radio_dict = local_conf.GetRadioDict(self.radio_name) radio_dict = local_conf.GetRadioDict(self.radio_name)
+128 -2
View File
@@ -3,7 +3,7 @@
# #
# This module is used to add extra widgets to the QUISK screen. # This module is used to add extra widgets to the QUISK screen.
import wx import wx, math
class BottomWidgets: # Add extra widgets to the bottom of the screen class BottomWidgets: # Add extra widgets to the bottom of the screen
def __init__(self, app, hardware, conf, frame, gbs, vertBox): def __init__(self, app, hardware, conf, frame, gbs, vertBox):
@@ -15,17 +15,143 @@ class BottomWidgets: # Add extra widgets to the bottom of the screen
self.num_rows_added = 1 self.num_rows_added = 1
self.btnPreamp = app.QuiskCheckbutton(frame, self.OnBtnPreamp, text='Preamp') self.btnPreamp = app.QuiskCheckbutton(frame, self.OnBtnPreamp, text='Preamp')
gbs.Add(self.btnPreamp, (self.start_row, self.start_col), flag=wx.EXPAND) gbs.Add(self.btnPreamp, (self.start_row, self.start_col), flag=wx.EXPAND)
bw, bh = self.btnPreamp.GetMinSize()
init = app.hermes_atten_dB init = app.hermes_atten_dB
self.sliderAtten = app.SliderBoxHH(frame, 'Atten %d dB', init, 0, 31, self.OnAtten, True) self.sliderAtten = app.SliderBoxHH(frame, 'Atten %d dB', init, 0, 31, self.OnAtten, True)
self.sliderAtten.idName = "RfAtten" self.sliderAtten.idName = "RfAtten"
app.midiControls["RfAtten"] = (self.sliderAtten, self.OnAtten) app.midiControls["RfAtten"] = (self.sliderAtten, self.OnAtten)
hardware.ChangeAtten(init) hardware.ChangeAtten(init)
gbs.Add(self.sliderAtten, (self.start_row, self.start_col + 2), (1, 6), flag=wx.EXPAND) gbs.Add(self.sliderAtten, (self.start_row, self.start_col + 2), (1, 6), flag=wx.EXPAND)
if conf.button_layout == "Small screen":
# Display four data items in a single window
self.text_temperature = app.QuiskText1(frame, '', bh)
self.text_pa_current = app.QuiskText1(frame, '', bh)
self.text_fwd_power = app.QuiskText1(frame, '', bh)
self.text_swr = app.QuiskText1(frame, '', bh)
self.text_data = self.text_temperature
self.text_pa_current.Hide()
self.text_fwd_power.Hide()
self.text_swr.Hide()
b = app.QuiskPushbutton(frame, self.OnTextDataMenu, '..')
szr = self.data_sizer = wx.BoxSizer(wx.HORIZONTAL)
szr.Add(self.text_data, 1, flag=wx.ALIGN_CENTER_VERTICAL)
szr.Add(b, 0, flag=wx.ALIGN_CENTER_VERTICAL)
gbs.Add(szr, (self.start_row, self.start_col + 10), (1, 2), flag=wx.EXPAND)
# Make a popup menu for the data window
self.text_data_menu = wx.Menu()
item = self.text_data_menu.Append(-1, 'Temperature')
app.Bind(wx.EVT_MENU, self.OnDataTemperature, item)
item = self.text_data_menu.Append(-1, 'PA Current')
app.Bind(wx.EVT_MENU, self.OnDataPaCurrent, item)
item = self.text_data_menu.Append(-1, 'Fwd Power')
app.Bind(wx.EVT_MENU, self.OnDataFwdPower, item)
item = self.text_data_menu.Append(-1, 'SWR')
app.Bind(wx.EVT_MENU, self.OnDataSwr, item)
else:
szr = wx.BoxSizer(wx.HORIZONTAL)
gbs.Add(szr, (self.start_row, self.start_col + 10), (1, 18), flag=wx.EXPAND)
text_temperature = wx.StaticText(frame, -1, ' Temp 100DC XX', style=wx.ST_NO_AUTORESIZE)
size = text_temperature.GetBestSize()
text_temperature.Destroy()
self.text_temperature = wx.StaticText(frame, -1, '', size=size, style=wx.ST_NO_AUTORESIZE)
self.text_pa_current = wx.StaticText(frame, -1, '', size=size, style=wx.ST_NO_AUTORESIZE)
self.text_fwd_power = wx.StaticText(frame, -1, '', size=size, style=wx.ST_NO_AUTORESIZE)
self.text_swr = wx.StaticText(frame, -1, '', size=size, style=wx.ST_NO_AUTORESIZE)
flag=wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL
szr.Add(self.text_temperature, 0, flag=flag)
szr.Add(self.text_pa_current, 0, flag=flag)
szr.Add(self.text_fwd_power, 0, flag=flag)
szr.Add(self.text_swr, 0, flag=flag)
def OnBtnPreamp(self, event): def OnBtnPreamp(self, event):
self.hardware.ChangePreamp(self.btnPreamp.GetValue()) self.hardware.ChangePreamp(self.btnPreamp.GetValue())
def OnAtten(self, event): def OnAtten(self, event):
value = self.sliderAtten.GetValue() value = self.sliderAtten.GetValue()
self.hardware.ChangeAtten(value) self.hardware.ChangeAtten(value)
self.application.hermes_atten_dB = value self.application.hermes_atten_dB = value
def Code2FwdRevWatts(self, fwd, rev): # Convert the fwd/rev power code to watts forward and reverse
#print (self.hardware.hermes_rev_power, self.hardware.hermes_fwd_power)
fwd = self.hardware.InterpolatePower(fwd)
rev = self.hardware.InterpolatePower(rev)
# Which voltage is forward and reverse depends on the polarity of the current sense transformer
if fwd >= rev:
return fwd, rev
else:
return rev, fwd
def UpdateText(self): # Called at intervals def UpdateText(self): # Called at intervals
pass conf = self.config
# Temperature
temp = self.hardware.hermes_temperature
if conf.calibrate_temp_20 == conf.calibrate_temp_40:
temp = " Temp ADC %4d" % temp
else:
m = 20.0 / (conf.calibrate_temp_40 - conf.calibrate_temp_20)
b = 20.0 - m * conf.calibrate_temp_20
temp = m * temp + b
temp = (" Temp %3.0f" % temp) + u'\u2103'
self.text_temperature.SetLabel(temp)
# power amp current
current = self.hardware.hermes_pa_current
if conf.calibrate_current_0 == conf.calibrate_current_1:
current = " Amps ADC %4d" % current
else:
m = 1.0 / (conf.calibrate_current_1 - conf.calibrate_current_0)
b = - m * conf.calibrate_current_0
current = m * current + b
current = " PA %4.0f ma" % (1000*current)
self.text_pa_current.SetLabel(current)
# forward and reverse peak power
if not self.hardware.power_interpolator: # For no interpolator, show ADC
fwd = self.hardware.hermes_fwd_power
rev = self.hardware.hermes_rev_power
self.text_fwd_power.SetLabel("Fwd ADC %4d" % fwd)
self.text_swr.SetLabel("Rev ADC %4d" % rev)
return
fwd, rev = self.Code2FwdRevWatts(self.hardware.hermes_fwd_peak, self.hardware.hermes_rev_peak)
# forward less reverse power
power = fwd - rev
if power < 0.0:
power = 0.0
text = " PEP %3.1f watts" % power
self.text_fwd_power.SetLabel(text)
# SWR based on average power
fwd, rev = self.Code2FwdRevWatts(self.hardware.hermes_fwd_power, self.hardware.hermes_rev_power)
if fwd >= 0.05:
gamma = math.sqrt(rev / fwd)
if gamma < 0.98:
swr = (1.0 + gamma) / (1.0 - gamma)
else:
swr = 99.0
if swr < 9.95:
text = " SWR %4.2f" % swr
else:
text = " SWR %4.0f" % swr
else:
text = " SWR ---"
self.text_swr.SetLabel(text)
def OnTextDataMenu(self, event):
btn = event.GetEventObject()
btn.PopupMenu(self.text_data_menu, (0,0))
def OnDataTemperature(self, event):
self.data_sizer.Replace(self.text_data, self.text_temperature)
self.text_data.Hide()
self.text_data = self.text_temperature
self.text_data.Show()
self.data_sizer.Layout()
def OnDataPaCurrent(self, event):
self.data_sizer.Replace(self.text_data, self.text_pa_current)
self.text_data.Hide()
self.text_data = self.text_pa_current
self.text_data.Show()
self.data_sizer.Layout()
def OnDataFwdPower(self, event):
self.data_sizer.Replace(self.text_data, self.text_fwd_power)
self.text_data.Hide()
self.text_data = self.text_fwd_power
self.text_data.Show()
self.data_sizer.Layout()
def OnDataSwr(self, event):
self.data_sizer.Replace(self.text_data, self.text_swr)
self.text_data.Hide()
self.text_data = self.text_swr
self.text_data.Show()
self.data_sizer.Layout()
+23 -7
View File
@@ -15,6 +15,7 @@ from quisk_hardware_model import Hardware as BaseHardware
DEBUG = 0 DEBUG = 0
DEBUG_I2C = 0 DEBUG_I2C = 0
DEBUG_SENSORS = 0
class IOBoard: class IOBoard:
"This class controls the N2ADR IO Board for the HermesLite 2" "This class controls the N2ADR IO Board for the HermesLite 2"
@@ -46,9 +47,9 @@ class IOBoard:
self.old_receive = None self.old_receive = None
self.slow = 0 self.slow = 0
def HeartBeat(self): # Called at 10 Hz for housekeeping tasks def HeartBeat(self): # Called at 10 Hz for housekeeping tasks
if not QS.get_params('rx_udp_started'): if not self.hardware.is_HermesLite2:
return return
if self.hardware.hermes_board_id != 6: if not QS.get_params('rx_udp_started'):
return return
if self.have_IO_Board is None: if self.have_IO_Board is None:
resp = self.hardware.ReadI2C(0x7d, 0x41, 0) # Check for the N2ADR HL2 IO Board resp = self.hardware.ReadI2C(0x7d, 0x41, 0) # Check for the N2ADR HL2 IO Board
@@ -156,6 +157,7 @@ class Hardware(BaseHardware):
self.hermes_ip = "" self.hermes_ip = ""
self.hermes_code_version = -1 self.hermes_code_version = -1
self.hermes_board_id = -1 self.hermes_board_id = -1
self.is_HermesLite2 = False
self.hermes_temperature = 0.0 self.hermes_temperature = 0.0
self.hermes_fwd_power = 0.0 self.hermes_fwd_power = 0.0
self.hermes_rev_power = 0.0 self.hermes_rev_power = 0.0
@@ -276,6 +278,7 @@ class Hardware(BaseHardware):
self.hermes_ip = addr[0] self.hermes_ip = addr[0]
self.hermes_code_version = data[9] self.hermes_code_version = data[9]
self.hermes_board_id = data[10] self.hermes_board_id = data[10]
self.is_HermesLite2 = self.hermes_board_id == 6 and self.conf.hardware_file_type == "Hermes"
QS.set_hermes_id(data[9], data[10]) QS.set_hermes_id(data[9], data[10])
if data[0x16] >> 6 == 0: if data[0x16] >> 6 == 0:
QS.set_params(bandscopeScale = 2048) QS.set_params(bandscopeScale = 2048)
@@ -322,6 +325,7 @@ class Hardware(BaseHardware):
bid = 6 bid = 6
self.hermes_code_version = code self.hermes_code_version = code
self.hermes_board_id = bid self.hermes_board_id = bid
self.is_HermesLite2 = self.hermes_board_id == 6 and self.conf.hardware_file_type == "Hermes"
QS.set_hermes_id(code, bid) QS.set_hermes_id(code, bid)
st = 'Capture from Hermes device at specified IP %s' % self.hermes_ip st = 'Capture from Hermes device at specified IP %s' % self.hermes_ip
found = True found = True
@@ -336,9 +340,13 @@ class Hardware(BaseHardware):
def open(self): def open(self):
self.delay_config = True # Delay sending message to HL2 until after sound starts self.delay_config = True # Delay sending message to HL2 until after sound starts
# This list only changes control bits; no use of WriteQueue() # This list only changes control bits; no use of WriteQueue()
for name in ('keyupDelay', self.ImmediateChange('keyupDelay')
'hermes_lowpwr_tr_enable', 'hermes_PWM', 'hermes_disable_sync', 'hermes_power_amp', 'Hermes_BandDictEnTx'): self.ImmediateChange('Hermes_BandDictEnTx')
self.ImmediateChange(name) if self.is_HermesLite2:
for name in ('hermes_lowpwr_tr_enable', 'hermes_PWM', 'hermes_disable_sync', 'hermes_power_amp'):
self.ImmediateChange(name)
#else: # DEBUG: Turn on power amp for testing OpenHPSDR radio with my Hermes Lite2
# self.SetControlBit(0x09, 19, 1)
return self.config_text return self.config_text
def GetValue(self, name): # return values stored in the hardware def GetValue(self, name): # return values stored in the hardware
if name == 'Hware_Hl2_EepromIP': if name == 'Hware_Hl2_EepromIP':
@@ -497,7 +505,15 @@ class Hardware(BaseHardware):
self.key_was_down = key_down self.key_was_down = key_down
if self.TFRC_counter >= 3: if self.TFRC_counter >= 3:
self.TFRC_counter = 0 self.TFRC_counter = 0
self.hermes_temperature, self.hermes_fwd_power, self.hermes_rev_power, self.hermes_pa_current, self.hermes_fwd_peak, self.hermes_rev_peak = QS.get_hermes_TFRC() AIN1_avg, AIN1_peak, AIN2_avg, AIN2_peak, AIN3, AIN4, AIN5, AIN6 = QS.get_hermes_TFRC()
if self.hermes_board_id == 6:
(self.hermes_fwd_power, self.hermes_fwd_peak, self.hermes_rev_power, self.hermes_rev_peak,
self.hermes_pa_current, self.hermes_temperature) = AIN1_avg, AIN1_peak, AIN2_avg, AIN2_peak, AIN3, AIN5
else:
(self.hermes_fwd_power, self.hermes_fwd_peak, self.hermes_rev_power, self.hermes_rev_peak,
self.hermes_pa_current, self.hermes_temperature) = AIN1_avg, AIN1_peak, AIN2_avg, AIN2_peak, AIN3, AIN5
if DEBUG_SENSORS:
print("AIN 1-6 %4d %4d %4d %4d %4d %4d" % (AIN1_avg, AIN2_avg, AIN3, AIN4, AIN5, AIN6))
if self.application.bottom_widgets: if self.application.bottom_widgets:
self.application.bottom_widgets.UpdateText() self.application.bottom_widgets.UpdateText()
if self.delay_config and QS.get_params('rx_udp_started'): if self.delay_config and QS.get_params('rx_udp_started'):
@@ -886,7 +902,7 @@ class Hardware(BaseHardware):
QS.set_hermeslite_writepointer(0) QS.set_hermeslite_writepointer(0)
return False return False
def WriteQueue(self, wait=False): def WriteQueue(self, wait=False):
if self.hermes_board_id != 6: if not self.is_HermesLite2:
return False return False
self._wait_queue() self._wait_queue()
# Send next write # Send next write
+6
View File
@@ -154,6 +154,12 @@ class BottomWidgets: # Add extra widgets to the bottom of the screen
current = " PA %4.0f ma" % (1000*current) current = " PA %4.0f ma" % (1000*current)
self.text_pa_current.SetLabel(current) self.text_pa_current.SetLabel(current)
# forward and reverse peak power # forward and reverse peak power
if not self.hardware.power_interpolator: # For no interpolator, show ADC
fwd = self.hardware.hermes_fwd_power
rev = self.hardware.hermes_rev_power
self.text_fwd_power.SetLabel("Fwd ADC %4d" % fwd)
self.text_swr.SetLabel("Rev ADC %4d" % rev)
return
fwd, rev = self.Code2FwdRevWatts(self.hardware.hermes_fwd_peak, self.hardware.hermes_rev_peak) fwd, rev = self.Code2FwdRevWatts(self.hardware.hermes_fwd_peak, self.hardware.hermes_rev_peak)
# forward less reverse power # forward less reverse power
power = fwd - rev power = fwd - rev
+57 -37
View File
@@ -218,14 +218,17 @@ static unsigned char quisk_hermes_to_pc[5 * 4]; // data received from the Herme
static unsigned char quisk_hermeslite_response[5]; // response from Hermes-Lite commands static unsigned char quisk_hermeslite_response[5]; // response from Hermes-Lite commands
unsigned int quisk_hermes_code_version = -1; // code version returned by the Hermes hardware unsigned int quisk_hermes_code_version = -1; // code version returned by the Hermes hardware
unsigned int quisk_hermes_board_id = -1; // board ID returned by the Hermes hardware unsigned int quisk_hermes_board_id = -1; // board ID returned by the Hermes hardware
static double hermes_temperature; // average temperature static double hpsdr_AIN1_avg; // average forward power
static double hermes_fwd_power; // average forward power static double hpsdr_AIN1_peak; // peak forward power
static double hermes_rev_power; // average reverse power static double hpsdr_AIN2_avg; // average reverse power
static double hermes_fwd_peak; // peak forward power static double hpsdr_AIN2_peak; // peak reverse power
static double hermes_rev_peak; // peak reverse power static double hpsdr_AIN3; // average power amp current
static double hermes_pa_current; // average power amp current static double hpsdr_AIN4;
static int hermes_count_temperature; // number of temperature samples static double hpsdr_AIN5; // average temperature
static int hermes_count_current; // number of current samples static double hpsdr_AIN6;
static int hpsdr_AIN3_count; // number of current samples
static int hpsdr_AIN4_count; // number of current samples
static int hpsdr_AIN5_count; // number of current samples
static int hardware_ptt; // hardware PTT switch static int hardware_ptt; // hardware PTT switch
int quisk_hardware_cwkey; // hardware CW key from UDP or USB int quisk_hardware_cwkey; // hardware CW key from UDP or USB
@@ -3136,31 +3139,43 @@ static PyObject * get_hermes_TFRC(PyObject * self, PyObject * args)
if (!PyArg_ParseTuple (args, "")) if (!PyArg_ParseTuple (args, ""))
return NULL; return NULL;
if (hermes_count_temperature > 0) { if (hpsdr_AIN5_count > 0) {
hermes_temperature /= hermes_count_temperature; hpsdr_AIN5 /= hpsdr_AIN5_count;
hermes_fwd_power /= hermes_count_temperature; hpsdr_AIN1_avg /= hpsdr_AIN5_count;
} }
else { else {
hermes_temperature = 0.0; hpsdr_AIN5 = 0.0;
hermes_fwd_power = 0.0; hpsdr_AIN1_avg = 0.0;
} }
if (hermes_count_current > 0) { if (hpsdr_AIN3_count > 0) {
hermes_rev_power /= hermes_count_current; hpsdr_AIN2_avg /= hpsdr_AIN3_count;
hermes_pa_current /= hermes_count_current; hpsdr_AIN3 /= hpsdr_AIN3_count;
} }
else { else {
hermes_rev_power = 0.0; hpsdr_AIN2_avg = 0.0;
hermes_pa_current = 0.0; hpsdr_AIN3 = 0.0;
} }
ret = Py_BuildValue("dddddd", hermes_temperature, hermes_fwd_power, hermes_rev_power, hermes_pa_current, hermes_fwd_peak, hermes_rev_peak); if (hpsdr_AIN4_count > 0) {
hermes_temperature = 0; hpsdr_AIN4 /= hpsdr_AIN4_count;
hermes_fwd_power = 0; hpsdr_AIN6 /= hpsdr_AIN4_count;
hermes_rev_power = 0; }
hermes_fwd_peak = 0; else {
hermes_rev_peak = 0; hpsdr_AIN4 = 0.0;
hermes_pa_current = 0; hpsdr_AIN6 = 0.0;
hermes_count_temperature = 0; }
hermes_count_current = 0; ret = Py_BuildValue("dddddddd", hpsdr_AIN1_avg, hpsdr_AIN1_peak, hpsdr_AIN2_avg, hpsdr_AIN2_peak,
hpsdr_AIN3, hpsdr_AIN4, hpsdr_AIN5, hpsdr_AIN6);
hpsdr_AIN1_avg = 0;
hpsdr_AIN1_peak = 0;
hpsdr_AIN2_avg = 0;
hpsdr_AIN2_peak = 0;
hpsdr_AIN3 = 0;
hpsdr_AIN4 = 0;
hpsdr_AIN5 = 0;
hpsdr_AIN6 = 0;
hpsdr_AIN3_count = 0;
hpsdr_AIN4_count = 0;
hpsdr_AIN5_count = 0;
return ret; return ret;
} }
@@ -3708,19 +3723,24 @@ static int read_rx_udp10(complex double * samp) // Read samples from UDP using t
quisk_set_play_state(); quisk_set_play_state();
} }
} }
else if(dindex == 1) { // temperature and forward power else if(dindex == 1) {
hermes_temperature += quisk_hermes_to_pc[4] << 8 | quisk_hermes_to_pc[5]; hpsdr_AIN5 += quisk_hermes_to_pc[4] << 8 | quisk_hermes_to_pc[5];
power = quisk_hermes_to_pc[6] << 8 | quisk_hermes_to_pc[7]; power = quisk_hermes_to_pc[6] << 8 | quisk_hermes_to_pc[7];
hermes_fwd_power += power; hpsdr_AIN1_avg += power;
hermes_fwd_peak = fmax(hermes_fwd_peak, (double)power); hpsdr_AIN1_peak = fmax(hpsdr_AIN1_peak, (double)power);
hermes_count_temperature++; hpsdr_AIN5_count++;
} }
else if (dindex == 2) { // reverse power and current else if (dindex == 2) {
power = quisk_hermes_to_pc[8] << 8 | quisk_hermes_to_pc[9]; power = quisk_hermes_to_pc[8] << 8 | quisk_hermes_to_pc[9];
hermes_rev_power += power; hpsdr_AIN2_avg += power;
hermes_rev_peak = fmax(hermes_rev_peak, (double)power); hpsdr_AIN2_peak = fmax(hpsdr_AIN2_peak, (double)power);
hermes_pa_current += quisk_hermes_to_pc[10] << 8 | quisk_hermes_to_pc[11]; hpsdr_AIN3 += quisk_hermes_to_pc[10] << 8 | quisk_hermes_to_pc[11];
hermes_count_current++; hpsdr_AIN3_count++;
}
else if (dindex == 3) {
hpsdr_AIN4 += quisk_hermes_to_pc[12] << 8 | quisk_hermes_to_pc[13];
hpsdr_AIN6 += quisk_hermes_to_pc[14] << 8 | quisk_hermes_to_pc[15];
hpsdr_AIN4_count++;
} }
// convert 24-bit samples to 32-bit samples; int must be 32 bits. // convert 24-bit samples to 32-bit samples; int must be 32 bits.
index = start + 5; index = start + 5;
+1 -1
View File
@@ -6799,7 +6799,7 @@ class App(wx.App):
if self.timer - self.heart_time0 > 0.10: # call hardware to perform background tasks: if self.timer - self.heart_time0 > 0.10: # call hardware to perform background tasks:
self.heart_time0 = self.timer self.heart_time0 = self.timer
Hardware.HeartBeat() Hardware.HeartBeat()
if conf.use_rx_udp == 10 and Hardware.hermes_board_id == 6: if Hardware.is_HermesLite2:
self.tx_inhibit = QS.get_params('quisk_tx_inhibit') self.tx_inhibit = QS.get_params('quisk_tx_inhibit')
else: else:
self.tx_inhibit = 0 self.tx_inhibit = 0
+37
View File
@@ -488,6 +488,7 @@ power_meter_std_calibrations['HL2FilterE3'] = [[ 0, 0.0 ], [ 25.865384615384617,
[ 4093.1785714285716, 7.28994845808807 ], [ 4502.496428571429, 8.820837634286566 ], [ 4952.746071428572, 10.673213537486745 ] ] [ 4093.1785714285716, 7.28994845808807 ], [ 4502.496428571429, 8.820837634286566 ], [ 4952.746071428572, 10.673213537486745 ] ]
#power_meter_std_calibrations['HL2FilterE1'] = [[0, 0.0], [9.07, 0.002], [54.98, 0.014], [148.6, 0.057], #power_meter_std_calibrations['HL2FilterE1'] = [[0, 0.0], [9.07, 0.002], [54.98, 0.014], [148.6, 0.057],
# [328.0, 0.208], [611.1, 0.646], [807.0, 1.098], [982.1, 1.6], [1223.3, 2.471], [1517.7, 3.738], [1758.7, 5.02]] # [328.0, 0.208], [611.1, 0.646], [807.0, 1.098], [982.1, 1.6], [1223.3, 2.471], [1517.7, 3.738], [1758.7, 5.02]]
power_meter_std_calibrations['Show ADC'] = []
## Hermes_BandDict Rx IO Bus, dict ## Hermes_BandDict Rx IO Bus, dict
# The Hermes_BandDict sets the 7 bits on the J16 connector for Rx. # The Hermes_BandDict sets the 7 bits on the J16 connector for Rx.
@@ -662,6 +663,42 @@ Hermes_BandDictTx = {'160':0, '80':0, '60':0, '40':0, '30':0, '20':0, '17':0, '1
Hermes_BandDictEnTx = False Hermes_BandDictEnTx = False
#Hermes_BandDictEnTx = True #Hermes_BandDictEnTx = True
## calibrate_temp_20 PA temp ADC at 20\u2103, integer
# This is the ADC code returned for a temperature of 20C.
# To see the ADC code being returned, enter zero for both temperature codes.
# Linear interpolation is used to calculate the temperature.
# Changes are immediate (no need to restart).
calibrate_temp_20 = 0
## calibrate_temp_40 PA temp ADC at 40\u2103, integer
# This is the ADC code returned for a temperature of 40C.
# To see the ADC code being returned, enter zero for both temperature codes.
# Linear interpolation is used to calculate the temperature.
# Changes are immediate (no need to restart).
calibrate_temp_40 = 0
## calibrate_current_0 PA current ADC at zero, integer
# This is the ADC code returned for a PA current of zero.
# To see the ADC code being returned, enter zero for both current codes.
# Linear interpolation is used to calculate the current.
# Changes are immediate (no need to restart).
calibrate_current_0 = 0
## calibrate_current_1 PA current ADC at 1 amp, integer
# This is the ADC code returned for a PA current of one amp.
# To see the ADC code being returned, enter zero for both current codes.
# Linear interpolation is used to calculate the current.
# Changes are immediate (no need to restart).
calibrate_current_1 = 0
## power_meter_calib_name Power meter calibration, text choice
# This is the calibration table used to convert the power sensor voltage measured by the ADC to the transmit power display.
# It is a table of ADC codes and the corresponding measured power level. If you have a power meter, you can create your own
# table by selecting "New". Then enter ten or more power measurements from low to full power.
# For the Hermes Lite2 version E3 filter board, use the built-in table "HL2FilterE3".
# Changes are immediate (no need to restart).
power_meter_calib_name = 'HL2FilterE3'
################ Receivers SoapySDR, The SoapySDR interface to multiple SDRs. ################ Receivers SoapySDR, The SoapySDR interface to multiple SDRs.
## hardware_file_name Hardware file path, rfile ## hardware_file_name Hardware file path, rfile
+1 -1
View File
@@ -7,7 +7,7 @@ import os
# You must define the version here. A title string including # You must define the version here. A title string including
# the version will be written to __init__.py and read by quisk.py. # the version will be written to __init__.py and read by quisk.py.
Version = '4.2.48' Version = '4.2.49'
fp = open("__init__.py", "w") # write title string fp = open("__init__.py", "w") # write title string
fp.write("#Quisk version %s\n" % Version) fp.write("#Quisk version %s\n" % Version)