15 Commits
Author SHA1 Message Date
James C. Ahlstrom b45a71b017 Quisk version 4.2.52 2026-05-30 11:18:52 -04:00
James C. Ahlstrom e6b9d9dd05 Quisk version 4.2.51 2026-05-10 15:22:33 -04:00
James C. Ahlstrom 1f68d05cd5 Quisk version 4.2.50 2025-12-29 14:01:36 -05:00
James C. Ahlstrom 018fc0e871 Quisk version 4.2.49 2025-12-22 15:12:47 -05:00
James C. Ahlstrom 1111d24333 Quisk version 4.2.48 2025-11-24 12:28:01 -05:00
James C. Ahlstrom d8aee9284d Quisk version 4.2.47 2025-11-22 13:39:10 -05:00
James C. Ahlstrom 7121dacbbd Quisk version 4.2.46 2025-10-12 12:43:21 -04:00
James C. Ahlstrom 0528e8bbbe Quisk version 4.2.45 2025-10-08 16:29:31 -04:00
James C. Ahlstrom 458e43252d Quisk version 4.2.44 2025-09-22 16:24:45 -04:00
James C. Ahlstrom fd0a8a3d0e Correct comment in Makefile 2025-09-09 17:00:29 -04:00
James C. Ahlstrom 6c2e9012c4 Quisk version 4.2.43 2025-09-08 12:06:26 -04:00
James C. Ahlstrom f5c4732eba Quisk version 4.2.42 2025-04-11 15:33:20 -04:00
James C. Ahlstrom 1c209457dc Quisk version 4.2.41 2025-01-30 13:48:38 -05:00
James C. Ahlstrom 7e8a0846f7 Quisk version 4.2.40 2024-11-15 16:39:47 -05:00
James C. Ahlstrom fb888a2e08 Quisk version 4.2.39 2024-10-01 14:22:23 -04:00
180 changed files with 70967 additions and 1899 deletions
+112
View File
@@ -1,3 +1,115 @@
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
================================
I fixed a bug in the Kenwood IF CAT command. Thanks Nick! I moved the Pulse server IP address to the Sound screen.
I added a CAT method to imitate the Elecraft K4 command set over a TCP port. Quisk now has four CAT controls. To
make this clearer I added more documentation to the Remote screen.
Quisk Version 4.2.41 January 2025
==================================
I made some improvements to the top-level Config/Radios screen. There is now an option to
make a new radio by copying settings from an existing radio. I worked around a bug in wxPython
and gtk3 that caused drawing problems on the config screens. This problem is not present on Windows.
Quisk Version 4.2.40 November 2024
===================================
Python 3.13 eliminated the telnetlib module, so I wrote a replacement. I fixed a bug in window sizing.
If you use the Dx Cluster feature, please report any problems. Dx Cluster used to use the telnetlib module
but is now new code.
I updated the install to Python 3.13. Please report any problems with Python 3.13.
Quisk Version 4.2.39 October 2024
==================================
I added an optional Hardware method PollGuiControl(self). If you define this in your Hardware file,
the GUI thread will call it frequently. It can be used to set PTT from your hardware.
I am trying to work around a wxPython quirk that causes mouse wheel tuning to fail. Please test.
Quisk Version 4.2.38 September 2024
====================================
This is a bugfix release. I fixed a bug in the Hamlib serial port when using a physical TTY instead
-21
View File
@@ -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
View File
@@ -1,2 +1,2 @@
#Quisk version 4.2.38
from .quisk import main
#Quisk version 4.2.52
from .quisk import main
+1 -1
View File
@@ -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"):
self.RemoteCtlSend("%s;%d\n" % (idName, app.idName2Button[idName].GetIndex()))
# 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:
for nid in menu.id2data:
menu_item = menu.FindItemById(nid)
+155
View File
@@ -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;
}
+19
View File
@@ -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 */
+261 -90
View File
@@ -48,6 +48,7 @@ name2format = {
# Increasing the software version will display a message to re-read the soapy device.
soapy_software_version = 3
wxpython_gtk3_bug = 0
def FormatKhz(dnum): # Round to 3 decimal places; remove ending ".000"
t = "%.3f" % dnum
@@ -87,6 +88,7 @@ class Configuration:
platform_ignore = 'win_'
self.sections = []
self.receiver_data = []
self.receiver_explain = []
self.StatePath = conf.settings_file_path
if not self.StatePath:
self.StatePath = os.path.join(app.QuiskFilesDir, "quisk_settings.json")
@@ -157,9 +159,9 @@ class Configuration:
dlg.Destroy()
else:
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'
if radio_type in ("Hermes", "Red Pitaya", "Odyssey2"):
if radio_type in ("Hermes", "Red Pitaya", "Odyssey2", "OpenHPSDR"):
if "Hermes_BandDict" not in radio_dict:
radio_dict["Hermes_BandDict"] = {}
if "Hermes_BandDictTx" not in radio_dict:
@@ -384,10 +386,20 @@ class Configuration:
app.bottom_widgets = dct['BottomWidgets'](app, hardware, conf, frame, gbs, vertBox)
return True
def OnPageChanging(self, event): # Called when the top level page changes (not RadioNotebook pages)
global wxpython_gtk3_bug
event.Skip()
notebook = event.GetEventObject()
index = event.GetSelection()
page = notebook.GetPage(index)
if sys.platform != 'win32': # Work around a bug in wxPython 4.2.1 and gtk3
w, h = application.main_frame.GetSize() # Change main window size by +/- one pixel to recalculate sizes
if wxpython_gtk3_bug:
wxpython_gtk3_bug = 0
h += 1
else:
wxpython_gtk3_bug = 1
h -= 1
application.main_frame.SetSize((w, h))
if isinstance(page, RadioNotebook):
if not page.pages:
page.MakePages()
@@ -419,6 +431,14 @@ class Configuration:
elif udp > 0:
return 'HiQSDR'
return 'SoftRock USB'
def FindNotebookPage(self, name):
for index in range(self.radios_page_start, self.notebk.GetPageCount()):
tab_text = self.notebk.GetPageText(index)
if tab_text[0] == '*':
tab_text = tab_text[1:-1]
if tab_text == name:
return index
return None
def AddRadio(self, radio_name, typ):
radio_dict = {}
radio_dict['hardware_file_type'] = typ
@@ -435,13 +455,24 @@ class Configuration:
page = RadioNotebook(self.notebk, radio_name)
self.notebk.AddPage(page, radio_name)
return True
def CopyRadio(self, old_name, new_name):
radio_dict = {}
index = Settings[2].index(old_name)
radio_dict.update(Settings[3][index])
Settings[2].append(new_name)
Settings[3].append(radio_dict)
page = RadioNotebook(self.notebk, new_name)
self.notebk.AddPage(page, new_name)
return True
def RenameRadio(self, old, new):
index = Settings[2].index(old)
n = self.radios_page_start + index
n = self.FindNotebookPage(old)
if n is None:
return
if old == Settings[1]:
self.notebk.SetPageText(n, "*%s*" % new)
else:
self.notebk.SetPageText(n, new)
index = Settings[2].index(old)
Settings[2][index] = new
self.notebk.GetPage(n).NewName(new)
if old == "ConfigFileRadio":
@@ -452,12 +483,10 @@ class Configuration:
index = Settings[2].index(name)
del Settings[2][index]
del Settings[3][index]
try:
n = self.radios_page_start + index
n = self.FindNotebookPage(name)
if n is not None:
self.notebk.DeletePage(n)
except:
pass
return True
return True
def GetRadioDict(self, radio_name=None): # None radio_name means the current radio
if radio_name:
index = Settings[2].index(radio_name)
@@ -573,6 +602,7 @@ class Configuration:
# Receiver sections start with 16 #, "Receivers ", receiver name, explain
# self.receiver_data is a list of [receiver_name, receiver_data]
# 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
# The format is integer, number, text, boolean, integer choice, text choice, rfile
@@ -610,6 +640,7 @@ class Configuration:
continue
if line[0:27] == '################ Receivers ':
section = 'Receivers'
self.receiver_explain.append(line[27:].strip())
args = line[27:].split(',', 1)
rxname = args[0].strip()
section_data = []
@@ -1295,6 +1326,7 @@ class RadioNotebook(wx.Notebook): # The second-level notebook for each radio nam
self.SetBackgroundColour(parent.bg_color)
self.radio_name = radio_name
self.pages = []
self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanging)
def MakePages(self):
radio_name = self.radio_name
radio_dict = local_conf.GetRadioDict(radio_name)
@@ -1309,7 +1341,16 @@ class RadioNotebook(wx.Notebook): # The second-level notebook for each radio nam
self.AddPage(page, "Sound")
self.pages.append(page)
for section, names in local_conf.sections:
if section in ('Sound', 'Bands', 'Filters'): # There is a special page for these sections
if section == 'Sound': # There is a special page for these sections
continue
if section == 'Bands':
continue
if section == 'Filters':
continue
if section == 'Remote':
page = RadioRemote(self, radio_name, section, names)
self.AddPage(page, section)
self.pages.append(page)
continue
page = RadioSection(self, radio_name, section, names)
self.AddPage(page, section)
@@ -1325,6 +1366,20 @@ class RadioNotebook(wx.Notebook): # The second-level notebook for each radio nam
self.radio_name = new_name
for page in self.pages:
page.radio_name = new_name
def OnPageChanging(self, event):
global wxpython_gtk3_bug
event.Skip()
index = event.GetSelection()
page = self.GetPage(index)
if sys.platform != 'win32': # Work around a bug in wxPython 4.2.1 and gtk3
w, h = application.main_frame.GetSize() # Change main window size by +/- one pixel to recalculate sizes
if wxpython_gtk3_bug:
wxpython_gtk3_bug = 0
h += 1
else:
wxpython_gtk3_bug = 1
h -= 1
application.main_frame.SetSize((w, h))
class ChoiceCombo(wx.Choice):
text_for_blank = "-blank-"
@@ -1439,7 +1494,13 @@ class ComboCtrl(wxcombo.ComboCtrl):
wm = w
wm += charx * 5
self.SetSizeHints(wm, self.height, 9999, self.height)
def SetValue(self, value):
wxcombo.ComboCtrl.SetValue(self, value)
self.SetBackgroundColour(self.bgColor)
self.Refresh()
def SetSelection(self, n): # Set text to item in list box. Name conflict with wxcombo.ComboCtrl.
self.SetBackgroundColour(self.bgColor)
self.Refresh()
try:
text = self.choices[n]
except IndexError:
@@ -1930,6 +1991,16 @@ class ControlMixin:
btn.SetSizeHints(h, h, h, h)
self.gbs.Add(btn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border)
return sld, btn
def AddTextHeading(self, key, text, help_text):
if key in text: # Start of heading
self.NextRow()
self.SetFont(self.heading_font)
self.AddTextL(1, help_text, span=7)
self.SetFont(self.font)
self.NextRow()
self.col = 2
return key
return None
def AddPopupMenuHelp(self, Qclass, col, text, btn_text, help_text, span1=1, span2=1, border=1):
if text:
txt = wx.StaticText(self, -1, text)
@@ -2230,8 +2301,8 @@ class ControlMixin:
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',
'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',
'fixed_tune_offset'):
'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec',
'fixed_tune_offset', 'calibrate_temp_20', 'calibrate_temp_40', 'calibrate_current_0', 'calibrate_current_1'):
setattr(conf, name, x)
application.ImmediateChange(name)
elif name[0:4] in ('lin_', 'win_'):
@@ -2328,10 +2399,11 @@ class ControlMixin:
else:
return value
class BaseWindow(wx.ScrolledCanvas, ControlMixin):
class BaseWindow(wx.ScrolledWindow, ControlMixin):
def __init__(self, parent):
wx.ScrolledCanvas.__init__(self, parent)
wx.ScrolledWindow.__init__(self, parent)
ControlMixin.__init__(self, parent)
self.SetScrollRate(8, 8)
class ConfigConfig(BaseWindow):
def __init__(self, parent, width):
@@ -2442,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)
self.NextRow()
self.FitInside()
self.SetScrollRate(1, 1)
def MakeFileButton(self, text, path, index, name, help_text):
if index < 10: # record buttons
cb = self.AddCheckBox(4, text, self.OnCheckRecPlay)
@@ -2620,7 +2691,6 @@ class ConfigTxAudio(BaseWindow):
application.CtrlTxAudioPreemph = sld
self.NextRow()
self.FitInside()
self.SetScrollRate(1, 1)
def OnTimer(self, event):
if not self.tmp_playing:
self.timer.Stop()
@@ -2653,48 +2723,81 @@ class Radios(BaseWindow): # The "Radios" first-level page
def __init__(self, parent):
BaseWindow.__init__(self, parent)
self.SetBackgroundColour(parent.bg_color)
self.num_cols = 8
self.num_cols = 5
self.radio_name = None
self.cur_radio_text = self.AddTextL(1, 'xx', self.num_cols - 1)
self.SetCurrentRadioText()
self.index = 0
txt = 'Each "radio" is a block of settings that describes a radio to use with Quisk.'
self.AddTextL(1, txt, self.num_cols - 1)
self.NextRow()
self.NextRow()
item = self.AddTextL(1, "When Quisk starts, use the radio")
self.radio_buttons = []
rb = self.AddRadioButton(1, "When Quisk starts, use the radio", self.NewTop, start=True)
rb.SetValue(True)
self.start_radio = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
self.start_radio.handler = self.OnChoiceStartup
self.radio_buttons.append((rb, self.start_radio))
self.NextRow()
item = self.AddTextL(1, "Add a new radio with the general type")
rb = self.AddRadioButton(1, "Add a new radio with the general type", self.NewTop)
choices = []
for name, data in local_conf.receiver_data:
choices.append(name)
for explain in local_conf.receiver_explain:
choices.append(explain)
choices.sort()
self.add_type = self.AddComboCtrl(2, '', choices=choices, no_edit=True)
self.add_type.SetSelection(0)
item = self.AddTextL(3, "and name the new radio")
self.add_name = self.AddComboCtrl(4, '', choices=["My Radio", "SR with XVtr", "SoftRock"])
item = self.AddPushButton(5, "Add", self.OnBtnAdd)
self.add_name = self.AddComboCtrl(4, '', choices=["My Radio", "Transverter", "SoftRock", "HL2"])
self.radio_buttons.append((rb, self.add_type, self.add_name))
self.NextRow()
item = self.AddTextL(1, "Rename the radio named")
rb = self.AddRadioButton(1, "Add a new radio copied from radio", self.NewTop)
self.add_from = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
item = self.AddTextL(3, "and name the new radio")
self.add_name2 = self.AddComboCtrl(4, '', choices=["My Radio", "Transverter", "SoftRock", "HL2"])
self.radio_buttons.append((rb, self.add_from, self.add_name2))
self.NextRow()
rb = self.AddRadioButton(1, "Rename the radio named", self.NewTop)
self.rename_old = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
item = self.AddTextL(3, "to the new name")
self.rename_new = self.AddComboCtrl(4, '', choices=["My Radio", "SR with XVtr", "SoftRock"])
item = self.AddPushButton(5, "Rename", self.OnBtnRename)
self.rename_new = self.AddComboCtrl(4, '', choices=["My Radio", "Transverter", "SoftRock", "HL2"])
self.radio_buttons.append((rb, self.rename_old, self.rename_new))
self.NextRow()
item = self.AddTextL(1, "Delete the radio named")
rb = self.AddRadioButton(1, "Delete the radio named", self.NewTop)
self.delete_name = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
item = self.AddPushButton(3, "Delete", self.OnBtnDelete)
self.radio_buttons.append((rb, self.delete_name))
self.NextRow()
self.FitInside()
self.SetScrollRate(1, 1)
self.NextRow()
self.apply_btn = self.AddPushButton(3, "Apply", self.OnBtnApply)
self.NewRadioNames()
def SetCurrentRadioText(self):
radio_dict = local_conf.GetRadioDict(self.radio_name)
radio_type = radio_dict['hardware_file_type']
if Settings[1] == "ConfigFileRadio":
text = 'The current radio is ConfigFileRadio, so all settings come from the config file. The hardware type is %s.' % radio_type
self.NewTop()
def NewTop(self, event=None):
if event:
btn = event.GetEventObject()
else: # set first button
btn = self.radio_buttons[0][0]
for i in range(len(self.radio_buttons)):
data = self.radio_buttons[i]
rb = data[0]
if rb == btn:
self.index = i
for c in data[1:]:
c.Enable(rb == btn)
if self.index == 0:
self.apply_btn.Enable(False)
else:
text = "Quisk is running with settings from the radio %s. The hardware type is %s." % (Settings[1], radio_type)
self.cur_radio_text.SetLabel(text)
self.apply_btn.Enable(True)
def OnBtnApply(self, event):
index = self.index
ok = False
if index == 1: # add from hardware type
ok = self.OnBtnAdd()
elif index == 2: # add from existing radio
ok = self.OnBtnCopy()
elif index == 3: # rename
ok = self.OnBtnRename()
elif index == 4: # delete
ok = self.OnBtnDelete()
if ok:
self.radio_buttons[0][0].SetValue(True)
self.NewTop()
def DuplicateName(self, name):
if name in Settings[2] or name == "ConfigFileRadio":
dlg = wx.MessageDialog(self, "The name already exists. Please choose a different name.",
@@ -2703,34 +2806,39 @@ class Radios(BaseWindow): # The "Radios" first-level page
dlg.Destroy()
return True
return False
def OnBtnAdd(self, event):
def OnBtnAdd(self):
name = self.add_name.GetValue().strip()
if not name or self.DuplicateName(name):
return
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 Settings[0] != "Ask me":
Settings[0] = name
self.NewRadioNames()
local_conf.settings_changed = True
def OnBtnRename(self, event):
return True
def OnBtnCopy(self):
new_name = self.add_name2.GetValue().strip()
if not new_name or self.DuplicateName(new_name):
return
self.add_name2.SetValue('')
old_name = self.add_from.GetValue()
if local_conf.CopyRadio(old_name, new_name):
self.NewRadioNames()
local_conf.settings_changed = True
return True
def OnBtnRename(self):
old = self.rename_old.GetValue()
new = self.rename_new.GetValue().strip()
if not old or not new or self.DuplicateName(new):
return
self.rename_new.SetValue('')
if local_conf.RenameRadio(old, new):
if old == 'ConfigFileRadio' and Settings[1] == "ConfigFileRadio":
if Settings[1] == old:
Settings[1] = new
elif Settings[1] == old:
Settings[1] = new
self.SetCurrentRadioText()
if Settings[0] != "Ask me":
Settings[0] = new
self.NewRadioNames()
local_conf.settings_changed = True
def OnBtnDelete(self, event):
return True
def OnBtnDelete(self):
name = self.delete_name.GetValue()
if not name:
return
@@ -2742,6 +2850,7 @@ class Radios(BaseWindow): # The "Radios" first-level page
if ret == wx.ID_OK and local_conf.DeleteRadio(name):
self.NewRadioNames()
local_conf.settings_changed = True
return True
def OnChoiceStartup(self, ctrl):
choice = self.start_radio.GetValue()
if Settings[0] != choice:
@@ -2751,6 +2860,8 @@ class Radios(BaseWindow): # The "Radios" first-level page
choices = Settings[2][:] # can rename any available radio
self.rename_old.SetItems(choices)
self.rename_old.SetSelection(0)
self.add_from.SetItems(choices)
self.add_from.SetSelection(0)
if "ConfigFileRadio" in choices: # can not delete ConfigFileRadio
choices.remove("ConfigFileRadio")
self.delete_name.SetItems(choices)
@@ -2762,13 +2873,13 @@ class Radios(BaseWindow): # The "Radios" first-level page
try: # Set text in control
index = choices.index(Settings[0]) # last used radio, or new or renamed radio
except:
num = len(Settings[2])
if len == 0:
index = 1
elif num == 1:
index = 0
num = len(Settings[2]) # number of radios
if num == 0: # no radios
index = 1 # use ConfigFileRadio
elif num == 1: # one radio
index = 0 # use the one radio
else:
index = len(choices) - 2
index = len(choices) - 2 # use the last radio
Settings[0] = choices[index]
self.start_radio.SetSelection(index)
@@ -2799,16 +2910,7 @@ class RadioSection(BaseWindow): # The pages for each section in the second-leve
radio_dict = local_conf.GetRadioDict(self.radio_name)
radio_type = radio_dict['hardware_file_type']
for name, text, fmt, help_text, values in self.names:
if name == 'remote_radio_password':
self.AddTextButtonHelp(col, text, "Change", self.OnChangePassword, help_text)
if col == 1:
col = 4
else:
col = 1
self.NextRow()
elif name == 'remote_radio_ip' and radio_type != "Control Head":
continue
elif name == 'favorites_file_path':
if name == 'favorites_file_path':
self.favorites_path = radio_dict.get('favorites_file_path', '')
row = self.row
self.row = 1
@@ -2902,7 +3004,6 @@ class RadioSection(BaseWindow): # The pages for each section in the second-leve
self.AddColSpacer(2, 20)
self.AddColSpacer(5, 20)
self.FitInside()
self.SetScrollRate(1, 1)
def OnButtonChangeFavorites(self, event):
if isinstance(event, (ChoiceCombo, ComboCtrl)):
path = event.GetValue()
@@ -2996,14 +3097,6 @@ class RadioSection(BaseWindow): # The pages for each section in the second-leve
index = self.list_ctrl.GetFirstSelected()
if index >= 0:
self.list_ctrl.Select(index, 0)
def OnChangePassword(self, event):
text = local_conf.globals.get('remote_radio_password', '')
dlg = wx.TextEntryDialog(self, "Please enter a password for remote access",
"Password Entry", text)
if dlg.ShowModal() == wx.ID_OK:
local_conf.globals["remote_radio_password"] = dlg.GetValue()
local_conf.settings_changed = True
local_conf.SaveState()
class RadioHardwareBase(BaseWindow): # The Hardware page in the second-level notebook for each radio
def __init__(self, parent, radio_name):
@@ -3035,7 +3128,6 @@ class RadioHardwareBase(BaseWindow): # The Hardware page in the second-level no
self.NextRow(7)
self.AddColSpacer(2, 20)
self.AddColSpacer(5, 20)
self.SetScrollRate(1, 1)
def OnButtonChangeHardware(self, event):
if isinstance(event, (ChoiceCombo, ComboCtrl)):
path = event.GetValue()
@@ -3082,25 +3174,24 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level
data_names = local_conf.GetReceiverData(radio_type)
col = 1
border = 2
hermes_board_id = 0
if radio_type == "Hermes":
try:
hermes_board_id = application.Hardware.hermes_board_id
except:
pass
try:
is_HermesLite2 = application.Hardware.is_HermesLite2
except:
is_HermesLite2 = False
if radio_name == Settings[1] and hasattr(application.Hardware, "ProgramGateware"):
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)
col = 1
self.NextRow(self.row + 2)
for name, text, fmt, help_text, values in data_names:
text = text.replace("\\u2103", "\u2103")
if name in ('hardware_file_name', 'widgets_file_name'):
pass
elif name[0:4] == platform_ignore:
pass
elif name in ('Hermes_BandDictEnTx', ):
pass
elif 'Hl2_' in name and hermes_board_id != 6:
elif 'Hl2_' in name and not is_HermesLite2:
pass
elif fmt[0:4] in ('dict', 'list'):
pass
@@ -3129,7 +3220,7 @@ class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level
col = 1
border = 2
self.NextRow()
if hermes_board_id == 6:
if is_HermesLite2:
if col == 4:
self.NextRow()
help_text = ('This controls the bias level for transistors in the final power amplifier. Enter a level from 0 to 255.'
@@ -3145,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.Enable(enbl)
self.FitInside()
self.SetScrollRate(1, 1)
def OnHermesChangeBias0(self, event):
value = self.HermesBias0.GetValue()
application.Hardware.ChangeBias0(value)
@@ -3656,7 +3746,7 @@ class RadioSound(BaseWindow): # The Sound page in the second-level notebook for
self.radio_dict = local_conf.GetRadioDict(self.radio_name)
self.num_cols = 8
for name, text, fmt, help_text, values in local_conf.GetSectionData('Sound'):
if name in ('digital_output_level', 'file_play_level'):
if name in ('digital_output_level', 'file_play_level', 'IQ_Server_IP'):
value = self.GetValue(name, self.radio_dict)
no_edit = "choice" in fmt or fmt == 'boolean'
txt, cb, btn = self.AddTextComboHelp(1, text, value, values, help_text, no_edit)
@@ -3759,7 +3849,6 @@ class RadioSound(BaseWindow): # The Sound page in the second-level notebook for
break
self.gbs = gbs
self.FitInside()
self.SetScrollRate(1, 1)
def ItemValue(self, row, col):
data_name = self.sound_names[row][col]
if col == 4: # Device names
@@ -3987,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.NextRow()
self.FitInside()
self.SetScrollRate(1, 1)
def SortCmp(self, item1):
# Numerical conversion of band name to megahertz
try:
@@ -4091,6 +4179,90 @@ The Tx bits are sent as the "Alex" filters and are sent to the J16 interface.'
if hasattr(application.Hardware, "ImmediateChange"):
application.Hardware.ImmediateChange(name)
class RadioRemote(BaseWindow): # The Remote page in the second-level notebook for each radio
def __init__(self, parent, radio_name, section, names):
BaseWindow.__init__(self, parent)
self.radio_name = radio_name
self.section = section
self.names = names
self.heading_font = wx.Font(conf.config_font_size, wx.FONTFAMILY_SWISS, wx.ITALIC,
wx.FONTWEIGHT_NORMAL, False, conf.quisk_typeface)
self.MakeControls()
def MakeControls(self):
self.num_cols = 8
#self.MarkCols()
self.AddColSpacer(1, 3)
self.NextRow()
self.col = 2
radio_dict = local_conf.GetRadioDict(self.radio_name)
radio_type = radio_dict['hardware_file_type']
heading = None
for name, text, fmt, help_text, values in self.names:
if heading:
if heading not in text: # End of heading
heading = None
self.NextRow()
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:
heading = self.AddTextHeading("Hamlib Rig 2", text,
'Rig control commands using "Hamlib Rig 2" are sent to a Quisk TCP port from multiple clients.')
if not heading:
heading = self.AddTextHeading("XML-RPC", text,
'XML-RPC is a protocol that allows clients to call methods on a remote server using XML passed via HTTP')
if not heading:
heading = self.AddTextHeading("CAT serial", text,
'"CAT serial" supports the FlexRadio PowerSDR 2.x command set sent over a serial port. Some Kenwood commands are included.')
if not heading:
heading = self.AddTextHeading("remote radio", text,
'Quisk can be used as a control head to control a real radio located remotely. This feature is due to AC2YD.')
if not heading:
heading = self.AddTextHeading("K4 TCP", text,
'Rig control commands using "K4 TCP" use the Elecraft K4 command set, and are sent to a Quisk TCP port from multiple clients.')
if not heading:
heading = self.AddTextHeading("Dx cluster", text,
'"Dx Cluster" is a telnet service used to announce Dx spots in real time.')
if name == 'remote_radio_password':
self.AddTextButtonHelp(self.col, text, "Change", self.OnChangePassword, help_text)
if self.col == 2:
self.col = 5
else:
self.col = 2
self.NextRow()
elif name == 'remote_radio_ip' and radio_type != "Control Head":
continue
else:
if fmt[0:4] in ('dict', 'list'):
continue
if name[0:4] == platform_ignore:
continue
value = self.GetValue(name, radio_dict)
no_edit = "choice" in fmt or fmt == 'boolean'
txt, cb, btn = self.AddTextComboHelp(self.col, text, value, values, help_text, no_edit)
cb.handler = self.OnChange
cb.quisk_data_name = name
if self.col == 2:
self.col = 5
else:
self.col = 2
self.NextRow()
self.NextRow()
self.AddTextL(1, "End of list", span=7)
self.AddColSpacer(3, 20)
self.AddColSpacer(6, 20)
self.FitInside()
def OnChangePassword(self, event):
text = local_conf.globals.get('remote_radio_password', '')
dlg = wx.TextEntryDialog(self, "Please enter a password for remote access",
"Password Entry", text)
if dlg.ShowModal() == wx.ID_OK:
local_conf.globals["remote_radio_password"] = dlg.GetValue()
local_conf.settings_changed = True
local_conf.SaveState()
class xxRadioFilters(BaseWindow): # The Filters page in the second-level notebook for each radio
def __init__(self, parent, radio_name):
BaseWindow.__init__(self, parent)
@@ -4162,7 +4334,6 @@ class xxRadioFilters(BaseWindow): # The Filters page in the second-level notebo
self.NextRow()
index += 1
self.FitInside()
self.SetScrollRate(1, 1)
def OnChangeFreq(self, event):
freq = event.GetString()
radio_dict = local_conf.GetRadioDict(self.radio_name)
+89 -7
View File
@@ -73,6 +73,9 @@ h3 {font-size:1.6rem;}
<li><a href="#Digital">Digital Modes</a></li>
<li><a href="#Hamlib">Rig Control and Logging</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="#Remote">Remote Operation</a></li>
</ul>
@@ -81,7 +84,7 @@ h3 {font-size:1.6rem;}
<div class="contents">
<h1>
<a id="Welcome"></a>
Welcome to QUISK (December 2023)
Welcome to QUISK (September 2025)
</h1>
<p>
@@ -194,17 +197,18 @@ Quisk is free open source software written in Python and C. It is hosted on the
<br>
<p>
If you have Python 3 installed on your computer, just continue to use that version.
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11 or 3.12.</b>
<b>On Windows Quisk requires Python 3.8, 3.9, 3.10, 3.11, 3.12 or 3.13.</b>
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.
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>
<br>
<h3 id="g0.2.2">Windows Initial Installation</h3>
<p>
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.
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11 or 3.12.</b>
<b>On Windows Quisk requires Python 3.8, 3.9, 3.10, 3.11, 3.12 or 3.13.</b>
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>.
There is an option to <b>Add Python to PATH</b>. You MUST select
@@ -256,6 +260,7 @@ If you change to that directory you can run Quisk with "pythonw quisk.py".
<br>
<p>
To get started you must tell Quisk what kind of radio hardware you have. Press the Config button and select Radios.
Add a radio from the list of hardware radios available. Or add the best match.
Then set your sound devices for that radio; the device for the radio speakers, the microphone and so forth. All configuration
is (mostly) from the Config button. Ignore old directions and don't bother with a config file.
</p>
@@ -372,12 +377,13 @@ python3 quisk.py
<br>
<p>
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>
<br>
<pre>
cd ~/quisk
git pull
make
</pre>
<br>
<p>
@@ -385,6 +391,7 @@ You run Quisk by running the file quisk.py with python3. For example, "python3 /
You can create a desktop icon to do that, but the method depends on your version of Linux.
<b>Never run Quisk with root privelege by using sudo. Fix the permission problem instead.</b>
To get started you must tell Quisk what kind of radio hardware you have. Press the Config button and select Radios.
Add a radio from the list of hardware radios available. Or add the best match.
Then set your sound devices for that radio; the device for the radio speakers, the microphone and so forth. All configuration
is (mostly) from the Config button. Ignore old directions and don't bother with a config file.
To uninstall Quisk, just delete the quisk directory.
@@ -532,7 +539,7 @@ When you first install Quisk, you will not have any settings for your radio.
Press the Config button and go to the Radios screen.
Then create a radio by specifying the general hardware type and give it a name of your choosing.
For a Hermes-Lite, specify "Hermes" as the hardware type and call it "HL2" (or some other name).
Press "Add" and a new tab for your radio will appear. Look through the various settings on the HL2 tab.
Press "Apply" and a new tab for your radio will appear. Look through the various settings on the HL2 tab.
The parameters for the radios are stored in the file quisk_settings.json.
<br>
<br>
@@ -1427,9 +1434,35 @@ is no longer needed
<br>
</p>
<h2 id="Digital">Digital Modes </h2>
<br>
<h3>Digital Modes with the TCI Protocol</h3>
<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
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
device. The left and right channel are the same.
The bandwidth is set with the filter buttons as
@@ -1493,16 +1526,24 @@ at his site <a href="https://dk1mi.radio/quisk-fldigi/">https://dk1mi.radio/quis
If your digital program only uses Alsa devices or you don't want to use PulseAudio,
you can use the ALSA loopback device.
It works the same way as the Windows VAC. First create the loopback device with this command:
<br>
<br>
<pre>
sudo modprobe snd-aloop index=19
</pre>
<br>
<p>
The "index" is the card number, and it is best to use a number higher than your regular cards.
The loopback device will be gone on the next restart.
You can create the loopback device every time the system starts, but the way to do that depends on your version of Linux.
You could add this line to /etc/modprobe.d/alsa-base.conf:
</p>
<br>
<pre>
options snd-aloop index=19
</pre>
<br>
<p>
Or you could add snd-aloop to /etc/modules or put the modprobe command in /etc/rc.local instead.
Restart Linux. Now you can enter "cat /proc/asound/cards" to
print out your sound cards, and you should see a "Loopback" card listed.
@@ -1517,6 +1558,7 @@ Your audio is now connected and you
should be able to receive digital signals. Be sure to test your
transmit signal off the air. You may need to reduce power to
improve linearity.
</p>
<br>
<br>
@@ -1592,6 +1634,46 @@ For Linux, use the pulseaudio devices:
</tr>
</table>
<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>
<p>
If you have my transceiver hardware from 2010 QEX, or the newer HiQSDR
+87 -39
View File
@@ -1,9 +1,9 @@
from __future__ import absolute_import
# This code was contributed by Christof, DJ4CM. Many Thanks!!
# Modified November 2024 by N2ADR to remove deprecated telnetlib module.
import threading
import socket
import time
import telnetlib
import quisk_conf_defaults as conf
class DxEntry:
@@ -81,61 +81,109 @@ class DxEntry:
class DxCluster(threading.Thread):
def __init__(self):
self.do_init = 1
self.error = 'Starting'
self.dxSpots = []
threading.Thread.__init__(self)
self.doQuit = threading.Event()
self.dxSpots = []
self.doQuit.clear()
self.dxLock = threading.Lock()
self.addr = conf.dxClHost + ':' + str(conf.dxClPort)
self.msg_no_spots = "No DX Cluster data from " + self.addr
self.msg_one_spot = '1 DX spot received from ' + self.addr
self.msg_spots = ' DX spots received from ' + self.addr
def run(self):
self.telnetInit()
self.telnetConnect()
if self.error:
self.sock.close()
return
while not self.doQuit.isSet():
try:
self.telnetRead()
by = self.sock.recv(1024)
except TimeoutError:
continue
except:
self.tn.close()
time.sleep(20)
by = b''
if by:
with self.dxLock:
self.bytes += by
else:
self.sock.close()
self.error = "Restarting " + self.addr
time.sleep(2)
if not self.doQuit.isSet():
self.telnetConnect()
self.tn.close()
def setListener (self, listener):
self.listener = listener
if self.error:
self.sock.close()
return
self.sock.close()
def telnetInit(self):
self.tn = telnetlib.Telnet()
def telnetConnect(self):
self.bytes = bytearray(0)
self.error = 'Starting'
self.sock = socket.socket()
self.sock.settimeout(20)
try:
self.sock.connect( (conf.dxClHost, conf.dxClPort) )
except:
self.error = "Failed to connect to " + self.addr
return
self.sock.settimeout(1)
for i in range(10):
try:
self.tn.open(conf.dxClHost, conf.dxClPort, 10)
self.tn.read_until(b"login:", 10)
self.tn.write(conf.user_call_sign.encode('utf-8', errors='ignore') + b"\n") # user_call_sign may be Unicode
break
self.bytes += self.sock.recv(1024)
except:
time.sleep(0.5)
pass
if b"login:" in self.bytes:
break
else:
self.error = 'No "login:" prompt from ' + self.addr
return
self.sock.sendall(conf.user_call_sign.encode('utf-8', errors='ignore') + b"\r\n")
if conf.dxClPassword:
self.tn.read_until(b"Password: ")
self.tn.write(conf.dxClPassword.encode('utf-8', errors='ignore') + b"\n")
for i in range(10):
try:
self.bytes += self.sock.recv(1024)
except:
pass
if b"Password:" in self.bytes:
break
else:
self.error = 'No "Password:" prompt from ' + self.addr
return
self.sock.sendall(conf.dxClPassword.encode('utf-8', errors='ignore') + b"\r\n")
self.error = ''
self.bytes = bytearray(0)
def telnetRead(self):
message = self.tn.read_until(b'\n', 60).decode(encoding='utf-8', errors='replace')
if self.doQuit.isSet() == False:
dxEntry = DxEntry();
if dxEntry.parseMessage(message):
for i, listElement in enumerate(self.dxSpots):
if (listElement.equal(dxEntry)):
listElement.join (dxEntry)
return
if listElement.isExpired():
del (self.dxSpots[i])
self.dxSpots.append(dxEntry)
if self.listener:
self.listener()
def getHost(self):
return self.tn.host + ':' + str(self.tn.port)
def Poll(self):
with self.dxLock:
index = self.bytes.find(b"\n")
if index >= 0:
message = self.bytes[0:index + 1]
self.bytes = self.bytes[index + 1:]
else:
return
message = message.decode(encoding='utf-8', errors='replace')
dxEntry = DxEntry()
if dxEntry.parseMessage(message):
for i, listElement in enumerate(self.dxSpots):
if (listElement.equal(dxEntry)):
listElement.join (dxEntry)
return True
if listElement.isExpired():
del (self.dxSpots[i])
self.dxSpots.append(dxEntry)
return True
def stop(self):
self.doQuit.set()
def dxStatus(self):
if self.error:
return self.error
nSpots = len(self.dxSpots)
if nSpots == 0:
return self.msg_no_spots
elif nSpots == 1:
return self.msg_one_spot
return str(nSpots) + self.msg_spots
+1 -681
View File
@@ -1,681 +1 @@
#include <Python.h>
#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);
}
// This file is no longer used.
+1 -24
View File
@@ -1,24 +1 @@
extern int freedv_current_mode; // the current FreeDV running mode
// 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
// This header file is no longer used.
+1 -129
View File
@@ -1,129 +1 @@
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
This directory is no longer used by FreeDV. Please see the documentation.
Binary file not shown.
Binary file not shown.
+144
View File
@@ -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);
}
+157
View File
@@ -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
View File
@@ -15,6 +15,7 @@ from quisk_hardware_model import Hardware as BaseHardware
DEBUG = 0
DEBUG_I2C = 0
DEBUG_SENSORS = 0
class IOBoard:
"This class controls the N2ADR IO Board for the HermesLite 2"
@@ -46,6 +47,8 @@ class IOBoard:
self.old_receive = None
self.slow = 0
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'):
return
if self.have_IO_Board is None:
@@ -154,6 +157,7 @@ class Hardware(BaseHardware):
self.hermes_ip = ""
self.hermes_code_version = -1
self.hermes_board_id = -1
self.is_HermesLite2 = False
self.hermes_temperature = 0.0
self.hermes_fwd_power = 0.0
self.hermes_rev_power = 0.0
@@ -166,7 +170,10 @@ class Hardware(BaseHardware):
self.vna_count = 0
self.vna_started = False
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.delay_config = True # Delay sending message to HL2 until after sound starts
self.TFRC_counter = 0 # Call for power etc. at intervals
@@ -271,6 +278,7 @@ class Hardware(BaseHardware):
self.hermes_ip = addr[0]
self.hermes_code_version = data[9]
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])
if data[0x16] >> 6 == 0:
QS.set_params(bandscopeScale = 2048)
@@ -317,6 +325,7 @@ class Hardware(BaseHardware):
bid = 6
self.hermes_code_version = code
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)
st = 'Capture from Hermes device at specified IP %s' % self.hermes_ip
found = True
@@ -331,9 +340,13 @@ class Hardware(BaseHardware):
def open(self):
self.delay_config = True # Delay sending message to HL2 until after sound starts
# This list only changes control bits; no use of WriteQueue()
for name in ('keyupDelay',
'hermes_lowpwr_tr_enable', 'hermes_PWM', 'hermes_disable_sync', 'hermes_power_amp', 'Hermes_BandDictEnTx'):
self.ImmediateChange(name)
self.ImmediateChange('keyupDelay')
self.ImmediateChange('Hermes_BandDictEnTx')
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
def GetValue(self, name): # return values stored in the hardware
if name == 'Hware_Hl2_EepromIP':
@@ -492,7 +505,15 @@ class Hardware(BaseHardware):
self.key_was_down = key_down
if self.TFRC_counter >= 3:
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:
self.application.bottom_widgets.UpdateText()
if self.delay_config and QS.get_params('rx_udp_started'):
@@ -529,7 +550,9 @@ class Hardware(BaseHardware):
def ChangeBandFilters(self):
if not hasattr(self.application, "multi_rx_screen"):
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
freq = self.conf.BandEdge.get(highest, (0, 0))[0]
for pane in self.application.multi_rx_screen.receiver_list:
@@ -606,12 +629,7 @@ class Hardware(BaseHardware):
return (48000, 384000)
## Hardware AGC is no longer supported in HL2 identifying as version >=40
def ChangeAGC(self, value):
if value:
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)
pass
## Simpler LNA setting for HL2 identifying as version >=40, see HL2 wiki for details
def ChangeLNA(self, value): # LNA for Rx
# value is -12 to +48
@@ -624,7 +642,7 @@ class Hardware(BaseHardware):
value = 51 - value
else:
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)
if DEBUG: print ("Change LNA to", value)
def ChangeTxLNA(self, value): # LNA for Tx
@@ -634,15 +652,25 @@ class Hardware(BaseHardware):
elif value > 48:
value = 48
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)
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):
try:
tx_level = self.conf.tx_level[self.band]
except KeyError:
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
else:
reduc = self.application.tx_level
@@ -651,7 +679,7 @@ class Hardware(BaseHardware):
tx_level = 0
elif 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)
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
@@ -874,6 +902,8 @@ class Hardware(BaseHardware):
QS.set_hermeslite_writepointer(0)
return False
def WriteQueue(self, wait=False):
if not self.is_HermesLite2:
return False
self._wait_queue()
# Send next write
self.old_writequeue = self.pc2hermeslitewritequeue[:]
+36 -3
View File
@@ -8,6 +8,7 @@ from __future__ import absolute_import
from __future__ import division
import math, wx, time
import _quisk as QS
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):
self.num_rows_added = 1
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)
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()
frame.Bind(wx.EVT_COMBOBOX_CLOSEUP, self.OnAtu)
init = app.hermes_LNA_dB
@@ -33,7 +40,7 @@ class BottomWidgets: # Add extra widgets to the bottom of the screen
self.sliderLNA.idName = "RfLna"
app.midiControls["RfLna"] = (self.sliderLNA, self.OnLNA)
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":
# Display four data items in a single window
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()
self.hardware.ChangeLNA(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
temp = self.hardware.hermes_temperature
# 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
else:
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
temp = self.Code2Temp()
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)
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
+2
View File
@@ -402,6 +402,8 @@ class Hardware(BaseHardware):
if do_tx:
try:
self.rx_udp_socket.send(s)
if DEBUG:
self.PrintStatus(' Send', self.want_udp_status)
except:
pass
def SetVNA(self, key_down=None, vna_start=None, vna_stop=None, vna_count=None, do_tx=False):
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+638 -161
View File
@@ -8,7 +8,6 @@
#include <sys/types.h>
#include "microphone.h"
#include "filter.h"
#include "freedv.h"
#ifdef MS_WINDOWS
#include <winsock2.h>
@@ -75,6 +74,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 tx_sample_rate = 48000; // Used for SoapySDR
static int reverse_tx_sideband;
static int PsEnable = 0;
static int PsCal = 0;
static int doTxCorrect = 0; // Corrections for UDP sample transmit
static double TxCorrectLevel;
@@ -243,11 +244,9 @@ static void init_alc(struct alc * pt, int size)
case DGT_U:
case DGT_L:
case DGT_IQ:
pt->gain_now[i] = 1.4;
break;
case FDV_U:
case FDV_L:
pt->gain_now[i] = 2.0;
pt->gain_now[i] = 1.4;
break;
default:
pt->gain_now[i] = 1.0;
@@ -615,7 +614,7 @@ static int tx_filter_digital(complex double * filtered, int count)
quisk_filt_dInit(&filter1, quiskDgtFilt48Coefs, sizeof(quiskDgtFilt48Coefs)/sizeof(double)); // pass 1350, stop 1650
}
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;
}
for (i = 0; i < count; i++) // FIR bandpass filter; separate into I and Q
@@ -624,140 +623,6 @@ static int tx_filter_digital(complex double * filtered, int 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)
{ // return the TX filter response to display on the graph
// This is for debugging. Change quisk.py to call QS.get_tx_filter() instead
@@ -894,6 +759,8 @@ static void quisk_hermes_tx_add(complex double * cSamples, int tx_count, int key
if (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;
for (i = 0; i < tx_count; i++) { // Put transmit mic samples into the buffer
hermes_buf[hermes_write_index++] = (short)cimag(cSamples[i]);
@@ -909,6 +776,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.
// If the key is up we send samples anyway, but the samples are zero.
int i, j, offset, sent, ratio;
int PsInUse;
short s;
unsigned char sendbuf[1032];
unsigned char * pt_buf;
@@ -916,6 +784,7 @@ void quisk_hermes_tx_send(int tx_socket, int * tx_records)
static unsigned char C0_index = 0;
complex double cw_samples[63 * 2];
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);
if (tx_records == NULL) {
@@ -928,6 +797,7 @@ void quisk_hermes_tx_send(int tx_socket, int * tx_records)
last_play_state = quisk_play_state;
serial_key_samples(NULL, 0);
}
PsInUse = PsEnable || PsCal;
//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
//printf ("quisk_hermes_tx_send ratio %d count %d\n", ratio, *tx_records);
@@ -971,15 +841,33 @@ void quisk_hermes_tx_send(int tx_socket, int * tx_records)
sendbuf[13] = quisk_pc_to_hermes[offset++]; // C2
sendbuf[14] = quisk_pc_to_hermes[offset++]; // C3
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
if (hermes_mox_bit) // send filter selection on J16
sendbuf[13] = hermes_filter_tx << 1;
else
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)
C0_index = 0;
pt_buf = sendbuf + 16;
@@ -1028,15 +916,33 @@ void quisk_hermes_tx_send(int tx_socket, int * tx_records)
sendbuf[525] = quisk_pc_to_hermes[offset++]; // C2
sendbuf[526] = quisk_pc_to_hermes[offset++]; // C3
sendbuf[527] = quisk_pc_to_hermes[offset++]; // C4
if (C0_index == 0) {
sendbuf[527] = quisk_multirx_count << 3 | 0x04; // Send the old count, not the changed count
switch (C0_index) {
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
sendbuf[525] = hermes_filter_tx << 1;
else
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)
C0_index = 0;
}
@@ -1102,12 +1008,47 @@ static void transmit_udp(complex double * cSamples, int count)
static void transmit_mic_carrier(complex double * cSamples, int count, double level)
{ // 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
// transmit a carrier equal to the number of samples
int i;
for (i = 0; i < count; i++)
cSamples[i] = level * CLIP16;
#else
#endif
#if 0
// replace mic samples with a sin wave
int i;
double freq;
@@ -1278,6 +1219,10 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
count *= interp;
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) {
case LSB: // LSB
case USB: // USB
@@ -1319,6 +1264,8 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
case DGT_L:
case DGT_IQ:
case DGT_FM:
case FDV_U: // FreeDV
case FDV_L:
count = tx_filter_digital(cSamples, count); // filter samples, minimal processing
process_alc(cSamples, count, &tx_alc, rxMode);
break;
@@ -1326,11 +1273,6 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
count *= interp;
transmit_mic_imd(cSamples, count, quiskImdLevel / 1000.0);
break;
case FDV_U: // FDV
case FDV_L:
count = tx_filter_freedv(cSamples, count, 1);
process_alc(cSamples, count, &tx_alc, rxMode);
break;
default:
break;
}
@@ -1421,11 +1363,6 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
count = tx_filter(cSamples, count);
process_alc(cSamples, count, &tx_alc, rxMode);
break;
case FDV_U: // FDV
case FDV_L:
count = tx_filter_freedv(cSamples, count, 0);
process_alc(cSamples, count, &tx_alc, rxMode);
break;
default:
for (i = 0; i < count; i++)
cSamples[i] = 0;
@@ -1447,14 +1384,31 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
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)
{ /* Call with keyword arguments ONLY; change Tx audio parameters */
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;
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiidii", kwlist,
&vlevel, &timeVOX, &clevel, &quisk_mic_preemphasis, &tx_sample_rate, &reverse_tx_sideband))
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiidiiii", kwlist,
&vlevel, &timeVOX, &clevel, &quisk_mic_preemphasis, &tx_sample_rate, &reverse_tx_sideband,
&PsEnable, &PsCal))
return NULL;
if (vlevel != -9999)
vox_level = (int)(pow(10.0, vlevel / 20.0) * CLIP16); // Convert dB to 16-bit sample
@@ -1578,7 +1532,6 @@ void quisk_set_tx_mode(void) // called when the mode rxMode is changed
tx_filter(NULL, 0);
tx_filter_digital(NULL, 0);
transmit_udp(NULL, 0);
tx_filter_freedv(NULL, 0, 0);
serial_key_samples(NULL, 0);
}
@@ -1624,3 +1577,527 @@ static void serial_key_samples(complex double * cSamples, int count) // called f
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;
}
}
+22 -1
View File
@@ -91,6 +91,8 @@ class Hardware(BaseHardware):
conf.si570_direct_control = False
conf.si570_xtal_freq = 114285000
conf.repeater_delay = 0.25
self.ptt_count = 200
self.ptt_on = 0
BaseHardware.__init__(self, app, conf)
QS.set_sparams(multus_cw_samples=1)
self.use_softrock = True
@@ -104,6 +106,13 @@ class Hardware(BaseHardware):
self.usb_dev.ctrl_transfer(OUT, address, self.si570_i2c_address + 0x700, 0, message)
if DEBUG:
print ("USB send to 0x%X" % address, message)
def TransferIn(self, address, length):
if self.usb_dev:
recv = self.usb_dev.ctrl_transfer(IN, address, self.si570_i2c_address + 0x700, 0, length)
if DEBUG:
print ("USB receive from 0x%X" % address, recv)
return recv
return None
def open(self):
text = BaseHardware.open(self)
self.InitKeyer()
@@ -120,8 +129,20 @@ class Hardware(BaseHardware):
print ("Mode is not CW")
self.TransferOut(0x70, cw_mode)
return ret
def PollCwKey(self): # Called frequently by Quisk to check the CW key status
def PollCwKey(self): # Called frequently from the sound thread to check the CW key status
return # Quisk is always in Rx
def PollGuiControl(self): # Called frequently from the GUI thread
self.ptt_count -= 1
if self.ptt_count <= 0:
self.ptt_count = 200
reply = self.TransferIn(0xA5, 1)
if DEBUG:
print ("PollGuiControl got", reply)
if reply:
ptt = reply[0] # This is 255 for error
if ptt in (0, 1) and ptt != self.ptt_on:
self.ptt_on = ptt
self.application.pttButton.SetValue(ptt, True)
def InitKeyer(self):
# Initialize the keyer parameters
conf = self.conf
+3 -9
View File
@@ -1,8 +1,5 @@
# This is the hardware control file for my shack.
# It is for the Hermes-Lite2 5 watt output which uses only the antenna tuner.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
# It is for the Hermes-Lite2 RF1 output and Alt Rx input.
from hermes.quisk_hardware import Hardware as BaseHw
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.v2filter = station_hardware.FilterBoxV2(app, conf) # Control V2 filter box
def open(self):
if False:
if False: # Start the station control GUI
from n2adr.station_hardware import StationControlGUI
self.GUI = StationControlGUI(self.application.main_frame, self, self.application, self.conf)
self.GUI.Show()
@@ -29,12 +26,11 @@ class Hardware(BaseHw):
return BaseHw.close(self)
def ChangeFilterFrequency(self, tx_freq):
if tx_freq and tx_freq > 0:
self.v2filter.SetTxFreq(tx_freq)
if self.GUI:
self.GUI.SetTxFreq(tx_freq)
self.GUI.freq_entry.ChangeValue("%.3f" % (tx_freq * 1E-6))
else:
self.anttuner.SetTxFreq(tx_freq)
self.v2filter.SetTxFreq(tx_freq)
def ChangeFrequency(self, tx_freq, vfo_freq, source='', band='', event=None):
self.ChangeFilterFrequency(tx_freq)
return BaseHw.ChangeFrequency(self, tx_freq, vfo_freq, source, band, event)
@@ -42,8 +38,6 @@ class Hardware(BaseHw):
# band is a string: "60", "40", "WWV", etc.
ret = BaseHw.ChangeBand(self, band)
self.anttuner.ChangeBand(band)
#self.lpfilter.ChangeBand(band)
#self.hpfilter.ChangeBand(band)
self.v2filter.ChangeBand(band)
self.CorrectSmeter()
return ret
+4 -50
View File
@@ -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.
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import wx, time
import _quisk as QS
class BottomWidgets: # Add extra widgets to the bottom of the screen
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):
#self.config = conf
#self.hardware = hardware
#self.application = app
#start_row = app.widget_row # The first available row
#start_col = app.button_start_col # The start of the button columns
#b = app.QuiskCycleCheckbutton(frame, self.OnAntTuner, ('Antenna', 'Ant 0', 'Ant 1'))
#bw, bh = b.GetMinSize()
#gbs.Add(b, (start_row, start_col), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'L+')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 2), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'L-')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 4), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'C+')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 6), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'C-')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 8), (1, 2), flag=wx.EXPAND)
#b = app.QuiskPushbutton(frame, self.OnAntTuner, 'Save')
#b.Enable(0)
#gbs.Add(b, (start_row, start_col + 10), (1, 2), flag=wx.EXPAND)
#self.swr_label = app.QuiskText(frame, 'Watts 000 SWR 10.1 Zh Ind 22 Cap 33 Freq 28100 (7777)', bh)
#gbs.Add(self.swr_label, (start_row, start_col + 2), (1, 10), flag=wx.EXPAND)
#b = app.QuiskCheckbutton(frame, None, text='')
#gbs.Add(b, (start_row, start_col + 25), (1, 2), flag=wx.EXPAND)
# Example of a horizontal slider:
# lab = wx.StaticText(frame, -1, 'Preamp', style=wx.ALIGN_CENTER)
# gbs.Add(lab, (5,0), flag=wx.EXPAND)
# sl = wx.Slider(frame, -1, 1024, 0, 2048) # parent, -1, initial, min, max
# gbs.Add(sl, (5,1), (1, 5), flag=wx.EXPAND)
# sl.Bind(wx.EVT_SCROLL, self.OnPreamp)
# def OnPreamp(self, event):
# print event.GetPosition()
self.num_rows_added = 0
#def OnAntTuner(self, event):
# btn = event.GetEventObject()
# text = btn.GetLabel()
# self.hardware.OnAntTuner(text)
#def UpdateText(self, text):
# self.swr_label.SetLabel(text)
BaseWidgets.__init__(self, app, hardware, conf, frame, gbs, vertBox)
+85 -123
View File
@@ -1,9 +1,5 @@
# 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 wx, wx.lib.buttons
@@ -411,18 +407,15 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
self.set_HighZ = 0
self.antnum = 0 # Antenna number 0 or 1
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_HL2.txt'
else:
path = 'TunerLCZ.txt'
path = 'TunerLCZ.txt'
if sys.platform == "win32":
path = 'C:/pub/' + path
self.path = 'C:/pub/' + path
else:
path = '/home/jim/pub/' + path
fp = open(path, "r")
self.path = '/home/jim/pub/' + path
fp = open(self.path, "r")
lines = fp.readlines()
fp.close()
self.TunerLCZ = [(0, 0, 0, 0), (999111000, 0, 0, 0)] # Add dummy first and last entry.
self.TunerLCZ = []
for line in lines:
freq, antL, antC, hilo = line.split()
freq = int(freq)
@@ -451,7 +444,12 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
pass
def ChangeBand(self, band):
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
i2 = len(self.TunerLCZ) - 1
while 1: # binary partition
@@ -462,20 +460,14 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
i2 = i
if i2 - i1 <= 1:
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.
delta1 = tx_freq - self.TunerLCZ[i1][0]
delta2 = self.TunerLCZ[i2][0] - tx_freq
if i1 == 0: # below the first frequency
if abs(tx_freq - self.TunerLCZ[i1][0]) < abs(tx_freq - self.TunerLCZ[i2][0]):
index = i1
elif i2 == len(self.TunerLCZ) - 1: # above the last frequency
else:
index = i2
elif delta2 < delta1: # i2 is closer
index = i2
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]
if DEBUG: print ("StatuinControlGUI FindIndexTup", tx_freq, i1, i2, len(self.TunerLCZ), index, self.TunerLCZ[index])
return index, self.TunerLCZ[index]
def SetTxFreq(self, tx_freq, no_tune=False):
#if DEBUG: print("AntennaTuner SetTxFreq", 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 DEBUG and self.antnum == 1: print ("antnum 0")
self.antnum = 0
else:
elif self.dipole2:
if DEBUG and self.antnum == 0: print ("antnum 1")
self.antnum = 1
if self.antnum == 1:
if self.antnum == 1 and self.dipole2:
self.dipole2.SetTxFreq(tx_freq)
if not self.socket:
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
return
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:
self.set_HighZ = newH
self.set_C = newC
@@ -521,7 +513,8 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
self.timer = 0
else:
self.timer = 0
self.dipole2.HeartBeat()
if self.dipole2:
self.dipole2.HeartBeat()
def Send(self):
try:
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.data_saving = False
self.tx_freq = 0
if sys.platform == "win32":
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.anttuner = self.hware.anttuner
self.SetBackgroundColour('light steel blue')
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
sizer = wx.GridBagSizer(hgap=5, vgap=3)
@@ -568,6 +542,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
self.bands = []
for band in ('160', '80', '60', '40', '30', '20', '17', '15', '12', '10'):
b = wx.lib.buttons.GenToggleButton(self, -1, band)
b.Enable(False)
b.SetUseFocusIndicator(False)
b.SetBezelWidth(4)
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.AddSpacer(20)
b = wx.lib.buttons.GenToggleButton(self, -1, "Key Down")
b.Enable(False)
b.SetFont(font)
b.SetUseFocusIndicator(False)
b.SetBezelWidth(4)
@@ -611,6 +587,7 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
szr.Add(b)
szr.AddSpacer(10)
b = wx.lib.buttons.GenToggleButton(self, -1, "FilterV2 Preamp")
b.Enable(False)
b.SetFont(font)
b.SetUseFocusIndicator(False)
b.SetBezelWidth(4)
@@ -622,16 +599,31 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
b.SetFont(font)
szr.Add(b, flag=wx.ALIGN_CENTER_VERTICAL)
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.Bind(wx.EVT_TEXT_ENTER, self.OnFreqEntry, self.freq_entry)
szr.Add(self.freq_entry)
#self.Bind(wx.EVT_TEXT_ENTER, self.OnFreqEntry, self.freq_entry)
szr.Add(self.freq_entry, flag=wx.ALIGN_CENTER_VERTICAL)
szr.AddSpacer(10)
b = wx.lib.buttons.GenButton(self, -1, "Search Freq")
b = wx.lib.buttons.GenButton(self, -1, "Freq-")
b.SetUseFocusIndicator(False)
b.SetBezelWidth(4)
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)
sizer.Add(szr, pos=(row, 0), span=(1, 3))
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.timer.Start(100)
def SetTxFreq(self, tx_freq):
#print("StationControlGUI SetTxFreq", tx_freq)
tx_freq = ((tx_freq + 500) // 1000) * 1000
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.sliderC.SetValue(tup[2])
self.btnHiLo.SetValue(tup[3])
anttuner = self.hware.anttuner
fff, anttuner.set_L, anttuner.set_C, anttuner.set_HighZ = tup
if tx_freq < 17000000:
anttuner.antnum = 0
else:
elif anttuner.dipole2:
anttuner.antnum = 1
if anttuner.antnum == 1:
if anttuner.antnum == 1 and anttuner.dipole2:
anttuner.dipole2.SetTxFreq(tx_freq)
anttuner.WantData()
def FindIndexTup(self, tx_freq):
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]
self.freq_entry.SetLabel("%.3f" % (tx_freq * 1E-6))
def OnBtnClose(self, event):
self.hware.anttuner.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
if self.hware.v2filter:
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.SetTxFreq(f)
def OnButtonHiLo(self, event):
anttuner = self.hware.anttuner
if self.btnHiLo.GetValue():
anttuner.set_HighZ = 1
self.anttuner.set_HighZ = 1
else:
anttuner.set_HighZ = 0
anttuner.WantData()
self.anttuner.set_HighZ = 0
self.anttuner.WantData()
def OnFreqEntry(self, event=None):
freq = self.freq_entry.GetValue()
freq = float(freq) * 1E6
@@ -751,11 +718,27 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
freq = int(freq + 0.1)
self.tx_freq = 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):
freq = self.freq_entry.GetValue()
freq = float(freq) * 1E6
freq = int(freq + 0.1)
self.tx_freq = freq
L = self.sliderL.GetValue()
C = self.sliderC.GetValue()
if self.btnHiLo.GetValue():
@@ -763,22 +746,19 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
else:
hilo = 0
tup = (self.tx_freq, L, C, hilo)
index, t = self.FindIndexTup(self.tx_freq)
if self.tx_freq == self.TunerLCZ[index][0]:
self.TunerLCZ[index] = tup
index, t = self.anttuner.FindIndexTup(self.tx_freq)
if self.tx_freq == self.anttuner.TunerLCZ[index][0]:
self.anttuner.TunerLCZ[index] = tup
else:
self.TunerLCZ.append(tup)
self.TunerLCZ.sort()
fp = open(self.filename, 'w')
for tup in self.TunerLCZ[1:-1]: # Throw away dummy entries
fp.write("%12d %2d %2d %d\n" % tup)
fp.close()
self.anttuner.TunerLCZ.append(tup)
self.anttuner.TunerLCZ.sort()
self.anttuner.SaveLCZ()
def OnSliderL(self, event=None):
self.hware.anttuner.set_L = self.sliderL.GetValue()
self.hware.anttuner.WantData()
self.anttuner.set_L = self.sliderL.GetValue()
self.anttuner.WantData()
def OnSliderC(self, event=None):
self.hware.anttuner.set_C = self.sliderC.GetValue()
self.hware.anttuner.WantData()
self.anttuner.set_C = self.sliderC.GetValue()
self.anttuner.WantData()
def OnBtnV2Preamp(self, event):
if self.hware.v2filter:
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
elif text == 'C-':
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()
+1 -1
View File
@@ -18,7 +18,7 @@ from quisk_hardware_model import Hardware as BaseHardware
DEBUG = 1
# Define the name of the hardware and the items on the hardware screen (see quisk_conf_defaults.py):
################ Receivers PerseusSDR, The PerseusSDR interface to multiple hardware SDRs.
################ Receivers PerseusSDR, The PerseusSDR interface to multiple SDRs.
## hardware_file_name Hardware file path, rfile
# This is the file that contains the control logic for each radio.
#hardware_file_name = 'perseuspkg/quisk_hardware.py'
+108 -162
View File
@@ -151,7 +151,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 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 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 double softrock_correct_gain;
static double softrock_correct_phase;
@@ -219,14 +218,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
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
static double hermes_temperature; // average temperature
static double hermes_fwd_power; // average forward power
static double hermes_rev_power; // average reverse power
static double hermes_fwd_peak; // peak forward power
static double hermes_rev_peak; // peak reverse power
static double hermes_pa_current; // average power amp current
static int hermes_count_temperature; // number of temperature samples
static int hermes_count_current; // number of current samples
static double hpsdr_AIN1_avg; // average forward power
static double hpsdr_AIN1_peak; // peak forward power
static double hpsdr_AIN2_avg; // average reverse power
static double hpsdr_AIN2_peak; // peak reverse power
static double hpsdr_AIN3; // average power amp current
static double hpsdr_AIN4;
static double hpsdr_AIN5; // average temperature
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
int quisk_hardware_cwkey; // hardware CW key from UDP or USB
@@ -569,15 +571,10 @@ void QuiskWavClose(struct QuiskWav * hWav)
}
#endif
// These are used for digital voice codecs
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)
void quisk_dvoice_freedv(void) // Not used.
{
pt_quisk_freedv_rx = rx;
pt_quisk_freedv_tx = tx;
}
#if 0
static int fFracDecim(double * dSamples, int nSamples, double fdecim)
{ // fractional decimation by fdecim > 1.0
@@ -1851,10 +1848,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)
{ // Changes here will require changes to get_filter_rate();
int i;
bool isLSB;
complex double cx;
double d, di, dd;
static struct AgcState Agc1 = {0.3, 16000, 0}, Agc2 = {0.3, 16000, 0};
//static int count=0;
//static double phase=0;
static struct stStorage {
@@ -2090,6 +2085,7 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
MeasureSquelch[bank].squelch_active = MeasureSquelch[bank].squelch < squelch_level;
break;
case DGT_U: // digital mode DGT-U at 48 ksps
case FDV_U:
if (filter_bandwidth[nFilter] < DGT_NARROW_FREQ) { // filter at 6 ksps
quisk_filter_srate = quisk_decim_srate / 8;
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &Storage[bank].HalfBand5);
@@ -2116,6 +2112,7 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
}
break;
case DGT_L: // digital mode DGT-L
case FDV_L:
if (filter_bandwidth[nFilter] < DGT_NARROW_FREQ) { // filter at 6 ksps
quisk_filter_srate = quisk_decim_srate / 8;
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &Storage[bank].HalfBand5);
@@ -2154,102 +2151,6 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
}
}
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) {
measured_audio = sqrt(measure_audio_sum / measure_audio_count) / CLIP32 * 1e6;
@@ -2544,7 +2445,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.
// Thanks to WB4JFI for the code to add a third FFT buffer, July 2010.
@@ -2756,7 +2658,7 @@ int quisk_process_samples(complex double * cSamples, int nSamples)
quisk_decim_srate = 48000;
}
// 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.
switch (quisk_sound_state.playback_rate / 48000) {
@@ -2784,9 +2686,6 @@ start_agc:
if (rxMode == EXT || rxMode == DGT_IQ) { // Ext and DGT-IQ stereo sound
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
for (i = 0; i < nSamples; i++) {
orig_cSamples[i] = cimag(cSamples[i]);
@@ -2944,6 +2843,8 @@ static PyObject * get_filter_rate(PyObject * self, PyObject * args)
break;
case DGT_U: // digital modes DGT-*
case DGT_L:
case FDV_U: // digital voice
case FDV_L:
if (bandwidth < DGT_NARROW_FREQ)
filter_srate = decim_srate / 8;
else
@@ -2952,10 +2853,6 @@ static PyObject * get_filter_rate(PyObject * self, PyObject * args)
case DGT_IQ: // digital mode at 48 ksps
filter_srate = decim_srate;
break;
case FDV_U: // digital voice
case FDV_L:
filter_srate = n_modem_sample_rate;
break;
}
//QuiskPrintf("Filter rate %d\n", filter_srate);
return PyInt_FromLong(filter_srate);
@@ -3195,14 +3092,13 @@ static PyObject * set_params(PyObject * self, PyObject * args, PyObject * keywds
{ /* Call with keyword arguments ONLY; change local parameters */
static char * kwlist[] = {"quisk_is_vna", "rx_bytes", "rx_endian", "read_error", "clip",
"bscope_bytes", "bscope_endian", "bscope_size", "bandscopeScale", "hermes_pause",
"freedv_monitor", NULL} ;
NULL} ;
int i, nbytes, read_error, clip, bscope_size, hermes_pause;
nbytes = read_error = clip = bscope_size = hermes_pause = -1;
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiiiiiiidii", kwlist,
&quisk_is_vna, &nbytes, &py_sample_rx_endian, &read_error, &clip,
&py_bscope_bytes, &py_bscope_endian, &bscope_size, &bandscopeScale, &hermes_pause,
&freedv_monitor))
&py_bscope_bytes, &py_bscope_endian, &bscope_size, &bandscopeScale, &hermes_pause))
return NULL;
if (nbytes != -1) {
py_sample_rx_bytes = nbytes;
@@ -3243,31 +3139,43 @@ static PyObject * get_hermes_TFRC(PyObject * self, PyObject * args)
if (!PyArg_ParseTuple (args, ""))
return NULL;
if (hermes_count_temperature > 0) {
hermes_temperature /= hermes_count_temperature;
hermes_fwd_power /= hermes_count_temperature;
if (hpsdr_AIN5_count > 0) {
hpsdr_AIN5 /= hpsdr_AIN5_count;
hpsdr_AIN1_avg /= hpsdr_AIN5_count;
}
else {
hermes_temperature = 0.0;
hermes_fwd_power = 0.0;
hpsdr_AIN5 = 0.0;
hpsdr_AIN1_avg = 0.0;
}
if (hermes_count_current > 0) {
hermes_rev_power /= hermes_count_current;
hermes_pa_current /= hermes_count_current;
if (hpsdr_AIN3_count > 0) {
hpsdr_AIN2_avg /= hpsdr_AIN3_count;
hpsdr_AIN3 /= hpsdr_AIN3_count;
}
else {
hermes_rev_power = 0.0;
hermes_pa_current = 0.0;
hpsdr_AIN2_avg = 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);
hermes_temperature = 0;
hermes_fwd_power = 0;
hermes_rev_power = 0;
hermes_fwd_peak = 0;
hermes_rev_peak = 0;
hermes_pa_current = 0;
hermes_count_temperature = 0;
hermes_count_current = 0;
if (hpsdr_AIN4_count > 0) {
hpsdr_AIN4 /= hpsdr_AIN4_count;
hpsdr_AIN6 /= hpsdr_AIN4_count;
}
else {
hpsdr_AIN4 = 0.0;
hpsdr_AIN6 = 0.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;
}
@@ -3815,19 +3723,24 @@ static int read_rx_udp10(complex double * samp) // Read samples from UDP using t
quisk_set_play_state();
}
}
else if(dindex == 1) { // temperature and forward power
hermes_temperature += quisk_hermes_to_pc[4] << 8 | quisk_hermes_to_pc[5];
else if(dindex == 1) {
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];
hermes_fwd_power += power;
hermes_fwd_peak = fmax(hermes_fwd_peak, (double)power);
hermes_count_temperature++;
hpsdr_AIN1_avg += power;
hpsdr_AIN1_peak = fmax(hpsdr_AIN1_peak, (double)power);
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];
hermes_rev_power += power;
hermes_rev_peak = fmax(hermes_rev_peak, (double)power);
hermes_pa_current += quisk_hermes_to_pc[10] << 8 | quisk_hermes_to_pc[11];
hermes_count_current++;
hpsdr_AIN2_avg += power;
hpsdr_AIN2_peak = fmax(hpsdr_AIN2_peak, (double)power);
hpsdr_AIN3 += quisk_hermes_to_pc[10] << 8 | quisk_hermes_to_pc[11];
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.
index = start + 5;
@@ -3850,6 +3763,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
( ! 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
@@ -6190,6 +6140,7 @@ static PyMethodDef QuiskMethods[] = {
{"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_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."},
{"set_split_rxtx", set_split_rxtx, METH_VARARGS, "Set split for rx/tx."},
{"set_tune", set_tune, METH_VARARGS, "Set the tuning frequency."},
@@ -6230,13 +6181,6 @@ static PyMethodDef QuiskMethods[] = {
{"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_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."},
{"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."},
@@ -6244,6 +6188,8 @@ static PyMethodDef QuiskMethods[] = {
{"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."},
{"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
{"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."},
+13 -21
View File
@@ -260,6 +260,13 @@ extern PyObject * quisk_stop_remote_radio_remote_sound(PyObject * self, PyObject
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);
// 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);
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 *);
int QuiskWavWriteOpen(struct QuiskWav *, char *, short, short, short, int, double);
void QuiskWavWriteC(struct QuiskWav *, complex double *, int);
@@ -312,10 +319,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_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 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_ssb_delay; // milliseconds to discard output for all modes except CW
extern struct sound_dev * quiskPlaybackDevices[]; // array of Playback sound devices
@@ -339,20 +342,14 @@ extern PyObject * quisk_sound_errors(PyObject *, PyObject *);
extern PyObject * quisk_set_file_record(PyObject *, PyObject *);
extern PyObject * quisk_set_file_name(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_set_udp_tx_correct(PyObject *, PyObject *);
extern PyObject * quisk_set_hermes_filter(PyObject *, PyObject *);
extern PyObject * quisk_set_alex_hpf(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_freedv_set_squelch_en(PyObject *, PyObject *);
extern PyObject * quisk_open_key(PyObject *, PyObject *, PyObject *);
extern PyObject * quisk_close_key(PyObject *, PyObject *);
extern PyObject * quisk_set_sound_name(PyObject *, PyObject *);
@@ -362,7 +359,7 @@ extern PyObject * quisk_wasapi_control_midi(PyObject *, PyObject *, PyObject *);
// WDSP interface
#define QUISK_WDSP_RX 1
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
// the SDR-IQ and any other C-language extension modules that return
@@ -396,7 +393,6 @@ void quisk_file_playback(complex double *, int, double);
void quisk_tmp_playback(complex double *, int, double);
void quisk_hermes_tx_send(int, int *);
void quisk_udp_mic_error(char *);
void quisk_check_freedv_mode(void);
void quisk_calc_audio_graph(double, complex double *, double *, int, int);
double QuiskDeltaSec(int);
void * quisk_make_sidetone(struct sound_dev *, int);
@@ -404,12 +400,8 @@ void * quisk_make_txIQ(struct sound_dev *, int);
int quisk_play_sidetone(struct sound_dev *);
void quisk_set_play_state(void);
void quisk_poll_hardware_key(void);
// Functions supporting digital voice codecs
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;
void PreDistort(complex double * amp_in_samples, complex double * amp_out_samples, int nSamples, complex double * tx_samples, int num_tx);
int CircularBuffer(int channel, complex double * cSamples, int nRead, int nWrite);
// Driver function definitions=================================================
int quisk_read_alsa(struct sound_dev *, complex double *);
@@ -466,7 +458,7 @@ int import_quisk_api(void); // used to initialize Quisk_API
#define QuiskSleepMicrosec (*( void (*) (int) )Quisk_API[5])
#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_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_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])
@@ -481,7 +473,7 @@ double QuiskTimeSec(void);
void QuiskSleepMicrosec(int);
void QuiskPrintTime(const char *, int);
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);
void quisk_sample_source4(ty_sample_start, ty_sample_stop, ty_sample_read, ty_sample_write);
char * strMcpy(char *, const char *, size_t);
+415 -174
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# All QUISK software is Copyright (C) 2006-2024 by James C. Ahlstrom.
# All QUISK software is Copyright (C) 2006-2025 by James C. Ahlstrom.
# This free software is licensed for use under the GNU General Public
# License (GPL), see http://www.opensource.org.
# Note that there is NO WARRANTY AT ALL. USE AT YOUR OWN RISK!!
@@ -292,9 +292,9 @@ class HamlibHandlerSerial:
# 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'}
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'}
ConvertZZAC_P1_2_Step = { 0: 1,
1: 10,
@@ -585,11 +585,18 @@ class HamlibHandlerSerial:
mode = self.app.mode
info = cmd
info += "%011d" % (self.app.rxFreq + self.app.VFO) # frequency, ZZFA
info += '0000'
if ritFreq < 0: # RIT freq
info += "-%05d" % -ritFreq
else:
info += "+%05d" % ritFreq
if len(cmd) == 4: # Flex ZZIF
info += '0000'
if ritFreq < 0: # RIT freq
info += "-%05d" % -ritFreq
else:
info += "+%05d" % ritFreq
else: # Kenwood IF
info += '00000'
if ritFreq < 0: # RIT freq
info += "-%04d" % -ritFreq
else:
info += "+%04d" % ritFreq
info += "%d" % rit # RIT status
info += '0000'
if QS.is_key_down(): # MOX, key down
@@ -933,6 +940,8 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
if not isinstance(text, Q3StringTypes):
text = text.decode('utf-8')
self.received += text
if HAMLIB_DEBUG and text:
print ("Raw rig2 received:", text)
if '\n' in self.received: # A complete command ending with newline is available
self.input, self.received = self.received.split('\n', 1) # Split off the command, save any further characters
else:
@@ -986,17 +995,12 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
else:
self.Send('0\n')
def GetFreq(self): # The Rx frequency
rx = self.app.multi_rx_screen.receiver_list
if rx:
rx = rx[0]
self.Reply('Frequency', rx.txFreq + rx.VFO, 0)
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)
# This is always the main Rx frequency without reference to added receivers.
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
# This is always the main Rx frequency without reference to added receivers.
freq = self.GetParamNumber()
try:
freq = float(freq)
@@ -1005,12 +1009,7 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
self.ErrParam()
else:
freq = int(freq + 0.5)
# For multiple receivers this controls the first added receiver frequency; else it controls split Rx/Tx frequency.
rx = self.app.multi_rx_screen.receiver_list
if rx:
rx[0].ChangeRxTxFrequency(freq)
else:
self.app.ChangeRxTxFrequency(freq, None)
self.app.ChangeRxTxFrequency(freq, None)
def GetSplitFreq(self): # The Tx Frequency
self.Reply('TX Frequency', self.app.txFreq + self.app.VFO, 0)
def SetSplitFreq(self): # The Tx Frequency
@@ -1052,9 +1051,9 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
mode = 'CW'
elif mode == 'CWL': # Is this what CWR means?
mode = 'CWR'
elif mode == 'DGT-U':
elif mode in ('DGT-U', 'FDV-U'):
mode = 'PKTUSB'
elif mode == 'DGT-L':
elif mode in ('DGT-L', 'FDV-L'):
mode = 'PKTLSB'
elif mode == 'DGT-FM':
mode = 'PKTFM'
@@ -1218,6 +1217,267 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
else:
self.ErrParam()
class CatControl:
def GetFreqVfo(self, vfoB=False):
if vfoB: # The Tx Frequency
return self.app.txFreq + self.app.VFO
else: # The Rx Frequency
rx = self.app.multi_rx_screen.receiver_list
if rx:
rx = rx[0]
return rx.txFreq + rx.VFO
else:
return self.app.rxFreq + self.app.VFO
def SetFreqVfo(self, freq, vfoB=False):
if vfoB: # The Tx Frequency
self.app.ChangeRxTxFrequency(None, freq)
else: # The Rx Frequency
# For multiple receivers this controls the first added receiver frequency; else it controls split Rx/Tx frequency.
rx = self.app.multi_rx_screen.receiver_list
if rx:
rx[0].ChangeRxTxFrequency(freq)
else:
self.app.ChangeRxTxFrequency(freq, None)
def GetFilterBW(self):
return self.app.filter_bandwidth
def SetFilterBW(self, bw):
# Choose button closest to requested bandwidth
buttons = self.app.filterButns.GetButtons()
Lab = buttons[0].GetLabel()
diff = abs(int(Lab) - bw)
for i in range(1, len(buttons) - 1):
label = buttons[i].GetLabel()
df = abs(int(label) - bw)
if df < diff:
Lab = label
diff = df
self.app.filterButns.SetLabel(Lab, True)
class ElecraftK4Handler(CatControl): # Test with telnet localhost 9200
Mo2CoElecraft = {'LSB':1, 'USB':2, 'CWU':3, 'FM':4, 'AM':5, 'DGT-U':6, 'CWL':7, 'DGT-L':9, 'DGT-FM':4, 'DGT-IQ':6}
Co2MoElecraft = {1:'LSB', 2:'USB', 3:'CWU', 4:'FM', 5:'AM', 6:'DGT-U', 7:'CWL', 9:'DGT-L'}
def __init__(self, app, conf, sock, address):
self.app = app
self.conf = conf
self.sock = sock
sock.settimeout(0.0)
self.address = address
self.params = '' # params is the string following the command
self.received = ''
self.cmds = []
self.K31Elecraft = False # K31 mode
h = self.Handlers = {} # Branch on the first two letters of the command
h['AI'] = self.AiAutoInfo
h['CW'] = self.CwCWPitch
h['DT'] = self.DtDataMode
h['FA'] = self.FaFbVfoFreq
h['FB'] = self.FaFbVfoFreq
h['FT'] = self.FtTxVfo
h['FW'] = self.FwFilterBW
h['ID'] = self.IdIdent
h['IF'] = self.IfInfo
h['IS'] = self.IsCenterPitch
h['KS'] = self.KsKeyerSpeed
h['K3'] = self.K3Elecraft
h['LN'] = self.LnLinkVfo
h['MD'] = self.MdMode
h['OM'] = self.OmOptionModules
h['RV'] = self.RvFirmware
h['RX'] = self.RxReceive
h['SB'] = self.SbSubReceiver
h['SM'] = self.SmSmeter
h['TX'] = self.TxTransmit
def Send(self, text):
"""Send text back to the client."""
if HAMLIB_DEBUG:
for cmd in self.cmds[0:-1]:
print ("CatTcp received: %-10s" % cmd)
print ("CatTcp received: %-10s ; sent %s" % (self.cmds[-1], text))
del self.cmds[:]
text = text.encode('utf-8', errors='ignore')
try:
self.sock.sendall(text)
except socket.error:
self.sock.close()
self.sock = None
def Process(self):
"""This is the main processing loop, and is called frequently. It reads and satisfies requests."""
if not self.sock:
return 0
try: # Read any data from the socket
text = self.sock.recv(1024)
except socket.timeout: # This does not work
pass
except socket.error: # Nothing to read
pass
else: # We got some characters
text = text.decode('utf-8', errors='ignore')
self.received += text
if HAMLIB_DEBUG and text:
pass #print ("Raw CatTcp received:", text)
if ';' in self.received: # A complete command ending with semicolon is available
cmd, self.received = self.received.split(';', 1) # Split off the command, save any further characters
else:
return 1
cmd = cmd.strip() # Here is our command line
if len(cmd) < 2:
return 1
if HAMLIB_DEBUG:
self.cmds.append(cmd)
base = cmd[0:2].upper()
try:
handler = self.Handlers[base]
except KeyError:
self.Send(cmd[0:2] + "?;")
#print ("Unknown command", cmd)
return 1
if len(cmd) == 2:
args = ''
elif cmd[2] == '$': # base is the first two letters upper case and any following "$"
base += '$'
args = cmd[3:]
else:
args = cmd[2:]
handler(base, args) # args is the remainder
return 1
def AiAutoInfo(self, base, args):
if not args:
self.Send("AI0;")
elif args != '0':
self.Send("AI0;")
def CwCWPitch(self, base, args):
pitch = self.conf.cwTone
pitch = (pitch + 5) // 10
if pitch < 25:
pitch = 25
elif pitch > 95:
pitch = 95
self.Send("CW%d;" % pitch)
def DtDataMode(self, base, args):
self.Send("%s0;" % base)
def IdIdent(self, base, args):
if args:
self.Send("ID?;")
else:
self.Send("ID017;")
#self.Send("IDQuisk;")
def IfInfo(self, base, args):
rxfreq = self.GetFreqVfo()
info = "%011d " % rxfreq # Rx frequency, five blanks
ritFreq = self.app.ritScale.GetValue()
if ritFreq < 0: # +/- RIT freq
info += "-%04d" % -ritFreq
else:
info += "+%04d" % ritFreq
if self.app.ritButton.GetValue(): # RIT status, XIT status, blank "00"
info += "10 00"
else:
info += "00 00"
if QS.is_key_down(): # MOX, key down
info += '1'
else:
info += '0'
mode = self.Mo2CoElecraft.get(self.app.mode, 2)
info += "%d" % mode # Mode 1-9
info += '00' # Rx on VFOA, no scan
if self.app.split_rxtx: # VFO split status
info += '1'
else:
info += '0'
info += '001 ;'
self.Send(info)
def FaFbVfoFreq(self, base, args):
if base[1] in ('B', 'b'):
vfoB = True
else:
vfoB = False
if not args: # Get command
freq = "%06d" % self.GetFreqVfo(vfoB)
self.Send(base + freq + ';')
else: # Put command
try:
freq = int(args)
except:
self.Send(base + "?;")
else:
l = len(args)
if l <= 2:
freq *= 1000000
elif l <= 5:
freq *= 1000
self.SetFreqVfo(freq, vfoB)
def FtTxVfo(self, base, args):
if not args:
if self.app.split_rxtx:
self.Send("FT1;")
else:
self.Send("FT0;")
else:
# Set split Rx/Tx mode.
is_split = self.app.splitButton.GetValue()
split = int(args)
if split and not is_split:
self.app.splitButton.SetValue(split, True)
elif is_split and not split:
self.app.splitButton.SetValue(split, True)
def FwFilterBW(self, base, args):
if not args:
code = self.GetFilterBW()
self.Send("%s%04d;" % (base, (code + 5) // 10))
else:
self.SetFilterBW(int(args) * 10)
def IsCenterPitch(self, base, args):
if not args:
code = self.GetFilterBW() // 2
if self.K31Elecraft:
self.Send("%s %04d;" % (base, code))
else:
code = (code + 5) // 10
self.Send("%s%04d;" % (base, code))
else:
if self.K31Elecraft:
self.SetFilterBW(int(args) * 2)
else:
self.SetFilterBW(int(args) * 2 * 10)
def K3Elecraft(self, base, args):
if args == '1':
self.K31Elecraft = True
else:
self.K31Elecraft = False
def KsKeyerSpeed(self, base, args):
if len(base) == 2:
self.Send("KS013;")
def LnLinkVfo(self, base, args):
if args != '0':
self.Send("LN0;")
def MdMode(self, base, args):
if args: # set the mode
code = int(args)
mode = self.Co2MoElecraft[code]
self.app.modeButns.SetLabel(mode, True)
else: # return the mode
code = self.Mo2CoElecraft.get(self.app.mode, 2)
self.Send("%s%d;" % (base, code))
def OmOptionModules(self, base, args):
self.Send("OM ------------;")
def RvFirmware(self, base, args):
self.Send("%s99.99;" % base)
def RxReceive(self, base, args):
self.app.pttButton.SetValue(0, True)
def SbSubReceiver(self, base, args):
self.Send("SB0;")
def SmSmeter(self, base, args):
if base[0:3].upper() == "SMH":
self.Send(base + "?;")
elif args:
self.Send(base + "?;")
elif self.K31Elecraft:
self.Send("SM0000;")
else:
self.Send("SM00;")
def TxTransmit(self, base, args):
self.app.pttButton.SetValue(1, True)
class SoundThread(threading.Thread):
"""Create a second (non-GUI) thread to read, process and play sound."""
def __init__(self, samples_from_python):
@@ -1434,12 +1694,8 @@ class ConfigStatus(wx.ScrolledCanvas):
self.MakeRow2("Sample interrupts", self.interrupts, cfile)
self.MakeRow2("Microphone or DGT level dB", level, qfile)
self.MakeRow2("FFT number of points", self.fft_size, err_msg)
if conf.dxClHost: # connection to dx cluster
nSpots = len(application.dxCluster.dxSpots)
if nSpots > 0:
msg = str(nSpots) + ' DX spot' + ('' if nSpots==1 else 's') + ' received from ' + application.dxCluster.getHost()
else:
msg = "No DX Cluster data from %s" % conf.dxClHost
if application.dxCluster:
msg = application.dxCluster.dxStatus()
self.MakeRow2("FFT number of errors", self.fft_error, msg)
else:
self.MakeRow2("FFT number of errors", self.fft_error)
@@ -2123,7 +2379,7 @@ class GraphScreen(wx.Window):
"""For mouse clicks in our display, translate to our screen coordinates."""
mouse_x, mouse_y = event.GetPosition()
win = event.GetEventObject()
if win is not self:
if win and win is not self:
x, y = win.GetPosition().Get()
mouse_x += x
mouse_y += y
@@ -3028,16 +3284,27 @@ class MultiReceiverScreen(wx.SplitterWindow):
if pane != excpt:
pane.play_btn.SetValue(False)
pane.is_playing = False
def OnAddReceiver(self, event):
def OnAddReceiver(self, event=None, hide_it=False):
index = len(self.receiver_list)
if index >= 7:
return
if index == 0:
pane2 = self.rx_zero
splitter = pane2.GetParent()
pane1 = MultiRxGraph(self, self.data_width, self.graph_width, index)
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:
pane2 = self.receiver_list[-1]
parent = pane2.GetParent()
@@ -3450,8 +3717,8 @@ class App(wx.App):
'txAudioPreemphUsb', 'txAudioPreemphAm', 'txAudioPreemphFm', 'txAudioPreemphFdv',
'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_play_audio', 'file_name_play_samples', 'file_name_play_cq', 'freedv_mode',
'file_play_source', 'hermes_LNA_dB']
'file_name_play_audio', 'file_name_play_samples', 'file_name_play_cq',
'file_play_source', 'hermes_LNA_dB', 'hermes_atten_dB']
def __init__(self):
global application
QS.AppStatus(1)
@@ -3524,6 +3791,7 @@ class App(wx.App):
if not os.path.isdir(self.QuiskFilesDir):
self.QuiskFilesDir = DefaultConfigDir
self.std_out_err = StdOutput(self)
self.std_out_err.Logfile("WxPython " + wx.version())
QS.set_params(quisk_is_vna=0) # We are not the VNA program
# Read in configuration from the selected radio
self.BandPlan = []
@@ -3614,6 +3882,7 @@ class App(wx.App):
setattr(conf, 'widgets_file_name', '')
Hardware = self.Hardware
self.use_fast_heart_beat = hasattr(Hardware, "FastHeartBeat")
self.poll_gui_control = hasattr(Hardware, "PollGuiControl")
# Initialization - may be over-written by persistent state
self.local_conf.Initialize()
self.clip_time0 = 0 # timer to display a CLIP message on ADC overflow
@@ -3669,9 +3938,9 @@ class App(wx.App):
self.hot_key_ptt_active = False
self.serial_ptt_active = False
self.vox_ptt_active = False
self.freedv_mode = 'Mode 700D' # restore FreeDV mode setting
self.freedv_menu = None
self.tci_ptt_active = False
self.hermes_LNA_dB = 20
self.hermes_atten_dB = 0
if hasattr(Hardware, "OnChangeRxTx"):
self.want_RxTx = True
else:
@@ -3700,6 +3969,19 @@ class App(wx.App):
# traceback.print_exc()
else:
self.hamlib_socket = None
# Quisk control by K4 commands through a TCP port
self.k4_tcp_clients = [] # list of TCP connections to handle
if conf.k4_tcp_port:
try:
self.k4_tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.k4_tcp_socket.bind((conf.k4_tcp_ip, conf.k4_tcp_port))
self.k4_tcp_socket.settimeout(0.0)
self.k4_tcp_socket.listen(5) # listen for TCP connections from multiple clients
except:
self.k4_tcp_socket = None
# traceback.print_exc()
else:
self.k4_tcp_socket = None
# Quisk control by fldigi
self.fldigi_new_freq = None
self.fldigi_freq = None
@@ -3747,6 +4029,8 @@ class App(wx.App):
self.file_name_play_samples = ''
self.file_name_play_cq = ''
self.midiControls = {} # Control object and associated function for Midi control name
self.tci_vfo = 0
self.tci_started = False
# get the screen size - thanks to Lucian Langa
x, y, self.screen_width, self.screen_height = wx.Display().GetGeometry() # Using display index 0
self.Bind(wx.EVT_IDLE, self.OnIdle)
@@ -3927,6 +4211,8 @@ class App(wx.App):
# Start wdsp
self.wdsp = quisk_wdsp.Cwdsp(self)
self.wdsp_channel = 1
self.wdsp_SNB = 0
self.wdsp_NR2 = 0
self.wdsp.open(self.wdsp_channel)
#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,
@@ -3989,8 +4275,13 @@ class App(wx.App):
minh = 100
maxh = -1
self.main_frame.SetSizeHints(minw, minh, maxw, maxh)
self.main_frame.SetClientSize(wx.Size(width, self.height))
if hasattr(Hardware, 'pre_open'): # pre_open() is called before 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):
pass
elif conf.quisk_widgets:
@@ -4028,7 +4319,6 @@ class App(wx.App):
if conf.dxClHost:
# create DX Cluster and register listener for change notification
self.dxCluster = dxcluster.DxCluster()
self.dxCluster.setListener(self.OnDxClChange)
self.dxCluster.start()
# Create shortcut keys for buttons
if conf.button_layout == 'Large screen':
@@ -4066,6 +4356,19 @@ class App(wx.App):
if hasattr(bw, "sliderLNA"):
self.midiControls["RfLna"] = (bw.sliderLNA, bw.OnLNA)
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_started = True
QS.tci_set_params(tci_dds=self.VFO)
QS.tci_set_params(tci_if=self.txFreq)
self.tci_vfo = self.txFreq + self.VFO
QS.tci_set_params(tci_vfo=self.tci_vfo)
QS.tci_set_params(tci_modulation=self.mode)
QS.tci_set_params(tci_trx=0)
QS.tci_set_params(tci_split_enable=0)
return True
def OnStartWsjtx(self, ctrl):
method = ctrl.GetValue()
@@ -4379,8 +4682,6 @@ class App(wx.App):
conf.tx_ip, conf.tx_audio_port,
conf.mic_sample_rate, conf.mic_channel_I, conf.mic_channel_Q,
0.7, conf.mic_playback_rate)
def OnDxClChange(self):
self.station_screen.Refresh()
def OnIdle(self, event):
if self.screen:
self.screen.OnIdle(event)
@@ -4410,14 +4711,15 @@ class App(wx.App):
if self.hamlib_socket:
self.hamlib_socket.close()
self.hamlib_socket = None
if self.k4_tcp_socket:
self.k4_tcp_socket.close()
self.k4_tcp_socket = None
if self.dxCluster:
self.dxCluster.stop()
if self.hamlib_com1_handler:
self.hamlib_com1_handler.close()
if self.hamlib_com2_handler:
self.hamlib_com2_handler.close()
if conf.add_freedv_button:
QS.freedv_close()
msg = QS.GetQuiskPrintf()
if msg:
print(msg, end='')
@@ -4476,6 +4778,7 @@ class App(wx.App):
def ImmediateChange(self, name):
if name == "pulse_audio_verbose_output":
value = getattr(conf, name)
QS.tci_set_params(verbose=value)
if value == 0:
self.std_out_err.Show(False)
else:
@@ -4615,7 +4918,7 @@ class App(wx.App):
b.char_shortcut = 'u'
self.MakeAccel(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:
b.Enable(False)
left_row2.append(b)
@@ -4756,14 +5059,9 @@ class App(wx.App):
self.config_screen.config.EnableRecPlay()
### 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']
labels = [('CWL', 'CWU'), ('LSB', 'USB'), 'AM', 'FM', ('DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ')]
shortcuts = ['C', 'S', 'A', 'M', 'D']
count = 5 # There is room for seven buttons
if conf.add_freedv_button:
n_freedv = count
count += 1
labels.append('FDV-U')
shortcuts.append('F')
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', 'F']
count = 6 # There is room for seven buttons
if conf.add_imd_button:
n_imd = count
count += 1
@@ -4785,45 +5083,13 @@ class App(wx.App):
self.modeButns.buttons[0].idName = "CW U/L"
self.modeButns.buttons[1].idName = "SSB U/L"
self.modeButns.buttons[4].idName = "DGT"
self.modeButns.buttons[5].idName = "FDV"
else:
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.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:
val = 500
val = 1000
QS.set_imd_level(val)
if conf.button_layout == 'Large screen':
b = QuiskCheckbutton(frame, None, 'IMD', color=conf.color_test)
@@ -5100,16 +5366,6 @@ class App(wx.App):
t = '%13.2f' % (QS.measure_frequency(-1) + vfo)
t = t[0:4] + ' ' + t[4:7] + ' ' + t[7:] + ' Hz'
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):
self.smeter_db_count += 1 # count for average
x = QS.get_smeter()
@@ -5279,66 +5535,6 @@ class App(wx.App):
self.multi_rx_screen.waterfall.pane2.filter_center = center
if self.screen is self.filter_screen:
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):
if event.GetEventObject().GetValue():
self.OnBtnScreen(None, 'Help')
@@ -5586,6 +5782,7 @@ class App(wx.App):
QS.set_sidetone(self.sidetone_volume, self.sidetone_0to1, self.ritFreq, conf.keyupDelay)
def OnBtnSplit(self, event): # Called when the Split check button is pressed
self.split_rxtx = self.splitButton.GetValue()
QS.tci_set_params(tci_split_enable=self.split_rxtx)
if self.split_rxtx:
if self.split_offset == 0:
if self.mode in ("CWL", "CWU"):
@@ -5803,11 +6000,6 @@ class App(wx.App):
QS.set_squelch(self.levelSquelch / 12.0 - 120.0)
else:
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:
self.levelSquelchSSB = btn.slider_value
if value:
@@ -5821,11 +6013,18 @@ class App(wx.App):
QS.set_auto_notch(0)
def OnBtnNR2(self, event):
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)
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:
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):
# menu is ("NB 1", "NB 2", "NB 3", "SNB")
for item in self.NB_menu.GetMenuItems():
@@ -5841,14 +6040,17 @@ class App(wx.App):
if text == "SNB":
QS.set_noise_blanker(0)
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:
self.wdsp.put(self.wdsp.SetRXASNBARun, self.wdsp_channel, 0)
self.wdsp_SNB = 0
index = int(text[-1])
QS.set_noise_blanker(index)
else: # Noise blanker Off
QS.set_noise_blanker(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):
freq = event.GetString()
win = event.GetEventObject()
@@ -5966,6 +6168,7 @@ class App(wx.App):
self.ChangeRxTxFrequency(self.rxFreq + delta + self.VFO, self.txFreq + delta + self.VFO)
Hardware.ChangeMode(mode)
self.mode = mode
QS.tci_set_params(tci_modulation=mode)
self.MakeFilterButtons(self.Mode2Filters(mode))
QS.set_rx_mode(Mode2Index.get(mode, 3))
if mode == 'CWL':
@@ -6387,7 +6590,7 @@ class App(wx.App):
if rxtx != 'rx':
self.fldigi_server.main.rx()
self.fldigi_timer = time.time()
def HamlibPoll(self): # Poll for Hamlib commands
def HamlibPoll(self): # Poll for Hamlib and K4 TCP commands
if self.hamlib_socket:
try: # Poll for new client connections.
conn, address = self.hamlib_socket.accept()
@@ -6401,6 +6604,19 @@ class App(wx.App):
self.hamlib_clients.remove(client)
# print ('Remove', client.address)
break
if self.k4_tcp_socket:
try: # Poll for new client connections.
conn, address = self.k4_tcp_socket.accept()
except socket.error:
pass
else:
# print ('Connection from', address)
self.k4_tcp_clients.append(ElecraftK4Handler(self, conf, conn, address))
for client in self.k4_tcp_clients: # Service existing clients
if not client.Process(): # False return indicates a closed connection; remove the handler for this client
self.k4_tcp_clients.remove(client)
# print ('Remove', client.address)
break
def OnKeyHook(self, event):
event.Skip()
if conf.hot_key_ptt_if_hidden: # Hot key PTT operates even if Quisk is hidden
@@ -6455,6 +6671,8 @@ class App(wx.App):
self.hamlib_com2_handler.Process()
if self.use_fast_heart_beat:
Hardware.FastHeartBeat()
if self.poll_gui_control:
Hardware.PollGuiControl()
if conf.do_repeater_offset:
hold = QS.tx_hold_state(-1)
if hold == 2: # Tx is being held for an FM repeater TX frequency shift
@@ -6478,11 +6696,13 @@ class App(wx.App):
if not self.tx_indicator:
self.tx_indicator = True
self.pttButton.Tx.TurnOn(True)
QS.tci_set_params(tci_trx=1)
else:
if self.tx_indicator:
self.tx_indicator = 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 = None
if conf.quisk_serial_cts[0:4] == "PTT " or conf.quisk_serial_dsr[0:4] == "PTT ":
@@ -6537,6 +6757,14 @@ class App(wx.App):
self.hot_key_ptt_pressed = False
if self.tx_inhibit:
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:
self.SetPTT(True)
elif ptt is False and ptt_button_down:
@@ -6576,10 +6804,7 @@ class App(wx.App):
d = 1E-10
self.smeter.SetLabel(" ADC %.0f%% %.0fdB" % (d * 100.0, 20 * math.log10(d)))
elif self.smeter_usage == "smeter": # update the S-meter
if self.mode in ('FDV-U', 'FDV-L'):
self.NewDVmeter()
else:
self.NewSmeter()
self.NewSmeter()
elif self.smeter_usage == "freq":
self.MeasureFrequency() # display measured frequency
else:
@@ -6607,7 +6832,20 @@ class App(wx.App):
if self.timer - self.heart_time0 > 0.10: # call hardware to perform background tasks:
self.heart_time0 = self.timer
Hardware.HeartBeat()
if conf.hermes_lite2_enable:
if self.tci_started:
if self.tci_vfo != self.txFreq + self.VFO: # limit the speed of frequency updates
self.tci_vfo = self.txFreq + self.VFO
QS.tci_set_params(tci_vfo=self.tci_vfo)
freq = QS.tci_get_params("tci_vfo")
if freq is not None: # TCI frequency
self.ChangeRxTxFrequency(freq, None)
mode = QS.tci_get_params("tci_modulation")
if mode is not None: # TCI mode
self.modeButns.SetLabel(mode, True)
split = QS.tci_get_params("tci_split_enable")
if split is not None: # TCI split_enable
self.splitButton.SetValue(split, True)
if self.is_HermesLite2:
self.tx_inhibit = QS.get_params('quisk_tx_inhibit')
else:
self.tx_inhibit = 0
@@ -6635,6 +6873,9 @@ class App(wx.App):
self.ChangeDisplayFrequency(tune - vfo, vfo)
self.FldigiPoll()
self.HamlibPoll()
if self.dxCluster:
if self.dxCluster.Poll():
self.station_screen.Refresh()
if self.timer - self.slowheart_time0 > 0.5:
self.slowheart_time0 = self.timer
if self.w_phase:
+204 -79
View File
@@ -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
# This is the file that contains the control logic for each radio.
#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
# This is the file that contains the control logic for each radio.
#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
# This is the file that contains the control logic for each radio.
#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
# This is the file that contains the control logic for each radio.
#hardware_file_name = 'hermes/quisk_hardware.py'
@@ -275,7 +275,7 @@ sndp_active = True
# You can set these options:
## 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
## 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".
# And you may want to set "Hermes known IP" too. Make sure the address does not conflict
# 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.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.
# "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.
# Power cycle the HL2 after changing this item.
Hware_Hl2_EepromIPUse = 'Ignore'
#Hware_Hl2_EepromIPUse = 'Use DHCP first'
#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,
# 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.
# Power cycle the HL2 after changing this item.
Hware_Hl2_EepromMAC = '0xA1 0x6B'
#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.
# If you change this item, the bit is written to the HL2.
# The HL2 only uses these bits at power on.
# Power cycle the HL2 after changing this item.
Hware_Hl2_EepromMACUse = 'Ignore'
#Hware_Hl2_EepromMACUse = 'Set address'
## 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).
hermes_TxLNA_dB = -12
@@ -473,13 +478,6 @@ hermes_iob_rxin = 'J10 available'
#hermes_iob_rxin = 'HL2 Rx to J9'
#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.
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 ] ]
#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]]
power_meter_std_calibrations['Show ADC'] = []
## Hermes_BandDict Rx IO Bus, dict
# The Hermes_BandDict sets the 7 bits on the J16 connector for Rx.
@@ -507,17 +506,17 @@ Hermes_BandDictEnTx = False
#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
# 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 = ''
#widgets_file_name = 'hermes/hpsdr_widgets.py'
## 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
## rx_udp_ip IP change, text
@@ -585,7 +584,123 @@ Hermes_BandDictEnTx = False
#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
# This is the file that contains the control logic for each radio.
#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_*
################ Receivers SdrIQ, The SDR-IQ receiver by RfSpace
################ Receivers SdrIQ, The SDR-IQ receiver by RfSpace.
## hardware_file_name Hardware file path, rfile
# This is the file that contains the control logic for each radio.
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
# This is the file that contains the control logic for each radio.
#hardware_file_name = 'hiqsdr/quisk_hardware.py'
@@ -743,7 +858,7 @@ sndp_active = 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
# This is the file that contains the control logic for each radio.
#hardware_file_name = 'hermes/quisk_hardware.py'
@@ -758,7 +873,7 @@ sndp_active = True
# You can set these options:
## 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
## rx_udp_ip IP change, text
@@ -827,7 +942,7 @@ Hermes_BandDictEnTx = False
#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
# This is the file that contains the control logic for each radio.
#hardware_file_name = 'afedrinet/quisk_hardware.py'
@@ -859,7 +974,7 @@ Hermes_BandDictEnTx = False
#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
# 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.
@@ -1094,6 +1209,11 @@ file_play_level = 1.000
#file_play_level = 0.500
#file_play_level = 0.100
## IQ_Server_IP Pulse server IP address, text
# IP Address for remote PulseAudio IQ server.
IQ_Server_IP = ""
#IQ_Server_IP = "192.168.230.102"
# Sound card names:
#
# In PortAudio, soundcards have an index number 0, 1, 2, ... and a name.
@@ -1303,51 +1423,28 @@ fixed_tune_offset = 0
################ Remote
# DX cluster telent login data, thanks to DJ4CM. Must have station_display_lines > 0.
## dxClHost Dx cluster host name, text
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# dxClHost is the telnet host name.
dxClHost = ''
#dxClHost = 'example.host.net'
## 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"
## dxClPort Dx cluster port number, integer
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# dxClPort is the telnet port number.
dxClPort = 7373
## user_call_sign Call sign for Dx cluster, text
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# user_call_sign is your call sign which may be needed for login.
user_call_sign = ''
## dxClPassword Password for Dx cluster, text
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# dxClPassword is the telnet password for the server.
dxClPassword = ''
#dxClPassword = 'getsomedx'
## dxClExpireTime Dx cluster expire minutes, integer
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# dxClExpireTime is the time in minutes until DX Cluster entries are removed.
dxClExpireTime = 20
## IQ_Server_IP Pulse server IP address, text
#IP Address for remote PulseAudio IQ server.
IQ_Server_IP = ""
## 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
# 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
# in the controlling program.
# hamlib_ip is the IP name or address.
hamlib_ip = "localhost"
hamlib_ip = ""
#hamlib_ip = "localhost"
## hamlib_port IP port for Hamlib, integer
## hamlib_port IP port for Hamlib Rig 2, integer
# You can control Quisk from Hamlib. For direct control, set the external program to rig 2
# "Hamlib NET rigctl", and set the Quisk hamlib port to 4532. To use the rigctld program to control
# Quisk, set the Quisk hamlib port to 4575. To turn off Hamlib control, set the Quisk port to zero.
@@ -1398,19 +1495,64 @@ win_hamlib_com2_name = ""
#win_hamlib_com2_name = "COM15"
#win_hamlib_com2_name = "COM16"
## remote_radio_ip Remote radio IP or name, text
## remote_radio_ip IP or name of the remote radio, text
# Quisk can be used as a control head to control a real radio located remotely.
# This is the IP adddress or the host name of the remote radio.
remote_radio_ip = ""
#remote_radio_ip = "192.168.1.56"
## remote_radio_password Password for remote radio, text
## remote_radio_password Password for the remote radio, text
# Quisk can be used as a control head to control a real radio located remotely.
# For security it requires the same password to be entered on both computers.
# Enter a fairly long pass phrase of 20 or more characters.
# It is only necessary to enter it once on each computer.
remote_radio_password = ""
## k4_tcp_ip IP address for K4 TCP, text
# This is the Quisk IP address for the TCP server implementing K4 commands.
k4_tcp_ip = ""
#k4_tcp_ip = "localhost"
#k4_tcp_ip = "127.0.0.1"
## k4_tcp_port Port number for K4 TCP, integer
# This is the Quisk port for the TCP server implementing K4 commands.
k4_tcp_port = 0
#k4_tcp_port = 9200
#k4_tcp_port = 50004
# DX cluster telent login data, thanks to DJ4CM. Must have station_display_lines > 0.
## dxClHost Dx cluster host name, text
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# dxClHost is the telnet host name.
dxClHost = ''
#dxClHost = 'example.host.net'
## dxClPort Dx cluster port number, integer
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# dxClPort is the telnet port number.
dxClPort = 7373
## user_call_sign Call sign for Dx cluster, text
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# user_call_sign is your call sign which may be needed for login.
user_call_sign = ''
## dxClPassword Password for Dx cluster, text
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# dxClPassword is the telnet password for the server.
dxClPassword = ''
#dxClPassword = 'getsomedx'
## dxClExpireTime Dx cluster expire minutes, integer
# The Dx cluster options log into a Dx cluster server, and put station information
# on the station window under the graph and waterfall screens.
# dxClExpireTime is the time in minutes until DX Cluster entries are removed.
dxClExpireTime = 20
hamlib_com1_name = ""
@@ -1782,23 +1924,6 @@ add_extern_demod = ""
add_fdx_button = 0
#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
# for these values, but you can change them if you want.
FilterBwCW = (200, 400, 600, 1000, 1500, 3000)
+2
View File
@@ -120,6 +120,8 @@ class Hardware:
self.application.bandscope_clock = clock
#def PollCwKey(self): # Optional. Called frequently by the sound thread to check the CW key status.
# pass # Do not define if not needed.
#def PollGuiControl(self): # Optional. Called frequently by the GUI thread to change GUI settings (PTT etc.)
# pass # Do not define if not needed.
def StartSamples(self): # Quisk calls this from the sound thread to start sending samples.
# If you return a string, it replaces the string returned from hardware open()
pass
+35 -27
View File
@@ -2,60 +2,68 @@
#include <Python.h>
#include <stdlib.h>
#include <complex.h>
#define CLIP32 2147483647
#define CLIP16 32767
#define MAX_CHANNELS 32
#define SAMPLE_BYTES (sizeof(double) * 2)
static struct _channel {
double * cBuf; // save samples until nBuf reaches in_size
int sizeBuf; // current size of cBuf in count of complex samples
int nBuf; // current number of complex samples in cBuf
int in_size; // count of complex samples needed for wdsp_fexchange0
int in_use; // is this channel being used?
complex double * cBuf; // circular sample buffer; save samples until nBuf reaches in_size
int sizeBuf; // current size of cBuf in complex samples; a multiple of in_size
int nBuf; // current number of complex samples in cBuf
int in_size; // count of complex samples needed for wdsp_fexchange0
int in_use; // is this channel being used?
int Windex; // index of write
int Rindex; // index of read
} wdspChannel[MAX_CHANNELS];
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.
// When we have enough samples process the samples by calling wdsp. Return the samples in the same array.
// cSamples is complex double. We treat it as interleaved Real/Imag doubles twice as long.
// nSamples is the count of complex samples.
// When we have enough samples process the samples by calling wdsp. Return the samples in cSamples.
// cSamples is complex double. nSamples is the count of complex samples.
struct _channel * ptChannel = wdspChannel + channel;
int i, error;
double * ptD, * ptS;
int i, error, in_size;
if ( ! ptChannel->in_use)
if ( ! ptChannel->in_use) {
ptChannel->Windex = 0;
ptChannel->Rindex = 0;
ptChannel->nBuf = 0;
return nSamples;
}
if ( ! wdsp_fexchange0)
return nSamples;
if (nSamples <= 0)
return nSamples;
if (ptChannel->nBuf + nSamples >= ptChannel->sizeBuf) {
ptChannel->sizeBuf = ptChannel->nBuf + nSamples * 3;
ptChannel->cBuf = (double *)realloc(ptChannel->cBuf, ptChannel->sizeBuf * SAMPLE_BYTES);
in_size = ptChannel->in_size;
i = nSamples / in_size + 3; // blocks need for samples plus a partial block
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
ptD = ptChannel->cBuf + ptChannel->nBuf * 2;
for (i = 0; i < nSamples; i++) {
*ptD++ = *ptS++ / CLIP32;
*ptD++ = *ptS++ / CLIP32;
for (i = 0; i < nSamples; i++) { // Copy samples from cSamples to cBuf
ptChannel->cBuf[ptChannel->Windex++] = cSamples[i] / CLIP32;
if (ptChannel->Windex >= ptChannel->sizeBuf)
ptChannel->Windex = 0;
}
ptChannel->nBuf += nSamples;
nSamples = 0;
while (ptChannel->nBuf >= ptChannel->in_size) {
(*wdsp_fexchange0)(channel, ptChannel->cBuf, cSamples + nSamples * 2, &error);
while (ptChannel->nBuf >= in_size) {
(*wdsp_fexchange0)(channel, (double *)(ptChannel->cBuf + ptChannel->Rindex), (double *)(cSamples + nSamples), &error);
if (error)
printf("WDSP: wdsp_fexchange0 error %d\n", error);
nSamples += ptChannel->in_size;
ptChannel->nBuf -= ptChannel->in_size;
memmove(ptChannel->cBuf, ptChannel->cBuf + ptChannel->in_size * 2, ptChannel->nBuf * SAMPLE_BYTES);
ptChannel->Rindex += in_size;
if (ptChannel->Rindex >= ptChannel->sizeBuf)
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;
return nSamples;
}
+14 -10
View File
@@ -25,16 +25,20 @@ class Cwdsp:
pass
else:
try:
self.Lib = ctypes.CDLL("./libwdsp.so")
Log ("Found private wdsp")
self.Lib = ctypes.CDLL("./wdsp/libwdsp.so")
Log ("Found private wdsp in quisk/wdsp")
except:
name = ctypes.util.find_library("wdsp")
if name:
try:
self.Lib = ctypes.CDLL(name)
Log ("Found public wdsp")
except:
pass
try:
self.Lib = ctypes.CDLL("./libwdsp.so")
Log ("Found private wdsp in quisk/")
except:
name = ctypes.util.find_library("wdsp")
if name:
try:
self.Lib = ctypes.CDLL(name)
Log ("Found public wdsp")
except:
pass
if not self.Lib:
Log("Wdsp was not found")
return
@@ -73,7 +77,7 @@ class Cwdsp:
dsp_size = 256
QS.wdsp_set_parameter(channel, in_size=in_size)
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.RXANBPSetRun (channel, 0)
Lib.SetRXAAMSQRun (channel, 0)
+7 -1
View File
@@ -582,6 +582,7 @@ class QuiskPushbutton(QuiskButtons, wx.lib.buttons.GenButton):
self.Bind(wx.EVT_BUTTON, self.OnButton)
self.InitButtons(text, text_color)
self.direction = 1
self.normal_brush = self.up_brush
if use_right:
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
@@ -603,7 +604,12 @@ class QuiskPushbutton(QuiskButtons, wx.lib.buttons.GenButton):
event = wx.PyEvent()
event.SetEventObject(self)
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):
"""A push button that repeats when held down."""
+3 -2
View File
@@ -7,7 +7,7 @@ import os
# You must define the version here. A title string including
# the version will be written to __init__.py and read by quisk.py.
Version = '4.2.38'
Version = '4.2.52'
fp = open("__init__.py", "w") # write title string
fp.write("#Quisk version %s\n" % Version)
@@ -16,7 +16,8 @@ fp.close()
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',
'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
mAfedri = Extension ('quisk.afedrinet.afedrinet_io',
+389
View File
@@ -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);
}
+73
View File
@@ -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
+16 -7
View File
@@ -113,7 +113,7 @@ void quisk_sample_level(const char * msg, complex double * cSamples, int nSample
level = d;
}
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;
count = 0;
time0 = QuiskTimeSec();
@@ -1000,22 +1000,28 @@ int quisk_read_sound(void) // Called from sound thread
if (remote_control_slave)
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)
quisk_tmp_playback(cSamples, nSamples, 1.0); // replace radio sound
else if (quisk_record_state == FILE_PLAY_SPKR_MIC)
quisk_file_playback(cSamples, nSamples, file_play_level); // replace radio sound
// Play the demodulated audio
#if DEBUG_MIC != 2
if ( ! quisk_play_sidetone(&quisk_Playback)) // play sidetone
play_sound_interface(&quisk_Playback, nSamples, cSamples, 1, quisk_audioVolume); // else play radio sound
if ( ! quisk_play_sidetone(&quisk_Playback)) { // play sidetone
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
if (radio_sound_socket != INVALID_SOCKET)
send_radio_sound_socket(cSamples, nSamples, quisk_audioVolume);
// Play digital if required
if (is_DGT)
play_sound_interface(&DigitalOutput, nSamples, cSamples, 1, digital_output_level);
//if (is_DGT)
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
if ( ! key_state && file_rec_audio.fp)
@@ -1061,7 +1067,10 @@ int quisk_read_sound(void) // Called from sound thread
for (i = 0; i < mic_count; i++)
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
if (key_state && file_rec_audio.fp)
quisk_record_audio(&file_rec_audio, cSamples, mic_count);
+8 -8
View File
@@ -199,14 +199,14 @@ static int quisk_open_capture(struct sound_dev * dev)
DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)DsEnumCapture, dev);
if (errFound != DS_OK) {
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)
QuiskPrintf("%s\n", dev->dev_errmsg);
return 1;
}
if (errOpen != DS_OK) {
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)
QuiskPrintf("%s\n", dev->dev_errmsg);
return 1;
@@ -226,7 +226,7 @@ static int quisk_open_capture(struct sound_dev * dev)
}
else {
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;
}
ptBuf = (LPDIRECTSOUNDCAPTUREBUFFER)dev->buffer;
@@ -235,7 +235,7 @@ static int quisk_open_capture(struct sound_dev * dev)
if (quisk_sound_state.verbose_sound)
QuiskPrintf("Capture start error 0x%lX", hr);
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;
}
if (quisk_sound_state.verbose_sound) {
@@ -270,18 +270,18 @@ static int quisk_open_playback(struct sound_dev * dev)
DirectSoundEnumerate((LPDSENUMCALLBACK)DsEnumPlay, dev);
if (errFound != DS_OK) {
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;
}
if (errOpen != DS_OK) {
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;
}
hr = IDirectSound_SetCooperativeLevel ((LPDIRECTSOUND8)dev->handle, quisk_mainwin_handle, DSSCL_PRIORITY);
if (hr != DS_OK) {
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;
}
dev->sample_bytes = 4;
@@ -299,7 +299,7 @@ static int quisk_open_playback(struct sound_dev * dev)
}
else {
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;
}
if (quisk_sound_state.verbose_sound) {
+47 -50
View File
@@ -64,12 +64,20 @@ volatile int streams_to_start;
// remember all open devices for easy cleanup on exit
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 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
* tell the quisk thread when streams are ready.
*/
@@ -1043,19 +1051,23 @@ 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.
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;
source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
if ( ! strcmp(l->name, "QuiskDigitalInput"))
have_QuiskDigitalInput = 1;
if ( ! strcmp(l->name, "QuiskDigitalOutput"))
have_QuiskDigitalOutput = 1;
source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
for (i = 0; i < NUM_NEW_SINKS; i++) {
if ( ! strcmp(l->name, sink_list[i].name)) {
sink_list[i].create = 0;
}
}
}
static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) {
if (eol > 0)
static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata)
{
if (eol > 0)
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) {
@@ -1070,6 +1082,9 @@ PyObject * quisk_pulseaudio_sound_devices(PyObject * self, PyObject * args)
pa_operation *pa_op=NULL;
pa_context *pa_names_ctx;
int state = 0;
int sink_index = 0;
char * sink_name;
char buf256[256];
if (!PyArg_ParseTuple (args, ""))
return NULL;
@@ -1113,59 +1128,41 @@ PyObject * quisk_pulseaudio_sound_devices(PyObject * self, PyObject * args)
pa_mainloop_iterate(pa_names_ml, 1, NULL);
break;
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) {
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++;
}
pa_mainloop_iterate(pa_names_ml, 1, NULL);
else
pa_mainloop_iterate(pa_names_ml, 1, NULL);
break;
// The following loads null sinks for digital input and output if they are not present.
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) {
pa_operation_unref(pa_op);
state = 4;
state = 3;
}
else
pa_mainloop_iterate(pa_names_ml, 1, NULL);
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:
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
pa_operation_unref(pa_op);
state = 6;
}
else
pa_mainloop_iterate(pa_names_ml, 1, NULL);
pa_op = pa_context_get_source_info_list(pa_names_ctx, pa_sourcelist_cb, pycapt);
state++;
pa_mainloop_iterate(pa_names_ml, 1, NULL);
break;
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) {
pa_operation_unref(pa_op);
state = 9;
+725
View File
@@ -0,0 +1,725 @@
#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ws.h"
#include <pthread.h>
#include <complex.h>
#include <time.h>
#include "quisk.h"
#define TCI_STREAM_DATA_BYTES 16384
#define TCI_RX_BUF_SIZE 1024
#define TCI_COMMAND_SIZE 32
static int verbose; // non-zero for verbose log messages
static int tci_port;
// Implement the ExpertSDR2 Version 1.4 protocol:
// The only sample rate is 48000. The Tx audio parameters are copied from the Rx audio stream.
// The only sample type is FLOAT32 type 3.
// WSJT-X RX_AUDIO_STREAM must be two channels and Stream.length is the number of floats (not samples).
// WSJT-X TX_AUDIO_STREAM always returns two channels and Stream.length is the number of floats (not samples).
// WSJT-X TX_AUDIO_STREAM is twice as long, but the data is in the first half and garbage is in the second half.
// WSJT-X TX_CHRONO Stream.length is the number of floats, and the returned TX_AUDIO_STREAM length is equal to it.
// 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 int64_t quisk_dds;
//static int quisk_if;
static long long quisk_vfo;
static long long client_vfo=-1;
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 _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)
{
struct _Stream * pt = (struct _Stream *)msg;
if (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);
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);
for (i = 0; ctx->msg_buf[i]; i++)
ctx->msg_buf[i] = tolower(ctx->msg_buf[i]);
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;
break;
}
}
pthread_mutex_unlock(&clients_list_mutex);
}
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);
}
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;
}
else {
return 0;
}
}
else if (strcmp(command, "audio_samplerate") == 0) {
if (atoi(arg1) != 48000)
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;
}
else if (strcmp(command, "audio_stream_samples") == 0) {
return 0; // do not send
}
break;
case 'i':
if (strcmp(command, "iq_start") == 0)
return 0; // do not send
else if (strcmp(command, "iq_stop") == 0)
return 0; // do not send
else if (strcmp(command, "iq_samplerate") == 0)
return 0; // do not send
break;
case 'm':
if (strcmp(command, "modulation") == 0) {
if (arg2) {
strncpy(client_modulation, arg2, TCI_COMMAND_SIZE - 1);
}
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;
}
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;
}
}
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);
}
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->audio_stream_samplerate = 48000;
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
//sendframe_txt(client, "if_limits:-48000,48000;");
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,%d;", 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 in WSJT-X.
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);
}
}
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;
}
void tci_send_audio(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_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;
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++;
if (two_channels) {
*(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));
stream.length = 0;
}
}
break;
}
}
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", "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, new_if=-1, new_trx=-1, new_split_enable=-1;
char * mode=NULL;
char char_buf[TCI_COMMAND_SIZE];
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiLiLiis", kwlist,
&start, &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 (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 (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_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 PyString_FromString(char_buf);
}
Py_INCREF (Py_None);
return Py_None;
}
+89
View File
@@ -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;
}
+37
View File
@@ -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
+63
View File
@@ -321,3 +321,66 @@ PyObject * QuiskPrintf(char * format, ...)
return NULL;
}
#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
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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
+7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+60
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+195
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+167
View File
@@ -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
BIN
View File
Binary file not shown.
+29045
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
#ifndef _calculus_h
#define _calculus_h
extern double GG[];
extern double GGS[];
#endif
+126
View File
@@ -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);
}
+73
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+84
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+64
View File
@@ -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
+135
View File
@@ -0,0 +1,135 @@
/* delay.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2013, 2019 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"
DELAY create_delay (int run, int size, double* in, double* out, int rate, double tdelta, double tdelay)
{
DELAY a = (DELAY) malloc0 (sizeof (delay));
a->run = run;
a->size = size;
a->in = in;
a->out = out;
a->rate = rate;
a->tdelta = tdelta;
a->tdelay = tdelay;
a->L = (int)(0.5 + 1.0 / (a->tdelta * (double)a->rate));
a->adelta = 1.0 / (a->rate * a->L);
a->ft = 0.45 / (double)a->L;
a->ncoef = (int)(60.0 / a->ft);
a->ncoef = (a->ncoef / a->L + 1) * a->L;
a->cpp = a->ncoef / a->L;
a->phnum = (int)(0.5 + a->tdelay / a->adelta);
a->snum = a->phnum / a->L;
a->phnum %= a->L;
a->idx_in = 0;
a->adelay = a->adelta * (a->snum * a->L + a->phnum);
a->h = fir_bandpass (a->ncoef,-a->ft, +a->ft, 1.0, 1, 0, (double)a->L);
a->rsize = a->cpp + (WSDEL - 1);
a->ring = (double *) malloc0 (a->rsize * sizeof (complex));
InitializeCriticalSectionAndSpinCount ( &a->cs_update, 2500 );
return a;
}
void destroy_delay (DELAY a)
{
DeleteCriticalSection (&a->cs_update);
_aligned_free (a->ring);
_aligned_free (a->h);
_aligned_free (a);
}
void flush_delay (DELAY a)
{
memset (a->ring, 0, a->cpp * sizeof (complex));
a->idx_in = 0;
}
void xdelay (DELAY a)
{
EnterCriticalSection (&a->cs_update);
if (a->run)
{
int i, j, k, idx, n;
double Itmp, Qtmp;
for (i = 0; i < a->size; i++)
{
a->ring[2 * a->idx_in + 0] = a->in[2 * i + 0];
a->ring[2 * a->idx_in + 1] = a->in[2 * i + 1];
Itmp = 0.0;
Qtmp = 0.0;
if ((n = a->idx_in + a->snum) >= a->rsize) n -= a->rsize;
for (j = 0, k = a->L - 1 - a->phnum; j < a->cpp; j++, k+= a->L)
{
if ((idx = n + j) >= a->rsize) idx -= a->rsize;
Itmp += a->ring[2 * idx + 0] * a->h[k];
Qtmp += a->ring[2 * idx + 1] * a->h[k];
}
a->out[2 * i + 0] = Itmp;
a->out[2 * i + 1] = Qtmp;
if (--a->idx_in < 0) a->idx_in = a->rsize - 1;
}
}
else if (a->out != a->in)
memcpy (a->out, a->in, a->size * sizeof (complex));
LeaveCriticalSection (&a->cs_update);
}
/********************************************************************************************************
* *
* Properties *
* *
********************************************************************************************************/
void SetDelayRun (DELAY a, int run)
{
EnterCriticalSection (&a->cs_update);
a->run = run;
LeaveCriticalSection (&a->cs_update);
}
double SetDelayValue (DELAY a, double tdelay)
{
double adelay;
EnterCriticalSection (&a->cs_update);
a->tdelay = tdelay;
a->phnum = (int)(0.5 + a->tdelay / a->adelta);
a->snum = a->phnum / a->L;
a->phnum %= a->L;
a->adelay = a->adelta * (a->snum * a->L + a->phnum);
adelay = a->adelay;
LeaveCriticalSection (&a->cs_update);
return adelay;
}
void SetDelayBuffs (DELAY a, int size, double* in, double* out)
{
EnterCriticalSection (&a->cs_update);
a->size = size;
a->in = in;
a->out = out;
LeaveCriticalSection (&a->cs_update);
}
+77
View File
@@ -0,0 +1,77 @@
/* delay.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2013, 2019 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 _delay_h
#define _delay_h
#define WSDEL 1025 // number of supported whole sample delays
typedef struct _delay
{
int run; // run
int size; // number of input samples per buffer
double* in; // input buffer
double* out; // output buffer
int rate; // samplerate
double tdelta; // delay increment required (seconds)
double tdelay; // delay requested (seconds)
int L; // interpolation factor
int ncoef; // number of coefficients
int cpp; // coefficients per phase
double ft; // normalized cutoff frequency
double* h; // coefficients
int snum; // starting sample number (0 for sub-sample delay)
int phnum; // phase number
int idx_in; // index for input into ring
int rsize; // ring size in complex samples
double* ring; // ring buffer
double adelta; // actual delay increment
double adelay; // actual delay
CRITICAL_SECTION cs_update;
} delay, *DELAY;
extern DELAY create_delay (int run, int size, double* in, double* out, int rate, double tdelta, double tdelay);
extern void destroy_delay (DELAY a);
extern void flush_delay (DELAY a);
extern void xdelay (DELAY a);
// Properties
extern void SetDelayRun (DELAY a, int run);
extern double SetDelayValue (DELAY a, double delay); // returns actual delay in seconds
extern void SetDelayBuffs (DELAY a, int size, double* in, double* out);
#endif
+716
View File
@@ -0,0 +1,716 @@
/* dexp.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2018, 2019 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"
DEXP pdexp[4];
DELRING calc_delring (int rsize, int size, int delay, double* in, double* out)
{
DELRING a = (DELRING) malloc0 (sizeof (delring));
a->rsize = rsize;
a->size = size;
a->rdelay = delay;
a->in = in;
a->out = out;
a->ring = (double *) malloc0 (a->rsize * sizeof (complex));
a->inptr = a->rdelay;
a->outptr = 0;
return a;
}
void decalc_delring (DELRING a)
{
_aligned_free (a->ring);
_aligned_free (a);
}
void flush_delring (DELRING a)
{
memset (a->ring, 0, a->rsize * sizeof (complex));
a->inptr = a->rdelay;
a->outptr = 0;
}
void xdelring (DELRING a)
{
int first, second;
// copy in
if (a->size > (a->rsize - a->inptr))
{
first = a->rsize - a->inptr;
second = a->size - first;
}
else
{
first = a->size;
second = 0;
}
memcpy (a->ring + 2 * a->inptr, a->in, first * sizeof (complex));
memcpy (a->ring, a->in + 2 * first, second * sizeof (complex));
a->inptr = (a->inptr + a->size) % a->rsize;
// copy out
if (a->size > (a->rsize - a->outptr))
{
first = a->rsize - a->outptr;
second = a->size - first;
}
else
{
first = a->size;
second = 0;
}
memcpy (a->out, a->ring + 2 * a->outptr, first * sizeof (complex));
memcpy (a->out + 2 * first, a->ring, second * sizeof (complex));
a->outptr = (a->outptr + a->size) % a->rsize;
}
void calc_slews (DEXP a)
{
int i;
double delta, theta;
delta = PI / (double)a->nattack;
theta = 0.0;
for (i = 0; i <= a->nattack; i++)
{
a->cattack[i] = a->low_gain + (1.0 - a->low_gain) * 0.5 * (1.0 - cos (theta));
theta += delta;
}
delta = PI / (double)a->ndecay;
theta = 0.0;
for (i = 0; i <= a->ndecay; i++)
{
a->cdecay[i] = a->low_gain + (1.0 - a->low_gain) * 0.5 * (1.0 + cos (theta));
theta += delta;
}
}
void calc_buffs (DEXP a)
{
a->trigsig = (double *)malloc0 (2 * a->size * sizeof(complex)); // allow for double-sized output of filter
a->delsig = (double *)malloc0 ( a->size * sizeof(complex));
a->audbuffer = (double *)malloc0 ( a->size * sizeof(complex));
}
void decalc_buffs (DEXP a)
{
_aligned_free (a->audbuffer);
_aligned_free (a->delsig);
_aligned_free (a->trigsig);
}
void calc_dexp (DEXP a)
{
// trigger signal preparation
a->avm = exp(-1.0 / (a->rate * a->dettau));
a->onem_avm = 1.0 - a->avm;
a->avsig = 0.0;
// level change
a->nattack = (int)(a->tattack * a->rate);
a->ndecay = (int)(a->tdecay * a->rate);
a->cattack = (double *)malloc0((a->nattack + 1) * sizeof(double));
a->cdecay = (double *)malloc0((a->ndecay + 1) * sizeof(double));
a->low_gain = 1.0 / a->exp_ratio;
calc_slews(a);
// control
a->state = 0;
a->count = 0;
a->hold_thresh = a->hysteresis_ratio * a->attack_thresh; // hysteresis ratio < 1.0
a->nhold = (int)(a->thold * a->rate);
// vox
a->vox_count = (int)(a->audelay * a->rate);
// audio delay
a->audring = calc_delring ((int)a->rate, a->size, (int)(a->audelay * a->rate), a->audbuffer, a->out);
}
void decalc_dexp (DEXP a)
{
decalc_delring (a->audring);
_aligned_free (a->cdecay);
_aligned_free (a->cattack);
}
void calc_filter (DEXP a)
{
double* impulse;
// 2.0 gain on filter is somewhat arbitrarily chosen to get trigger input similar to that without the filter, knowing
// that for any reasonable use of the filter there will be a reduction in trigger signal.
impulse = fir_bandpass (a->nc, a->low_cut, a->high_cut, a->rate, a->wintype, 1, 2.0/(double)(2 * a->size));
// print_impulse ("scf.txt", a->nc, impulse, 1, 0);
a->p = create_fircore (a->size, a->in, a->trigsig, a->nc, 1, impulse);
_aligned_free (impulse);
a->scdring = calc_delring (a->size + a->nc / 2, a->size, a->nc / 64, a->in, a->delsig);
}
void decalc_filter (DEXP a)
{
destroy_fircore (a->p);
decalc_delring (a->scdring);
}
void calc_antivox(DEXP a)
{
a->antivox_mult = exp(-1.0 / (a->antivox_rate * a->antivox_tau));
a->antivox_onemmult = 1.0 - a->antivox_mult;
a->antivox_data = (double *) malloc0 (a->antivox_size * sizeof (complex));
}
void decalc_antivox(DEXP a)
{
_aligned_free (a->antivox_data);
}
PORT
void create_dexp (int id, int run_dexp, int size, double* in, double* out, int rate, double dettau, double tattack, double tdecay,
double thold, double exp_ratio, double hyst_ratio, double attack_thresh, int nc, int wtype, double lowcut, double highcut,
int run_filt, int run_vox, int run_audelay, double audelay, void (__stdcall *pushvox)(int id, int active),
int antivox_run, int antivox_size, int antivox_rate, double antivox_gain, double antivox_tau)
{
DEXP a = (DEXP) malloc0 (sizeof (dexp));
a->id = id;
a->run_dexp = run_dexp;
a->size = size;
a->in = in;
a->out = out;
a->rate = (double)rate;
a->dettau = dettau;
a->tattack = tattack;
a->tdecay = tdecay;
a->thold = thold;
a->exp_ratio = exp_ratio;
a->hysteresis_ratio = hyst_ratio;
a->attack_thresh = attack_thresh;
a->nc = nc;
a->wintype = wtype;
a->low_cut = lowcut;
a->high_cut = highcut;
a->run_filt = run_filt;
a->run_vox = run_vox;
a->run_audelay = run_audelay;
a->audelay = audelay;
a->pushvox = pushvox;
a->antivox_run = antivox_run;
a->antivox_size = antivox_size;
a->antivox_rate = (double)antivox_rate;
a->antivox_gain = antivox_gain;
a->antivox_tau = antivox_tau;
calc_buffs (a);
calc_dexp (a);
calc_filter (a);
calc_antivox (a);
InitializeCriticalSectionAndSpinCount(&a->cs_update, 2500);
pdexp[id] = a;
return;
}
PORT
void destroy_dexp (int id)
{
DEXP a = pdexp[id];
DeleteCriticalSection (&a->cs_update);
decalc_antivox (a);
decalc_filter (a);
decalc_dexp (a);
decalc_buffs (a);
_aligned_free (a);
}
PORT
void flush_dexp (int id)
{
DEXP a = pdexp[id];
memset (a->audbuffer, 0, a->size * sizeof (complex));
memset (a->trigsig, 0, a->size * sizeof (complex));
memset (a->delsig, 0, a->size * sizeof (complex));
a->avsig = 0.0;
a->state = 0;
a->count = 0;
flush_fircore (a->p);
flush_delring (a->scdring);
flush_delring (a->audring);
}
enum _dexpstate
{
DEXP_LOW,
DEXP_ATTACK,
DEXP_HIGH,
DEXP_HOLD,
DEXP_DECAY
};
PORT
void xdexp (int id)
{
DEXP a = pdexp[id];
int i;
double sig, gain, asig;
double max = 0.0;
EnterCriticalSection (&a->cs_update);
// ******* BEGIN SIDE-CHANNEL FILTER *******
if (a->run_filt)
{
xdelring (a->scdring); // input is 'a->in'; output is 'a->delsig'
xfircore (a->p); // input is 'a->in'; output is 'a->trigsig'
}
else
{
memcpy (a->delsig, a->in, a->size * sizeof (complex));
memcpy (a->trigsig, a->in, a->size * sizeof (complex));
}
// ******* END SIDE-CHANNEL FILTER *******
// ******* CALCULATE ANTIVOX LEVEL *******
if (a->state == DEXP_LOW && a->antivox_new != 0)
{
// if VOX is currently NOT triggered, and, if we have new antivox data to process
for (i = 0; i < a->antivox_size; i++)
{
sig = sqrt (a->antivox_data[2 * i + 0] * a->antivox_data[2 * i + 0] + a->antivox_data[2 * i + 1] * a->antivox_data[2 * i + 1]);
a->antivox_level = a->antivox_mult * a->antivox_level + a->antivox_onemmult * sig;
}
// set the new_data flag to zero
a->antivox_new = 0;
}
// ******* END CALCULATE ANTIVOX LEVEL *******
// ******* BEGIN DEXP *******
// uses 'a->trigsig' as trigger signal; uses 'a->delsig' as audio input
// 'a->audbuffer' is audio output
// DEXP code runs continuously so it can be used to trigger VOX also.
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;
if (a->avsig > max) max = a->avsig;
switch (a->state)
{
case DEXP_LOW:
if (a->antivox_run)
asig = a->avsig - a->antivox_gain * a->antivox_level;
else
asig = a->avsig;
if (asig > a->attack_thresh)
{
a->state = DEXP_ATTACK;
a->count = a->nattack;
}
a->audbuffer[2 * i + 0] = a->low_gain * a->delsig[2 * i + 0];
a->audbuffer[2 * i + 1] = a->low_gain * a->delsig[2 * i + 1];
// ******* BEGIN VOX *******
// If we're going to attack, turn on VOX immediately.
// Prepare 'vox_count' for the next turnoff too.
if (a->run_vox && a->state == DEXP_ATTACK)
{
(a->pushvox)(a->id, 1);
// Set vox_count for delay IF the audio delay is also enabled.
if (a->run_audelay)
a->vox_count = (int)(a->audelay * a->rate);
else
a->vox_count = 1;
}
// If we're sitting in this state and the delayed vox count expires, turn OFF VOX.
else if (a->run_vox && --(a->vox_count) == 0)
(a->pushvox)(a->id, 0);
// Don't let 'vox_count' keep counting down.
else if (a->vox_count < 0)
a->vox_count = 0;
// ******* END VOX *******
break;
case DEXP_ATTACK:
gain = a->cattack[a->nattack - a->count];
a->audbuffer[2 * i + 0] = a->delsig[2 * i + 0] * gain;
a->audbuffer[2 * i + 1] = a->delsig[2 * i + 1] * gain;
if (a->count-- == 0)
a->state = DEXP_HIGH;
break;
case DEXP_HIGH:
if (a->avsig < a->hold_thresh)
{
a->state = DEXP_HOLD;
a->count = a->nhold;
}
a->audbuffer[2 * i + 0] = a->delsig[2 * i + 0];
a->audbuffer[2 * i + 1] = a->delsig[2 * i + 1];
break;
case DEXP_HOLD:
a->audbuffer[2 * i + 0] = a->delsig[2 * i + 0];
a->audbuffer[2 * i + 1] = a->delsig[2 * i + 1];
if (a->avsig > a->attack_thresh)
a->state = DEXP_HIGH;
else if (a->count-- == 0)
{
a->state = DEXP_DECAY;
a->count = a->ndecay;
}
break;
case DEXP_DECAY:
gain = a->cdecay[a->ndecay - a->count];
a->audbuffer[2 * i + 0] = a->delsig[2 * i + 0] * gain;
a->audbuffer[2 * i + 1] = a->delsig[2 * i + 1] * gain;
if (a->count-- == 0)
a->state = DEXP_LOW;
break;
}
}
a->peak = max;
// If DEXP functionality is set to OFF, copy its input to overwrite its output.
if (!a->run_dexp)
memcpy (a->audbuffer, a->delsig, a->size * sizeof (complex));
// ******* END DEXP *******
// ******* BEGIN AUDIO DELAY *******
if (a->run_audelay)
xdelring (a->audring); // uses 'a->audbuffer' as audio input; uses 'a->out' as audio output
else
memcpy (a->out, a->audbuffer, a->size * sizeof (complex));
// ******* END AUDIO DELAY *******
LeaveCriticalSection (&a->cs_update);
}
PORT
void SendCBPushDexpVox (int id, void (__stdcall *pushvox)(int id, int active))
{
// Call to set the address of the callback to operate VOX.
DEXP a = pdexp[id];
a->pushvox = pushvox;
}
PORT
void SetDEXPRun (int id, int run)
{
// run != 0, puts dexp in the audio processing path; otherwise, it's only used to trigger VOX
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
a->run_dexp = run;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPSize (int id, int size)
{
// There are some constraints on the input/output buffer sizes.
// * must be a power-of-two
// * must be less than or equal to 'nc', the number of filter coefficients, which is also a power-of-two
// * must be less than 'rate' samples because of the sizing of 'audring'
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_filter (a);
decalc_dexp (a);
decalc_buffs (a);
a->size = size;
calc_buffs (a);
calc_dexp (a);
calc_filter (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPIOBuffers (int id, double* in, double* out)
{
// Sets the input/output buffers. They can be the same.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_filter (a);
decalc_dexp (a);
a->in = in;
a->out = out;
calc_dexp (a);
calc_filter (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPRate (int id, double rate)
{
// Sets the sample rate.
// This is used for timing and filter calculations as well as sizing 'audring'.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_filter (a);
decalc_dexp (a);
a->rate = rate;
calc_dexp (a);
calc_filter (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPDetectorTau (int id, double tau)
{
// Time-constant for smoothing the signal for detection (seconds).
// 0.01 seconds is a good starting point to try.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_dexp (a);
a->dettau = tau;
calc_dexp (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPAttackTime (int id, double time)
{
// Set attack time, seconds.
// 0.002 - 0.100 should be a good range.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_dexp (a);
a->tattack = time;
calc_dexp (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPReleaseTime (int id, double time)
{
// Set release time, seconds.
// 0.002 - 0.999 should be a good range.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_dexp (a);
a->tdecay = time;
calc_dexp (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPHoldTime (int id, double time)
{
// Set hold time, seconds.
// 0.000 - 2.000 should be a good range.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_dexp (a);
a->thold = time;
calc_dexp (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPExpansionRatio (int id, double ratio)
{
// Set expansion ratio. High_gain = 1.0; Low_gain = 1.0/exp_ratio.
// Range of 1.0 - 30.0 should be good. Could use dB: 0.0 - 30.0dB.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_dexp (a);
a->exp_ratio = ratio;
calc_dexp (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPHysteresisRatio (int id, double ratio)
{
// Set Hysteresis Ratio. Hold_thresh = hysteresis_ratio * Attack_thresh.
// Expose to operator in dB: 0.0dB - 9.9dB should be good (1.000 - 0.320).
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_dexp (a);
a->hysteresis_ratio = ratio;
calc_dexp (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPAttackThreshold (int id, double thresh)
{
// Set attack threshold.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_dexp (a);
a->attack_thresh = thresh;
calc_dexp (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPFilterTaps (int id, int taps)
{
// Set number of taps. Must be a power of two and an even multiple of 'size'.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_filter (a);
a->nc = taps;
calc_filter (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPWindowType (int id, int type)
{
// Set filter window type.
// * 0 - 4-term Blackman-Harris.
// * 1 - 7-term Blackman-Harris.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_filter (a);
a->wintype = type;
calc_filter (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPLowCut (int id, double lowcut)
{
// Set side-channel filter low_cut (Hertz).
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_filter (a);
a->low_cut = lowcut;
calc_filter (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPHighCut (int id, double highcut)
{
// Set side-channel filter high_cut (Hertz).
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_filter (a);
a->high_cut = highcut;
calc_filter (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPRunSideChannelFilter (int id, int run)
{
// Turn OFF/ON the side-channel filter and its compensating delay.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
a->run_filt = run;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPRunVox (int id, int run)
{
// Turn OFF/ON calls to 'pushvox(...)'.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
a->run_vox = run;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPRunAudioDelay (int id, int run)
{
// Turn OFF/ON audio delay line.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
a->run_audelay = run;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetDEXPAudioDelay (int id, double delay)
{
// Set the audio delay, seconds.
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_dexp (a);
a->audelay = delay;
calc_dexp (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void GetDEXPPeakSignal (int id, double* peak)
{
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
*peak = a->peak;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetAntiVOXRun (int id, int run)
{
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
a->antivox_run = run;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetAntiVOXSize (int id, int size)
{
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_antivox(a);
a->antivox_size = size;
calc_antivox(a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetAntiVOXRate (int id, double rate)
{
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_antivox(a);
a->antivox_rate = rate;
calc_antivox(a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetAntiVOXGain (int id, double gain)
{
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
a->antivox_gain = gain;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetAntiVOXDetectorTau (int id, double tau)
{
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
decalc_antivox (a);
a->antivox_tau = tau;
calc_antivox (a);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SendAntiVOXData (int id, int nsamples, double* data)
{
// note: 'nsamples' is not used as it has been previously specified
DEXP a = pdexp[id];
EnterCriticalSection (&a->cs_update);
memcpy (a->antivox_data, data, a->antivox_size * sizeof (complex));
a->antivox_new = 1;
LeaveCriticalSection (&a->cs_update);
}
+123
View File
@@ -0,0 +1,123 @@
/* dexp.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2018, 2019 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 _dexp_h
#define _dexp_h
typedef struct _delring
{
int rsize; // ringsize (measured in complex samples)
double* ring; // ring buffer
int inptr; // ring input pointer (counts in complex samples)
int outptr; // ring output pointer (counts in complex samples)
int rdelay; // ring delay (measured in complex samples)
int size; // input/output size in complex samples
double* in; // source buffer
double* out; // destination buffer
} delring, *DELRING;
typedef struct _dexp
{
int id; // 'id' for this dexp
int run_dexp; // 0 if dexp is OFF; 1 if it's ON
int size; // size of input/output buffers
double* in; // audio input buffer
double* out; // audio output buffer; can be same as 'in'
double rate; // sample rate
double dettau; // detection averaging time constant
double avm; // averaging multiplier
double onem_avm; // one minus averaging multiplier
double avsig; // averaged detection signal
int state; // state machine control
int count; // count variable used within a state
double tattack; // attack time
double tdecay; // decay time
int nattack; // one less than total number of attack multipliers
int ndecay; // one less than total number of decay multipliers
double* cattack; // attack curve multipliers
double* cdecay; // decay curve multipliers
double attack_thresh; // attack threshold
double hold_thresh; // hold & decay threshold
double thold; // hold time
int nhold; // hold count
double exp_ratio; // expander ratio (high-gain to low-gain)
double hysteresis_ratio; // ratio hold_thresh/attack_thresh. 0.0 < ratio < 1.0
double low_gain; // gain when gate is closed
double* trigsig; // buffer for trigger signal (signal after side-channel filter)
double* delsig; // buffer for signal delayed to match trigger signal
double peak; // peak signal value to return to console
// side-channel bandpass filter & and buffer for compensating delay
int run_filt; // 1 = side-channel filter and compensating delay are ON, 0 = OFF
int nc; // number of coefficients
int wintype; // window type
double low_cut; // low cutoff frequency
double high_cut; // high cutoff frequency
FIRCORE p; // filter structure
DELRING scdring; // delay ring for side channel
// output audio delay to cover RF_Delay + Xmtr_delay_and_upslew
double* audbuffer; // buffer to serve as input to audring
int run_audelay; // 'run' variable for audio delay ring
double audelay; // audio output delay in seconds
DELRING audring; // audio delay ring
// vox
int run_vox;
void (__stdcall *pushvox)(int channel, int active);
int vox_count;
// update critical section
CRITICAL_SECTION cs_update;
// anti-vox
int antivox_run; // 'run' for anti-vox
int antivox_new; // internal variable indicating new anti-vox data is available
int antivox_size; // size of anti-vox data buffer
double antivox_rate; // sample-rate of anti-vox data
double antivox_tau; // time-constant of anti-vox smoothing
double antivox_gain; // anti-vox gain factor
double antivox_mult; // multiplier for anti-vox smoothing
double antivox_onemmult; // one minus antivox_mult
double antivox_level; // current anti-vox smoothed signal level
double* antivox_data; // buffer to hold new anti-vox data
} dexp, *DEXP;
extern DEXP pdexp[];
__declspec (dllexport) void create_dexp (int id, int run_dexp, int size, double* in, double* out, int rate, double dettau, double tattack, double tdecay,
double thold, double exp_ratio, double hyst_ratio, double attack_thresh, int nc, int wtype, double lowcut, double highcut,
int run_filt, int run_vox, int run_audelay, double audelay, void (__stdcall *pushvox)(int id, int active),
int antivox_run, int antivox_size, int antivox_rate, double antivox_gain, double antivox_tau);
__declspec (dllexport) void destroy_dexp (int id);
__declspec (dllexport) void flush_dexp (int id);
__declspec (dllexport) void xdexp (int id);
__declspec (dllexport) void SetDEXPSize (int id, int size);
__declspec (dllexport) void SetDEXPRate (int id, double rate);
__declspec (dllexport) void SendAntiVOXData (int id, int nsamples, double* data);
#endif
+219
View File
@@ -0,0 +1,219 @@
/* div.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014 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"
#define MAX_NR (8) // maximum number of receivers to mix
MDIV create_div (int run, int nr, int size, double **in, double *out)
{
int i;
MDIV a = (MDIV) malloc0 (sizeof (mdiv));
a->run = run;
a->nr = nr;
a->size = size;
a->out = out;
a->in = (double **) malloc0 ( MAX_NR * sizeof (double *));
if (in != 0)
for (i = 0; i < nr; i++) a->in[i] = in[i];
a->Irotate = (double *) malloc0 (MAX_NR * sizeof (double));
a->Qrotate = (double *) malloc0 (MAX_NR * sizeof (double));
InitializeCriticalSectionAndSpinCount (&a->cs_update, 2500);
for (i = 0; i < 4; i++) ///////////// legacy interface - remove
a->legacy[i] = (double *) malloc0 (2048 * sizeof (complex)); ///////////// legacy interface - remove
return a;
}
void destroy_div (MDIV a)
{
int i; ///////////// legacy interface - remove
DeleteCriticalSection (&a->cs_update);
for (i = 0; i < 4; i++) ///////////// legacy interface - remove
_aligned_free (a->legacy[i]); ///////////// legacy interface - remove
_aligned_free (a->Qrotate);
_aligned_free (a->Irotate);
_aligned_free (a->in);
_aligned_free (a);
}
void flush_div (MDIV a)
{
}
void xdiv (MDIV a)
{
if (a->run)
{
EnterCriticalSection (&a->cs_update);
if (a->output != a->nr)
{
if (a->out != a->in[a->output])
memcpy (a->out, a->in[a->output], a->size * sizeof (complex));
}
else
{
int i, j;
double I, Q;
memset (a->out, 0, a->size * sizeof (complex));
for (i = 0; i < a->nr; i++)
for (j = 0; j < a->size; j++)
{
I = a->in[i][2 * j + 0];
Q = a->in[i][2 * j + 1];
a->out[2 * j + 0] += a->Irotate[i] * I - a->Qrotate[i] * Q;
a->out[2 * j + 1] += a->Irotate[i] * Q + a->Qrotate[i] * I;
}
}
LeaveCriticalSection (&a->cs_update);
}
else
memcpy (a->out, a->in[0], a->size * sizeof (complex));
}
/********************************************************************************************************
* *
* CALLS FOR EXTERNAL USE *
* *
********************************************************************************************************/
#define MAX_EXT_DIVS (2) // maximum number of DIVs called from outside wdsp
__declspec (align (16)) MDIV pdiv[MAX_EXT_DIVS]; // array of pointers for DIVs used EXTERNAL to wdsp
PORT
void create_divEXT (int id, int run, int nr, int size)
{
pdiv[id] = create_div (run, nr, size, 0, 0);
}
PORT
void destroy_divEXT (int id)
{
destroy_div (pdiv[id]);
}
PORT
void flush_divEXT (int id)
{
flush_div (pdiv[id]);
}
PORT
void xdivEXT (int id, int nsamples, double **in, double *out)
{
int i;
MDIV a = pdiv[id];
a->size = nsamples;
a->out = out;
for (i = 0; i < a->nr; i++) a->in[i] = in[i];
xdiv (a);
}
// 0 - does nothing; 1 - operates
PORT
void SetEXTDIVRun (int id, int run)
{
MDIV a = pdiv[id];
EnterCriticalSection (&a->cs_update);
a->run = run;
LeaveCriticalSection (&a->cs_update);
}
// size of data buffer in complex samples
PORT
void SetEXTDIVBuffsize (int id, int size)
{
MDIV a = pdiv[id];
EnterCriticalSection (&a->cs_update);
a->size = size;
LeaveCriticalSection (&a->cs_update);
}
// number of receivers being used for diversity
PORT
void SetEXTDIVNr (int id, int nr)
{
MDIV a = pdiv[id];
EnterCriticalSection (&a->cs_update);
a->nr = nr;
LeaveCriticalSection (&a->cs_update);
}
// number of which receiver to output
// if output==nr, mixing occurs
PORT
void SetEXTDIVOutput (int id, int output)
{
MDIV a = pdiv[id];
EnterCriticalSection (&a->cs_update);
a->output = output;
LeaveCriticalSection (&a->cs_update);
}
// I and Q "rotate" multipliers for each receiver
// can be set to 1.0 and 0.0 for "reference receiver"
PORT
void SetEXTDIVRotate (int id, int nr, double *Irotate, double *Qrotate)
{
MDIV a = pdiv[id];
EnterCriticalSection (&a->cs_update);
memcpy (a->Irotate, Irotate, nr * sizeof (double));
memcpy (a->Qrotate, Qrotate, nr * sizeof (double));
LeaveCriticalSection (&a->cs_update);
}
/********************************************************************************************************
* *
* LEGACY INTERFACE - REMOVE *
* *
********************************************************************************************************/
PORT
void xdivEXTF (int id, int size, float **input, float *Iout, float *Qout)
{
int i, j;
MDIV a = pdiv[id];
if (a->run)
{
a->size = size;
for (i = 0; i < a->nr; i++)
{
for (j = 0; j < a->size; j++)
{
a->legacy[i][2 * j + 0] = (double)input[2 * i + 0][j];
a->legacy[i][2 * j + 1] = (double)input[2 * i + 1][j];
}
a->in[i] = a->legacy[i];
}
a->out = a->legacy[3];
xdiv (a);
for (j = 0; j < a->size; j++)
{
Iout[j] = (float)a->legacy[3][2 * j + 0];
Qout[j] = (float)a->legacy[3][2 * j + 1];
}
}
}
+56
View File
@@ -0,0 +1,56 @@
/* div.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014 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 _div_h
#define _div_h
typedef struct _div
{
int run;
int nr; // number of receivers to mix
int size; // size of input/output buffers
double **in; // input buffers
double *out; // output buffer
int output; // which rcvr to output; ==nr for mix
double *Irotate;
double *Qrotate;
CRITICAL_SECTION cs_update;
double *legacy[4]; ///////////// legacy interface - remove
} mdiv, *MDIV;
extern MDIV create_div (int run, int nr, int size, double **in, double *out);
extern void destroy_div (MDIV pdiv);
extern void xdiv (MDIV pdiv);
extern __declspec(dllexport) void xdivEXT (int id, int nsamples, double **in, double *out);
extern __declspec(dllexport) void create_divEXT (int id, int run, int nr, int size);
extern __declspec(dllexport) void destroy_divEXT (int id);
#endif
+393
View File
@@ -0,0 +1,393 @@
/* eer.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014 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"
PORT
EER create_eer (int run, int size, double* in, double* out, double* outM, int rate, double mgain, double pgain, int rundelays, double mdelay, double pdelay, int amiq)
{
EER a = (EER) malloc0 (sizeof (eer));
a->run = run;
a->size = size;
a->in = in;
a->out = out;
a->outM = outM;
a->rate = rate;
a->mgain = mgain;
a->pgain = pgain;
a->rundelays = rundelays;
a->mdelay = mdelay;
a->pdelay = pdelay;
a->amiq = amiq;
a->mdel = create_delay (
a->rundelays, // run
a->size, // size
a->outM, // input buffer
a->outM, // output buffer
a->rate, // sample rate
20.0e-09, // delta (delay stepsize)
a->mdelay); // delay
a->pdel = create_delay (
a->rundelays, // run
a->size, // size
a->out, // input buffer
a->out, // output buffer
a->rate, // sample rate
20.0e-09, // delta (delay stepsize)
a->pdelay); // delay
InitializeCriticalSectionAndSpinCount(&a->cs_update, 2500);
a->legacy = (double *) malloc0 (2048 * sizeof (complex)); /////////////// legacy interface - remove
a->legacyM = (double *) malloc0 (2048 * sizeof (complex)); /////////////// legacy interface - remove
return a;
}
PORT
void destroy_eer (EER a)
{
DeleteCriticalSection (&a->cs_update);
destroy_delay (a->pdel);
destroy_delay (a->mdel);
_aligned_free (a);
}
PORT
void flush_eer (EER a)
{
flush_delay (a->mdel);
flush_delay (a->pdel);
}
PORT
void xeer (EER a)
{
EnterCriticalSection (&a->cs_update);
if (a->run)
{
int i;
double I, Q, mag;
for (i = 0; i < a->size; i++)
{
I = a->in[2 * i + 0];
Q = a->in[2 * i + 1];
a->outM[2 * i + 0] = I * a->mgain;
a->outM[2 * i + 1] = Q * a->mgain;
switch (a->amiq)
{
case 0: // send phase info only, magnitude is constant
mag = sqrt (I * I + Q * Q);
a->out [2 * i + 0] = a->pgain * I / mag;
a->out [2 * i + 1] = a->pgain * Q / mag;
break;
case 1: // send magnitude and phase information, I and Q
a->out [2 * i + 0] = a->pgain * I;
a->out [2 * i + 1] = a->pgain * Q;
break;
case 2: // send envelope
mag = sqrt (I * I + Q * Q);
a->out [2 * i + 0] = a->out[2 * i + 1] = a->pgain * mag;
break;
}
}
xdelay (a->mdel); // delay for outM
xdelay (a->pdel); // delay for out
}
else if (a->out != a->in)
memcpy (a->out, a->in, a->size * sizeof (complex));
LeaveCriticalSection (&a->cs_update);
}
/********************************************************************************************************
* *
* CALLS FOR EXTERNAL USE *
* *
********************************************************************************************************/
#define MAX_EXT_EERS (2) // maximum number of EERs called from outside wdsp
__declspec (align (16)) EER peer[MAX_EXT_EERS]; // array of pointers for EERs used EXTERNAL to wdsp
PORT
void create_eerEXT (int id, int run, int size, int rate, double mgain, double pgain, int rundelays, double mdelay, double pdelay, int amiq)
{
peer[id] = create_eer (run, size, 0, 0, 0, rate, mgain, pgain, rundelays, mdelay, pdelay, amiq);
}
PORT
void destroy_eerEXT (int id)
{
destroy_eer (peer[id]);
}
PORT
void flush_eerEXT (int id)
{
flush_eer (peer[id]);
}
PORT
void SetEERRun (int id, int run)
{
EER a = peer[id];
EnterCriticalSection (&a->cs_update);
a->run = run;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetEERAMIQ (int id, int amiq)
{
EER a = peer[id];
EnterCriticalSection (&a->cs_update);
a->amiq = amiq;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetEERMgain (int id, double gain)
{
EER a = peer[id];
EnterCriticalSection (&a->cs_update);
a->mgain = gain;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetEERPgain (int id, double gain)
{
EER a = peer[id];
EnterCriticalSection (&a->cs_update);
a->pgain = gain;
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetEERRunDelays (int id, int run)
{
EER a = peer[id];
EnterCriticalSection (&a->cs_update);
a->rundelays = run;
SetDelayRun (a->mdel, a->rundelays);
SetDelayRun (a->pdel, a->rundelays);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetEERMdelay (int id, double delay)
{
EER a = peer[id];
EnterCriticalSection (&a->cs_update);
a->mdelay = delay;
SetDelayValue (a->mdel, a->mdelay);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetEERPdelay (int id, double delay)
{
EER a = peer[id];
EnterCriticalSection (&a->cs_update);
a->pdelay = delay;
SetDelayValue (a->pdel, a->pdelay);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetEERSize (int id, int size)
{
EER a = peer[id];
EnterCriticalSection (&a->cs_update);
a->size = size;
SetDelayBuffs (a->mdel, a->size, a->outM, a->outM);
SetDelayBuffs (a->pdel, a->size, a->out, a->out);
LeaveCriticalSection (&a->cs_update);
}
PORT
void SetEERSamplerate (int id, int rate)
{
EER a = peer[id];
EnterCriticalSection (&a->cs_update);
a->rate = rate;
destroy_delay (a->mdel);
destroy_delay (a->pdel);
a->mdel = create_delay (
a->rundelays, // run
a->size, // size
a->outM, // input buffer
a->outM, // output buffer
a->rate, // sample rate
20.0e-09, // delta (delay stepsize)
a->mdelay); // delay
a->pdel = create_delay (
a->rundelays, // run
a->size, // size
a->out, // input buffer
a->out, // output buffer
a->rate, // sample rate
20.0e-09, // delta (delay stepsize)
a->pdelay); // delay
LeaveCriticalSection (&a->cs_update);
}
/********************************************************************************************************
* *
* POINTER-BASED PROPERTIES *
* *
********************************************************************************************************/
PORT
void pSetEERRun (EER a, int run)
{
EnterCriticalSection (&a->cs_update);
a->run = run;
LeaveCriticalSection (&a->cs_update);
}
PORT
void pSetEERAMIQ (EER a, int amiq)
{
EnterCriticalSection (&a->cs_update);
a->amiq = amiq;
LeaveCriticalSection (&a->cs_update);
}
PORT
void pSetEERMgain (EER a, double gain)
{
EnterCriticalSection (&a->cs_update);
a->mgain = gain;
LeaveCriticalSection (&a->cs_update);
}
PORT
void pSetEERPgain (EER a, double gain)
{
EnterCriticalSection (&a->cs_update);
a->pgain = gain;
LeaveCriticalSection (&a->cs_update);
}
PORT
void pSetEERRunDelays (EER a, int run)
{
EnterCriticalSection (&a->cs_update);
a->rundelays = run;
SetDelayRun (a->mdel, a->rundelays);
SetDelayRun (a->pdel, a->rundelays);
LeaveCriticalSection (&a->cs_update);
}
PORT
void pSetEERMdelay (EER a, double delay)
{
EnterCriticalSection (&a->cs_update);
a->mdelay = delay;
SetDelayValue (a->mdel, a->mdelay);
LeaveCriticalSection (&a->cs_update);
}
PORT
void pSetEERPdelay (EER a, double delay)
{
EnterCriticalSection (&a->cs_update);
a->pdelay = delay;
SetDelayValue (a->pdel, a->pdelay);
LeaveCriticalSection (&a->cs_update);
}
PORT
void pSetEERSize (EER a, int size)
{
EnterCriticalSection (&a->cs_update);
a->size = size;
SetDelayBuffs (a->mdel, a->size, a->outM, a->outM);
SetDelayBuffs (a->pdel, a->size, a->out, a->out);
LeaveCriticalSection (&a->cs_update);
}
PORT
void pSetEERSamplerate (EER a, int rate)
{
EnterCriticalSection (&a->cs_update);
a->rate = rate;
destroy_delay (a->mdel);
destroy_delay (a->pdel);
a->mdel = create_delay (
a->rundelays, // run
a->size, // size
a->outM, // input buffer
a->outM, // output buffer
a->rate, // sample rate
20.0e-09, // delta (delay stepsize)
a->mdelay); // delay
a->pdel = create_delay (
a->rundelays, // run
a->size, // size
a->out, // input buffer
a->out, // output buffer
a->rate, // sample rate
20.0e-09, // delta (delay stepsize)
a->pdelay); // delay
LeaveCriticalSection (&a->cs_update);
}
/********************************************************************************************************
* *
* Legacy Interface *
* *
********************************************************************************************************/
PORT
void xeerEXTF (int id, float* inI, float* inQ, float* outI, float* outQ, float* outMI, float* outMQ, int mox, int size)
{
EER a = peer[id];
if (mox && a->run)
{
int i;
a->in = a->legacy;
a->out = a->legacy;
a->outM = a->legacyM;
a->size = size;
SetDelayBuffs (a->mdel, a->size, a->outM, a->outM);
SetDelayBuffs (a->pdel, a->size, a->out, a->out);
for (i = 0; i < a->size; i++)
{
a->legacy[2 * i + 0] = (double)inI[i];
a->legacy[2 * i + 1] = (double)inQ[i];
}
xeer (a);
for (i = 0; i < a->size; i++)
{
outI[i] = (float)a->legacy [2 * i + 0];
outQ[i] = (float)a->legacy [2 * i + 1];
outMI[i] = (float)a->legacyM[2 * i + 0];
outMQ[i] = (float)a->legacyM[2 * i + 1];
}
}
}
+77
View File
@@ -0,0 +1,77 @@
/* eer.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014 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 _eer_h
#define _eer_h
typedef struct _eer
{
int run;
int amiq;
int size;
double* in;
double* out;
double* outM;
int rate;
double mgain;
double pgain;
int rundelays;
double mdelay;
double pdelay;
DELAY mdel;
DELAY pdel;
CRITICAL_SECTION cs_update;
double *legacy; //////////// legacy interface - remove
double *legacyM; //////////// legacy interface - remove
} eer, *EER;
__declspec (dllexport) EER create_eer (int run, int size, double* in, double* out, double* outM, int rate, double mgain, double pgain, int rundelays, double mdelay, double pdelay, int amiq);
__declspec (dllexport) void destroy_eer (EER a);
__declspec (dllexport) void flush_eer (EER a);
__declspec (dllexport) void xeer (EER a);
__declspec (dllexport) void pSetEERRun (EER a, int run);
__declspec (dllexport) void pSetEERAMIQ (EER a, int amiq);
__declspec (dllexport) void pSetEERMgain (EER a, double gain);
__declspec (dllexport) void pSetEERPgain (EER a, double gain);
__declspec (dllexport) void pSetEERRunDelays (EER a, int run);
__declspec (dllexport) void pSetEERMdelay (EER a, double delay);
__declspec (dllexport) void pSetEERPdelay (EER a, double delay);
__declspec (dllexport) void pSetEERSize (EER a, int size);
__declspec (dllexport) void pSetEERSamplerate (EER a, int rate);
#endif
+1174
View File
File diff suppressed because it is too large Load Diff
+207
View File
@@ -0,0 +1,207 @@
/* emnr.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2015 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 _emnr_h
#define _emnr_h
typedef struct _emnr
{
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* mask;
double* revfftin;
double* revfftout;
double** save;
int oasize;
double* outaccum;
double rate;
int wintype;
double ogain;
double gain;
int nsamps;
int iainidx;
int iaoutidx;
int init_oainidx;
int oainidx;
int oaoutidx;
int saveidx;
fftw_plan Rfor;
fftw_plan Rrev;
struct _g
{
int gain_method;
int npe_method;
int ae_run;
double msize;
double* mask;
double* y;
double* lambda_y;
double* lambda_d;
double* prev_mask;
double* prev_gamma;
double gf1p5;
double alpha;
double eps_floor;
double gamma_max;
double xi_min;
double q;
double gmax;
//
double* GG;
double* GGS;
FILE* fileb;
//
int dim_zeta;
double* zeta_hat;
int* zeta_true;
double z_gamma_min;
double z_gamma_max;
double z_xihat_min;
double z_xihat_max;
double zeta_thresh;
} g;
struct _npest
{
int incr;
double rate;
int msize;
double* lambda_y;
double* lambda_d;
double* p;
double* alphaOptHat;
double alphaC;
double alphaCsmooth;
double alphaCmin;
double* alphaHat;
double alphaMax;
double* sigma2N;
double alphaMin_max_value;
double snrq;
double betamax;
double* pbar;
double* p2bar;
double invQeqMax;
double av;
double* Qeq;
int U;
double Dtime;
int V;
int D;
double MofD;
double MofV;
double* bmin;
double* bmin_sub;
int* k_mod;
double* actmin;
double* actmin_sub;
int subwc;
int* lmin_flag;
double* pmin_u;
double invQbar_points[4];
double nsmax[4];
double** actminbuff;
int amb_idx;
} np;
struct _npests
{
int incr;
double rate;
int msize;
double* lambda_y;
double* lambda_d;
double alpha_pow;
double alpha_Pbar;
double epsH1;
double epsH1r;
double* sigma2N;
double* PH1y;
double* Pbar;
double* EN2y;
} nps;
struct _npestl
{
double rate;
int msize;
int incr;
double* Ysq;
double* P;
double* Pmin;
double* p;
double* D;
double* lambda_d;
double eta;
double gamma;
double beta;
double delta_LF;
double delta_MF;
double delta_0;
double delta_1;
double delta_2;
double alpha_d;
double alpha_p;
} npl;
struct _ae
{
int msize;
double* lambda_y;
double zetaThresh;
double psi;
double* nmask;
double t2;
} ae;
}emnr, *EMNR;
extern EMNR create_emnr (int run, int position, int size, double* in, double* out, int fsize, int ovrlp,
int rate, int wintype, double gain, int gain_method, int npe_method, int ae_run);
extern void destroy_emnr (EMNR a);
extern void flush_emnr (EMNR a);
extern void xemnr (EMNR a, int pos);
extern void setBuffers_emnr (EMNR a, double* in, double* out);
extern void setSamplerate_emnr (EMNR a, int rate);
extern void setSize_emnr (EMNR a, int size);
#endif
+270
View File
@@ -0,0 +1,270 @@
/* emph.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 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
*/
#include "comm.h"
/********************************************************************************************************
* *
* Partitioned Overlap-Save FM Pre-Emphasis *
* *
********************************************************************************************************/
EMPHP create_emphp (int run, int position, int size, int nc, int mp, double* in, double* out, int rate, int ctype, double f_low, double f_high)
{
EMPHP a = (EMPHP) malloc0 (sizeof (emphp));
double* impulse;
a->run = run;
a->position = position;
a->size = size;
a->nc = nc;
a->mp = mp;
a->in = in;
a->out = out;
a->rate = rate;
a->ctype = ctype;
a->f_low = f_low;
a->f_high = f_high;
impulse = fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
a->p = create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
_aligned_free (impulse);
return a;
}
void destroy_emphp (EMPHP a)
{
destroy_fircore (a->p);
_aligned_free (a);
}
void flush_emphp (EMPHP a)
{
flush_fircore (a->p);
}
void xemphp (EMPHP a, int position)
{
if (a->run && a->position == position)
xfircore (a->p);
else if (a->in != a->out)
memcpy (a->out, a->in, a->size * sizeof (complex));
}
void setBuffers_emphp (EMPHP a, double* in, double* out)
{
a->in = in;
a->out = out;
setBuffers_fircore (a->p, a->in, a->out);
}
void setSamplerate_emphp (EMPHP a, int rate)
{
double* impulse;
a->rate = rate;
impulse = fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
void setSize_emphp (EMPHP a, int size)
{
double* impulse;
a->size = size;
setSize_fircore (a->p, a->size);
impulse = fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save FM Pre-Emphasis: TXA Properties *
* *
********************************************************************************************************/
PORT
void SetTXAFMEmphPosition (int channel, int position)
{
EnterCriticalSection (&ch[channel].csDSP);
txa[channel].preemph.p->position = position;
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetTXAFMEmphMP (int channel, int mp)
{
EMPHP a;
a = txa[channel].preemph.p;
if (a->mp != mp)
{
a->mp = mp;
setMp_fircore (a->p, a->mp);
}
}
PORT
void SetTXAFMEmphNC (int channel, int nc)
{
EMPHP a;
double* impulse;
EnterCriticalSection (&ch[channel].csDSP);
a = txa[channel].preemph.p;
if (a->nc != nc)
{
a->nc = nc;
impulse = fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
setNc_fircore (a->p, a->nc, impulse);
_aligned_free (impulse);
}
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetTXAFMPreEmphFreqs (int channel, double low, double high)
{
EMPHP a;
double* impulse;
EnterCriticalSection (&ch[channel].csDSP);
a = txa[channel].preemph.p;
if (a->f_low != low || a->f_high != high)
{
a->f_low = low;
a->f_high = high;
impulse = fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
LeaveCriticalSection (&ch[channel].csDSP);
}
/********************************************************************************************************
* *
* Overlap-Save FM Pre-Emphasis *
* *
********************************************************************************************************/
void calc_emph (EMPH a)
{
a->infilt = (double *)malloc0(2 * a->size * sizeof(complex));
a->product = (double *)malloc0(2 * a->size * sizeof(complex));
a->mults = fc_mults(a->size, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
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);
}
void decalc_emph (EMPH a)
{
fftw_destroy_plan(a->CRev);
fftw_destroy_plan(a->CFor);
_aligned_free(a->mults);
_aligned_free(a->product);
_aligned_free(a->infilt);
}
EMPH create_emph (int run, int position, int size, double* in, double* out, int rate, int ctype, double f_low, double f_high)
{
EMPH a = (EMPH) malloc0 (sizeof (emph));
a->run = run;
a->position = position;
a->size = size;
a->in = in;
a->out = out;
a->rate = (double)rate;
a->ctype = ctype;
a->f_low = f_low;
a->f_high = f_high;
calc_emph (a);
return a;
}
void destroy_emph (EMPH a)
{
decalc_emph (a);
_aligned_free (a);
}
void flush_emph (EMPH a)
{
memset (a->infilt, 0, 2 * a->size * sizeof (complex));
}
void xemph (EMPH a, int position)
{
int i;
double I, Q;
if (a->run && a->position == 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->product[2 * i + 0];
Q = 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_emph (EMPH a, double* in, double* out)
{
decalc_emph (a);
a->in = in;
a->out = out;
calc_emph (a);
}
void setSamplerate_emph (EMPH a, int rate)
{
decalc_emph (a);
a->rate = rate;
calc_emph (a);
}
void setSize_emph (EMPH a, int size)
{
decalc_emph(a);
a->size = size;
calc_emph(a);
}
/********************************************************************************************************
* *
* Overlap-Save FM Pre-Emphasis: TXA Properties *
* *
********************************************************************************************************/
/* // Uncomment when needed
PORT
void SetTXAFMEmphPosition (int channel, int position)
{
EnterCriticalSection (&ch[channel].csDSP);
txa[channel].preemph.p->position = position;
LeaveCriticalSection (&ch[channel].csDSP);
}
*/
+116
View File
@@ -0,0 +1,116 @@
/* emph.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 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
*/
/********************************************************************************************************
* *
* Partitioned Overlap-Save FM Pre-Emphasis *
* *
********************************************************************************************************/
#ifndef _emphp_h
#define _emphp_h
#include "firmin.h"
typedef struct _emphp
{
int run;
int position;
int size;
int nc;
int mp;
double* in;
double* out;
int ctype;
double f_low;
double f_high;
double rate;
FIRCORE p;
} emphp, *EMPHP;
extern EMPHP create_emphp (int run, int position, int size, int nc, int mp,
double* in, double* out, int rate, int ctype, double f_low, double f_high);
extern void destroy_emphp (EMPHP a);
extern void flush_emphp (EMPHP a);
extern void xemphp (EMPHP a, int position);
extern void setBuffers_emphp (EMPHP a, double* in, double* out);
extern void setSamplerate_emphp (EMPHP a, int rate);
extern void setSize_emphp (EMPHP a, int size);
__declspec (dllexport) void SetTXAFMEmphMP (int channel, int mp);
__declspec (dllexport) void SetTXAFMEmphNC (int channel, int nc);
__declspec (dllexport) void SetTXAFMPreEmphFreqs(int channel, double low, double high);
#endif
/********************************************************************************************************
* *
* Overlap-Save FM Pre-Emphasis *
* *
********************************************************************************************************/
#ifndef _emph_h
#define _emph_h
typedef struct _emph
{
int run;
int position;
int size;
double* in;
double* out;
int ctype;
double f_low;
double f_high;
double* infilt;
double* product;
double* mults;
double rate;
fftw_plan CFor;
fftw_plan CRev;
} emph, *EMPH;
extern EMPH create_emph (int run, int position, int size, double* in, double* out, int rate, int ctype, double f_low, double f_high);
extern void destroy_emph (EMPH a);
extern void flush_emph (EMPH a);
extern void xemph (EMPH a, int position);
extern void setBuffers_emph (EMPH a, double* in, double* out);
extern void setSamplerate_emph (EMPH a, int rate);
extern void setSize_emph (EMPH a, int size);
#endif
+853
View File
@@ -0,0 +1,853 @@
/* eq.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"
int fEQcompare (const void * a, const void * b)
{
if (*(double*)a < *(double*)b)
return -1;
else if (*(double*)a == *(double*)b)
return 0;
else
return 1;
}
double* eq_impulse (int N, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype)
{
double* fp = (double *) malloc0 ((nfreqs + 2) * sizeof (double));
double* gp = (double *) malloc0 ((nfreqs + 2) * sizeof (double));
double* A = (double *) malloc0 ((N / 2 + 1) * sizeof (double));
double* sary = (double *) malloc0 (2 * nfreqs * sizeof (double));
double gpreamp, f, frac;
double* impulse;
int i, j, mid;
fp[0] = 0.0;
fp[nfreqs + 1] = 1.0;
gpreamp = G[0];
for (i = 1; i <= nfreqs; i++)
{
fp[i] = 2.0 * F[i] / samplerate;
if (fp[i] < 0.0) fp[i] = 0.0;
if (fp[i] > 1.0) fp[i] = 1.0;
gp[i] = G[i];
}
for (i = 1, j = 0; i <= nfreqs; i++, j+=2)
{
sary[j + 0] = fp[i];
sary[j + 1] = gp[i];
}
qsort (sary, nfreqs, 2 * sizeof (double), fEQcompare);
for (i = 1, j = 0; i <= nfreqs; i++, j+=2)
{
fp[i] = sary[j + 0];
gp[i] = sary[j + 1];
}
gp[0] = gp[1];
gp[nfreqs + 1] = gp[nfreqs];
mid = N / 2;
j = 0;
if (N & 1)
{
for (i = 0; i <= mid; i++)
{
f = (double)i / (double)mid;
while (f > fp[j + 1]) j++;
frac = (f - fp[j]) / (fp[j + 1] - fp[j]);
A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale;
}
}
else
{
for (i = 0; i < mid; i++)
{
f = ((double)i + 0.5) / (double)mid;
while (f > fp[j + 1]) j++;
frac = (f - fp[j]) / (fp[j + 1] - fp[j]);
A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale;
}
}
if (ctfmode == 0)
{
int k, low, high;
double lowmag, highmag, flow4, fhigh4;
if (N & 1)
{
low = (int)(fp[1] * mid);
high = (int)(fp[nfreqs] * mid + 0.5);
lowmag = A[low];
highmag = A[high];
flow4 = pow((double)low / (double)mid, 4.0);
fhigh4 = pow((double)high / (double)mid, 4.0);
k = low;
while (--k >= 0)
{
f = (double)k / (double)mid;
lowmag *= (f * f * f * f) / flow4;
if (lowmag < 1.0e-100) lowmag = 1.0e-100;
A[k] = lowmag;
}
k = high;
while (++k <= mid)
{
f = (double)k / (double)mid;
highmag *= fhigh4 / (f * f * f * f);
if (highmag < 1.0e-100) highmag = 1.0e-100;
A[k] = highmag;
}
}
else
{
low = (int)(fp[1] * mid - 0.5);
high = (int)(fp[nfreqs] * mid - 0.5);
lowmag = A[low];
highmag = A[high];
flow4 = pow((double)low / (double)mid, 4.0);
fhigh4 = pow((double)high / (double)mid, 4.0);
k = low;
while (--k >= 0)
{
f = (double)k / (double)mid;
lowmag *= (f * f * f * f) / flow4;
if (lowmag < 1.0e-100) lowmag = 1.0e-100;
A[k] = lowmag;
}
k = high;
while (++k < mid)
{
f = (double)k / (double)mid;
highmag *= fhigh4 / (f * f * f * f);
if (highmag < 1.0e-100) highmag = 1.0e-100;
A[k] = highmag;
}
}
}
if (N & 1)
impulse = fir_fsamp_odd(N, A, 1, 1.0, wintype);
else
impulse = fir_fsamp(N, A, 1, 1.0, wintype);
// print_impulse("eq.txt", N, impulse, 1, 0);
_aligned_free (sary);
_aligned_free (A);
_aligned_free (gp);
_aligned_free (fp);
return impulse;
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer *
* *
********************************************************************************************************/
EQP create_eqp (int run, int size, int nc, int mp, double *in, double *out, int nfreqs, double* F, double* G, int ctfmode, int wintype, int samplerate)
{
// NOTE: 'nc' must be >= 'size'
EQP a = (EQP) malloc0 (sizeof (eqp));
double* impulse;
a->run = run;
a->size = size;
a->nc = nc;
a->mp = mp;
a->in = in;
a->out = out;
a->nfreqs = nfreqs;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
a->ctfmode = ctfmode;
a->wintype = wintype;
a->samplerate = (double)samplerate;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
a->p = create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
_aligned_free (impulse);
return a;
}
void destroy_eqp (EQP a)
{
destroy_fircore (a->p);
_aligned_free (a);
}
void flush_eqp (EQP a)
{
flush_fircore (a->p);
}
void xeqp (EQP a)
{
if (a->run)
xfircore (a->p);
else
memcpy (a->out, a->in, a->size * sizeof (complex));
}
void setBuffers_eqp (EQP a, double* in, double* out)
{
a->in = in;
a->out = out;
setBuffers_fircore (a->p, a->in, a->out);
}
void setSamplerate_eqp (EQP a, int rate)
{
double* impulse;
a->samplerate = rate;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
void setSize_eqp (EQP a, int size)
{
double* impulse;
a->size = size;
setSize_fircore (a->p, a->size);
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer: RXA Properties *
* *
********************************************************************************************************/
PORT
void SetRXAEQRun (int channel, int run)
{
EnterCriticalSection (&ch[channel].csDSP);
rxa[channel].eqp.p->run = run;
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetRXAEQNC (int channel, int nc)
{
EQP a;
double* impulse;
EnterCriticalSection (&ch[channel].csDSP);
a = rxa[channel].eqp.p;
if (a->nc != nc)
{
a->nc = nc;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setNc_fircore (a->p, a->nc, impulse);
_aligned_free (impulse);
}
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetRXAEQMP (int channel, int mp)
{
EQP a;
a = rxa[channel].eqp.p;
if (a->mp != mp)
{
a->mp = mp;
setMp_fircore (a->p, a->mp);
}
}
PORT
void SetRXAEQProfile (int channel, int nfreqs, double* F, double* G)
{
EQP a;
double* impulse;
a = rxa[channel].eqp.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = nfreqs;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G,
a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
PORT
void SetRXAEQCtfmode (int channel, int mode)
{
EQP a;
double* impulse;
a = rxa[channel].eqp.p;
a->ctfmode = mode;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
PORT
void SetRXAEQWintype (int channel, int wintype)
{
EQP a;
double* impulse;
a = rxa[channel].eqp.p;
a->wintype = wintype;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
PORT
void SetRXAGrphEQ (int channel, int *rxeq)
{ // three band equalizer (legacy compatibility)
EQP a;
double* impulse;
a = rxa[channel].eqp.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = 4;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->F[1] = 150.0;
a->F[2] = 400.0;
a->F[3] = 1500.0;
a->F[4] = 6000.0;
a->G[0] = (double)rxeq[0];
a->G[1] = (double)rxeq[1];
a->G[2] = (double)rxeq[1];
a->G[3] = (double)rxeq[2];
a->G[4] = (double)rxeq[3];
a->ctfmode = 0;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
PORT
void SetRXAGrphEQ10 (int channel, int *rxeq)
{ // ten band equalizer (legacy compatibility)
EQP a;
double* impulse;
int i;
a = rxa[channel].eqp.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = 10;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->F[1] = 32.0;
a->F[2] = 63.0;
a->F[3] = 125.0;
a->F[4] = 250.0;
a->F[5] = 500.0;
a->F[6] = 1000.0;
a->F[7] = 2000.0;
a->F[8] = 4000.0;
a->F[9] = 8000.0;
a->F[10] = 16000.0;
for (i = 0; i <= a->nfreqs; i++)
a->G[i] = (double)rxeq[i];
a->ctfmode = 0;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
// print_impulse ("rxeq.txt", a->nc, impulse, 1, 0);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer: TXA Properties *
* *
********************************************************************************************************/
PORT
void SetTXAEQRun (int channel, int run)
{
EnterCriticalSection (&ch[channel].csDSP);
txa[channel].eqp.p->run = run;
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetTXAEQNC (int channel, int nc)
{
EQP a;
double* impulse;
EnterCriticalSection (&ch[channel].csDSP);
a = txa[channel].eqp.p;
if (a->nc != nc)
{
a->nc = nc;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setNc_fircore (a->p, a->nc, impulse);
_aligned_free (impulse);
}
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetTXAEQMP (int channel, int mp)
{
EQP a;
a = txa[channel].eqp.p;
if (a->mp != mp)
{
a->mp = mp;
setMp_fircore (a->p, a->mp);
}
}
PORT
void SetTXAEQProfile (int channel, int nfreqs, double* F, double* G)
{
EQP a;
double* impulse;
a = txa[channel].eqp.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = nfreqs;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
PORT
void SetTXAEQCtfmode (int channel, int mode)
{
EQP a;
double* impulse;
a = txa[channel].eqp.p;
a->ctfmode = mode;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
PORT
void SetTXAEQWintype (int channel, int wintype)
{
EQP a;
double* impulse;
a = txa[channel].eqp.p;
a->wintype = wintype;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
PORT
void SetTXAGrphEQ (int channel, int *txeq)
{ // three band equalizer (legacy compatibility)
EQP a;
double* impulse;
a = txa[channel].eqp.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = 4;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->F[1] = 150.0;
a->F[2] = 400.0;
a->F[3] = 1500.0;
a->F[4] = 6000.0;
a->G[0] = (double)txeq[0];
a->G[1] = (double)txeq[1];
a->G[2] = (double)txeq[1];
a->G[3] = (double)txeq[2];
a->G[4] = (double)txeq[3];
a->ctfmode = 0;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
PORT
void SetTXAGrphEQ10 (int channel, int *txeq)
{ // ten band equalizer (legacy compatibility)
EQP a;
double* impulse;
int i;
a = txa[channel].eqp.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = 10;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->F[1] = 32.0;
a->F[2] = 63.0;
a->F[3] = 125.0;
a->F[4] = 250.0;
a->F[5] = 500.0;
a->F[6] = 1000.0;
a->F[7] = 2000.0;
a->F[8] = 4000.0;
a->F[9] = 8000.0;
a->F[10] = 16000.0;
for (i = 0; i <= a->nfreqs; i++)
a->G[i] = (double)txeq[i];
a->ctfmode = 0;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
setImpulse_fircore (a->p, impulse, 1);
_aligned_free (impulse);
}
/********************************************************************************************************
* *
* Overlap-Save Equalizer *
* *
********************************************************************************************************/
double* eq_mults (int size, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype)
{
double* impulse = eq_impulse (size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype);
double* mults = fftcv_mults(2 * size, impulse);
_aligned_free (impulse);
return mults;
}
void calc_eq (EQ a)
{
a->scale = 1.0 / (double)(2 * a->size);
a->infilt = (double *)malloc0(2 * a->size * sizeof(complex));
a->product = (double *)malloc0(2 * a->size * sizeof(complex));
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);
a->mults = eq_mults(a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
}
void decalc_eq (EQ a)
{
fftw_destroy_plan(a->CRev);
fftw_destroy_plan(a->CFor);
_aligned_free(a->mults);
_aligned_free(a->product);
_aligned_free(a->infilt);
}
EQ create_eq (int run, int size, double *in, double *out, int nfreqs, double* F, double* G, int ctfmode, int wintype, int samplerate)
{
EQ a = (EQ) malloc0 (sizeof (eq));
a->run = run;
a->size = size;
a->in = in;
a->out = out;
a->nfreqs = nfreqs;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
a->ctfmode = ctfmode;
a->wintype = wintype;
a->samplerate = (double)samplerate;
calc_eq (a);
return a;
}
void destroy_eq (EQ a)
{
decalc_eq (a);
_aligned_free (a->G);
_aligned_free (a->F);
_aligned_free (a);
}
void flush_eq (EQ a)
{
memset (a->infilt, 0, 2 * a->size * sizeof (complex));
}
void xeq (EQ a)
{
int i;
double I, Q;
if (a->run)
{
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->product[2 * i + 0];
Q = 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_eq (EQ a, double* in, double* out)
{
decalc_eq (a);
a->in = in;
a->out = out;
calc_eq (a);
}
void setSamplerate_eq (EQ a, int rate)
{
decalc_eq (a);
a->samplerate = rate;
calc_eq (a);
}
void setSize_eq (EQ a, int size)
{
decalc_eq (a);
a->size = size;
calc_eq (a);
}
/********************************************************************************************************
* *
* Overlap-Save Equalizer: RXA Properties *
* *
********************************************************************************************************/
/* // UNCOMMENT properties when a pointer is in place in rxa[channel]
PORT
void SetRXAEQRun (int channel, int run)
{
EnterCriticalSection (&ch[channel].csDSP);
rxa[channel].eq.p->run = run;
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetRXAEQProfile (int channel, int nfreqs, double* F, double* G)
{
EQ a;
EnterCriticalSection (&ch[channel].csDSP);
a = rxa[channel].eq.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = nfreqs;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetRXAEQCtfmode (int channel, int mode)
{
EQ a;
EnterCriticalSection (&ch[channel].csDSP);
a = rxa[channel].eq.p;
a->ctfmode = mode;
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetRXAEQWintype (int channel, int wintype)
{
EQ a;
EnterCriticalSection (&ch[channel].csDSP);
a = rxa[channel].eq.p;
a->wintype = wintype;
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetRXAGrphEQ (int channel, int *rxeq)
{ // three band equalizer (legacy compatibility)
EQ a;
EnterCriticalSection (&ch[channel].csDSP);
a = rxa[channel].eq.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = 4;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->F[1] = 150.0;
a->F[2] = 400.0;
a->F[3] = 1500.0;
a->F[4] = 6000.0;
a->G[0] = (double)rxeq[0];
a->G[1] = (double)rxeq[1];
a->G[2] = (double)rxeq[1];
a->G[3] = (double)rxeq[2];
a->G[4] = (double)rxeq[3];
a->ctfmode = 0;
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetRXAGrphEQ10 (int channel, int *rxeq)
{ // ten band equalizer (legacy compatibility)
EQ a;
int i;
EnterCriticalSection (&ch[channel].csDSP);
a = rxa[channel].eq.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = 10;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->F[1] = 32.0;
a->F[2] = 63.0;
a->F[3] = 125.0;
a->F[4] = 250.0;
a->F[5] = 500.0;
a->F[6] = 1000.0;
a->F[7] = 2000.0;
a->F[8] = 4000.0;
a->F[9] = 8000.0;
a->F[10] = 16000.0;
for (i = 0; i <= a->nfreqs; i++)
a->G[i] = (double)rxeq[i];
a->ctfmode = 0;
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
*/
/********************************************************************************************************
* *
* Overlap-Save Equalizer: TXA Properties *
* *
********************************************************************************************************/
/* // UNCOMMENT properties when a pointer is in place in rxa[channel]
PORT
void SetTXAEQRun (int channel, int run)
{
EnterCriticalSection (&ch[channel].csDSP);
txa[channel].eq.p->run = run;
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetTXAEQProfile (int channel, int nfreqs, double* F, double* G)
{
EQ a;
EnterCriticalSection (&ch[channel].csDSP);
a = txa[channel].eq.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = nfreqs;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetTXAEQCtfmode (int channel, int mode)
{
EQ a;
EnterCriticalSection (&ch[channel].csDSP);
a = txa[channel].eq.p;
a->ctfmode = mode;
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetTXAEQMethod (int channel, int wintype)
{
EQ a;
EnterCriticalSection (&ch[channel].csDSP);
a = txa[channel].eq.p;
a->wintype = wintype;
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetTXAGrphEQ (int channel, int *txeq)
{ // three band equalizer (legacy compatibility)
EQ a;
EnterCriticalSection (&ch[channel].csDSP);
a = txa[channel].eq.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = 4;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->F[1] = 150.0;
a->F[2] = 400.0;
a->F[3] = 1500.0;
a->F[4] = 6000.0;
a->G[0] = (double)txeq[0];
a->G[1] = (double)txeq[1];
a->G[2] = (double)txeq[1];
a->G[3] = (double)txeq[2];
a->G[4] = (double)txeq[3];
a->ctfmode = 0;
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
PORT
void SetTXAGrphEQ10 (int channel, int *txeq)
{ // ten band equalizer (legacy compatibility)
EQ a;
int i;
EnterCriticalSection (&ch[channel].csDSP);
a = txa[channel].eq.p;
_aligned_free (a->G);
_aligned_free (a->F);
a->nfreqs = 10;
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
a->F[1] = 32.0;
a->F[2] = 63.0;
a->F[3] = 125.0;
a->F[4] = 250.0;
a->F[5] = 500.0;
a->F[6] = 1000.0;
a->F[7] = 2000.0;
a->F[8] = 4000.0;
a->F[9] = 8000.0;
a->F[10] = 16000.0;
for (i = 0; i <= a->nfreqs; i++)
a->G[i] = (double)txeq[i];
a->ctfmode = 0;
_aligned_free (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
LeaveCriticalSection (&ch[channel].csDSP);
}
*/
+127
View File
@@ -0,0 +1,127 @@
/* eq.h
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
*/
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer *
* *
********************************************************************************************************/
#ifndef _eqp_h
#define _eqp_h
#include "firmin.h"
typedef struct _eqp
{
int run;
int size;
int nc;
int mp;
double* in;
double* out;
int nfreqs;
double* F;
double* G;
int ctfmode;
int wintype;
double samplerate;
FIRCORE p;
} eqp, *EQP;
extern double* eq_impulse (int N, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype);
extern EQP create_eqp (int run, int size, int nc, int mp, double *in, double *out,
int nfreqs, double* F, double* G, int ctfmode, int wintype, int samplerate);
extern void destroy_eqp (EQP a);
extern void flush_eqp (EQP a);
extern void xeqp (EQP a);
extern void setBuffers_eqp (EQP a, double* in, double* out);
extern void setSamplerate_eqp (EQP a, int rate);
extern void setSize_eqp (EQP a, int size);
__declspec (dllexport) void SetRXAEQNC (int channel, int nc);
__declspec (dllexport) void SetRXAEQMP (int channel, int mp);
__declspec (dllexport) void SetTXAEQNC (int channel, int nc);
__declspec (dllexport) void SetTXAEQMP (int channel, int mp);
#endif
/********************************************************************************************************
* *
* Overlap-Save Equalizer *
* *
********************************************************************************************************/
#ifndef _eq_h
#define _eq_h
typedef struct _eq
{
int run;
int size;
double* in;
double* out;
int nfreqs;
double* F;
double* G;
double* infilt;
double* product;
double* mults;
double scale;
int ctfmode;
int wintype;
double samplerate;
fftw_plan CFor;
fftw_plan CRev;
}eq, *EQ;
extern double* eq_mults (int size, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype);
extern EQ create_eq (int run, int size, double *in, double *out, int nfreqs, double* F, double* G, int ctfmode, int wintype, int samplerate);
extern void destroy_eq (EQ a);
extern void flush_eq (EQ a);
extern void xeq (EQ a);
extern void setBuffers_eq (EQ a, double* in, double* out);
extern void setSamplerate_eq (EQ a, int rate);
extern void setSize_eq (EQ a, int size);
#endif
View File

Some files were not shown because too many files have changed in this diff Show More