diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 75e33ab..f6e6192 100755
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,12 @@
+Quisk Version 4.1.54 March 2020
+================================
+There are now two new radios available in Quisk. David Fainitski contributed code for the Sdr Micron,
+and Andrea Montefusco IW0HDV contributed code for the Perseus SDR. The radios should appear on the
+list of supported radios in Config/Radios screen. Check the Config/radio/Hardware screen to see if you
+need any more options. Please test these radios to make sure everything is working.
+
+I added a general way to add new radios to the configuration screens. See docs.html under Custom Hardware.
+
Quisk Version 4.1.53 March 2020
================================
I changed the Afedri radio module to be compatible with Python3. The graph Zoom feature now is available
@@ -8,7 +17,7 @@ noise floor such as PlutoSDR. You will have to readjust your waterfall colors.
I added a hardware setting for the LNA gain during transmit for the HL2. I made some changes to the CW
and PTT hardware interface for the HL2. These work great with the HL2, but test the new code if you have
-diferent Hermes hardware such as Red Pitaya.
+different Hermes hardware such as Red Pitaya.
Quisk Version 4.1.52 December 2019
===================================
diff --git a/MANIFEST.in b/MANIFEST.in
index dcd2e88..2084fb0 100755
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -7,4 +7,6 @@ graft n2adr
graft sdriqpkg
graft softrock
graft soapypkg
+graft sdrmicronpkg
+graft perseuspkg
global-exclude *.pyc
diff --git a/PKG-INFO b/PKG-INFO
index 9163563..909e803 100755
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: quisk
-Version: 4.1.53
+Version: 4.1.54
Summary: QUISK is a Software Defined Radio (SDR) transceiver that can control various radio hardware.
Home-page: http://james.ahlstrom.name/quisk/
Author: James C. Ahlstrom
diff --git a/__init__.py b/__init__.py
index d47ec66..9af07ff 100755
--- a/__init__.py
+++ b/__init__.py
@@ -1 +1 @@
-#Quisk version 4.1.53
+#Quisk version 4.1.54
diff --git a/configure.py b/configure.py
index 4714992..3e37718 100755
--- a/configure.py
+++ b/configure.py
@@ -157,8 +157,16 @@ class Configuration:
try:
if fmt4 == 'text': # Note: JSON returns Unicode strings !!!
setattr(conf, k, v)
- elif fmt4 in ('dict', 'list'):
- setattr(conf, k, v)
+ elif fmt4 == 'dict':
+ if isinstance(v, dict):
+ setattr(conf, k, v)
+ else:
+ raise ValueError()
+ elif fmt4 == 'list':
+ if isinstance(v, list):
+ setattr(conf, k, v)
+ else:
+ raise ValueError()
elif fmt4 == 'inte':
setattr(conf, k, int(v, base=0))
elif fmt4 == 'numb':
@@ -448,13 +456,27 @@ class Configuration:
# Then some help text starting with "# "
# Then a list of possible value#explain with the default first
# Then a blank line to end.
-
self.format4name = {}
self.format4name['hardware_file_type'] = 'text'
+ self._ParserConf('quisk_conf_defaults.py')
+ # Read any user-defined radio types
+ for dirname in os.listdir('.'):
+ if not os.path.isdir(dirname) or dirname[-3:] != 'pkg':
+ continue
+ if dirname in ('freedvpkg', 'sdriqpkg', 'soapypkg'):
+ continue
+ filename = os.path.join(dirname, 'quisk_hardware.py')
+ if not os.path.isfile(filename):
+ continue
+ try:
+ self._ParserConf(filename)
+ except:
+ traceback.print_exc()
+ def _ParserConf(self, filename):
re_AeqB = re.compile("^#?(\w+)\s*=\s*([^#]+)#*(.*)") # item values "a = b"
section = None
data_name = None
- fp = open("quisk_conf_defaults.py", "r")
+ fp = open(filename, "r")
for line in fp:
line = line.strip()
if not line:
@@ -486,7 +508,7 @@ class Configuration:
value_list = []
if data_name in self.format4name:
if self.format4name[data_name] != fmt:
- print ("Inconsistent format for", data_name, self.format4name[data_name], fmt)
+ print (filename, ": Inconsistent format for", data_name, self.format4name[data_name], fmt)
else:
self.format4name[data_name] = fmt
section_data.append([data_name, dspl, fmt, '', value_list])
@@ -495,7 +517,7 @@ class Configuration:
mo = re_AeqB.match(line)
if mo:
if data_name != mo.group(1):
- print ("Parse error for", data_name)
+ print (filename, ": Parse error for", data_name)
continue
value = mo.group(2).strip()
expln = mo.group(3).strip()
diff --git a/docs.html b/docs.html
index 3c1092d..4365627 100755
--- a/docs.html
+++ b/docs.html
@@ -50,6 +50,7 @@ div.contents {
Configuration
Sound Cards
SDR-IQ
+Perseus
Timing
USB Control
Custom Hardware
@@ -80,6 +81,7 @@ complete transceiver. Quisk works with this hardware:
SoftRock connected to the sound card
Many other SDR's connected to the sound card
SDR-IQ connected by USB
+Perseus connected by USB
N2ADR hardware connected by Ethernet and IP
HiQSDR hardware connected by Ethernet and IP
The Hermes-Lite project at hermeslite.com
@@ -874,6 +876,35 @@ In earlier versions of Windows, port names are COM1, COM2 etc. and use the "USB
Windows should find this driver by itself.
+Perseus as Input
+
+Quisk can use an Perseus HF receiver from Microtelecom instead of a sound card as input.
+Set up a radio of type Perseus. The Perseus uses a native USB interface to connect to Quisk.
+The Quisk perseuspkg extension relies on libperseus-sdr
+ open source library to manage Perseus hardware and receive the I/Q samples stream.
+
+
+
+Follow the instruction into GitHub repository to compile and install the library.
+On Suse distribution the library is available as binary package.
+Next compile the perseuspkg using the command:
+
+
+
+make perseus3
+
+
+
+The several sample rates can be selected opening Config panel: in
+the Config tab there is the Samples rates dropdown.
+
+The input analog filter can be switched in using the button Wideband.
+The input attenuator is operate via the button RF, that allows to select
+the four attenuator steps.
+The ADC commands for dithering and preamplifier are found on
+left bottom corner as ADC Dither and ADC Preamp.
+
+
Timing
There are several configuration parameters devoted to tuning; read the
@@ -988,6 +1019,7 @@ can put code there to poll a serial port or to perform other
housekeeping functions (try to be efficient). The two remaining
functions deserve more documentation.
+
ChangeFrequency(self, tune, vfo, source='', band='', event=None)
Quisk calls the ChangeFrequency() function when the user changes the Tx
frequency with a mouse click on the graph or waterfall, with the entry
@@ -1117,6 +1149,29 @@ return the new frequencies from ReturnFrequency() or else Quisk will be
unaware of the change.
+
+Adding Custom Hardware to the Config/Radios Screen
+Once you write your own hardware file for your custom hardware, you still need to add it to the
+list of available radio types, and add its configuration options to its Config/radios/Hardware screen.
+First create a subdirectory of the Quisk directory with a name ending in "pkg"; for example,
+"myradiopkg". Then put your hardware file in this subdirectory with the name "quisk_hardware.py".
+You will need at least one configuration option to specify the hardware file name.
+Add this code (all comments) near the top of quisk_hardware.py:
+
+
+# Define the name of the hardware and the items on the hardware screen (see quisk_conf_defaults.py):
+################ Receivers MyRadio, The special radio that I own
+## hardware_file_name Hardware file path, rfile
+# This is the file that contains the control logic for each radio.
+#hardware_file_name = 'myradiopkg/quisk_hardware.py'
+
+
+Of course, change the names of the radio and subdirectory as appropriate. Your radio of general type "MyRadio"
+will now appear in the list of radios on the Config/Radios screen, and its configuration items will appear
+on the Config/radio/Hardware screen. You can add additional items. See quisk_conf_defaults.py and the *pkg
+subdirectories for examples.
+
+
Extension Packages
Quisk comes with two extension packages. The freedvpkg package
diff --git a/makefile b/makefile
index 078bf1f..87903ac 100755
--- a/makefile
+++ b/makefile
@@ -7,11 +7,13 @@ quisk2:
python2 setup.py build_ext --force --inplace
@echo
@echo 'Use "make soapy2" to make the Python2 soapy module'
+ @echo 'Use "make perseus2" to make the Python2 perseus package'
quisk3:
python3 setup.py build_ext --force --inplace
@echo
@echo 'Use "make soapy3" to make the Python3 soapy module'
+ @echo 'Use "make perseus3" to make the Python3 perseus package'
soapy2:
(cd soapypkg; make soapy2)
@@ -19,5 +21,11 @@ soapy2:
soapy3:
(cd soapypkg; make soapy3)
+perseus2:
+ (cd perseuspkg; make perseus2)
+
+perseus3:
+ (cd perseuspkg; make perseus3)
+
macports:
env ARCHFLAGS="-arch x86_64" python setup.py build_ext --force --inplace -D USE_MACPORTS
diff --git a/perseuspkg/README.txt b/perseuspkg/README.txt
new file mode 100755
index 0000000..b6fe841
--- /dev/null
+++ b/perseuspkg/README.txt
@@ -0,0 +1,10 @@
+Microtelecom Perseus HF receiver
+
+Prerequisite are:
+
+libusb-1.0-0-dev (found in all major Linux distro)
+
+libperseus-sdr as found in https://github.com/Microtelecom/libperseus-sdr/archive/master.zip
+
+
+
diff --git a/perseuspkg/__init__.py b/perseuspkg/__init__.py
new file mode 100755
index 0000000..792d600
--- /dev/null
+++ b/perseuspkg/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/perseuspkg/makefile b/perseuspkg/makefile
new file mode 100755
index 0000000..08b7f11
--- /dev/null
+++ b/perseuspkg/makefile
@@ -0,0 +1,7 @@
+
+perseus2:
+ python2 setup.py build_ext --force --inplace
+
+perseus3:
+ python3 setup.py build_ext --force --inplace
+
diff --git a/perseuspkg/perseus.c b/perseuspkg/perseus.c
new file mode 100755
index 0000000..2bfb534
--- /dev/null
+++ b/perseuspkg/perseus.c
@@ -0,0 +1,498 @@
+/*
+ *
+ * Microtelecom perseus HF receiver
+ *
+ * access module: exposes Python functions needed in quisk_hardware.py
+ * to control hardware
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define IMPORT_QUISK_API
+#include "quisk.h"
+#include "filter.h"
+
+// This module was written by Andrea Montefusco IW0HDV.
+
+typedef union {
+ struct {
+ int32_t i;
+ int32_t q;
+ } __attribute__((__packed__)) iq;
+ struct {
+ uint8_t i1;
+ uint8_t i2;
+ uint8_t i3;
+ uint8_t i4;
+ uint8_t q1;
+ uint8_t q2;
+ uint8_t q3;
+ uint8_t q4;
+ } __attribute__((__packed__)) ;
+} iq_sample;
+
+
+// buffer size for libperseus-sdr
+const static int nb = 6;
+const static int bs = 1024;
+
+
+// This module uses the Python interface to import symbols from the parent _quisk
+// extension module. It must be linked with import_quisk_api.c. See the documentation
+// at the start of import_quisk_api.c.
+
+#define DEBUG 1
+
+static int num_perseus = 0;
+static perseus_descr *descr = 0;
+static int sr = 48000;
+static float freq = 7050000.0;
+static int adc_dither = 0;
+static int adc_preamp = 0;
+
+static void quisk_stop_samples(void);
+
+static const char *fname = "/tmp/quiskperseus";
+static int rfd = 0;
+static int wfd = 0;
+static int running = 0;
+static int wb_filter = 0;
+
+// Called in a loop to read samples; called from the sound thread.
+static int quisk_read_samples(complex double * cSamples)
+{
+ //fprintf (stderr, "r"); fflush(stderr);
+
+ int n = read(rfd, cSamples, sizeof(complex double)*SAMP_BUFFER_SIZE);
+ //fprintf(stderr, "%d ", n);
+ if (n >= 0)
+ return n/sizeof(complex double); // return number of samples
+ else
+ return 0;
+}
+
+// Called in a loop to write samples; called from the sound thread.
+static int quisk_write_samples(complex double * cSamples, int nSamples)
+{
+ return 0;
+}
+
+
+//
+// callback that writes in the output pipe IQ values as
+// complex floating point
+//
+static int user_data_callback_c_f(void *buf, int buf_size, void *extra)
+{
+ // The buffer received contains 24-bit IQ samples (6 bytes per sample)
+ // Here we save the received IQ samples as 32 bit
+ // (msb aligned) integer IQ samples.
+
+ uint8_t *samplebuf = (uint8_t*)buf;
+ int nSamples = buf_size/6;
+ int k;
+ iq_sample s;
+
+ // the 24 bit data is scaled to a 32bit value (so that the machine's
+ // natural signed arithmetic will work)
+ for (k=0; k < nSamples; k++) {
+ s.i1 = s.q1 = 0;
+ s.i2 = *samplebuf++;
+ s.i3 = *samplebuf++;
+ s.i4 = *samplebuf++;
+ s.q2 = *samplebuf++;
+ s.q3 = *samplebuf++;
+ s.q4 = *samplebuf++;
+
+ // move I/Q to complex number
+ complex double x = (double)(s.iq.i)*10 + (double)(s.iq.q)*10 * _Complex_I;
+ if (wfd > 0) {
+ int n = write(wfd, &x, sizeof(complex double));
+ if (n<0 && ! -EAGAIN )
+ fprintf(stderr, "perseus c: Can't write output file: %s, descriptor: %d\n", strerror(errno), wfd);
+ }
+ }
+ return 0;
+}
+
+
+
+// Start sample capture; called from the sound thread.
+static void quisk_start_samples(void)
+{
+ fprintf (stderr, "perseus c: quisk_start_samples\n"); fflush(stderr);
+
+ int rc = mkfifo(fname, 0666);
+
+ if ((rc == -1) && (errno != EEXIST)) {
+ perror("perseus c: Error creating the named pipe");
+ }
+
+ rfd = open(fname, O_RDONLY|O_NONBLOCK);
+ if (rfd < 0) fprintf(stderr, "perseus c: Can't open read FIFO (%s)\n", strerror(errno));
+ else fprintf(stderr, "perseus c: read FIFO (%d)\n", rfd);
+
+ wfd = open(fname, O_WRONLY|O_NONBLOCK);
+ if (wfd < 0) fprintf(stderr, "perseus c: Can't open write FIFO (%s)\n", strerror(errno));
+ else fprintf(stderr, "perseus c: write FIFO (%d)\n", wfd);
+
+ if (perseus_set_sampling_rate(descr, sr) < 0) { // specify the sampling rate value in Samples/second
+ fprintf(stderr, "perseus c: fpga configuration error: %s\n", perseus_errorstr());
+ } else {
+ fprintf(stderr, "perseus c: sampling rate set to: %d\n", sr);
+
+ // Re-enable preselection filters (WB_MODE Off)
+ perseus_set_ddc_center_freq(descr, freq, wb_filter);
+ // start sampling ops
+ if (perseus_start_async_input(descr, nb*bs, user_data_callback_c_f, 0)<0) {
+ fprintf(stderr, "perseus c: start async input error: %s\n", perseus_errorstr());
+ } else
+ fprintf(stderr, "perseus c: start async input\n");
+
+ running = 1;
+ }
+}
+
+// Stop sample capture; called from the sound thread.
+static void quisk_stop_samples(void)
+{
+ fprintf (stderr, "perseus c: quisk_stop_samples\n"); fflush(stderr);
+
+ // We stop the acquisition...
+ fprintf(stderr, "perseus c: stopping async data acquisition...\n");
+ perseus_stop_async_input(descr);
+ running = 0;
+ // clearing FIFO...
+ close(rfd);
+ close(wfd);
+ unlink(fname);
+}
+
+
+// Called to close the sample source; called from the GUI thread.
+static PyObject * close_device(PyObject * self, PyObject * args)
+{
+ fprintf (stderr, "perseus c: close_device\n");
+ int sample_device; // for now one only Perseus can be managed
+
+ if (!PyArg_ParseTuple (args, "i", &sample_device))
+ return NULL;
+
+ if (descr) {
+ // We stop the acquisition...
+ if (running) {
+ perseus_stop_async_input(descr);
+ running = 0;
+ }
+ perseus_close(descr);
+ descr = 0;
+ }
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+// Called to open the Perseus SDR device; called from the GUI thread.
+static PyObject * open_device(PyObject * self, PyObject * args)
+{
+ char buf128[128] = "Capture Microtelecom Perseus HF receiver";
+ eeprom_prodid prodid;
+
+ fprintf (stderr, "perseus c: open device (%d)\n", num_perseus); fflush(stderr);
+
+
+ // Check how many Perseus receivers are connected to the system
+ if (num_perseus == 0) num_perseus = perseus_init();
+ fprintf(stderr, "perseus c: %d Perseus receivers found\n",num_perseus);
+
+ if (num_perseus == 0) {
+ sprintf(buf128, "No Perseus receivers detected\n");
+ perseus_exit();
+ goto main_cleanup;
+ }
+
+ // Open the first one...
+ if ((descr = perseus_open(0)) == NULL) {
+ sprintf(buf128, "error: %s\n", perseus_errorstr());
+ fprintf(stderr, "perseus c: open error: %s\n", perseus_errorstr());
+ goto main_cleanup;
+ }
+
+ // Download the standard firmware to the unit
+ fprintf(stderr, "perseus c: Downloading firmware...\n");
+ if (perseus_firmware_download(descr,NULL)<0) {
+ sprintf(buf128, "perseus c: firmware download error: %s", perseus_errorstr());
+ goto main_cleanup;
+ }
+ // Dump some information about the receiver (S/N and HW rev)
+ if (perseus_is_preserie(descr, 0) == PERSEUS_SNNOTAVAILABLE)
+ fprintf(stderr, "perseus c: The device is a preserie unit");
+ else
+ if (perseus_get_product_id(descr,&prodid)<0)
+ fprintf(stderr, "perseus c: get product id error: %s", perseus_errorstr());
+ else
+ fprintf(stderr, "perseus c: Receiver S/N: %05d-%02hX%02hX-%02hX%02hX-%02hX%02hX - HW Release:%hd.%hd\n",
+ (uint16_t) prodid.sn,
+ (uint16_t) prodid.signature[5],
+ (uint16_t) prodid.signature[4],
+ (uint16_t) prodid.signature[3],
+ (uint16_t) prodid.signature[2],
+ (uint16_t) prodid.signature[1],
+ (uint16_t) prodid.signature[0],
+ (uint16_t) prodid.hwrel,
+ (uint16_t) prodid.hwver);
+
+ // Printing all sampling rates available .....
+ {
+ int buf[BUFSIZ];
+
+ if (perseus_get_sampling_rates (descr, buf, sizeof(buf)/sizeof(buf[0])) < 0) {
+ fprintf(stderr, "perseus c: get sampling rates error: %s\n", perseus_errorstr());
+ goto main_cleanup;
+ } else {
+ int i = 0;
+ while (buf[i]) {
+ fprintf(stderr, "perseus c: #%d: sample rate: %d\n", i, buf[i]);
+ i++;
+ }
+ }
+ }
+
+ // Configure the receiver for 2 MS/s operations
+ fprintf(stderr, "perseus c: Configuring FPGA...\n");
+ if (perseus_set_sampling_rate(descr, sr) < 0) { // specify the sampling rate value in Samples/second
+ //if (perseus_set_sampling_rate_n(descr, 0)<0) // specify the sampling rate value as ordinal in the vector
+ fprintf(stderr, "perseus c: fpga configuration error: %s\n", perseus_errorstr());
+ goto main_cleanup;
+ }
+
+ // ADC settings
+ perseus_set_adc (descr, adc_dither, adc_preamp);
+
+ // Disable preselection filters (WB_MODE On)
+ //perseus_set_ddc_center_freq(descr, freq, 0);
+ //sleep(1);
+ // Re-enable preselection filters (WB_MODE Off)
+ perseus_set_ddc_center_freq(descr, freq, wb_filter);
+
+ quisk_sample_source4(&quisk_start_samples, &quisk_stop_samples, &quisk_read_samples, &quisk_write_samples);
+
+ fprintf (stderr, "perseus c: quisk sample source callbacks established\n"); fflush(stderr);
+ goto exit_success;
+
+
+
+ main_cleanup:
+ return PyString_FromString("ERROR");
+
+ exit_success:
+
+ return PyString_FromString(buf128);
+
+
+}
+
+static PyObject * set_frequency(PyObject * self, PyObject * args) // Called from GUI thread
+{
+ float param;
+
+ if (!PyArg_ParseTuple (args, "f", ¶m))
+ return NULL;
+ if (DEBUG)
+ fprintf (stderr, "perseus c: set DDC frequency%lf\n", param);
+ freq= param;
+ if (descr) perseus_set_ddc_center_freq(descr, freq, wb_filter == 0);
+
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+
+static PyObject * set_input_filter(PyObject * self, PyObject * args) // Called from GUI thread
+{
+ int param;
+
+ if (!PyArg_ParseTuple (args, "i", ¶m))
+ return NULL;
+ if (DEBUG)
+ fprintf (stderr, "perseus c: set input filter%d\n", param);
+ wb_filter = param;
+ if (descr) perseus_set_ddc_center_freq(descr, freq, wb_filter == 0);
+
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+
+static PyObject * set_sampling_rate(PyObject * self, PyObject * args) // Called from GUI thread
+{
+ int param;
+
+ if (!PyArg_ParseTuple (args, "i", ¶m))
+ return NULL;
+
+ fprintf (stderr, "perseus c: Set sampling rate %d\n", param);
+ if (param < 48000) sr = param * 1000;
+ else sr = param;
+
+ if (descr) {
+ if (running) {
+ fprintf(stderr, "perseus c: stop async input\n");
+ perseus_stop_async_input(descr);
+ }
+
+ // specify the sampling rate value in Samples/secon
+ if (perseus_set_sampling_rate(descr, sr) < 0) {
+ fprintf(stderr, "perseus c: fpga configuration error: %s\n", perseus_errorstr());
+ }
+
+ if (running) {
+ if (perseus_start_async_input(descr, nb*bs, user_data_callback_c_f, 0)<0) {
+ fprintf(stderr, "perseus c: start async input error: %s\n", perseus_errorstr());
+ } else
+ fprintf(stderr, "perseus c: start async input @%d\n", sr);
+ }
+ } else {
+ fprintf(stderr, "perseus c: tryng to start async input with no device open\n");
+ }
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+
+static PyObject * set_attenuator(PyObject * self, PyObject * args) // Called from GUI thread
+{ int param;
+
+ if (!PyArg_ParseTuple (args, "i", ¶m))
+ return NULL;
+ if (DEBUG)
+ fprintf (stderr, "perseus c: Set attenuator %d\n", param);
+
+ if (descr) {
+ // specify the sampling rate value in Samples/secon
+ if (perseus_set_attenuator_n(descr, (int)(param / -10)) < 0) {
+ fprintf(stderr, "perseus c: fpga configuration error: %s\n", perseus_errorstr());
+ }
+ }
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+
+// Enable ADC Dither, Disable ADC Preamp
+// perseus_set_adc(descr, true, false);
+
+static PyObject * set_adc_dither (PyObject * self, PyObject * args) // Called from GUI thread
+{ int dither_;
+
+ if (!PyArg_ParseTuple (args, "i", &dither_))
+ return NULL;
+ if (DEBUG)
+ fprintf (stderr, "perseus c: Set ADC: dither %d\n", dither_);
+
+ adc_dither = dither_;
+ if (descr) {
+ // specify the ADC dithering
+ if (perseus_set_adc(descr, adc_dither == 1, adc_preamp == 1) < 0) {
+ fprintf(stderr, "perseus c: ADC configuration error: %s\n", perseus_errorstr());
+ }
+ }
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+static PyObject * set_adc_preamp (PyObject * self, PyObject * args) // Called from GUI thread
+{ int preamp_;
+
+ if (!PyArg_ParseTuple (args, "i", &preamp_))
+ return NULL;
+ if (DEBUG)
+ fprintf (stderr, "perseus c: Set ADC: preamp: %d\n", preamp_);
+
+ adc_preamp = preamp_;
+ if (descr) {
+ // specify the sampling rate value in Samples/secon
+ if (perseus_set_adc(descr, adc_dither == 1, adc_preamp == 1) < 0) {
+ fprintf(stderr, "perseus c: ADC configuration error: %s\n", perseus_errorstr());
+ }
+ }
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+
+static PyObject * deinit(PyObject * self, PyObject * args) // Called from dctor
+{
+ perseus_exit();
+
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+
+// Functions callable from Python are listed here:
+static PyMethodDef QuiskMethods[] = {
+ {"open_device", open_device, METH_VARARGS, "Open the hardware."},
+ {"close_device", close_device, METH_VARARGS, "Close the hardware"},
+ {"set_frequency", set_frequency, METH_VARARGS, "set frequency"},
+ {"set_input_filter", set_input_filter, METH_VARARGS, "set input filter"},
+ {"set_sampling_rate", set_sampling_rate, METH_VARARGS, "set sampling rate"},
+ {"set_attenuator", set_attenuator, METH_VARARGS, "set attenuator"},
+ {"set_adc_dither", set_adc_dither, METH_VARARGS, "set ADC dither"},
+ {"set_adc_preamp", set_adc_preamp, METH_VARARGS, "set ADC preamplifier"},
+ {"deinit", deinit, METH_VARARGS, "deinit"},
+// {"get_device_list", get_device_list, METH_VARARGS, "Return a list of Perseus SDR devices"},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION < 3
+// Python 2.7:
+// Initialization, and registration of public symbol "initperseus":
+PyMODINIT_FUNC initperseus (void)
+{
+ if (Py_InitModule ("perseus", QuiskMethods) == NULL) {
+ fprintf(stderr, "perseus c: Py_InitModule failed!\n");
+ return;
+ }
+ // Import pointers to functions and variables from module _quisk
+ if (import_quisk_api()) {
+ fprintf(stderr, "perseus c: Failure to import pointers from _quisk\n");
+ return; //Error
+ }
+}
+
+// Python 3:
+#else
+static struct PyModuleDef perseusmodule = {
+ PyModuleDef_HEAD_INIT,
+ "perseus",
+ NULL,
+ -1,
+ QuiskMethods
+} ;
+
+PyMODINIT_FUNC PyInit_perseus(void)
+{
+ PyObject * m;
+
+ m = PyModule_Create(&perseusmodule);
+ if (m == NULL)
+ return NULL;
+
+ // Import pointers to functions and variables from module _quisk
+ if (import_quisk_api()) {
+ fprintf(stderr, "perseus c: Failure to import pointers from _quisk\n");
+ return m; //Error
+ }
+ return m;
+}
+#endif
+
diff --git a/perseuspkg/quisk_hardware.py b/perseuspkg/quisk_hardware.py
new file mode 100755
index 0000000..aaea0b8
--- /dev/null
+++ b/perseuspkg/quisk_hardware.py
@@ -0,0 +1,187 @@
+# This is the hardware file to support radios accessed by the PerseusSDR interface.
+
+from __future__ import print_function
+from __future__ import absolute_import
+from __future__ import division
+
+import socket, traceback, time, math
+import _quisk as QS
+try:
+ from perseuspkg import perseus
+except:
+ #traceback.print_exc()
+ perseus = None
+ print ("Error: Perseus package not found.\n")
+
+from quisk_hardware_model import Hardware as BaseHardware
+
+DEBUG = 1
+
+# Define the name of the hardware and the items on the hardware screen (see quisk_conf_defaults.py):
+################ Receivers PerseusSDR, The PerseusSDR interface to multiple hardware SDRs.
+## hardware_file_name Hardware file path, rfile
+# This is the file that contains the control logic for each radio.
+#hardware_file_name = 'perseuspkg/quisk_hardware.py'
+
+## widgets_file_name Widget file path, rfile
+# This optional file adds additional controls for the radio.
+#widgets_file_name = 'perseuspkg/quisk_widgets.py'
+
+class Hardware(BaseHardware):
+ def __init__(self, app, conf):
+ BaseHardware.__init__(self, app, conf)
+
+ self.rf_gain_labels = ('RF +0', 'RF -10', 'RF -20', 'RF -30')
+ self.antenna_labels = ('Wide Band', 'Band Filter')
+
+ self.vardecim_index = 0
+ self.fVFO = 0.0 # Careful, this is a float
+ print ("__init__: %s" % conf)
+ self.rates = [ 48000, \
+ 95000, \
+ 96000, \
+ 125000, \
+ 192000, \
+ 250000, \
+ 500000, \
+ 1000000, \
+ 1600000, \
+ 2000000 \
+ ]
+ self.current_rate = 192000
+ self.att = 0;
+ self.wb = 0
+
+ def __del__(self):
+ # try to clear hardware
+ if perseus:
+ perseus.close()
+ perseus.deinit()
+
+ def get_hw (self):
+ return perseus
+
+ def pre_open(self):
+ print ("pre_open")
+ pass
+
+ def set_parameter(self, *args):
+ pass
+
+ def open(self): # Called once to open the Hardware
+
+ if not perseus:
+ return "Perseus module not available"
+
+ txt = perseus.open_device("perseus",2,3)
+ print ("perseus hardware: open")
+
+ return txt
+
+ def close(self): # Called once to close the Hardware
+ print ("perseus hardware: close")
+ if perseus:
+ perseus.close_device(1)
+
+ def ChangeGain(self, rxtx): # rxtx is '_rx' or '_tx'
+ if not perseus:
+ return
+ print ("perseus hardware: ChangeGain", rxtx)
+ pass
+
+ def OnButtonRfGain(self, event):
+ #btn = event.GetEventObject()
+ n = event.GetEventObject().index
+ self.att = n * -10
+ print ("perseus hardware: OnButtonRfGain: %d new attenuation: %d" % (n, self.att))
+ perseus.set_attenuator (self.att)
+
+ def ChangeFrequency(self, tune, vfo, source='', band='', event=None):
+ fVFO = float(vfo)
+ if self.fVFO != fVFO:
+ self.fVFO = fVFO
+ perseus.set_frequency(fVFO)
+ 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 self.fVFO
+
+# def OnBtnFDX(self, fdx): # fdx is 0 or 1
+# pass
+#
+# def OnButtonPTT(self, event):
+# pass
+#
+# def OnSpot(self, level):
+# # level is -1 for Spot button Off; else the Spot level 0 to 1000.
+# pass
+#
+# def ChangeMode(self, mode): # Change the tx/rx mode
+# # mode is a string: "USB", "AM", etc.
+# pass
+#
+# def ChangeBand(self, band):
+# pass
+#
+# def HeartBeat(self): # Called at about 10 Hz by the main
+# pass
+
+ def ImmediateChange(self, name, value):
+ print ("perseus hardware: ImmediateChange: perseus: name: %s value: %s" % (name, value))
+ if name == 'perseus_setSampleRate_rx':
+ value = int(value)
+ self.application.OnBtnDecimation(rate=value)
+ perseus.set_sampling_rate(value)
+ self.curren_dec = value
+
+
+ def VarDecimGetChoices(self): # Not used to set sample rate
+ print ("perseus hardware: VarDecimGetChoices")
+ return list(map(str, self.rates)) # convert integer to string
+
+ def VarDecimGetLabel(self): # Return a text label for the decimation control.
+ return 'Sample rates: '
+
+ def VarDecimGetIndex(self): # Return the index 0, 1, ... of the current decimation.
+ for i in range(len(self.rates)):
+ if self.rates[i] == self.current_rate:
+ return i
+ return 0
+
+ def VarDecimSet(self, index=None): # Called when the control is operated; if index==None, called on startup.
+ print ("perseus hardware: VarDecimSet: index: %s" % (index))
+ if index == None:
+ print ("perseus hardware: VarDecimSet: current sampling rate: %d" % self.current_rate)
+ new_rate = self.current_rate
+ else:
+ new_rate = self.rates[index]
+
+ print ("perseus hardware: VarDecimSet: New sampling rate: %d" % new_rate)
+ perseus.set_sampling_rate(int(new_rate))
+
+ return int(new_rate)
+
+ def VarDecimRange(self): # Return the lowest and highest sample rate.
+ print ("perseus hardware: VarDecimRange: %s" % self.rates)
+ return (self.rates[0], self.rates[-1])
+
+ def OnButtonAntenna(self, event):
+ btn = event.GetEventObject()
+ self.wb_filter = n = btn.index
+ print ("OnButtonAntenna: %d" % n)
+ perseus.set_input_filter (self.wb_filter)
+
+# def StartSamples(self): # called by the sound thread
+# print("perseus hardware: StartSamples")
+
+# def StopSamples(self): # called by the sound thread
+# print("perseus hardware: StopSamples")
diff --git a/perseuspkg/quisk_widgets.py b/perseuspkg/quisk_widgets.py
new file mode 100755
index 0000000..e1b4721
--- /dev/null
+++ b/perseuspkg/quisk_widgets.py
@@ -0,0 +1,37 @@
+# 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 math, wx
+
+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.start_row = app.widget_row # The first available row
+ self.start_col = app.button_start_col # The start of the button columns
+ self.Widgets_0x06(app, hardware, conf, frame, gbs, vertBox)
+
+ def Widgets_0x06(self, app, hardware, conf, frame, gbs, vertBox):
+ self.num_rows_added = 1
+ start_row = self.start_row
+ b1 = app.QuiskCheckbutton(frame, self.OnADC_dither, 'ADC Dither')
+ gbs.Add(b1, (start_row, self.start_col), (1, 2), flag=wx.EXPAND)
+ b2 = app.QuiskCheckbutton(frame, self.OnADC_preamp, 'ADC Preamp')
+ gbs.Add(b2, (start_row, self.start_col + 2), (1, 2), flag=wx.EXPAND)
+
+ def OnADC_dither(self, event):
+ btn = event.GetEventObject()
+ value = btn.GetValue()
+ self.hardware.get_hw().set_adc_dither (value)
+
+ def OnADC_preamp(self, event):
+ btn = event.GetEventObject()
+ value = btn.GetValue()
+ self.hardware.get_hw().set_adc_preamp (value)
diff --git a/perseuspkg/setup.py b/perseuspkg/setup.py
new file mode 100755
index 0000000..ef48fc7
--- /dev/null
+++ b/perseuspkg/setup.py
@@ -0,0 +1,48 @@
+from distutils.core import setup, Extension
+import sys
+
+module2 = Extension ('perseus',
+ libraries = ['m', 'perseus-sdr'],
+ sources = ['../import_quisk_api.c', 'perseus.c'],
+ include_dirs = ['.', '..'],
+ )
+
+modulew2 = Extension ('perseus',
+ sources = ['../import_quisk_api.c', 'perseus.c'],
+ include_dirs = ['.', '..'],
+ libraries = ['WS2_32', 'perseus-sdr'],
+ )
+
+if sys.platform == "win32":
+ Modules = [modulew2]
+else:
+ Modules = [module2]
+
+setup (name = 'perseus',
+ version = '0.1',
+ description = 'perseus is an extension to Quisk to support Microtelecom Perseus SDR hardware',
+ long_description = """Microtelecom Perseus SDR HF receiver.
+""",
+ author = 'Andrea Montefusco IW0HDV',
+ author_email = 'andrew@montefusco.com',
+ url = 'http://www.montefusco.com',
+ download_url = 'http://james.ahlstrom.name/quisk/',
+ packages = ['quisk.perseuspkg'],
+ package_dir = {'perseus' : '.'},
+ ext_modules = Modules,
+ classifiers = [
+ 'Development Status :: 6 - Mature',
+ 'Environment :: X11 Applications',
+ 'Environment :: Win32 (MS Windows)',
+ 'Intended Audience :: End Users/Desktop',
+ 'License :: OSI Approved :: GNU General Public License (GPL)',
+ 'Natural Language :: English',
+ 'Operating System :: POSIX :: Linux',
+ 'Operating System :: Microsoft :: Windows',
+ 'Programming Language :: Python',
+ 'Programming Language :: C',
+ 'Topic :: Communications :: Ham Radio',
+ ],
+)
+
+
diff --git a/quisk.egg-info/PKG-INFO b/quisk.egg-info/PKG-INFO
index 9163563..909e803 100755
--- a/quisk.egg-info/PKG-INFO
+++ b/quisk.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: quisk
-Version: 4.1.53
+Version: 4.1.54
Summary: QUISK is a Software Defined Radio (SDR) transceiver that can control various radio hardware.
Home-page: http://james.ahlstrom.name/quisk/
Author: James C. Ahlstrom
diff --git a/quisk.egg-info/SOURCES.txt b/quisk.egg-info/SOURCES.txt
index 1b9bbef..c23424b 100755
--- a/quisk.egg-info/SOURCES.txt
+++ b/quisk.egg-info/SOURCES.txt
@@ -7,7 +7,6 @@ WinQuisk.pyw
WinQuiskVna.pyw
__init__.py
__main__.py
-_quisk.pyd
configure.py
defaults.html
docs.html
@@ -51,7 +50,6 @@ quisk_hardware_hamlib.py
quisk_hardware_model.py
quisk_hardware_sdr8600.py
quisk_hardware_sdriq.py
-quisk_hardware_sdrmicron.py
quisk_utils.py
quisk_vna.py
quisk_widgets.py
@@ -99,7 +97,6 @@ winsound.txt
./quisk_hardware_model.py
./quisk_hardware_sdr8600.py
./quisk_hardware_sdriq.py
-./quisk_hardware_sdrmicron.py
./quisk_utils.py
./quisk_vna.py
./quisk_widgets.py
@@ -151,6 +148,11 @@ winsound.txt
./n2adr/uhf_conf.py
./n2adr/uhf_hardware.py
./n2adr/uhf_widgets.py
+./perseuspkg/README.txt
+./perseuspkg/__init__.py
+./perseuspkg/quisk_hardware.py
+./perseuspkg/quisk_widgets.py
+./perseuspkg/setup.py
./sdriqpkg/README.txt
./sdriqpkg/__init__.py
./sdriqpkg/quisk_hardware.py
@@ -217,6 +219,13 @@ n2adr/uhf_conf.py
n2adr/uhf_hardware.old
n2adr/uhf_hardware.py
n2adr/uhf_widgets.py
+perseuspkg/README.txt
+perseuspkg/__init__.py
+perseuspkg/makefile
+perseuspkg/perseus.c
+perseuspkg/quisk_hardware.py
+perseuspkg/quisk_widgets.py
+perseuspkg/setup.py
quisk.egg-info/PKG-INFO
quisk.egg-info/SOURCES.txt
quisk.egg-info/dependency_links.txt
@@ -228,6 +237,7 @@ sdriqpkg/quisk_hardware.py
sdriqpkg/sdriq.c
sdriqpkg/sdriq.h
sdriqpkg/sdriq.pyd
+sdrmicronpkg/quisk_hardware.py
soapypkg/__init__.py
soapypkg/makefile
soapypkg/quisk_hardware.py
diff --git a/sdrmicronpkg/quisk_hardware.py b/sdrmicronpkg/quisk_hardware.py
new file mode 100755
index 0000000..7c48db7
--- /dev/null
+++ b/sdrmicronpkg/quisk_hardware.py
@@ -0,0 +1,266 @@
+# -*- coding: cp1251 -*-
+#
+# It provides support for the SDR Micron
+
+from __future__ import print_function
+from __future__ import absolute_import
+from __future__ import division
+
+import sys, wx, traceback
+
+import ftd2xx as d2xx
+import time
+
+from quisk_hardware_model import Hardware as BaseHardware
+
+DEBUG = 0
+
+# https://github.com/Dfinitski/SDR-Micron
+#
+# RX control, to device, 32 bytes total
+# Preamble + 'RX0' + enable + rate + 4 bytes frequency + attenuation + 14 binary zeroes
+#
+# where:
+# Preamble is 7*0x55, 0xd5
+# bytes:
+# enable - binary 0 or 1, for enable receiver
+# rate:
+# binary
+# 0 for 48 kHz
+# 1 for 96 kHz
+# 2 for 192 kHz
+# 3 for 240 kHz
+# 4 for 384 kHz
+# 5 for 480 kHz
+# 6 for 640 kHz
+# 7 for 768 kHz
+# 8 for 960 kHz
+# 9 for 1536 kHz
+# 10 for 1920 kHz
+#
+# frequency - 32 bits of tuning frequency, MSB is first
+# attenuation - binary 0, 10, 20, 30 for needed attenuation
+#
+# RX data, to PC, 508 bytes total
+# Preamble + 'RX0' + FW1 + FW2 + CLIP + 2 zeroes + 492 bytes IQ data
+#
+# Where:
+# FW1 and FW2 - char digits firmware version number
+# CLIP - ADC overflow indicator, 0 or 1 binary
+# IQ data for 0 - 7 rate:
+# 82 IQ pairs formatted as "I2 I1 I0 Q2 Q1 Q0.... ", MSB is first, 24 bits per sample
+# IQ data for 8 - 10 rate:
+# 123 IQ pairs formatted as "I1 I0 Q1 Q0..... ", MSB is first, 16 bits per sample
+#
+# Band Scope control, to device, 32 bytes total
+# Preamble + 'BS0' + enable + period + 19 binary zeroes
+#
+# Where period is the full frame period in ms, from 50 to 255ms, 100ms is recommended
+# for 10Hz refresh rate window.
+#
+# Band Scope data, to PC, 16384 16bit samples, 32768 bytes by 492 in each packet
+# Preamble + 'BS0' + FW1 + FW2 + CLIP + PN + 1 zero + 492 bytes BS data
+#
+# Where PN is packet number 0, 1, 2, ..., 66
+# BS data in format "BS1, BS0, BS1, BS0, ...", MSB is first
+#
+# 66 packets with PN = 0 - 65 contain 492 bytes each, and 67-th packet with PN = 66 contains
+# the remaining 296 bytes of data and junk data to full 492 bytes size
+#
+
+# Define the name of the hardware and the items on the hardware screen (see quisk_conf_defaults.py):
+################ Receivers SdrMicron, The SDR Micron project by David Fainitski.
+## hardware_file_name Hardware file path, rfile
+# This is the file that contains the control logic for each radio.
+#hardware_file_name = 'sdrmicronpkg/quisk_hardware.py'
+
+
+class Hardware(BaseHardware):
+ sample_rates = [48, 96, 192, 240, 384, 480, 640 ,768, 960, 1536, 1920]
+ def __init__(self, app, conf):
+ BaseHardware.__init__(self, app, conf)
+ self.device = None
+ self.usb = None
+ self.rf_gain_labels = ('RF +10', 'RF 0', 'RF -10', 'RF -20')
+ self.index = 1
+ self.enable = 0
+ self.rate = 0
+ self.att = 10
+ self.freq = 7220000
+ self.old_freq = 0
+ self.sdrmicron_clock = 76800000
+ self.sdrmicron_decim = 1600
+ self.bscope_data = bytearray(0)
+ self.fw_ver = None
+ self.frame_msg = ''
+
+ if conf.fft_size_multiplier == 0:
+ conf.fft_size_multiplier = 3 # Set size needed by VarDecim
+
+ rx_bytes = 3 # rx_bytes is the number of bytes in each I or Q sample: 1, 2, 3, or 4
+ rx_endian = 1 # rx_endian is the order of bytes in the sample array: 0 == little endian; 1 == big endian
+ self.InitSamples(rx_bytes, rx_endian) # Initialize: read samples from this hardware file and send them to Quisk
+ bs_bytes = 2
+ bs_endian = 1
+ self.InitBscope(bs_bytes, bs_endian, self.sdrmicron_clock, 16384) # Initialize bandscope
+
+ def open(self): # This method must return a string showing whether the open succeeded or failed.
+ enum = d2xx.createDeviceInfoList() # quantity of FTDI devices
+ if(enum==0):
+ return 'Device was not found'
+ for i in range(enum): # Searching and openinq needed device
+ a = d2xx.getDeviceInfoDetail(i)
+ if(a['description']==b'SDR-Micron'):
+ try: self.usb = d2xx.openEx(a['serial'])
+ except:
+ return 'Device was not found'
+ Mode = 64 # Configure FT2232H into 0x40 Sync FIFO Mode
+ self.usb.setBitMode(255, 0) # reset
+ time.sleep(0.1)
+ self.usb.setBitMode(255, Mode) #configure FT2232H into Sync FIFO mode
+ self.usb.setTimeouts(100, 100) # read, write
+ self.usb.setLatencyTimer(2)
+ self.usb.setUSBParameters(32, 32) # in_tx_size, out_tx_size
+ time.sleep(1.5) # waiting for initialisation device
+ data = self.usb.read(self.usb.getQueueStatus()) # clean the usb data buffer
+ self.device = 'Opened'
+ self.frame_msg = a['description'].decode('utf-8') + ' S/N - ' + a['serial'].decode('utf-8')
+ return self.frame_msg
+ return 'Device was not found'
+
+ def close(self):
+ if(self.usb):
+ if(self.device=='Opened'):
+ enable = 0
+ self.device = None
+ self.rx_control_upd()
+ time.sleep(0.5)
+ self.usb.setBitMode(255, 0) # reset
+ self.usb.close()
+
+ def OnButtonRfGain(self, event):
+ btn = event.GetEventObject()
+ n = btn.index
+ self.att = n * 10
+ self.rx_control_upd()
+
+ def ChangeFrequency(self, tune, vfo, source='', band='', event=None):
+ if vfo:
+ self.freq = (vfo - self.transverter_offset)
+ if(self.freq!=self.old_freq):
+ self.old_freq = self.freq
+ self.rx_control_upd()
+ return tune, vfo
+
+ def ChangeBand(self, band):
+ # band is a string: "60", "40", "WWV", etc.
+ BaseHardware.ChangeBand(self, band)
+ btn = self.application.BtnRfGain
+ if btn:
+ if band in ('160', '80', '60', '40'):
+ btn.SetLabel('RF -10', True)
+ elif band in ('20',):
+ btn.SetLabel('RF 0', True)
+ else:
+ btn.SetLabel('RF +10', True)
+
+ def VarDecimGetChoices(self): # Return a list/tuple of strings for the decimation control.
+ return list(map(str, self.sample_rates)) # convert integer to string
+
+ 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 VarDecimSet(self, index=None): # return sample rate
+ if index is None: # initial call to set the sample rate before the call to open()
+ rate = self.application.vardecim_set
+ try:
+ self.index = self.sample_rates.index(rate // 1000)
+ except:
+ self.index = 0
+ else:
+ self.index = index
+ rate = self.sample_rates[self.index] * 1000
+ self.rate = self.index
+ if(rate>=960000):
+ rx_bytes = 2
+ rx_endian = 1
+ self.InitSamples(rx_bytes, rx_endian)
+ else:
+ rx_bytes = 3
+ rx_endian = 1
+ self.InitSamples(rx_bytes, rx_endian)
+ self.rx_control_upd()
+ return rate
+
+ def VarDecimRange(self): # Return the lowest and highest sample rate.
+ return (48000, 1920000)
+
+ def StartSamples(self): # called by the sound thread
+ self.enable = 1
+ self.rx_control_upd()
+ self.bscope_control_upd()
+
+ def StopSamples(self): # called by the sound thread
+ self.enable = 0
+ self.rx_control_upd()
+ self.bscope_control_upd()
+
+ def rx_control_upd(self):
+ if(self.device=='Opened'):
+ work = self.freq
+ freq4 = work & 0xFF
+ work = work >> 8
+ freq3 = work & 0xFF
+ work = work >> 8
+ freq2 = work & 0xFF
+ work = work >> 8
+ freq1 = work & 0xFF
+ if sys.version_info.major <= 2:
+ MESSAGE = 7*chr(0x55) + chr(0xd5) + 'RX0' + chr(self.enable) + chr(self.rate)
+ MESSAGE += chr(freq1) + chr(freq2) + chr(freq3) + chr(freq4) + chr(self.att) + 14*chr(0)
+ else:
+ MESSAGE = b"\x55\x55\x55\x55\x55\x55\x55\xd5RX0"
+ MESSAGE += bytes((self.enable, self.rate, freq1, freq2, freq3, freq4, self.att))
+ MESSAGE += bytes(14)
+ try: self.usb.write(MESSAGE)
+ except: print('Error while rx_control_upd')
+
+ def bscope_control_upd(self):
+ if self.device == 'Opened':
+ if sys.version_info.major <= 2:
+ MESSAGE = 7*chr(0x55) + chr(0xd5) + 'BS0' + chr(self.enable) + chr(100) + 19 * chr(0)
+ else:
+ MESSAGE = b"\x55\x55\x55\x55\x55\x55\x55\xd5BS0"
+ MESSAGE += bytes((self.enable, 100))
+ MESSAGE += bytes(19)
+ try: self.usb.write(MESSAGE)
+ except: None
+
+ def GetRxSamples(self): # Read all data from the SDR Micron and process it.
+ if self.device == None:
+ return
+ while (self.usb.getQueueStatus() >= 508):
+ data = self.usb.read(508)
+ data = bytearray(data)
+ if data[8:11] == bytearray(b'RX0'): # Rx I/Q data
+ if data[13]:
+ self.GotClip()
+ if self.fw_ver is None:
+ self.fw_ver = chr(data[11]) + '.' + chr(data[12])
+ self.frame_msg += ' F/W version - ' + self.fw_ver
+ self.application.main_frame.SetConfigText(self.frame_msg)
+ self.AddRxSamples(data[16:])
+ elif data[8:11] == bytearray(b'BS0'): # bandscope data
+ packet_number = data[14]
+ if packet_number == 0: # start of a block of data
+ self.bscope_data = data[16:] # 492 bytes
+ elif packet_number < 66:
+ self.bscope_data += data[16:] # 492 bytes
+ else: # end of a block of data, 296 bytes
+ self.bscope_data += data[16:312]
+ self.AddBscopeSamples(self.bscope_data)
+
+
diff --git a/setup.py b/setup.py
index e5cfc8c..0b8883a 100755
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,7 @@ import struct
# 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.1.53'
+Version = '4.1.54'
fp = open("__init__.py", "w") # write title string
fp.write("#Quisk version %s\n" % Version)
@@ -155,7 +155,7 @@ N1MM+ and software that uses Hamlib.
author_email = 'jahlstr@gmail.com',
url = 'http://james.ahlstrom.name/quisk/',
packages = ['quisk', 'quisk.sdriqpkg', 'quisk.n2adr', 'quisk.softrock', 'quisk.freedvpkg',
- 'quisk.hermes', 'quisk.hiqsdr', 'quisk.afedrinet', 'quisk.soapypkg'],
+ 'quisk.hermes', 'quisk.hiqsdr', 'quisk.afedrinet', 'quisk.soapypkg', 'quisk.perseuspkg'],
package_dir = {'quisk' : '.'},
package_data = {'' : ['*.txt', '*.html', '*.so', '*.dll']},
entry_points = {'gui_scripts' : ['quisk = quisk.quisk:main', 'quisk_vna = quisk.quisk_vna:main']},
diff --git a/soapypkg/build/temp.linux-x86_64-3.6/soapy.o b/soapypkg/build/temp.linux-x86_64-3.6/soapy.o
index 76a600f..9a11e0e 100755
Binary files a/soapypkg/build/temp.linux-x86_64-3.6/soapy.o and b/soapypkg/build/temp.linux-x86_64-3.6/soapy.o differ