Quisk version 4.2.28

This commit is contained in:
James C. Ahlstrom
2023-12-26 15:39:37 -05:00
commit ffa047c2ef
143 changed files with 52434 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
#
+13
View File
@@ -0,0 +1,13 @@
from n2adr.quisk_conf import n2adr_sound_pc_capt, n2adr_sound_pc_play, n2adr_sound_usb_play, n2adr_sound_usb_mic, favorites_file_path
settings_file_path = "../quisk_settings.json"
name_of_sound_play = n2adr_sound_usb_play
microphone_name = n2adr_sound_usb_mic
digital_input_name = ""
digital_output_name = ""
use_rx_udp = 1 # Get ADC samples from UDP
rx_udp_ip = "192.168.1.196" # Sample source IP address
rx_udp_port = 0xBC77 # Sample source UDP port
graph_width = 0.80
+20
View File
@@ -0,0 +1,20 @@
# This is a second config file that I use to test various hardware configurations.
from n2adr.quisk_conf import n2adr_sound_pc_capt, n2adr_sound_pc_play, n2adr_sound_usb_play, n2adr_sound_usb_mic
from n2adr.quisk_conf import latency_millisecs, data_poll_usec, favorites_file_path
settings_file_path = "../quisk_settings.json"
name_of_sound_play = n2adr_sound_usb_play
name_of_sound_capt = n2adr_sound_pc_capt
sdriq_name = "/dev/ttyUSB0" # Name of the SDR-IQ device to open
default_screen = 'WFall'
waterfall_y_scale = 80
waterfall_y_zero = 40
waterfall_graph_y_scale = 40
waterfall_graph_y_zero = 90
waterfall_graph_size = 160
display_fraction = 1.00 # The edges of the full bandwidth are not valid
+13
View File
@@ -0,0 +1,13 @@
from n2adr.quisk_conf import n2adr_sound_pc_capt, n2adr_sound_pc_play, n2adr_sound_usb_play, n2adr_sound_usb_mic, favorites_file_path
name_of_sound_play = n2adr_sound_usb_play
microphone_name = n2adr_sound_usb_mic
settings_file_path = "../quisk_settings.json"
rx_udp_clock = 73728000 - 102
rx_udp_ip = "192.168.1.213" # Sample source IP address "" for DHCP
do_repeater_offset = True
#bandTransverterOffset = {'10' : 300000}
spot_button_keys_tx = True
+5
View File
@@ -0,0 +1,5 @@
from n2adr import conf3
settings_file_path = "../quisk_settings.json"
microphone_name = ""
+40
View File
@@ -0,0 +1,40 @@
# This is a config file to test the microphone by sending microphone Tx playback to the audio out.
# Set the frequency to zero, and press FDX and PTT.
# Set these values for DEBUG_MIC in sound.c:
# 0: Normal FFT.
# 1: Send filtered Tx audio to the FFT.
# 2: Send mic playback to the FFT.
# 3: Send unfiltered mono mic audio to the FFT.
import sys
from quisk_hardware_model import Hardware as BaseHardware
import _quisk as QS
from n2adr.quisk_conf import n2adr_sound_pc_capt, n2adr_sound_pc_play, n2adr_sound_usb_play, n2adr_sound_usb_mic
from n2adr.quisk_conf import latency_millisecs, data_poll_usec, favorites_file_path
from n2adr.quisk_conf import mixer_settings
settings_file_path = "../quisk_settings.json"
name_of_sound_capt = n2adr_sound_pc_capt
name_of_sound_play = ''
microphone_name = n2adr_sound_usb_mic
name_of_mic_play = n2adr_sound_usb_play
graph_y_scale = 160
mic_sample_rate = 48000
sample_rate = 48000
mic_playback_rate = 48000
mic_out_volume = 0.6
add_fdx_button = 1
class Hardware(BaseHardware):
def __init__(self, app, conf):
BaseHardware.__init__(self, app, conf)
def OnButtonPTT(self, event):
if event.GetEventObject().GetValue():
QS.set_key_down(1)
else:
QS.set_key_down(0)
+15
View File
@@ -0,0 +1,15 @@
import sys
settings_file_path = "../quisk_settings.json"
#hamlib_port = 4575 # Standard port for Quisk control. Set the port in Hamlib to 4575 too.
hamlib_port = 4532 # Default port for rig 2. Use this if you can not set the Hamlib port.
if sys.platform == "win32":
pass
elif 0:
digital_input_name = 'pulse'
digital_output_name =''
else:
digital_input_name = 'hw:Loopback,0'
digital_output_name = digital_input_name
+30
View File
@@ -0,0 +1,30 @@
# This is a second config file to test the softrock radios.
from n2adr.quisk_conf import n2adr_sound_pc_capt, n2adr_sound_pc_play, n2adr_sound_usb_play, n2adr_sound_usb_mic
from n2adr.quisk_conf import latency_millisecs, data_poll_usec, favorites_file_path
from n2adr.quisk_conf import mixer_settings
settings_file_path = "../quisk_settings.json"
name_of_sound_capt = n2adr_sound_pc_capt
name_of_sound_play = n2adr_sound_usb_play
default_screen = 'WFall'
waterfall_y_scale = 80
waterfall_y_zero = 40
waterfall_graph_y_scale = 40
waterfall_graph_y_zero = 90
waterfall_graph_size = 160
display_fraction = 1.00
sample_rate = 48000
playback_rate = 48000
do_repeater_offset = True
#bandTransverterOffset = {'40' : 300000}
# Microphone capture and playback:
microphone_name = n2adr_sound_usb_mic
name_of_mic_play = n2adr_sound_pc_play
mic_playback_rate = sample_rate
mic_out_volume = 0.6
+14
View File
@@ -0,0 +1,14 @@
import sys
settings_file_path = "../quisk_settings.json"
if sys.platform == "win32":
digital_output_name = 'CABLE-A Input'
elif 0:
digital_input_name = 'pulse'
digital_output_name =''
else:
digital_output_name = 'hw:Loopback,0'
name_of_sound_play = ''
microphone_name = ''
+72
View File
@@ -0,0 +1,72 @@
# This is the hardware control file for my shack.
# It is for the Hermes-Lite2 5 watt output which uses only the antenna tuner.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
from hermes.quisk_hardware import Hardware as BaseHw
from n2adr import station_hardware
class Hardware(BaseHw):
def __init__(self, app, conf):
BaseHw.__init__(self, app, conf)
self.GUI = None
self.vfo_frequency = 0 # current vfo frequency
# Other hardware
self.anttuner = station_hardware.AntennaTuner(app, conf) # Control the antenna tuner
self.controlbox = station_hardware.ControlBox(app, conf) # Control my Station Control Box
self.v2filter = station_hardware.FilterBoxV2(app, conf) # Control V2 filter box
def open(self):
if False:
from n2adr.station_hardware import StationControlGUI
self.GUI = StationControlGUI(self.application.main_frame, self, self.application, self.conf)
self.GUI.Show()
self.anttuner.open()
return BaseHw.open(self)
def close(self):
self.anttuner.close()
self.controlbox.close()
return BaseHw.close(self)
def ChangeFilterFrequency(self, tx_freq):
if tx_freq and tx_freq > 0:
if self.GUI:
self.GUI.SetTxFreq(tx_freq)
self.GUI.freq_entry.ChangeValue("%.3f" % (tx_freq * 1E-6))
else:
self.anttuner.SetTxFreq(tx_freq)
self.v2filter.SetTxFreq(tx_freq)
def ChangeFrequency(self, tx_freq, vfo_freq, source='', band='', event=None):
self.ChangeFilterFrequency(tx_freq)
return BaseHw.ChangeFrequency(self, tx_freq, vfo_freq, source, band, event)
def ChangeBand(self, band):
# band is a string: "60", "40", "WWV", etc.
ret = BaseHw.ChangeBand(self, band)
self.anttuner.ChangeBand(band)
#self.lpfilter.ChangeBand(band)
#self.hpfilter.ChangeBand(band)
self.v2filter.ChangeBand(band)
self.CorrectSmeter()
return ret
def HeartBeat(self): # Called at about 10 Hz by the main
self.anttuner.HeartBeat()
self.v2filter.HeartBeat()
self.controlbox.HeartBeat()
return BaseHw.HeartBeat(self)
def OnSpot(self, level):
# level is -1 for Spot button Off; else the Spot level 0 to 1000.
self.anttuner.OnSpot(level)
return BaseHw.OnSpot(self, level)
def OnButtonRfGain(self, event):
self.v2filter.OnButtonRfGain(event)
self.CorrectSmeter()
def CorrectSmeter(self): # S-meter correction can change with band or RF gain
return
if self.band == '40': # Basic S-meter correction by band
self.correct_smeter = 20.5
else:
self.correct_smeter = 20.5
#self.correct_smeter -= self.rf_gain / 6.0 # Correct S-meter for RF gain
#self.application.waterfall.ChangeRfGain(self.rf_gain) # Waterfall colors are constant
def OnButtonPTT(self, event):
self.controlbox.OnButtonPTT(event)
return BaseHw.OnButtonPTT(self, event)
+47
View File
@@ -0,0 +1,47 @@
# This is the config file from my shack, which controls various hardware.
# The files to control my 2010 transceiver and for the improved version HiQSDR
# are in the package directory HiQSDR.
import sys
sample_rate = 48000
if sys.platform == 'win32':
favorites_file_path = "C:/pub/quisk_favorites.txt"
settings_file_path = "C:/pub/quisk_settings.json"
name_of_sound_play = ""
name_of_sound_capt = "Primary"
microphone_name = ""
name_of_mic_play = ""
else:
favorites_file_path = "/home/jim/pub/quisk_favorites.txt"
settings_file_path = "/home/jim/pub/quisk_settings.json"
name_of_sound_play = ""
name_of_sound_capt = "hw:0"
microphone_name = ""
name_of_mic_play = ""
#lin_microphone_name = "ALC1150 Analog"
#lin_name_of_mic_play = "USB Sound Device"
# These are for CM106 like sound device
#n2adr_sound_usb_mic = 'alsa:USB Sound Device'
#microphone_name = n2adr_sound_usb_mic
#mixer_settings = [
# (microphone_name, 16, 1), # PCM capture from line
# (microphone_name, 14, 0), # PCM capture switch
# (microphone_name, 11, 1), # line capture switch
# (microphone_name, 12, 0.70), # line capture volume
# (microphone_name, 3, 0), # mic playback switch
# (microphone_name, 9, 0), # mic capture switch
# ]
# These are for Asus internal sound
n2adr_sound_usb_mic = 'alsa:ALC1150 Analog'
microphone_name = n2adr_sound_usb_mic
mixer_settings = [
(microphone_name, 19, 2), # PCM capture from line
(microphone_name, 24, 0), # PCM capture switch
(microphone_name, 22, 1), # line capture switch
(microphone_name, 27, 0), # line capture volume boost
(microphone_name, 21, 0.70), # line capture volume
]
+202
View File
@@ -0,0 +1,202 @@
# These are the configuration parameters for receiving the
# 10.7 MHz IF output of the AOR AR8600 receiver with my
# transceiver. This results in a 100 kHz to 3 GHz
# wide range receiver with pan adapter.
#
# Due to noise starting at 11.18 MHz when tuned to 449.0 MHz, we tune to 10.5 MHz center.
#
# Note: The AR8600 IF output in WFM mode seems to tune in 10kHz increments
# no matter what the step size, even though the display reads a
# different frequency.
# The AR8600 inverts the spectrum of these bands: 10, 2, 220, 440, 900
# The AR8600 does not invert these bands: 1240
# The change from inverted to non-inverted is about 1040 MHz.
# Please do not change this sample file.
# Instead copy it to your own .quisk_conf.py and make changes there.
# See quisk_conf_defaults.py for more information.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import time, traceback, os
import _quisk as QS
import serial # From the pyserial package
from n2adr.quisk_conf import *
from n2adr import scanner_widgets as quisk_widgets
settings_file_path = "../quisk_settings.json"
bandLabels = [ ('60',) * 5, '40', '20',
'15', '12', '10', '2', '220', '440', '900', '1240', ('Time',) * len(bandTime)]
# Define the Hardware class in this config file instead of a separate file.
from hiqsdr.quisk_hardware import Hardware as BaseHardware
class Hardware(BaseHardware):
def __init__(self, app, conf):
BaseHardware.__init__(self, app, conf)
self.ar8600_frequency = 0 # current AR8600 tuning frequency
self.hware_frequency = 0 # current hardware VFO frequency
self.vfo_frequency = 0 # current Quisk VFO frequency
self.invert = 1 # The frequency spectrum is backwards
self.serial = None # the open serial port
self.timer = 0.02 # time between AR8600 commands in seconds
self.time0 = 0 # time of last AR8600 command
self.serial_out = [] # send commands slowly
self.offset = 10700000 # frequency offset from AR8600 tuning freq to IF output
self.tx_freq = 0 # current frequency
conf.BandEdge['220'] = (222000000, 225000000)
conf.BandEdge['440'] = (420000000, 450000000)
conf.BandEdge['900'] = (902000000, 928000000)
conf.BandEdge['1240'] = (1240000000, 1300000000)
rpt_file = os.path.normpath(os.path.join(os.getcwd(), '..'))
rpt_file = os.path.join(rpt_file, 'MetroCor.txt')
fp = open(rpt_file, 'r')
self.repeaters = {}
for line in fp:
line = line.strip()
if line and line[0] != '#':
line = line.split('\t')
fout = int(float(line[0]) * 1000000 + 0.1)
text = "%s %s, %s" % (line[2], line[3], line[5])
if fout in self.repeaters:
self.repeaters[fout] = "%s ; %s" % (self.repeaters[fout], text)
else:
self.repeaters[fout] = text
fp.close()
rpt_file = os.path.normpath(os.path.join(os.getcwd(), '..'))
rpt_file = os.path.join(rpt_file, 'ARCC.csv')
fp = open(rpt_file, 'r')
for line in fp:
line = line.strip()
if line and line[0] != '#':
line = line.split(',')
fout = float(line[3])
if fout >= 2000.0:
continue
fout = int(fout * 1000000 + 0.1)
text = "%s %s, %s" % (line[5], line[2], line[0])
if fout in self.repeaters:
self.repeaters[fout] = "%s ; %s" % (self.repeaters[fout], text)
else:
self.repeaters[fout] = text
fp.close()
rpt_file = os.path.normpath(os.path.join(os.getcwd(), '..'))
rpt_file = os.path.join(rpt_file, 'Repeaters.csv')
fp = open(rpt_file, 'r')
for line in fp:
line = line.strip()
if line and line[0] != '#':
line = line.split(',')
fout = float(line[3])
if fout >= 2000.0:
continue
fout = int(fout * 1000000 + 0.1)
if line[0]:
text = "%s %s, %s" % (line[5], line[2], line[0])
else:
text = line[5]
if fout in self.repeaters:
self.repeaters[fout] = "%s ; %s" % (self.repeaters[fout], text)
else:
self.repeaters[fout] = text
fp.close()
for freq, text in list(self.repeaters.items()):
if len(text) > 80:
t =''
stations = text.split(';')
for s in stations:
s = s.strip()
t = t + s.split()[0] + ' ' + s.split(',')[1] + '; '
self.repeaters[freq] = t
self.rpt_freq_list = list(self.repeaters)
self.rpt_freq_list.sort()
def OpenPort(self):
if sys.platform == "win32":
tty_list = ("COM7", "COM8")
else:
tty_list = ("/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2")
for tty_name in tty_list:
try:
port = serial.Serial(port=tty_name, baudrate=9600,
stopbits=serial.STOPBITS_TWO, xonxoff=1, timeout=0)
except:
#traceback.print_exc()
pass
else:
time.sleep(0.1)
for i in range(3):
port.write('VR\r')
time.sleep(0.1)
chars = port.read(1024)
if "VR0101" in chars:
self.serial = port
port.write('MD0\r') # set WFM mode so the IF output is available
break
if self.serial:
break
else:
port.close()
def open(self):
self.OpenPort()
QS.invert_spectrum(self.invert)
t = BaseHardware.open(self) # save the message
return t
def close(self):
BaseHardware.close(self)
if self.serial:
self.serial.write('EX\r')
time.sleep(1) # wait for output to drain, but don't block
self.serial.close()
self.serial = None
def ChangeFrequency(self, tx_freq, vfo_freq, source='', band='', event=None):
self.tx_freq = tx_freq
try:
rpt = self.repeaters[tx_freq]
except KeyError:
self.application.bottom_widgets.UpdateText('')
else:
self.application.bottom_widgets.UpdateText(rpt)
if vfo_freq != self.vfo_frequency and vfo_freq >= 10000:
self.vfo_frequency = vfo_freq
# Calculate new AR8600 and hardware frequencies
ar8600 = (vfo_freq + 50000) // 100000 * 100000 - 200000
if self.ar8600_frequency != ar8600:
self.ar8600_frequency = ar8600
self.SendAR8600('RF%010d\r' % ar8600)
if ar8600 < 1040000000:
self.invert = 1
else:
self.invert = 0
QS.invert_spectrum(self.invert)
if self.invert:
hware = self.offset - vfo_freq + self.ar8600_frequency
else:
hware = self.offset + vfo_freq - self.ar8600_frequency
if self.hware_frequency != hware:
self.hware_frequency = hware
BaseHardware.ChangeFrequency(self, 0, hware)
#print 'AR8600 Hware', self.ar8600_frequency, self.hware_frequency
return tx_freq, vfo_freq
def SendAR8600(self, msg): # Send commands to the AR8600, but not too fast
if self.serial:
if time.time() - self.time0 > self.timer:
self.serial.write(msg) # send message now
self.time0 = time.time()
else:
self.serial_out.append(msg) # send message later
def HeartBeat(self): # Called at about 10 Hz by the main
BaseHardware.HeartBeat(self)
if self.serial:
chars = self.serial.read(1024)
#if chars:
# print chars
if self.serial_out and time.time() - self.time0 > self.timer:
self.serial.write(self.serial_out[0])
self.time0 = time.time()
del self.serial_out[0]
+80
View File
@@ -0,0 +1,80 @@
# This is the hardware file from my shack, which controls various hardware.
# The files to control my 2010 transceiver and for the improved version HiQSDR
# are in the package directory HiQSDR.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
from hiqsdr.quisk_hardware import Hardware as BaseHw
from n2adr import station_hardware
class Hardware(BaseHw):
def __init__(self, app, conf):
BaseHw.__init__(self, app, conf)
self.GUI = None
self.vfo_frequency = 0 # current vfo frequency
self.rf_gain_labels = ('RF 0 dB', 'RF +16')
self.rf_gain = 0 # Preamp or attenuation in dB; changed via app.Hardware
# Other hardware
self.anttuner = station_hardware.AntennaTuner(app, conf) # Control the antenna tuner
#self.lpfilter = station_hardware.LowPassFilter(app, conf) # Control LP filter box
#self.hpfilter = station_hardware.HighPassFilter(app, conf) # Control HP filter box
self.controlbox = station_hardware.ControlBox(app, conf) # Control my Station Control Box
self.v2filter = station_hardware.FilterBoxV2(app, conf) # Control V2 filter box
def open(self):
if False:
from n2adr.station_hardware import StationControlGUI
self.GUI = StationControlGUI(self.application.main_frame, self, self.application, self.conf)
self.GUI.Show()
self.anttuner.open()
return BaseHw.open(self)
def close(self):
self.anttuner.close()
self.controlbox.close()
return BaseHw.close(self)
def ChangeFilterFrequency(self, tx_freq):
if tx_freq and tx_freq > 0:
if self.GUI:
self.GUI.SetTxFreq(tx_freq)
self.GUI.freq_entry.ChangeValue("%.3f" % (tx_freq * 1E-6))
else:
self.anttuner.SetTxFreq(tx_freq)
self.v2filter.SetTxFreq(tx_freq)
def ChangeFrequency(self, tx_freq, vfo_freq, source='', band='', event=None):
self.ChangeFilterFrequency(tx_freq)
return BaseHw.ChangeFrequency(self, tx_freq, vfo_freq, source, band, event)
def ChangeBand(self, band):
# band is a string: "60", "40", "WWV", etc.
ret = BaseHw.ChangeBand(self, band)
self.anttuner.ChangeBand(band)
#self.lpfilter.ChangeBand(band)
#self.hpfilter.ChangeBand(band)
self.v2filter.ChangeBand(band)
self.CorrectSmeter()
return ret
def HeartBeat(self): # Called at about 10 Hz by the main
self.anttuner.HeartBeat()
#self.lpfilter.HeartBeat()
#self.hpfilter.HeartBeat()
self.v2filter.HeartBeat()
self.controlbox.HeartBeat()
return BaseHw.HeartBeat(self)
def OnSpot(self, level):
# level is -1 for Spot button Off; else the Spot level 0 to 1000.
self.anttuner.OnSpot(level)
return BaseHw.OnSpot(self, level)
def OnButtonRfGain(self, event):
#self.hpfilter.OnButtonRfGain(event)
self.v2filter.OnButtonRfGain(event)
self.CorrectSmeter()
def CorrectSmeter(self): # S-meter correction can change with band or RF gain
if self.band == '40': # Basic S-meter correction by band
self.correct_smeter = 20.5
else:
self.correct_smeter = 20.5
self.correct_smeter -= self.rf_gain / 6.0 # Correct S-meter for RF gain
self.application.waterfall.ChangeRfGain(self.rf_gain) # Waterfall colors are constant
def OnButtonPTT(self, event):
self.controlbox.OnButtonPTT(event)
return BaseHw.OnButtonPTT(self, event)
+56
View File
@@ -0,0 +1,56 @@
# Please do not change this widgets module for Quisk. Instead copy
# it to your own quisk_widgets.py and make changes there.
#
# This module is used to add extra widgets to the QUISK screen.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import wx, time
import _quisk as QS
class BottomWidgets: # Add extra widgets to the bottom of the screen
def __init__(self, app, hardware, conf, frame, gbs, vertBox):
#self.config = conf
#self.hardware = hardware
#self.application = app
#start_row = app.widget_row # The first available row
#start_col = app.button_start_col # The start of the button columns
#b = app.QuiskCycleCheckbutton(frame, self.OnAntTuner, ('Antenna', 'Ant 0', 'Ant 1'))
#bw, bh = b.GetMinSize()
#gbs.Add(b, (start_row, start_col), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'L+')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 2), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'L-')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 4), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'C+')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 6), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'C-')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 8), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'Save')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 10), (1, 2), flag=wx.EXPAND)
#self.swr_label = app.QuiskText(frame, 'Watts 000 SWR 10.1 Zh Ind 22 Cap 33 Freq 28100 (7777)', bh)
#gbs.Add(self.swr_label, (start_row, start_col + 2), (1, 10), flag=wx.EXPAND)
#b = app.QuiskCheckbutton(frame, None, text='')
#gbs.Add(b, (start_row, start_col + 25), (1, 2), flag=wx.EXPAND)
# Example of a horizontal slider:
# lab = wx.StaticText(frame, -1, 'Preamp', style=wx.ALIGN_CENTER)
# gbs.Add(lab, (5,0), flag=wx.EXPAND)
# sl = wx.Slider(frame, -1, 1024, 0, 2048) # parent, -1, initial, min, max
# gbs.Add(sl, (5,1), (1, 5), flag=wx.EXPAND)
# sl.Bind(wx.EVT_SCROLL, self.OnPreamp)
# def OnPreamp(self, event):
# print event.GetPosition()
self.num_rows_added = 0
#def OnAntTuner(self, event):
# btn = event.GetEventObject()
# text = btn.GetLabel()
# self.hardware.OnAntTuner(text)
#def UpdateText(self, text):
# self.swr_label.SetLabel(text)
+140
View File
@@ -0,0 +1,140 @@
# Please do not change this widgets module for Quisk. Instead copy
# it to your own quisk_widgets.py and make changes there.
#
# This module is used to add extra widgets to the QUISK screen.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import wx, time
import _quisk as QS
class BottomWidgets: # Add extra widgets to the bottom of the screen
def __init__(self, app, hardware, conf, frame, gbs, vertBox):
self.config = conf
self.hardware = hardware
self.application = app
row = 4 # The next available row
b = app.QuiskPushbutton(frame, None, 'Tune')
bw, bh = b.GetMinSize()
b.Enable(0)
gbs.Add(b, (row, 0), (1, 2), flag=wx.EXPAND)
b = app.QuiskPushbutton(frame, None, '')
gbs.Add(b, (row, 2), (1, 2), flag=wx.EXPAND)
b = app.QuiskPushbutton(frame, None, '')
gbs.Add(b, (row, 4), (1, 2), flag=wx.EXPAND)
b = self.btnScanner = app.QuiskCheckbutton(frame, self.OnBtnScanner, text='Scanner', use_right=True)
self.scan_timer = wx.Timer(b) # timed events for the scanner
b.Bind(wx.EVT_TIMER, self.OnTimerEvent)
gbs.Add(b, (row, 6), (1, 2), flag=wx.EXPAND)
b = self.btnNext = app.QuiskPushbutton(frame, self.OnBtnNext, 'Next', True)
gbs.Add(b, (row, 8), (1, 2), flag=wx.EXPAND)
b = app.QuiskCheckbutton(frame, self.OnBtnRptr, text='Rptr')
b.SetValue(True, True)
gbs.Add(b, (row, 10), (1, 2), flag=wx.EXPAND)
self.swr_label = app.QuiskText(frame, 'Watts 000 SWR 10.1 Zh Ind 22 Cap 33 Freq 28100 (7777)', bh)
gbs.Add(self.swr_label, (row, 15), (1, 12), flag=wx.EXPAND)
# Example of a horizontal slider:
# lab = wx.StaticText(frame, -1, 'Preamp', style=wx.ALIGN_CENTER)
# gbs.Add(lab, (5,0), flag=wx.EXPAND)
# sl = wx.Slider(frame, -1, 1024, 0, 2048) # parent, -1, initial, min, max
# gbs.Add(sl, (5,1), (1, 5), flag=wx.EXPAND)
# sl.Bind(wx.EVT_SCROLL, self.OnPreamp)
# def OnPreamp(self, event):
# print event.GetPosition()
def UpdateText(self, text):
self.swr_label.SetLabel(text)
def OnBtnRptr(self, event):
btn = event.GetEventObject()
if btn.GetValue():
self.config.freq_spacing = 5000
else:
self.config.freq_spacing = 0
def OnBtnNext(self, event):
self.direction = self.btnNext.direction # +1 for left -> go up; -1 for down
self.keep_going = wx.GetKeyState(wx.WXK_SHIFT) # if Shift is down, move to next band
self.scanner = False
if self.keep_going:
if not self.ScanScreen(event):
self.MoveVfo(event)
self.scan_timer.Start(500)
else:
self.ScanScreen(event)
def ScanScreen(self, event): # Look for signals on the current screen
lst = self.hardware.rpt_freq_list
app = self.application
vfo = app.VFO
tx_freq = vfo + app.txFreq
sample_rate = app.sample_rate
limit = int(sample_rate / 2.0 * self.config.display_fraction * 0.95) # edge of screen
self.scan_n1 = None
self.scan_n = None
for n in range(len(lst)):
if lst[n] > vfo - limit and self.scan_n1 is None:
self.scan_n1 = n # inclusive
if lst[n] >= tx_freq and self.scan_n is None:
self.scan_n = n
if lst[n] > vfo + limit:
break
self.scan_n2 = n # inclusive
if self.scan_n is None:
self.scan_n = self.scan_n1
if self.direction > 0: # left click; go up
seq = list(range(self.scan_n + 1, self.scan_n2 + 1))
if not self.keep_going:
seq += list(range(self.scan_n1, self.scan_n))
else: # right click; go down
seq = list(range(self.scan_n - 1, self.scan_n1 - 1, -1))
if not self.keep_going:
seq += list(range(self.scan_n2, self.scan_n, -1))
for n in seq:
freq = lst[n]
if not QS.get_squelch(freq - vfo):
app.ChangeHwFrequency(freq - vfo, vfo, 'Repeater', event)
return True # frequency was changed
return False # frequency was not changed
def MoveVfo(self, event): # Move the VFO to look for further signals
lst = self.hardware.rpt_freq_list
app = self.application
vfo = app.VFO
tx_freq = vfo + app.txFreq
sample_rate = app.sample_rate
if self.direction > 0: # left click; go up
n = self.scan_n2 + 1
if n >= len(lst):
n = 0
freq = lst[n]
vfo = freq + sample_rate * 4 // 10
app.ChangeHwFrequency(freq - vfo, vfo, 'Repeater', event)
else: # right click; go down
n = self.scan_n1 - 1
if n < 0:
n = len(lst) - 1
freq = lst[n]
vfo = freq - sample_rate * 4 // 10
app.ChangeHwFrequency(freq - vfo, vfo, 'Repeater', event)
def OnBtnScanner(self, event):
self.direction = self.btnScanner.direction # +1 for left -> go up; -1 for down
self.keep_going = wx.GetKeyState(wx.WXK_SHIFT) # if Shift is down, move to next band
self.scanner = True
if self.btnScanner.GetValue():
self.btnNext.Enable(0)
if self.keep_going:
if not self.ScanScreen(event):
self.MoveVfo(event)
else:
self.ScanScreen(event)
self.scan_timer.Start(500)
else:
self.btnNext.Enable(1)
self.scan_timer.Stop()
def OnTimerEvent(self, event):
if QS.get_squelch(self.application.txFreq):
if self.keep_going:
if not self.ScanScreen(event):
self.MoveVfo(event)
else:
self.ScanScreen(event)
elif not self.scanner:
self.scan_timer.Stop()
+92
View File
@@ -0,0 +1,92 @@
#! /usr/bin/python
# All QUISK software is Copyright (C) 2006-2011 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!!
"Select the desired hardware, and start Quisk"
import sys, wx, subprocess, os
Choices = [
(' My Transceiver', 'n2adr/quisk_conf.py', ''),
(' VHF/UHF Receiver', 'n2adr/uhfrx_conf.py', ''),
(' Softrock Rx Ensemble', 'softrock/conf_rx_ensemble2.py', 'n2adr/conf2.py'),
(' Softrock Rx/Tx Ensemble', 'softrock/conf_rx_tx_ensemble.py', 'n2adr/conf6.py'),
(' Plain Sound Card, Rx only', 'n2adr/conf2.py', ''),
(' Test microphone sound', 'n2adr/conf4.py', ''),
(' SDR-IQ, receive only, antenna to RF input', 'quisk_conf_sdriq.py', 'n2adr/conf2.py'),
(' AOR AR8600 with IF to my hardware', 'n2adr/quisk_conf_8600.py', ''),
(' AOR AR8600 with IF to SDR-IQ', 'quisk_conf_sdr8600.py', 'n2adr/conf2.py'),
(' Fldigi with my transceiver', 'n2adr/quisk_conf.py', 'n2adr/conf5.py'),
(' Freedv.org Rx with my transceiver', 'n2adr/quisk_conf.py', 'n2adr/conf7.py'),
(' Hermes-Lite', 'hermes/quisk_conf.py', 'n2adr/conf3.py'),
(' Odyssey', 'odyssey/quisk_conf.py', 'n2adr/conf1.py'),
(' My Transceiver to Hermes-Lite', 'Quisk2Hermes', ''),
]
if sys.platform == 'win32':
os.chdir('C:\\pub\\quisk')
exe = "C:\\python27\\pythonw.exe"
else:
os.chdir('/home/jim/pub/quisk')
exe = "/usr/bin/python"
class ListBoxFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Select Hardware')
font = wx.Font(14, wx.FONTFAMILY_SWISS, wx.NORMAL, wx.FONTWEIGHT_NORMAL)
self.SetFont(font)
charx = self.GetCharWidth()
chary = self.GetCharHeight()
width = 0
height = chary * 2
tlist = []
for txt, conf1, conf2 in Choices:
text = "%s, %s" % (txt, conf1)
if conf2:
text = "%s, %s" % (text, conf2)
tlist.append(text)
w, h = self.GetTextExtent(text)
width = max(width, w)
height += h
width += 3 * chary
lb = wx.ListBox(self, -1, (0, 0), (width, height), tlist, wx.LB_SINGLE)
lb.SetSelection(0)
lb.SetFont(font)
lb.Bind(wx.EVT_LISTBOX_DCLICK, self.OnDClick, lb)
lb.Bind(wx.EVT_KEY_DOWN, self.OnChar)
self.SetClientSize((width, height))
def OnDClick(self, event):
lb = event.GetEventObject()
index = lb.GetSelection()
text, conf1, conf2 = Choices[index]
if conf1 == "Quisk2Hermes":
subprocess.Popen([exe, 'quisk.py', '-c', 'n2adr/quisk_conf.py', '--local', 'Q2H'])
subprocess.Popen([exe, 'quisk.py', '-c', 'hermes/quisk_conf.py', '--config2', 'n2adr/conf3A.py', '--local', 'Q2H'])
else:
cmd = [exe, 'quisk.py', '-c', conf1]
if conf2:
cmd = cmd + ['--config2', conf2]
subprocess.Popen(cmd)
self.Destroy()
def OnChar(self, event):
if event.GetKeyCode() == 13:
self.OnDClick(event)
else:
event.Skip()
class App(wx.App):
def __init__(self):
if sys.stdout.isatty():
wx.App.__init__(self, redirect=False)
else:
wx.App.__init__(self, redirect=True)
def OnInit(self):
frame = ListBoxFrame()
frame.Show()
return True
app = App()
app.MainLoop()
File diff suppressed because it is too large Load Diff
+65
View File
@@ -0,0 +1,65 @@
# This is the config file for the VHF/UHF receiver and transmitter.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import sys, struct, socket, traceback
settings_file_path = "../quisk_settings.json"
DEBUG = 0
if sys.platform == "win32":
n2adr_sound_pc_capt = 'Line In (Realtek High Definition Audio)'
n2adr_sound_pc_play = 'Speakers (Realtek High Definition Audio)'
n2adr_sound_usb_play = 'Primary'
n2adr_sound_usb_mic = 'Primary'
latency_millisecs = 150
data_poll_usec = 20000
favorites_file_path = "C:/pub/quisk_favorites.txt"
elif 0: # portaudio devices
name_of_sound_play = 'portaudio:CODEC USB'
microphone_name = "portaudio:AK5370"
latency_millisecs = 150
data_poll_usec = 5000
favorites_file_path = "/home/jim/pub/quisk_favorites.txt"
else: # alsa devices
n2adr_sound_pc_capt = 'alsa:ALC1150 Analog'
n2adr_sound_pc_play = 'alsa:ALC1150 Analog'
n2adr_sound_usb_play = 'alsa:USB Sound Device'
n2adr_sound_usb_mic = 'alsa:USB Sound Device'
latency_millisecs = 150
data_poll_usec = 5000
favorites_file_path = "/home/jim/pub/quisk_favorites.txt"
name_of_sound_capt = ""
name_of_sound_play = n2adr_sound_pc_play
microphone_name = n2adr_sound_pc_capt
playback_rate = 48000
agc_off_gain = 80
do_repeater_offset = True
station_display_lines = 1
# DX cluster telent login data, thanks to DJ4CM.
dxClHost = ''
#dxClHost = 'dxc.w8wts.net'
dxClPort = 7373
user_call_sign = 'n2adr'
bandLabels = ['6', '2', '1.25', '70cm', '33cm', '23cm', 'WWV']
bandState['WWV'] = (19990000, 10000, 'AM')
BandEdge['WWV'] = (19500000, 20500000)
use_rx_udp = 17 # Get ADC samples from UDP
rx_udp_ip = "192.168.1.199" # Sample source IP address
rx_udp_port = 0xAA53 # Sample source UDP port
#rx_clk38 = 38880000 - 30 # master clock frequency, 38880 kHz nominal
#rx_udp_clock = rx_clk38 * 32 // 2 // 9 # ADC sample rate in Hertz
sample_rate = 96000 # 96, 192, 384, 768, 1152 (for 69120/3/10)
display_fraction = 1.00
fft_size_multiplier = 16
tx_ip = "192.168.1.201"
tx_audio_port = 0xBC79
add_imd_button = 1
add_fdx_button = 1
+498
View File
@@ -0,0 +1,498 @@
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import sys, struct, socket, traceback
from quisk_hardware_model import Hardware as BaseHardware
import _quisk as QS
DEBUG = 0
class Adf4351: # class to hold adf4351 attributes
def __init__(self, receiver, clock, r_counter):
self.receiver = receiver
self.clock = clock
self.r_counter = r_counter
self.int_mode = 1 # integer one, fractional zero
self.band_sel_clock_div = 40
self.aux_rf_out = 0b000 # enable 1/0, power 00 to 11
self.frac_value = 0
self.modulus = 23
self.changed = 0
class Preamp: # Lone Wire Bus control of preamp for 2 meters and 70 cm
def __init__(self):
self.IP = '192.168.1.194'
self.PORT = 0x3A00 + 67
# Create a socket for the Lone Wire Bus control of the preamp
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.connect((self.IP, self.PORT))
self.socket.settimeout(0)
self.want_preamp = self.have_preamp = '\000'
def ChangeBand(self, band):
if band == '2':
self.want_preamp = '\001'
elif band == '70cm':
self.want_preamp = '\002'
else:
self.want_preamp = '\000'
#print ("ChangeBand", band)
def HeartBeat(self):
try:
data = self.socket.recv(4096)
except:
pass
else:
if len(data) == 1:
self.have_preamp = data
#print ("Got data 0x%X" % ord(data))
if self.want_preamp != self.have_preamp:
self.socket.send(self.want_preamp)
#print ("Send data 0x%X" % ord(self.want_preamp))
class Hardware(BaseHardware):
def __init__(self, app, conf):
BaseHardware.__init__(self, app, conf)
self.vfo_frequency = 52000000
self.vfo_sample_rate = conf.sample_rate
self.vfo_test = 0 # JIM
self.tx_frequency = 0
self.CorrectTxDc = {
'23cm':(1270.0, 0.167081, 0.150557),
'2':(146.0, 0.018772, 0.038658),
'33cm':(915.0, 0.140150, 0.051967),
'6':(52.0, 0.020590, 0.024557),
'70cm':(435.0, 0.004495, 0.096879),
'1.25':(223.5, 0.042958, 0.055212),
}
self.rx_clock38 = 38880000 - 30 # master clock frequency, 38880 kHz nominal
#rx_udp_clock = rx_clock38 * 32 // 2 // 9 # ADC sample rate in Hertz
self.rx_udp_clock_nominal = 69120000 # rate to display
self.tx_clock80 = 80000000 + 14
self.firmware_version = None # firmware version is initially unknown
self.rx_udp_socket = None
self.tx_udp_socket = None
self.got_rx_udp_status = ''
self.got_tx_udp_status = ''
self.band = ''
self.rx_phase0 = self.rx_phase1 = 0
self.tx_phase = 0
self.button_PTT = 0
self.mode_is_cw = 0
self.scan_enable = 0
self.scan_blocks = 0
self.scan_samples = 1
self.scan_phase = 0
self.fft_scan_valid = 0.84
self.preamp = Preamp()
self.Rx4351 = Adf4351(True, self.rx_clock38, 8)
self.Tx4351 = Adf4351(False, 10700000, 2)
self.Tx4351.aux_rf_out = 0b000 # enable aux RF out 0b111 or turn off 0b000
self.decim3 = 10
self.SetDecim(192000)
self.var_rates = ['31X', '19X', '9X', '5X', '3X', '2X', '1728', '1152', '768', '384', '192', '96', '48'] # supported sample rates as strings
self.index = 0
self.repeater_freq = None
self.DcI, self.DcQ = (0.0, 0.0)
self.NewAdf4351(self.Rx4351, 146E6)
self.NewAdf4351(self.Tx4351, 146E6)
self.NewAd9951(52e6)
self.NewUdpStatus()
def ChangeFrequency(self, tx_freq, vfo_freq, source='', band='', event=None):
self.tx_frequency = tx_freq
if not self.Rx4351.frequency - 3E6 < vfo_freq < self.Rx4351.frequency + 3E6:
self.NewAdf4351(self.Rx4351, vfo_freq)
self.vfo_frequency = -1
self.NewAd9951(tx_freq)
if abs(self.ad9951_freq - 10.7e6) > 15000:
self.NewAdf4351(self.Tx4351, tx_freq)
self.NewAd9951(tx_freq)
self.NewAd9951(tx_freq)
if self.vfo_frequency != vfo_freq:
self.vfo_frequency = vfo_freq
self.scan_deltaf = int(1152E3 * self.fft_scan_valid + 0.5)
self.scan_phase = int(1152.E3 * self.fft_scan_valid / self.conf.rx_udp_clock * 2.0**32 + 0.5)
self.scan_vfo0 = vfo_freq
rx_phase1 = int((vfo_freq - self.Rx4351.frequency) / self.conf.rx_udp_clock * 2.0**32 + 0.5)
if self.scan_enable:
self.scan_vfo0 = self.scan_vfo0 - self.scan_deltaf * (self.scan_blocks - 1) // 2
rx_phase1 = rx_phase1 - int(self.scan_phase * (self.scan_blocks - 1) / 2.0 + 0.5)
self.rx_phase1 = rx_phase1 & 0xFFFFFFFF
rx_tune_freq = float(rx_phase1) * self.conf.rx_udp_clock / 2.0**32
QS.change_rates(96000, tx_freq, self.vfo_sample_rate, vfo_freq)
QS.change_scan(self.scan_blocks, 1152000, self.fft_scan_valid, self.scan_vfo0, self.scan_deltaf)
if DEBUG:
#print( "vfo", vfo_freq, "adf4351", self.Rx4351.frequency, "phase", rx_phase1, "rx_tune", self.Rx4351.frequency - vfo_freq, rx_tune_freq)
#print ("VFO", self.Rx4351.frequency + rx_tune_freq)
print ("Change to Tx %d Vfo %d; VFO %.0f = adf4351_freq %.0f + rx_tune_freq %.0f" % (tx_freq, vfo_freq,
self.Rx4351.frequency + rx_tune_freq, self.Rx4351.frequency, rx_tune_freq))
#print ("scan_enable %d, scan_blocks %d, scan_vfo0 %d, scan_deltaf %d" % (self.scan_enable, self.scan_blocks, self.scan_vfo0, self.scan_deltaf))
else:
QS.change_rates(96000, tx_freq, self.vfo_sample_rate, self.vfo_frequency)
rx_phase0 = int((tx_freq - self.Rx4351.frequency) / self.conf.rx_udp_clock * 2.0**32 + 0.5)
self.rx_phase0 = rx_phase0 & 0xFFFFFFFF
self.NewUdpStatus()
if self.application.bottom_widgets:
Rx1 = self.Rx4351.frequency * 1e-6
Rx2 = (self.ReturnVfoFloat() - self.Rx4351.frequency) * 1e-6
t = "Rx Div %d; ADF4351 %.6f + rx_tune %.6f = %.6f Tx Adf4351 %.6f AD9951 %.6f" % (
2**self.Rx4351.rf_divider, Rx1, Rx2, Rx1 + Rx2, self.Tx4351.frequency * 1e-6, self.ad9951_freq * 1e-6)
self.application.bottom_widgets.UpdateText(t)
return tx_freq, vfo_freq
def RepeaterOffset(self, offset=None): # Change frequency for repeater offset during Tx
if offset is None: # Return True if frequency change is complete
self.HeartBeat()
return self.want_rx_udp_status[16:] == self.got_tx_udp_status[16:]
if offset == 0: # Change back to the original frequency
if self.repeater_freq is None: # Frequency was already reset
return self.want_rx_udp_status[16:] == self.got_tx_udp_status[16:]
self.ChangeFrequency(self.repeater_freq, self.vfo_frequency)
self.repeater_freq = None
else: # Shift to repeater input frequency
self.repeater_freq = self.tx_frequency
offset = int(offset * 1000) # Convert kHz to Hz
self.ChangeFrequency(self.tx_frequency + offset, self.vfo_frequency)
return False
def ReturnVfoFloat(self): # Return the accurate VFO as a float
rx_phase1 = int((self.vfo_frequency - self.Rx4351.frequency) / self.conf.rx_udp_clock * 2.0**32 + 0.5)
rx_tune_freq = float(rx_phase1) * self.conf.rx_udp_clock / 2.0**32
return self.Rx4351.frequency + rx_tune_freq
def open(self):
##self.application.config_screen.config.tx_phase.Enable(1)
# Create the proper broadcast address for rx_udp_ip.
nm = self.conf.rx_udp_ip_netmask.split('.')
ip = self.conf.rx_udp_ip.split('.')
nm = list(map(int, nm))
ip = list(map(int, ip))
bc = ''
for i in range(4):
x = (ip[i] | ~ nm[i]) & 0xFF
bc = bc + str(x) + '.'
self.broadcast_addr = bc[:-1]
# This socket is used for the Simple Network Discovery Protocol by AE4JY
self.socket_sndp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket_sndp.setblocking(0)
self.socket_sndp.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.sndp_request = chr(56) + chr(0) + chr(0x5A) + chr(0xA5) + chr(0) * 52
self.sndp_rx_active = True
# conf.rx_udp_port is used for returning ADC samples
# conf.rx_udp_port + 1 is used for control
self.rx_udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.rx_udp_socket.setblocking(0)
self.rx_udp_socket.connect((self.conf.rx_udp_ip, self.conf.rx_udp_port + 1))
# conf.tx_audio_port + 1 is used for control
if self.conf.tx_ip:
self.sndp_tx_active = True
self.tx_udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.tx_udp_socket.setblocking(0)
self.tx_udp_socket.connect((self.conf.tx_ip, self.conf.tx_audio_port + 1))
else:
self.sndp_tx_active = False
QS.change_rates(96000, 0, 96000, 0)
self.application.test1Button.Enable(0)
return QS.open_rx_udp(self.conf.rx_udp_ip, self.conf.rx_udp_port)
def close(self):
if self.rx_udp_socket:
self.rx_udp_socket.close()
self.rx_udp_socket = None
if self.tx_udp_socket:
self.tx_udp_socket.close()
self.tx_udp_socket = None
def PrintStatus(self, msg, string):
print (msg, ' ', end=' ')
print (string[0:2], end=' ')
for c in string[2:]:
print ("%2X" % ord(c), end=' ')
print ()
def GetFirmwareVersion(self):
return self.firmware_version
def ChangeMode(self, mode):
# mode is a string: "USB", "AM", etc.
if mode in ("CWL", "CWU"):
self.mode_is_cw = 1
else:
self.mode_is_cw = 0
self.NewUdpStatus()
def ChangeBand(self, band):
# band is a string: "60", "40", "WWV", etc.
self.band = band
try:
freq, DcI, DcQ = self.CorrectTxDc[band]
except KeyError:
DcI, DcQ = (0.0, 0.0)
self.NewUdpCorrect(DcI, DcQ)
self.preamp.ChangeBand(band)
def NewUdpCorrect(self, DcI, DcQ):
self.DcI = DcI
self.DcQ = DcQ
QS.set_udp_tx_correct(DcI, DcQ, 0.828)
self.NewUdpStatus()
def PrintUdpCorrect(self):
for band in self.CorrectTxDc:
freq, DcI, DcQ = self.CorrectTxDc[band]
print ("'%s':(%.1f, %.6f, %.6f)," % (band, freq, DcI, DcQ))
def OnButtonPTT(self, event):
btn = event.GetEventObject()
if btn.GetValue(): # Turn the software key bit on or off
self.button_PTT = 1
else:
self.button_PTT = 0
QS.set_key_down(self.button_PTT)
self.NewUdpStatus()
def OnSpot(self, level):
# level is -1 for Spot button Off; else the Spot level 0 to 1000.
pass
def Sndp(self): # AE4JY Simple Network Discovery Protocol - attempt to set the FPGA IP address
try:
self.socket_sndp.sendto(self.sndp_request, (self.broadcast_addr, 48321))
except:
if DEBUG:
traceback.print_exc()
return
for i in range(5):
try:
data = self.socket_sndp.recv(1024)
except:
break
if len(data) != 56:
continue
if data[5:17] == 'QuiskUHFR-v1':
ip = self.conf.rx_udp_ip.split('.')
ip = list(map(int, ip))
ip = list(map(chr, ip))
if data[37] == ip[3] and data[38] == ip[2] and data[39] == ip[1] and data[40] == ip[0]:
self.sndp_rx_active = False
if DEBUG: print("SNDP success for Rx")
else:
t = (data[0:4] + chr(2) + data[5:37] + ip[3] + ip[2] + ip[1] + ip[0]
+ chr(0) * 12 + chr(self.conf.rx_udp_port & 0xFF) + chr(self.conf.rx_udp_port >> 8) + chr(0))
self.socket_sndp.sendto(t, (self.broadcast_addr, 48321))
elif data[5:17] == 'QuiskUHFT-v1':
if self.conf.tx_ip:
ip = self.conf.tx_ip.split('.')
ip = list(map(int, ip))
ip = list(map(chr, ip))
if data[37] == ip[3] and data[38] == ip[2] and data[39] == ip[1] and data[40] == ip[0]:
self.sndp_tx_active = False
if DEBUG: print("SNDP success for Tx")
else:
t = (data[0:4] + chr(2) + data[5:37] + ip[3] + ip[2] + ip[1] + ip[0]
+ chr(0) * 12 + chr(self.conf.tx_audio_port & 0xFF) + chr(self.conf.tx_audio_port >> 8) + chr(0))
self.socket_sndp.sendto(t, (self.broadcast_addr, 48321))
def HeartBeat(self):
if self.sndp_rx_active or self.sndp_tx_active:
self.Sndp()
return # SNDP is required
for i in range(10):
try: # receive the Rx status if any
data = self.rx_udp_socket.recv(1024)
if DEBUG > 1:
self.PrintStatus(' gotRx ', data)
except:
break
else:
if data[0:2] == 'Sx':
self.got_rx_udp_status = data
if self.tx_udp_socket:
for i in range(10):
try: # receive the Tx status if any
data = self.tx_udp_socket.recv(1024)
if DEBUG > 1:
self.PrintStatus(' gotTx ', data)
except:
break
else:
if data[0:2] == 'Sx':
self.got_tx_udp_status = data
if self.want_rx_udp_status[16:] == self.got_rx_udp_status[16:]: # The first part returns information from the hardware
self.firmware_version = ord(self.got_rx_udp_status[2]) # Firmware version is returned here
self.Rx4351.changed = 0
else:
if DEBUG > 1:
self.PrintStatus('HaveRx', self.got_rx_udp_status[0:20])
self.PrintStatus('sendRx', self.want_rx_udp_status[0:20])
try:
self.rx_udp_socket.send(self.want_rx_udp_status)
except:
#traceback.print_exc()
pass
if not self.tx_udp_socket:
pass
elif self.want_rx_udp_status[16:] == self.got_tx_udp_status[16:]: # The first part returns information from the hardware
self.Tx4351.changed = 0
self.Tx9951_changed = 0
else:
if DEBUG > 1:
self.PrintStatus('HaveTx', self.got_rx_udp_status[0:20])
self.PrintStatus('sendTx', self.want_rx_udp_status[0:20])
try:
self.tx_udp_socket.send(self.want_rx_udp_status)
except:
#traceback.print_exc()
pass
if 0:
self.rx_udp_socket.send('Qs')
self.preamp.HeartBeat()
def VarDecimGetChoices(self): # return text labels for the control
return self.var_rates
def VarDecimGetLabel(self): # return a text label for the control
return "Sample rate ksps"
def VarDecimGetIndex(self): # return the current index
return self.index
def VarDecimRange(self):
return (48000, 1152000)
def VarDecimSet(self, index=None): # set decimation, return sample rate
if index is None: # initial call to set decimation before the call to open()
rate = self.application.vardecim_set # May be None or from different hardware
try:
rate = rate // 1000
if rate > 1152:
rate = 1152
index = self.var_rates.index(str(rate))
except:
rate = 192
index = self.var_rates.index(str(rate))
self.index = index
rate = self.var_rates[index]
if rate[-1] == 'X':
self.scan_enable = 1
self.scan_blocks = int(rate[0:-1])
self.scan_samples = self.application.fft_size
self.decim1 = 2
self.decim2 = 3
rate = 1152000 * self.scan_blocks
else:
self.scan_enable = 0
self.scan_blocks = 0
rate = int(rate)
rate = rate * 1000
self.SetDecim(rate)
vfo = self.vfo_frequency
self.vfo_frequency = -1
self.vfo_sample_rate = rate
self.ChangeFrequency(self.tx_frequency, vfo)
self.NewUdpStatus()
return rate
def SetDecim(self, rate):
# self.decim1, decim2, decim3 are the first, second, third decimations in the hardware
if rate >= 1152000:
self.decim1 = 2
elif rate >= 192000:
self.decim1 = 3
elif rate == 96000:
self.decim1 = 6
else:
self.decim1 = 12
self.decim2 = self.rx_udp_clock_nominal // rate // self.decim1 // self.decim3
def NewUdpStatus(self):
# Start of 16 bytes sent to the hardware:
s = "Sx" # 0:2 Fixed string
s += chr(0) # 2 Version number is returned here
s += chr(0) # 3
s += chr(0) * 12 # 4:16
# Start of 80 bytes of data sent to the hardware:
s += chr( 6 - 1) # 0 Variable decimation less one channel 0 first
s += chr(12 - 1) # 1 Variable decimation less one channel 0 second
s += struct.pack("<L", self.rx_phase0) # 2: 6 Channel zero Rx tune phase
s += struct.pack("<L", self.rx_phase1) # 6:10 Channel one Rx tune phase)
s += chr(0x3 | self.scan_enable << 2 | self.Rx4351.changed << 3 |
self.Tx4351.changed << 4 | self.Tx9951_changed << 5 |
self.button_PTT << 6 | self.mode_is_cw << 7) # 10 Flags
# 0: enable samples on channel 0
# 1: enable samples on channel 1
# 2: enable scan on channel 1
# 3: the receive adf4351 registers have changed
# 4: the transmit adf4351 registers have changed
# 5: the transmit ad9951 register changed
# 6: the PTT button is down
# 7: the mode is CWU or CWL
s += chr(self.scan_blocks) # 11 For scan, the number of frequency blocks
s += struct.pack("<H", self.scan_samples) # 12:14 For scan, the number of samples per block
s += struct.pack("<L", self.scan_phase) # 14:18 For scan, the tuning phase increment
s += chr(self.decim1 - 1) # 18 Variable decimation less one channel 1 first
s += chr(self.decim2 - 1) # 19 Variable decimation less one channel 1 second
s += self.Rx4351.regs # 20:44 Receive adf4351; six 32-bit registers, 24 bytes
s += self.Tx4351.regs # 44:68 Transmit adf4351; six 32-bit registers, 24 bytes
s += self.ad9951_data # 68:74 Transmit ad9951: data length, instruction, 4 bytes of data
DcI = int(self.DcI * 32767.0)
DcQ = int(self.DcQ * 32767.0)
s += struct.pack("<h", DcI) # 74:76 Transmit DC correction for I channel
s += struct.pack("<h", DcQ) # 76:78 Transmit DC correction for Q channel
s += chr(0) * (96 - len(s)) # Fixed length message 16 + 80
self.want_rx_udp_status = s
def NewAd9951(self, tx_freq):
# Fpfd = Fref / 2 / R = Fvco / N
# Fout = Fvco / 2**D and is twice the transmit frequency
# Fref = 2R(2**d)Fout / N
# Fout = Fref * N / 2 / R / 2**D
adf = self.Tx4351
freq = 2.0 * adf.r_counter * (2 ** adf.rf_divider) / adf.int_value * (tx_freq * 2.0)
phase = int(freq / self.tx_clock80 * 2.0**32 + 0.5)
try:
self.ad9951_data = chr(40) + struct.pack("<L", phase ) + chr(4)
except struct.error:
self.ad9951_data = chr(0) * 6
self.ad9951_freq = float(phase) * self.tx_clock80 / 2 ** 32
##adf.frequency = 0.5 * self.ad9951_freq * adf.int_value / 2.0 / adf.r_counter / (2 ** adf.rf_divider)
self.Tx9951_changed = 1
def NewAdf4351(self, adf, vfo_freq):
# Set the adf4351 to the nearest integer-mode frequency
Fpfd = adf.clock / 2.0 / adf.r_counter
if adf.receiver:
vfo_freq += Fpfd * self.vfo_test / 8 # test the VFO at an offset from the center
vfo = vfo_freq * 2 # Local oscillator runs at 2X frequency
for div in range(0, 7):
Fvco = vfo * 2 ** div
if 2200E6 <= Fvco < 4400E6:
adf.rf_divider = div
adf.int_value = int(vfo * 2 ** div / Fpfd + 0.5)
break
else:
if vfo < 500e6:
adf.rf_divider = 6
adf.int_value = int(2200E6 / Fpfd)
else:
adf.rf_divider = 0
adf.int_value = int(4400E6 / Fpfd)
adf.frequency = 0.5 * Fpfd * adf.int_value / 2 ** div
if DEBUG:
print ("int, div, Fvco, ADF4351", adf.int_value, div, int(vfo * 2 ** div / 1e6), adf.frequency)
print ("New adf4351_freq", adf.frequency)
# Number of bits for each field:
# intNmode 1
# aux_rf_out 3
# rf_divider 3
# band_sel_clock_div 8
# r_counter 10 Fpfd = Fref / 2 / r_counter
# int_value 16 Fvco = Fpfd * (int_value + frac_value / modulus)
# frac_value 12
# modulus 12
reg = 0b00000000000000000000000000000000 # Register 0
reg = reg | adf.int_value << 15 | adf.frac_value << 3
s = struct.pack("<L", reg)
reg = 0b00001000000000001000000000000001 # Register 1
reg = reg | adf.modulus << 3
s += struct.pack("<L", reg)
reg = 0b00000001000000000001111001000010 # Register 2
reg = reg | adf.r_counter << 14 | adf.int_mode << 8 | adf.int_mode << 7
s += struct.pack("<L", reg)
reg = 0b00000000000001000000000000000011 # Register 3
reg = reg | adf.int_mode << 22 | adf.int_mode << 21
s += struct.pack("<L", reg)
reg = 0b00000000100000000000010000111100 # Register 4
reg = reg | adf.rf_divider << 20 | adf.band_sel_clock_div << 12 | adf.aux_rf_out << 6
s += struct.pack("<L", reg)
reg = 0b00000000010110000000000000000101 # Register 5
s += struct.pack("<L", reg)
adf.regs = s
adf.changed = 1
def TestVfoPlus(self, event):
self.vfo_test += 1
self.Rx4351.frequency = 1
self.ChangeFrequency(self.tx_frequency, self.vfo_frequency)
def TestVfoMinus(self, event):
self.vfo_test -= 1
self.Rx4351.frequency = 1
self.ChangeFrequency(self.tx_frequency, self.vfo_frequency)
+526
View File
@@ -0,0 +1,526 @@
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import sys, struct, socket, traceback
from quisk_hardware_model import Hardware as BaseHardware
import _quisk as QS
DEBUG = 0
class Adf4351: # class to hold adf4351 attributes
def __init__(self, receiver, clock, r_counter):
self.receiver = receiver
self.clock = clock
self.r_counter = r_counter
self.int_mode = 1 # integer one, fractional zero
self.band_sel_clock_div = 40
self.aux_rf_out = 0b000 # enable 1/0, power 00 to 11
self.frac_value = 0
self.modulus = 23
self.changed = 0
class Preamp: # Lone Wire Bus control of preamp for 2 meters and 70 cm
def __init__(self):
self.IP = '192.168.1.194'
self.PORT = 0x3A00 + 67
# Create a socket for the Lone Wire Bus control of the preamp
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.connect((self.IP, self.PORT))
self.socket.settimeout(0)
self.want_preamp = self.have_preamp = b'\000'
def ChangeBand(self, band):
if band == '2':
self.want_preamp = b'\001'
elif band == '70cm':
self.want_preamp = b'\002'
else:
self.want_preamp = b'\000'
#print ("Preamp ChangeBand", band)
def HeartBeat(self):
try:
data = self.socket.recv(4096)
except:
pass
else:
if len(data) == 1:
self.have_preamp = data
#print ("Got data 0x%X" % ord(data))
if self.want_preamp != self.have_preamp:
self.socket.send(self.want_preamp)
#print ("Send data 0x%X" % ord(self.want_preamp))
class Hardware(BaseHardware):
def __init__(self, app, conf):
BaseHardware.__init__(self, app, conf)
self.vfo_frequency = 52000000
self.vfo_sample_rate = conf.sample_rate
self.vfo_test = 0 # JIM
self.tx_frequency = 0
self.CorrectTxDc = {
'23cm':(1270.0, 0.167081, 0.150557),
'2':(146.0, 0.018772, 0.038658),
'33cm':(915.0, 0.140150, 0.051967),
'6':(52.0, 0.020590, 0.024557),
'70cm':(435.0, 0.004495, 0.096879),
'1.25':(223.5, 0.042958, 0.055212),
}
self.rx_clock38 = 38880000 - 30 # master clock frequency, 38880 kHz nominal
#rx_udp_clock = rx_clock38 * 32 // 2 // 9 # ADC sample rate in Hertz
self.rx_udp_clock_nominal = 69120000 # rate to display
self.tx_clock80 = 80000000 + 14
self.firmware_version = None # firmware version is initially unknown
self.rx_udp_socket = None
self.tx_udp_socket = None
self.got_rx_udp_status = bytearray()
self.got_tx_udp_status = bytearray()
self.band = ''
self.rx_phase0 = self.rx_phase1 = 0
self.tx_phase = 0
self.button_PTT = 0
self.mode_is_cw = 0
self.scan_enable = 0
self.scan_blocks = 0
self.scan_samples = 1
self.scan_phase = 0
self.fft_scan_valid = 0.84
self.preamp = Preamp()
self.Rx4351 = Adf4351(True, self.rx_clock38, 8)
self.Tx4351 = Adf4351(False, 10700000, 2)
self.Tx4351.aux_rf_out = 0b000 # enable aux RF out 0b111 or turn off 0b000
self.decim3 = 10
self.SetDecim(192000)
self.var_rates = ['31X', '19X', '9X', '5X', '3X', '2X', '1728', '1152', '768', '384', '192', '96', '48'] # supported sample rates as strings
self.index = 0
self.repeater_freq = None
self.DcI, self.DcQ = (0.0, 0.0)
self.NewAdf4351(self.Rx4351, 146E6)
self.NewAdf4351(self.Tx4351, 146E6)
self.NewAd9951(52e6)
self.NewUdpStatus()
def ChangeFrequency(self, tx_freq, vfo_freq, source='', band='', event=None):
self.tx_frequency = tx_freq
if not self.Rx4351.frequency - 3E6 < vfo_freq < self.Rx4351.frequency + 3E6:
self.NewAdf4351(self.Rx4351, vfo_freq)
self.vfo_frequency = -1
self.NewAd9951(tx_freq)
if abs(self.ad9951_freq - 10.7e6) > 15000:
self.NewAdf4351(self.Tx4351, tx_freq)
self.NewAd9951(tx_freq)
self.NewAd9951(tx_freq)
if self.vfo_frequency != vfo_freq:
self.vfo_frequency = vfo_freq
self.scan_deltaf = int(1152E3 * self.fft_scan_valid + 0.5)
self.scan_phase = int(1152.E3 * self.fft_scan_valid / self.conf.rx_udp_clock * 2.0**32 + 0.5)
self.scan_vfo0 = vfo_freq
rx_phase1 = int((vfo_freq - self.Rx4351.frequency) / self.conf.rx_udp_clock * 2.0**32 + 0.5)
if self.scan_enable:
self.scan_vfo0 = self.scan_vfo0 - self.scan_deltaf * (self.scan_blocks - 1) // 2
rx_phase1 = rx_phase1 - int(self.scan_phase * (self.scan_blocks - 1) / 2.0 + 0.5)
self.rx_phase1 = rx_phase1 & 0xFFFFFFFF
rx_tune_freq = float(rx_phase1) * self.conf.rx_udp_clock / 2.0**32
QS.change_rates(96000, tx_freq, self.vfo_sample_rate, vfo_freq)
QS.change_scan(self.scan_blocks, 1152000, self.fft_scan_valid, self.scan_vfo0, self.scan_deltaf)
if DEBUG:
#print( "vfo", vfo_freq, "adf4351", self.Rx4351.frequency, "phase", rx_phase1, "rx_tune", self.Rx4351.frequency - vfo_freq, rx_tune_freq)
#print ("VFO", self.Rx4351.frequency + rx_tune_freq)
print ("Change to Tx %d Vfo %d; VFO %.0f = adf4351_freq %.0f + rx_tune_freq %.0f" % (tx_freq, vfo_freq,
self.Rx4351.frequency + rx_tune_freq, self.Rx4351.frequency, rx_tune_freq))
#print ("scan_enable %d, scan_blocks %d, scan_vfo0 %d, scan_deltaf %d" % (self.scan_enable, self.scan_blocks, self.scan_vfo0, self.scan_deltaf))
else:
QS.change_rates(96000, tx_freq, self.vfo_sample_rate, self.vfo_frequency)
rx_phase0 = int((tx_freq - self.Rx4351.frequency) / self.conf.rx_udp_clock * 2.0**32 + 0.5)
self.rx_phase0 = rx_phase0 & 0xFFFFFFFF
self.NewUdpStatus()
if self.application.bottom_widgets:
Rx1 = self.Rx4351.frequency * 1e-6
Rx2 = (self.ReturnVfoFloat() - self.Rx4351.frequency) * 1e-6
t = "Rx Div %d; ADF4351 %.6f + rx_tune %.6f = %.6f Tx Adf4351 %.6f AD9951 %.6f" % (
2**self.Rx4351.rf_divider, Rx1, Rx2, Rx1 + Rx2, self.Tx4351.frequency * 1e-6, self.ad9951_freq * 1e-6)
self.application.bottom_widgets.UpdateText(t)
return tx_freq, vfo_freq
def RepeaterOffset(self, offset=None): # Change frequency for repeater offset during Tx
if offset is None: # Return True if frequency change is complete
self.HeartBeat()
return self.want_rx_udp_status[16:] == self.got_tx_udp_status[16:]
if offset == 0: # Change back to the original frequency
if self.repeater_freq is None: # Frequency was already reset
return self.want_rx_udp_status[16:] == self.got_tx_udp_status[16:]
self.ChangeFrequency(self.repeater_freq, self.vfo_frequency)
self.repeater_freq = None
else: # Shift to repeater input frequency
self.repeater_freq = self.tx_frequency
offset = int(offset * 1000) # Convert kHz to Hz
self.ChangeFrequency(self.tx_frequency + offset, self.vfo_frequency)
return False
def ReturnVfoFloat(self): # Return the accurate VFO as a float
rx_phase1 = int((self.vfo_frequency - self.Rx4351.frequency) / self.conf.rx_udp_clock * 2.0**32 + 0.5)
rx_tune_freq = float(rx_phase1) * self.conf.rx_udp_clock / 2.0**32
return self.Rx4351.frequency + rx_tune_freq
def open(self):
##self.application.config_screen.config.tx_phase.Enable(1)
# Create the proper broadcast address for rx_udp_ip.
nm = self.conf.rx_udp_ip_netmask.split('.')
ip = self.conf.rx_udp_ip.split('.')
nm = list(map(int, nm))
ip = list(map(int, ip))
bc = ''
for i in range(4):
x = (ip[i] | ~ nm[i]) & 0xFF
bc = bc + str(x) + '.'
self.broadcast_addr = bc[:-1]
# This socket is used for the Simple Network Discovery Protocol by AE4JY
self.socket_sndp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket_sndp.setblocking(0)
self.socket_sndp.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.sndp_request = bytearray()
self.sndp_request.append(56)
self.sndp_request.append(0)
self.sndp_request.append(0x5A)
self.sndp_request.append(0xA5)
self.sndp_request += bytearray(52)
self.sndp_rx_active = True
# conf.rx_udp_port is used for returning ADC samples
# conf.rx_udp_port + 1 is used for control
self.rx_udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.rx_udp_socket.setblocking(0)
self.rx_udp_socket.connect((self.conf.rx_udp_ip, self.conf.rx_udp_port + 1))
# conf.tx_audio_port + 1 is used for control
if self.conf.tx_ip:
self.sndp_tx_active = True
self.tx_udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.tx_udp_socket.setblocking(0)
self.tx_udp_socket.connect((self.conf.tx_ip, self.conf.tx_audio_port + 1))
else:
self.sndp_tx_active = False
QS.change_rates(96000, 0, 96000, 0)
self.application.test1Button.Enable(0)
return QS.open_rx_udp(self.conf.rx_udp_ip, self.conf.rx_udp_port)
def close(self):
if self.rx_udp_socket:
self.rx_udp_socket.close()
self.rx_udp_socket = None
if self.tx_udp_socket:
self.tx_udp_socket.close()
self.tx_udp_socket = None
def PrintStatus(self, msg, string):
print (msg, ' ', end=' ')
print (string[0:2], end=' ')
for c in string[2:]:
print ("%2X" % ord(c), end=' ')
print ()
def GetFirmwareVersion(self):
return self.firmware_version
def ChangeMode(self, mode):
# mode is a string: "USB", "AM", etc.
if mode in ("CWL", "CWU"):
self.mode_is_cw = 1
else:
self.mode_is_cw = 0
self.NewUdpStatus()
def ChangeBand(self, band):
# band is a string: "60", "40", "WWV", etc.
self.band = band
try:
freq, DcI, DcQ = self.CorrectTxDc[band]
except KeyError:
DcI, DcQ = (0.0, 0.0)
self.NewUdpCorrect(DcI, DcQ)
self.preamp.ChangeBand(band)
def NewUdpCorrect(self, DcI, DcQ):
self.DcI = DcI
self.DcQ = DcQ
QS.set_udp_tx_correct(DcI, DcQ, 0.828)
self.NewUdpStatus()
def PrintUdpCorrect(self):
for band in self.CorrectTxDc:
freq, DcI, DcQ = self.CorrectTxDc[band]
print ("'%s':(%.1f, %.6f, %.6f)," % (band, freq, DcI, DcQ))
def OnButtonPTT(self, event):
btn = event.GetEventObject()
if btn.GetValue(): # Turn the software key bit on or off
self.button_PTT = 1
else:
self.button_PTT = 0
QS.set_key_down(self.button_PTT)
self.NewUdpStatus()
def OnSpot(self, level):
# level is -1 for Spot button Off; else the Spot level 0 to 1000.
pass
def Sndp(self): # AE4JY Simple Network Discovery Protocol - attempt to set the FPGA IP address
try:
self.socket_sndp.sendto(self.sndp_request, (self.broadcast_addr, 48321))
except:
if DEBUG:
traceback.print_exc()
return
for i in range(5):
try:
data = self.socket_sndp.recv(1024)
except:
break
if len(data) != 56:
continue
if data[5:17] == b'QuiskUHFR-v1':
ip = self.conf.rx_udp_ip.split('.')
ip = list(map(int, ip))
if data[37] == ip[3] and data[38] == ip[2] and data[39] == ip[1] and data[40] == ip[0]:
self.sndp_rx_active = False
if DEBUG: print("SNDP success for Rx")
else:
t = bytearray()
t += data[0:4]
t.append(2)
t += data[5:37]
t.append(ip[3])
t.append(ip[2])
t.append(ip[1])
t.append(ip[0])
t += bytearray(12)
t.append(self.conf.rx_udp_port & 0xFF)
t.append(self.conf.rx_udp_port >> 8)
t.append(0)
self.socket_sndp.sendto(t, (self.broadcast_addr, 48321))
elif data[5:17] == b'QuiskUHFT-v1':
if self.conf.tx_ip:
ip = self.conf.tx_ip.split('.')
ip = list(map(int, ip))
if data[37] == ip[3] and data[38] == ip[2] and data[39] == ip[1] and data[40] == ip[0]:
self.sndp_tx_active = False
if DEBUG: print("SNDP success for Tx")
else:
t = bytearray()
t += data[0:4]
t.append(2)
t += data[5:37]
t.append(ip[3])
t.append(ip[2])
t.append(ip[1])
t.append(ip[0])
t += bytearray(12)
t.append(self.conf.tx_audio_port & 0xFF)
t.append(self.conf.tx_audio_port >> 8)
t.append(0)
self.socket_sndp.sendto(t, (self.broadcast_addr, 48321))
def HeartBeat(self):
if self.sndp_rx_active or self.sndp_tx_active:
if DEBUG: print ("Try SNDP", self.sndp_rx_active, self.sndp_tx_active)
self.Sndp()
return # SNDP is required
for i in range(10):
try: # receive the Rx status if any
data = self.rx_udp_socket.recv(1024)
if DEBUG > 1:
self.PrintStatus(' gotRx ', data)
except:
break
else:
if data[0:2] == b'Sx':
self.got_rx_udp_status = data
if self.tx_udp_socket:
for i in range(10):
try: # receive the Tx status if any
data = self.tx_udp_socket.recv(1024)
if DEBUG > 1:
self.PrintStatus(' gotTx ', data)
except:
break
else:
if data[0:2] == b'Sx':
self.got_tx_udp_status = data
if self.want_rx_udp_status[16:] == self.got_rx_udp_status[16:]: # The first part returns information from the hardware
self.firmware_version = self.got_rx_udp_status[2] # Firmware version is returned here
self.Rx4351.changed = 0
else:
if DEBUG > 1:
self.PrintStatus('HaveRx', self.got_rx_udp_status[0:20])
self.PrintStatus('sendRx', self.want_rx_udp_status[0:20])
try:
self.rx_udp_socket.send(self.want_rx_udp_status)
except:
#traceback.print_exc()
pass
if not self.tx_udp_socket:
pass
elif self.want_rx_udp_status[16:] == self.got_tx_udp_status[16:]: # The first part returns information from the hardware
self.Tx4351.changed = 0
self.Tx9951_changed = 0
else:
if DEBUG > 1:
self.PrintStatus('HaveTx', self.got_rx_udp_status[0:20])
self.PrintStatus('sendTx', self.want_rx_udp_status[0:20])
try:
self.tx_udp_socket.send(self.want_rx_udp_status)
except:
#traceback.print_exc()
pass
if 0:
self.rx_udp_socket.send(b'Qs')
self.preamp.HeartBeat()
def VarDecimGetChoices(self): # return text labels for the control
return self.var_rates
def VarDecimGetLabel(self): # return a text label for the control
return "Sample rate ksps"
def VarDecimGetIndex(self): # return the current index
return self.index
def VarDecimRange(self):
return (48000, 1152000)
def VarDecimSet(self, index=None): # set decimation, return sample rate
if index is None: # initial call to set decimation before the call to open()
rate = self.application.vardecim_set # May be None or from different hardware
try:
rate = rate // 1000
if rate > 1152:
rate = 1152
index = self.var_rates.index(str(rate))
except:
rate = 192
index = self.var_rates.index(str(rate))
self.index = index
rate = self.var_rates[index]
if rate[-1] == 'X':
self.scan_enable = 1
self.scan_blocks = int(rate[0:-1])
self.scan_samples = self.application.fft_size
self.decim1 = 2
self.decim2 = 3
rate = 1152000 * self.scan_blocks
else:
self.scan_enable = 0
self.scan_blocks = 0
rate = int(rate)
rate = rate * 1000
self.SetDecim(rate)
vfo = self.vfo_frequency
self.vfo_frequency = -1
self.vfo_sample_rate = rate
self.ChangeFrequency(self.tx_frequency, vfo)
self.NewUdpStatus()
return rate
def SetDecim(self, rate):
# self.decim1, decim2, decim3 are the first, second, third decimations in the hardware
if rate >= 1152000:
self.decim1 = 2
elif rate >= 192000:
self.decim1 = 3
elif rate == 96000:
self.decim1 = 6
else:
self.decim1 = 12
self.decim2 = self.rx_udp_clock_nominal // rate // self.decim1 // self.decim3
def NewUdpStatus(self):
# Start of 16 bytes sent to the hardware:
s = bytearray()
s += b"Sx" # 0:2 Fixed string
s.append(0) # 2 Version number is returned here
s.append(0) # 3
s += bytes(12) # 4:16
# Start of 80 bytes of data sent to the hardware:
s.append( 6 - 1) # 0 Variable decimation less one channel 0 first
s.append(12 - 1) # 1 Variable decimation less one channel 0 second
s += struct.pack("<L", self.rx_phase0) # 2: 6 Channel zero Rx tune phase
s += struct.pack("<L", self.rx_phase1) # 6:10 Channel one Rx tune phase)
s.append(0x3 | self.scan_enable << 2 | self.Rx4351.changed << 3 |
self.Tx4351.changed << 4 | self.Tx9951_changed << 5 |
self.button_PTT << 6 | self.mode_is_cw << 7) # 10 Flags
# 0: enable samples on channel 0
# 1: enable samples on channel 1
# 2: enable scan on channel 1
# 3: the receive adf4351 registers have changed
# 4: the transmit adf4351 registers have changed
# 5: the transmit ad9951 register changed
# 6: the PTT button is down
# 7: the mode is CWU or CWL
s.append(self.scan_blocks) # 11 For scan, the number of frequency blocks
s += struct.pack("<H", self.scan_samples) # 12:14 For scan, the number of samples per block
s += struct.pack("<L", self.scan_phase) # 14:18 For scan, the tuning phase increment
s.append(self.decim1 - 1) # 18 Variable decimation less one channel 1 first
s.append(self.decim2 - 1) # 19 Variable decimation less one channel 1 second
s += self.Rx4351.regs # 20:44 Receive adf4351; six 32-bit registers, 24 bytes
s += self.Tx4351.regs # 44:68 Transmit adf4351; six 32-bit registers, 24 bytes
s += self.ad9951_data # 68:74 Transmit ad9951: data length, instruction, 4 bytes of data
DcI = int(self.DcI * 32767.0)
DcQ = int(self.DcQ * 32767.0)
s += struct.pack("<h", DcI) # 74:76 Transmit DC correction for I channel
s += struct.pack("<h", DcQ) # 76:78 Transmit DC correction for Q channel
s += bytearray(96 - len(s)) # Fixed length message 16 + 80
self.want_rx_udp_status = s
def NewAd9951(self, tx_freq):
# Fpfd = Fref / 2 / R = Fvco / N
# Fout = Fvco / 2**D and is twice the transmit frequency
# Fref = 2R(2**d)Fout / N
# Fout = Fref * N / 2 / R / 2**D
adf = self.Tx4351
freq = 2.0 * adf.r_counter * (2 ** adf.rf_divider) / adf.int_value * (tx_freq * 2.0)
phase = int(freq / self.tx_clock80 * 2.0**32 + 0.5)
try:
self.ad9951_data = bytearray()
self.ad9951_data.append(40)
self.ad9951_data += struct.pack("<L", phase )
self.ad9951_data.append(4)
except struct.error:
self.ad9951_data = bytearray(6)
self.ad9951_freq = float(phase) * self.tx_clock80 / 2 ** 32
##adf.frequency = 0.5 * self.ad9951_freq * adf.int_value / 2.0 / adf.r_counter / (2 ** adf.rf_divider)
self.Tx9951_changed = 1
def NewAdf4351(self, adf, vfo_freq):
# Set the adf4351 to the nearest integer-mode frequency
Fpfd = adf.clock / 2.0 / adf.r_counter
if adf.receiver:
vfo_freq += Fpfd * self.vfo_test / 8 # test the VFO at an offset from the center
vfo = vfo_freq * 2 # Local oscillator runs at 2X frequency
for div in range(0, 7):
Fvco = vfo * 2 ** div
if 2200E6 <= Fvco < 4400E6:
adf.rf_divider = div
adf.int_value = int(vfo * 2 ** div / Fpfd + 0.5)
break
else:
if vfo < 500e6:
adf.rf_divider = 6
adf.int_value = int(2200E6 / Fpfd)
else:
adf.rf_divider = 0
adf.int_value = int(4400E6 / Fpfd)
adf.frequency = 0.5 * Fpfd * adf.int_value / 2 ** div
if DEBUG:
print ("int, div, Fvco, ADF4351", adf.int_value, div, int(vfo * 2 ** div / 1e6), adf.frequency)
print ("New adf4351_freq", adf.frequency)
# Number of bits for each field:
# intNmode 1
# aux_rf_out 3
# rf_divider 3
# band_sel_clock_div 8
# r_counter 10 Fpfd = Fref / 2 / r_counter
# int_value 16 Fvco = Fpfd * (int_value + frac_value / modulus)
# frac_value 12
# modulus 12
reg = 0b00000000000000000000000000000000 # Register 0
reg = reg | adf.int_value << 15 | adf.frac_value << 3
s = struct.pack("<L", reg)
reg = 0b00001000000000001000000000000001 # Register 1
reg = reg | adf.modulus << 3
s += struct.pack("<L", reg)
reg = 0b00000001000000000001111001000010 # Register 2
reg = reg | adf.r_counter << 14 | adf.int_mode << 8 | adf.int_mode << 7
s += struct.pack("<L", reg)
reg = 0b00000000000001000000000000000011 # Register 3
reg = reg | adf.int_mode << 22 | adf.int_mode << 21
s += struct.pack("<L", reg)
reg = 0b00000000100000000000010000111100 # Register 4
reg = reg | adf.rf_divider << 20 | adf.band_sel_clock_div << 12 | adf.aux_rf_out << 6
s += struct.pack("<L", reg)
reg = 0b00000000010110000000000000000101 # Register 5
s += struct.pack("<L", reg)
adf.regs = s
adf.changed = 1
def TestVfoPlus(self, event):
self.vfo_test += 1
self.Rx4351.frequency = 1
self.ChangeFrequency(self.tx_frequency, self.vfo_frequency)
def TestVfoMinus(self, event):
self.vfo_test -= 1
self.Rx4351.frequency = 1
self.ChangeFrequency(self.tx_frequency, self.vfo_frequency)
+145
View File
@@ -0,0 +1,145 @@
# Please do not change this widgets module for Quisk. Instead copy
# it to your own quisk_widgets.py and make changes there.
#
# This module is used to add extra widgets to the QUISK screen.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import wx, time
import _quisk as QS
class BottomWidgets: # Add extra widgets to the bottom of the screen
def __init__(self, app, hardware, conf, frame, gbs, vertBox):
self.config = conf
self.hardware = hardware
self.application = app
self.correct_screen = None
self.num_rows_added = 1
start_row = app.widget_row # The first available row
start_col = app.button_start_col # The start of the button columns
b = app.QuiskPushbutton(frame, hardware.TestVfoPlus, 'Adf+')
bw, bh = b.GetMinSize()
gbs.Add(b, (start_row, start_col), flag=wx.EXPAND)
b = app.QuiskPushbutton(frame, hardware.TestVfoMinus, 'Adf-')
gbs.Add(b, (start_row, start_col + 1), flag=wx.EXPAND)
b = app.QuiskPushbutton(frame, self.OnBtnCorrect, 'Corr')
gbs.Add(b, (start_row, start_col + 2), flag=wx.EXPAND)
self.status_label = app.QuiskText(frame, 'Ready', bh)
gbs.Add(self.status_label, (start_row, start_col + 3), (1, 24), flag=wx.EXPAND)
def UpdateText(self, text):
self.status_label.SetLabel(text)
def OnBtnCorrect(self, event):
if self.correct_screen:
self.correct_screen.Raise()
else:
self.correct_screen = QCorrect(self, self.application.width)
class QCorrect(wx.Frame):
"""Create a window with DC adjustment controls"""
f_DcI = "DC adjustment for In-Phase %.6f"
f_DcQ = "DC adjustment for Quadrature %.6f"
def __init__(self, parent, width):
self.parent = parent
self.application = parent.application
self.config = parent.config
self.hardware = parent.hardware
t = "DC Null Adjustment"
wx.Frame.__init__(self, self.application.main_frame, -1, t, pos=(50, 100), style=wx.CAPTION)
panel = wx.Panel(self)
self.MakeControls(panel, width)
self.Show()
def MakeControls(self, panel, width): # Make controls for DC adjustment
self.old_DcI = self.DcI = self.hardware.DcI
self.old_DcQ = self.DcQ = self.hardware.DcQ
self.hardware.NewUdpCorrect(self.DcI, self.DcQ)
sl_max = width * 4 // 10 # maximum +/- value for slider
self.dc_scale = float(0.3) / sl_max
font = wx.Font(self.config.default_font_size, wx.FONTFAMILY_SWISS, wx.NORMAL,
wx.FONTWEIGHT_NORMAL, False, self.config.quisk_typeface)
chary = self.GetCharHeight()
y = chary * 3 // 10
self.t_DcI = wx.StaticText(panel, -1, self.f_DcI % self.old_DcI, pos=(0, y))
self.t_DcI.SetFont(font)
y += self.t_DcI.GetSize().GetHeight()
self.DcI1 = wx.Slider(panel, -1, 0, -sl_max, sl_max,
pos=(0, y), size=(width, -1))
y += self.DcI1.GetSize().GetHeight()
self.DcI2 = wx.Slider(panel, -1, 0, -sl_max, sl_max,
pos=(0, y), size=(width, -1))
y += self.DcI2.GetSize().GetHeight()
self.PositionDcI(self.old_DcI)
self.t_DcQ = wx.StaticText(panel, -1, self.f_DcQ % self.old_DcQ, pos=(0, y))
self.t_DcQ.SetFont(font)
y += self.t_DcQ.GetSize().GetHeight()
self.DcQ1 = wx.Slider(panel, -1, 0, -sl_max, sl_max,
pos=(0, y), size=(width, -1))
y += self.DcQ1.GetSize().GetHeight()
self.DcQ2 = wx.Slider(panel, -1, 0, -sl_max, sl_max,
pos=(0, y), size=(width, -1))
y += self.DcQ2.GetSize().GetHeight()
sv = self.application.QuiskPushbutton(panel, self.OnBtnSave, 'Save')
cn = self.application.QuiskPushbutton(panel, self.OnBtnCancel, 'Cancel')
w, h = cn.GetSize().Get()
sv.SetSize((w, h))
y += h // 4
x = (width - w * 3) // 4
sv.SetPosition((x, y))
cn.SetPosition((x*3 + w*2, y))
sv.SetBackgroundColour('light blue')
cn.SetBackgroundColour('light blue')
y += h
y += h // 4
self.DcI1.SetBackgroundColour('aquamarine')
self.DcI2.SetBackgroundColour('orange')
self.DcQ1.SetBackgroundColour('aquamarine')
self.DcQ2.SetBackgroundColour('orange')
self.PositionDcQ(self.old_DcQ)
self.SetClientSize(wx.Size(width, y))
self.DcI1.Bind(wx.EVT_SCROLL, self.OnChange)
self.DcI2.Bind(wx.EVT_SCROLL, self.OnFineDcI)
self.DcQ1.Bind(wx.EVT_SCROLL, self.OnChange)
self.DcQ2.Bind(wx.EVT_SCROLL, self.OnFineDcQ)
def PositionDcI(self, dc): # set pos1, pos2 for I
pos2 = round(dc / self.dc_scale)
remain = dc - pos2 * self.dc_scale
pos1 = round(remain / self.dc_scale * 50.0)
self.DcI1.SetValue(pos1)
self.DcI2.SetValue(pos2)
def PositionDcQ(self, dc): # set pos1, pos2 for Q
pos2 = round(dc / self.dc_scale)
remain = dc - pos2 * self.dc_scale
pos1 = round(remain / self.dc_scale * 50.0)
self.DcQ1.SetValue(pos1)
self.DcQ2.SetValue(pos2)
def OnChange(self, event):
dc = self.dc_scale * self.DcI1.GetValue() / 50.0 + self.dc_scale * self.DcI2.GetValue()
if abs(dc) < self.dc_scale * 3.0 / 50.0:
dc = 0.0
self.t_DcI.SetLabel(self.f_DcI % dc)
self.DcI = dc
dc = self.dc_scale * self.DcQ1.GetValue() / 50.0 + self.dc_scale * self.DcQ2.GetValue()
if abs(dc) < self.dc_scale * 3.0 / 50.0:
dc = 0.0
self.t_DcQ.SetLabel(self.f_DcQ % dc)
self.DcQ = dc
self.hardware.NewUdpCorrect(self.DcI, self.DcQ)
def OnFineDcI(self, event): # re-center the fine slider when the coarse slider is adjusted
dc = self.dc_scale * self.DcI1.GetValue() / 50.0 + self.dc_scale * self.DcI2.GetValue()
self.PositionDcI(dc)
self.OnChange(event)
def OnFineDcQ(self, event): # re-center the fine slider when the coarse slider is adjusted
dc = self.dc_scale * self.DcQ1.GetValue() / 50.0 + self.dc_scale * self.DcQ2.GetValue()
self.PositionDcQ(dc)
self.OnChange(event)
def OnBtnSave(self, event):
self.hardware.CorrectTxDc[self.hardware.band] = (self.hardware.tx_frequency * 1E-6, self.DcI, self.DcQ)
self.hardware.PrintUdpCorrect()
self.parent.correct_screen = None
self.Destroy()
def OnBtnCancel(self, event=None):
self.hardware.NewUdpCorrect(self.old_DcI, self.old_DcQ)
self.parent.correct_screen = None
self.Destroy()