149 lines
8.5 KiB
Python
149 lines
8.5 KiB
Python
|
# Please do not change this hardware control module for Quisk.
|
||
|
# You should use it as a base class for your own hardware modules.
|
||
|
|
||
|
# A custom hardware module should subclass this module; start it with:
|
||
|
# from quisk_hardware_model import Hardware as BaseHardware
|
||
|
# class Hardware(BaseHardware):
|
||
|
# def __init__(self, app, conf):
|
||
|
# BaseHardware.__init__(self, app, conf)
|
||
|
# ### your module starts here
|
||
|
|
||
|
from __future__ import print_function
|
||
|
from __future__ import absolute_import
|
||
|
from __future__ import division
|
||
|
|
||
|
import _quisk as QS
|
||
|
|
||
|
class Hardware:
|
||
|
def __init__(self, app, conf):
|
||
|
self.application = app # Application instance (to provide attributes)
|
||
|
self.conf = conf # Config file module
|
||
|
self.rf_gain_labels = () # Do not add the Rf Gain button
|
||
|
self.correct_smeter = conf.correct_smeter # Default correction for S-meter
|
||
|
self.use_sidetone = conf.use_sidetone # Copy from the config file
|
||
|
self.transverter_offset = 0 # Calculate the transverter offset in Hertz for each band
|
||
|
def pre_open(self): # Quisk calls this once before open() is called
|
||
|
pass
|
||
|
def open(self): # Quisk calls this once to open the Hardware
|
||
|
# Return an informative message for the config screen.
|
||
|
# This method must return a string showing whether the open succeeded or failed.
|
||
|
t = "Capture from sound card %s." % self.conf.name_of_sound_capt
|
||
|
return t
|
||
|
def close(self): # Quisk calls this once to close the Hardware
|
||
|
pass
|
||
|
def ChangeFrequency(self, tune, vfo, source='', band='', event=None):
|
||
|
# Change and return the tuning and VFO frequency in Hertz. The VFO frequency is the
|
||
|
# frequency in the center of the display; that is, the RF frequency corresponding to an
|
||
|
# audio frequency of zero Hertz. The tuning frequency is the RF frequency indicated by
|
||
|
# the tuning line on the display, and is equivalent to the transmit frequency. The quisk
|
||
|
# receive frequency is the tuning frequency plus the RIT (receive incremental tuning).
|
||
|
# If your hardware will not change to the requested frequencies, return different
|
||
|
# frequencies.
|
||
|
# The source is a string indicating the source of the change:
|
||
|
# BtnBand A band button
|
||
|
# BtnUpDown The band Up/Down buttons
|
||
|
# FreqEntry The user entered a frequency in the box
|
||
|
# MouseBtn1 Left mouse button press
|
||
|
# MouseBtn3 Right mouse button press
|
||
|
# MouseMotion The user is dragging with the left button
|
||
|
# MouseWheel The mouse wheel up/down
|
||
|
# NewDecim The decimation changed
|
||
|
# For "BtnBand", the string band is in the band argument.
|
||
|
# For the mouse events, the handler event is in the event argument.
|
||
|
return tune, vfo
|
||
|
def ReturnFrequency(self):
|
||
|
# Return the current tuning and VFO frequency. If neither have changed,
|
||
|
# you can return (None, None). This is called at about 10 Hz by the main.
|
||
|
# return (tune, vfo) # return changed frequencies
|
||
|
return None, None # frequencies have not changed
|
||
|
def ReturnVfoFloat(self):
|
||
|
# Return the accurate VFO frequency as a floating point number.
|
||
|
# You can return None to indicate that the integer VFO frequency is valid.
|
||
|
return None
|
||
|
def ChangeMode(self, mode): # Change the tx/rx mode
|
||
|
# mode is a string: "USB", "AM", etc.
|
||
|
pass
|
||
|
def ChangeBand(self, band):
|
||
|
# band is a string: "60", "40", "WWV", etc.
|
||
|
try:
|
||
|
self.transverter_offset = self.conf.bandTransverterOffset[band]
|
||
|
except:
|
||
|
self.transverter_offset = 0
|
||
|
def OnBtnFDX(self, is_fdx): # Status of FDX button, 0 or 1
|
||
|
pass
|
||
|
def HeartBeat(self): # Called at about 10 Hz by the main
|
||
|
pass
|
||
|
# The "VarDecim" methods are used to change the hardware decimation rate.
|
||
|
# If VarDecimGetChoices() returns any False value, no other methods are called.
|
||
|
def VarDecimGetChoices(self): # Return a list/tuple of strings for the decimation control.
|
||
|
return False # Return a False value for no decimation changes possible.
|
||
|
def VarDecimGetLabel(self): # Return a text label for the decimation control.
|
||
|
return ''
|
||
|
def VarDecimGetIndex(self): # Return the index 0, 1, ... of the current decimation.
|
||
|
return 0 # This is called before open() to initialize the control.
|
||
|
def VarDecimSet(self, index=None): # Called when the control is operated.
|
||
|
# Change the decimation here, and return the sample rate. The index is 0, 1, 2, ....
|
||
|
# Called with index == None before open() to set the initial sample rate.
|
||
|
# Note: The last used sample rate is available as self.application.vardecim_set if
|
||
|
# the persistent state option is True. If the value is unavailable for
|
||
|
# any reason, self.application.vardecim_set is None.
|
||
|
return 48000
|
||
|
def VarDecimRange(self): # Return the lowest and highest sample rate.
|
||
|
return (48000, 960000)
|
||
|
#
|
||
|
# The following methods are used to return I/Q samples from the hardware file to Quisk.
|
||
|
# None of these methods are called unless you call InitSamples().
|
||
|
# For an example of their use, see quisk_hardware_sdriq.py.
|
||
|
# Quisk calls all methods in the hardware file from the GUI thread except for StartSamples(),
|
||
|
# GetRxSamples() and StopSamples() which are called from the sound thread.
|
||
|
# The sound thread starts with StartSamples() and is not running during the calls to pre_open() and open().
|
||
|
def InitSamples(self, int_size, endian): # Rx sample initialization; you must call this from your hardware __init__().
|
||
|
# int_size is the number of bytes in each I or Q sample: 1, 2, 3, or 4
|
||
|
# endian is the order of bytes in the sample: 0 == little endian; 1 == big endian
|
||
|
# This can be called again to change the format. For example, a different number of bytes for different sample rates.
|
||
|
QS.set_params(rx_bytes=int_size, rx_endian=endian)
|
||
|
self.application.samples_from_python = True
|
||
|
def InitBscope(self, int_size, endian, clock, length): # Bandscope initialization; accept raw samples from the ADC
|
||
|
# You may call this once from your hardware __init__() after calling InitSamples(). The bandscope format can not be changed.
|
||
|
# int_size is the number of bytes in each sample: 1, 2, 3, or 4
|
||
|
# endian is the order of bytes in the sample: 0 == little endian; 1 == big endian
|
||
|
# clock is the integer ADC sample rate in Hertz
|
||
|
# length is the number of samples in each block of ADC samples, and equals the FFT size.
|
||
|
QS.set_params(bscope_bytes=int_size, bscope_endian=endian, bscope_size=length)
|
||
|
self.application.bandscope_clock = clock
|
||
|
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
|
||
|
def StopSamples(self): # Quisk calls this from the sound thread to stop sending samples.
|
||
|
pass
|
||
|
def GetRxSamples(self): # Quisk calls this frequently from the sound thread. Poll your hardware for samples.
|
||
|
# Return any available samples by calling AddRxSamples() and perhaps AddBscopeSamples() from within this method.
|
||
|
pass
|
||
|
def AddRxSamples(self, samples): # Call this from within GetRxSamples() to record the Rx samples.
|
||
|
# "samples" is int_size of integer I data followed by int_size of integer Q data, repeated.
|
||
|
# For Python 3, "samples" must be a byte array or bytes; use s = bytearray(2), or s = b"\x55\x44" or similar.
|
||
|
# For Python 2, "samples" must be a byte array or bytes or a string.
|
||
|
# The byte length must represent a whole number of samples. No partial records.
|
||
|
QS.add_rx_samples(samples)
|
||
|
def AddBscopeSamples(self, samples): # Call this from within GetRxSamples() to record the bandscope samples.
|
||
|
# "samples" is the whole block of integer samples from the ADC.
|
||
|
# For Python 3, "samples" must be a byte array or bytes; use s = bytearray(2), or s = b"\x55\x44" or similar.
|
||
|
# For Python 2, "samples" must be a byte array or bytes or a string.
|
||
|
# The number of bytes in "samples" must equal the block length times the bytes per sample.
|
||
|
QS.add_bscope_samples(samples)
|
||
|
def GotClip(self): # Call this to indicate that samples were received with the clip (overrange) indicator true.
|
||
|
QS.set_params(clip=1)
|
||
|
def GotReadError(self, print_msg, msg): # Call this to indicate an error in receiving the Rx samples.
|
||
|
if print_msg:
|
||
|
print(msg)
|
||
|
QS.set_params(read_error=1)
|
||
|
# If you import wx, there a few useful functions available. To set a busy cursor do this:
|
||
|
# try:
|
||
|
# wx.BeginBusyCursor()
|
||
|
# wx.Yield()
|
||
|
# self.ReallyTimeConsumingOperation()
|
||
|
# finally:
|
||
|
# wx.EndBusyCursor()
|
||
|
# To update the GUI during a long running operation, you can use wx.Yield() or wx.SafeYield().
|
||
|
|