Skip to content

Commit

Permalink
Quisk version 4.2.32
Browse files Browse the repository at this point in the history
  • Loading branch information
jimahlstrom committed Apr 25, 2024
1 parent 814e4ce commit 71146c9
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 35 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
Quisk Version 4.2.32 April 2024
================================
I found and fixed a bug in the microphone filters that can result in a buffer overrun. You should update Quisk.

Some Linux digital programs can not use the usual Quisk Pulse loopback device and must use an
Alsa loopback. I added more documentation for Alsa loopbacks, added more information on the Sound
screen and I changed the default Alsa sample size to 16 bit.

Quisk now responds to the Hermes Lite2 Tx inhibit input by displaying a message and extinguishing the "Tx" light.
There is a new item "Hermes Lite2 protocol" on the Hardware screen to control this behavior.

Quisk Version 4.2.31 March 2024
================================
The "Tune" item was missing from the Midi menus. I fixed it but I didn't test it because I did
Expand Down
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#Quisk version 4.2.31
#Quisk version 4.2.32
from .quisk import main
12 changes: 9 additions & 3 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -2005,7 +2005,7 @@ def OnChange2(self, ctrl, value):
if self.radio_name == Settings[1]: # changed for current radio
if name in ('hot_key_ptt_toggle', 'hot_key_ptt_if_hidden', 'keyupDelay', 'cwTone', 'pulse_audio_verbose_output',
'start_cw_delay', 'start_ssb_delay', 'maximum_tx_secs', 'quisk_serial_cts', 'quisk_serial_dsr',
'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec'):
'hot_key_ptt1', 'hot_key_ptt2', 'midi_ptt_toggle', 'TxRxSilenceMsec', 'hermes_lite2_enable'):
setattr(conf, name, x)
application.ImmediateChange(name)
elif name[0:4] in ('lin_', 'win_'):
Expand Down Expand Up @@ -3399,14 +3399,20 @@ class RadioSound(BaseWindow): # The Sound page in the second-level notebook for
('', '', '', '', 'digital_rx8_name'),
('', '', '', '', 'digital_rx9_name'),
)
hRx0 = "This is almost the same as the radio sound, but it is sent to a digital mode program on a different sound device."\
" It is analog audio and it is only sent if one of the DGT- modes is selected."\
" The volume is set by the digital output level instead of the volume control so you can mute the speaker if desired."\
" It can receive a greater bandwidth than the 3000 Hz limit of SSB."
hTx0 = "This is used instead of the microphone when one of the DGT- modes is selected."\
" The normal speech clipping and filtering is not used for DGT- modes."
label_help = ( # Same order as sound_names
(1, "Radio Sound Output", "This is the radio sound going to the headphones or speakers."),
(0, "Microphone Input", "This is the monophonic microphone source. Set the channel if the source is stereo."),
(0, "I/Q Rx Sample Input", "This is the sample source if it comes from a sound device, such as a SoftRock."),
(1, "I/Q Tx Sample Output", "This is the transmit sample audio sent to a SoftRock."),
(1, "Raw Digital Output", "This sends the received I/Q data to another program as stereo."),
(0, "Digital Tx0 Input", "This is the transmit audio coming from a digital mode program."),
(1, "Digital Rx0 Output", "This is the main receiver Rx0 audio going to a digital mode program."),
(0, "Digital Tx0 Input", hTx0),
(1, "Digital Rx0 Output", hRx0),
(1, "Digital Rx1 Output", "This is the sub-receiver 1 audio going to a digital mode program."),
(1, "Digital Rx2 Output", "This is the sub-receiver 2 audio going to a digital mode program."),
(1, "Digital Rx3 Output", "This is the sub-receiver 3 audio going to a digital mode program."),
Expand Down
29 changes: 18 additions & 11 deletions docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -1470,22 +1470,29 @@ <h2 id="Digital">Digital Modes </h2>
<br>

<br>
If you don't have PulseAudio or don't want to use it, you can use the ALSA loopback device.
The ALSA loopback device works the same way as the Windows VAC. First
create the loopback device with the command "modprobe snd-aloop" (you
will need to be root). You can create the loopback device
when the system starts, but the way to do that depends on your version of
Linux. I added snd-aloop to /etc/modules. You could put the modprobe command in /etc/rc.local instead.
If your digital program only uses Alsa devices or you don't want to use PulseAudio,
you can use the ALSA loopback device.
It works the same way as the Windows VAC. First create the loopback device with this command:
<pre>
sudo modprobe snd-aloop index=19
</pre>
The "index" is the card number, and it is best to use a number higher than your regular cards.
The loopback device will be gone on the next restart.
You can create the loopback device every time the system starts, but the way to do that depends on your version of Linux.
You could add this line to /etc/modprobe.d/alsa-base.conf:
<pre>
options snd-aloop index=19
</pre>
Or you could add snd-aloop to /etc/modules or put the modprobe command in /etc/rc.local instead.
Restart Linux. Now you can enter "cat /proc/asound/cards" to
print out your sound cards, and you should see a "Loopback" card
listed. The cards are also shown on the Quisk Sound
screen. The Loopback card has one side that connects to Quisk and
print out your sound cards, and you should see a "Loopback" card listed.
The Loopback card has one side that connects to Quisk and
another side that connects to your digital program. For the Quisk
side connect both Digital Input and Digital Output to Loopback,0.
side connect both Digital Input and Digital Output to the Loopback at hw:19,0.
Note that the Loopback card is full duplex, and handles both
input and output. There are actually eight loopbacks created at once,
but we are only using subdevice 0. For the digital program side, set the input and
output to "Loopback,1".
output to the Loopback at hw:19,1.
Your audio is now connected and you
should be able to receive digital signals. Be sure to test your
transmit signal off the air. You may need to reduce power to
Expand Down
13 changes: 7 additions & 6 deletions microphone.c
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ static void process_alc(complex double * cSamples, int count, struct alc * pt, r

static int tx_filter(complex double * filtered, int count)
{ // Input samples are creal(filtered), output is filtered. The input rate must be 8000 or 48000 sps.
int i, is_ssb;
int i, is_ssb, mic_interp;
int sample_rate = 8000;
double dsample, dtmp, magn;
complex double csample;
Expand Down Expand Up @@ -423,9 +423,10 @@ static int tx_filter(complex double * filtered, int count)
}
return 0;
}
mic_interp = MIC_OUT_RATE / sample_rate;
// check size of dsamples[] and csamples[] buffer
if (count > samples_size) {
samples_size = count * 2;
samples_size = count * 2 * mic_interp;
if (dsamples)
free(dsamples);
if (csamples)
Expand Down Expand Up @@ -541,8 +542,8 @@ static int tx_filter(complex double * filtered, int count)
// remove clipping distortion
count = quisk_cDecimate(csamples, count, &cfiltAudio3, 1);
// Interpolate up to 48000
if (MIC_OUT_RATE != sample_rate)
count = quisk_cInterpolate(csamples, count, &cfiltInterp, MIC_OUT_RATE / sample_rate);
if (mic_interp != 1)
count = quisk_cInterpolate(csamples, count, &cfiltInterp, mic_interp);
// convert back to 16 bits
for (i = 0; i < count; i++) {
filtered[i] = csamples[i] * CLIP16;
Expand Down Expand Up @@ -573,8 +574,8 @@ static int tx_filter(complex double * filtered, int count)
// remove clipping distortion
count = quisk_dFilter(dsamples, count, &dfiltAudio3);
// Interpolate up to 48000
if (MIC_OUT_RATE != sample_rate)
count = quisk_dInterpolate(dsamples, count, &dfiltInterp, MIC_OUT_RATE / sample_rate);
if (mic_interp != 1)
count = quisk_dInterpolate(dsamples, count, &dfiltInterp, mic_interp);
// convert back to 16 bits
for (i = 0; i < count; i++) {
filtered[i] = dsamples[i] * CLIP16;
Expand Down
6 changes: 5 additions & 1 deletion quisk.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ static int freedv_monitor; // pass the Freedv audio to the speakers instead of

static complex double PySampleBuf[SAMP_BUFFER_SIZE]; // buffer for samples returned from Python
static int PySampleCount; // count of samples in buffer
static bool quisk_tx_inhibit;

static int multirx_data_width; // width of graph data to return
static int multirx_fft_width; // size of FFT samples
Expand Down Expand Up @@ -2741,7 +2742,7 @@ int quisk_process_samples(complex double * cSamples, int nSamples)
multirx_cSamples[0][i] = dsamples2[i] + I * dsamples2[i];
process_agc(&Agc3, multirx_cSamples[0], n, 0);
}
play_sound_interface(quiskPlaybackDevices[QUISK_INDEX_SUB_RX1], n, multirx_cSamples[0], 1, 1.0);
play_sound_interface(quiskPlaybackDevices[QUISK_INDEX_SUB_RX1], n, multirx_cSamples[0], 1, digital_output_level);
}

// Perhaps decimate by an additional fraction
Expand Down Expand Up @@ -3156,6 +3157,8 @@ static PyObject * get_params(PyObject * self, PyObject * args)
return PyInt_FromLong(quisk_serial_ptt);
if (strcmp(name, "hl2_txbuf_errors") == 0)
return PyInt_FromLong(hl2_txbuf_errors);
if (strcmp(name, "quisk_tx_inhibit") == 0)
return PyInt_FromLong((long)quisk_tx_inhibit);
Py_INCREF (Py_None);
return Py_None;
}
Expand Down Expand Up @@ -3759,6 +3762,7 @@ static int read_rx_udp10(complex double * samp) // Read samples from UDP using t
//code_version = quisk_hermes_to_pc[3];
if ((quisk_hermes_to_pc[0] & 0x01) != 0) // C1
quisk_sound_state.overrange++;
quisk_tx_inhibit = (quisk_hermes_to_pc[0] & 0x02) == 0; // should limit to Hermes Lite2 only
hardware_ptt = buf[start] & 0x01; // C0 bit zero is PTT
quisk_hardware_cwkey = (buf[start] & 0x04) >> 2; // C0 bit two is CW key state
switch (hl2_txbuf_state) {
Expand Down
1 change: 1 addition & 0 deletions quisk.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ extern int quisk_start_cw_delay; // milliseconds to delay output on serial or M
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 int quisk_close_file_play;
extern double digital_output_level;

extern PyObject * quisk_set_spot_level(PyObject * , PyObject *);
extern PyObject * quisk_get_tx_filter(PyObject * , PyObject *);
Expand Down
20 changes: 19 additions & 1 deletion quisk.py
Original file line number Diff line number Diff line change
Expand Up @@ -1762,6 +1762,12 @@ def OnPaint(self, event):
dc.SetTextForeground(conf.color_graph_msg_fg)
dc.SetBackgroundMode(wx.SOLID)
dc.DrawText(self.display_text, 0, 0)
if application.tx_inhibit:
dc.SetFont(self.font)
dc.SetTextBackground(conf.color_graph_msg_bg)
dc.SetTextForeground("red")
dc.SetBackgroundMode(wx.SOLID)
dc.DrawText(" *** Tx Inhibit ***", 0, self.chary * 15 // 10)
def DrawFilter(self, dc):
dc.SetPen(wx.TRANSPARENT_PEN)
dc.SetLogicalFunction(wx.COPY)
Expand Down Expand Up @@ -3497,6 +3503,8 @@ def OnInit(self):
self.midi_message = []
self.idName2Button = {}
self.midi_handler = None
self.tx_inhibit = 0
self.old_tx_inhibit = 0
if conf.use_rx_udp == 10: # Hermes UDP protocol
self.bandscope_clock = conf.rx_udp_clock
else:
Expand Down Expand Up @@ -6370,7 +6378,7 @@ def OnReadSound(self): # called at frequent intervals
Hardware.RepeaterOffset(0)
QS.set_ctcss(0)
QS.tx_hold_state(1)
if QS.is_key_down(): # Tx indicator
if QS.is_key_down() and not self.tx_inhibit: # Tx indicator
if not self.tx_indicator:
self.tx_indicator = True
self.pttButton.Tx.TurnOn(True)
Expand Down Expand Up @@ -6431,6 +6439,8 @@ def OnReadSound(self): # called at frequent intervals
self.hot_key_ptt_active = False
self.hot_key_ptt_was_down = self.hot_key_ptt_is_down
self.hot_key_ptt_pressed = False
if self.tx_inhibit:
ptt = False
if ptt is True and not ptt_button_down:
self.SetPTT(True)
elif ptt is False and ptt_button_down:
Expand Down Expand Up @@ -6501,6 +6511,14 @@ def OnReadSound(self): # called at frequent intervals
if self.timer - self.heart_time0 > 0.10: # call hardware to perform background tasks:
self.heart_time0 = self.timer
Hardware.HeartBeat()
if conf.hermes_lite2_enable:
self.tx_inhibit = QS.get_params('quisk_tx_inhibit')
else:
self.tx_inhibit = 0
if self.tx_inhibit != self.old_tx_inhibit:
self.old_tx_inhibit = self.tx_inhibit
self.graph.display.Refresh()
self.waterfall.pane1.display.Refresh()
msg = QS.GetQuiskPrintf()
if msg:
print(msg, end='')
Expand Down
7 changes: 7 additions & 0 deletions quisk_conf_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,13 @@
#hermes_iob_rxin = 'HL2 Rx to J9'
#hermes_iob_rxin = 'Use J9 and J10'

## hermes_lite2_enable Hermes Lite2 protocol, boolean
# This turns on some of the protocol extensions for the Hermes Lite2 that are not part
# of the official Hermes Protocol 1. Currently this only enables Tx Inhibit (CN8).
# Changes are immediate (no need to restart).
hermes_lite2_enable = True
#hermes_lite2_enable = False


# These are known power meter calibration tables. This table is not present in the JSON settings file.
power_meter_std_calibrations = {}
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# 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.31'
Version = '4.2.32'

fp = open("__init__.py", "w") # write title string
fp.write("#Quisk version %s\n" % Version)
Expand Down
2 changes: 1 addition & 1 deletion sound.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ char * sound_format_names[4] = {"Int32", "Int16", "Float32", "Int24"} ;
static struct wav_file file_rec_audio, file_rec_samples, file_rec_mic;
static int close_file_rec;

static double digital_output_level = 0.7;
double digital_output_level = 0.7;
static int dc_remove_bw=100; // bandwidth of DC removal filter

static ty_sample_start pt_sample_start;
Expand Down
40 changes: 30 additions & 10 deletions sound_alsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,37 @@ static snd_pcm_format_t check_formats(struct sound_dev * dev, snd_pcm_hw_params_
{
snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
dev->sample_bytes = 0;

#if 0
char * card_name;
int card;
snd_pcm_info_t * pcm_info;
printf("driver %s\n", snd_ctl_card_info_get_driver(info));
int loopback = 0;
if (snd_pcm_info_malloc(&pcm_info) == 0) {
if (snd_pcm_info (dev->handle, pcm_info) == 0) {
card = snd_pcm_info_get_card (pcm_info);
if (card >= 0) {
if (snd_card_get_name(card, &card_name) == 0) {
if (strcmp(card_name, "Loopback") == 0)
loopback = 1;
printf("name %s\n", card_name);
free(card_name);
}
}
}
snd_pcm_info_free(pcm_info);
}
#endif
strMcpy (dev->msg1, "Available formats: ", QUISK_SC_SIZE);
if (snd_pcm_hw_params_test_format (dev->handle, hware, SND_PCM_FORMAT_S16) == 0) {
if (!dev->sample_bytes) {
strncat(dev->msg1, "*", QUISK_SC_SIZE);
dev->sample_bytes = 2;
dev->sound_format = Int16;
format = SND_PCM_FORMAT_S16;
}
strncat(dev->msg1, "S16 ", QUISK_SC_SIZE);
}
if (snd_pcm_hw_params_test_format (dev->handle, hware, SND_PCM_FORMAT_S32) == 0) {
if (!dev->sample_bytes) {
strncat(dev->msg1, "*", QUISK_SC_SIZE);
Expand All @@ -515,15 +544,6 @@ static snd_pcm_format_t check_formats(struct sound_dev * dev, snd_pcm_hw_params_
if (snd_pcm_hw_params_test_format (dev->handle, hware, SND_PCM_FORMAT_U24) == 0) {
strncat(dev->msg1, "U24 ", QUISK_SC_SIZE);
}
if (snd_pcm_hw_params_test_format (dev->handle, hware, SND_PCM_FORMAT_S16) == 0) {
if (!dev->sample_bytes) {
strncat(dev->msg1, "*", QUISK_SC_SIZE);
dev->sample_bytes = 2;
dev->sound_format = Int16;
format = SND_PCM_FORMAT_S16;
}
strncat(dev->msg1, "S16 ", QUISK_SC_SIZE);
}
if (snd_pcm_hw_params_test_format (dev->handle, hware, SND_PCM_FORMAT_S24_3LE) == 0) {
if (!dev->sample_bytes) {
strncat(dev->msg1, "*", QUISK_SC_SIZE);
Expand Down

0 comments on commit 71146c9

Please sign in to comment.