Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84e194c644 | ||
|
|
b45a71b017 | ||
|
|
e6b9d9dd05 | ||
|
|
1f68d05cd5 | ||
|
|
018fc0e871 | ||
|
|
1111d24333 | ||
|
|
d8aee9284d | ||
|
|
7121dacbbd | ||
|
|
0528e8bbbe | ||
|
|
458e43252d | ||
|
|
fd0a8a3d0e | ||
|
|
6c2e9012c4 |
@@ -1,3 +1,97 @@
|
|||||||
|
Quisk Version 4.2.53 June 2026
|
||||||
|
===============================
|
||||||
|
I added TCI support for Software Defined Connectors CW skimmer. Please test.
|
||||||
|
|
||||||
|
This version of Quisk now works on Python 3.14 and higher. I changed to a new build system because
|
||||||
|
the old one has been deprecated by Python. They no longer support running setup.py.
|
||||||
|
The Linux version has a new makefile. The Windows version is installed by pip as usual.
|
||||||
|
Please test.
|
||||||
|
|
||||||
|
Quisk Version 4.2.52 May 2026
|
||||||
|
==============================
|
||||||
|
This is a fix for various TCI bugs. Please test with WSJT-X and let me know if all problems are solved.
|
||||||
|
It is unlikely that other digital software will connect without problems, but please test and let me know.
|
||||||
|
|
||||||
|
Quisk Version 4.2.51 May 2026
|
||||||
|
==============================
|
||||||
|
This version can use the TCI protocol to connect to WSJT-X. Please see Digital Modes in the documentation.
|
||||||
|
|
||||||
|
Quisk Version 4.2.50 December 2025
|
||||||
|
===================================
|
||||||
|
This is a quick fix for a bug in 4.2.49 that results in the repeated message
|
||||||
|
AttributeError: 'Hardware' object has no attribute 'is_HermesLite2'
|
||||||
|
|
||||||
|
Quisk Version 4.2.49 December 2025
|
||||||
|
===================================
|
||||||
|
This version adds more features for the Anan 10E and other OpenHPSDR radios. Please test.
|
||||||
|
|
||||||
|
I added the forward power, SWR, temperature and PA current display to the OpenHPSDR radio. But
|
||||||
|
I don't have that radio so I need some help. I think the forward and reverse power connections
|
||||||
|
are correct, but it is still necessary to create a table to convert analog input codes to power.
|
||||||
|
Go to the Hardware screen and check the "Power meter calibration" setting. You will need to enter
|
||||||
|
"New" and create a suitable table. To see the ADC values, choose table "Show ADC".
|
||||||
|
|
||||||
|
There is no protocol standard to specify which analog input is used for temperature and PA current.
|
||||||
|
And AFAIK the Anan 10E has no such sensors. I am hoping to use the board ID to find the right input.
|
||||||
|
The ADC conversion is linear and the two points are specified on the Hardware screen.
|
||||||
|
|
||||||
|
I currently assigned the analog inputs as follows:
|
||||||
|
AIN1 Forward power
|
||||||
|
AIN2 Reverse power
|
||||||
|
AIN3 PA current
|
||||||
|
AIN5 Temperature
|
||||||
|
If you need to see the ADC codes 0-4095 for each input, go to hermes/quisk_hardware.py and
|
||||||
|
change DEBUG_SENSORS to one.
|
||||||
|
|
||||||
|
Quisk Version 4.2.48 November 2025
|
||||||
|
===================================
|
||||||
|
This is a quisk fix for a bug in the new OpenHPSDR radios. It fixes use_rx_udp that was reset to zero.
|
||||||
|
|
||||||
|
Quisk Version 4.2.47 November 2025
|
||||||
|
===================================
|
||||||
|
To improve the performance of Quisk with the Anan 10-E and other OpenHPSDR radios, I added
|
||||||
|
a new type of radio "OpenHPSDR". Use this type for the Anan 10-E and similar radios. It removes
|
||||||
|
all the protocol extensions added by the Hermes-Lite2 project. I removed the configuration
|
||||||
|
option "Hermes Lite2 protocol enable". This is now automatic.
|
||||||
|
|
||||||
|
I added buttons for antennas 0, 1, 2 and 3 for OpenHPSDR radios that support more antennas.
|
||||||
|
I added controls for the OpenHPSDR preamplifier and attenuator.
|
||||||
|
|
||||||
|
I do not have any Apache Labs radios, so I must depend on my users to report problems and solutions.
|
||||||
|
|
||||||
|
Quisk Version 4.2.46 October 2025
|
||||||
|
==================================
|
||||||
|
I fixed a bug in Hamlib rig 2 which happens with the new Pure Signal code. The Hamlib "f" command
|
||||||
|
now always returns the main receiver frequency, not one of the added receiver frequencies. This is
|
||||||
|
consistent with the Flex ZZ commands.
|
||||||
|
|
||||||
|
Quisk Version 4.2.45 October 2025
|
||||||
|
==================================
|
||||||
|
The new FreeDV including RADE mode is now a built-in mode of Quisk. Just press "FDV" to talk FreeDV.
|
||||||
|
In brief, install FreeDV-GUI from the web site. Then configure the usual Quisk digital input and
|
||||||
|
output that you use for digital modes. In FreeDV-GUI use the digital input and output. For the
|
||||||
|
speakers and microphone use the same ones as Quisk. Don't forget to configure your call sign in
|
||||||
|
FreeDV so it works with FreeDV Reporter. For detailed instructions see http://james.ahlstrom.name/rade.html.
|
||||||
|
|
||||||
|
The Quisk digital output device is now always active; a DGT mode is not required.
|
||||||
|
|
||||||
|
Quisk Version 4.2.44 September 2025
|
||||||
|
====================================
|
||||||
|
Quisk now has Pure Signal for the Hermes Lite2. This is baseband predistortion to reduce IMD products.
|
||||||
|
To use it, press "PsCal" for calibration, and "PS" to enable Pure Signal. There are no adjustments needed.
|
||||||
|
More details are in the documentation.
|
||||||
|
|
||||||
|
Quisk Version 4.2.43 September 2025
|
||||||
|
====================================
|
||||||
|
I fixed a bug in the WDSP library NR2 noise reduction feature. I added the "Trained" method of NR2.
|
||||||
|
Quisk uses the WDSP library for the NR2 noise reduction and the SNB noise blanker. The WDSP library must be available
|
||||||
|
for these features to work. For windows, the library is included. For Linux you must build it by changing to the
|
||||||
|
quisk/wdsp directory and entering "make". The library is used in-place and no installation is necessary.
|
||||||
|
The new "trained" method of NR2 is included. Press the NR2 button twice to select "NR2t".
|
||||||
|
|
||||||
|
I added documentation for the new FreeDV mode RADE. See http://james.ahlstrom.name/rade.html.
|
||||||
|
The default IMD magnitude is now 100%.
|
||||||
|
|
||||||
Quisk Version 4.2.42 April 2025
|
Quisk Version 4.2.42 April 2025
|
||||||
================================
|
================================
|
||||||
I fixed a bug in the Kenwood IF CAT command. Thanks Nick! I moved the Pulse server IP address to the Sound screen.
|
I fixed a bug in the Kenwood IF CAT command. Thanks Nick! I moved the Pulse server IP address to the Sound screen.
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
# Quisk Software Defined Radio by N2ADR
|
|
||||||
**December 27, 2023**
|
|
||||||
|
|
||||||
This is Quisk, a Software Defined Radio (SDR).
|
|
||||||
You supply radio hardware such as Hermes Lite2 or SoftRock to convert
|
|
||||||
the antenna voltage to I/Q samples.
|
|
||||||
Then send the samples to a computer running Quisk.
|
|
||||||
The Quisk software will read the I/Q data, tune it, filter it,
|
|
||||||
demodulate it, and send the audio to headphones or speakers.
|
|
||||||
Quisk has a microphone input and a key input so it can operate as a
|
|
||||||
complete transceiver.
|
|
||||||
|
|
||||||
The web page for this project is [https://groups.io/g/n2adr-sdr](https://groups.io/g/n2adr-sdr).
|
|
||||||
|
|
||||||
The change log is [here](CHANGELOG.txt).
|
|
||||||
|
|
||||||
Installation instructions are [here](http://james.ahlstrom.name/quisk/docs.html#Installation).
|
|
||||||
|
|
||||||
The documentation is [here](http://james.ahlstrom.name/quisk/docs.html).
|
|
||||||
|
|
||||||
The help file is [here](http://james.ahlstrom.name/quisk/help.html).
|
|
||||||
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
#Quisk version 4.2.42
|
#Quisk version 4.2.53
|
||||||
from .quisk import main
|
from .quisk import main
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ class ControlCommon(BaseHardware): # This is the Hardware class for the control
|
|||||||
for idName in ("Mute", "NR2", "AGC", "Sqlch", "NB 1", "Notch", "Test 1", "Spot", "FDX", "PTT", "VOX"):
|
for idName in ("Mute", "NR2", "AGC", "Sqlch", "NB 1", "Notch", "Test 1", "Spot", "FDX", "PTT", "VOX"):
|
||||||
self.RemoteCtlSend("%s;%d\n" % (idName, app.idName2Button[idName].GetIndex()))
|
self.RemoteCtlSend("%s;%d\n" % (idName, app.idName2Button[idName].GetIndex()))
|
||||||
# Menus
|
# Menus
|
||||||
for menu in (app.NB_menu, app.split_menu, app.freedv_menu, app.smeter_menu):
|
for menu in (app.NB_menu, app.split_menu, app.smeter_menu):
|
||||||
if menu:
|
if menu:
|
||||||
for nid in menu.id2data:
|
for nid in menu.id2data:
|
||||||
menu_item = menu.FindItemById(nid)
|
menu_item = menu.FindItemById(nid)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
#include <Winsock2.h>
|
#include <Winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <string.h>
|
||||||
#else
|
#else
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -105,7 +106,7 @@ static PyObject * open_rx_udp(const char * ip, int port)
|
|||||||
wVersionRequested = MAKEWORD(2, 2);
|
wVersionRequested = MAKEWORD(2, 2);
|
||||||
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
|
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
|
||||||
sprintf(buf, "Failed to initialize Winsock (WSAStartup)");
|
sprintf(buf, "Failed to initialize Winsock (WSAStartup)");
|
||||||
return PyString_FromString(buf);
|
return PyUnicode_FromString(buf);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// quisk_using_udp = 1;
|
// quisk_using_udp = 1;
|
||||||
@@ -145,7 +146,7 @@ static PyObject * open_rx_udp(const char * ip, int port)
|
|||||||
else {
|
else {
|
||||||
sprintf(buf, "Failed to open socket");
|
sprintf(buf, "Failed to open socket");
|
||||||
}
|
}
|
||||||
return PyString_FromString(buf);
|
return PyUnicode_FromString(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject * close_rx_udp(PyObject * self, PyObject * args)
|
static PyObject * close_rx_udp(PyObject * self, PyObject * args)
|
||||||
@@ -343,7 +344,7 @@ static PyObject * open_samples(PyObject * self, PyObject * args)
|
|||||||
quisk_sample_source(NULL, NULL, &afedri_read_rx_udp);
|
quisk_sample_source(NULL, NULL, &afedri_read_rx_udp);
|
||||||
//////////////
|
//////////////
|
||||||
return open_rx_udp(ip, port); // AFEDRI specific
|
return open_rx_udp(ip, port); // AFEDRI specific
|
||||||
// return PyString_FromString(buf); // return a string message
|
// return PyUnicode_FromString(buf); // return a string message
|
||||||
}
|
}
|
||||||
|
|
||||||
// Miscellaneous functions needed by the SDR-IQ; called from the GUI thread as
|
// Miscellaneous functions needed by the SDR-IQ; called from the GUI thread as
|
||||||
|
|||||||
+21
-4
@@ -1,7 +1,24 @@
|
|||||||
|
MODULE_NAME := afedrinet_io
|
||||||
|
OBJDIR := ../build/local
|
||||||
|
|
||||||
afedrinet2:
|
CFLAGS := $(shell python3 ../makefil.py --cflags)
|
||||||
python2 setup.py build_ext --force --inplace
|
LDFLAGS := $(shell python3 ../makefil.py --ldflags)
|
||||||
|
EXTENSION_SUFFIX := $(shell python3 ../makefil.py --suffix)
|
||||||
|
TARGET := $(MODULE_NAME)$(EXTENSION_SUFFIX)
|
||||||
|
|
||||||
afedrinet3:
|
# Default target: Link the shared library object
|
||||||
|
$(TARGET): afedrinet_io.o $(OBJDIR)/import_quisk_api.o $(OBJDIR)/is_key_down.o
|
||||||
|
$(CC) -o $@ afedrinet_io.o $(LDFLAGS) $(OBJDIR)/import_quisk_api.o $(OBJDIR)/is_key_down.o
|
||||||
|
|
||||||
|
# Compile the C source files into object files
|
||||||
|
afedrinet_io.o: afedrinet_io.c ../quisk.h
|
||||||
|
$(CC) -o $@ -c $< $(CFLAGS) -I..
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
-rm -f *.o
|
||||||
|
-rm -f $(MODULE_NAME)*.so
|
||||||
|
|
||||||
|
.PHONY: afedrinet-old
|
||||||
|
afedrinet-old:
|
||||||
python3 setup.py build_ext --force --inplace
|
python3 setup.py build_ext --force --inplace
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Base64 encoding/decoding (RFC1341)
|
||||||
|
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
|
static const unsigned char base64_table[65] =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64_encode - Base64 encode
|
||||||
|
* @src: Data to be encoded
|
||||||
|
* @len: Length of the data to be encoded
|
||||||
|
* @out_len: Pointer to output length variable, or %NULL if not used
|
||||||
|
* Returns: Allocated buffer of out_len bytes of encoded data,
|
||||||
|
* or %NULL on failure
|
||||||
|
*
|
||||||
|
* Caller is responsible for freeing the returned buffer. Returned buffer is
|
||||||
|
* nul terminated to make it easier to use as a C string. The nul terminator is
|
||||||
|
* not included in out_len.
|
||||||
|
*/
|
||||||
|
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||||||
|
size_t *out_len)
|
||||||
|
{
|
||||||
|
unsigned char *out, *pos;
|
||||||
|
const unsigned char *end, *in;
|
||||||
|
size_t olen;
|
||||||
|
int line_len;
|
||||||
|
|
||||||
|
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
|
||||||
|
olen += olen / 72; /* line feeds */
|
||||||
|
olen++; /* nul termination */
|
||||||
|
if (olen < len)
|
||||||
|
return NULL; /* integer overflow */
|
||||||
|
out = malloc(olen);
|
||||||
|
if (out == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
end = src + len;
|
||||||
|
in = src;
|
||||||
|
pos = out;
|
||||||
|
line_len = 0;
|
||||||
|
while (end - in >= 3) {
|
||||||
|
*pos++ = base64_table[in[0] >> 2];
|
||||||
|
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
|
||||||
|
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
|
||||||
|
*pos++ = base64_table[in[2] & 0x3f];
|
||||||
|
in += 3;
|
||||||
|
line_len += 4;
|
||||||
|
if (line_len >= 72) {
|
||||||
|
*pos++ = '\n';
|
||||||
|
line_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end - in) {
|
||||||
|
*pos++ = base64_table[in[0] >> 2];
|
||||||
|
if (end - in == 1) {
|
||||||
|
*pos++ = base64_table[(in[0] & 0x03) << 4];
|
||||||
|
*pos++ = '=';
|
||||||
|
} else {
|
||||||
|
*pos++ = base64_table[((in[0] & 0x03) << 4) |
|
||||||
|
(in[1] >> 4)];
|
||||||
|
*pos++ = base64_table[(in[1] & 0x0f) << 2];
|
||||||
|
}
|
||||||
|
*pos++ = '=';
|
||||||
|
line_len += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line_len)
|
||||||
|
*pos++ = '\n';
|
||||||
|
|
||||||
|
*pos = '\0';
|
||||||
|
if (out_len)
|
||||||
|
*out_len = pos - out;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64_decode - Base64 decode
|
||||||
|
* @src: Data to be decoded
|
||||||
|
* @len: Length of the data to be decoded
|
||||||
|
* @out_len: Pointer to output length variable
|
||||||
|
* Returns: Allocated buffer of out_len bytes of decoded data,
|
||||||
|
* or %NULL on failure
|
||||||
|
*
|
||||||
|
* Caller is responsible for freeing the returned buffer.
|
||||||
|
*/
|
||||||
|
unsigned char * base64_decode(const unsigned char *src, size_t len,
|
||||||
|
size_t *out_len)
|
||||||
|
{
|
||||||
|
unsigned char dtable[256], *out, *pos, block[4], tmp;
|
||||||
|
size_t i, count, olen;
|
||||||
|
int pad = 0;
|
||||||
|
|
||||||
|
memset(dtable, 0x80, 256);
|
||||||
|
for (i = 0; i < sizeof(base64_table) - 1; i++)
|
||||||
|
dtable[base64_table[i]] = (unsigned char) i;
|
||||||
|
dtable['='] = 0;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (dtable[src[i]] != 0x80)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0 || count % 4)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
olen = count / 4 * 3;
|
||||||
|
pos = out = malloc(olen);
|
||||||
|
if (out == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
tmp = dtable[src[i]];
|
||||||
|
if (tmp == 0x80)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (src[i] == '=')
|
||||||
|
pad++;
|
||||||
|
block[count] = tmp;
|
||||||
|
count++;
|
||||||
|
if (count == 4) {
|
||||||
|
*pos++ = (block[0] << 2) | (block[1] >> 4);
|
||||||
|
*pos++ = (block[1] << 4) | (block[2] >> 2);
|
||||||
|
*pos++ = (block[2] << 6) | block[3];
|
||||||
|
count = 0;
|
||||||
|
if (pad) {
|
||||||
|
if (pad == 1)
|
||||||
|
pos--;
|
||||||
|
else if (pad == 2)
|
||||||
|
pos -= 2;
|
||||||
|
else {
|
||||||
|
/* Invalid padding */
|
||||||
|
free(out);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_len = pos - out;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Base64 encoding/decoding (RFC1341)
|
||||||
|
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BASE64_H
|
||||||
|
#define BASE64_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||||||
|
size_t *out_len);
|
||||||
|
unsigned char * base64_decode(const unsigned char *src, size_t len,
|
||||||
|
size_t *out_len);
|
||||||
|
|
||||||
|
#endif /* BASE64_H */
|
||||||
+21
-25
@@ -88,6 +88,7 @@ class Configuration:
|
|||||||
platform_ignore = 'win_'
|
platform_ignore = 'win_'
|
||||||
self.sections = []
|
self.sections = []
|
||||||
self.receiver_data = []
|
self.receiver_data = []
|
||||||
|
self.receiver_explain = []
|
||||||
self.StatePath = conf.settings_file_path
|
self.StatePath = conf.settings_file_path
|
||||||
if not self.StatePath:
|
if not self.StatePath:
|
||||||
self.StatePath = os.path.join(app.QuiskFilesDir, "quisk_settings.json")
|
self.StatePath = os.path.join(app.QuiskFilesDir, "quisk_settings.json")
|
||||||
@@ -158,9 +159,9 @@ class Configuration:
|
|||||||
dlg.Destroy()
|
dlg.Destroy()
|
||||||
else:
|
else:
|
||||||
radio_dict["use_soapy"] = '0'
|
radio_dict["use_soapy"] = '0'
|
||||||
if radio_type not in ("HiQSDR", "Hermes", "Red Pitaya", "Odyssey", "Odyssey2"):
|
if radio_type not in ("HiQSDR", "Hermes", "Red Pitaya", "Odyssey", "Odyssey2", "OpenHPSDR"):
|
||||||
radio_dict["use_rx_udp"] = '0'
|
radio_dict["use_rx_udp"] = '0'
|
||||||
if radio_type in ("Hermes", "Red Pitaya", "Odyssey2"):
|
if radio_type in ("Hermes", "Red Pitaya", "Odyssey2", "OpenHPSDR"):
|
||||||
if "Hermes_BandDict" not in radio_dict:
|
if "Hermes_BandDict" not in radio_dict:
|
||||||
radio_dict["Hermes_BandDict"] = {}
|
radio_dict["Hermes_BandDict"] = {}
|
||||||
if "Hermes_BandDictTx" not in radio_dict:
|
if "Hermes_BandDictTx" not in radio_dict:
|
||||||
@@ -601,6 +602,7 @@ class Configuration:
|
|||||||
# Receiver sections start with 16 #, "Receivers ", receiver name, explain
|
# Receiver sections start with 16 #, "Receivers ", receiver name, explain
|
||||||
# self.receiver_data is a list of [receiver_name, receiver_data]
|
# self.receiver_data is a list of [receiver_name, receiver_data]
|
||||||
# receiver_data is a list of [data_name, text, fmt, help_text, values]
|
# receiver_data is a list of [data_name, text, fmt, help_text, values]
|
||||||
|
# self.receiver_explain is a list of the text in Receivers: the name, a comma and the explanation.
|
||||||
|
|
||||||
# Variable names start with ## variable_name variable_text, format
|
# Variable names start with ## variable_name variable_text, format
|
||||||
# The format is integer, number, text, boolean, integer choice, text choice, rfile
|
# The format is integer, number, text, boolean, integer choice, text choice, rfile
|
||||||
@@ -638,6 +640,7 @@ class Configuration:
|
|||||||
continue
|
continue
|
||||||
if line[0:27] == '################ Receivers ':
|
if line[0:27] == '################ Receivers ':
|
||||||
section = 'Receivers'
|
section = 'Receivers'
|
||||||
|
self.receiver_explain.append(line[27:].strip())
|
||||||
args = line[27:].split(',', 1)
|
args = line[27:].split(',', 1)
|
||||||
rxname = args[0].strip()
|
rxname = args[0].strip()
|
||||||
section_data = []
|
section_data = []
|
||||||
@@ -2298,8 +2301,8 @@ class ControlMixin:
|
|||||||
if self.radio_name == Settings[1]: # changed for current radio
|
if self.radio_name == Settings[1]: # changed for current radio
|
||||||
if name in ('hot_key_ptt_toggle', 'hot_key_ptt_if_hidden', 'keyupDelay', 'cwTone', 'pulse_audio_verbose_output',
|
if name in ('hot_key_ptt_toggle', 'hot_key_ptt_if_hidden', 'keyupDelay', 'cwTone', 'pulse_audio_verbose_output',
|
||||||
'start_cw_delay', 'start_ssb_delay', 'maximum_tx_secs', 'quisk_serial_cts', 'quisk_serial_dsr',
|
'start_cw_delay', 'start_ssb_delay', 'maximum_tx_secs', 'quisk_serial_cts', 'quisk_serial_dsr',
|
||||||
'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec', 'hermes_lite2_enable',
|
'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec',
|
||||||
'fixed_tune_offset'):
|
'fixed_tune_offset', 'calibrate_temp_20', 'calibrate_temp_40', 'calibrate_current_0', 'calibrate_current_1'):
|
||||||
setattr(conf, name, x)
|
setattr(conf, name, x)
|
||||||
application.ImmediateChange(name)
|
application.ImmediateChange(name)
|
||||||
elif name[0:4] in ('lin_', 'win_'):
|
elif name[0:4] in ('lin_', 'win_'):
|
||||||
@@ -2400,6 +2403,7 @@ class BaseWindow(wx.ScrolledWindow, ControlMixin):
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
wx.ScrolledWindow.__init__(self, parent)
|
wx.ScrolledWindow.__init__(self, parent)
|
||||||
ControlMixin.__init__(self, parent)
|
ControlMixin.__init__(self, parent)
|
||||||
|
self.SetScrollRate(8, 8)
|
||||||
|
|
||||||
class ConfigConfig(BaseWindow):
|
class ConfigConfig(BaseWindow):
|
||||||
def __init__(self, parent, width):
|
def __init__(self, parent, width):
|
||||||
@@ -2510,7 +2514,6 @@ The CW message will then repeat. A time of zero means no repeat.'
|
|||||||
sl, btn = self.AddTextSliderHelp(4, " Repeat secs %.1f ", 0, 0, 100, self.OnPlayFileRepeat, help_text, span=2, scale=0.1)
|
sl, btn = self.AddTextSliderHelp(4, " Repeat secs %.1f ", 0, 0, 100, self.OnPlayFileRepeat, help_text, span=2, scale=0.1)
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
self.FitInside()
|
self.FitInside()
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def MakeFileButton(self, text, path, index, name, help_text):
|
def MakeFileButton(self, text, path, index, name, help_text):
|
||||||
if index < 10: # record buttons
|
if index < 10: # record buttons
|
||||||
cb = self.AddCheckBox(4, text, self.OnCheckRecPlay)
|
cb = self.AddCheckBox(4, text, self.OnCheckRecPlay)
|
||||||
@@ -2688,7 +2691,6 @@ class ConfigTxAudio(BaseWindow):
|
|||||||
application.CtrlTxAudioPreemph = sld
|
application.CtrlTxAudioPreemph = sld
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
self.FitInside()
|
self.FitInside()
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def OnTimer(self, event):
|
def OnTimer(self, event):
|
||||||
if not self.tmp_playing:
|
if not self.tmp_playing:
|
||||||
self.timer.Stop()
|
self.timer.Stop()
|
||||||
@@ -2737,8 +2739,8 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
|||||||
self.NextRow()
|
self.NextRow()
|
||||||
rb = self.AddRadioButton(1, "Add a new radio with the general type", self.NewTop)
|
rb = self.AddRadioButton(1, "Add a new radio with the general type", self.NewTop)
|
||||||
choices = []
|
choices = []
|
||||||
for name, data in local_conf.receiver_data:
|
for explain in local_conf.receiver_explain:
|
||||||
choices.append(name)
|
choices.append(explain)
|
||||||
choices.sort()
|
choices.sort()
|
||||||
self.add_type = self.AddComboCtrl(2, '', choices=choices, no_edit=True)
|
self.add_type = self.AddComboCtrl(2, '', choices=choices, no_edit=True)
|
||||||
self.add_type.SetSelection(0)
|
self.add_type.SetSelection(0)
|
||||||
@@ -2766,7 +2768,6 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
|||||||
self.apply_btn = self.AddPushButton(3, "Apply", self.OnBtnApply)
|
self.apply_btn = self.AddPushButton(3, "Apply", self.OnBtnApply)
|
||||||
self.NewRadioNames()
|
self.NewRadioNames()
|
||||||
self.NewTop()
|
self.NewTop()
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def NewTop(self, event=None):
|
def NewTop(self, event=None):
|
||||||
if event:
|
if event:
|
||||||
btn = event.GetEventObject()
|
btn = event.GetEventObject()
|
||||||
@@ -2810,7 +2811,7 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
|||||||
if not name or self.DuplicateName(name):
|
if not name or self.DuplicateName(name):
|
||||||
return
|
return
|
||||||
self.add_name.SetValue('')
|
self.add_name.SetValue('')
|
||||||
typ = self.add_type.GetValue().strip()
|
typ = self.add_type.GetValue().split(',')[0].strip()
|
||||||
if local_conf.AddRadio(name, typ):
|
if local_conf.AddRadio(name, typ):
|
||||||
self.NewRadioNames()
|
self.NewRadioNames()
|
||||||
local_conf.settings_changed = True
|
local_conf.settings_changed = True
|
||||||
@@ -3003,7 +3004,6 @@ class RadioSection(BaseWindow): # The pages for each section in the second-leve
|
|||||||
self.AddColSpacer(2, 20)
|
self.AddColSpacer(2, 20)
|
||||||
self.AddColSpacer(5, 20)
|
self.AddColSpacer(5, 20)
|
||||||
self.FitInside()
|
self.FitInside()
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def OnButtonChangeFavorites(self, event):
|
def OnButtonChangeFavorites(self, event):
|
||||||
if isinstance(event, (ChoiceCombo, ComboCtrl)):
|
if isinstance(event, (ChoiceCombo, ComboCtrl)):
|
||||||
path = event.GetValue()
|
path = event.GetValue()
|
||||||
@@ -3128,7 +3128,6 @@ class RadioHardwareBase(BaseWindow): # The Hardware page in the second-level no
|
|||||||
self.NextRow(7)
|
self.NextRow(7)
|
||||||
self.AddColSpacer(2, 20)
|
self.AddColSpacer(2, 20)
|
||||||
self.AddColSpacer(5, 20)
|
self.AddColSpacer(5, 20)
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def OnButtonChangeHardware(self, event):
|
def OnButtonChangeHardware(self, event):
|
||||||
if isinstance(event, (ChoiceCombo, ComboCtrl)):
|
if isinstance(event, (ChoiceCombo, ComboCtrl)):
|
||||||
path = event.GetValue()
|
path = event.GetValue()
|
||||||
@@ -3175,25 +3174,24 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level
|
|||||||
data_names = local_conf.GetReceiverData(radio_type)
|
data_names = local_conf.GetReceiverData(radio_type)
|
||||||
col = 1
|
col = 1
|
||||||
border = 2
|
border = 2
|
||||||
hermes_board_id = 0
|
try:
|
||||||
if radio_type == "Hermes":
|
is_HermesLite2 = application.Hardware.is_HermesLite2
|
||||||
try:
|
except:
|
||||||
hermes_board_id = application.Hardware.hermes_board_id
|
is_HermesLite2 = False
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if radio_name == Settings[1] and hasattr(application.Hardware, "ProgramGateware"):
|
if radio_name == Settings[1] and hasattr(application.Hardware, "ProgramGateware"):
|
||||||
help_text = "Choose an RBF file and program the Gateware (FPGA software) over Ethernet."
|
help_text = "Choose an RBF file and program the Gateware (FPGA software) over Ethernet."
|
||||||
self.AddTextButtonHelp(1, "Gateware Update", "Program from RBF file..", application.Hardware.ProgramGateware, help_text)
|
self.AddTextButtonHelp(1, "Gateware Update", "Program from RBF file..", application.Hardware.ProgramGateware, help_text)
|
||||||
col = 1
|
col = 1
|
||||||
self.NextRow(self.row + 2)
|
self.NextRow(self.row + 2)
|
||||||
for name, text, fmt, help_text, values in data_names:
|
for name, text, fmt, help_text, values in data_names:
|
||||||
|
text = text.replace("\\u2103", "\u2103")
|
||||||
if name in ('hardware_file_name', 'widgets_file_name'):
|
if name in ('hardware_file_name', 'widgets_file_name'):
|
||||||
pass
|
pass
|
||||||
elif name[0:4] == platform_ignore:
|
elif name[0:4] == platform_ignore:
|
||||||
pass
|
pass
|
||||||
elif name in ('Hermes_BandDictEnTx', ):
|
elif name in ('Hermes_BandDictEnTx', ):
|
||||||
pass
|
pass
|
||||||
elif 'Hl2_' in name and hermes_board_id != 6:
|
elif 'Hl2_' in name and not is_HermesLite2:
|
||||||
pass
|
pass
|
||||||
elif fmt[0:4] in ('dict', 'list'):
|
elif fmt[0:4] in ('dict', 'list'):
|
||||||
pass
|
pass
|
||||||
@@ -3222,7 +3220,7 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level
|
|||||||
col = 1
|
col = 1
|
||||||
border = 2
|
border = 2
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
if hermes_board_id == 6:
|
if is_HermesLite2:
|
||||||
if col == 4:
|
if col == 4:
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
help_text = ('This controls the bias level for transistors in the final power amplifier. Enter a level from 0 to 255.'
|
help_text = ('This controls the bias level for transistors in the final power amplifier. Enter a level from 0 to 255.'
|
||||||
@@ -3238,7 +3236,6 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level
|
|||||||
self.HermesWriteBiasButton = self.AddPushButton(7, "Write", self.OnButtonHermesWriteBias, border=0)
|
self.HermesWriteBiasButton = self.AddPushButton(7, "Write", self.OnButtonHermesWriteBias, border=0)
|
||||||
self.HermesWriteBiasButton.Enable(enbl)
|
self.HermesWriteBiasButton.Enable(enbl)
|
||||||
self.FitInside()
|
self.FitInside()
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def OnHermesChangeBias0(self, event):
|
def OnHermesChangeBias0(self, event):
|
||||||
value = self.HermesBias0.GetValue()
|
value = self.HermesBias0.GetValue()
|
||||||
application.Hardware.ChangeBias0(value)
|
application.Hardware.ChangeBias0(value)
|
||||||
@@ -3852,7 +3849,6 @@ class RadioSound(BaseWindow): # The Sound page in the second-level notebook for
|
|||||||
break
|
break
|
||||||
self.gbs = gbs
|
self.gbs = gbs
|
||||||
self.FitInside()
|
self.FitInside()
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def ItemValue(self, row, col):
|
def ItemValue(self, row, col):
|
||||||
data_name = self.sound_names[row][col]
|
data_name = self.sound_names[row][col]
|
||||||
if col == 4: # Device names
|
if col == 4: # Device names
|
||||||
@@ -4080,7 +4076,6 @@ The Tx bits are sent as the "Alex" filters and are sent to the J16 interface.'
|
|||||||
self.AddBitField(col + 1, 7, 'Hermes_BandDictTx', band, bus, self.ChangeIO)
|
self.AddBitField(col + 1, 7, 'Hermes_BandDictTx', band, bus, self.ChangeIO)
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
self.FitInside()
|
self.FitInside()
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def SortCmp(self, item1):
|
def SortCmp(self, item1):
|
||||||
# Numerical conversion of band name to megahertz
|
# Numerical conversion of band name to megahertz
|
||||||
try:
|
try:
|
||||||
@@ -4208,6 +4203,9 @@ class RadioRemote(BaseWindow): # The Remote page in the second-level notebook f
|
|||||||
heading = None
|
heading = None
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
self.col = 2
|
self.col = 2
|
||||||
|
if not heading:
|
||||||
|
heading = self.AddTextHeading("TCI", text,
|
||||||
|
'Rig control commands and digital sample streams are sent to multiple clients using the TCI protocol.')
|
||||||
if not heading:
|
if not heading:
|
||||||
heading = self.AddTextHeading("Hamlib Rig 2", text,
|
heading = self.AddTextHeading("Hamlib Rig 2", text,
|
||||||
'Rig control commands using "Hamlib Rig 2" are sent to a Quisk TCP port from multiple clients.')
|
'Rig control commands using "Hamlib Rig 2" are sent to a Quisk TCP port from multiple clients.')
|
||||||
@@ -4255,7 +4253,6 @@ class RadioRemote(BaseWindow): # The Remote page in the second-level notebook f
|
|||||||
self.AddColSpacer(3, 20)
|
self.AddColSpacer(3, 20)
|
||||||
self.AddColSpacer(6, 20)
|
self.AddColSpacer(6, 20)
|
||||||
self.FitInside()
|
self.FitInside()
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def OnChangePassword(self, event):
|
def OnChangePassword(self, event):
|
||||||
text = local_conf.globals.get('remote_radio_password', '')
|
text = local_conf.globals.get('remote_radio_password', '')
|
||||||
dlg = wx.TextEntryDialog(self, "Please enter a password for remote access",
|
dlg = wx.TextEntryDialog(self, "Please enter a password for remote access",
|
||||||
@@ -4337,7 +4334,6 @@ class xxRadioFilters(BaseWindow): # The Filters page in the second-level notebo
|
|||||||
self.NextRow()
|
self.NextRow()
|
||||||
index += 1
|
index += 1
|
||||||
self.FitInside()
|
self.FitInside()
|
||||||
self.SetScrollRate(1, 1)
|
|
||||||
def OnChangeFreq(self, event):
|
def OnChangeFreq(self, event):
|
||||||
freq = event.GetString()
|
freq = event.GetString()
|
||||||
radio_dict = local_conf.GetRadioDict(self.radio_name)
|
radio_dict = local_conf.GetRadioDict(self.radio_name)
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ h3 {font-size:1.6rem;}
|
|||||||
<li><a href="#Digital">Digital Modes</a></li>
|
<li><a href="#Digital">Digital Modes</a></li>
|
||||||
<li><a href="#Hamlib">Rig Control and Logging</a></li>
|
<li><a href="#Hamlib">Rig Control and Logging</a></li>
|
||||||
<li><a href="#wsjtx">WSJT-X</a></li>
|
<li><a href="#wsjtx">WSJT-X</a></li>
|
||||||
|
<li><a href="#freedv">FreeDV</a></li>
|
||||||
|
<li><a href="#wdsp">NR2 and WDSP</a></li>
|
||||||
|
<li><a href="#pure">Pure Signal</a></li>
|
||||||
<li><a href="#VNA">VNA</a></li>
|
<li><a href="#VNA">VNA</a></li>
|
||||||
<li><a href="#Remote">Remote Operation</a></li>
|
<li><a href="#Remote">Remote Operation</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -81,7 +84,7 @@ h3 {font-size:1.6rem;}
|
|||||||
<div class="contents">
|
<div class="contents">
|
||||||
<h1>
|
<h1>
|
||||||
<a id="Welcome"></a>
|
<a id="Welcome"></a>
|
||||||
Welcome to QUISK (December 2023)
|
Welcome to QUISK (June 2026)
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -194,18 +197,18 @@ Quisk is free open source software written in Python and C. It is hosted on the
|
|||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
If you have Python 3 installed on your computer, just continue to use that version.
|
If you have Python 3 installed on your computer, just continue to use that version.
|
||||||
<b>On Windows Quisk requires Python 3.8, 3.9, 3.10, 3.11, 3.12 or 3.13.</b>
|
On Windows Quisk requires Python 3.8 or higher.
|
||||||
If you want to install a more recent version of Python, uninstall the old version first.
|
If you want to install a more recent version of Python, uninstall the old version first.
|
||||||
Running multiple versions of Python is possible but can be confusing and is not necessary.
|
Running multiple versions of Python is possible but can be confusing and is not necessary.
|
||||||
A Quisk installation is needed for each version of Python you have.
|
<b>A Quisk installation is needed for each version of Python you have. If you upgrade to a newer Python
|
||||||
|
you must re-install Quisk. But your Quisk settings will be preserved.</b>
|
||||||
</p>
|
</p>
|
||||||
<br>
|
<br>
|
||||||
<h3 id="g0.2.2">Windows Initial Installation</h3>
|
<h3 id="g0.2.2">Windows Initial Installation</h3>
|
||||||
<p>
|
<p>
|
||||||
Windows does not include Python, so you must first install a recent version of 64-bit Python 3.
|
Windows does not include Python, so you must first install a recent version of 64-bit Python 3.
|
||||||
If you already have 64-bit Python 3 installed, just keep using it.
|
If you already have 64-bit Python 3 installed, just keep using it.
|
||||||
<b>On Windows Quisk requires Python 3.8, 3.9, 3.10, 3.11, 3.12 or 3.13.</b>
|
On Windows Quisk requires Python 3.8 or higher.
|
||||||
There may be newer versions of Python, but until support is added, use the versions listed above.
|
|
||||||
Python is available from <a href="http://www.python.org/">http://www.python.org</a>.
|
Python is available from <a href="http://www.python.org/">http://www.python.org</a>.
|
||||||
There is an option to <b>Add Python to PATH</b>. You MUST select
|
There is an option to <b>Add Python to PATH</b>. You MUST select
|
||||||
this option so that you can start python by just typing "python" instead of the whole path
|
this option so that you can start python by just typing "python" instead of the whole path
|
||||||
@@ -373,12 +376,14 @@ python3 quisk.py
|
|||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
If "make" fails, you probably have missing packages or missing "-dev" packages. Try to figure out what is missing from the error messages.
|
If "make" fails, you probably have missing packages or missing "-dev" packages. Try to figure out what is missing from the error messages.
|
||||||
You only clone github for the initial installation. To update Quisk, use pull:
|
You only clone github for the initial installation. To update Quisk, use pull followed by make:
|
||||||
</p>
|
</p>
|
||||||
<br>
|
<br>
|
||||||
<pre>
|
<pre>
|
||||||
cd ~/quisk
|
cd ~/quisk
|
||||||
git pull
|
git pull
|
||||||
|
make clean
|
||||||
|
make
|
||||||
</pre>
|
</pre>
|
||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
@@ -1429,9 +1434,35 @@ is no longer needed
|
|||||||
<br>
|
<br>
|
||||||
</p>
|
</p>
|
||||||
<h2 id="Digital">Digital Modes </h2>
|
<h2 id="Digital">Digital Modes </h2>
|
||||||
|
<br>
|
||||||
|
<h3>Digital Modes with the TCI Protocol</h3>
|
||||||
<p>
|
<p>
|
||||||
|
Using the sound devices in a PC to transfer digital sample streams has always caused problems.
|
||||||
|
Several new methods are now becoming available. These are the <a href="https://github.com/ExpertSDR3/TCI">TCI protocol</a>
|
||||||
|
used by <a href="https://eesdr.com/en/">Expert Electronics</a> for
|
||||||
|
their SunSDR hardware
|
||||||
|
and <a href="https://www.vita.com/page-1855484">Vita49</a> used by <a href="https://www.flexradio.com">FlexRadio</a>.
|
||||||
|
And Hamlib is planning to add digital stream support.
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
Quisk can now connect to WSJT-X version 3.0.0 with the TCI protocol.
|
||||||
|
On the Quisk Config/radio/Remote screen enter IP address 127.0.0.1 and port 40001.
|
||||||
|
Use USB for digital modes. The DGT-* modes are for sound cards.
|
||||||
|
In WSJT-X choose the radio "TCI Client RX1", and for audio choose "TCI audio".
|
||||||
|
And enter the same IP and port. Everything then just works.
|
||||||
|
If your WSJT-X does not have "TCI Client RX1" you need to update to a more recent version.
|
||||||
|
This feature is new. To see the TCI conversation, use the "Debug level" on the Config/radio/Options screen.
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h3>Digital Modes with Sound Cards</h3>
|
||||||
|
<p>
|
||||||
|
Digital modes need to receive the audio from the radio to decode it.
|
||||||
|
They need to send audio to the radio to transmit.
|
||||||
|
These audio streams have traditionally been sent using PC sound cards or
|
||||||
|
an artificial sound card known as a Virtual Audio Cable.
|
||||||
Quisk has a number of modes "DGT-" to receive and transmit digital
|
Quisk has a number of modes "DGT-" to receive and transmit digital
|
||||||
signals. The modes "DGT-U" and "DGT-L" decode the signal as upper
|
signals on sound devices. The modes "DGT-U" and "DGT-L" decode the signal as upper
|
||||||
or lower sideband, and send the stereo audio to the digital sound
|
or lower sideband, and send the stereo audio to the digital sound
|
||||||
device. The left and right channel are the same.
|
device. The left and right channel are the same.
|
||||||
The bandwidth is set with the filter buttons as
|
The bandwidth is set with the filter buttons as
|
||||||
@@ -1603,6 +1634,46 @@ For Linux, use the pulseaudio devices:
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
<h2 id="freedv">FreeDV Digital Voice</h2>
|
||||||
|
<p>
|
||||||
|
The <a href="https://freedv.org/">FreeDV project</a> has produced a new and exciting digital voice mode, the
|
||||||
|
<a href="https://github.com/drowe67/radae/blob/main/doc/rade_intro_waveform.pdf">Radio Autoencoder RADE</a>.
|
||||||
|
The RADE mode is a real advance. It has a small peak to average power radio. And its digital constellation is not
|
||||||
|
discrete dots but rather a continuous analog pattern. It works great and sounds great!
|
||||||
|
You can make digital voice QSO's by using the FreeDV-GUI program. It runs in its own window and connects to your
|
||||||
|
HF radio using the same virtual audio connections used by other digital programs such as WSJT-X.
|
||||||
|
I wrote a <a href="http://james.ahlstrom.name/rade.html">guide to using FreeDV-GUI</a> to make it easier to install and use it.
|
||||||
|
</p>
|
||||||
|
<br><br>
|
||||||
|
<h2 id="wdsp">NR2 and WDSP</h2>
|
||||||
|
<p>
|
||||||
|
Quisk uses the WDSP library for the NR2 noise reduction and the SNB noise blanker. The WDSP library must be available
|
||||||
|
for these features to work. For windows, the library is included. For Linux you must build it by changing to the
|
||||||
|
quisk/wdsp directory and entering "make". The library is used in-place and no installation is necessary.
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
The new "trained" method of NR2 is included. Press the NR2 button twice to select "NR2t".
|
||||||
|
</p>
|
||||||
|
<br><br>
|
||||||
|
<h2 id="pure">Pure Signal</h2>
|
||||||
|
<p>
|
||||||
|
Pure Signal is a term used to describe baseband predistortion, a technique used to reduce IMD products
|
||||||
|
caused by a non-linear power amp. Pure Signal is available for the Hermes Lite2.
|
||||||
|
To use it first press the PsCal button to create a calibration. This will key a two second CW carrier.
|
||||||
|
Then press PS to enable corrections. You can view the result by using the "FDX" button to see the Tx output.
|
||||||
|
The feature is automatic and no settings are required. You can re-calibrate if you change bands, but this is not
|
||||||
|
really necessary.
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
You need to supply a sample of the amplifier output so Pure Signal can correct it. First set the "LNA during Tx dB" to
|
||||||
|
the minimum gain of -12 on the Hermes Hardware screen. This minimizes the preamp gain and the noise picked up from the HL2 board.
|
||||||
|
Then provide a Tx sample much above the remaining board noise but below clipping. Use the FDX button to see the Tx output
|
||||||
|
with and without the Tx sample connected. Twenty or thirty dB over board noise should be enough. To learn how to provide a Tx
|
||||||
|
signal sample, see <a href="https://github.com/softerhardware/Hermes-Lite2/wiki/PureSignal">the HL2 Pure Signal wiki</a>.
|
||||||
|
</p>
|
||||||
|
<br><br>
|
||||||
<h2 id="VNA">Vector Network Analyzer</h2>
|
<h2 id="VNA">Vector Network Analyzer</h2>
|
||||||
<p>
|
<p>
|
||||||
If you have my transceiver hardware from 2010 QEX, or the newer HiQSDR
|
If you have my transceiver hardware from 2010 QEX, or the newer HiQSDR
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
#include "quisk.h"
|
#include "quisk.h"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
#include <complex.h> // Use native C99 complex type for fftw3
|
#include <complex.h> // Use native C99 complex type for fftw3
|
||||||
#include "quisk.h"
|
#include "quisk.h"
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
|
|||||||
@@ -1,681 +1 @@
|
|||||||
#include <Python.h>
|
// This file is no longer used.
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <complex.h> // Use native C99 complex type for fftw3
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "quisk.h"
|
|
||||||
#include "freedv.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
int DEBUG;
|
|
||||||
|
|
||||||
#define MAX_RECEIVERS 2
|
|
||||||
|
|
||||||
typedef struct { // from comp.h
|
|
||||||
float real;
|
|
||||||
float imag;
|
|
||||||
} COMP;
|
|
||||||
|
|
||||||
struct freedv; // from freedv_api.h
|
|
||||||
typedef void (*freedv_callback_rx)(void *, char);
|
|
||||||
typedef char (*freedv_callback_tx)(void *);
|
|
||||||
/* Protocol bits are packed MSB-first */
|
|
||||||
/* Called when a frame containing protocol data is decoded */
|
|
||||||
typedef void (*freedv_callback_protorx)(void *, char *);
|
|
||||||
/* Called when a frame containing protocol data is to be sent */
|
|
||||||
typedef void (*freedv_callback_prototx)(void *, char *);
|
|
||||||
/* Data packet callbacks */
|
|
||||||
/* Called when a packet has been received */
|
|
||||||
typedef void (*freedv_callback_datarx)(void *, unsigned char *packet, size_t size);
|
|
||||||
/* Called when a new packet can be send */
|
|
||||||
typedef void (*freedv_callback_datatx)(void *, unsigned char *packet, size_t *size);
|
|
||||||
|
|
||||||
/* advanced freedv open options required by some modes */
|
|
||||||
struct freedv_advanced {
|
|
||||||
int interleave_frames; // now unused but remains to prevent breaking API for
|
|
||||||
// legacy apps
|
|
||||||
|
|
||||||
// parameters for FREEDV_MODE_FSK_LDPC
|
|
||||||
int M; // 2 or 4 FSK
|
|
||||||
int Rs; // Symbol rate Hz
|
|
||||||
int Fs; // Sample rate Hz
|
|
||||||
int first_tone; // Freq of first tone Hz
|
|
||||||
int tone_spacing; // Spacing between tones Hz
|
|
||||||
char *codename; // LDPC codename, from codes listed in ldpc_codes.c
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
|
||||||
#include <windows.h>
|
|
||||||
HMODULE hLib;
|
|
||||||
#define GET_HANDLE1 hLib = LoadLibrary("libcodec2.dll")
|
|
||||||
#define GET_HANDLE2 hLib = LoadLibrary(".\\freedvpkg\\libcodec2.dll")
|
|
||||||
#define GET_HANDLE3 hLib = LoadLibrary(".\\freedvpkg\\libcodec2_32.dll")
|
|
||||||
#define GET_HANDLE4 hLib = LoadLibrary(".\\freedvpkg\\libcodec2_64.dll")
|
|
||||||
#define GET_ADDR(name) (void *)GetProcAddress(hLib, name)
|
|
||||||
#define CLOSE_LIB FreeLibrary(hLib)
|
|
||||||
#else
|
|
||||||
#include <dlfcn.h>
|
|
||||||
void * hLib;
|
|
||||||
#define GET_HANDLE1 hLib = dlopen("libcodec2.so", RTLD_LAZY)
|
|
||||||
#define GET_HANDLE2 hLib = dlopen("./freedvpkg/libcodec2.so", RTLD_LAZY)
|
|
||||||
#define GET_HANDLE3 hLib = dlopen("./freedvpkg/libcodec2_32.so", RTLD_LAZY)
|
|
||||||
#define GET_HANDLE4 hLib = dlopen("./freedvpkg/libcodec2_64.so", RTLD_LAZY)
|
|
||||||
#define GET_ADDR(name) dlsym(hLib, name)
|
|
||||||
#define CLOSE_LIB dlclose(hLib)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int requested_mode = -1; // requested mode
|
|
||||||
int freedv_current_mode = -1; // the current running mode
|
|
||||||
static int quisk_freedv_squelch;
|
|
||||||
static int interleave_frames = 1;
|
|
||||||
static int freedv_version = -1;
|
|
||||||
static int quisk_set_tx_bpf = 1;
|
|
||||||
static int handle_index = -1;
|
|
||||||
// need access to these here and in quisk.c
|
|
||||||
int n_speech_sample_rate = 8000;
|
|
||||||
int n_modem_sample_rate = 8000;
|
|
||||||
int n_max_modem_samples = 1000;
|
|
||||||
|
|
||||||
#define SPEECH_BUF_SIZE 12000 // speech buffer size increased from 3000
|
|
||||||
#define MAX_MODEM_SAMPLES 8192 // maximum of n_max_modem_samples for any mode
|
|
||||||
#define MAX_SPEECH_SAMPLES 8192 // maximum of n_speech_samples for any mode
|
|
||||||
static struct _rx_channel{
|
|
||||||
struct freedv * hFreedv;
|
|
||||||
short * demod_in;
|
|
||||||
int rxdata_index;
|
|
||||||
short speech_out[SPEECH_BUF_SIZE]; // output buffer
|
|
||||||
int speech_available; // number of samples in output buffer
|
|
||||||
int playing; // are we currently returning speech samples?
|
|
||||||
} rx_channel[MAX_RECEIVERS] = { 0 }; // safe initialisation to 0
|
|
||||||
|
|
||||||
// freedv_version is the library version number, or
|
|
||||||
// -1 no library was found
|
|
||||||
// -2 a library was found, but freedv_get_version is missing
|
|
||||||
|
|
||||||
// FreeDV API functions:
|
|
||||||
// open, close
|
|
||||||
struct freedv * (*freedv_open)(int mode);
|
|
||||||
struct freedv * (*freedv_open_advanced)(int mode, struct freedv_advanced *adv);
|
|
||||||
void (*freedv_close)(struct freedv *freedv);
|
|
||||||
// Transmit
|
|
||||||
void (*freedv_tx)(struct freedv *freedv, short *, short *);
|
|
||||||
void (*freedv_comptx)(struct freedv *freedv, COMP *, short *);
|
|
||||||
// Receive
|
|
||||||
int (*freedv_nin)(struct freedv *freedv);
|
|
||||||
int (*freedv_rx)(struct freedv *freedv, short *, short demod_in[]);
|
|
||||||
int (*freedv_floatrx)(struct freedv *freedv, short *, float demod_in[]);
|
|
||||||
int (*freedv_comprx)(struct freedv *freedv, short *, COMP demod_in[]);
|
|
||||||
// Set parameters
|
|
||||||
void (*freedv_set_callback_txt) (struct freedv *freedv, freedv_callback_rx rx, freedv_callback_tx tx, void *callback_state);
|
|
||||||
void (*freedv_set_test_frames) (struct freedv *freedv, int test_frames);
|
|
||||||
void (*freedv_set_smooth_symbols) (struct freedv *freedv, int smooth_symbols);
|
|
||||||
void (*freedv_set_squelch_en) (struct freedv *freedv, int squelch_en);
|
|
||||||
void (*freedv_set_snr_squelch_thresh) (struct freedv *freedv, float snr_squelch_thresh);
|
|
||||||
void (*freedv_set_tx_bpf) (struct freedv *freedv, int val);
|
|
||||||
// Get parameters
|
|
||||||
int (*freedv_get_version)(void);
|
|
||||||
void (*freedv_get_modem_stats)(struct freedv *freedv, int *sync, float *snr_est);
|
|
||||||
int (*freedv_get_test_frames) (struct freedv *freedv);
|
|
||||||
int (*freedv_get_n_speech_samples) (struct freedv *freedv);
|
|
||||||
int (*freedv_get_n_max_modem_samples) (struct freedv *freedv);
|
|
||||||
int (*freedv_get_n_nom_modem_samples) (struct freedv *freedv);
|
|
||||||
int (*freedv_get_total_bits) (struct freedv *freedv);
|
|
||||||
int (*freedv_get_total_bit_errors) (struct freedv *freedv);
|
|
||||||
int (*freedv_get_modem_sample_rate) (struct freedv *freedv);
|
|
||||||
int (*freedv_get_speech_sample_rate) (struct freedv *freedv);
|
|
||||||
//
|
|
||||||
int (*freedv_get_sync) (struct freedv *freedv);
|
|
||||||
void (*freedv_set_callback_protocol) (struct freedv *freedv, freedv_callback_protorx rx, freedv_callback_prototx tx, void *callback_state);
|
|
||||||
void (*freedv_set_callback_data) (struct freedv *freedv, freedv_callback_datarx datarx, freedv_callback_datatx datatx, void *callback_state);
|
|
||||||
|
|
||||||
//
|
|
||||||
// checkAvxSupport
|
|
||||||
//
|
|
||||||
// This function was copied from freedv, file fdmdv2_main.cpp and tweaked for use here
|
|
||||||
//
|
|
||||||
// Tests the underlying platform for AVX support. 2020 needs AVX support to run
|
|
||||||
// in real-time, and old processors do not offer AVX support
|
|
||||||
//
|
|
||||||
|
|
||||||
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)
|
|
||||||
#include <cpuid.h>
|
|
||||||
static int checkAvxSupport(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
int isAvxPresent = 0;
|
|
||||||
uint32_t eax, ebx, ecx, edx;
|
|
||||||
eax = ebx = ecx = edx = 0;
|
|
||||||
__cpuid(1, eax, ebx, ecx, edx);
|
|
||||||
|
|
||||||
if (ecx & (1<<27) && ecx & (1<<28)) {
|
|
||||||
// CPU supports XSAVE and AVX
|
|
||||||
uint32_t xcr0, xcr0_high;
|
|
||||||
asm("xgetbv" : "=a" (xcr0), "=d" (xcr0_high) : "c" (0));
|
|
||||||
isAvxPresent = (xcr0 & 6) == 6; // AVX state saving enabled?
|
|
||||||
}
|
|
||||||
return isAvxPresent;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static int checkAvxSupport(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Called when a new packet can be sent */
|
|
||||||
void my_datatx(void *callback_state, unsigned char *packet, size_t *size) {
|
|
||||||
*size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GetAddrs(void)
|
|
||||||
{
|
|
||||||
handle_index = 1;
|
|
||||||
GET_HANDLE1;
|
|
||||||
if ( ! hLib) {
|
|
||||||
handle_index = 2;
|
|
||||||
GET_HANDLE2;
|
|
||||||
if ( ! hLib) {
|
|
||||||
handle_index = 4;
|
|
||||||
GET_HANDLE4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hLib) {
|
|
||||||
freedv_get_version = GET_ADDR("freedv_get_version");
|
|
||||||
if (freedv_get_version != NULL) {
|
|
||||||
freedv_version = freedv_get_version();
|
|
||||||
if (DEBUG)
|
|
||||||
printf("FreeDV codec2 library %d found, version %d\n", handle_index, freedv_version);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
freedv_version = -2;
|
|
||||||
if (DEBUG)
|
|
||||||
printf("FreeDV codec2 library %d found, but no freedv_get_version() !!!\n", handle_index);
|
|
||||||
CLOSE_LIB;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
handle_index = -1;
|
|
||||||
if (DEBUG)
|
|
||||||
printf("Could not find the FreeDV codec2 shared library\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// open, close
|
|
||||||
freedv_open = GET_ADDR("freedv_open");
|
|
||||||
freedv_open_advanced = GET_ADDR("freedv_open_advanced");
|
|
||||||
freedv_close = GET_ADDR("freedv_close");
|
|
||||||
// Transmit
|
|
||||||
freedv_tx = GET_ADDR("freedv_tx");
|
|
||||||
freedv_comptx = GET_ADDR("freedv_comptx");
|
|
||||||
// Receive
|
|
||||||
freedv_nin = GET_ADDR("freedv_nin");
|
|
||||||
freedv_rx = GET_ADDR("freedv_rx");
|
|
||||||
freedv_floatrx = GET_ADDR("freedv_floatrx");
|
|
||||||
freedv_comprx = GET_ADDR("freedv_comprx");
|
|
||||||
// Set parameters
|
|
||||||
freedv_set_callback_txt = GET_ADDR("freedv_set_callback_txt");
|
|
||||||
freedv_set_callback_protocol = GET_ADDR("freedv_set_callback_protocol");
|
|
||||||
freedv_set_callback_data = GET_ADDR("freedv_set_callback_data");
|
|
||||||
freedv_set_test_frames = GET_ADDR("freedv_set_test_frames");
|
|
||||||
freedv_set_smooth_symbols = GET_ADDR("freedv_set_smooth_symbols");
|
|
||||||
freedv_set_squelch_en = GET_ADDR("freedv_set_squelch_en");
|
|
||||||
freedv_set_snr_squelch_thresh = GET_ADDR("freedv_set_snr_squelch_thresh");
|
|
||||||
freedv_set_tx_bpf = GET_ADDR("freedv_set_tx_bpf");
|
|
||||||
// Get parameters
|
|
||||||
freedv_get_modem_stats = GET_ADDR("freedv_get_modem_stats");
|
|
||||||
freedv_get_test_frames = GET_ADDR("freedv_get_test_frames");
|
|
||||||
freedv_get_n_speech_samples = GET_ADDR("freedv_get_n_speech_samples");
|
|
||||||
freedv_get_n_max_modem_samples = GET_ADDR("freedv_get_n_max_modem_samples");
|
|
||||||
freedv_get_n_nom_modem_samples = GET_ADDR("freedv_get_n_nom_modem_samples");
|
|
||||||
freedv_get_total_bits = GET_ADDR("freedv_get_total_bits");
|
|
||||||
freedv_get_total_bit_errors = GET_ADDR("freedv_get_total_bit_errors");
|
|
||||||
freedv_get_sync = GET_ADDR("freedv_get_sync"); // requires version 11
|
|
||||||
if( freedv_version >= 11)
|
|
||||||
freedv_get_modem_sample_rate = GET_ADDR("freedv_get_modem_sample_rate"); // requires version 11 or later
|
|
||||||
if (freedv_version >= 12)
|
|
||||||
freedv_get_speech_sample_rate = GET_ADDR("freedv_get_speech_sample_rate"); // requires version 12
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int quisk_freedv_rx(short * sSamples, double * dsamples, int count, int bank) // Called from the sound thread.
|
|
||||||
{ // Input digital modulation is sSamples; decoded voice is dsamples. Each "bank" is a stream of audio.
|
|
||||||
// caller will have decimated input audio down to n_modem_sample_rate and reduced block size to match reduction
|
|
||||||
// output data will be returned at n_speech_sample_rate with corresponding block sizes
|
|
||||||
int i, nout, need, have, sync;
|
|
||||||
int n_speech_samples;
|
|
||||||
struct freedv * hF;
|
|
||||||
struct _rx_channel * pCh;
|
|
||||||
int nCountOut = 1; // In case we have a higher (or lower) value for o/p count
|
|
||||||
|
|
||||||
if (sSamples == NULL) { // shutdown
|
|
||||||
for (i = 0; i < MAX_RECEIVERS; i++) {
|
|
||||||
if (rx_channel[i].demod_in) {
|
|
||||||
free(rx_channel[i].demod_in);
|
|
||||||
rx_channel[i].demod_in = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bank < 0 || bank >= MAX_RECEIVERS)
|
|
||||||
return 0;
|
|
||||||
hF = rx_channel[bank].hFreedv;
|
|
||||||
if ( ! hF)
|
|
||||||
return 0;
|
|
||||||
pCh = rx_channel + bank;
|
|
||||||
n_speech_samples = freedv_get_n_speech_samples(hF);
|
|
||||||
// if (say) input modem rate is the 2020 mode rate of 8000 sa/sec
|
|
||||||
// and output rate is the corresponding 16000 sa/sec then for an input
|
|
||||||
// block size of N, outbut 2N samples block size.
|
|
||||||
// Apply a multiplier/divisor to 'count' for the output samples count
|
|
||||||
if(n_speech_sample_rate >= n_modem_sample_rate ) {
|
|
||||||
int nRatio = n_speech_sample_rate / n_modem_sample_rate;
|
|
||||||
if( nRatio >= 1 && nRatio <= 6)
|
|
||||||
nCountOut = nRatio * count;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int nRatio = n_modem_sample_rate / n_speech_sample_rate;
|
|
||||||
if( nRatio >= 1 && nRatio <= 6)
|
|
||||||
nCountOut = count / nRatio;
|
|
||||||
}
|
|
||||||
nout = 0;
|
|
||||||
need = freedv_nin(hF);
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
pCh->demod_in[pCh->rxdata_index] = sSamples[i];
|
|
||||||
pCh->rxdata_index++;
|
|
||||||
if (pCh->rxdata_index >= need) {
|
|
||||||
if (pCh->speech_available + n_speech_samples < SPEECH_BUF_SIZE) { // check for buffer space
|
|
||||||
have = freedv_rx(hF, pCh->speech_out + pCh->speech_available, pCh->demod_in);
|
|
||||||
if (freedv_version > 10)
|
|
||||||
sync = freedv_get_sync(hF);
|
|
||||||
else
|
|
||||||
freedv_get_modem_stats(hF, &sync, NULL);
|
|
||||||
if (freedv_current_mode == 0) { // mode 1600
|
|
||||||
if (sync) // throw away speech if not in sync
|
|
||||||
pCh->speech_available += have;
|
|
||||||
}
|
|
||||||
else if (pCh->speech_available < SPEECH_BUF_SIZE * 2 / 3) {
|
|
||||||
pCh->speech_available += have; // keep speech if there is space
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (DEBUG) printf("Close to maximum in speech output buffer\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // no space in buffer
|
|
||||||
if (DEBUG) printf("Overflow in speech output buffer\n");
|
|
||||||
}
|
|
||||||
pCh->rxdata_index = 0;
|
|
||||||
need = freedv_nin(hF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( ! pCh->playing) {
|
|
||||||
if (pCh->speech_available >= 2 * n_speech_samples) {
|
|
||||||
pCh->playing = 1;
|
|
||||||
}
|
|
||||||
else { // return zero samples
|
|
||||||
for (i = 0; i < nCountOut; i++)
|
|
||||||
dsamples[i] = 0;
|
|
||||||
//if (DEBUG) printf("Rx buffer playing %d available %d\n", pCh->playing, pCh->speech_available);
|
|
||||||
return nCountOut;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (nout = 0; nout < pCh->speech_available && nout < nCountOut; nout++)
|
|
||||||
dsamples[nout] = pCh->speech_out[nout] * ((double)CLIP32 / CLIP16 * 0.99);
|
|
||||||
if (nout) {
|
|
||||||
pCh->speech_available -= nout;
|
|
||||||
memmove(pCh->speech_out, pCh->speech_out + nout, (pCh->speech_available) * sizeof(short));
|
|
||||||
}
|
|
||||||
if ( ! pCh->speech_available) {
|
|
||||||
pCh->playing = 0;
|
|
||||||
while (nout < nCountOut)
|
|
||||||
dsamples[nout++] = 0;
|
|
||||||
}
|
|
||||||
//if (DEBUG) printf("Rx buffer playing %d available %d\n", pCh->playing, pCh->speech_available);
|
|
||||||
return nout;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int quisk_freedv_tx(complex double * cSamples, double * dsamples, int count, int is_real) // Called from the sound thread.
|
|
||||||
{ // Input voice samples are dsamples; output digital modulation is cSamples.
|
|
||||||
int i, nout;
|
|
||||||
int n_speech_samples;
|
|
||||||
int n_nom_modem_samples;
|
|
||||||
static COMP * mod_out = NULL;
|
|
||||||
static short * speech_in = NULL;
|
|
||||||
static int speech_index=0, mod_index=0;
|
|
||||||
// g8kbb - add for 800XA
|
|
||||||
static short *real_mod_out = NULL;
|
|
||||||
|
|
||||||
if (dsamples == NULL) { // shutdown
|
|
||||||
return 0; // nothing to do
|
|
||||||
}
|
|
||||||
if ( ! rx_channel[0].hFreedv)
|
|
||||||
return 0;
|
|
||||||
n_speech_samples = freedv_get_n_speech_samples(rx_channel[0].hFreedv);
|
|
||||||
n_nom_modem_samples = freedv_get_n_nom_modem_samples(rx_channel[0].hFreedv);
|
|
||||||
// find if need to output at faster rate
|
|
||||||
int nRatio = n_modem_sample_rate / n_speech_sample_rate;
|
|
||||||
if( nRatio < 1 || nRatio > 6 )
|
|
||||||
nRatio = 1;
|
|
||||||
if (mod_out == NULL) { // initialize
|
|
||||||
mod_out = (COMP *)malloc(sizeof(COMP) * MAX_MODEM_SAMPLES);
|
|
||||||
memset(mod_out, 0, sizeof(COMP) * MAX_MODEM_SAMPLES);
|
|
||||||
speech_in = (short*)malloc(sizeof(short) * MAX_SPEECH_SAMPLES);
|
|
||||||
speech_index=0;
|
|
||||||
mod_index=0;
|
|
||||||
real_mod_out = (short *)malloc(sizeof(short) * MAX_MODEM_SAMPLES);
|
|
||||||
memset(real_mod_out, 0, sizeof(short) * MAX_MODEM_SAMPLES);
|
|
||||||
//if( DEBUG ) printf("Initialise tx buffer size = %d, i/p sample size = %d\n", n_nom_modem_samples, n_speech_samples );
|
|
||||||
}
|
|
||||||
nout = 0;
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
speech_in[speech_index++] = (short)dsamples[i];
|
|
||||||
if (speech_index >= n_speech_samples) {
|
|
||||||
// Calculate a new block, but first write out the rest of the old block
|
|
||||||
// 800xa uses real not complex
|
|
||||||
if( is_real ) {
|
|
||||||
for ( ; mod_index < n_nom_modem_samples; mod_index++)
|
|
||||||
cSamples[nout++] = real_mod_out[mod_index];
|
|
||||||
freedv_tx(rx_channel[0].hFreedv, real_mod_out, speech_in);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for ( ; mod_index < n_nom_modem_samples; mod_index++)
|
|
||||||
cSamples[nout++] = mod_out[mod_index].real + I * mod_out[mod_index].imag;
|
|
||||||
freedv_comptx(rx_channel[0].hFreedv, mod_out, speech_in);
|
|
||||||
}
|
|
||||||
//if( DEBUG ) printf("Block processed, speech index=%d, n_speech_samples=%d, count=%d\n", speech_index, n_speech_samples, count );
|
|
||||||
mod_index = 0;
|
|
||||||
speech_index = 0;
|
|
||||||
}
|
|
||||||
else { // write out samples
|
|
||||||
for( int j=0; j<nRatio; j++) {
|
|
||||||
if (mod_index < n_nom_modem_samples ) {
|
|
||||||
if( is_real ) {
|
|
||||||
cSamples[nout++] = real_mod_out[mod_index];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cSamples[nout++] = mod_out[mod_index].real + I * mod_out[mod_index].imag;
|
|
||||||
}
|
|
||||||
mod_index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rxMode == FDV_L)
|
|
||||||
for (i = 0; i < nout; i++)
|
|
||||||
cSamples[i] = conj(cSamples[i]);
|
|
||||||
return nout;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TX_MSG_SIZE 80
|
|
||||||
static char quisk_tx_msg[TX_MSG_SIZE];
|
|
||||||
|
|
||||||
static char get_next_tx_char(void * callback_state)
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
static int index = 0;
|
|
||||||
|
|
||||||
c = quisk_tx_msg[index++];
|
|
||||||
if (index >= TX_MSG_SIZE)
|
|
||||||
index = 0;
|
|
||||||
if ( ! c) {
|
|
||||||
index = 0;
|
|
||||||
c = quisk_tx_msg[index++];
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RX_MSG_SIZE 80
|
|
||||||
static char quisk_rx_msg[RX_MSG_SIZE + 1];
|
|
||||||
|
|
||||||
static void put_next_rx_char(void * callback_state, char ch)
|
|
||||||
{
|
|
||||||
if (ch == '\n' || ch == '\r')
|
|
||||||
ch = ' ';
|
|
||||||
if (ch < 32 || ch > 126) // printable characters
|
|
||||||
return;
|
|
||||||
if (strlen(quisk_rx_msg) < RX_MSG_SIZE)
|
|
||||||
strncat(quisk_rx_msg, &ch, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject * quisk_freedv_get_rx_char(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
||||||
{
|
|
||||||
PyObject * txt;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple (args, ""))
|
|
||||||
return NULL;
|
|
||||||
txt = PyString_FromString(quisk_rx_msg);
|
|
||||||
quisk_rx_msg[0] = 0;
|
|
||||||
return txt;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CloseFreedv(void) // Called from the GUI thread or sound thread
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_RECEIVERS; i++) {
|
|
||||||
if (rx_channel[i].hFreedv) {
|
|
||||||
freedv_close(rx_channel[i].hFreedv);
|
|
||||||
rx_channel[i].hFreedv = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
quisk_freedv_rx(NULL, NULL, 0, 0);
|
|
||||||
quisk_freedv_tx(NULL, NULL, 0, 0);
|
|
||||||
freedv_current_mode = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int OpenFreedv(void) // Called from the GUI thread or sound thread
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct freedv * hF;
|
|
||||||
|
|
||||||
if ( ! hLib)
|
|
||||||
GetAddrs(); // Get the entry points for funtions in the codec2 library
|
|
||||||
if (DEBUG) {
|
|
||||||
switch (handle_index) {
|
|
||||||
case 1:
|
|
||||||
printf("freedv_open: system codec2 library found, version %d\n", freedv_version);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
printf("freedv_open: codec2 library freedvpkg/libcodec2.dll|so found, version %d\n", freedv_version);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
printf("freedv_open: codec2 library freedvpkg/libcodec2_64 found, version %d\n", freedv_version);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("freedv_open: Could not find the FreeDV codec2 shared library\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (freedv_version < 10) {
|
|
||||||
CloseFreedv();
|
|
||||||
requested_mode = -1;
|
|
||||||
if (DEBUG)
|
|
||||||
printf("freedv_open: Failure because version is less than 10\n");
|
|
||||||
return 0; // failure
|
|
||||||
}
|
|
||||||
if (requested_mode == FREEDV_MODE_2020) {
|
|
||||||
if (checkAvxSupport() == 0) {
|
|
||||||
CloseFreedv();
|
|
||||||
requested_mode = -1;
|
|
||||||
if (DEBUG)
|
|
||||||
printf("freedv_open: Failure because mode 2020 requires Avx support\n");
|
|
||||||
return 0; // failure
|
|
||||||
}
|
|
||||||
if (handle_index >= 3) { // Quisk provided codec2
|
|
||||||
CloseFreedv();
|
|
||||||
requested_mode = -1;
|
|
||||||
if (DEBUG)
|
|
||||||
printf("freedv_open: Failure because mode 2020 requires a system installation of codec2\n");
|
|
||||||
return 0; // failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((requested_mode == FREEDV_MODE_700D || requested_mode == FREEDV_MODE_700E) && freedv_open_advanced) {
|
|
||||||
struct freedv_advanced adv;
|
|
||||||
memset(&adv, 0, sizeof(adv));
|
|
||||||
adv.interleave_frames = interleave_frames;
|
|
||||||
hF = freedv_open_advanced(requested_mode, &adv);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hF = freedv_open(requested_mode);
|
|
||||||
}
|
|
||||||
if (hF == NULL) {
|
|
||||||
CloseFreedv();
|
|
||||||
requested_mode = -1;
|
|
||||||
return 0; // failure
|
|
||||||
}
|
|
||||||
rx_channel[0].hFreedv = hF;
|
|
||||||
quisk_dvoice_freedv(&quisk_freedv_rx, &quisk_freedv_tx);
|
|
||||||
if (quisk_tx_msg[0])
|
|
||||||
freedv_set_callback_txt(hF, &put_next_rx_char, &get_next_tx_char, NULL);
|
|
||||||
else
|
|
||||||
freedv_set_callback_txt(hF, &put_next_rx_char, NULL, NULL);
|
|
||||||
if (freedv_set_callback_protocol)
|
|
||||||
freedv_set_callback_protocol(hF, NULL, NULL, NULL);
|
|
||||||
if (freedv_set_callback_data)
|
|
||||||
freedv_set_callback_data(hF, NULL, my_datatx, NULL);
|
|
||||||
freedv_set_squelch_en(hF, quisk_freedv_squelch);
|
|
||||||
if (freedv_set_tx_bpf)
|
|
||||||
freedv_set_tx_bpf(hF, quisk_set_tx_bpf);
|
|
||||||
n_max_modem_samples = freedv_get_n_max_modem_samples(hF);
|
|
||||||
n_speech_sample_rate = 8000;
|
|
||||||
if (freedv_version >= 12 && freedv_get_speech_sample_rate)
|
|
||||||
n_speech_sample_rate = freedv_get_speech_sample_rate(hF);
|
|
||||||
n_modem_sample_rate = 8000;
|
|
||||||
if (freedv_version >= 11 && freedv_get_modem_sample_rate)
|
|
||||||
n_modem_sample_rate = freedv_get_modem_sample_rate(hF);
|
|
||||||
for (i = 0; i < MAX_RECEIVERS; i++) {
|
|
||||||
rx_channel[i].rxdata_index = 0;
|
|
||||||
rx_channel[i].speech_available = 0;
|
|
||||||
rx_channel[i].playing = 0;
|
|
||||||
if (rx_channel[i].demod_in)
|
|
||||||
free(rx_channel[i].demod_in);
|
|
||||||
rx_channel[i].demod_in = (short *)malloc(sizeof(short) * n_max_modem_samples);
|
|
||||||
if( rx_channel[i].demod_in == NULL) {
|
|
||||||
CloseFreedv();
|
|
||||||
requested_mode = -1;
|
|
||||||
return 0; // failure
|
|
||||||
}
|
|
||||||
if (i > 0) {
|
|
||||||
rx_channel[i].hFreedv = freedv_open(requested_mode);
|
|
||||||
if (rx_channel[i].hFreedv)
|
|
||||||
freedv_set_squelch_en(rx_channel[i].hFreedv, quisk_freedv_squelch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (DEBUG) printf("n_nom_modem_samples %d\n", freedv_get_n_nom_modem_samples(rx_channel[0].hFreedv));
|
|
||||||
if (DEBUG) printf("n_speech_samples %d\n", freedv_get_n_speech_samples(rx_channel[0].hFreedv));
|
|
||||||
if (DEBUG) printf("n_max_modem_samples %d\n", n_max_modem_samples);
|
|
||||||
if (DEBUG) {
|
|
||||||
if (freedv_version >= 11) printf("modem_sample_rate %d\n", n_modem_sample_rate);
|
|
||||||
if (freedv_version >= 12) printf("speech_sample_rate %d\n", n_speech_sample_rate);
|
|
||||||
}
|
|
||||||
freedv_current_mode = requested_mode;
|
|
||||||
return 1; // success
|
|
||||||
}
|
|
||||||
|
|
||||||
void quisk_check_freedv_mode(void)
|
|
||||||
{ // see if we need to change the mode
|
|
||||||
if (requested_mode == freedv_current_mode)
|
|
||||||
return;
|
|
||||||
if (DEBUG) printf("Change in mode to %d\n", requested_mode);
|
|
||||||
CloseFreedv();
|
|
||||||
if (requested_mode >= 0)
|
|
||||||
OpenFreedv();
|
|
||||||
else
|
|
||||||
requested_mode = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject * quisk_freedv_open(PyObject * self, PyObject * args) // Called from the GUI thread before freedv is open
|
|
||||||
{
|
|
||||||
if (!PyArg_ParseTuple (args, ""))
|
|
||||||
return NULL;
|
|
||||||
return PyInt_FromLong(OpenFreedv());
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject * quisk_freedv_close(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
||||||
{
|
|
||||||
if (!PyArg_ParseTuple (args, ""))
|
|
||||||
return NULL;
|
|
||||||
requested_mode = -1; // request close
|
|
||||||
Py_INCREF (Py_None);
|
|
||||||
return Py_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject * quisk_freedv_set_options(PyObject * self, PyObject * args, PyObject * keywds) // Called from the GUI thread.
|
|
||||||
{ // Call with keyword arguments ONLY to change parameters. Call before quisk_freedv_open() to set an initial mode.
|
|
||||||
int mode=-1; // Call again to change the mode.
|
|
||||||
int bpf=-1;
|
|
||||||
char * ptMsg=NULL;
|
|
||||||
static char * kwlist[] = {"mode", "tx_msg", "DEBUG", "squelch", "interleave_frames", "set_tx_bpf", NULL} ;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|isiiii", kwlist, &mode, &ptMsg, &DEBUG, &quisk_freedv_squelch, &interleave_frames, &bpf))
|
|
||||||
return NULL;
|
|
||||||
if (ptMsg)
|
|
||||||
strMcpy(quisk_tx_msg, ptMsg, TX_MSG_SIZE);
|
|
||||||
if (bpf != -1) {
|
|
||||||
quisk_set_tx_bpf = bpf;
|
|
||||||
if (freedv_set_tx_bpf && rx_channel[0].hFreedv)
|
|
||||||
freedv_set_tx_bpf(rx_channel[0].hFreedv, quisk_set_tx_bpf);
|
|
||||||
}
|
|
||||||
if (mode == -1) { // mode was not entered
|
|
||||||
;
|
|
||||||
}
|
|
||||||
else if (mode == FREEDV_MODE_700E && freedv_version >= 14) {
|
|
||||||
requested_mode = mode;
|
|
||||||
}
|
|
||||||
else if (mode == FREEDV_MODE_2020) {
|
|
||||||
if (checkAvxSupport() && handle_index <= 2 && freedv_version >= 13)
|
|
||||||
requested_mode = mode;
|
|
||||||
}
|
|
||||||
else if (freedv_version == 10) {
|
|
||||||
if (mode == 0)
|
|
||||||
requested_mode = mode;
|
|
||||||
}
|
|
||||||
else if (freedv_version == 11) {
|
|
||||||
if (mode <= 2)
|
|
||||||
requested_mode = mode;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
requested_mode = mode;
|
|
||||||
}
|
|
||||||
return PyInt_FromLong(requested_mode); // Return the mode
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject * quisk_freedv_get_snr(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
||||||
{
|
|
||||||
float snr_est = 0.0;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple (args, ""))
|
|
||||||
return NULL;
|
|
||||||
if (rx_channel[0].hFreedv)
|
|
||||||
freedv_get_modem_stats(rx_channel[0].hFreedv, NULL, &snr_est);
|
|
||||||
return PyFloat_FromDouble(snr_est);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject * quisk_freedv_get_version(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
||||||
{
|
|
||||||
if (!PyArg_ParseTuple (args, ""))
|
|
||||||
return NULL;
|
|
||||||
if ( ! hLib)
|
|
||||||
GetAddrs(); // Get the entry points for funtions in the codec2 library
|
|
||||||
return PyInt_FromLong(freedv_version);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject * quisk_freedv_set_squelch_en(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
||||||
{
|
|
||||||
int i,state;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple (args, "i", &state))
|
|
||||||
return NULL;
|
|
||||||
quisk_freedv_squelch = state;
|
|
||||||
for (i = 0; i < MAX_RECEIVERS; i++) {
|
|
||||||
if( rx_channel[i].hFreedv )
|
|
||||||
freedv_set_squelch_en(rx_channel[i].hFreedv, state);
|
|
||||||
}
|
|
||||||
return PyInt_FromLong(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1 @@
|
|||||||
extern int freedv_current_mode; // the current FreeDV running mode
|
// This header file is no longer used.
|
||||||
|
|
||||||
// available speech modes
|
|
||||||
#define FREEDV_MODE_1600 0
|
|
||||||
#define FREEDV_MODE_2400A 3
|
|
||||||
#define FREEDV_MODE_2400B 4
|
|
||||||
#define FREEDV_MODE_800XA 5
|
|
||||||
#define FREEDV_MODE_700C 6
|
|
||||||
#define FREEDV_MODE_700D 7
|
|
||||||
#define FREEDV_MODE_700E 13
|
|
||||||
#define FREEDV_MODE_2020 8
|
|
||||||
#define FREEDV_MODE_2020B 16
|
|
||||||
|
|
||||||
// available data modes
|
|
||||||
#define FREEDV_MODE_FSK_LDPC 9
|
|
||||||
#define FREEDV_MODE_DATAC1 10
|
|
||||||
#define FREEDV_MODE_DATAC3 12
|
|
||||||
#define FREEDV_MODE_DATAC0 14
|
|
||||||
#define FREEDV_MODE_DATAC4 18
|
|
||||||
#define FREEDV_MODE_DATAC13 19
|
|
||||||
|
|
||||||
// Sample rates used
|
|
||||||
#define FREEDV_FS_8000 8000
|
|
||||||
#define FREEDV_FS_16000 16000
|
|
||||||
|
|||||||
+1
-129
@@ -1,129 +1 @@
|
|||||||
|
This directory is no longer used by FreeDV. Please see the documentation.
|
||||||
Note: The directory freedvpkg no longer contains source files. It only contains copies of
|
|
||||||
the codec2 libraries for use by Quisk. If any other files are present, delete them.
|
|
||||||
|
|
||||||
FreeDV and Directory freedvpkg
|
|
||||||
==============================
|
|
||||||
|
|
||||||
FreeDV is the combination of the codec2 codec and the fdmdv modem. It provides digital voice
|
|
||||||
suitable for HF and VHF transmission. Quisk has native (built-in) support for
|
|
||||||
FreeDV. Just push the FDV mode and talk. This freedvpkg directory contains copies of the
|
|
||||||
codec2 libraries for use by Quisk.
|
|
||||||
|
|
||||||
You can also use the separate FreeDV program available at freedv.org, and the Quisk DGT-U mode
|
|
||||||
which attaches to external digital programs. The setup is identical to fldigi and other external
|
|
||||||
digital programs.
|
|
||||||
|
|
||||||
Quisk will add the FDV button if specified on the Config/radio/Controls tab.
|
|
||||||
If there is a problem with the freedv module, the button will be grayed out.
|
|
||||||
|
|
||||||
The freedv module requires the codec2 library. This library is included for Windows and for
|
|
||||||
Ubuntu 18.04 LTS 32-bit and 64-bit. For other systems (such as ARM) you will need to build another
|
|
||||||
codec2. Just try the FDV mode and see if it works. It should always work on Windows, and may work
|
|
||||||
on Linux. If the FDV button is grayed out, you need a different codec2 than the one included. Make
|
|
||||||
a new codec2 library, and copy it to freedvpkg or to the correct system directory.
|
|
||||||
|
|
||||||
2020 mode
|
|
||||||
=========
|
|
||||||
You can now use 2020 mode with Quisk. Note however that 2020 mode requires a processor with AVX.
|
|
||||||
Also note a Raspberry PI (model 4B) does not have enough processing power even if neon is used
|
|
||||||
and it is overclocked.
|
|
||||||
|
|
||||||
There are two parts to this:
|
|
||||||
1. build LPCNet
|
|
||||||
2. rebuild codec2
|
|
||||||
The details are found here: https://github.com/drowe67/codec2#freedv-2020-support-building-with-lpcnet
|
|
||||||
|
|
||||||
Both liblpcnetfreedv and libcodec2 are needed. If you are running under windows, the resultant
|
|
||||||
liblpcnetfreedv.dll file needs to be placed in the quisk folder. Libcodec2 may be placed in the
|
|
||||||
quisk freedvpkg folder. Or install the DLL's in the correct system folders.
|
|
||||||
|
|
||||||
Search Order
|
|
||||||
============
|
|
||||||
|
|
||||||
Quisk will search for a valid codec2 library in this order on Windows:
|
|
||||||
1. The system codec2 library installed outside of Quisk by another program.
|
|
||||||
2. freedvpkg/libcodec2.dll. Not included. You can copy your own codec2 here but a system-wide installation is better.
|
|
||||||
3. freedvpkg/libcodec2_32.dll. The 32-bit codec2 shipped with Quisk.
|
|
||||||
4. freedvpkg/libcodec2_64.dll. The 64-bit codec2 shipped with Quisk.
|
|
||||||
|
|
||||||
Quisk will search for a valid codec2 library in this order on Linux:
|
|
||||||
1. The system codec2 library installed outside of Quisk by another program.
|
|
||||||
2. freedvpkg/libcodec2.so. Not included. You can copy your own codec2 here but a system-wide installation is better.
|
|
||||||
3. freedvpkg/libcodec2_32.so. The 32-bit codec2 shipped with Quisk.
|
|
||||||
4. freedvpkg/libcodec2_64.so. The 64-bit codec2 shipped with Quisk.
|
|
||||||
|
|
||||||
How to Build a New codec2
|
|
||||||
=========================
|
|
||||||
|
|
||||||
The source for codec2 is available on github. Or google for other sources or perhaps a pre-built library.
|
|
||||||
If you need to compile codec2 from source, first change to a suitable directory (not the Quisk directory)
|
|
||||||
and download the source with git:
|
|
||||||
|
|
||||||
git clone https://github.com/drowe67/codec2.git
|
|
||||||
|
|
||||||
Then build codec2 using the directions found at https://github.com/drowe67/codec2/blob/master/INSTALL.
|
|
||||||
The directions given below are current as of June 2020, but check for changes.
|
|
||||||
Then copy the codec2 library to the freedvpkg directory under Quisk.
|
|
||||||
|
|
||||||
Build a New codec2 on Linux
|
|
||||||
===========================
|
|
||||||
Create the codec2 shared library. This assumes a 64-bit linux. Change the directory
|
|
||||||
name from build_linux64 to build_linux32 for 32-bit linux. Note the "../".
|
|
||||||
|
|
||||||
cd codec2
|
|
||||||
mkdir build_linux64
|
|
||||||
cd build_linux64
|
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release ../
|
|
||||||
make
|
|
||||||
cd src
|
|
||||||
cp libcodec2.so my-quisk-directory/freedvpkg
|
|
||||||
|
|
||||||
Build a New 32-bit codec2 on 64-bit Linux
|
|
||||||
=========================================
|
|
||||||
Make sure package libc6-dev-i386 is installed.
|
|
||||||
Create the codec2 shared library. Note the "../".
|
|
||||||
|
|
||||||
cd codec2
|
|
||||||
mkdir build_linux32
|
|
||||||
cd build_linux32
|
|
||||||
export CFLAGS=-m32
|
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release ../
|
|
||||||
make
|
|
||||||
cd src
|
|
||||||
cp libcodec2.so my-quisk-directory/freedvpkg
|
|
||||||
|
|
||||||
Build a New codec2 on Windows
|
|
||||||
=============================
|
|
||||||
For Windows you need to install MinGW-w64, MSYS2, and g++. Use the mingw shell. The Speex libraries
|
|
||||||
are not needed by codec2, but are required for the Unit Test modules. To build the Unit Test modules,
|
|
||||||
you need to install Speex and add -DSPEEXDSP_INCLUDE_DIR=../speex/include/speex -DSPEEXDSP_LIBRARY=../speex/bin.
|
|
||||||
|
|
||||||
# Use mingw32.exe 32-bit shell:
|
|
||||||
cd codec2
|
|
||||||
mkdir build_win32
|
|
||||||
cd build_win32
|
|
||||||
cmake -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -DUNITTEST=OFF ../
|
|
||||||
make codec2
|
|
||||||
cd src
|
|
||||||
cp libcodec2.dll my-quisk-directory/freedvpkg
|
|
||||||
|
|
||||||
# Use mingw64.exe 64-bit shell:
|
|
||||||
cd codec2
|
|
||||||
mkdir build_win64
|
|
||||||
cd build_win64
|
|
||||||
cmake -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -DUNITTEST=OFF -DCMAKE_SYSTEM_PROCESSOR=x86_64 ../
|
|
||||||
make codec2
|
|
||||||
cd src
|
|
||||||
cp libcodec2.dll my-quisk-directory/freedvpkg
|
|
||||||
|
|
||||||
Testing
|
|
||||||
=======
|
|
||||||
You can just start Quisk and see if the FDV button is not grayed out, and FDV works. Or you can
|
|
||||||
test the import of freedv and look for error messages.
|
|
||||||
|
|
||||||
cd my-quisk-directory
|
|
||||||
python # Use the correct Python 2 or 3 in 32 or 64 bit
|
|
||||||
import _quisk
|
|
||||||
_quisk.freedv_get_version() # This should return 13 or higher for a recent codec2
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
+144
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2024 Davidson Francis <davidsondfgl@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include "base64.h"
|
||||||
|
#include "sha1.h"
|
||||||
|
#include "ws.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dir src/
|
||||||
|
* @brief Handshake routines directory
|
||||||
|
*
|
||||||
|
* @file handshake.c
|
||||||
|
* @brief Handshake routines.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the field Sec-WebSocket-Accept on response, by
|
||||||
|
* an previously informed key.
|
||||||
|
*
|
||||||
|
* @param wsKey Sec-WebSocket-Key
|
||||||
|
* @param dest source to be stored the value.
|
||||||
|
*
|
||||||
|
* @return Returns 0 if success and a negative number
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* @attention This is part of the internal API and is documented just
|
||||||
|
* for completeness.
|
||||||
|
*/
|
||||||
|
int get_handshake_accept(char *wsKey, unsigned char **dest)
|
||||||
|
{
|
||||||
|
unsigned char hash[SHA1HashSize]; /* SHA-1 Hash. */
|
||||||
|
SHA1Context ctx; /* SHA-1 Context. */
|
||||||
|
char *str; /* WebSocket key + magic string. */
|
||||||
|
|
||||||
|
/* Invalid key. */
|
||||||
|
if (!wsKey)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
str = calloc(1, sizeof(char) * (WS_KEY_LEN + WS_MS_LEN + 1));
|
||||||
|
if (!str)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
strncpy(str, wsKey, WS_KEY_LEN);
|
||||||
|
strcat(str, MAGIC_STRING);
|
||||||
|
|
||||||
|
SHA1Reset(&ctx);
|
||||||
|
SHA1Input(&ctx, (const uint8_t *)str, WS_KEYMS_LEN);
|
||||||
|
SHA1Result(&ctx, hash);
|
||||||
|
|
||||||
|
*dest = base64_encode(hash, SHA1HashSize, NULL);
|
||||||
|
*(*dest + strlen((const char *)*dest) - 1) = '\0';
|
||||||
|
free(str);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finds the ocorrence of @p needle in @p haystack, case
|
||||||
|
* insensitive.
|
||||||
|
*
|
||||||
|
* @param haystack Target string to be searched.
|
||||||
|
* @param needle Substring to search for.
|
||||||
|
*
|
||||||
|
* @returns If found, returns a pointer at the beginning of the
|
||||||
|
* found substring. Otherwise, returns NULL.
|
||||||
|
*/
|
||||||
|
static const char *strstricase(const char *haystack, const char *needle)
|
||||||
|
{
|
||||||
|
size_t length;
|
||||||
|
for (length = strlen(needle); *haystack; haystack++)
|
||||||
|
if (!strncasecmp(haystack, needle, length))
|
||||||
|
return haystack;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the complete response to accomplish a succesfully
|
||||||
|
* handshake.
|
||||||
|
*
|
||||||
|
* @param hsrequest Client request.
|
||||||
|
* @param hsresponse Server response.
|
||||||
|
*
|
||||||
|
* @return Returns 0 if success and a negative number
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* @attention This is part of the internal API and is documented just
|
||||||
|
* for completeness.
|
||||||
|
*/
|
||||||
|
int get_handshake_response(char *hsrequest, char **hsresponse)
|
||||||
|
{
|
||||||
|
unsigned char *accept; /* Accept message. */
|
||||||
|
char *saveptr; /* strtok_r() pointer. */
|
||||||
|
char *s; /* Current string. */
|
||||||
|
int ret; /* Return value. */
|
||||||
|
|
||||||
|
saveptr = NULL;
|
||||||
|
for (s = strtok_r(hsrequest, "\r\n", &saveptr); s != NULL;
|
||||||
|
s = strtok_r(NULL, "\r\n", &saveptr))
|
||||||
|
{
|
||||||
|
if (strstricase(s, WS_HS_REQ) != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure that we have a valid pointer. */
|
||||||
|
if (s == NULL)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
saveptr = NULL;
|
||||||
|
s = strtok_r(s, " ", &saveptr);
|
||||||
|
s = strtok_r(NULL, " ", &saveptr);
|
||||||
|
|
||||||
|
ret = get_handshake_accept(s, &accept);
|
||||||
|
if (ret < 0)
|
||||||
|
return (ret);
|
||||||
|
|
||||||
|
*hsresponse = malloc(sizeof(char) * WS_HS_ACCLEN);
|
||||||
|
if (*hsresponse == NULL)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
strcpy(*hsresponse, WS_HS_ACCEPT);
|
||||||
|
strcat(*hsresponse, (const char *)accept);
|
||||||
|
strcat(*hsresponse, "\r\n\r\n");
|
||||||
|
|
||||||
|
free(accept);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import wx, math
|
||||||
|
|
||||||
|
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.num_rows_added = 1
|
||||||
|
self.btnPreamp = app.QuiskCheckbutton(frame, self.OnBtnPreamp, text='Preamp')
|
||||||
|
gbs.Add(self.btnPreamp, (self.start_row, self.start_col), flag=wx.EXPAND)
|
||||||
|
bw, bh = self.btnPreamp.GetMinSize()
|
||||||
|
init = app.hermes_atten_dB
|
||||||
|
self.sliderAtten = app.SliderBoxHH(frame, 'Atten %d dB', init, 0, 31, self.OnAtten, True)
|
||||||
|
self.sliderAtten.idName = "RfAtten"
|
||||||
|
app.midiControls["RfAtten"] = (self.sliderAtten, self.OnAtten)
|
||||||
|
hardware.ChangeAtten(init)
|
||||||
|
gbs.Add(self.sliderAtten, (self.start_row, self.start_col + 2), (1, 6), flag=wx.EXPAND)
|
||||||
|
if conf.button_layout == "Small screen":
|
||||||
|
# Display four data items in a single window
|
||||||
|
self.text_temperature = app.QuiskText1(frame, '', bh)
|
||||||
|
self.text_pa_current = app.QuiskText1(frame, '', bh)
|
||||||
|
self.text_fwd_power = app.QuiskText1(frame, '', bh)
|
||||||
|
self.text_swr = app.QuiskText1(frame, '', bh)
|
||||||
|
self.text_data = self.text_temperature
|
||||||
|
self.text_pa_current.Hide()
|
||||||
|
self.text_fwd_power.Hide()
|
||||||
|
self.text_swr.Hide()
|
||||||
|
b = app.QuiskPushbutton(frame, self.OnTextDataMenu, '..')
|
||||||
|
szr = self.data_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
szr.Add(self.text_data, 1, flag=wx.ALIGN_CENTER_VERTICAL)
|
||||||
|
szr.Add(b, 0, flag=wx.ALIGN_CENTER_VERTICAL)
|
||||||
|
gbs.Add(szr, (self.start_row, self.start_col + 10), (1, 2), flag=wx.EXPAND)
|
||||||
|
# Make a popup menu for the data window
|
||||||
|
self.text_data_menu = wx.Menu()
|
||||||
|
item = self.text_data_menu.Append(-1, 'Temperature')
|
||||||
|
app.Bind(wx.EVT_MENU, self.OnDataTemperature, item)
|
||||||
|
item = self.text_data_menu.Append(-1, 'PA Current')
|
||||||
|
app.Bind(wx.EVT_MENU, self.OnDataPaCurrent, item)
|
||||||
|
item = self.text_data_menu.Append(-1, 'Fwd Power')
|
||||||
|
app.Bind(wx.EVT_MENU, self.OnDataFwdPower, item)
|
||||||
|
item = self.text_data_menu.Append(-1, 'SWR')
|
||||||
|
app.Bind(wx.EVT_MENU, self.OnDataSwr, item)
|
||||||
|
else:
|
||||||
|
szr = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
gbs.Add(szr, (self.start_row, self.start_col + 10), (1, 18), flag=wx.EXPAND)
|
||||||
|
text_temperature = wx.StaticText(frame, -1, ' Temp 100DC XX', style=wx.ST_NO_AUTORESIZE)
|
||||||
|
size = text_temperature.GetBestSize()
|
||||||
|
text_temperature.Destroy()
|
||||||
|
self.text_temperature = wx.StaticText(frame, -1, '', size=size, style=wx.ST_NO_AUTORESIZE)
|
||||||
|
self.text_pa_current = wx.StaticText(frame, -1, '', size=size, style=wx.ST_NO_AUTORESIZE)
|
||||||
|
self.text_fwd_power = wx.StaticText(frame, -1, '', size=size, style=wx.ST_NO_AUTORESIZE)
|
||||||
|
self.text_swr = wx.StaticText(frame, -1, '', size=size, style=wx.ST_NO_AUTORESIZE)
|
||||||
|
flag=wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL
|
||||||
|
szr.Add(self.text_temperature, 0, flag=flag)
|
||||||
|
szr.Add(self.text_pa_current, 0, flag=flag)
|
||||||
|
szr.Add(self.text_fwd_power, 0, flag=flag)
|
||||||
|
szr.Add(self.text_swr, 0, flag=flag)
|
||||||
|
def OnBtnPreamp(self, event):
|
||||||
|
self.hardware.ChangePreamp(self.btnPreamp.GetValue())
|
||||||
|
def OnAtten(self, event):
|
||||||
|
value = self.sliderAtten.GetValue()
|
||||||
|
self.hardware.ChangeAtten(value)
|
||||||
|
self.application.hermes_atten_dB = value
|
||||||
|
def Code2FwdRevWatts(self, fwd, rev): # Convert the fwd/rev power code to watts forward and reverse
|
||||||
|
#print (self.hardware.hermes_rev_power, self.hardware.hermes_fwd_power)
|
||||||
|
fwd = self.hardware.InterpolatePower(fwd)
|
||||||
|
rev = self.hardware.InterpolatePower(rev)
|
||||||
|
# Which voltage is forward and reverse depends on the polarity of the current sense transformer
|
||||||
|
if fwd >= rev:
|
||||||
|
return fwd, rev
|
||||||
|
else:
|
||||||
|
return rev, fwd
|
||||||
|
def UpdateText(self): # Called at intervals
|
||||||
|
conf = self.config
|
||||||
|
# Temperature
|
||||||
|
temp = self.hardware.hermes_temperature
|
||||||
|
if conf.calibrate_temp_20 == conf.calibrate_temp_40:
|
||||||
|
temp = " Temp ADC %4d" % temp
|
||||||
|
else:
|
||||||
|
m = 20.0 / (conf.calibrate_temp_40 - conf.calibrate_temp_20)
|
||||||
|
b = 20.0 - m * conf.calibrate_temp_20
|
||||||
|
temp = m * temp + b
|
||||||
|
temp = (" Temp %3.0f" % temp) + u'\u2103'
|
||||||
|
self.text_temperature.SetLabel(temp)
|
||||||
|
# power amp current
|
||||||
|
current = self.hardware.hermes_pa_current
|
||||||
|
if conf.calibrate_current_0 == conf.calibrate_current_1:
|
||||||
|
current = " Amps ADC %4d" % current
|
||||||
|
else:
|
||||||
|
m = 1.0 / (conf.calibrate_current_1 - conf.calibrate_current_0)
|
||||||
|
b = - m * conf.calibrate_current_0
|
||||||
|
current = m * current + b
|
||||||
|
current = " PA %4.0f ma" % (1000*current)
|
||||||
|
self.text_pa_current.SetLabel(current)
|
||||||
|
# forward and reverse peak power
|
||||||
|
if not self.hardware.power_interpolator: # For no interpolator, show ADC
|
||||||
|
fwd = self.hardware.hermes_fwd_power
|
||||||
|
rev = self.hardware.hermes_rev_power
|
||||||
|
self.text_fwd_power.SetLabel("Fwd ADC %4d" % fwd)
|
||||||
|
self.text_swr.SetLabel("Rev ADC %4d" % rev)
|
||||||
|
return
|
||||||
|
fwd, rev = self.Code2FwdRevWatts(self.hardware.hermes_fwd_peak, self.hardware.hermes_rev_peak)
|
||||||
|
# forward less reverse power
|
||||||
|
power = fwd - rev
|
||||||
|
if power < 0.0:
|
||||||
|
power = 0.0
|
||||||
|
text = " PEP %3.1f watts" % power
|
||||||
|
self.text_fwd_power.SetLabel(text)
|
||||||
|
# SWR based on average power
|
||||||
|
fwd, rev = self.Code2FwdRevWatts(self.hardware.hermes_fwd_power, self.hardware.hermes_rev_power)
|
||||||
|
if fwd >= 0.05:
|
||||||
|
gamma = math.sqrt(rev / fwd)
|
||||||
|
if gamma < 0.98:
|
||||||
|
swr = (1.0 + gamma) / (1.0 - gamma)
|
||||||
|
else:
|
||||||
|
swr = 99.0
|
||||||
|
if swr < 9.95:
|
||||||
|
text = " SWR %4.2f" % swr
|
||||||
|
else:
|
||||||
|
text = " SWR %4.0f" % swr
|
||||||
|
else:
|
||||||
|
text = " SWR ---"
|
||||||
|
self.text_swr.SetLabel(text)
|
||||||
|
def OnTextDataMenu(self, event):
|
||||||
|
btn = event.GetEventObject()
|
||||||
|
btn.PopupMenu(self.text_data_menu, (0,0))
|
||||||
|
def OnDataTemperature(self, event):
|
||||||
|
self.data_sizer.Replace(self.text_data, self.text_temperature)
|
||||||
|
self.text_data.Hide()
|
||||||
|
self.text_data = self.text_temperature
|
||||||
|
self.text_data.Show()
|
||||||
|
self.data_sizer.Layout()
|
||||||
|
def OnDataPaCurrent(self, event):
|
||||||
|
self.data_sizer.Replace(self.text_data, self.text_pa_current)
|
||||||
|
self.text_data.Hide()
|
||||||
|
self.text_data = self.text_pa_current
|
||||||
|
self.text_data.Show()
|
||||||
|
self.data_sizer.Layout()
|
||||||
|
def OnDataFwdPower(self, event):
|
||||||
|
self.data_sizer.Replace(self.text_data, self.text_fwd_power)
|
||||||
|
self.text_data.Hide()
|
||||||
|
self.text_data = self.text_fwd_power
|
||||||
|
self.text_data.Show()
|
||||||
|
self.data_sizer.Layout()
|
||||||
|
def OnDataSwr(self, event):
|
||||||
|
self.data_sizer.Replace(self.text_data, self.text_swr)
|
||||||
|
self.text_data.Hide()
|
||||||
|
self.text_data = self.text_swr
|
||||||
|
self.text_data.Show()
|
||||||
|
self.data_sizer.Layout()
|
||||||
+46
-16
@@ -15,6 +15,7 @@ from quisk_hardware_model import Hardware as BaseHardware
|
|||||||
|
|
||||||
DEBUG = 0
|
DEBUG = 0
|
||||||
DEBUG_I2C = 0
|
DEBUG_I2C = 0
|
||||||
|
DEBUG_SENSORS = 0
|
||||||
|
|
||||||
class IOBoard:
|
class IOBoard:
|
||||||
"This class controls the N2ADR IO Board for the HermesLite 2"
|
"This class controls the N2ADR IO Board for the HermesLite 2"
|
||||||
@@ -46,6 +47,8 @@ class IOBoard:
|
|||||||
self.old_receive = None
|
self.old_receive = None
|
||||||
self.slow = 0
|
self.slow = 0
|
||||||
def HeartBeat(self): # Called at 10 Hz for housekeeping tasks
|
def HeartBeat(self): # Called at 10 Hz for housekeeping tasks
|
||||||
|
if not self.hardware.is_HermesLite2:
|
||||||
|
return
|
||||||
if not QS.get_params('rx_udp_started'):
|
if not QS.get_params('rx_udp_started'):
|
||||||
return
|
return
|
||||||
if self.have_IO_Board is None:
|
if self.have_IO_Board is None:
|
||||||
@@ -154,6 +157,7 @@ class Hardware(BaseHardware):
|
|||||||
self.hermes_ip = ""
|
self.hermes_ip = ""
|
||||||
self.hermes_code_version = -1
|
self.hermes_code_version = -1
|
||||||
self.hermes_board_id = -1
|
self.hermes_board_id = -1
|
||||||
|
self.is_HermesLite2 = False
|
||||||
self.hermes_temperature = 0.0
|
self.hermes_temperature = 0.0
|
||||||
self.hermes_fwd_power = 0.0
|
self.hermes_fwd_power = 0.0
|
||||||
self.hermes_rev_power = 0.0
|
self.hermes_rev_power = 0.0
|
||||||
@@ -166,7 +170,10 @@ class Hardware(BaseHardware):
|
|||||||
self.vna_count = 0
|
self.vna_count = 0
|
||||||
self.vna_started = False
|
self.vna_started = False
|
||||||
self.repeater_freq = None # original repeater output frequency
|
self.repeater_freq = None # original repeater output frequency
|
||||||
self.antenna_labels = ('Ant 0', 'Ant 1') # labels for the antenna button
|
if conf.hardware_file_type == "Red Pitaya":
|
||||||
|
self.antenna_labels = ('Ant 0', 'Ant 1') # labels for the antenna button
|
||||||
|
else:
|
||||||
|
self.antenna_labels = ('Ant 0', 'Ant 1', 'Ant 2', 'Ant 3')
|
||||||
self.antenna_index = 0 # index of antenna in use
|
self.antenna_index = 0 # index of antenna in use
|
||||||
self.delay_config = True # Delay sending message to HL2 until after sound starts
|
self.delay_config = True # Delay sending message to HL2 until after sound starts
|
||||||
self.TFRC_counter = 0 # Call for power etc. at intervals
|
self.TFRC_counter = 0 # Call for power etc. at intervals
|
||||||
@@ -271,6 +278,7 @@ class Hardware(BaseHardware):
|
|||||||
self.hermes_ip = addr[0]
|
self.hermes_ip = addr[0]
|
||||||
self.hermes_code_version = data[9]
|
self.hermes_code_version = data[9]
|
||||||
self.hermes_board_id = data[10]
|
self.hermes_board_id = data[10]
|
||||||
|
self.is_HermesLite2 = self.hermes_board_id == 6 and self.conf.hardware_file_type == "Hermes"
|
||||||
QS.set_hermes_id(data[9], data[10])
|
QS.set_hermes_id(data[9], data[10])
|
||||||
if data[0x16] >> 6 == 0:
|
if data[0x16] >> 6 == 0:
|
||||||
QS.set_params(bandscopeScale = 2048)
|
QS.set_params(bandscopeScale = 2048)
|
||||||
@@ -317,6 +325,7 @@ class Hardware(BaseHardware):
|
|||||||
bid = 6
|
bid = 6
|
||||||
self.hermes_code_version = code
|
self.hermes_code_version = code
|
||||||
self.hermes_board_id = bid
|
self.hermes_board_id = bid
|
||||||
|
self.is_HermesLite2 = self.hermes_board_id == 6 and self.conf.hardware_file_type == "Hermes"
|
||||||
QS.set_hermes_id(code, bid)
|
QS.set_hermes_id(code, bid)
|
||||||
st = 'Capture from Hermes device at specified IP %s' % self.hermes_ip
|
st = 'Capture from Hermes device at specified IP %s' % self.hermes_ip
|
||||||
found = True
|
found = True
|
||||||
@@ -331,9 +340,13 @@ class Hardware(BaseHardware):
|
|||||||
def open(self):
|
def open(self):
|
||||||
self.delay_config = True # Delay sending message to HL2 until after sound starts
|
self.delay_config = True # Delay sending message to HL2 until after sound starts
|
||||||
# This list only changes control bits; no use of WriteQueue()
|
# This list only changes control bits; no use of WriteQueue()
|
||||||
for name in ('keyupDelay',
|
self.ImmediateChange('keyupDelay')
|
||||||
'hermes_lowpwr_tr_enable', 'hermes_PWM', 'hermes_disable_sync', 'hermes_power_amp', 'Hermes_BandDictEnTx'):
|
self.ImmediateChange('Hermes_BandDictEnTx')
|
||||||
self.ImmediateChange(name)
|
if self.is_HermesLite2:
|
||||||
|
for name in ('hermes_lowpwr_tr_enable', 'hermes_PWM', 'hermes_disable_sync', 'hermes_power_amp'):
|
||||||
|
self.ImmediateChange(name)
|
||||||
|
#else: # DEBUG: Turn on power amp for testing OpenHPSDR radio with my Hermes Lite2
|
||||||
|
# self.SetControlBit(0x09, 19, 1)
|
||||||
return self.config_text
|
return self.config_text
|
||||||
def GetValue(self, name): # return values stored in the hardware
|
def GetValue(self, name): # return values stored in the hardware
|
||||||
if name == 'Hware_Hl2_EepromIP':
|
if name == 'Hware_Hl2_EepromIP':
|
||||||
@@ -492,7 +505,15 @@ class Hardware(BaseHardware):
|
|||||||
self.key_was_down = key_down
|
self.key_was_down = key_down
|
||||||
if self.TFRC_counter >= 3:
|
if self.TFRC_counter >= 3:
|
||||||
self.TFRC_counter = 0
|
self.TFRC_counter = 0
|
||||||
self.hermes_temperature, self.hermes_fwd_power, self.hermes_rev_power, self.hermes_pa_current, self.hermes_fwd_peak, self.hermes_rev_peak = QS.get_hermes_TFRC()
|
AIN1_avg, AIN1_peak, AIN2_avg, AIN2_peak, AIN3, AIN4, AIN5, AIN6 = QS.get_hermes_TFRC()
|
||||||
|
if self.hermes_board_id == 6:
|
||||||
|
(self.hermes_fwd_power, self.hermes_fwd_peak, self.hermes_rev_power, self.hermes_rev_peak,
|
||||||
|
self.hermes_pa_current, self.hermes_temperature) = AIN1_avg, AIN1_peak, AIN2_avg, AIN2_peak, AIN3, AIN5
|
||||||
|
else:
|
||||||
|
(self.hermes_fwd_power, self.hermes_fwd_peak, self.hermes_rev_power, self.hermes_rev_peak,
|
||||||
|
self.hermes_pa_current, self.hermes_temperature) = AIN1_avg, AIN1_peak, AIN2_avg, AIN2_peak, AIN3, AIN5
|
||||||
|
if DEBUG_SENSORS:
|
||||||
|
print("AIN 1-6 %4d %4d %4d %4d %4d %4d" % (AIN1_avg, AIN2_avg, AIN3, AIN4, AIN5, AIN6))
|
||||||
if self.application.bottom_widgets:
|
if self.application.bottom_widgets:
|
||||||
self.application.bottom_widgets.UpdateText()
|
self.application.bottom_widgets.UpdateText()
|
||||||
if self.delay_config and QS.get_params('rx_udp_started'):
|
if self.delay_config and QS.get_params('rx_udp_started'):
|
||||||
@@ -529,7 +550,9 @@ class Hardware(BaseHardware):
|
|||||||
def ChangeBandFilters(self):
|
def ChangeBandFilters(self):
|
||||||
if not hasattr(self.application, "multi_rx_screen"):
|
if not hasattr(self.application, "multi_rx_screen"):
|
||||||
return # Needed for the VNA program
|
return # Needed for the VNA program
|
||||||
self.SetControlBit(0x00, 13, self.antenna_index)
|
self.SetControlBit(0x00, 13, self.antenna_index & 0x01)
|
||||||
|
self.SetControlBit(0x00, 14, self.antenna_index & 0x02)
|
||||||
|
self.SetControlBit(0x00, 15, self.antenna_index > 0)
|
||||||
highest = self.band
|
highest = self.band
|
||||||
freq = self.conf.BandEdge.get(highest, (0, 0))[0]
|
freq = self.conf.BandEdge.get(highest, (0, 0))[0]
|
||||||
for pane in self.application.multi_rx_screen.receiver_list:
|
for pane in self.application.multi_rx_screen.receiver_list:
|
||||||
@@ -606,12 +629,7 @@ class Hardware(BaseHardware):
|
|||||||
return (48000, 384000)
|
return (48000, 384000)
|
||||||
## Hardware AGC is no longer supported in HL2 identifying as version >=40
|
## Hardware AGC is no longer supported in HL2 identifying as version >=40
|
||||||
def ChangeAGC(self, value):
|
def ChangeAGC(self, value):
|
||||||
if value:
|
pass
|
||||||
self.pc2hermes[2] |= 0x10 # C0 index == 0, C3[4]: AGC enable
|
|
||||||
else:
|
|
||||||
self.pc2hermes[2] &= ~0x10
|
|
||||||
QS.pc_to_hermes(self.pc2hermes)
|
|
||||||
if DEBUG: print ("Change AGC to", value)
|
|
||||||
## Simpler LNA setting for HL2 identifying as version >=40, see HL2 wiki for details
|
## Simpler LNA setting for HL2 identifying as version >=40, see HL2 wiki for details
|
||||||
def ChangeLNA(self, value): # LNA for Rx
|
def ChangeLNA(self, value): # LNA for Rx
|
||||||
# value is -12 to +48
|
# value is -12 to +48
|
||||||
@@ -624,7 +642,7 @@ class Hardware(BaseHardware):
|
|||||||
value = 51 - value
|
value = 51 - value
|
||||||
else:
|
else:
|
||||||
value = ((value+12) & 0x3f) | 0x40
|
value = ((value+12) & 0x3f) | 0x40
|
||||||
self.pc2hermes[4 * 10 + 3] = value # C0 index == 0x1010, C4[4:0] LNA 0-32 dB gain
|
self.pc2hermes[4 * 10 + 3] = value # C0 index == 0b1010, C4[4:0] LNA 0-32 dB gain
|
||||||
QS.pc_to_hermes(self.pc2hermes)
|
QS.pc_to_hermes(self.pc2hermes)
|
||||||
if DEBUG: print ("Change LNA to", value)
|
if DEBUG: print ("Change LNA to", value)
|
||||||
def ChangeTxLNA(self, value): # LNA for Tx
|
def ChangeTxLNA(self, value): # LNA for Tx
|
||||||
@@ -634,15 +652,25 @@ class Hardware(BaseHardware):
|
|||||||
elif value > 48:
|
elif value > 48:
|
||||||
value = 48
|
value = 48
|
||||||
value = ((value+12) & 0x3f) | 0x40 | 0x80
|
value = ((value+12) & 0x3f) | 0x40 | 0x80
|
||||||
self.SetControlByte(0x0e, 3, value, False) # C0 index == 0x0e, C3
|
self.SetControlByte(0x0e, 3, value, False) # C0 index == 0b1110 == 0x0e, C3
|
||||||
QS.pc_to_hermes(self.pc2hermes)
|
QS.pc_to_hermes(self.pc2hermes)
|
||||||
if DEBUG: print ("Change Tx LNA to", value)
|
if DEBUG: print ("Change Tx LNA to", value)
|
||||||
|
def ChangeAtten(self, value):
|
||||||
|
# value is 0 to 31
|
||||||
|
self.pc2hermes[4 * 10 + 3] = value & 0x1F # C0 index == 0b1010, C4[4:0] attenuator, 0 to 31 dB
|
||||||
|
self.SetControlBit(0b1010, 5, value) # enable attenuator
|
||||||
|
QS.pc_to_hermes(self.pc2hermes)
|
||||||
|
if DEBUG: print ("Change attenuator to", value)
|
||||||
|
def ChangePreamp(self, value):
|
||||||
|
self.SetControlBit(0b0000, 10, value) # enable preamp
|
||||||
def SetTxLevel(self):
|
def SetTxLevel(self):
|
||||||
try:
|
try:
|
||||||
tx_level = self.conf.tx_level[self.band]
|
tx_level = self.conf.tx_level[self.band]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
tx_level = self.conf.tx_level.get(None, 127) # The default
|
tx_level = self.conf.tx_level.get(None, 127) # The default
|
||||||
if self.mode[0:3] in ('DGT', 'FDV'): # Digital modes; change power by a percentage
|
if not self.mode:
|
||||||
|
reduc = self.application.tx_level
|
||||||
|
elif self.mode[0:3] in ('DGT', 'FDV'): # Digital modes; change power by a percentage
|
||||||
reduc = self.application.digital_tx_level
|
reduc = self.application.digital_tx_level
|
||||||
else:
|
else:
|
||||||
reduc = self.application.tx_level
|
reduc = self.application.tx_level
|
||||||
@@ -651,7 +679,7 @@ class Hardware(BaseHardware):
|
|||||||
tx_level = 0
|
tx_level = 0
|
||||||
elif tx_level > 255:
|
elif tx_level > 255:
|
||||||
tx_level = 255
|
tx_level = 255
|
||||||
self.pc2hermes[4 * 9] = tx_level # C0 index == 0x1001, C1[7:0] Tx level
|
self.pc2hermes[4 * 9] = tx_level # C0 index == 0b1001, C1[7:0] Tx level
|
||||||
QS.pc_to_hermes(self.pc2hermes)
|
QS.pc_to_hermes(self.pc2hermes)
|
||||||
if DEBUG: print("Change tx_level to", tx_level)
|
if DEBUG: print("Change tx_level to", tx_level)
|
||||||
def MultiRxCount(self, count): # count == number of additional receivers besides the Tx/Rx receiver: 1, 2, 3
|
def MultiRxCount(self, count): # count == number of additional receivers besides the Tx/Rx receiver: 1, 2, 3
|
||||||
@@ -874,6 +902,8 @@ class Hardware(BaseHardware):
|
|||||||
QS.set_hermeslite_writepointer(0)
|
QS.set_hermeslite_writepointer(0)
|
||||||
return False
|
return False
|
||||||
def WriteQueue(self, wait=False):
|
def WriteQueue(self, wait=False):
|
||||||
|
if not self.is_HermesLite2:
|
||||||
|
return False
|
||||||
self._wait_queue()
|
self._wait_queue()
|
||||||
# Send next write
|
# Send next write
|
||||||
self.old_writequeue = self.pc2hermeslitewritequeue[:]
|
self.old_writequeue = self.pc2hermeslitewritequeue[:]
|
||||||
|
|||||||
+36
-3
@@ -8,6 +8,7 @@ from __future__ import absolute_import
|
|||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
|
||||||
import math, wx, time
|
import math, wx, time
|
||||||
|
import _quisk as QS
|
||||||
from configure import ComboCtrl
|
from configure import ComboCtrl
|
||||||
|
|
||||||
|
|
||||||
@@ -24,8 +25,14 @@ class BottomWidgets: # Add extra widgets to the bottom of the screen
|
|||||||
def Widgets_0x06(self, app, hardware, conf, frame, gbs, vertBox):
|
def Widgets_0x06(self, app, hardware, conf, frame, gbs, vertBox):
|
||||||
self.num_rows_added = 1
|
self.num_rows_added = 1
|
||||||
start_row = self.start_row
|
start_row = self.start_row
|
||||||
|
self.btnPS = app.QuiskCheckbutton(frame, self.OnBtnPS, text='PS')
|
||||||
|
self.btnPS.Enable(False)
|
||||||
|
gbs.Add(self.btnPS, (start_row, self.start_col), flag=wx.EXPAND)
|
||||||
|
self.btnPsCal = app.QuiskPushbutton(frame, self.OnBtnPsCal, 'PS Cal')
|
||||||
|
self.btnPsCal.active = False
|
||||||
|
gbs.Add(self.btnPsCal, (start_row, self.start_col + 1), flag=wx.EXPAND)
|
||||||
self.atu_ctrl = ComboCtrl(frame, "ATU", ["Tune", "Bypass"], True)
|
self.atu_ctrl = ComboCtrl(frame, "ATU", ["Tune", "Bypass"], True)
|
||||||
gbs.Add(self.atu_ctrl, (start_row, self.start_col), (1, 2), flag=wx.EXPAND)
|
gbs.Add(self.atu_ctrl, (start_row, self.start_col + 2), (1, 2), flag=wx.EXPAND)
|
||||||
bw, bh = self.atu_ctrl.GetMinSize()
|
bw, bh = self.atu_ctrl.GetMinSize()
|
||||||
frame.Bind(wx.EVT_COMBOBOX_CLOSEUP, self.OnAtu)
|
frame.Bind(wx.EVT_COMBOBOX_CLOSEUP, self.OnAtu)
|
||||||
init = app.hermes_LNA_dB
|
init = app.hermes_LNA_dB
|
||||||
@@ -33,7 +40,7 @@ class BottomWidgets: # Add extra widgets to the bottom of the screen
|
|||||||
self.sliderLNA.idName = "RfLna"
|
self.sliderLNA.idName = "RfLna"
|
||||||
app.midiControls["RfLna"] = (self.sliderLNA, self.OnLNA)
|
app.midiControls["RfLna"] = (self.sliderLNA, self.OnLNA)
|
||||||
hardware.ChangeLNA(init)
|
hardware.ChangeLNA(init)
|
||||||
gbs.Add(self.sliderLNA, (start_row, self.start_col + 2), (1, 8), flag=wx.EXPAND)
|
gbs.Add(self.sliderLNA, (start_row, self.start_col + 4), (1, 6), flag=wx.EXPAND)
|
||||||
if conf.button_layout == "Small screen":
|
if conf.button_layout == "Small screen":
|
||||||
# Display four data items in a single window
|
# Display four data items in a single window
|
||||||
self.text_temperature = app.QuiskText1(frame, '', bh)
|
self.text_temperature = app.QuiskText1(frame, '', bh)
|
||||||
@@ -93,6 +100,20 @@ class BottomWidgets: # Add extra widgets to the bottom of the screen
|
|||||||
value = self.sliderLNA.GetValue()
|
value = self.sliderLNA.GetValue()
|
||||||
self.hardware.ChangeLNA(value)
|
self.hardware.ChangeLNA(value)
|
||||||
self.application.hermes_LNA_dB = value
|
self.application.hermes_LNA_dB = value
|
||||||
|
def OnBtnPS(self, event):
|
||||||
|
if self.btnPS.GetValue():
|
||||||
|
QS.set_tx_audio(PsEnable=1)
|
||||||
|
self.btnPsCal.Enable(False)
|
||||||
|
else:
|
||||||
|
QS.set_tx_audio(PsEnable=0)
|
||||||
|
self.btnPsCal.Enable(True)
|
||||||
|
def OnBtnPsCal(self, event):
|
||||||
|
if len(self.application.multi_rx_screen.receiver_list) == 0:
|
||||||
|
self.application.multi_rx_screen.OnAddReceiver(event=None, hide_it=True)
|
||||||
|
self.btnPS.Enable(False)
|
||||||
|
self.btnPsCal.active = True
|
||||||
|
self.btnPsCal.SetColor(self.config.color_check_btn)
|
||||||
|
QS.set_tx_audio(PsCal=1)
|
||||||
def Code2Temp(self): # Convert the HermesLite temperature code to the temperature
|
def Code2Temp(self): # Convert the HermesLite temperature code to the temperature
|
||||||
temp = self.hardware.hermes_temperature
|
temp = self.hardware.hermes_temperature
|
||||||
# For best accuracy, 3.26 should be a user's measured 3.3V supply voltage.
|
# For best accuracy, 3.26 should be a user's measured 3.3V supply voltage.
|
||||||
@@ -117,7 +138,13 @@ class BottomWidgets: # Add extra widgets to the bottom of the screen
|
|||||||
return fwd, rev
|
return fwd, rev
|
||||||
else:
|
else:
|
||||||
return rev, fwd
|
return rev, fwd
|
||||||
def UpdateText(self):
|
def UpdateText(self): # Called at intervals
|
||||||
|
# Button state
|
||||||
|
if self.btnPsCal.active:
|
||||||
|
if not QS.get_tx_audio("PsCal"):
|
||||||
|
self.btnPsCal.active = False
|
||||||
|
self.btnPsCal.SetColorNormal()
|
||||||
|
self.btnPS.Enable(True)
|
||||||
# Temperature
|
# Temperature
|
||||||
temp = self.Code2Temp()
|
temp = self.Code2Temp()
|
||||||
temp = (" Temp %3.0f" % temp) + u'\u2103'
|
temp = (" Temp %3.0f" % temp) + u'\u2103'
|
||||||
@@ -127,6 +154,12 @@ class BottomWidgets: # Add extra widgets to the bottom of the screen
|
|||||||
current = " PA %4.0f ma" % (1000*current)
|
current = " PA %4.0f ma" % (1000*current)
|
||||||
self.text_pa_current.SetLabel(current)
|
self.text_pa_current.SetLabel(current)
|
||||||
# forward and reverse peak power
|
# forward and reverse peak power
|
||||||
|
if not self.hardware.power_interpolator: # For no interpolator, show ADC
|
||||||
|
fwd = self.hardware.hermes_fwd_power
|
||||||
|
rev = self.hardware.hermes_rev_power
|
||||||
|
self.text_fwd_power.SetLabel("Fwd ADC %4d" % fwd)
|
||||||
|
self.text_swr.SetLabel("Rev ADC %4d" % rev)
|
||||||
|
return
|
||||||
fwd, rev = self.Code2FwdRevWatts(self.hardware.hermes_fwd_peak, self.hardware.hermes_rev_peak)
|
fwd, rev = self.Code2FwdRevWatts(self.hardware.hermes_fwd_peak, self.hardware.hermes_rev_peak)
|
||||||
# forward less reverse power
|
# forward less reverse power
|
||||||
power = fwd - rev
|
power = fwd - rev
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ with this:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
void ** Quisk_API; // array of pointers to functions and variables from module _quisk
|
void ** Quisk_API; // array of pointers to functions and variables from module _quisk
|
||||||
struct sound_conf * pt_quisk_sound_state; // pointer to quisk_sound_state
|
struct sound_conf * pt_quisk_sound_state; // pointer to quisk_sound_state
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <Python.h> // used by quisk.h
|
#include <Python.h> // used by quisk.h
|
||||||
#include <complex.h> // Used by quisk.h
|
#include <complex.h> // Used by quisk.h
|
||||||
|
#include <string.h>
|
||||||
#include "quisk.h"
|
#include "quisk.h"
|
||||||
|
|
||||||
// This module provides methods to access the state of the key.
|
// This module provides methods to access the state of the key.
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
+70
@@ -0,0 +1,70 @@
|
|||||||
|
# This provides helper functions for our makefile.
|
||||||
|
|
||||||
|
# Changes for MacOS support thanks to Mario, DL3LSM.
|
||||||
|
# Changes for building from macports provided by Eric, KM4DSJ
|
||||||
|
# Updated code for a Mac build contributed by Christoph, DL1YCF, December 2020.
|
||||||
|
# Converted from setup.py to makefile by Jim Ahlstrom, June 2026
|
||||||
|
|
||||||
|
import sys, os, subprocess, sysconfig
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
job = sys.argv[1]
|
||||||
|
|
||||||
|
def flags ():
|
||||||
|
# Fetch compiler flags and link flags from Python
|
||||||
|
result = subprocess.run(["python3-config", "--cflags"], capture_output=True, text=True, check=True)
|
||||||
|
cflags = result.stdout.strip()
|
||||||
|
cflags += " -fPIC"
|
||||||
|
result = subprocess.run(["python3-config", "--ldflags"], capture_output=True, text=True, check=True)
|
||||||
|
ldflags = result.stdout.strip()
|
||||||
|
ldflags += " -shared -lm -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fwrapv -O2 -lfftw3"
|
||||||
|
if sys.platform == "darwin": # Build for Macintosh
|
||||||
|
cflags += " -DQUISK_HAVE_PORTAUDIO "
|
||||||
|
ldflags += " -lportaudio "
|
||||||
|
if os.path.isdir('/opt/local/include'): # MacPorts
|
||||||
|
base_dir = '/opt/local'
|
||||||
|
elif os.path.isdir('/usr/local/include'): # HomeBrew on macOS Intel
|
||||||
|
base_dir = '/usr/local'
|
||||||
|
elif os.path.isdir('/opt/homebrew/include'): # HomeBrew on Apple Silicon
|
||||||
|
base_dir = '/opt/homebrew'
|
||||||
|
else: # Regular build?
|
||||||
|
base_dir = '/usr'
|
||||||
|
if os.path.isfile(base_dir + "/include/pulse/pulseaudio.h"):
|
||||||
|
cflags += " -DQUISK_HAVE_PULSEAUDIO "
|
||||||
|
ldflags += " -lpulse "
|
||||||
|
cflags += " -I. -I%s/include " % base_dir
|
||||||
|
ldflags += " -L. -L%s/lib " % base_dir
|
||||||
|
elif "freebsd" in sys.platform: #Build for FreeBSD
|
||||||
|
cflags += " -DQUISK_HAVE_PULSEAUDIO -I. -I/usr/local/include"
|
||||||
|
ldflags += " -lpulse -L. -L/usr/local/lib"
|
||||||
|
else: # Linux
|
||||||
|
cflags += " -DQUISK_HAVE_ALSA -DQUISK_HAVE_PULSEAUDIO"
|
||||||
|
ldflags += " -lasound -lpulse"
|
||||||
|
if os.path.isfile("/usr/include/portaudio.h"):
|
||||||
|
cflags += " -DQUISK_HAVE_PORTAUDIO"
|
||||||
|
ldflags += " -lportaudio"
|
||||||
|
return cflags, ldflags
|
||||||
|
|
||||||
|
if job == "--soapy":
|
||||||
|
# See if soapy is available
|
||||||
|
if os.path.isdir("/usr/include/SoapySDR") or os.path.isdir("/usr/local/include/SoapySDR"):
|
||||||
|
print('1')
|
||||||
|
else:
|
||||||
|
print('0')
|
||||||
|
|
||||||
|
elif job == "--suffix":
|
||||||
|
# Determine the platform-specific extension suffix (e.g., .cpython-311-x86_64-linux-gnu.so)
|
||||||
|
print(sysconfig.get_config_var('EXT_SUFFIX'))
|
||||||
|
|
||||||
|
elif job == "--cflags":
|
||||||
|
cc, ld = flags()
|
||||||
|
print(cc)
|
||||||
|
|
||||||
|
elif job == "--ldflags":
|
||||||
|
cc, ld = flags()
|
||||||
|
print(ld)
|
||||||
|
|
||||||
|
else:
|
||||||
|
sys.exit(1)
|
||||||
@@ -1,18 +1,93 @@
|
|||||||
.PHONY: quisk
|
# This makefile replaces the old Python "setup.py build_ext" which has been deprecated.
|
||||||
|
# See "quisk-old" below to make Quisk using the old setup.py.
|
||||||
|
|
||||||
quisk:
|
MODULE_NAME := _quisk
|
||||||
|
OBJDIR := build/local
|
||||||
|
SUBDIRS := afedrinet
|
||||||
|
|
||||||
|
ifeq ($(shell python3 makefil.py --soapy), 1)
|
||||||
|
SUBDIRS := $(SUBDIRS) soapypkg
|
||||||
|
endif
|
||||||
|
|
||||||
|
SRCS := \
|
||||||
|
base64.c \
|
||||||
|
extdemod.c \
|
||||||
|
filter.c \
|
||||||
|
freedv.c \
|
||||||
|
handshake.c \
|
||||||
|
is_key_down.c \
|
||||||
|
microphone.c \
|
||||||
|
quisk.c \
|
||||||
|
quisk_wdsp.c \
|
||||||
|
sha1.c \
|
||||||
|
sound.c\
|
||||||
|
sound_alsa.c \
|
||||||
|
sound_directx.c \
|
||||||
|
sound_portaudio.c \
|
||||||
|
sound_pulseaudio.c \
|
||||||
|
sound_wasapi.c \
|
||||||
|
tci.c \
|
||||||
|
utf8.c \
|
||||||
|
utility.c \
|
||||||
|
ws.c
|
||||||
|
|
||||||
|
CFLAGS := $(shell python3 makefil.py --cflags)
|
||||||
|
LDFLAGS := $(shell python3 makefil.py --ldflags)
|
||||||
|
EXTENSION_SUFFIX := $(shell python3 makefil.py --suffix)
|
||||||
|
TARGET := $(MODULE_NAME)$(EXTENSION_SUFFIX)
|
||||||
|
|
||||||
|
#$(info Info CFLAGS $(CFLAGS))
|
||||||
|
#$(info Info LDFLAGS $(LDFLAGS))
|
||||||
|
#$(info Info TARGET $(TARGET))
|
||||||
|
#$(info Info SUBDIRS $(SUBDIRS))
|
||||||
|
|
||||||
|
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(SRCS))
|
||||||
|
|
||||||
|
.PHONY: all clean test
|
||||||
|
.PHONY: $(SUBDIRS)
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
all: $(OBJDIR) $(OBJDIR)/import_quisk_api.o ac2yd/remote.o $(TARGET) $(SUBDIRS)
|
||||||
|
|
||||||
|
$(SUBDIRS):
|
||||||
|
$(MAKE) -C $@
|
||||||
|
|
||||||
|
$(OBJDIR):
|
||||||
|
@mkdir -p $(OBJDIR)
|
||||||
|
|
||||||
|
# Link the shared library object
|
||||||
|
$(TARGET): $(OBJS)
|
||||||
|
$(CC) -o $@ $(OBJS) ac2yd/remote.o $(LDFLAGS)
|
||||||
|
|
||||||
|
# Define the location for dependency files
|
||||||
|
DEPS = $(patsubst %.o,$(OBJDIR)/%.d,$(notdir $(OBJS)))
|
||||||
|
DEPFLAGS = -MMD -MP -MF $(OBJDIR)/$(notdir $*).d
|
||||||
|
-include $(DEPS)
|
||||||
|
|
||||||
|
# Compile the C source files into object files
|
||||||
|
ac2yd/remote.o: ac2yd/remote.c quisk.h filter.h
|
||||||
|
$(CC) -o $@ -c $< $(CFLAGS) $(DEPFLAGS)
|
||||||
|
|
||||||
|
$(OBJDIR)/import_quisk_api.o: import_quisk_api.c
|
||||||
|
$(CC) -o $@ -c $< $(CFLAGS) $(DEPFLAGS)
|
||||||
|
|
||||||
|
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||||
|
$(CC) -o $@ -c $< $(CFLAGS) $(DEPFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -f $(OBJDIR)/*.o $(OBJDIR)/*.d
|
||||||
|
-rm -f ac2yd/*.o
|
||||||
|
-rm -f $(MODULE_NAME)*.so
|
||||||
|
for dir in $(SUBDIRS); do \
|
||||||
|
$(MAKE) -C $$dir clean; \
|
||||||
|
done
|
||||||
|
|
||||||
|
# Run a test script to verify that the module can be imported
|
||||||
|
test: $(TARGET)
|
||||||
|
@python3 -c "import $(MODULE_NAME); print('Successfully imported:', $(MODULE_NAME).__file__)"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: quisk-old
|
||||||
|
quisk-old:
|
||||||
python3 setup.py build_ext --force --inplace
|
python3 setup.py build_ext --force --inplace
|
||||||
@echo
|
|
||||||
|
|
||||||
quisk3:
|
|
||||||
python3 setup.py build_ext --force --inplace
|
|
||||||
@echo
|
|
||||||
|
|
||||||
soapy3:
|
|
||||||
(cd soapypkg; make soapy3)
|
|
||||||
|
|
||||||
afedrinet3:
|
|
||||||
(cd afedrinet; make afedrinet3)
|
|
||||||
|
|
||||||
perseus3:
|
|
||||||
(cd perseuspkg; make perseus3)
|
|
||||||
|
|||||||
+639
-161
@@ -3,12 +3,12 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
|
#include <string.h>
|
||||||
#include <fftw3.h>
|
#include <fftw3.h>
|
||||||
#include "quisk.h"
|
#include "quisk.h"
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include "microphone.h"
|
#include "microphone.h"
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
#include "freedv.h"
|
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
@@ -75,6 +75,8 @@ static int vox_level = CLIP16; // VOX trigger level as a number 0 to CLIP16
|
|||||||
static int timeVOX = 2000; // VOX hang time in milliseconds
|
static int timeVOX = 2000; // VOX hang time in milliseconds
|
||||||
static int tx_sample_rate = 48000; // Used for SoapySDR
|
static int tx_sample_rate = 48000; // Used for SoapySDR
|
||||||
static int reverse_tx_sideband;
|
static int reverse_tx_sideband;
|
||||||
|
static int PsEnable = 0;
|
||||||
|
static int PsCal = 0;
|
||||||
|
|
||||||
static int doTxCorrect = 0; // Corrections for UDP sample transmit
|
static int doTxCorrect = 0; // Corrections for UDP sample transmit
|
||||||
static double TxCorrectLevel;
|
static double TxCorrectLevel;
|
||||||
@@ -243,11 +245,9 @@ static void init_alc(struct alc * pt, int size)
|
|||||||
case DGT_U:
|
case DGT_U:
|
||||||
case DGT_L:
|
case DGT_L:
|
||||||
case DGT_IQ:
|
case DGT_IQ:
|
||||||
pt->gain_now[i] = 1.4;
|
|
||||||
break;
|
|
||||||
case FDV_U:
|
case FDV_U:
|
||||||
case FDV_L:
|
case FDV_L:
|
||||||
pt->gain_now[i] = 2.0;
|
pt->gain_now[i] = 1.4;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pt->gain_now[i] = 1.0;
|
pt->gain_now[i] = 1.0;
|
||||||
@@ -615,7 +615,7 @@ static int tx_filter_digital(complex double * filtered, int count)
|
|||||||
quisk_filt_dInit(&filter1, quiskDgtFilt48Coefs, sizeof(quiskDgtFilt48Coefs)/sizeof(double)); // pass 1350, stop 1650
|
quisk_filt_dInit(&filter1, quiskDgtFilt48Coefs, sizeof(quiskDgtFilt48Coefs)/sizeof(double)); // pass 1350, stop 1650
|
||||||
}
|
}
|
||||||
if ( ! filtered) { // Change to rxMode
|
if ( ! filtered) { // Change to rxMode
|
||||||
quisk_filt_tune(&filter1, 1650.0 / 48000, rxMode != DGT_L && rxMode != LSB);
|
quisk_filt_tune(&filter1, 1650.0 / 48000, rxMode != DGT_L && rxMode != LSB && rxMode != FDV_L);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
for (i = 0; i < count; i++) // FIR bandpass filter; separate into I and Q
|
for (i = 0; i < count; i++) // FIR bandpass filter; separate into I and Q
|
||||||
@@ -624,140 +624,6 @@ static int tx_filter_digital(complex double * filtered, int count)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tx_filter_freedv(complex double * filtered, int count, int encode)
|
|
||||||
{ // Input samples are creal(filtered), output is filtered.
|
|
||||||
// This filter is used for digital voice.
|
|
||||||
// Changes by Dave Roberts, G8KBB, for additional modes June, 2020.
|
|
||||||
// Modified by N2ADR January, 2024.
|
|
||||||
int i;
|
|
||||||
// The input sample rate is mic_sample_rate, either 8000 or 48000 sps.
|
|
||||||
// The FreeDV codec requires an input speech_sample_rate of either 8000 or 16000 sps depending on the mode. Input is always real.
|
|
||||||
// The FreeDV codec will output samples at modem_sample_rate of 8000 or 48000 sps depending on the mode, and output may be real or complex.
|
|
||||||
double dtmp, magn, dsample;
|
|
||||||
int modem_sample_rate, speech_sample_rate, use_filter, is_real;
|
|
||||||
static int samples_size = 0;
|
|
||||||
static int do_init = 1;
|
|
||||||
static double aaa, bbb, ccc, Xmin, Xmax, Ymax;
|
|
||||||
static double time_long, time_short;
|
|
||||||
static double inMax=0.3;
|
|
||||||
static double * dsamples = NULL;
|
|
||||||
static struct quisk_cFilter filter1, filter2;
|
|
||||||
static struct quisk_dFilter filtDecim48to16, filtDecim16to8;;
|
|
||||||
static struct quisk_cFilter cfiltInterp8to48;
|
|
||||||
|
|
||||||
if (do_init) { // initialization
|
|
||||||
//QuiskWavWriteOpen(&hWav, "jim.wav", 3, 1, 4, 8000, 1.0 / CLIP16);
|
|
||||||
do_init = 0;
|
|
||||||
quisk_filt_cInit(&filter1, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double)); // at 48000 sps pass 3000 stop 4000 times two plus 3000
|
|
||||||
quisk_filt_cInit(&filter2, quiskFilt53D2Coefs, sizeof(quiskFilt53D2Coefs)/sizeof(double)); // at 8000 sps pass 1500 stop 1800 times two plus 1500
|
|
||||||
quisk_filt_cInit(&cfiltInterp8to48, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double));
|
|
||||||
quisk_filt_dInit(&filtDecim48to16, quiskAudio24p3Coefs, sizeof(quiskAudio24p3Coefs)/sizeof(double)); // pass 6000 stop 8000
|
|
||||||
quisk_filt_dInit(&filtDecim16to8, quiskFilt16dec8Coefs, sizeof(quiskFilt16dec8Coefs)/sizeof(double)); // pass 3000 stop 4000
|
|
||||||
Ymax = pow(10.0, - 1 / 20.0); // maximum y
|
|
||||||
Xmax = pow(10.0, 3 / 20.0); // x where slope is zero; for x > Xmax, y == Ymax
|
|
||||||
Xmin = Ymax - fabs(Ymax - Xmax); // x where slope is 1 and y = x; start of compression
|
|
||||||
//printf ("Xmin %f\n", Xmin);
|
|
||||||
aaa = 1.0 / (2.0 * (Xmin - Xmax)); // quadratic
|
|
||||||
bbb = -2.0 * aaa * Xmax;
|
|
||||||
ccc = Ymax - aaa * Xmax * Xmax - bbb * Xmax;
|
|
||||||
}
|
|
||||||
switch (freedv_current_mode) { // n_modem_sample_rate and n_speech_sample_rate may not be valid
|
|
||||||
case FREEDV_MODE_2400A:
|
|
||||||
case FREEDV_MODE_2400B:
|
|
||||||
use_filter = 1; // center frequency 3000
|
|
||||||
is_real = 1;
|
|
||||||
modem_sample_rate = 48000;
|
|
||||||
speech_sample_rate = 8000;
|
|
||||||
break;
|
|
||||||
case FREEDV_MODE_2020:
|
|
||||||
use_filter = 0;
|
|
||||||
is_real = 0;
|
|
||||||
modem_sample_rate = 8000;
|
|
||||||
speech_sample_rate = 16000;
|
|
||||||
break;
|
|
||||||
case FREEDV_MODE_800XA:
|
|
||||||
use_filter = 2; // center frequency 1500
|
|
||||||
is_real = 1;
|
|
||||||
modem_sample_rate = 8000;
|
|
||||||
speech_sample_rate = 8000;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
use_filter = 0;
|
|
||||||
is_real = 0;
|
|
||||||
modem_sample_rate = 8000;
|
|
||||||
speech_sample_rate = 8000;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( ! filtered) { // rxMode changed
|
|
||||||
quisk_filt_tune((struct quisk_dFilter *)&filter1, 3000.0 / 48000.0, rxMode != FDV_L);
|
|
||||||
quisk_filt_tune((struct quisk_dFilter *)&filter2, 1500.0 / 8000.0, rxMode != FDV_L);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// check size of dsamples[] buffer
|
|
||||||
if (count > samples_size) {
|
|
||||||
samples_size = count * 2;
|
|
||||||
if (dsamples)
|
|
||||||
free(dsamples);
|
|
||||||
dsamples = (double *)malloc(samples_size * sizeof(double));
|
|
||||||
}
|
|
||||||
// copy to dsamples[]
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
dsamples[i] = creal(filtered[i]) / CLIP16; // normalize to 1.0000
|
|
||||||
// Decimate to 8000 Hz or to 16000 depending on mode (and hence setting of sample rate)
|
|
||||||
if (quisk_sound_state.mic_sample_rate == 48000) {
|
|
||||||
switch (speech_sample_rate) {
|
|
||||||
case 16000:
|
|
||||||
count = quisk_dDecimate(dsamples, count, &filtDecim48to16, 3);
|
|
||||||
break;
|
|
||||||
case 8000:
|
|
||||||
count = quisk_dDecimate(dsamples, count, &filtDecim48to16, 3);
|
|
||||||
count = quisk_dDecimate(dsamples, count, &filtDecim16to8, 2);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
QuiskPrintf("Failure to convert speech input rate in tx_filter_freedv\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (quisk_sound_state.mic_sample_rate == 8000 && speech_sample_rate != 8000) {
|
|
||||||
QuiskPrintf("Failure to convert input rate in tx_filter_freedv\n");
|
|
||||||
}
|
|
||||||
// Measure average peak input audio level and limit
|
|
||||||
dtmp = 1.0 / speech_sample_rate; // sample time
|
|
||||||
time_long = 1.0 - exp(- dtmp / 3.000);
|
|
||||||
time_short = 1.0 - exp(- dtmp / 0.005);
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
dsample = dsamples[i];
|
|
||||||
magn = fabs(dsample);
|
|
||||||
if (magn > inMax)
|
|
||||||
inMax = inMax * (1 - time_short) + time_short * magn;
|
|
||||||
else if(magn > mic_agc_level)
|
|
||||||
inMax = inMax * (1 - time_long) + time_long * magn;
|
|
||||||
else
|
|
||||||
inMax = inMax * (1 - time_long) + time_long * mic_agc_level;
|
|
||||||
dsample = dsample / inMax * Xmin * 0.7;
|
|
||||||
magn = fabs(dsample);
|
|
||||||
if (magn < Xmin)
|
|
||||||
dsamples[i] = dsample;
|
|
||||||
else if (magn > Xmax)
|
|
||||||
dsamples[i] = copysign(Ymax, dsample);
|
|
||||||
else
|
|
||||||
dsamples[i] = copysign(aaa * magn * magn + bbb * magn + ccc, dsample);
|
|
||||||
dsamples[i] = dsamples[i] * CLIP16;
|
|
||||||
}
|
|
||||||
//QuiskWavWriteD(&hWav, dsamples, count);
|
|
||||||
if (encode && pt_quisk_freedv_tx) // Encode audio into digital modulation
|
|
||||||
count = (* pt_quisk_freedv_tx)(filtered, dsamples, count, is_real);
|
|
||||||
//quisk_calc_audio_graph(CLIP16, filtered, NULL, count, 0);
|
|
||||||
if (use_filter == 1) // convert float samples to complex with an analytic filter
|
|
||||||
count = quisk_cCDecimate(filtered, count, &filter1, 1);
|
|
||||||
else if (use_filter == 2)
|
|
||||||
count = quisk_cCDecimate(filtered, count, &filter2, 1);
|
|
||||||
// Interpolate up to 48000
|
|
||||||
if (MIC_OUT_RATE != modem_sample_rate )
|
|
||||||
count = quisk_cInterpolate(filtered, count, &cfiltInterp8to48, MIC_OUT_RATE / modem_sample_rate);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject * quisk_get_tx_filter(PyObject * self, PyObject * args)
|
PyObject * quisk_get_tx_filter(PyObject * self, PyObject * args)
|
||||||
{ // return the TX filter response to display on the graph
|
{ // return the TX filter response to display on the graph
|
||||||
// This is for debugging. Change quisk.py to call QS.get_tx_filter() instead
|
// This is for debugging. Change quisk.py to call QS.get_tx_filter() instead
|
||||||
@@ -894,6 +760,8 @@ static void quisk_hermes_tx_add(complex double * cSamples, int tx_count, int key
|
|||||||
if (hermes_read_index >= HERMES_TX_BUF_SHORTS)
|
if (hermes_read_index >= HERMES_TX_BUF_SHORTS)
|
||||||
hermes_read_index -= HERMES_TX_BUF_SHORTS;
|
hermes_read_index -= HERMES_TX_BUF_SHORTS;
|
||||||
}
|
}
|
||||||
|
// Send the transmit samples to the predistortion logic. Modify the samples in place.
|
||||||
|
PreDistort(NULL, NULL, 0, cSamples, tx_count);
|
||||||
hermes_num_samples += tx_count;
|
hermes_num_samples += tx_count;
|
||||||
for (i = 0; i < tx_count; i++) { // Put transmit mic samples into the buffer
|
for (i = 0; i < tx_count; i++) { // Put transmit mic samples into the buffer
|
||||||
hermes_buf[hermes_write_index++] = (short)cimag(cSamples[i]);
|
hermes_buf[hermes_write_index++] = (short)cimag(cSamples[i]);
|
||||||
@@ -909,6 +777,7 @@ void quisk_hermes_tx_send(int tx_socket, int * tx_records)
|
|||||||
{ // Send one UDP block of mic samples using the Metis-Hermes protocol. Timing is from blocks received, rate is 48k.
|
{ // Send one UDP block of mic samples using the Metis-Hermes protocol. Timing is from blocks received, rate is 48k.
|
||||||
// If the key is up we send samples anyway, but the samples are zero.
|
// If the key is up we send samples anyway, but the samples are zero.
|
||||||
int i, j, offset, sent, ratio;
|
int i, j, offset, sent, ratio;
|
||||||
|
int PsInUse;
|
||||||
short s;
|
short s;
|
||||||
unsigned char sendbuf[1032];
|
unsigned char sendbuf[1032];
|
||||||
unsigned char * pt_buf;
|
unsigned char * pt_buf;
|
||||||
@@ -916,6 +785,7 @@ void quisk_hermes_tx_send(int tx_socket, int * tx_records)
|
|||||||
static unsigned char C0_index = 0;
|
static unsigned char C0_index = 0;
|
||||||
complex double cw_samples[63 * 2];
|
complex double cw_samples[63 * 2];
|
||||||
static double writequeue_time0 = 0;
|
static double writequeue_time0 = 0;
|
||||||
|
static unsigned char rx1_freq[4]; // frequency of the first receiver
|
||||||
|
|
||||||
//printf("hermes_tx_send start 1: hermes_num_samples %d\n", hermes_num_samples);
|
//printf("hermes_tx_send start 1: hermes_num_samples %d\n", hermes_num_samples);
|
||||||
if (tx_records == NULL) {
|
if (tx_records == NULL) {
|
||||||
@@ -928,6 +798,7 @@ void quisk_hermes_tx_send(int tx_socket, int * tx_records)
|
|||||||
last_play_state = quisk_play_state;
|
last_play_state = quisk_play_state;
|
||||||
serial_key_samples(NULL, 0);
|
serial_key_samples(NULL, 0);
|
||||||
}
|
}
|
||||||
|
PsInUse = PsEnable || PsCal;
|
||||||
//printf("hermes_tx_send start 2: hermes_num_samples %d\n", hermes_num_samples);
|
//printf("hermes_tx_send start 2: hermes_num_samples %d\n", hermes_num_samples);
|
||||||
ratio = quisk_sound_state.sample_rate / 48000; // send rate is 48 ksps
|
ratio = quisk_sound_state.sample_rate / 48000; // send rate is 48 ksps
|
||||||
//printf ("quisk_hermes_tx_send ratio %d count %d\n", ratio, *tx_records);
|
//printf ("quisk_hermes_tx_send ratio %d count %d\n", ratio, *tx_records);
|
||||||
@@ -971,15 +842,33 @@ void quisk_hermes_tx_send(int tx_socket, int * tx_records)
|
|||||||
sendbuf[13] = quisk_pc_to_hermes[offset++]; // C2
|
sendbuf[13] = quisk_pc_to_hermes[offset++]; // C2
|
||||||
sendbuf[14] = quisk_pc_to_hermes[offset++]; // C3
|
sendbuf[14] = quisk_pc_to_hermes[offset++]; // C3
|
||||||
sendbuf[15] = quisk_pc_to_hermes[offset++]; // C4
|
sendbuf[15] = quisk_pc_to_hermes[offset++]; // C4
|
||||||
if (C0_index == 0) { // Do not change receiver count without stopping Hermes and restarting
|
switch (C0_index) {
|
||||||
|
case 0: // Do not change receiver count without stopping Hermes and restarting
|
||||||
sendbuf[15] = quisk_multirx_count << 3 | 0x04; // Send the old count, not the changed count
|
sendbuf[15] = quisk_multirx_count << 3 | 0x04; // Send the old count, not the changed count
|
||||||
if (hermes_mox_bit) // send filter selection on J16
|
if (hermes_mox_bit) // send filter selection on J16
|
||||||
sendbuf[13] = hermes_filter_tx << 1;
|
sendbuf[13] = hermes_filter_tx << 1;
|
||||||
else
|
else
|
||||||
sendbuf[13] = hermes_filter_rx << 1;
|
sendbuf[13] = hermes_filter_rx << 1;
|
||||||
|
break;
|
||||||
|
case 2: // Rx1 frequency
|
||||||
|
rx1_freq[0] = sendbuf[12];
|
||||||
|
rx1_freq[1] = sendbuf[13];
|
||||||
|
rx1_freq[2] = sendbuf[14];
|
||||||
|
rx1_freq[3] = sendbuf[15];
|
||||||
|
break;
|
||||||
|
case 3: // Rx2 frequency
|
||||||
|
if (PsInUse && hermes_mox_bit) {
|
||||||
|
sendbuf[12] = rx1_freq[0];
|
||||||
|
sendbuf[13] = rx1_freq[1];
|
||||||
|
sendbuf[14] = rx1_freq[2];
|
||||||
|
sendbuf[15] = rx1_freq[3];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
if (PsInUse && hermes_mox_bit)
|
||||||
|
sendbuf[13] |= 0x40; // assert pure signal
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if ( ! quisk_is_vna && C0_index == 9) {
|
|
||||||
}
|
|
||||||
if (++C0_index > 16)
|
if (++C0_index > 16)
|
||||||
C0_index = 0;
|
C0_index = 0;
|
||||||
pt_buf = sendbuf + 16;
|
pt_buf = sendbuf + 16;
|
||||||
@@ -1028,15 +917,33 @@ void quisk_hermes_tx_send(int tx_socket, int * tx_records)
|
|||||||
sendbuf[525] = quisk_pc_to_hermes[offset++]; // C2
|
sendbuf[525] = quisk_pc_to_hermes[offset++]; // C2
|
||||||
sendbuf[526] = quisk_pc_to_hermes[offset++]; // C3
|
sendbuf[526] = quisk_pc_to_hermes[offset++]; // C3
|
||||||
sendbuf[527] = quisk_pc_to_hermes[offset++]; // C4
|
sendbuf[527] = quisk_pc_to_hermes[offset++]; // C4
|
||||||
if (C0_index == 0) {
|
switch (C0_index) {
|
||||||
sendbuf[527] = quisk_multirx_count << 3 | 0x04; // Send the old count, not the changed count
|
case 0: // Do not change receiver count without stopping Hermes and restarting
|
||||||
|
sendbuf[527] = quisk_multirx_count << 3 | 0x04; // Send the old count, not the changed count
|
||||||
if (hermes_mox_bit) // send filter selection on J16
|
if (hermes_mox_bit) // send filter selection on J16
|
||||||
sendbuf[525] = hermes_filter_tx << 1;
|
sendbuf[525] = hermes_filter_tx << 1;
|
||||||
else
|
else
|
||||||
sendbuf[525] = hermes_filter_rx << 1;
|
sendbuf[525] = hermes_filter_rx << 1;
|
||||||
|
break;
|
||||||
|
case 2: // Rx1 frequency
|
||||||
|
rx1_freq[0] = sendbuf[524];
|
||||||
|
rx1_freq[1] = sendbuf[525];
|
||||||
|
rx1_freq[2] = sendbuf[526];
|
||||||
|
rx1_freq[3] = sendbuf[527];
|
||||||
|
break;
|
||||||
|
case 3: // Rx2 frequency
|
||||||
|
if (PsInUse && hermes_mox_bit) {
|
||||||
|
sendbuf[524] = rx1_freq[0];
|
||||||
|
sendbuf[525] = rx1_freq[1];
|
||||||
|
sendbuf[526] = rx1_freq[2];
|
||||||
|
sendbuf[527] = rx1_freq[3];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
if (PsInUse && hermes_mox_bit)
|
||||||
|
sendbuf[525] |= 0x40; // assert pure signal
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if ( ! quisk_is_vna && C0_index == 9) {
|
|
||||||
}
|
|
||||||
if (++C0_index > 16)
|
if (++C0_index > 16)
|
||||||
C0_index = 0;
|
C0_index = 0;
|
||||||
}
|
}
|
||||||
@@ -1102,12 +1009,47 @@ static void transmit_udp(complex double * cSamples, int count)
|
|||||||
|
|
||||||
static void transmit_mic_carrier(complex double * cSamples, int count, double level)
|
static void transmit_mic_carrier(complex double * cSamples, int count, double level)
|
||||||
{ // send a CW carrier instead of mic samples
|
{ // send a CW carrier instead of mic samples
|
||||||
|
#if 0
|
||||||
|
// transmit a saw tooth
|
||||||
|
static int up = 1;
|
||||||
|
static int ramp = 0;
|
||||||
|
int i;
|
||||||
|
int max = 5000;
|
||||||
|
static int pause = 5000 / 10;
|
||||||
|
int half_max = (int)(0.1 / 0.2339 * max);
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
cSamples[i] = (double)ramp / max * CLIP16;
|
||||||
|
if (ramp == half_max && pause) {
|
||||||
|
pause--;
|
||||||
|
}
|
||||||
|
else if (up) {
|
||||||
|
if (ramp == max) {
|
||||||
|
up = 0;
|
||||||
|
pause = max / 10;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ramp += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (ramp == 0) {
|
||||||
|
up = 1;
|
||||||
|
pause = max / 10;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ramp -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//printf("Ramp %d\n", ramp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if 1
|
#if 1
|
||||||
// transmit a carrier equal to the number of samples
|
// transmit a carrier equal to the number of samples
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
cSamples[i] = level * CLIP16;
|
cSamples[i] = level * CLIP16;
|
||||||
#else
|
#endif
|
||||||
|
#if 0
|
||||||
// replace mic samples with a sin wave
|
// replace mic samples with a sin wave
|
||||||
int i;
|
int i;
|
||||||
double freq;
|
double freq;
|
||||||
@@ -1278,6 +1220,10 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
|
|||||||
count *= interp;
|
count *= interp;
|
||||||
transmit_mic_carrier(cSamples, count, quiskSpotLevel / 1000.0);
|
transmit_mic_carrier(cSamples, count, quiskSpotLevel / 1000.0);
|
||||||
}
|
}
|
||||||
|
else if (tci_tx_audio_client) {
|
||||||
|
count = tx_filter_digital(cSamples, count); // filter samples, minimal processing
|
||||||
|
process_alc(cSamples, count, &tx_alc, DGT_U);
|
||||||
|
}
|
||||||
else switch (rxMode) {
|
else switch (rxMode) {
|
||||||
case LSB: // LSB
|
case LSB: // LSB
|
||||||
case USB: // USB
|
case USB: // USB
|
||||||
@@ -1319,6 +1265,8 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
|
|||||||
case DGT_L:
|
case DGT_L:
|
||||||
case DGT_IQ:
|
case DGT_IQ:
|
||||||
case DGT_FM:
|
case DGT_FM:
|
||||||
|
case FDV_U: // FreeDV
|
||||||
|
case FDV_L:
|
||||||
count = tx_filter_digital(cSamples, count); // filter samples, minimal processing
|
count = tx_filter_digital(cSamples, count); // filter samples, minimal processing
|
||||||
process_alc(cSamples, count, &tx_alc, rxMode);
|
process_alc(cSamples, count, &tx_alc, rxMode);
|
||||||
break;
|
break;
|
||||||
@@ -1326,11 +1274,6 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
|
|||||||
count *= interp;
|
count *= interp;
|
||||||
transmit_mic_imd(cSamples, count, quiskImdLevel / 1000.0);
|
transmit_mic_imd(cSamples, count, quiskImdLevel / 1000.0);
|
||||||
break;
|
break;
|
||||||
case FDV_U: // FDV
|
|
||||||
case FDV_L:
|
|
||||||
count = tx_filter_freedv(cSamples, count, 1);
|
|
||||||
process_alc(cSamples, count, &tx_alc, rxMode);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1421,11 +1364,6 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
|
|||||||
count = tx_filter(cSamples, count);
|
count = tx_filter(cSamples, count);
|
||||||
process_alc(cSamples, count, &tx_alc, rxMode);
|
process_alc(cSamples, count, &tx_alc, rxMode);
|
||||||
break;
|
break;
|
||||||
case FDV_U: // FDV
|
|
||||||
case FDV_L:
|
|
||||||
count = tx_filter_freedv(cSamples, count, 0);
|
|
||||||
process_alc(cSamples, count, &tx_alc, rxMode);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
cSamples[i] = 0;
|
cSamples[i] = 0;
|
||||||
@@ -1447,14 +1385,31 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject * quisk_get_tx_audio(PyObject * self, PyObject * args)
|
||||||
|
{
|
||||||
|
const char * name;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple (args, "s", &name))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strcmp(name, "PsEnable") == 0)
|
||||||
|
return PyInt_FromLong(PsEnable);
|
||||||
|
if (strcmp(name, "PsCal") == 0)
|
||||||
|
return PyInt_FromLong(PsCal);
|
||||||
|
Py_INCREF (Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyObject * quisk_set_tx_audio(PyObject * self, PyObject * args, PyObject * keywds)
|
PyObject * quisk_set_tx_audio(PyObject * self, PyObject * args, PyObject * keywds)
|
||||||
{ /* Call with keyword arguments ONLY; change Tx audio parameters */
|
{ /* Call with keyword arguments ONLY; change Tx audio parameters */
|
||||||
static char * kwlist[] = {"vox_level", "vox_time", "mic_clip", "mic_preemphasis", "tx_sample_rate",
|
static char * kwlist[] = {"vox_level", "vox_time", "mic_clip", "mic_preemphasis", "tx_sample_rate",
|
||||||
"reverse_tx_sideband", NULL} ;
|
"reverse_tx_sideband", "PsEnable", "PsCal", NULL} ;
|
||||||
int vlevel = -9999, clevel = -9999;
|
int vlevel = -9999, clevel = -9999;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiidii", kwlist,
|
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiidiiii", kwlist,
|
||||||
&vlevel, &timeVOX, &clevel, &quisk_mic_preemphasis, &tx_sample_rate, &reverse_tx_sideband))
|
&vlevel, &timeVOX, &clevel, &quisk_mic_preemphasis, &tx_sample_rate, &reverse_tx_sideband,
|
||||||
|
&PsEnable, &PsCal))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (vlevel != -9999)
|
if (vlevel != -9999)
|
||||||
vox_level = (int)(pow(10.0, vlevel / 20.0) * CLIP16); // Convert dB to 16-bit sample
|
vox_level = (int)(pow(10.0, vlevel / 20.0) * CLIP16); // Convert dB to 16-bit sample
|
||||||
@@ -1578,7 +1533,6 @@ void quisk_set_tx_mode(void) // called when the mode rxMode is changed
|
|||||||
tx_filter(NULL, 0);
|
tx_filter(NULL, 0);
|
||||||
tx_filter_digital(NULL, 0);
|
tx_filter_digital(NULL, 0);
|
||||||
transmit_udp(NULL, 0);
|
transmit_udp(NULL, 0);
|
||||||
tx_filter_freedv(NULL, 0, 0);
|
|
||||||
serial_key_samples(NULL, 0);
|
serial_key_samples(NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1624,3 +1578,527 @@ static void serial_key_samples(complex double * cSamples, int count) // called f
|
|||||||
cSamples[i] = ampl * CLIP16;
|
cSamples[i] = ampl * CLIP16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spline(double x[], double y[], int n, double yp1, double ypn, double y2[])
|
||||||
|
{
|
||||||
|
int i,k;
|
||||||
|
double p,qn,sig,un,*u;
|
||||||
|
|
||||||
|
u = (double *)malloc(n * sizeof(double));
|
||||||
|
if (yp1 > 0.99e30)
|
||||||
|
y2[1]=u[1]=0.0;
|
||||||
|
else {
|
||||||
|
y2[1] = -0.5;
|
||||||
|
u[1]=(3.0/(x[2]-x[1]))*((y[2]-y[1])/(x[2]-x[1])-yp1);
|
||||||
|
}
|
||||||
|
for (i=2;i<=n-1;i++) {
|
||||||
|
sig=(x[i]-x[i-1])/(x[i+1]-x[i-1]);
|
||||||
|
p=sig*y2[i-1]+2.0;
|
||||||
|
y2[i]=(sig-1.0)/p;
|
||||||
|
u[i]=(y[i+1]-y[i])/(x[i+1]-x[i]) - (y[i]-y[i-1])/(x[i]-x[i-1]);
|
||||||
|
u[i]=(6.0*u[i]/(x[i+1]-x[i-1])-sig*u[i-1])/p;
|
||||||
|
}
|
||||||
|
if (ypn > 0.99e30)
|
||||||
|
qn=un=0.0;
|
||||||
|
else {
|
||||||
|
qn=0.5;
|
||||||
|
un=(3.0/(x[n]-x[n-1]))*(ypn-(y[n]-y[n-1])/(x[n]-x[n-1]));
|
||||||
|
}
|
||||||
|
y2[n]=(un-qn*u[n-1])/(qn*y2[n-1]+1.0);
|
||||||
|
for (k=n-1;k>=1;k--)
|
||||||
|
y2[k]=y2[k]*y2[k+1]+u[k];
|
||||||
|
free(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void polint(double xa[], double ya[], double x, double *y)
|
||||||
|
{
|
||||||
|
int i,m,ns=1;
|
||||||
|
double den,dif,dift,ho,hp,w, dy;
|
||||||
|
double c[4], d[4];
|
||||||
|
|
||||||
|
dif=fabs(x-xa[1]);
|
||||||
|
for (i=1;i<=3;i++) {
|
||||||
|
if ( (dift=fabs(x-xa[i])) < dif) {
|
||||||
|
ns=i;
|
||||||
|
dif=dift;
|
||||||
|
}
|
||||||
|
c[i]=ya[i];
|
||||||
|
d[i]=ya[i];
|
||||||
|
}
|
||||||
|
*y=ya[ns--];
|
||||||
|
for (m=1;m<3;m++) {
|
||||||
|
for (i=1;i<=3-m;i++) {
|
||||||
|
ho=xa[i]-x;
|
||||||
|
hp=xa[i+m]-x;
|
||||||
|
w=c[i+1]-d[i];
|
||||||
|
den = ho - hp;
|
||||||
|
if ( (den=ho-hp) == 0.0)
|
||||||
|
printf("Error in routine polint\n");
|
||||||
|
den=w/den;
|
||||||
|
d[i]=hp*den;
|
||||||
|
c[i]=ho*den;
|
||||||
|
}
|
||||||
|
*y += (dy=(2*ns < (3-m) ? c[ns+1] : d[ns--]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void splint(double xa[], double ya[], double y2a[], int n, double x, double *y)
|
||||||
|
{
|
||||||
|
static int klo = 1;
|
||||||
|
int khi,k;
|
||||||
|
double h,b,a;
|
||||||
|
|
||||||
|
khi = klo + 1;
|
||||||
|
if (x < xa[klo] || x > xa[khi]) {
|
||||||
|
klo=1;
|
||||||
|
khi=n;
|
||||||
|
while (khi-klo > 1) {
|
||||||
|
k=(khi+klo) >> 1;
|
||||||
|
if (xa[k] > x) khi=k;
|
||||||
|
else klo=k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h=xa[khi]-xa[klo];
|
||||||
|
if (h == 0.0)
|
||||||
|
printf("Bad xa input to routine splint\n");
|
||||||
|
a=(xa[khi]-x)/h;
|
||||||
|
b=(x-xa[klo])/h;
|
||||||
|
*y=a*ya[klo]+b*ya[khi]+((a*a*a-a)*y2a[klo]+(b*b*b-b)*y2a[khi])*(h*h)/6.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NUM_BINS 21
|
||||||
|
#define BIN_SPACING (1.0 / (NUM_BINS - 1))
|
||||||
|
#define BIN_TOL (BIN_SPACING * 0.20)
|
||||||
|
#define BLOCK_MSECS 2000 // Time of one data block.
|
||||||
|
#define MILLISECS(x) (48 * (x)) // Convert milliseconds to samples at 48000 samples per second.
|
||||||
|
#define DEBUG_PS 0
|
||||||
|
#define HOLD 0 // Make calibration run permanent
|
||||||
|
#define RX1_GAIN 0.238
|
||||||
|
void PreDistort(complex double * amp_in_samples, complex double * amp_out_samples, int nSamples, complex double * tx_samples, int num_tx)
|
||||||
|
{
|
||||||
|
int i, bin, num_bin_data;
|
||||||
|
double G0, phase, mag_in, mag_out, new_mag, aaa, ppp;
|
||||||
|
double error, xp, xpg, xp0, xpg0;
|
||||||
|
double quadX[4], quadY[4], quadZ[4];
|
||||||
|
complex double in_sample, out_sample;
|
||||||
|
static int state = 0;
|
||||||
|
static int Fault;
|
||||||
|
static int isCalibrated = 0;
|
||||||
|
static int ramp_counter, ramp_count0;
|
||||||
|
static int input_samples = 0;
|
||||||
|
static double tx_to_RX1_gain = 0;
|
||||||
|
static double max_out_mag, starting_phase;
|
||||||
|
#if DEBUG_PS
|
||||||
|
static double clip_tx, clip_mag_in, clip_aaa;
|
||||||
|
#endif
|
||||||
|
#if DEBUG_PS > 2
|
||||||
|
static int old_state;
|
||||||
|
static double time0;
|
||||||
|
#endif
|
||||||
|
static int spline_P_size;
|
||||||
|
static double spline_P_X[NUM_BINS + 1];
|
||||||
|
static double spline_P_mag[NUM_BINS + 1];
|
||||||
|
static double spline_P_mag2[NUM_BINS + 1];
|
||||||
|
static double spline_P_phase[NUM_BINS + 1];
|
||||||
|
static double spline_P_phase2[NUM_BINS + 1];
|
||||||
|
|
||||||
|
struct BPD {
|
||||||
|
int XP_count;
|
||||||
|
double table_XP;
|
||||||
|
double XP_mag;
|
||||||
|
double mag_X;
|
||||||
|
double mag_P;
|
||||||
|
double phase_P;
|
||||||
|
complex double XP_gain;
|
||||||
|
double XP_gain_mag;
|
||||||
|
double XP_gain_phase;
|
||||||
|
};
|
||||||
|
static struct BPD Bins[NUM_BINS];
|
||||||
|
|
||||||
|
static enum {
|
||||||
|
NothingHappens,
|
||||||
|
SendCW,
|
||||||
|
StopCW
|
||||||
|
} tx_state = NothingHappens;
|
||||||
|
|
||||||
|
if (quisk_multirx_count == 0) {
|
||||||
|
state = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (PsCal) {
|
||||||
|
if (state == 0) {
|
||||||
|
isCalibrated = 0;
|
||||||
|
state = 10;
|
||||||
|
#if DEBUG_PS > 2
|
||||||
|
time0 = QuiskTimeSec();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( ! PsEnable) {
|
||||||
|
state = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( ! hermes_mox_bit) {
|
||||||
|
state = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_tx) {
|
||||||
|
input_samples += num_tx;
|
||||||
|
if (isCalibrated) { // perform predistortion of Tx samples; multiply input X by P
|
||||||
|
for (i = 0; i < num_tx; i++) {
|
||||||
|
mag_in = cabs(tx_samples[i]) / CLIP16;
|
||||||
|
splint(spline_P_X, spline_P_mag, spline_P_mag2, spline_P_size, mag_in, &aaa);
|
||||||
|
splint(spline_P_X, spline_P_phase, spline_P_phase2, spline_P_size, mag_in, &ppp);
|
||||||
|
tx_samples[i] *= aaa * cexp(I * ppp);
|
||||||
|
new_mag = mag_in * aaa;
|
||||||
|
if (new_mag > 1.0) {
|
||||||
|
tx_samples[i] /= new_mag;
|
||||||
|
#if DEBUG_PS
|
||||||
|
if (new_mag > 1.0001 && new_mag > clip_tx) {
|
||||||
|
clip_tx = new_mag;
|
||||||
|
clip_mag_in = mag_in;
|
||||||
|
clip_aaa = aaa;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (tx_state) {
|
||||||
|
case NothingHappens:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
case SendCW: // Start sending a CW carrier.
|
||||||
|
for (i = 0; i < num_tx; i++) {
|
||||||
|
if (ramp_counter > 0) // slow increase
|
||||||
|
tx_samples[i] = CLIP16 * (1.0 - (double)ramp_counter-- / ramp_count0);
|
||||||
|
else
|
||||||
|
tx_samples[i] = CLIP16;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case StopCW: // Stop sending a CW carrier.
|
||||||
|
for (i = 0; i < num_tx; i++) {
|
||||||
|
if (ramp_counter > 0) // slow decrease
|
||||||
|
tx_samples[i] = (double)CLIP16 * ramp_counter-- / ramp_count0;
|
||||||
|
else
|
||||||
|
tx_samples[i] = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nSamples <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if DEBUG_PS > 2
|
||||||
|
if (state != old_state) {
|
||||||
|
aaa = cabs(amp_in_samples[0] / CLIP32);
|
||||||
|
printf("State %2d time %6.3lf sample %12.6f\n", old_state, QuiskTimeSec() - time0, aaa);
|
||||||
|
old_state = state;
|
||||||
|
time0 = QuiskTimeSec();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case 0: // Start a timer
|
||||||
|
tx_state = NothingHappens;
|
||||||
|
input_samples = 0;
|
||||||
|
starting_phase = 0;
|
||||||
|
if ( ! HOLD)
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (input_samples >= MILLISECS(200)) {
|
||||||
|
#if DEBUG_PS
|
||||||
|
clip_tx = 0.0;
|
||||||
|
#endif
|
||||||
|
for (bin = 0; bin < NUM_BINS; bin++) { // initialize
|
||||||
|
Bins[bin].XP_count = 0;
|
||||||
|
Bins[bin].XP_mag = 0;
|
||||||
|
Bins[bin].XP_gain = 0;
|
||||||
|
}
|
||||||
|
input_samples = 0;
|
||||||
|
state++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 15:
|
||||||
|
if (input_samples >= MILLISECS(BLOCK_MSECS)) {
|
||||||
|
num_bin_data = 0;
|
||||||
|
Fault = 0;
|
||||||
|
for (bin = 0; bin < NUM_BINS; bin++) {
|
||||||
|
if (Bins[bin].XP_count) {
|
||||||
|
num_bin_data++;
|
||||||
|
Bins[bin].XP_mag /= Bins[bin].XP_count;
|
||||||
|
Bins[bin].XP_gain /= Bins[bin].XP_count;
|
||||||
|
if (starting_phase == 0 && bin >= NUM_BINS / 2) {
|
||||||
|
starting_phase = carg(Bins[bin].XP_gain);
|
||||||
|
#if DEBUG_PS
|
||||||
|
printf("starting_phase %.3f\n", starting_phase);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (PsCal && num_bin_data < NUM_BINS - 4) {
|
||||||
|
printf("PS calibration failed to fill all bins ");
|
||||||
|
for (bin = 0; bin < NUM_BINS; bin++)
|
||||||
|
if (Bins[bin].XP_count)
|
||||||
|
printf("1");
|
||||||
|
else
|
||||||
|
printf("0");
|
||||||
|
printf("\n");
|
||||||
|
Fault = 1;
|
||||||
|
}
|
||||||
|
state++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (i = 0; i < nSamples; i++) {
|
||||||
|
in_sample = amp_in_samples[i] / CLIP32 / RX1_GAIN;
|
||||||
|
mag_in = cabs(in_sample);
|
||||||
|
if (tx_to_RX1_gain < mag_in) {
|
||||||
|
tx_to_RX1_gain = mag_in;
|
||||||
|
#if DEBUG_PS > 2
|
||||||
|
printf("tx RX1 gain %.6f, max_out_mag %.6f\n", tx_to_RX1_gain, max_out_mag);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
bin = (int)(mag_in / BIN_SPACING + 0.5);
|
||||||
|
if ( ! bin)
|
||||||
|
continue;
|
||||||
|
error = fabs(mag_in - Bins[bin].table_XP);
|
||||||
|
if (error < BIN_TOL) {
|
||||||
|
out_sample = amp_out_samples[i] / CLIP32;
|
||||||
|
Bins[bin].XP_gain += out_sample / in_sample;
|
||||||
|
Bins[bin].XP_mag += mag_in;
|
||||||
|
Bins[bin].XP_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == 15) {
|
||||||
|
if (tx_state != StopCW) {
|
||||||
|
ramp_counter = ramp_count0 = MILLISECS(BLOCK_MSECS - 500);
|
||||||
|
tx_state = StopCW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 16:
|
||||||
|
#if DEBUG_PS
|
||||||
|
if (clip_tx != 0.0) {
|
||||||
|
printf ("clip output sample %.6f, mag_in %.6f, predistort %.6f\n", clip_tx, clip_mag_in, clip_aaa);
|
||||||
|
clip_tx = 0.0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (bin = 1; bin < NUM_BINS - 1; bin++) {
|
||||||
|
if (Bins[bin].XP_count > 0) { // This bin has data.
|
||||||
|
Bins[bin].XP_gain_mag = cabs(Bins[bin].XP_gain);
|
||||||
|
phase = carg(Bins[bin].XP_gain) - starting_phase;
|
||||||
|
if (phase < - M_PI)
|
||||||
|
phase += 2 * M_PI;
|
||||||
|
else if (phase > M_PI)
|
||||||
|
phase -= 2 * M_PI;
|
||||||
|
Bins[bin].XP_gain_phase = phase;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Bins[bin].XP_gain_mag = 1.0;
|
||||||
|
Bins[bin].XP_gain_phase = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Bins[0].XP_count = 1;
|
||||||
|
Bins[0].XP_mag = 0.0;
|
||||||
|
i = 1;
|
||||||
|
for (bin = 1; bin < 6; bin++) {
|
||||||
|
if (Bins[bin].XP_count) {
|
||||||
|
quadX[i] = Bins[bin].XP_mag;
|
||||||
|
quadY[i] = Bins[bin].XP_gain_mag;
|
||||||
|
quadZ[i] = Bins[bin].XP_gain_phase;
|
||||||
|
if (i == 3)
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == 3) {
|
||||||
|
polint(quadX, quadY, 0.0, &Bins[0].XP_gain_mag);
|
||||||
|
polint(quadX, quadZ, 0.0, &Bins[0].XP_gain_phase);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Bins[0].XP_gain_mag = 1.0;
|
||||||
|
Bins[0].XP_gain_phase = 0.0;
|
||||||
|
Fault = 1;
|
||||||
|
if (DEBUG_PS || PsCal)
|
||||||
|
printf("Failure to find G0 at zero\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Bins[NUM_BINS - 1].XP_count = 1;
|
||||||
|
Bins[NUM_BINS - 1].XP_mag = 1.0;
|
||||||
|
i = 3;
|
||||||
|
for (bin = NUM_BINS - 2; bin >= NUM_BINS - 6; bin--) {
|
||||||
|
if (Bins[bin].XP_count) {
|
||||||
|
quadX[i] = Bins[bin].XP_mag;
|
||||||
|
quadY[i] = Bins[bin].XP_gain_mag;
|
||||||
|
quadZ[i] = Bins[bin].XP_gain_phase;
|
||||||
|
if (--i == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
polint(quadX, quadY, 1.0, &Bins[NUM_BINS - 1].XP_gain_mag);
|
||||||
|
polint(quadX, quadZ, 1.0, &Bins[NUM_BINS - 1].XP_gain_phase);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Bins[NUM_BINS - 1].XP_gain_mag = 1.0;
|
||||||
|
Bins[NUM_BINS - 1].XP_gain_phase = 0.0;
|
||||||
|
Fault = 1;
|
||||||
|
if (DEBUG_PS || PsCal)
|
||||||
|
printf("Failure to find G0 at one\n");
|
||||||
|
}
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
case 17:
|
||||||
|
G0 = Bins[NUM_BINS - 1].XP_gain_mag;
|
||||||
|
num_bin_data = 0;
|
||||||
|
xp0 = -1.0;
|
||||||
|
xpg0 = -1.0;
|
||||||
|
for (bin = 0; bin < NUM_BINS; bin++) {
|
||||||
|
if (bin == NUM_BINS - 1) {
|
||||||
|
num_bin_data++;
|
||||||
|
Bins[NUM_BINS - 1].mag_P = 1.0;
|
||||||
|
Bins[NUM_BINS - 1].phase_P = - Bins[NUM_BINS - 1].XP_gain_phase;
|
||||||
|
Bins[NUM_BINS - 1].mag_X = 1.0;
|
||||||
|
}
|
||||||
|
else if (Bins[bin].XP_count) {
|
||||||
|
num_bin_data++;
|
||||||
|
Bins[bin].mag_P = G0 / Bins[bin].XP_gain_mag;
|
||||||
|
Bins[bin].phase_P = - Bins[bin].XP_gain_phase;
|
||||||
|
Bins[bin].mag_X = Bins[bin].XP_mag / Bins[bin].mag_P;
|
||||||
|
xp = Bins[bin].mag_X * Bins[bin].mag_P;
|
||||||
|
if (xp < xp0) {
|
||||||
|
if (DEBUG_PS || PsCal)
|
||||||
|
printf("xp is not monotonic at bin %d\n", bin);
|
||||||
|
Fault = 1;
|
||||||
|
}
|
||||||
|
xpg = xp * Bins[bin].XP_gain_mag;
|
||||||
|
if (xpg < xpg0) {
|
||||||
|
if (DEBUG_PS || PsCal)
|
||||||
|
printf("xpg is not monotonic at bin %d\n", bin);
|
||||||
|
Fault = 1;
|
||||||
|
}
|
||||||
|
xp0 = xp;
|
||||||
|
xpg0 = xpg;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Bins[bin].mag_P = 0.0;
|
||||||
|
Bins[bin].phase_P = 0.0;
|
||||||
|
Bins[bin].mag_X = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (num_bin_data < NUM_BINS * 7 / 10) {
|
||||||
|
if (DEBUG_PS || state == 17)
|
||||||
|
printf("Not enough data for corrections %d\n", num_bin_data);
|
||||||
|
Fault = 1;
|
||||||
|
}
|
||||||
|
if ( ! Fault) {
|
||||||
|
#if DEBUG_PS
|
||||||
|
printf("Create a new predistortion\n");
|
||||||
|
#endif
|
||||||
|
spline_P_size = 0;
|
||||||
|
for (bin = 0; bin < NUM_BINS; bin++) {
|
||||||
|
if (Bins[bin].XP_count) {
|
||||||
|
spline_P_size++;
|
||||||
|
spline_P_X[spline_P_size] = Bins[bin].mag_X;
|
||||||
|
spline_P_mag[spline_P_size] = Bins[bin].mag_P;
|
||||||
|
spline_P_phase[spline_P_size] = Bins[bin].phase_P;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spline(spline_P_X, spline_P_mag, spline_P_size, 2E30, 2E30, spline_P_mag2);
|
||||||
|
spline(spline_P_X, spline_P_phase, spline_P_size, 2E30, 2E30, spline_P_phase2);
|
||||||
|
}
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
case 18:
|
||||||
|
#if DEBUG_PS
|
||||||
|
if (Fault)
|
||||||
|
printf("Data for predistortion was rejected\n");
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
input_samples = 0;
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
if ( ! HOLD)
|
||||||
|
state = 1;
|
||||||
|
input_samples = 0;
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
isCalibrated = 0;
|
||||||
|
input_samples = 0;
|
||||||
|
quisk_set_key_down(1);
|
||||||
|
max_out_mag = 0;
|
||||||
|
tx_to_RX1_gain = 0;
|
||||||
|
starting_phase = 0;
|
||||||
|
ramp_counter = ramp_count0 = MILLISECS(10);
|
||||||
|
#if DEBUG_PS
|
||||||
|
clip_tx = 0.0;
|
||||||
|
#endif
|
||||||
|
for (bin = 0; bin < NUM_BINS; bin++) {
|
||||||
|
Bins[bin].table_XP = BIN_SPACING * bin;
|
||||||
|
Bins[bin].XP_mag = 0;
|
||||||
|
Bins[bin].XP_count = 0;
|
||||||
|
Bins[bin].XP_gain = 0;
|
||||||
|
}
|
||||||
|
tx_state = SendCW;
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
if (input_samples >= MILLISECS(400)) {
|
||||||
|
input_samples = 0;
|
||||||
|
state++;
|
||||||
|
}
|
||||||
|
if (input_samples >= MILLISECS(50) && cabs(amp_in_samples[0]) / CLIP32 / RX1_GAIN > 0.1) {
|
||||||
|
input_samples = 0;
|
||||||
|
state++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
if (input_samples >= MILLISECS(200)) {
|
||||||
|
state++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (i = 0; i < nSamples; i++) {
|
||||||
|
in_sample = amp_in_samples[i] / CLIP32;
|
||||||
|
mag_in = cabs(in_sample);
|
||||||
|
if (tx_to_RX1_gain < mag_in)
|
||||||
|
tx_to_RX1_gain = mag_in;
|
||||||
|
out_sample = amp_out_samples[i] / CLIP32;
|
||||||
|
mag_out = cabs(out_sample);
|
||||||
|
if (max_out_mag < mag_out)
|
||||||
|
max_out_mag = mag_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
#if DEBUG_PS
|
||||||
|
printf("tx RX1 gain %.5f, max_out_mag %.6f\n", tx_to_RX1_gain, max_out_mag);
|
||||||
|
#endif
|
||||||
|
if (fabs(tx_to_RX1_gain - RX1_GAIN) / RX1_GAIN > 1E-2)
|
||||||
|
printf("Measured %.6f and assumed %.6f RX1 gain differ\n", tx_to_RX1_gain, RX1_GAIN);
|
||||||
|
input_samples = 0;
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
input_samples = 0;
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
quisk_set_key_down(0);
|
||||||
|
tx_state = NothingHappens;
|
||||||
|
PsCal = 0;
|
||||||
|
if ( ! Fault)
|
||||||
|
isCalibrated = 1;
|
||||||
|
input_samples = 0;
|
||||||
|
state = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
# This is the hardware control file for my shack.
|
# This is the hardware control file for my shack.
|
||||||
# It is for the Hermes-Lite2 5 watt output which uses only the antenna tuner.
|
# It is for the Hermes-Lite2 RF1 output and Alt Rx input.
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
|
|
||||||
from hermes.quisk_hardware import Hardware as BaseHw
|
from hermes.quisk_hardware import Hardware as BaseHw
|
||||||
from n2adr import station_hardware
|
from n2adr import station_hardware
|
||||||
@@ -17,7 +14,7 @@ class Hardware(BaseHw):
|
|||||||
self.controlbox = station_hardware.ControlBox(app, conf) # Control my Station Control Box
|
self.controlbox = station_hardware.ControlBox(app, conf) # Control my Station Control Box
|
||||||
self.v2filter = station_hardware.FilterBoxV2(app, conf) # Control V2 filter box
|
self.v2filter = station_hardware.FilterBoxV2(app, conf) # Control V2 filter box
|
||||||
def open(self):
|
def open(self):
|
||||||
if False:
|
if False: # Start the station control GUI
|
||||||
from n2adr.station_hardware import StationControlGUI
|
from n2adr.station_hardware import StationControlGUI
|
||||||
self.GUI = StationControlGUI(self.application.main_frame, self, self.application, self.conf)
|
self.GUI = StationControlGUI(self.application.main_frame, self, self.application, self.conf)
|
||||||
self.GUI.Show()
|
self.GUI.Show()
|
||||||
@@ -29,12 +26,11 @@ class Hardware(BaseHw):
|
|||||||
return BaseHw.close(self)
|
return BaseHw.close(self)
|
||||||
def ChangeFilterFrequency(self, tx_freq):
|
def ChangeFilterFrequency(self, tx_freq):
|
||||||
if tx_freq and tx_freq > 0:
|
if tx_freq and tx_freq > 0:
|
||||||
|
self.v2filter.SetTxFreq(tx_freq)
|
||||||
if self.GUI:
|
if self.GUI:
|
||||||
self.GUI.SetTxFreq(tx_freq)
|
self.GUI.SetTxFreq(tx_freq)
|
||||||
self.GUI.freq_entry.ChangeValue("%.3f" % (tx_freq * 1E-6))
|
|
||||||
else:
|
else:
|
||||||
self.anttuner.SetTxFreq(tx_freq)
|
self.anttuner.SetTxFreq(tx_freq)
|
||||||
self.v2filter.SetTxFreq(tx_freq)
|
|
||||||
def ChangeFrequency(self, tx_freq, vfo_freq, source='', band='', event=None):
|
def ChangeFrequency(self, tx_freq, vfo_freq, source='', band='', event=None):
|
||||||
self.ChangeFilterFrequency(tx_freq)
|
self.ChangeFilterFrequency(tx_freq)
|
||||||
return BaseHw.ChangeFrequency(self, tx_freq, vfo_freq, source, band, event)
|
return BaseHw.ChangeFrequency(self, tx_freq, vfo_freq, source, band, event)
|
||||||
@@ -42,8 +38,6 @@ class Hardware(BaseHw):
|
|||||||
# band is a string: "60", "40", "WWV", etc.
|
# band is a string: "60", "40", "WWV", etc.
|
||||||
ret = BaseHw.ChangeBand(self, band)
|
ret = BaseHw.ChangeBand(self, band)
|
||||||
self.anttuner.ChangeBand(band)
|
self.anttuner.ChangeBand(band)
|
||||||
#self.lpfilter.ChangeBand(band)
|
|
||||||
#self.hpfilter.ChangeBand(band)
|
|
||||||
self.v2filter.ChangeBand(band)
|
self.v2filter.ChangeBand(band)
|
||||||
self.CorrectSmeter()
|
self.CorrectSmeter()
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
+4
-50
@@ -1,56 +1,10 @@
|
|||||||
# 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.
|
# 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 wx, time
|
||||||
import _quisk as QS
|
import _quisk as QS
|
||||||
|
|
||||||
class BottomWidgets: # Add extra widgets to the bottom of the screen
|
from hermes.quisk_widgets import BottomWidgets as BaseWidgets
|
||||||
|
|
||||||
|
class BottomWidgets(BaseWidgets): # Add extra widgets to the bottom of the screen
|
||||||
def __init__(self, app, hardware, conf, frame, gbs, vertBox):
|
def __init__(self, app, hardware, conf, frame, gbs, vertBox):
|
||||||
#self.config = conf
|
BaseWidgets.__init__(self, app, hardware, conf, frame, gbs, vertBox)
|
||||||
#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)
|
|
||||||
|
|||||||
+85
-123
@@ -1,9 +1,5 @@
|
|||||||
# This file supports various hardware boxes at my shack
|
# This file supports various hardware boxes at my shack
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
|
|
||||||
import sys, struct, math, socket, select, time, traceback, os
|
import sys, struct, math, socket, select, time, traceback, os
|
||||||
import wx, wx.lib.buttons
|
import wx, wx.lib.buttons
|
||||||
|
|
||||||
@@ -411,18 +407,15 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
|
|||||||
self.set_HighZ = 0
|
self.set_HighZ = 0
|
||||||
self.antnum = 0 # Antenna number 0 or 1
|
self.antnum = 0 # Antenna number 0 or 1
|
||||||
self.dipole2 = AntennaControl(app, conf) # Control the KI8BV dipole
|
self.dipole2 = AntennaControl(app, conf) # Control the KI8BV dipole
|
||||||
if False and conf.use_rx_udp == 10: # Hermes UDP protocol for Hermes-Lite2
|
path = 'TunerLCZ.txt'
|
||||||
path = 'TunerLCZ_HL2.txt'
|
|
||||||
else:
|
|
||||||
path = 'TunerLCZ.txt'
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
path = 'C:/pub/' + path
|
self.path = 'C:/pub/' + path
|
||||||
else:
|
else:
|
||||||
path = '/home/jim/pub/' + path
|
self.path = '/home/jim/pub/' + path
|
||||||
fp = open(path, "r")
|
fp = open(self.path, "r")
|
||||||
lines = fp.readlines()
|
lines = fp.readlines()
|
||||||
fp.close()
|
fp.close()
|
||||||
self.TunerLCZ = [(0, 0, 0, 0), (999111000, 0, 0, 0)] # Add dummy first and last entry.
|
self.TunerLCZ = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
freq, antL, antC, hilo = line.split()
|
freq, antL, antC, hilo = line.split()
|
||||||
freq = int(freq)
|
freq = int(freq)
|
||||||
@@ -451,7 +444,12 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
|
|||||||
pass
|
pass
|
||||||
def ChangeBand(self, band):
|
def ChangeBand(self, band):
|
||||||
pass
|
pass
|
||||||
def FindLCZ(self, tx_freq):
|
def SaveLCZ(self):
|
||||||
|
fp = open(self.path, 'w')
|
||||||
|
for tup in self.TunerLCZ:
|
||||||
|
fp.write("%12d %2d %2d %d\n" % tup)
|
||||||
|
fp.close()
|
||||||
|
def FindIndexTup(self, tx_freq):
|
||||||
i1 = 0
|
i1 = 0
|
||||||
i2 = len(self.TunerLCZ) - 1
|
i2 = len(self.TunerLCZ) - 1
|
||||||
while 1: # binary partition
|
while 1: # binary partition
|
||||||
@@ -462,20 +460,14 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
|
|||||||
i2 = i
|
i2 = i
|
||||||
if i2 - i1 <= 1:
|
if i2 - i1 <= 1:
|
||||||
break
|
break
|
||||||
# The correct setting is between i1 and i2.
|
# The correct setting is between i1 and i2, or below i1 or above i2.
|
||||||
# Choose the index that is closest in frequency.
|
# Choose the index that is closest in frequency.
|
||||||
delta1 = tx_freq - self.TunerLCZ[i1][0]
|
if abs(tx_freq - self.TunerLCZ[i1][0]) < abs(tx_freq - self.TunerLCZ[i2][0]):
|
||||||
delta2 = self.TunerLCZ[i2][0] - tx_freq
|
|
||||||
if i1 == 0: # below the first frequency
|
|
||||||
index = i1
|
index = i1
|
||||||
elif i2 == len(self.TunerLCZ) - 1: # above the last frequency
|
else:
|
||||||
index = i2
|
index = i2
|
||||||
elif delta2 < delta1: # i2 is closer
|
if DEBUG: print ("StatuinControlGUI FindIndexTup", tx_freq, i1, i2, len(self.TunerLCZ), index, self.TunerLCZ[index])
|
||||||
index = i2
|
return index, self.TunerLCZ[index]
|
||||||
else: # i1 is closer
|
|
||||||
index = i1
|
|
||||||
if DEBUG: print ("AntennaTuner FindLCZ", tx_freq, i1, i2, len(self.TunerLCZ), self.TunerLCZ[index])
|
|
||||||
return self.TunerLCZ[index]
|
|
||||||
def SetTxFreq(self, tx_freq, no_tune=False):
|
def SetTxFreq(self, tx_freq, no_tune=False):
|
||||||
#if DEBUG: print("AntennaTuner SetTxFreq", tx_freq)
|
#if DEBUG: print("AntennaTuner SetTxFreq", tx_freq)
|
||||||
self.tx_freq = tx_freq
|
self.tx_freq = tx_freq
|
||||||
@@ -484,10 +476,10 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
|
|||||||
if tx_freq < 17000000:
|
if tx_freq < 17000000:
|
||||||
if DEBUG and self.antnum == 1: print ("antnum 0")
|
if DEBUG and self.antnum == 1: print ("antnum 0")
|
||||||
self.antnum = 0
|
self.antnum = 0
|
||||||
else:
|
elif self.dipole2:
|
||||||
if DEBUG and self.antnum == 0: print ("antnum 1")
|
if DEBUG and self.antnum == 0: print ("antnum 1")
|
||||||
self.antnum = 1
|
self.antnum = 1
|
||||||
if self.antnum == 1:
|
if self.antnum == 1 and self.dipole2:
|
||||||
self.dipole2.SetTxFreq(tx_freq)
|
self.dipole2.SetTxFreq(tx_freq)
|
||||||
if not self.socket:
|
if not self.socket:
|
||||||
return
|
return
|
||||||
@@ -496,7 +488,7 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
|
|||||||
if abs(self.tune_freq - tx_freq) < 5000: # ignore small changes
|
if abs(self.tune_freq - tx_freq) < 5000: # ignore small changes
|
||||||
return
|
return
|
||||||
self.tune_freq = tx_freq
|
self.tune_freq = tx_freq
|
||||||
f, newL, newC, newH = self.FindLCZ(tx_freq)
|
index, (f, newL, newC, newH) = self.FindIndexTup(tx_freq)
|
||||||
if newH != self.set_HighZ or self.set_C != newC or self.set_L != newL:
|
if newH != self.set_HighZ or self.set_C != newC or self.set_L != newL:
|
||||||
self.set_HighZ = newH
|
self.set_HighZ = newH
|
||||||
self.set_C = newC
|
self.set_C = newC
|
||||||
@@ -521,7 +513,8 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
|
|||||||
self.timer = 0
|
self.timer = 0
|
||||||
else:
|
else:
|
||||||
self.timer = 0
|
self.timer = 0
|
||||||
self.dipole2.HeartBeat()
|
if self.dipole2:
|
||||||
|
self.dipole2.HeartBeat()
|
||||||
def Send(self):
|
def Send(self):
|
||||||
try:
|
try:
|
||||||
self.socket.send(self.want_data)
|
self.socket.send(self.want_data)
|
||||||
@@ -539,26 +532,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
|||||||
self.conf = conf
|
self.conf = conf
|
||||||
self.data_saving = False
|
self.data_saving = False
|
||||||
self.tx_freq = 0
|
self.tx_freq = 0
|
||||||
if sys.platform == "win32":
|
self.anttuner = self.hware.anttuner
|
||||||
self.filename = 'C:/pub/TunerLCZ.tmp'
|
|
||||||
else:
|
|
||||||
self.filename = '/home/jim/pub/TunerLCZ.tmp'
|
|
||||||
try:
|
|
||||||
fp = open(self.filename, "r")
|
|
||||||
except FileNotFoundError:
|
|
||||||
lines = ()
|
|
||||||
else:
|
|
||||||
lines = fp.readlines()
|
|
||||||
fp.close()
|
|
||||||
self.TunerLCZ = [(0, 0, 0, 0), (999111000, 0, 0, 0)] # Add dummy first and last entry.
|
|
||||||
for line in lines:
|
|
||||||
freq, antL, antC, hilo = line.split()
|
|
||||||
freq = int(freq)
|
|
||||||
antL = int(antL)
|
|
||||||
antC = int(antC)
|
|
||||||
hilo = int(hilo)
|
|
||||||
self.TunerLCZ.append((freq, antL, antC, hilo))
|
|
||||||
self.TunerLCZ.sort()
|
|
||||||
self.SetBackgroundColour('light steel blue')
|
self.SetBackgroundColour('light steel blue')
|
||||||
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
|
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
|
||||||
sizer = wx.GridBagSizer(hgap=5, vgap=3)
|
sizer = wx.GridBagSizer(hgap=5, vgap=3)
|
||||||
@@ -568,6 +542,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
|||||||
self.bands = []
|
self.bands = []
|
||||||
for band in ('160', '80', '60', '40', '30', '20', '17', '15', '12', '10'):
|
for band in ('160', '80', '60', '40', '30', '20', '17', '15', '12', '10'):
|
||||||
b = wx.lib.buttons.GenToggleButton(self, -1, band)
|
b = wx.lib.buttons.GenToggleButton(self, -1, band)
|
||||||
|
b.Enable(False)
|
||||||
b.SetUseFocusIndicator(False)
|
b.SetUseFocusIndicator(False)
|
||||||
b.SetBezelWidth(4)
|
b.SetBezelWidth(4)
|
||||||
self.bands.append(b)
|
self.bands.append(b)
|
||||||
@@ -604,6 +579,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
|||||||
szr = wx.BoxSizer(wx.HORIZONTAL)
|
szr = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
szr.AddSpacer(20)
|
szr.AddSpacer(20)
|
||||||
b = wx.lib.buttons.GenToggleButton(self, -1, "Key Down")
|
b = wx.lib.buttons.GenToggleButton(self, -1, "Key Down")
|
||||||
|
b.Enable(False)
|
||||||
b.SetFont(font)
|
b.SetFont(font)
|
||||||
b.SetUseFocusIndicator(False)
|
b.SetUseFocusIndicator(False)
|
||||||
b.SetBezelWidth(4)
|
b.SetBezelWidth(4)
|
||||||
@@ -611,6 +587,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
|||||||
szr.Add(b)
|
szr.Add(b)
|
||||||
szr.AddSpacer(10)
|
szr.AddSpacer(10)
|
||||||
b = wx.lib.buttons.GenToggleButton(self, -1, "FilterV2 Preamp")
|
b = wx.lib.buttons.GenToggleButton(self, -1, "FilterV2 Preamp")
|
||||||
|
b.Enable(False)
|
||||||
b.SetFont(font)
|
b.SetFont(font)
|
||||||
b.SetUseFocusIndicator(False)
|
b.SetUseFocusIndicator(False)
|
||||||
b.SetBezelWidth(4)
|
b.SetBezelWidth(4)
|
||||||
@@ -622,16 +599,31 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
|||||||
b.SetFont(font)
|
b.SetFont(font)
|
||||||
szr.Add(b, flag=wx.ALIGN_CENTER_VERTICAL)
|
szr.Add(b, flag=wx.ALIGN_CENTER_VERTICAL)
|
||||||
szr.AddSpacer(10)
|
szr.AddSpacer(10)
|
||||||
self.freq_entry = wx.TextCtrl(self, value='0000.000', style=wx.TE_PROCESS_ENTER)
|
self.freq_entry = wx.StaticText(self, -1, "0000.000")
|
||||||
|
#self.freq_entry = wx.TextCtrl(self, value='0000.000', style=wx.TE_PROCESS_ENTER)
|
||||||
self.freq_entry.SetFont(font)
|
self.freq_entry.SetFont(font)
|
||||||
self.Bind(wx.EVT_TEXT_ENTER, self.OnFreqEntry, self.freq_entry)
|
#self.Bind(wx.EVT_TEXT_ENTER, self.OnFreqEntry, self.freq_entry)
|
||||||
szr.Add(self.freq_entry)
|
szr.Add(self.freq_entry, flag=wx.ALIGN_CENTER_VERTICAL)
|
||||||
szr.AddSpacer(10)
|
szr.AddSpacer(10)
|
||||||
b = wx.lib.buttons.GenButton(self, -1, "Search Freq")
|
b = wx.lib.buttons.GenButton(self, -1, "Freq-")
|
||||||
b.SetUseFocusIndicator(False)
|
b.SetUseFocusIndicator(False)
|
||||||
b.SetBezelWidth(4)
|
b.SetBezelWidth(4)
|
||||||
b.SetFont(font)
|
b.SetFont(font)
|
||||||
self.Bind(wx.EVT_BUTTON, self.OnButtonSearch, b)
|
self.Bind(wx.EVT_BUTTON, self.OnButtonDown, b)
|
||||||
|
szr.Add(b)
|
||||||
|
szr.AddSpacer(10)
|
||||||
|
b = wx.lib.buttons.GenButton(self, -1, "Freq+")
|
||||||
|
b.SetUseFocusIndicator(False)
|
||||||
|
b.SetBezelWidth(4)
|
||||||
|
b.SetFont(font)
|
||||||
|
self.Bind(wx.EVT_BUTTON, self.OnButtonUp, b)
|
||||||
|
szr.Add(b)
|
||||||
|
szr.AddSpacer(10)
|
||||||
|
b = wx.lib.buttons.GenButton(self, -1, "Delete")
|
||||||
|
b.SetUseFocusIndicator(False)
|
||||||
|
b.SetBezelWidth(4)
|
||||||
|
b.SetFont(font)
|
||||||
|
self.Bind(wx.EVT_BUTTON, self.OnButtonDelete, b)
|
||||||
szr.Add(b)
|
szr.Add(b)
|
||||||
sizer.Add(szr, pos=(row, 0), span=(1, 3))
|
sizer.Add(szr, pos=(row, 0), span=(1, 3))
|
||||||
if frame is None:
|
if frame is None:
|
||||||
@@ -647,46 +639,22 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
|||||||
self.Bind(wx.EVT_TIMER, self.HeartBeat)
|
self.Bind(wx.EVT_TIMER, self.HeartBeat)
|
||||||
self.timer.Start(100)
|
self.timer.Start(100)
|
||||||
def SetTxFreq(self, tx_freq):
|
def SetTxFreq(self, tx_freq):
|
||||||
#print("StationControlGUI SetTxFreq", tx_freq)
|
tx_freq = ((tx_freq + 500) // 1000) * 1000
|
||||||
self.tx_freq = tx_freq
|
self.tx_freq = tx_freq
|
||||||
index, tup = self.FindIndexTup(tx_freq)
|
anttuner = self.anttuner
|
||||||
|
index, tup = anttuner.FindIndexTup(tx_freq)
|
||||||
self.sliderL.SetValue(tup[1])
|
self.sliderL.SetValue(tup[1])
|
||||||
self.sliderC.SetValue(tup[2])
|
self.sliderC.SetValue(tup[2])
|
||||||
self.btnHiLo.SetValue(tup[3])
|
self.btnHiLo.SetValue(tup[3])
|
||||||
anttuner = self.hware.anttuner
|
|
||||||
fff, anttuner.set_L, anttuner.set_C, anttuner.set_HighZ = tup
|
fff, anttuner.set_L, anttuner.set_C, anttuner.set_HighZ = tup
|
||||||
if tx_freq < 17000000:
|
if tx_freq < 17000000:
|
||||||
anttuner.antnum = 0
|
anttuner.antnum = 0
|
||||||
else:
|
elif anttuner.dipole2:
|
||||||
anttuner.antnum = 1
|
anttuner.antnum = 1
|
||||||
if anttuner.antnum == 1:
|
if anttuner.antnum == 1 and anttuner.dipole2:
|
||||||
anttuner.dipole2.SetTxFreq(tx_freq)
|
anttuner.dipole2.SetTxFreq(tx_freq)
|
||||||
anttuner.WantData()
|
anttuner.WantData()
|
||||||
def FindIndexTup(self, tx_freq):
|
self.freq_entry.SetLabel("%.3f" % (tx_freq * 1E-6))
|
||||||
i1 = 0
|
|
||||||
i2 = len(self.TunerLCZ) - 1
|
|
||||||
while 1: # binary partition
|
|
||||||
i = (i1 + i2) // 2
|
|
||||||
if self.TunerLCZ[i][0] < tx_freq:
|
|
||||||
i1 = i
|
|
||||||
else:
|
|
||||||
i2 = i
|
|
||||||
if i2 - i1 <= 1:
|
|
||||||
break
|
|
||||||
# The correct setting is between i1 and i2.
|
|
||||||
# Choose the index that is closest in frequency.
|
|
||||||
delta1 = tx_freq - self.TunerLCZ[i1][0]
|
|
||||||
delta2 = self.TunerLCZ[i2][0] - tx_freq
|
|
||||||
if i1 == 0: # below the first frequency
|
|
||||||
index = i1
|
|
||||||
elif i2 == len(self.TunerLCZ) - 1: # above the last frequency
|
|
||||||
index = i2
|
|
||||||
elif delta2 < delta1: # i2 is closer
|
|
||||||
index = i2
|
|
||||||
else: # i1 is closer
|
|
||||||
index = i1
|
|
||||||
if DEBUG: print ("StatuinControlGUI FindLCZ", tx_freq, i1, i2, len(self.TunerLCZ), index, self.TunerLCZ[index])
|
|
||||||
return index, self.TunerLCZ[index]
|
|
||||||
def OnBtnClose(self, event):
|
def OnBtnClose(self, event):
|
||||||
self.hware.anttuner.close()
|
self.hware.anttuner.close()
|
||||||
self.hware.controlbox.close()
|
self.hware.controlbox.close()
|
||||||
@@ -730,16 +698,15 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
|||||||
f = ((f + 500) // 1000) * 1000
|
f = ((f + 500) // 1000) * 1000
|
||||||
if self.hware.v2filter:
|
if self.hware.v2filter:
|
||||||
self.hware.v2filter.SetTxFreq(f)
|
self.hware.v2filter.SetTxFreq(f)
|
||||||
self.hware.anttuner.SetTxFreq(f, no_tune=True)
|
self.anttuner.SetTxFreq(f, no_tune=True)
|
||||||
self.freq_entry.ChangeValue("%.3f" % (f * 1E-6))
|
self.freq_entry.ChangeValue("%.3f" % (f * 1E-6))
|
||||||
self.SetTxFreq(f)
|
self.SetTxFreq(f)
|
||||||
def OnButtonHiLo(self, event):
|
def OnButtonHiLo(self, event):
|
||||||
anttuner = self.hware.anttuner
|
|
||||||
if self.btnHiLo.GetValue():
|
if self.btnHiLo.GetValue():
|
||||||
anttuner.set_HighZ = 1
|
self.anttuner.set_HighZ = 1
|
||||||
else:
|
else:
|
||||||
anttuner.set_HighZ = 0
|
self.anttuner.set_HighZ = 0
|
||||||
anttuner.WantData()
|
self.anttuner.WantData()
|
||||||
def OnFreqEntry(self, event=None):
|
def OnFreqEntry(self, event=None):
|
||||||
freq = self.freq_entry.GetValue()
|
freq = self.freq_entry.GetValue()
|
||||||
freq = float(freq) * 1E6
|
freq = float(freq) * 1E6
|
||||||
@@ -751,11 +718,27 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
|||||||
freq = int(freq + 0.1)
|
freq = int(freq + 0.1)
|
||||||
self.tx_freq = freq
|
self.tx_freq = freq
|
||||||
self.SetTxFreq(self.tx_freq)
|
self.SetTxFreq(self.tx_freq)
|
||||||
|
def OnButtonDown(self, event):
|
||||||
|
index, tup = self.anttuner.FindIndexTup(self.tx_freq)
|
||||||
|
index -= 1
|
||||||
|
if index >= 0:
|
||||||
|
self.SetTxFreq(self.anttuner.TunerLCZ[index][0])
|
||||||
|
def OnButtonUp(self, event):
|
||||||
|
index, tup = self.anttuner.FindIndexTup(self.tx_freq)
|
||||||
|
index += 1
|
||||||
|
if index < len(self.anttuner.TunerLCZ):
|
||||||
|
self.SetTxFreq(self.anttuner.TunerLCZ[index][0])
|
||||||
|
def OnButtonDelete(self, event):
|
||||||
|
index, tup = self.anttuner.FindIndexTup(self.tx_freq)
|
||||||
|
if self.tx_freq == self.anttuner.TunerLCZ[index][0]:
|
||||||
|
del self.anttuner.TunerLCZ[index]
|
||||||
|
if index > 0:
|
||||||
|
self.SetTxFreq(self.anttuner.TunerLCZ[index - 1][0])
|
||||||
|
elif len(self.anttuner.TunerLCZ) > 0:
|
||||||
|
self.SetTxFreq(self.anttuner.TunerLCZ[0][0])
|
||||||
|
else:
|
||||||
|
self.SetTxFreq(0)
|
||||||
def OnButtonSave(self, event):
|
def OnButtonSave(self, event):
|
||||||
freq = self.freq_entry.GetValue()
|
|
||||||
freq = float(freq) * 1E6
|
|
||||||
freq = int(freq + 0.1)
|
|
||||||
self.tx_freq = freq
|
|
||||||
L = self.sliderL.GetValue()
|
L = self.sliderL.GetValue()
|
||||||
C = self.sliderC.GetValue()
|
C = self.sliderC.GetValue()
|
||||||
if self.btnHiLo.GetValue():
|
if self.btnHiLo.GetValue():
|
||||||
@@ -763,22 +746,19 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
|||||||
else:
|
else:
|
||||||
hilo = 0
|
hilo = 0
|
||||||
tup = (self.tx_freq, L, C, hilo)
|
tup = (self.tx_freq, L, C, hilo)
|
||||||
index, t = self.FindIndexTup(self.tx_freq)
|
index, t = self.anttuner.FindIndexTup(self.tx_freq)
|
||||||
if self.tx_freq == self.TunerLCZ[index][0]:
|
if self.tx_freq == self.anttuner.TunerLCZ[index][0]:
|
||||||
self.TunerLCZ[index] = tup
|
self.anttuner.TunerLCZ[index] = tup
|
||||||
else:
|
else:
|
||||||
self.TunerLCZ.append(tup)
|
self.anttuner.TunerLCZ.append(tup)
|
||||||
self.TunerLCZ.sort()
|
self.anttuner.TunerLCZ.sort()
|
||||||
fp = open(self.filename, 'w')
|
self.anttuner.SaveLCZ()
|
||||||
for tup in self.TunerLCZ[1:-1]: # Throw away dummy entries
|
|
||||||
fp.write("%12d %2d %2d %d\n" % tup)
|
|
||||||
fp.close()
|
|
||||||
def OnSliderL(self, event=None):
|
def OnSliderL(self, event=None):
|
||||||
self.hware.anttuner.set_L = self.sliderL.GetValue()
|
self.anttuner.set_L = self.sliderL.GetValue()
|
||||||
self.hware.anttuner.WantData()
|
self.anttuner.WantData()
|
||||||
def OnSliderC(self, event=None):
|
def OnSliderC(self, event=None):
|
||||||
self.hware.anttuner.set_C = self.sliderC.GetValue()
|
self.anttuner.set_C = self.sliderC.GetValue()
|
||||||
self.hware.anttuner.WantData()
|
self.anttuner.WantData()
|
||||||
def OnBtnV2Preamp(self, event):
|
def OnBtnV2Preamp(self, event):
|
||||||
if self.hware.v2filter:
|
if self.hware.v2filter:
|
||||||
self.hware.v2filter.SetPreamp(event.GetEventObject().GetValue())
|
self.hware.v2filter.SetPreamp(event.GetEventObject().GetValue())
|
||||||
@@ -1072,21 +1052,3 @@ class AT200PC: # Control an AT-200PC autotuner made by LDG
|
|||||||
self.set_C += 1
|
self.set_C += 1
|
||||||
elif text == 'C-':
|
elif text == 'C-':
|
||||||
self.set_C -= 1
|
self.set_C -= 1
|
||||||
|
|
||||||
class App(wx.App):
|
|
||||||
def OnInit(self):
|
|
||||||
if sys.path[0] != "'.'": # Make sure the current working directory is on path
|
|
||||||
sys.path.insert(0, '.')
|
|
||||||
import quisk_conf_defaults as conf
|
|
||||||
import quisk_hardware_model as hardware
|
|
||||||
self.bottom_widgets = None
|
|
||||||
hardware.anttuner = AntennaTuner(self, conf) # Control the antenna tuner
|
|
||||||
hardware.v2filter = FilterBoxV2(self, conf) # Control V2 filter box
|
|
||||||
hardware.controlbox = ControlBox(self, conf) # Control my Station Control Box
|
|
||||||
self.main_frame = frame = StationControlGUI (None, hardware, self, conf)
|
|
||||||
frame.Open()
|
|
||||||
frame.Show()
|
|
||||||
return True
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
App().MainLoop()
|
|
||||||
|
|||||||
+21
-3
@@ -1,7 +1,25 @@
|
|||||||
|
MODULE_NAME := perseus
|
||||||
|
OBJDIR := ../build/local
|
||||||
|
|
||||||
perseus2:
|
CFLAGS := $(shell python3 ../makefil.py --cflags)
|
||||||
python2 setup.py build_ext --force --inplace
|
LDFLAGS := $(shell python3 ../makefil.py --ldflags)
|
||||||
|
EXTENSION_SUFFIX := $(shell python3 ../makefil.py --suffix)
|
||||||
|
TARGET := $(MODULE_NAME)$(EXTENSION_SUFFIX)
|
||||||
|
|
||||||
perseus3:
|
# Default target: Link the shared library object
|
||||||
|
$(TARGET): perseus.o $(OBJDIR)/import_quisk_api.o
|
||||||
|
$(CC) -o $@ perseus.o $(LDFLAGS) $(OBJDIR)/import_quisk_api.o
|
||||||
|
|
||||||
|
# Compile the C source files into object files
|
||||||
|
perseus.o: perseus.c ../quisk.h ../filter.h
|
||||||
|
$(CC) -o $@ -c $< $(CFLAGS) -I..
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
-rm -f *.o
|
||||||
|
-rm -f $(TARGET)
|
||||||
|
|
||||||
|
.PHONY: perseus-old
|
||||||
|
perseus-old:
|
||||||
python3 setup.py build_ext --force --inplace
|
python3 setup.py build_ext --force --inplace
|
||||||
|
|
||||||
|
|||||||
@@ -297,11 +297,11 @@ static PyObject * open_device(PyObject * self, PyObject * args)
|
|||||||
|
|
||||||
|
|
||||||
main_cleanup:
|
main_cleanup:
|
||||||
return PyString_FromString("ERROR");
|
return PyUnicode_FromString("ERROR");
|
||||||
|
|
||||||
exit_success:
|
exit_success:
|
||||||
|
|
||||||
return PyString_FromString(buf128);
|
return PyUnicode_FromString(buf128);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from quisk_hardware_model import Hardware as BaseHardware
|
|||||||
DEBUG = 1
|
DEBUG = 1
|
||||||
|
|
||||||
# Define the name of the hardware and the items on the hardware screen (see quisk_conf_defaults.py):
|
# 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.
|
################ Receivers PerseusSDR, The PerseusSDR interface to multiple SDRs.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'perseuspkg/quisk_hardware.py'
|
#hardware_file_name = 'perseuspkg/quisk_hardware.py'
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=61.0.0"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "quisk"
|
||||||
|
version = "4.2.53"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
description = 'QUISK is a Software Defined Radio (SDR) transceiver that can control various radio hardware.'
|
||||||
|
license = "GPL-2.0-or-later"
|
||||||
|
license-files = ["license.txt"]
|
||||||
|
authors = [
|
||||||
|
{name = "James C. Ahlstrom", email="jahlstr@gmail.com"}
|
||||||
|
]
|
||||||
|
keywords = ['quisk', 'sdr', 'software defined radio']
|
||||||
|
classifiers = [
|
||||||
|
'Development Status :: 6 - Mature',
|
||||||
|
'Environment :: X11 Applications',
|
||||||
|
'Environment :: Win32 (MS Windows)',
|
||||||
|
'Intended Audience :: End Users/Desktop',
|
||||||
|
'Natural Language :: English',
|
||||||
|
'Operating System :: POSIX :: Linux',
|
||||||
|
'Operating System :: Microsoft :: Windows',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: C',
|
||||||
|
'Topic :: Communications :: Ham Radio',
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.gui-scripts]
|
||||||
|
quisk = "quisk.quisk:main"
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Homepage = "https://james.ahlstrom.name/quisk/index.html"
|
||||||
|
Documentation = "https://james.ahlstrom.name/quisk/docs.html"
|
||||||
|
Repository = "https://github.com/jimahlstrom/quisk"
|
||||||
|
Changelog = "https://james.ahlstrom.name/quisk/CHANGELOG.txt"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["."]
|
||||||
|
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <complex.h> // Use native C99 complex type for fftw3
|
#include <complex.h> // Use native C99 complex type for fftw3
|
||||||
|
#include <string.h>
|
||||||
#include <fftw3.h>
|
#include <fftw3.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -151,7 +152,6 @@ static int waterfall_scroll_mode = 1; // draw the first lines multiple times
|
|||||||
static int quisk_use_sidetone; // is there a sidetone volume control?
|
static int quisk_use_sidetone; // is there a sidetone volume control?
|
||||||
static int hl2_txbuf_errors; // errors in the Hermes-Lite2 Tx buffer
|
static int hl2_txbuf_errors; // errors in the Hermes-Lite2 Tx buffer
|
||||||
static int hl2_txbuf_state; // state machine for errors in the Hermes-Lite2 Tx buffer
|
static int hl2_txbuf_state; // state machine for errors in the Hermes-Lite2 Tx buffer
|
||||||
static int freedv_monitor; // pass the Freedv audio to the speakers instead of to the demodulator
|
|
||||||
static int softrock_correct_freq; // gain and phase corrections for SoftRock
|
static int softrock_correct_freq; // gain and phase corrections for SoftRock
|
||||||
static double softrock_correct_gain;
|
static double softrock_correct_gain;
|
||||||
static double softrock_correct_phase;
|
static double softrock_correct_phase;
|
||||||
@@ -219,14 +219,17 @@ static unsigned char quisk_hermes_to_pc[5 * 4]; // data received from the Herme
|
|||||||
static unsigned char quisk_hermeslite_response[5]; // response from Hermes-Lite commands
|
static unsigned char quisk_hermeslite_response[5]; // response from Hermes-Lite commands
|
||||||
unsigned int quisk_hermes_code_version = -1; // code version returned by the Hermes hardware
|
unsigned int quisk_hermes_code_version = -1; // code version returned by the Hermes hardware
|
||||||
unsigned int quisk_hermes_board_id = -1; // board ID returned by the Hermes hardware
|
unsigned int quisk_hermes_board_id = -1; // board ID returned by the Hermes hardware
|
||||||
static double hermes_temperature; // average temperature
|
static double hpsdr_AIN1_avg; // average forward power
|
||||||
static double hermes_fwd_power; // average forward power
|
static double hpsdr_AIN1_peak; // peak forward power
|
||||||
static double hermes_rev_power; // average reverse power
|
static double hpsdr_AIN2_avg; // average reverse power
|
||||||
static double hermes_fwd_peak; // peak forward power
|
static double hpsdr_AIN2_peak; // peak reverse power
|
||||||
static double hermes_rev_peak; // peak reverse power
|
static double hpsdr_AIN3; // average power amp current
|
||||||
static double hermes_pa_current; // average power amp current
|
static double hpsdr_AIN4;
|
||||||
static int hermes_count_temperature; // number of temperature samples
|
static double hpsdr_AIN5; // average temperature
|
||||||
static int hermes_count_current; // number of current samples
|
static double hpsdr_AIN6;
|
||||||
|
static int hpsdr_AIN3_count; // number of current samples
|
||||||
|
static int hpsdr_AIN4_count; // number of current samples
|
||||||
|
static int hpsdr_AIN5_count; // number of current samples
|
||||||
static int hardware_ptt; // hardware PTT switch
|
static int hardware_ptt; // hardware PTT switch
|
||||||
|
|
||||||
int quisk_hardware_cwkey; // hardware CW key from UDP or USB
|
int quisk_hardware_cwkey; // hardware CW key from UDP or USB
|
||||||
@@ -569,15 +572,10 @@ void QuiskWavClose(struct QuiskWav * hWav)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// These are used for digital voice codecs
|
void quisk_dvoice_freedv(void) // Not used.
|
||||||
ty_dvoice_codec_rx pt_quisk_freedv_rx;
|
|
||||||
ty_dvoice_codec_tx pt_quisk_freedv_tx;
|
|
||||||
|
|
||||||
void quisk_dvoice_freedv(ty_dvoice_codec_rx rx, ty_dvoice_codec_tx tx)
|
|
||||||
{
|
{
|
||||||
pt_quisk_freedv_rx = rx;
|
|
||||||
pt_quisk_freedv_tx = tx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static int fFracDecim(double * dSamples, int nSamples, double fdecim)
|
static int fFracDecim(double * dSamples, int nSamples, double fdecim)
|
||||||
{ // fractional decimation by fdecim > 1.0
|
{ // fractional decimation by fdecim > 1.0
|
||||||
@@ -1851,10 +1849,8 @@ static int quisk_process_decimate(complex double * cSamples, int nSamples, int b
|
|||||||
static int quisk_process_demodulate(complex double * cSamples, double * dsamples, int nSamples, int bank, int nFilter, rx_mode_type rx_mode)
|
static int quisk_process_demodulate(complex double * cSamples, double * dsamples, int nSamples, int bank, int nFilter, rx_mode_type rx_mode)
|
||||||
{ // Changes here will require changes to get_filter_rate();
|
{ // Changes here will require changes to get_filter_rate();
|
||||||
int i;
|
int i;
|
||||||
bool isLSB;
|
|
||||||
complex double cx;
|
complex double cx;
|
||||||
double d, di, dd;
|
double d, di, dd;
|
||||||
static struct AgcState Agc1 = {0.3, 16000, 0}, Agc2 = {0.3, 16000, 0};
|
|
||||||
//static int count=0;
|
//static int count=0;
|
||||||
//static double phase=0;
|
//static double phase=0;
|
||||||
static struct stStorage {
|
static struct stStorage {
|
||||||
@@ -2090,6 +2086,7 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
|
|||||||
MeasureSquelch[bank].squelch_active = MeasureSquelch[bank].squelch < squelch_level;
|
MeasureSquelch[bank].squelch_active = MeasureSquelch[bank].squelch < squelch_level;
|
||||||
break;
|
break;
|
||||||
case DGT_U: // digital mode DGT-U at 48 ksps
|
case DGT_U: // digital mode DGT-U at 48 ksps
|
||||||
|
case FDV_U:
|
||||||
if (filter_bandwidth[nFilter] < DGT_NARROW_FREQ) { // filter at 6 ksps
|
if (filter_bandwidth[nFilter] < DGT_NARROW_FREQ) { // filter at 6 ksps
|
||||||
quisk_filter_srate = quisk_decim_srate / 8;
|
quisk_filter_srate = quisk_decim_srate / 8;
|
||||||
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &Storage[bank].HalfBand5);
|
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &Storage[bank].HalfBand5);
|
||||||
@@ -2116,6 +2113,7 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DGT_L: // digital mode DGT-L
|
case DGT_L: // digital mode DGT-L
|
||||||
|
case FDV_L:
|
||||||
if (filter_bandwidth[nFilter] < DGT_NARROW_FREQ) { // filter at 6 ksps
|
if (filter_bandwidth[nFilter] < DGT_NARROW_FREQ) { // filter at 6 ksps
|
||||||
quisk_filter_srate = quisk_decim_srate / 8;
|
quisk_filter_srate = quisk_decim_srate / 8;
|
||||||
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &Storage[bank].HalfBand5);
|
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &Storage[bank].HalfBand5);
|
||||||
@@ -2154,102 +2152,6 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FDV_U: // digital voice
|
|
||||||
case FDV_L: // Extra modes added by Dave Roberts, G8KBB, June 2020. Modified by N2ADR 2024.
|
|
||||||
quisk_check_freedv_mode();
|
|
||||||
// current coding assumes input rate n_modem_sample_rate is 8000 or 48000.
|
|
||||||
// decimate:
|
|
||||||
switch (n_modem_sample_rate) {
|
|
||||||
case 8000:
|
|
||||||
quisk_filter_srate = quisk_decim_srate / 6;
|
|
||||||
nSamples = quisk_cDecimate(cSamples, nSamples, &Storage[bank].filtDecim48to16, 3);
|
|
||||||
nSamples = quisk_cDecimate(cSamples, nSamples, &Storage[bank].filtDecim16to8, 2);
|
|
||||||
break;
|
|
||||||
case 48000:
|
|
||||||
quisk_filter_srate = quisk_decim_srate;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
quisk_filter_srate = quisk_decim_srate / 6;
|
|
||||||
QuiskPrintf("Bad FreeDV modem rate in quisk_process_demodulate\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// filter and demodulate to mono audio:
|
|
||||||
isLSB = (rx_mode == FDV_L);
|
|
||||||
complex double cBuf[nSamples];
|
|
||||||
for (i = 0; i < nSamples; i++) {
|
|
||||||
cx = cRxFilterOut(cSamples[i], bank, nFilter);
|
|
||||||
if (isLSB)
|
|
||||||
dd = creal(cx) + cimag(cx);
|
|
||||||
else
|
|
||||||
dd = creal(cx) - cimag(cx);
|
|
||||||
if(bank == 0) {
|
|
||||||
measure_audio_sum += dd * dd;
|
|
||||||
measure_audio_count += 1;
|
|
||||||
}
|
|
||||||
cBuf[i] = dd;
|
|
||||||
}
|
|
||||||
// Perhaps decimate by an additional fraction
|
|
||||||
if (quisk_decim_srate != 48000) {
|
|
||||||
dd = quisk_decim_srate / 48000.0;
|
|
||||||
nSamples = cFracDecim(cBuf, nSamples, dd);
|
|
||||||
quisk_decim_srate = 48000;
|
|
||||||
}
|
|
||||||
if (bank == 0)
|
|
||||||
process_agc(&Agc1, cBuf, nSamples, 0);
|
|
||||||
else
|
|
||||||
process_agc(&Agc2, cBuf, nSamples, 0);
|
|
||||||
// pass data decimated down to n_modem_sample_rate
|
|
||||||
// return data is at n_speech_sample_rate
|
|
||||||
#if 0
|
|
||||||
static double dmax_in = 0;
|
|
||||||
for (i = 0; i < nSamples; i++) {
|
|
||||||
dd = fabs(creal(cBuf[i])) / CLIP16;
|
|
||||||
if (dd > dmax_in)
|
|
||||||
dmax_in = dd;
|
|
||||||
}
|
|
||||||
if (bank == 0 && measure_audio_count >= quisk_filter_srate * measure_audio_time) {
|
|
||||||
//printf("Max sample in %.0lf\n", dmax_in);
|
|
||||||
dmax_in = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (nSamples > n_max_modem_samples) {
|
|
||||||
printf ("FreeDV: nSamples %d exceeds max_modem_samples %d\n", nSamples, n_max_modem_samples);
|
|
||||||
nSamples = n_max_modem_samples;
|
|
||||||
}
|
|
||||||
short sBuf[nSamples];
|
|
||||||
for (i = 0; i < nSamples; i++)
|
|
||||||
sBuf[i] = (short)(creal(cBuf[i]) / CLIP16);
|
|
||||||
if (freedv_monitor) {
|
|
||||||
for (i = 0; i < nSamples; i++)
|
|
||||||
dsamples[i] = (float)sBuf[i] * CLIP16;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (pt_quisk_freedv_rx)
|
|
||||||
nSamples = (* pt_quisk_freedv_rx)(sBuf, dsamples, nSamples, bank);
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
static double dmax_out = 0;
|
|
||||||
for (i = 0; i < nSamples; i++) {
|
|
||||||
dd = fabs(dsamples[i]) / CLIP16;
|
|
||||||
if (dd > dmax_out)
|
|
||||||
dmax_out = dd;
|
|
||||||
}
|
|
||||||
if (bank == 0 && measure_audio_count >= quisk_filter_srate * measure_audio_time) {
|
|
||||||
//printf(" Max sample out %.0lf\n", dmax_out);
|
|
||||||
dmax_out = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// current coding assumes output rate n_speech_sample_rate is 8000 or 16000
|
|
||||||
switch (n_speech_sample_rate) {
|
|
||||||
case 8000:
|
|
||||||
nSamples = quisk_dInterpolate(dsamples, nSamples, &Storage[bank].filtAudio24p3, 3);
|
|
||||||
nSamples = quisk_dInterp2HB45(dsamples, nSamples, &Storage[bank].HalfBand7);
|
|
||||||
break;
|
|
||||||
case 16000:
|
|
||||||
nSamples = quisk_dInterpolate(dsamples, nSamples, &Storage[bank].filtAudio24p3, 3);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (bank == 0 && measure_audio_count >= quisk_filter_srate * measure_audio_time) {
|
if (bank == 0 && measure_audio_count >= quisk_filter_srate * measure_audio_time) {
|
||||||
measured_audio = sqrt(measure_audio_sum / measure_audio_count) / CLIP32 * 1e6;
|
measured_audio = sqrt(measure_audio_sum / measure_audio_count) / CLIP32 * 1e6;
|
||||||
@@ -2544,7 +2446,8 @@ int quisk_process_samples(complex double * cSamples, int nSamples)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NoiseBlanker(cSamples, nSamples);
|
if ( ! quisk_is_key_down())
|
||||||
|
NoiseBlanker(cSamples, nSamples);
|
||||||
|
|
||||||
// Put samples into the fft input array.
|
// Put samples into the fft input array.
|
||||||
// Thanks to WB4JFI for the code to add a third FFT buffer, July 2010.
|
// Thanks to WB4JFI for the code to add a third FFT buffer, July 2010.
|
||||||
@@ -2756,7 +2659,7 @@ int quisk_process_samples(complex double * cSamples, int nSamples)
|
|||||||
quisk_decim_srate = 48000;
|
quisk_decim_srate = 48000;
|
||||||
}
|
}
|
||||||
// Process the Rx path with the WDSP library
|
// Process the Rx path with the WDSP library
|
||||||
nSamples = wdspFexchange0(QUISK_WDSP_RX, (double *)cSamples, nSamples);
|
nSamples = wdspFexchange0(QUISK_WDSP_RX, cSamples, nSamples);
|
||||||
|
|
||||||
// Interpolate the samples from 48000 sps to the play rate.
|
// Interpolate the samples from 48000 sps to the play rate.
|
||||||
switch (quisk_sound_state.playback_rate / 48000) {
|
switch (quisk_sound_state.playback_rate / 48000) {
|
||||||
@@ -2784,9 +2687,6 @@ start_agc:
|
|||||||
if (rxMode == EXT || rxMode == DGT_IQ) { // Ext and DGT-IQ stereo sound
|
if (rxMode == EXT || rxMode == DGT_IQ) { // Ext and DGT-IQ stereo sound
|
||||||
process_agc(&Agc1, cSamples, nSamples, 1);
|
process_agc(&Agc1, cSamples, nSamples, 1);
|
||||||
}
|
}
|
||||||
else if (rxMode == FDV_U || rxMode == FDV_L) { // Agc already done
|
|
||||||
;
|
|
||||||
}
|
|
||||||
else if (split_rxtx || multirx_play_channel >= 0) { // separate AGC for left and right channels
|
else if (split_rxtx || multirx_play_channel >= 0) { // separate AGC for left and right channels
|
||||||
for (i = 0; i < nSamples; i++) {
|
for (i = 0; i < nSamples; i++) {
|
||||||
orig_cSamples[i] = cimag(cSamples[i]);
|
orig_cSamples[i] = cimag(cSamples[i]);
|
||||||
@@ -2944,6 +2844,8 @@ static PyObject * get_filter_rate(PyObject * self, PyObject * args)
|
|||||||
break;
|
break;
|
||||||
case DGT_U: // digital modes DGT-*
|
case DGT_U: // digital modes DGT-*
|
||||||
case DGT_L:
|
case DGT_L:
|
||||||
|
case FDV_U: // digital voice
|
||||||
|
case FDV_L:
|
||||||
if (bandwidth < DGT_NARROW_FREQ)
|
if (bandwidth < DGT_NARROW_FREQ)
|
||||||
filter_srate = decim_srate / 8;
|
filter_srate = decim_srate / 8;
|
||||||
else
|
else
|
||||||
@@ -2952,10 +2854,6 @@ static PyObject * get_filter_rate(PyObject * self, PyObject * args)
|
|||||||
case DGT_IQ: // digital mode at 48 ksps
|
case DGT_IQ: // digital mode at 48 ksps
|
||||||
filter_srate = decim_srate;
|
filter_srate = decim_srate;
|
||||||
break;
|
break;
|
||||||
case FDV_U: // digital voice
|
|
||||||
case FDV_L:
|
|
||||||
filter_srate = n_modem_sample_rate;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
//QuiskPrintf("Filter rate %d\n", filter_srate);
|
//QuiskPrintf("Filter rate %d\n", filter_srate);
|
||||||
return PyInt_FromLong(filter_srate);
|
return PyInt_FromLong(filter_srate);
|
||||||
@@ -3195,14 +3093,13 @@ static PyObject * set_params(PyObject * self, PyObject * args, PyObject * keywds
|
|||||||
{ /* Call with keyword arguments ONLY; change local parameters */
|
{ /* Call with keyword arguments ONLY; change local parameters */
|
||||||
static char * kwlist[] = {"quisk_is_vna", "rx_bytes", "rx_endian", "read_error", "clip",
|
static char * kwlist[] = {"quisk_is_vna", "rx_bytes", "rx_endian", "read_error", "clip",
|
||||||
"bscope_bytes", "bscope_endian", "bscope_size", "bandscopeScale", "hermes_pause",
|
"bscope_bytes", "bscope_endian", "bscope_size", "bandscopeScale", "hermes_pause",
|
||||||
"freedv_monitor", NULL} ;
|
NULL} ;
|
||||||
int i, nbytes, read_error, clip, bscope_size, hermes_pause;
|
int i, nbytes, read_error, clip, bscope_size, hermes_pause;
|
||||||
|
|
||||||
nbytes = read_error = clip = bscope_size = hermes_pause = -1;
|
nbytes = read_error = clip = bscope_size = hermes_pause = -1;
|
||||||
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiiiiiiidii", kwlist,
|
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiiiiiiidii", kwlist,
|
||||||
&quisk_is_vna, &nbytes, &py_sample_rx_endian, &read_error, &clip,
|
&quisk_is_vna, &nbytes, &py_sample_rx_endian, &read_error, &clip,
|
||||||
&py_bscope_bytes, &py_bscope_endian, &bscope_size, &bandscopeScale, &hermes_pause,
|
&py_bscope_bytes, &py_bscope_endian, &bscope_size, &bandscopeScale, &hermes_pause))
|
||||||
&freedv_monitor))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
if (nbytes != -1) {
|
if (nbytes != -1) {
|
||||||
py_sample_rx_bytes = nbytes;
|
py_sample_rx_bytes = nbytes;
|
||||||
@@ -3243,31 +3140,43 @@ static PyObject * get_hermes_TFRC(PyObject * self, PyObject * args)
|
|||||||
|
|
||||||
if (!PyArg_ParseTuple (args, ""))
|
if (!PyArg_ParseTuple (args, ""))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (hermes_count_temperature > 0) {
|
if (hpsdr_AIN5_count > 0) {
|
||||||
hermes_temperature /= hermes_count_temperature;
|
hpsdr_AIN5 /= hpsdr_AIN5_count;
|
||||||
hermes_fwd_power /= hermes_count_temperature;
|
hpsdr_AIN1_avg /= hpsdr_AIN5_count;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
hermes_temperature = 0.0;
|
hpsdr_AIN5 = 0.0;
|
||||||
hermes_fwd_power = 0.0;
|
hpsdr_AIN1_avg = 0.0;
|
||||||
}
|
}
|
||||||
if (hermes_count_current > 0) {
|
if (hpsdr_AIN3_count > 0) {
|
||||||
hermes_rev_power /= hermes_count_current;
|
hpsdr_AIN2_avg /= hpsdr_AIN3_count;
|
||||||
hermes_pa_current /= hermes_count_current;
|
hpsdr_AIN3 /= hpsdr_AIN3_count;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
hermes_rev_power = 0.0;
|
hpsdr_AIN2_avg = 0.0;
|
||||||
hermes_pa_current = 0.0;
|
hpsdr_AIN3 = 0.0;
|
||||||
}
|
}
|
||||||
ret = Py_BuildValue("dddddd", hermes_temperature, hermes_fwd_power, hermes_rev_power, hermes_pa_current, hermes_fwd_peak, hermes_rev_peak);
|
if (hpsdr_AIN4_count > 0) {
|
||||||
hermes_temperature = 0;
|
hpsdr_AIN4 /= hpsdr_AIN4_count;
|
||||||
hermes_fwd_power = 0;
|
hpsdr_AIN6 /= hpsdr_AIN4_count;
|
||||||
hermes_rev_power = 0;
|
}
|
||||||
hermes_fwd_peak = 0;
|
else {
|
||||||
hermes_rev_peak = 0;
|
hpsdr_AIN4 = 0.0;
|
||||||
hermes_pa_current = 0;
|
hpsdr_AIN6 = 0.0;
|
||||||
hermes_count_temperature = 0;
|
}
|
||||||
hermes_count_current = 0;
|
ret = Py_BuildValue("dddddddd", hpsdr_AIN1_avg, hpsdr_AIN1_peak, hpsdr_AIN2_avg, hpsdr_AIN2_peak,
|
||||||
|
hpsdr_AIN3, hpsdr_AIN4, hpsdr_AIN5, hpsdr_AIN6);
|
||||||
|
hpsdr_AIN1_avg = 0;
|
||||||
|
hpsdr_AIN1_peak = 0;
|
||||||
|
hpsdr_AIN2_avg = 0;
|
||||||
|
hpsdr_AIN2_peak = 0;
|
||||||
|
hpsdr_AIN3 = 0;
|
||||||
|
hpsdr_AIN4 = 0;
|
||||||
|
hpsdr_AIN5 = 0;
|
||||||
|
hpsdr_AIN6 = 0;
|
||||||
|
hpsdr_AIN3_count = 0;
|
||||||
|
hpsdr_AIN4_count = 0;
|
||||||
|
hpsdr_AIN5_count = 0;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3815,19 +3724,24 @@ static int read_rx_udp10(complex double * samp) // Read samples from UDP using t
|
|||||||
quisk_set_play_state();
|
quisk_set_play_state();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(dindex == 1) { // temperature and forward power
|
else if(dindex == 1) {
|
||||||
hermes_temperature += quisk_hermes_to_pc[4] << 8 | quisk_hermes_to_pc[5];
|
hpsdr_AIN5 += quisk_hermes_to_pc[4] << 8 | quisk_hermes_to_pc[5];
|
||||||
power = quisk_hermes_to_pc[6] << 8 | quisk_hermes_to_pc[7];
|
power = quisk_hermes_to_pc[6] << 8 | quisk_hermes_to_pc[7];
|
||||||
hermes_fwd_power += power;
|
hpsdr_AIN1_avg += power;
|
||||||
hermes_fwd_peak = fmax(hermes_fwd_peak, (double)power);
|
hpsdr_AIN1_peak = fmax(hpsdr_AIN1_peak, (double)power);
|
||||||
hermes_count_temperature++;
|
hpsdr_AIN5_count++;
|
||||||
}
|
}
|
||||||
else if (dindex == 2) { // reverse power and current
|
else if (dindex == 2) {
|
||||||
power = quisk_hermes_to_pc[8] << 8 | quisk_hermes_to_pc[9];
|
power = quisk_hermes_to_pc[8] << 8 | quisk_hermes_to_pc[9];
|
||||||
hermes_rev_power += power;
|
hpsdr_AIN2_avg += power;
|
||||||
hermes_rev_peak = fmax(hermes_rev_peak, (double)power);
|
hpsdr_AIN2_peak = fmax(hpsdr_AIN2_peak, (double)power);
|
||||||
hermes_pa_current += quisk_hermes_to_pc[10] << 8 | quisk_hermes_to_pc[11];
|
hpsdr_AIN3 += quisk_hermes_to_pc[10] << 8 | quisk_hermes_to_pc[11];
|
||||||
hermes_count_current++;
|
hpsdr_AIN3_count++;
|
||||||
|
}
|
||||||
|
else if (dindex == 3) {
|
||||||
|
hpsdr_AIN4 += quisk_hermes_to_pc[12] << 8 | quisk_hermes_to_pc[13];
|
||||||
|
hpsdr_AIN6 += quisk_hermes_to_pc[14] << 8 | quisk_hermes_to_pc[15];
|
||||||
|
hpsdr_AIN4_count++;
|
||||||
}
|
}
|
||||||
// convert 24-bit samples to 32-bit samples; int must be 32 bits.
|
// convert 24-bit samples to 32-bit samples; int must be 32 bits.
|
||||||
index = start + 5;
|
index = start + 5;
|
||||||
@@ -3850,6 +3764,43 @@ static int read_rx_udp10(complex double * samp) // Read samples from UDP using t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PreDistort(multirx_cSamples[0], samp, nSamples, NULL, 0); //amp in samples, amp out samples, count
|
||||||
|
#if 0
|
||||||
|
float mag_in, mag_out;
|
||||||
|
complex float gain;
|
||||||
|
complex double * amp_in_samples;
|
||||||
|
complex double * amp_out_samples;
|
||||||
|
static int up = 0;
|
||||||
|
static float old_mag_in = 0;
|
||||||
|
amp_in_samples = multirx_cSamples[0];
|
||||||
|
amp_out_samples = samp;
|
||||||
|
//if (hermes_mox_bit && quisk_multirx_count > 0) {
|
||||||
|
if (quisk_multirx_count == 1) {
|
||||||
|
for (i = 0; i < nSamples; i++) {
|
||||||
|
//mag_in = cabs(amp_in_samples[i]) / 0.236875 / CLIP32;
|
||||||
|
//mag_out = cabs(amp_out_samples[i]) / 0.088037 / CLIP32;
|
||||||
|
mag_in = cabs(amp_in_samples[i]) / CLIP32;
|
||||||
|
mag_out = cabs(amp_out_samples[i]) / CLIP32;
|
||||||
|
//if (0.100 - 0.01 < mag_in && mag_in < 0.100 + 0.01) {
|
||||||
|
//if (mag_in > .001) {
|
||||||
|
if (mag_in > 0.001 && mag_in < 0.234) {
|
||||||
|
if (up && mag_in < old_mag_in) {
|
||||||
|
//printf("\n");
|
||||||
|
up = 0;
|
||||||
|
}
|
||||||
|
else if ( ! up && mag_in > old_mag_in) {
|
||||||
|
//printf("\n");
|
||||||
|
up = 1;
|
||||||
|
}
|
||||||
|
old_mag_in = mag_in;
|
||||||
|
//gain = amp_out_samples[i] / amp_in_samples[i] / (0.088037 / 0.236875);
|
||||||
|
gain = amp_out_samples[i] / amp_in_samples[i];
|
||||||
|
printf("In %12.6f %8.3f Out %12.6f %8.3f Gain %12.6f %8.3f\n",
|
||||||
|
mag_in, cargf(amp_in_samples[i]), mag_out, cargf(amp_out_samples[i]), cabsf(gain), cargf(gain));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if ((quisk_pc_to_hermes[3] >> 3 & 0x7) != quisk_multirx_count && // change in number of receivers
|
if ((quisk_pc_to_hermes[3] >> 3 & 0x7) != quisk_multirx_count && // change in number of receivers
|
||||||
( ! quisk_multirx_count || multirx_fft_next_state == 2)) { // wait until the current FFT is finished
|
( ! quisk_multirx_count || multirx_fft_next_state == 2)) { // wait until the current FFT is finished
|
||||||
quisk_multirx_state = 0; // Do not change receiver count without stopping Hermes and restarting
|
quisk_multirx_state = 0; // Do not change receiver count without stopping Hermes and restarting
|
||||||
@@ -4077,7 +4028,7 @@ static PyObject * open_rx_udp(PyObject * self, PyObject * args)
|
|||||||
wVersionRequested = MAKEWORD(2, 2);
|
wVersionRequested = MAKEWORD(2, 2);
|
||||||
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
|
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
|
||||||
sprintf(buf, "Failed to initialize Winsock (WSAStartup)");
|
sprintf(buf, "Failed to initialize Winsock (WSAStartup)");
|
||||||
return PyString_FromString(buf);
|
return PyUnicode_FromString(buf);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cleanupWSA = 1;
|
cleanupWSA = 1;
|
||||||
@@ -4126,7 +4077,7 @@ static PyObject * open_rx_udp(PyObject * self, PyObject * args)
|
|||||||
else {
|
else {
|
||||||
sprintf(buf, "Failed to open socket");
|
sprintf(buf, "Failed to open socket");
|
||||||
}
|
}
|
||||||
return PyString_FromString(buf);
|
return PyUnicode_FromString(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject * open_sound(PyObject * self, PyObject * args)
|
static PyObject * open_sound(PyObject * self, PyObject * args)
|
||||||
@@ -4340,7 +4291,7 @@ static PyObject * mixer_set(PyObject * self, PyObject * args)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
quisk_alsa_mixer_set(card_name, numid, value, err_msg, QUISK_SC_SIZE);
|
quisk_alsa_mixer_set(card_name, numid, value, err_msg, QUISK_SC_SIZE);
|
||||||
return PyString_FromString(err_msg);
|
return PyUnicode_FromString(err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject * pc_to_hermes(PyObject * self, PyObject * args)
|
static PyObject * pc_to_hermes(PyObject * self, PyObject * args)
|
||||||
@@ -4543,12 +4494,12 @@ static PyObject * ip_interfaces(PyObject * self, PyObject * args)
|
|||||||
baddr |= ~netmask;
|
baddr |= ~netmask;
|
||||||
tup = PyTuple_New(4);
|
tup = PyTuple_New(4);
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
PyTuple_SetItem(tup, 0, PyString_FromString("unnamed"));
|
PyTuple_SetItem(tup, 0, PyUnicode_FromString("unnamed"));
|
||||||
else
|
else
|
||||||
PyTuple_SetItem(tup, 0, PyString_FromString(name));
|
PyTuple_SetItem(tup, 0, PyUnicode_FromString(name));
|
||||||
PyTuple_SetItem(tup, 1, PyString_FromString(Win_NtoA(ipAddr)));
|
PyTuple_SetItem(tup, 1, PyUnicode_FromString(Win_NtoA(ipAddr)));
|
||||||
PyTuple_SetItem(tup, 2, PyString_FromString(Win_NtoA(netmask)));
|
PyTuple_SetItem(tup, 2, PyUnicode_FromString(Win_NtoA(netmask)));
|
||||||
PyTuple_SetItem(tup, 3, PyString_FromString(Win_NtoA(baddr)));
|
PyTuple_SetItem(tup, 3, PyUnicode_FromString(Win_NtoA(baddr)));
|
||||||
PyList_Append(pylist, tup);
|
PyList_Append(pylist, tup);
|
||||||
Py_DECREF(tup);
|
Py_DECREF(tup);
|
||||||
}
|
}
|
||||||
@@ -4567,10 +4518,10 @@ static PyObject * ip_interfaces(PyObject * self, PyObject * args)
|
|||||||
while(p) {
|
while(p) {
|
||||||
if ((p->ifa_addr) && p->ifa_addr->sa_family == AF_INET) {
|
if ((p->ifa_addr) && p->ifa_addr->sa_family == AF_INET) {
|
||||||
tup = PyTuple_New(4);
|
tup = PyTuple_New(4);
|
||||||
PyTuple_SetItem(tup, 0, PyString_FromString(p->ifa_name));
|
PyTuple_SetItem(tup, 0, PyUnicode_FromString(p->ifa_name));
|
||||||
PyTuple_SetItem(tup, 1, PyString_FromString(Lin_NtoA(p->ifa_addr)));
|
PyTuple_SetItem(tup, 1, PyUnicode_FromString(Lin_NtoA(p->ifa_addr)));
|
||||||
PyTuple_SetItem(tup, 2, PyString_FromString(Lin_NtoA(p->ifa_netmask)));
|
PyTuple_SetItem(tup, 2, PyUnicode_FromString(Lin_NtoA(p->ifa_netmask)));
|
||||||
PyTuple_SetItem(tup, 3, PyString_FromString(Lin_NtoA(p->ifa_broadaddr)));
|
PyTuple_SetItem(tup, 3, PyUnicode_FromString(Lin_NtoA(p->ifa_broadaddr)));
|
||||||
PyList_Append(pylist, tup);
|
PyList_Append(pylist, tup);
|
||||||
Py_DECREF(tup);
|
Py_DECREF(tup);
|
||||||
}
|
}
|
||||||
@@ -5710,7 +5661,7 @@ static PyObject * Xdft(PyObject * pyseq, int inverse, int window)
|
|||||||
static fftw_complex * samples; // complex data for fft
|
static fftw_complex * samples; // complex data for fft
|
||||||
static fftw_plan planF, planB; // fft plan for fftW
|
static fftw_plan planF, planB; // fft plan for fftW
|
||||||
static double * fft_window; // window function
|
static double * fft_window; // window function
|
||||||
Py_complex pycx; // Python C complex value
|
complex double pycx;
|
||||||
|
|
||||||
if (PySequence_Check(pyseq) != 1) {
|
if (PySequence_Check(pyseq) != 1) {
|
||||||
PyErr_SetString (QuiskError, "DFT input data is not a sequence");
|
PyErr_SetString (QuiskError, "DFT input data is not a sequence");
|
||||||
@@ -5745,22 +5696,20 @@ static PyObject * Xdft(PyObject * pyseq, int inverse, int window)
|
|||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
obj = PySequence_GetItem(pyseq, j);
|
obj = PySequence_GetItem(pyseq, j);
|
||||||
if (PyComplex_Check(obj)) {
|
if (PyComplex_Check(obj)) {
|
||||||
pycx = PyComplex_AsCComplex(obj);
|
pycx = PyComplex_RealAsDouble(obj) + I * PyComplex_ImagAsDouble(obj);
|
||||||
}
|
}
|
||||||
else if (PyFloat_Check(obj)) {
|
else if (PyFloat_Check(obj)) {
|
||||||
pycx.real = PyFloat_AsDouble(obj);
|
pycx = PyFloat_AsDouble(obj);
|
||||||
pycx.imag = 0;
|
|
||||||
}
|
}
|
||||||
else if (PyInt_Check(obj)) {
|
else if (PyInt_Check(obj)) {
|
||||||
pycx.real = PyInt_AsLong(obj);
|
pycx = PyInt_AsLong(obj);
|
||||||
pycx.imag = 0;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Py_XDECREF(obj);
|
Py_XDECREF(obj);
|
||||||
PyErr_SetString (QuiskError, "DFT input data is not a complex/float/int number");
|
PyErr_SetString (QuiskError, "DFT input data is not a complex/float/int number");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
samples[i] = pycx.real + I * pycx.imag;
|
samples[i] = pycx;
|
||||||
if (++j >= size)
|
if (++j >= size)
|
||||||
j = 0;
|
j = 0;
|
||||||
Py_XDECREF(obj);
|
Py_XDECREF(obj);
|
||||||
@@ -5786,9 +5735,7 @@ static PyObject * Xdft(PyObject * pyseq, int inverse, int window)
|
|||||||
pyseq = PyList_New(fft_size);
|
pyseq = PyList_New(fft_size);
|
||||||
j = (size - 1) / 2; // zero frequency in input
|
j = (size - 1) / 2; // zero frequency in input
|
||||||
for (i = 0; i < fft_size; i++) {
|
for (i = 0; i < fft_size; i++) {
|
||||||
pycx.real = creal(samples[i]);
|
PyList_SetItem(pyseq, j, PyComplex_FromDoubles(creal(samples[i]), cimag(samples[i])));
|
||||||
pycx.imag = cimag(samples[i]);
|
|
||||||
PyList_SetItem(pyseq, j, PyComplex_FromCComplex(pycx));
|
|
||||||
if (++j >= size)
|
if (++j >= size)
|
||||||
j = 0;
|
j = 0;
|
||||||
}
|
}
|
||||||
@@ -6190,6 +6137,7 @@ static PyMethodDef QuiskMethods[] = {
|
|||||||
{"XXset_transmit_mode", set_transmit_mode, METH_VARARGS, "Change the radio to transmit mode independent of key_down."},
|
{"XXset_transmit_mode", set_transmit_mode, METH_VARARGS, "Change the radio to transmit mode independent of key_down."},
|
||||||
{"set_volume", set_volume, METH_VARARGS, "Set the audio output volume."},
|
{"set_volume", set_volume, METH_VARARGS, "Set the audio output volume."},
|
||||||
{"set_tx_audio", (PyCFunction)quisk_set_tx_audio, METH_VARARGS|METH_KEYWORDS, "Set the transmit audio parameters."},
|
{"set_tx_audio", (PyCFunction)quisk_set_tx_audio, METH_VARARGS|METH_KEYWORDS, "Set the transmit audio parameters."},
|
||||||
|
{"get_tx_audio", (PyCFunction)quisk_get_tx_audio, METH_VARARGS, "Return some transmit audio parameters."},
|
||||||
{"is_vox", quisk_is_vox, METH_VARARGS, "return the VOX state zero or one."},
|
{"is_vox", quisk_is_vox, METH_VARARGS, "return the VOX state zero or one."},
|
||||||
{"set_split_rxtx", set_split_rxtx, METH_VARARGS, "Set split for rx/tx."},
|
{"set_split_rxtx", set_split_rxtx, METH_VARARGS, "Set split for rx/tx."},
|
||||||
{"set_tune", set_tune, METH_VARARGS, "Set the tuning frequency."},
|
{"set_tune", set_tune, METH_VARARGS, "Set the tuning frequency."},
|
||||||
@@ -6230,13 +6178,6 @@ static PyMethodDef QuiskMethods[] = {
|
|||||||
{"set_cwkey", set_hardware_cwkey, METH_VARARGS, "Change the CW key up/down state."},
|
{"set_cwkey", set_hardware_cwkey, METH_VARARGS, "Change the CW key up/down state."},
|
||||||
{"set_remote_cwkey", set_remote_cwkey, METH_VARARGS, "Change the remote control CW key up/down state."},
|
{"set_remote_cwkey", set_remote_cwkey, METH_VARARGS, "Change the remote control CW key up/down state."},
|
||||||
{"set_PTT", set_PTT, METH_VARARGS, "Record the PTT button state."},
|
{"set_PTT", set_PTT, METH_VARARGS, "Record the PTT button state."},
|
||||||
{"freedv_open", quisk_freedv_open, METH_VARARGS, "Open FreeDV."},
|
|
||||||
{"freedv_close", quisk_freedv_close, METH_VARARGS, "Close FreeDV."},
|
|
||||||
{"freedv_get_snr", quisk_freedv_get_snr, METH_VARARGS, "Return the signal to noise ratio in dB."},
|
|
||||||
{"freedv_get_version", quisk_freedv_get_version, METH_VARARGS, "Return the codec2 API version."},
|
|
||||||
{"freedv_get_rx_char", quisk_freedv_get_rx_char, METH_VARARGS, "Get text characters received from freedv."},
|
|
||||||
{"freedv_set_options", (PyCFunction)quisk_freedv_set_options, METH_VARARGS|METH_KEYWORDS, "Set the freedv parameters."},
|
|
||||||
{"freedv_set_squelch_en", quisk_freedv_set_squelch_en, METH_VARARGS, "Enable or disable FreeDV squelch."},
|
|
||||||
{"wdsp_set_parameter", (PyCFunction)quisk_wdsp_set_parameter, METH_VARARGS|METH_KEYWORDS, "Set parameters for the WDSP SDR library."},
|
{"wdsp_set_parameter", (PyCFunction)quisk_wdsp_set_parameter, METH_VARARGS|METH_KEYWORDS, "Set parameters for the WDSP SDR library."},
|
||||||
{"tmp_record_save", tmp_record_save, METH_VARARGS, "Save the temporary recording in a WAV file."},
|
{"tmp_record_save", tmp_record_save, METH_VARARGS, "Save the temporary recording in a WAV file."},
|
||||||
{"watfall_RgbData", watfall_RgbData, METH_VARARGS, "Return a cookie for the Waterfall pixel data."},
|
{"watfall_RgbData", watfall_RgbData, METH_VARARGS, "Return a cookie for the Waterfall pixel data."},
|
||||||
@@ -6244,6 +6185,8 @@ static PyMethodDef QuiskMethods[] = {
|
|||||||
{"watfall_GetPixels", watfall_GetPixels, METH_VARARGS, "Write the Waterfall image to be displayed."},
|
{"watfall_GetPixels", watfall_GetPixels, METH_VARARGS, "Write the Waterfall image to be displayed."},
|
||||||
{"write_fftw_wisdom", write_fftw_wisdom, METH_VARARGS, "Write the current fftw wisdom to the wisdom file."},
|
{"write_fftw_wisdom", write_fftw_wisdom, METH_VARARGS, "Write the current fftw wisdom to the wisdom file."},
|
||||||
{"read_fftw_wisdom", read_fftw_wisdom, METH_VARARGS, "Return the current fftw wisdom as a byte array."},
|
{"read_fftw_wisdom", read_fftw_wisdom, METH_VARARGS, "Return the current fftw wisdom as a byte array."},
|
||||||
|
{"tci_get_params", (PyCFunction)quisk_tci_get_params, METH_VARARGS, "Return parameters from TCI."},
|
||||||
|
{"tci_set_params", (PyCFunction)quisk_tci_set_params, METH_VARARGS|METH_KEYWORDS, "Set parameters for TCI."},
|
||||||
// Remote Quisk control head and slave by Ben, AC2YD
|
// Remote Quisk control head and slave by Ben, AC2YD
|
||||||
{"start_control_head_remote_sound", quisk_start_control_head_remote_sound, METH_VARARGS, "Start running UDP remote sound on control_head."},
|
{"start_control_head_remote_sound", quisk_start_control_head_remote_sound, METH_VARARGS, "Start running UDP remote sound on control_head."},
|
||||||
{"stop_control_head_remote_sound", quisk_stop_control_head_remote_sound, METH_VARARGS, "Stop running UDP remote sound on control_head."},
|
{"stop_control_head_remote_sound", quisk_stop_control_head_remote_sound, METH_VARARGS, "Stop running UDP remote sound on control_head."},
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
#define QUISK_CWKEY_DOWN (quisk_hardware_cwkey || quisk_serial_key_down || quisk_midi_cwkey || quisk_remote_cwkey)
|
#define QUISK_CWKEY_DOWN (quisk_hardware_cwkey || quisk_serial_key_down || quisk_midi_cwkey || quisk_remote_cwkey)
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
#define QUISK_SHUT_RD SD_RECEIVE
|
#define QUISK_SHUT_RD SD_RECEIVE
|
||||||
#define QUISK_SHUT_BOTH SD_BOTH
|
#define QUISK_SHUT_BOTH SD_BOTH
|
||||||
@@ -260,6 +262,14 @@ extern PyObject * quisk_stop_remote_radio_remote_sound(PyObject * self, PyObject
|
|||||||
extern int receive_graph_data(double * fft_avg);
|
extern int receive_graph_data(double * fft_avg);
|
||||||
extern void send_graph_data(double * fft_avg, int fft_size, double zoom, double deltaf, int fft_sample_rate, double scale);
|
extern void send_graph_data(double * fft_avg, int fft_size, double zoom, double deltaf, int fft_sample_rate, double scale);
|
||||||
|
|
||||||
|
// TCI server
|
||||||
|
PyObject * quisk_tci_set_params(PyObject * self, PyObject * args, PyObject * keywds);
|
||||||
|
PyObject * quisk_tci_get_params(PyObject * self, PyObject * args);
|
||||||
|
void tci_send_audio(complex double * cSamples, int nSamples);
|
||||||
|
void tci_send_iq(complex double * cSamples, int nSamples);
|
||||||
|
int tci_get_mic(complex double * cSamples, int mic_count);
|
||||||
|
extern uint64_t tci_tx_audio_client; // This is ws_cli_conn_t defined in ws.h.
|
||||||
|
|
||||||
void QuiskWavClose(struct QuiskWav *);
|
void QuiskWavClose(struct QuiskWav *);
|
||||||
int QuiskWavWriteOpen(struct QuiskWav *, char *, short, short, short, int, double);
|
int QuiskWavWriteOpen(struct QuiskWav *, char *, short, short, short, int, double);
|
||||||
void QuiskWavWriteC(struct QuiskWav *, complex double *, int);
|
void QuiskWavWriteC(struct QuiskWav *, complex double *, int);
|
||||||
@@ -312,10 +322,6 @@ extern int quisk_active_sidetone; // Whether and how to generate a sidetone
|
|||||||
extern int quisk_isFDX; // Are we in full duplex mode?
|
extern int quisk_isFDX; // Are we in full duplex mode?
|
||||||
extern int quisk_use_serial_port; // Are we using the serial port for CW key or PTT?
|
extern int quisk_use_serial_port; // Are we using the serial port for CW key or PTT?
|
||||||
extern play_state_t quisk_play_state; // startup, receiving, sidetone
|
extern play_state_t quisk_play_state; // startup, receiving, sidetone
|
||||||
extern int freedv_current_mode; // current FreeDV mode; 700D, @)@) etc.
|
|
||||||
extern int n_modem_sample_rate; // Receive data, decimate to modem_sample_rate, FreeDV codec output data at speech_sample_rate
|
|
||||||
extern int n_speech_sample_rate; // Microphone decimate to speech_sample_rate, Freedv codec output data at modem_sample_rate, interpolate to 48000
|
|
||||||
extern int n_max_modem_samples; // maximum input to freedv_rx()
|
|
||||||
extern int quisk_start_cw_delay; // milliseconds to delay output on serial or MIDI CW key down
|
extern int quisk_start_cw_delay; // milliseconds to delay output on serial or MIDI CW key down
|
||||||
extern int quisk_start_ssb_delay; // milliseconds to discard output for all modes except CW
|
extern int quisk_start_ssb_delay; // milliseconds to discard output for all modes except CW
|
||||||
extern struct sound_dev * quiskPlaybackDevices[]; // array of Playback sound devices
|
extern struct sound_dev * quiskPlaybackDevices[]; // array of Playback sound devices
|
||||||
@@ -339,20 +345,14 @@ extern PyObject * quisk_sound_errors(PyObject *, PyObject *);
|
|||||||
extern PyObject * quisk_set_file_record(PyObject *, PyObject *);
|
extern PyObject * quisk_set_file_record(PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_set_file_name(PyObject *, PyObject *, PyObject *);
|
extern PyObject * quisk_set_file_name(PyObject *, PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_set_tx_audio(PyObject *, PyObject *, PyObject *);
|
extern PyObject * quisk_set_tx_audio(PyObject *, PyObject *, PyObject *);
|
||||||
|
extern PyObject * quisk_get_tx_audio(PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_is_vox(PyObject *, PyObject *);
|
extern PyObject * quisk_is_vox(PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_set_udp_tx_correct(PyObject *, PyObject *);
|
extern PyObject * quisk_set_udp_tx_correct(PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_set_hermes_filter(PyObject *, PyObject *);
|
extern PyObject * quisk_set_hermes_filter(PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_set_alex_hpf(PyObject *, PyObject *);
|
extern PyObject * quisk_set_alex_hpf(PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_set_alex_lpf(PyObject *, PyObject *);
|
extern PyObject * quisk_set_alex_lpf(PyObject *, PyObject *);
|
||||||
|
|
||||||
extern PyObject * quisk_freedv_open(PyObject *, PyObject *);
|
|
||||||
extern PyObject * quisk_freedv_close(PyObject *, PyObject *);
|
|
||||||
extern PyObject * quisk_freedv_get_snr(PyObject *, PyObject *);
|
|
||||||
extern PyObject * quisk_freedv_get_version(PyObject *, PyObject *);
|
|
||||||
extern PyObject * quisk_freedv_get_rx_char(PyObject *, PyObject *);
|
|
||||||
extern PyObject * quisk_freedv_set_options(PyObject *, PyObject *, PyObject *);
|
|
||||||
extern PyObject * quisk_set_sparams(PyObject *, PyObject *, PyObject *);
|
extern PyObject * quisk_set_sparams(PyObject *, PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_freedv_set_squelch_en(PyObject *, PyObject *);
|
|
||||||
extern PyObject * quisk_open_key(PyObject *, PyObject *, PyObject *);
|
extern PyObject * quisk_open_key(PyObject *, PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_close_key(PyObject *, PyObject *);
|
extern PyObject * quisk_close_key(PyObject *, PyObject *);
|
||||||
extern PyObject * quisk_set_sound_name(PyObject *, PyObject *);
|
extern PyObject * quisk_set_sound_name(PyObject *, PyObject *);
|
||||||
@@ -362,7 +362,7 @@ extern PyObject * quisk_wasapi_control_midi(PyObject *, PyObject *, PyObject *);
|
|||||||
// WDSP interface
|
// WDSP interface
|
||||||
#define QUISK_WDSP_RX 1
|
#define QUISK_WDSP_RX 1
|
||||||
extern PyObject * quisk_wdsp_set_parameter(PyObject *, PyObject *, PyObject *);
|
extern PyObject * quisk_wdsp_set_parameter(PyObject *, PyObject *, PyObject *);
|
||||||
extern int wdspFexchange0(int, double *, int);
|
extern int wdspFexchange0(int, complex double *, int);
|
||||||
|
|
||||||
// These function pointers are the Start/Stop/Read interface for
|
// These function pointers are the Start/Stop/Read interface for
|
||||||
// the SDR-IQ and any other C-language extension modules that return
|
// the SDR-IQ and any other C-language extension modules that return
|
||||||
@@ -396,7 +396,6 @@ void quisk_file_playback(complex double *, int, double);
|
|||||||
void quisk_tmp_playback(complex double *, int, double);
|
void quisk_tmp_playback(complex double *, int, double);
|
||||||
void quisk_hermes_tx_send(int, int *);
|
void quisk_hermes_tx_send(int, int *);
|
||||||
void quisk_udp_mic_error(char *);
|
void quisk_udp_mic_error(char *);
|
||||||
void quisk_check_freedv_mode(void);
|
|
||||||
void quisk_calc_audio_graph(double, complex double *, double *, int, int);
|
void quisk_calc_audio_graph(double, complex double *, double *, int, int);
|
||||||
double QuiskDeltaSec(int);
|
double QuiskDeltaSec(int);
|
||||||
void * quisk_make_sidetone(struct sound_dev *, int);
|
void * quisk_make_sidetone(struct sound_dev *, int);
|
||||||
@@ -404,12 +403,8 @@ void * quisk_make_txIQ(struct sound_dev *, int);
|
|||||||
int quisk_play_sidetone(struct sound_dev *);
|
int quisk_play_sidetone(struct sound_dev *);
|
||||||
void quisk_set_play_state(void);
|
void quisk_set_play_state(void);
|
||||||
void quisk_poll_hardware_key(void);
|
void quisk_poll_hardware_key(void);
|
||||||
|
void PreDistort(complex double * amp_in_samples, complex double * amp_out_samples, int nSamples, complex double * tx_samples, int num_tx);
|
||||||
// Functions supporting digital voice codecs
|
int CircularBuffer(int channel, complex double * cSamples, int nRead, int nWrite);
|
||||||
typedef int (* ty_dvoice_codec_rx)(short *, double *, int, int);
|
|
||||||
typedef int (* ty_dvoice_codec_tx)(complex double *, double *, int, int);
|
|
||||||
extern ty_dvoice_codec_rx pt_quisk_freedv_rx;
|
|
||||||
extern ty_dvoice_codec_tx pt_quisk_freedv_tx;
|
|
||||||
|
|
||||||
// Driver function definitions=================================================
|
// Driver function definitions=================================================
|
||||||
int quisk_read_alsa(struct sound_dev *, complex double *);
|
int quisk_read_alsa(struct sound_dev *, complex double *);
|
||||||
@@ -466,7 +461,7 @@ int import_quisk_api(void); // used to initialize Quisk_API
|
|||||||
#define QuiskSleepMicrosec (*( void (*) (int) )Quisk_API[5])
|
#define QuiskSleepMicrosec (*( void (*) (int) )Quisk_API[5])
|
||||||
#define QuiskPrintTime (*( void (*) (const char *, int) )Quisk_API[6])
|
#define QuiskPrintTime (*( void (*) (const char *, int) )Quisk_API[6])
|
||||||
#define quisk_sample_source (*( void (*) (ty_sample_start, ty_sample_stop, ty_sample_read) )Quisk_API[7])
|
#define quisk_sample_source (*( void (*) (ty_sample_start, ty_sample_stop, ty_sample_read) )Quisk_API[7])
|
||||||
#define quisk_dvoice_freedv (*( void (*) (ty_dvoice_codec_rx, ty_dvoice_codec_tx) )Quisk_API[8])
|
#define quisk_dvoice_freedv (*( void (*) (void) )Quisk_API[8])
|
||||||
#define quisk_is_key_down (*( int (*) (void) )Quisk_API[9])
|
#define quisk_is_key_down (*( int (*) (void) )Quisk_API[9])
|
||||||
#define quisk_sample_source4 (*( void (*) (ty_sample_start, ty_sample_stop, ty_sample_read, ty_sample_write) )Quisk_API[10])
|
#define quisk_sample_source4 (*( void (*) (ty_sample_start, ty_sample_stop, ty_sample_read, ty_sample_write) )Quisk_API[10])
|
||||||
#define strMcpy (*( char * (*) (char *, const char *, size_t) )Quisk_API[11])
|
#define strMcpy (*( char * (*) (char *, const char *, size_t) )Quisk_API[11])
|
||||||
@@ -481,7 +476,7 @@ double QuiskTimeSec(void);
|
|||||||
void QuiskSleepMicrosec(int);
|
void QuiskSleepMicrosec(int);
|
||||||
void QuiskPrintTime(const char *, int);
|
void QuiskPrintTime(const char *, int);
|
||||||
void quisk_sample_source(ty_sample_start, ty_sample_stop, ty_sample_read);
|
void quisk_sample_source(ty_sample_start, ty_sample_stop, ty_sample_read);
|
||||||
void quisk_dvoice_freedv(ty_dvoice_codec_rx, ty_dvoice_codec_tx);
|
void quisk_dvoice_freedv(void);
|
||||||
int quisk_is_key_down(void);
|
int quisk_is_key_down(void);
|
||||||
void quisk_sample_source4(ty_sample_start, ty_sample_stop, ty_sample_read, ty_sample_write);
|
void quisk_sample_source4(ty_sample_start, ty_sample_stop, ty_sample_read, ty_sample_write);
|
||||||
char * strMcpy(char *, const char *, size_t);
|
char * strMcpy(char *, const char *, size_t);
|
||||||
|
|||||||
@@ -292,9 +292,9 @@ class HamlibHandlerSerial:
|
|||||||
|
|
||||||
# Additional logic contributed by Dr. Karsten Schmidt
|
# Additional logic contributed by Dr. Karsten Schmidt
|
||||||
|
|
||||||
Mo2CoKen = {'CWL':7, 'CWU':3, 'LSB':1, 'USB':2, 'AM':5, 'FM':4, 'DGT-U':9, 'DGT-L':6, 'DGT-FM':4, 'DGT-IQ':9}
|
Mo2CoKen = {'CWL':7, 'CWU':3, 'LSB':1, 'USB':2, 'AM':5, 'FM':4, 'DGT-U':9, 'FDV-U':9, 'DGT-L':6, 'FDV-L':6, 'DGT-FM':4, 'DGT-IQ':9}
|
||||||
Co2MoKen = {1:'LSB', 2:'USB', 3:'CWU', 4:'FM', 5:'AM', 6:'DGT-L', 7:'CWL', 9:'DGT-U'}
|
Co2MoKen = {1:'LSB', 2:'USB', 3:'CWU', 4:'FM', 5:'AM', 6:'DGT-L', 7:'CWL', 9:'DGT-U'}
|
||||||
Mo2CoFlex = {'CWL':3, 'CWU':4, 'LSB':0, 'USB':1, 'AM':6, 'FM':5, 'DGT-U':7, 'DGT-L':9, 'DGT-FM':5, 'DGT-IQ':7}
|
Mo2CoFlex = {'CWL':3, 'CWU':4, 'LSB':0, 'USB':1, 'AM':6, 'FM':5, 'DGT-U':7, 'FDV-U':7, 'DGT-L':9, 'FDV-L':9, 'DGT-FM':5, 'DGT-IQ':7}
|
||||||
Co2MoFlex = {0:'LSB', 1:'USB', 3:'CWL', 4:'CWU', 5:'FM', 6:'AM', 7:'DGT-U', 9:'DGT-L'}
|
Co2MoFlex = {0:'LSB', 1:'USB', 3:'CWL', 4:'CWU', 5:'FM', 6:'AM', 7:'DGT-U', 9:'DGT-L'}
|
||||||
ConvertZZAC_P1_2_Step = { 0: 1,
|
ConvertZZAC_P1_2_Step = { 0: 1,
|
||||||
1: 10,
|
1: 10,
|
||||||
@@ -995,17 +995,12 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
|
|||||||
else:
|
else:
|
||||||
self.Send('0\n')
|
self.Send('0\n')
|
||||||
def GetFreq(self): # The Rx frequency
|
def GetFreq(self): # The Rx frequency
|
||||||
rx = self.app.multi_rx_screen.receiver_list
|
# This is always the main Rx frequency without reference to added receivers.
|
||||||
if rx:
|
self.Reply('Frequency', self.app.rxFreq + self.app.VFO, 0)
|
||||||
rx = rx[0]
|
if HAMLIB_DEBUG:
|
||||||
self.Reply('Frequency', rx.txFreq + rx.VFO, 0)
|
print ("GetFreq app", self.app.rxFreq, self.app.txFreq, self.app.VFO)
|
||||||
if HAMLIB_DEBUG:
|
|
||||||
print ("GetFreq rx", rx.txFreq, rx.VFO)
|
|
||||||
else:
|
|
||||||
self.Reply('Frequency', self.app.rxFreq + self.app.VFO, 0)
|
|
||||||
if HAMLIB_DEBUG:
|
|
||||||
print ("GetFreq app", self.app.rxFreq, self.app.txFreq, self.app.VFO)
|
|
||||||
def SetFreq(self): # The Rx frequency
|
def SetFreq(self): # The Rx frequency
|
||||||
|
# This is always the main Rx frequency without reference to added receivers.
|
||||||
freq = self.GetParamNumber()
|
freq = self.GetParamNumber()
|
||||||
try:
|
try:
|
||||||
freq = float(freq)
|
freq = float(freq)
|
||||||
@@ -1014,12 +1009,7 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
|
|||||||
self.ErrParam()
|
self.ErrParam()
|
||||||
else:
|
else:
|
||||||
freq = int(freq + 0.5)
|
freq = int(freq + 0.5)
|
||||||
# For multiple receivers this controls the first added receiver frequency; else it controls split Rx/Tx frequency.
|
self.app.ChangeRxTxFrequency(freq, None)
|
||||||
rx = self.app.multi_rx_screen.receiver_list
|
|
||||||
if rx:
|
|
||||||
rx[0].ChangeRxTxFrequency(freq)
|
|
||||||
else:
|
|
||||||
self.app.ChangeRxTxFrequency(freq, None)
|
|
||||||
def GetSplitFreq(self): # The Tx Frequency
|
def GetSplitFreq(self): # The Tx Frequency
|
||||||
self.Reply('TX Frequency', self.app.txFreq + self.app.VFO, 0)
|
self.Reply('TX Frequency', self.app.txFreq + self.app.VFO, 0)
|
||||||
def SetSplitFreq(self): # The Tx Frequency
|
def SetSplitFreq(self): # The Tx Frequency
|
||||||
@@ -1061,9 +1051,9 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
|
|||||||
mode = 'CW'
|
mode = 'CW'
|
||||||
elif mode == 'CWL': # Is this what CWR means?
|
elif mode == 'CWL': # Is this what CWR means?
|
||||||
mode = 'CWR'
|
mode = 'CWR'
|
||||||
elif mode == 'DGT-U':
|
elif mode in ('DGT-U', 'FDV-U'):
|
||||||
mode = 'PKTUSB'
|
mode = 'PKTUSB'
|
||||||
elif mode == 'DGT-L':
|
elif mode in ('DGT-L', 'FDV-L'):
|
||||||
mode = 'PKTLSB'
|
mode = 'PKTLSB'
|
||||||
elif mode == 'DGT-FM':
|
elif mode == 'DGT-FM':
|
||||||
mode = 'PKTFM'
|
mode = 'PKTFM'
|
||||||
@@ -3294,16 +3284,27 @@ class MultiReceiverScreen(wx.SplitterWindow):
|
|||||||
if pane != excpt:
|
if pane != excpt:
|
||||||
pane.play_btn.SetValue(False)
|
pane.play_btn.SetValue(False)
|
||||||
pane.is_playing = False
|
pane.is_playing = False
|
||||||
def OnAddReceiver(self, event):
|
def OnAddReceiver(self, event=None, hide_it=False):
|
||||||
index = len(self.receiver_list)
|
index = len(self.receiver_list)
|
||||||
if index >= 7:
|
if index >= 7:
|
||||||
return
|
return
|
||||||
if index == 0:
|
if index == 0:
|
||||||
pane2 = self.rx_zero
|
|
||||||
splitter = pane2.GetParent()
|
|
||||||
pane1 = MultiRxGraph(self, self.data_width, self.graph_width, index)
|
pane1 = MultiRxGraph(self, self.data_width, self.graph_width, index)
|
||||||
self.receiver_list.append(pane1)
|
self.receiver_list.append(pane1)
|
||||||
splitter.SplitHorizontally(pane1, self.rx_zero)
|
if hide_it:
|
||||||
|
pane1.Show(False)
|
||||||
|
pane1.graph_display.Show(False)
|
||||||
|
else:
|
||||||
|
pane2 = self.rx_zero
|
||||||
|
splitter = pane2.GetParent()
|
||||||
|
splitter.SplitHorizontally(pane1, self.rx_zero)
|
||||||
|
elif index == 1 and not self.receiver_list[0].IsShown():
|
||||||
|
pane1 = self.receiver_list[0]
|
||||||
|
pane2 = self.rx_zero
|
||||||
|
splitter = pane2.GetParent()
|
||||||
|
pane1.Show(True)
|
||||||
|
pane1.graph_display.Show(True)
|
||||||
|
splitter.SplitHorizontally(pane1, self.rx_zero)
|
||||||
else:
|
else:
|
||||||
pane2 = self.receiver_list[-1]
|
pane2 = self.receiver_list[-1]
|
||||||
parent = pane2.GetParent()
|
parent = pane2.GetParent()
|
||||||
@@ -3716,8 +3717,8 @@ class App(wx.App):
|
|||||||
'txAudioPreemphUsb', 'txAudioPreemphAm', 'txAudioPreemphFm', 'txAudioPreemphFdv',
|
'txAudioPreemphUsb', 'txAudioPreemphAm', 'txAudioPreemphFm', 'txAudioPreemphFdv',
|
||||||
'wfallScaleZ', 'wfallGrScaleZ', 'graphScaleZ', 'split_rxtx_play', 'modeFilter',
|
'wfallScaleZ', 'wfallGrScaleZ', 'graphScaleZ', 'split_rxtx_play', 'modeFilter',
|
||||||
'file_name_rec_audio', 'file_name_rec_samples', 'file_name_rec_mic', 'file_name_rec_tmp',
|
'file_name_rec_audio', 'file_name_rec_samples', 'file_name_rec_mic', 'file_name_rec_tmp',
|
||||||
'file_name_play_audio', 'file_name_play_samples', 'file_name_play_cq', 'freedv_mode',
|
'file_name_play_audio', 'file_name_play_samples', 'file_name_play_cq',
|
||||||
'file_play_source', 'hermes_LNA_dB']
|
'file_play_source', 'hermes_LNA_dB', 'hermes_atten_dB']
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
global application
|
global application
|
||||||
QS.AppStatus(1)
|
QS.AppStatus(1)
|
||||||
@@ -3937,9 +3938,9 @@ class App(wx.App):
|
|||||||
self.hot_key_ptt_active = False
|
self.hot_key_ptt_active = False
|
||||||
self.serial_ptt_active = False
|
self.serial_ptt_active = False
|
||||||
self.vox_ptt_active = False
|
self.vox_ptt_active = False
|
||||||
self.freedv_mode = 'Mode 700D' # restore FreeDV mode setting
|
self.tci_ptt_active = False
|
||||||
self.freedv_menu = None
|
|
||||||
self.hermes_LNA_dB = 20
|
self.hermes_LNA_dB = 20
|
||||||
|
self.hermes_atten_dB = 0
|
||||||
if hasattr(Hardware, "OnChangeRxTx"):
|
if hasattr(Hardware, "OnChangeRxTx"):
|
||||||
self.want_RxTx = True
|
self.want_RxTx = True
|
||||||
else:
|
else:
|
||||||
@@ -4028,6 +4029,10 @@ class App(wx.App):
|
|||||||
self.file_name_play_samples = ''
|
self.file_name_play_samples = ''
|
||||||
self.file_name_play_cq = ''
|
self.file_name_play_cq = ''
|
||||||
self.midiControls = {} # Control object and associated function for Midi control name
|
self.midiControls = {} # Control object and associated function for Midi control name
|
||||||
|
self.tci_vfo = 0
|
||||||
|
self.tci_dds = 0
|
||||||
|
self.tci_modulation = ''
|
||||||
|
self.tci_split_enable = 0
|
||||||
# get the screen size - thanks to Lucian Langa
|
# get the screen size - thanks to Lucian Langa
|
||||||
x, y, self.screen_width, self.screen_height = wx.Display().GetGeometry() # Using display index 0
|
x, y, self.screen_width, self.screen_height = wx.Display().GetGeometry() # Using display index 0
|
||||||
self.Bind(wx.EVT_IDLE, self.OnIdle)
|
self.Bind(wx.EVT_IDLE, self.OnIdle)
|
||||||
@@ -4208,6 +4213,8 @@ class App(wx.App):
|
|||||||
# Start wdsp
|
# Start wdsp
|
||||||
self.wdsp = quisk_wdsp.Cwdsp(self)
|
self.wdsp = quisk_wdsp.Cwdsp(self)
|
||||||
self.wdsp_channel = 1
|
self.wdsp_channel = 1
|
||||||
|
self.wdsp_SNB = 0
|
||||||
|
self.wdsp_NR2 = 0
|
||||||
self.wdsp.open(self.wdsp_channel)
|
self.wdsp.open(self.wdsp_channel)
|
||||||
#print ('data_width %d, FFT size %d, FFT mult %d, average_count %d, rate %d, Refresh %.2f Hz' % (
|
#print ('data_width %d, FFT size %d, FFT mult %d, average_count %d, rate %d, Refresh %.2f Hz' % (
|
||||||
# self.data_width, self.fft_size, self.fft_size / self.data_width, average_count, self.sample_rate,
|
# self.data_width, self.fft_size, self.fft_size / self.data_width, average_count, self.sample_rate,
|
||||||
@@ -4273,6 +4280,10 @@ class App(wx.App):
|
|||||||
self.main_frame.SetClientSize(wx.Size(width, self.height))
|
self.main_frame.SetClientSize(wx.Size(width, self.height))
|
||||||
if hasattr(Hardware, 'pre_open'): # pre_open() is called before open()
|
if hasattr(Hardware, 'pre_open'): # pre_open() is called before open()
|
||||||
Hardware.pre_open()
|
Hardware.pre_open()
|
||||||
|
try:
|
||||||
|
self.is_HermesLite2 = Hardware.is_HermesLite2 # This is set in pre_open()
|
||||||
|
except:
|
||||||
|
self.is_HermesLite2 = False
|
||||||
if self.local_conf.GetWidgets(self, Hardware, conf, frame, gbs, vertBox):
|
if self.local_conf.GetWidgets(self, Hardware, conf, frame, gbs, vertBox):
|
||||||
pass
|
pass
|
||||||
elif conf.quisk_widgets:
|
elif conf.quisk_widgets:
|
||||||
@@ -4347,6 +4358,19 @@ class App(wx.App):
|
|||||||
if hasattr(bw, "sliderLNA"):
|
if hasattr(bw, "sliderLNA"):
|
||||||
self.midiControls["RfLna"] = (bw.sliderLNA, bw.OnLNA)
|
self.midiControls["RfLna"] = (bw.sliderLNA, bw.OnLNA)
|
||||||
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyHook)
|
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyHook)
|
||||||
|
# Start the TCI server
|
||||||
|
if conf.tci_ip and conf.tci_port:
|
||||||
|
value = getattr(conf, "pulse_audio_verbose_output")
|
||||||
|
QS.tci_set_params(verbose=value)
|
||||||
|
QS.tci_set_params(start=1);
|
||||||
|
self.tci_dds = self.VFO
|
||||||
|
QS.tci_set_params(tci_dds=self.tci_dds)
|
||||||
|
self.tci_vfo = self.rxFreq + self.VFO
|
||||||
|
QS.tci_set_params(tci_vfo=self.tci_vfo)
|
||||||
|
self.tci_modulation = self.mode
|
||||||
|
QS.tci_set_params(tci_modulation=self.tci_modulation)
|
||||||
|
QS.tci_set_params(tci_trx=0)
|
||||||
|
QS.tci_set_params(tci_split_enable=0)
|
||||||
return True
|
return True
|
||||||
def OnStartWsjtx(self, ctrl):
|
def OnStartWsjtx(self, ctrl):
|
||||||
method = ctrl.GetValue()
|
method = ctrl.GetValue()
|
||||||
@@ -4671,6 +4695,8 @@ class App(wx.App):
|
|||||||
if msg:
|
if msg:
|
||||||
print(msg, end='')
|
print(msg, end='')
|
||||||
QS.close_key()
|
QS.close_key()
|
||||||
|
if QS.tci_get_params("tci_clients_count") > 0:
|
||||||
|
QS.tci_set_params(close=1)
|
||||||
QS.set_file_name(record_button=0) # Turn off file recording
|
QS.set_file_name(record_button=0) # Turn off file recording
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
if self.sound_thread:
|
if self.sound_thread:
|
||||||
@@ -4698,8 +4724,6 @@ class App(wx.App):
|
|||||||
self.hamlib_com1_handler.close()
|
self.hamlib_com1_handler.close()
|
||||||
if self.hamlib_com2_handler:
|
if self.hamlib_com2_handler:
|
||||||
self.hamlib_com2_handler.close()
|
self.hamlib_com2_handler.close()
|
||||||
if conf.add_freedv_button:
|
|
||||||
QS.freedv_close()
|
|
||||||
msg = QS.GetQuiskPrintf()
|
msg = QS.GetQuiskPrintf()
|
||||||
if msg:
|
if msg:
|
||||||
print(msg, end='')
|
print(msg, end='')
|
||||||
@@ -4758,6 +4782,7 @@ class App(wx.App):
|
|||||||
def ImmediateChange(self, name):
|
def ImmediateChange(self, name):
|
||||||
if name == "pulse_audio_verbose_output":
|
if name == "pulse_audio_verbose_output":
|
||||||
value = getattr(conf, name)
|
value = getattr(conf, name)
|
||||||
|
QS.tci_set_params(verbose=value)
|
||||||
if value == 0:
|
if value == 0:
|
||||||
self.std_out_err.Show(False)
|
self.std_out_err.Show(False)
|
||||||
else:
|
else:
|
||||||
@@ -4897,7 +4922,7 @@ class App(wx.App):
|
|||||||
b.char_shortcut = 'u'
|
b.char_shortcut = 'u'
|
||||||
self.MakeAccel(b)
|
self.MakeAccel(b)
|
||||||
left_row2.append(b)
|
left_row2.append(b)
|
||||||
b = QuiskCheckbutton(frame, self.OnBtnNR2, text='NR2')
|
b = QuiskCycleCheckbutton(frame, self.OnBtnNR2, ['NR2', 'NR2', 'NR2t'])
|
||||||
if not self.wdsp.version:
|
if not self.wdsp.version:
|
||||||
b.Enable(False)
|
b.Enable(False)
|
||||||
left_row2.append(b)
|
left_row2.append(b)
|
||||||
@@ -5038,14 +5063,9 @@ class App(wx.App):
|
|||||||
self.config_screen.config.EnableRecPlay()
|
self.config_screen.config.EnableRecPlay()
|
||||||
### Right bank of buttons
|
### Right bank of buttons
|
||||||
mode_names = ['CWL', 'CWU', 'LSB', 'USB', 'AM', 'FM', 'DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ', 'FDV-U', 'FDV-L', 'IMD']
|
mode_names = ['CWL', 'CWU', 'LSB', 'USB', 'AM', 'FM', 'DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ', 'FDV-U', 'FDV-L', 'IMD']
|
||||||
labels = [('CWL', 'CWU'), ('LSB', 'USB'), 'AM', 'FM', ('DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ')]
|
labels = [('CWL', 'CWU'), ('LSB', 'USB'), 'AM', 'FM', ('DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ'), ('FDV-U', 'FDV-L')]
|
||||||
shortcuts = ['C', 'S', 'A', 'M', 'D']
|
shortcuts = ['C', 'S', 'A', 'M', 'D', 'F']
|
||||||
count = 5 # There is room for seven buttons
|
count = 6 # There is room for seven buttons
|
||||||
if conf.add_freedv_button:
|
|
||||||
n_freedv = count
|
|
||||||
count += 1
|
|
||||||
labels.append('FDV-U')
|
|
||||||
shortcuts.append('F')
|
|
||||||
if conf.add_imd_button:
|
if conf.add_imd_button:
|
||||||
n_imd = count
|
n_imd = count
|
||||||
count += 1
|
count += 1
|
||||||
@@ -5067,45 +5087,13 @@ class App(wx.App):
|
|||||||
self.modeButns.buttons[0].idName = "CW U/L"
|
self.modeButns.buttons[0].idName = "CW U/L"
|
||||||
self.modeButns.buttons[1].idName = "SSB U/L"
|
self.modeButns.buttons[1].idName = "SSB U/L"
|
||||||
self.modeButns.buttons[4].idName = "DGT"
|
self.modeButns.buttons[4].idName = "DGT"
|
||||||
|
self.modeButns.buttons[5].idName = "FDV"
|
||||||
else:
|
else:
|
||||||
labels = ['CWL', 'CWU', 'LSB', 'USB', 'AM', 'FM', 'DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ', 'FDV-U', 'IMD']
|
labels = ['CWL', 'CWU', 'LSB', 'USB', 'AM', 'FM', 'DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ', 'FDV-U', 'IMD']
|
||||||
self.modeButns = RadioBtnPopup(frame, self.OnBtnMode, labels, None, 'modeButns')
|
self.modeButns = RadioBtnPopup(frame, self.OnBtnMode, labels, None, 'modeButns')
|
||||||
self.modeButns.idName = "modeButns"
|
self.modeButns.idName = "modeButns"
|
||||||
self.freedv_menu_items = {}
|
|
||||||
if conf.add_freedv_button:
|
|
||||||
self.freedv_menu = QuiskMenu("freedv_menu")
|
|
||||||
msg = conf.freedv_tx_msg
|
|
||||||
QS.freedv_set_options(mode=conf.freedv_modes[0][1], tx_msg=msg, DEBUG=0, squelch=1)
|
|
||||||
self.freedv_menu.AppendCheckItem("Monitor", self.OnFreedvMonitor)
|
|
||||||
self.freedv_menu.AppendSeparator()
|
|
||||||
for mode, index in conf.freedv_modes:
|
|
||||||
item = self.freedv_menu.AppendRadioItem(mode, self.OnFreedvMenu, mode == self.freedv_mode)
|
|
||||||
self.freedv_menu_items[index] = item
|
|
||||||
if mode == self.freedv_mode: # Restore mode
|
|
||||||
QS.freedv_set_options(mode=index)
|
|
||||||
if mode in ("Mode 2020", "Mode 2400A", "Mode 2400B"):
|
|
||||||
item.Enable(False)
|
|
||||||
if conf.button_layout == 'Large screen':
|
|
||||||
b = QuiskCycleCheckbutton(frame, None, ('FDV-U', 'FDV-L'), is_radio=True)
|
|
||||||
b.idName = "FDV"
|
|
||||||
b.char_shortcut = 'F'
|
|
||||||
self.btnFreeDV = WrapMenu(b, self.freedv_menu)
|
|
||||||
self.modeButns.ReplaceButton(n_freedv, self.btnFreeDV)
|
|
||||||
else:
|
|
||||||
self.btnFreeDV = self.modeButns.AddMenu('FDV-U', self.freedv_menu)
|
|
||||||
try:
|
|
||||||
ok = QS.freedv_open()
|
|
||||||
except:
|
|
||||||
traceback.print_exc()
|
|
||||||
ok = 0
|
|
||||||
if not ok:
|
|
||||||
conf.add_freedv_button = False
|
|
||||||
if conf.button_layout == 'Large screen':
|
|
||||||
self.modeButns.GetButtons()[n_freedv].Enable(0)
|
|
||||||
else:
|
|
||||||
self.modeButns.Enable('FDV-U', False)
|
|
||||||
if conf.add_imd_button:
|
if conf.add_imd_button:
|
||||||
val = 500
|
val = 1000
|
||||||
QS.set_imd_level(val)
|
QS.set_imd_level(val)
|
||||||
if conf.button_layout == 'Large screen':
|
if conf.button_layout == 'Large screen':
|
||||||
b = QuiskCheckbutton(frame, None, 'IMD', color=conf.color_test)
|
b = QuiskCheckbutton(frame, None, 'IMD', color=conf.color_test)
|
||||||
@@ -5382,16 +5370,6 @@ class App(wx.App):
|
|||||||
t = '%13.2f' % (QS.measure_frequency(-1) + vfo)
|
t = '%13.2f' % (QS.measure_frequency(-1) + vfo)
|
||||||
t = t[0:4] + ' ' + t[4:7] + ' ' + t[7:] + ' Hz'
|
t = t[0:4] + ' ' + t[4:7] + ' ' + t[7:] + ' Hz'
|
||||||
self.smeter.SetLabel(t)
|
self.smeter.SetLabel(t)
|
||||||
def NewDVmeter(self):
|
|
||||||
if conf.add_freedv_button:
|
|
||||||
snr = QS.freedv_get_snr()
|
|
||||||
txt = QS.freedv_get_rx_char()
|
|
||||||
self.graph.ScrollMsg(txt)
|
|
||||||
self.waterfall.ScrollMsg(txt)
|
|
||||||
else:
|
|
||||||
snr = 0.0
|
|
||||||
t = " SNR %3.0f" % snr
|
|
||||||
self.smeter.SetLabel(t)
|
|
||||||
def NewSmeter(self):
|
def NewSmeter(self):
|
||||||
self.smeter_db_count += 1 # count for average
|
self.smeter_db_count += 1 # count for average
|
||||||
x = QS.get_smeter()
|
x = QS.get_smeter()
|
||||||
@@ -5561,66 +5539,6 @@ class App(wx.App):
|
|||||||
self.multi_rx_screen.waterfall.pane2.filter_center = center
|
self.multi_rx_screen.waterfall.pane2.filter_center = center
|
||||||
if self.screen is self.filter_screen:
|
if self.screen is self.filter_screen:
|
||||||
self.screen.NewFilter()
|
self.screen.NewFilter()
|
||||||
def OnFreedvMonitor(self, event):
|
|
||||||
for item in self.freedv_menu.GetMenuItems():
|
|
||||||
if item.GetKind() == wx.ITEM_CHECK:
|
|
||||||
if item.IsChecked():
|
|
||||||
QS.set_params(freedv_monitor=1)
|
|
||||||
else:
|
|
||||||
QS.set_params(freedv_monitor=0)
|
|
||||||
break
|
|
||||||
def OnFreedvMenu(self, event):
|
|
||||||
text = ''
|
|
||||||
for item in self.freedv_menu.GetMenuItems():
|
|
||||||
kind = item.GetKind()
|
|
||||||
if kind != wx.ITEM_RADIO:
|
|
||||||
continue
|
|
||||||
if item.IsChecked():
|
|
||||||
text = item.GetItemLabel()
|
|
||||||
break
|
|
||||||
for mode, index in conf.freedv_modes:
|
|
||||||
if mode == text:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print ("Failure in OnFreedvMenu")
|
|
||||||
return
|
|
||||||
mode = QS.freedv_set_options(mode=index)
|
|
||||||
if mode == index:
|
|
||||||
self.freedv_mode = text
|
|
||||||
elif not self.remote_control_slave: # change to new mode failed
|
|
||||||
self.freedv_menu_items[mode].Check(1)
|
|
||||||
pos = (self.width//2, self.height//2)
|
|
||||||
if index == 8:
|
|
||||||
dlg = wx.MessageDialog(self.main_frame, "Quisk does not install %s. Please install a system-wide codec2." % text, "FreeDV Modes", wx.OK, pos)
|
|
||||||
else:
|
|
||||||
dlg = wx.MessageDialog(self.main_frame, "No codec2 support for " + text, "FreeDV Modes", wx.OK, pos)
|
|
||||||
dlg.ShowModal()
|
|
||||||
else:
|
|
||||||
self.freedv_menu_items[mode].Check(1)
|
|
||||||
print ("FreeDV change mode failed.")
|
|
||||||
self.OnChangeFilter()
|
|
||||||
def OnChangeFilter(self):
|
|
||||||
# Used by OnFreedvMenu because the FreeDV mode may change the filter sample rate
|
|
||||||
bw = int(self.filterButns.GetLabel())
|
|
||||||
# We can not use QS.get_filter_srate() because the FreeDV mode is not changed yet.
|
|
||||||
if self.freedv_mode in ("Mode 2400A", "Mode 2400B"):
|
|
||||||
frate = 48000;
|
|
||||||
else:
|
|
||||||
frate = 8000;
|
|
||||||
bw = min(bw, frate // 2)
|
|
||||||
self.filter_bandwidth = bw
|
|
||||||
center = self.GetFilterCenter(self.mode, bw)
|
|
||||||
filtI, filtQ = self.MakeFilterCoef(frate, None, bw, center)
|
|
||||||
lower_edge = center - bw // 2
|
|
||||||
QS.set_filters(filtI, filtQ, bw, lower_edge, 0)
|
|
||||||
self.multi_rx_screen.graph.filter_bandwidth = bw
|
|
||||||
self.multi_rx_screen.graph.filter_center = center
|
|
||||||
self.multi_rx_screen.waterfall.pane1.filter_bandwidth = bw
|
|
||||||
self.multi_rx_screen.waterfall.pane1.filter_center = center
|
|
||||||
self.multi_rx_screen.waterfall.pane2.filter_bandwidth = bw
|
|
||||||
self.multi_rx_screen.waterfall.pane2.filter_center = center
|
|
||||||
if self.screen is self.filter_screen:
|
|
||||||
self.screen.NewFilter()
|
|
||||||
def OnBtnHelp(self, event):
|
def OnBtnHelp(self, event):
|
||||||
if event.GetEventObject().GetValue():
|
if event.GetEventObject().GetValue():
|
||||||
self.OnBtnScreen(None, 'Help')
|
self.OnBtnScreen(None, 'Help')
|
||||||
@@ -5881,7 +5799,7 @@ class App(wx.App):
|
|||||||
QS.set_split_rxtx(0)
|
QS.set_split_rxtx(0)
|
||||||
self.split_offset = self.txFreq - self.rxFreq
|
self.split_offset = self.txFreq - self.rxFreq
|
||||||
self.txFreq = self.rxFreq
|
self.txFreq = self.rxFreq
|
||||||
self.ChangeHwFrequency(self.txFreq, self.VFO, 'OnSplit', event=event)
|
self.ChangeHwFrequency(self.txFreq, self.VFO, 'OnSplit', event=event, rx_freq=self.rxFreq)
|
||||||
self.screen.SetTxFreq(self.txFreq, self.rxFreq)
|
self.screen.SetTxFreq(self.txFreq, self.rxFreq)
|
||||||
def OnMenuSplitPlay1(self, event):
|
def OnMenuSplitPlay1(self, event):
|
||||||
self.split_rxtx_play = 1
|
self.split_rxtx_play = 1
|
||||||
@@ -6085,11 +6003,6 @@ class App(wx.App):
|
|||||||
QS.set_squelch(self.levelSquelch / 12.0 - 120.0)
|
QS.set_squelch(self.levelSquelch / 12.0 - 120.0)
|
||||||
else:
|
else:
|
||||||
QS.set_squelch(-999.0)
|
QS.set_squelch(-999.0)
|
||||||
elif self.mode[0:3] == 'FDV':
|
|
||||||
if value:
|
|
||||||
QS.freedv_set_squelch_en(1)
|
|
||||||
else:
|
|
||||||
QS.freedv_set_squelch_en(0)
|
|
||||||
else:
|
else:
|
||||||
self.levelSquelchSSB = btn.slider_value
|
self.levelSquelchSSB = btn.slider_value
|
||||||
if value:
|
if value:
|
||||||
@@ -6103,11 +6016,18 @@ class App(wx.App):
|
|||||||
QS.set_auto_notch(0)
|
QS.set_auto_notch(0)
|
||||||
def OnBtnNR2(self, event):
|
def OnBtnNR2(self, event):
|
||||||
btn = event.GetEventObject()
|
btn = event.GetEventObject()
|
||||||
if btn.GetValue():
|
if btn.index == 1:
|
||||||
|
self.wdsp.put(self.wdsp.SetRXAEMNRgainMethod, self.wdsp_channel, 2)
|
||||||
self.wdsp.put(self.wdsp.SetRXAEMNRRun, self.wdsp_channel, 1)
|
self.wdsp.put(self.wdsp.SetRXAEMNRRun, self.wdsp_channel, 1)
|
||||||
QS.wdsp_set_parameter(self.wdsp_channel, in_use=1)
|
self.wdsp_NR2 = 1
|
||||||
|
elif btn.index == 2:
|
||||||
|
self.wdsp.put(self.wdsp.SetRXAEMNRgainMethod, self.wdsp_channel, 3)
|
||||||
|
self.wdsp.put(self.wdsp.SetRXAEMNRRun, self.wdsp_channel, 1)
|
||||||
|
self.wdsp_NR2 = 1
|
||||||
else:
|
else:
|
||||||
self.wdsp.put(self.wdsp.SetRXAEMNRRun, self.wdsp_channel, 0)
|
self.wdsp.put(self.wdsp.SetRXAEMNRRun, self.wdsp_channel, 0)
|
||||||
|
self.wdsp_NR2 = 0
|
||||||
|
QS.wdsp_set_parameter(self.wdsp_channel, in_use=self.wdsp_NR2 + self.wdsp_SNB)
|
||||||
def OnMenuNB(self, event):
|
def OnMenuNB(self, event):
|
||||||
# menu is ("NB 1", "NB 2", "NB 3", "SNB")
|
# menu is ("NB 1", "NB 2", "NB 3", "SNB")
|
||||||
for item in self.NB_menu.GetMenuItems():
|
for item in self.NB_menu.GetMenuItems():
|
||||||
@@ -6123,14 +6043,17 @@ class App(wx.App):
|
|||||||
if text == "SNB":
|
if text == "SNB":
|
||||||
QS.set_noise_blanker(0)
|
QS.set_noise_blanker(0)
|
||||||
self.wdsp.put(self.wdsp.SetRXASNBARun, self.wdsp_channel, 1)
|
self.wdsp.put(self.wdsp.SetRXASNBARun, self.wdsp_channel, 1)
|
||||||
QS.wdsp_set_parameter(self.wdsp_channel, in_use=1)
|
self.wdsp_SNB = 1
|
||||||
else:
|
else:
|
||||||
self.wdsp.put(self.wdsp.SetRXASNBARun, self.wdsp_channel, 0)
|
self.wdsp.put(self.wdsp.SetRXASNBARun, self.wdsp_channel, 0)
|
||||||
|
self.wdsp_SNB = 0
|
||||||
index = int(text[-1])
|
index = int(text[-1])
|
||||||
QS.set_noise_blanker(index)
|
QS.set_noise_blanker(index)
|
||||||
else: # Noise blanker Off
|
else: # Noise blanker Off
|
||||||
QS.set_noise_blanker(0)
|
QS.set_noise_blanker(0)
|
||||||
self.wdsp.put(self.wdsp.SetRXASNBARun, self.wdsp_channel, 0)
|
self.wdsp.put(self.wdsp.SetRXASNBARun, self.wdsp_channel, 0)
|
||||||
|
self.wdsp_SNB = 0
|
||||||
|
QS.wdsp_set_parameter(self.wdsp_channel, in_use=self.wdsp_NR2 + self.wdsp_SNB)
|
||||||
def FreqEntry(self, event):
|
def FreqEntry(self, event):
|
||||||
freq = event.GetString()
|
freq = event.GetString()
|
||||||
win = event.GetEventObject()
|
win = event.GetEventObject()
|
||||||
@@ -6775,11 +6698,13 @@ class App(wx.App):
|
|||||||
if not self.tx_indicator:
|
if not self.tx_indicator:
|
||||||
self.tx_indicator = True
|
self.tx_indicator = True
|
||||||
self.pttButton.Tx.TurnOn(True)
|
self.pttButton.Tx.TurnOn(True)
|
||||||
|
QS.tci_set_params(tci_trx=1)
|
||||||
else:
|
else:
|
||||||
if self.tx_indicator:
|
if self.tx_indicator:
|
||||||
self.tx_indicator = False
|
self.tx_indicator = False
|
||||||
self.pttButton.Tx.TurnOn(False)
|
self.pttButton.Tx.TurnOn(False)
|
||||||
if True: # Manage the PTT button using serial port, VOX, hot keys, WAV file play, PTT button, MIDI, CAT
|
QS.tci_set_params(tci_trx=0)
|
||||||
|
if True: # Manage the PTT button using serial port, VOX, hot keys, WAV file play, PTT button, MIDI, CAT, TCI
|
||||||
ptt_button_down = self.pttButton.GetValue()
|
ptt_button_down = self.pttButton.GetValue()
|
||||||
ptt = None
|
ptt = None
|
||||||
if conf.quisk_serial_cts[0:4] == "PTT " or conf.quisk_serial_dsr[0:4] == "PTT ":
|
if conf.quisk_serial_cts[0:4] == "PTT " or conf.quisk_serial_dsr[0:4] == "PTT ":
|
||||||
@@ -6834,6 +6759,14 @@ class App(wx.App):
|
|||||||
self.hot_key_ptt_pressed = False
|
self.hot_key_ptt_pressed = False
|
||||||
if self.tx_inhibit:
|
if self.tx_inhibit:
|
||||||
ptt = False
|
ptt = False
|
||||||
|
tci = QS.tci_get_params("tci_trx")
|
||||||
|
if tci is not None:
|
||||||
|
if tci:
|
||||||
|
self.tci_ptt_active = True
|
||||||
|
ptt = True
|
||||||
|
elif self.tci_ptt_active:
|
||||||
|
self.tci_ptt_active = False
|
||||||
|
ptt = False
|
||||||
if ptt is True and not ptt_button_down:
|
if ptt is True and not ptt_button_down:
|
||||||
self.SetPTT(True)
|
self.SetPTT(True)
|
||||||
elif ptt is False and ptt_button_down:
|
elif ptt is False and ptt_button_down:
|
||||||
@@ -6873,10 +6806,7 @@ class App(wx.App):
|
|||||||
d = 1E-10
|
d = 1E-10
|
||||||
self.smeter.SetLabel(" ADC %.0f%% %.0fdB" % (d * 100.0, 20 * math.log10(d)))
|
self.smeter.SetLabel(" ADC %.0f%% %.0fdB" % (d * 100.0, 20 * math.log10(d)))
|
||||||
elif self.smeter_usage == "smeter": # update the S-meter
|
elif self.smeter_usage == "smeter": # update the S-meter
|
||||||
if self.mode in ('FDV-U', 'FDV-L'):
|
self.NewSmeter()
|
||||||
self.NewDVmeter()
|
|
||||||
else:
|
|
||||||
self.NewSmeter()
|
|
||||||
elif self.smeter_usage == "freq":
|
elif self.smeter_usage == "freq":
|
||||||
self.MeasureFrequency() # display measured frequency
|
self.MeasureFrequency() # display measured frequency
|
||||||
else:
|
else:
|
||||||
@@ -6901,10 +6831,57 @@ class App(wx.App):
|
|||||||
if self.timer - self.clip_time0 > 1.0:
|
if self.timer - self.clip_time0 > 1.0:
|
||||||
self.clip_time0 = 0
|
self.clip_time0 = 0
|
||||||
self.freqDisplay.Clip(0)
|
self.freqDisplay.Clip(0)
|
||||||
|
if QS.tci_get_params("tci_clients_count") > 0:
|
||||||
|
# These are change requests from TCI clients. All must be echoed with the new value from Quisk.
|
||||||
|
# modulation
|
||||||
|
mode = QS.tci_get_params("tci_modulation")
|
||||||
|
if mode is not None: # TCI set new modulation
|
||||||
|
self.modeButns.SetLabel(mode, True)
|
||||||
|
self.tci_modulation = self.mode
|
||||||
|
QS.tci_set_params(tci_modulation=self.tci_modulation)
|
||||||
|
# split
|
||||||
|
split = QS.tci_get_params("tci_split_enable")
|
||||||
|
if split is not None: # TCI set split_enable
|
||||||
|
self.splitButton.SetValue(split, True)
|
||||||
|
self.tci_split_enable = self.split_rxtx
|
||||||
|
QS.tci_set_params(tci_split_enable=self.tci_split_enable)
|
||||||
|
# tci dds == self.VFO
|
||||||
|
tci_dds = QS.tci_get_params("tci_dds")
|
||||||
|
if tci_dds is not None:
|
||||||
|
self.ChangeHwFrequency(0, tci_dds, 'TCIrequest')
|
||||||
|
self.tci_dds = self.VFO
|
||||||
|
QS.tci_set_params(tci_dds=self.tci_dds)
|
||||||
|
# tci_vfo == self.VFO + self.rxFreq
|
||||||
|
tci_vfo = QS.tci_get_params("tci_vfo")
|
||||||
|
if tci_vfo is not None:
|
||||||
|
self.ChangeRxTxFrequency(tci_vfo, None)
|
||||||
|
self.tci_vfo = self.VFO + self.rxFreq
|
||||||
|
QS.tci_set_params(tci_vfo=self.tci_vfo)
|
||||||
|
# tci_trx handled elsewhere
|
||||||
if self.timer - self.heart_time0 > 0.10: # call hardware to perform background tasks:
|
if self.timer - self.heart_time0 > 0.10: # call hardware to perform background tasks:
|
||||||
self.heart_time0 = self.timer
|
self.heart_time0 = self.timer
|
||||||
Hardware.HeartBeat()
|
Hardware.HeartBeat()
|
||||||
if conf.hermes_lite2_enable:
|
if QS.tci_get_params("tci_clients_count") > 0: # limit the speed of frequency updates
|
||||||
|
# these are changes made in Quisk that must be broadcast to all clients.
|
||||||
|
# modulation
|
||||||
|
if self.tci_modulation != self.mode:
|
||||||
|
self.tci_modulation = self.mode
|
||||||
|
QS.tci_set_params(tci_modulation=self.tci_modulation)
|
||||||
|
# split
|
||||||
|
if self.tci_split_enable != self.split_rxtx:
|
||||||
|
self.tci_split_enable = self.split_rxtx
|
||||||
|
QS.tci_set_params(tci_split_enable=self.tci_split_enable)
|
||||||
|
# tci dds == self.VFO
|
||||||
|
if self.tci_dds != self.VFO:
|
||||||
|
self.tci_dds = self.VFO
|
||||||
|
QS.tci_set_params(tci_dds=self.tci_dds)
|
||||||
|
# tci_vfo == self.VFO + self.rxFreq
|
||||||
|
tci_vfo = self.VFO + self.rxFreq
|
||||||
|
if self.tci_vfo != tci_vfo:
|
||||||
|
self.tci_vfo = tci_vfo
|
||||||
|
QS.tci_set_params(tci_vfo=self.tci_vfo)
|
||||||
|
# tci_trx handled elsewhere
|
||||||
|
if self.is_HermesLite2:
|
||||||
self.tx_inhibit = QS.get_params('quisk_tx_inhibit')
|
self.tx_inhibit = QS.get_params('quisk_tx_inhibit')
|
||||||
else:
|
else:
|
||||||
self.tx_inhibit = 0
|
self.tx_inhibit = 0
|
||||||
|
|||||||
+151
-40
@@ -48,7 +48,7 @@ quisk_widgets = None
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
################ Receivers SoftRock USB, Devices controlled by USB that capture samples from a sound card, and (for Tx) play samples to a sound card
|
################ Receivers SoftRock USB, Sound card radios controlled by USB.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'softrock/hardware_usb.py'
|
#hardware_file_name = 'softrock/hardware_usb.py'
|
||||||
@@ -133,7 +133,7 @@ digital_tx_level = 100
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
################ Receivers SoftRock Fixed, Fixed frequency devices that capture samples from a sound card, and (for Tx) play samples to a sound card
|
################ Receivers SoftRock Fixed, Fixed frequency sound card radios.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'quisk_hardware_fixed.py'
|
#hardware_file_name = 'quisk_hardware_fixed.py'
|
||||||
@@ -174,7 +174,7 @@ digital_tx_level = 100
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
################ Receivers HiQSDR, The original N2ADR hardware and the improved HiQSDR using UDP
|
################ Receivers HiQSDR, The original N2ADR and HiQSDR radios.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'hiqsdr/quisk_hardware.py'
|
#hardware_file_name = 'hiqsdr/quisk_hardware.py'
|
||||||
@@ -258,7 +258,7 @@ sndp_active = True
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
################ Receivers Hermes, The Hermes-Lite Project and possibly other hardware with the Hermes FPGA code.
|
################ Receivers Hermes, The Hermes-Lite project by Steve Haynal.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'hermes/quisk_hardware.py'
|
#hardware_file_name = 'hermes/quisk_hardware.py'
|
||||||
@@ -275,7 +275,7 @@ sndp_active = True
|
|||||||
# You can set these options:
|
# You can set these options:
|
||||||
|
|
||||||
## use_rx_udp Hardware type, integer choice
|
## use_rx_udp Hardware type, integer choice
|
||||||
# This is the type of UDP hardware. Use 10 for the Hermes protocol.
|
# This is the type of UDP hardware. Use 10 for the Hermes Lite2 protocol.
|
||||||
#use_rx_udp = 10
|
#use_rx_udp = 10
|
||||||
|
|
||||||
## udp_rx_ip Hermes known IP, text
|
## udp_rx_ip Hermes known IP, text
|
||||||
@@ -373,6 +373,7 @@ hermes_disable_sync = False
|
|||||||
# be sure to write it down. To use this address you must set "Eeprom IP Usage".
|
# be sure to write it down. To use this address you must set "Eeprom IP Usage".
|
||||||
# And you may want to set "Hermes known IP" too. Make sure the address does not conflict
|
# And you may want to set "Hermes known IP" too. Make sure the address does not conflict
|
||||||
# with your DHCP server.
|
# with your DHCP server.
|
||||||
|
# Power cycle the HL2 after changing this item.
|
||||||
Hware_Hl2_EepromIP = '192.168.1.6'
|
Hware_Hl2_EepromIP = '192.168.1.6'
|
||||||
#Hware_Hl2_EepromIP = '192.168.1.241'
|
#Hware_Hl2_EepromIP = '192.168.1.241'
|
||||||
|
|
||||||
@@ -380,6 +381,7 @@ Hware_Hl2_EepromIP = '192.168.1.6'
|
|||||||
# This is the way the EEPROM IP address is used at power on.
|
# This is the way the EEPROM IP address is used at power on.
|
||||||
# "Ignore" means it is not used at all. "Set address" means DHCP is not used and the EEPROM IP address is used.
|
# "Ignore" means it is not used at all. "Set address" means DHCP is not used and the EEPROM IP address is used.
|
||||||
# "Use DHCP first" means the EEPROM IP address is only used if DHCP fails.
|
# "Use DHCP first" means the EEPROM IP address is only used if DHCP fails.
|
||||||
|
# Power cycle the HL2 after changing this item.
|
||||||
Hware_Hl2_EepromIPUse = 'Ignore'
|
Hware_Hl2_EepromIPUse = 'Ignore'
|
||||||
#Hware_Hl2_EepromIPUse = 'Use DHCP first'
|
#Hware_Hl2_EepromIPUse = 'Use DHCP first'
|
||||||
#Hware_Hl2_EepromIPUse = 'Set address'
|
#Hware_Hl2_EepromIPUse = 'Set address'
|
||||||
@@ -390,6 +392,7 @@ Hware_Hl2_EepromIPUse = 'Ignore'
|
|||||||
# at power on instead of the factory supplied MAC address 0:1c:c0:a2:13:dd. It is only used at power on,
|
# at power on instead of the factory supplied MAC address 0:1c:c0:a2:13:dd. It is only used at power on,
|
||||||
# and it is only used if it is marked valid by setting "Eeprom MAC usage" to "Set address".
|
# and it is only used if it is marked valid by setting "Eeprom MAC usage" to "Set address".
|
||||||
# If you have two HL2's, you must have a unique MAC address for each.
|
# If you have two HL2's, you must have a unique MAC address for each.
|
||||||
|
# Power cycle the HL2 after changing this item.
|
||||||
Hware_Hl2_EepromMAC = '0xA1 0x6B'
|
Hware_Hl2_EepromMAC = '0xA1 0x6B'
|
||||||
#Hware_Hl2_EepromMAC = '0x4C 0x33'
|
#Hware_Hl2_EepromMAC = '0x4C 0x33'
|
||||||
|
|
||||||
@@ -398,11 +401,13 @@ Hware_Hl2_EepromMAC = '0xA1 0x6B'
|
|||||||
# the HL2 will use the last two MAC bytes from "Eeprom MAC address"; otherwise the default MAC is used.
|
# the HL2 will use the last two MAC bytes from "Eeprom MAC address"; otherwise the default MAC is used.
|
||||||
# If you change this item, the bit is written to the HL2.
|
# If you change this item, the bit is written to the HL2.
|
||||||
# The HL2 only uses these bits at power on.
|
# The HL2 only uses these bits at power on.
|
||||||
|
# Power cycle the HL2 after changing this item.
|
||||||
Hware_Hl2_EepromMACUse = 'Ignore'
|
Hware_Hl2_EepromMACUse = 'Ignore'
|
||||||
#Hware_Hl2_EepromMACUse = 'Set address'
|
#Hware_Hl2_EepromMACUse = 'Set address'
|
||||||
|
|
||||||
## hermes_TxLNA_dB LNA during Tx dB, integer
|
## hermes_TxLNA_dB LNA during Tx dB, integer
|
||||||
# During transmit the low noise Rx amplifier gain changes to this value (in dB) if the hardware supports it.
|
# During transmit the low noise Rx amplifier gain changes to this value (in dB).
|
||||||
|
# The LNA gain is -12 to 48 dB. Use -12 for Pure Signal.
|
||||||
# Changes are immediate (no need to restart).
|
# Changes are immediate (no need to restart).
|
||||||
hermes_TxLNA_dB = -12
|
hermes_TxLNA_dB = -12
|
||||||
|
|
||||||
@@ -473,13 +478,6 @@ hermes_iob_rxin = 'J10 available'
|
|||||||
#hermes_iob_rxin = 'HL2 Rx to J9'
|
#hermes_iob_rxin = 'HL2 Rx to J9'
|
||||||
#hermes_iob_rxin = 'Use J9 and J10'
|
#hermes_iob_rxin = 'Use J9 and J10'
|
||||||
|
|
||||||
## hermes_lite2_enable Hermes Lite2 protocol, boolean
|
|
||||||
# This turns on some of the protocol extensions for the Hermes Lite2 that are not part
|
|
||||||
# of the official Hermes Protocol 1. Currently this only enables Tx Inhibit (CN8).
|
|
||||||
# Changes are immediate (no need to restart).
|
|
||||||
hermes_lite2_enable = True
|
|
||||||
#hermes_lite2_enable = False
|
|
||||||
|
|
||||||
|
|
||||||
# These are known power meter calibration tables. This table is not present in the JSON settings file.
|
# These are known power meter calibration tables. This table is not present in the JSON settings file.
|
||||||
power_meter_std_calibrations = {}
|
power_meter_std_calibrations = {}
|
||||||
@@ -490,6 +488,7 @@ power_meter_std_calibrations['HL2FilterE3'] = [[ 0, 0.0 ], [ 25.865384615384617,
|
|||||||
[ 4093.1785714285716, 7.28994845808807 ], [ 4502.496428571429, 8.820837634286566 ], [ 4952.746071428572, 10.673213537486745 ] ]
|
[ 4093.1785714285716, 7.28994845808807 ], [ 4502.496428571429, 8.820837634286566 ], [ 4952.746071428572, 10.673213537486745 ] ]
|
||||||
#power_meter_std_calibrations['HL2FilterE1'] = [[0, 0.0], [9.07, 0.002], [54.98, 0.014], [148.6, 0.057],
|
#power_meter_std_calibrations['HL2FilterE1'] = [[0, 0.0], [9.07, 0.002], [54.98, 0.014], [148.6, 0.057],
|
||||||
# [328.0, 0.208], [611.1, 0.646], [807.0, 1.098], [982.1, 1.6], [1223.3, 2.471], [1517.7, 3.738], [1758.7, 5.02]]
|
# [328.0, 0.208], [611.1, 0.646], [807.0, 1.098], [982.1, 1.6], [1223.3, 2.471], [1517.7, 3.738], [1758.7, 5.02]]
|
||||||
|
power_meter_std_calibrations['Show ADC'] = []
|
||||||
|
|
||||||
## Hermes_BandDict Rx IO Bus, dict
|
## Hermes_BandDict Rx IO Bus, dict
|
||||||
# The Hermes_BandDict sets the 7 bits on the J16 connector for Rx.
|
# The Hermes_BandDict sets the 7 bits on the J16 connector for Rx.
|
||||||
@@ -507,17 +506,17 @@ Hermes_BandDictEnTx = False
|
|||||||
#Hermes_BandDictEnTx = True
|
#Hermes_BandDictEnTx = True
|
||||||
|
|
||||||
|
|
||||||
################ Receivers Red Pitaya, The Red Pitaya Project by Pavel Demin. This uses the Hermes FPGA code.
|
################ Receivers Red Pitaya, The Red Pitaya project by Pavel Demin.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'hermes/quisk_hardware.py'
|
#hardware_file_name = 'hermes/quisk_hardware.py'
|
||||||
|
|
||||||
## widgets_file_name Widget file path, rfile
|
## widgets_file_name Widget file path, rfile
|
||||||
# This optional file adds additional controls for the radio.
|
# This optional file adds additional controls for the radio.
|
||||||
#widgets_file_name = ''
|
#widgets_file_name = 'hermes/hpsdr_widgets.py'
|
||||||
|
|
||||||
## use_rx_udp Hardware type, integer choice
|
## use_rx_udp Hardware type, integer choice
|
||||||
# This is the type of UDP hardware. Use 10 for the Hermes protocol.
|
# This is the type of UDP hardware. Use 10 for the Red Pitaya using the OpenHPSDR protocol.
|
||||||
#use_rx_udp = 10
|
#use_rx_udp = 10
|
||||||
|
|
||||||
## rx_udp_ip IP change, text
|
## rx_udp_ip IP change, text
|
||||||
@@ -585,7 +584,123 @@ Hermes_BandDictEnTx = False
|
|||||||
#Hermes_BandDictEnTx = True
|
#Hermes_BandDictEnTx = True
|
||||||
|
|
||||||
|
|
||||||
################ Receivers SoapySDR, The SoapySDR interface to multiple hardware SDRs.
|
################ Receivers OpenHPSDR, Radios using the HPSDR Protocol 1.
|
||||||
|
## hardware_file_name Hardware file path, rfile
|
||||||
|
# This is the file that contains the control logic for each radio.
|
||||||
|
#hardware_file_name = 'hermes/quisk_hardware.py'
|
||||||
|
|
||||||
|
## widgets_file_name Widget file path, rfile
|
||||||
|
# This optional file adds additional controls for the radio.
|
||||||
|
#widgets_file_name = 'hermes/hpsdr_widgets.py'
|
||||||
|
|
||||||
|
## use_rx_udp Hardware type, integer choice
|
||||||
|
# This is the type of UDP hardware. Use 10 for hardware using the OpenHPSDR protocol 1.
|
||||||
|
#use_rx_udp = 10
|
||||||
|
|
||||||
|
## rx_udp_ip IP change, text
|
||||||
|
# This item should be left blank. It is used to change the IP address of the hardware to a different
|
||||||
|
# IP once the hardware is found. Not all Hermes firmware supports changing the IP address.
|
||||||
|
#rx_udp_ip = ""
|
||||||
|
|
||||||
|
## rx_udp_port Hardware UDP port, integer
|
||||||
|
# This is the UDP port number of your hardware.
|
||||||
|
#rx_udp_port = 1024
|
||||||
|
|
||||||
|
## rx_udp_ip_netmask Network netmask, text
|
||||||
|
# This is the netmask for the network.
|
||||||
|
#rx_udp_ip_netmask = '255.255.255.0'
|
||||||
|
|
||||||
|
## tx_ip Transmit IP, text
|
||||||
|
# Leave this blank to use the same IP address as the receive hardware. Otherwise, enter "disable"
|
||||||
|
# to disable sending transmit I/Q samples, or enter the actual IP address. You must enter "disable"
|
||||||
|
# if you have multiple hardwares on the network, and only one should transmit. This item is normally blank.
|
||||||
|
tx_ip = ""
|
||||||
|
#tx_ip = "disable"
|
||||||
|
|
||||||
|
## tx_audio_port Tx audio UDP port, integer
|
||||||
|
# This is the UDP port for transmit audio I/Q samples. Enter zero to calculate this from the
|
||||||
|
# base hardware port. Otherwise enter the special custom port.
|
||||||
|
tx_audio_port = 0
|
||||||
|
|
||||||
|
## rx_udp_clock Clock frequency Hertz, integer
|
||||||
|
# This is the clock frequency of the hardware in Hertz.
|
||||||
|
#rx_udp_clock = 122880000
|
||||||
|
#rx_udp_clock = 76800000
|
||||||
|
#rx_udp_clock = 125000000
|
||||||
|
|
||||||
|
## tx_level Tx Level, dict
|
||||||
|
# This sets the transmit level 0 to 255 for each band.
|
||||||
|
# The config screen has a slider 0 to 100% so you can reduce the transmit power.
|
||||||
|
# The hardware only supports a limited adjustment range, so zero is still a small amount of power.
|
||||||
|
tx_level = {}
|
||||||
|
|
||||||
|
## digital_tx_level Digital Tx power %, integer
|
||||||
|
# Digital modes reduce power by the percentage on the config screen.
|
||||||
|
# This is the maximum value of the slider.
|
||||||
|
#digital_tx_level = 100
|
||||||
|
|
||||||
|
## hermes_code_version Hermes code version, integer
|
||||||
|
# There can be multiple Hermes devices on a network, but Quisk can only use one of these. If you have multiple
|
||||||
|
# hermes devices, you can use this to specify a unique device. Or use -1 to accept any board.
|
||||||
|
hermes_code_version = -1
|
||||||
|
|
||||||
|
## hermes_board_id Hermes board ID, integer
|
||||||
|
# There can be multiple Hermes devices on a network, but Quisk can only use one of these. If you have multiple
|
||||||
|
# hermes devices, you can use this to specify a unique device. Or use -1 to accept any board.
|
||||||
|
hermes_board_id = -1
|
||||||
|
|
||||||
|
## Hermes_BandDict Hermes Bus, dict
|
||||||
|
# The Hermes_BandDict sets the 7 bits on the J16 connector.
|
||||||
|
Hermes_BandDict = {
|
||||||
|
'160':0b0000001, '80':0b0000010, '60':0b0000100, '40':0b0001000, '30':0b0010000, '20':0b0100000, '15':0b1000000}
|
||||||
|
|
||||||
|
## Hermes_BandDictTx Tx IO Bus, dict
|
||||||
|
# The Hermes_BandDictTx sets the 7 bits on the J16 connector for Tx if enabled.
|
||||||
|
Hermes_BandDictTx = {'160':0, '80':0, '60':0, '40':0, '30':0, '20':0, '17':0, '15':0, '12':0, '10':0}
|
||||||
|
|
||||||
|
## Hermes_BandDictEnTx Enable Tx Filt, boolean
|
||||||
|
# Enable the separate Rx and Tx settings for the J16 connector.
|
||||||
|
Hermes_BandDictEnTx = False
|
||||||
|
#Hermes_BandDictEnTx = True
|
||||||
|
|
||||||
|
## calibrate_temp_20 PA temp ADC at 20\u2103, integer
|
||||||
|
# This is the ADC code returned for a temperature of 20C.
|
||||||
|
# To see the ADC code being returned, enter zero for both temperature codes.
|
||||||
|
# Linear interpolation is used to calculate the temperature.
|
||||||
|
# Changes are immediate (no need to restart).
|
||||||
|
calibrate_temp_20 = 0
|
||||||
|
|
||||||
|
## calibrate_temp_40 PA temp ADC at 40\u2103, integer
|
||||||
|
# This is the ADC code returned for a temperature of 40C.
|
||||||
|
# To see the ADC code being returned, enter zero for both temperature codes.
|
||||||
|
# Linear interpolation is used to calculate the temperature.
|
||||||
|
# Changes are immediate (no need to restart).
|
||||||
|
calibrate_temp_40 = 0
|
||||||
|
|
||||||
|
## calibrate_current_0 PA current ADC at zero, integer
|
||||||
|
# This is the ADC code returned for a PA current of zero.
|
||||||
|
# To see the ADC code being returned, enter zero for both current codes.
|
||||||
|
# Linear interpolation is used to calculate the current.
|
||||||
|
# Changes are immediate (no need to restart).
|
||||||
|
calibrate_current_0 = 0
|
||||||
|
|
||||||
|
## calibrate_current_1 PA current ADC at 1 amp, integer
|
||||||
|
# This is the ADC code returned for a PA current of one amp.
|
||||||
|
# To see the ADC code being returned, enter zero for both current codes.
|
||||||
|
# Linear interpolation is used to calculate the current.
|
||||||
|
# Changes are immediate (no need to restart).
|
||||||
|
calibrate_current_1 = 0
|
||||||
|
|
||||||
|
## power_meter_calib_name Power meter calibration, text choice
|
||||||
|
# This is the calibration table used to convert the power sensor voltage measured by the ADC to the transmit power display.
|
||||||
|
# It is a table of ADC codes and the corresponding measured power level. If you have a power meter, you can create your own
|
||||||
|
# table by selecting "New". Then enter ten or more power measurements from low to full power.
|
||||||
|
# For the Hermes Lite2 version E3 filter board, use the built-in table "HL2FilterE3".
|
||||||
|
# Changes are immediate (no need to restart).
|
||||||
|
power_meter_calib_name = 'HL2FilterE3'
|
||||||
|
|
||||||
|
|
||||||
|
################ Receivers SoapySDR, The SoapySDR interface to multiple SDRs.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'soapypkg/quisk_hardware.py'
|
#hardware_file_name = 'soapypkg/quisk_hardware.py'
|
||||||
@@ -601,7 +716,7 @@ Hermes_BandDictEnTx = False
|
|||||||
# Further items are present in the radio dictionary with names soapy_*
|
# Further items are present in the radio dictionary with names soapy_*
|
||||||
|
|
||||||
|
|
||||||
################ Receivers SdrIQ, The SDR-IQ receiver by RfSpace
|
################ Receivers SdrIQ, The SDR-IQ receiver by RfSpace.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
hardware_file_name = 'quisk_hardware_sdriq.py'
|
hardware_file_name = 'quisk_hardware_sdriq.py'
|
||||||
@@ -632,7 +747,7 @@ sdriq_decimation = 1250
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
################ Receivers Odyssey, The Odyssey project using a UDP protocol similar to the HiQSDR
|
################ Receivers Odyssey, The Odyssey project.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'hiqsdr/quisk_hardware.py'
|
#hardware_file_name = 'hiqsdr/quisk_hardware.py'
|
||||||
@@ -743,7 +858,7 @@ sndp_active = True
|
|||||||
#radio_sound_mic_boost = True
|
#radio_sound_mic_boost = True
|
||||||
|
|
||||||
|
|
||||||
################ Receivers Odyssey2, The Odyssey-2 project using the HPSDR Hermes protocol
|
################ Receivers Odyssey2, The Odyssey-2 project.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'hermes/quisk_hardware.py'
|
#hardware_file_name = 'hermes/quisk_hardware.py'
|
||||||
@@ -758,7 +873,7 @@ sndp_active = True
|
|||||||
# You can set these options:
|
# You can set these options:
|
||||||
|
|
||||||
## use_rx_udp Hardware type, integer choice
|
## use_rx_udp Hardware type, integer choice
|
||||||
# This is the type of UDP hardware. Use 10 for the Hermes protocol.
|
# This is the type of UDP hardware. Use 10 for the Odyssey-2.
|
||||||
#use_rx_udp = 10
|
#use_rx_udp = 10
|
||||||
|
|
||||||
## rx_udp_ip IP change, text
|
## rx_udp_ip IP change, text
|
||||||
@@ -827,7 +942,7 @@ Hermes_BandDictEnTx = False
|
|||||||
#Hermes_BandDictEnTx = True
|
#Hermes_BandDictEnTx = True
|
||||||
|
|
||||||
|
|
||||||
################ Receivers Afedri, The Afedri SDR receiver with the Ethernet interface.
|
################ Receivers Afedri, The Afedri SDR receiver with Ethernet.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# This is the file that contains the control logic for each radio.
|
# This is the file that contains the control logic for each radio.
|
||||||
#hardware_file_name = 'afedrinet/quisk_hardware.py'
|
#hardware_file_name = 'afedrinet/quisk_hardware.py'
|
||||||
@@ -859,7 +974,7 @@ Hermes_BandDictEnTx = False
|
|||||||
#default_rf_gain = 11
|
#default_rf_gain = 11
|
||||||
|
|
||||||
|
|
||||||
################ Receivers Control Head, Use Quisk to control a remote radio. No real hardware is used here.
|
################ Receivers Control Head, Use Quisk to control a remote Quisk radio.
|
||||||
## hardware_file_name Hardware file path, rfile
|
## hardware_file_name Hardware file path, rfile
|
||||||
# Quisk can be used as a control head to control a real radio located remotely.
|
# Quisk can be used as a control head to control a real radio located remotely.
|
||||||
# This file contains the control logic for remote radios of type Hermes, SoftRock or HiQSDR.
|
# This file contains the control logic for remote radios of type Hermes, SoftRock or HiQSDR.
|
||||||
@@ -1308,6 +1423,19 @@ fixed_tune_offset = 0
|
|||||||
|
|
||||||
|
|
||||||
################ Remote
|
################ Remote
|
||||||
|
## tci_ip IP address for TCI, text
|
||||||
|
# Quisk can connect to WSJT-X and other digital programs using the TCI protocol.
|
||||||
|
# Enter the TCI server host name.
|
||||||
|
tci_ip = "127.0.0.1"
|
||||||
|
|
||||||
|
## tci_port IP port for TCI, integer
|
||||||
|
# Quisk can connect to WSJT-X and other digital programs using the TCI protocol.
|
||||||
|
# Enter the TCI port number. If the port number is zero, TCI is turned off.
|
||||||
|
# The preferred port is 40001.
|
||||||
|
tci_port = 0
|
||||||
|
#tci_port = 40001
|
||||||
|
#tci_port = 50001
|
||||||
|
|
||||||
## hamlib_ip IP address for Hamlib Rig 2, text
|
## hamlib_ip IP address for Hamlib Rig 2, text
|
||||||
# You can control Quisk from Hamlib. Set the Hamlib rig to 2 and the device for rig 2 to
|
# You can control Quisk from Hamlib. Set the Hamlib rig to 2 and the device for rig 2 to
|
||||||
# localhost:4575. Or choose a different name and port here. Set the same name and port
|
# localhost:4575. Or choose a different name and port here. Set the same name and port
|
||||||
@@ -1796,23 +1924,6 @@ add_extern_demod = ""
|
|||||||
add_fdx_button = 0
|
add_fdx_button = 0
|
||||||
#add_fdx_button = 1
|
#add_fdx_button = 1
|
||||||
|
|
||||||
## add_freedv_button Add FreeDv button, integer choice
|
|
||||||
# These buttons add up to two additional mode buttons after CW, USB, etc.
|
|
||||||
# Set this to add the FDV mode button for digital voice:
|
|
||||||
add_freedv_button = 1
|
|
||||||
#add_freedv_button = 0
|
|
||||||
|
|
||||||
## freedv_tx_msg FreeDv Tx message, text
|
|
||||||
# For freedv, this is the text message to send.
|
|
||||||
freedv_tx_msg = ''
|
|
||||||
#freedv_tx_msg = 'N2XXX Jim, New Jersey, USA \n'
|
|
||||||
|
|
||||||
|
|
||||||
# This is the list of FreeDV modes and their index number. The starting mode is the first listed.
|
|
||||||
freedv_modes = (('Mode 1600', 0),
|
|
||||||
('Mode 2400A', 3), ('Mode 2400B', 4), ('Mode 800XA', 5),
|
|
||||||
('Mode 700C', 6), ('Mode 700D', 7), ('Mode 700E', 13), ('Mode 2020', 8))
|
|
||||||
|
|
||||||
# These are the filter bandwidths for each mode. Quisk has built-in optimized filters
|
# These are the filter bandwidths for each mode. Quisk has built-in optimized filters
|
||||||
# for these values, but you can change them if you want.
|
# for these values, but you can change them if you want.
|
||||||
FilterBwCW = (200, 400, 600, 1000, 1500, 3000)
|
FilterBwCW = (200, 400, 600, 1000, 1500, 3000)
|
||||||
|
|||||||
+37
-27
@@ -1,61 +1,71 @@
|
|||||||
// This module provides C access to the WDSP SDR library.
|
// This module provides C access to the WDSP SDR library.
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <complex.h>
|
||||||
|
|
||||||
#define CLIP32 2147483647
|
#define CLIP32 2147483647
|
||||||
#define CLIP16 32767
|
#define CLIP16 32767
|
||||||
|
|
||||||
#define MAX_CHANNELS 32
|
#define MAX_CHANNELS 32
|
||||||
#define SAMPLE_BYTES (sizeof(double) * 2)
|
|
||||||
|
|
||||||
static struct _channel {
|
static struct _channel {
|
||||||
double * cBuf; // save samples until nBuf reaches in_size
|
complex double * cBuf; // circular sample buffer; save samples until nBuf reaches in_size
|
||||||
int sizeBuf; // current size of cBuf in count of complex samples
|
int sizeBuf; // current size of cBuf in complex samples; a multiple of in_size
|
||||||
int nBuf; // current number of complex samples in cBuf
|
int nBuf; // current number of complex samples in cBuf
|
||||||
int in_size; // count of complex samples needed for wdsp_fexchange0
|
int in_size; // count of complex samples needed for wdsp_fexchange0
|
||||||
int in_use; // is this channel being used?
|
int in_use; // is this channel being used?
|
||||||
|
int Windex; // index of write
|
||||||
|
int Rindex; // index of read
|
||||||
} wdspChannel[MAX_CHANNELS];
|
} wdspChannel[MAX_CHANNELS];
|
||||||
|
|
||||||
static void (*wdsp_fexchange0) (int channel, double * in, double * out, int * error);
|
static void (*wdsp_fexchange0) (int channel, double * in, double * out, int * error);
|
||||||
|
|
||||||
int wdspFexchange0(int channel, double * cSamples, int nSamples)
|
int wdspFexchange0(int channel, complex double * cSamples, int nSamples)
|
||||||
{
|
{
|
||||||
// Call with an arbitrary number of input samples nSamples. cSamples is scaled to CLIP32.
|
// Call with an arbitrary number of input samples nSamples. cSamples is scaled to CLIP32.
|
||||||
// When we have enough samples process the samples by calling wdsp. Return the samples in the same array.
|
// When we have enough samples process the samples by calling wdsp. Return the samples in cSamples.
|
||||||
// cSamples is complex double. We treat it as interleaved Real/Imag doubles twice as long.
|
// cSamples is complex double. nSamples is the count of complex samples.
|
||||||
// nSamples is the count of complex samples.
|
|
||||||
struct _channel * ptChannel = wdspChannel + channel;
|
struct _channel * ptChannel = wdspChannel + channel;
|
||||||
int i, error;
|
int i, error, in_size;
|
||||||
double * ptD, * ptS;
|
|
||||||
|
|
||||||
if ( ! ptChannel->in_use)
|
if ( ! ptChannel->in_use) {
|
||||||
|
ptChannel->Windex = 0;
|
||||||
|
ptChannel->Rindex = 0;
|
||||||
|
ptChannel->nBuf = 0;
|
||||||
return nSamples;
|
return nSamples;
|
||||||
|
}
|
||||||
if ( ! wdsp_fexchange0)
|
if ( ! wdsp_fexchange0)
|
||||||
return nSamples;
|
return nSamples;
|
||||||
if (nSamples <= 0)
|
if (nSamples <= 0)
|
||||||
return nSamples;
|
return nSamples;
|
||||||
if (ptChannel->nBuf + nSamples >= ptChannel->sizeBuf) {
|
in_size = ptChannel->in_size;
|
||||||
ptChannel->sizeBuf = ptChannel->nBuf + nSamples * 3;
|
i = nSamples / in_size + 3; // blocks need for samples plus a partial block
|
||||||
ptChannel->cBuf = (double *)realloc(ptChannel->cBuf, ptChannel->sizeBuf * SAMPLE_BYTES);
|
if (i * in_size > ptChannel->sizeBuf) {
|
||||||
|
i *= in_size;
|
||||||
|
ptChannel->sizeBuf = i;
|
||||||
|
ptChannel->cBuf = (complex double *)realloc(ptChannel->cBuf, i * sizeof(complex double));
|
||||||
}
|
}
|
||||||
ptS = cSamples; // Copy samples from cSamples to cBuf
|
for (i = 0; i < nSamples; i++) { // Copy samples from cSamples to cBuf
|
||||||
ptD = ptChannel->cBuf + ptChannel->nBuf * 2;
|
ptChannel->cBuf[ptChannel->Windex++] = cSamples[i] / CLIP32;
|
||||||
for (i = 0; i < nSamples; i++) {
|
if (ptChannel->Windex >= ptChannel->sizeBuf)
|
||||||
*ptD++ = *ptS++ / CLIP32;
|
ptChannel->Windex = 0;
|
||||||
*ptD++ = *ptS++ / CLIP32;
|
|
||||||
}
|
}
|
||||||
ptChannel->nBuf += nSamples;
|
ptChannel->nBuf += nSamples;
|
||||||
nSamples = 0;
|
nSamples = 0;
|
||||||
while (ptChannel->nBuf >= ptChannel->in_size) {
|
while (ptChannel->nBuf >= in_size) {
|
||||||
(*wdsp_fexchange0)(channel, ptChannel->cBuf, cSamples + nSamples * 2, &error);
|
(*wdsp_fexchange0)(channel, (double *)(ptChannel->cBuf + ptChannel->Rindex), (double *)(cSamples + nSamples), &error);
|
||||||
if (error)
|
if (error)
|
||||||
printf("WDSP: wdsp_fexchange0 error %d\n", error);
|
printf("WDSP: wdsp_fexchange0 error %d\n", error);
|
||||||
nSamples += ptChannel->in_size;
|
ptChannel->Rindex += in_size;
|
||||||
ptChannel->nBuf -= ptChannel->in_size;
|
if (ptChannel->Rindex >= ptChannel->sizeBuf)
|
||||||
memmove(ptChannel->cBuf, ptChannel->cBuf + ptChannel->in_size * 2, ptChannel->nBuf * SAMPLE_BYTES);
|
ptChannel->Rindex = 0;
|
||||||
|
nSamples += in_size;
|
||||||
|
ptChannel->nBuf -= in_size;
|
||||||
}
|
}
|
||||||
for (i = 0; i < nSamples * 2; i++)
|
for (i = 0; i < nSamples; i++)
|
||||||
cSamples[i] *= CLIP32;
|
cSamples[i] *= CLIP32;
|
||||||
return nSamples;
|
return nSamples;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-10
@@ -25,16 +25,20 @@ class Cwdsp:
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self.Lib = ctypes.CDLL("./libwdsp.so")
|
self.Lib = ctypes.CDLL("./wdsp/libwdsp.so")
|
||||||
Log ("Found private wdsp")
|
Log ("Found private wdsp in quisk/wdsp")
|
||||||
except:
|
except:
|
||||||
name = ctypes.util.find_library("wdsp")
|
try:
|
||||||
if name:
|
self.Lib = ctypes.CDLL("./libwdsp.so")
|
||||||
try:
|
Log ("Found private wdsp in quisk/")
|
||||||
self.Lib = ctypes.CDLL(name)
|
except:
|
||||||
Log ("Found public wdsp")
|
name = ctypes.util.find_library("wdsp")
|
||||||
except:
|
if name:
|
||||||
pass
|
try:
|
||||||
|
self.Lib = ctypes.CDLL(name)
|
||||||
|
Log ("Found public wdsp")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
if not self.Lib:
|
if not self.Lib:
|
||||||
Log("Wdsp was not found")
|
Log("Wdsp was not found")
|
||||||
return
|
return
|
||||||
@@ -73,7 +77,7 @@ class Cwdsp:
|
|||||||
dsp_size = 256
|
dsp_size = 256
|
||||||
QS.wdsp_set_parameter(channel, in_size=in_size)
|
QS.wdsp_set_parameter(channel, in_size=in_size)
|
||||||
Lib.OpenChannel (channel, in_size, dsp_size, 48000, 48000, 48000, 0, 1,
|
Lib.OpenChannel (channel, in_size, dsp_size, 48000, 48000, 48000, 0, 1,
|
||||||
ctypes.c_double(0.010), ctypes.c_double(0.025), ctypes.c_double(0.0), ctypes.c_double(0.010), 0)
|
ctypes.c_double(0.010), ctypes.c_double(0.025), ctypes.c_double(0.0), ctypes.c_double(0.010), 1)
|
||||||
Lib.SetRXAShiftRun (channel, 0)
|
Lib.SetRXAShiftRun (channel, 0)
|
||||||
Lib.RXANBPSetRun (channel, 0)
|
Lib.RXANBPSetRun (channel, 0)
|
||||||
Lib.SetRXAAMSQRun (channel, 0)
|
Lib.SetRXAAMSQRun (channel, 0)
|
||||||
|
|||||||
+7
-1
@@ -582,6 +582,7 @@ class QuiskPushbutton(QuiskButtons, wx.lib.buttons.GenButton):
|
|||||||
self.Bind(wx.EVT_BUTTON, self.OnButton)
|
self.Bind(wx.EVT_BUTTON, self.OnButton)
|
||||||
self.InitButtons(text, text_color)
|
self.InitButtons(text, text_color)
|
||||||
self.direction = 1
|
self.direction = 1
|
||||||
|
self.normal_brush = self.up_brush
|
||||||
if use_right:
|
if use_right:
|
||||||
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
|
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
|
||||||
self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
|
self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
|
||||||
@@ -603,7 +604,12 @@ class QuiskPushbutton(QuiskButtons, wx.lib.buttons.GenButton):
|
|||||||
event = wx.PyEvent()
|
event = wx.PyEvent()
|
||||||
event.SetEventObject(self)
|
event.SetEventObject(self)
|
||||||
self.command(event)
|
self.command(event)
|
||||||
|
def SetColorNormal(self):
|
||||||
|
self.up_brush = self.normal_brush
|
||||||
|
self.Refresh()
|
||||||
|
def SetColor(self, color):
|
||||||
|
self.up_brush = wx.Brush(color)
|
||||||
|
self.Refresh()
|
||||||
|
|
||||||
class QuiskRepeatbutton(QuiskButtons, wx.lib.buttons.GenButton):
|
class QuiskRepeatbutton(QuiskButtons, wx.lib.buttons.GenButton):
|
||||||
"""A push button that repeats when held down."""
|
"""A push button that repeats when held down."""
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
#
|
||||||
@@ -0,0 +1,569 @@
|
|||||||
|
# SDR-TRX Quisk Configuration File
|
||||||
|
# This file is to integrate the function of control of the SDR-TRX wint Quisk and with WSJT-X.
|
||||||
|
# This is for control of the SDR-TRX with using the UDP port port 12346. WSJT-X interfaces with this
|
||||||
|
# locally using UDP on port 2237. Capital letters begin commands to the SDR-TRX from Quisk,
|
||||||
|
# and lowercase letters are for commands coming from WSJT-X (through this Quisk interface).
|
||||||
|
# This is for usinng the SDR-TRX without a 3.5 mm audio card to the soundcard of the computer,
|
||||||
|
# and the WSJT-X software using UDP, and the data coming over UDP part 12345 too.
|
||||||
|
# It requires the libldl/ directory from weakmon to be in the directory you run this from.
|
||||||
|
# It also requires the WSJTXClass.py file from wsjtx_transceiver_interface to be in the lib directory
|
||||||
|
# and the version of that class. I have supplied a pruned down versions of ft8.py and ft4.45, because we don't need much
|
||||||
|
# other than FT8Send and FT4Send from those files. You need to edit transceiver_config.yml to have your callsign and grid, etc.
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import yaml
|
||||||
|
import struct
|
||||||
|
import datetime
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from ft8 import FT8Send
|
||||||
|
from ft4 import FT4Send
|
||||||
|
from quisk_hardware_model import Hardware as BaseHardware
|
||||||
|
import struct
|
||||||
|
import collections
|
||||||
|
import lib.WSJTXClass as WSJTXClass # To use encoders
|
||||||
|
|
||||||
|
sys.path.append(os.path.expandvars('$WEAKMON'))
|
||||||
|
|
||||||
|
DEBUG_ON = True
|
||||||
|
|
||||||
|
#
|
||||||
|
# Define the name of the hardware and the items on the hardware screen (see quisk_conf_defaults.py):
|
||||||
|
#
|
||||||
|
|
||||||
|
################ Receivers SdrTrx, The SdrTrx radio by Rob Frohne, KL7NA
|
||||||
|
## hardware_file_name Hardware file path, rfile
|
||||||
|
# This is the file that contains the control logic for each radio.
|
||||||
|
#hardware_file_name = 'sdrtrxpkg/quisk_hardware.py'
|
||||||
|
|
||||||
|
## widgets_file_name Widget file path, rfile
|
||||||
|
# This optional file adds additional controls for the radio.
|
||||||
|
#widgets_file_name = ''
|
||||||
|
|
||||||
|
## si570_xtal_freq Si570 crystal frequency, integer
|
||||||
|
# This is the Si570 startup frequency in Hz. 114.285MHz is the typical
|
||||||
|
# value from the data sheet; you can use 'usbsoftrock calibrate' to find
|
||||||
|
# the value for your device.
|
||||||
|
si570_xtal_freq = 114285000
|
||||||
|
|
||||||
|
## rx_max_amplitude_correct Max ampl correct, number
|
||||||
|
# If you get your I/Q samples from a sound card, you will need to correct the
|
||||||
|
# amplitude and phase for inaccuracies in the analog hardware. The correction is
|
||||||
|
# entered using the controls from the "Rx Phase" button on the config screen.
|
||||||
|
# You must enter a positive number. This controls the range of the control.
|
||||||
|
rx_max_amplitude_correct = 0.2
|
||||||
|
|
||||||
|
## rx_max_phase_correct Max phase correct, number
|
||||||
|
# If you get your I/Q samples from a sound card, you will need to correct the
|
||||||
|
# amplitude and phase for inaccuracies in the analog hardware. The correction is
|
||||||
|
# entered using the controls from the "Rx Phase" button on the config screen.
|
||||||
|
# You must enter a positive number. This controls the range of the control in degrees.
|
||||||
|
rx_max_phase_correct = 10.0
|
||||||
|
|
||||||
|
|
||||||
|
# SDR-TRX Hardware Control Class
|
||||||
|
#
|
||||||
|
|
||||||
|
class Hardware(BaseHardware):
|
||||||
|
# These are "static variable substitutes" since python doesn't have them.
|
||||||
|
# They are shared by all instances of the class and don't get redefinede each time a method is called.
|
||||||
|
# Move these into the Hardware class.
|
||||||
|
# Uncomment these lines if you wish to use Pulseaudio
|
||||||
|
name_of_sound_play = "pulse"
|
||||||
|
|
||||||
|
# Radio's Frequency limits.
|
||||||
|
radio_lower = 3500000
|
||||||
|
radio_upper = 30000000
|
||||||
|
PACKET_SIZE = 1444 # 4 bytes for packet number + 360 * 4 bytes for int32_t pairs
|
||||||
|
# Using 16 bit samples, so 2 bytes per sample.
|
||||||
|
|
||||||
|
# Set the number of Hz the signal is tuned to above the center frequency to avoid 1/f noise.
|
||||||
|
vfo_Center_Offset = 10000
|
||||||
|
|
||||||
|
tx_ready_wsjtx = False
|
||||||
|
tx_ready_wsjtx_sent = False
|
||||||
|
tx_now = False
|
||||||
|
|
||||||
|
# This is the open code for the WSJT server.
|
||||||
|
ft8_encoder = FT8Send()
|
||||||
|
ft4_encoder = FT4Send()
|
||||||
|
|
||||||
|
class Packet:
|
||||||
|
def __init__(self, data):
|
||||||
|
self.number = struct.unpack('<I', data[:4])[0]
|
||||||
|
self.pairs = [struct.unpack('<hh', data[i:i+4]) for i in range(4, len(data), 4)]
|
||||||
|
|
||||||
|
class PacketQueue:
|
||||||
|
def __init__(self):
|
||||||
|
self.queue = collections.deque()
|
||||||
|
|
||||||
|
def add_packet(self, packet):
|
||||||
|
self.queue.append(packet)
|
||||||
|
self.queue = collections.deque(sorted(self.queue, key=lambda p: p.number))
|
||||||
|
|
||||||
|
def check_missing_packets(self):
|
||||||
|
for i in range(1, len(self.queue)):
|
||||||
|
if self.queue[i].number != self.queue[i-1].number + 1:
|
||||||
|
avg_pairs = [((self.queue[i-1].pairs[j][0] + self.queue[i].pairs[j][0]) // 2,
|
||||||
|
(self.queue[i-1].pairs[j][1] + self.queue[i].pairs[j][1]) // 2)
|
||||||
|
for j in range(len(self.queue[i-1].pairs))]
|
||||||
|
self.queue.insert(i, Packet(struct.pack('<I', self.queue[i-1].number + 1) +
|
||||||
|
b''.join(struct.pack('<ii', pair[0], pair[1]) for pair in avg_pairs)))
|
||||||
|
|
||||||
|
def get_packets(self):
|
||||||
|
packets = list(self.queue)
|
||||||
|
self.queue.clear()
|
||||||
|
return packets
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
# Connection for WSJT-X and data
|
||||||
|
self.PICO_UDP_IP = None # Will be set to the IP of the machine sending data
|
||||||
|
self.COMMAND_UDP_PORT = 12346 # This is the port the Pico listens on for UDP commands coming from quisk.
|
||||||
|
self.DATA_UDP_PORT = 12345 # This is the port the quisk listens on for UDP IQ data coming from the Pico W.
|
||||||
|
self.BROADCAST_PORT = 12347 # This is the port the Pico gets our IP address from.-
|
||||||
|
self.broadcast_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
self.broadcast_sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
|
self.broadcast_message = "Quisk"
|
||||||
|
self.broadcast_sock.setblocking(False)
|
||||||
|
self.isConnected = False
|
||||||
|
self.command_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
self.command_sock.setblocking(False)
|
||||||
|
self.data_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
self.data_sock.bind(('', self.DATA_UDP_PORT))
|
||||||
|
print("Data socket bound to IP:", self.data_sock.getsockname()[0])
|
||||||
|
self.data_sock.setblocking(False)
|
||||||
|
self.establish_connection()
|
||||||
|
|
||||||
|
self.InitSamples(2, 0) # Two bytes per sample, little endian.
|
||||||
|
self.queue = self.PacketQueue()
|
||||||
|
time.sleep(2)
|
||||||
|
# Poll for version. Should probably confirm the response on this.
|
||||||
|
version = str(self.get_parameter("VER")) # The way the firmware is now, this sets it up to use the UART or UDP.
|
||||||
|
# UART is used if get_parameter uses the UART, and UDP is used if it uses the UDP.
|
||||||
|
print(version)
|
||||||
|
# Return an informative message for the config screen
|
||||||
|
t = str(version) + ". Capture from sound card %s." % self.conf.name_of_sound_capt
|
||||||
|
configs_file = open('transceiver_config.yml', 'r')
|
||||||
|
configs = yaml.load(configs_file, Loader=yaml.BaseLoader)
|
||||||
|
|
||||||
|
# Global variables
|
||||||
|
self.callsign = configs['callsign']
|
||||||
|
self.grid = configs['grid']
|
||||||
|
self.current_msg = ''
|
||||||
|
self.rx_callsign = ''
|
||||||
|
self.mode = 'FT8'
|
||||||
|
self.tx_freq = 1200
|
||||||
|
|
||||||
|
# Connection for WSJT-X
|
||||||
|
WSJTX_UDP_IP = "127.0.0.1" # Assuming you are running WSJT-X on the same computer as Quisk...
|
||||||
|
WSJTX_UDP_PORT = 2237
|
||||||
|
self.wsjtx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
self.wsjtx_sock.bind((WSJTX_UDP_IP, WSJTX_UDP_PORT))
|
||||||
|
self.wsjtx_sock.setblocking(False)
|
||||||
|
|
||||||
|
self.StartSamples()
|
||||||
|
return t
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
# Called once to close the Hardware
|
||||||
|
self.get_parameter("RX") # Make sure we are in RX mode.
|
||||||
|
time.sleep(1) # Wait for RX to be set.
|
||||||
|
self.StopSamples()
|
||||||
|
self.command_sock.close()
|
||||||
|
self.wsjtx_sock.close()
|
||||||
|
self.broadcast_sock.close()
|
||||||
|
|
||||||
|
def ChangeFrequency(self, tune, vfo, source='', band='', event=None):
|
||||||
|
# Called whenever quisk requests a frequency change.
|
||||||
|
# This sends the FREQ command dto set the centre frequency of the radio,
|
||||||
|
# and will also move the 'tune' frequency (the section within the RX passband
|
||||||
|
# which is to be demodulated) if it falls outside the passband (+/- sample_rate/2).
|
||||||
|
print("Setting VFO to %d, with tune to %d." % (vfo, tune))
|
||||||
|
print("Setting VFO to %d." % vfo)
|
||||||
|
if vfo < self.radio_lower:
|
||||||
|
vfo = self.radio_lower
|
||||||
|
print("Outside range! Setting to %d" % self.radio_lower)
|
||||||
|
|
||||||
|
if vfo > self.radio_upper:
|
||||||
|
vfo = self.radio_upper
|
||||||
|
print("Outside range! Setting to %d" % self.radio_upper)
|
||||||
|
|
||||||
|
# print("sample_rate =", sample_rate) # I believe sample_rate comes from quisk.
|
||||||
|
# If the tune frequency is outside the RX bandwidth, set it to somewhere within that bandwidth.
|
||||||
|
if tune > (vfo + sample_rate / 2) or tune < (vfo - sample_rate / 2):
|
||||||
|
tune = vfo + self.vfo_Center_Offset
|
||||||
|
print("Bringing tune frequency back into the RX bandwidth.")
|
||||||
|
|
||||||
|
# success = self.set_parameter("FREQ",str(vfo))
|
||||||
|
self.set_parameter("FREQ", str(vfo))
|
||||||
|
self.set_parameter("TX_FREQ", str(tune))
|
||||||
|
|
||||||
|
return tune, vfo
|
||||||
|
|
||||||
|
def OnButtonPTT(self, event):
|
||||||
|
btn = event.GetEventObject()
|
||||||
|
if btn.GetValue():
|
||||||
|
self.get_parameter("TX")
|
||||||
|
print("TX")
|
||||||
|
else:
|
||||||
|
self.get_parameter("RX")
|
||||||
|
print("RX")
|
||||||
|
return BaseHardware.OnButtonPTT(self, event)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def establish_connection(self):
|
||||||
|
while not self.isConnected:
|
||||||
|
print("isConnected is: ", self.isConnected)
|
||||||
|
# Check if a connection is established
|
||||||
|
# Wait for data to arrive and set PICO_UDP_IP to the sender's IP
|
||||||
|
while self.PICO_UDP_IP is None: # This needs to be fixed. It continues to send broadcast packets.
|
||||||
|
try:
|
||||||
|
print("Sending broadcast message...")
|
||||||
|
self.broadcast_sock.sendto(self.broadcast_message.encode(), ('<broadcast>', self.BROADCAST_PORT))
|
||||||
|
time.sleep(1) # Wait for 1 second before sending the next broadcast message
|
||||||
|
except Exception as e:
|
||||||
|
print("Broadcast message did not work: {0}".format(e))
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
data, addr = self.data_sock.recvfrom(self.PACKET_SIZE) # Adjust the buffer size as needed
|
||||||
|
self.PICO_UDP_IP = addr[0]
|
||||||
|
print("PICO_UDP_IP set to:", self.PICO_UDP_IP)
|
||||||
|
# if data == self.broadcast_message:
|
||||||
|
self.isConnected = True
|
||||||
|
except socket.error:
|
||||||
|
if self.PICO_UDP_IP is not None:
|
||||||
|
print("Connection established with Pico W at IP:", self.PICO_UDP_IP, "but exception occurred.")
|
||||||
|
break # Exit the loop if self.PICO_UDP_IP is not None
|
||||||
|
pass # No data available yet
|
||||||
|
|
||||||
|
def GetRxSamples(self):
|
||||||
|
# Initialize packet counter and time marker
|
||||||
|
# This routine is made for FT8 and FT4, where we need to send a packet every 15 seconds.
|
||||||
|
# The Pico W sending the data has a sample rate of 48046 samples per second > 48 ks/s.
|
||||||
|
# We made the packets so they carry 180 samples per packet. That gives 4000 packets
|
||||||
|
# per 15 seconds. We discard any packets after that four times a minute at 59, 14, 29,
|
||||||
|
# and 44 seconds. This should make four ticks per minute, and we should be able to ignore
|
||||||
|
# # them if we are doing FT8 or FT4. Something else might be more optimum for other modes.
|
||||||
|
|
||||||
|
packet_counter = 0
|
||||||
|
next_time_marker = 0
|
||||||
|
send_packets = False
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Check if we've reached the next time marker
|
||||||
|
current_time = time.time()
|
||||||
|
current_seconds = current_time % 60
|
||||||
|
|
||||||
|
# Define the time markers
|
||||||
|
if 59 <= current_seconds < 60:
|
||||||
|
next_time_marker = current_time + 1 - current_seconds
|
||||||
|
send_packets = True
|
||||||
|
elif 14 <= current_seconds < 15:
|
||||||
|
next_time_marker = current_time + 1 - (current_seconds - 14)
|
||||||
|
send_packets = True
|
||||||
|
elif 29 <= current_seconds < 30:
|
||||||
|
next_time_marker = current_time + 1 - (current_seconds - 29)
|
||||||
|
send_packets = True
|
||||||
|
elif 44 <= current_seconds < 45:
|
||||||
|
next_time_marker = current_time + 1 - (current_seconds - 44)
|
||||||
|
send_packets = True
|
||||||
|
|
||||||
|
# If we've reached the next time marker, reset the packet counter
|
||||||
|
if current_time >= next_time_marker and send_packets:
|
||||||
|
packet_counter = 0
|
||||||
|
send_packets = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
data, addr = self.data_sock.recvfrom(self.PACKET_SIZE)
|
||||||
|
self.no_data_repeat = 0
|
||||||
|
except socket.error:
|
||||||
|
# Send a broadcast message to the Pico W to get the IP address in case it has reset.
|
||||||
|
self.broadcast_sock.sendto(self.broadcast_message.encode(), ('<broadcast>', self.BROADCAST_PORT))
|
||||||
|
break # No more packets available
|
||||||
|
|
||||||
|
packet = self.Packet(data)
|
||||||
|
self.queue.add_packet(packet)
|
||||||
|
|
||||||
|
# Check for missing packets and fill them in
|
||||||
|
self.queue.check_missing_packets()
|
||||||
|
|
||||||
|
# Get the packets and pass them to the processing thread
|
||||||
|
packets = self.queue.get_packets()
|
||||||
|
|
||||||
|
for packet in packets:
|
||||||
|
data = b''.join(struct.pack('<hh', pair[0], pair[1]) for pair in packet.pairs)
|
||||||
|
# if len(data) % 4 != 0: # Each I/Q pair is now 4 bytes (2 * 2 bytes)
|
||||||
|
# self.GotReadError(self, DEBUG_ON, "Error: Mismatched number of I and Q samples")
|
||||||
|
# continue
|
||||||
|
# If we've already sent 4000 packets since the last time marker, discard the rest
|
||||||
|
if packet_counter >= 4000:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.AddRxSamples(data)
|
||||||
|
packet_counter += 1
|
||||||
|
|
||||||
|
#
|
||||||
|
# UDP comms functions, to communicate with the Raspberry Pi Pico W board used in the SDR-TRX.
|
||||||
|
#
|
||||||
|
|
||||||
|
def get_parameter(self, string):
|
||||||
|
string = string + "\n"
|
||||||
|
self.command_sock.sendto(string.encode(), (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
return self.get_argument()
|
||||||
|
|
||||||
|
def set_parameter(self, string, arg):
|
||||||
|
string = string + "," + arg + "\r" + "\n"
|
||||||
|
self.command_sock.sendto(string.encode(), (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
print('arg is: ', arg)
|
||||||
|
temp_arg = self.get_argument()
|
||||||
|
print('temp_arg is: ', temp_arg)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_argument(self):
|
||||||
|
self.command_sock.setblocking(True)
|
||||||
|
try:
|
||||||
|
data1 = self.command_sock.recv(1024)
|
||||||
|
print('Received on line 178: ', data1)
|
||||||
|
except Exception as e:
|
||||||
|
print("Exception occurred: {0}".format(e))
|
||||||
|
return -1
|
||||||
|
self.command_sock.setblocking(False)
|
||||||
|
if not data1.startswith(b'OK'):
|
||||||
|
print('Received on line 185: ', data1)
|
||||||
|
# Maybe we didn't catch an OK line?
|
||||||
|
else:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data1 = self.command_sock.recv(1024)
|
||||||
|
print('Received on line 190: ', data1)
|
||||||
|
break
|
||||||
|
except BlockingIOError:
|
||||||
|
pass
|
||||||
|
# Check to see if we have a comma in the string. If not, there is no argument.
|
||||||
|
if data1.find(b',') == -1:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
data1 = data1.split(b',')[1].rstrip(b'\r\n')
|
||||||
|
print("data1 =")
|
||||||
|
print(data1)
|
||||||
|
|
||||||
|
# Check for the OK string. Should we wait for the OK string?
|
||||||
|
try:
|
||||||
|
data2 = self.command_sock.recv(1024)
|
||||||
|
if data2.startswith(b'OK'):
|
||||||
|
return data2
|
||||||
|
except BlockingIOError:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
# We might want to integrate the UDP reads below into the stuff above.
|
||||||
|
|
||||||
|
# The code below is to interface WSJT-X with SDR-TRX.
|
||||||
|
|
||||||
|
def encode_ft8(self, msg):
|
||||||
|
try:
|
||||||
|
a77 = self.ft8_encoder.pack(msg, 1)
|
||||||
|
symbols = self.ft8_encoder.make_symbols(a77)
|
||||||
|
except Exception as e:
|
||||||
|
print("FT8 encoder error, check message!")
|
||||||
|
print(f"Exception type: {type(e).__name__}")
|
||||||
|
print(f"Exception message: {str(e)}")
|
||||||
|
symbols = None
|
||||||
|
time.sleep(3)
|
||||||
|
return symbols
|
||||||
|
|
||||||
|
def encode_ft4(self, msg):
|
||||||
|
try:
|
||||||
|
a77 = self.ft4_encoder.pack(msg, 1)
|
||||||
|
symbols = self.ft4_encoder.make_symbols(a77)
|
||||||
|
except:
|
||||||
|
print("FT4 encoder error, check message!")
|
||||||
|
symbols = None
|
||||||
|
time.sleep(3)
|
||||||
|
return symbols
|
||||||
|
|
||||||
|
def load_symbols(self, symbols):
|
||||||
|
print("Load symbols into transmitter..")
|
||||||
|
self.command_sock.sendto(b'm', (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
count = 0
|
||||||
|
for symbol in symbols:
|
||||||
|
self.command_sock.sendto(struct.pack('>B', symbol), (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
count += 1
|
||||||
|
# Wait to avoid Arduino serial buffer overflow. This may not be needed with the Pico.
|
||||||
|
if count % 50 == 0:
|
||||||
|
time.sleep(0.05)
|
||||||
|
self.command_sock.sendto(b'\0', (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
resp = self.command_sock.recv(1)
|
||||||
|
if resp == b'm':
|
||||||
|
print("Load OK")
|
||||||
|
else:
|
||||||
|
print(resp)
|
||||||
|
|
||||||
|
def change_freq(self, new_freq):
|
||||||
|
#global self.tx_freq
|
||||||
|
self.command_sock.setblocking(True)
|
||||||
|
print("Change TX frequency to:", new_freq)
|
||||||
|
self.command_sock.sendto(b'o', (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
self.command_sock.sendto(struct.pack('<H', new_freq), (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
time.sleep(0.05)
|
||||||
|
resp = self.command_sock.recv(1)
|
||||||
|
print("resp after changing offset is: ", resp)
|
||||||
|
self.command_sock.setblocking(False)
|
||||||
|
if resp == b'o':
|
||||||
|
print("New freq OK")
|
||||||
|
self.tx_freq = new_freq
|
||||||
|
|
||||||
|
def set_mode(self, new_mode): # New more robust protocol.
|
||||||
|
#global mode
|
||||||
|
if new_mode == 'FT8':
|
||||||
|
self.command_sock.sendto(b'e', (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
time.sleep(.05)
|
||||||
|
resp = self.command_sock.read(1)
|
||||||
|
print("resp = ", resp)
|
||||||
|
if resp == b'e':
|
||||||
|
self.mode = new_mode
|
||||||
|
print("Switched to: {0}".format(new_mode))
|
||||||
|
self.current_msg = ''
|
||||||
|
return True
|
||||||
|
elif new_mode == 'FT4':
|
||||||
|
self.command_sock.sendto(b'f', (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
time.sleep(0.05)
|
||||||
|
resp = self.command_sock.recv(1)
|
||||||
|
print("resp = ", resp)
|
||||||
|
if resp == b'f':
|
||||||
|
self.mode = new_mode
|
||||||
|
self.current_msg = ''
|
||||||
|
print("Switched to: {0}".format(new_mode))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def new_msg(self, msg):
|
||||||
|
#global self.current_msg
|
||||||
|
#global self.mode
|
||||||
|
if msg != self.current_msg:
|
||||||
|
print("Message: {0}".format(msg))
|
||||||
|
if 'FT8' in self.mode:
|
||||||
|
symbols = self.encode_ft8(msg)
|
||||||
|
else:
|
||||||
|
symbols = self.encode_ft4(msg)
|
||||||
|
if symbols.any():
|
||||||
|
# symbols = [kk for kk in range(79)]
|
||||||
|
self.load_symbols(symbols)
|
||||||
|
self.current_msg = msg
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
time.sleep(0.005) # Do we need this?
|
||||||
|
|
||||||
|
def transmit(self):
|
||||||
|
if False: # not current_msg:
|
||||||
|
print("No previous message!")
|
||||||
|
time.sleep(1)
|
||||||
|
else:
|
||||||
|
print("TX!")
|
||||||
|
self.command_sock.sendto(b't', (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
# self.tx_now = False
|
||||||
|
|
||||||
|
def check_time_window(self, utc_time):
|
||||||
|
time_window = 15 if 'FT8' in self.mode else 7
|
||||||
|
print("Time window: ", time_window) # Is correct for FT8 and FT4.
|
||||||
|
rm = utc_time.second % time_window
|
||||||
|
if rm > 1 and rm < time_window - 1: # Within one second of exact time for FT8.
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# See if HeartBeat will work for this:
|
||||||
|
def HeartBeat(self): # Should be called at 10 Hz from the main.
|
||||||
|
# Check transmitter is initialized
|
||||||
|
#raise Exception("In HeartBeat")
|
||||||
|
|
||||||
|
# print("\n\nWait for transmitter ready...")
|
||||||
|
if self.tx_ready_wsjtx == False:
|
||||||
|
if self.tx_ready_wsjtx_sent == False:
|
||||||
|
self.command_sock.sendto(b'r', (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
time.sleep(0.05)
|
||||||
|
self.tx_ready_wsjtx_sent = True
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
x = self.command_sock.recv(1)
|
||||||
|
if x == b'r':
|
||||||
|
print("Transmitter ready!")
|
||||||
|
self.tx_ready_wsjtx = True
|
||||||
|
break
|
||||||
|
except BlockingIOError:
|
||||||
|
pass
|
||||||
|
if self.tx_ready_wsjtx == True:
|
||||||
|
try:
|
||||||
|
fileContent = self.wsjtx_sock.recv(1024)
|
||||||
|
except Exception as e:
|
||||||
|
if e == BlockingIOError: # This is our way of pollig the socket.
|
||||||
|
# Maybe use select to stop the errors printing after we quit.
|
||||||
|
return BaseHardware.HeartBeat(self)
|
||||||
|
else:
|
||||||
|
# print("Error in HeartBeat")
|
||||||
|
# print(f"Exception type: {type(e).__name__}")
|
||||||
|
# print(f"Exception message: {str(e)}")
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
NewPacket = WSJTXClass.WSJTX_Packet(fileContent, 0)
|
||||||
|
NewPacket.Decode()
|
||||||
|
|
||||||
|
if NewPacket.PacketType == 1:
|
||||||
|
# print('New Packet is type 1')
|
||||||
|
StatusPacket = WSJTXClass.WSJTX_Status(fileContent, NewPacket.index)
|
||||||
|
StatusPacket.Decode()
|
||||||
|
|
||||||
|
# Check TX frequency and update transceiver
|
||||||
|
new_freq = StatusPacket.TxDF
|
||||||
|
new_mode = StatusPacket.TxMode.strip()
|
||||||
|
|
||||||
|
|
||||||
|
if new_freq != self.tx_freq:
|
||||||
|
# time.sleep(0.03)
|
||||||
|
print('Changing frequency from {0} to {1}'.format(self.tx_freq, new_freq))
|
||||||
|
try:
|
||||||
|
self.change_freq(new_freq)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Exception occurred: {e}")
|
||||||
|
print ('It is now ', self.tx_freq)
|
||||||
|
|
||||||
|
# print('new_mode', new_mode, ' self.mode is: ', self.mode)
|
||||||
|
if new_mode != self.mode:
|
||||||
|
print("Mode before: {0}".format(self.mode))
|
||||||
|
self.set_mode(new_mode)
|
||||||
|
print("New mode after: {0}".format(self.mode))
|
||||||
|
|
||||||
|
# Check if TX is enabled
|
||||||
|
if StatusPacket.Transmitting == 1:
|
||||||
|
# Check time, avoid transmitting out of the time slot
|
||||||
|
current_time = datetime.datetime.now()
|
||||||
|
utc_time = current_time.astimezone(datetime.timezone.utc)
|
||||||
|
self.tx_now = self.check_time_window(utc_time)
|
||||||
|
if self.tx_now:
|
||||||
|
self.command_sock.sendto(b'p', (self.PICO_UDP_IP, self.COMMAND_UDP_PORT))
|
||||||
|
message = StatusPacket.TxMessage
|
||||||
|
message = message.replace('<', '')
|
||||||
|
message = message.replace('>', '')
|
||||||
|
print('Message is: ', message)
|
||||||
|
self.new_msg(message.strip())
|
||||||
|
print("Encoded message is: ", self.current_msg)
|
||||||
|
# print("tx_now is: ", self.tx_now)
|
||||||
|
if self.tx_now:
|
||||||
|
print('Transmitting')
|
||||||
|
self.transmit()
|
||||||
|
print("Time: {0}:{1}:{2}".format(utc_time.hour, utc_time.minute, utc_time.second))
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
# print('Not Transmitting')
|
||||||
|
return BaseHardware.HeartBeat(self)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# print("Error in HeartBeat")
|
||||||
|
# print(f"Exception type: {type(e).__name__}")
|
||||||
|
# print(f"Exception message: {str(e)}")
|
||||||
|
# traceback.print_exc()
|
||||||
|
return BaseHardware.HeartBeat(self)
|
||||||
@@ -4,19 +4,14 @@ import os
|
|||||||
|
|
||||||
# This file is used to build the Linux and Mac versions of Quisk. Windows builds are not included.
|
# This file is used to build the Linux and Mac versions of Quisk. Windows builds are not included.
|
||||||
|
|
||||||
# You must define the version here. A title string including
|
fp = open('__init__.py') # Read in the version
|
||||||
# the version will be written to __init__.py and read by quisk.py.
|
Version = fp.readline().strip()[15:]
|
||||||
|
|
||||||
Version = '4.2.42'
|
|
||||||
|
|
||||||
fp = open("__init__.py", "w") # write title string
|
|
||||||
fp.write("#Quisk version %s\n" % Version)
|
|
||||||
fp.write("from .quisk import main\n")
|
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
sources = ['quisk.c', 'sound.c', 'is_key_down.c', 'microphone.c', 'utility.c',
|
sources = ['quisk.c', 'sound.c', 'is_key_down.c', 'microphone.c', 'utility.c',
|
||||||
'sound_alsa.c', 'sound_pulseaudio.c', 'sound_portaudio.c', 'sound_directx.c', 'sound_wasapi.c',
|
'sound_alsa.c', 'sound_pulseaudio.c', 'sound_portaudio.c', 'sound_directx.c', 'sound_wasapi.c',
|
||||||
'filter.c', 'extdemod.c', 'freedv.c', 'quisk_wdsp.c', 'ac2yd/remote.c']
|
'filter.c', 'extdemod.c', 'freedv.c', 'quisk_wdsp.c', 'ac2yd/remote.c',
|
||||||
|
'tci.c', 'base64.c', 'handshake.c', 'sha1.c', 'utf8.c', 'ws.c']
|
||||||
|
|
||||||
# Afedri hardware support added by Alex, Alex@gmail.com
|
# Afedri hardware support added by Alex, Alex@gmail.com
|
||||||
mAfedri = Extension ('quisk.afedrinet.afedrinet_io',
|
mAfedri = Extension ('quisk.afedrinet.afedrinet_io',
|
||||||
|
|||||||
@@ -0,0 +1,389 @@
|
|||||||
|
/*
|
||||||
|
* sha1.c
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* This file implements the Secure Hashing Algorithm 1 as
|
||||||
|
* defined in FIPS PUB 180-1 published April 17, 1995.
|
||||||
|
*
|
||||||
|
* The SHA-1, produces a 160-bit message digest for a given
|
||||||
|
* data stream. It should take about 2**n steps to find a
|
||||||
|
* message with the same digest as a given message and
|
||||||
|
* 2**(n/2) to find any two messages with the same digest,
|
||||||
|
* when n is the digest size in bits. Therefore, this
|
||||||
|
* algorithm can serve as a means of providing a
|
||||||
|
* "fingerprint" for a message.
|
||||||
|
*
|
||||||
|
* Portability Issues:
|
||||||
|
* SHA-1 is defined in terms of 32-bit "words". This code
|
||||||
|
* uses <stdint.h> (included via "sha1.h" to define 32 and 8
|
||||||
|
* bit unsigned integer types. If your C compiler does not
|
||||||
|
* support 32 bit unsigned integers, this code is not
|
||||||
|
* appropriate.
|
||||||
|
*
|
||||||
|
* Caveats:
|
||||||
|
* SHA-1 is designed to work with messages less than 2^64 bits
|
||||||
|
* long. Although SHA-1 allows a message digest to be generated
|
||||||
|
* for messages of any number of bits less than 2^64, this
|
||||||
|
* implementation only works with messages with a length that is
|
||||||
|
* a multiple of the size of an 8-bit character.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sha1.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define the SHA1 circular left shift macro
|
||||||
|
*/
|
||||||
|
#define SHA1CircularShift(bits,word) \
|
||||||
|
(((word) << (bits)) | ((word) >> (32-(bits))))
|
||||||
|
|
||||||
|
/* Local Function Prototyptes */
|
||||||
|
void SHA1PadMessage(SHA1Context *);
|
||||||
|
void SHA1ProcessMessageBlock(SHA1Context *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SHA1Reset
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* This function will initialize the SHA1Context in preparation
|
||||||
|
* for computing a new SHA1 message digest.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* context: [in/out]
|
||||||
|
* The context to reset.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* sha Error Code.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int SHA1Reset(SHA1Context *context)
|
||||||
|
{
|
||||||
|
if (!context)
|
||||||
|
{
|
||||||
|
return shaNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->Length_Low = 0;
|
||||||
|
context->Length_High = 0;
|
||||||
|
context->Message_Block_Index = 0;
|
||||||
|
|
||||||
|
context->Intermediate_Hash[0] = 0x67452301;
|
||||||
|
context->Intermediate_Hash[1] = 0xEFCDAB89;
|
||||||
|
context->Intermediate_Hash[2] = 0x98BADCFE;
|
||||||
|
context->Intermediate_Hash[3] = 0x10325476;
|
||||||
|
context->Intermediate_Hash[4] = 0xC3D2E1F0;
|
||||||
|
|
||||||
|
context->Computed = 0;
|
||||||
|
context->Corrupted = 0;
|
||||||
|
|
||||||
|
return shaSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SHA1Result
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* This function will return the 160-bit message digest into the
|
||||||
|
* Message_Digest array provided by the caller.
|
||||||
|
* NOTE: The first octet of hash is stored in the 0th element,
|
||||||
|
* the last octet of hash in the 19th element.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* context: [in/out]
|
||||||
|
* The context to use to calculate the SHA-1 hash.
|
||||||
|
* Message_Digest: [out]
|
||||||
|
* Where the digest is returned.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* sha Error Code.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int SHA1Result( SHA1Context *context,
|
||||||
|
uint8_t Message_Digest[SHA1HashSize])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!context || !Message_Digest)
|
||||||
|
{
|
||||||
|
return shaNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->Corrupted)
|
||||||
|
{
|
||||||
|
return context->Corrupted;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context->Computed)
|
||||||
|
{
|
||||||
|
SHA1PadMessage(context);
|
||||||
|
for(i=0; i<64; ++i)
|
||||||
|
{
|
||||||
|
/* message may be sensitive, clear it out */
|
||||||
|
context->Message_Block[i] = 0;
|
||||||
|
}
|
||||||
|
context->Length_Low = 0; /* and clear length */
|
||||||
|
context->Length_High = 0;
|
||||||
|
context->Computed = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < SHA1HashSize; ++i)
|
||||||
|
{
|
||||||
|
Message_Digest[i] = context->Intermediate_Hash[i>>2]
|
||||||
|
>> 8 * ( 3 - ( i & 0x03 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return shaSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SHA1Input
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* This function accepts an array of octets as the next portion
|
||||||
|
* of the message.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* context: [in/out]
|
||||||
|
* The SHA context to update
|
||||||
|
* message_array: [in]
|
||||||
|
* An array of characters representing the next portion of
|
||||||
|
* the message.
|
||||||
|
* length: [in]
|
||||||
|
* The length of the message in message_array
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* sha Error Code.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int SHA1Input( SHA1Context *context,
|
||||||
|
const uint8_t *message_array,
|
||||||
|
unsigned length)
|
||||||
|
{
|
||||||
|
if (!length)
|
||||||
|
{
|
||||||
|
return shaSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context || !message_array)
|
||||||
|
{
|
||||||
|
return shaNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->Computed)
|
||||||
|
{
|
||||||
|
context->Corrupted = shaStateError;
|
||||||
|
|
||||||
|
return shaStateError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->Corrupted)
|
||||||
|
{
|
||||||
|
return context->Corrupted;
|
||||||
|
}
|
||||||
|
while(length-- && !context->Corrupted)
|
||||||
|
{
|
||||||
|
context->Message_Block[context->Message_Block_Index++] =
|
||||||
|
(*message_array & 0xFF);
|
||||||
|
|
||||||
|
context->Length_Low += 8;
|
||||||
|
if (context->Length_Low == 0)
|
||||||
|
{
|
||||||
|
context->Length_High++;
|
||||||
|
if (context->Length_High == 0)
|
||||||
|
{
|
||||||
|
/* Message is too long */
|
||||||
|
context->Corrupted = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->Message_Block_Index == 64)
|
||||||
|
{
|
||||||
|
SHA1ProcessMessageBlock(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
message_array++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shaSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SHA1ProcessMessageBlock
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* This function will process the next 512 bits of the message
|
||||||
|
* stored in the Message_Block array.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* None.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*
|
||||||
|
* Comments:
|
||||||
|
|
||||||
|
* Many of the variable names in this code, especially the
|
||||||
|
* single character names, were used because those were the
|
||||||
|
* names used in the publication.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||||
|
{
|
||||||
|
const uint32_t K[] = { /* Constants defined in SHA-1 */
|
||||||
|
0x5A827999,
|
||||||
|
0x6ED9EBA1,
|
||||||
|
0x8F1BBCDC,
|
||||||
|
0xCA62C1D6
|
||||||
|
};
|
||||||
|
int t; /* Loop counter */
|
||||||
|
uint32_t temp; /* Temporary word value */
|
||||||
|
uint32_t W[80]; /* Word sequence */
|
||||||
|
uint32_t A, B, C, D, E; /* Word buffers */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the first 16 words in the array W
|
||||||
|
*/
|
||||||
|
for(t = 0; t < 16; t++)
|
||||||
|
{
|
||||||
|
W[t] = (uint32_t)context->Message_Block[t * 4] << 24;
|
||||||
|
W[t] |= (uint32_t)context->Message_Block[t * 4 + 1] << 16;
|
||||||
|
W[t] |= (uint32_t)context->Message_Block[t * 4 + 2] << 8;
|
||||||
|
W[t] |= (uint32_t)context->Message_Block[t * 4 + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(t = 16; t < 80; t++)
|
||||||
|
{
|
||||||
|
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
||||||
|
}
|
||||||
|
|
||||||
|
A = context->Intermediate_Hash[0];
|
||||||
|
B = context->Intermediate_Hash[1];
|
||||||
|
C = context->Intermediate_Hash[2];
|
||||||
|
D = context->Intermediate_Hash[3];
|
||||||
|
E = context->Intermediate_Hash[4];
|
||||||
|
|
||||||
|
for(t = 0; t < 20; t++)
|
||||||
|
{
|
||||||
|
temp = SHA1CircularShift(5,A) +
|
||||||
|
((B & C) | ((~B) & D)) + E + W[t] + K[0];
|
||||||
|
E = D;
|
||||||
|
D = C;
|
||||||
|
C = SHA1CircularShift(30,B);
|
||||||
|
|
||||||
|
B = A;
|
||||||
|
A = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(t = 20; t < 40; t++)
|
||||||
|
{
|
||||||
|
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
|
||||||
|
E = D;
|
||||||
|
D = C;
|
||||||
|
C = SHA1CircularShift(30,B);
|
||||||
|
B = A;
|
||||||
|
A = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(t = 40; t < 60; t++)
|
||||||
|
{
|
||||||
|
temp = SHA1CircularShift(5,A) +
|
||||||
|
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
|
||||||
|
E = D;
|
||||||
|
D = C;
|
||||||
|
C = SHA1CircularShift(30,B);
|
||||||
|
B = A;
|
||||||
|
A = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(t = 60; t < 80; t++)
|
||||||
|
{
|
||||||
|
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
|
||||||
|
E = D;
|
||||||
|
D = C;
|
||||||
|
C = SHA1CircularShift(30,B);
|
||||||
|
B = A;
|
||||||
|
A = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->Intermediate_Hash[0] += A;
|
||||||
|
context->Intermediate_Hash[1] += B;
|
||||||
|
context->Intermediate_Hash[2] += C;
|
||||||
|
context->Intermediate_Hash[3] += D;
|
||||||
|
context->Intermediate_Hash[4] += E;
|
||||||
|
|
||||||
|
context->Message_Block_Index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SHA1PadMessage
|
||||||
|
*
|
||||||
|
|
||||||
|
* Description:
|
||||||
|
* According to the standard, the message must be padded to an even
|
||||||
|
* 512 bits. The first padding bit must be a '1'. The last 64
|
||||||
|
* bits represent the length of the original message. All bits in
|
||||||
|
* between should be 0. This function will pad the message
|
||||||
|
* according to those rules by filling the Message_Block array
|
||||||
|
* accordingly. It will also call the ProcessMessageBlock function
|
||||||
|
* provided appropriately. When it returns, it can be assumed that
|
||||||
|
* the message digest has been computed.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* context: [in/out]
|
||||||
|
* The context to pad
|
||||||
|
* ProcessMessageBlock: [in]
|
||||||
|
* The appropriate SHA*ProcessMessageBlock function
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void SHA1PadMessage(SHA1Context *context)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check to see if the current message block is too small to hold
|
||||||
|
* the initial padding bits and length. If so, we will pad the
|
||||||
|
* block, process it, and then continue padding into a second
|
||||||
|
* block.
|
||||||
|
*/
|
||||||
|
if (context->Message_Block_Index > 55)
|
||||||
|
{
|
||||||
|
context->Message_Block[context->Message_Block_Index++] = 0x80;
|
||||||
|
while(context->Message_Block_Index < 64)
|
||||||
|
{
|
||||||
|
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHA1ProcessMessageBlock(context);
|
||||||
|
|
||||||
|
while(context->Message_Block_Index < 56)
|
||||||
|
{
|
||||||
|
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context->Message_Block[context->Message_Block_Index++] = 0x80;
|
||||||
|
while(context->Message_Block_Index < 56)
|
||||||
|
{
|
||||||
|
|
||||||
|
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the message length as the last 8 octets
|
||||||
|
*/
|
||||||
|
context->Message_Block[56] = context->Length_High >> 24;
|
||||||
|
context->Message_Block[57] = context->Length_High >> 16;
|
||||||
|
context->Message_Block[58] = context->Length_High >> 8;
|
||||||
|
context->Message_Block[59] = context->Length_High;
|
||||||
|
context->Message_Block[60] = context->Length_Low >> 24;
|
||||||
|
context->Message_Block[61] = context->Length_Low >> 16;
|
||||||
|
context->Message_Block[62] = context->Length_Low >> 8;
|
||||||
|
context->Message_Block[63] = context->Length_Low;
|
||||||
|
|
||||||
|
SHA1ProcessMessageBlock(context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* sha1.h
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* This is the header file for code which implements the Secure
|
||||||
|
* Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
|
||||||
|
* April 17, 1995.
|
||||||
|
*
|
||||||
|
* Many of the variable names in this code, especially the
|
||||||
|
* single character names, were used because those were the names
|
||||||
|
* used in the publication.
|
||||||
|
*
|
||||||
|
* Please read the file sha1.c for more information.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SHA1_H_
|
||||||
|
#define _SHA1_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
/*
|
||||||
|
* If you do not have the ISO standard stdint.h header file, then you
|
||||||
|
* must typdef the following:
|
||||||
|
* name meaning
|
||||||
|
* uint32_t unsigned 32 bit integer
|
||||||
|
* uint8_t unsigned 8 bit integer (i.e., unsigned char)
|
||||||
|
* int_least16_t integer of >= 16 bits
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SHA_enum_
|
||||||
|
#define _SHA_enum_
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
shaSuccess = 0,
|
||||||
|
shaNull, /* Null pointer parameter */
|
||||||
|
shaInputTooLong, /* input data too long */
|
||||||
|
shaStateError /* called Input after Result */
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#define SHA1HashSize 20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure will hold context information for the SHA-1
|
||||||
|
* hashing operation
|
||||||
|
*/
|
||||||
|
typedef struct SHA1Context
|
||||||
|
{
|
||||||
|
uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
|
||||||
|
|
||||||
|
uint32_t Length_Low; /* Message length in bits */
|
||||||
|
uint32_t Length_High; /* Message length in bits */
|
||||||
|
|
||||||
|
/* Index into message block array */
|
||||||
|
int_least16_t Message_Block_Index;
|
||||||
|
uint8_t Message_Block[64]; /* 512-bit message blocks */
|
||||||
|
|
||||||
|
int Computed; /* Is the digest computed? */
|
||||||
|
int Corrupted; /* Is the message digest corrupted? */
|
||||||
|
} SHA1Context;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function Prototypes
|
||||||
|
*/
|
||||||
|
|
||||||
|
int SHA1Reset( SHA1Context *);
|
||||||
|
int SHA1Input( SHA1Context *,
|
||||||
|
const uint8_t *,
|
||||||
|
unsigned int);
|
||||||
|
int SHA1Result( SHA1Context *,
|
||||||
|
uint8_t Message_Digest[SHA1HashSize]);
|
||||||
|
|
||||||
|
#endif
|
||||||
+21
-4
@@ -1,7 +1,24 @@
|
|||||||
|
MODULE_NAME := soapy
|
||||||
|
OBJDIR := ../build/local
|
||||||
|
|
||||||
soapy2:
|
CFLAGS := $(shell python3 ../makefil.py --cflags)
|
||||||
python2 setup.py build_ext --force --inplace
|
LDFLAGS := $(shell python3 ../makefil.py --ldflags)
|
||||||
|
EXTENSION_SUFFIX := $(shell python3 ../makefil.py --suffix)
|
||||||
|
TARGET := $(MODULE_NAME)$(EXTENSION_SUFFIX)
|
||||||
|
|
||||||
soapy3:
|
# Default target: Link the shared library object
|
||||||
|
$(TARGET): soapy.o $(OBJDIR)/import_quisk_api.o
|
||||||
|
$(CC) -o $@ soapy.o $(LDFLAGS) -lSoapySDR $(OBJDIR)/import_quisk_api.o
|
||||||
|
|
||||||
|
# Compile the C source files into object files
|
||||||
|
soapy.o: soapy.c ../quisk.h ../filter.h
|
||||||
|
$(CC) -o $@ -c $< $(CFLAGS) -I..
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
-rm -f *.o
|
||||||
|
-rm -f $(MODULE_NAME)*.so
|
||||||
|
|
||||||
|
.PHONY: soapy-old
|
||||||
|
soapy-old:
|
||||||
python3 setup.py build_ext --force --inplace
|
python3 setup.py build_ext --force --inplace
|
||||||
|
|
||||||
|
|||||||
+8
-7
@@ -1,5 +1,6 @@
|
|||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
|
#include <string.h>
|
||||||
#include <SoapySDR/Device.h>
|
#include <SoapySDR/Device.h>
|
||||||
#include <SoapySDR/Formats.h>
|
#include <SoapySDR/Formats.h>
|
||||||
#include <SoapySDR/Version.h>
|
#include <SoapySDR/Version.h>
|
||||||
@@ -223,7 +224,7 @@ static PyObject * open_device(PyObject * self, PyObject * args)
|
|||||||
else {
|
else {
|
||||||
snprintf(buf128, 128, "SoapySDRDevice_make fail: %s", SoapySDRDevice_lastError());
|
snprintf(buf128, 128, "SoapySDRDevice_make fail: %s", SoapySDRDevice_lastError());
|
||||||
}
|
}
|
||||||
return PyString_FromString(buf128);
|
return PyUnicode_FromString(buf128);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_direc_len(const char * name, int * direction, int * length)
|
static void get_direc_len(const char * name, int * direction, int * length)
|
||||||
@@ -254,7 +255,7 @@ static PyObject * get_device_list(PyObject * self, PyObject * args) // Called fr
|
|||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
dict = PyDict_New();
|
dict = PyDict_New();
|
||||||
for (j = 0; j < results[i].size; j++)
|
for (j = 0; j < results[i].size; j++)
|
||||||
PyDict_SetItemString(dict, results[i].keys[j], PyString_FromString(results[i].vals[j]));
|
PyDict_SetItemString(dict, results[i].keys[j], PyUnicode_FromString(results[i].vals[j]));
|
||||||
PyList_Append(devices, dict);
|
PyList_Append(devices, dict);
|
||||||
Py_DECREF(dict);
|
Py_DECREF(dict);
|
||||||
}
|
}
|
||||||
@@ -329,7 +330,7 @@ static PyObject * set_parameter(PyObject * self, PyObject * args) // Called from
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (msg200[0])
|
if (msg200[0])
|
||||||
return PyString_FromString(msg200);
|
return PyUnicode_FromString(msg200);
|
||||||
Py_INCREF (Py_None);
|
Py_INCREF (Py_None);
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
@@ -377,7 +378,7 @@ static PyObject * get_parameter(PyObject * self, PyObject * args) // Called from
|
|||||||
pylist = PyList_New(0);
|
pylist = PyList_New(0);
|
||||||
names = SoapySDRDevice_listAntennas(sdev, direction, 0, &len_list);
|
names = SoapySDRDevice_listAntennas(sdev, direction, 0, &len_list);
|
||||||
for (i = 0; i < len_list; i++) {
|
for (i = 0; i < len_list; i++) {
|
||||||
pyobj = PyString_FromString(names[i]);
|
pyobj = PyUnicode_FromString(names[i]);
|
||||||
PyList_Append(pylist, pyobj);
|
PyList_Append(pylist, pyobj);
|
||||||
Py_DECREF(pyobj);
|
Py_DECREF(pyobj);
|
||||||
}
|
}
|
||||||
@@ -440,7 +441,7 @@ static PyObject * get_parameter(PyObject * self, PyObject * args) // Called from
|
|||||||
pylist = PyList_New(0);
|
pylist = PyList_New(0);
|
||||||
names = SoapySDRDevice_listGains(sdev, direction, 0, &len_list);
|
names = SoapySDRDevice_listGains(sdev, direction, 0, &len_list);
|
||||||
for (i = 0; i < len_list; i++) {
|
for (i = 0; i < len_list; i++) {
|
||||||
pyobj = PyString_FromString(names[i]);
|
pyobj = PyUnicode_FromString(names[i]);
|
||||||
PyList_Append(pylist, pyobj);
|
PyList_Append(pylist, pyobj);
|
||||||
Py_DECREF(pyobj);
|
Py_DECREF(pyobj);
|
||||||
}
|
}
|
||||||
@@ -450,7 +451,7 @@ static PyObject * get_parameter(PyObject * self, PyObject * args) // Called from
|
|||||||
else if ( ! strncmp(name, "soapy_listGainsValues", length)) {
|
else if ( ! strncmp(name, "soapy_listGainsValues", length)) {
|
||||||
pylist = PyList_New(0);
|
pylist = PyList_New(0);
|
||||||
pylst2 = PyList_New(0); // First element is the total gain
|
pylst2 = PyList_New(0); // First element is the total gain
|
||||||
pyobj = PyString_FromString("total");
|
pyobj = PyUnicode_FromString("total");
|
||||||
PyList_Append(pylst2, pyobj);
|
PyList_Append(pylst2, pyobj);
|
||||||
Py_DECREF(pyobj);
|
Py_DECREF(pyobj);
|
||||||
range = SoapySDRDevice_getGainRange(sdev, direction, 0);
|
range = SoapySDRDevice_getGainRange(sdev, direction, 0);
|
||||||
@@ -460,7 +461,7 @@ static PyObject * get_parameter(PyObject * self, PyObject * args) // Called from
|
|||||||
names = SoapySDRDevice_listGains(sdev, direction, 0, &len_list);
|
names = SoapySDRDevice_listGains(sdev, direction, 0, &len_list);
|
||||||
for (i = 0; i < len_list; i++) {
|
for (i = 0; i < len_list; i++) {
|
||||||
pylst2 = PyList_New(0);
|
pylst2 = PyList_New(0);
|
||||||
pyobj = PyString_FromString(names[i]);
|
pyobj = PyUnicode_FromString(names[i]);
|
||||||
PyList_Append(pylst2, pyobj);
|
PyList_Append(pylst2, pyobj);
|
||||||
Py_DECREF(pyobj);
|
Py_DECREF(pyobj);
|
||||||
range = SoapySDRDevice_getGainElementRange(sdev, direction, 0, names[i]);
|
range = SoapySDRDevice_getGainElementRange(sdev, direction, 0, names[i]);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#define PY_SSIZE_T_CLEAN
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@@ -113,7 +114,7 @@ void quisk_sample_level(const char * msg, complex double * cSamples, int nSample
|
|||||||
level = d;
|
level = d;
|
||||||
}
|
}
|
||||||
if (QuiskTimeSec() - time0 > 0.1) {
|
if (QuiskTimeSec() - time0 > 0.1) {
|
||||||
printf ("sample_level %s: %10.6lf count %8d\n", msg, level / scale, count);
|
printf ("peak sample level %s: %14.6lf count %8d\n", msg, level / scale, count);
|
||||||
level = 0;
|
level = 0;
|
||||||
count = 0;
|
count = 0;
|
||||||
time0 = QuiskTimeSec();
|
time0 = QuiskTimeSec();
|
||||||
@@ -986,6 +987,8 @@ int quisk_read_sound(void) // Called from sound thread
|
|||||||
// Perhaps replace the samples with samples from a file
|
// Perhaps replace the samples with samples from a file
|
||||||
if (quisk_record_state == FILE_PLAY_SAMPLES)
|
if (quisk_record_state == FILE_PLAY_SAMPLES)
|
||||||
quisk_play_samples(cSamples, nSamples);
|
quisk_play_samples(cSamples, nSamples);
|
||||||
|
// Send radio IQ samples to TCI
|
||||||
|
tci_send_iq(cSamples, nSamples);
|
||||||
#if ! DEBUG_MIC
|
#if ! DEBUG_MIC
|
||||||
nSamples = quisk_process_samples(cSamples, nSamples);
|
nSamples = quisk_process_samples(cSamples, nSamples);
|
||||||
#endif
|
#endif
|
||||||
@@ -1000,22 +1003,28 @@ int quisk_read_sound(void) // Called from sound thread
|
|||||||
if (remote_control_slave)
|
if (remote_control_slave)
|
||||||
send_remote_radio_sound_socket(cSamples, nSamples);
|
send_remote_radio_sound_socket(cSamples, nSamples);
|
||||||
|
|
||||||
is_DGT = rxMode == DGT_U || rxMode == DGT_L || rxMode == DGT_IQ || rxMode == DGT_FM;
|
is_DGT = (rxMode == DGT_U || rxMode == DGT_L || rxMode == DGT_IQ || rxMode == DGT_FM || rxMode == FDV_U || rxMode == FDV_L);
|
||||||
if (quisk_record_state == TMP_PLAY_SPKR_MIC)
|
if (quisk_record_state == TMP_PLAY_SPKR_MIC)
|
||||||
quisk_tmp_playback(cSamples, nSamples, 1.0); // replace radio sound
|
quisk_tmp_playback(cSamples, nSamples, 1.0); // replace radio sound
|
||||||
else if (quisk_record_state == FILE_PLAY_SPKR_MIC)
|
else if (quisk_record_state == FILE_PLAY_SPKR_MIC)
|
||||||
quisk_file_playback(cSamples, nSamples, file_play_level); // replace radio sound
|
quisk_file_playback(cSamples, nSamples, file_play_level); // replace radio sound
|
||||||
// Play the demodulated audio
|
// Play the demodulated audio
|
||||||
#if DEBUG_MIC != 2
|
#if DEBUG_MIC != 2
|
||||||
if ( ! quisk_play_sidetone(&quisk_Playback)) // play sidetone
|
if ( ! quisk_play_sidetone(&quisk_Playback)) { // play sidetone
|
||||||
play_sound_interface(&quisk_Playback, nSamples, cSamples, 1, quisk_audioVolume); // else play radio sound
|
if (rxMode == FDV_U || rxMode == FDV_L)
|
||||||
|
play_sound_interface(&quisk_Playback, nSamples, cSamples, 1, 0); // play silence
|
||||||
|
else
|
||||||
|
play_sound_interface(&quisk_Playback, nSamples, cSamples, 1, quisk_audioVolume); // play radio sound
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (radio_sound_socket != INVALID_SOCKET)
|
if (radio_sound_socket != INVALID_SOCKET)
|
||||||
send_radio_sound_socket(cSamples, nSamples, quisk_audioVolume);
|
send_radio_sound_socket(cSamples, nSamples, quisk_audioVolume);
|
||||||
|
|
||||||
// Play digital if required
|
// Play digital if required
|
||||||
if (is_DGT)
|
//if (is_DGT)
|
||||||
play_sound_interface(&DigitalOutput, nSamples, cSamples, 1, digital_output_level);
|
play_sound_interface(&DigitalOutput, nSamples, cSamples, 1, digital_output_level);
|
||||||
|
// Send radio sound to TCI
|
||||||
|
tci_send_audio(cSamples, nSamples);
|
||||||
|
|
||||||
// Perhaps record the speaker audio to a file
|
// Perhaps record the speaker audio to a file
|
||||||
if ( ! key_state && file_rec_audio.fp)
|
if ( ! key_state && file_rec_audio.fp)
|
||||||
@@ -1061,7 +1070,10 @@ int quisk_read_sound(void) // Called from sound thread
|
|||||||
for (i = 0; i < mic_count; i++)
|
for (i = 0; i < mic_count; i++)
|
||||||
cSamples[i] = 0;
|
cSamples[i] = 0;
|
||||||
}
|
}
|
||||||
//quisk_sample_level("read mic or DGT", cSamples, mic_count, CLIP16);
|
// Receive microphone sound from TCI
|
||||||
|
//quisk_sample_level("read mic or DGT", cSamples, mic_count, CLIP32);
|
||||||
|
mic_count = tci_get_mic(cSamples, mic_count);
|
||||||
|
//quisk_sample_level("quisk_tci", cSamples, mic_count, CLIP32);
|
||||||
// Perhaps record the microphone audio to the speaker audio file
|
// Perhaps record the microphone audio to the speaker audio file
|
||||||
if (key_state && file_rec_audio.fp)
|
if (key_state && file_rec_audio.fp)
|
||||||
quisk_record_audio(&file_rec_audio, cSamples, mic_count);
|
quisk_record_audio(&file_rec_audio, cSamples, mic_count);
|
||||||
|
|||||||
+5
-4
@@ -2,6 +2,7 @@
|
|||||||
* This modue provides sound access for QUISK using the ALSA
|
* This modue provides sound access for QUISK using the ALSA
|
||||||
* library for Linux.
|
* library for Linux.
|
||||||
*/
|
*/
|
||||||
|
#include <string.h>
|
||||||
#ifdef QUISK_HAVE_ALSA
|
#ifdef QUISK_HAVE_ALSA
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
@@ -454,7 +455,7 @@ static int device_list(PyObject * py, snd_pcm_stream_t stream, char * name)
|
|||||||
}
|
}
|
||||||
snprintf(buf100, 100, "%s %s (hw:%d,%d)", card_text, pcm_text, card, dev);
|
snprintf(buf100, 100, "%s %s (hw:%d,%d)", card_text, pcm_text, card, dev);
|
||||||
if (py) { // add to list of devices
|
if (py) { // add to list of devices
|
||||||
PyList_Append(py, PyString_FromString(buf100));
|
PyList_Append(py, PyUnicode_FromString(buf100));
|
||||||
}
|
}
|
||||||
if (name) { // return the "hw:" name
|
if (name) { // return the "hw:" name
|
||||||
if (strstr(buf100, name)) {
|
if (strstr(buf100, name)) {
|
||||||
@@ -1129,10 +1130,10 @@ static void midi_in_devices(PyObject * pylist, int just_names)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pytup = PyTuple_New(2);
|
pytup = PyTuple_New(2);
|
||||||
PyList_Append(pylist, pytup);
|
PyTuple_SetItem(pytup, 0, PyUnicode_DecodeUTF8(friendly_name, strlen(friendly_name), "replace"));
|
||||||
PyTuple_SET_ITEM(pytup, 0, PyUnicode_DecodeUTF8(friendly_name, strlen(friendly_name), "replace"));
|
|
||||||
snprintf(card_name, 32, "hw:%d,%d,%d", card, device, sub);
|
snprintf(card_name, 32, "hw:%d,%d,%d", card, device, sub);
|
||||||
PyTuple_SET_ITEM(pytup, 1, PyUnicode_DecodeUTF8(card_name, strlen(card_name), "replace"));
|
PyTuple_SetItem(pytup, 1, PyUnicode_DecodeUTF8(card_name, strlen(card_name), "replace"));
|
||||||
|
PyList_Append(pylist, pytup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-16
@@ -1,3 +1,5 @@
|
|||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef QUISK_HAVE_DIRECTX
|
#ifdef QUISK_HAVE_DIRECTX
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
@@ -55,14 +57,7 @@ static int match_name(LPCTSTR lpszDesc, const char * name)
|
|||||||
Py_DECREF(py_unicode);
|
Py_DECREF(py_unicode);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#if PY_MAJOR_VERSION >= 3
|
length = PyUnicode_GetLength(py_unicode);
|
||||||
if (PyUnicode_READY(py_unicode) == 0)
|
|
||||||
length = PyUnicode_GET_LENGTH(py_unicode);
|
|
||||||
else
|
|
||||||
length = 0;
|
|
||||||
#else
|
|
||||||
length = PyUnicode_GET_SIZE(py_unicode);
|
|
||||||
#endif
|
|
||||||
if (length <= 0) {
|
if (length <= 0) {
|
||||||
Py_DECREF(py_unicode);
|
Py_DECREF(py_unicode);
|
||||||
Py_DECREF(py_substring);
|
Py_DECREF(py_substring);
|
||||||
@@ -199,14 +194,14 @@ static int quisk_open_capture(struct sound_dev * dev)
|
|||||||
DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)DsEnumCapture, dev);
|
DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)DsEnumCapture, dev);
|
||||||
if (errFound != DS_OK) {
|
if (errFound != DS_OK) {
|
||||||
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
||||||
"DirectSound capture device name %s not found", dev->name);
|
"DirectSound capture device name %.200s not found", dev->name);
|
||||||
if (quisk_sound_state.verbose_sound)
|
if (quisk_sound_state.verbose_sound)
|
||||||
QuiskPrintf("%s\n", dev->dev_errmsg);
|
QuiskPrintf("%s\n", dev->dev_errmsg);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (errOpen != DS_OK) {
|
if (errOpen != DS_OK) {
|
||||||
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
||||||
"DirectSound capture device %s open failed", dev->name);
|
"DirectSound capture device %.200s open failed", dev->name);
|
||||||
if (quisk_sound_state.verbose_sound)
|
if (quisk_sound_state.verbose_sound)
|
||||||
QuiskPrintf("%s\n", dev->dev_errmsg);
|
QuiskPrintf("%s\n", dev->dev_errmsg);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -226,7 +221,7 @@ static int quisk_open_capture(struct sound_dev * dev)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
||||||
"DirectSound capture device %s buffer create failed (0x%lX)", dev->name, hr);
|
"DirectSound capture device %.200s buffer create failed (0x%lX)", dev->name, hr);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ptBuf = (LPDIRECTSOUNDCAPTUREBUFFER)dev->buffer;
|
ptBuf = (LPDIRECTSOUNDCAPTUREBUFFER)dev->buffer;
|
||||||
@@ -235,7 +230,7 @@ static int quisk_open_capture(struct sound_dev * dev)
|
|||||||
if (quisk_sound_state.verbose_sound)
|
if (quisk_sound_state.verbose_sound)
|
||||||
QuiskPrintf("Capture start error 0x%lX", hr);
|
QuiskPrintf("Capture start error 0x%lX", hr);
|
||||||
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
||||||
"DirectSound capture device %s capture start failed", dev->name);
|
"DirectSound capture device %.200s capture start failed", dev->name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (quisk_sound_state.verbose_sound) {
|
if (quisk_sound_state.verbose_sound) {
|
||||||
@@ -270,18 +265,18 @@ static int quisk_open_playback(struct sound_dev * dev)
|
|||||||
DirectSoundEnumerate((LPDSENUMCALLBACK)DsEnumPlay, dev);
|
DirectSoundEnumerate((LPDSENUMCALLBACK)DsEnumPlay, dev);
|
||||||
if (errFound != DS_OK) {
|
if (errFound != DS_OK) {
|
||||||
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
||||||
"DirectSound play device name %s not found", dev->name);
|
"DirectSound play device name %.200s not found", dev->name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (errOpen != DS_OK) {
|
if (errOpen != DS_OK) {
|
||||||
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
||||||
"DirectSound play device %s open failed", dev->name);
|
"DirectSound play device %.200s open failed", dev->name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
hr = IDirectSound_SetCooperativeLevel ((LPDIRECTSOUND8)dev->handle, quisk_mainwin_handle, DSSCL_PRIORITY);
|
hr = IDirectSound_SetCooperativeLevel ((LPDIRECTSOUND8)dev->handle, quisk_mainwin_handle, DSSCL_PRIORITY);
|
||||||
if (hr != DS_OK) {
|
if (hr != DS_OK) {
|
||||||
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
||||||
"DirectSound play device %s cooperative level failed", dev->name);
|
"DirectSound play device %.200s cooperative level failed", dev->name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
dev->sample_bytes = 4;
|
dev->sample_bytes = 4;
|
||||||
@@ -299,7 +294,7 @@ static int quisk_open_playback(struct sound_dev * dev)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
snprintf (dev->dev_errmsg, QUISK_SC_SIZE,
|
||||||
"DirectSound play device %s buffer create failed (0x%X)", dev->name, (unsigned int)hr);
|
"DirectSound play device %.180s buffer create failed (0x%X)", dev->name, (unsigned int)hr);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (quisk_sound_state.verbose_sound) {
|
if (quisk_sound_state.verbose_sound) {
|
||||||
|
|||||||
+3
-1
@@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* This module provides sound access for QUISK using the portaudio library.
|
* This module provides sound access for QUISK using the portaudio library.
|
||||||
*/
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef QUISK_HAVE_PORTAUDIO
|
#ifdef QUISK_HAVE_PORTAUDIO
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
@@ -475,7 +477,7 @@ static int device_list(PyObject * py, int input)
|
|||||||
// found one
|
// found one
|
||||||
if (py) {
|
if (py) {
|
||||||
snprintf(buf100, 100, "%s", info->name);
|
snprintf(buf100, 100, "%s", info->name);
|
||||||
PyList_Append(py, PyString_FromString(buf100));
|
PyList_Append(py, PyUnicode_FromString(buf100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-56
@@ -34,6 +34,8 @@
|
|||||||
* 2020 Apr by N2ADR Changes in printf's
|
* 2020 Apr by N2ADR Changes in printf's
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef QUISK_HAVE_PULSEAUDIO
|
#ifdef QUISK_HAVE_PULSEAUDIO
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
@@ -64,12 +66,20 @@ volatile int streams_to_start;
|
|||||||
// remember all open devices for easy cleanup on exit
|
// remember all open devices for easy cleanup on exit
|
||||||
static pa_stream *OpenPulseDevices[PA_LIST_SIZE * 2] = {NULL};
|
static pa_stream *OpenPulseDevices[PA_LIST_SIZE * 2] = {NULL};
|
||||||
|
|
||||||
static int have_QuiskDigitalInput; // Do we need to create this null sink?
|
|
||||||
static int have_QuiskDigitalOutput; // Do we need to create this null sink?
|
|
||||||
|
|
||||||
static int underrunPlayback;
|
static int underrunPlayback;
|
||||||
static int tlength_bytes;
|
static int tlength_bytes;
|
||||||
|
|
||||||
|
// list of additional sink devices to add
|
||||||
|
struct _NewSinks {
|
||||||
|
char * name; // name of the new sink
|
||||||
|
int create; // create the sink if it doesn't exist
|
||||||
|
};
|
||||||
|
#define NUM_NEW_SINKS 2
|
||||||
|
static struct _NewSinks sink_list[NUM_NEW_SINKS] = {
|
||||||
|
{"QuiskDigitalInput", 1},
|
||||||
|
{"QuiskDigitalOutput", 1}
|
||||||
|
};
|
||||||
|
|
||||||
/* This callback happens any time a stream changes state. Here, it's primary used to
|
/* This callback happens any time a stream changes state. Here, it's primary used to
|
||||||
* tell the quisk thread when streams are ready.
|
* tell the quisk thread when streams are ready.
|
||||||
*/
|
*/
|
||||||
@@ -1022,20 +1032,20 @@ static void source_sink(const char * name, const char * descr, pa_proplist * pro
|
|||||||
PyObject * pytup;
|
PyObject * pytup;
|
||||||
|
|
||||||
pytup = PyTuple_New(3);
|
pytup = PyTuple_New(3);
|
||||||
PyList_Append(pylist, pytup);
|
PyTuple_SetItem(pytup, 0, PyUnicode_FromString(name));
|
||||||
PyTuple_SET_ITEM(pytup, 0, PyString_FromString(name));
|
PyTuple_SetItem(pytup, 1, PyUnicode_FromString(descr));
|
||||||
PyTuple_SET_ITEM(pytup, 1, PyString_FromString(descr));
|
|
||||||
value = pa_proplist_gets(props, "device.api");
|
value = pa_proplist_gets(props, "device.api");
|
||||||
|
|
||||||
if (value && ! strcmp(value, "alsa")) {
|
if (value && ! strcmp(value, "alsa")) {
|
||||||
snprintf(buf300, 300, "%s %s (hw:%s,%s)", pa_proplist_gets(props, "alsa.card_name"), pa_proplist_gets(props, "alsa.name"),
|
snprintf(buf300, 300, "%s %s (hw:%s,%s)", pa_proplist_gets(props, "alsa.card_name"), pa_proplist_gets(props, "alsa.name"),
|
||||||
pa_proplist_gets(props, "alsa.card"), pa_proplist_gets(props, "alsa.device"));
|
pa_proplist_gets(props, "alsa.card"), pa_proplist_gets(props, "alsa.device"));
|
||||||
|
|
||||||
PyTuple_SET_ITEM(pytup, 2, PyString_FromString(buf300));
|
PyTuple_SetItem(pytup, 2, PyUnicode_FromString(buf300));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyTuple_SET_ITEM(pytup, 2, PyString_FromString(""));
|
PyTuple_SetItem(pytup, 2, PyUnicode_FromString(""));
|
||||||
}
|
}
|
||||||
|
PyList_Append(pylist, pytup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1043,26 +1053,30 @@ static void source_sink(const char * name, const char * descr, pa_proplist * pro
|
|||||||
|
|
||||||
// pa_mainloop will call this function when it's ready to tell us about a sink.
|
// pa_mainloop will call this function when it's ready to tell us about a sink.
|
||||||
static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
|
static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
|
||||||
if (eol > 0) // If eol is set to a positive number, you're at the end of the list
|
int i;
|
||||||
|
|
||||||
|
if (eol > 0) // If eol is set to a positive number, you're at the end of the list
|
||||||
return;
|
return;
|
||||||
source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
|
source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
|
||||||
if ( ! strcmp(l->name, "QuiskDigitalInput"))
|
for (i = 0; i < NUM_NEW_SINKS; i++) {
|
||||||
have_QuiskDigitalInput = 1;
|
if ( ! strcmp(l->name, sink_list[i].name)) {
|
||||||
if ( ! strcmp(l->name, "QuiskDigitalOutput"))
|
sink_list[i].create = 0;
|
||||||
have_QuiskDigitalOutput = 1;
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) {
|
static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata)
|
||||||
if (eol > 0)
|
{
|
||||||
|
if (eol > 0)
|
||||||
return;
|
return;
|
||||||
source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
|
source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
|
static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
|
||||||
//printf("%u\n", idx);
|
//printf("%u\n", idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject * quisk_pulseaudio_sound_devices(PyObject * self, PyObject * args)
|
PyObject * quisk_pulseaudio_sound_devices(PyObject * self, PyObject * args) // Called from the GUI thread
|
||||||
{ // Return a list of PulseAudio device names [pycapt, pyplay]
|
{ // Return a list of PulseAudio device names [pycapt, pyplay]
|
||||||
PyObject * pylist, * pycapt, * pyplay;
|
PyObject * pylist, * pycapt, * pyplay;
|
||||||
pa_mainloop *pa_names_ml;
|
pa_mainloop *pa_names_ml;
|
||||||
@@ -1070,6 +1084,9 @@ PyObject * quisk_pulseaudio_sound_devices(PyObject * self, PyObject * args)
|
|||||||
pa_operation *pa_op=NULL;
|
pa_operation *pa_op=NULL;
|
||||||
pa_context *pa_names_ctx;
|
pa_context *pa_names_ctx;
|
||||||
int state = 0;
|
int state = 0;
|
||||||
|
int sink_index = 0;
|
||||||
|
char * sink_name;
|
||||||
|
char buf256[256];
|
||||||
|
|
||||||
if (!PyArg_ParseTuple (args, ""))
|
if (!PyArg_ParseTuple (args, ""))
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1113,59 +1130,41 @@ PyObject * quisk_pulseaudio_sound_devices(PyObject * self, PyObject * args)
|
|||||||
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// Now we wait for our operation to complete. When it's
|
|
||||||
// complete our pa_output_devicelist is filled out, and we move
|
|
||||||
// along to the next state
|
|
||||||
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
|
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
|
||||||
pa_operation_unref(pa_op);
|
pa_operation_unref(pa_op);
|
||||||
// Now we perform another operation to get the source
|
|
||||||
// (input device) list just like before.
|
|
||||||
pa_op = pa_context_get_source_info_list(pa_names_ctx, pa_sourcelist_cb, pycapt);
|
|
||||||
// Update the state so we know what to do next
|
|
||||||
state++;
|
state++;
|
||||||
}
|
}
|
||||||
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
else
|
||||||
|
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
||||||
break;
|
break;
|
||||||
|
// The following loads null sinks for digital input and output if they are not present.
|
||||||
case 3:
|
case 3:
|
||||||
|
if (sink_index >= NUM_NEW_SINKS) {
|
||||||
|
state = 5;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sink_list[sink_index].create) {
|
||||||
|
sink_name = sink_list[sink_index].name;
|
||||||
|
snprintf(buf256, 256, "sink_name=%s sink_properties=device.description=%s", sink_name, sink_name);
|
||||||
|
pa_op = pa_context_load_module(pa_names_ctx, "module-null-sink", buf256, index_callback, NULL);
|
||||||
|
state++;
|
||||||
|
}
|
||||||
|
sink_index++;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
|
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
|
||||||
pa_operation_unref(pa_op);
|
pa_operation_unref(pa_op);
|
||||||
state = 4;
|
state = 3;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
||||||
break;
|
break;
|
||||||
// The following loads modules for digital input and output if they are not present.
|
|
||||||
case 4:
|
|
||||||
if ( ! have_QuiskDigitalInput) {
|
|
||||||
pa_op = pa_context_load_module(pa_names_ctx, "module-null-sink",
|
|
||||||
"sink_name=QuiskDigitalInput sink_properties=device.description=QuiskDigitalInput",
|
|
||||||
index_callback, NULL);
|
|
||||||
state = 5;
|
|
||||||
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
state = 6;
|
|
||||||
break;
|
|
||||||
case 5:
|
case 5:
|
||||||
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
|
pa_op = pa_context_get_source_info_list(pa_names_ctx, pa_sourcelist_cb, pycapt);
|
||||||
pa_operation_unref(pa_op);
|
state++;
|
||||||
state = 6;
|
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
||||||
}
|
|
||||||
else
|
|
||||||
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
if ( ! have_QuiskDigitalOutput) {
|
|
||||||
pa_op = pa_context_load_module(pa_names_ctx, "module-null-sink",
|
|
||||||
"sink_name=QuiskDigitalOutput sink_properties=device.description=QuiskDigitalOutput",
|
|
||||||
index_callback, NULL);
|
|
||||||
state = 7;
|
|
||||||
pa_mainloop_iterate(pa_names_ml, 1, NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
state = 9;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
|
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
|
||||||
pa_operation_unref(pa_op);
|
pa_operation_unref(pa_op);
|
||||||
state = 9;
|
state = 9;
|
||||||
|
|||||||
+9
-6
@@ -7,6 +7,9 @@
|
|||||||
* Note that there is NO WARRANTY AT ALL. USE AT YOUR OWN RISK!!
|
* Note that there is NO WARRANTY AT ALL. USE AT YOUR OWN RISK!!
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef QUISK_HAVE_WASAPI
|
#ifdef QUISK_HAVE_WASAPI
|
||||||
|
|
||||||
#define UNICODE
|
#define UNICODE
|
||||||
@@ -1125,10 +1128,10 @@ PyObject * quisk_wasapi_sound_devices(PyObject * self, PyObject * args) // Call
|
|||||||
EXIT_ON_ERROR(hr)
|
EXIT_ON_ERROR(hr)
|
||||||
// data items are (name, id, is_raw)
|
// data items are (name, id, is_raw)
|
||||||
pytup = PyTuple_New(3);
|
pytup = PyTuple_New(3);
|
||||||
|
PyTuple_SetItem(pytup, 0, PyUnicode_FromWideChar(varName.pwszVal, -1));
|
||||||
|
PyTuple_SetItem(pytup, 1, PyUnicode_FromWideChar(pwszID, -1));
|
||||||
|
PyTuple_SetItem(pytup, 2, PyInt_FromLong(1));
|
||||||
PyList_Append(pyplay, pytup);
|
PyList_Append(pyplay, pytup);
|
||||||
PyTuple_SET_ITEM(pytup, 0, PyUnicode_FromWideChar(varName.pwszVal, -1));
|
|
||||||
PyTuple_SET_ITEM(pytup, 1, PyUnicode_FromWideChar(pwszID, -1));
|
|
||||||
PyTuple_SET_ITEM(pytup, 2, PyInt_FromLong(1));
|
|
||||||
CoTaskMemFree(pwszID);
|
CoTaskMemFree(pwszID);
|
||||||
pwszID = NULL;
|
pwszID = NULL;
|
||||||
PropVariantClear(&varName);
|
PropVariantClear(&varName);
|
||||||
@@ -1155,10 +1158,10 @@ PyObject * quisk_wasapi_sound_devices(PyObject * self, PyObject * args) // Call
|
|||||||
EXIT_ON_ERROR(hr)
|
EXIT_ON_ERROR(hr)
|
||||||
// data items are (name, id, is_raw)
|
// data items are (name, id, is_raw)
|
||||||
pytup = PyTuple_New(3);
|
pytup = PyTuple_New(3);
|
||||||
|
PyTuple_SetItem(pytup, 0, PyUnicode_FromWideChar(varName.pwszVal, -1));
|
||||||
|
PyTuple_SetItem(pytup, 1, PyUnicode_FromWideChar(pwszID, -1));
|
||||||
|
PyTuple_SetItem(pytup, 2, PyInt_FromLong(1));
|
||||||
PyList_Append(pycapt, pytup);
|
PyList_Append(pycapt, pytup);
|
||||||
PyTuple_SET_ITEM(pytup, 0, PyUnicode_FromWideChar(varName.pwszVal, -1));
|
|
||||||
PyTuple_SET_ITEM(pytup, 1, PyUnicode_FromWideChar(pwszID, -1));
|
|
||||||
PyTuple_SET_ITEM(pytup, 2, PyInt_FromLong(1));
|
|
||||||
CoTaskMemFree(pwszID);
|
CoTaskMemFree(pwszID);
|
||||||
pwszID = NULL;
|
pwszID = NULL;
|
||||||
PropVariantClear(&varName);
|
PropVariantClear(&varName);
|
||||||
|
|||||||
@@ -0,0 +1,928 @@
|
|||||||
|
#include <Python.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "ws.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <complex.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "quisk.h"
|
||||||
|
#include "filter.h"
|
||||||
|
|
||||||
|
#define TCI_STREAM_DATA_BYTES 16384
|
||||||
|
#define TCI_RX_BUF_SIZE 1024
|
||||||
|
#define TCI_COMMAND_SIZE 64
|
||||||
|
#define BAD_TCI_IF 999999999
|
||||||
|
|
||||||
|
static int verbose; // non-zero for verbose log messages
|
||||||
|
static int tci_port;
|
||||||
|
|
||||||
|
// Implement the ExpertSDR2 Version 1.4 protocol:
|
||||||
|
// The Tx audio parameters are copied from the Rx audio stream.
|
||||||
|
// The only sample type is FLOAT32 type 3.
|
||||||
|
// It is unclear whether Stream.length is the number of floats, or half that for two channels. And version 1.4 lacks Stream.channels.
|
||||||
|
// The protocol name for TCI version 1.4 is "protocol:ESDR,1.4;" and the port is 40001.
|
||||||
|
// The protocol name for TCI version 2.0 is "protocol:ExpertSDR3,2.0;" and the port is 50001.
|
||||||
|
// The TCI modulations are: am, sam, dsb, lsb, usb, cw, nfm, wfm, spec, digl, digu, drm.
|
||||||
|
|
||||||
|
// These are TCI parameters:
|
||||||
|
static long long quisk_dds;
|
||||||
|
static long long client_dds=-1;
|
||||||
|
static long long quisk_vfo;
|
||||||
|
static long long client_vfo=-1;
|
||||||
|
static int quisk_if;
|
||||||
|
static int client_if=BAD_TCI_IF;
|
||||||
|
static int quisk_trx;
|
||||||
|
static int client_trx=-1;
|
||||||
|
static int quisk_split_enable;
|
||||||
|
static int client_split_enable=-1;
|
||||||
|
static char quisk_modulation[TCI_COMMAND_SIZE];
|
||||||
|
static char client_modulation[TCI_COMMAND_SIZE]={0};
|
||||||
|
|
||||||
|
static ws_cli_conn_t tci_clients_list[MAX_CLIENTS]; // list of TCI clients
|
||||||
|
static int tci_clients_count;
|
||||||
|
static int tci_started; // Did we start the TCI server?
|
||||||
|
|
||||||
|
// Tx Audio
|
||||||
|
ws_cli_conn_t tci_tx_audio_client; // single client providing Tx audio
|
||||||
|
double tci_tx_audio_time; // seconds since the start of Tx audio
|
||||||
|
uint64_t tci_tx_audio_samples; // number of samples requested since the start of Tx audio
|
||||||
|
int tci_tx_audio_rate; // samples per second
|
||||||
|
int tci_tx_audio_request; // the Stream.length to request in TX_CHRONO
|
||||||
|
|
||||||
|
static pthread_mutex_t clients_list_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static pthread_mutex_t tx_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
enum StreamType // ExpertSDR3
|
||||||
|
{
|
||||||
|
IQ_STREAM = 0, // Receiver IQ signal stream
|
||||||
|
RX_AUDIO_STREAM = 1, // Receiver audio stream
|
||||||
|
TX_AUDIO_STREAM = 2, // Audio stream for transmitter
|
||||||
|
TX_CHRONO = 3, // Time marker flow for audio signal transmission
|
||||||
|
LINEOUT_STREAM = 4 // Linear audio output audio stream
|
||||||
|
} ;
|
||||||
|
|
||||||
|
enum SampleType // ExpertSDR3
|
||||||
|
{
|
||||||
|
TCI_INT16 = 0, // 16-bit integral character type
|
||||||
|
TCI_INT24 = 1, // 24-bit integral character type
|
||||||
|
TCI_INT32 = 2, // 32-bit integral character type
|
||||||
|
TCI_FLOAT32 = 3, // 32-bit type with a floating point
|
||||||
|
} ;
|
||||||
|
|
||||||
|
struct ClientData { // data for each client
|
||||||
|
char msg_buf[TCI_RX_BUF_SIZE];
|
||||||
|
int msg_length;
|
||||||
|
int send_Rx_audio_stream;
|
||||||
|
int audio_stream_samplerate;
|
||||||
|
int audio_stream_sample_type;
|
||||||
|
int audio_stream_channels;
|
||||||
|
int audio_stream_samples;
|
||||||
|
int audio_stream_bytes_per_sample;
|
||||||
|
struct quisk_cHB45Filter HalfBand1;
|
||||||
|
struct quisk_cFilter filtDecim3;
|
||||||
|
struct quisk_cFilter filtDecim48to24;
|
||||||
|
int send_IQ_stream;
|
||||||
|
int IQ_stream_samplerate;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
struct _Stream {
|
||||||
|
uint32_t receiver; // receiver number
|
||||||
|
uint32_t sample_rate; // sampling rate
|
||||||
|
uint32_t format; // sample type determined in SampleType
|
||||||
|
uint32_t codec; // compression algorithm (not implemented), always 0
|
||||||
|
uint32_t crc; // checksum (not implemented), always 0
|
||||||
|
int32_t length; // number of samples
|
||||||
|
uint32_t type; // stream type determined in StreamType
|
||||||
|
uint32_t channels; // number of channels for ExpertSDR3
|
||||||
|
uint32_t reserv[8]; // reserved
|
||||||
|
uint8_t data[TCI_STREAM_DATA_BYTES]; // samples
|
||||||
|
} ;
|
||||||
|
|
||||||
|
struct _TxChrono {
|
||||||
|
uint32_t receiver; // receiver number
|
||||||
|
uint32_t sample_rate; // sampling rate
|
||||||
|
uint32_t format; // sample type determined in SampleType
|
||||||
|
uint32_t codec; // compression algorithm (not implemented), always 0
|
||||||
|
uint32_t crc; // checksum (not implemented), always 0
|
||||||
|
int32_t length; // number of samples
|
||||||
|
uint32_t type; // stream type determined in StreamType
|
||||||
|
uint32_t channels; // number of channels for ExpertSDR3
|
||||||
|
uint32_t reserv[8]; // reserved
|
||||||
|
} ;
|
||||||
|
|
||||||
|
static struct _TxChrono TxChrono = {
|
||||||
|
.receiver = 0,
|
||||||
|
.sample_rate = 48000,
|
||||||
|
.format = TCI_FLOAT32,
|
||||||
|
.codec = 0,
|
||||||
|
.crc = 0,
|
||||||
|
.length = 0,
|
||||||
|
.type = TX_CHRONO,
|
||||||
|
.channels = 2,
|
||||||
|
.reserv = {0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
} ;
|
||||||
|
|
||||||
|
struct _StreamTypes {
|
||||||
|
char * name;
|
||||||
|
time_t when;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
#define NUM_STREAM_TYPES 5
|
||||||
|
struct _StreamTypes StreamTypes[NUM_STREAM_TYPES] = {
|
||||||
|
{"iq ", 0},
|
||||||
|
{"rx_audio ", 0},
|
||||||
|
{"tx_audio ", 0},
|
||||||
|
{"tx_chrono", 0},
|
||||||
|
{"lineout ", 0}
|
||||||
|
} ;
|
||||||
|
|
||||||
|
static void PrintStream(const char * msg, size_t size, int always)
|
||||||
|
{
|
||||||
|
struct _Stream * pt = (struct _Stream *)msg;
|
||||||
|
|
||||||
|
if (always || time(NULL) - StreamTypes[pt->type].when > 5) {
|
||||||
|
StreamTypes[pt->type].when = time(NULL);
|
||||||
|
if (pt->type == TX_AUDIO_STREAM)
|
||||||
|
QuiskPrintf("TCI Receiving %slength %5d:", StreamTypes[pt->type].name, (int)size);
|
||||||
|
else
|
||||||
|
QuiskPrintf("TCI Sending %s length %5d:", StreamTypes[pt->type].name, (int)size);
|
||||||
|
QuiskPrintf("%d %5d %d %d %d %5d %d %d\n", (int)(pt->receiver), (int)(pt->sample_rate), (int)(pt->format), (int)(pt->codec),
|
||||||
|
(int)(pt->crc), (int)(pt->length), (int)(pt->type), (int)(pt->channels));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sendframe_txt(ws_cli_conn_t client, const char * msg)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
QuiskPrintf("TCI Send text %s\n", msg);
|
||||||
|
return ws_sendframe_txt(client, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sendframe_txt_bcast(uint16_t port, const char * msg)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
QuiskPrintf("TCI Broadcast text %s\n", msg);
|
||||||
|
return ws_sendframe_txt_bcast(port, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t sendframe_bin(ws_cli_conn_t client, const char * msg, size_t size)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
PrintStream(msg, size, 0);
|
||||||
|
return ws_sendframe_bin(client, msg, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int text_message(ws_cli_conn_t client, struct ClientData * ctx)
|
||||||
|
{ // This function alters the message ctx->msg_buf.
|
||||||
|
int i;
|
||||||
|
char char_buf[TCI_COMMAND_SIZE];
|
||||||
|
char * saveptr, * command;
|
||||||
|
char * arg1, * arg2, * arg3;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
QuiskPrintf("TCI Received text %s\n", ctx->msg_buf);
|
||||||
|
//QuiskPrintf("TCI Received text %s %20.6lf\n", ctx->msg_buf, QuiskTimeSec());
|
||||||
|
for (i = 0; ctx->msg_buf[i]; i++) {
|
||||||
|
char_buf[i] = ctx->msg_buf[i]; // make a copy of the command
|
||||||
|
ctx->msg_buf[i] = tolower(ctx->msg_buf[i]);
|
||||||
|
}
|
||||||
|
char_buf[i] = 0;
|
||||||
|
command = strtok_r(ctx->msg_buf, ":", &saveptr);
|
||||||
|
if (command == NULL)
|
||||||
|
return 0;
|
||||||
|
arg1 = strtok_r(NULL, ",;", &saveptr);
|
||||||
|
arg2 = strtok_r(NULL, ",;", &saveptr);
|
||||||
|
arg3 = strtok_r(NULL, ",;", &saveptr);
|
||||||
|
// The command will be broadcast to all clients unless the return value is zero.
|
||||||
|
switch (command[0]) {
|
||||||
|
case 'a':
|
||||||
|
if (strcmp(command, "audio_start") == 0) {
|
||||||
|
pthread_mutex_lock(&clients_list_mutex);
|
||||||
|
for (i = 0; i < tci_clients_count; i++) {
|
||||||
|
if (tci_clients_list[i] == client) {
|
||||||
|
ctx->send_Rx_audio_stream = 1;
|
||||||
|
memset(&ctx->HalfBand1,
|
||||||
|
0, sizeof(struct quisk_cHB45Filter));
|
||||||
|
quisk_filt_cInit(&ctx->filtDecim3,
|
||||||
|
quiskFilt144D3Coefs, sizeof(quiskFilt144D3Coefs)/sizeof(double));
|
||||||
|
quisk_filt_cInit(&ctx->filtDecim48to24,
|
||||||
|
quiskFilt48dec24Coefs, sizeof(quiskFilt48dec24Coefs)/sizeof(double));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&clients_list_mutex);
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "audio_stop") == 0) {
|
||||||
|
pthread_mutex_lock(&clients_list_mutex);
|
||||||
|
for (i = 0; i < tci_clients_count; i++) {
|
||||||
|
if (tci_clients_list[i] == client) {
|
||||||
|
ctx->send_Rx_audio_stream = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&clients_list_mutex);
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "audio_stream_sample_type") == 0) {
|
||||||
|
if (strcmp(arg1, "float32") == 0) {
|
||||||
|
ctx->audio_stream_sample_type = TCI_FLOAT32;
|
||||||
|
ctx->audio_stream_bytes_per_sample = 4;
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "audio_samplerate") == 0) {
|
||||||
|
i = atoi(arg1);
|
||||||
|
if (i != 48000 && i != 24000 && i != 12000 && i != 8000)
|
||||||
|
return 0;
|
||||||
|
ctx->audio_stream_samplerate = i;
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "audio_stream_channels") == 0) {
|
||||||
|
if (strcmp(arg1, "1") == 0)
|
||||||
|
ctx->audio_stream_channels = 1;
|
||||||
|
else if (strcmp(arg1, "2") == 0)
|
||||||
|
ctx->audio_stream_channels = 2;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "audio_stream_samples") == 0) {
|
||||||
|
return 0; // do not send
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
if (strcmp(command, "dds") == 0) {
|
||||||
|
if (arg2) {
|
||||||
|
client_dds = atoll(arg2);
|
||||||
|
// Echo comes from quisk_tci_set_params()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snprintf(char_buf, TCI_COMMAND_SIZE, "dds:0,%lld;", quisk_dds);
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
if (strcmp(command, "iq_start") == 0) {
|
||||||
|
pthread_mutex_lock(&clients_list_mutex);
|
||||||
|
for (i = 0; i < tci_clients_count; i++) {
|
||||||
|
if (tci_clients_list[i] == client) {
|
||||||
|
ctx->send_IQ_stream = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&clients_list_mutex);
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "iq_stop") == 0) {
|
||||||
|
pthread_mutex_lock(&clients_list_mutex);
|
||||||
|
for (i = 0; i < tci_clients_count; i++) {
|
||||||
|
if (tci_clients_list[i] == client) {
|
||||||
|
ctx->send_IQ_stream = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&clients_list_mutex);
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "iq_samplerate") == 0) {
|
||||||
|
ctx->IQ_stream_samplerate = quisk_sound_state.sample_rate;
|
||||||
|
snprintf(char_buf, TCI_COMMAND_SIZE, "iq_samplerate:%d;", quisk_sound_state.sample_rate);
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "if") == 0) {
|
||||||
|
if (arg3) {
|
||||||
|
client_if = atoi(arg3);
|
||||||
|
// Echo comes from quisk_tci_set_params() UNIMPLEMENTED
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snprintf(char_buf, TCI_COMMAND_SIZE, "if:0,0,%d;", quisk_if);
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
if (strcmp(command, "modulation") == 0) {
|
||||||
|
if (arg2) {
|
||||||
|
strncpy(client_modulation, arg2, TCI_COMMAND_SIZE - 1);
|
||||||
|
// Echo comes from quisk_tci_set_params()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snprintf(char_buf, TCI_COMMAND_SIZE, "modulation:0,%.9s;", quisk_modulation);
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if (strcmp(command, "split_enable") == 0) {
|
||||||
|
if (arg2) {
|
||||||
|
if (strcmp(arg2, "true") == 0)
|
||||||
|
client_split_enable = 1;
|
||||||
|
else
|
||||||
|
client_split_enable = 0;
|
||||||
|
// Echo comes from quisk_tci_set_params()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (quisk_split_enable)
|
||||||
|
sendframe_txt(client, "split_enable:0,true;");
|
||||||
|
else
|
||||||
|
sendframe_txt(client, "split_enable:0,false;");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
if (strcmp(command, "trx") == 0) {
|
||||||
|
if (arg2) {
|
||||||
|
if (strcmp(arg2, "true") == 0) {
|
||||||
|
if (quisk_trx == 0) {
|
||||||
|
pthread_mutex_lock(&tx_buffer_mutex);
|
||||||
|
client_trx = 1;
|
||||||
|
tci_tx_audio_request = TCI_STREAM_DATA_BYTES /
|
||||||
|
ctx->audio_stream_bytes_per_sample / ctx->audio_stream_channels;
|
||||||
|
CircularBuffer(0, NULL, tci_tx_audio_request * 2, 0);
|
||||||
|
tci_tx_audio_client = client;
|
||||||
|
tci_tx_audio_time = QuiskTimeSec();
|
||||||
|
tci_tx_audio_samples = 0;
|
||||||
|
tci_tx_audio_rate = ctx->audio_stream_samplerate;
|
||||||
|
pthread_mutex_unlock(&tx_buffer_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (client == tci_tx_audio_client) {
|
||||||
|
client_trx = 0;
|
||||||
|
tci_tx_audio_client = 0;
|
||||||
|
}
|
||||||
|
// Echo comes from quisk_tci_set_params()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (quisk_trx)
|
||||||
|
sendframe_txt(client, "trx:0,true;");
|
||||||
|
else
|
||||||
|
sendframe_txt(client, "trx:0,false;");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "tx_stream_audio_buffering") == 0) {
|
||||||
|
return 0; // do not send
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
if (strcmp(command, "vfo") == 0) {
|
||||||
|
if (arg3) {
|
||||||
|
client_vfo = atoll(arg3);
|
||||||
|
// Echo comes from quisk_tci_set_params()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snprintf(char_buf, TCI_COMMAND_SIZE, "vfo:0,0,%lld;", quisk_vfo);
|
||||||
|
sendframe_txt(client, char_buf);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1; // send to all clients
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onopen(ws_cli_conn_t client)
|
||||||
|
{
|
||||||
|
char *cli;
|
||||||
|
char command[TCI_COMMAND_SIZE];
|
||||||
|
struct ClientData * ctx;
|
||||||
|
|
||||||
|
// Make parameters for each client:
|
||||||
|
ctx = malloc(sizeof(struct ClientData));
|
||||||
|
ctx->msg_length = 0;
|
||||||
|
ctx->send_Rx_audio_stream = 0;
|
||||||
|
ctx->send_IQ_stream = 0;
|
||||||
|
ctx->audio_stream_samplerate = 48000;
|
||||||
|
ctx->IQ_stream_samplerate = quisk_sound_state.sample_rate;
|
||||||
|
ctx->audio_stream_sample_type = TCI_FLOAT32;
|
||||||
|
ctx->audio_stream_channels = 2;
|
||||||
|
ctx->audio_stream_samples = 0;
|
||||||
|
ctx->audio_stream_bytes_per_sample = 4;
|
||||||
|
ws_set_connection_context(client, ctx);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&clients_list_mutex);
|
||||||
|
tci_clients_list[tci_clients_count++] = client;
|
||||||
|
pthread_mutex_unlock(&clients_list_mutex);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
cli = ws_getaddress(client);
|
||||||
|
QuiskPrintf("TCI *Connection opened, addr: %s\n", cli);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendframe_txt(client, "protocol:ESDR,1.4;");
|
||||||
|
//sendframe_txt(client, "vfo_limits:30000,30000000;"); Not known due to transverters
|
||||||
|
int if_limit = quisk_sound_state.sample_rate / 2;
|
||||||
|
snprintf(command, TCI_COMMAND_SIZE, "if_limits:%d,%d;", - if_limit, if_limit);
|
||||||
|
sendframe_txt(client, command);
|
||||||
|
sendframe_txt(client, "trx_count:1;");
|
||||||
|
sendframe_txt(client, "channel_count:1;");
|
||||||
|
sendframe_txt(client, "device:QuiskSDR;");
|
||||||
|
sendframe_txt(client, "receive_only:false;");
|
||||||
|
sendframe_txt(client, "modulations_list:usb,lsb,cw,am,fm,digl,digu;");
|
||||||
|
sendframe_txt(client, "ready;");
|
||||||
|
|
||||||
|
sendframe_txt(client, "start;");
|
||||||
|
sendframe_txt(client, "tx_enable:0,true;");
|
||||||
|
snprintf(command, TCI_COMMAND_SIZE, "dds:0,%lld;", quisk_dds);
|
||||||
|
sendframe_txt(client, command);
|
||||||
|
snprintf(command, TCI_COMMAND_SIZE, "if:0,0,%d;", quisk_if);
|
||||||
|
sendframe_txt(client, command);
|
||||||
|
snprintf(command, TCI_COMMAND_SIZE, "modulation:0,%.9s;", quisk_modulation);
|
||||||
|
sendframe_txt(client, command);
|
||||||
|
snprintf(command, TCI_COMMAND_SIZE, "vfo:0,0,%lld;", quisk_vfo);
|
||||||
|
sendframe_txt(client, command);
|
||||||
|
if (quisk_trx)
|
||||||
|
sendframe_txt(client, "trx:0,true;");
|
||||||
|
else
|
||||||
|
sendframe_txt(client, "trx:0,false;");
|
||||||
|
if (quisk_split_enable)
|
||||||
|
sendframe_txt(client, "split_enable:0,true;");
|
||||||
|
else
|
||||||
|
sendframe_txt(client, "split_enable:0,false;");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onclose(ws_cli_conn_t client)
|
||||||
|
{
|
||||||
|
int i, count;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&clients_list_mutex);
|
||||||
|
count = tci_clients_count;
|
||||||
|
tci_clients_count = 0;
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
if (tci_clients_list[i] != client)
|
||||||
|
tci_clients_list[tci_clients_count++] = tci_clients_list[i];
|
||||||
|
pthread_mutex_unlock(&clients_list_mutex);
|
||||||
|
|
||||||
|
free(ws_get_connection_context(client));
|
||||||
|
if (verbose) {
|
||||||
|
char * cli = ws_getaddress(client);
|
||||||
|
QuiskPrintf("TCI *Connection closed, addr: %s\n", cli);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onmessage(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, int type)
|
||||||
|
{
|
||||||
|
char echo_copy[TCI_RX_BUF_SIZE];
|
||||||
|
complex double sample;
|
||||||
|
|
||||||
|
if (type == WS_FR_OP_TXT) {
|
||||||
|
if (size > TCI_RX_BUF_SIZE / 2)
|
||||||
|
return; // prevent overflow
|
||||||
|
struct ClientData * ctx = ws_get_connection_context(client);
|
||||||
|
if (ctx->msg_length + size >= TCI_RX_BUF_SIZE - 2)
|
||||||
|
ctx->msg_length = 0; // prevent overflow
|
||||||
|
memcpy(ctx->msg_buf + ctx->msg_length, msg, size); // append msg to buffer
|
||||||
|
ctx->msg_length += size;
|
||||||
|
ctx->msg_buf[ctx->msg_length] = '\0';
|
||||||
|
char * semicolon;
|
||||||
|
char save;
|
||||||
|
while ((semicolon = strchr(ctx->msg_buf, ';')) != NULL) {
|
||||||
|
save = *(semicolon + 1);
|
||||||
|
*(semicolon + 1) = '\0';
|
||||||
|
strncpy(echo_copy, ctx->msg_buf, TCI_RX_BUF_SIZE);
|
||||||
|
if (text_message(client, ctx)) // should we send to all clients?
|
||||||
|
sendframe_txt_bcast(tci_port, echo_copy);
|
||||||
|
*(semicolon + 1) = save;
|
||||||
|
size_t processed_len = (semicolon - ctx->msg_buf) + 1;
|
||||||
|
size_t remaining_len = ctx->msg_length - processed_len;
|
||||||
|
memmove(ctx->msg_buf, ctx->msg_buf + processed_len, remaining_len);
|
||||||
|
ctx->msg_length = remaining_len;
|
||||||
|
ctx->msg_buf[ctx->msg_length] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == WS_FR_OP_BIN) {
|
||||||
|
struct _Stream * pt = (struct _Stream *)msg;
|
||||||
|
int count = 0;
|
||||||
|
float re, im;
|
||||||
|
int channels = TxChrono.channels;
|
||||||
|
int two_channels = channels == 2;
|
||||||
|
int header = 16 * sizeof(uint32_t);
|
||||||
|
const unsigned char * vpt = msg + header;
|
||||||
|
const unsigned char * vpt_end = vpt + (size - header);
|
||||||
|
const unsigned char * vpt2;
|
||||||
|
const unsigned char * vpt2_end;
|
||||||
|
int count2;
|
||||||
|
int buf_start=0, buf_end;
|
||||||
|
|
||||||
|
if (verbose >= 3) {
|
||||||
|
QuiskPrintf("\n\nMSG size %ld length %d\n 0 ", size, pt->length);
|
||||||
|
vpt2 = msg + header;
|
||||||
|
vpt2_end = vpt2 + (size - header);
|
||||||
|
count2 = 0;
|
||||||
|
while (vpt2 < vpt2_end) {
|
||||||
|
re = *(float *)vpt2;
|
||||||
|
vpt2 += sizeof(float);
|
||||||
|
im = *(float *)vpt2;
|
||||||
|
vpt2 += sizeof(float);
|
||||||
|
if (fabsf(re) > 1.1)
|
||||||
|
re = 9;
|
||||||
|
if (fabsf(im) > 1.1)
|
||||||
|
im = 9;
|
||||||
|
count2++;
|
||||||
|
QuiskPrintf("%8.4f %8.4f ", re, im);
|
||||||
|
if (count2 % 5 == 0)
|
||||||
|
QuiskPrintf("\n%4d ", count2);
|
||||||
|
}
|
||||||
|
QuiskPrintf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt->type == TX_AUDIO_STREAM) {
|
||||||
|
if (pt->length > 0 && client == tci_tx_audio_client) {
|
||||||
|
pthread_mutex_lock(&tx_buffer_mutex);
|
||||||
|
if (verbose >= 2)
|
||||||
|
buf_start = CircularBuffer(0, &sample, 0, 0);
|
||||||
|
switch (TxChrono.format) {
|
||||||
|
case TCI_INT16:
|
||||||
|
break;
|
||||||
|
case TCI_FLOAT32:
|
||||||
|
// Version 1.4 does not return the number of channels. We assume two channels.
|
||||||
|
// pt->length is the number of floats
|
||||||
|
while (vpt < vpt_end && count < pt->length) {
|
||||||
|
re = *(float *)vpt;
|
||||||
|
vpt += sizeof(float);
|
||||||
|
count++;
|
||||||
|
if (two_channels) {
|
||||||
|
im = *(float *)vpt;
|
||||||
|
vpt += sizeof(float);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
im = re;
|
||||||
|
}
|
||||||
|
sample = (re + I * im) * (CLIP32 / 2);
|
||||||
|
CircularBuffer(0, &sample, 0, 1);
|
||||||
|
}
|
||||||
|
if (verbose && count != pt->length)
|
||||||
|
QuiskPrintf("TCI *TX count %ld %d %d\n", size, count, pt->length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (verbose >= 2) {
|
||||||
|
buf_end = CircularBuffer(0, &sample, 0, 0);
|
||||||
|
QuiskPrintf("TCI Tx buffer add %4d start %5d end %5d\n", count, buf_start, buf_end);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&tx_buffer_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (verbose)
|
||||||
|
PrintStream((const char *)msg, size, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tci_startup(void)
|
||||||
|
{
|
||||||
|
struct ws_server tci = {
|
||||||
|
.host = NULL,
|
||||||
|
.port = 0,
|
||||||
|
.thread_loop = 1,
|
||||||
|
.timeout_ms = 1000,
|
||||||
|
.evs.onopen = &onopen,
|
||||||
|
.evs.onclose = &onclose,
|
||||||
|
.evs.onmessage = &onmessage
|
||||||
|
};
|
||||||
|
char host32[32];
|
||||||
|
|
||||||
|
tci_clients_count = 0;
|
||||||
|
tci_port = QuiskGetConfigInt("tci_port", 40001);
|
||||||
|
strncpy(host32, QuiskGetConfigString("tci_ip", "127.0.0.1"), 31);
|
||||||
|
tci.host = host32;
|
||||||
|
tci.port = tci_port;
|
||||||
|
ws_socket(&tci);
|
||||||
|
if (verbose)
|
||||||
|
QuiskPrintf("TCI Start server on host %s port %d\n", tci.host, tci.port);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int decimate_audio_stream(complex double * cSamples, int nSamples, struct ClientData * ctx)
|
||||||
|
{
|
||||||
|
switch(ctx->audio_stream_samplerate) { // Starting audio rate is 48000
|
||||||
|
case 48000:
|
||||||
|
break;
|
||||||
|
case 24000:
|
||||||
|
nSamples = quisk_cDecimate(cSamples, nSamples, &ctx->filtDecim48to24, 2);
|
||||||
|
break;
|
||||||
|
case 12000:
|
||||||
|
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &ctx->HalfBand1);
|
||||||
|
nSamples = quisk_cDecimate(cSamples, nSamples, &ctx->filtDecim48to24, 2);
|
||||||
|
break;
|
||||||
|
case 8000:
|
||||||
|
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &ctx->HalfBand1);
|
||||||
|
nSamples = quisk_cDecimate(cSamples, nSamples, &ctx->filtDecim3, 3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
nSamples = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return nSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tci_send_audio(complex double * cSamples, int nSamples) // called from the sound thread
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
struct _Stream stream;
|
||||||
|
struct ClientData * ctx;
|
||||||
|
static complex double * cCopy = NULL;
|
||||||
|
static int cCopy_size = 0;
|
||||||
|
complex double * cAudio;
|
||||||
|
|
||||||
|
if (tci_clients_count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (nSamples <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&clients_list_mutex);
|
||||||
|
for (n = 0; n < tci_clients_count; n++) {
|
||||||
|
ctx = ws_get_connection_context(tci_clients_list[n]);
|
||||||
|
if (ctx->send_Rx_audio_stream == 0)
|
||||||
|
continue;
|
||||||
|
memset(&stream, 0, 16 * sizeof(uint32_t));
|
||||||
|
stream.sample_rate = ctx->audio_stream_samplerate;
|
||||||
|
stream.format = ctx->audio_stream_sample_type;
|
||||||
|
stream.type = RX_AUDIO_STREAM;
|
||||||
|
stream.channels = ctx->audio_stream_channels;
|
||||||
|
int two_channels = stream.channels == 2;
|
||||||
|
int bytes_per_sample = ctx->audio_stream_bytes_per_sample;
|
||||||
|
void * vpt = &stream.data;
|
||||||
|
void * vpt_end = vpt + TCI_STREAM_DATA_BYTES;
|
||||||
|
if (ctx->audio_stream_samplerate != 48000) { // decimation must not change cSamples
|
||||||
|
if (cCopy_size < nSamples) {
|
||||||
|
if (cCopy)
|
||||||
|
free(cCopy);
|
||||||
|
cCopy_size = nSamples * 2;
|
||||||
|
cCopy = (complex double *)malloc(cCopy_size * sizeof(complex double));
|
||||||
|
}
|
||||||
|
memcpy(cCopy, cSamples, nSamples * sizeof(complex double));
|
||||||
|
nSamples = decimate_audio_stream(cCopy, nSamples, ctx);
|
||||||
|
cAudio = cCopy;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cAudio = cSamples;
|
||||||
|
}
|
||||||
|
switch (ctx->audio_stream_sample_type) {
|
||||||
|
case TCI_FLOAT32:
|
||||||
|
stream.length = 0; // make a frame
|
||||||
|
for (i = 0; i < nSamples; i++) {
|
||||||
|
*(float *)vpt = (float)(creal(cAudio[i]) * (1.0 / 2147483648.0 / 2));
|
||||||
|
vpt += bytes_per_sample;
|
||||||
|
stream.length++;
|
||||||
|
if (two_channels) {
|
||||||
|
*(float *)vpt = (float)(cimag(cAudio[i]) * (1.0 / 2147483648.0 / 2));
|
||||||
|
vpt += bytes_per_sample;
|
||||||
|
stream.length++;
|
||||||
|
}
|
||||||
|
if (vpt >= vpt_end || i == nSamples - 1) {
|
||||||
|
sendframe_bin(tci_clients_list[n], (const char *)&stream,
|
||||||
|
(16 * sizeof(uint32_t) + stream.length * bytes_per_sample));
|
||||||
|
stream.length = 0;
|
||||||
|
vpt = &stream.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&clients_list_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tci_send_iq(complex double * cSamples, int nSamples) // called from the sound thread
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
struct _Stream stream;
|
||||||
|
struct ClientData * ctx;
|
||||||
|
|
||||||
|
if (tci_clients_count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (nSamples <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&clients_list_mutex);
|
||||||
|
for (n = 0; n < tci_clients_count; n++) {
|
||||||
|
ctx = ws_get_connection_context(tci_clients_list[n]);
|
||||||
|
if (ctx->send_IQ_stream == 0)
|
||||||
|
continue;
|
||||||
|
memset(&stream, 0, 16 * sizeof(uint32_t));
|
||||||
|
stream.sample_rate = ctx->IQ_stream_samplerate;
|
||||||
|
stream.format = ctx->audio_stream_sample_type;
|
||||||
|
stream.type = IQ_STREAM;
|
||||||
|
stream.channels = 2;
|
||||||
|
int bytes_per_sample = ctx->audio_stream_bytes_per_sample;
|
||||||
|
void * vpt = &stream.data;
|
||||||
|
void * vpt_end = vpt + TCI_STREAM_DATA_BYTES;
|
||||||
|
int original_length = nSamples;
|
||||||
|
switch (ctx->audio_stream_sample_type) {
|
||||||
|
case TCI_FLOAT32:
|
||||||
|
stream.length = 0; // make a frame
|
||||||
|
for (i = 0; i < nSamples; i++) {
|
||||||
|
*(float *)vpt = (float)(creal(cSamples[i]) * (1.0 / 2147483648.0 / 2));
|
||||||
|
vpt += bytes_per_sample;
|
||||||
|
stream.length++;
|
||||||
|
*(float *)vpt = (float)(cimag(cSamples[i]) * (1.0 / 2147483648.0 / 2));
|
||||||
|
vpt += bytes_per_sample;
|
||||||
|
stream.length++;
|
||||||
|
if (vpt >= vpt_end || i == nSamples - 1) {
|
||||||
|
sendframe_bin(tci_clients_list[n], (const char *)&stream,
|
||||||
|
(16 * sizeof(uint32_t) + stream.length * bytes_per_sample));
|
||||||
|
original_length -= stream.length / 2;
|
||||||
|
stream.length = 0;
|
||||||
|
vpt = &stream.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (verbose && original_length != 0)
|
||||||
|
QuiskPrintf("Error in tci_send_iq: Samples remain %d\n", original_length);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&clients_list_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tci_get_mic(complex double * cSamples, int mic_count) // called from the sound thread
|
||||||
|
{
|
||||||
|
if (tci_tx_audio_client) {
|
||||||
|
pthread_mutex_lock(&tx_buffer_mutex); // Copy samples from the Tx buffer
|
||||||
|
int count = CircularBuffer(0, cSamples, mic_count, 0);
|
||||||
|
pthread_mutex_unlock(&tx_buffer_mutex);
|
||||||
|
if (count < mic_count) {
|
||||||
|
if (verbose >= 2)
|
||||||
|
QuiskPrintf("TCI *TX replace mic %d %d\n", count, mic_count);
|
||||||
|
while (count < mic_count)
|
||||||
|
cSamples[count++] = 0;
|
||||||
|
}
|
||||||
|
// Ask for more samples
|
||||||
|
double dtime = QuiskTimeSec();
|
||||||
|
if (tci_tx_audio_samples < (dtime - tci_tx_audio_time) * tci_tx_audio_rate) {
|
||||||
|
TxChrono.length = tci_tx_audio_request;
|
||||||
|
sendframe_bin(tci_tx_audio_client, (const char *)&TxChrono, 16 * sizeof(uint32_t));
|
||||||
|
//tci_tx_audio_samples += tci_tx_audio_request;
|
||||||
|
tci_tx_audio_samples += tci_tx_audio_request / 2; // Stream.length is floats, not samples
|
||||||
|
if (verbose >= 3)
|
||||||
|
QuiskPrintf("TCI Request more Tx samples\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mic_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject * quisk_tci_set_params(PyObject * self, PyObject * args, PyObject * keywds) // called from the GUI thread
|
||||||
|
{ /* Call with keyword arguments ONLY.
|
||||||
|
Sent from Quisk when parameters change. Broadcast the change to clients.*/
|
||||||
|
static char * kwlist[] = {"start", "close", "verbose", "tci_dds", "tci_if", "tci_vfo", "tci_trx", "tci_split_enable",
|
||||||
|
"tci_modulation", NULL} ;
|
||||||
|
long long new_vfo=-1;
|
||||||
|
long long new_dds=-1;
|
||||||
|
int start=-1, close=-1, new_trx=-1, new_split_enable=-1;
|
||||||
|
int new_if = BAD_TCI_IF;
|
||||||
|
char * mode=NULL;
|
||||||
|
char char_buf[TCI_COMMAND_SIZE];
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiiLiLiis", kwlist,
|
||||||
|
&start, &close, &verbose, &new_dds, &new_if, &new_vfo, &new_trx, &new_split_enable,
|
||||||
|
&mode))
|
||||||
|
return NULL;
|
||||||
|
if (start == 1) {
|
||||||
|
tci_started = 1;
|
||||||
|
tci_startup(); // start the TCP server for TCI
|
||||||
|
}
|
||||||
|
if (tci_started == 0) {
|
||||||
|
Py_INCREF (Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
if (close != -1) {
|
||||||
|
sendframe_txt_bcast(tci_port, "stop;");
|
||||||
|
}
|
||||||
|
if (new_dds != -1) {
|
||||||
|
quisk_dds = new_dds;
|
||||||
|
snprintf(char_buf, TCI_COMMAND_SIZE, "dds:0,%lld;", quisk_dds);
|
||||||
|
sendframe_txt_bcast(tci_port, char_buf);
|
||||||
|
}
|
||||||
|
if (new_if != BAD_TCI_IF) {
|
||||||
|
quisk_if = new_if;
|
||||||
|
snprintf(char_buf, TCI_COMMAND_SIZE, "if:0,0,%d;", quisk_if);
|
||||||
|
sendframe_txt_bcast(tci_port, char_buf);
|
||||||
|
}
|
||||||
|
if (new_vfo != -1) {
|
||||||
|
quisk_vfo = new_vfo;
|
||||||
|
snprintf(char_buf, TCI_COMMAND_SIZE, "vfo:0,0,%lld;", quisk_vfo);
|
||||||
|
sendframe_txt_bcast(tci_port, char_buf);
|
||||||
|
}
|
||||||
|
if (new_split_enable != -1) {
|
||||||
|
quisk_split_enable = new_split_enable;
|
||||||
|
if (quisk_split_enable)
|
||||||
|
sendframe_txt_bcast(tci_port, "split_enable:0,true;");
|
||||||
|
else
|
||||||
|
sendframe_txt_bcast(tci_port, "split_enable:0,false;");
|
||||||
|
}
|
||||||
|
if (new_trx != -1) {
|
||||||
|
quisk_trx = new_trx;
|
||||||
|
if (quisk_trx) {
|
||||||
|
sendframe_txt_bcast(tci_port, "trx:0,true;");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tci_tx_audio_client = 0;
|
||||||
|
sendframe_txt_bcast(tci_port, "trx:0,false;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mode) {
|
||||||
|
if (strcmp(mode, "DGT-U") == 0)
|
||||||
|
strcpy(quisk_modulation, "digu");
|
||||||
|
else if (strcmp(mode, "DGT-L") == 0)
|
||||||
|
strcpy(quisk_modulation, "digl");
|
||||||
|
else if (strcmp(mode, "CWL") == 0 || strcmp(mode, "CWU") == 0)
|
||||||
|
strcpy(quisk_modulation, "cw");
|
||||||
|
else if (strcmp(mode, "LSB") == 0)
|
||||||
|
strcpy(quisk_modulation, "lsb");
|
||||||
|
else if (strcmp(mode, "USB") == 0)
|
||||||
|
strcpy(quisk_modulation, "usb");
|
||||||
|
else if (strcmp(mode, "AM") == 0)
|
||||||
|
strcpy(quisk_modulation, "am");
|
||||||
|
else if (strcmp(mode, "FM") == 0)
|
||||||
|
strcpy(quisk_modulation, "fm");
|
||||||
|
else
|
||||||
|
strncpy(quisk_modulation, mode, TCI_COMMAND_SIZE - 1);
|
||||||
|
snprintf(char_buf, TCI_COMMAND_SIZE, "modulation:0,%.9s", quisk_modulation);
|
||||||
|
sendframe_txt_bcast(tci_port, char_buf);
|
||||||
|
}
|
||||||
|
Py_INCREF (Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject * quisk_tci_get_params(PyObject * self, PyObject * args) // called from the GUI thread
|
||||||
|
{ /* Return client request for a parameter change */
|
||||||
|
const char * name;
|
||||||
|
int ii;
|
||||||
|
long long ll;
|
||||||
|
char char_buf[TCI_COMMAND_SIZE];
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple (args, "s", &name))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strcmp(name, "tci_clients_count") == 0)
|
||||||
|
return PyLong_FromLong(tci_clients_count);
|
||||||
|
|
||||||
|
if (tci_started == 0) {
|
||||||
|
}
|
||||||
|
else if (strcmp(name, "tci_vfo") == 0 && client_vfo >= 0) {
|
||||||
|
ll = client_vfo;
|
||||||
|
client_vfo = -1;
|
||||||
|
return PyLong_FromLongLong(ll);
|
||||||
|
}
|
||||||
|
else if (strcmp(name, "tci_dds") == 0 && client_dds >= 0) {
|
||||||
|
ll = client_dds;
|
||||||
|
client_dds = -1;
|
||||||
|
return PyLong_FromLongLong(ll);
|
||||||
|
}
|
||||||
|
else if (strcmp(name, "tci_if") == 0 && client_if != BAD_TCI_IF) {
|
||||||
|
ii = client_if;
|
||||||
|
client_if = BAD_TCI_IF;
|
||||||
|
return PyLong_FromLong(ii);
|
||||||
|
}
|
||||||
|
else if (strcmp(name, "tci_split_enable") == 0 && client_split_enable >= 0) {
|
||||||
|
ii = client_split_enable;
|
||||||
|
client_split_enable = -1;
|
||||||
|
return PyLong_FromLong(ii);
|
||||||
|
}
|
||||||
|
else if (strcmp(name, "tci_trx") == 0 && client_trx >= 0) {
|
||||||
|
ii = client_trx;
|
||||||
|
client_trx = -1;
|
||||||
|
return PyLong_FromLong(ii);
|
||||||
|
}
|
||||||
|
else if (strcmp(name, "tci_modulation") == 0 && client_modulation[0] != 0) {
|
||||||
|
char_buf[0] = 0;
|
||||||
|
if (strcmp(client_modulation, "digu") == 0)
|
||||||
|
strcpy(char_buf, "DGT-U");
|
||||||
|
else if (strcmp(client_modulation, "digl") == 0)
|
||||||
|
strcpy(char_buf, "DGT-L");
|
||||||
|
else if (strcmp(client_modulation, "cw") == 0)
|
||||||
|
strcpy(char_buf, "CWU");
|
||||||
|
else if (strcmp(client_modulation, "lsb") == 0)
|
||||||
|
strcpy(char_buf, "LSB");
|
||||||
|
else if (strcmp(client_modulation, "usb") == 0)
|
||||||
|
strcpy(char_buf, "USB");
|
||||||
|
else if (strcmp(client_modulation, "am") == 0)
|
||||||
|
strcpy(char_buf, "AM");
|
||||||
|
else if (strcmp(client_modulation, "fm") == 0)
|
||||||
|
strcpy(char_buf, "FM");
|
||||||
|
client_modulation[0] = 0;
|
||||||
|
if (char_buf[0])
|
||||||
|
return PyUnicode_FromString(char_buf);
|
||||||
|
}
|
||||||
|
Py_INCREF (Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Amazing utf8 decoder & validator grabbed from:
|
||||||
|
* http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||||
|
*
|
||||||
|
* All rights goes to the original author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utf8.h"
|
||||||
|
|
||||||
|
static const uint8_t utf8d[] = {
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
|
||||||
|
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
|
||||||
|
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
|
||||||
|
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
|
||||||
|
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
|
||||||
|
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
|
||||||
|
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
|
||||||
|
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
|
||||||
|
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
|
||||||
|
uint32_t type = utf8d[byte];
|
||||||
|
|
||||||
|
*codep = (*state != UTF8_ACCEPT) ?
|
||||||
|
(byte & 0x3fu) | (*codep << 6) :
|
||||||
|
(0xff >> type) & (byte);
|
||||||
|
|
||||||
|
*state = utf8d[256 + *state*16 + type];
|
||||||
|
return *state;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_utf8(uint8_t *s) {
|
||||||
|
uint32_t codepoint, state = 0;
|
||||||
|
|
||||||
|
while (*s)
|
||||||
|
decode(&state, &codepoint, *s++);
|
||||||
|
|
||||||
|
return state == UTF8_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_utf8_len(uint8_t *s, size_t len) {
|
||||||
|
uint32_t codepoint, state = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
decode(&state, &codepoint, *s++);
|
||||||
|
|
||||||
|
return state == UTF8_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t is_utf8_len_state(uint8_t *s, size_t len, uint32_t state) {
|
||||||
|
uint32_t codepoint;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
codepoint = 0;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
decode(&state, &codepoint, *s++);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTF8_DECODE_H
|
||||||
|
#define UTF8_DECODE_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* UTF8 return state. */
|
||||||
|
#define UTF8_ACCEPT 0
|
||||||
|
#define UTF8_REJECT 1
|
||||||
|
|
||||||
|
extern int is_utf8(uint8_t* s);
|
||||||
|
extern int is_utf8_len(uint8_t *s, size_t len);
|
||||||
|
extern uint32_t is_utf8_len_state(uint8_t *s, size_t len, uint32_t state);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
|
#include <string.h>
|
||||||
#include "quisk.h"
|
#include "quisk.h"
|
||||||
|
|
||||||
// Access to config file attributes.
|
// Access to config file attributes.
|
||||||
@@ -71,40 +72,27 @@ double QuiskGetConfigDouble(const char * name, double deflt)
|
|||||||
|
|
||||||
char * QuiskGetConfigString(const char * name, char * deflt)
|
char * QuiskGetConfigString(const char * name, char * deflt)
|
||||||
{ // Return the UTF-8 configuration string. Return deflt for failure.
|
{ // Return the UTF-8 configuration string. Return deflt for failure.
|
||||||
char * res;
|
Py_ssize_t size;
|
||||||
PyObject * attr;
|
|
||||||
#if PY_MAJOR_VERSION < 3
|
|
||||||
static char retbuf[QUISK_SC_SIZE];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!quisk_pyConfig || PyErr_Occurred())
|
if (!quisk_pyConfig || PyErr_Occurred())
|
||||||
return deflt;
|
return deflt; // failure
|
||||||
attr = PyObject_GetAttrString(quisk_pyConfig, name);
|
PyObject * attr = PyObject_GetAttrString(quisk_pyConfig, name);
|
||||||
if (attr) {
|
if ( ! attr) {
|
||||||
#if PY_MAJOR_VERSION >= 3
|
PyErr_Clear();
|
||||||
res = (char *)PyUnicode_AsUTF8(attr);
|
return deflt; // failure
|
||||||
#else
|
}
|
||||||
if (PyUnicode_Check(attr)) {
|
if ( ! PyUnicode_Check(attr)) {
|
||||||
PyObject * pystr = PyUnicode_AsUTF8String(attr);
|
Py_DECREF(attr);
|
||||||
strMcpy(retbuf, PyString_AsString(pystr), QUISK_SC_SIZE);
|
return deflt; // failure
|
||||||
retbuf[QUISK_SC_SIZE - 1] = 0;
|
}
|
||||||
res = retbuf;
|
const char * utf8_str = PyUnicode_AsUTF8AndSize(attr, &size);
|
||||||
Py_DECREF(pystr);
|
if ( ! utf8_str) {
|
||||||
}
|
Py_DECREF(attr);
|
||||||
else {
|
PyErr_Clear();
|
||||||
res = PyString_AsString(attr);
|
return deflt; // failure
|
||||||
}
|
}
|
||||||
#endif
|
Py_DECREF(attr);
|
||||||
Py_DECREF(attr);
|
return (char *)utf8_str;
|
||||||
if (res)
|
|
||||||
return res; // success
|
|
||||||
else
|
|
||||||
PyErr_Clear();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
PyErr_Clear();
|
|
||||||
}
|
|
||||||
return deflt; // failure
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double QuiskTimeSec(void)
|
double QuiskTimeSec(void)
|
||||||
@@ -321,3 +309,66 @@ PyObject * QuiskPrintf(char * format, ...)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MAX_CHANNELS 8
|
||||||
|
int CircularBuffer(int channel, complex double * cSamples, int nRead, int nWrite)
|
||||||
|
{
|
||||||
|
// Initialize with cSamples NULL and nRead equal to the buffer delay in samples.
|
||||||
|
// Read with nRead, write with nWrite. Only one can be non-zero for read/write. If both are zero, return nBuf.
|
||||||
|
// Return the number of samples read or written.
|
||||||
|
static struct _channel {
|
||||||
|
complex double * cBuf; // circular sample buffer; save samples until the buffer fills to nDelay
|
||||||
|
int sizeBuf; // current size of cBuf in complex samples
|
||||||
|
int nDelay; // number of samples needed to start reading the buffer
|
||||||
|
int nBuf; // current number of complex samples in cBuf
|
||||||
|
int filling; // if true, we are filling the buffer and not returning samples
|
||||||
|
int Windex; // index of write
|
||||||
|
int Rindex; // index of read
|
||||||
|
} Channels[MAX_CHANNELS] = {0};
|
||||||
|
struct _channel * ptChannel = Channels + channel;
|
||||||
|
int i, num;
|
||||||
|
|
||||||
|
if ( ! cSamples) { // initialize
|
||||||
|
ptChannel->nDelay = nRead;
|
||||||
|
ptChannel->sizeBuf = nRead * 16;
|
||||||
|
ptChannel->cBuf = (complex double *)realloc(ptChannel->cBuf, ptChannel->sizeBuf * sizeof(complex double));
|
||||||
|
memset(ptChannel->cBuf, 0, ptChannel->sizeBuf * sizeof(complex double));
|
||||||
|
ptChannel->nBuf = 0;
|
||||||
|
ptChannel->Windex = 0;
|
||||||
|
ptChannel->Rindex = 0;
|
||||||
|
ptChannel->filling = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ( ! ptChannel->cBuf)
|
||||||
|
return 0;
|
||||||
|
if (nRead == 0 && nWrite == 0)
|
||||||
|
return ptChannel->nBuf;
|
||||||
|
num = 0; // number of samples read or written
|
||||||
|
if (nWrite > 0) { // Copy samples from cSamples to cBuf
|
||||||
|
for (i = 0; i < nWrite && ptChannel->nBuf < ptChannel->sizeBuf; i++) {
|
||||||
|
ptChannel->cBuf[ptChannel->Windex++] = cSamples[i];
|
||||||
|
if (ptChannel->Windex >= ptChannel->sizeBuf)
|
||||||
|
ptChannel->Windex = 0;
|
||||||
|
ptChannel->nBuf++;
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
if (ptChannel->nBuf >= ptChannel->nDelay)
|
||||||
|
ptChannel->filling = 0;
|
||||||
|
//if (num != nWrite)
|
||||||
|
// QuiskPrintf("Circ nWrite %d %d %d %d\n", num, nWrite, ptChannel->nBuf, ptChannel->sizeBuf);
|
||||||
|
}
|
||||||
|
else if (nRead > 0) {
|
||||||
|
if ( ! ptChannel->filling) { // Copy samples from cBuf to cSamples
|
||||||
|
for (i = 0; i < nRead && ptChannel->nBuf > 0; i++) {
|
||||||
|
cSamples[i] = ptChannel->cBuf[ptChannel->Rindex++];
|
||||||
|
if (ptChannel->Rindex >= ptChannel->sizeBuf)
|
||||||
|
ptChannel->Rindex = 0;
|
||||||
|
ptChannel->nBuf--;
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
if (ptChannel->nBuf <= 0)
|
||||||
|
ptChannel->filling = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|||||||
+340
@@ -0,0 +1,340 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
||||||
|
|
||||||
+640
@@ -0,0 +1,640 @@
|
|||||||
|
#
|
||||||
|
# Makefile for WDSP library
|
||||||
|
# This one creates libwdsp.so, the shared library.
|
||||||
|
#
|
||||||
|
|
||||||
|
LIBS=-lfftw3 -lpthread
|
||||||
|
|
||||||
|
#CFLAGS?= -pthread -O3 -D_GNU_SOURCE -Wno-parentheses
|
||||||
|
CFLAGS=-g -fPIC -O3 -D _GNU_SOURCE
|
||||||
|
|
||||||
|
FFTWINCLUDE=`pkg-config --cflags fftw3`
|
||||||
|
|
||||||
|
COMPILE=$(CC) $(CFLAGS) $(FFTWINCLUDE)
|
||||||
|
|
||||||
|
SOURCES= amd.c\
|
||||||
|
ammod.c\
|
||||||
|
amsq.c\
|
||||||
|
analyzer.c\
|
||||||
|
anf.c\
|
||||||
|
anr.c\
|
||||||
|
bandpass.c\
|
||||||
|
calcc.c\
|
||||||
|
calculus.c\
|
||||||
|
cblock.c\
|
||||||
|
cfcomp.c\
|
||||||
|
cfir.c\
|
||||||
|
channel.c\
|
||||||
|
compress.c\
|
||||||
|
delay.c\
|
||||||
|
dexp.c\
|
||||||
|
div.c\
|
||||||
|
eer.c\
|
||||||
|
emnr.c\
|
||||||
|
emph.c\
|
||||||
|
eq.c\
|
||||||
|
fcurve.c\
|
||||||
|
fir.c\
|
||||||
|
firmin.c\
|
||||||
|
fmd.c\
|
||||||
|
fmmod.c\
|
||||||
|
fmsq.c\
|
||||||
|
gain.c\
|
||||||
|
gen.c\
|
||||||
|
icfir.c\
|
||||||
|
iir.c\
|
||||||
|
iobuffs.c\
|
||||||
|
iqc.c\
|
||||||
|
linux_port.c\
|
||||||
|
lmath.c\
|
||||||
|
main.c\
|
||||||
|
meter.c\
|
||||||
|
meterlog10.c\
|
||||||
|
nbp.c\
|
||||||
|
nob.c\
|
||||||
|
nobII.c\
|
||||||
|
osctrl.c\
|
||||||
|
patchpanel.c\
|
||||||
|
resample.c\
|
||||||
|
rmatch.c\
|
||||||
|
RXA.c\
|
||||||
|
sender.c\
|
||||||
|
shift.c\
|
||||||
|
siphon.c\
|
||||||
|
slew.c\
|
||||||
|
snb.c\
|
||||||
|
ssql.c \
|
||||||
|
syncbuffs.c\
|
||||||
|
TXA.c\
|
||||||
|
utilities.c\
|
||||||
|
varsamp.c\
|
||||||
|
version.c\
|
||||||
|
wcpAGC.c\
|
||||||
|
wisdom.c\
|
||||||
|
zetahat.c
|
||||||
|
|
||||||
|
HEADERS=amd.h\
|
||||||
|
ammod.h\
|
||||||
|
amsq.h\
|
||||||
|
analyzer.h\
|
||||||
|
anf.h\
|
||||||
|
anr.h\
|
||||||
|
bandpass.h\
|
||||||
|
calcc.h\
|
||||||
|
calculus.h\
|
||||||
|
cblock.h\
|
||||||
|
cfcomp.h\
|
||||||
|
cfir.h\
|
||||||
|
channel.h\
|
||||||
|
comm.h\
|
||||||
|
compress.h\
|
||||||
|
delay.h\
|
||||||
|
dexp.h\
|
||||||
|
div.h\
|
||||||
|
eer.h\
|
||||||
|
emnr.h\
|
||||||
|
emph.h\
|
||||||
|
eq.h\
|
||||||
|
fastmath.h\
|
||||||
|
fcurve.h\
|
||||||
|
fir.h\
|
||||||
|
firmin.h \
|
||||||
|
fmd.h\
|
||||||
|
fmmod.h\
|
||||||
|
fmsq.h\
|
||||||
|
gain.h\
|
||||||
|
gen.h\
|
||||||
|
icfir.h\
|
||||||
|
iir.h\
|
||||||
|
iobuffs.h\
|
||||||
|
iqc.h\
|
||||||
|
linux_port.h\
|
||||||
|
lmath.h\
|
||||||
|
main.h\
|
||||||
|
meter.h\
|
||||||
|
meterlog10.h\
|
||||||
|
nbp.h\
|
||||||
|
nob.h\
|
||||||
|
nobII.h\
|
||||||
|
osctrl.h\
|
||||||
|
patchpanel.h\
|
||||||
|
resample.h\
|
||||||
|
resource.h\
|
||||||
|
rmatch.h\
|
||||||
|
RXA.h\
|
||||||
|
sender.h\
|
||||||
|
shift.h\
|
||||||
|
siphon.h\
|
||||||
|
slew.h\
|
||||||
|
snb.h\
|
||||||
|
ssql.h \
|
||||||
|
syncbuffs.h\
|
||||||
|
TXA.h\
|
||||||
|
utilities.h\
|
||||||
|
wcpAGC.h \
|
||||||
|
zetahat.h
|
||||||
|
|
||||||
|
OBJS=linux_port.o\
|
||||||
|
amd.o\
|
||||||
|
ammod.o\
|
||||||
|
amsq.o\
|
||||||
|
analyzer.o\
|
||||||
|
anf.o\
|
||||||
|
anr.o\
|
||||||
|
bandpass.o\
|
||||||
|
calcc.o\
|
||||||
|
calculus.o\
|
||||||
|
cblock.o\
|
||||||
|
cfcomp.o\
|
||||||
|
cfir.o\
|
||||||
|
channel.o\
|
||||||
|
compress.o\
|
||||||
|
delay.o\
|
||||||
|
dexp.o\
|
||||||
|
div.o\
|
||||||
|
eer.o\
|
||||||
|
emnr.o\
|
||||||
|
emph.o\
|
||||||
|
eq.o\
|
||||||
|
fcurve.o\
|
||||||
|
fir.o\
|
||||||
|
firmin.o\
|
||||||
|
fmd.o\
|
||||||
|
fmmod.o\
|
||||||
|
fmsq.o\
|
||||||
|
gain.o\
|
||||||
|
gen.o\
|
||||||
|
icfir.o\
|
||||||
|
iir.o\
|
||||||
|
iobuffs.o\
|
||||||
|
iqc.o\
|
||||||
|
lmath.o\
|
||||||
|
main.o\
|
||||||
|
meter.o\
|
||||||
|
meterlog10.o\
|
||||||
|
nbp.o\
|
||||||
|
nob.o\
|
||||||
|
nobII.o\
|
||||||
|
osctrl.o\
|
||||||
|
patchpanel.o\
|
||||||
|
resample.o\
|
||||||
|
rmatch.o\
|
||||||
|
RXA.o\
|
||||||
|
sender.o\
|
||||||
|
shift.o\
|
||||||
|
siphon.o\
|
||||||
|
slew.o\
|
||||||
|
snb.o\
|
||||||
|
ssql.o \
|
||||||
|
syncbuffs.o\
|
||||||
|
TXA.o\
|
||||||
|
utilities.o\
|
||||||
|
version.o\
|
||||||
|
varsamp.o\
|
||||||
|
wcpAGC.o\
|
||||||
|
wisdom.o\
|
||||||
|
zetahat.o
|
||||||
|
|
||||||
|
libwdsp.so: $(OBJS)
|
||||||
|
$(CC) -shared -o libwdsp.so $(OBJS) $(LIBS)
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
$(COMPILE) -c -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -f libwdsp.so *.o
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
#
|
||||||
|
# What follows is automatically generated by the "makedepend" program
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
# DO NOT DELETE
|
||||||
|
|
||||||
|
RXA.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
RXA.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
RXA.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
RXA.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
RXA.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
RXA.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
RXA.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
TXA.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
TXA.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
TXA.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
TXA.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
TXA.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
TXA.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
TXA.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
amd.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
amd.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
amd.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
amd.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
amd.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
amd.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
amd.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
ammod.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
ammod.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
ammod.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
ammod.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
ammod.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
ammod.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
ammod.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
amsq.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
amsq.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
amsq.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
amsq.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
amsq.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
amsq.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
amsq.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
analyzer.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
analyzer.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
analyzer.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
analyzer.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
analyzer.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
analyzer.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h RXA.h
|
||||||
|
analyzer.o: sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h
|
||||||
|
analyzer.o: utilities.h
|
||||||
|
anf.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
anf.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
anf.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
anf.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
anf.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
anf.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
anf.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
anr.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
anr.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
anr.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
anr.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
anr.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
anr.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
anr.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
bandpass.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
bandpass.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
bandpass.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
bandpass.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
bandpass.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
bandpass.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h RXA.h
|
||||||
|
bandpass.o: sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h
|
||||||
|
bandpass.o: utilities.h
|
||||||
|
calcc.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
calcc.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
calcc.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
calcc.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
calcc.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
calcc.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
calcc.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
cblock.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
cblock.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
cblock.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
cblock.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
cblock.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
cblock.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
cblock.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
cfcomp.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
cfcomp.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
cfcomp.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
cfcomp.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
cfcomp.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
cfcomp.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
cfcomp.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
cfir.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
cfir.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
cfir.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
cfir.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
cfir.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
cfir.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
cfir.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
channel.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
channel.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
channel.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
channel.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
channel.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
channel.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h RXA.h
|
||||||
|
channel.o: sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h
|
||||||
|
channel.o: utilities.h
|
||||||
|
comm.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
comm.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
comm.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
comm.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
comm.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
comm.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
comm.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
compress.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
compress.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
compress.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
compress.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
compress.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
compress.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h RXA.h
|
||||||
|
compress.o: sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h
|
||||||
|
compress.o: utilities.h
|
||||||
|
delay.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
delay.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
delay.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
delay.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
delay.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
delay.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
delay.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
dexp.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
dexp.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
dexp.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
dexp.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
dexp.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
dexp.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
dexp.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
div.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
div.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
div.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
div.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
div.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
div.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
div.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
eer.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
eer.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
eer.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
eer.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
eer.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
eer.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
eer.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
emnr.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
emnr.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
emnr.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
emnr.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
emnr.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
emnr.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
emnr.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h calculus.h
|
||||||
|
emph.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
emph.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
emph.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
emph.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
emph.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
emph.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
emph.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
eq.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
eq.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
eq.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
eq.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
eq.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
eq.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
eq.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
fcurve.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
fcurve.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
fcurve.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
fcurve.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
fcurve.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
fcurve.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
fcurve.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
fir.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
fir.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
fir.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
fir.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
fir.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
fir.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
fir.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
firmin.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
firmin.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
firmin.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
firmin.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
firmin.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
firmin.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
firmin.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
fmd.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
fmd.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
fmd.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
fmd.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
fmd.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
fmd.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
fmd.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
fmmod.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
fmmod.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
fmmod.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
fmmod.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
fmmod.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
fmmod.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
fmmod.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
fmsq.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
fmsq.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
fmsq.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
fmsq.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
fmsq.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
fmsq.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
fmsq.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
gain.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
gain.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
gain.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
gain.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
gain.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
gain.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
gain.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
gen.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
gen.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
gen.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
gen.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
gen.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
gen.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
gen.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
icfir.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
icfir.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
icfir.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
icfir.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
icfir.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
icfir.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
icfir.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
iir.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
iir.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
iir.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
iir.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
iir.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
iir.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
iir.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
iobuffs.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
iobuffs.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
iobuffs.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
iobuffs.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
iobuffs.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
iobuffs.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h RXA.h
|
||||||
|
iobuffs.o: sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h
|
||||||
|
iobuffs.o: utilities.h
|
||||||
|
iqc.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
iqc.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
iqc.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
iqc.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
iqc.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
iqc.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
iqc.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
linux_port.o: linux_port.h comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h
|
||||||
|
linux_port.o: bandpass.h firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h
|
||||||
|
linux_port.o: cfir.h channel.h compress.h dexp.h div.h eer.h emnr.h emph.h
|
||||||
|
linux_port.o: eq.h fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h
|
||||||
|
linux_port.o: gen.h icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h
|
||||||
|
linux_port.o: nob.h nobII.h osctrl.h patchpanel.h resample.h rmatch.h
|
||||||
|
linux_port.o: varsamp.h RXA.h sender.h shift.h siphon.h slew.h snb.h ssql.h
|
||||||
|
linux_port.o: syncbuffs.h TXA.h utilities.h
|
||||||
|
lmath.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
lmath.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
lmath.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
lmath.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
lmath.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
lmath.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
lmath.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
main.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
main.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
main.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
main.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
main.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
main.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
main.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
meter.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
meter.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
meter.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
meter.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
meter.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
meter.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
meter.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
meterlog10.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
meterlog10.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
meterlog10.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
meterlog10.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
meterlog10.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
meterlog10.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h
|
||||||
|
meterlog10.o: RXA.h sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h
|
||||||
|
meterlog10.o: TXA.h utilities.h
|
||||||
|
nbp.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
nbp.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
nbp.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
nbp.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
nbp.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
nbp.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
nbp.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
nob.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
nob.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
nob.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
nob.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
nob.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
nob.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
nob.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
nobII.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
nobII.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
nobII.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
nobII.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
nobII.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
nobII.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
nobII.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
osctrl.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
osctrl.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
osctrl.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
osctrl.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
osctrl.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
osctrl.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
osctrl.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
patchpanel.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
patchpanel.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
patchpanel.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
patchpanel.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
patchpanel.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
patchpanel.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h
|
||||||
|
patchpanel.o: RXA.h sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h
|
||||||
|
patchpanel.o: TXA.h utilities.h
|
||||||
|
resample.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
resample.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
resample.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
resample.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
resample.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
resample.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h RXA.h
|
||||||
|
resample.o: sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h
|
||||||
|
resample.o: utilities.h
|
||||||
|
rmatch.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
rmatch.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
rmatch.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
rmatch.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
rmatch.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
rmatch.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
rmatch.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
sender.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
sender.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
sender.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
sender.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
sender.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
sender.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
sender.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
shift.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
shift.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
shift.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
shift.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
shift.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
shift.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
shift.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
siphon.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
siphon.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
siphon.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
siphon.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
siphon.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
siphon.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
siphon.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
slew.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
slew.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
slew.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
slew.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
slew.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
slew.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
slew.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
snb.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h firmin.h
|
||||||
|
snb.o: calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h compress.h
|
||||||
|
snb.o: dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h iir.h
|
||||||
|
snb.o: wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h main.h
|
||||||
|
snb.o: meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
snb.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
snb.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
ssql.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
ssql.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
ssql.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h fmd.h
|
||||||
|
ssql.o: iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h iqc.h
|
||||||
|
ssql.o: main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h patchpanel.h
|
||||||
|
ssql.o: resample.h rmatch.h varsamp.h RXA.h sender.h shift.h siphon.h slew.h
|
||||||
|
ssql.o: snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
syncbuffs.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
syncbuffs.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
syncbuffs.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
syncbuffs.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
syncbuffs.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
syncbuffs.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h
|
||||||
|
syncbuffs.o: RXA.h sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h
|
||||||
|
syncbuffs.o: TXA.h utilities.h
|
||||||
|
utilities.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
utilities.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
utilities.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
utilities.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
utilities.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
utilities.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h
|
||||||
|
utilities.o: RXA.h sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h
|
||||||
|
utilities.o: TXA.h utilities.h
|
||||||
|
varsamp.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
varsamp.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
varsamp.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
varsamp.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
varsamp.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
varsamp.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h RXA.h
|
||||||
|
varsamp.o: sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h
|
||||||
|
varsamp.o: utilities.h
|
||||||
|
version.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
version.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h
|
||||||
|
version.o: channel.h compress.h dexp.h div.h eer.h emnr.h emph.h eq.h
|
||||||
|
version.o: fcurve.h fir.h fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h
|
||||||
|
version.o: icfir.h iobuffs.h iqc.h main.h meter.h meterlog10.h nbp.h nob.h
|
||||||
|
version.o: nobII.h osctrl.h patchpanel.h resample.h rmatch.h varsamp.h RXA.h
|
||||||
|
version.o: sender.h shift.h siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h
|
||||||
|
version.o: utilities.h
|
||||||
|
wcpAGC.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
wcpAGC.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
wcpAGC.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
wcpAGC.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
wcpAGC.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
wcpAGC.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
wcpAGC.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
|
wisdom.o: comm.h amd.h ammod.h amsq.h analyzer.h anf.h anr.h bandpass.h
|
||||||
|
wisdom.o: firmin.h calcc.h delay.h lmath.h cblock.h cfcomp.h cfir.h channel.h
|
||||||
|
wisdom.o: compress.h dexp.h div.h eer.h emnr.h emph.h eq.h fcurve.h fir.h
|
||||||
|
wisdom.o: fmd.h iir.h wcpAGC.h fmmod.h fmsq.h gain.h gen.h icfir.h iobuffs.h
|
||||||
|
wisdom.o: iqc.h main.h meter.h meterlog10.h nbp.h nob.h nobII.h osctrl.h
|
||||||
|
wisdom.o: patchpanel.h resample.h rmatch.h varsamp.h RXA.h sender.h shift.h
|
||||||
|
wisdom.o: siphon.h slew.h snb.h ssql.h syncbuffs.h TXA.h utilities.h
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# wdsp
|
||||||
|
WDSP by Warren Pratt, NR0V
|
||||||
|
DSP Library originally written for Windows
|
||||||
|
Ported to Linux and Android by John Melton g0orx/n6lyt
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
To use this Linux library with Quisk, enter "make". It supports the NR2 and SNB features.
|
||||||
|
The resulting libwdsp.so file is used in-place. No installation is necessary.
|
||||||
|
For Windows, the libwdsp.dll file is included in the quisk directory.
|
||||||
|
|
||||||
|
The WDSP library was originally written for Windows by Warren Pratt, NR0V.
|
||||||
|
John Melton, g0orx/n6lyt, contributed a Linux port.
|
||||||
|
Christian, DL1YCF, contributed further improvements.
|
||||||
+958
@@ -0,0 +1,958 @@
|
|||||||
|
/* RXA.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2014, 2015, 2016, 2023 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
struct _rxa rxa[MAX_CHANNELS];
|
||||||
|
|
||||||
|
void create_rxa (int channel)
|
||||||
|
{
|
||||||
|
rxa[channel].mode = RXA_LSB;
|
||||||
|
rxa[channel].inbuff = (double *) malloc0 (1 * ch[channel].dsp_insize * sizeof (complex));
|
||||||
|
rxa[channel].outbuff = (double *) malloc0 (1 * ch[channel].dsp_outsize * sizeof (complex));
|
||||||
|
rxa[channel].midbuff = (double *) malloc0 (2 * ch[channel].dsp_size * sizeof (complex));
|
||||||
|
|
||||||
|
// shift to select a slice of spectrum
|
||||||
|
rxa[channel].shift.p = create_shift (
|
||||||
|
1, // run
|
||||||
|
ch[channel].dsp_insize, // input buffer size
|
||||||
|
rxa[channel].inbuff, // pointer to input buffer
|
||||||
|
rxa[channel].inbuff, // pointer to output buffer
|
||||||
|
ch[channel].in_rate, // samplerate
|
||||||
|
0.0); // amount to shift (Hz)
|
||||||
|
|
||||||
|
// resample to dsp rate for main processing
|
||||||
|
rxa[channel].rsmpin.p = create_resample (
|
||||||
|
0, // run - will be turned ON below if needed
|
||||||
|
ch[channel].dsp_insize, // input buffer size
|
||||||
|
rxa[channel].inbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
ch[channel].in_rate, // input samplerate
|
||||||
|
ch[channel].dsp_rate, // output samplerate
|
||||||
|
0.0, // select cutoff automatically
|
||||||
|
0, // select ncoef automatically
|
||||||
|
1.0); // gain
|
||||||
|
|
||||||
|
// signal generator
|
||||||
|
rxa[channel].gen0.p = create_gen (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // input buffer
|
||||||
|
rxa[channel].midbuff, // output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
2); // mode
|
||||||
|
|
||||||
|
// adc (input) meter
|
||||||
|
rxa[channel].adcmeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
0, // optional pointer to another 'run'
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
rxa[channel].midbuff, // pointer to buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
rxa[channel].meter, // result vector
|
||||||
|
rxa[channel].pmtupdate, // locks for meter access
|
||||||
|
RXA_ADC_AV, // index for average value
|
||||||
|
RXA_ADC_PK, // index for peak value
|
||||||
|
-1, // index for gain value
|
||||||
|
0); // pointer for gain computation
|
||||||
|
|
||||||
|
// notch database
|
||||||
|
rxa[channel].ndb.p = create_notchdb (
|
||||||
|
0, // master run for all nbp's
|
||||||
|
1024); // max number of notches
|
||||||
|
|
||||||
|
// notched bandpass
|
||||||
|
rxa[channel].nbp0.p = create_nbp (
|
||||||
|
1, // run, always runs
|
||||||
|
0, // run the notches
|
||||||
|
0, // position
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of coefficients
|
||||||
|
0, // minimum phase flag
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
-4150.0, // lower filter frequency
|
||||||
|
-150.0, // upper filter frequency
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0, // wintype
|
||||||
|
1.0, // gain
|
||||||
|
1, // auto-increase notch width
|
||||||
|
1025, // max number of passbands
|
||||||
|
&rxa[channel].ndb.p); // addr of database pointer
|
||||||
|
|
||||||
|
// bandpass for snba
|
||||||
|
rxa[channel].bpsnba.p = create_bpsnba (
|
||||||
|
0, // bpsnba run flag
|
||||||
|
0, // run the notches
|
||||||
|
0, // position
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of filter coefficients
|
||||||
|
0, // minimum phase flag
|
||||||
|
rxa[channel].midbuff, // input buffer
|
||||||
|
rxa[channel].midbuff, // output buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
+ 250.0, // abs value of cutoff nearest zero
|
||||||
|
+ 5700.0, // abs value of cutoff farthest zero
|
||||||
|
- 5700.0, // current low frequency
|
||||||
|
- 250.0, // current high frequency
|
||||||
|
0, // wintype
|
||||||
|
1.0, // gain
|
||||||
|
1, // auto-increase notch width
|
||||||
|
1025, // max number of passbands
|
||||||
|
&rxa[channel].ndb.p); // addr of database pointer
|
||||||
|
|
||||||
|
// send spectrum display
|
||||||
|
rxa[channel].sender.p = create_sender (
|
||||||
|
channel == 0, // run
|
||||||
|
0, // flag
|
||||||
|
0, // mode
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
0, // arg0 <- disp
|
||||||
|
1, // arg1 <- ss
|
||||||
|
0, // arg2 <- LO
|
||||||
|
0); // arg3 <- NOT USED
|
||||||
|
|
||||||
|
// S-meter
|
||||||
|
rxa[channel].smeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
0, // optional pointer to another 'run'
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
rxa[channel].midbuff, // pointer to buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
rxa[channel].meter, // result vector
|
||||||
|
rxa[channel].pmtupdate, // locks for meter access
|
||||||
|
RXA_S_AV, // index for average value
|
||||||
|
RXA_S_PK, // index for peak value
|
||||||
|
-1, // index for gain value
|
||||||
|
0); // pointer for gain computation
|
||||||
|
|
||||||
|
// AM squelch
|
||||||
|
rxa[channel].amsq.p = create_amsq (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // pointer to signal input buffer used by xamsq
|
||||||
|
rxa[channel].midbuff, // pointer to signal output buffer used by xamsq
|
||||||
|
rxa[channel].midbuff, // pointer to trigger buffer that xamsqcap will capture
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0.010, // time constant for averaging signal level
|
||||||
|
0.070, // signal up transition time
|
||||||
|
0.070, // signal down transition time
|
||||||
|
0.009, // signal level to initiate tail
|
||||||
|
0.010, // signal level to initiate unmute
|
||||||
|
0.000, // minimum tail length
|
||||||
|
1.500, // maximum tail length
|
||||||
|
0.0); // muted gain
|
||||||
|
|
||||||
|
// AM demod
|
||||||
|
rxa[channel].amd.p = create_amd (
|
||||||
|
0, // run - OFF by default
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
0, // mode: 0->AM, 1->SAM
|
||||||
|
1, // levelfade: 0->OFF, 1->ON
|
||||||
|
0, // sideband mode: 0->OFF
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
-2000.0, // minimum lock frequency
|
||||||
|
+2000.0, // maximum lock frequency
|
||||||
|
1.0, // zeta
|
||||||
|
250.0, // omegaN
|
||||||
|
0.02, // tauR
|
||||||
|
1.4); // tauI
|
||||||
|
|
||||||
|
// FM demod
|
||||||
|
rxa[channel].fmd.p = create_fmd (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
5000.0, // deviation
|
||||||
|
300.0, // f_low
|
||||||
|
3000.0, // f_high
|
||||||
|
-8000.0, // fmin
|
||||||
|
+8000.0, // fmax
|
||||||
|
1.0, // zeta
|
||||||
|
20000.0, // omegaN
|
||||||
|
0.02, // tau - for dc removal
|
||||||
|
0.5, // audio gain
|
||||||
|
1, // run tone filter
|
||||||
|
254.1, // ctcss frequency
|
||||||
|
max(2048, ch[channel].dsp_size), // # coefs for de-emphasis filter
|
||||||
|
0, // min phase flag for de-emphasis filter
|
||||||
|
max(2048, ch[channel].dsp_size), // # coefs for audio cutoff filter
|
||||||
|
0); // min phase flag for audio cutoff filter
|
||||||
|
|
||||||
|
// FM squelch
|
||||||
|
rxa[channel].fmsq.p = create_fmsq (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // pointer to input signal buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output signal buffer
|
||||||
|
rxa[channel].fmd.p->audio, // pointer to trigger buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
5000.0, // cutoff freq for noise filter (Hz)
|
||||||
|
&rxa[channel].fmd.p->pllpole, // pointer to pole frequency of the fmd pll (Hz)
|
||||||
|
0.100, // delay time after channel flush
|
||||||
|
0.001, // tau for noise averaging
|
||||||
|
0.100, // tau for long noise averaging
|
||||||
|
0.050, // signal up transition time
|
||||||
|
0.010, // signal down transition time
|
||||||
|
0.750, // noise level to initiate tail
|
||||||
|
0.562, // noise level to initiate unmute
|
||||||
|
0.000, // minimum tail time
|
||||||
|
1.200, // maximum tail time
|
||||||
|
max(2048, ch[channel].dsp_size), // number of coefficients for noise filter
|
||||||
|
0); // minimum phase flag
|
||||||
|
|
||||||
|
// snba
|
||||||
|
rxa[channel].snba.p = create_snba (
|
||||||
|
0, // run
|
||||||
|
rxa[channel].midbuff, // input buffer
|
||||||
|
rxa[channel].midbuff, // output buffer
|
||||||
|
ch[channel].dsp_rate, // input / output sample rate
|
||||||
|
12000, // internal processing sample rate
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
4, // overlap factor to use
|
||||||
|
256, // frame size to use; sized for 12K rate
|
||||||
|
64, // asize
|
||||||
|
2, // npasses
|
||||||
|
8.0, // k1
|
||||||
|
20.0, // k2
|
||||||
|
10, // b
|
||||||
|
2, // pre
|
||||||
|
2, // post
|
||||||
|
0.5, // pmultmin
|
||||||
|
200.0, // output resampler low cutoff
|
||||||
|
5400.0); // output resampler high cutoff
|
||||||
|
|
||||||
|
// EQ
|
||||||
|
{
|
||||||
|
double default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0};
|
||||||
|
//double default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0};
|
||||||
|
double default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||||
|
rxa[channel].eqp.p = create_eqp (
|
||||||
|
0, // run - OFF by default
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of filter coefficients
|
||||||
|
0, // minimum phase flag
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
10, // number of frequencies
|
||||||
|
default_F, // frequency vector
|
||||||
|
default_G, // gain vector
|
||||||
|
0, // cutoff mode
|
||||||
|
0, // wintype
|
||||||
|
ch[channel].dsp_rate); // sample rate
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANF
|
||||||
|
rxa[channel].anf.p = create_anf (
|
||||||
|
0, // run - OFF by default
|
||||||
|
0, // position
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
ANF_DLINE_SIZE, // dline_size
|
||||||
|
64, // taps
|
||||||
|
16, // delay
|
||||||
|
0.0001, // two_mu
|
||||||
|
0.1, // gamma
|
||||||
|
1.0, // lidx
|
||||||
|
0.0, // lidx_min
|
||||||
|
200.0, // lidx_max
|
||||||
|
6.25e-12, // ngamma
|
||||||
|
6.25e-10, // den_mult
|
||||||
|
1.0, // lincr
|
||||||
|
3.0); // ldecr
|
||||||
|
|
||||||
|
// ANR
|
||||||
|
rxa[channel].anr.p = create_anr (
|
||||||
|
0, // run - OFF by default
|
||||||
|
0, // position
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
ANR_DLINE_SIZE, // dline_size
|
||||||
|
64, // taps
|
||||||
|
16, // delay
|
||||||
|
0.0001, // two_mu
|
||||||
|
0.1, // gamma
|
||||||
|
120.0, // lidx
|
||||||
|
120.0, // lidx_min
|
||||||
|
200.0, // lidx_max
|
||||||
|
0.001, // ngamma
|
||||||
|
6.25e-10, // den_mult
|
||||||
|
1.0, // lincr
|
||||||
|
3.0); // ldecr
|
||||||
|
|
||||||
|
|
||||||
|
// EMNR
|
||||||
|
rxa[channel].emnr.p = create_emnr (
|
||||||
|
0, // run
|
||||||
|
0, // position
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // input buffer
|
||||||
|
rxa[channel].midbuff, // output buffer
|
||||||
|
4096, // FFT size
|
||||||
|
4, // overlap
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0, // window type
|
||||||
|
1.0, // gain
|
||||||
|
2, // gain method
|
||||||
|
0, // npe_method
|
||||||
|
1); // ae_run
|
||||||
|
|
||||||
|
// AGC
|
||||||
|
rxa[channel].agc.p = create_wcpagc (
|
||||||
|
1, // run
|
||||||
|
3, // mode
|
||||||
|
1, // peakmode = envelope
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0.001, // tau_attack
|
||||||
|
0.250, // tau_decay
|
||||||
|
4, // n_tau
|
||||||
|
10000.0, // max_gain
|
||||||
|
1.5, // var_gain
|
||||||
|
1000.0, // fixed_gain
|
||||||
|
1.0, // max_input
|
||||||
|
1.0, // out_target
|
||||||
|
0.250, // tau_fast_backaverage
|
||||||
|
0.005, // tau_fast_decay
|
||||||
|
5.0, // pop_ratio
|
||||||
|
1, // hang_enable
|
||||||
|
0.500, // tau_hang_backmult
|
||||||
|
0.250, // hangtime
|
||||||
|
0.250, // hang_thresh
|
||||||
|
0.100); // tau_hang_decay
|
||||||
|
|
||||||
|
// agc gain meter
|
||||||
|
rxa[channel].agcmeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
0, // optional pointer to another 'run'
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
rxa[channel].midbuff, // pointer to buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
rxa[channel].meter, // result vector
|
||||||
|
rxa[channel].pmtupdate, // locks for meter access
|
||||||
|
RXA_AGC_AV, // index for average value
|
||||||
|
RXA_AGC_PK, // index for peak value
|
||||||
|
RXA_AGC_GAIN, // index for gain value
|
||||||
|
&rxa[channel].agc.p->gain); // pointer for gain computation
|
||||||
|
|
||||||
|
// bandpass filter
|
||||||
|
rxa[channel].bp1.p = create_bandpass (
|
||||||
|
1, // run - used only with ( AM || ANF || ANR || EMNR)
|
||||||
|
0, // position
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of coefficients
|
||||||
|
0, // flag for minimum phase
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
-4150.0, // lower filter frequency
|
||||||
|
-150.0, // upper filter frequency
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
1, // wintype
|
||||||
|
1.0); // gain
|
||||||
|
|
||||||
|
// pull phase & scope display data
|
||||||
|
rxa[channel].sip1.p = create_siphon (
|
||||||
|
1, // run - needed only for phase display
|
||||||
|
0, // position
|
||||||
|
0, // mode
|
||||||
|
0, // disp
|
||||||
|
ch[channel].dsp_size, // size of input buffer
|
||||||
|
rxa[channel].midbuff, // input buffer
|
||||||
|
4096, // number of samples to store
|
||||||
|
4096, // fft size for spectrum
|
||||||
|
0); // specmode
|
||||||
|
|
||||||
|
// carrier block
|
||||||
|
rxa[channel].cbl.p = create_cbl (
|
||||||
|
0, // run - needed only if set to ON
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
0, // mode
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0.02); // tau
|
||||||
|
|
||||||
|
// peaking filter
|
||||||
|
rxa[channel].speak.p = create_speak (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // buffer size,
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
600.0, // center frequency
|
||||||
|
100.0, // bandwidth
|
||||||
|
2.0, // gain
|
||||||
|
4, // number of stages
|
||||||
|
1); // design
|
||||||
|
|
||||||
|
// multiple peak filter
|
||||||
|
{
|
||||||
|
int def_enable[2] = {1, 1};
|
||||||
|
double def_freq[2] = {2125.0, 2295.0};
|
||||||
|
double def_bw[2] = {75.0, 75.0};
|
||||||
|
double def_gain[2] = {1.0, 1.0};
|
||||||
|
rxa[channel].mpeak.p = create_mpeak (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
2, // number of peaking filters
|
||||||
|
def_enable, // enable vector
|
||||||
|
def_freq, // frequency vector
|
||||||
|
def_bw, // bandwidth vector
|
||||||
|
def_gain, // gain vector
|
||||||
|
4 ); // number of stages
|
||||||
|
}
|
||||||
|
|
||||||
|
// syllabic squelch
|
||||||
|
rxa[channel].ssql.p = create_ssql(
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0.070, // signal up transition time
|
||||||
|
0.070, // signal down transition time
|
||||||
|
0.0, // muted gain
|
||||||
|
0.1, // mute time-constant
|
||||||
|
0.1, // unmute time-constant
|
||||||
|
0.08, // window threshold
|
||||||
|
0.8197, // trigger threshold
|
||||||
|
2400, // ring size for f_to_v converter
|
||||||
|
2000.0); // max freq for f_to_v converter
|
||||||
|
|
||||||
|
// patchpanel
|
||||||
|
rxa[channel].panel.p = create_panel (
|
||||||
|
channel, // channel number
|
||||||
|
1, // run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
4.0, // gain1
|
||||||
|
1.0, // gain2I
|
||||||
|
1.0, // gain2Q
|
||||||
|
3, // 3 for I and Q
|
||||||
|
0); // no copy
|
||||||
|
|
||||||
|
// resample
|
||||||
|
rxa[channel].rsmpout.p = create_resample (
|
||||||
|
0, // run - will be turned ON below if needed
|
||||||
|
ch[channel].dsp_size, // input buffer size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].outbuff, // pointer to output buffer
|
||||||
|
ch[channel].dsp_rate, // input sample rate
|
||||||
|
ch[channel].out_rate, // output sample rate
|
||||||
|
0.0, // select cutoff automatically
|
||||||
|
0, // select ncoef automatically
|
||||||
|
1.0); // gain
|
||||||
|
|
||||||
|
// turn OFF / ON resamplers as needed
|
||||||
|
RXAResCheck (channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_rxa (int channel)
|
||||||
|
{
|
||||||
|
destroy_resample (rxa[channel].rsmpout.p);
|
||||||
|
destroy_panel (rxa[channel].panel.p);
|
||||||
|
destroy_ssql (rxa[channel].ssql.p);
|
||||||
|
destroy_mpeak (rxa[channel].mpeak.p);
|
||||||
|
destroy_speak (rxa[channel].speak.p);
|
||||||
|
destroy_cbl (rxa[channel].cbl.p);
|
||||||
|
destroy_siphon (rxa[channel].sip1.p);
|
||||||
|
destroy_bandpass (rxa[channel].bp1.p);
|
||||||
|
destroy_meter (rxa[channel].agcmeter.p);
|
||||||
|
destroy_wcpagc (rxa[channel].agc.p);
|
||||||
|
destroy_emnr (rxa[channel].emnr.p);
|
||||||
|
destroy_anr (rxa[channel].anr.p);
|
||||||
|
destroy_anf (rxa[channel].anf.p);
|
||||||
|
destroy_eqp (rxa[channel].eqp.p);
|
||||||
|
destroy_snba (rxa[channel].snba.p);
|
||||||
|
destroy_fmsq (rxa[channel].fmsq.p);
|
||||||
|
destroy_fmd (rxa[channel].fmd.p);
|
||||||
|
destroy_amd (rxa[channel].amd.p);
|
||||||
|
destroy_amsq (rxa[channel].amsq.p);
|
||||||
|
destroy_meter (rxa[channel].smeter.p);
|
||||||
|
destroy_sender (rxa[channel].sender.p);
|
||||||
|
destroy_bpsnba (rxa[channel].bpsnba.p);
|
||||||
|
destroy_nbp (rxa[channel].nbp0.p);
|
||||||
|
destroy_notchdb (rxa[channel].ndb.p);
|
||||||
|
destroy_meter (rxa[channel].adcmeter.p);
|
||||||
|
destroy_gen (rxa[channel].gen0.p);
|
||||||
|
destroy_resample (rxa[channel].rsmpin.p);
|
||||||
|
destroy_shift (rxa[channel].shift.p);
|
||||||
|
_aligned_free (rxa[channel].midbuff);
|
||||||
|
_aligned_free (rxa[channel].outbuff);
|
||||||
|
_aligned_free (rxa[channel].inbuff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_rxa (int channel)
|
||||||
|
{
|
||||||
|
memset (rxa[channel].inbuff, 0, 1 * ch[channel].dsp_insize * sizeof (complex));
|
||||||
|
memset (rxa[channel].outbuff, 0, 1 * ch[channel].dsp_outsize * sizeof (complex));
|
||||||
|
memset (rxa[channel].midbuff, 0, 2 * ch[channel].dsp_size * sizeof (complex));
|
||||||
|
flush_shift (rxa[channel].shift.p);
|
||||||
|
flush_resample (rxa[channel].rsmpin.p);
|
||||||
|
flush_gen (rxa[channel].gen0.p);
|
||||||
|
flush_meter (rxa[channel].adcmeter.p);
|
||||||
|
flush_nbp (rxa[channel].nbp0.p);
|
||||||
|
flush_bpsnba (rxa[channel].bpsnba.p);
|
||||||
|
flush_sender (rxa[channel].sender.p);
|
||||||
|
flush_meter (rxa[channel].smeter.p);
|
||||||
|
flush_amsq (rxa[channel].amsq.p);
|
||||||
|
flush_amd (rxa[channel].amd.p);
|
||||||
|
flush_fmd (rxa[channel].fmd.p);
|
||||||
|
flush_fmsq (rxa[channel].fmsq.p);
|
||||||
|
flush_snba (rxa[channel].snba.p);
|
||||||
|
flush_eqp (rxa[channel].eqp.p);
|
||||||
|
flush_anf (rxa[channel].anf.p);
|
||||||
|
flush_anr (rxa[channel].anr.p);
|
||||||
|
flush_emnr (rxa[channel].emnr.p);
|
||||||
|
flush_wcpagc (rxa[channel].agc.p);
|
||||||
|
flush_meter (rxa[channel].agcmeter.p);
|
||||||
|
flush_bandpass (rxa[channel].bp1.p);
|
||||||
|
flush_siphon (rxa[channel].sip1.p);
|
||||||
|
flush_cbl (rxa[channel].cbl.p);
|
||||||
|
flush_speak (rxa[channel].speak.p);
|
||||||
|
flush_mpeak (rxa[channel].mpeak.p);
|
||||||
|
flush_ssql (rxa[channel].ssql.p);
|
||||||
|
flush_panel (rxa[channel].panel.p);
|
||||||
|
flush_resample (rxa[channel].rsmpout.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xrxa (int channel)
|
||||||
|
{
|
||||||
|
xshift (rxa[channel].shift.p);
|
||||||
|
xresample (rxa[channel].rsmpin.p);
|
||||||
|
xgen (rxa[channel].gen0.p);
|
||||||
|
xmeter (rxa[channel].adcmeter.p);
|
||||||
|
xbpsnbain (rxa[channel].bpsnba.p, 0);
|
||||||
|
xnbp (rxa[channel].nbp0.p, 0);
|
||||||
|
xmeter (rxa[channel].smeter.p);
|
||||||
|
xsender (rxa[channel].sender.p);
|
||||||
|
xamsqcap (rxa[channel].amsq.p);
|
||||||
|
xbpsnbaout (rxa[channel].bpsnba.p, 0);
|
||||||
|
xamd (rxa[channel].amd.p);
|
||||||
|
xfmd (rxa[channel].fmd.p);
|
||||||
|
xfmsq (rxa[channel].fmsq.p);
|
||||||
|
xbpsnbain (rxa[channel].bpsnba.p, 1);
|
||||||
|
xbpsnbaout (rxa[channel].bpsnba.p, 1);
|
||||||
|
xsnba (rxa[channel].snba.p);
|
||||||
|
xeqp (rxa[channel].eqp.p);
|
||||||
|
xanf (rxa[channel].anf.p, 0);
|
||||||
|
xanr (rxa[channel].anr.p, 0);
|
||||||
|
xemnr (rxa[channel].emnr.p, 0);
|
||||||
|
xbandpass (rxa[channel].bp1.p, 0);
|
||||||
|
xwcpagc (rxa[channel].agc.p);
|
||||||
|
xanf (rxa[channel].anf.p, 1);
|
||||||
|
xanr (rxa[channel].anr.p, 1);
|
||||||
|
xemnr (rxa[channel].emnr.p, 1);
|
||||||
|
xbandpass (rxa[channel].bp1.p, 1);
|
||||||
|
xmeter (rxa[channel].agcmeter.p);
|
||||||
|
xsiphon (rxa[channel].sip1.p, 0);
|
||||||
|
xcbl (rxa[channel].cbl.p);
|
||||||
|
xspeak (rxa[channel].speak.p);
|
||||||
|
xmpeak (rxa[channel].mpeak.p);
|
||||||
|
xssql (rxa[channel].ssql.p);
|
||||||
|
xpanel (rxa[channel].panel.p);
|
||||||
|
xamsq (rxa[channel].amsq.p);
|
||||||
|
xresample (rxa[channel].rsmpout.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputSamplerate_rxa (int channel)
|
||||||
|
{
|
||||||
|
// buffers
|
||||||
|
_aligned_free (rxa[channel].inbuff);
|
||||||
|
rxa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
|
||||||
|
// shift
|
||||||
|
setBuffers_shift (rxa[channel].shift.p, rxa[channel].inbuff, rxa[channel].inbuff);
|
||||||
|
setSize_shift (rxa[channel].shift.p, ch[channel].dsp_insize);
|
||||||
|
setSamplerate_shift (rxa[channel].shift.p, ch[channel].in_rate);
|
||||||
|
// input resampler
|
||||||
|
setBuffers_resample (rxa[channel].rsmpin.p, rxa[channel].inbuff, rxa[channel].midbuff);
|
||||||
|
setSize_resample (rxa[channel].rsmpin.p, ch[channel].dsp_insize);
|
||||||
|
setInRate_resample (rxa[channel].rsmpin.p, ch[channel].in_rate);
|
||||||
|
RXAResCheck (channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOutputSamplerate_rxa (int channel)
|
||||||
|
{
|
||||||
|
// buffers
|
||||||
|
_aligned_free (rxa[channel].outbuff);
|
||||||
|
rxa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
|
||||||
|
// output resampler
|
||||||
|
setBuffers_resample (rxa[channel].rsmpout.p, rxa[channel].midbuff, rxa[channel].outbuff);
|
||||||
|
setOutRate_resample (rxa[channel].rsmpout.p, ch[channel].out_rate);
|
||||||
|
RXAResCheck (channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDSPSamplerate_rxa (int channel)
|
||||||
|
{
|
||||||
|
// buffers
|
||||||
|
_aligned_free (rxa[channel].inbuff);
|
||||||
|
rxa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
|
||||||
|
_aligned_free (rxa[channel].outbuff);
|
||||||
|
rxa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
|
||||||
|
// shift
|
||||||
|
setBuffers_shift (rxa[channel].shift.p, rxa[channel].inbuff, rxa[channel].inbuff);
|
||||||
|
setSize_shift (rxa[channel].shift.p, ch[channel].dsp_insize);
|
||||||
|
// input resampler
|
||||||
|
setBuffers_resample (rxa[channel].rsmpin.p, rxa[channel].inbuff, rxa[channel].midbuff);
|
||||||
|
setSize_resample (rxa[channel].rsmpin.p, ch[channel].dsp_insize);
|
||||||
|
setOutRate_resample (rxa[channel].rsmpin.p, ch[channel].dsp_rate);
|
||||||
|
// dsp_rate blocks
|
||||||
|
setSamplerate_gen (rxa[channel].gen0.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_meter (rxa[channel].adcmeter.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_nbp (rxa[channel].nbp0.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_bpsnba (rxa[channel].bpsnba.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_meter (rxa[channel].smeter.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_sender (rxa[channel].sender.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_amsq (rxa[channel].amsq.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_amd (rxa[channel].amd.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_fmd (rxa[channel].fmd.p, ch[channel].dsp_rate);
|
||||||
|
setBuffers_fmsq (rxa[channel].fmsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].fmd.p->audio);
|
||||||
|
setSamplerate_fmsq (rxa[channel].fmsq.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_snba (rxa[channel].snba.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_eqp (rxa[channel].eqp.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_anf (rxa[channel].anf.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_anr (rxa[channel].anr.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_emnr (rxa[channel].emnr.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_bandpass (rxa[channel].bp1.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_wcpagc (rxa[channel].agc.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_meter (rxa[channel].agcmeter.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_siphon (rxa[channel].sip1.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_cbl (rxa[channel].cbl.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_speak (rxa[channel].speak.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_mpeak (rxa[channel].mpeak.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_ssql (rxa[channel].ssql.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_panel (rxa[channel].panel.p, ch[channel].dsp_rate);
|
||||||
|
// output resampler
|
||||||
|
setBuffers_resample (rxa[channel].rsmpout.p, rxa[channel].midbuff, rxa[channel].outbuff);
|
||||||
|
setInRate_resample (rxa[channel].rsmpout.p, ch[channel].dsp_rate);
|
||||||
|
RXAResCheck (channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDSPBuffsize_rxa (int channel)
|
||||||
|
{
|
||||||
|
// buffers
|
||||||
|
_aligned_free(rxa[channel].inbuff);
|
||||||
|
rxa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
|
||||||
|
_aligned_free (rxa[channel].midbuff);
|
||||||
|
rxa[channel].midbuff = (double *)malloc0(2 * ch[channel].dsp_size * sizeof(complex));
|
||||||
|
_aligned_free (rxa[channel].outbuff);
|
||||||
|
rxa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
|
||||||
|
// shift
|
||||||
|
setBuffers_shift (rxa[channel].shift.p, rxa[channel].inbuff, rxa[channel].inbuff);
|
||||||
|
setSize_shift (rxa[channel].shift.p, ch[channel].dsp_insize);
|
||||||
|
// input resampler
|
||||||
|
setBuffers_resample (rxa[channel].rsmpin.p, rxa[channel].inbuff, rxa[channel].midbuff);
|
||||||
|
setSize_resample (rxa[channel].rsmpin.p, ch[channel].dsp_insize);
|
||||||
|
// dsp_size blocks
|
||||||
|
setBuffers_gen (rxa[channel].gen0.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_gen (rxa[channel].gen0.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_meter (rxa[channel].adcmeter.p, rxa[channel].midbuff);
|
||||||
|
setSize_meter (rxa[channel].adcmeter.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_nbp (rxa[channel].nbp0.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_nbp (rxa[channel].nbp0.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_bpsnba (rxa[channel].bpsnba.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_bpsnba (rxa[channel].bpsnba.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_meter (rxa[channel].smeter.p, rxa[channel].midbuff);
|
||||||
|
setSize_meter (rxa[channel].smeter.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_sender (rxa[channel].sender.p, rxa[channel].midbuff);
|
||||||
|
setSize_sender (rxa[channel].sender.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_amsq (rxa[channel].amsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_amsq (rxa[channel].amsq.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_amd (rxa[channel].amd.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_amd (rxa[channel].amd.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_fmd (rxa[channel].fmd.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_fmd (rxa[channel].fmd.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_fmsq (rxa[channel].fmsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].fmd.p->audio);
|
||||||
|
setSize_fmsq (rxa[channel].fmsq.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_snba (rxa[channel].snba.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_snba (rxa[channel].snba.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_eqp (rxa[channel].eqp.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_eqp (rxa[channel].eqp.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_anf (rxa[channel].anf.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_anf (rxa[channel].anf.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_anr (rxa[channel].anr.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_anr (rxa[channel].anr.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_emnr (rxa[channel].emnr.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_emnr (rxa[channel].emnr.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_bandpass (rxa[channel].bp1.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_bandpass (rxa[channel].bp1.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_wcpagc (rxa[channel].agc.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_wcpagc (rxa[channel].agc.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_meter (rxa[channel].agcmeter.p, rxa[channel].midbuff);
|
||||||
|
setSize_meter (rxa[channel].agcmeter.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_siphon (rxa[channel].sip1.p, rxa[channel].midbuff);
|
||||||
|
setSize_siphon (rxa[channel].sip1.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_cbl (rxa[channel].cbl.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_cbl (rxa[channel].cbl.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_speak (rxa[channel].speak.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_speak (rxa[channel].speak.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_mpeak (rxa[channel].mpeak.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_mpeak (rxa[channel].mpeak.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_ssql (rxa[channel].ssql.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_ssql (rxa[channel].ssql.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_panel (rxa[channel].panel.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_panel (rxa[channel].panel.p, ch[channel].dsp_size);
|
||||||
|
// output resampler
|
||||||
|
setBuffers_resample (rxa[channel].rsmpout.p, rxa[channel].midbuff, rxa[channel].outbuff);
|
||||||
|
setSize_resample (rxa[channel].rsmpout.p, ch[channel].dsp_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* RXA Mode & Filter Controls *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAMode (int channel, int mode)
|
||||||
|
{
|
||||||
|
if (rxa[channel].mode != mode)
|
||||||
|
{
|
||||||
|
int amd_run = (mode == RXA_AM) || (mode == RXA_SAM);
|
||||||
|
RXAbpsnbaCheck (channel, mode, rxa[channel].ndb.p->master_run);
|
||||||
|
RXAbp1Check (channel, amd_run, rxa[channel].snba.p->run, rxa[channel].emnr.p->run,
|
||||||
|
rxa[channel].anf.p->run, rxa[channel].anr.p->run);
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].mode = mode;
|
||||||
|
rxa[channel].amd.p->run = 0;
|
||||||
|
rxa[channel].fmd.p->run = 0;
|
||||||
|
rxa[channel].agc.p->run = 1;
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case RXA_AM:
|
||||||
|
rxa[channel].amd.p->run = 1;
|
||||||
|
rxa[channel].amd.p->mode = 0;
|
||||||
|
break;
|
||||||
|
case RXA_SAM:
|
||||||
|
rxa[channel].amd.p->run = 1;
|
||||||
|
rxa[channel].amd.p->mode = 1;
|
||||||
|
break;
|
||||||
|
case RXA_DSB:
|
||||||
|
|
||||||
|
break;
|
||||||
|
case RXA_FM:
|
||||||
|
rxa[channel].fmd.p->run = 1;
|
||||||
|
rxa[channel].agc.p->run = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
RXAbp1Set (channel);
|
||||||
|
RXAbpsnbaSet (channel); // update variables
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RXAResCheck (int channel)
|
||||||
|
{
|
||||||
|
// turn OFF/ON resamplers depending upon whether they're needed
|
||||||
|
RESAMPLE a = rxa[channel].rsmpin.p;
|
||||||
|
if (ch[channel].in_rate != ch[channel].dsp_rate) a->run = 1;
|
||||||
|
else a->run = 0;
|
||||||
|
a = rxa[channel].rsmpout.p;
|
||||||
|
if (ch[channel].dsp_rate != ch[channel].out_rate) a->run = 1;
|
||||||
|
else a->run = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RXAbp1Check (int channel, int amd_run, int snba_run,
|
||||||
|
int emnr_run, int anf_run, int anr_run)
|
||||||
|
{
|
||||||
|
BANDPASS a = rxa[channel].bp1.p;
|
||||||
|
double gain;
|
||||||
|
if (amd_run ||
|
||||||
|
snba_run ||
|
||||||
|
emnr_run ||
|
||||||
|
anf_run ||
|
||||||
|
anr_run) gain = 2.0;
|
||||||
|
else gain = 1.0;
|
||||||
|
if (a->gain != gain)
|
||||||
|
setGain_bandpass (a, gain, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RXAbp1Set (int channel)
|
||||||
|
{
|
||||||
|
BANDPASS a = rxa[channel].bp1.p;
|
||||||
|
int old = a->run;
|
||||||
|
if ((rxa[channel].amd.p->run == 1) ||
|
||||||
|
(rxa[channel].snba.p->run == 1) ||
|
||||||
|
(rxa[channel].emnr.p->run == 1) ||
|
||||||
|
(rxa[channel].anf.p->run == 1) ||
|
||||||
|
(rxa[channel].anr.p->run == 1)) a->run = 1;
|
||||||
|
else a->run = 0;
|
||||||
|
if (!old && a->run) flush_bandpass (a);
|
||||||
|
setUpdate_fircore (a->p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RXAbpsnbaCheck (int channel, int mode, int notch_run)
|
||||||
|
{
|
||||||
|
// for BPSNBA: set run, position, freqs, run_notches
|
||||||
|
// call this upon change in RXA_mode, snba_run, notch_master_run
|
||||||
|
BPSNBA a = rxa[channel].bpsnba.p;
|
||||||
|
double f_low = 0.0, f_high = 0.0;
|
||||||
|
int run_notches = 0;
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case RXA_LSB:
|
||||||
|
case RXA_CWL:
|
||||||
|
case RXA_DIGL:
|
||||||
|
f_low = -a->abs_high_freq;
|
||||||
|
f_high = -a->abs_low_freq;
|
||||||
|
run_notches = notch_run;
|
||||||
|
break;
|
||||||
|
case RXA_USB:
|
||||||
|
case RXA_CWU:
|
||||||
|
case RXA_DIGU:
|
||||||
|
f_low = +a->abs_low_freq;
|
||||||
|
f_high = +a->abs_high_freq;
|
||||||
|
run_notches = notch_run;
|
||||||
|
break;
|
||||||
|
case RXA_AM:
|
||||||
|
case RXA_SAM:
|
||||||
|
case RXA_DSB:
|
||||||
|
f_low = +a->abs_low_freq;
|
||||||
|
f_high = +a->abs_high_freq;
|
||||||
|
run_notches = 0;
|
||||||
|
break;
|
||||||
|
case RXA_FM:
|
||||||
|
f_low = +a->abs_low_freq;
|
||||||
|
f_high = +a->abs_high_freq;
|
||||||
|
run_notches = 0;
|
||||||
|
break;
|
||||||
|
case RXA_DRM:
|
||||||
|
case RXA_SPEC:
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 'run' and 'position' are examined at run time; no filter changes required.
|
||||||
|
// Recalculate filter if frequencies OR 'run_notches' changed.
|
||||||
|
if ((a->f_low != f_low ) ||
|
||||||
|
(a->f_high != f_high ) ||
|
||||||
|
(a->run_notches != run_notches))
|
||||||
|
{
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
a->run_notches = run_notches;
|
||||||
|
// f_low, f_high, run_notches are needed for the filter recalculation
|
||||||
|
recalc_bpsnba_filter (a, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RXAbpsnbaSet (int channel)
|
||||||
|
{
|
||||||
|
// for BPSNBA: set run, position, freqs, run_notches
|
||||||
|
// call this upon change in RXA_mode, snba_run, notch_master_run
|
||||||
|
BPSNBA a = rxa[channel].bpsnba.p;
|
||||||
|
switch (rxa[channel].mode)
|
||||||
|
{
|
||||||
|
case RXA_LSB:
|
||||||
|
case RXA_CWL:
|
||||||
|
case RXA_DIGL:
|
||||||
|
a->run = rxa[channel].snba.p->run;
|
||||||
|
a->position = 0;
|
||||||
|
break;
|
||||||
|
case RXA_USB:
|
||||||
|
case RXA_CWU:
|
||||||
|
case RXA_DIGU:
|
||||||
|
a->run = rxa[channel].snba.p->run;
|
||||||
|
a->position = 0;
|
||||||
|
break;
|
||||||
|
case RXA_AM:
|
||||||
|
case RXA_SAM:
|
||||||
|
case RXA_DSB:
|
||||||
|
a->run = rxa[channel].snba.p->run;
|
||||||
|
a->position = 1;
|
||||||
|
break;
|
||||||
|
case RXA_FM:
|
||||||
|
a->run = rxa[channel].snba.p->run;
|
||||||
|
a->position = 1;
|
||||||
|
break;
|
||||||
|
case RXA_DRM:
|
||||||
|
case RXA_SPEC:
|
||||||
|
a->run = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setUpdate_fircore (a->bpsnba->p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* Collectives *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void RXASetPassband (int channel, double f_low, double f_high)
|
||||||
|
{
|
||||||
|
SetRXABandpassFreqs (channel, f_low, f_high);
|
||||||
|
SetRXASNBAOutputBandwidth (channel, f_low, f_high);
|
||||||
|
RXANBPSetFreqs (channel, f_low, f_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void RXASetNC (int channel, int nc)
|
||||||
|
{
|
||||||
|
int oldstate = SetChannelState (channel, 0, 1);
|
||||||
|
RXANBPSetNC (channel, nc);
|
||||||
|
RXABPSNBASetNC (channel, nc);
|
||||||
|
SetRXABandpassNC (channel, nc);
|
||||||
|
SetRXAEQNC (channel, nc);
|
||||||
|
SetRXAFMSQNC (channel, nc);
|
||||||
|
SetRXAFMNCde (channel, nc);
|
||||||
|
SetRXAFMNCaud (channel, nc);
|
||||||
|
SetChannelState (channel, oldstate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void RXASetMP (int channel, int mp)
|
||||||
|
{
|
||||||
|
RXANBPSetMP (channel, mp);
|
||||||
|
RXABPSNBASetMP (channel, mp);
|
||||||
|
SetRXABandpassMP (channel, mp);
|
||||||
|
SetRXAEQMP (channel, mp);
|
||||||
|
SetRXAFMSQMP (channel, mp);
|
||||||
|
SetRXAFMMPde (channel, mp);
|
||||||
|
SetRXAFMMPaud (channel, mp);
|
||||||
|
}
|
||||||
+201
@@ -0,0 +1,201 @@
|
|||||||
|
/* RXA.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2014, 2015, 2016 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _rxa_h
|
||||||
|
#define _rxa_h
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
enum rxaMode
|
||||||
|
{
|
||||||
|
RXA_LSB,
|
||||||
|
RXA_USB,
|
||||||
|
RXA_DSB,
|
||||||
|
RXA_CWL,
|
||||||
|
RXA_CWU,
|
||||||
|
RXA_FM,
|
||||||
|
RXA_AM,
|
||||||
|
RXA_DIGU,
|
||||||
|
RXA_SPEC,
|
||||||
|
RXA_DIGL,
|
||||||
|
RXA_SAM,
|
||||||
|
RXA_DRM
|
||||||
|
};
|
||||||
|
|
||||||
|
enum rxaMeterType
|
||||||
|
{
|
||||||
|
RXA_S_PK,
|
||||||
|
RXA_S_AV,
|
||||||
|
RXA_ADC_PK,
|
||||||
|
RXA_ADC_AV,
|
||||||
|
RXA_AGC_GAIN,
|
||||||
|
RXA_AGC_PK,
|
||||||
|
RXA_AGC_AV,
|
||||||
|
RXA_METERTYPE_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _rxa
|
||||||
|
{
|
||||||
|
double* inbuff;
|
||||||
|
double* outbuff;
|
||||||
|
double* midbuff;
|
||||||
|
int mode;
|
||||||
|
double meter[RXA_METERTYPE_LAST];
|
||||||
|
CRITICAL_SECTION* pmtupdate[RXA_METERTYPE_LAST];
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
METER p;
|
||||||
|
} smeter, adcmeter, agcmeter;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
SHIFT p;
|
||||||
|
} shift;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
RESAMPLE p;
|
||||||
|
} rsmpin, rsmpout;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
GEN p;
|
||||||
|
} gen0;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
BANDPASS p;
|
||||||
|
} bp1;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
NOTCHDB p;
|
||||||
|
} ndb;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
NBP p;
|
||||||
|
} nbp0;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
BPSNBA p;
|
||||||
|
} bpsnba;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
SNBA p;
|
||||||
|
} snba;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
SENDER p;
|
||||||
|
} sender;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
AMSQ p;
|
||||||
|
} amsq;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
AMD p;
|
||||||
|
} amd;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
FMD p;
|
||||||
|
} fmd;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
FMSQ p;
|
||||||
|
} fmsq;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
EQP p;
|
||||||
|
} eqp;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
ANF p;
|
||||||
|
} anf;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
ANR p;
|
||||||
|
} anr;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
EMNR p;
|
||||||
|
} emnr;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
WCPAGC p;
|
||||||
|
} agc;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
SPEAK p;
|
||||||
|
} speak;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
MPEAK p;
|
||||||
|
} mpeak;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
PANEL p;
|
||||||
|
} panel;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
SIPHON p;
|
||||||
|
} sip1;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
CBL p;
|
||||||
|
} cbl;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
SSQL p;
|
||||||
|
} ssql;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct _rxa rxa[];
|
||||||
|
|
||||||
|
extern void create_rxa (int channel);
|
||||||
|
|
||||||
|
extern void destroy_rxa (int channel);
|
||||||
|
|
||||||
|
extern void flush_rxa (int channel);
|
||||||
|
|
||||||
|
extern void xrxa (int channel);
|
||||||
|
|
||||||
|
extern void setInputSamplerate_rxa (int channel);
|
||||||
|
|
||||||
|
extern void setOutputSamplerate_rxa (int channel);
|
||||||
|
|
||||||
|
extern void setDSPSamplerate_rxa (int channel);
|
||||||
|
|
||||||
|
extern void setDSPBuffsize_rxa (int channel);
|
||||||
|
|
||||||
|
// RXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAMode (int channel, int mode);
|
||||||
|
|
||||||
|
extern void RXAResCheck (int channel);
|
||||||
|
|
||||||
|
extern void RXAbp1Check (int channel, int amd_run, int snba_run, int emnr_run, int anf_run, int anr_run);
|
||||||
|
|
||||||
|
extern void RXAbp1Set (int channel);
|
||||||
|
|
||||||
|
extern void RXAbpsnbaCheck (int channel, int mode, int notch_run);
|
||||||
|
|
||||||
|
extern void RXAbpsnbaSet (int channel);
|
||||||
|
|
||||||
|
#endif
|
||||||
+935
@@ -0,0 +1,935 @@
|
|||||||
|
/* TXA.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2014, 2016, 2017, 2021, 2023 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
struct _txa txa[MAX_CHANNELS];
|
||||||
|
|
||||||
|
void create_txa (int channel)
|
||||||
|
{
|
||||||
|
txa[channel].mode = TXA_LSB;
|
||||||
|
txa[channel].f_low = -5000.0;
|
||||||
|
txa[channel].f_high = - 100.0;
|
||||||
|
txa[channel].inbuff = (double *) malloc0 (1 * ch[channel].dsp_insize * sizeof (complex));
|
||||||
|
txa[channel].outbuff = (double *) malloc0 (1 * ch[channel].dsp_outsize * sizeof (complex));
|
||||||
|
txa[channel].midbuff = (double *) malloc0 (2 * ch[channel].dsp_size * sizeof (complex));
|
||||||
|
|
||||||
|
txa[channel].rsmpin.p = create_resample (
|
||||||
|
0, // run - will be turned on below if needed
|
||||||
|
ch[channel].dsp_insize, // input buffer size
|
||||||
|
txa[channel].inbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to output buffer
|
||||||
|
ch[channel].in_rate, // input sample rate
|
||||||
|
ch[channel].dsp_rate, // output sample rate
|
||||||
|
0.0, // select cutoff automatically
|
||||||
|
0, // select ncoef automatically
|
||||||
|
1.0); // gain
|
||||||
|
|
||||||
|
txa[channel].gen0.p = create_gen (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
2); // mode
|
||||||
|
|
||||||
|
txa[channel].panel.p = create_panel (
|
||||||
|
channel, // channel number
|
||||||
|
1, // run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to output buffer
|
||||||
|
1.0, // gain1
|
||||||
|
1.0, // gain2I
|
||||||
|
1.0, // gain2Q
|
||||||
|
2, // 1 to use Q, 2 to use I for input
|
||||||
|
0); // 0, no copy
|
||||||
|
|
||||||
|
txa[channel].phrot.p = create_phrot (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
338.0, // 1/2 of phase frequency
|
||||||
|
8); // number of stages
|
||||||
|
|
||||||
|
txa[channel].micmeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
0, // optional pointer to another 'run'
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
txa[channel].meter, // result vector
|
||||||
|
txa[channel].pmtupdate, // locks for meter access
|
||||||
|
TXA_MIC_AV, // index for average value
|
||||||
|
TXA_MIC_PK, // index for peak value
|
||||||
|
-1, // index for gain value
|
||||||
|
0); // pointer for gain computation
|
||||||
|
|
||||||
|
txa[channel].amsq.p = create_amsq (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer
|
||||||
|
txa[channel].midbuff, // trigger buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0.010, // time constant for averaging signal
|
||||||
|
0.004, // up-slew time
|
||||||
|
0.004, // down-slew time
|
||||||
|
0.180, // signal level to initiate tail
|
||||||
|
0.200, // signal level to initiate unmute
|
||||||
|
0.000, // minimum tail length
|
||||||
|
0.025, // maximum tail length
|
||||||
|
0.200); // muted gain
|
||||||
|
|
||||||
|
{
|
||||||
|
double default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0};
|
||||||
|
double default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0};
|
||||||
|
//double default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||||
|
txa[channel].eqp.p = create_eqp (
|
||||||
|
0, // run - OFF by default
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of filter coefficients
|
||||||
|
0, // minimum phase flag
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to output buffer
|
||||||
|
10, // nfreqs
|
||||||
|
default_F, // vector of frequencies
|
||||||
|
default_G, // vector of gain values
|
||||||
|
0, // cutoff mode
|
||||||
|
0, // wintype
|
||||||
|
ch[channel].dsp_rate); // samplerate
|
||||||
|
}
|
||||||
|
|
||||||
|
txa[channel].eqmeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
&(txa[channel].eqp.p->run), // pointer to eqp 'run'
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
txa[channel].meter, // result vector
|
||||||
|
txa[channel].pmtupdate, // locks for meter access
|
||||||
|
TXA_EQ_AV, // index for average value
|
||||||
|
TXA_EQ_PK, // index for peak value
|
||||||
|
-1, // index for gain value
|
||||||
|
0); // pointer for gain computation
|
||||||
|
|
||||||
|
txa[channel].preemph.p = create_emphp (
|
||||||
|
0, // run
|
||||||
|
1, // position
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of filter coefficients
|
||||||
|
0, // minimum phase flag
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer,
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0, // pre-emphasis type
|
||||||
|
300.0, // f_low
|
||||||
|
3000.0); // f_high
|
||||||
|
|
||||||
|
txa[channel].leveler.p = create_wcpagc (
|
||||||
|
0, // run - OFF by default
|
||||||
|
5, // mode
|
||||||
|
0, // 0 for max(I,Q), 1 for envelope
|
||||||
|
txa[channel].midbuff, // input buff pointer
|
||||||
|
txa[channel].midbuff, // output buff pointer
|
||||||
|
ch[channel].dsp_size, // io_buffsize
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0.001, // tau_attack
|
||||||
|
0.500, // tau_decay
|
||||||
|
6, // n_tau
|
||||||
|
1.778, // max_gain
|
||||||
|
1.0, // var_gain
|
||||||
|
1.0, // fixed_gain
|
||||||
|
1.0, // max_input
|
||||||
|
1.05, // out_targ
|
||||||
|
0.250, // tau_fast_backaverage
|
||||||
|
0.005, // tau_fast_decay
|
||||||
|
5.0, // pop_ratio
|
||||||
|
0, // hang_enable
|
||||||
|
0.500, // tau_hang_backmult
|
||||||
|
0.500, // hangtime
|
||||||
|
2.000, // hang_thresh
|
||||||
|
0.100); // tau_hang_decay
|
||||||
|
|
||||||
|
txa[channel].lvlrmeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
&(txa[channel].leveler.p->run), // pointer to leveler 'run'
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
txa[channel].meter, // result vector
|
||||||
|
txa[channel].pmtupdate, // locks for meter access
|
||||||
|
TXA_LVLR_AV, // index for average value
|
||||||
|
TXA_LVLR_PK, // index for peak value
|
||||||
|
TXA_LVLR_GAIN, // index for gain value
|
||||||
|
&txa[channel].leveler.p->gain); // pointer for gain computation
|
||||||
|
|
||||||
|
{
|
||||||
|
double default_F[5] = {200.0, 1000.0, 2000.0, 3000.0, 4000.0};
|
||||||
|
double default_G[5] = {0.0, 5.0, 10.0, 10.0, 5.0};
|
||||||
|
double default_E[5] = {7.0, 7.0, 7.0, 7.0, 7.0};
|
||||||
|
txa[channel].cfcomp.p = create_cfcomp(
|
||||||
|
0, // run
|
||||||
|
0, // position
|
||||||
|
0, // post-equalizer run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer
|
||||||
|
2048, // fft size
|
||||||
|
4, // overlap
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
1, // window type
|
||||||
|
0, // compression method
|
||||||
|
5, // nfreqs
|
||||||
|
0.0, // pre-compression
|
||||||
|
0.0, // pre-postequalization
|
||||||
|
default_F, // frequency array
|
||||||
|
default_G, // compression array
|
||||||
|
default_E, // eq array
|
||||||
|
0.25, // metering time constant
|
||||||
|
0.50); // display time constant
|
||||||
|
}
|
||||||
|
|
||||||
|
txa[channel].cfcmeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
&(txa[channel].cfcomp.p->run), // pointer to eqp 'run'
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
txa[channel].meter, // result vector
|
||||||
|
txa[channel].pmtupdate, // locks for meter access
|
||||||
|
TXA_CFC_AV, // index for average value
|
||||||
|
TXA_CFC_PK, // index for peak value
|
||||||
|
TXA_CFC_GAIN, // index for gain value
|
||||||
|
&txa[channel].cfcomp.p->gain); // pointer for gain computation
|
||||||
|
|
||||||
|
txa[channel].bp0.p = create_bandpass (
|
||||||
|
1, // always runs
|
||||||
|
0, // position
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of coefficients
|
||||||
|
0, // flag for minimum phase
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to output buffer
|
||||||
|
txa[channel].f_low, // low freq cutoff
|
||||||
|
txa[channel].f_high, // high freq cutoff
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
1, // wintype
|
||||||
|
2.0); // gain
|
||||||
|
|
||||||
|
txa[channel].compressor.p = create_compressor (
|
||||||
|
0, // run - OFF by default
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to output buffer
|
||||||
|
3.0); // gain
|
||||||
|
|
||||||
|
txa[channel].bp1.p = create_bandpass (
|
||||||
|
0, // ONLY RUNS WHEN COMPRESSOR IS USED
|
||||||
|
0, // position
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of coefficients
|
||||||
|
0, // flag for minimum phase
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to output buffer
|
||||||
|
txa[channel].f_low, // low freq cutoff
|
||||||
|
txa[channel].f_high, // high freq cutoff
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
1, // wintype
|
||||||
|
2.0); // gain
|
||||||
|
|
||||||
|
txa[channel].osctrl.p = create_osctrl (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
1.95); // gain for clippings
|
||||||
|
|
||||||
|
txa[channel].bp2.p = create_bandpass (
|
||||||
|
0, // ONLY RUNS WHEN COMPRESSOR IS USED
|
||||||
|
0, // position
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of coefficients
|
||||||
|
0, // flag for minimum phase
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to output buffer
|
||||||
|
txa[channel].f_low, // low freq cutoff
|
||||||
|
txa[channel].f_high, // high freq cutoff
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
1, // wintype
|
||||||
|
1.0); // gain
|
||||||
|
|
||||||
|
txa[channel].compmeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
&(txa[channel].compressor.p->run), // pointer to compressor 'run'
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
txa[channel].meter, // result vector
|
||||||
|
txa[channel].pmtupdate, // locks for meter access
|
||||||
|
TXA_COMP_AV, // index for average value
|
||||||
|
TXA_COMP_PK, // index for peak value
|
||||||
|
-1, // index for gain value
|
||||||
|
0); // pointer for gain computation
|
||||||
|
|
||||||
|
txa[channel].alc.p = create_wcpagc (
|
||||||
|
1, // run - always ON
|
||||||
|
5, // mode
|
||||||
|
1, // 0 for max(I,Q), 1 for envelope
|
||||||
|
txa[channel].midbuff, // input buff pointer
|
||||||
|
txa[channel].midbuff, // output buff pointer
|
||||||
|
ch[channel].dsp_size, // io_buffsize
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0.001, // tau_attack
|
||||||
|
0.010, // tau_decay
|
||||||
|
6, // n_tau
|
||||||
|
1.0, // max_gain
|
||||||
|
1.0, // var_gain
|
||||||
|
1.0, // fixed_gain
|
||||||
|
1.0, // max_input
|
||||||
|
1.0, // out_targ
|
||||||
|
0.250, // tau_fast_backaverage
|
||||||
|
0.005, // tau_fast_decay
|
||||||
|
5.0, // pop_ratio
|
||||||
|
0, // hang_enable
|
||||||
|
0.500, // tau_hang_backmult
|
||||||
|
0.500, // hangtime
|
||||||
|
2.000, // hang_thresh
|
||||||
|
0.100); // tau_hang_decay
|
||||||
|
|
||||||
|
txa[channel].ammod.p = create_ammod (
|
||||||
|
0, // run - OFF by default
|
||||||
|
0, // mode: 0=>AM, 1=>DSB
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to output buffer
|
||||||
|
0.5); // carrier level
|
||||||
|
|
||||||
|
|
||||||
|
txa[channel].fmmod.p = create_fmmod (
|
||||||
|
0, // run - OFF by default
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
5000.0, // deviation
|
||||||
|
300.0, // low cutoff frequency
|
||||||
|
3000.0, // high cutoff frequency
|
||||||
|
1, // ctcss run control
|
||||||
|
0.10, // ctcss level
|
||||||
|
100.0, // ctcss frequency
|
||||||
|
1, // run bandpass filter
|
||||||
|
max(2048, ch[channel].dsp_size), // number coefficients for bandpass filter
|
||||||
|
0); // minimum phase flag
|
||||||
|
|
||||||
|
txa[channel].gen1.p = create_gen (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0); // mode
|
||||||
|
|
||||||
|
txa[channel].uslew.p = create_uslew (
|
||||||
|
channel, // channel
|
||||||
|
&ch[channel].iob.ch_upslew, // pointer to channel upslew flag
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
0.000, // delay time
|
||||||
|
0.005); // upslew time
|
||||||
|
|
||||||
|
txa[channel].alcmeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
0, // optional pointer to a 'run'
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
txa[channel].meter, // result vector
|
||||||
|
txa[channel].pmtupdate, // locks for meter access
|
||||||
|
TXA_ALC_AV, // index for average value
|
||||||
|
TXA_ALC_PK, // index for peak value
|
||||||
|
TXA_ALC_GAIN, // index for gain value
|
||||||
|
&txa[channel].alc.p->gain); // pointer for gain computation
|
||||||
|
|
||||||
|
txa[channel].sip1.p = create_siphon (
|
||||||
|
1, // run
|
||||||
|
0, // position
|
||||||
|
0, // mode
|
||||||
|
0, // disp
|
||||||
|
ch[channel].dsp_size, // input buffer size
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
16384, // number of samples to buffer
|
||||||
|
16384, // fft size for spectrum
|
||||||
|
1); // specmode
|
||||||
|
|
||||||
|
txa[channel].calcc.p = create_calcc (
|
||||||
|
channel, // channel number
|
||||||
|
1, // run calibration
|
||||||
|
1024, // input buffer size
|
||||||
|
ch[channel].in_rate, // samplerate
|
||||||
|
16, // ints
|
||||||
|
256, // spi
|
||||||
|
(1.0 / 0.4072), // hw_scale
|
||||||
|
0.1, // mox delay
|
||||||
|
0.0, // loop delay
|
||||||
|
0.8, // ptol
|
||||||
|
0, // mox
|
||||||
|
0, // solidmox
|
||||||
|
1, // pin mode
|
||||||
|
1, // map mode
|
||||||
|
0, // stbl mode
|
||||||
|
256, // pin samples
|
||||||
|
0.9); // alpha
|
||||||
|
|
||||||
|
txa[channel].iqc.p0 = txa[channel].iqc.p1 = create_iqc (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer
|
||||||
|
(double)ch[channel].dsp_rate, // sample rate
|
||||||
|
16, // ints
|
||||||
|
0.005, // changeover time
|
||||||
|
256); // spi
|
||||||
|
|
||||||
|
txa[channel].cfir.p = create_cfir(
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
max(2048, ch[channel].dsp_size), // number of filter coefficients
|
||||||
|
0, // minimum phase flag
|
||||||
|
txa[channel].midbuff, // input buffer
|
||||||
|
txa[channel].midbuff, // output buffer
|
||||||
|
ch[channel].dsp_rate, // input sample rate
|
||||||
|
ch[channel].out_rate, // CIC input sample rate
|
||||||
|
1, // CIC differential delay
|
||||||
|
640, // CIC interpolation factor
|
||||||
|
5, // CIC integrator-comb pairs
|
||||||
|
20000.0, // cutoff frequency
|
||||||
|
2, // brick-wall windowed rolloff
|
||||||
|
0.0, // raised-cosine transition width
|
||||||
|
0); // window type
|
||||||
|
|
||||||
|
txa[channel].rsmpout.p = create_resample (
|
||||||
|
0, // run - will be turned ON below if needed
|
||||||
|
ch[channel].dsp_size, // input size
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].outbuff, // pointer to output buffer
|
||||||
|
ch[channel].dsp_rate, // input sample rate
|
||||||
|
ch[channel].out_rate, // output sample rate
|
||||||
|
0.0, // select cutoff automatically
|
||||||
|
0, // select ncoef automatically
|
||||||
|
0.980); // gain
|
||||||
|
|
||||||
|
txa[channel].outmeter.p = create_meter (
|
||||||
|
1, // run
|
||||||
|
0, // optional pointer to another 'run'
|
||||||
|
ch[channel].dsp_outsize, // size
|
||||||
|
txa[channel].outbuff, // pointer to buffer
|
||||||
|
ch[channel].out_rate, // samplerate
|
||||||
|
0.100, // averaging time constant
|
||||||
|
0.100, // peak decay time constant
|
||||||
|
txa[channel].meter, // result vector
|
||||||
|
txa[channel].pmtupdate, // locks for meter access
|
||||||
|
TXA_OUT_AV, // index for average value
|
||||||
|
TXA_OUT_PK, // index for peak value
|
||||||
|
-1, // index for gain value
|
||||||
|
0); // pointer for gain computation
|
||||||
|
|
||||||
|
// turn OFF / ON resamplers as needed
|
||||||
|
TXAResCheck (channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_txa (int channel)
|
||||||
|
{
|
||||||
|
// in reverse order, free each item we created
|
||||||
|
destroy_meter (txa[channel].outmeter.p);
|
||||||
|
destroy_resample (txa[channel].rsmpout.p);
|
||||||
|
destroy_cfir(txa[channel].cfir.p);
|
||||||
|
destroy_calcc (txa[channel].calcc.p);
|
||||||
|
destroy_iqc (txa[channel].iqc.p0);
|
||||||
|
destroy_siphon (txa[channel].sip1.p);
|
||||||
|
destroy_meter (txa[channel].alcmeter.p);
|
||||||
|
destroy_uslew (txa[channel].uslew.p);
|
||||||
|
destroy_gen (txa[channel].gen1.p);
|
||||||
|
destroy_fmmod (txa[channel].fmmod.p);
|
||||||
|
destroy_ammod (txa[channel].ammod.p);
|
||||||
|
destroy_wcpagc (txa[channel].alc.p);
|
||||||
|
destroy_meter (txa[channel].compmeter.p);
|
||||||
|
destroy_bandpass (txa[channel].bp2.p);
|
||||||
|
destroy_osctrl (txa[channel].osctrl.p);
|
||||||
|
destroy_bandpass (txa[channel].bp1.p);
|
||||||
|
destroy_compressor (txa[channel].compressor.p);
|
||||||
|
destroy_bandpass (txa[channel].bp0.p);
|
||||||
|
destroy_meter (txa[channel].cfcmeter.p);
|
||||||
|
destroy_cfcomp (txa[channel].cfcomp.p);
|
||||||
|
destroy_meter (txa[channel].lvlrmeter.p);
|
||||||
|
destroy_wcpagc (txa[channel].leveler.p);
|
||||||
|
destroy_emphp (txa[channel].preemph.p);
|
||||||
|
destroy_meter (txa[channel].eqmeter.p);
|
||||||
|
destroy_eqp (txa[channel].eqp.p);
|
||||||
|
destroy_amsq (txa[channel].amsq.p);
|
||||||
|
destroy_meter (txa[channel].micmeter.p);
|
||||||
|
destroy_phrot (txa[channel].phrot.p);
|
||||||
|
destroy_panel (txa[channel].panel.p);
|
||||||
|
destroy_gen (txa[channel].gen0.p);
|
||||||
|
destroy_resample (txa[channel].rsmpin.p);
|
||||||
|
_aligned_free (txa[channel].midbuff);
|
||||||
|
_aligned_free (txa[channel].outbuff);
|
||||||
|
_aligned_free (txa[channel].inbuff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_txa (int channel)
|
||||||
|
{
|
||||||
|
memset (txa[channel].inbuff, 0, 1 * ch[channel].dsp_insize * sizeof (complex));
|
||||||
|
memset (txa[channel].outbuff, 0, 1 * ch[channel].dsp_outsize * sizeof (complex));
|
||||||
|
memset (txa[channel].midbuff, 0, 2 * ch[channel].dsp_size * sizeof (complex));
|
||||||
|
flush_resample (txa[channel].rsmpin.p);
|
||||||
|
flush_gen (txa[channel].gen0.p);
|
||||||
|
flush_panel (txa[channel].panel.p);
|
||||||
|
flush_phrot (txa[channel].phrot.p);
|
||||||
|
flush_meter (txa[channel].micmeter.p);
|
||||||
|
flush_amsq (txa[channel].amsq.p);
|
||||||
|
flush_eqp (txa[channel].eqp.p);
|
||||||
|
flush_meter (txa[channel].eqmeter.p);
|
||||||
|
flush_emphp (txa[channel].preemph.p);
|
||||||
|
flush_wcpagc (txa[channel].leveler.p);
|
||||||
|
flush_meter (txa[channel].lvlrmeter.p);
|
||||||
|
flush_cfcomp (txa[channel].cfcomp.p);
|
||||||
|
flush_meter (txa[channel].cfcmeter.p);
|
||||||
|
flush_bandpass (txa[channel].bp0.p);
|
||||||
|
flush_compressor (txa[channel].compressor.p);
|
||||||
|
flush_bandpass (txa[channel].bp1.p);
|
||||||
|
flush_osctrl (txa[channel].osctrl.p);
|
||||||
|
flush_bandpass (txa[channel].bp2.p);
|
||||||
|
flush_meter (txa[channel].compmeter.p);
|
||||||
|
flush_wcpagc (txa[channel].alc.p);
|
||||||
|
flush_ammod (txa[channel].ammod.p);
|
||||||
|
flush_fmmod (txa[channel].fmmod.p);
|
||||||
|
flush_gen (txa[channel].gen1.p);
|
||||||
|
flush_uslew (txa[channel].uslew.p);
|
||||||
|
flush_meter (txa[channel].alcmeter.p);
|
||||||
|
flush_siphon (txa[channel].sip1.p);
|
||||||
|
flush_iqc (txa[channel].iqc.p0);
|
||||||
|
flush_cfir(txa[channel].cfir.p);
|
||||||
|
flush_resample (txa[channel].rsmpout.p);
|
||||||
|
flush_meter (txa[channel].outmeter.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xtxa (int channel)
|
||||||
|
{
|
||||||
|
xresample (txa[channel].rsmpin.p); // input resampler
|
||||||
|
xgen (txa[channel].gen0.p); // input signal generator
|
||||||
|
xpanel (txa[channel].panel.p); // includes MIC gain
|
||||||
|
xphrot (txa[channel].phrot.p); // phase rotator
|
||||||
|
xmeter (txa[channel].micmeter.p); // MIC meter
|
||||||
|
xamsqcap (txa[channel].amsq.p); // downward expander capture
|
||||||
|
xamsq (txa[channel].amsq.p); // downward expander action
|
||||||
|
xeqp (txa[channel].eqp.p); // pre-EQ
|
||||||
|
xmeter (txa[channel].eqmeter.p); // EQ meter
|
||||||
|
xemphp (txa[channel].preemph.p, 0); // FM pre-emphasis (first option)
|
||||||
|
xwcpagc (txa[channel].leveler.p); // Leveler
|
||||||
|
xmeter (txa[channel].lvlrmeter.p); // Leveler Meter
|
||||||
|
xcfcomp (txa[channel].cfcomp.p, 0); // Continuous Frequency Compressor with post-EQ
|
||||||
|
xmeter (txa[channel].cfcmeter.p); // CFC+PostEQ Meter
|
||||||
|
xbandpass (txa[channel].bp0.p, 0); // primary bandpass filter
|
||||||
|
xcompressor (txa[channel].compressor.p); // COMP compressor
|
||||||
|
xbandpass (txa[channel].bp1.p, 0); // aux bandpass (runs if COMP)
|
||||||
|
xosctrl (txa[channel].osctrl.p); // CESSB Overshoot Control
|
||||||
|
xbandpass (txa[channel].bp2.p, 0); // aux bandpass (runs if CESSB)
|
||||||
|
xmeter (txa[channel].compmeter.p); // COMP meter
|
||||||
|
xwcpagc (txa[channel].alc.p); // ALC
|
||||||
|
xammod (txa[channel].ammod.p); // AM Modulator
|
||||||
|
xemphp (txa[channel].preemph.p, 1); // FM pre-emphasis (second option)
|
||||||
|
xfmmod (txa[channel].fmmod.p); // FM Modulator
|
||||||
|
xgen (txa[channel].gen1.p); // output signal generator (TUN and Two-tone)
|
||||||
|
xuslew (txa[channel].uslew.p); // up-slew for AM, FM, and gens
|
||||||
|
xmeter (txa[channel].alcmeter.p); // ALC Meter
|
||||||
|
xsiphon (txa[channel].sip1.p, 0); // siphon data for display
|
||||||
|
xiqc (txa[channel].iqc.p0); // PureSignal correction
|
||||||
|
xcfir(txa[channel].cfir.p); // compensating FIR filter (used Protocol_2 only)
|
||||||
|
xresample (txa[channel].rsmpout.p); // output resampler
|
||||||
|
xmeter (txa[channel].outmeter.p); // output meter
|
||||||
|
// print_peak_env ("env_exception.txt", ch[channel].dsp_outsize, txa[channel].outbuff, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputSamplerate_txa (int channel)
|
||||||
|
{
|
||||||
|
// buffers
|
||||||
|
_aligned_free (txa[channel].inbuff);
|
||||||
|
txa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
|
||||||
|
// input resampler
|
||||||
|
setBuffers_resample (txa[channel].rsmpin.p, txa[channel].inbuff, txa[channel].midbuff);
|
||||||
|
setSize_resample (txa[channel].rsmpin.p, ch[channel].dsp_insize);
|
||||||
|
setInRate_resample (txa[channel].rsmpin.p, ch[channel].in_rate);
|
||||||
|
TXAResCheck (channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOutputSamplerate_txa (int channel)
|
||||||
|
{
|
||||||
|
// buffers
|
||||||
|
_aligned_free (txa[channel].outbuff);
|
||||||
|
txa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
|
||||||
|
// cfir - needs to know input rate of firmware CIC
|
||||||
|
setOutRate_cfir (txa[channel].cfir.p, ch[channel].out_rate);
|
||||||
|
// output resampler
|
||||||
|
setBuffers_resample (txa[channel].rsmpout.p, txa[channel].midbuff, txa[channel].outbuff);
|
||||||
|
setOutRate_resample (txa[channel].rsmpout.p, ch[channel].out_rate);
|
||||||
|
TXAResCheck (channel);
|
||||||
|
// output meter
|
||||||
|
setBuffers_meter (txa[channel].outmeter.p, txa[channel].outbuff);
|
||||||
|
setSize_meter (txa[channel].outmeter.p, ch[channel].dsp_outsize);
|
||||||
|
setSamplerate_meter (txa[channel].outmeter.p, ch[channel].out_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDSPSamplerate_txa (int channel)
|
||||||
|
{
|
||||||
|
// buffers
|
||||||
|
_aligned_free (txa[channel].inbuff);
|
||||||
|
txa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
|
||||||
|
_aligned_free (txa[channel].outbuff);
|
||||||
|
txa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
|
||||||
|
// input resampler
|
||||||
|
setBuffers_resample (txa[channel].rsmpin.p, txa[channel].inbuff, txa[channel].midbuff);
|
||||||
|
setSize_resample (txa[channel].rsmpin.p, ch[channel].dsp_insize);
|
||||||
|
setOutRate_resample (txa[channel].rsmpin.p, ch[channel].dsp_rate);
|
||||||
|
// dsp_rate blocks
|
||||||
|
setSamplerate_gen (txa[channel].gen0.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_panel (txa[channel].panel.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_phrot (txa[channel].phrot.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_meter (txa[channel].micmeter.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_amsq (txa[channel].amsq.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_eqp (txa[channel].eqp.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_meter (txa[channel].eqmeter.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_emphp (txa[channel].preemph.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_wcpagc (txa[channel].leveler.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_meter (txa[channel].lvlrmeter.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_cfcomp (txa[channel].cfcomp.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_meter (txa[channel].cfcmeter.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_bandpass (txa[channel].bp0.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_compressor (txa[channel].compressor.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_bandpass (txa[channel].bp1.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_osctrl (txa[channel].osctrl.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_bandpass (txa[channel].bp2.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_meter (txa[channel].compmeter.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_wcpagc (txa[channel].alc.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_ammod (txa[channel].ammod.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_fmmod (txa[channel].fmmod.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_gen (txa[channel].gen1.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_uslew (txa[channel].uslew.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_meter (txa[channel].alcmeter.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_siphon (txa[channel].sip1.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_iqc (txa[channel].iqc.p0, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_cfir (txa[channel].cfir.p, ch[channel].dsp_rate);
|
||||||
|
// output resampler
|
||||||
|
setBuffers_resample (txa[channel].rsmpout.p, txa[channel].midbuff, txa[channel].outbuff);
|
||||||
|
setInRate_resample (txa[channel].rsmpout.p, ch[channel].dsp_rate);
|
||||||
|
TXAResCheck (channel);
|
||||||
|
// output meter
|
||||||
|
setBuffers_meter (txa[channel].outmeter.p, txa[channel].outbuff);
|
||||||
|
setSize_meter (txa[channel].outmeter.p, ch[channel].dsp_outsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDSPBuffsize_txa (int channel)
|
||||||
|
{
|
||||||
|
// buffers
|
||||||
|
_aligned_free (txa[channel].inbuff);
|
||||||
|
txa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
|
||||||
|
_aligned_free (txa[channel].midbuff);
|
||||||
|
txa[channel].midbuff = (double *)malloc0(2 * ch[channel].dsp_size * sizeof(complex));
|
||||||
|
_aligned_free (txa[channel].outbuff);
|
||||||
|
txa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
|
||||||
|
// input resampler
|
||||||
|
setBuffers_resample (txa[channel].rsmpin.p, txa[channel].inbuff, txa[channel].midbuff);
|
||||||
|
setSize_resample (txa[channel].rsmpin.p, ch[channel].dsp_insize);
|
||||||
|
// dsp_size blocks
|
||||||
|
setBuffers_gen (txa[channel].gen0.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_gen (txa[channel].gen0.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_panel (txa[channel].panel.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_panel (txa[channel].panel.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_phrot (txa[channel].phrot.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_phrot (txa[channel].phrot.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_meter (txa[channel].micmeter.p, txa[channel].midbuff);
|
||||||
|
setSize_meter (txa[channel].micmeter.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_amsq (txa[channel].amsq.p, txa[channel].midbuff, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_amsq (txa[channel].amsq.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_eqp (txa[channel].eqp.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_eqp (txa[channel].eqp.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_meter (txa[channel].eqmeter.p, txa[channel].midbuff);
|
||||||
|
setSize_meter (txa[channel].eqmeter.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_emphp (txa[channel].preemph.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_emphp (txa[channel].preemph.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_wcpagc (txa[channel].leveler.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_wcpagc (txa[channel].leveler.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_meter (txa[channel].lvlrmeter.p, txa[channel].midbuff);
|
||||||
|
setSize_meter (txa[channel].lvlrmeter.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_cfcomp (txa[channel].cfcomp.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_cfcomp (txa[channel].cfcomp.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_meter (txa[channel].cfcmeter.p, txa[channel].midbuff);
|
||||||
|
setSize_meter (txa[channel].cfcmeter.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_bandpass (txa[channel].bp0.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_bandpass (txa[channel].bp0.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_compressor (txa[channel].compressor.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_compressor (txa[channel].compressor.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_bandpass (txa[channel].bp1.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_bandpass (txa[channel].bp1.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_osctrl (txa[channel].osctrl.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_osctrl (txa[channel].osctrl.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_bandpass (txa[channel].bp2.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_bandpass (txa[channel].bp2.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_meter (txa[channel].compmeter.p, txa[channel].midbuff);
|
||||||
|
setSize_meter (txa[channel].compmeter.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_wcpagc (txa[channel].alc.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_wcpagc (txa[channel].alc.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_ammod (txa[channel].ammod.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_ammod (txa[channel].ammod.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_fmmod (txa[channel].fmmod.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_fmmod (txa[channel].fmmod.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_gen (txa[channel].gen1.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_gen (txa[channel].gen1.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_uslew (txa[channel].uslew.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_uslew (txa[channel].uslew.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_meter (txa[channel].alcmeter.p, txa[channel].midbuff);
|
||||||
|
setSize_meter (txa[channel].alcmeter.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_siphon (txa[channel].sip1.p, txa[channel].midbuff);
|
||||||
|
setSize_siphon (txa[channel].sip1.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_iqc (txa[channel].iqc.p0, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_iqc (txa[channel].iqc.p0, ch[channel].dsp_size);
|
||||||
|
setBuffers_cfir (txa[channel].cfir.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_cfir (txa[channel].cfir.p, ch[channel].dsp_size);
|
||||||
|
// output resampler
|
||||||
|
setBuffers_resample (txa[channel].rsmpout.p, txa[channel].midbuff, txa[channel].outbuff);
|
||||||
|
setSize_resample (txa[channel].rsmpout.p, ch[channel].dsp_size);
|
||||||
|
// output meter
|
||||||
|
setBuffers_meter (txa[channel].outmeter.p, txa[channel].outbuff);
|
||||||
|
setSize_meter (txa[channel].outmeter.p, ch[channel].dsp_outsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* TXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXAMode (int channel, int mode)
|
||||||
|
{
|
||||||
|
if (txa[channel].mode != mode)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
txa[channel].mode = mode;
|
||||||
|
txa[channel].ammod.p->run = 0;
|
||||||
|
txa[channel].fmmod.p->run = 0;
|
||||||
|
txa[channel].preemph.p->run = 0;
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case TXA_AM:
|
||||||
|
case TXA_SAM:
|
||||||
|
txa[channel].ammod.p->run = 1;
|
||||||
|
txa[channel].ammod.p->mode = 0;
|
||||||
|
break;
|
||||||
|
case TXA_DSB:
|
||||||
|
txa[channel].ammod.p->run = 1;
|
||||||
|
txa[channel].ammod.p->mode = 1;
|
||||||
|
break;
|
||||||
|
case TXA_AM_LSB:
|
||||||
|
case TXA_AM_USB:
|
||||||
|
txa[channel].ammod.p->run = 1;
|
||||||
|
txa[channel].ammod.p->mode = 2;
|
||||||
|
break;
|
||||||
|
case TXA_FM:
|
||||||
|
txa[channel].fmmod.p->run = 1;
|
||||||
|
txa[channel].preemph.p->run = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
TXASetupBPFilters (channel);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXABandpassFreqs (int channel, double f_low, double f_high)
|
||||||
|
{
|
||||||
|
if ((txa[channel].f_low != f_low) || (txa[channel].f_high != f_high))
|
||||||
|
{
|
||||||
|
txa[channel].f_low = f_low;
|
||||||
|
txa[channel].f_high = f_high;
|
||||||
|
TXASetupBPFilters (channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* TXA Internal Functions *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
void TXAResCheck (int channel)
|
||||||
|
{
|
||||||
|
RESAMPLE a = txa[channel].rsmpin.p;
|
||||||
|
if (ch[channel].in_rate != ch[channel].dsp_rate) a->run = 1;
|
||||||
|
else a->run = 0;
|
||||||
|
a = txa[channel].rsmpout.p;
|
||||||
|
if (ch[channel].dsp_rate != ch[channel].out_rate) a->run = 1;
|
||||||
|
else a->run = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TXAUslewCheck (int channel)
|
||||||
|
{
|
||||||
|
return (txa[channel].ammod.p->run == 1) ||
|
||||||
|
(txa[channel].fmmod.p->run == 1) ||
|
||||||
|
(txa[channel].gen0.p->run == 1) ||
|
||||||
|
(txa[channel].gen1.p->run == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TXASetupBPFilters (int channel)
|
||||||
|
{
|
||||||
|
txa[channel].bp0.p->run = 1;
|
||||||
|
txa[channel].bp1.p->run = 0;
|
||||||
|
txa[channel].bp2.p->run = 0;
|
||||||
|
switch (txa[channel].mode)
|
||||||
|
{
|
||||||
|
case TXA_LSB:
|
||||||
|
case TXA_USB:
|
||||||
|
case TXA_CWL:
|
||||||
|
case TXA_CWU:
|
||||||
|
case TXA_DIGL:
|
||||||
|
case TXA_DIGU:
|
||||||
|
case TXA_SPEC:
|
||||||
|
case TXA_DRM:
|
||||||
|
CalcBandpassFilter (txa[channel].bp0.p, txa[channel].f_low, txa[channel].f_high, 2.0);
|
||||||
|
if (txa[channel].compressor.p->run)
|
||||||
|
{
|
||||||
|
CalcBandpassFilter (txa[channel].bp1.p, txa[channel].f_low, txa[channel].f_high, 2.0);
|
||||||
|
txa[channel].bp1.p->run = 1;
|
||||||
|
if (txa[channel].osctrl.p->run)
|
||||||
|
{
|
||||||
|
CalcBandpassFilter (txa[channel].bp2.p, txa[channel].f_low, txa[channel].f_high, 1.0);
|
||||||
|
txa[channel].bp2.p->run = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TXA_DSB:
|
||||||
|
case TXA_AM:
|
||||||
|
case TXA_SAM:
|
||||||
|
case TXA_FM:
|
||||||
|
if (txa[channel].compressor.p->run)
|
||||||
|
{
|
||||||
|
CalcBandpassFilter (txa[channel].bp0.p, 0.0, txa[channel].f_high, 2.0);
|
||||||
|
CalcBandpassFilter (txa[channel].bp1.p, 0.0, txa[channel].f_high, 2.0);
|
||||||
|
txa[channel].bp1.p->run = 1;
|
||||||
|
if (txa[channel].osctrl.p->run)
|
||||||
|
{
|
||||||
|
CalcBandpassFilter (txa[channel].bp2.p, 0.0, txa[channel].f_high, 1.0);
|
||||||
|
txa[channel].bp2.p->run = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CalcBandpassFilter (txa[channel].bp0.p, txa[channel].f_low, txa[channel].f_high, 1.0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TXA_AM_LSB:
|
||||||
|
CalcBandpassFilter (txa[channel].bp0.p, -txa[channel].f_high, 0.0, 2.0);
|
||||||
|
if (txa[channel].compressor.p->run)
|
||||||
|
{
|
||||||
|
CalcBandpassFilter (txa[channel].bp1.p, -txa[channel].f_high, 0.0, 2.0);
|
||||||
|
txa[channel].bp1.p->run = 1;
|
||||||
|
if (txa[channel].osctrl.p->run)
|
||||||
|
{
|
||||||
|
CalcBandpassFilter (txa[channel].bp2.p, -txa[channel].f_high, 0.0, 1.0);
|
||||||
|
txa[channel].bp2.p->run = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TXA_AM_USB:
|
||||||
|
CalcBandpassFilter (txa[channel].bp0.p, 0.0, txa[channel].f_high, 2.0);
|
||||||
|
if (txa[channel].compressor.p->run)
|
||||||
|
{
|
||||||
|
CalcBandpassFilter (txa[channel].bp1.p, 0.0, txa[channel].f_high, 2.0);
|
||||||
|
txa[channel].bp1.p->run = 1;
|
||||||
|
if (txa[channel].osctrl.p->run)
|
||||||
|
{
|
||||||
|
CalcBandpassFilter (txa[channel].bp2.p, 0.0, txa[channel].f_high, 1.0);
|
||||||
|
txa[channel].bp2.p->run = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* Collectives *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void TXASetNC (int channel, int nc)
|
||||||
|
{
|
||||||
|
int oldstate = SetChannelState (channel, 0, 1);
|
||||||
|
SetTXABandpassNC (channel, nc);
|
||||||
|
SetTXAFMEmphNC (channel, nc);
|
||||||
|
SetTXAEQNC (channel, nc);
|
||||||
|
SetTXAFMNC (channel, nc);
|
||||||
|
SetTXACFIRNC (channel, nc);
|
||||||
|
SetChannelState (channel, oldstate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void TXASetMP (int channel, int mp)
|
||||||
|
{
|
||||||
|
SetTXABandpassMP (channel, mp);
|
||||||
|
SetTXAFMEmphMP (channel, mp);
|
||||||
|
SetTXAEQMP (channel, mp);
|
||||||
|
SetTXAFMMP (channel, mp);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXAFMAFFilter (int channel, double low, double high)
|
||||||
|
{
|
||||||
|
SetTXAFMPreEmphFreqs (channel, low, high);
|
||||||
|
SetTXAFMAFFreqs (channel, low, high);
|
||||||
|
}
|
||||||
+193
@@ -0,0 +1,193 @@
|
|||||||
|
/* TXA.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2017 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _txa_h
|
||||||
|
#define _txa_h
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
enum txaMode
|
||||||
|
{
|
||||||
|
TXA_LSB,
|
||||||
|
TXA_USB,
|
||||||
|
TXA_DSB,
|
||||||
|
TXA_CWL,
|
||||||
|
TXA_CWU,
|
||||||
|
TXA_FM,
|
||||||
|
TXA_AM,
|
||||||
|
TXA_DIGU,
|
||||||
|
TXA_SPEC,
|
||||||
|
TXA_DIGL,
|
||||||
|
TXA_SAM,
|
||||||
|
TXA_DRM,
|
||||||
|
TXA_AM_LSB,
|
||||||
|
TXA_AM_USB
|
||||||
|
};
|
||||||
|
|
||||||
|
enum txaMeterType
|
||||||
|
{
|
||||||
|
TXA_MIC_PK,
|
||||||
|
TXA_MIC_AV,
|
||||||
|
TXA_EQ_PK,
|
||||||
|
TXA_EQ_AV,
|
||||||
|
TXA_LVLR_PK,
|
||||||
|
TXA_LVLR_AV,
|
||||||
|
TXA_LVLR_GAIN,
|
||||||
|
TXA_CFC_PK,
|
||||||
|
TXA_CFC_AV,
|
||||||
|
TXA_CFC_GAIN,
|
||||||
|
TXA_COMP_PK,
|
||||||
|
TXA_COMP_AV,
|
||||||
|
TXA_ALC_PK,
|
||||||
|
TXA_ALC_AV,
|
||||||
|
TXA_ALC_GAIN,
|
||||||
|
TXA_OUT_PK,
|
||||||
|
TXA_OUT_AV,
|
||||||
|
TXA_METERTYPE_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _txa
|
||||||
|
{
|
||||||
|
double* inbuff;
|
||||||
|
double* outbuff;
|
||||||
|
double* midbuff;
|
||||||
|
int mode;
|
||||||
|
double f_low;
|
||||||
|
double f_high;
|
||||||
|
double meter[TXA_METERTYPE_LAST];
|
||||||
|
CRITICAL_SECTION* pmtupdate[TXA_METERTYPE_LAST];
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
METER p;
|
||||||
|
} micmeter, eqmeter, lvlrmeter, cfcmeter, compmeter, alcmeter, outmeter;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
RESAMPLE p;
|
||||||
|
} rsmpin, rsmpout;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
PANEL p;
|
||||||
|
} panel;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
AMSQ p;
|
||||||
|
} amsq;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
EQP p;
|
||||||
|
} eqp;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
PHROT p;
|
||||||
|
} phrot;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
CFCOMP p;
|
||||||
|
} cfcomp;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
COMPRESSOR p;
|
||||||
|
} compressor;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
BANDPASS p;
|
||||||
|
} bp0, bp1, bp2;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
OSCTRL p;
|
||||||
|
} osctrl;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
WCPAGC p;
|
||||||
|
} leveler, alc;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
AMMOD p;
|
||||||
|
} ammod;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
EMPHP p;
|
||||||
|
} preemph;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
FMMOD p;
|
||||||
|
} fmmod;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
SIPHON p;
|
||||||
|
} sip1;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
GEN p;
|
||||||
|
} gen0, gen1;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
USLEW p;
|
||||||
|
} uslew;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
CALCC p;
|
||||||
|
CRITICAL_SECTION cs_update;
|
||||||
|
} calcc;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
IQC p0, p1;
|
||||||
|
// p0 for dsp-synchronized reference, p1 for other
|
||||||
|
} iqc;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
CFIR p;
|
||||||
|
} cfir;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct _txa txa[];
|
||||||
|
|
||||||
|
extern void create_txa (int channel);
|
||||||
|
|
||||||
|
extern void destroy_txa (int channel);
|
||||||
|
|
||||||
|
extern void flush_txa (int channel);
|
||||||
|
|
||||||
|
extern void xtxa (int channel);
|
||||||
|
|
||||||
|
extern int TXAUslewCheck (int channel);
|
||||||
|
|
||||||
|
extern void setInputSamplerate_txa (int channel);
|
||||||
|
|
||||||
|
extern void setOutputSamplerate_txa (int channel);
|
||||||
|
|
||||||
|
extern void setDSPSamplerate_txa (int channel);
|
||||||
|
|
||||||
|
extern void setDSPBuffsize_txa (int channel);
|
||||||
|
|
||||||
|
// TXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAMode (int channel, int mode);
|
||||||
|
|
||||||
|
extern void TXAResCheck (int channel);
|
||||||
|
|
||||||
|
extern void TXASetupBPFilters (int channel);
|
||||||
|
|
||||||
|
#endif
|
||||||
+293
@@ -0,0 +1,293 @@
|
|||||||
|
/* amd.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
AMD create_amd
|
||||||
|
(
|
||||||
|
int run,
|
||||||
|
int buff_size,
|
||||||
|
double *in_buff,
|
||||||
|
double *out_buff,
|
||||||
|
int mode,
|
||||||
|
int levelfade,
|
||||||
|
int sbmode,
|
||||||
|
int sample_rate,
|
||||||
|
double fmin,
|
||||||
|
double fmax,
|
||||||
|
double zeta,
|
||||||
|
double omegaN,
|
||||||
|
double tauR,
|
||||||
|
double tauI
|
||||||
|
)
|
||||||
|
{
|
||||||
|
AMD a = (AMD) malloc0 (sizeof(amd));
|
||||||
|
a->run = run;
|
||||||
|
a->buff_size = buff_size;
|
||||||
|
a->in_buff = in_buff;
|
||||||
|
a->out_buff = out_buff;
|
||||||
|
a->mode = mode;
|
||||||
|
a->levelfade = levelfade;
|
||||||
|
a->sbmode = sbmode;
|
||||||
|
a->sample_rate = (double)sample_rate;
|
||||||
|
a->fmin = fmin;
|
||||||
|
a->fmax = fmax;
|
||||||
|
a->zeta = zeta;
|
||||||
|
a->omegaN = omegaN;
|
||||||
|
a->tauR = tauR;
|
||||||
|
a->tauI = tauI;
|
||||||
|
|
||||||
|
init_amd(a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_amd(AMD a)
|
||||||
|
{
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_amd(AMD a)
|
||||||
|
{
|
||||||
|
//pll
|
||||||
|
a->omega_min = TWOPI * a->fmin / a->sample_rate;
|
||||||
|
a->omega_max = TWOPI * a->fmax / a->sample_rate;
|
||||||
|
a->g1 = 1.0 - exp(-2.0 * a->omegaN * a->zeta / a->sample_rate);
|
||||||
|
a->g2 = -a->g1 + 2.0 * (1 - exp(-a->omegaN * a->zeta / a->sample_rate) * cos(a->omegaN / a->sample_rate * sqrt(1.0 - a->zeta * a->zeta)));
|
||||||
|
a->phs = 0.0;
|
||||||
|
a->fil_out = 0.0;
|
||||||
|
a->omega = 0.0;
|
||||||
|
|
||||||
|
//fade leveler
|
||||||
|
a->dc = 0.0;
|
||||||
|
a->dc_insert = 0.0;
|
||||||
|
a->mtauR = exp(-1.0 / (a->sample_rate * a->tauR));
|
||||||
|
a->onem_mtauR = 1.0 - a->mtauR;
|
||||||
|
a->mtauI = exp(-1.0 / (a->sample_rate * a->tauI));
|
||||||
|
a->onem_mtauI = 1.0 - a->mtauI;
|
||||||
|
|
||||||
|
//sideband separation
|
||||||
|
a->c0[0] = -0.328201924180698;
|
||||||
|
a->c0[1] = -0.744171491539427;
|
||||||
|
a->c0[2] = -0.923022915444215;
|
||||||
|
a->c0[3] = -0.978490468768238;
|
||||||
|
a->c0[4] = -0.994128272402075;
|
||||||
|
a->c0[5] = -0.998458978159551;
|
||||||
|
a->c0[6] = -0.999790306259206;
|
||||||
|
|
||||||
|
a->c1[0] = -0.0991227952747244;
|
||||||
|
a->c1[1] = -0.565619728761389;
|
||||||
|
a->c1[2] = -0.857467122550052;
|
||||||
|
a->c1[3] = -0.959123933111275;
|
||||||
|
a->c1[4] = -0.988739372718090;
|
||||||
|
a->c1[5] = -0.996959189310611;
|
||||||
|
a->c1[6] = -0.999282492800792;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_amd (AMD a)
|
||||||
|
{
|
||||||
|
a->dc = 0.0;
|
||||||
|
a->dc_insert = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xamd (AMD a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double audio;
|
||||||
|
double vco[2];
|
||||||
|
double corr[2];
|
||||||
|
double det;
|
||||||
|
double del_out;
|
||||||
|
double ai, bi, aq, bq;
|
||||||
|
double ai_ps, bi_ps, aq_ps, bq_ps;
|
||||||
|
int j, k;
|
||||||
|
if (a->run)
|
||||||
|
{
|
||||||
|
switch (a->mode)
|
||||||
|
{
|
||||||
|
|
||||||
|
case 0: //AM Demodulator
|
||||||
|
{
|
||||||
|
for (i = 0; i < a->buff_size; i++)
|
||||||
|
{
|
||||||
|
audio = sqrt(a->in_buff[2 * i + 0] * a->in_buff[2 * i + 0] + a->in_buff[2 * i + 1] * a->in_buff[2 * i + 1]);
|
||||||
|
if (a->levelfade)
|
||||||
|
{
|
||||||
|
a->dc = a->mtauR * a->dc + a->onem_mtauR * audio;
|
||||||
|
a->dc_insert = a->mtauI * a->dc_insert + a->onem_mtauI * audio;
|
||||||
|
audio += a->dc_insert - a->dc;
|
||||||
|
}
|
||||||
|
a->out_buff[2 * i + 0] = audio;
|
||||||
|
a->out_buff[2 * i + 1] = audio;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1: //Synchronous AM Demodulator with Sideband Separation
|
||||||
|
{
|
||||||
|
for (i = 0; i < a->buff_size; i++)
|
||||||
|
{
|
||||||
|
vco[0] = cos(a->phs);
|
||||||
|
vco[1] = sin(a->phs);
|
||||||
|
|
||||||
|
ai = a->in_buff[2 * i + 0] * vco[0];
|
||||||
|
bi = a->in_buff[2 * i + 0] * vco[1];
|
||||||
|
aq = a->in_buff[2 * i + 1] * vco[0];
|
||||||
|
bq = a->in_buff[2 * i + 1] * vco[1];
|
||||||
|
|
||||||
|
if (a->sbmode != 0)
|
||||||
|
{
|
||||||
|
a->a[0] = a->dsI;
|
||||||
|
a->b[0] = bi;
|
||||||
|
a->c[0] = a->dsQ;
|
||||||
|
a->d[0] = aq;
|
||||||
|
a->dsI = ai;
|
||||||
|
a->dsQ = bq;
|
||||||
|
|
||||||
|
for (j = 0; j < STAGES; j++)
|
||||||
|
{
|
||||||
|
k = 3 * j;
|
||||||
|
a->a[k + 3] = a->c0[j] * (a->a[k] - a->a[k + 5]) + a->a[k + 2];
|
||||||
|
a->b[k + 3] = a->c1[j] * (a->b[k] - a->b[k + 5]) + a->b[k + 2];
|
||||||
|
a->c[k + 3] = a->c0[j] * (a->c[k] - a->c[k + 5]) + a->c[k + 2];
|
||||||
|
a->d[k + 3] = a->c1[j] * (a->d[k] - a->d[k + 5]) + a->d[k + 2];
|
||||||
|
}
|
||||||
|
ai_ps = a->a[OUT_IDX];
|
||||||
|
bi_ps = a->b[OUT_IDX];
|
||||||
|
bq_ps = a->c[OUT_IDX];
|
||||||
|
aq_ps = a->d[OUT_IDX];
|
||||||
|
|
||||||
|
for (j = OUT_IDX + 2; j > 0; j--)
|
||||||
|
{
|
||||||
|
a->a[j] = a->a[j - 1];
|
||||||
|
a->b[j] = a->b[j - 1];
|
||||||
|
a->c[j] = a->c[j - 1];
|
||||||
|
a->d[j] = a->d[j - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
corr[0] = +ai + bq;
|
||||||
|
corr[1] = -bi + aq;
|
||||||
|
|
||||||
|
switch(a->sbmode)
|
||||||
|
{
|
||||||
|
case 0: //both sidebands
|
||||||
|
{
|
||||||
|
audio = corr[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: //LSB
|
||||||
|
{
|
||||||
|
audio = (ai_ps - bi_ps) + (aq_ps + bq_ps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: //USB
|
||||||
|
{
|
||||||
|
audio = (ai_ps + bi_ps) - (aq_ps - bq_ps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a->levelfade)
|
||||||
|
{
|
||||||
|
a->dc = a->mtauR * a->dc + a->onem_mtauR * audio;
|
||||||
|
a->dc_insert = a->mtauI * a->dc_insert + a->onem_mtauI * corr[0];
|
||||||
|
audio += a->dc_insert - a->dc;
|
||||||
|
}
|
||||||
|
a->out_buff[2 * i + 0] = audio;
|
||||||
|
a->out_buff[2 * i + 1] = audio;
|
||||||
|
|
||||||
|
if ((corr[0] == 0.0) && (corr[1] == 0.0)) corr[0] = 1.0;
|
||||||
|
det = atan2(corr[1], corr[0]);
|
||||||
|
del_out = a->fil_out;
|
||||||
|
a->omega += a->g2 * det;
|
||||||
|
if (a->omega < a->omega_min) a->omega = a->omega_min;
|
||||||
|
if (a->omega > a->omega_max) a->omega = a->omega_max;
|
||||||
|
a->fil_out = a->g1 * det + a->omega;
|
||||||
|
a->phs += del_out;
|
||||||
|
while (a->phs >= TWOPI) a->phs -= TWOPI;
|
||||||
|
while (a->phs < 0.0) a->phs += TWOPI;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a->in_buff != a->out_buff)
|
||||||
|
memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof(complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_amd (AMD a, double* in, double* out)
|
||||||
|
{
|
||||||
|
a->in_buff = in;
|
||||||
|
a->out_buff = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_amd (AMD a, int rate)
|
||||||
|
{
|
||||||
|
a->sample_rate = rate;
|
||||||
|
init_amd(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_amd (AMD a, int size)
|
||||||
|
{
|
||||||
|
a->buff_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* RXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAAMDRun(int channel, int run)
|
||||||
|
{
|
||||||
|
AMD a = rxa[channel].amd.p;
|
||||||
|
if (a->run != run)
|
||||||
|
{
|
||||||
|
RXAbp1Check (channel, run, rxa[channel].snba.p->run, rxa[channel].emnr.p->run,
|
||||||
|
rxa[channel].anf.p->run, rxa[channel].anr.p->run);
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->run = run;
|
||||||
|
RXAbp1Set (channel);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAAMDSBMode(int channel, int sbmode)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].amd.p->sbmode = sbmode;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAAMDFadeLevel(int channel, int levelfade)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].amd.p->levelfade = levelfade;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
/* amd.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _amd_h
|
||||||
|
#define _amd_h
|
||||||
|
|
||||||
|
// ff defines for sbdemod
|
||||||
|
#ifndef STAGES
|
||||||
|
#define STAGES 7
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OUT_IDX
|
||||||
|
#define OUT_IDX (3 * STAGES)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _amd
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int buff_size; // buffer size
|
||||||
|
double *in_buff; // pointer to input buffer
|
||||||
|
double *out_buff; // pointer to output buffer
|
||||||
|
int mode; // demodulation mode
|
||||||
|
double sample_rate; // sample rate
|
||||||
|
double dc; // dc component in demodulated output
|
||||||
|
double fmin; // pll - minimum carrier freq to lock
|
||||||
|
double fmax; // pll - maximum carrier freq to lock
|
||||||
|
double omega_min; // pll - minimum lock check parameter
|
||||||
|
double omega_max; // pll - maximum lock check parameter
|
||||||
|
double zeta; // pll - damping factor; as coded, must be <=1.0
|
||||||
|
double omegaN; // pll - natural frequency
|
||||||
|
double phs; // pll - phase accumulator
|
||||||
|
double omega; // pll - locked pll frequency
|
||||||
|
double fil_out; // pll - filter output
|
||||||
|
double g1, g2; // pll - filter gain parameters
|
||||||
|
double tauR; // carrier removal time constant
|
||||||
|
double tauI; // carrier insertion time constant
|
||||||
|
double mtauR; // carrier removal multiplier
|
||||||
|
double onem_mtauR; // 1.0 - carrier_removal_multiplier
|
||||||
|
double mtauI; // carrier insertion multiplier
|
||||||
|
double onem_mtauI; // 1.0 - carrier_insertion_multiplier
|
||||||
|
double a[3 * STAGES + 3]; // Filter a variables
|
||||||
|
double b[3 * STAGES + 3]; // Filter b variables
|
||||||
|
double c[3 * STAGES + 3]; // Filter c variables
|
||||||
|
double d[3 * STAGES + 3]; // Filter d variables
|
||||||
|
double c0[STAGES]; // Filter coefficients - path 0
|
||||||
|
double c1[STAGES]; // Filter coefficients - path 1
|
||||||
|
double dsI; // delayed sample, I path
|
||||||
|
double dsQ; // delayed sample, Q path
|
||||||
|
double dc_insert; // dc component to insert in output
|
||||||
|
int sbmode; // sideband mode
|
||||||
|
int levelfade; // Fade Leveler switch
|
||||||
|
|
||||||
|
}amd, *AMD;
|
||||||
|
|
||||||
|
extern AMD create_amd
|
||||||
|
(
|
||||||
|
int run,
|
||||||
|
int buff_size,
|
||||||
|
double *in_buff,
|
||||||
|
double *out_buff,
|
||||||
|
int mode,
|
||||||
|
int levelfade,
|
||||||
|
int sbmode,
|
||||||
|
int sample_rate,
|
||||||
|
double fmin,
|
||||||
|
double fmax,
|
||||||
|
double zeta,
|
||||||
|
double omegaN,
|
||||||
|
double tauR,
|
||||||
|
double tauI
|
||||||
|
);
|
||||||
|
|
||||||
|
extern void init_amd (AMD a);
|
||||||
|
|
||||||
|
extern void destroy_amd (AMD a);
|
||||||
|
|
||||||
|
extern void flush_amd (AMD a);
|
||||||
|
|
||||||
|
extern void xamd (AMD a);
|
||||||
|
|
||||||
|
extern void setBuffers_amd (AMD a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_amd (AMD a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_amd (AMD a, int size);
|
||||||
|
|
||||||
|
// RXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAAMDRun(int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAAMDSBMode(int channel, int sbmode);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAAMDFadeLevel(int channel, int levelfade);
|
||||||
|
|
||||||
|
#endif
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
/* ammod.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2017 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
AMMOD create_ammod (int run, int mode, int size, double* in, double* out, double c_level)
|
||||||
|
{
|
||||||
|
AMMOD a = (AMMOD) malloc0 (sizeof (ammod));
|
||||||
|
a->run = run;
|
||||||
|
a->mode = mode;
|
||||||
|
a->size = size;
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
a->c_level = c_level;
|
||||||
|
a->a_level = 1.0 - a->c_level;
|
||||||
|
a->mult = 1.0 / sqrt (2.0);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_ammod (AMMOD a)
|
||||||
|
{
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_ammod (AMMOD a)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void xammod (AMMOD a)
|
||||||
|
{
|
||||||
|
if (a->run)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
switch (a->mode)
|
||||||
|
{
|
||||||
|
case 0: // AM
|
||||||
|
for (i = 0; i < a->size; i++)
|
||||||
|
a->out[2 * i + 0] = a->out[2 * i + 1] = a->mult * (a->c_level + a->a_level * a->in[2 * i + 0]);
|
||||||
|
break;
|
||||||
|
case 1: // DSB
|
||||||
|
for (i = 0; i < a->size; i++)
|
||||||
|
a->out[2 * i + 0] = a->out[2 * i + 1] = a->mult * a->in[2 * i + 0];
|
||||||
|
break;
|
||||||
|
case 2: // SSB w/Carrier
|
||||||
|
for (i = 0; i < a->size; i++)
|
||||||
|
{
|
||||||
|
a->out[2 * i + 0] = a->mult * a->c_level + a->a_level * a->in[2 * i + 0];
|
||||||
|
a->out[2 * i + 1] = a->mult * a->c_level + a->a_level * a->in[2 * i + 1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a->in != a->out)
|
||||||
|
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_ammod (AMMOD a, double* in, double* out)
|
||||||
|
{
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_ammod (AMMOD a, int rate)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_ammod (AMMOD a, int size)
|
||||||
|
{
|
||||||
|
a->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* TXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetTXAAMCarrierLevel (int channel, double c_level)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
txa[channel].ammod.p->c_level = c_level;
|
||||||
|
txa[channel].ammod.p->a_level = 1.0 - c_level;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/* ammod.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ammod_h
|
||||||
|
#define _ammod_h
|
||||||
|
|
||||||
|
typedef struct _ammod
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int mode;
|
||||||
|
int size;
|
||||||
|
double* in;
|
||||||
|
double* out;
|
||||||
|
double c_level;
|
||||||
|
double a_level;
|
||||||
|
double mult;
|
||||||
|
}ammod, *AMMOD;
|
||||||
|
|
||||||
|
extern AMMOD create_ammod (int run, int mode, int size, double* in, double* out, double c_level);
|
||||||
|
|
||||||
|
extern void destroy_ammod (AMMOD a);
|
||||||
|
|
||||||
|
extern void flush_ammod (AMMOD a);
|
||||||
|
|
||||||
|
extern void xammod (AMMOD a);
|
||||||
|
|
||||||
|
extern void setBuffers_ammod (AMMOD a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_ammod (AMMOD a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_ammod (AMMOD a, int size);
|
||||||
|
|
||||||
|
// TXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAAMCarrierLevel (int channel, double c_level);
|
||||||
|
|
||||||
|
#endif
|
||||||
+272
@@ -0,0 +1,272 @@
|
|||||||
|
/* amsq.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
void compute_slews(AMSQ a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double delta, theta;
|
||||||
|
delta = PI / (double)a->ntup;
|
||||||
|
theta = 0.0;
|
||||||
|
for (i = 0; i <= a->ntup; i++)
|
||||||
|
{
|
||||||
|
a->cup[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 - cos (theta));
|
||||||
|
theta += delta;
|
||||||
|
}
|
||||||
|
delta = PI / (double)a->ntdown;
|
||||||
|
theta = 0.0;
|
||||||
|
for (i = 0; i <= a->ntdown; i++)
|
||||||
|
{
|
||||||
|
a->cdown[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 + cos (theta));
|
||||||
|
theta += delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void calc_amsq(AMSQ a)
|
||||||
|
{
|
||||||
|
// signal averaging
|
||||||
|
a->trigsig = (double *)malloc0(a->size * sizeof(complex));
|
||||||
|
a->avm = exp(-1.0 / (a->rate * a->avtau));
|
||||||
|
a->onem_avm = 1.0 - a->avm;
|
||||||
|
a->avsig = 0.0;
|
||||||
|
// level change
|
||||||
|
a->ntup = (int)(a->tup * a->rate);
|
||||||
|
a->ntdown = (int)(a->tdown * a->rate);
|
||||||
|
a->cup = (double *)malloc0((a->ntup + 1) * sizeof(double));
|
||||||
|
a->cdown = (double *)malloc0((a->ntdown + 1) * sizeof(double));
|
||||||
|
compute_slews(a);
|
||||||
|
// control
|
||||||
|
a->state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decalc_amsq (AMSQ a)
|
||||||
|
{
|
||||||
|
_aligned_free (a->cdown);
|
||||||
|
_aligned_free (a->cup);
|
||||||
|
_aligned_free (a->trigsig);
|
||||||
|
}
|
||||||
|
|
||||||
|
AMSQ create_amsq (int run, int size, double* in, double* out, double* trigger, int rate, double avtau,
|
||||||
|
double tup, double tdown, double tail_thresh, double unmute_thresh, double min_tail, double max_tail, double muted_gain)
|
||||||
|
{
|
||||||
|
AMSQ a = (AMSQ) malloc0 (sizeof (amsq));
|
||||||
|
a->run = run;
|
||||||
|
a->size = size;
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
a->rate = (double)rate;
|
||||||
|
a->muted_gain = muted_gain;
|
||||||
|
a->trigger = trigger;
|
||||||
|
a->avtau = avtau;
|
||||||
|
a->tup = tup;
|
||||||
|
a->tdown = tdown;
|
||||||
|
a->tail_thresh = tail_thresh;
|
||||||
|
a->unmute_thresh = unmute_thresh;
|
||||||
|
a->min_tail = min_tail;
|
||||||
|
a->max_tail = max_tail;
|
||||||
|
calc_amsq (a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_amsq (AMSQ a)
|
||||||
|
{
|
||||||
|
decalc_amsq (a);
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_amsq (AMSQ a)
|
||||||
|
{
|
||||||
|
memset (a->trigsig, 0, a->size * sizeof (complex));
|
||||||
|
a->avsig = 0.0;
|
||||||
|
a->state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _amsqstate
|
||||||
|
{
|
||||||
|
MUTED,
|
||||||
|
INCREASE,
|
||||||
|
UNMUTED,
|
||||||
|
TAIL,
|
||||||
|
DECREASE
|
||||||
|
};
|
||||||
|
|
||||||
|
void xamsq (AMSQ a)
|
||||||
|
{
|
||||||
|
if (a->run)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double sig, siglimit;
|
||||||
|
for (i = 0; i < a->size; i++)
|
||||||
|
{
|
||||||
|
sig = sqrt (a->trigsig[2 * i + 0] * a->trigsig[2 * i + 0] + a->trigsig[2 * i + 1] * a->trigsig[2 * i + 1]);
|
||||||
|
a->avsig = a->avm * a->avsig + a->onem_avm * sig;
|
||||||
|
switch (a->state)
|
||||||
|
{
|
||||||
|
case MUTED:
|
||||||
|
if (a->avsig > a->unmute_thresh)
|
||||||
|
{
|
||||||
|
a->state = INCREASE;
|
||||||
|
a->count = a->ntup;
|
||||||
|
}
|
||||||
|
a->out[2 * i + 0] = a->muted_gain * a->in[2 * i + 0];
|
||||||
|
a->out[2 * i + 1] = a->muted_gain * a->in[2 * i + 1];
|
||||||
|
break;
|
||||||
|
case INCREASE:
|
||||||
|
a->out[2 * i + 0] = a->in[2 * i + 0] * a->cup[a->ntup - a->count];
|
||||||
|
a->out[2 * i + 1] = a->in[2 * i + 1] * a->cup[a->ntup - a->count];
|
||||||
|
if (a->count-- == 0)
|
||||||
|
a->state = UNMUTED;
|
||||||
|
break;
|
||||||
|
case UNMUTED:
|
||||||
|
if (a->avsig < a->tail_thresh)
|
||||||
|
{
|
||||||
|
a->state = TAIL;
|
||||||
|
if ((siglimit = a->avsig) > 1.0) siglimit = 1.0;
|
||||||
|
a->count = (int)((a->min_tail + (a->max_tail - a->min_tail) * (1.0 - siglimit)) * a->rate);
|
||||||
|
}
|
||||||
|
a->out[2 * i + 0] = a->in[2 * i + 0];
|
||||||
|
a->out[2 * i + 1] = a->in[2 * i + 1];
|
||||||
|
break;
|
||||||
|
case TAIL:
|
||||||
|
a->out[2 * i + 0] = a->in[2 * i + 0];
|
||||||
|
a->out[2 * i + 1] = a->in[2 * i + 1];
|
||||||
|
if (a->avsig > a->unmute_thresh)
|
||||||
|
a->state = UNMUTED;
|
||||||
|
else if (a->count-- == 0)
|
||||||
|
{
|
||||||
|
a->state = DECREASE;
|
||||||
|
a->count = a->ntdown;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DECREASE:
|
||||||
|
a->out[2 * i + 0] = a->in[2 * i + 0] * a->cdown[a->ntdown - a->count];
|
||||||
|
a->out[2 * i + 1] = a->in[2 * i + 1] * a->cdown[a->ntdown - a->count];
|
||||||
|
if (a->count-- == 0)
|
||||||
|
a->state = MUTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a->in != a->out)
|
||||||
|
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void xamsqcap (AMSQ a)
|
||||||
|
{
|
||||||
|
memcpy (a->trigsig, a->trigger, a->size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_amsq (AMSQ a, double* in, double* out, double* trigger)
|
||||||
|
{
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
a->trigger = trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_amsq (AMSQ a, int rate)
|
||||||
|
{
|
||||||
|
decalc_amsq (a);
|
||||||
|
a->rate = rate;
|
||||||
|
calc_amsq (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_amsq (AMSQ a, int size)
|
||||||
|
{
|
||||||
|
decalc_amsq (a);
|
||||||
|
a->size = size;
|
||||||
|
calc_amsq (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* RXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAAMSQRun (int channel, int run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].amsq.p->run = run;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAAMSQThreshold (int channel, double threshold)
|
||||||
|
{
|
||||||
|
double thresh = pow (10.0, threshold / 20.0);
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].amsq.p->tail_thresh = 0.9 * thresh;
|
||||||
|
rxa[channel].amsq.p->unmute_thresh = thresh;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAAMSQMaxTail (int channel, double tail)
|
||||||
|
{
|
||||||
|
AMSQ a;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a = rxa[channel].amsq.p;
|
||||||
|
if (tail < a->min_tail) tail = a->min_tail;
|
||||||
|
a->max_tail = tail;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* TXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXAAMSQRun (int channel, int run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
txa[channel].amsq.p->run = run;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXAAMSQMutedGain (int channel, double dBlevel)
|
||||||
|
{ // dBlevel is negative
|
||||||
|
AMSQ a;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a = txa[channel].amsq.p;
|
||||||
|
a->muted_gain = pow (10.0, dBlevel / 20.0);
|
||||||
|
compute_slews(a);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXAAMSQThreshold (int channel, double threshold)
|
||||||
|
{
|
||||||
|
double thresh = pow (10.0, threshold / 20.0);
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
txa[channel].amsq.p->tail_thresh = 0.9 * thresh;
|
||||||
|
txa[channel].amsq.p->unmute_thresh = thresh;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
/* amsq.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef _amsq_h
|
||||||
|
#define _amsq_h
|
||||||
|
|
||||||
|
typedef struct _amsq
|
||||||
|
{
|
||||||
|
int run; // 0 if squelch system is OFF; 1 if it's ON
|
||||||
|
int size; // size of input/output buffers
|
||||||
|
double* in; // squelch input signal buffer
|
||||||
|
double* out; // squelch output signal buffer
|
||||||
|
double* trigger; // pointer to trigger data source
|
||||||
|
double* trigsig; // buffer containing trigger signal
|
||||||
|
double rate; // sample rate
|
||||||
|
double avtau; // time constant for averaging noise
|
||||||
|
double avm;
|
||||||
|
double onem_avm;
|
||||||
|
double avsig;
|
||||||
|
int state; // state machine control
|
||||||
|
int count;
|
||||||
|
double tup;
|
||||||
|
double tdown;
|
||||||
|
int ntup;
|
||||||
|
int ntdown;
|
||||||
|
double* cup;
|
||||||
|
double* cdown;
|
||||||
|
double tail_thresh;
|
||||||
|
double unmute_thresh;
|
||||||
|
double min_tail;
|
||||||
|
double max_tail;
|
||||||
|
double muted_gain;
|
||||||
|
} amsq, *AMSQ;
|
||||||
|
|
||||||
|
extern AMSQ create_amsq (int run, int size, double* in, double* out, double* trigger, int rate, double avtau, double tup, double tdown, double tail_thresh, double unmute_thresh, double min_tail, double max_tail, double muted_gain);
|
||||||
|
|
||||||
|
extern void destroy_amsq (AMSQ a);
|
||||||
|
|
||||||
|
extern void flush_amsq (AMSQ a);
|
||||||
|
|
||||||
|
extern void xamsq (AMSQ a);
|
||||||
|
|
||||||
|
extern void xamsqcap (AMSQ a);
|
||||||
|
|
||||||
|
extern void setBuffers_amsq (AMSQ a, double* in, double* out, double* trigger);
|
||||||
|
|
||||||
|
extern void setSamplerate_amsq (AMSQ a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_amsq (AMSQ a, int size);
|
||||||
|
|
||||||
|
// RXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAAMSQRun (int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAAMSQThreshold (int channel, double threshold);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAAMSQMaxTail (int channel, double tail);
|
||||||
|
|
||||||
|
// TXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAAMSQRun (int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAAMSQMutedGain (int channel, double dBlevel);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAAMSQThreshold (int channel, double threshold);
|
||||||
|
|
||||||
|
#endif
|
||||||
+1686
File diff suppressed because it is too large
Load Diff
+195
@@ -0,0 +1,195 @@
|
|||||||
|
/* analyzer.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Spectrum Analyzer
|
||||||
|
used in conjunction with software-defined-radio hardware.
|
||||||
|
|
||||||
|
Copyright (C) 2012, 2013, 2014, 2016, 2023 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef _analyzer_h
|
||||||
|
#define _analyzer_h
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
typedef struct _dp
|
||||||
|
{
|
||||||
|
int max_size; // maximum fft size to be used
|
||||||
|
int max_num_fft; // maximum number of LO positions per sub-span to be used
|
||||||
|
int max_stitch; // maximum number of sub-spans to be concatenated
|
||||||
|
// NOTE: max_size, max_num_fft, and max_stitch MUST BE <= THE
|
||||||
|
// CORRESPONDING VALUES IN <analyzer.h>!!
|
||||||
|
int num_fft; // current number of ffts in use
|
||||||
|
int num_pixout; // current number of detector/averages/pixel value outputs
|
||||||
|
int size; // current size of fft input sample vector
|
||||||
|
int out_size; // current size of fft output vector
|
||||||
|
int window_type; // type of the window function to be applied
|
||||||
|
int overlap; // number of samples re-used per fft, range 0 to size-1
|
||||||
|
int flip[dMAX_NUM_FFT]; // 0 for low-side LO => do NOT flip; 1 for high-side LO => FLIP
|
||||||
|
int clip; // number of bins to clip off on EACH end of the sub-span fft
|
||||||
|
// ASSUMES size/2 IS AN EVEN NUMBER!!!
|
||||||
|
double fsclipL; // number of intervals to clip off the lower end of the TOTAL SPAN
|
||||||
|
double fsclipH; // number of intervals to clip off the upper end of the TOTAL SPAN
|
||||||
|
int fscL; // fsclipL modulo (out_size - 2 * clip)
|
||||||
|
int fscH; // fsclipH modulo (out_size - 2 * clip)
|
||||||
|
int begin_ss; // number of first sub-span that is NOT completely clipped off
|
||||||
|
int end_ss; // number of last sub-span that is NOT completely clipped off
|
||||||
|
int ss_bins[dMAX_STITCH]; // number of bins delivered by eliminate()/Celiminate in each sub-span
|
||||||
|
volatile LONG input_busy[dMAX_STITCH][dMAX_NUM_FFT];
|
||||||
|
int num_pixels; // number of pixels requested
|
||||||
|
int num_stitch; // number of results to be stitched together to generate the pixel frame
|
||||||
|
unsigned long long stitch_flag;
|
||||||
|
int spec_flag[dMAX_STITCH]; // flags showing if all ffts for a sub-span are done so elimination can proceed
|
||||||
|
double pix_per_bin; // number of pixels per fft bin, note that this is fractional, not integral
|
||||||
|
double det_offset; // offset needed in detector
|
||||||
|
double bin_per_pix; // number of fft bins per pixel, this is fractional and != 1.0/pix_per_bin
|
||||||
|
double scale; // output amplitude scale factor
|
||||||
|
double PiAlpha; // parameter for Kaiser window function
|
||||||
|
|
||||||
|
int cal_set; // specifies which set of calibration data to use
|
||||||
|
double f_min; // frequency at first pixel (for calibration)
|
||||||
|
double f_max; // frequency at last pixel (for calibration)
|
||||||
|
int cal_changed; // flag to indicate that the calibration data has changed
|
||||||
|
|
||||||
|
double *window; // pointer to buffer to hold window coefficients
|
||||||
|
double *result[dMAX_STITCH]; // pointers to buffer to hold elimination results for each sub-span
|
||||||
|
dOUTREAL *pixels[dMAX_PIXOUTS][dNUM_PIXEL_BUFFS]; // pointers pixel output buffers
|
||||||
|
double *t_pixels[dMAX_PIXOUTS]; // pointer to temporary pixel buffer //pointer to temporary pixel buffer for non-averaged data
|
||||||
|
int w_pix_buff[dMAX_PIXOUTS]; // number of pixel buffer owned by writing process
|
||||||
|
int r_pix_buff[dMAX_PIXOUTS]; // number of pixel buffer owned by reading process
|
||||||
|
int last_pix_buff[dMAX_PIXOUTS]; // number of the last pixel buffer written
|
||||||
|
volatile LONG pb_ready[dMAX_PIXOUTS][dNUM_PIXEL_BUFFS]; // if value is 0, this data has already been read; 1 = fresh data to read
|
||||||
|
int num_average[dMAX_PIXOUTS]; // number of spans to average to create the pixels
|
||||||
|
int avail_frames[dMAX_PIXOUTS]; // number of pixel frames currently available to average
|
||||||
|
int av_in_idx[dMAX_PIXOUTS]; // input index in averaging pixel buffer ring
|
||||||
|
int av_out_idx[dMAX_PIXOUTS]; // output index in averaging pixel buffer ring
|
||||||
|
double *av_sum[dMAX_PIXOUTS]; // pointer to sum buffer for averaging
|
||||||
|
double *av_buff[dMAX_PIXOUTS][dMAX_AVERAGE]; // pointers to ring of buffers to hold pixel frames for averaging
|
||||||
|
double *pre_av_out;
|
||||||
|
int av_mode[dMAX_PIXOUTS];
|
||||||
|
double av_backmult[dMAX_PIXOUTS]; // back multiplier for weighted averaging
|
||||||
|
double *cd; // pointer to amplitude calibration buffer
|
||||||
|
int n_freqs[dMAX_CAL_SETS]; // number of frequencies in each calibration set
|
||||||
|
double *freqs[dMAX_CAL_SETS]; // pointers to vectors of calibration frequencies
|
||||||
|
double (*ac3[dMAX_CAL_SETS][dMAX_M]); // pointers to amplitude interpolant coefficients
|
||||||
|
double (*ac2[dMAX_CAL_SETS][dMAX_M]);
|
||||||
|
double (*ac1[dMAX_CAL_SETS][dMAX_M]);
|
||||||
|
double (*ac0[dMAX_CAL_SETS][dMAX_M]);
|
||||||
|
|
||||||
|
fftw_plan plan[dMAX_STITCH][dMAX_NUM_FFT]; // fftw plans
|
||||||
|
fftw_plan Cplan[dMAX_STITCH][dMAX_NUM_FFT];
|
||||||
|
double *fft_in[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to fftw real input vectors
|
||||||
|
fftw_complex *Cfft_in[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to fftw complex input vectors
|
||||||
|
fftw_complex *fft_out[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to fftw complex output vectors
|
||||||
|
volatile LONG *pnum_threads; // pointer to current number of active worker threads
|
||||||
|
int stop; // when set, fft threads will be returned to the pool
|
||||||
|
int end_dispatcher; // set this flag to one to destroy the dispatcher thread
|
||||||
|
volatile int dispatcher; // one if the dispatcher thread is alive & active
|
||||||
|
int ss; // sub-span being processed
|
||||||
|
int LO; // LO (within current sub-span) being processed
|
||||||
|
int flag;
|
||||||
|
int have_samples[dMAX_STITCH][dMAX_NUM_FFT]; // number of unused samples remaining in a buffer
|
||||||
|
int type; // 0 for REAL, 1 for COMPLEX
|
||||||
|
int incr; // size - overlap
|
||||||
|
int buff_size; // amount of data to be stored each time an input buffer is opened and closed = JanusAudio/BlockSize
|
||||||
|
dINREAL* I_samples[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to current input position in I/Q buffers
|
||||||
|
dINREAL* Q_samples[dMAX_STITCH][dMAX_NUM_FFT];
|
||||||
|
int bsize; // size of I_samples[][] and Q_samples[][] (number of samples they hold)
|
||||||
|
int IQout_index[dMAX_STITCH][dMAX_NUM_FFT]; // current output index for I_samples[ss][LO] and Q_samples[ss][LO]
|
||||||
|
int IQO_idx[dMAX_STITCH][dMAX_NUM_FFT];
|
||||||
|
int IQin_index[dMAX_STITCH][dMAX_NUM_FFT]; // current input index for I_samples[ss][LO] and Q_samples[ss][LO]
|
||||||
|
volatile LONG buff_ready[dMAX_STITCH][dMAX_NUM_FFT]; // 1 if buffer ready to read; 0 if needs to be filled
|
||||||
|
int max_writeahead; // max allowed input samples ahead of where reading output samples
|
||||||
|
|
||||||
|
volatile LONG snap[dMAX_STITCH][dMAX_NUM_FFT]; // set to 1 to allow a snap of raw spectrum data
|
||||||
|
HANDLE hSnapEvent[dMAX_STITCH][dMAX_NUM_FFT]; // mutex handles; mutexes will be used to signal a snap is complete
|
||||||
|
double *snap_buff[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to buffers for the snap
|
||||||
|
|
||||||
|
CRITICAL_SECTION PB_ControlsSection[dMAX_PIXOUTS];
|
||||||
|
CRITICAL_SECTION SetAnalyzerSection;
|
||||||
|
CRITICAL_SECTION BufferControlSection[dMAX_STITCH][dMAX_NUM_FFT];
|
||||||
|
CRITICAL_SECTION StitchSection;
|
||||||
|
CRITICAL_SECTION EliminateSection[dMAX_STITCH];
|
||||||
|
CRITICAL_SECTION ResampleSection;
|
||||||
|
|
||||||
|
int det_type[dMAX_PIXOUTS]; // detector type
|
||||||
|
double inv_coherent_gain;
|
||||||
|
double inherent_power_gain;
|
||||||
|
double inv_enb;
|
||||||
|
double norm_oneHz; // dB factor to normalize to one Hz bandwidth
|
||||||
|
int sample_rate; // sample rate; used for normalization calculations
|
||||||
|
int normalize[dMAX_PIXOUTS];
|
||||||
|
} dp, *DP;
|
||||||
|
|
||||||
|
extern DP pdisp[];
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void CreateAnalyzer ( int disp,
|
||||||
|
int *success,
|
||||||
|
char *app_data_path);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void XCreateAnalyzer ( int disp,
|
||||||
|
int *success, //writes '0' to success if all went well, <0 if mem alloc failed
|
||||||
|
int m_size, //maximum fft size to be used
|
||||||
|
int m_LO, //maximum number of LO positions per subspan
|
||||||
|
int m_stitch, //maximum number of subspans to be concatenated
|
||||||
|
char *app_data_path
|
||||||
|
);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void DestroyAnalyzer(int disp);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void SetCalibration ( int disp,
|
||||||
|
int set_num, //identifier for this calibration data set
|
||||||
|
int n_points, //number of calibration points in the set
|
||||||
|
double (*cal)[dMAX_M+1] //pointer to the calibration table, first
|
||||||
|
);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void OpenBuffer(int disp, int ss, int LO, void **Ipointer, void **Qpointer);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void CloseBuffer(int disp, int ss, int LO);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void Spectrum(int disp, int ss, int LO, dINREAL* pI, dINREAL* pQ);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void Spectrum2(int run, int disp, int ss, int LO, dINREAL* pbuff);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void Spectrum0(int run, int disp, int ss, int LO, double* pbuff);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void SnapSpectrum( int disp,
|
||||||
|
int ss,
|
||||||
|
int LO,
|
||||||
|
double *snap_buff);
|
||||||
|
|
||||||
|
extern __declspec( dllexport )
|
||||||
|
void SnapSpectrumTimeout (int disp,
|
||||||
|
int ss,
|
||||||
|
int LO,
|
||||||
|
double* snap_buff,
|
||||||
|
DWORD timeout,
|
||||||
|
int* flag);
|
||||||
|
|
||||||
|
#endif
|
||||||
+239
@@ -0,0 +1,239 @@
|
|||||||
|
/* anf.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
ANF create_anf (
|
||||||
|
int run,
|
||||||
|
int position,
|
||||||
|
int buff_size,
|
||||||
|
double *in_buff,
|
||||||
|
double *out_buff,
|
||||||
|
int dline_size,
|
||||||
|
int n_taps,
|
||||||
|
int delay,
|
||||||
|
double two_mu,
|
||||||
|
double gamma,
|
||||||
|
|
||||||
|
double lidx,
|
||||||
|
double lidx_min,
|
||||||
|
double lidx_max,
|
||||||
|
double ngamma,
|
||||||
|
double den_mult,
|
||||||
|
double lincr,
|
||||||
|
double ldecr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ANF a = (ANF) malloc0 (sizeof(anf));
|
||||||
|
a->run = run;
|
||||||
|
a->position = position;
|
||||||
|
a->buff_size = buff_size;
|
||||||
|
a->in_buff = in_buff;
|
||||||
|
a->out_buff = out_buff;
|
||||||
|
a->dline_size = dline_size;
|
||||||
|
a->mask = dline_size - 1;
|
||||||
|
a->n_taps = n_taps;
|
||||||
|
a->delay = delay;
|
||||||
|
a->two_mu = two_mu;
|
||||||
|
a->gamma = gamma;
|
||||||
|
a->in_idx = 0;
|
||||||
|
a->lidx = lidx;
|
||||||
|
a->lidx_min = lidx_min;
|
||||||
|
a->lidx_max = lidx_max;
|
||||||
|
a->ngamma = ngamma;
|
||||||
|
a->den_mult = den_mult;
|
||||||
|
a->lincr = lincr;
|
||||||
|
a->ldecr = ldecr;
|
||||||
|
|
||||||
|
memset (a->d, 0, sizeof(double) * ANF_DLINE_SIZE);
|
||||||
|
memset (a->w, 0, sizeof(double) * ANF_DLINE_SIZE);
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_anf (ANF a)
|
||||||
|
{
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xanf(ANF a, int position)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
double c0, c1;
|
||||||
|
double y, error, sigma, inv_sigp;
|
||||||
|
double nel, nev;
|
||||||
|
if (a->run && (a->position == position))
|
||||||
|
{
|
||||||
|
for (i = 0; i < a->buff_size; i++)
|
||||||
|
{
|
||||||
|
a->d[a->in_idx] = a->in_buff[2 * i + 0];
|
||||||
|
|
||||||
|
y = 0;
|
||||||
|
sigma = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < a->n_taps; j++)
|
||||||
|
{
|
||||||
|
idx = (a->in_idx + j + a->delay) & a->mask;
|
||||||
|
y += a->w[j] * a->d[idx];
|
||||||
|
sigma += a->d[idx] * a->d[idx];
|
||||||
|
}
|
||||||
|
inv_sigp = 1.0 / (sigma + 1e-10);
|
||||||
|
error = a->d[a->in_idx] - y;
|
||||||
|
|
||||||
|
a->out_buff[2 * i + 0] = error;
|
||||||
|
a->out_buff[2 * i + 1] = 0.0;
|
||||||
|
|
||||||
|
if((nel = error * (1.0 - a->two_mu * sigma * inv_sigp)) < 0.0) nel = -nel;
|
||||||
|
if((nev = a->d[a->in_idx] - (1.0 - a->two_mu * a->ngamma) * y - a->two_mu * error * sigma * inv_sigp) < 0.0) nev = -nev;
|
||||||
|
if (nev < nel)
|
||||||
|
{
|
||||||
|
if ((a->lidx += a->lincr) > a->lidx_max) a->lidx = a->lidx_max;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((a->lidx -= a->ldecr) < a->lidx_min) a->lidx = a->lidx_min;
|
||||||
|
}
|
||||||
|
a->ngamma = a->gamma * (a->lidx * a->lidx) * (a->lidx * a->lidx) * a->den_mult;
|
||||||
|
|
||||||
|
c0 = 1.0 - a->two_mu * a->ngamma;
|
||||||
|
c1 = a->two_mu * error * inv_sigp;
|
||||||
|
|
||||||
|
for (j = 0; j < a->n_taps; j++)
|
||||||
|
{
|
||||||
|
idx = (a->in_idx + j + a->delay) & a->mask;
|
||||||
|
a->w[j] = c0 * a->w[j] + c1 * a->d[idx];
|
||||||
|
}
|
||||||
|
a->in_idx = (a->in_idx + a->mask) & a->mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a->in_buff != a->out_buff)
|
||||||
|
memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_anf (ANF a)
|
||||||
|
{
|
||||||
|
memset (a->d, 0, sizeof(double) * ANF_DLINE_SIZE);
|
||||||
|
memset (a->w, 0, sizeof(double) * ANF_DLINE_SIZE);
|
||||||
|
a->in_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_anf (ANF a, double* in, double* out)
|
||||||
|
{
|
||||||
|
a->in_buff = in;
|
||||||
|
a->out_buff = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_anf (ANF a, int rate)
|
||||||
|
{
|
||||||
|
flush_anf (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_anf (ANF a, int size)
|
||||||
|
{
|
||||||
|
a->buff_size = size;
|
||||||
|
flush_anf (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* RXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANFRun (int channel, int run)
|
||||||
|
{
|
||||||
|
ANF a = rxa[channel].anf.p;
|
||||||
|
if (a->run != run)
|
||||||
|
{
|
||||||
|
RXAbp1Check (channel, rxa[channel].amd.p->run, rxa[channel].snba.p->run,
|
||||||
|
rxa[channel].emnr.p->run, run, rxa[channel].anr.p->run);
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->run = run;
|
||||||
|
RXAbp1Set (channel);
|
||||||
|
flush_anf (a);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANFVals (int channel, int taps, int delay, double gain, double leakage)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anf.p->n_taps = taps;
|
||||||
|
rxa[channel].anf.p->delay = delay;
|
||||||
|
rxa[channel].anf.p->two_mu = gain; //try two_mu = 1e-4
|
||||||
|
rxa[channel].anf.p->gamma = leakage; //try gamma = 0.10
|
||||||
|
flush_anf (rxa[channel].anf.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANFTaps (int channel, int taps)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anf.p->n_taps = taps;
|
||||||
|
flush_anf (rxa[channel].anf.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANFDelay (int channel, int delay)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anf.p->delay = delay;
|
||||||
|
flush_anf (rxa[channel].anf.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANFGain (int channel, double gain)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anf.p->two_mu = gain;
|
||||||
|
flush_anf (rxa[channel].anf.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANFLeakage (int channel, double leakage)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anf.p->gamma = leakage;
|
||||||
|
flush_anf (rxa[channel].anf.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANFPosition (int channel, int position)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anf.p->position = position;
|
||||||
|
rxa[channel].bp1.p->position = position;
|
||||||
|
flush_anf (rxa[channel].anf.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
/* anf.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _anf_h
|
||||||
|
#define _anf_h
|
||||||
|
|
||||||
|
#define ANF_DLINE_SIZE 2048
|
||||||
|
|
||||||
|
typedef struct _anf
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int position;
|
||||||
|
int buff_size;
|
||||||
|
double *in_buff;
|
||||||
|
double *out_buff;
|
||||||
|
int dline_size;
|
||||||
|
int mask;
|
||||||
|
int n_taps;
|
||||||
|
int delay;
|
||||||
|
double two_mu;
|
||||||
|
double gamma;
|
||||||
|
double d [ANF_DLINE_SIZE];
|
||||||
|
double w [ANF_DLINE_SIZE];
|
||||||
|
int in_idx;
|
||||||
|
|
||||||
|
double lidx;
|
||||||
|
double lidx_min;
|
||||||
|
double lidx_max;
|
||||||
|
double ngamma;
|
||||||
|
double den_mult;
|
||||||
|
double lincr;
|
||||||
|
double ldecr;
|
||||||
|
} anf, *ANF;
|
||||||
|
|
||||||
|
extern ANF create_anf (
|
||||||
|
int run,
|
||||||
|
int position,
|
||||||
|
int buff_size,
|
||||||
|
double *in_buff,
|
||||||
|
double *out_buff,
|
||||||
|
int dline_size,
|
||||||
|
int n_taps,
|
||||||
|
int delay,
|
||||||
|
double two_mu,
|
||||||
|
double gamma,
|
||||||
|
|
||||||
|
double lidx,
|
||||||
|
double lidx_min,
|
||||||
|
double lidx_max,
|
||||||
|
double ngamma,
|
||||||
|
double den_mult,
|
||||||
|
double lincr,
|
||||||
|
double ldecr
|
||||||
|
);
|
||||||
|
|
||||||
|
extern void destroy_anf (ANF a);
|
||||||
|
|
||||||
|
extern void flush_anf (ANF a);
|
||||||
|
|
||||||
|
extern void xanf (ANF a, int position);
|
||||||
|
|
||||||
|
extern void setBuffers_anf (ANF a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_anf (ANF a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_anf (ANF a, int size);
|
||||||
|
|
||||||
|
// RXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANFRun (int channel, int setit);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANFVals (int channel, int taps, int delay, double gain, double leakage);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANFTaps (int channel, int taps);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANFDelay (int channel, int delay);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANFGain (int channel, double gain);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANFLeakage (int channel, double leakage);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANFPosition (int channel, int position);
|
||||||
|
|
||||||
|
#endif
|
||||||
+238
@@ -0,0 +1,238 @@
|
|||||||
|
/* anr.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
ANR create_anr (
|
||||||
|
int run,
|
||||||
|
int position,
|
||||||
|
int buff_size,
|
||||||
|
double *in_buff,
|
||||||
|
double *out_buff,
|
||||||
|
int dline_size,
|
||||||
|
int n_taps,
|
||||||
|
int delay,
|
||||||
|
double two_mu,
|
||||||
|
double gamma,
|
||||||
|
|
||||||
|
double lidx,
|
||||||
|
double lidx_min,
|
||||||
|
double lidx_max,
|
||||||
|
double ngamma,
|
||||||
|
double den_mult,
|
||||||
|
double lincr,
|
||||||
|
double ldecr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ANR a = (ANR) malloc0 (sizeof(anr));
|
||||||
|
a->run = run;
|
||||||
|
a->position = position;
|
||||||
|
a->buff_size = buff_size;
|
||||||
|
a->in_buff = in_buff;
|
||||||
|
a->out_buff = out_buff;
|
||||||
|
a->dline_size = dline_size;
|
||||||
|
a->mask = dline_size - 1;
|
||||||
|
a->n_taps = n_taps;
|
||||||
|
a->delay = delay;
|
||||||
|
a->two_mu = two_mu;
|
||||||
|
a->gamma = gamma;
|
||||||
|
a->in_idx = 0;
|
||||||
|
a->lidx = lidx;
|
||||||
|
a->lidx_min = lidx_min;
|
||||||
|
a->lidx_max = lidx_max;
|
||||||
|
a->ngamma = ngamma;
|
||||||
|
a->den_mult = den_mult;
|
||||||
|
a->lincr = lincr;
|
||||||
|
a->ldecr = ldecr;
|
||||||
|
|
||||||
|
memset (a->d, 0, sizeof(double) * ANR_DLINE_SIZE);
|
||||||
|
memset (a->w, 0, sizeof(double) * ANR_DLINE_SIZE);
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_anr (ANR a)
|
||||||
|
{
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xanr (ANR a, int position)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
double c0, c1;
|
||||||
|
double y, error, sigma, inv_sigp;
|
||||||
|
double nel, nev;
|
||||||
|
if (a->run && (a->position == position))
|
||||||
|
{
|
||||||
|
for (i = 0; i < a->buff_size; i++)
|
||||||
|
{
|
||||||
|
a->d[a->in_idx] = a->in_buff[2 * i + 0];
|
||||||
|
|
||||||
|
y = 0;
|
||||||
|
sigma = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < a->n_taps; j++)
|
||||||
|
{
|
||||||
|
idx = (a->in_idx + j + a->delay) & a->mask;
|
||||||
|
y += a->w[j] * a->d[idx];
|
||||||
|
sigma += a->d[idx] * a->d[idx];
|
||||||
|
}
|
||||||
|
inv_sigp = 1.0 / (sigma + 1e-10);
|
||||||
|
error = a->d[a->in_idx] - y;
|
||||||
|
|
||||||
|
a->out_buff[2 * i + 0] = y;
|
||||||
|
a->out_buff[2 * i + 1] = 0.0;
|
||||||
|
|
||||||
|
if((nel = error * (1.0 - a->two_mu * sigma * inv_sigp)) < 0.0) nel = -nel;
|
||||||
|
if((nev = a->d[a->in_idx] - (1.0 - a->two_mu * a->ngamma) * y - a->two_mu * error * sigma * inv_sigp) < 0.0) nev = -nev;
|
||||||
|
if (nev < nel)
|
||||||
|
{
|
||||||
|
if ((a->lidx += a->lincr) > a->lidx_max) a->lidx = a->lidx_max;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((a->lidx -= a->ldecr) < a->lidx_min) a->lidx = a->lidx_min;
|
||||||
|
}
|
||||||
|
a->ngamma = a->gamma * (a->lidx * a->lidx) * (a->lidx * a->lidx) * a->den_mult;
|
||||||
|
|
||||||
|
c0 = 1.0 - a->two_mu * a->ngamma;
|
||||||
|
c1 = a->two_mu * error * inv_sigp;
|
||||||
|
|
||||||
|
for (j = 0; j < a->n_taps; j++)
|
||||||
|
{
|
||||||
|
idx = (a->in_idx + j + a->delay) & a->mask;
|
||||||
|
a->w[j] = c0 * a->w[j] + c1 * a->d[idx];
|
||||||
|
}
|
||||||
|
a->in_idx = (a->in_idx + a->mask) & a->mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a->in_buff != a->out_buff)
|
||||||
|
memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_anr (ANR a)
|
||||||
|
{
|
||||||
|
memset (a->d, 0, sizeof(double) * ANR_DLINE_SIZE);
|
||||||
|
memset (a->w, 0, sizeof(double) * ANR_DLINE_SIZE);
|
||||||
|
a->in_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_anr (ANR a, double* in, double* out)
|
||||||
|
{
|
||||||
|
a->in_buff = in;
|
||||||
|
a->out_buff = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_anr (ANR a, int rate)
|
||||||
|
{
|
||||||
|
flush_anr(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_anr (ANR a, int size)
|
||||||
|
{
|
||||||
|
a->buff_size = size;
|
||||||
|
flush_anr(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* RXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANRRun (int channel, int run)
|
||||||
|
{
|
||||||
|
ANR a = rxa[channel].anr.p;
|
||||||
|
if (a->run != run)
|
||||||
|
{
|
||||||
|
RXAbp1Check (channel, rxa[channel].amd.p->run, rxa[channel].snba.p->run,
|
||||||
|
rxa[channel].emnr.p->run, rxa[channel].anf.p->run, run);
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->run = run;
|
||||||
|
RXAbp1Set (channel);
|
||||||
|
flush_anr (a);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANRVals (int channel, int taps, int delay, double gain, double leakage)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anr.p->n_taps = taps;
|
||||||
|
rxa[channel].anr.p->delay = delay;
|
||||||
|
rxa[channel].anr.p->two_mu = gain;
|
||||||
|
rxa[channel].anr.p->gamma = leakage;
|
||||||
|
flush_anr (rxa[channel].anr.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANRTaps (int channel, int taps)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anr.p->n_taps = taps;
|
||||||
|
flush_anr (rxa[channel].anr.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANRDelay (int channel, int delay)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anr.p->delay = delay;
|
||||||
|
flush_anr (rxa[channel].anr.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANRGain (int channel, double gain)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anr.p->two_mu = gain;
|
||||||
|
flush_anr (rxa[channel].anr.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANRLeakage (int channel, double leakage)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anr.p->gamma = leakage;
|
||||||
|
flush_anr (rxa[channel].anr.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXAANRPosition (int channel, int position)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].anr.p->position = position;
|
||||||
|
rxa[channel].bp1.p->position = position;
|
||||||
|
flush_anr (rxa[channel].anr.p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
/* anr.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _anr_h
|
||||||
|
#define _anr_h
|
||||||
|
|
||||||
|
#define ANR_DLINE_SIZE 2048
|
||||||
|
|
||||||
|
typedef struct _anr
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int position;
|
||||||
|
int buff_size;
|
||||||
|
double *in_buff;
|
||||||
|
double *out_buff;
|
||||||
|
int dline_size;
|
||||||
|
int mask;
|
||||||
|
int n_taps;
|
||||||
|
int delay;
|
||||||
|
double two_mu;
|
||||||
|
double gamma;
|
||||||
|
double d [ANR_DLINE_SIZE];
|
||||||
|
double w [ANR_DLINE_SIZE];
|
||||||
|
int in_idx;
|
||||||
|
|
||||||
|
double lidx;
|
||||||
|
double lidx_min;
|
||||||
|
double lidx_max;
|
||||||
|
double ngamma;
|
||||||
|
double den_mult;
|
||||||
|
double lincr;
|
||||||
|
double ldecr;
|
||||||
|
} anr, *ANR;
|
||||||
|
|
||||||
|
extern ANR create_anr (
|
||||||
|
int run,
|
||||||
|
int position,
|
||||||
|
int buff_size,
|
||||||
|
double *in_buff,
|
||||||
|
double *out_buff,
|
||||||
|
int dline_size,
|
||||||
|
int n_taps,
|
||||||
|
int delay,
|
||||||
|
double two_mu,
|
||||||
|
double gamma,
|
||||||
|
|
||||||
|
double lidx,
|
||||||
|
double lidx_min,
|
||||||
|
double lidx_max,
|
||||||
|
double ngamma,
|
||||||
|
double den_mult,
|
||||||
|
double lincr,
|
||||||
|
double ldecr
|
||||||
|
);
|
||||||
|
|
||||||
|
extern void destroy_anr (ANR a);
|
||||||
|
|
||||||
|
extern void flush_anr (ANR a);
|
||||||
|
|
||||||
|
extern void xanr (ANR a, int position);
|
||||||
|
|
||||||
|
extern void setBuffers_anr (ANR a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_anr (ANR a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_anr (ANR a, int size);
|
||||||
|
|
||||||
|
// RXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANRRun (int channel, int setit);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANRVals (int channel, int taps, int delay, double gain, double leakage);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANRTaps (int channel, int taps);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANRDelay (int channel, int delay);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANRGain (int channel, double gain);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANRLeakage (int channel, double leakage);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAANRPosition (int channel, int position);
|
||||||
|
|
||||||
|
#endif
|
||||||
+594
@@ -0,0 +1,594 @@
|
|||||||
|
/* bandpass.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2016, 2017 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* Overlap-Save Bandpass *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
void calc_bps (BPS a)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
a->infilt = (double *)malloc0(2 * a->size * sizeof(complex));
|
||||||
|
a->product = (double *)malloc0(2 * a->size * sizeof(complex));
|
||||||
|
impulse = fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||||
|
a->mults = fftcv_mults(2 * a->size, impulse);
|
||||||
|
a->CFor = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->infilt, (fftw_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT);
|
||||||
|
a->CRev = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->product, (fftw_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT);
|
||||||
|
_aligned_free(impulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void decalc_bps (BPS a)
|
||||||
|
{
|
||||||
|
fftw_destroy_plan(a->CRev);
|
||||||
|
fftw_destroy_plan(a->CFor);
|
||||||
|
_aligned_free(a->mults);
|
||||||
|
_aligned_free(a->product);
|
||||||
|
_aligned_free(a->infilt);
|
||||||
|
}
|
||||||
|
|
||||||
|
BPS create_bps (int run, int position, int size, double* in, double* out,
|
||||||
|
double f_low, double f_high, int samplerate, int wintype, double gain)
|
||||||
|
{
|
||||||
|
BPS a = (BPS) malloc0 (sizeof (bps));
|
||||||
|
a->run = run;
|
||||||
|
a->position = position;
|
||||||
|
a->size = size;
|
||||||
|
a->samplerate = (double)samplerate;
|
||||||
|
a->wintype = wintype;
|
||||||
|
a->gain = gain;
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
calc_bps (a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_bps (BPS a)
|
||||||
|
{
|
||||||
|
decalc_bps (a);
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_bps (BPS a)
|
||||||
|
{
|
||||||
|
memset (a->infilt, 0, 2 * a->size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void xbps (BPS a, int pos)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double I, Q;
|
||||||
|
if (a->run && pos == a->position)
|
||||||
|
{
|
||||||
|
memcpy (&(a->infilt[2 * a->size]), a->in, a->size * sizeof (complex));
|
||||||
|
fftw_execute (a->CFor);
|
||||||
|
for (i = 0; i < 2 * a->size; i++)
|
||||||
|
{
|
||||||
|
I = a->gain * a->product[2 * i + 0];
|
||||||
|
Q = a->gain * a->product[2 * i + 1];
|
||||||
|
a->product[2 * i + 0] = I * a->mults[2 * i + 0] - Q * a->mults[2 * i + 1];
|
||||||
|
a->product[2 * i + 1] = I * a->mults[2 * i + 1] + Q * a->mults[2 * i + 0];
|
||||||
|
}
|
||||||
|
fftw_execute (a->CRev);
|
||||||
|
memcpy (a->infilt, &(a->infilt[2 * a->size]), a->size * sizeof(complex));
|
||||||
|
}
|
||||||
|
else if (a->in != a->out)
|
||||||
|
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_bps (BPS a, double* in, double* out)
|
||||||
|
{
|
||||||
|
decalc_bps (a);
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
calc_bps (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_bps (BPS a, int rate)
|
||||||
|
{
|
||||||
|
decalc_bps (a);
|
||||||
|
a->samplerate = rate;
|
||||||
|
calc_bps (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_bps (BPS a, int size)
|
||||||
|
{
|
||||||
|
decalc_bps (a);
|
||||||
|
a->size = size;
|
||||||
|
calc_bps (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFreqs_bps (BPS a, double f_low, double f_high)
|
||||||
|
{
|
||||||
|
decalc_bps (a);
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
calc_bps (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* Overlap-Save Bandpass: RXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
/* // UNCOMMENT properties when a pointer is in place in rxa[channel]
|
||||||
|
PORT
|
||||||
|
void SetRXABPSRun (int channel, int run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].bp1.p->run = run;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXABPSFreqs (int channel, double f_low, double f_high)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
BPS a1;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a1 = rxa[channel].bp1.p;
|
||||||
|
if ((f_low != a1->f_low) || (f_high != a1->f_high))
|
||||||
|
{
|
||||||
|
a1->f_low = f_low;
|
||||||
|
a1->f_high = f_high;
|
||||||
|
_aligned_free (a1->mults);
|
||||||
|
impulse = fir_bandpass(a1->size + 1, f_low, f_high, a1->samplerate, a1->wintype, 1, 1.0 / (double)(2 * a1->size));
|
||||||
|
a1->mults = fftcv_mults (2 * a1->size, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXABPSWindow (int channel, int wintype)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
BPS a1;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a1 = rxa[channel].bp1.p;
|
||||||
|
if ((a1->wintype != wintype))
|
||||||
|
{
|
||||||
|
a1->wintype = wintype;
|
||||||
|
_aligned_free (a1->mults);
|
||||||
|
impulse = fir_bandpass(a1->size + 1, a1->f_low, a1->f_high, a1->samplerate, a1->wintype, 1, 1.0 / (double)(2 * a1->size));
|
||||||
|
a1->mults = fftcv_mults (2 * a1->size, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* TXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
/* // UNCOMMENT properties when pointers in place in txa[channel]
|
||||||
|
PORT
|
||||||
|
void SetTXABPSRun (int channel, int run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
txa[channel].bp1.p->run = run;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXABPSFreqs (int channel, double f_low, double f_high)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
BPS a;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a = txa[channel].bp0.p;
|
||||||
|
if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||||
|
{
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
_aligned_free (a->mults);
|
||||||
|
impulse = fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||||
|
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp1.p;
|
||||||
|
if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||||
|
{
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
_aligned_free (a->mults);
|
||||||
|
impulse = fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||||
|
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp2.p;
|
||||||
|
if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||||
|
{
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
_aligned_free (a->mults);
|
||||||
|
impulse = fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||||
|
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXABPSWindow (int channel, int wintype)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
BPS a;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a = txa[channel].bp0.p;
|
||||||
|
if (a->wintype != wintype)
|
||||||
|
{
|
||||||
|
a->wintype = wintype;
|
||||||
|
_aligned_free (a->mults);
|
||||||
|
impulse = fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||||
|
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp1.p;
|
||||||
|
if (a->wintype != wintype)
|
||||||
|
{
|
||||||
|
a->wintype = wintype;
|
||||||
|
_aligned_free (a->mults);
|
||||||
|
impulse = fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||||
|
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp2.p;
|
||||||
|
if (a->wintype != wintype)
|
||||||
|
{
|
||||||
|
a->wintype = wintype;
|
||||||
|
_aligned_free (a->mults);
|
||||||
|
impulse = fir_bandpass (a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||||
|
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* Partitioned Overlap-Save Bandpass *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
BANDPASS create_bandpass (int run, int position, int size, int nc, int mp, double* in, double* out,
|
||||||
|
double f_low, double f_high, int samplerate, int wintype, double gain)
|
||||||
|
{
|
||||||
|
// NOTE: 'nc' must be >= 'size'
|
||||||
|
BANDPASS a = (BANDPASS) malloc0 (sizeof (bandpass));
|
||||||
|
double* impulse;
|
||||||
|
a->run = run;
|
||||||
|
a->position = position;
|
||||||
|
a->size = size;
|
||||||
|
a->nc = nc;
|
||||||
|
a->mp = mp;
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
a->samplerate = samplerate;
|
||||||
|
a->wintype = wintype;
|
||||||
|
a->gain = gain;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
a->p = create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_bandpass (BANDPASS a)
|
||||||
|
{
|
||||||
|
destroy_fircore (a->p);
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_bandpass (BANDPASS a)
|
||||||
|
{
|
||||||
|
flush_fircore (a->p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xbandpass (BANDPASS a, int pos)
|
||||||
|
{
|
||||||
|
if (a->run && a->position == pos)
|
||||||
|
xfircore (a->p);
|
||||||
|
else if (a->out != a->in)
|
||||||
|
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_bandpass (BANDPASS a, double* in, double* out)
|
||||||
|
{
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
setBuffers_fircore (a->p, a->in, a->out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_bandpass (BANDPASS a, int rate)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
a->samplerate = rate;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setImpulse_fircore (a->p, impulse, 1);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_bandpass (BANDPASS a, int size)
|
||||||
|
{
|
||||||
|
// NOTE: 'size' must be <= 'nc'
|
||||||
|
double* impulse;
|
||||||
|
a->size = size;
|
||||||
|
setSize_fircore (a->p, a->size);
|
||||||
|
// recalc impulse because scale factor is a function of size
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setImpulse_fircore (a->p, impulse, 1);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setGain_bandpass (BANDPASS a, double gain, int update)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
a->gain = gain;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setImpulse_fircore (a->p, impulse, update);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalcBandpassFilter (BANDPASS a, double f_low, double f_high, double gain)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
if ((a->f_low != f_low) || (a->f_high != f_high) || (a->gain != gain))
|
||||||
|
{
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
a->gain = gain;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setImpulse_fircore (a->p, impulse, 1);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* RXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXABandpassRun (int channel, int run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].bp1.p->run = run;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXABandpassFreqs (int channel, double f_low, double f_high)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
BANDPASS a = rxa[channel].bp1.p;
|
||||||
|
if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||||
|
{
|
||||||
|
impulse = fir_bandpass (a->nc, f_low, f_high, a->samplerate,
|
||||||
|
a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setImpulse_fircore (a->p, impulse, 0);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
setUpdate_fircore (a->p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXABandpassWindow (int channel, int wintype)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
BANDPASS a = rxa[channel].bp1.p;
|
||||||
|
if ((a->wintype != wintype))
|
||||||
|
{
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate,
|
||||||
|
wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setImpulse_fircore (a->p, impulse, 0);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->wintype = wintype;
|
||||||
|
setUpdate_fircore (a->p);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXABandpassNC (int channel, int nc)
|
||||||
|
{
|
||||||
|
// NOTE: 'nc' must be >= 'size'
|
||||||
|
double* impulse;
|
||||||
|
BANDPASS a;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a = rxa[channel].bp1.p;
|
||||||
|
if (nc != a->nc)
|
||||||
|
{
|
||||||
|
a->nc = nc;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setNc_fircore (a->p, a->nc, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXABandpassMP (int channel, int mp)
|
||||||
|
{
|
||||||
|
BANDPASS a;
|
||||||
|
a = rxa[channel].bp1.p;
|
||||||
|
if (mp != a->mp)
|
||||||
|
{
|
||||||
|
a->mp = mp;
|
||||||
|
setMp_fircore (a->p, a->mp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* TXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXABandpassRun (int channel, int run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
txa[channel].bp1.p->run = run;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
//PORT
|
||||||
|
//void SetTXABandpassFreqs (int channel, double f_low, double f_high)
|
||||||
|
//{
|
||||||
|
// double* impulse;
|
||||||
|
// BANDPASS a;
|
||||||
|
// a = txa[channel].bp0.p;
|
||||||
|
// if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||||
|
// {
|
||||||
|
// a->f_low = f_low;
|
||||||
|
// a->f_high = f_high;
|
||||||
|
// impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
// setImpulse_fircore (a->p, impulse, 1);
|
||||||
|
// _aligned_free (impulse);
|
||||||
|
// }
|
||||||
|
// a = txa[channel].bp1.p;
|
||||||
|
// if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||||
|
// {
|
||||||
|
// a->f_low = f_low;
|
||||||
|
// a->f_high = f_high;
|
||||||
|
// impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
// setImpulse_fircore (a->p, impulse, 1);
|
||||||
|
// _aligned_free (impulse);
|
||||||
|
// }
|
||||||
|
// a = txa[channel].bp2.p;
|
||||||
|
// if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||||
|
// {
|
||||||
|
// a->f_low = f_low;
|
||||||
|
// a->f_high = f_high;
|
||||||
|
// impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
// setImpulse_fircore (a->p, impulse, 1);
|
||||||
|
// _aligned_free (impulse);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXABandpassWindow (int channel, int wintype)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
BANDPASS a;
|
||||||
|
a = txa[channel].bp0.p;
|
||||||
|
if (a->wintype != wintype)
|
||||||
|
{
|
||||||
|
a->wintype = wintype;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setImpulse_fircore (a->p, impulse, 1);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp1.p;
|
||||||
|
if (a->wintype != wintype)
|
||||||
|
{
|
||||||
|
a->wintype = wintype;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setImpulse_fircore (a->p, impulse, 1);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp2.p;
|
||||||
|
if (a->wintype != wintype)
|
||||||
|
{
|
||||||
|
a->wintype = wintype;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setImpulse_fircore (a->p, impulse, 1);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXABandpassNC (int channel, int nc)
|
||||||
|
{
|
||||||
|
// NOTE: 'nc' must be >= 'size'
|
||||||
|
double* impulse;
|
||||||
|
BANDPASS a;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a = txa[channel].bp0.p;
|
||||||
|
if (a->nc != nc)
|
||||||
|
{
|
||||||
|
a->nc = nc;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setNc_fircore (a->p, a->nc, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp1.p;
|
||||||
|
if (a->nc != nc)
|
||||||
|
{
|
||||||
|
a->nc = nc;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setNc_fircore (a->p, a->nc, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp2.p;
|
||||||
|
if (a->nc != nc)
|
||||||
|
{
|
||||||
|
a->nc = nc;
|
||||||
|
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||||
|
setNc_fircore (a->p, a->nc, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXABandpassMP (int channel, int mp)
|
||||||
|
{
|
||||||
|
BANDPASS a;
|
||||||
|
a = txa[channel].bp0.p;
|
||||||
|
if (mp != a->mp)
|
||||||
|
{
|
||||||
|
a->mp = mp;
|
||||||
|
setMp_fircore (a->p, a->mp);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp1.p;
|
||||||
|
if (mp != a->mp)
|
||||||
|
{
|
||||||
|
a->mp = mp;
|
||||||
|
setMp_fircore (a->p, a->mp);
|
||||||
|
}
|
||||||
|
a = txa[channel].bp2.p;
|
||||||
|
if (mp != a->mp)
|
||||||
|
{
|
||||||
|
a->mp = mp;
|
||||||
|
setMp_fircore (a->p, a->mp);
|
||||||
|
}
|
||||||
|
}
|
||||||
+143
@@ -0,0 +1,143 @@
|
|||||||
|
/* bandpass.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2016, 2017 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* Overlap-Save Bandpass *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _bps_h
|
||||||
|
#define _bps_h
|
||||||
|
|
||||||
|
typedef struct _bps
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int position;
|
||||||
|
int size;
|
||||||
|
double* in;
|
||||||
|
double* out;
|
||||||
|
double f_low;
|
||||||
|
double f_high;
|
||||||
|
double* infilt;
|
||||||
|
double* product;
|
||||||
|
double* mults;
|
||||||
|
double samplerate;
|
||||||
|
int wintype;
|
||||||
|
double gain;
|
||||||
|
fftw_plan CFor;
|
||||||
|
fftw_plan CRev;
|
||||||
|
}bps, *BPS;
|
||||||
|
|
||||||
|
extern BPS create_bps (int run, int position, int size, double* in, double* out,
|
||||||
|
double f_low, double f_high, int samplerate, int wintype, double gain);
|
||||||
|
|
||||||
|
extern void destroy_bps (BPS a);
|
||||||
|
|
||||||
|
extern void flush_bps (BPS a);
|
||||||
|
|
||||||
|
extern void xbps (BPS a, int pos);
|
||||||
|
|
||||||
|
extern void setBuffers_bps (BPS a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_bps (BPS a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_bps (BPS a, int size);
|
||||||
|
|
||||||
|
extern void setFreqs_bps (BPS a, double f_low, double f_high);
|
||||||
|
|
||||||
|
// RXA Prototypes
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXABPSRun (int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXABPSFreqs (int channel, double low, double high);
|
||||||
|
|
||||||
|
// TXA Prototypes
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXABPSRun (int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXABPSFreqs (int channel, double low, double high);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* Partitioned Overlap-Save Bandpass *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _bandpass_h
|
||||||
|
#define _bandpass_h
|
||||||
|
#include "firmin.h"
|
||||||
|
typedef struct _bandpass
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int position;
|
||||||
|
int size;
|
||||||
|
int nc;
|
||||||
|
int mp;
|
||||||
|
double* in;
|
||||||
|
double* out;
|
||||||
|
double f_low;
|
||||||
|
double f_high;
|
||||||
|
double samplerate;
|
||||||
|
int wintype;
|
||||||
|
double gain;
|
||||||
|
FIRCORE p;
|
||||||
|
}bandpass, *BANDPASS;
|
||||||
|
|
||||||
|
extern BANDPASS create_bandpass (int run, int position, int size, int nc, int mp, double* in, double* out,
|
||||||
|
double f_low, double f_high, int samplerate, int wintype, double gain);
|
||||||
|
|
||||||
|
extern void destroy_bandpass (BANDPASS a);
|
||||||
|
|
||||||
|
extern void flush_bandpass (BANDPASS a);
|
||||||
|
|
||||||
|
extern void xbandpass (BANDPASS a, int pos);
|
||||||
|
|
||||||
|
extern void setBuffers_bandpass (BANDPASS a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_bandpass (BANDPASS a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_bandpass (BANDPASS a, int size);
|
||||||
|
|
||||||
|
extern void setGain_bandpass (BANDPASS a, double gain, int update);
|
||||||
|
|
||||||
|
extern void CalcBandpassFilter (BANDPASS a, double f_low, double f_high, double gain);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXABandpassFreqs (int channel, double f_low, double f_high);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXABandpassNC (int channel, int nc);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXABandpassMP (int channel, int mp);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXABandpassNC (int channel, int nc);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXABandpassMP (int channel, int mp);
|
||||||
|
|
||||||
|
#endif
|
||||||
+1154
File diff suppressed because it is too large
Load Diff
+167
@@ -0,0 +1,167 @@
|
|||||||
|
/* calcc.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2016, 2023 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _calcc_h
|
||||||
|
#define _calcc_h
|
||||||
|
#include "delay.h"
|
||||||
|
#include "lmath.h"
|
||||||
|
typedef struct _calcc
|
||||||
|
{
|
||||||
|
int channel;
|
||||||
|
int runcal;
|
||||||
|
int size;
|
||||||
|
volatile long mox;
|
||||||
|
volatile long solidmox;
|
||||||
|
int rate;
|
||||||
|
int ints;
|
||||||
|
int spi;
|
||||||
|
int nsamps;
|
||||||
|
int npsamps;
|
||||||
|
int pin;
|
||||||
|
int map;
|
||||||
|
int convex;
|
||||||
|
int stbl;
|
||||||
|
int scOK;
|
||||||
|
double hw_scale;
|
||||||
|
double rx_scale;
|
||||||
|
double alpha;
|
||||||
|
|
||||||
|
int tsamps;
|
||||||
|
double* env_TX;
|
||||||
|
double* env_RX;
|
||||||
|
double* x;
|
||||||
|
double* ym;
|
||||||
|
double* yc;
|
||||||
|
double* ys;
|
||||||
|
double* cat;
|
||||||
|
|
||||||
|
double* t;
|
||||||
|
double* tmap;
|
||||||
|
double* cm;
|
||||||
|
double* cc;
|
||||||
|
double* cs;
|
||||||
|
double* cm_old;
|
||||||
|
double* rxs;
|
||||||
|
double* txs;
|
||||||
|
double ptol;
|
||||||
|
int* info;
|
||||||
|
int* binfo;
|
||||||
|
double txdel;
|
||||||
|
BLDR ccbld;
|
||||||
|
volatile long savecorr_bypass;
|
||||||
|
HANDLE Sem_SaveCorr;
|
||||||
|
volatile long restcorr_bypass;
|
||||||
|
HANDLE Sem_RestCorr;
|
||||||
|
volatile long calccorr_bypass;
|
||||||
|
HANDLE Sem_CalcCorr;
|
||||||
|
volatile long turnoff_bypass;
|
||||||
|
HANDLE Sem_TurnOff;
|
||||||
|
struct _ctrl
|
||||||
|
{
|
||||||
|
double moxdelay;
|
||||||
|
double loopdelay;
|
||||||
|
int state;
|
||||||
|
int reset;
|
||||||
|
int automode;
|
||||||
|
int mancal;
|
||||||
|
int turnon;
|
||||||
|
int moxsamps;
|
||||||
|
int moxcount;
|
||||||
|
int count;
|
||||||
|
int* cpi;
|
||||||
|
int* sindex;
|
||||||
|
int* sbase;
|
||||||
|
int full_ints;
|
||||||
|
int calcinprogress;
|
||||||
|
volatile LONG calcdone;
|
||||||
|
int waitsamps;
|
||||||
|
int waitcount;
|
||||||
|
double env_maxtx;
|
||||||
|
volatile long running;
|
||||||
|
int bs_count;
|
||||||
|
volatile long current_state;
|
||||||
|
CRITICAL_SECTION cs_SafeToEnd;
|
||||||
|
} ctrl;
|
||||||
|
struct _disp
|
||||||
|
{
|
||||||
|
double* x;
|
||||||
|
double* ym;
|
||||||
|
double* yc;
|
||||||
|
double* ys;
|
||||||
|
double* cm;
|
||||||
|
double* cc;
|
||||||
|
double* cs;
|
||||||
|
CRITICAL_SECTION cs_disp;
|
||||||
|
} disp;
|
||||||
|
DELAY rxdelay;
|
||||||
|
DELAY txdelay;
|
||||||
|
struct _util
|
||||||
|
{
|
||||||
|
char savefile[256];
|
||||||
|
char restfile[256];
|
||||||
|
int ints;
|
||||||
|
int channel;
|
||||||
|
double* pm;
|
||||||
|
double* pc;
|
||||||
|
double* ps;
|
||||||
|
} util;
|
||||||
|
double* temptx; //////////////////////////////////////////////////// temporary tx complex buffer - remove with new callback3port()
|
||||||
|
double* temprx; //////////////////////////////////////////////////// temporary rx complex buffer - remove with new callback3port()
|
||||||
|
} calcc, *CALCC;
|
||||||
|
|
||||||
|
extern CALCC create_calcc (int channel, int runcal, int size, int rate, int ints, int spi, double hw_scale,
|
||||||
|
double moxdelay, double loopdelay, double ptol, int mox, int solidmox, int pin, int map, int stbl,
|
||||||
|
int npsamps, double alpha);
|
||||||
|
|
||||||
|
extern void destroy_calcc (CALCC a);
|
||||||
|
|
||||||
|
extern void flush_calcc (CALCC a);
|
||||||
|
|
||||||
|
extern __declspec(dllexport) void pscc (int channel, int size, double* tx, double* rx);
|
||||||
|
|
||||||
|
extern void __cdecl PSSaveCorrection(void* pargs);
|
||||||
|
|
||||||
|
extern void __cdecl PSRestoreCorrection(void* pargs);
|
||||||
|
|
||||||
|
extern void __cdecl doPSCalcCorrection(void* arg);
|
||||||
|
|
||||||
|
extern void __cdecl doPSTurnoff(void* arg);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 'info' assignments:
|
||||||
|
// 0 - builder for rx_scale
|
||||||
|
// 1 - builder for cm
|
||||||
|
// 2 - builder for cc
|
||||||
|
// 3 - builder for cs
|
||||||
|
// 4 - feedback level warning
|
||||||
|
// 5 - count of attempted calibrations
|
||||||
|
// 6 - results from scheck()
|
||||||
|
// 7 - results from rxscheck()
|
||||||
|
//
|
||||||
|
// 13 - dogcount
|
||||||
|
// 14 - indicates iqc_Run = 1
|
||||||
|
// 15 - control state
|
||||||
Binary file not shown.
+29045
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _calculus_h
|
||||||
|
#define _calculus_h
|
||||||
|
|
||||||
|
extern double GG[];
|
||||||
|
|
||||||
|
extern double GGS[];
|
||||||
|
|
||||||
|
#endif
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
/* cblock.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2016 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
void calc_cbl (CBL a)
|
||||||
|
{
|
||||||
|
a->prevIin = 0.0;
|
||||||
|
a->prevQin = 0.0;
|
||||||
|
a->prevIout = 0.0;
|
||||||
|
a->prevQout = 0.0;
|
||||||
|
a->mtau = exp(-1.0 / (a->sample_rate * a->tau));
|
||||||
|
}
|
||||||
|
|
||||||
|
CBL create_cbl
|
||||||
|
(
|
||||||
|
int run,
|
||||||
|
int buff_size,
|
||||||
|
double *in_buff,
|
||||||
|
double *out_buff,
|
||||||
|
int mode,
|
||||||
|
int sample_rate,
|
||||||
|
double tau
|
||||||
|
)
|
||||||
|
{
|
||||||
|
CBL a = (CBL) malloc0 (sizeof(cbl));
|
||||||
|
a->run = run;
|
||||||
|
a->buff_size = buff_size;
|
||||||
|
a->in_buff = in_buff;
|
||||||
|
a->out_buff = out_buff;
|
||||||
|
a->mode = mode;
|
||||||
|
a->sample_rate = (double)sample_rate;
|
||||||
|
a->tau = tau;
|
||||||
|
calc_cbl (a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_cbl(CBL a)
|
||||||
|
{
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_cbl (CBL a)
|
||||||
|
{
|
||||||
|
a->prevIin = 0.0;
|
||||||
|
a->prevQin = 0.0;
|
||||||
|
a->prevIout = 0.0;
|
||||||
|
a->prevQout = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xcbl (CBL a)
|
||||||
|
{
|
||||||
|
if (a->run)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double tempI, tempQ;
|
||||||
|
for (i = 0; i < a->buff_size; i++)
|
||||||
|
{
|
||||||
|
tempI = a->in_buff[2 * i + 0];
|
||||||
|
tempQ = a->in_buff[2 * i + 1];
|
||||||
|
a->out_buff[2 * i + 0] = a->in_buff[2 * i + 0] - a->prevIin + a->mtau * a->prevIout;
|
||||||
|
a->out_buff[2 * i + 1] = a->in_buff[2 * i + 1] - a->prevQin + a->mtau * a->prevQout;
|
||||||
|
a->prevIin = tempI;
|
||||||
|
a->prevQin = tempQ;
|
||||||
|
if (fabs(a->prevIout = a->out_buff[2 * i + 0]) < 1.0e-100) a->prevIout = 0.0;
|
||||||
|
if (fabs(a->prevQout = a->out_buff[2 * i + 1]) < 1.0e-100) a->prevQout = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a->in_buff != a->out_buff)
|
||||||
|
memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_cbl (CBL a, double* in, double* out)
|
||||||
|
{
|
||||||
|
a->in_buff = in;
|
||||||
|
a->out_buff = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_cbl (CBL a, int rate)
|
||||||
|
{
|
||||||
|
a->sample_rate = rate;
|
||||||
|
calc_cbl (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_cbl (CBL a, int size)
|
||||||
|
{
|
||||||
|
a->buff_size = size;
|
||||||
|
flush_cbl (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* RXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetRXACBLRun(int channel, int setit)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
rxa[channel].cbl.p->run = setit;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/* cblock.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _cblock_h
|
||||||
|
#define _cblock_h
|
||||||
|
|
||||||
|
typedef struct _cbl
|
||||||
|
{
|
||||||
|
int run; //run
|
||||||
|
int buff_size; //buffer size
|
||||||
|
double *in_buff; //pointer to input buffer
|
||||||
|
double *out_buff; //pointer to output buffer
|
||||||
|
int mode;
|
||||||
|
double sample_rate; //sample rate
|
||||||
|
double prevIin;
|
||||||
|
double prevQin;
|
||||||
|
double prevIout;
|
||||||
|
double prevQout;
|
||||||
|
double tau; //carrier removal time constant
|
||||||
|
double mtau; //carrier removal multiplier
|
||||||
|
} cbl, *CBL;
|
||||||
|
|
||||||
|
extern CBL create_cbl
|
||||||
|
(
|
||||||
|
int run,
|
||||||
|
int buff_size,
|
||||||
|
double *in_buff,
|
||||||
|
double *out_buff,
|
||||||
|
int mode,
|
||||||
|
int sample_rate,
|
||||||
|
double tau
|
||||||
|
);
|
||||||
|
|
||||||
|
extern void destroy_cbl (CBL a);
|
||||||
|
|
||||||
|
extern void flush_cbl (CBL a);
|
||||||
|
|
||||||
|
extern void xcbl (CBL a);
|
||||||
|
|
||||||
|
extern void setBuffers_cbl (CBL a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_cbl (CBL a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_cbl (CBL a, int size);
|
||||||
|
|
||||||
|
// RXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXACBLRun(int channel, int setit);
|
||||||
|
|
||||||
|
#endif
|
||||||
+519
@@ -0,0 +1,519 @@
|
|||||||
|
/* cfcomp.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2017, 2021 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
void calc_cfcwindow (CFCOMP a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double arg0, arg1, cgsum, igsum, coherent_gain, inherent_power_gain, wmult;
|
||||||
|
switch (a->wintype)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
arg0 = 2.0 * PI / (double)a->fsize;
|
||||||
|
cgsum = 0.0;
|
||||||
|
igsum = 0.0;
|
||||||
|
for (i = 0; i < a->fsize; i++)
|
||||||
|
{
|
||||||
|
a->window[i] = sqrt (0.54 - 0.46 * cos((double)i * arg0));
|
||||||
|
cgsum += a->window[i];
|
||||||
|
igsum += a->window[i] * a->window[i];
|
||||||
|
}
|
||||||
|
coherent_gain = cgsum / (double)a->fsize;
|
||||||
|
inherent_power_gain = igsum / (double)a->fsize;
|
||||||
|
wmult = 1.0 / sqrt (inherent_power_gain);
|
||||||
|
for (i = 0; i < a->fsize; i++)
|
||||||
|
a->window[i] *= wmult;
|
||||||
|
a->winfudge = sqrt (1.0 / coherent_gain);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
arg0 = 2.0 * PI / (double)a->fsize;
|
||||||
|
cgsum = 0.0;
|
||||||
|
igsum = 0.0;
|
||||||
|
for (i = 0; i < a->fsize; i++)
|
||||||
|
{
|
||||||
|
arg1 = cos(arg0 * (double)i);
|
||||||
|
a->window[i] = sqrt (+0.21747
|
||||||
|
+ arg1 * (-0.45325
|
||||||
|
+ arg1 * (+0.28256
|
||||||
|
+ arg1 * (-0.04672))));
|
||||||
|
cgsum += a->window[i];
|
||||||
|
igsum += a->window[i] * a->window[i];
|
||||||
|
}
|
||||||
|
coherent_gain = cgsum / (double)a->fsize;
|
||||||
|
inherent_power_gain = igsum / (double)a->fsize;
|
||||||
|
wmult = 1.0 / sqrt (inherent_power_gain);
|
||||||
|
for (i = 0; i < a->fsize; i++)
|
||||||
|
a->window[i] *= wmult;
|
||||||
|
a->winfudge = sqrt (1.0 / coherent_gain);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int fCOMPcompare (const void * a, const void * b)
|
||||||
|
{
|
||||||
|
if (*(double*)a < *(double*)b)
|
||||||
|
return -1;
|
||||||
|
else if (*(double*)a == *(double*)b)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void calc_comp (CFCOMP a)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
double f, frac, fincr, fmax;
|
||||||
|
double* sary;
|
||||||
|
a->precomplin = pow (10.0, 0.05 * a->precomp);
|
||||||
|
a->prepeqlin = pow (10.0, 0.05 * a->prepeq);
|
||||||
|
fmax = 0.5 * a->rate;
|
||||||
|
for (i = 0; i < a->nfreqs; i++)
|
||||||
|
{
|
||||||
|
a->F[i] = max (a->F[i], 0.0);
|
||||||
|
a->F[i] = min (a->F[i], fmax);
|
||||||
|
a->G[i] = max (a->G[i], 0.0);
|
||||||
|
}
|
||||||
|
sary = (double *)malloc0 (3 * a->nfreqs * sizeof (double));
|
||||||
|
for (i = 0; i < a->nfreqs; i++)
|
||||||
|
{
|
||||||
|
sary[3 * i + 0] = a->F[i];
|
||||||
|
sary[3 * i + 1] = a->G[i];
|
||||||
|
sary[3 * i + 2] = a->E[i];
|
||||||
|
}
|
||||||
|
qsort (sary, a->nfreqs, 3 * sizeof (double), fCOMPcompare);
|
||||||
|
for (i = 0; i < a->nfreqs; i++)
|
||||||
|
{
|
||||||
|
a->F[i] = sary[3 * i + 0];
|
||||||
|
a->G[i] = sary[3 * i + 1];
|
||||||
|
a->E[i] = sary[3 * i + 2];
|
||||||
|
}
|
||||||
|
_aligned_free (sary);
|
||||||
|
a->fp[0] = 0.0;
|
||||||
|
a->fp[a->nfreqs + 1] = fmax;
|
||||||
|
a->gp[0] = a->G[0];
|
||||||
|
a->gp[a->nfreqs + 1] = a->G[a->nfreqs - 1];
|
||||||
|
a->ep[0] = a->E[0]; // cutoff?
|
||||||
|
a->ep[a->nfreqs + 1] = a->E[a->nfreqs - 1]; // cutoff?
|
||||||
|
for (i = 0, j = 1; i < a->nfreqs; i++, j++)
|
||||||
|
{
|
||||||
|
a->fp[j] = a->F[i];
|
||||||
|
a->gp[j] = a->G[i];
|
||||||
|
a->ep[j] = a->E[i];
|
||||||
|
}
|
||||||
|
fincr = a->rate / (double)a->fsize;
|
||||||
|
j = 0;
|
||||||
|
// print_impulse ("gp.txt", a->nfreqs+2, a->gp, 0, 0);
|
||||||
|
for (i = 0; i < a->msize; i++)
|
||||||
|
{
|
||||||
|
f = fincr * (double)i;
|
||||||
|
while (f >= a->fp[j + 1] && j < a->nfreqs) j++;
|
||||||
|
frac = (f - a->fp[j]) / (a->fp[j + 1] - a->fp[j]);
|
||||||
|
a->comp[i] = pow (10.0, 0.05 * (frac * a->gp[j + 1] + (1.0 - frac) * a->gp[j]));
|
||||||
|
a->peq[i] = pow (10.0, 0.05 * (frac * a->ep[j + 1] + (1.0 - frac) * a->ep[j]));
|
||||||
|
a->cfc_gain[i] = a->precomplin * a->comp[i];
|
||||||
|
}
|
||||||
|
// print_impulse ("comp.txt", a->msize, a->comp, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void calc_cfcomp(CFCOMP a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
a->incr = a->fsize / a->ovrlp;
|
||||||
|
if (a->fsize > a->bsize)
|
||||||
|
a->iasize = a->fsize;
|
||||||
|
else
|
||||||
|
a->iasize = a->bsize + a->fsize - a->incr;
|
||||||
|
a->iainidx = 0;
|
||||||
|
a->iaoutidx = 0;
|
||||||
|
if (a->fsize > a->bsize)
|
||||||
|
{
|
||||||
|
if (a->bsize > a->incr) a->oasize = a->bsize;
|
||||||
|
else a->oasize = a->incr;
|
||||||
|
a->oainidx = (a->fsize - a->bsize - a->incr) % a->oasize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a->oasize = a->bsize;
|
||||||
|
a->oainidx = a->fsize - a->incr;
|
||||||
|
}
|
||||||
|
a->init_oainidx = a->oainidx;
|
||||||
|
a->oaoutidx = 0;
|
||||||
|
a->msize = a->fsize / 2 + 1;
|
||||||
|
a->window = (double *)malloc0 (a->fsize * sizeof(double));
|
||||||
|
a->inaccum = (double *)malloc0 (a->iasize * sizeof(double));
|
||||||
|
a->forfftin = (double *)malloc0 (a->fsize * sizeof(double));
|
||||||
|
a->forfftout = (double *)malloc0 (a->msize * sizeof(complex));
|
||||||
|
a->cmask = (double *)malloc0 (a->msize * sizeof(double));
|
||||||
|
a->mask = (double *)malloc0 (a->msize * sizeof(double));
|
||||||
|
a->cfc_gain = (double *)malloc0 (a->msize * sizeof(double));
|
||||||
|
a->revfftin = (double *)malloc0 (a->msize * sizeof(complex));
|
||||||
|
a->revfftout = (double *)malloc0 (a->fsize * sizeof(double));
|
||||||
|
a->save = (double **)malloc0(a->ovrlp * sizeof(double *));
|
||||||
|
for (i = 0; i < a->ovrlp; i++)
|
||||||
|
a->save[i] = (double *)malloc0(a->fsize * sizeof(double));
|
||||||
|
a->outaccum = (double *)malloc0(a->oasize * sizeof(double));
|
||||||
|
a->nsamps = 0;
|
||||||
|
a->saveidx = 0;
|
||||||
|
a->Rfor = fftw_plan_dft_r2c_1d(a->fsize, a->forfftin, (fftw_complex *)a->forfftout, FFTW_ESTIMATE);
|
||||||
|
a->Rrev = fftw_plan_dft_c2r_1d(a->fsize, (fftw_complex *)a->revfftin, a->revfftout, FFTW_ESTIMATE);
|
||||||
|
calc_cfcwindow(a);
|
||||||
|
|
||||||
|
a->pregain = (2.0 * a->winfudge) / (double)a->fsize;
|
||||||
|
a->postgain = 0.5 / ((double)a->ovrlp * a->winfudge);
|
||||||
|
|
||||||
|
a->fp = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||||
|
a->gp = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||||
|
a->ep = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||||
|
a->comp = (double *) malloc0 (a->msize * sizeof (double));
|
||||||
|
a->peq = (double *) malloc0 (a->msize * sizeof (double));
|
||||||
|
calc_comp (a);
|
||||||
|
|
||||||
|
a->gain = 0.0;
|
||||||
|
a->mmult = exp (-1.0 / (a->rate * a->ovrlp * a->mtau));
|
||||||
|
a->dmult = exp (-(double)a->fsize / (a->rate * a->ovrlp * a->dtau));
|
||||||
|
|
||||||
|
a->delta = (double*)malloc0 (a->msize * sizeof(double));
|
||||||
|
a->delta_copy = (double*)malloc0 (a->msize * sizeof(double));
|
||||||
|
a->cfc_gain_copy = (double*)malloc0 (a->msize * sizeof(double));
|
||||||
|
}
|
||||||
|
|
||||||
|
void decalc_cfcomp(CFCOMP a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
_aligned_free (a->cfc_gain_copy);
|
||||||
|
_aligned_free (a->delta_copy);
|
||||||
|
_aligned_free (a->delta);
|
||||||
|
_aligned_free (a->peq);
|
||||||
|
_aligned_free (a->comp);
|
||||||
|
_aligned_free (a->ep);
|
||||||
|
_aligned_free (a->gp);
|
||||||
|
_aligned_free (a->fp);
|
||||||
|
|
||||||
|
fftw_destroy_plan(a->Rrev);
|
||||||
|
fftw_destroy_plan(a->Rfor);
|
||||||
|
_aligned_free(a->outaccum);
|
||||||
|
for (i = 0; i < a->ovrlp; i++)
|
||||||
|
_aligned_free(a->save[i]);
|
||||||
|
_aligned_free(a->save);
|
||||||
|
_aligned_free(a->revfftout);
|
||||||
|
_aligned_free(a->revfftin);
|
||||||
|
_aligned_free(a->cfc_gain);
|
||||||
|
_aligned_free(a->mask);
|
||||||
|
_aligned_free(a->cmask);
|
||||||
|
_aligned_free(a->forfftout);
|
||||||
|
_aligned_free(a->forfftin);
|
||||||
|
_aligned_free(a->inaccum);
|
||||||
|
_aligned_free(a->window);
|
||||||
|
}
|
||||||
|
|
||||||
|
CFCOMP create_cfcomp (int run, int position, int peq_run, int size, double* in, double* out, int fsize, int ovrlp,
|
||||||
|
int rate, int wintype, int comp_method, int nfreqs, double precomp, double prepeq, double* F, double* G, double* E, double mtau, double dtau)
|
||||||
|
{
|
||||||
|
CFCOMP a = (CFCOMP) malloc0 (sizeof (cfcomp));
|
||||||
|
|
||||||
|
a->run = run;
|
||||||
|
a->position = position;
|
||||||
|
a->peq_run = peq_run;
|
||||||
|
a->bsize = size;
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
a->fsize = fsize;
|
||||||
|
a->ovrlp = ovrlp;
|
||||||
|
a->rate = rate;
|
||||||
|
a->wintype = wintype;
|
||||||
|
a->comp_method = comp_method;
|
||||||
|
a->nfreqs = nfreqs;
|
||||||
|
a->precomp = precomp;
|
||||||
|
a->prepeq = prepeq;
|
||||||
|
a->mtau = mtau; // compression metering time constant
|
||||||
|
a->dtau = dtau; // compression display time constant
|
||||||
|
a->F = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||||
|
a->G = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||||
|
a->E = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||||
|
memcpy (a->F, F, a->nfreqs * sizeof (double));
|
||||||
|
memcpy (a->G, G, a->nfreqs * sizeof (double));
|
||||||
|
memcpy (a->E, E, a->nfreqs * sizeof (double));
|
||||||
|
calc_cfcomp (a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_cfcomp (CFCOMP a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
memset (a->inaccum, 0, a->iasize * sizeof (double));
|
||||||
|
for (i = 0; i < a->ovrlp; i++)
|
||||||
|
memset (a->save[i], 0, a->fsize * sizeof (double));
|
||||||
|
memset (a->outaccum, 0, a->oasize * sizeof (double));
|
||||||
|
a->nsamps = 0;
|
||||||
|
a->iainidx = 0;
|
||||||
|
a->iaoutidx = 0;
|
||||||
|
a->oainidx = a->init_oainidx;
|
||||||
|
a->oaoutidx = 0;
|
||||||
|
a->saveidx = 0;
|
||||||
|
a->gain = 0.0;
|
||||||
|
memset(a->delta, 0, a->msize * sizeof(double));
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_cfcomp (CFCOMP a)
|
||||||
|
{
|
||||||
|
decalc_cfcomp (a);
|
||||||
|
_aligned_free (a->E);
|
||||||
|
_aligned_free (a->G);
|
||||||
|
_aligned_free (a->F);
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void calc_mask (CFCOMP a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double comp, mask, delta;
|
||||||
|
switch (a->comp_method)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
double mag, test;
|
||||||
|
for (i = 0; i < a->msize; i++)
|
||||||
|
{
|
||||||
|
mag = sqrt (a->forfftout[2 * i + 0] * a->forfftout[2 * i + 0]
|
||||||
|
+ a->forfftout[2 * i + 1] * a->forfftout[2 * i + 1]);
|
||||||
|
comp = a->cfc_gain[i];
|
||||||
|
test = comp * mag;
|
||||||
|
if (test > 1.0)
|
||||||
|
mask = 1.0 / mag;
|
||||||
|
else
|
||||||
|
mask = comp;
|
||||||
|
a->cmask[i] = mask;
|
||||||
|
if (test > a->gain) a->gain = test;
|
||||||
|
else a->gain = a->mmult * a->gain;
|
||||||
|
|
||||||
|
delta = a->cfc_gain[i] - a->cmask[i];
|
||||||
|
if (delta > a->delta[i]) a->delta[i] = delta;
|
||||||
|
else a->delta[i] *= a->dmult;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a->peq_run)
|
||||||
|
{
|
||||||
|
for (i = 0; i < a->msize; i++)
|
||||||
|
{
|
||||||
|
a->mask[i] = a->cmask[i] * a->prepeqlin * a->peq[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
memcpy (a->mask, a->cmask, a->msize * sizeof (double));
|
||||||
|
// print_impulse ("mask.txt", a->msize, a->mask, 0, 0);
|
||||||
|
a->mask_ready = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xcfcomp (CFCOMP a, int pos)
|
||||||
|
{
|
||||||
|
if (a->run && pos == a->position)
|
||||||
|
{
|
||||||
|
int i, j, k, sbuff, sbegin;
|
||||||
|
for (i = 0; i < 2 * a->bsize; i += 2)
|
||||||
|
{
|
||||||
|
a->inaccum[a->iainidx] = a->in[i];
|
||||||
|
a->iainidx = (a->iainidx + 1) % a->iasize;
|
||||||
|
}
|
||||||
|
a->nsamps += a->bsize;
|
||||||
|
while (a->nsamps >= a->fsize)
|
||||||
|
{
|
||||||
|
for (i = 0, j = a->iaoutidx; i < a->fsize; i++, j = (j + 1) % a->iasize)
|
||||||
|
a->forfftin[i] = a->pregain * a->window[i] * a->inaccum[j];
|
||||||
|
a->iaoutidx = (a->iaoutidx + a->incr) % a->iasize;
|
||||||
|
a->nsamps -= a->incr;
|
||||||
|
fftw_execute (a->Rfor);
|
||||||
|
calc_mask(a);
|
||||||
|
for (i = 0; i < a->msize; i++)
|
||||||
|
{
|
||||||
|
a->revfftin[2 * i + 0] = a->mask[i] * a->forfftout[2 * i + 0];
|
||||||
|
a->revfftin[2 * i + 1] = a->mask[i] * a->forfftout[2 * i + 1];
|
||||||
|
}
|
||||||
|
fftw_execute (a->Rrev);
|
||||||
|
for (i = 0; i < a->fsize; i++)
|
||||||
|
a->save[a->saveidx][i] = a->postgain * a->window[i] * a->revfftout[i];
|
||||||
|
for (i = a->ovrlp; i > 0; i--)
|
||||||
|
{
|
||||||
|
sbuff = (a->saveidx + i) % a->ovrlp;
|
||||||
|
sbegin = a->incr * (a->ovrlp - i);
|
||||||
|
for (j = sbegin, k = a->oainidx; j < a->incr + sbegin; j++, k = (k + 1) % a->oasize)
|
||||||
|
{
|
||||||
|
if ( i == a->ovrlp)
|
||||||
|
a->outaccum[k] = a->save[sbuff][j];
|
||||||
|
else
|
||||||
|
a->outaccum[k] += a->save[sbuff][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a->saveidx = (a->saveidx + 1) % a->ovrlp;
|
||||||
|
a->oainidx = (a->oainidx + a->incr) % a->oasize;
|
||||||
|
}
|
||||||
|
for (i = 0; i < a->bsize; i++)
|
||||||
|
{
|
||||||
|
a->out[2 * i + 0] = a->outaccum[a->oaoutidx];
|
||||||
|
a->out[2 * i + 1] = 0.0;
|
||||||
|
a->oaoutidx = (a->oaoutidx + 1) % a->oasize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a->out != a->in)
|
||||||
|
memcpy (a->out, a->in, a->bsize * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_cfcomp (CFCOMP a, double* in, double* out)
|
||||||
|
{
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_cfcomp (CFCOMP a, int rate)
|
||||||
|
{
|
||||||
|
decalc_cfcomp (a);
|
||||||
|
a->rate = rate;
|
||||||
|
calc_cfcomp (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_cfcomp (CFCOMP a, int size)
|
||||||
|
{
|
||||||
|
decalc_cfcomp (a);
|
||||||
|
a->bsize = size;
|
||||||
|
calc_cfcomp (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* TXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXACFCOMPRun (int channel, int run)
|
||||||
|
{
|
||||||
|
CFCOMP a = txa[channel].cfcomp.p;
|
||||||
|
if (a->run != run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->run = run;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXACFCOMPPosition (int channel, int pos)
|
||||||
|
{
|
||||||
|
CFCOMP a = txa[channel].cfcomp.p;
|
||||||
|
if (a->position != pos)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->position = pos;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXACFCOMPprofile (int channel, int nfreqs, double* F, double* G, double *E)
|
||||||
|
{
|
||||||
|
CFCOMP a = txa[channel].cfcomp.p;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->nfreqs = nfreqs;
|
||||||
|
_aligned_free (a->E);
|
||||||
|
_aligned_free (a->F);
|
||||||
|
_aligned_free (a->G);
|
||||||
|
a->F = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||||
|
a->G = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||||
|
a->E = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||||
|
memcpy (a->F, F, a->nfreqs * sizeof (double));
|
||||||
|
memcpy (a->G, G, a->nfreqs * sizeof (double));
|
||||||
|
memcpy (a->E, E, a->nfreqs * sizeof (double));
|
||||||
|
_aligned_free (a->ep);
|
||||||
|
_aligned_free (a->gp);
|
||||||
|
_aligned_free (a->fp);
|
||||||
|
a->fp = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||||
|
a->gp = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||||
|
a->ep = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||||
|
calc_comp(a);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXACFCOMPPrecomp (int channel, double precomp)
|
||||||
|
{
|
||||||
|
CFCOMP a = txa[channel].cfcomp.p;
|
||||||
|
if (a->precomp != precomp)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->precomp = precomp;
|
||||||
|
a->precomplin = pow (10.0, 0.05 * a->precomp);
|
||||||
|
for (int i = 0; i < a->msize; i++)
|
||||||
|
{
|
||||||
|
a->cfc_gain[i] = a->precomplin * a->comp[i];
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXACFCOMPPeqRun (int channel, int run)
|
||||||
|
{
|
||||||
|
CFCOMP a = txa[channel].cfcomp.p;
|
||||||
|
if (a->peq_run != run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->peq_run = run;
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXACFCOMPPrePeq (int channel, double prepeq)
|
||||||
|
{
|
||||||
|
CFCOMP a = txa[channel].cfcomp.p;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a->prepeq = prepeq;
|
||||||
|
a->prepeqlin = pow (10.0, 0.05 * a->prepeq);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void GetTXACFCOMPDisplayCompression (int channel, double* comp_values, int* ready)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
CFCOMP a = txa[channel].cfcomp.p;
|
||||||
|
EnterCriticalSection(&ch[channel].csDSP);
|
||||||
|
if (*ready = a->mask_ready)
|
||||||
|
{
|
||||||
|
memcpy(a->delta_copy, a->delta, a->msize * sizeof(double));
|
||||||
|
memcpy(a->cfc_gain_copy, a->cfc_gain, a->msize * sizeof(double));
|
||||||
|
a->mask_ready = 0;
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&ch[channel].csDSP);
|
||||||
|
if (*ready)
|
||||||
|
{
|
||||||
|
for (i = 0; i < a->msize; i++)
|
||||||
|
comp_values[i] = 20.0 * mlog10 (a->cfc_gain_copy[i] / (a->cfc_gain_copy[i] - a->delta_copy[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
/* cfcomp.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2017, 2021 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _cfcomp_h
|
||||||
|
#define _cfcomp_h
|
||||||
|
|
||||||
|
typedef struct _cfcomp
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int position;
|
||||||
|
int bsize;
|
||||||
|
double* in;
|
||||||
|
double* out;
|
||||||
|
int fsize;
|
||||||
|
int ovrlp;
|
||||||
|
int incr;
|
||||||
|
double* window;
|
||||||
|
int iasize;
|
||||||
|
double* inaccum;
|
||||||
|
double* forfftin;
|
||||||
|
double* forfftout;
|
||||||
|
int msize;
|
||||||
|
double* cmask;
|
||||||
|
double* mask;
|
||||||
|
int mask_ready;
|
||||||
|
double* cfc_gain;
|
||||||
|
double* revfftin;
|
||||||
|
double* revfftout;
|
||||||
|
double** save;
|
||||||
|
int oasize;
|
||||||
|
double* outaccum;
|
||||||
|
double rate;
|
||||||
|
int wintype;
|
||||||
|
double pregain;
|
||||||
|
double postgain;
|
||||||
|
int nsamps;
|
||||||
|
int iainidx;
|
||||||
|
int iaoutidx;
|
||||||
|
int init_oainidx;
|
||||||
|
int oainidx;
|
||||||
|
int oaoutidx;
|
||||||
|
int saveidx;
|
||||||
|
fftw_plan Rfor;
|
||||||
|
fftw_plan Rrev;
|
||||||
|
|
||||||
|
int comp_method;
|
||||||
|
int nfreqs;
|
||||||
|
double* F;
|
||||||
|
double* G;
|
||||||
|
double* E;
|
||||||
|
double* fp;
|
||||||
|
double* gp;
|
||||||
|
double* ep;
|
||||||
|
double* comp;
|
||||||
|
double precomp;
|
||||||
|
double precomplin;
|
||||||
|
double* peq;
|
||||||
|
int peq_run;
|
||||||
|
double prepeq;
|
||||||
|
double prepeqlin;
|
||||||
|
double winfudge;
|
||||||
|
|
||||||
|
double gain;
|
||||||
|
double mtau;
|
||||||
|
double mmult;
|
||||||
|
// display stuff
|
||||||
|
double dtau;
|
||||||
|
double dmult;
|
||||||
|
double* delta;
|
||||||
|
double* delta_copy;
|
||||||
|
double* cfc_gain_copy;
|
||||||
|
}cfcomp, *CFCOMP;
|
||||||
|
|
||||||
|
extern CFCOMP create_cfcomp (int run, int position, int peq_run, int size, double* in, double* out, int fsize, int ovrlp,
|
||||||
|
int rate, int wintype, int comp_method, int nfreqs, double precomp, double prepeq, double* F, double* G, double* E, double mtau, double dtau);
|
||||||
|
|
||||||
|
extern void destroy_cfcomp (CFCOMP a);
|
||||||
|
|
||||||
|
extern void flush_cfcomp (CFCOMP a);
|
||||||
|
|
||||||
|
extern void xcfcomp (CFCOMP a, int pos);
|
||||||
|
|
||||||
|
extern void setBuffers_cfcomp (CFCOMP a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_cfcomp (CFCOMP a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_cfcomp (CFCOMP a, int size);
|
||||||
|
|
||||||
|
#endif
|
||||||
+254
@@ -0,0 +1,254 @@
|
|||||||
|
/* cfir.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2014, 2016, 2021 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
#define _CRT_SECURE_NO_DEPRECATE
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
void calc_cfir (CFIR a)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
a->scale = 1.0 / (double)(2 * a->size);
|
||||||
|
impulse = cfir_impulse (a->nc, a->DD, a->R, a->Pairs, a->runrate, a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype);
|
||||||
|
a->p = create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void decalc_cfir (CFIR a)
|
||||||
|
{
|
||||||
|
destroy_fircore (a->p);
|
||||||
|
}
|
||||||
|
|
||||||
|
CFIR create_cfir (int run, int size, int nc, int mp, double* in, double* out, int runrate, int cicrate,
|
||||||
|
int DD, int R, int Pairs, double cutoff, int xtype, double xbw, int wintype)
|
||||||
|
// run: 0 - no action; 1 - operate
|
||||||
|
// size: number of complex samples in an input buffer to the CFIR filter
|
||||||
|
// nc: number of filter coefficients
|
||||||
|
// mp: minimum phase flag
|
||||||
|
// in: pointer to the input buffer
|
||||||
|
// out: pointer to the output buffer
|
||||||
|
// rate: samplerate
|
||||||
|
// DD: differential delay of the CIC to be compensated (usually 1 or 2)
|
||||||
|
// R: interpolation factor of CIC
|
||||||
|
// Pairs: number of comb-integrator pairs in the CIC
|
||||||
|
// cutoff: cutoff frequency
|
||||||
|
// xtype: 0 - fourth power transition; 1 - raised cosine transition; 2 - brick wall
|
||||||
|
// xbw: width of raised cosine transition
|
||||||
|
{
|
||||||
|
CFIR a = (CFIR) malloc0 (sizeof (cfir));
|
||||||
|
a->run = run;
|
||||||
|
a->size = size;
|
||||||
|
a->nc = nc;
|
||||||
|
a->mp = mp;
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
a->runrate = runrate;
|
||||||
|
a->cicrate = cicrate;
|
||||||
|
a->DD = DD;
|
||||||
|
a->R = R;
|
||||||
|
a->Pairs = Pairs;
|
||||||
|
a->cutoff = cutoff;
|
||||||
|
a->xtype = xtype;
|
||||||
|
a->xbw = xbw;
|
||||||
|
a->wintype = wintype;
|
||||||
|
calc_cfir (a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_cfir (CFIR a)
|
||||||
|
{
|
||||||
|
decalc_cfir (a);
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_cfir (CFIR a)
|
||||||
|
{
|
||||||
|
flush_fircore (a->p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xcfir (CFIR a)
|
||||||
|
{
|
||||||
|
if (a->run)
|
||||||
|
xfircore (a->p);
|
||||||
|
else if (a->in != a->out)
|
||||||
|
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_cfir (CFIR a, double* in, double* out)
|
||||||
|
{
|
||||||
|
decalc_cfir (a);
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
calc_cfir (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_cfir (CFIR a, int rate)
|
||||||
|
{
|
||||||
|
decalc_cfir (a);
|
||||||
|
a->runrate = rate;
|
||||||
|
calc_cfir (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_cfir (CFIR a, int size)
|
||||||
|
{
|
||||||
|
decalc_cfir (a);
|
||||||
|
a->size = size;
|
||||||
|
calc_cfir (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOutRate_cfir (CFIR a, int rate)
|
||||||
|
{
|
||||||
|
decalc_cfir (a);
|
||||||
|
a->cicrate = rate;
|
||||||
|
calc_cfir (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
double* cfir_impulse (int N, int DD, int R, int Pairs, double runrate, double cicrate, double cutoff, int xtype, double xbw, int rtype, double scale, int wintype)
|
||||||
|
{
|
||||||
|
// N: number of impulse response samples
|
||||||
|
// DD: differential delay used in the CIC filter
|
||||||
|
// R: interpolation / decimation factor of the CIC
|
||||||
|
// Pairs: number of comb-integrator pairs in the CIC
|
||||||
|
// runrate: sample rate at which this filter is to run (assumes there may be flat interp. between this filter and the CIC)
|
||||||
|
// cicrate: sample rate at interface to CIC
|
||||||
|
// cutoff: cutoff frequency
|
||||||
|
// xtype: transition type, 0 for 4th-power rolloff, 1 for raised cosine, 2 for brick wall
|
||||||
|
// xbw: transition bandwidth for raised cosine
|
||||||
|
// rtype: 0 for real output, 1 for complex output
|
||||||
|
// scale: scale factor to be applied to the output
|
||||||
|
int i, j;
|
||||||
|
double tmp, local_scale, ri, mag, fn;
|
||||||
|
double* impulse;
|
||||||
|
double* A = (double *) malloc0 (N * sizeof (double));
|
||||||
|
double ft = cutoff / cicrate; // normalized cutoff frequency
|
||||||
|
int u_samps = (N + 1) / 2; // number of unique samples, OK for odd or even N
|
||||||
|
int c_samps = (int)(cutoff / runrate * N) + (N + 1) / 2 - N / 2; // number of unique samples within bandpass, OK for odd or even N
|
||||||
|
int x_samps = (int)(xbw / runrate * N); // number of unique samples in transition region, OK for odd or even N
|
||||||
|
double offset = 0.5 - 0.5 * (double)((N + 1) / 2 - N / 2); // sample offset from center, OK for odd or even N
|
||||||
|
double* xistion = (double *) malloc0 ((x_samps + 1) * sizeof (double));
|
||||||
|
double delta = PI / (double)x_samps;
|
||||||
|
double L = cicrate / runrate;
|
||||||
|
double phs = 0.0;
|
||||||
|
for (i = 0; i <= x_samps; i++)
|
||||||
|
{
|
||||||
|
xistion[i] = 0.5 * (cos (phs) + 1.0);
|
||||||
|
phs += delta;
|
||||||
|
}
|
||||||
|
if ((tmp = DD * R * sin (PI * ft / R) / sin (PI * DD * ft)) < 0.0) //normalize by peak gain
|
||||||
|
tmp = -tmp;
|
||||||
|
local_scale = scale / pow (tmp, Pairs);
|
||||||
|
if (xtype == 0)
|
||||||
|
{
|
||||||
|
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
|
||||||
|
{
|
||||||
|
fn = ri / (L * (double)N);
|
||||||
|
if (fn <= ft)
|
||||||
|
{
|
||||||
|
if (fn == 0.0) tmp = 1.0;
|
||||||
|
else if ((tmp = DD * R * sin (PI * fn / R) / sin (PI * DD * fn)) < 0.0)
|
||||||
|
tmp = -tmp;
|
||||||
|
mag = pow (tmp, Pairs) * local_scale;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mag *= (ft * ft * ft * ft) / (fn * fn * fn * fn);
|
||||||
|
A[i] = mag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (xtype == 1)
|
||||||
|
{
|
||||||
|
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
|
||||||
|
{
|
||||||
|
fn = ri / (L *(double)N);
|
||||||
|
if (i < c_samps)
|
||||||
|
{
|
||||||
|
if (fn == 0.0) tmp = 1.0;
|
||||||
|
else if ((tmp = DD * R * sin (PI * fn / R) / sin (PI * DD * fn)) < 0.0)
|
||||||
|
tmp = -tmp;
|
||||||
|
mag = pow (tmp, Pairs) * local_scale;
|
||||||
|
A[i] = mag;
|
||||||
|
}
|
||||||
|
else if ( i >= c_samps && i <= c_samps + x_samps)
|
||||||
|
A[i] = mag * xistion[i - c_samps];
|
||||||
|
else
|
||||||
|
A[i] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (xtype == 2)
|
||||||
|
{
|
||||||
|
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
|
||||||
|
{
|
||||||
|
fn = ri / (L * (double)N);
|
||||||
|
if (fn <= ft)
|
||||||
|
{
|
||||||
|
if (fn == 0.0) tmp = 1.0;
|
||||||
|
else if ((tmp = DD * R * sin(PI * fn / R) / sin(PI * DD * fn)) < 0.0)
|
||||||
|
tmp = -tmp;
|
||||||
|
mag = pow (tmp, Pairs) * local_scale;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mag = 0.0;
|
||||||
|
A[i] = mag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (N & 1)
|
||||||
|
for (i = u_samps, j = 2; i < N; i++, j++)
|
||||||
|
A[i] = A[u_samps - j];
|
||||||
|
else
|
||||||
|
for (i = u_samps, j = 1; i < N; i++, j++)
|
||||||
|
A[i] = A[u_samps - j];
|
||||||
|
impulse = fir_fsamp (N, A, rtype, 1.0, wintype);
|
||||||
|
// print_impulse ("cfirImpulse.txt", N, impulse, 1, 0);
|
||||||
|
_aligned_free (A);
|
||||||
|
return impulse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* TXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXACFIRRun (int channel, int run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&ch[channel].csDSP);
|
||||||
|
txa[channel].cfir.p->run = run;
|
||||||
|
LeaveCriticalSection(&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetTXACFIRNC(int channel, int nc)
|
||||||
|
{
|
||||||
|
// NOTE: 'nc' must be >= 'size'
|
||||||
|
CFIR a;
|
||||||
|
EnterCriticalSection(&ch[channel].csDSP);
|
||||||
|
a = txa[channel].cfir.p;
|
||||||
|
if (a->nc != nc)
|
||||||
|
{
|
||||||
|
a->nc = nc;
|
||||||
|
decalc_cfir(a);
|
||||||
|
calc_cfir(a);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&ch[channel].csDSP);
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/* cfir.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2014, 2016 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _cfir_h
|
||||||
|
#define _cfir_h
|
||||||
|
#include "firmin.h"
|
||||||
|
typedef struct _cfir
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int size;
|
||||||
|
int nc;
|
||||||
|
int mp;
|
||||||
|
double* in;
|
||||||
|
double* out;
|
||||||
|
int runrate;
|
||||||
|
int cicrate;
|
||||||
|
int DD;
|
||||||
|
int R;
|
||||||
|
int Pairs;
|
||||||
|
double cutoff;
|
||||||
|
double scale;
|
||||||
|
int xtype;
|
||||||
|
double xbw;
|
||||||
|
int wintype;
|
||||||
|
FIRCORE p;
|
||||||
|
} cfir, *CFIR;
|
||||||
|
|
||||||
|
extern CFIR create_cfir (int run, int size, int nc, int mp, double* in, double* out, int runrate, int cicrate,
|
||||||
|
int DD, int R, int Pairs, double cutoff, int xtype, double xbw, int wintype);
|
||||||
|
|
||||||
|
extern void destroy_cfir (CFIR a);
|
||||||
|
|
||||||
|
extern void flush_cfir (CFIR a);
|
||||||
|
|
||||||
|
extern void xcfir (CFIR a);
|
||||||
|
|
||||||
|
extern void setBuffers_cfir (CFIR a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_cfir (CFIR a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_cfir (CFIR a, int size);
|
||||||
|
|
||||||
|
extern void setOutRate_cfir (CFIR a, int rate);
|
||||||
|
|
||||||
|
extern double* cfir_impulse (int N, int DD, int R, int Pairs, double runrate, double cicrate,
|
||||||
|
double cutoff, int xtype, double xbw, int rtype, double scale, int wintype);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXACFIRRun(int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXACFIRNC(int channel, int nc);
|
||||||
|
|
||||||
|
#endif
|
||||||
+347
@@ -0,0 +1,347 @@
|
|||||||
|
/* channel.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
struct _ch ch[MAX_CHANNELS];
|
||||||
|
|
||||||
|
void start_thread (int channel)
|
||||||
|
{
|
||||||
|
HANDLE handle = (HANDLE) _beginthread(wdspmain, 0, (void *)(uintptr_t)channel);
|
||||||
|
//SetThreadPriority(handle, THREAD_PRIORITY_HIGHEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pre_main_build (int channel)
|
||||||
|
{
|
||||||
|
if (ch[channel].in_rate >= ch[channel].dsp_rate)
|
||||||
|
ch[channel].dsp_insize = ch[channel].dsp_size * (ch[channel].in_rate / ch[channel].dsp_rate);
|
||||||
|
else
|
||||||
|
ch[channel].dsp_insize = ch[channel].dsp_size / (ch[channel].dsp_rate / ch[channel].in_rate);
|
||||||
|
|
||||||
|
if (ch[channel].out_rate >= ch[channel].dsp_rate)
|
||||||
|
ch[channel].dsp_outsize = ch[channel].dsp_size * (ch[channel].out_rate / ch[channel].dsp_rate);
|
||||||
|
else
|
||||||
|
ch[channel].dsp_outsize = ch[channel].dsp_size / (ch[channel].dsp_rate / ch[channel].out_rate);
|
||||||
|
|
||||||
|
if (ch[channel].in_rate >= ch[channel].out_rate)
|
||||||
|
ch[channel].out_size = ch[channel].in_size / (ch[channel].in_rate / ch[channel].out_rate);
|
||||||
|
else
|
||||||
|
ch[channel].out_size = ch[channel].in_size * (ch[channel].out_rate / ch[channel].in_rate);
|
||||||
|
|
||||||
|
InitializeCriticalSectionAndSpinCount ( &ch[channel].csDSP, 2500 );
|
||||||
|
InitializeCriticalSectionAndSpinCount ( &ch[channel].csEXCH, 2500 );
|
||||||
|
InterlockedBitTestAndReset (&ch[channel].flushflag, 0);
|
||||||
|
create_iobuffs (channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void post_main_build (int channel)
|
||||||
|
{
|
||||||
|
InterlockedBitTestAndSet (&ch[channel].run, 0);
|
||||||
|
start_thread (channel);
|
||||||
|
if (ch[channel].state == 1)
|
||||||
|
InterlockedBitTestAndSet (&ch[channel].exchange, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void build_channel (int channel)
|
||||||
|
{
|
||||||
|
pre_main_build (channel);
|
||||||
|
create_main (channel);
|
||||||
|
post_main_build (channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void OpenChannel (int channel, int in_size, int dsp_size, int input_samplerate, int dsp_rate, int output_samplerate,
|
||||||
|
int type, int state, double tdelayup, double tslewup, double tdelaydown, double tslewdown, int bfo)
|
||||||
|
{
|
||||||
|
ch[channel].in_size = in_size;
|
||||||
|
ch[channel].dsp_size = dsp_size;
|
||||||
|
ch[channel].in_rate = input_samplerate;
|
||||||
|
ch[channel].dsp_rate = dsp_rate;
|
||||||
|
ch[channel].out_rate = output_samplerate;
|
||||||
|
ch[channel].type = type;
|
||||||
|
ch[channel].state = state;
|
||||||
|
ch[channel].tdelayup = tdelayup;
|
||||||
|
ch[channel].tslewup = tslewup;
|
||||||
|
ch[channel].tdelaydown = tdelaydown;
|
||||||
|
ch[channel].tslewdown = tslewdown;
|
||||||
|
ch[channel].bfo = bfo;
|
||||||
|
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
||||||
|
build_channel (channel);
|
||||||
|
if (ch[channel].state)
|
||||||
|
{
|
||||||
|
InterlockedBitTestAndSet (&ch[channel].iob.pc->slew.upflag, 0);
|
||||||
|
InterlockedBitTestAndSet (&ch[channel].iob.ch_upslew, 0);
|
||||||
|
InterlockedBitTestAndReset (&ch[channel].iob.pc->exec_bypass, 0);
|
||||||
|
InterlockedBitTestAndSet (&ch[channel].exchange, 0);
|
||||||
|
}
|
||||||
|
#if !defined(linux) && !defined(__APPLE__)
|
||||||
|
_MM_SET_FLUSH_ZERO_MODE (_MM_FLUSH_ZERO_ON);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void pre_main_destroy (int channel)
|
||||||
|
{
|
||||||
|
IOB a = ch[channel].iob.pc;
|
||||||
|
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
||||||
|
InterlockedBitTestAndReset (&ch[channel].run, 0);
|
||||||
|
InterlockedBitTestAndSet (&ch[channel].iob.pc->exec_bypass, 0);
|
||||||
|
ReleaseSemaphore (a->Sem_BuffReady, 1, 0);
|
||||||
|
Sleep (25);
|
||||||
|
}
|
||||||
|
|
||||||
|
void post_main_destroy (int channel)
|
||||||
|
{
|
||||||
|
destroy_iobuffs (channel);
|
||||||
|
DeleteCriticalSection ( &ch[channel].csEXCH );
|
||||||
|
DeleteCriticalSection ( &ch[channel].csDSP );
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void CloseChannel (int channel)
|
||||||
|
{
|
||||||
|
pre_main_destroy (channel);
|
||||||
|
destroy_main (channel);
|
||||||
|
post_main_destroy (channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flushChannel (void* p)
|
||||||
|
{
|
||||||
|
int channel = (int)(uintptr_t)p;
|
||||||
|
IOB a = ch[channel].iob.pc;
|
||||||
|
while (!InterlockedAnd(&a->flush_bypass, 0xffffffff))
|
||||||
|
{
|
||||||
|
WaitForSingleObject(a->Sem_Flush, INFINITE);
|
||||||
|
if (!InterlockedAnd(&a->flush_bypass, 0xffffffff))
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&ch[channel].csDSP);
|
||||||
|
EnterCriticalSection(&ch[channel].csEXCH);
|
||||||
|
flush_iobuffs(channel);
|
||||||
|
InterlockedBitTestAndSet(&a->exec_bypass, 0);
|
||||||
|
flush_main(channel);
|
||||||
|
LeaveCriticalSection(&ch[channel].csEXCH);
|
||||||
|
LeaveCriticalSection(&ch[channel].csDSP);
|
||||||
|
InterlockedBitTestAndReset(&ch[channel].flushflag, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InterlockedBitTestAndReset(&a->flush_bypass, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* Channel Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetType (int channel, int type)
|
||||||
|
{ // no need to rebuild buffers; but we did anyway
|
||||||
|
if (type != ch[channel].type)
|
||||||
|
{
|
||||||
|
CloseChannel (channel);
|
||||||
|
ch[channel].type = type;
|
||||||
|
build_channel (channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetInputBuffsize (int channel, int in_size)
|
||||||
|
{ // we do not rebuild main here since it didn't change
|
||||||
|
if (in_size != ch[channel].in_size)
|
||||||
|
{
|
||||||
|
pre_main_destroy (channel);
|
||||||
|
post_main_destroy (channel);
|
||||||
|
ch[channel].in_size = in_size;
|
||||||
|
pre_main_build (channel);
|
||||||
|
post_main_build (channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetDSPBuffsize (int channel, int dsp_size)
|
||||||
|
{
|
||||||
|
if (dsp_size != ch[channel].dsp_size)
|
||||||
|
{
|
||||||
|
int oldstate = SetChannelState (channel, 0, 1);
|
||||||
|
pre_main_destroy (channel);
|
||||||
|
post_main_destroy (channel);
|
||||||
|
ch[channel].dsp_size = dsp_size;
|
||||||
|
pre_main_build (channel);
|
||||||
|
setDSPBuffsize_main (channel);
|
||||||
|
post_main_build (channel);
|
||||||
|
SetChannelState (channel, oldstate, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetInputSamplerate (int channel, int in_rate)
|
||||||
|
{ // no re-build of main required
|
||||||
|
if (in_rate != ch[channel].in_rate)
|
||||||
|
{
|
||||||
|
pre_main_destroy (channel);
|
||||||
|
post_main_destroy (channel);
|
||||||
|
ch[channel].in_rate = in_rate;
|
||||||
|
pre_main_build (channel);
|
||||||
|
setInputSamplerate_main (channel);
|
||||||
|
post_main_build (channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetDSPSamplerate (int channel, int dsp_rate)
|
||||||
|
{
|
||||||
|
if (dsp_rate != ch[channel].dsp_rate)
|
||||||
|
{
|
||||||
|
int oldstate = SetChannelState (channel, 0, 1);
|
||||||
|
pre_main_destroy (channel);
|
||||||
|
post_main_destroy (channel);
|
||||||
|
ch[channel].dsp_rate = dsp_rate;
|
||||||
|
pre_main_build (channel);
|
||||||
|
setDSPSamplerate_main (channel);
|
||||||
|
post_main_build (channel);
|
||||||
|
SetChannelState (channel, oldstate, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetOutputSamplerate (int channel, int out_rate)
|
||||||
|
{ // no re-build of main required
|
||||||
|
if (out_rate != ch[channel].out_rate)
|
||||||
|
{
|
||||||
|
pre_main_destroy (channel);
|
||||||
|
post_main_destroy (channel);
|
||||||
|
ch[channel].out_rate = out_rate;
|
||||||
|
pre_main_build (channel);
|
||||||
|
setOutputSamplerate_main (channel);
|
||||||
|
post_main_build (channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetAllRates (int channel, int in_rate, int dsp_rate, int out_rate)
|
||||||
|
{
|
||||||
|
if ((in_rate != ch[channel].in_rate) || (dsp_rate != ch[channel].dsp_rate) || (out_rate != ch[channel].out_rate))
|
||||||
|
{
|
||||||
|
pre_main_destroy (channel);
|
||||||
|
post_main_destroy (channel);
|
||||||
|
ch[channel].in_rate = in_rate;
|
||||||
|
ch[channel].dsp_rate = dsp_rate;
|
||||||
|
ch[channel].out_rate = out_rate;
|
||||||
|
pre_main_build (channel);
|
||||||
|
setInputSamplerate_main (channel);
|
||||||
|
setDSPSamplerate_main (channel);
|
||||||
|
setOutputSamplerate_main (channel);
|
||||||
|
post_main_build (channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
int SetChannelState (int channel, int state, int dmode)
|
||||||
|
{
|
||||||
|
IOB a = ch[channel].iob.pc;
|
||||||
|
int prior_state = ch[channel].state;
|
||||||
|
int count = 0;
|
||||||
|
const int timeout = 100;
|
||||||
|
if (ch[channel].state != state)
|
||||||
|
{
|
||||||
|
ch[channel].state = state;
|
||||||
|
switch (ch[channel].state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
InterlockedBitTestAndSet (&a->slew.downflag, 0);
|
||||||
|
InterlockedBitTestAndSet (&ch[channel].flushflag, 0);
|
||||||
|
if (dmode)
|
||||||
|
{
|
||||||
|
while (_InterlockedAnd (&ch[channel].flushflag, 1) && count < timeout)
|
||||||
|
{
|
||||||
|
Sleep(1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count >= timeout)
|
||||||
|
{
|
||||||
|
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
||||||
|
InterlockedBitTestAndReset (&ch[channel].flushflag, 0);
|
||||||
|
InterlockedBitTestAndReset (&a->slew.downflag, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
InterlockedBitTestAndSet (&a->slew.upflag, 0);
|
||||||
|
InterlockedBitTestAndSet (&ch[channel].iob.ch_upslew, 0);
|
||||||
|
InterlockedBitTestAndReset (&ch[channel].iob.pc->exec_bypass, 0);
|
||||||
|
InterlockedBitTestAndSet (&ch[channel].exchange, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prior_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetChannelTDelayUp (int channel, double time)
|
||||||
|
{
|
||||||
|
IOB a;
|
||||||
|
EnterCriticalSection (&ch[channel].csEXCH);
|
||||||
|
a = ch[channel].iob.pc;
|
||||||
|
ch[channel].tdelayup = time;
|
||||||
|
a->slew.ndelup = (int)(ch[a->channel].tdelayup * ch[a->channel].in_rate);
|
||||||
|
flush_slews (a);
|
||||||
|
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetChannelTSlewUp (int channel, double time)
|
||||||
|
{
|
||||||
|
IOB a;
|
||||||
|
EnterCriticalSection (&ch[channel].csEXCH);
|
||||||
|
a = ch[channel].iob.pc;
|
||||||
|
ch[channel].tslewup = time;
|
||||||
|
destroy_slews (a);
|
||||||
|
create_slews (a);
|
||||||
|
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetChannelTDelayDown (int channel, double time)
|
||||||
|
{
|
||||||
|
IOB a;
|
||||||
|
EnterCriticalSection (&ch[channel].csEXCH);
|
||||||
|
a = ch[channel].iob.pc;
|
||||||
|
ch[channel].tdelaydown = time;
|
||||||
|
a->slew.ndeldown = (int)(ch[a->channel].tdelaydown * ch[a->channel].out_rate);
|
||||||
|
flush_slews (a);
|
||||||
|
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetChannelTSlewDown (int channel, double time)
|
||||||
|
{
|
||||||
|
IOB a;
|
||||||
|
EnterCriticalSection (&ch[channel].csEXCH);
|
||||||
|
a = ch[channel].iob.pc;
|
||||||
|
ch[channel].tslewdown = time;
|
||||||
|
destroy_slews (a);
|
||||||
|
create_slews (a);
|
||||||
|
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/* channel.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _setupchannel_h
|
||||||
|
#define _setupchannel_h
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
struct _ch
|
||||||
|
{
|
||||||
|
int type;
|
||||||
|
volatile long run; // when 1, thread loops; when 0, thread terminates
|
||||||
|
volatile long exchange; // when 1, fexchange() operates; when 0, it just returns
|
||||||
|
int in_rate; // input samplerate
|
||||||
|
int out_rate; // output samplerate
|
||||||
|
int in_size; // input buffsize (complex samples) in a fexchange() operation
|
||||||
|
int dsp_rate; // sample rate for mainstream dsp processing
|
||||||
|
int dsp_size; // number complex samples processed per buffer in mainstream dsp processing
|
||||||
|
int dsp_insize; // size (complex samples) of the output of the r1 (input) buffer
|
||||||
|
int dsp_outsize; // size (complex samples) of the input of the r2 (output) buffer
|
||||||
|
int out_size; // output buffsize (complex samples) in a fexchange() operation
|
||||||
|
CRITICAL_SECTION csDSP; // used to block dsp while parameters are updated or buffers flushed
|
||||||
|
CRITICAL_SECTION csEXCH; // used to block fexchange() while parameters are updated or buffers flushed
|
||||||
|
int state; // 0 for channel OFF; 1 for channel ON
|
||||||
|
double tdelayup;
|
||||||
|
double tslewup;
|
||||||
|
double tdelaydown;
|
||||||
|
double tslewdown;
|
||||||
|
int bfo; // 'block_for_output', block fexchange until output is available
|
||||||
|
volatile long flushflag;
|
||||||
|
struct //io buffers
|
||||||
|
{
|
||||||
|
IOB pc, pd, pe, pf; // copies for console calls, dsp, exchange, and flush thread
|
||||||
|
volatile long ch_upslew;
|
||||||
|
} iob;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct _ch ch[];
|
||||||
|
|
||||||
|
PORT void OpenChannel (int channel, int in_size, int dsp_size, int input_samplerate, int dsp_rate, int output_samplerate, int type, int state, double tdelayup, double tslewup, double tdelaydown, double tslewdown, int bfo);
|
||||||
|
|
||||||
|
PORT void CloseChannel (int channel);
|
||||||
|
|
||||||
|
extern void flushChannel (void* p);
|
||||||
|
|
||||||
|
PORT void SetType (int channel, int type);
|
||||||
|
|
||||||
|
PORT void SetInputBuffsize (int channel, int in_size);
|
||||||
|
|
||||||
|
PORT void SetDSPBuffsize (int channel, int dsp_size);
|
||||||
|
|
||||||
|
PORT void SetInputSamplerate (int channel, int samplerate);
|
||||||
|
|
||||||
|
PORT void SetDSPSamplerate (int channel, int samplerate);
|
||||||
|
|
||||||
|
PORT void SetOutputSamplerate (int channel, int samplerate);
|
||||||
|
|
||||||
|
PORT void SetAllRates (int channel, int in_rate, int dsp_rate, int out_rate);
|
||||||
|
|
||||||
|
PORT int SetChannelState (int channel, int state, int dmode);
|
||||||
|
|
||||||
|
#endif
|
||||||
+151
@@ -0,0 +1,151 @@
|
|||||||
|
/* comm.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(linux) || defined(__APPLE__)
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "linux_port.h"
|
||||||
|
#ifdef ANDROID
|
||||||
|
#include <android/log.h>
|
||||||
|
#define APPNAME "WDSP"
|
||||||
|
#define LOGD(LOG_TAG, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||||
|
#define LOGI(LOG_TAG, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG,__VA_ARGS__)
|
||||||
|
#define LOGV(LOG_TAG, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
|
||||||
|
#define LOGW(LOG_TAG, ...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||||
|
#define LOGE(LOG_TAG, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <process.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <avrt.h>
|
||||||
|
#endif
|
||||||
|
#include "fftw3.h"
|
||||||
|
|
||||||
|
#include "amd.h"
|
||||||
|
#include "ammod.h"
|
||||||
|
#include "amsq.h"
|
||||||
|
#include "analyzer.h"
|
||||||
|
#include "anf.h"
|
||||||
|
#include "anr.h"
|
||||||
|
#include "bandpass.h"
|
||||||
|
#include "calcc.h"
|
||||||
|
#include "cblock.h"
|
||||||
|
#include "cfcomp.h"
|
||||||
|
#include "cfir.h"
|
||||||
|
#include "channel.h"
|
||||||
|
#include "compress.h"
|
||||||
|
#include "delay.h"
|
||||||
|
#include "dexp.h"
|
||||||
|
#include "div.h"
|
||||||
|
#include "eer.h"
|
||||||
|
#include "emnr.h"
|
||||||
|
#include "emph.h"
|
||||||
|
#include "eq.h"
|
||||||
|
#include "fcurve.h"
|
||||||
|
#include "fir.h"
|
||||||
|
#include "firmin.h"
|
||||||
|
#include "fmd.h"
|
||||||
|
#include "fmmod.h"
|
||||||
|
#include "fmsq.h"
|
||||||
|
#include "gain.h"
|
||||||
|
#include "gen.h"
|
||||||
|
#include "icfir.h"
|
||||||
|
#include "iir.h"
|
||||||
|
#include "iobuffs.h"
|
||||||
|
#include "iqc.h"
|
||||||
|
#include "lmath.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "meter.h"
|
||||||
|
#include "meterlog10.h"
|
||||||
|
#include "nbp.h"
|
||||||
|
#include "nob.h"
|
||||||
|
#include "nobII.h"
|
||||||
|
#include "osctrl.h"
|
||||||
|
#include "patchpanel.h"
|
||||||
|
#include "resample.h"
|
||||||
|
#include "rmatch.h"
|
||||||
|
#include "RXA.h"
|
||||||
|
#include "sender.h"
|
||||||
|
#include "shift.h"
|
||||||
|
#include "siphon.h"
|
||||||
|
#include "slew.h"
|
||||||
|
#include "snb.h"
|
||||||
|
#include "ssql.h"
|
||||||
|
#include "syncbuffs.h"
|
||||||
|
#include "TXA.h"
|
||||||
|
#include "utilities.h"
|
||||||
|
#include "varsamp.h"
|
||||||
|
#include "wcpAGC.h"
|
||||||
|
|
||||||
|
// manage differences among consoles
|
||||||
|
#define _Thetis
|
||||||
|
|
||||||
|
// channel definitions
|
||||||
|
#define MAX_CHANNELS 32 // maximum number of supported channels
|
||||||
|
#define DSP_MULT 2 // number of dsp_buffsizes that are held in an iobuff pseudo-ring
|
||||||
|
#define INREAL float // data type for channel input buffer
|
||||||
|
#define OUTREAL float // data type for channel output buffer
|
||||||
|
|
||||||
|
// display definitions
|
||||||
|
#define dMAX_DISPLAYS 64 // maximum number of displays = max instances
|
||||||
|
#define dMAX_STITCH 4 // maximum number of sub-spans to stitch together
|
||||||
|
#define dMAX_NUM_FFT 1 // maximum number of ffts for an elimination
|
||||||
|
#define dMAX_PIXELS 16384 // maximum number of pixels that can be requested
|
||||||
|
#define dMAX_AVERAGE 60 // maximum number of pixel frames that will be window-averaged
|
||||||
|
#ifdef _Thetis
|
||||||
|
#define dINREAL double
|
||||||
|
#else
|
||||||
|
#define dINREAL float
|
||||||
|
#endif
|
||||||
|
#define dOUTREAL float
|
||||||
|
#define dSAMP_BUFF_MULT 2 // ratio of input sample buffer size to fft size (for overlap)
|
||||||
|
#define dNUM_PIXEL_BUFFS 3 // number of pixel output buffers
|
||||||
|
#define dMAX_M 1 // number of variables to calibrate
|
||||||
|
#define dMAX_N 100 // maximum number of frequencies at which to calibrate
|
||||||
|
#define dMAX_CAL_SETS 2 // maximum number of calibration data sets
|
||||||
|
#define dMAX_PIXOUTS 4 // maximum number of det/avg/outputs per display instance
|
||||||
|
|
||||||
|
// wisdom definitions
|
||||||
|
#define MAX_WISDOM_SIZE_DISPLAY 262144
|
||||||
|
#define MAX_WISDOM_SIZE_FILTER 262144 // was 32769
|
||||||
|
|
||||||
|
// math definitions
|
||||||
|
#define PI 3.1415926535897932
|
||||||
|
#define TWOPI 6.2831853071795864
|
||||||
|
|
||||||
|
// miscellaneous
|
||||||
|
typedef double complex[2];
|
||||||
|
#define PORT __declspec( dllexport )
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
/* compress.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2011, 2013, 2017 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
This software is based upon the algorithm described by Peter Martinez, G3PLX,
|
||||||
|
in the January 2010 issue of RadCom magazine.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
|
||||||
|
COMPRESSOR create_compressor (
|
||||||
|
int run,
|
||||||
|
int buffsize,
|
||||||
|
double* inbuff,
|
||||||
|
double* outbuff,
|
||||||
|
double gain )
|
||||||
|
{
|
||||||
|
COMPRESSOR a;
|
||||||
|
a = (COMPRESSOR) malloc0 (sizeof (compressor));
|
||||||
|
a->run = run;
|
||||||
|
a->inbuff = inbuff;
|
||||||
|
a->outbuff = outbuff;
|
||||||
|
a->buffsize = buffsize;
|
||||||
|
a->gain = gain;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_compressor (COMPRESSOR a)
|
||||||
|
{
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_compressor (COMPRESSOR a)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void xcompressor (COMPRESSOR a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double mag;
|
||||||
|
if (a->run)
|
||||||
|
for (i = 0; i < a->buffsize; i++)
|
||||||
|
{
|
||||||
|
mag = sqrt(a->inbuff[2 * i + 0] * a->inbuff[2 * i + 0] + a->inbuff[2 * i + 1] * a->inbuff[2 * i + 1]);
|
||||||
|
if (a->gain * mag > 1.0)
|
||||||
|
a->outbuff[2 * i + 0] = a->inbuff[2 * i + 0] / mag;
|
||||||
|
else
|
||||||
|
a->outbuff[2 * i + 0] = a->inbuff[2 * i + 0] * a->gain;
|
||||||
|
a->outbuff[2 * i + 1] = 0.0;
|
||||||
|
}
|
||||||
|
else if (a->inbuff != a->outbuff)
|
||||||
|
memcpy(a->outbuff, a->inbuff, a->buffsize * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_compressor (COMPRESSOR a, double* in, double* out)
|
||||||
|
{
|
||||||
|
a->inbuff = in;
|
||||||
|
a->outbuff = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_compressor (COMPRESSOR a, int rate)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_compressor (COMPRESSOR a, int size)
|
||||||
|
{
|
||||||
|
a->buffsize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* TXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetTXACompressorRun (int channel, int run)
|
||||||
|
{
|
||||||
|
if (txa[channel].compressor.p->run != run)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
txa[channel].compressor.p->run = run;
|
||||||
|
TXASetupBPFilters (channel);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT void
|
||||||
|
SetTXACompressorGain (int channel, double gain)
|
||||||
|
{
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
txa[channel].compressor.p->gain = pow (10.0, gain / 20.0);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/* compress.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2011, 2013 Warren Pratt, NR0V
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
The author can be reached by email at
|
||||||
|
|
||||||
|
warren@wpratt.com
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _compressor_h
|
||||||
|
#define _compressor_h
|
||||||
|
|
||||||
|
typedef struct _compressor
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int buffsize;
|
||||||
|
double *inbuff;
|
||||||
|
double *outbuff;
|
||||||
|
double gain;
|
||||||
|
} compressor, *COMPRESSOR;
|
||||||
|
|
||||||
|
extern void xcompressor (COMPRESSOR a);
|
||||||
|
|
||||||
|
extern COMPRESSOR create_compressor (
|
||||||
|
int run,
|
||||||
|
int buffsize,
|
||||||
|
double* inbuff,
|
||||||
|
double* outbuff,
|
||||||
|
double gain );
|
||||||
|
|
||||||
|
extern void destroy_compressor (COMPRESSOR a);
|
||||||
|
|
||||||
|
extern void flush_compressor (COMPRESSOR a);
|
||||||
|
|
||||||
|
extern void setBuffers_compressor (COMPRESSOR a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_compressor (COMPRESSOR a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_compressor (COMPRESSOR a, int size);
|
||||||
|
|
||||||
|
// TXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXACompressorRun (int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXACompressorGain (int channel, double gain);
|
||||||
|
|
||||||
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user