From f5c4732eba2f36ade90c80e9202774ca628fd35b Mon Sep 17 00:00:00 2001 From: "James C. Ahlstrom" Date: Fri, 11 Apr 2025 15:33:20 -0400 Subject: [PATCH] Quisk version 4.2.42 --- CHANGELOG.txt | 6 + __init__.py | 2 +- configure.py | 124 +++++++++++++--- quisk.py | 313 ++++++++++++++++++++++++++++++++++++++++- quisk_conf_defaults.py | 96 +++++++------ setup.py | 2 +- 6 files changed, 473 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index eeb2319..bbcee56 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,9 @@ +Quisk Version 4.2.42 April 2025 +================================ +I fixed a bug in the Kenwood IF CAT command. Thanks Nick! I moved the Pulse server IP address to the Sound screen. +I added a CAT method to imitate the Elecraft K4 command set over a TCP port. Quisk now has four CAT controls. To +make this clearer I added more documentation to the Remote screen. + Quisk Version 4.2.41 January 2025 ================================== I made some improvements to the top-level Config/Radios screen. There is now an option to diff --git a/__init__.py b/__init__.py index f7f82fa..5cf960e 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,2 @@ -#Quisk version 4.2.41 +#Quisk version 4.2.42 from .quisk import main diff --git a/configure.py b/configure.py index 370b105..d2253d8 100644 --- a/configure.py +++ b/configure.py @@ -1338,7 +1338,16 @@ class RadioNotebook(wx.Notebook): # The second-level notebook for each radio nam self.AddPage(page, "Sound") self.pages.append(page) for section, names in local_conf.sections: - if section in ('Sound', 'Bands', 'Filters'): # There is a special page for these sections + if section == 'Sound': # There is a special page for these sections + continue + if section == 'Bands': + continue + if section == 'Filters': + continue + if section == 'Remote': + page = RadioRemote(self, radio_name, section, names) + self.AddPage(page, section) + self.pages.append(page) continue page = RadioSection(self, radio_name, section, names) self.AddPage(page, section) @@ -1979,6 +1988,16 @@ class ControlMixin: btn.SetSizeHints(h, h, h, h) self.gbs.Add(btn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) return sld, btn + def AddTextHeading(self, key, text, help_text): + if key in text: # Start of heading + self.NextRow() + self.SetFont(self.heading_font) + self.AddTextL(1, help_text, span=7) + self.SetFont(self.font) + self.NextRow() + self.col = 2 + return key + return None def AddPopupMenuHelp(self, Qclass, col, text, btn_text, help_text, span1=1, span2=1, border=1): if text: txt = wx.StaticText(self, -1, text) @@ -2890,16 +2909,7 @@ class RadioSection(BaseWindow): # The pages for each section in the second-leve radio_dict = local_conf.GetRadioDict(self.radio_name) radio_type = radio_dict['hardware_file_type'] for name, text, fmt, help_text, values in self.names: - if name == 'remote_radio_password': - self.AddTextButtonHelp(col, text, "Change", self.OnChangePassword, help_text) - if col == 1: - col = 4 - else: - col = 1 - self.NextRow() - elif name == 'remote_radio_ip' and radio_type != "Control Head": - continue - elif name == 'favorites_file_path': + if name == 'favorites_file_path': self.favorites_path = radio_dict.get('favorites_file_path', '') row = self.row self.row = 1 @@ -3087,14 +3097,6 @@ class RadioSection(BaseWindow): # The pages for each section in the second-leve index = self.list_ctrl.GetFirstSelected() if index >= 0: self.list_ctrl.Select(index, 0) - def OnChangePassword(self, event): - text = local_conf.globals.get('remote_radio_password', '') - dlg = wx.TextEntryDialog(self, "Please enter a password for remote access", - "Password Entry", text) - if dlg.ShowModal() == wx.ID_OK: - local_conf.globals["remote_radio_password"] = dlg.GetValue() - local_conf.settings_changed = True - local_conf.SaveState() class RadioHardwareBase(BaseWindow): # The Hardware page in the second-level notebook for each radio def __init__(self, parent, radio_name): @@ -3747,7 +3749,7 @@ class RadioSound(BaseWindow): # The Sound page in the second-level notebook for self.radio_dict = local_conf.GetRadioDict(self.radio_name) self.num_cols = 8 for name, text, fmt, help_text, values in local_conf.GetSectionData('Sound'): - if name in ('digital_output_level', 'file_play_level'): + if name in ('digital_output_level', 'file_play_level', 'IQ_Server_IP'): value = self.GetValue(name, self.radio_dict) no_edit = "choice" in fmt or fmt == 'boolean' txt, cb, btn = self.AddTextComboHelp(1, text, value, values, help_text, no_edit) @@ -4182,6 +4184,88 @@ The Tx bits are sent as the "Alex" filters and are sent to the J16 interface.' if hasattr(application.Hardware, "ImmediateChange"): application.Hardware.ImmediateChange(name) +class RadioRemote(BaseWindow): # The Remote page in the second-level notebook for each radio + def __init__(self, parent, radio_name, section, names): + BaseWindow.__init__(self, parent) + self.radio_name = radio_name + self.section = section + self.names = names + self.heading_font = wx.Font(conf.config_font_size, wx.FONTFAMILY_SWISS, wx.ITALIC, + wx.FONTWEIGHT_NORMAL, False, conf.quisk_typeface) + self.MakeControls() + def MakeControls(self): + self.num_cols = 8 + #self.MarkCols() + self.AddColSpacer(1, 3) + self.NextRow() + self.col = 2 + radio_dict = local_conf.GetRadioDict(self.radio_name) + radio_type = radio_dict['hardware_file_type'] + heading = None + for name, text, fmt, help_text, values in self.names: + if heading: + if heading not in text: # End of heading + heading = None + self.NextRow() + self.col = 2 + if not heading: + heading = self.AddTextHeading("Hamlib Rig 2", text, +'Rig control commands using "Hamlib Rig 2" are sent to a Quisk TCP port from multiple clients.') + if not heading: + heading = self.AddTextHeading("XML-RPC", text, +'XML-RPC is a protocol that allows clients to call methods on a remote server using XML passed via HTTP') + if not heading: + heading = self.AddTextHeading("CAT serial", text, +'"CAT serial" supports the FlexRadio PowerSDR 2.x command set sent over a serial port. Some Kenwood commands are included.') + if not heading: + heading = self.AddTextHeading("remote radio", text, +'Quisk can be used as a control head to control a real radio located remotely. This feature is due to AC2YD.') + if not heading: + heading = self.AddTextHeading("K4 TCP", text, +'Rig control commands using "K4 TCP" use the Elecraft K4 command set, and are sent to a Quisk TCP port from multiple clients.') + if not heading: + heading = self.AddTextHeading("Dx cluster", text, +'"Dx Cluster" is a telnet service used to announce Dx spots in real time.') + if name == 'remote_radio_password': + self.AddTextButtonHelp(self.col, text, "Change", self.OnChangePassword, help_text) + if self.col == 2: + self.col = 5 + else: + self.col = 2 + self.NextRow() + elif name == 'remote_radio_ip' and radio_type != "Control Head": + continue + else: + if fmt[0:4] in ('dict', 'list'): + continue + if name[0:4] == platform_ignore: + continue + value = self.GetValue(name, radio_dict) + no_edit = "choice" in fmt or fmt == 'boolean' + txt, cb, btn = self.AddTextComboHelp(self.col, text, value, values, help_text, no_edit) + cb.handler = self.OnChange + cb.quisk_data_name = name + if self.col == 2: + self.col = 5 + else: + self.col = 2 + self.NextRow() + self.NextRow() + self.AddTextL(1, "End of list", span=7) + 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", + "Password Entry", text) + if dlg.ShowModal() == wx.ID_OK: + local_conf.globals["remote_radio_password"] = dlg.GetValue() + local_conf.settings_changed = True + local_conf.SaveState() + + class xxRadioFilters(BaseWindow): # The Filters page in the second-level notebook for each radio def __init__(self, parent, radio_name): BaseWindow.__init__(self, parent) diff --git a/quisk.py b/quisk.py index fc139a9..3aa7b98 100644 --- a/quisk.py +++ b/quisk.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# All QUISK software is Copyright (C) 2006-2024 by James C. Ahlstrom. +# All QUISK software is Copyright (C) 2006-2025 by James C. Ahlstrom. # This free software is licensed for use under the GNU General Public # License (GPL), see http://www.opensource.org. # Note that there is NO WARRANTY AT ALL. USE AT YOUR OWN RISK!! @@ -585,11 +585,18 @@ class HamlibHandlerSerial: mode = self.app.mode info = cmd info += "%011d" % (self.app.rxFreq + self.app.VFO) # frequency, ZZFA - info += '0000' - if ritFreq < 0: # RIT freq - info += "-%05d" % -ritFreq - else: - info += "+%05d" % ritFreq + if len(cmd) == 4: # Flex ZZIF + info += '0000' + if ritFreq < 0: # RIT freq + info += "-%05d" % -ritFreq + else: + info += "+%05d" % ritFreq + else: # Kenwood IF + info += '00000' + if ritFreq < 0: # RIT freq + info += "-%04d" % -ritFreq + else: + info += "+%04d" % ritFreq info += "%d" % rit # RIT status info += '0000' if QS.is_key_down(): # MOX, key down @@ -933,6 +940,8 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532 if not isinstance(text, Q3StringTypes): text = text.decode('utf-8') self.received += text + if HAMLIB_DEBUG and text: + print ("Raw rig2 received:", text) if '\n' in self.received: # A complete command ending with newline is available self.input, self.received = self.received.split('\n', 1) # Split off the command, save any further characters else: @@ -1218,6 +1227,267 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532 else: self.ErrParam() +class CatControl: + def GetFreqVfo(self, vfoB=False): + if vfoB: # The Tx Frequency + return self.app.txFreq + self.app.VFO + else: # The Rx Frequency + rx = self.app.multi_rx_screen.receiver_list + if rx: + rx = rx[0] + return rx.txFreq + rx.VFO + else: + return self.app.rxFreq + self.app.VFO + def SetFreqVfo(self, freq, vfoB=False): + if vfoB: # The Tx Frequency + self.app.ChangeRxTxFrequency(None, freq) + else: # The Rx Frequency + # For multiple receivers this controls the first added receiver frequency; else it controls split Rx/Tx frequency. + rx = self.app.multi_rx_screen.receiver_list + if rx: + rx[0].ChangeRxTxFrequency(freq) + else: + self.app.ChangeRxTxFrequency(freq, None) + def GetFilterBW(self): + return self.app.filter_bandwidth + def SetFilterBW(self, bw): + # Choose button closest to requested bandwidth + buttons = self.app.filterButns.GetButtons() + Lab = buttons[0].GetLabel() + diff = abs(int(Lab) - bw) + for i in range(1, len(buttons) - 1): + label = buttons[i].GetLabel() + df = abs(int(label) - bw) + if df < diff: + Lab = label + diff = df + self.app.filterButns.SetLabel(Lab, True) + +class ElecraftK4Handler(CatControl): # Test with telnet localhost 9200 + Mo2CoElecraft = {'LSB':1, 'USB':2, 'CWU':3, 'FM':4, 'AM':5, 'DGT-U':6, 'CWL':7, 'DGT-L':9, 'DGT-FM':4, 'DGT-IQ':6} + Co2MoElecraft = {1:'LSB', 2:'USB', 3:'CWU', 4:'FM', 5:'AM', 6:'DGT-U', 7:'CWL', 9:'DGT-L'} + def __init__(self, app, conf, sock, address): + self.app = app + self.conf = conf + self.sock = sock + sock.settimeout(0.0) + self.address = address + self.params = '' # params is the string following the command + self.received = '' + self.cmds = [] + self.K31Elecraft = False # K31 mode + h = self.Handlers = {} # Branch on the first two letters of the command + h['AI'] = self.AiAutoInfo + h['CW'] = self.CwCWPitch + h['DT'] = self.DtDataMode + h['FA'] = self.FaFbVfoFreq + h['FB'] = self.FaFbVfoFreq + h['FT'] = self.FtTxVfo + h['FW'] = self.FwFilterBW + h['ID'] = self.IdIdent + h['IF'] = self.IfInfo + h['IS'] = self.IsCenterPitch + h['KS'] = self.KsKeyerSpeed + h['K3'] = self.K3Elecraft + h['LN'] = self.LnLinkVfo + h['MD'] = self.MdMode + h['OM'] = self.OmOptionModules + h['RV'] = self.RvFirmware + h['RX'] = self.RxReceive + h['SB'] = self.SbSubReceiver + h['SM'] = self.SmSmeter + h['TX'] = self.TxTransmit + def Send(self, text): + """Send text back to the client.""" + if HAMLIB_DEBUG: + for cmd in self.cmds[0:-1]: + print ("CatTcp received: %-10s" % cmd) + print ("CatTcp received: %-10s ; sent %s" % (self.cmds[-1], text)) + del self.cmds[:] + text = text.encode('utf-8', errors='ignore') + try: + self.sock.sendall(text) + except socket.error: + self.sock.close() + self.sock = None + def Process(self): + """This is the main processing loop, and is called frequently. It reads and satisfies requests.""" + if not self.sock: + return 0 + try: # Read any data from the socket + text = self.sock.recv(1024) + except socket.timeout: # This does not work + pass + except socket.error: # Nothing to read + pass + else: # We got some characters + text = text.decode('utf-8', errors='ignore') + self.received += text + if HAMLIB_DEBUG and text: + pass #print ("Raw CatTcp received:", text) + if ';' in self.received: # A complete command ending with semicolon is available + cmd, self.received = self.received.split(';', 1) # Split off the command, save any further characters + else: + return 1 + cmd = cmd.strip() # Here is our command line + if len(cmd) < 2: + return 1 + if HAMLIB_DEBUG: + self.cmds.append(cmd) + base = cmd[0:2].upper() + try: + handler = self.Handlers[base] + except KeyError: + self.Send(cmd[0:2] + "?;") + #print ("Unknown command", cmd) + return 1 + if len(cmd) == 2: + args = '' + elif cmd[2] == '$': # base is the first two letters upper case and any following "$" + base += '$' + args = cmd[3:] + else: + args = cmd[2:] + handler(base, args) # args is the remainder + return 1 + def AiAutoInfo(self, base, args): + if not args: + self.Send("AI0;") + elif args != '0': + self.Send("AI0;") + def CwCWPitch(self, base, args): + pitch = self.conf.cwTone + pitch = (pitch + 5) // 10 + if pitch < 25: + pitch = 25 + elif pitch > 95: + pitch = 95 + self.Send("CW%d;" % pitch) + def DtDataMode(self, base, args): + self.Send("%s0;" % base) + def IdIdent(self, base, args): + if args: + self.Send("ID?;") + else: + self.Send("ID017;") + #self.Send("IDQuisk;") + def IfInfo(self, base, args): + rxfreq = self.GetFreqVfo() + info = "%011d " % rxfreq # Rx frequency, five blanks + ritFreq = self.app.ritScale.GetValue() + if ritFreq < 0: # +/- RIT freq + info += "-%04d" % -ritFreq + else: + info += "+%04d" % ritFreq + if self.app.ritButton.GetValue(): # RIT status, XIT status, blank "00" + info += "10 00" + else: + info += "00 00" + if QS.is_key_down(): # MOX, key down + info += '1' + else: + info += '0' + mode = self.Mo2CoElecraft.get(self.app.mode, 2) + info += "%d" % mode # Mode 1-9 + info += '00' # Rx on VFOA, no scan + if self.app.split_rxtx: # VFO split status + info += '1' + else: + info += '0' + info += '001 ;' + self.Send(info) + def FaFbVfoFreq(self, base, args): + if base[1] in ('B', 'b'): + vfoB = True + else: + vfoB = False + if not args: # Get command + freq = "%06d" % self.GetFreqVfo(vfoB) + self.Send(base + freq + ';') + else: # Put command + try: + freq = int(args) + except: + self.Send(base + "?;") + else: + l = len(args) + if l <= 2: + freq *= 1000000 + elif l <= 5: + freq *= 1000 + self.SetFreqVfo(freq, vfoB) + def FtTxVfo(self, base, args): + if not args: + if self.app.split_rxtx: + self.Send("FT1;") + else: + self.Send("FT0;") + else: + # Set split Rx/Tx mode. + is_split = self.app.splitButton.GetValue() + split = int(args) + if split and not is_split: + self.app.splitButton.SetValue(split, True) + elif is_split and not split: + self.app.splitButton.SetValue(split, True) + def FwFilterBW(self, base, args): + if not args: + code = self.GetFilterBW() + self.Send("%s%04d;" % (base, (code + 5) // 10)) + else: + self.SetFilterBW(int(args) * 10) + def IsCenterPitch(self, base, args): + if not args: + code = self.GetFilterBW() // 2 + if self.K31Elecraft: + self.Send("%s %04d;" % (base, code)) + else: + code = (code + 5) // 10 + self.Send("%s%04d;" % (base, code)) + else: + if self.K31Elecraft: + self.SetFilterBW(int(args) * 2) + else: + self.SetFilterBW(int(args) * 2 * 10) + def K3Elecraft(self, base, args): + if args == '1': + self.K31Elecraft = True + else: + self.K31Elecraft = False + def KsKeyerSpeed(self, base, args): + if len(base) == 2: + self.Send("KS013;") + def LnLinkVfo(self, base, args): + if args != '0': + self.Send("LN0;") + def MdMode(self, base, args): + if args: # set the mode + code = int(args) + mode = self.Co2MoElecraft[code] + self.app.modeButns.SetLabel(mode, True) + else: # return the mode + code = self.Mo2CoElecraft.get(self.app.mode, 2) + self.Send("%s%d;" % (base, code)) + def OmOptionModules(self, base, args): + self.Send("OM ------------;") + def RvFirmware(self, base, args): + self.Send("%s99.99;" % base) + def RxReceive(self, base, args): + self.app.pttButton.SetValue(0, True) + def SbSubReceiver(self, base, args): + self.Send("SB0;") + def SmSmeter(self, base, args): + if base[0:3].upper() == "SMH": + self.Send(base + "?;") + elif args: + self.Send(base + "?;") + elif self.K31Elecraft: + self.Send("SM0000;") + else: + self.Send("SM00;") + def TxTransmit(self, base, args): + self.app.pttButton.SetValue(1, True) + class SoundThread(threading.Thread): """Create a second (non-GUI) thread to read, process and play sound.""" def __init__(self, samples_from_python): @@ -3698,6 +3968,19 @@ class App(wx.App): # traceback.print_exc() else: self.hamlib_socket = None + # Quisk control by K4 commands through a TCP port + self.k4_tcp_clients = [] # list of TCP connections to handle + if conf.k4_tcp_port: + try: + self.k4_tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.k4_tcp_socket.bind((conf.k4_tcp_ip, conf.k4_tcp_port)) + self.k4_tcp_socket.settimeout(0.0) + self.k4_tcp_socket.listen(5) # listen for TCP connections from multiple clients + except: + self.k4_tcp_socket = None + # traceback.print_exc() + else: + self.k4_tcp_socket = None # Quisk control by fldigi self.fldigi_new_freq = None self.fldigi_freq = None @@ -4406,6 +4689,9 @@ class App(wx.App): if self.hamlib_socket: self.hamlib_socket.close() self.hamlib_socket = None + if self.k4_tcp_socket: + self.k4_tcp_socket.close() + self.k4_tcp_socket = None if self.dxCluster: self.dxCluster.stop() if self.hamlib_com1_handler: @@ -6383,7 +6669,7 @@ class App(wx.App): if rxtx != 'rx': self.fldigi_server.main.rx() self.fldigi_timer = time.time() - def HamlibPoll(self): # Poll for Hamlib commands + def HamlibPoll(self): # Poll for Hamlib and K4 TCP commands if self.hamlib_socket: try: # Poll for new client connections. conn, address = self.hamlib_socket.accept() @@ -6397,6 +6683,19 @@ class App(wx.App): self.hamlib_clients.remove(client) # print ('Remove', client.address) break + if self.k4_tcp_socket: + try: # Poll for new client connections. + conn, address = self.k4_tcp_socket.accept() + except socket.error: + pass + else: + # print ('Connection from', address) + self.k4_tcp_clients.append(ElecraftK4Handler(self, conf, conn, address)) + for client in self.k4_tcp_clients: # Service existing clients + if not client.Process(): # False return indicates a closed connection; remove the handler for this client + self.k4_tcp_clients.remove(client) + # print ('Remove', client.address) + break def OnKeyHook(self, event): event.Skip() if conf.hot_key_ptt_if_hidden: # Hot key PTT operates even if Quisk is hidden diff --git a/quisk_conf_defaults.py b/quisk_conf_defaults.py index 643572f..fa59628 100644 --- a/quisk_conf_defaults.py +++ b/quisk_conf_defaults.py @@ -1094,6 +1094,11 @@ file_play_level = 1.000 #file_play_level = 0.500 #file_play_level = 0.100 +## IQ_Server_IP Pulse server IP address, text +# IP Address for remote PulseAudio IQ server. +IQ_Server_IP = "" +#IQ_Server_IP = "192.168.230.102" + # Sound card names: # # In PortAudio, soundcards have an index number 0, 1, 2, ... and a name. @@ -1303,51 +1308,15 @@ fixed_tune_offset = 0 ################ Remote -# DX cluster telent login data, thanks to DJ4CM. Must have station_display_lines > 0. -## dxClHost Dx cluster host name, text -# The Dx cluster options log into a Dx cluster server, and put station information -# on the station window under the graph and waterfall screens. -# dxClHost is the telnet host name. -dxClHost = '' -#dxClHost = 'example.host.net' - -## dxClPort Dx cluster port number, integer -# The Dx cluster options log into a Dx cluster server, and put station information -# on the station window under the graph and waterfall screens. -# dxClPort is the telnet port number. -dxClPort = 7373 - -## user_call_sign Call sign for Dx cluster, text -# The Dx cluster options log into a Dx cluster server, and put station information -# on the station window under the graph and waterfall screens. -# user_call_sign is your call sign which may be needed for login. -user_call_sign = '' - -## dxClPassword Password for Dx cluster, text -# The Dx cluster options log into a Dx cluster server, and put station information -# on the station window under the graph and waterfall screens. -# dxClPassword is the telnet password for the server. -dxClPassword = '' -#dxClPassword = 'getsomedx' - -## dxClExpireTime Dx cluster expire minutes, integer -# The Dx cluster options log into a Dx cluster server, and put station information -# on the station window under the graph and waterfall screens. -# dxClExpireTime is the time in minutes until DX Cluster entries are removed. -dxClExpireTime = 20 - -## IQ_Server_IP Pulse server IP address, text -#IP Address for remote PulseAudio IQ server. -IQ_Server_IP = "" - ## hamlib_ip IP address for Hamlib Rig 2, text # You can control Quisk from Hamlib. Set the Hamlib rig to 2 and the device for rig 2 to # localhost:4575. Or choose a different name and port here. Set the same name and port # in the controlling program. # hamlib_ip is the IP name or address. -hamlib_ip = "localhost" +hamlib_ip = "" +#hamlib_ip = "localhost" -## hamlib_port IP port for Hamlib, integer +## hamlib_port IP port for Hamlib Rig 2, integer # You can control Quisk from Hamlib. For direct control, set the external program to rig 2 # "Hamlib NET rigctl", and set the Quisk hamlib port to 4532. To use the rigctld program to control # Quisk, set the Quisk hamlib port to 4575. To turn off Hamlib control, set the Quisk port to zero. @@ -1398,19 +1367,64 @@ win_hamlib_com2_name = "" #win_hamlib_com2_name = "COM15" #win_hamlib_com2_name = "COM16" -## remote_radio_ip Remote radio IP or name, text +## remote_radio_ip IP or name of the remote radio, text # Quisk can be used as a control head to control a real radio located remotely. # This is the IP adddress or the host name of the remote radio. remote_radio_ip = "" #remote_radio_ip = "192.168.1.56" -## remote_radio_password Password for remote radio, text +## remote_radio_password Password for the remote radio, text # Quisk can be used as a control head to control a real radio located remotely. # For security it requires the same password to be entered on both computers. # Enter a fairly long pass phrase of 20 or more characters. # It is only necessary to enter it once on each computer. remote_radio_password = "" +## k4_tcp_ip IP address for K4 TCP, text +# This is the Quisk IP address for the TCP server implementing K4 commands. +k4_tcp_ip = "" +#k4_tcp_ip = "localhost" +#k4_tcp_ip = "127.0.0.1" + +## k4_tcp_port Port number for K4 TCP, integer +# This is the Quisk port for the TCP server implementing K4 commands. +k4_tcp_port = 0 +#k4_tcp_port = 9200 +#k4_tcp_port = 50004 + +# DX cluster telent login data, thanks to DJ4CM. Must have station_display_lines > 0. +## dxClHost Dx cluster host name, text +# The Dx cluster options log into a Dx cluster server, and put station information +# on the station window under the graph and waterfall screens. +# dxClHost is the telnet host name. +dxClHost = '' +#dxClHost = 'example.host.net' + +## dxClPort Dx cluster port number, integer +# The Dx cluster options log into a Dx cluster server, and put station information +# on the station window under the graph and waterfall screens. +# dxClPort is the telnet port number. +dxClPort = 7373 + +## user_call_sign Call sign for Dx cluster, text +# The Dx cluster options log into a Dx cluster server, and put station information +# on the station window under the graph and waterfall screens. +# user_call_sign is your call sign which may be needed for login. +user_call_sign = '' + +## dxClPassword Password for Dx cluster, text +# The Dx cluster options log into a Dx cluster server, and put station information +# on the station window under the graph and waterfall screens. +# dxClPassword is the telnet password for the server. +dxClPassword = '' +#dxClPassword = 'getsomedx' + +## dxClExpireTime Dx cluster expire minutes, integer +# The Dx cluster options log into a Dx cluster server, and put station information +# on the station window under the graph and waterfall screens. +# dxClExpireTime is the time in minutes until DX Cluster entries are removed. +dxClExpireTime = 20 + hamlib_com1_name = "" diff --git a/setup.py b/setup.py index 0b419ba..77fb90d 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.41' +Version = '4.2.42' fp = open("__init__.py", "w") # write title string fp.write("#Quisk version %s\n" % Version)