diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 14c2228..aa43cb3 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,8 @@ +Quisk Version 4.2.36 August 2024 +================================= +I added two new color schemes by Michael, DK1MI. The On/Off button color and text color can +now be configured. (Michael, send me the final colors). There are a few other small changes. + Quisk Version 4.2.35 June 2024 =============================== There is a new item "File play level" on the Config/radio/Sound screen to reduce the volume when playing files. diff --git a/__init__.py b/__init__.py index 768eee0..86f7764 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,2 @@ -#Quisk version 4.2.35 +#Quisk version 4.2.36 from .quisk import main diff --git a/configure.py b/configure.py index 553ce61..68608d2 100644 --- a/configure.py +++ b/configure.py @@ -284,10 +284,8 @@ class Configuration: self.settings_changed = True errors = errors + "Failed to set %s to %s using format %s\n" % (k, v, fmt) #traceback.print_exc() - if conf.color_scheme == 'B': - conf.__dict__.update(conf.color_scheme_B) - elif conf.color_scheme == 'C': - conf.__dict__.update(conf.color_scheme_C) + if conf.color_scheme != 'A': + conf.__dict__.update(getattr(conf, 'color_scheme_' + conf.color_scheme)) self.RequiredValues(radio_dict) # Why not update conf too??? This only updates the radio_dict. if errors: dlg = wx.MessageDialog(None, errors, @@ -601,7 +599,7 @@ class Configuration: section = None data_name = None multi_line = False - fp = open(filename, "r") + fp = open(filename, "r", encoding="utf8") for line in fp: line = line.strip() if not line: @@ -986,6 +984,7 @@ class QAdjustPhase(wx.Frame): btnbox.Add(1, 1, proportion=10) btns.append(b) b.Bind(wx.EVT_BUTTON, self.OnBtnHelp) + #b.Bind(wx.EVT_BUTTON, self.OnGraphData) for b in btns: b.SetBezelWidth(3) b.SetBackgroundColour("#DDD") @@ -1220,6 +1219,7 @@ class QAdjustPhase(wx.Frame): application.bandAmplPhase = copy.deepcopy(self.bandAmplPhase) self.dirty = False def OnBtnExit(self, event): + #self.OnGraphData(None) if self.dirty: dlg = wx.MessageDialog(self, "Your changes are not saved. Do you want to save them?", "Changes Were Made", @@ -1245,6 +1245,27 @@ The maximum slider adjustment range can be changed on the radio Hardware screen. For more information, press the main "Help" button, then "Documentation", then "SoftRock".' , "Adjustment Help", style=wx.OK) dlg.ShowModal() + def OnGraphData(self, event): + if not hasattr(self, "VFO"): + self.VFO = [] + self.Tune = [] + self.Gain = [] + self.Phase = [] + if event: + self.VFO.append(application.VFO) + self.Tune.append(self.new_tune) + self.Gain.append(self.new_amplitude + 1.0) + self.Phase.append(self.new_phase) + else: + for name in ("VFO", "Tune", "Gain", "Phase"): + print ("%s = [" % name) + lst = getattr(self, name) + for i in range(0, len(lst)): + if name[0] in "VT": + print("%d," % lst[i]) + else: + print("%.5f," % lst[i]) + print ("]") class ListEditDialog(wx.Dialog): # Display a dialog with a List-Edit control, plus Ok/Cancel def __init__(self, parent, title, choice, choices, width): diff --git a/docs.html b/docs.html index e8bd1a4..f8a7a46 100644 --- a/docs.html +++ b/docs.html @@ -1469,7 +1469,10 @@ show the devices being used. Change the Fldigi playback to QuiskDigitalInput, an Monitor of QuiskDigitalOutput. Then everything should work. You do not need to use pavucontrol again because PulseAudio will remember the settings.
- +
+Michael, DK1MI/N1BSD, has written a nice description of how to set up Fldigi. It is available +at his site https://dk1mi.radio/quisk-fldigi/. +

