Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e8a0846f7 | ||
|
|
fb888a2e08 | ||
|
|
78a1ca3d60 | ||
|
|
14a80f09d5 |
@@ -1,3 +1,34 @@
|
||||
Quisk Version 4.2.40 November 2024
|
||||
===================================
|
||||
Python 3.13 eliminated the telnetlib module, so I wrote a replacement. I fixed a bug in window sizing.
|
||||
If you use the Dx Cluster feature, please report any problems. Dx Cluster used to use the telnetlib module
|
||||
but is now new code.
|
||||
|
||||
I updated the install to Python 3.13. Please report any problems with Python 3.13.
|
||||
|
||||
Quisk Version 4.2.39 October 2024
|
||||
==================================
|
||||
I added an optional Hardware method PollGuiControl(self). If you define this in your Hardware file,
|
||||
the GUI thread will call it frequently. It can be used to set PTT from your hardware.
|
||||
|
||||
I am trying to work around a wxPython quirk that causes mouse wheel tuning to fail. Please test.
|
||||
|
||||
Quisk Version 4.2.38 September 2024
|
||||
====================================
|
||||
This is a bugfix release. I fixed a bug in the Hamlib serial port when using a physical TTY instead
|
||||
of a pseudo-tty. The Hamlib ZZFA command now changes the receive frequency instead of the transmit
|
||||
frequency. I fixed some window sizing issues.
|
||||
|
||||
Quisk Version 4.2.37 August 2024
|
||||
=================================
|
||||
Quisk now has a new option "Fixed tune offset" on the Config/radio/Options screen. This is meant for
|
||||
Softrock radios that need a constant audio frequency to simplify amplitude and phase corrections.
|
||||
The value zero means no change to Quisk tuning. Otherwise enter the fixed frequency offset from center.
|
||||
If you are using the obsolete "mouse_tune_method = 1" please delete it and use this new feature.
|
||||
If you use softrock_tune_vfo.py as your hardware file, you should replace it with softrock/hardware_usb.py.
|
||||
|
||||
Quisk now works on Python 3.12. And I fixed a bug in the small screen graph width.
|
||||
|
||||
Quisk Version 4.2.36 August 2024
|
||||
=================================
|
||||
I added two new color schemes by Michael, DK1MI. The On/Off button color and text color can
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
#Quisk version 4.2.36
|
||||
#Quisk version 4.2.40
|
||||
from .quisk import main
|
||||
|
||||
+22
-20
@@ -328,7 +328,7 @@ class Configuration:
|
||||
path = self.NormPath(path)
|
||||
if not os.path.isfile(path):
|
||||
dlg = wx.MessageDialog(None,
|
||||
"Failure for hardware file %s!" % path,
|
||||
"Can not find the hardware file %s!" % path,
|
||||
'Hardware File', wx.OK|wx.ICON_ERROR)
|
||||
ret = dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
@@ -399,11 +399,12 @@ class Configuration:
|
||||
notebk.AddPage(self.radio_page, "Radios")
|
||||
self.radios_page_start = notebk.GetPageCount()
|
||||
notebk.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanging, notebk)
|
||||
if Settings[1] in Settings[2]:
|
||||
page = RadioNotebook(notebk, Settings[1])
|
||||
notebk.AddPage(page, "*%s*" % Settings[1])
|
||||
for name in Settings[2]:
|
||||
page = RadioNotebook(notebk, name)
|
||||
if name == Settings[1]:
|
||||
notebk.AddPage(page, "*%s*" % name)
|
||||
else:
|
||||
if name != Settings[1]:
|
||||
page = RadioNotebook(notebk, name)
|
||||
notebk.AddPage(page, name)
|
||||
def GuessType(self):
|
||||
udp = conf.use_rx_udp
|
||||
@@ -477,7 +478,7 @@ class Configuration:
|
||||
for rxname, data in self.receiver_data:
|
||||
if rxname == receiver_name:
|
||||
return data
|
||||
return None
|
||||
return ()
|
||||
def GetReceiverDatum(self, receiver_name, item_name):
|
||||
for rxname, data in self.receiver_data:
|
||||
if rxname == receiver_name:
|
||||
@@ -563,6 +564,8 @@ class Configuration:
|
||||
# self.format4name with the items that Configuration understands.
|
||||
# Dicts and lists are Python objects. All other items are text, not Python objects.
|
||||
#
|
||||
# This parses the quisk_conf_defaults.py file and user-defined radios in ./*pkg/quisk_hardware.py.
|
||||
#
|
||||
# Sections start with 16 #, section name
|
||||
# self.sections is a list of [section_name, section_data]
|
||||
# section_data is a list of [data_name, text, fmt, help_text, values]
|
||||
@@ -595,7 +598,7 @@ class Configuration:
|
||||
except:
|
||||
traceback.print_exc()
|
||||
def _ParserConf(self, filename):
|
||||
re_AeqB = re.compile("^#?(\w+)\s*=\s*([^#]+)#*(.*)") # item values "a = b"
|
||||
re_AeqB = re.compile(r"^#?(\w+)\s*=\s*([^#]+)#*(.*)") # item values "a = b"
|
||||
section = None
|
||||
data_name = None
|
||||
multi_line = False
|
||||
@@ -2227,7 +2230,8 @@ class ControlMixin:
|
||||
if self.radio_name == Settings[1]: # changed for current radio
|
||||
if name in ('hot_key_ptt_toggle', 'hot_key_ptt_if_hidden', 'keyupDelay', 'cwTone', 'pulse_audio_verbose_output',
|
||||
'start_cw_delay', 'start_ssb_delay', 'maximum_tx_secs', 'quisk_serial_cts', 'quisk_serial_dsr',
|
||||
'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec', 'hermes_lite2_enable'):
|
||||
'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec', 'hermes_lite2_enable',
|
||||
'fixed_tune_offset'):
|
||||
setattr(conf, name, x)
|
||||
application.ImmediateChange(name)
|
||||
elif name[0:4] in ('lin_', 'win_'):
|
||||
@@ -2258,6 +2262,9 @@ class ControlMixin:
|
||||
elif hasattr(application.Hardware, "ImmediateChange"):
|
||||
setattr(conf, name, x)
|
||||
application.Hardware.ImmediateChange(name)
|
||||
elif name[0:6] == 'keyer_':
|
||||
setattr(conf, name, x)
|
||||
application.ImmediateChange(name)
|
||||
def FormatOK(self, value, fmt4): # Check formats integer, number, boolean
|
||||
ok, v = self.EvalItem(value, fmt4)
|
||||
return ok
|
||||
@@ -2660,6 +2667,7 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
||||
choices = []
|
||||
for name, data in local_conf.receiver_data:
|
||||
choices.append(name)
|
||||
choices.sort()
|
||||
self.add_type = self.AddComboCtrl(2, '', choices=choices, no_edit=True)
|
||||
self.add_type.SetSelection(0)
|
||||
item = self.AddTextL(3, "and name the new radio")
|
||||
@@ -2743,11 +2751,9 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
||||
choices = Settings[2][:] # can rename any available radio
|
||||
self.rename_old.SetItems(choices)
|
||||
self.rename_old.SetSelection(0)
|
||||
if "ConfigFileRadio" in choices:
|
||||
if "ConfigFileRadio" in choices: # can not delete ConfigFileRadio
|
||||
choices.remove("ConfigFileRadio")
|
||||
if Settings[1] in choices:
|
||||
choices.remove(Settings[1])
|
||||
self.delete_name.SetItems(choices) # can not delete ConfigFileRadio nor the current radio
|
||||
self.delete_name.SetItems(choices)
|
||||
self.delete_name.SetSelection(0)
|
||||
choices = Settings[2] + ["Ask me"]
|
||||
if "ConfigFileRadio" not in choices:
|
||||
@@ -2892,12 +2898,6 @@ class RadioSection(BaseWindow): # The pages for each section in the second-leve
|
||||
hlp = "To delete a row, select it and press this button."
|
||||
self.AddTextButtonHelp(2, "Delete Midi row", "Delete", self.OnMidiDelete, hlp)
|
||||
self.NextRow()
|
||||
if self.section == "Timing and CW":
|
||||
if col != 1:
|
||||
col = 1
|
||||
self.NextRow()
|
||||
self.NextRow()
|
||||
self.AddTextL(1, 'Midi CW key moved to "Keys"', span=4)
|
||||
if not Keys:
|
||||
self.AddColSpacer(2, 20)
|
||||
self.AddColSpacer(5, 20)
|
||||
@@ -3688,8 +3688,10 @@ class RadioSound(BaseWindow): # The Sound page in the second-level notebook for
|
||||
self.NextRow()
|
||||
choices = (("48000", "96000", "192000"), ("0", "1"), ("0", "1"), (" ", "0", "1"))
|
||||
r = 0
|
||||
if "SoftRock" in self.radio_dict['hardware_file_type']: # Samples come from sound card
|
||||
softrock = True
|
||||
if "SoftRock" in self.radio_dict['hardware_file_type']:
|
||||
softrock = True # Samples come from sound card
|
||||
elif hasattr(application.Hardware, "use_softrock"):
|
||||
softrock = application.Hardware.use_softrock
|
||||
else:
|
||||
softrock = False
|
||||
last_row = 8
|
||||
|
||||
@@ -194,7 +194,7 @@ Quisk is free open source software written in Python and C. It is hosted on the
|
||||
<br>
|
||||
<p>
|
||||
If you have Python 3 installed on your computer, just continue to use that version.
|
||||
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11.</b>
|
||||
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11 or 3.12.</b>
|
||||
If you want to install a more recent version of Python, uninstall the old version first.
|
||||
Running multiple versions of Python is possible but can be confusing and is not necessary.
|
||||
A Quisk installation is needed for each version of Python you have.
|
||||
@@ -204,7 +204,7 @@ A Quisk installation is needed for each version of Python you have.
|
||||
<p>
|
||||
Windows does not include Python, so you must first install a recent version of 64-bit Python 3.
|
||||
If you already have 64-bit Python 3 installed, just keep using it.
|
||||
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11.</b>
|
||||
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11 or 3.12.</b>
|
||||
There may be newer versions of Python, but until support is added, use the versions listed above.
|
||||
Python is available from <a href="http://www.python.org/">http://www.python.org</a>.
|
||||
There is an option to <b>Add Python to PATH</b>. You MUST select
|
||||
@@ -239,13 +239,19 @@ python -m pip install --upgrade pyserial
|
||||
<br><br>
|
||||
python -m pip install --upgrade quisk
|
||||
<br><br>
|
||||
You should then be able to start Quisk with the command "quisk".
|
||||
You can also start Quisk with "python -m quisk".
|
||||
To create a Quisk shortcut on your desktop, right-click an empty space and select "New" and "Shortcut".
|
||||
Use "quisk.exe" as the command and "Quisk" as the name.
|
||||
The "quisk.exe" program is in <b>your_install_directory</b>\Scripts.
|
||||
Enter this as the location of the item:
|
||||
</p>
|
||||
<br>
|
||||
<pre>
|
||||
pythonw -m quisk
|
||||
</pre>
|
||||
<br>
|
||||
<p>
|
||||
and then name the shortcut, perhaps "Quisk".
|
||||
If you check the properties you will see that Windows expanded the location and start directory to the full path.
|
||||
If you are curious, Quisk and its Python source files are installed in "<b>your_install_directory</b>\Lib\site-packages\quisk".
|
||||
If you change to that directory you can run Quisk with "python quisk.py".
|
||||
If you change to that directory you can run Quisk with "pythonw quisk.py".
|
||||
</p>
|
||||
<br>
|
||||
<p>
|
||||
@@ -824,8 +830,14 @@ SoftRock radios use an analog mixer to change the RF signal to stereo audio, and
|
||||
A high quality sound card is advisable. The analog mixer is not perfect, and it will be necessary to adjust the I and Q
|
||||
signals to equal amplitude and 90 degrees phase difference. Use the Config/Config screen buttons to
|
||||
bring up an adjustment screen. Adjustments must be made for each band, and separately for transmit and receive.
|
||||
The adjustment depends on both the VFO frequency and the tuning offset from the VFO. See my paper
|
||||
The adjustment depends on both the VFO frequency and the tuning offset from the VFO.
|
||||
Quisk has an option to fix the tuning offset to a constant and to tune with the hardware.
|
||||
The tuning offset then has a constant correction and it is only necessary to correct for the VFO,
|
||||
a much less sensitive correction.
|
||||
See my paper
|
||||
<a href="http://james.ahlstrom.name/phase_corr.html">http://james.ahlstrom.name/phase_corr.html</a> for more information.
|
||||
<br>
|
||||
<br>
|
||||
A good strategy is to pick a VFO near the band center, and record corrections at an Rx frequency of -15000, -1000, 1000 and 15000 Hertz.
|
||||
If the corrections are sensitive to VFO, record the corrections for these same Rx frequencies at VFOs equal to
|
||||
or slightly outside the upper and lower band edges.
|
||||
@@ -834,6 +846,10 @@ You can add as many correction points as desired. The corrections are saved in t
|
||||
by hand if you are a Python expert. Otherwise you can just read the values.
|
||||
<br>
|
||||
<br>
|
||||
If you fix the tuning offset to a constant, you can create a correction for VFO at the band center, or
|
||||
the center and both edges.
|
||||
<br>
|
||||
<br>
|
||||
To create a Receive correction point for a given VFO and frequency, attach a signal generator to the SoftRock through an attenuator,
|
||||
and look at the image on the graph screen. If the signal is 3500 Hertz above the VFO, the image is 3500 Hertz below it.
|
||||
Choose "Measure" to measure the corrections and then "Add Cell".
|
||||
|
||||
+87
-39
@@ -1,9 +1,9 @@
|
||||
from __future__ import absolute_import
|
||||
# This code was contributed by Christof, DJ4CM. Many Thanks!!
|
||||
# Modified November 2024 by N2ADR to remove deprecated telnetlib module.
|
||||
|
||||
import threading
|
||||
import socket
|
||||
import time
|
||||
import telnetlib
|
||||
import quisk_conf_defaults as conf
|
||||
|
||||
class DxEntry:
|
||||
@@ -81,61 +81,109 @@ class DxEntry:
|
||||
|
||||
class DxCluster(threading.Thread):
|
||||
def __init__(self):
|
||||
self.do_init = 1
|
||||
self.error = 'Starting'
|
||||
self.dxSpots = []
|
||||
threading.Thread.__init__(self)
|
||||
self.doQuit = threading.Event()
|
||||
self.dxSpots = []
|
||||
self.doQuit.clear()
|
||||
self.dxLock = threading.Lock()
|
||||
self.addr = conf.dxClHost + ':' + str(conf.dxClPort)
|
||||
self.msg_no_spots = "No DX Cluster data from " + self.addr
|
||||
self.msg_one_spot = '1 DX spot received from ' + self.addr
|
||||
self.msg_spots = ' DX spots received from ' + self.addr
|
||||
|
||||
def run(self):
|
||||
self.telnetInit()
|
||||
self.telnetConnect()
|
||||
if self.error:
|
||||
self.sock.close()
|
||||
return
|
||||
while not self.doQuit.isSet():
|
||||
try:
|
||||
self.telnetRead()
|
||||
by = self.sock.recv(1024)
|
||||
except TimeoutError:
|
||||
continue
|
||||
except:
|
||||
self.tn.close()
|
||||
time.sleep(20)
|
||||
by = b''
|
||||
if by:
|
||||
with self.dxLock:
|
||||
self.bytes += by
|
||||
else:
|
||||
self.sock.close()
|
||||
self.error = "Restarting " + self.addr
|
||||
time.sleep(2)
|
||||
if not self.doQuit.isSet():
|
||||
self.telnetConnect()
|
||||
self.tn.close()
|
||||
|
||||
def setListener (self, listener):
|
||||
self.listener = listener
|
||||
if self.error:
|
||||
self.sock.close()
|
||||
return
|
||||
self.sock.close()
|
||||
|
||||
def telnetInit(self):
|
||||
self.tn = telnetlib.Telnet()
|
||||
|
||||
def telnetConnect(self):
|
||||
self.bytes = bytearray(0)
|
||||
self.error = 'Starting'
|
||||
self.sock = socket.socket()
|
||||
self.sock.settimeout(20)
|
||||
try:
|
||||
self.sock.connect( (conf.dxClHost, conf.dxClPort) )
|
||||
except:
|
||||
self.error = "Failed to connect to " + self.addr
|
||||
return
|
||||
self.sock.settimeout(1)
|
||||
for i in range(10):
|
||||
try:
|
||||
self.tn.open(conf.dxClHost, conf.dxClPort, 10)
|
||||
self.tn.read_until(b"login:", 10)
|
||||
self.tn.write(conf.user_call_sign.encode('utf-8', errors='ignore') + b"\n") # user_call_sign may be Unicode
|
||||
break
|
||||
self.bytes += self.sock.recv(1024)
|
||||
except:
|
||||
time.sleep(0.5)
|
||||
pass
|
||||
if b"login:" in self.bytes:
|
||||
break
|
||||
else:
|
||||
self.error = 'No "login:" prompt from ' + self.addr
|
||||
return
|
||||
self.sock.sendall(conf.user_call_sign.encode('utf-8', errors='ignore') + b"\r\n")
|
||||
if conf.dxClPassword:
|
||||
self.tn.read_until(b"Password: ")
|
||||
self.tn.write(conf.dxClPassword.encode('utf-8', errors='ignore') + b"\n")
|
||||
for i in range(10):
|
||||
try:
|
||||
self.bytes += self.sock.recv(1024)
|
||||
except:
|
||||
pass
|
||||
if b"Password:" in self.bytes:
|
||||
break
|
||||
else:
|
||||
self.error = 'No "Password:" prompt from ' + self.addr
|
||||
return
|
||||
self.sock.sendall(conf.dxClPassword.encode('utf-8', errors='ignore') + b"\r\n")
|
||||
self.error = ''
|
||||
self.bytes = bytearray(0)
|
||||
|
||||
def telnetRead(self):
|
||||
message = self.tn.read_until(b'\n', 60).decode(encoding='utf-8', errors='replace')
|
||||
if self.doQuit.isSet() == False:
|
||||
dxEntry = DxEntry();
|
||||
if dxEntry.parseMessage(message):
|
||||
for i, listElement in enumerate(self.dxSpots):
|
||||
if (listElement.equal(dxEntry)):
|
||||
listElement.join (dxEntry)
|
||||
return
|
||||
if listElement.isExpired():
|
||||
del (self.dxSpots[i])
|
||||
self.dxSpots.append(dxEntry)
|
||||
if self.listener:
|
||||
self.listener()
|
||||
|
||||
def getHost(self):
|
||||
return self.tn.host + ':' + str(self.tn.port)
|
||||
def Poll(self):
|
||||
with self.dxLock:
|
||||
index = self.bytes.find(b"\n")
|
||||
if index >= 0:
|
||||
message = self.bytes[0:index + 1]
|
||||
self.bytes = self.bytes[index + 1:]
|
||||
else:
|
||||
return
|
||||
message = message.decode(encoding='utf-8', errors='replace')
|
||||
dxEntry = DxEntry()
|
||||
if dxEntry.parseMessage(message):
|
||||
for i, listElement in enumerate(self.dxSpots):
|
||||
if (listElement.equal(dxEntry)):
|
||||
listElement.join (dxEntry)
|
||||
return True
|
||||
if listElement.isExpired():
|
||||
del (self.dxSpots[i])
|
||||
self.dxSpots.append(dxEntry)
|
||||
return True
|
||||
|
||||
def stop(self):
|
||||
self.doQuit.set()
|
||||
|
||||
def dxStatus(self):
|
||||
if self.error:
|
||||
return self.error
|
||||
nSpots = len(self.dxSpots)
|
||||
if nSpots == 0:
|
||||
return self.msg_no_spots
|
||||
elif nSpots == 1:
|
||||
return self.msg_one_spot
|
||||
return str(nSpots) + self.msg_spots
|
||||
|
||||
@@ -402,6 +402,8 @@ class Hardware(BaseHardware):
|
||||
if do_tx:
|
||||
try:
|
||||
self.rx_udp_socket.send(s)
|
||||
if DEBUG:
|
||||
self.PrintStatus(' Send', self.want_udp_status)
|
||||
except:
|
||||
pass
|
||||
def SetVNA(self, key_down=None, vna_start=None, vna_stop=None, vna_count=None, do_tx=False):
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#
|
||||
@@ -0,0 +1,209 @@
|
||||
# This file is part of the Multus project. See https://www.multus-sdr.com/.
|
||||
# It controls what items appear on the Hardware configuration screen and
|
||||
# provides logic for each radio.
|
||||
|
||||
|
||||
################ Receivers Multus CW, A transceiver from the Multus project.
|
||||
## hardware_file_name Hardware file path, rfile
|
||||
# This is the file that contains the control logic for each radio.
|
||||
#hardware_file_name = "multuspkg/quisk_hardware.py"
|
||||
|
||||
## widgets_file_name Widget file path, rfile
|
||||
# This optional file adds additional controls for the radio.
|
||||
#widgets_file_name = ""
|
||||
|
||||
## rx_max_amplitude_correct Max ampl correct, number
|
||||
# If you get your I/Q samples from a sound card, you will need to correct the
|
||||
# amplitude and phase for inaccuracies in the analog hardware. The correction is
|
||||
# entered using the controls from the "Rx Phase" button on the config screen.
|
||||
# You must enter a positive number. This controls the range of the control.
|
||||
#rx_max_amplitude_correct = 0.2
|
||||
|
||||
## rx_max_phase_correct Max phase correct, number
|
||||
# If you get your I/Q samples from a sound card, you will need to correct the
|
||||
# amplitude and phase for inaccuracies in the analog hardware. The correction is
|
||||
# entered using the controls from the "Rx Phase" button on the config screen.
|
||||
# You must enter a positive number. This controls the range of the control in degrees.
|
||||
#rx_max_phase_correct = 10.0
|
||||
|
||||
## tx_level Tx Level, dict
|
||||
# This is the level of the Tx audio sent to SoftRock hardware after all processing as a percentage
|
||||
# number from 0 to 100.
|
||||
# The level should be below 100 to allow headroom for amplitude and phase adjustments.
|
||||
# Changes are immediate (no need to restart).
|
||||
#tx_level = {}
|
||||
|
||||
## digital_tx_level Digital Tx power %, integer
|
||||
# Digital modes reduce power by the percentage on the config screen.
|
||||
# This is the maximum value of the slider.
|
||||
#digital_tx_level = 100
|
||||
|
||||
## keyer_speed Keyer speed WPM, integer
|
||||
# This sets the keyer speed in words per minute.
|
||||
#keyer_speed = 8
|
||||
#keyer_speed = 13
|
||||
#keyer_speed = 18
|
||||
#keyer_speed = 20
|
||||
#keyer_speed = 30
|
||||
#keyer_speed = 40
|
||||
|
||||
## keyer_type Type of keyer, text choice
|
||||
# Use "Straight" mode for a straight key or a bug. Connect the tip of the key to the transceiver.
|
||||
# Otherwise, select Iambic-A or Iambic-B mode of operation.
|
||||
#keyer_type = "Straight"
|
||||
#keyer_type = "Iambic-A"
|
||||
#keyer_type = "Iambic-B"
|
||||
|
||||
## keyer_space Keyer space, text choice
|
||||
# This sets the type of spacing between elements or letters.
|
||||
# "Element" attempts to provide proper spacing between the elements of a character.
|
||||
# (The keyer always provides proper spacing between elements).
|
||||
# "Letter" turns on letter spacing. This attempts to provide proper spacing between characters.
|
||||
#keyer_space = "Element"
|
||||
#keyer_space = "Letter"
|
||||
|
||||
## keyer_weight Keyer weight, integer choice
|
||||
# A weight of 50% means that the dit time equals the inter-element time, which is standard.
|
||||
# Reducing the weight increases the inter-element time, increasing the weight reduces it.
|
||||
#keyer_weight = 50
|
||||
#keyer_weight = 25
|
||||
#keyer_weight = 75
|
||||
|
||||
## keyer_paddle Keyer paddle, text choice
|
||||
# "Normal" means the left paddle is DIT. "Reverse" means the left paddle is DAH.
|
||||
#keyer_paddle = "Normal"
|
||||
#keyer_paddle = "Reverse"
|
||||
|
||||
import configure
|
||||
import softrock
|
||||
from softrock.hardware_usb import Hardware as BaseHardware
|
||||
from softrock.hardware_usb import IN, OUT, UBYTE2, UBYTE4
|
||||
import _quisk as QS
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
class Hardware(BaseHardware):
|
||||
def __init__(self, app, conf):
|
||||
# Set constant parameters here:
|
||||
conf.usb_vendor_id = 0x16c0
|
||||
conf.usb_product_id = 0x05dc
|
||||
conf.si570_i2c_address = 0x55
|
||||
conf.si570_direct_control = False
|
||||
conf.si570_xtal_freq = 114285000
|
||||
conf.repeater_delay = 0.25
|
||||
self.ptt_count = 200
|
||||
self.ptt_on = 0
|
||||
BaseHardware.__init__(self, app, conf)
|
||||
QS.set_sparams(multus_cw_samples=1)
|
||||
self.use_softrock = True
|
||||
if DEBUG:
|
||||
print ("The name of this radio is", configure.Settings[1])
|
||||
print ("The hardware file type is", conf.hardware_file_type)
|
||||
def TransferOut(self, address, message): # message is a bytes array or an integer 0 to 255
|
||||
if isinstance(message, int):
|
||||
message = message.to_bytes(1, 'big')
|
||||
if self.usb_dev:
|
||||
self.usb_dev.ctrl_transfer(OUT, address, self.si570_i2c_address + 0x700, 0, message)
|
||||
if DEBUG:
|
||||
print ("USB send to 0x%X" % address, message)
|
||||
def TransferIn(self, address, length):
|
||||
if self.usb_dev:
|
||||
recv = self.usb_dev.ctrl_transfer(IN, address, self.si570_i2c_address + 0x700, 0, length)
|
||||
if DEBUG:
|
||||
print ("USB receive from 0x%X" % address, recv)
|
||||
return recv
|
||||
return None
|
||||
def open(self):
|
||||
text = BaseHardware.open(self)
|
||||
self.InitKeyer()
|
||||
return text
|
||||
def ChangeMode(self, mode):
|
||||
ret = BaseHardware.ChangeMode(self, mode)
|
||||
if mode in ("CWL", "CWU"):
|
||||
cw_mode = b'C' # Adding "b" does not affect the value sent
|
||||
if DEBUG:
|
||||
print ("Mode is CW")
|
||||
else:
|
||||
cw_mode = b'U'
|
||||
if DEBUG:
|
||||
print ("Mode is not CW")
|
||||
self.TransferOut(0x70, cw_mode)
|
||||
return ret
|
||||
def PollCwKey(self): # Called frequently from the sound thread to check the CW key status
|
||||
return # Quisk is always in Rx
|
||||
def PollGuiControl(self): # Called frequently from the GUI thread
|
||||
self.ptt_count -= 1
|
||||
if self.ptt_count <= 0:
|
||||
self.ptt_count = 200
|
||||
reply = self.TransferIn(0xA5, 1)
|
||||
if DEBUG:
|
||||
print ("PollGuiControl got", reply)
|
||||
if reply:
|
||||
ptt = reply[0] # This is 255 for error
|
||||
if ptt in (0, 1) and ptt != self.ptt_on:
|
||||
self.ptt_on = ptt
|
||||
self.application.pttButton.SetValue(ptt, True)
|
||||
def InitKeyer(self):
|
||||
# Initialize the keyer parameters
|
||||
conf = self.conf
|
||||
if not hasattr(conf, "keyer_speed"):
|
||||
conf.keyer_speed = 18
|
||||
if not hasattr(conf, "keyer_type"):
|
||||
conf.keyer_type = "Straight"
|
||||
if not hasattr(conf, "keyer_space"):
|
||||
conf.keyer_space = "Element"
|
||||
if not hasattr(conf, "keyer_weight"):
|
||||
conf.keyer_weight = 50
|
||||
if not hasattr(conf, "keyer_paddle"):
|
||||
conf.keyer_paddle = "Normal"
|
||||
# We need to initialize in case the persistent hardware values differ from the Quisk values.
|
||||
for name in ("keyer_speed", "keyer_type", "keyer_space", "keyer_weight", "keyer_paddle", "cwTone"):
|
||||
self.ImmediateChange(name)
|
||||
def ImmediateChange(self, name):
|
||||
BaseHardware.ImmediateChange(self, name)
|
||||
value = getattr(self.conf, name)
|
||||
if DEBUG:
|
||||
print ("ImmediateChange", name, value)
|
||||
if name == "keyer_speed":
|
||||
self.TransferOut(0x7B, value)
|
||||
elif name == "keyer_type":
|
||||
if value == "Straight":
|
||||
mode = 0
|
||||
elif value == "Iambic-A":
|
||||
mode = 1
|
||||
elif value == "Iambic-B":
|
||||
mode = 2
|
||||
else:
|
||||
mode = 0
|
||||
self.TransferOut(0x71, mode)
|
||||
elif name == "keyer_space":
|
||||
if value == "Element":
|
||||
spacing = 0
|
||||
elif value == "Letter":
|
||||
spacing = 1
|
||||
else:
|
||||
spacing = 0
|
||||
self.TransferOut(0x75, spacing)
|
||||
elif name == "keyer_weight":
|
||||
weight = int(value)
|
||||
self.TransferOut(0x77, weight)
|
||||
elif name == "keyer_paddle":
|
||||
if value == "Normal":
|
||||
paddle = 0
|
||||
elif value == "Reverse":
|
||||
paddle = 1
|
||||
else:
|
||||
paddle = 0
|
||||
self.TransferOut(0x73, paddle)
|
||||
elif name == "cwTone":
|
||||
if value < 500:
|
||||
tone_index = 0 # 400 Hz
|
||||
elif 500 <= value < 700:
|
||||
tone_index = 1 # 600 Hz
|
||||
elif 700 <= value < 900:
|
||||
tone_index = 2 # 800 Hz
|
||||
else:
|
||||
tone_index = 3 # 1000 Hz
|
||||
self.TransferOut(0x7F, tone_index)
|
||||
|
||||
|
||||
@@ -334,6 +334,7 @@ class HamlibHandlerSerial:
|
||||
self.public_name = public_name # the public name for the serial port
|
||||
self.tune_step = 1000
|
||||
if sys.platform == 'win32' or public_name.startswith("/dev/"):
|
||||
self.port_is_mod_serial = True # self.port is from the serial module
|
||||
try:
|
||||
import serial
|
||||
except:
|
||||
@@ -346,6 +347,7 @@ class HamlibHandlerSerial:
|
||||
print ("The serial port %s could not be opened." % public_name)
|
||||
else:
|
||||
import tty
|
||||
self.port_is_mod_serial = False # self.port is a file descriptor
|
||||
if os.path.lexists(public_name):
|
||||
try:
|
||||
os.remove(public_name)
|
||||
@@ -369,7 +371,7 @@ class HamlibHandlerSerial:
|
||||
def open(self):
|
||||
return
|
||||
def close(self):
|
||||
if sys.platform == 'win32':
|
||||
if self.port_is_mod_serial:
|
||||
if self.port:
|
||||
self.port.close()
|
||||
self.port = None
|
||||
@@ -382,7 +384,7 @@ class HamlibHandlerSerial:
|
||||
def Read(self):
|
||||
if self.port is None:
|
||||
return
|
||||
if sys.platform == 'win32':
|
||||
if self.port_is_mod_serial:
|
||||
text = self.port.read(99)
|
||||
if not isinstance(text, Q3StringTypes):
|
||||
text = text.decode('utf-8')
|
||||
@@ -433,22 +435,14 @@ class HamlibHandlerSerial:
|
||||
return
|
||||
if isinstance(data, Q3StringTypes):
|
||||
data = data.encode('utf-8', errors='ignore')
|
||||
if sys.platform == 'win32':
|
||||
if self.port_is_mod_serial:
|
||||
self.port.write(data)
|
||||
else:
|
||||
r, w, x = select.select((), (self.port,), (), 0)
|
||||
if w:
|
||||
os.write(self.port, data)
|
||||
def set_frequency(self, freq):
|
||||
tune = freq - self.app.VFO
|
||||
d = self.app.sample_rate * 45 // 100
|
||||
if -d <= tune <= d: # Frequency is on-screen
|
||||
vfo = self.app.VFO
|
||||
else: # Change the VFO
|
||||
vfo = (freq // 5000) * 5000 - 5000
|
||||
tune = freq - vfo
|
||||
self.app.BandFromFreq(freq)
|
||||
self.app.ChangeHwFrequency(tune, vfo, 'FreqEntry')
|
||||
def set_frequency(self, freq): # This changes the Rx frequency
|
||||
self.app.ChangeRxTxFrequency(freq, None)
|
||||
if HAMLIB_DEBUG: print("New Freq rx,tx", self.app.txFreq + self.app.VFO, self.app.rxFreq + self.app.VFO)
|
||||
def ZZAC(self, cmd, data, length): # Set or read tune steps
|
||||
if length == 0:
|
||||
@@ -836,6 +830,8 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
|
||||
"""Send text back to the client."""
|
||||
if isinstance(text, Q3StringTypes):
|
||||
text = text.encode('utf-8', errors='ignore')
|
||||
if HAMLIB_DEBUG:
|
||||
print ("Rig2 send", text)
|
||||
try:
|
||||
self.sock.sendall(text)
|
||||
except socket.error:
|
||||
@@ -942,6 +938,8 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
|
||||
else:
|
||||
return 1
|
||||
self.input = self.input.strip() # Here is our command line
|
||||
if HAMLIB_DEBUG:
|
||||
print ("Rig2 received", self.input)
|
||||
while self.input:
|
||||
# Parse the commands and call the appropriate handlers
|
||||
self.params = ''
|
||||
@@ -992,8 +990,12 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
|
||||
if rx:
|
||||
rx = rx[0]
|
||||
self.Reply('Frequency', rx.txFreq + rx.VFO, 0)
|
||||
if HAMLIB_DEBUG:
|
||||
print ("GetFreq rx", rx.txFreq, rx.VFO)
|
||||
else:
|
||||
self.Reply('Frequency', self.app.rxFreq + self.app.VFO, 0)
|
||||
if HAMLIB_DEBUG:
|
||||
print ("GetFreq app", self.app.rxFreq, self.app.txFreq, self.app.VFO)
|
||||
def SetFreq(self): # The Rx frequency
|
||||
freq = self.GetParamNumber()
|
||||
try:
|
||||
@@ -1432,12 +1434,8 @@ class ConfigStatus(wx.ScrolledCanvas):
|
||||
self.MakeRow2("Sample interrupts", self.interrupts, cfile)
|
||||
self.MakeRow2("Microphone or DGT level dB", level, qfile)
|
||||
self.MakeRow2("FFT number of points", self.fft_size, err_msg)
|
||||
if conf.dxClHost: # connection to dx cluster
|
||||
nSpots = len(application.dxCluster.dxSpots)
|
||||
if nSpots > 0:
|
||||
msg = str(nSpots) + ' DX spot' + ('' if nSpots==1 else 's') + ' received from ' + application.dxCluster.getHost()
|
||||
else:
|
||||
msg = "No DX Cluster data from %s" % conf.dxClHost
|
||||
if application.dxCluster:
|
||||
msg = application.dxCluster.dxStatus()
|
||||
self.MakeRow2("FFT number of errors", self.fft_error, msg)
|
||||
else:
|
||||
self.MakeRow2("FFT number of errors", self.fft_error)
|
||||
@@ -2121,7 +2119,7 @@ class GraphScreen(wx.Window):
|
||||
"""For mouse clicks in our display, translate to our screen coordinates."""
|
||||
mouse_x, mouse_y = event.GetPosition()
|
||||
win = event.GetEventObject()
|
||||
if win is not self:
|
||||
if win and win is not self:
|
||||
x, y = win.GetPosition().Get()
|
||||
mouse_x += x
|
||||
mouse_y += y
|
||||
@@ -2218,7 +2216,8 @@ class GraphScreen(wx.Window):
|
||||
self.ChangeHwFrequency(self.txFreq, self.VFO, 'MouseBtn1', event=event, rx_freq=freq)
|
||||
else:
|
||||
self.ChangeHwFrequency(freq, self.VFO, 'MouseBtn1', event=event)
|
||||
self.CaptureMouse()
|
||||
if not self.HasCapture():
|
||||
self.CaptureMouse()
|
||||
def OnLeftUp(self, event):
|
||||
if self.HasCapture():
|
||||
self.ReleaseMouse()
|
||||
@@ -2231,7 +2230,7 @@ class GraphScreen(wx.Window):
|
||||
mouse_x, mouse_y = self.GetMousePosition(event)
|
||||
if wx.GetKeyState(wx.WXK_SHIFT):
|
||||
mouse_x -= self.filter_center * self.data_width / sample_rate
|
||||
if conf.mouse_tune_method: # Mouse motion changes the VFO frequency
|
||||
if False: #conf.mouse_tune_method: # Mouse motion changes the VFO frequency
|
||||
x = (mouse_x - self.mouse_x) # Thanks to VK6JBL
|
||||
self.mouse_x = mouse_x
|
||||
freq = float(x) * sample_rate / self.data_width
|
||||
@@ -2634,7 +2633,7 @@ class WaterfallScreen(wx.SplitterWindow):
|
||||
self.y_zero = conf.waterfall_y_zero
|
||||
self.zoom_control = 0
|
||||
wx.SplitterWindow.__init__(self, frame)
|
||||
self.SetSizeHints(width, -1, width)
|
||||
self.SetSizeHints(width, -1)
|
||||
self.SetSashGravity(0.50)
|
||||
self.SetMinimumPaneSize(1)
|
||||
self.SetSize((width, conf.waterfall_graph_size + 100)) # be able to set sash size
|
||||
@@ -2988,7 +2987,7 @@ class MultiReceiverScreen(wx.SplitterWindow):
|
||||
self.rx_zero = self.graph
|
||||
self.Initialize(self.rx_zero)
|
||||
self.waterfall.Hide()
|
||||
self.SetSizeHints(self.width, -1, self.width)
|
||||
self.SetSizeHints(self.width, -1)
|
||||
# Calculate control width
|
||||
rx_btn = QuiskPushbutton(self, None, "Rx 8....", style=wx.BU_EXACTFIT)
|
||||
self.rx_btn_width, self.rx_btn_height = rx_btn.GetSize().Get()
|
||||
@@ -3521,6 +3520,7 @@ class App(wx.App):
|
||||
if not os.path.isdir(self.QuiskFilesDir):
|
||||
self.QuiskFilesDir = DefaultConfigDir
|
||||
self.std_out_err = StdOutput(self)
|
||||
self.std_out_err.Logfile("WxPython " + wx.version())
|
||||
QS.set_params(quisk_is_vna=0) # We are not the VNA program
|
||||
# Read in configuration from the selected radio
|
||||
self.BandPlan = []
|
||||
@@ -3611,6 +3611,7 @@ class App(wx.App):
|
||||
setattr(conf, 'widgets_file_name', '')
|
||||
Hardware = self.Hardware
|
||||
self.use_fast_heart_beat = hasattr(Hardware, "FastHeartBeat")
|
||||
self.poll_gui_control = hasattr(Hardware, "PollGuiControl")
|
||||
# Initialization - may be over-written by persistent state
|
||||
self.local_conf.Initialize()
|
||||
self.clip_time0 = 0 # timer to display a CLIP message on ADC overflow
|
||||
@@ -3821,16 +3822,16 @@ class App(wx.App):
|
||||
self.std_out_err.Show(False)
|
||||
else:
|
||||
self.std_out_err.Show(True)
|
||||
#w, h = frame.GetSize().Get()
|
||||
#ww, hh = frame.GetClientSizeTuple()
|
||||
#print ('Main frame: size', w, h, 'client', ww, hh)
|
||||
wClient, hClient = self.main_frame.GetClientSize()
|
||||
wFrame, hFrame = self.main_frame.GetSize()
|
||||
main_border_w = wFrame - wClient
|
||||
#print ('Main frame: size', wFrame, hFrame, 'client', wClient, hClient, 'border', main_border_w)
|
||||
# Find the data width from a list of preferred sizes; it is the width of returned graph data.
|
||||
# The graph_width is the width of data_width that is displayed.
|
||||
# The total width of the graph is graph_width + self.graph_borders
|
||||
if conf.window_width > 0:
|
||||
#wFrame, h = frame.GetClientSize().Get() # client window width
|
||||
wFrame = conf.window_width - 15 # Thanks to Nick Abramenko, RA3PKJ
|
||||
graph = GraphScreen(frame, self.width//2, self.width//2, None) # make a GraphScreen to calculate borders
|
||||
self.graph_width = wFrame - (graph.width - graph.graph_width) # less graph borders equals actual graph_width
|
||||
self.graph_width = wClient - (graph.width - graph.graph_width) # less graph borders equals actual graph_width
|
||||
graph.Destroy()
|
||||
del graph
|
||||
if self.graph_width % 2 == 1: # Both data_width and graph_width are even numbers
|
||||
@@ -3955,14 +3956,14 @@ class App(wx.App):
|
||||
frame.SetSizer(vertBox)
|
||||
# Add the screens
|
||||
vertBox.Add(self.config_screen, 1, wx.EXPAND)
|
||||
vertBox.Add(self.multi_rx_screen, 1)
|
||||
vertBox.Add(self.scope, 1)
|
||||
vertBox.Add(self.bandscope_screen, 1)
|
||||
vertBox.Add(self.filter_screen, 1)
|
||||
vertBox.Add(self.multi_rx_screen, 1, wx.EXPAND)
|
||||
vertBox.Add(self.scope, 1, wx.EXPAND)
|
||||
vertBox.Add(self.bandscope_screen, 1, wx.EXPAND)
|
||||
vertBox.Add(self.filter_screen, 1, wx.EXPAND)
|
||||
if self.rate_audio_fft:
|
||||
vertBox.Add(self.audio_fft_screen, 1)
|
||||
vertBox.Add(self.help_screen, 1)
|
||||
vertBox.Add(self.station_screen)
|
||||
vertBox.Add(self.audio_fft_screen, 1, wx.EXPAND)
|
||||
vertBox.Add(self.help_screen, 1, wx.EXPAND)
|
||||
vertBox.Add(self.station_screen, 0, wx.EXPAND)
|
||||
# Add the spacer
|
||||
vertBox.Add(Spacer(frame), 0, wx.EXPAND)
|
||||
# Add the sizer for the controls
|
||||
@@ -3975,13 +3976,16 @@ class App(wx.App):
|
||||
vertBox.AddSpacer(5) # Thanks to Christof, DJ4CM
|
||||
# End of vertical box.
|
||||
self.MakeButtons(frame, gbs)
|
||||
minw = width = self.graph.width
|
||||
maxw = maxh = -1
|
||||
minh = 100
|
||||
if conf.window_width > 0:
|
||||
minw = width = maxw = conf.window_width
|
||||
minw = maxw = conf.window_width
|
||||
else:
|
||||
minw = self.graph.width + main_border_w
|
||||
maxw = -1
|
||||
if conf.window_height > 0:
|
||||
minh = maxh = self.height = conf.window_height
|
||||
else:
|
||||
minh = 100
|
||||
maxh = -1
|
||||
self.main_frame.SetSizeHints(minw, minh, maxw, maxh)
|
||||
self.main_frame.SetClientSize(wx.Size(width, self.height))
|
||||
if hasattr(Hardware, 'pre_open'): # pre_open() is called before open()
|
||||
@@ -4023,7 +4027,6 @@ class App(wx.App):
|
||||
if conf.dxClHost:
|
||||
# create DX Cluster and register listener for change notification
|
||||
self.dxCluster = dxcluster.DxCluster()
|
||||
self.dxCluster.setListener(self.OnDxClChange)
|
||||
self.dxCluster.start()
|
||||
# Create shortcut keys for buttons
|
||||
if conf.button_layout == 'Large screen':
|
||||
@@ -4374,8 +4377,6 @@ class App(wx.App):
|
||||
conf.tx_ip, conf.tx_audio_port,
|
||||
conf.mic_sample_rate, conf.mic_channel_I, conf.mic_channel_Q,
|
||||
0.7, conf.mic_playback_rate)
|
||||
def OnDxClChange(self):
|
||||
self.station_screen.Refresh()
|
||||
def OnIdle(self, event):
|
||||
if self.screen:
|
||||
self.screen.OnIdle(event)
|
||||
@@ -4706,7 +4707,7 @@ class App(wx.App):
|
||||
b.char_shortcut = 'l'
|
||||
self.MakeAccel(b)
|
||||
self.splitButton = WrapMenu(b, self.split_menu)
|
||||
if conf.mouse_tune_method: # Mouse motion changes the VFO frequency
|
||||
if False: #conf.mouse_tune_method: # Mouse motion changes the VFO frequency
|
||||
self.splitButton.Enable(False)
|
||||
left_row3.append(self.splitButton)
|
||||
b = QuiskCheckbutton(frame, self.OnBtnFDX, 'FDX', color=conf.color_test)
|
||||
@@ -5873,16 +5874,31 @@ class App(wx.App):
|
||||
The hardware will reply with the updated frequencies which may be different
|
||||
from those requested; use and display the returned tune and vfo.
|
||||
"""
|
||||
#print ("ChangeHW", source, tune, vfo, vfo + tune)
|
||||
if not self.split_rxtx:
|
||||
self.rxFreq = tune # rxFreq must be correct before the call to Hardware.ChangeFrequency()
|
||||
elif rx_freq is not None:
|
||||
self.rxFreq = rx_freq
|
||||
rx_offset = self.rxFreq - tune
|
||||
if self.screen == self.bandscope_screen:
|
||||
freq = vfo + tune
|
||||
tune = freq % 10000
|
||||
vfo = freq - tune
|
||||
tune, vfo = Hardware.ChangeFrequency(vfo + tune, vfo, source, band, event)
|
||||
self.ChangeDisplayFrequency(tune - vfo, vfo, rx_freq is not None)
|
||||
tune, vfo = Hardware.ChangeFrequency(vfo + tune, vfo, source, band, event)
|
||||
self.ChangeDisplayFrequency(tune - vfo, vfo, rx_freq is not None)
|
||||
elif conf.fixed_tune_offset:
|
||||
# Tune with the VFO and keep a constant audio offset from the center. Tx tune is always conf.fixed_tune_offset.
|
||||
tune_freq = vfo + tune
|
||||
self.rxFreq = conf.fixed_tune_offset + rx_offset
|
||||
if source == "BtnUpDown":
|
||||
tune_freq = vfo + conf.fixed_tune_offset
|
||||
else:
|
||||
vfo = tune_freq - conf.fixed_tune_offset
|
||||
tune_freq, vfo = Hardware.ChangeFrequency(tune_freq, vfo, source, band, event)
|
||||
self.ChangeDisplayFrequency(tune_freq - vfo, vfo, True)
|
||||
else:
|
||||
tune, vfo = Hardware.ChangeFrequency(vfo + tune, vfo, source, band, event)
|
||||
self.ChangeDisplayFrequency(tune - vfo, vfo, rx_freq is not None)
|
||||
def ChangeDisplayFrequency(self, tune, vfo, new_rxfreq=False):
|
||||
'Change the frequency displayed by Quisk'
|
||||
change = 0
|
||||
@@ -6379,7 +6395,7 @@ class App(wx.App):
|
||||
for client in self.hamlib_clients: # Service existing clients
|
||||
if not client.Process(): # False return indicates a closed connection; remove the handler for this client
|
||||
self.hamlib_clients.remove(client)
|
||||
# print 'Remove', client.address
|
||||
# print ('Remove', client.address)
|
||||
break
|
||||
def OnKeyHook(self, event):
|
||||
event.Skip()
|
||||
@@ -6435,6 +6451,8 @@ class App(wx.App):
|
||||
self.hamlib_com2_handler.Process()
|
||||
if self.use_fast_heart_beat:
|
||||
Hardware.FastHeartBeat()
|
||||
if self.poll_gui_control:
|
||||
Hardware.PollGuiControl()
|
||||
if conf.do_repeater_offset:
|
||||
hold = QS.tx_hold_state(-1)
|
||||
if hold == 2: # Tx is being held for an FM repeater TX frequency shift
|
||||
@@ -6615,6 +6633,9 @@ class App(wx.App):
|
||||
self.ChangeDisplayFrequency(tune - vfo, vfo)
|
||||
self.FldigiPoll()
|
||||
self.HamlibPoll()
|
||||
if self.dxCluster:
|
||||
if self.dxCluster.Poll():
|
||||
self.station_screen.Refresh()
|
||||
if self.timer - self.slowheart_time0 > 0.5:
|
||||
self.slowheart_time0 = self.timer
|
||||
if self.w_phase:
|
||||
|
||||
@@ -1287,6 +1287,18 @@ dc_remove_bw = 100
|
||||
#dc_remove_bw = 200
|
||||
#dc_remove_bw = 400
|
||||
|
||||
## fixed_tune_offset Fixed tune offset, integer
|
||||
# If this is zero, Quisk tunes as usual. The hardware frequency (the VFO or center frequency)
|
||||
# is fixed and Quisk tunes
|
||||
# within the spectrum returned by the hardware. Otherwise enter a fixed offset between the
|
||||
# center frequency and the tuning frequency. Then tuning is done by the hardware.
|
||||
# This is useful with Softrock hardware as
|
||||
# it simplifies the corrections for amplitude and phase imbalance.
|
||||
# Changes are immediate (no need to restart).
|
||||
fixed_tune_offset = 0
|
||||
#fixed_tune_offset = 5000
|
||||
#fixed_tune_offset = 10000
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -29,9 +29,10 @@ class Hardware(BaseHardware):
|
||||
# self.hamlib_rigctld_port = 4532 # Standard rigctld control port
|
||||
# self.hamlib_poll_seconds = 0.2 # Time interval to poll for changes
|
||||
def open(self):
|
||||
# Thanks to Peter, DC6AR, Rig ID changed from 229 to 2045.
|
||||
ret = BaseHardware.open(self)
|
||||
if not self.hamlib_connected: # rigctld is not started. Try to start it.
|
||||
os.system("rigctld -m 229 -r /dev/ttyUSB0 -s 4800 & ") # Check the baud rate menu setting
|
||||
os.system("rigctld -m 2045 -r /dev/ttyUSB0 -s 4800 & ") # Check the baud rate menu setting
|
||||
# If this fails, start rigctld by hand.
|
||||
return ret
|
||||
|
||||
|
||||
@@ -120,6 +120,8 @@ class Hardware:
|
||||
self.application.bandscope_clock = clock
|
||||
#def PollCwKey(self): # Optional. Called frequently by the sound thread to check the CW key status.
|
||||
# pass # Do not define if not needed.
|
||||
#def PollGuiControl(self): # Optional. Called frequently by the GUI thread to change GUI settings (PTT etc.)
|
||||
# pass # Do not define if not needed.
|
||||
def StartSamples(self): # Quisk calls this from the sound thread to start sending samples.
|
||||
# If you return a string, it replaces the string returned from hardware open()
|
||||
pass
|
||||
|
||||
@@ -7,7 +7,7 @@ import os
|
||||
# You must define the version here. A title string including
|
||||
# the version will be written to __init__.py and read by quisk.py.
|
||||
|
||||
Version = '4.2.36'
|
||||
Version = '4.2.40'
|
||||
|
||||
fp = open("__init__.py", "w") # write title string
|
||||
fp.write("#Quisk version %s\n" % Version)
|
||||
@@ -85,7 +85,7 @@ N1MM+ and software that uses Hamlib.
|
||||
url = 'http://james.ahlstrom.name/quisk/',
|
||||
packages = ['quisk', 'quisk.n2adr', 'quisk.softrock', 'quisk.freedvpkg',
|
||||
'quisk.hermes', 'quisk.hiqsdr', 'quisk.afedrinet', 'quisk.soapypkg',
|
||||
'quisk.sdrmicronpkg', 'quisk.perseuspkg', 'quisk.ac2yd'],
|
||||
'quisk.sdrmicronpkg', 'quisk.perseuspkg', 'quisk.ac2yd', 'quisk.multuspkg'],
|
||||
package_dir = {'quisk' : '.'},
|
||||
package_data = {'' : ['*.txt', '*.html', '*.so', '*.dll']},
|
||||
entry_points = {'gui_scripts' : ['quisk = quisk.quisk:main', 'quisk_vna = quisk.quisk_vna:main']},
|
||||
|
||||
@@ -178,6 +178,8 @@ class Hardware(BaseHardware):
|
||||
vfo = self.vfo + int(offset * 1000) # Convert kHz to Hz
|
||||
self.ChangeFrequency(vfo, vfo, 'repeater')
|
||||
return False
|
||||
def ImmediateChange(self, name):
|
||||
return
|
||||
def OnSpot(self, level):
|
||||
pass
|
||||
def HeartBeat(self): # Called at about 10 Hz by the main
|
||||
|
||||
Reference in New Issue
Block a user