From 018fc0e871220e1ef9255866478125dc729ce419 Mon Sep 17 00:00:00 2001 From: "James C. Ahlstrom" Date: Mon, 22 Dec 2025 15:12:47 -0500 Subject: [PATCH] Quisk version 4.2.49 --- CHANGELOG.txt | 22 +++++++ __init__.py | 2 +- configure.py | 28 +++------ hermes/hpsdr_widgets.py | 130 ++++++++++++++++++++++++++++++++++++++- hermes/quisk_hardware.py | 30 ++++++--- hermes/quisk_widgets.py | 6 ++ quisk.c | 94 +++++++++++++++++----------- quisk.py | 2 +- quisk_conf_defaults.py | 37 +++++++++++ setup.py | 2 +- 10 files changed, 285 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index bca5f66..4a612a2 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -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 =================================== This is a quisk fix for a bug in the new OpenHPSDR radios. It fixes use_rx_udp that was reset to zero. diff --git a/__init__.py b/__init__.py index 7bb4587..acd7eee 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,2 @@ -#Quisk version 4.2.48 +#Quisk version 4.2.49 from .quisk import main diff --git a/configure.py b/configure.py index 0b24037..5c61055 100644 --- a/configure.py +++ b/configure.py @@ -2302,7 +2302,7 @@ class ControlMixin: 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', - 'fixed_tune_offset'): + 'fixed_tune_offset', 'calibrate_temp_20', 'calibrate_temp_40', 'calibrate_current_0', 'calibrate_current_1'): setattr(conf, name, x) application.ImmediateChange(name) elif name[0:4] in ('lin_', 'win_'): @@ -2403,6 +2403,7 @@ class BaseWindow(wx.ScrolledWindow, ControlMixin): def __init__(self, parent): wx.ScrolledWindow.__init__(self, parent) ControlMixin.__init__(self, parent) + self.SetScrollRate(8, 8) class ConfigConfig(BaseWindow): 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) self.NextRow() self.FitInside() - self.SetScrollRate(1, 1) def MakeFileButton(self, text, path, index, name, help_text): if index < 10: # record buttons cb = self.AddCheckBox(4, text, self.OnCheckRecPlay) @@ -2691,7 +2691,6 @@ class ConfigTxAudio(BaseWindow): application.CtrlTxAudioPreemph = sld self.NextRow() self.FitInside() - self.SetScrollRate(1, 1) def OnTimer(self, event): if not self.tmp_playing: 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.NewRadioNames() self.NewTop() - self.SetScrollRate(1, 1) def NewTop(self, event=None): if event: 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(5, 20) self.FitInside() - self.SetScrollRate(1, 1) def OnButtonChangeFavorites(self, event): if isinstance(event, (ChoiceCombo, ComboCtrl)): path = event.GetValue() @@ -3131,7 +3128,6 @@ class RadioHardwareBase(BaseWindow): # The Hardware page in the second-level no self.NextRow(7) self.AddColSpacer(2, 20) self.AddColSpacer(5, 20) - self.SetScrollRate(1, 1) def OnButtonChangeHardware(self, event): if isinstance(event, (ChoiceCombo, ComboCtrl)): path = event.GetValue() @@ -3178,25 +3174,24 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level data_names = local_conf.GetReceiverData(radio_type) col = 1 border = 2 - hermes_board_id = 0 - if radio_type == "Hermes": - try: - hermes_board_id = application.Hardware.hermes_board_id - except: - pass + try: + is_HermesLite2 = application.Hardware.is_HermesLite2 + except: + is_HermesLite2 = False if radio_name == Settings[1] and hasattr(application.Hardware, "ProgramGateware"): 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) col = 1 self.NextRow(self.row + 2) for name, text, fmt, help_text, values in data_names: + text = text.replace("\\u2103", "\u2103") if name in ('hardware_file_name', 'widgets_file_name'): pass elif name[0:4] == platform_ignore: pass elif name in ('Hermes_BandDictEnTx', ): pass - elif 'Hl2_' in name and hermes_board_id != 6: + elif 'Hl2_' in name and not is_HermesLite2: pass elif fmt[0:4] in ('dict', 'list'): pass @@ -3225,7 +3220,7 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level col = 1 border = 2 self.NextRow() - if hermes_board_id == 6: + if is_HermesLite2: if col == 4: self.NextRow() 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.Enable(enbl) self.FitInside() - self.SetScrollRate(1, 1) def OnHermesChangeBias0(self, event): value = self.HermesBias0.GetValue() application.Hardware.ChangeBias0(value) @@ -3855,7 +3849,6 @@ class RadioSound(BaseWindow): # The Sound page in the second-level notebook for break self.gbs = gbs self.FitInside() - self.SetScrollRate(1, 1) def ItemValue(self, row, col): data_name = self.sound_names[row][col] 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.NextRow() self.FitInside() - self.SetScrollRate(1, 1) def SortCmp(self, item1): # Numerical conversion of band name to megahertz try: @@ -4258,7 +4250,6 @@ class RadioRemote(BaseWindow): # The Remote page in the second-level notebook f self.AddColSpacer(3, 20) self.AddColSpacer(6, 20) self.FitInside() - self.SetScrollRate(1, 1) def OnChangePassword(self, event): text = local_conf.globals.get('remote_radio_password', '') 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() index += 1 self.FitInside() - self.SetScrollRate(1, 1) def OnChangeFreq(self, event): freq = event.GetString() radio_dict = local_conf.GetRadioDict(self.radio_name) diff --git a/hermes/hpsdr_widgets.py b/hermes/hpsdr_widgets.py index 4466330..4ca7357 100644 --- a/hermes/hpsdr_widgets.py +++ b/hermes/hpsdr_widgets.py @@ -3,7 +3,7 @@ # # 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 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.btnPreamp = app.QuiskCheckbutton(frame, self.OnBtnPreamp, text='Preamp') gbs.Add(self.btnPreamp, (self.start_row, self.start_col), flag=wx.EXPAND) + bw, bh = self.btnPreamp.GetMinSize() 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) + 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): 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 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 - 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() diff --git a/hermes/quisk_hardware.py b/hermes/quisk_hardware.py index 52ba171..5ceb710 100644 --- a/hermes/quisk_hardware.py +++ b/hermes/quisk_hardware.py @@ -15,6 +15,7 @@ from quisk_hardware_model import Hardware as BaseHardware DEBUG = 0 DEBUG_I2C = 0 +DEBUG_SENSORS = 0 class IOBoard: "This class controls the N2ADR IO Board for the HermesLite 2" @@ -46,9 +47,9 @@ class IOBoard: self.old_receive = None self.slow = 0 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 - if self.hardware.hermes_board_id != 6: + if not QS.get_params('rx_udp_started'): return if self.have_IO_Board is None: 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_code_version = -1 self.hermes_board_id = -1 + self.is_HermesLite2 = False self.hermes_temperature = 0.0 self.hermes_fwd_power = 0.0 self.hermes_rev_power = 0.0 @@ -276,6 +278,7 @@ class Hardware(BaseHardware): self.hermes_ip = addr[0] self.hermes_code_version = data[9] 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]) if data[0x16] >> 6 == 0: QS.set_params(bandscopeScale = 2048) @@ -322,6 +325,7 @@ class Hardware(BaseHardware): bid = 6 self.hermes_code_version = code 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) st = 'Capture from Hermes device at specified IP %s' % self.hermes_ip found = True @@ -336,9 +340,13 @@ class Hardware(BaseHardware): def open(self): self.delay_config = True # Delay sending message to HL2 until after sound starts # This list only changes control bits; no use of WriteQueue() - for name in ('keyupDelay', - 'hermes_lowpwr_tr_enable', 'hermes_PWM', 'hermes_disable_sync', 'hermes_power_amp', 'Hermes_BandDictEnTx'): - self.ImmediateChange(name) + self.ImmediateChange('keyupDelay') + self.ImmediateChange('Hermes_BandDictEnTx') + 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 def GetValue(self, name): # return values stored in the hardware if name == 'Hware_Hl2_EepromIP': @@ -497,7 +505,15 @@ class Hardware(BaseHardware): self.key_was_down = key_down if self.TFRC_counter >= 3: 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: self.application.bottom_widgets.UpdateText() if self.delay_config and QS.get_params('rx_udp_started'): @@ -886,7 +902,7 @@ class Hardware(BaseHardware): QS.set_hermeslite_writepointer(0) return False def WriteQueue(self, wait=False): - if self.hermes_board_id != 6: + if not self.is_HermesLite2: return False self._wait_queue() # Send next write diff --git a/hermes/quisk_widgets.py b/hermes/quisk_widgets.py index 2e5700a..f2ef95e 100644 --- a/hermes/quisk_widgets.py +++ b/hermes/quisk_widgets.py @@ -154,6 +154,12 @@ class BottomWidgets: # Add extra widgets to the bottom of the screen 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 diff --git a/quisk.c b/quisk.c index c41709d..b511599 100644 --- a/quisk.c +++ b/quisk.c @@ -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 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 -static double hermes_temperature; // average temperature -static double hermes_fwd_power; // average forward power -static double hermes_rev_power; // average reverse power -static double hermes_fwd_peak; // peak forward power -static double hermes_rev_peak; // peak reverse power -static double hermes_pa_current; // average power amp current -static int hermes_count_temperature; // number of temperature samples -static int hermes_count_current; // number of current samples +static double hpsdr_AIN1_avg; // average forward power +static double hpsdr_AIN1_peak; // peak forward power +static double hpsdr_AIN2_avg; // average reverse power +static double hpsdr_AIN2_peak; // peak reverse power +static double hpsdr_AIN3; // average power amp current +static double hpsdr_AIN4; +static double hpsdr_AIN5; // average temperature +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 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, "")) return NULL; - if (hermes_count_temperature > 0) { - hermes_temperature /= hermes_count_temperature; - hermes_fwd_power /= hermes_count_temperature; + if (hpsdr_AIN5_count > 0) { + hpsdr_AIN5 /= hpsdr_AIN5_count; + hpsdr_AIN1_avg /= hpsdr_AIN5_count; } else { - hermes_temperature = 0.0; - hermes_fwd_power = 0.0; + hpsdr_AIN5 = 0.0; + hpsdr_AIN1_avg = 0.0; } - if (hermes_count_current > 0) { - hermes_rev_power /= hermes_count_current; - hermes_pa_current /= hermes_count_current; + if (hpsdr_AIN3_count > 0) { + hpsdr_AIN2_avg /= hpsdr_AIN3_count; + hpsdr_AIN3 /= hpsdr_AIN3_count; } else { - hermes_rev_power = 0.0; - hermes_pa_current = 0.0; + hpsdr_AIN2_avg = 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); - hermes_temperature = 0; - hermes_fwd_power = 0; - hermes_rev_power = 0; - hermes_fwd_peak = 0; - hermes_rev_peak = 0; - hermes_pa_current = 0; - hermes_count_temperature = 0; - hermes_count_current = 0; + if (hpsdr_AIN4_count > 0) { + hpsdr_AIN4 /= hpsdr_AIN4_count; + hpsdr_AIN6 /= hpsdr_AIN4_count; + } + else { + hpsdr_AIN4 = 0.0; + hpsdr_AIN6 = 0.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; } @@ -3708,19 +3723,24 @@ static int read_rx_udp10(complex double * samp) // Read samples from UDP using t quisk_set_play_state(); } } - else if(dindex == 1) { // temperature and forward power - hermes_temperature += quisk_hermes_to_pc[4] << 8 | quisk_hermes_to_pc[5]; + else if(dindex == 1) { + 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]; - hermes_fwd_power += power; - hermes_fwd_peak = fmax(hermes_fwd_peak, (double)power); - hermes_count_temperature++; + hpsdr_AIN1_avg += power; + hpsdr_AIN1_peak = fmax(hpsdr_AIN1_peak, (double)power); + 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]; - hermes_rev_power += power; - hermes_rev_peak = fmax(hermes_rev_peak, (double)power); - hermes_pa_current += quisk_hermes_to_pc[10] << 8 | quisk_hermes_to_pc[11]; - hermes_count_current++; + hpsdr_AIN2_avg += power; + hpsdr_AIN2_peak = fmax(hpsdr_AIN2_peak, (double)power); + hpsdr_AIN3 += quisk_hermes_to_pc[10] << 8 | quisk_hermes_to_pc[11]; + 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. index = start + 5; diff --git a/quisk.py b/quisk.py index 4a2ed9f..948cd6d 100644 --- a/quisk.py +++ b/quisk.py @@ -6799,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.use_rx_udp == 10 and Hardware.hermes_board_id == 6: + if Hardware.is_HermesLite2: 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 e80c491..4302c6c 100644 --- a/quisk_conf_defaults.py +++ b/quisk_conf_defaults.py @@ -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 ] ] #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]] +power_meter_std_calibrations['Show ADC'] = [] ## Hermes_BandDict Rx IO Bus, dict # 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 = 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. ## hardware_file_name Hardware file path, rfile diff --git a/setup.py b/setup.py index d39c5dd..d7e310c 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.48' +Version = '4.2.49' fp = open("__init__.py", "w") # write title string fp.write("#Quisk version %s\n" % Version)