If your digital program only uses Alsa devices or you don't want to use PulseAudio, you can use the ALSA loopback device. diff --git a/quisk.c b/quisk.c index da8049c..c65c782 100644 --- a/quisk.c +++ b/quisk.c @@ -1976,7 +1976,7 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples ssb_squelch(dsamples, nSamples, quisk_filter_srate, MeasureSquelch + bank); d_delay(dsamples, nSamples, bank, SQUELCH_FFT_SIZE); } - quisk_calc_audio_graph(pow(2, 31) - 1, NULL, dsamples, nSamples, 1); + //quisk_calc_audio_graph(pow(2, 31) - 1, NULL, dsamples, nSamples, 1); nSamples = quisk_dInterpolate(dsamples, nSamples, &Storage[bank].filtAudio24p4, 2); nSamples = quisk_dInterp2HB45(dsamples, nSamples, &Storage[bank].HalfBand7); //quisk_calc_audio_graph(pow(2, 31) - 1, NULL, dsamples, nSamples, 1); @@ -5060,6 +5060,62 @@ static PyObject * get_bandscope(PyObject * self, PyObject * args) // Called by t return Py_None; } +#if 0 +static void check_channel_delay(fft_data * ptFft) +{ + int i, maxi, freq; + static int count=0, freq0; + static double gain=0, phase=0; + double d, ampl, maxa; + static double complex * samples1=NULL, * samples2; + + if (samples1 == NULL) { + samples1 = (double complex *)malloc(fft_size * sizeof(double complex)); + samples2 = (double complex *)malloc(fft_size * sizeof(double complex)); + } + for (i = 0; i < fft_size; i++) { + samples1[i] = creal(ptFft->samples[i]); + samples2[i] = cimag(ptFft->samples[i]); + } + fftw_execute_dft(quisk_fft_plan, samples1, samples1); + fftw_execute_dft(quisk_fft_plan, samples2, samples2); + maxa = 0; + maxi = 0; + for (i = 0; i < fft_size / 2; i++) { + ampl = cabs(samples1[i]); + if (ampl > maxa) { + maxa = ampl; + maxi = i; + } + } + freq = maxi * fft_sample_rate / fft_size; + //freq = maxi * 44100 / fft_size; + gain += cabs(samples1[maxi]) / cabs(samples2[maxi]); + d = carg(samples1[maxi]) - carg(samples2[maxi]); + if (d > M_PI) + d -= M_PI * 2; + else if(d < - M_PI) + d += M_PI * 2; + phase += d; + count += 1; + if (count == 1) { + freq0 = freq; + } + else if (freq != freq0) { + gain = phase = count = 0; + return; + } + if (count > 10) { + gain /= count; + phase /= count; + //printf("Channel delay freq %6d, gain %6.3lf, phase %8.5lf radians, %6.3lf degrees\n", + // freq, gain, phase, phase * 57.29578); + printf("%6d, %6.3lf, %6.3lf\n", freq, gain, phase * 57.29578); + gain = phase = count = 0; + } +} +#endif + static void softrock_correct_fft(fft_data * ptFft, int fixit) { // See i/Q balance in Rocky: https://www.dxatlas.com/Rocky/Advanced.asp double ampl, r1, r2, i1, i2, Zr, Zi, Pwr, Gain, Phase, OutR, OutI, S1R, S1I, S2R, S2I; @@ -5205,6 +5261,7 @@ static PyObject * get_graph(PyObject * self, PyObject * args) // Called by the G // Continue with FFT calculation for (i = 0; i < fft_size; i++) // multiply by window ptFft->samples[i] *= fft_window[i]; + //check_channel_delay(ptFft); fftw_execute_dft(quisk_fft_plan, ptFft->samples, ptFft->samples); // Calculate FFT if (softrock_correct_active == 2) softrock_correct_fft(ptFft, 0); diff --git a/quisk.py b/quisk.py index a77bca6..1e2b646 100644 --- a/quisk.py +++ b/quisk.py @@ -2537,12 +2537,10 @@ class WaterfallDisplay(wx.Window): self.top_key = 8 self.top_size = (self.top_key + 2) * (self.top_key - 1) // 2 # Make the palette - if conf.waterfall_palette == 'B': - pal2 = conf.waterfallPaletteB - elif conf.waterfall_palette == 'C': - pal2 = conf.waterfallPaletteC - else: + if conf.waterfall_palette == 'A': pal2 = conf.waterfallPalette + else: + pal2 = getattr(conf, "waterfallPalette" + conf.waterfall_palette) red = bytearray(256) green = bytearray(256) blue = bytearray(256) @@ -4918,7 +4916,8 @@ class App(wx.App): self.freqDisplay.Display(self.txFreq + self.VFO) # On/Off button if conf.button_layout == 'Large screen': - b_onoff = QuiskCheckbutton(frame, self.OnBtnOnOff, "On", color='#77DD77') + b_onoff = QuiskCheckbutton(frame, self.OnBtnOnOff, "On", color=conf.color_onoff) + b_onoff.text_color = conf.color_onoff_text b_onoff.SetValue(True, do_cmd=False) h = b_freqdisp.height b_onoff.SetSizeHints(h, h, h, h) diff --git a/quisk_conf_defaults.py b/quisk_conf_defaults.py index e958f10..a178355 100644 --- a/quisk_conf_defaults.py +++ b/quisk_conf_defaults.py @@ -1916,6 +1916,8 @@ btn_text_switch = u"\u21C4" # Character to switch left-right color_scheme = 'A' #color_scheme = 'B' #color_scheme = 'C' +#color_scheme = 'DK1MI_GR' +#color_scheme = 'DK1MI_AM' ## waterfall_palette Waterfall colors, text choice # This controls the colors used in the waterfall. The default color scheme is A, and you can change this scheme @@ -1923,6 +1925,7 @@ color_scheme = 'A' waterfall_palette = 'A' #waterfall_palette = 'B' #waterfall_palette = 'C' +#waterfall_palette = 'DK1MI_AM' @@ -1957,6 +1960,8 @@ color_txline = 'red' # vertical line color for tx in graph color_rxline = 'green' # vertical line color for rx in graph color_graph_msg_fg = 'black' # text messages on the graph screen color_graph_msg_bg = 'lemonchiffon2' # background of text messages on the graph screen +color_onoff = '#77DD77' # on/off power button +color_onoff_text = 'black' # on/off power button text # This color scheme B, a dark color scheme designed by Steve Murphy, KB8RWQ. # Additional colors added by N2ADR. @@ -1986,6 +1991,8 @@ color_scheme_B = { 'color_rxline' : 'green', 'color_graph_msg_fg' : 'white', 'color_graph_msg_bg' : '#111111', +'color_onoff' : '#77DD77', +'color_onoff_text' : 'black', } # This is color scheme C: @@ -2023,12 +2030,73 @@ color_scheme_C = { 'color_rxline' : '#3CC918', 'color_graph_msg_fg' : '#000000', 'color_graph_msg_bg' : 'lemonchiffon2', +'color_onoff' : '#77DD77', +'color_onoff_text' : 'black', } ############################################################################################# +# This is a green dark color scheme designed by Michael +color_scheme_DK1MI_GR = { + 'color_bg' : '#0F0F0F', # Lower screen background + 'color_bg_txt' : '#00CC00', # Lower screen text color + 'color_graph' : '#0F0F0F', # Graph background + 'color_config2' : '#008000', # color in tab row of config screen + 'color_gl' : '#2F4F4F', # Lines on the graph + 'color_graphticks' : '#CCCCCC', # Graph ticks + 'color_graphline' : '#00FF00', # graph data line color + 'color_graphlabels' : '#00FF00', # graph label color + 'color_btn' : '#141414', # button color + 'color_check_btn' : '#005500', # color of a check button when it is checked + 'color_cycle_btn' : '#005500', # color of a cycle button when it is checked + 'color_adjust_btn' : '#005500', # color of an adjustable button when it is checked + 'color_test' : '#005500', # color of a button used for test (turn off for tx) + 'color_freq' : '#208B57', # background color of frequency and s-meter + 'color_freq_txt' : '#00FF00', # text color of frequency display + 'color_entry' : '#2E2E2E', # frequency entry box + 'color_entry_txt' : '#00FF00', # text color of entry box + 'color_enable' : '#00CC00', # text color for an enabled button + 'color_disable' : '#FF0000', # text color for a disabled button + 'color_popchoice' : '#555555', # text color for button that pops up a row of buttons + 'color_bandwidth' : '#003300', # color for bandwidth display; thanks to WB4JFI + 'color_txline' : '#00CC00', # vertical line color for tx in graph + 'color_rxline' : '#00FF00', # vertical line color for rx in graph + 'color_graph_msg_fg' : '#008800', # text messages on the graph screen + 'color_graph_msg_bg' : '#1E1E1E', # background of text messages on the graph screen + 'color_onoff' : '#005500', # On/Off button + 'color_onoff_text' : '#FF0000', # On/Off text +} +############################################################################################# +# This is an amber dark color scheme designed by Michael +color_scheme_DK1MI_AM = { + 'color_bg' : '#0F0F0F', # Lower screen background + 'color_bg_txt' : '#FFBF00', # Lower screen text color + 'color_graph' : '#0F0F0F', # Graph background + 'color_config2' : '#FF8000', # Color in tab row of config screen + 'color_gl' : '#2F4F4F', # Lines on the graph + 'color_graphticks' : '#CCCCCC', # Graph ticks + 'color_graphline' : '#FFBF00', # Graph data line color + 'color_graphlabels' : '#FFBF00', # Graph label color + 'color_btn' : '#141414', # Button color + 'color_check_btn' : '#865E2E', # Color of a check button when it is checked + 'color_cycle_btn' : '#865E2E', # Color of a cycle button when it is checked + 'color_adjust_btn' : '#865E2E', # Color of an adjustable button when it is checked + 'color_test' : '#865E2E', # Color of a button used for test (turn off for tx) + 'color_freq' : '#FF9933', # Background color of frequency and s-meter + 'color_freq_txt' : '#000000', # Text color of frequency display + 'color_entry' : '#2E2E2E', # Frequency entry box + 'color_entry_txt' : '#FFBF00', # Text color of entry box + 'color_enable' : '#FFBF00', # Text color for an enabled button + 'color_disable' : '#FF0000', # Text color for a disabled button + 'color_popchoice' : '#555555', # Text color for button that pops up a row of buttons + 'color_bandwidth' : '#4B2E0F', # Color for bandwidth display + 'color_txline' : '#FFBF00', # Vertical line color for tx in graph + 'color_rxline' : '#FF8000', # Vertical line color for rx in graph + 'color_graph_msg_fg' : '#FF8000', # Text messages on the graph screen + 'color_graph_msg_bg' : '#1E1E1E', # Background of text messages on the graph screen + 'color_onoff' : '#865E2E', # On/Off button + 'color_onoff_text' : '#FF0000', # On/Off text +} - -# These are the palettes for the waterfall. The one used is named waterfallPallette, -# so to use a different one, overwrite this name in your configuration file. +# These are the palettes for the waterfall. waterfallPalette = ( ( 0, 0, 0, 0), ( 36, 85, 0, 255), @@ -2086,6 +2154,18 @@ waterfallPaletteC = ( # from David Fainitski (255, 245, 99, 3) ) +waterfallPaletteDK1MI_AM = ( # from Michael + (0, 0, 0, 0), + (32, 25, 20, 0), + (64, 58, 40, 6), + (96, 78, 50, 16), + (128, 120, 85, 29), + (160, 144, 100, 51), + (192, 195, 140, 43), + (224, 198, 150, 35), + (255, 255, 191, 0) +) + # This is the data used to draw colored lines on the frequency X axis to # indicate CW and Phone sub-bands. You can make it anything you want. # These are the colors used for sub-bands: diff --git a/setup.py b/setup.py index 9fb4c39..510ae38 100644 --- a/setup.py +++ b/setup.py @@ -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.35' +Version = '4.2.36' fp = open("__init__.py", "w") # write title string fp.write("#Quisk version %s\n" % Version) diff --git a/sound.c b/sound.c index 4977629..c2bce46 100644 --- a/sound.c +++ b/sound.c @@ -88,6 +88,7 @@ static int close_file_rec; double digital_output_level = 0.7; static double file_play_level = 1.0; static int dc_remove_bw=100; // bandwidth of DC removal filter +static int multus_cw_samples = 0; // always send CW Tx samples to the Multus radio, even in Rx static ty_sample_start pt_sample_start; static ty_sample_stop pt_sample_stop; @@ -1109,7 +1110,7 @@ int quisk_read_sound(void) // Called from sound thread if (is_cw) { // Transmit CW; use capture device for timing, not microphone cwCount += (double)retval * quisk_MicPlayback.sample_rate / quisk_sound_state.sample_rate; mic_count = 0; - if (QUISK_CWKEY_DOWN || quiskSpotLevel >= 0) { + if (QUISK_CWKEY_DOWN || quiskSpotLevel >= 0 || multus_cw_samples) { while (cwCount >= 1.0) { if (cwEnvelope < 1.0) { cwEnvelope += 1. / (quisk_MicPlayback.sample_rate * 5e-3); // 5 milliseconds @@ -1601,10 +1602,9 @@ PyObject * quisk_micplay_channels(PyObject * self, PyObject * args) // Called fr PyObject * quisk_set_sparams(PyObject * self, PyObject * args, PyObject * keywds) { /* Call with keyword arguments ONLY; change local parameters */ static char * kwlist[] = {"dc_remove_bw", "digital_output_level", "remote_control_slave", "remote_control_head", - "file_play_level", NULL} ; - - if (!PyArg_ParseTupleAndKeywords (args, keywds, "|idiid", kwlist, &dc_remove_bw, &digital_output_level, - &remote_control_slave, &remote_control_head, &file_play_level)) + "file_play_level", "multus_cw_samples", NULL} ; + if (!PyArg_ParseTupleAndKeywords (args, keywds, "|idiidi", kwlist, &dc_remove_bw, &digital_output_level, + &remote_control_slave, &remote_control_head, &file_play_level, &multus_cw_samples)) return NULL; Py_INCREF (Py_None); return Py_None;