Quisk version 4.2.29
This commit is contained in:
@@ -1,3 +1,12 @@
|
|||||||
|
Quisk Version 4.2.29 January 2024
|
||||||
|
==================================
|
||||||
|
This version updates the FreeDV library files and program logic. I added a "Monitor"
|
||||||
|
item to the FreeDV menu. It routes the sound to the speakers instead of to the demodulator.
|
||||||
|
Turn on both the monitor and FDX to listen to your transmitted FreeDV signals. Please test.
|
||||||
|
|
||||||
|
Modes 700x and 1600 are finished. The other modes will be added shortly. If you use any of them
|
||||||
|
post here and I will move them to the top of the list.
|
||||||
|
|
||||||
Quisk Version 4.2.28 December 2023
|
Quisk Version 4.2.28 December 2023
|
||||||
===================================
|
===================================
|
||||||
This version includes a patch from Jon, AB8WU, to build Quisk on FreeBSD. Thanks Jon!
|
This version includes a patch from Jon, AB8WU, to build Quisk on FreeBSD. Thanks Jon!
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ button on the lower right.
|
|||||||
<h2>News</h2>
|
<h2>News</h2>
|
||||||
<p>
|
<p>
|
||||||
The newest versions of Linux and Raspberry Pi OS do not allow installing Quisk in the system Python package directory.
|
The newest versions of Linux and Raspberry Pi OS do not allow installing Quisk in the system Python package directory.
|
||||||
I am working on a solution for this.
|
Use the Linux Source Installation below instead.
|
||||||
</p>
|
</p>
|
||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
@@ -189,23 +189,22 @@ Many others contributed to Quisk, and are mentioned in comments in the source co
|
|||||||
<h2 id="Installation">Installation</h2>
|
<h2 id="Installation">Installation</h2>
|
||||||
<p>
|
<p>
|
||||||
Quisk is free open source software written in Python and C. It is hosted on the PyPi repository
|
Quisk is free open source software written in Python and C. It is hosted on the PyPi repository
|
||||||
<a href="https://pypi.org">https://pypi.org</a>.
|
<a href="https://pypi.org">https://pypi.org</a> and it is available on github.
|
||||||
It can be installed using the standard Python setup tools. See specific directions below.
|
|
||||||
</p>
|
</p>
|
||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
If you have Python 3 installed on your computer, just continue to use that version.
|
If you have Python 3 installed on your computer, just continue to use that version.
|
||||||
<b>Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11.</b>
|
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11.</b>
|
||||||
If you want to install a more recent version, uninstall the old version first.
|
If you want to install a more recent version of Python, uninstall the old version first.
|
||||||
Running multiple versions of Python is possible but can be confusing and is not necessary.
|
Running multiple versions of Python is possible but can be confusing and is not necessary.
|
||||||
A Quisk installation is needed for each version of Python you have.
|
A Quisk installation is needed for each version of Python you have.
|
||||||
</p>
|
</p>
|
||||||
<br>
|
<br>
|
||||||
<h3 id="g0.2.2">Windows Initial Installation</h3>
|
<h3 id="g0.2.2">Windows Initial Installation</h3>
|
||||||
<p>
|
<p>
|
||||||
Windows does not include Python, so you must first install the most recent version of 64-bit Python 3.
|
Windows does not include Python, so you must first install a recent version of 64-bit Python 3.
|
||||||
If you already have 64-bit Python 3 installed, just keep using it.
|
If you already have 64-bit Python 3 installed, just keep using it.
|
||||||
<b>Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11.</b>
|
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11.</b>
|
||||||
There may be newer versions of Python, but until support is added, use the versions listed above.
|
There may be newer versions of Python, but until support is added, use the versions listed above.
|
||||||
Python is available from <a href="http://www.python.org/">http://www.python.org</a>.
|
Python is available from <a href="http://www.python.org/">http://www.python.org</a>.
|
||||||
There is an option to <b>Add Python to PATH</b>. You MUST select
|
There is an option to <b>Add Python to PATH</b>. You MUST select
|
||||||
@@ -280,186 +279,111 @@ python -m pip uninstall quisk
|
|||||||
</p>
|
</p>
|
||||||
<h3 id="g0.2.1">Linux Initial Installation</h3>
|
<h3 id="g0.2.1">Linux Initial Installation</h3>
|
||||||
<p>
|
<p>
|
||||||
<br>
|
|
||||||
The libsoundio-dev module is NO LONGER required!
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Linux runs on a wide variety of computers with different processors and versions of Linux.
|
Linux runs on a wide variety of computers with different processors and versions of Linux.
|
||||||
Therefore Quisk is compiled for each one. To do this, Python and a number of other packages are required.
|
Therefore Quisk is compiled for each one. To do this, Python and a number of other packages are required.
|
||||||
Most likely both Python2 and Python3 are already installed on your computer. Either the 32-bit or 64-bit
|
Most likely both Python2 and Python3 are already installed on your computer.
|
||||||
version will work. On my Ubuntu machine, "python2" starts Python2 and "python3" starts Python3.
|
On my Ubuntu machine, "python2" starts Python2 and "python3" starts Python3.
|
||||||
Just "python" starts Python2, but this will change as Python2 is phased out.
|
Just "python" starts Python2, but this will change as Python2 is phased out.
|
||||||
Python2 is obsolete, so you should only install Quisk to Python3, and use "python3" to start it.
|
<b>Python2 is obsolete</b>, so you should only install Quisk to Python3, and use "python3" to start it.
|
||||||
Install these packages. Some of these can be installed from Python using "pip",
|
Install these packages by using your package manager, not Python pip.
|
||||||
but it is better to use your package manager because pip and the package manager do not coordinate.
|
|
||||||
If newer versions of these packages become available, Linux will notify you.
|
If newer versions of these packages become available, Linux will notify you.
|
||||||
Use the most recent version of python3-wxgtk available.
|
Use the most recent version of python3-wxgtk available for your Python version.
|
||||||
<br><br>
|
<br><br>
|
||||||
</p>
|
</p>
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Package Description</th>
|
|
||||||
<th>Python3 Package</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Discrete fourier transform</td>
|
|
||||||
<td>sudo apt-get install libfftw3-dev</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Alsa sound library</td>
|
|
||||||
<td>sudo apt-get install libasound2-dev</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Portaudio sound library</td>
|
|
||||||
<td>sudo apt-get install portaudio19-dev</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Pulseaudio sound library</td>
|
|
||||||
<td>sudo apt-get install libpulse-dev</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Python development</td>
|
|
||||||
<td>sudo apt-get install python3-dev</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Python library development</td>
|
|
||||||
<td>sudo apt-get install libpython3-dev</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>wxPython GUI library</td>
|
|
||||||
<td>sudo apt-get install python3-wxgtk4.0</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Python USB</td>
|
|
||||||
<td>sudo apt-get install python3-usb</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Python serial port</td>
|
|
||||||
<td>sudo apt-get install python3-serial</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Python installation tools</td>
|
|
||||||
<td>sudo apt-get install python3-setuptools</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Pip package installer</td>
|
|
||||||
<td>sudo apt-get install python3-pip</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<p>
|
<p>
|
||||||
<br><br>
|
These are part of your operating system. You only install them once unless it changes.
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<pre>
|
||||||
|
sudo apt-get install libfftw3-dev
|
||||||
|
sudo apt-get install libasound2-dev
|
||||||
|
sudo apt-get install portaudio19-dev
|
||||||
|
sudo apt-get install libpulse-dev
|
||||||
|
sudo apt-get install python3-dev
|
||||||
|
sudo apt-get install libpython3-dev
|
||||||
|
</pre>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
These are part of Python. You only install them once unless the Python started by "python3" changes.
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<pre>
|
||||||
|
sudo apt-get install python3-wxgtk4.0
|
||||||
|
sudo apt-get install python3-usb
|
||||||
|
sudo apt-get install python3-serial
|
||||||
|
sudo apt-get install python3-setuptools
|
||||||
|
</pre>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
If you want to use the SoapySDR module make sure that SoapySDR is installed from its Pothosware site before you build Quisk.
|
If you want to use the SoapySDR module make sure that SoapySDR is installed from its Pothosware site before you build Quisk.
|
||||||
Otherwise Quisk will not have the files it needs. You can install SoapySDR later, but then you
|
Otherwise Quisk will not have the files it needs. You can install SoapySDR later, but then you
|
||||||
will need to reinstall Quisk. This is also true of any other external packages that have their own C source.
|
will need to run "make" again. This is also true of any other external packages that have their own C source.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
</p>
|
</p>
|
||||||
<h3>Linux Pip Installation</h3>
|
<h3>Linux Pip Installation</h3>
|
||||||
|
<br>
|
||||||
<p>
|
<p>
|
||||||
You can use the Python program "pip" to install Quisk into the Python package directory. This is the easiest
|
<b> Installation of Quisk using Python tools like pip is deprecated</b>.
|
||||||
method, but it will not install the source files. If you want the source files see below.
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
sudo -H python3 -m pip install --upgrade quisk
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
This will install the Python files and all other files except for the C source. This is the easiest method
|
|
||||||
if you do not want to work in C.
|
|
||||||
To run Quisk, use this command from a terminal:
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
python3 -m quisk
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
You can create a panel launcher on your desktop with the command to run Quisk.
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
If the installation of Quisk fails, you can force re-installation:
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<br>
|
||||||
sudo -H python3 -m pip uninstall quisk
|
|
||||||
sudo -H python3 -m pip install --no-cache-dir --force-reinstall quisk
|
|
||||||
</pre>
|
|
||||||
<p>
|
<p>
|
||||||
|
Installation to the system Python is no longer allowed, and you may get the message "error: externally-managed-environment".
|
||||||
|
If you used pip to install Quisk you can continue to upgrade with "sudo python3 -m pip install --upgrade quisk"
|
||||||
|
until you get this error message. But it is best to uninstall the pip Quisk. If you installed with pipx, uninstall that too.
|
||||||
|
</p>
|
||||||
<br>
|
<br>
|
||||||
You can also install an older version of Quisk. You may need to do that if the most recent version fails for some reason. Use:
|
<pre>
|
||||||
|
sudo python3 -m pip uninstall quisk
|
||||||
|
pipx uninstall quisk
|
||||||
|
</pre>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
Then use the Linux Source Installation.
|
||||||
|
</p>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
sudo -H python3 -m pip install quisk==4.1.51
|
<h3 id="LSI">Linux Source Installation</h3>
|
||||||
|
<p>
|
||||||
|
On Linux, Quisk is installed from github, and it is contained in a "quisk" directory. First you need to decide where to put the
|
||||||
|
quisk directory. Anywhere will do, but it is best to use a directory where you have write permission so
|
||||||
|
you don't need to use "sudo". The examples below assume you used your home directory indicated by "~". Just replace "~"
|
||||||
|
by your desired location.
|
||||||
|
</p>
|
||||||
<br>
|
<br>
|
||||||
|
<p>
|
||||||
|
To install a fresh copy of Quisk, change to your desired directory, clone the github site, change to the quisk directory and run "make".
|
||||||
|
Then run the python program quisk.py.
|
||||||
|
</p>
|
||||||
<br>
|
<br>
|
||||||
|
<pre>
|
||||||
|
cd ~
|
||||||
|
git clone https://github.com/jimahlstrom/quisk
|
||||||
|
cd quisk
|
||||||
|
make
|
||||||
|
python3 quisk.py
|
||||||
|
</pre>
|
||||||
|
<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:
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<pre>
|
||||||
|
cd ~/quisk
|
||||||
|
git pull
|
||||||
|
</pre>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
You run Quisk by running the file quisk.py with python3. For example, "python3 /home/jim/quisk/quisk.py".
|
||||||
|
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.
|
To get started you must tell Quisk what kind of radio hardware you have. Press the Config button and select Radios.
|
||||||
Then set your sound devices for that radio; the device for the radio speakers, the microphone and so forth. All configuration
|
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.
|
is (mostly) from the Config button. Ignore old directions and don't bother with a config file.
|
||||||
<br>
|
To uninstall Quisk, just delete the quisk directory.
|
||||||
<br>
|
|
||||||
To edit the Quisk Python files you need to know where they are. To find out, import quisk and print it:
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
</p>
|
|
||||||
<pre>
|
|
||||||
jim@IntelNUC:~$ python3
|
|
||||||
Python 3.6.9 (default, Oct 8 2020, 12:12:24)
|
|
||||||
[GCC 8.4.0] on linux
|
|
||||||
Type "help", "copyright", "credits" or "license" for more information.
|
|
||||||
--- import quisk
|
|
||||||
--- quisk
|
|
||||||
module 'quisk' from '/usr/local/lib/python3.6/dist-packages/quisk/__init__.py'
|
|
||||||
---
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
<br>
|
|
||||||
<h3>Linux Pip Upgrade</h3>
|
|
||||||
<p>
|
|
||||||
To upgrade to a newer version of Quisk, use pip:
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
sudo python3 -m pip install --upgrade quisk
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
</p>
|
|
||||||
<h3>Linux Uninstall Quisk</h3>
|
|
||||||
<p>
|
|
||||||
sudo python3 -m pip uninstall quisk
|
|
||||||
</p>
|
</p>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
|
||||||
<h3>Linux Source Installation</h3>
|
|
||||||
<p>
|
|
||||||
The previous method using pip is simple, but will not install the C source files. If you want those proceed as follows.
|
|
||||||
Go to <a href="https://pypi.org/project/quisk/">https://pypi.org/project/quisk</a> and under "Navigation"
|
|
||||||
select "Download files". Download the quisk-x-x-xx.tar.gz file. This is the source file. The ".whl" files are for Windows.
|
|
||||||
Change directory to where the file was downloaded, probably "Downloads", and uncompress and untar it.
|
|
||||||
<br><br>
|
|
||||||
cd ~/Downloads
|
|
||||||
<br>
|
|
||||||
tar zxf quisk-x.x.xx.tar.gz
|
|
||||||
<br><br>
|
|
||||||
Change directories to the Quisk directory:
|
|
||||||
<br><br>
|
|
||||||
cd quisk-x.x.xx
|
|
||||||
<br><br>
|
|
||||||
Now compile Quisk with the "make" command:
|
|
||||||
<br><br>
|
|
||||||
make quisk3
|
|
||||||
<br><br>
|
|
||||||
If "make" fails, you probably have missing packages or missing "-dev" packages. Try to figure out what is missing from the error messages.
|
|
||||||
Now you can run quisk with the command:
|
|
||||||
<br><br>
|
|
||||||
python3 quisk.py
|
|
||||||
<br><br>
|
|
||||||
At this point you have a choice of where to install Quisk. You could just rename the quisk-x.x.xx directory
|
|
||||||
to a more convenient name, like "quisk" in your home directory, and run Quisk from there.
|
|
||||||
This is convenient if you want to
|
|
||||||
alter the Quisk code. Or you could install Quisk into the Python system.
|
|
||||||
<br><br>
|
|
||||||
python2 setup.py build
|
|
||||||
<br>
|
|
||||||
sudo python2 setup.py install
|
|
||||||
<br><br>
|
|
||||||
</p>
|
|
||||||
<h3 id="g0.2.4">Quisk Files</h3>
|
<h3 id="g0.2.4">Quisk Files</h3>
|
||||||
<p>
|
<p>
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
@@ -586,7 +586,7 @@ double quiskAudio24p6Coefs[36] = { 0.001199140008010727, 0.005953815908571521, 0
|
|||||||
-0.008973143363910490, 0.011839435384729619, 0.003122601697996235, -0.008665733839154772, -0.000319602525571569, 0.008621055763448699,
|
-0.008973143363910490, 0.011839435384729619, 0.003122601697996235, -0.008665733839154772, -0.000319602525571569, 0.008621055763448699,
|
||||||
0.005953815908571521, 0.001199140008010727} ;
|
0.005953815908571521, 0.001199140008010727} ;
|
||||||
|
|
||||||
// Sample 48 kHz, pass 6, stop 8, ripple 0.5 dB, atten 80 dB. Stop 0.16667.
|
// Sample 48 kHz, pass 6, stop 8, ripple 0.5 dB, atten 80 dB. Pass 0.125. Stop 0.16667.
|
||||||
double quiskAudio48p6Coefs[71] = { 0.000324677779792651, 0.001038192405843851, 0.002082337464742640, 0.002938055116822665,
|
double quiskAudio48p6Coefs[71] = { 0.000324677779792651, 0.001038192405843851, 0.002082337464742640, 0.002938055116822665,
|
||||||
0.002811674671240099, 0.001132278163758895, -0.001835394694865431, -0.004766409947196539, -0.005833007003128954, -0.003914015660218843,
|
0.002811674671240099, 0.001132278163758895, -0.001835394694865431, -0.004766409947196539, -0.005833007003128954, -0.003914015660218843,
|
||||||
0.000296099005521212, 0.004239593489554206, 0.004918390640978499, 0.001100872619673516, -0.005241634309386784,
|
0.000296099005521212, 0.004239593489554206, 0.004918390640978499, 0.001100872619673516, -0.005241634309386784,
|
||||||
|
|||||||
@@ -32,8 +32,17 @@ typedef void (*freedv_callback_datarx)(void *, unsigned char *packet, size_t siz
|
|||||||
typedef void (*freedv_callback_datatx)(void *, unsigned char *packet, size_t *size);
|
typedef void (*freedv_callback_datatx)(void *, unsigned char *packet, size_t *size);
|
||||||
|
|
||||||
/* advanced freedv open options required by some modes */
|
/* advanced freedv open options required by some modes */
|
||||||
struct freedv_advanced { // from freedv_api.h
|
struct freedv_advanced {
|
||||||
int interleave_frames;
|
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
|
#ifdef MS_WINDOWS
|
||||||
@@ -66,11 +75,14 @@ static int handle_index = -1;
|
|||||||
// need access to these here and in quisk.c
|
// need access to these here and in quisk.c
|
||||||
int n_speech_sample_rate = 8000;
|
int n_speech_sample_rate = 8000;
|
||||||
int n_modem_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 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{
|
static struct _rx_channel{
|
||||||
struct freedv * hFreedv;
|
struct freedv * hFreedv;
|
||||||
COMP * demod_in;
|
short * demod_in;
|
||||||
int rxdata_index;
|
int rxdata_index;
|
||||||
short speech_out[SPEECH_BUF_SIZE]; // output buffer
|
short speech_out[SPEECH_BUF_SIZE]; // output buffer
|
||||||
int speech_available; // number of samples in output buffer
|
int speech_available; // number of samples in output buffer
|
||||||
@@ -163,15 +175,11 @@ static void GetAddrs(void)
|
|||||||
if ( ! hLib) {
|
if ( ! hLib) {
|
||||||
handle_index = 2;
|
handle_index = 2;
|
||||||
GET_HANDLE2;
|
GET_HANDLE2;
|
||||||
if ( ! hLib) {
|
|
||||||
handle_index = 3;
|
|
||||||
GET_HANDLE3;
|
|
||||||
if ( ! hLib) {
|
if ( ! hLib) {
|
||||||
handle_index = 4;
|
handle_index = 4;
|
||||||
GET_HANDLE4;
|
GET_HANDLE4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (hLib) {
|
if (hLib) {
|
||||||
freedv_get_version = GET_ADDR("freedv_get_version");
|
freedv_get_version = GET_ADDR("freedv_get_version");
|
||||||
if (freedv_get_version != NULL) {
|
if (freedv_get_version != NULL) {
|
||||||
@@ -231,19 +239,17 @@ static void GetAddrs(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int quisk_freedv_rx(complex double * cSamples, double * dsamples, int count, int bank) // Called from the sound thread.
|
static int quisk_freedv_rx(short * sSamples, double * dsamples, int count, int bank) // Called from the sound thread.
|
||||||
{ // Input digital modulation is cSamples; decoded voice is dsamples. Each "bank" is a stream of audio.
|
{ // 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
|
// 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
|
// output data will be returned at n_speech_sample_rate with corresponding block sizes
|
||||||
int i, nout, need, have, sync;
|
int i, nout, need, have, sync;
|
||||||
int n_speech_samples;
|
int n_speech_samples;
|
||||||
complex double cx;
|
|
||||||
double scale = (double)CLIP32 / CLIP16; // convert 32 bits to 16 bits
|
|
||||||
struct freedv * hF;
|
struct freedv * hF;
|
||||||
struct _rx_channel * pCh;
|
struct _rx_channel * pCh;
|
||||||
int nCountOut = 1; // In case we have a higher (or lower) value for o/p count
|
int nCountOut = 1; // In case we have a higher (or lower) value for o/p count
|
||||||
|
|
||||||
if (cSamples == NULL) { // shutdown
|
if (sSamples == NULL) { // shutdown
|
||||||
for (i = 0; i < MAX_RECEIVERS; i++) {
|
for (i = 0; i < MAX_RECEIVERS; i++) {
|
||||||
if (rx_channel[i].demod_in) {
|
if (rx_channel[i].demod_in) {
|
||||||
free(rx_channel[i].demod_in);
|
free(rx_channel[i].demod_in);
|
||||||
@@ -277,20 +283,11 @@ static int quisk_freedv_rx(complex double * cSamples, double * dsamples, int cou
|
|||||||
nout = 0;
|
nout = 0;
|
||||||
need = freedv_nin(hF);
|
need = freedv_nin(hF);
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
cx = cRxFilterOut(cSamples[i], bank, 0);
|
pCh->demod_in[pCh->rxdata_index] = sSamples[i];
|
||||||
if (rxMode == FDV_L) // lower sideband
|
|
||||||
cx = conj(cx);
|
|
||||||
#if 1 // Try it the other way and remove scaling factor
|
|
||||||
pCh->demod_in[pCh->rxdata_index].real = creal(cx);
|
|
||||||
pCh->demod_in[pCh->rxdata_index].imag = cimag(cx);
|
|
||||||
#else
|
|
||||||
pCh->demod_in[pCh->rxdata_index].real = (creal(cx) - cimag(cx));
|
|
||||||
pCh->demod_in[pCh->rxdata_index].imag = 0;
|
|
||||||
#endif
|
|
||||||
pCh->rxdata_index++;
|
pCh->rxdata_index++;
|
||||||
if (pCh->rxdata_index >= need) {
|
if (pCh->rxdata_index >= need) {
|
||||||
if (pCh->speech_available + n_speech_samples < SPEECH_BUF_SIZE) { // check for buffer space
|
if (pCh->speech_available + n_speech_samples < SPEECH_BUF_SIZE) { // check for buffer space
|
||||||
have = freedv_comprx(hF, pCh->speech_out + pCh->speech_available, pCh->demod_in);
|
have = freedv_rx(hF, pCh->speech_out + pCh->speech_available, pCh->demod_in);
|
||||||
if (freedv_version > 10)
|
if (freedv_version > 10)
|
||||||
sync = freedv_get_sync(hF);
|
sync = freedv_get_sync(hF);
|
||||||
else
|
else
|
||||||
@@ -325,7 +322,7 @@ static int quisk_freedv_rx(complex double * cSamples, double * dsamples, int cou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (nout = 0; nout < pCh->speech_available && nout < nCountOut; nout++)
|
for (nout = 0; nout < pCh->speech_available && nout < nCountOut; nout++)
|
||||||
dsamples[nout] = pCh->speech_out[nout] * scale * 0.7;
|
dsamples[nout] = pCh->speech_out[nout] * ((double)CLIP32 / CLIP16 * 0.99);
|
||||||
if (nout) {
|
if (nout) {
|
||||||
pCh->speech_available -= nout;
|
pCh->speech_available -= nout;
|
||||||
memmove(pCh->speech_out, pCh->speech_out + nout, (pCh->speech_available) * sizeof(short));
|
memmove(pCh->speech_out, pCh->speech_out + nout, (pCh->speech_available) * sizeof(short));
|
||||||
@@ -339,7 +336,7 @@ static int quisk_freedv_rx(complex double * cSamples, double * dsamples, int cou
|
|||||||
return nout;
|
return nout;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int quisk_freedv_tx(complex double * cSamples, double * dsamples, int count) // Called from the sound thread.
|
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.
|
{ // Input voice samples are dsamples; output digital modulation is cSamples.
|
||||||
int i, nout;
|
int i, nout;
|
||||||
int n_speech_samples;
|
int n_speech_samples;
|
||||||
@@ -351,16 +348,7 @@ static int quisk_freedv_tx(complex double * cSamples, double * dsamples, int cou
|
|||||||
static short *real_mod_out = NULL;
|
static short *real_mod_out = NULL;
|
||||||
|
|
||||||
if (dsamples == NULL) { // shutdown
|
if (dsamples == NULL) { // shutdown
|
||||||
if (mod_out)
|
return 0; // nothing to do
|
||||||
free(mod_out);
|
|
||||||
mod_out = NULL;
|
|
||||||
if (real_mod_out)
|
|
||||||
free(real_mod_out);
|
|
||||||
real_mod_out = NULL;
|
|
||||||
if (speech_in)
|
|
||||||
free(speech_in);
|
|
||||||
speech_in = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
if ( ! rx_channel[0].hFreedv)
|
if ( ! rx_channel[0].hFreedv)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -371,13 +359,13 @@ static int quisk_freedv_tx(complex double * cSamples, double * dsamples, int cou
|
|||||||
if( nRatio < 1 || nRatio > 6 )
|
if( nRatio < 1 || nRatio > 6 )
|
||||||
nRatio = 1;
|
nRatio = 1;
|
||||||
if (mod_out == NULL) { // initialize
|
if (mod_out == NULL) { // initialize
|
||||||
mod_out = (COMP *)malloc(sizeof(COMP) * n_nom_modem_samples);
|
mod_out = (COMP *)malloc(sizeof(COMP) * MAX_MODEM_SAMPLES);
|
||||||
memset(mod_out, 0, sizeof(COMP) * n_nom_modem_samples);
|
memset(mod_out, 0, sizeof(COMP) * MAX_MODEM_SAMPLES);
|
||||||
speech_in = (short*)malloc(sizeof(short) * n_speech_samples);
|
speech_in = (short*)malloc(sizeof(short) * MAX_SPEECH_SAMPLES);
|
||||||
speech_index=0;
|
speech_index=0;
|
||||||
mod_index=0;
|
mod_index=0;
|
||||||
real_mod_out = (short *)malloc(sizeof(short) * n_nom_modem_samples);
|
real_mod_out = (short *)malloc(sizeof(short) * MAX_MODEM_SAMPLES);
|
||||||
memset(real_mod_out, 0, sizeof(short) * n_nom_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 );
|
//if( DEBUG ) printf("Initialise tx buffer size = %d, i/p sample size = %d\n", n_nom_modem_samples, n_speech_samples );
|
||||||
}
|
}
|
||||||
nout = 0;
|
nout = 0;
|
||||||
@@ -386,7 +374,7 @@ static int quisk_freedv_tx(complex double * cSamples, double * dsamples, int cou
|
|||||||
if (speech_index >= n_speech_samples) {
|
if (speech_index >= n_speech_samples) {
|
||||||
// Calculate a new block, but first write out the rest of the old block
|
// Calculate a new block, but first write out the rest of the old block
|
||||||
// 800xa uses real not complex
|
// 800xa uses real not complex
|
||||||
if( freedv_current_mode == FREEDV_MODE_800XA ) {
|
if( is_real ) {
|
||||||
for ( ; mod_index < n_nom_modem_samples; mod_index++)
|
for ( ; mod_index < n_nom_modem_samples; mod_index++)
|
||||||
cSamples[nout++] = real_mod_out[mod_index];
|
cSamples[nout++] = real_mod_out[mod_index];
|
||||||
freedv_tx(rx_channel[0].hFreedv, real_mod_out, speech_in);
|
freedv_tx(rx_channel[0].hFreedv, real_mod_out, speech_in);
|
||||||
@@ -403,7 +391,7 @@ static int quisk_freedv_tx(complex double * cSamples, double * dsamples, int cou
|
|||||||
else { // write out samples
|
else { // write out samples
|
||||||
for( int j=0; j<nRatio; j++) {
|
for( int j=0; j<nRatio; j++) {
|
||||||
if (mod_index < n_nom_modem_samples ) {
|
if (mod_index < n_nom_modem_samples ) {
|
||||||
if( freedv_current_mode == FREEDV_MODE_800XA ) {
|
if( is_real ) {
|
||||||
cSamples[nout++] = real_mod_out[mod_index];
|
cSamples[nout++] = real_mod_out[mod_index];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -471,21 +459,15 @@ static void CloseFreedv(void) // Called from the GUI thread or sound thread
|
|||||||
freedv_close(rx_channel[i].hFreedv);
|
freedv_close(rx_channel[i].hFreedv);
|
||||||
rx_channel[i].hFreedv = NULL;
|
rx_channel[i].hFreedv = NULL;
|
||||||
}
|
}
|
||||||
#if 0 // g8kbb - unnecessary - quisk_freedv_rx() will do it
|
|
||||||
if (rx_channel[i].demod_in) {
|
|
||||||
free(rx_channel[i].demod_in);
|
|
||||||
rx_channel[i].demod_in = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
quisk_freedv_rx(NULL, NULL, 0, 0);
|
quisk_freedv_rx(NULL, NULL, 0, 0);
|
||||||
quisk_freedv_tx(NULL, NULL, 0);
|
quisk_freedv_tx(NULL, NULL, 0, 0);
|
||||||
freedv_current_mode = -1;
|
freedv_current_mode = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int OpenFreedv(void) // Called from the GUI thread or sound thread
|
static int OpenFreedv(void) // Called from the GUI thread or sound thread
|
||||||
{
|
{
|
||||||
int i, n_max_modem_samples;
|
int i;
|
||||||
struct freedv * hF;
|
struct freedv * hF;
|
||||||
|
|
||||||
if ( ! hLib)
|
if ( ! hLib)
|
||||||
@@ -500,7 +482,7 @@ static int OpenFreedv(void) // Called from the GUI thread or sound thread
|
|||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
case 4:
|
case 4:
|
||||||
printf("freedv_open: codec2 library freedvpkg/libcodec2_32|64.dll|so found, version %d\n", freedv_version);
|
printf("freedv_open: codec2 library freedvpkg/libcodec2_64 found, version %d\n", freedv_version);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("freedv_open: Could not find the FreeDV codec2 shared library\n");
|
printf("freedv_open: Could not find the FreeDV codec2 shared library\n");
|
||||||
@@ -530,18 +512,10 @@ static int OpenFreedv(void) // Called from the GUI thread or sound thread
|
|||||||
return 0; // failure
|
return 0; // failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (requested_mode == FREEDV_MODE_700E) {
|
|
||||||
if (handle_index >= 3) { // Quisk provided codec2
|
|
||||||
CloseFreedv();
|
|
||||||
requested_mode = -1;
|
|
||||||
if (DEBUG)
|
|
||||||
printf("freedv_open: Failure because mode 700E requires a system installation of codec2\n");
|
|
||||||
return 0; // failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((requested_mode == FREEDV_MODE_700D || requested_mode == FREEDV_MODE_700E) && freedv_open_advanced) {
|
if ((requested_mode == FREEDV_MODE_700D || requested_mode == FREEDV_MODE_700E) && freedv_open_advanced) {
|
||||||
struct freedv_advanced adv;
|
struct freedv_advanced adv;
|
||||||
|
memset(&adv, 0, sizeof(adv));
|
||||||
adv.interleave_frames = interleave_frames;
|
adv.interleave_frames = interleave_frames;
|
||||||
hF = freedv_open_advanced(requested_mode, &adv);
|
hF = freedv_open_advanced(requested_mode, &adv);
|
||||||
}
|
}
|
||||||
@@ -579,7 +553,7 @@ static int OpenFreedv(void) // Called from the GUI thread or sound thread
|
|||||||
rx_channel[i].playing = 0;
|
rx_channel[i].playing = 0;
|
||||||
if (rx_channel[i].demod_in)
|
if (rx_channel[i].demod_in)
|
||||||
free(rx_channel[i].demod_in);
|
free(rx_channel[i].demod_in);
|
||||||
rx_channel[i].demod_in = (COMP *)malloc(sizeof(COMP) * n_max_modem_samples);
|
rx_channel[i].demod_in = (short *)malloc(sizeof(short) * n_max_modem_samples);
|
||||||
if( rx_channel[i].demod_in == NULL) {
|
if( rx_channel[i].demod_in == NULL) {
|
||||||
CloseFreedv();
|
CloseFreedv();
|
||||||
requested_mode = -1;
|
requested_mode = -1;
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
extern int freedv_current_mode; // the current FreeDV running mode
|
extern int freedv_current_mode; // the current FreeDV running mode
|
||||||
|
|
||||||
|
// available speech modes
|
||||||
#define FREEDV_MODE_1600 0
|
#define FREEDV_MODE_1600 0
|
||||||
#define FREEDV_MODE_700 1
|
|
||||||
#define FREEDV_MODE_700B 2
|
|
||||||
#define FREEDV_MODE_2400A 3
|
#define FREEDV_MODE_2400A 3
|
||||||
#define FREEDV_MODE_2400B 4
|
#define FREEDV_MODE_2400B 4
|
||||||
#define FREEDV_MODE_800XA 5
|
#define FREEDV_MODE_800XA 5
|
||||||
#define FREEDV_MODE_700C 6
|
#define FREEDV_MODE_700C 6
|
||||||
#define FREEDV_MODE_700D 7
|
#define FREEDV_MODE_700D 7
|
||||||
#define FREEDV_MODE_2020 8
|
|
||||||
#define FREEDV_MODE_700E 13
|
#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
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+63
-46
@@ -626,32 +626,32 @@ static int tx_filter_digital(complex double * filtered, int count)
|
|||||||
static int tx_filter_freedv(complex double * filtered, int count, int encode)
|
static int tx_filter_freedv(complex double * filtered, int count, int encode)
|
||||||
{ // Input samples are creal(filtered), output is filtered.
|
{ // Input samples are creal(filtered), output is filtered.
|
||||||
// This filter is used for digital voice.
|
// This filter is used for digital voice.
|
||||||
// changes by Dave Roberts, G8KBB, for additional modes June, 2020.
|
// Changes by Dave Roberts, G8KBB, for additional modes June, 2020.
|
||||||
|
// Modified by N2ADR January, 2024.
|
||||||
int i;
|
int i;
|
||||||
// The input sample rate is mic_sample_rate, either 8000 or 48000 sps.
|
// The input sample rate is mic_sample_rate, either 8000 or 48000 sps.
|
||||||
// The FreeDV codec requires an input n_speech_sample_rate of either 8000 or 16000 sps depending on the mode. Input is always real.
|
// 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 n_modem_sample_rate of 8000, 16000 or 48000 sps depending on the mode, and output may be real or complex.
|
// 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;
|
double dtmp, magn, dsample;
|
||||||
|
int modem_sample_rate, speech_sample_rate, use_filter, is_real;
|
||||||
static int samples_size = 0;
|
static int samples_size = 0;
|
||||||
static int last_rx_mode;
|
|
||||||
static int last_freedv_mode;
|
|
||||||
static int do_init = 1;
|
static int do_init = 1;
|
||||||
static double aaa, bbb, ccc, Xmin, Xmax, Ymax;
|
static double aaa, bbb, ccc, Xmin, Xmax, Ymax;
|
||||||
static double time_long, time_short;
|
static double time_long, time_short;
|
||||||
static double inMax=0.3;
|
static double inMax=0.3;
|
||||||
static double * dsamples = NULL;
|
static double * dsamples = NULL;
|
||||||
static struct quisk_cFilter filter2;
|
static struct quisk_cFilter filter1, filter2;
|
||||||
static struct quisk_dFilter filtDecim;
|
static struct quisk_dFilter filtDecim48to16, filtDecim16to8;;
|
||||||
static struct quisk_cFilter cfiltInterp;
|
static struct quisk_cFilter cfiltInterp8to48;
|
||||||
|
|
||||||
if (do_init) { // initialization
|
if (do_init) { // initialization
|
||||||
//QuiskWavWriteOpen(&hWav, "jim.wav", 3, 1, 4, 8000, 1.0 / CLIP16);
|
//QuiskWavWriteOpen(&hWav, "jim.wav", 3, 1, 4, 8000, 1.0 / CLIP16);
|
||||||
do_init = 0;
|
do_init = 0;
|
||||||
last_rx_mode = -1;
|
quisk_filt_cInit(&filter1, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double)); // at 48000 sps pass 3000 stop 4000 times two plus 3000
|
||||||
last_freedv_mode = -1;
|
quisk_filt_cInit(&filter2, quiskFilt53D2Coefs, sizeof(quiskFilt53D2Coefs)/sizeof(double)); // at 8000 sps pass 1500 stop 1800 times two plus 1500
|
||||||
memset(&filter2, 0, sizeof(filter2));
|
quisk_filt_cInit(&cfiltInterp8to48, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double));
|
||||||
quisk_filt_dInit(&filtDecim, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double)); // pass 3000, stop 4000
|
quisk_filt_dInit(&filtDecim48to16, quiskAudio24p3Coefs, sizeof(quiskAudio24p3Coefs)/sizeof(double)); // pass 6000 stop 8000
|
||||||
quisk_filt_cInit(&cfiltInterp, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double));
|
quisk_filt_dInit(&filtDecim16to8, quiskFilt16dec8Coefs, sizeof(quiskFilt16dec8Coefs)/sizeof(double)); // pass 3000 stop 4000
|
||||||
Ymax = pow(10.0, - 1 / 20.0); // maximum y
|
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
|
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
|
Xmin = Ymax - fabs(Ymax - Xmax); // x where slope is 1 and y = x; start of compression
|
||||||
@@ -660,36 +660,38 @@ static int tx_filter_freedv(complex double * filtered, int count, int encode)
|
|||||||
bbb = -2.0 * aaa * Xmax;
|
bbb = -2.0 * aaa * Xmax;
|
||||||
ccc = Ymax - aaa * Xmax * Xmax - bbb * Xmax;
|
ccc = Ymax - aaa * Xmax * Xmax - bbb * Xmax;
|
||||||
}
|
}
|
||||||
if (last_freedv_mode != freedv_current_mode) { // correct filter2 depends on freedv mode
|
switch (freedv_current_mode) { // n_modem_sample_rate and n_speech_sample_rate may not be valid
|
||||||
last_freedv_mode = freedv_current_mode;
|
case FREEDV_MODE_2400A:
|
||||||
last_rx_mode = -1; // re-tune new filter
|
case FREEDV_MODE_2400B:
|
||||||
if (filter2.cSamples)
|
use_filter = 1; // center frequency 3000
|
||||||
free(filter2.cSamples);
|
is_real = 1;
|
||||||
filter2.cSamples = NULL;
|
modem_sample_rate = 48000;
|
||||||
if (filter2.cpxCoefs)
|
speech_sample_rate = 8000;
|
||||||
free(filter2.cpxCoefs);
|
break;
|
||||||
filter2.cpxCoefs = NULL;
|
case FREEDV_MODE_2020:
|
||||||
if (filter2.cBuf)
|
use_filter = 0;
|
||||||
free(filter2.cBuf);
|
is_real = 0;
|
||||||
filter2.cBuf = NULL;
|
modem_sample_rate = 8000;
|
||||||
switch(freedv_current_mode) {
|
speech_sample_rate = 16000;
|
||||||
case FREEDV_MODE_700D: // filter2 is not used
|
break;
|
||||||
|
case FREEDV_MODE_800XA:
|
||||||
|
use_filter = 2; // center frequency 1500
|
||||||
|
is_real = 1;
|
||||||
|
modem_sample_rate = 8000;
|
||||||
|
speech_sample_rate = 8000;
|
||||||
break;
|
break;
|
||||||
// TODO: add modes that return real samples that must be converted to complex
|
|
||||||
default:
|
default:
|
||||||
quisk_filt_cInit(&filter2, quiskFilt53D2Coefs, sizeof(quiskFilt53D2Coefs)/sizeof(double)); // pass 1500, stop 1800
|
use_filter = 0;
|
||||||
|
is_real = 0;
|
||||||
|
modem_sample_rate = 8000;
|
||||||
|
speech_sample_rate = 8000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
if ( ! filtered) { // rxMode changed
|
||||||
if (last_rx_mode != (int)rxMode) { // change to sideband
|
quisk_filt_tune((struct quisk_dFilter *)&filter1, 3000.0 / 48000.0, rxMode != FDV_L);
|
||||||
last_rx_mode = rxMode;
|
quisk_filt_tune((struct quisk_dFilter *)&filter2, 1500.0 / 8000.0, rxMode != FDV_L);
|
||||||
if (rxMode == FDV_U) // upper sideband
|
|
||||||
quisk_filt_tune((struct quisk_dFilter *)&filter2, 1600.0 / n_modem_sample_rate, 1);
|
|
||||||
else if (rxMode == FDV_L) // lower sideband
|
|
||||||
quisk_filt_tune((struct quisk_dFilter *)&filter2, 1600.0 / n_modem_sample_rate, 0);
|
|
||||||
}
|
|
||||||
if ( ! filtered)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
// check size of dsamples[] buffer
|
// check size of dsamples[] buffer
|
||||||
if (count > samples_size) {
|
if (count > samples_size) {
|
||||||
samples_size = count * 2;
|
samples_size = count * 2;
|
||||||
@@ -701,12 +703,25 @@ static int tx_filter_freedv(complex double * filtered, int count, int encode)
|
|||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
dsamples[i] = creal(filtered[i]) / CLIP16; // normalize to 1.0000
|
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)
|
// Decimate to 8000 Hz or to 16000 depending on mode (and hence setting of sample rate)
|
||||||
if (quisk_sound_state.mic_sample_rate == 48000)
|
if (quisk_sound_state.mic_sample_rate == 48000) {
|
||||||
count = quisk_dDecimate(dsamples, count, &filtDecim, quisk_sound_state.mic_sample_rate / n_speech_sample_rate);
|
switch (speech_sample_rate) {
|
||||||
else if (quisk_sound_state.mic_sample_rate == 8000 && n_speech_sample_rate != 8000)
|
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");
|
QuiskPrintf("Failure to convert input rate in tx_filter_freedv\n");
|
||||||
|
}
|
||||||
// Measure average peak input audio level and limit
|
// Measure average peak input audio level and limit
|
||||||
dtmp = 1.0 / n_speech_sample_rate; // sample time
|
dtmp = 1.0 / speech_sample_rate; // sample time
|
||||||
time_long = 1.0 - exp(- dtmp / 3.000);
|
time_long = 1.0 - exp(- dtmp / 3.000);
|
||||||
time_short = 1.0 - exp(- dtmp / 0.005);
|
time_short = 1.0 - exp(- dtmp / 0.005);
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
@@ -730,13 +745,15 @@ static int tx_filter_freedv(complex double * filtered, int count, int encode)
|
|||||||
}
|
}
|
||||||
//QuiskWavWriteD(&hWav, dsamples, count);
|
//QuiskWavWriteD(&hWav, dsamples, count);
|
||||||
if (encode && pt_quisk_freedv_tx) // Encode audio into digital modulation
|
if (encode && pt_quisk_freedv_tx) // Encode audio into digital modulation
|
||||||
count = (* pt_quisk_freedv_tx)(filtered, dsamples, count);
|
count = (* pt_quisk_freedv_tx)(filtered, dsamples, count, is_real);
|
||||||
//quisk_calc_audio_graph(CLIP16, filtered, NULL, count, 0);
|
//quisk_calc_audio_graph(CLIP16, filtered, NULL, count, 0);
|
||||||
if (filter2.cSamples) // convert float samples to complex with an analytic filter
|
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);
|
count = quisk_cCDecimate(filtered, count, &filter2, 1);
|
||||||
// Interpolate up to 48000
|
// Interpolate up to 48000
|
||||||
if (MIC_OUT_RATE != n_modem_sample_rate )
|
if (MIC_OUT_RATE != modem_sample_rate )
|
||||||
count = quisk_cInterpolate(filtered, count, &cfiltInterp, MIC_OUT_RATE / n_modem_sample_rate);
|
count = quisk_cInterpolate(filtered, count, &cfiltInterp8to48, MIC_OUT_RATE / modem_sample_rate);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <complex.h> // Use native C99 complex type for fftw3
|
#include <complex.h> // Use native C99 complex type for fftw3
|
||||||
#include <fftw3.h>
|
#include <fftw3.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
@@ -107,7 +108,7 @@ static PyObject * pyApp; // Application instance
|
|||||||
static int fft_size; // size of fft, e.g. 1024
|
static int fft_size; // size of fft, e.g. 1024
|
||||||
int data_width; // number of points to return as graph data; fft_size * n
|
int data_width; // number of points to return as graph data; fft_size * n
|
||||||
static int graph_width; // width of the graph in pixels
|
static int graph_width; // width of the graph in pixels
|
||||||
rx_mode_type rxMode; // 0 to 13: CWL, CWU, LSB, USB, AM, FM, EXT, DGT-U, DGT-L, DGT-IQ, IMD, FDV-U, FDV-L, DGT-FM
|
rx_mode_type rxMode; // 0 to 13: CWL, CWU, LSB, USB, AM, FM, EXT, DGT_U, DGT_L, DGT_IQ, IMD, FDV_U, FDV_L, DGT_FM
|
||||||
int quisk_noise_blanker; // noise blanker level, 0 for off
|
int quisk_noise_blanker; // noise blanker level, 0 for off
|
||||||
int quiskTxHoldState; // hold Tx until the repeater frequency shift is complete
|
int quiskTxHoldState; // hold Tx until the repeater frequency shift is complete
|
||||||
int quisk_is_vna; // zero for normal program, one for the VNA program
|
int quisk_is_vna; // zero for normal program, one for the VNA program
|
||||||
@@ -150,6 +151,7 @@ static int waterfall_scroll_mode = 1; // draw the first lines multiple times
|
|||||||
static int quisk_use_sidetone; // is there a sidetone volume control?
|
static int quisk_use_sidetone; // is there a sidetone volume control?
|
||||||
static int hl2_txbuf_errors; // errors in the Hermes-Lite2 Tx buffer
|
static int hl2_txbuf_errors; // errors in the Hermes-Lite2 Tx buffer
|
||||||
static int hl2_txbuf_state; // state machine for errors in the Hermes-Lite2 Tx buffer
|
static int hl2_txbuf_state; // state machine for errors in the Hermes-Lite2 Tx buffer
|
||||||
|
static int freedv_monitor; // pass the Freedv audio to the speakers instead of to the demodulator
|
||||||
|
|
||||||
static complex double PySampleBuf[SAMP_BUFFER_SIZE]; // buffer for samples returned from Python
|
static complex double PySampleBuf[SAMP_BUFFER_SIZE]; // buffer for samples returned from Python
|
||||||
static int PySampleCount; // count of samples in buffer
|
static int PySampleCount; // count of samples in buffer
|
||||||
@@ -1843,6 +1845,7 @@ static int quisk_process_decimate(complex double * cSamples, int nSamples, int b
|
|||||||
static int quisk_process_demodulate(complex double * cSamples, double * dsamples, int nSamples, int bank, int nFilter, rx_mode_type rx_mode)
|
static int quisk_process_demodulate(complex double * cSamples, double * dsamples, int nSamples, int bank, int nFilter, rx_mode_type rx_mode)
|
||||||
{ // Changes here will require changes to get_filter_rate();
|
{ // Changes here will require changes to get_filter_rate();
|
||||||
int i;
|
int i;
|
||||||
|
bool isLSB;
|
||||||
complex double cx;
|
complex double cx;
|
||||||
double d, di, dd;
|
double d, di, dd;
|
||||||
static struct AgcState Agc1 = {0.3, 16000, 0}, Agc2 = {0.3, 16000, 0};
|
static struct AgcState Agc1 = {0.3, 16000, 0}, Agc2 = {0.3, 16000, 0};
|
||||||
@@ -2138,41 +2141,101 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FDV_U: // digital voice
|
case FDV_U: // digital voice
|
||||||
case FDV_L: // Extra modes added by Dave Roberts, G8KBB, June 2020.
|
case FDV_L: // Extra modes added by Dave Roberts, G8KBB, June 2020. Modified by N2ADR 2024.
|
||||||
quisk_check_freedv_mode();
|
quisk_check_freedv_mode();
|
||||||
// added conditional for 2400A/B mmodes
|
// current coding assumes input rate n_modem_sample_rate is 8000 or 48000.
|
||||||
// current coding assumes modem sample rate is 8000 16000 or 48000
|
// decimate:
|
||||||
if( n_modem_sample_rate <= 16000 )
|
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].filtDecim48to16, 3);
|
||||||
if (bank == 0)
|
nSamples = quisk_cDecimate(cSamples, nSamples, &Storage[bank].filtDecim16to8, 2);
|
||||||
process_agc(&Agc1, cSamples, nSamples, 1);
|
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
|
else
|
||||||
process_agc(&Agc2, cSamples, nSamples, 1);
|
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
|
// Perhaps decimate by an additional fraction
|
||||||
if (quisk_decim_srate != 48000) {
|
if (quisk_decim_srate != 48000) {
|
||||||
dd = quisk_decim_srate / 48000.0;
|
dd = quisk_decim_srate / 48000.0;
|
||||||
nSamples = cFracDecim(cSamples, nSamples, dd);
|
nSamples = cFracDecim(cBuf, nSamples, dd);
|
||||||
quisk_decim_srate = 48000;
|
quisk_decim_srate = 48000;
|
||||||
}
|
}
|
||||||
quisk_filter_srate = n_speech_sample_rate;
|
if (bank == 0)
|
||||||
// added conditional for 2400A/B mmodes
|
process_agc(&Agc1, cBuf, nSamples, 0);
|
||||||
if( n_modem_sample_rate == 8000 )
|
else
|
||||||
nSamples = quisk_cDecimate(cSamples, nSamples, &Storage[bank].filtDecim16to8, 2);
|
process_agc(&Agc2, cBuf, nSamples, 0);
|
||||||
// pass data decimated down to n_modem_sample_rate
|
// pass data decimated down to n_modem_sample_rate
|
||||||
// return data is as n_speech_sample_rate
|
// return data is at n_speech_sample_rate
|
||||||
if (pt_quisk_freedv_rx)
|
#if 0
|
||||||
nSamples = (* pt_quisk_freedv_rx)(cSamples, dsamples, nSamples, bank);
|
static double dmax_in = 0;
|
||||||
if(bank == 0) {
|
|
||||||
for (i = 0; i < nSamples; i++) {
|
for (i = 0; i < nSamples; i++) {
|
||||||
measure_audio_sum += dsamples[i] * dsamples[i];
|
dd = fabs(creal(cBuf[i])) / CLIP16;
|
||||||
measure_audio_count += 1;
|
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;
|
||||||
}
|
}
|
||||||
// current coding assumes speech sample rate is only 8000 or 16000 - needs tweak for others
|
#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_dInterpolate(dsamples, nSamples, &Storage[bank].filtAudio24p3, 3);
|
||||||
if( n_speech_sample_rate == 8000 )
|
|
||||||
nSamples = quisk_dInterp2HB45(dsamples, nSamples, &Storage[bank].HalfBand7);
|
nSamples = quisk_dInterp2HB45(dsamples, nSamples, &Storage[bank].HalfBand7);
|
||||||
break;
|
break;
|
||||||
|
case 16000:
|
||||||
|
nSamples = quisk_dInterpolate(dsamples, nSamples, &Storage[bank].filtAudio24p3, 3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (bank == 0 && measure_audio_count >= quisk_filter_srate * measure_audio_time) {
|
if (bank == 0 && measure_audio_count >= quisk_filter_srate * measure_audio_time) {
|
||||||
measured_audio = sqrt(measure_audio_sum / measure_audio_count) / CLIP32 * 1e6;
|
measured_audio = sqrt(measure_audio_sum / measure_audio_count) / CLIP32 * 1e6;
|
||||||
@@ -2877,7 +2940,7 @@ static PyObject * get_filter_rate(PyObject * self, PyObject * args)
|
|||||||
break;
|
break;
|
||||||
case FDV_U: // digital voice
|
case FDV_U: // digital voice
|
||||||
case FDV_L:
|
case FDV_L:
|
||||||
filter_srate = n_speech_sample_rate;
|
filter_srate = n_modem_sample_rate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//QuiskPrintf("Filter rate %d\n", filter_srate);
|
//QuiskPrintf("Filter rate %d\n", filter_srate);
|
||||||
@@ -3113,13 +3176,15 @@ static PyObject * read_fftw_wisdom(PyObject * self, PyObject * args)
|
|||||||
static PyObject * set_params(PyObject * self, PyObject * args, PyObject * keywds)
|
static PyObject * set_params(PyObject * self, PyObject * args, PyObject * keywds)
|
||||||
{ /* Call with keyword arguments ONLY; change local parameters */
|
{ /* Call with keyword arguments ONLY; change local parameters */
|
||||||
static char * kwlist[] = {"quisk_is_vna", "rx_bytes", "rx_endian", "read_error", "clip",
|
static char * kwlist[] = {"quisk_is_vna", "rx_bytes", "rx_endian", "read_error", "clip",
|
||||||
"bscope_bytes", "bscope_endian", "bscope_size", "bandscopeScale", "hermes_pause", NULL} ;
|
"bscope_bytes", "bscope_endian", "bscope_size", "bandscopeScale", "hermes_pause",
|
||||||
|
"freedv_monitor", NULL} ;
|
||||||
int i, nbytes, read_error, clip, bscope_size, hermes_pause;
|
int i, nbytes, read_error, clip, bscope_size, hermes_pause;
|
||||||
|
|
||||||
nbytes = read_error = clip = bscope_size = hermes_pause = -1;
|
nbytes = read_error = clip = bscope_size = hermes_pause = -1;
|
||||||
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiiiiiiidi", kwlist,
|
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiiiiiiidii", kwlist,
|
||||||
&quisk_is_vna, &nbytes, &py_sample_rx_endian, &read_error, &clip,
|
&quisk_is_vna, &nbytes, &py_sample_rx_endian, &read_error, &clip,
|
||||||
&py_bscope_bytes, &py_bscope_endian, &bscope_size, &bandscopeScale, &hermes_pause))
|
&py_bscope_bytes, &py_bscope_endian, &bscope_size, &bandscopeScale, &hermes_pause,
|
||||||
|
&freedv_monitor))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (nbytes != -1) {
|
if (nbytes != -1) {
|
||||||
py_sample_rx_bytes = nbytes;
|
py_sample_rx_bytes = nbytes;
|
||||||
|
|||||||
@@ -315,6 +315,7 @@ extern play_state_t quisk_play_state; // startup, receiving, sidetone
|
|||||||
extern int freedv_current_mode; // current FreeDV mode; 700D, @)@) etc.
|
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_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_speech_sample_rate; // Microphone decimate to speech_sample_rate, Freedv codec output data at modem_sample_rate, interpolate to 48000
|
||||||
|
extern int n_max_modem_samples; // maximum input to freedv_rx()
|
||||||
extern int quisk_start_cw_delay; // milliseconds to delay output on serial or MIDI CW key down
|
extern int quisk_start_cw_delay; // milliseconds to delay output on serial or MIDI CW key down
|
||||||
extern int quisk_start_ssb_delay; // milliseconds to discard output for all modes except CW
|
extern int quisk_start_ssb_delay; // milliseconds to discard output for all modes except CW
|
||||||
extern struct sound_dev * quiskPlaybackDevices[]; // array of Playback sound devices
|
extern struct sound_dev * quiskPlaybackDevices[]; // array of Playback sound devices
|
||||||
@@ -404,8 +405,8 @@ void quisk_set_play_state(void);
|
|||||||
void quisk_poll_hardware_key(void);
|
void quisk_poll_hardware_key(void);
|
||||||
|
|
||||||
// Functions supporting digital voice codecs
|
// Functions supporting digital voice codecs
|
||||||
typedef int (* ty_dvoice_codec_rx)(complex double *, double *, int, int);
|
typedef int (* ty_dvoice_codec_rx)(short *, double *, int, int);
|
||||||
typedef int (* ty_dvoice_codec_tx)(complex double *, double *, 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_rx pt_quisk_freedv_rx;
|
||||||
extern ty_dvoice_codec_tx pt_quisk_freedv_tx;
|
extern ty_dvoice_codec_tx pt_quisk_freedv_tx;
|
||||||
|
|
||||||
|
|||||||
@@ -4704,11 +4704,15 @@ class App(wx.App):
|
|||||||
self.freedv_menu = QuiskMenu("freedv_menu")
|
self.freedv_menu = QuiskMenu("freedv_menu")
|
||||||
msg = conf.freedv_tx_msg
|
msg = conf.freedv_tx_msg
|
||||||
QS.freedv_set_options(mode=conf.freedv_modes[0][1], tx_msg=msg, DEBUG=0, squelch=1)
|
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:
|
for mode, index in conf.freedv_modes:
|
||||||
item = self.freedv_menu.AppendRadioItem(mode, self.OnFreedvMenu, mode == self.freedv_mode)
|
item = self.freedv_menu.AppendRadioItem(mode, self.OnFreedvMenu, mode == self.freedv_mode)
|
||||||
self.freedv_menu_items[index] = item
|
self.freedv_menu_items[index] = item
|
||||||
if mode == self.freedv_mode: # Restore mode
|
if mode == self.freedv_mode: # Restore mode
|
||||||
QS.freedv_set_options(mode=index)
|
QS.freedv_set_options(mode=index)
|
||||||
|
if mode in ("Mode 2020", "Mode 2400A", "Mode 2400B"):
|
||||||
|
item.Enable(False)
|
||||||
if conf.button_layout == 'Large screen':
|
if conf.button_layout == 'Large screen':
|
||||||
b = QuiskCycleCheckbutton(frame, None, ('FDV-U', 'FDV-L'), is_radio=True)
|
b = QuiskCycleCheckbutton(frame, None, ('FDV-U', 'FDV-L'), is_radio=True)
|
||||||
b.idName = "FDV"
|
b.idName = "FDV"
|
||||||
@@ -5123,7 +5127,10 @@ class App(wx.App):
|
|||||||
elif mode in ('DGT-IQ', 'DGT-FM'):
|
elif mode in ('DGT-IQ', 'DGT-FM'):
|
||||||
center = 0
|
center = 0
|
||||||
elif mode in ('FDV-U', 'FDV-L'):
|
elif mode in ('FDV-U', 'FDV-L'):
|
||||||
center = max(1500, bandwidth // 2)
|
if bandwidth <= 3000:
|
||||||
|
center = 1500
|
||||||
|
else:
|
||||||
|
center = bandwidth // 2
|
||||||
elif mode in ('IMD',):
|
elif mode in ('IMD',):
|
||||||
center = 300 + bandwidth // 2
|
center = 300 + bandwidth // 2
|
||||||
else:
|
else:
|
||||||
@@ -5180,9 +5187,20 @@ class App(wx.App):
|
|||||||
self.multi_rx_screen.waterfall.pane2.filter_center = center
|
self.multi_rx_screen.waterfall.pane2.filter_center = center
|
||||||
if self.screen is self.filter_screen:
|
if self.screen is self.filter_screen:
|
||||||
self.screen.NewFilter()
|
self.screen.NewFilter()
|
||||||
|
def OnFreedvMonitor(self, event):
|
||||||
|
for item in self.freedv_menu.GetMenuItems():
|
||||||
|
if item.GetKind() == wx.ITEM_CHECK:
|
||||||
|
if item.IsChecked():
|
||||||
|
QS.set_params(freedv_monitor=1)
|
||||||
|
else:
|
||||||
|
QS.set_params(freedv_monitor=0)
|
||||||
|
break
|
||||||
def OnFreedvMenu(self, event):
|
def OnFreedvMenu(self, event):
|
||||||
text = ''
|
text = ''
|
||||||
for item in self.freedv_menu.GetMenuItems():
|
for item in self.freedv_menu.GetMenuItems():
|
||||||
|
kind = item.GetKind()
|
||||||
|
if kind != wx.ITEM_RADIO:
|
||||||
|
continue
|
||||||
if item.IsChecked():
|
if item.IsChecked():
|
||||||
text = item.GetItemLabel()
|
text = item.GetItemLabel()
|
||||||
break
|
break
|
||||||
@@ -5206,6 +5224,29 @@ class App(wx.App):
|
|||||||
else:
|
else:
|
||||||
self.freedv_menu_items[mode].Check(1)
|
self.freedv_menu_items[mode].Check(1)
|
||||||
print ("FreeDV change mode failed.")
|
print ("FreeDV change mode failed.")
|
||||||
|
self.OnChangeFilter()
|
||||||
|
def OnChangeFilter(self):
|
||||||
|
# Used by OnFreedvMenu because the FreeDV mode may change the filter sample rate
|
||||||
|
bw = int(self.filterButns.GetLabel())
|
||||||
|
# We can not use QS.get_filter_srate() because the FreeDV mode is not changed yet.
|
||||||
|
if self.freedv_mode in ("Mode 2400A", "Mode 2400B"):
|
||||||
|
frate = 48000;
|
||||||
|
else:
|
||||||
|
frate = 8000;
|
||||||
|
bw = min(bw, frate // 2)
|
||||||
|
self.filter_bandwidth = bw
|
||||||
|
center = self.GetFilterCenter(self.mode, bw)
|
||||||
|
filtI, filtQ = self.MakeFilterCoef(frate, None, bw, center)
|
||||||
|
lower_edge = center - bw // 2
|
||||||
|
QS.set_filters(filtI, filtQ, bw, lower_edge, 0)
|
||||||
|
self.multi_rx_screen.graph.filter_bandwidth = bw
|
||||||
|
self.multi_rx_screen.graph.filter_center = center
|
||||||
|
self.multi_rx_screen.waterfall.pane1.filter_bandwidth = bw
|
||||||
|
self.multi_rx_screen.waterfall.pane1.filter_center = center
|
||||||
|
self.multi_rx_screen.waterfall.pane2.filter_bandwidth = bw
|
||||||
|
self.multi_rx_screen.waterfall.pane2.filter_center = center
|
||||||
|
if self.screen is self.filter_screen:
|
||||||
|
self.screen.NewFilter()
|
||||||
def OnBtnHelp(self, event):
|
def OnBtnHelp(self, event):
|
||||||
if event.GetEventObject().GetValue():
|
if event.GetEventObject().GetValue():
|
||||||
self.OnBtnScreen(None, 'Help')
|
self.OnBtnScreen(None, 'Help')
|
||||||
|
|||||||
@@ -1759,7 +1759,7 @@ freedv_tx_msg = ''
|
|||||||
# This is the list of FreeDV modes and their index number. The starting mode is the first listed.
|
# This is the list of FreeDV modes and their index number. The starting mode is the first listed.
|
||||||
freedv_modes = (('Mode 1600', 0),
|
freedv_modes = (('Mode 1600', 0),
|
||||||
('Mode 2400A', 3), ('Mode 2400B', 4), ('Mode 800XA', 5),
|
('Mode 2400A', 3), ('Mode 2400B', 4), ('Mode 800XA', 5),
|
||||||
('Mode 700C', 6), ('Mode 700D', 7), ('Mode 2020', 8), ('Mode 700E', 13))
|
('Mode 700C', 6), ('Mode 700D', 7), ('Mode 700E', 13), ('Mode 2020', 8))
|
||||||
|
|
||||||
# These are the filter bandwidths for each mode. Quisk has built-in optimized filters
|
# These are the filter bandwidths for each mode. Quisk has built-in optimized filters
|
||||||
# for these values, but you can change them if you want.
|
# for these values, but you can change them if you want.
|
||||||
@@ -1770,7 +1770,7 @@ FilterBwFM = (8000, 10000, 12000, 16000, 18000, 20000)
|
|||||||
FilterBwIMD = FilterBwSSB
|
FilterBwIMD = FilterBwSSB
|
||||||
FilterBwDGT = (200, 400, 1500, 3200, 4800, 10000)
|
FilterBwDGT = (200, 400, 1500, 3200, 4800, 10000)
|
||||||
FilterBwEXT = (8000, 10000, 12000, 15000, 17000, 20000)
|
FilterBwEXT = (8000, 10000, 12000, 15000, 17000, 20000)
|
||||||
FilterBwFDV = (1500, 2000, 3000, '', '', '')
|
FilterBwFDV = (1500, 2000, 3000, '6000', '', '')
|
||||||
|
|
||||||
# If your hardware file defines the method OnButtonPTT(self, event), then Quisk will
|
# If your hardware file defines the method OnButtonPTT(self, event), then Quisk will
|
||||||
# display a PTT button you can press. The method must switch your hardware to
|
# display a PTT button you can press. The method must switch your hardware to
|
||||||
|
|||||||
@@ -1549,6 +1549,8 @@ class QuiskMenu(wx.Menu):
|
|||||||
item.Check(is_checked)
|
item.Check(is_checked)
|
||||||
self.InitData(item, handler)
|
self.InitData(item, handler)
|
||||||
return item
|
return item
|
||||||
|
def AppendSeparator(self):
|
||||||
|
wx.Menu.AppendSeparator(self)
|
||||||
def IsItemChecked(self, item_text):
|
def IsItemChecked(self, item_text):
|
||||||
nid = self.item_text2id[item_text]
|
nid = self.item_text2id[item_text]
|
||||||
menu_item = self.FindItemById(nid)
|
menu_item = self.FindItemById(nid)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import os
|
|||||||
# You must define the version here. A title string including
|
# You must define the version here. A title string including
|
||||||
# the version will be written to __init__.py and read by quisk.py.
|
# the version will be written to __init__.py and read by quisk.py.
|
||||||
|
|
||||||
Version = '4.2.28'
|
Version = '4.2.29'
|
||||||
|
|
||||||
fp = open("__init__.py", "w") # write title string
|
fp = open("__init__.py", "w") # write title string
|
||||||
fp.write("#Quisk version %s\n" % Version)
|
fp.write("#Quisk version %s\n" % Version)
|
||||||
|
|||||||
Reference in New Issue
Block a user