# 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().