Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7121dacbbd | ||
|
|
0528e8bbbe |
@@ -1,3 +1,19 @@
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
#Quisk version 4.2.44
|
||||
#Quisk version 4.2.46
|
||||
from .quisk import main
|
||||
|
||||
@@ -395,7 +395,7 @@ class ControlCommon(BaseHardware): # This is the Hardware class for the control
|
||||
for idName in ("Mute", "NR2", "AGC", "Sqlch", "NB 1", "Notch", "Test 1", "Spot", "FDX", "PTT", "VOX"):
|
||||
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)
|
||||
|
||||
@@ -200,7 +200,8 @@ If you have Python 3 installed on your computer, just continue to use that versi
|
||||
<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>
|
||||
|
||||
@@ -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 +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
@@ -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.
+4
-150
@@ -8,7 +8,6 @@
|
||||
#include <sys/types.h>
|
||||
#include "microphone.h"
|
||||
#include "filter.h"
|
||||
#include "freedv.h"
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
#include <winsock2.h>
|
||||
@@ -245,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;
|
||||
@@ -617,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
|
||||
@@ -626,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
|
||||
@@ -1397,6 +1260,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;
|
||||
@@ -1404,11 +1269,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;
|
||||
}
|
||||
@@ -1499,11 +1359,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;
|
||||
@@ -1673,7 +1528,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -42,8 +39,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
@@ -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)
|
||||
|
||||
@@ -410,7 +410,7 @@ class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole
|
||||
self.set_C = 0
|
||||
self.set_HighZ = 0
|
||||
self.antnum = 0 # Antenna number 0 or 1
|
||||
self.dipole2 = AntennaControl(app, conf) # Control the KI8BV dipole
|
||||
self.dipole2 = None #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:
|
||||
@@ -484,10 +484,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
|
||||
@@ -521,7 +521,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)
|
||||
@@ -657,9 +658,9 @@ class StationControlGUI(wx.Frame): # Display a stand-alone control window for
|
||||
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):
|
||||
|
||||
@@ -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;
|
||||
@@ -569,15 +568,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 +1845,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 +2082,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 +2109,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 +2148,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;
|
||||
@@ -2785,9 +2683,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]);
|
||||
@@ -2945,6 +2840,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
|
||||
@@ -2953,10 +2850,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);
|
||||
@@ -3196,14 +3089,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;
|
||||
@@ -6269,13 +6161,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."},
|
||||
|
||||
@@ -312,10 +312,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
|
||||
@@ -346,14 +342,7 @@ 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 *);
|
||||
@@ -397,7 +386,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);
|
||||
@@ -407,12 +395,6 @@ void quisk_set_play_state(void);
|
||||
void quisk_poll_hardware_key(void);
|
||||
void PreDistort(complex double * amp_in_samples, complex double * amp_out_samples, int nSamples, complex double * tx_samples, int num_tx);
|
||||
|
||||
// Functions supporting digital voice codecs
|
||||
typedef int (* ty_dvoice_codec_rx)(short *, double *, int, int);
|
||||
typedef int (* ty_dvoice_codec_tx)(complex double *, double *, int, int);
|
||||
extern ty_dvoice_codec_rx pt_quisk_freedv_rx;
|
||||
extern ty_dvoice_codec_tx pt_quisk_freedv_tx;
|
||||
|
||||
// Driver function definitions=================================================
|
||||
int quisk_read_alsa(struct sound_dev *, complex double *);
|
||||
void quisk_play_alsa(struct sound_dev *, int, complex double *, int, double);
|
||||
@@ -468,7 +450,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])
|
||||
@@ -483,7 +465,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);
|
||||
|
||||
@@ -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,
|
||||
@@ -995,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)
|
||||
@@ -1014,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
|
||||
@@ -1061,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'
|
||||
@@ -3727,7 +3717,7 @@ 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_name_play_audio', 'file_name_play_samples', 'file_name_play_cq',
|
||||
'file_play_source', 'hermes_LNA_dB']
|
||||
def __init__(self):
|
||||
global application
|
||||
@@ -3948,8 +3938,6 @@ 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.hermes_LNA_dB = 20
|
||||
if hasattr(Hardware, "OnChangeRxTx"):
|
||||
self.want_RxTx = True
|
||||
@@ -4711,8 +4699,6 @@ class App(wx.App):
|
||||
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='')
|
||||
@@ -5051,14 +5037,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
|
||||
@@ -5080,43 +5061,11 @@ 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 = 1000
|
||||
QS.set_imd_level(val)
|
||||
@@ -5395,16 +5344,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()
|
||||
@@ -5574,66 +5513,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')
|
||||
@@ -6098,11 +5977,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:
|
||||
@@ -6896,10 +6770,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:
|
||||
|
||||
@@ -1797,23 +1797,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)
|
||||
|
||||
@@ -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.44'
|
||||
Version = '4.2.46'
|
||||
|
||||
fp = open("__init__.py", "w") # write title string
|
||||
fp.write("#Quisk version %s\n" % Version)
|
||||
|
||||
@@ -1000,21 +1000,25 @@ 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)
|
||||
//if (is_DGT)
|
||||
play_sound_interface(&DigitalOutput, nSamples, cSamples, 1, digital_output_level);
|
||||
|
||||
// Perhaps record the speaker audio to a file
|
||||
|
||||
+47
-50
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user