From ffa047c2ef5a66bc5fa36df4cd0f45ed3b0f9eb5 Mon Sep 17 00:00:00 2001 From: "James C. Ahlstrom" Date: Tue, 26 Dec 2023 14:31:24 -0500 Subject: [PATCH] Quisk version 4.2.28 --- CHANGELOG.txt | 1989 ++++++++++ README.md | 21 + __init__.py | 2 + __main__.py | 8 + ac2yd/Design_2022_0531.pdf | Bin 0 -> 371367 bytes ac2yd/__init__.py | 1 + ac2yd/control_common.py | 408 ++ ac2yd/control_hermes.py | 54 + ac2yd/control_hiqsdr.py | 40 + ac2yd/control_softrock.py | 9 + ac2yd/remote.c | 748 ++++ ac2yd/remote_common.py | 439 +++ ac2yd/remote_hermes.py | 25 + ac2yd/remote_hiqsdr.py | 18 + ac2yd/remote_softrock.py | 19 + afedrinet/SOURCE.txt | 3 + afedrinet/__init__.py | 1 + afedrinet/af_comp.bat.makeit | 1 + afedrinet/afe_library | 4 + afedrinet/afe_library.mac | 5 + afedrinet/afedri.py | 219 ++ afedrinet/afedrinet_io.c | 399 ++ afedrinet/makefile | 7 + afedrinet/quisk_conf.py | 61 + afedrinet/quisk_conf_linux.py | 54 + afedrinet/quisk_conf_mac.py | 54 + afedrinet/quisk_hardware.py | 94 + afedrinet/readme.txt | 38 + afedrinet/sdr_control.py | 38 + afedrinet/setup.py | 49 + afedrinet/test.py | 16 + configure.py | 4294 +++++++++++++++++++++ defaults.html | 33 + docs.html | 1766 +++++++++ dxcluster.py | 141 + extdemod.c | 47 + filter.c | 489 +++ filter.h | 86 + filters.h | 1308 +++++++ filters.py | 770 ++++ freedv.c | 707 ++++ freedv.h | 12 + freedvpkg/README.txt | 129 + freedvpkg/__init__.py | 1 + freedvpkg/libcodec2_32.dll | Bin 0 -> 875574 bytes freedvpkg/libcodec2_32.so | Bin 0 -> 579488 bytes freedvpkg/libcodec2_64.dll | Bin 0 -> 1052823 bytes freedvpkg/libcodec2_64.so | Bin 0 -> 663848 bytes help.html | 527 +++ help_vna.html | 144 + hermes/README.txt | 65 + hermes/__init__.py | 1 + hermes/quisk_conf.py | 1 + hermes/quisk_conf2.py | 1 + hermes/quisk_hardware.py | 1221 ++++++ hermes/quisk_widgets.py | 203 + hiqsdr/__init__.py | 1 + hiqsdr/quisk_conf.py | 33 + hiqsdr/quisk_hardware.py | 426 +++ import_quisk_api.c | 83 + is_key_down.c | 240 ++ libfftw3-3.dll | Bin 0 -> 2310215 bytes libgcc_s_dw2-1.dll | Bin 0 -> 124020 bytes libusb.txt | 30 + libwdsp.dll | Bin 0 -> 1633363 bytes libwinpthread-1.dll | Bin 0 -> 63502 bytes license.txt | 129 + makefile | 18 + microphone.c | 1608 ++++++++ microphone.h | 1 + midi_handler.py | 161 + n2adr/__init__.py | 1 + n2adr/conf1.py | 13 + n2adr/conf2.py | 20 + n2adr/conf3.py | 13 + n2adr/conf3A.py | 5 + n2adr/conf4.py | 40 + n2adr/conf5.py | 15 + n2adr/conf6.py | 30 + n2adr/conf7.py | 14 + n2adr/hl2_hardware.py | 72 + n2adr/quisk_conf.py | 47 + n2adr/quisk_conf_8600.py | 202 + n2adr/quisk_hardware.py | 80 + n2adr/quisk_widgets.py | 56 + n2adr/scanner_widgets.py | 140 + n2adr/startup.py | 92 + n2adr/station_hardware.py | 1092 ++++++ n2adr/uhf_conf.py | 65 + n2adr/uhf_hardware.old | 498 +++ n2adr/uhf_hardware.py | 526 +++ n2adr/uhf_widgets.py | 145 + perseuspkg/README.txt | 10 + perseuspkg/__init__.py | 1 + perseuspkg/makefile | 7 + perseuspkg/perseus.c | 505 +++ perseuspkg/quisk_hardware.py | 189 + perseuspkg/quisk_widgets.py | 37 + perseuspkg/setup.py | 48 + portaudio.py | 85 + quisk.c | 6067 +++++++++++++++++++++++++++++ quisk.h | 494 +++ quisk.py | 6529 ++++++++++++++++++++++++++++++++ quisk_conf_defaults.py | 2389 ++++++++++++ quisk_conf_kx3.py | 37 + quisk_conf_model.py | 28 + quisk_conf_openradio.py | 122 + quisk_conf_peaberry.py | 16 + quisk_conf_sdr8600.py | 30 + quisk_conf_sdriq.py | 34 + quisk_conf_win.py | 50 + quisk_hardware_fifisdr.py | 156 + quisk_hardware_fixed.py | 24 + quisk_hardware_hamlib.py | 157 + quisk_hardware_hl2_oob.py | 63 + quisk_hardware_model.py | 157 + quisk_hardware_sdr8600.py | 71 + quisk_hardware_sdriq.py | 491 +++ quisk_utils.py | 70 + quisk_vna.py | 1423 +++++++ quisk_wdsp.c | 84 + quisk_wdsp.py | 135 + quisk_widgets.py | 1567 ++++++++ sdrmicronpkg/__init__.py | 1 + sdrmicronpkg/quisk_hardware.py | 266 ++ setup.py | 107 + soapypkg/__init__.py | 1 + soapypkg/makefile | 7 + soapypkg/quisk_hardware.py | 161 + soapypkg/setup.py | 50 + soapypkg/soapy.c | 532 +++ softrock/__init__.py | 1 + softrock/hardware_net.py | 105 + softrock/hardware_usb.py | 288 ++ softrock/widgets_tx.py | 15 + softrock_tune_vfo.py | 19 + sound.c | 1675 ++++++++ sound_alsa.c | 1272 +++++++ sound_directx.c | 707 ++++ sound_portaudio.c | 547 +++ sound_pulseaudio.c | 1236 ++++++ sound_wasapi.c | 1379 +++++++ utility.c | 323 ++ 143 files changed, 52434 insertions(+) create mode 100644 CHANGELOG.txt create mode 100644 README.md create mode 100644 __init__.py create mode 100644 __main__.py create mode 100644 ac2yd/Design_2022_0531.pdf create mode 100644 ac2yd/__init__.py create mode 100644 ac2yd/control_common.py create mode 100644 ac2yd/control_hermes.py create mode 100644 ac2yd/control_hiqsdr.py create mode 100644 ac2yd/control_softrock.py create mode 100644 ac2yd/remote.c create mode 100644 ac2yd/remote_common.py create mode 100644 ac2yd/remote_hermes.py create mode 100644 ac2yd/remote_hiqsdr.py create mode 100644 ac2yd/remote_softrock.py create mode 100644 afedrinet/SOURCE.txt create mode 100644 afedrinet/__init__.py create mode 100644 afedrinet/af_comp.bat.makeit create mode 100644 afedrinet/afe_library create mode 100644 afedrinet/afe_library.mac create mode 100644 afedrinet/afedri.py create mode 100644 afedrinet/afedrinet_io.c create mode 100644 afedrinet/makefile create mode 100644 afedrinet/quisk_conf.py create mode 100644 afedrinet/quisk_conf_linux.py create mode 100644 afedrinet/quisk_conf_mac.py create mode 100644 afedrinet/quisk_hardware.py create mode 100644 afedrinet/readme.txt create mode 100644 afedrinet/sdr_control.py create mode 100644 afedrinet/setup.py create mode 100644 afedrinet/test.py create mode 100644 configure.py create mode 100644 defaults.html create mode 100644 docs.html create mode 100644 dxcluster.py create mode 100644 extdemod.c create mode 100644 filter.c create mode 100644 filter.h create mode 100644 filters.h create mode 100644 filters.py create mode 100644 freedv.c create mode 100644 freedv.h create mode 100644 freedvpkg/README.txt create mode 100644 freedvpkg/__init__.py create mode 100644 freedvpkg/libcodec2_32.dll create mode 100644 freedvpkg/libcodec2_32.so create mode 100644 freedvpkg/libcodec2_64.dll create mode 100644 freedvpkg/libcodec2_64.so create mode 100644 help.html create mode 100644 help_vna.html create mode 100644 hermes/README.txt create mode 100644 hermes/__init__.py create mode 100644 hermes/quisk_conf.py create mode 100644 hermes/quisk_conf2.py create mode 100644 hermes/quisk_hardware.py create mode 100644 hermes/quisk_widgets.py create mode 100644 hiqsdr/__init__.py create mode 100644 hiqsdr/quisk_conf.py create mode 100644 hiqsdr/quisk_hardware.py create mode 100644 import_quisk_api.c create mode 100644 is_key_down.c create mode 100644 libfftw3-3.dll create mode 100644 libgcc_s_dw2-1.dll create mode 100644 libusb.txt create mode 100644 libwdsp.dll create mode 100644 libwinpthread-1.dll create mode 100644 license.txt create mode 100644 makefile create mode 100644 microphone.c create mode 100644 microphone.h create mode 100644 midi_handler.py create mode 100644 n2adr/__init__.py create mode 100644 n2adr/conf1.py create mode 100644 n2adr/conf2.py create mode 100644 n2adr/conf3.py create mode 100644 n2adr/conf3A.py create mode 100644 n2adr/conf4.py create mode 100644 n2adr/conf5.py create mode 100644 n2adr/conf6.py create mode 100644 n2adr/conf7.py create mode 100644 n2adr/hl2_hardware.py create mode 100644 n2adr/quisk_conf.py create mode 100644 n2adr/quisk_conf_8600.py create mode 100644 n2adr/quisk_hardware.py create mode 100644 n2adr/quisk_widgets.py create mode 100644 n2adr/scanner_widgets.py create mode 100644 n2adr/startup.py create mode 100644 n2adr/station_hardware.py create mode 100644 n2adr/uhf_conf.py create mode 100644 n2adr/uhf_hardware.old create mode 100644 n2adr/uhf_hardware.py create mode 100644 n2adr/uhf_widgets.py create mode 100644 perseuspkg/README.txt create mode 100644 perseuspkg/__init__.py create mode 100644 perseuspkg/makefile create mode 100644 perseuspkg/perseus.c create mode 100644 perseuspkg/quisk_hardware.py create mode 100644 perseuspkg/quisk_widgets.py create mode 100644 perseuspkg/setup.py create mode 100644 portaudio.py create mode 100644 quisk.c create mode 100644 quisk.h create mode 100644 quisk.py create mode 100644 quisk_conf_defaults.py create mode 100644 quisk_conf_kx3.py create mode 100644 quisk_conf_model.py create mode 100644 quisk_conf_openradio.py create mode 100644 quisk_conf_peaberry.py create mode 100644 quisk_conf_sdr8600.py create mode 100644 quisk_conf_sdriq.py create mode 100644 quisk_conf_win.py create mode 100644 quisk_hardware_fifisdr.py create mode 100644 quisk_hardware_fixed.py create mode 100644 quisk_hardware_hamlib.py create mode 100644 quisk_hardware_hl2_oob.py create mode 100644 quisk_hardware_model.py create mode 100644 quisk_hardware_sdr8600.py create mode 100644 quisk_hardware_sdriq.py create mode 100644 quisk_utils.py create mode 100644 quisk_vna.py create mode 100644 quisk_wdsp.c create mode 100644 quisk_wdsp.py create mode 100644 quisk_widgets.py create mode 100644 sdrmicronpkg/__init__.py create mode 100644 sdrmicronpkg/quisk_hardware.py create mode 100644 setup.py create mode 100644 soapypkg/__init__.py create mode 100644 soapypkg/makefile create mode 100644 soapypkg/quisk_hardware.py create mode 100644 soapypkg/setup.py create mode 100644 soapypkg/soapy.c create mode 100644 softrock/__init__.py create mode 100644 softrock/hardware_net.py create mode 100644 softrock/hardware_usb.py create mode 100644 softrock/widgets_tx.py create mode 100644 softrock_tune_vfo.py create mode 100644 sound.c create mode 100644 sound_alsa.c create mode 100644 sound_directx.c create mode 100644 sound_portaudio.c create mode 100644 sound_pulseaudio.c create mode 100644 sound_wasapi.c create mode 100644 utility.c diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100644 index 0000000..adb6fcf --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,1989 @@ +Quisk Version 4.2.28 December 2023 +=================================== +This version includes a patch from Jon, AB8WU, to build Quisk on FreeBSD. Thanks Jon! +I fixed a bug in setting the AF level in Hamlib. + +Quisk Version 4.2.27 December 2023 +=================================== +This release corrects the Hermes-Lite2 antenna tuner logic. Jim, N1ADJ, has an +antenna tuner and is helping debug. Thanks Jim! + +Quisk Version 4.2.26 December 2023 +=================================== +This release has support for the Hermes-Lite2 antenna tuner protocol. The tuner button is +on the last row of the screen at the left. Please test. + +Quisk Version 4.2.25 November 2023 +=================================== +When using the Hermes-Lite2 at a remote location over WiFi, the Tx buffer may underflow. +I added another line to the Config/Status screen to show the number of Tx buffer errors. + +Quisk Version 4.2.24 November 2023 +=================================== +You can now specify the WSJT-X option "--rig-name" on the Configure WSJT-X window. It used to be +fixed at "quisk". Please test. + +I worked around a bug in wxPython that caused some screens to be too small. I polished the code +for the RQST and ACK bits in the Hermes-Lite2 protocol. + +Quisk Version 4.2.23 September 2023 +==================================== +I fixed a bug in the HiQSDR when the clock is not a multiple of 48 kHz. I added more support for +the IO board. + +Quisk Version 4.2.22 August 2023 +================================= +This version adds more support for the Hermes Lite2 IO board. + +Quisk Version 4.2.21 July 2023 +=============================== +This version includes Windows support for Python 3.11. It also has the new IO board register numbers. + +Quisk Version 4.2.20 July 2023 +=============================== +I fixed a bug in the HiQSDR radio that caused the CW sidetone to play when not in CW mode. + +Quisk Version 4.2.19 May 2023 +============================== +I added the Flex ZZAR command to the Hamlib interface. This controls the AGC level from -20 to +120 dB. +The levels are mapped to the Quisk AGC level 0.0 to 1.0. The Flex AGC operates as a limiter but the +Quisk AGC is a compressor, so you will need to experiment to find the correct ZZAR command. Also the +Quisk AGC is never off. The off setting just changes the AGC level. So to turn AGC off send another ZZAR +command, perhaps "ZZAR+120;". + +When using the AC2YD remote feature, the serial port PTT now works correctly. + +Quisk Version 4.2.18 April 2023 +================================ +I added some small changes to the I2C messages sent to the Hermes Lite 2 IO board. + +Quisk Version 4.2.17 February 2023 +=================================== +I fixed the PTT keyboard shortcut on Linux. +The "documentation" link at the top on the Help screen now works on all platforms. + +The "Split" feature now defaults to 1000 Hz for CW, and 3000 Hz for other modes. You can now lock either +the Rx or Tx frequency. + +Quisk Version 4.2.16 January 2023 +================================== +The CAT serial port can now be a real hardware port or a virtual port. See the Config/radio/Remote screen. +Please test. + +Quisk Version 4.2.15 January 2023 +================================== +The Alsa sound driver and the Windows sound driver with the "Fast Sound" option are fast enough to produce a +useful sidetone for CW operation. I improved the PulseAudio sound driver to produce a fast sidetone too. + +Quisk Version 4.2.14 December 2022 +=================================== +The new file quisk_hardware_fifisdr.py supports the FiFi SDR hardware. Thanks to Joe, LA6GRA. +I added the rig 2 Hamlib command "U TUNER 1" to control the Spot button. +The Quisk remote feature now works with the "Small" screen format. Please test. + +Quisk Version 4.2.13 December 2022 +=================================== +Python 2 was obsolete as of January 1, 2020. And the new code by Ben, AC2YD, needs Python 3. It is troublesome to write +code that runs on both Python 2 and Python 3. So it is time to stop supporting Python 2 in Quisk. Please upgrade to +Python 3. If you have both versions on your computer use Python 3 for Quisk. + +Some have reported that the remote Quisk graph stops after a long Tx. No graph data is sent during Tx, so perhaps the +open UDP port has expired. I added keepalive graph data that is sent at one second intervals. Let's see if that fixes it. + +I added support for the HiQSDR radio to Remote Quisk. I corrected some drawing errors on the config screens. I removed +the "Help with Radios" config tab and moved the text to the documentation available from the Help button. + +Quisk Version 4.2.12 November 2022 +=================================== +I made the Hermes LNA slider control value persistent. This make more sense than having it be a configuration setting. +When using Quisk Remote, band changes from WSJT-X now correctly set the HL2 filters. I fixed a bug in the SoftRock +amplitude/phase adjustment screens. I started work on using the "small screen" version of Quisk work as a control head. +Please test, as more work may be needed. + +Quisk Version 4.2.11 November 2022 +=================================== +This is an update to the new Quisk Remote feature by Ben, AC2YD. This version reduces the audio sample rate from 48 ksps +to 8 ksps. This should help with slow networks, but is is hard to predict the effect. Please test. + +If you have dropouts at the new sample rate, try increasing the "Play latency msec" on the Config/Timing screen. Quisk +buffers sound, and the buffer size can be adjusted. + +I fixed the problem with the Split Rx/Tx feature. Please test. Ben made some additional changes for Windows networking. + +Quisk Version 4.2.10 October 2022 +================================== +I improved the Config/Config and Config/TxAudio screens and added help buttons for all items. The File Record +button no longer overwrites an existing file. It creates a new file each time it is pressed. Enter a base name +and the files will be base001.wav, base002.wav, etc. Decide on a directory for these files, perhaps the Music +directory on your computer or a special directory. Files will accumulate there, and must deleted. +The File Play names will follow the File Record names so that pressing Play after Record plays the current recording. +I had to change a lot of logic for this, and there may be bugs. So please test. + +I fixed some bugs in the new Quisk Remote feature by Ben, AC2YD. The Spot slider is now initialized. I fixed the +bugs in the Split feature. Please test, and if there are any remaining bugs, please report them. I will add +a feature to reduce the sample rate from 48 ksps to 8 or 12 ksps as the next step. I didn't add it now because +it may cause problems. Maybe it will increase latency, or the added processing will be too much for a Raspberry Pi +used as a remote. + +Quisk Version 4.2.9 October 2022 +================================= +This is an update to the new Quisk Remote feature by Ben, AC2YD. I fixed the problem with the favorites screen +and the station buttons below the X-axis. I fixed the "Failure in OnFreedvMenu" bug. + +Quisk Version 4.2.8 September 2022 +=================================== +This is an update to the new Quisk Remote feature by Ben, AC2YD. + +Midi now works with the control head. For HermesLite2, the RfLna is correctly initialized. +I changed the ports to 4585:TCP control; 4586: graph data; 4587:radio sound and mic. This should +work correctly with NAT. + +The main documentation page docs.html which is available from the Help button now describes the remote feature +in more detail. + +Quisk Version 4.2.7 September 2022 +=================================== +This is an update to the new Quisk Remote feature by Ben, AC2YD. + +These features are due to Jaroslav, OK2JRQ: The clip indicator is now sent from the remote to the control head. +The remote radio IP address can now be a host name or an IP address. TCP and UDP ports are now distinguished +in the documentation. The control head sends initial graph data to the remote in order to establish a path through NAT. +The remote radio can now be operated as a normal radio unless there is an active connection to the control head. + +The Favorites screen now works. I fixed all the bugs I know of except the segmentation fault on RPi4 raspian +when closing Quisk. But I need help testing everything. Please test and let me know of any new bugs or bugs +I missed. + +Quisk Version 4.2.6 September 2022 +=================================== +This is an update to the new Quisk Remote feature by Ben, AC2YD. The frequency is now correctly set when +changing bands. Initial menu items are now set. Frequencies stay synchronized when changing from USB to CWU. +I changed the graph data from 8 bit to 16 bit. + +I improved the config screen visibility when using "Dark" mode. + +Quisk Version 4.2.5 September 2022 +=================================== +This is an update to the new Quisk Remote feature by Ben, AC2YD. There is no longer a need to make the screen +sizes of the control head and remote radio the same. Any size should be OK. If you changed your screen sizes +to match, please go back to running with different sizes. Then verify that the graph on the control head is correct. + +I fixed all the bugs I know about, but this is still beta software. Please test. + +Quisk Version 4.2.4 August 2022 +================================ +This version adds the missing ac2yd directory. It is present in the Quisk source distribution but +missing in the "pip" installs. + +Quisk Version 4.2.3 August 2022 +================================ +This is an experimental release to test the new Quisk radio remote control software written by +Ben Cahill, AC2YD. This allows Quisk running on a local PC to control a remote radio. The remote radio +is connected to a PC also running Quisk. Ben has tested this on SoftRock CW operation. The current test +version supports SoftRock and HermesLite2. + +Quisk Version 4.2.2 July 2022 +============================== +I am still working on the Hambib Rig 2 interface. I changed the response to chk_vfo to agree with Hamlib 4.4. +I did some work on FM demodulation. Please test. + +Quisk Version 4.2.1 June 2022 +============================== +I improved the Hamlib Rig 2 interface. Please continue to report any problems. To test, start Quisk. Then +in a separate window start "telnet localhost 4532". Send commands to Quisk with telnet and view the response. + +I made some improvements to the station line shown below the X axis on the graph. Quisk now has some support +for the Hermes Lite IO Board. + +Quisk Version 4.2.0 May 2022 +============================== +I added a patch from Jaroslav, OK2JRQ, to improve compatibility with Hamlib. I removed the Python function +set_transmit_mode() because it seems to be unused. I fixed a lockup problem that happens when the Hermes Lite2 key +is pressed during SSB operation. + +Quisk Version 4.1.96 May 2022 +============================== +These changes were suggested by Neil, G4BRK. I fixed the bug that touching the CW key in SSB mode locked +up Quisk in transmit mode. All the Record/Play file names on the Config/Config screen were already saved when +starting Quisk. The state of the Play device is now saved too. I didn't save the Record check boxes +because restarting Quisk may overwrite an existing file. When changing between CW and SSB, the transmit and receive +frequencies are moved by the CW tone frequency. That way if you tune in a CW signal in SSB, and then change to CW +the CW tone is unchanged, and you are ready to transmit. + +I changed the return from Hamlib GetVfo() from "VFO" to "Main" to fix a compatibility problem. + +Quisk Version 4.1.95 May 2022 +============================== +This is a minor release to fix a Midi issue. There is now a separate item on the radio Keys screen to +control the Midi PTT toggle. I started to add support for my IO Board. But since the IO board is not released, +the code does nothing. + +Quisk Version 4.1.94 April 2022 +================================ +This is a minor release to fix some Midi issues. The Config/Keys "PTT key toggle" item now controls the +Midi PTT control as well as the hot key PTT. That is, it controls whether the Midi button must be held down +for PTT, or whether one press turns PTT on and the next press turns it off. + +Quisk Version 4.1.93 December 2021 +=================================== +I fixed a bug in the FreeDV button. It now works like other mode buttons. + +Quisk can now use the WDSP library to add the additional functions NR2 (noise reduction) and SNB (noise blanker). +The WDSP library is optional, and you don't have to use it. The library ships with Quisk on Windows. For Linux +it is used by many other SDR software, and you may already have it. If Quisk can find WDSP, the NR2 button will +be active. If the NR2 button is grayed out, you can install WDSP on Linux as follows: + + git clone https://github.com/g0orx/wdsp.git + cd wdsp + make + sudo make install + +This is an experimental feature, and I can use some feedback on its use. + +Quisk Version 4.1.92 October 2021 +================================== +I changed the power meter on the Hermes Lite 2 from an average value to PEP. The FreeDV sideband and mode +are now restored on startup. + +I fixed a bug that results in errors on tx_level when adding a new radio. I fixed a bug in the new +PTT key logic. The Midi control now updates the Rf gain display. + +Quisk Version 4.1.91 October 2021 +================================== +I changed the Midi control of the Rit frequency so the center includes the CW tuning offset. I tested this +with the DJControl Compact that Ben, AC2YD, lent me. + +The PTT accelerator key was failing for some users. It turns out that wx.GetKeyState() is not available +on all systems. So I had to program around the problem. If you have errors with GetKeyState(), +change "PTT key toggle" to True and "PTT key if hidden" to False. This is the only combination that +works with a bad GetKeyState(). While I was at it, I made changing the keys immediate (no need to restart). + +I made a change to fix the problem with "digital_rx2_name" not being found. I don't understand what +causes this, but the new code is more robust. + +I added a button to set the WSJT-X path and config option. It is on the Config/Config screen. + +Quisk Version 4.1.90 October 2021 +================================== +I changed the Midi feature to accommodate more Midi controllers. I removed the Midi message from +the Config/Status screen and moved it to the Midi screen (Config/radio/Keys). You can directly +assign the Midi message to a Quisk control, and the channel is now recognized. I added the RfLna control. + +I added a log file to Quisk. Log messages are sent to the file quisk_logfile.txt which is located with other +Quisk user files. The item "Debug level" on the Config/radio/Options screen controls the output. +A debug screen is shown when "Debug level" is greater than zero. + +The serial port PTT feature was not working. I fixed it and made some other improvements. Please test. + +Quisk Version 4.1.89 October 2021 +================================== +I fixed the problem of Quisk crashing when the Config button is pressed. This only happens on the +Raspberry Pi. I needed to work around a problem with wxPython on the Pi. + +Ben, AC2YD, lent me the Midi controller DJControl Compact, and I was then able to +add some improvements to the Midi feature. Midi now works for all buttons on the Small Screen layout. +I added control messages for the Volume, Ys and all other Quisk sliders. I added "Tune" to the controls. +Midi has two kinds of controllers. A "Knob" rotates a whole turn left and right and sends its level as +a number 0 to 127. A "Jog Wheel" rotates around and around, and sends up and down messages every few degrees. +I added both types of control. + +Quisk Version 4.1.88 October 2021 +================================== +This version makes digital programs like WSJT-X easier to use. There is a button on the Config/Config +screen to start WSJT-X. The default is "Never". Select "On startup" to start WSJT-X when Quisk starts. +Select "Now" to start it now. When Quisk starts WSJT-X, it uses "--rig-name quisk" so that the settings +you make are saved separately. + +On Linux, Quisk can set up virtual sound card names, and I changed these names on the drop down lists. +For Digital Tx0 Input, choose "Use name QuiskDigitalInput" and then find this name in the WSJT-X sound output menu. +For Digital Rx0 Output choose "Use name QuiskDigitalOutput.monitor" and then find this name in the WSJT-X sound input menu. +See the new Quisk documentation http://james.ahlstrom.name/quisk/docs.html#Digital for details. + +Quisk Version 4.1.87 September 2021 +==================================== +The documentation now has a better description of how to make a custom hardware file. + +I made some changes to the new Midi feature. The Config/Status screen now shows all three bytes of the Midi +message. I moved the MidiHandler logic to its own file midi_handler.py. If you copy this to your config file, +your MidiHandler will be used instead. This enables you to completely control Quisk with Midi. +For example, you can support Control Change messages. Take a look at the comments at the top of midi_handler.py. +If needed I can add logic to the Keys screen to extend Midi for those not comfortable with Python. + +Quisk Version 4.1.86 September 2021 +==================================== +This is a bug fix release to fix a problem with the new Midi logic and Keys screen. Please test. + +Quisk Version 4.1.85 September 2021 +==================================== +I made some visual improvements to the configuration screens. This should make them easier to use. + +You can now assign Midi notes to Quisk buttons. For example, you can assign note 57 to the PTT button, and +assign 58 to the Mute button. See the Config/radio/Keys screen. These assignments are global; that is, common +to all radios. I also moved the existing "Midi CW key" from the CW screen to the new Keys screen. This feature +is not complete for the "Small Screen" format because some buttons need two presses. Let me know if this is a problem. + +You can see the Midi note numbers on the Config/Status screen as they are received. This is useful for discovering +which note is sent by each Midi key. + +Quisk has a new hardware file quisk_hardware_hl2_oob.py. You can specify this name as your hardware file on the +Config/radio/Hardware screen. It is for use with the Hermes Lite 2, and it disables the power amp when the transmit +frequency including sidebands is out of band. Be sure to set accurate band edges on the Config/radio/Bands screen. +You might want to set a new band plan on the Config/Config screen. + +Quisk Version 4.1.84 August 2021 +================================= +Chuck Ritola submitted a patch to correct the phase adjustments in conjunction with channel delay. Thanks Chuck! + +I added the Transverter Offset from the Bands screen to SoapySDR. + +Quisk shows a color bar on the frequency X-axis to show the band plan. That is, the CW, phone and data segments +of the band. Since the band plan varies with country, there is a new feature to change it on the config screen. +Press the Config button and then the Config screen. Look for the "Band plan" button. It will bring up a screen +to set a list of frequencies and the mode (CW, phone) that starts at each frequency. The screen will start at +the band plan you are currently using. The band plan is the same for all your radios. You can mark any frequency +you want, even frequencies outside the ham bands. + +Quisk Version 4.1.83 June 2021 +=============================== +This version contains patches by Mooneer, K6AQ, for FreeDV mode 700E. Thanks Mooneer! + +Quisk generates its own CW waveform when keyed by the serial port or MIDI. Quisk delays this CW waveform +so that when changing from Rx to Tx there is time for relays to switch and power amps to turn on. +The CW key timing is preserved. This delay was 15 milliseconds, but is now adjustable. +It is controlled by the "Start CW delay msec" field on the Timing screen. Note that this does not work +if the key is connected to the hardware and the hardware generates the CW itself. + +When switching from Rx to Tx in all modes except CW, Quisk zeros the RF output for a few milliseconds +to allow time for relays to switch, power amps to turn on and filters to fill with samples. This time was 100 +milliseconds, but is now adjustable. It is controlled by the "Start SSB delay msec" field on the Timing screen. + +When Quisk is remote and controlled by another program it is possible that Quisk will be in the transmit state +when the link is broken. To prevent Quisk from transmitting forever there is now a timeout "Max Tx seconds" +on the Timing screen. This should normally be set to zero to disable the timeout. + +Quisk Version 4.1.82 May 2021 +============================== +Quisk now works with 64-bit Python 3.9. It is OK to continue using 64-bit Python 3.8, but please upgrade Python +from earlier versions. + +I changed PortAudio and added addition log messages. This is important for Apple Mac. + +Quisk Version 4.1.81 April 2021 +================================ +Quisk on Windows now handles devices with 3-byte samples, and works in both Exclusive and Shared mode. +I tried to catch up on all bug fixes. If I missed any please re-post. + +Quisk Version 4.1.80 February 2021 +=================================== +This is a bug fix release. I fixed a problem with sound on Windows when "Fast sound" is False. + +Quisk Version 4.1.79 February 2021 +=================================== +This is a bug fix release. I tried again to fix "Failure to convert device name" on Windows. I fixed the +5 second freeze in CW mode for SoftRock hardware. + +Quisk Version 4.1.78 February 2021 +=================================== +Hamlib Rig2 split VFO now controls the frequency of the first added receiver. This is used for working satellites. +Use the regular Quisk window for the uplink, and the extra receiver for the downlink. Control both frequencies +with Hamlib. The Hamlib "F" command controls Rx, and the "I" command controls Tx. + +I added code by Ben Cahill, AC2YD, to recover from a fault in the playback thread for "fast sound" on Windows. +I fixed a bug in the HermesLite 2 Small Screen layout. I added address 0x09 bit 17 for the HermesLite 2 radio. + +I may have fixed the problem "Failure to convert device name" on Windows. If it is not fixed, the new error +message should be more informative. + +Quisk Version 4.1.77 January 2021 +================================== +I added further changes to support the YU1LM designs and the Genesis 3020 QRP rigs. When using the ZZBS Hamlib +command, Quisk now remembers the last frequency and mode. I added back some softrock files that were removed. + +Quisk Version 4.1.76 January 2021 +================================== +If there are errors when Quisk starts on Windows, the window showing the error messages now stays open +so you can read them. It used to close too fast. + +I improved sound buffer levels so they stay closer to 50%. Ben Cahill, AC2YD, contributed code and testing +support. I corrected Quisk C code so no warning messages are produced by the latest compiler. + +I fixed the problem with Hermes write queue timeouts on startup. But these will still appear if the Hermes +is not running. + +I added back the RTS signal to the Linux serial port CW keying logic. This signal goes high when DSR goes high. +When DSR goes low, RTS goes low after a 1.5 second delay. + +Dr. Karsten Schmidt contributed additional logic to the Hamlib serial port. Thanks Karsten! +I added two new Hamlib commands: ZZAG and ZZBS. + +Quisk Version 4.1.75 December 2020 +=================================== +This version includes improvements to the Windows sound code contributed by Ben Cahill, AC2YD, and a fix +to the Macintosh contributed by Christoph, DL1YCF. Thanks! + +The pip install method on Linux now builds the Afedri and Soapy modules properly. Please test. + +Quisk Version 4.1.74 December 2020 +=================================== +I added some missing controls to the Hermes-Lite2 Hardware configuration screen. I removed the Alex filter +band screen and implemented the same logic in the regular Bands screen. If you need the Alex screen, +please complain. + +Quisk now ships with Linux binary files for Python3, not Python2. If you have Python2, run "make" +to make a Python2 version. You could also start using Python3 instead. + +Eric, WW4ET, Davide Gerhard, and Christoph, DL1YCF, provided help to make Quisk run on a Mac. Thanks! +Christoph provided code. Thanks Christoph! The Mac changes may not be complete. Please test. + +SdrIQ support was changed. Quisk now uses the hardware file quisk_hardware_sdriq.py instead of sdriqpkg. +The directory sdriqpkg and those binary files are now obsolete. The new code is 100% Python. + +Quisk Version 4.1.73 November 2020 +=================================== +I rewrote the logic for SoftRock I/Q amplitude and phase corrections. The new logic will enable much better +image suppression because it can correct I/Q based on both the VFO and the tuning offset. If you do nothing, +there is no change in the corrections. To use the new logic, enter new correction data. Use the Help button +on the Config/radio/Config correction screens. Corrections are in the file quisk_init.json, and you should +make a copy of this file to save your current corrections. + +I added a missing version.h file to SoapySDR. It is not clear that this fixes the version check for the +change in the Soapy API. Please test. + +When using the Split button, Quisk now leaves the Rx frequency unchanged and splits the Tx frequency. + +I added a new band "Aux1" that can be checked on the Config/radio/Bands screen. It can be used as a special +band for a panadapter or when Quisk is used as a 10.7 MHz IF. + +Quisk Version 4.1.72 September 2020 +==================================== +For SoftRock hardware, the Tx power can now be set for each band. See the Config/radio/Bands screen. I also +added the Tx level and Digital Tx Level sliders to the Config/Config screen, and the Digital Tx power % +to the Config/radio/Hardware screen. + +The Y-scale and Y-zero settings are now saved for the graph at the top of the Waterfall screen. These settings +are independent of the settings for the Graph display. + +I fixed a problem with the Hermes Lite 2 SWR display. I fixed a problem with SoftRock phase corrections. + +Quisk Version 4.1.71 September 2020 +==================================== +The last band and frequency etc. are saved in the file .quisk_init.pkl, but this binary file can not be +easily read. I am now writing initialization in the new file quisk_init.json, a text file. The location +of this and the other Quisk user files is now shown on the Config/Status screen. + +I fixed a bug in the new CW snap-to-peak feature. I added "PTT hang time" and "Tx buffer msec" to the +Hermes Lite 2 hardware screen. + +The Graph button can be pressed repeatedly to select display averaging. The Waterfall button can now be +pressed repeatedly to select averaging for the graph at the top. Remember that the Ys and Yz sliders on the +Waterfall screen adjust the waterfall colors. To adjust the graph at the top, hold down the Shift key. + +Quisk Version 4.1.70 August 2020 +================================= +I speeded up MIDI CW keying on Linux. + +There used to be two ways to set the level of Tx audio sent to SoftRock hardware, a slider or the +"Tx audio level" on the Config/radio/Hardware screen. I removed the slider. Please set the level on +the Hardware screen. I removed the 70% limitation and you can now set any level you want. + +For CW modes, if you click a peak within the filter bandwidth, the tuning frequency will snap to +the peak. This is an aid to tuning in CW signals. You can still adjust the frequency with the mouse +wheel or by dragging. I also made frequency rounding work with the Rx frequency in split mode. + +Quisk Version 4.1.69 August 2020 +================================= +Quisk can now use a MIDI device for CW keying. The setup options are on the Config/radio/Timing and CW +screen. Just enter the MIDI name and note number and restart. Performance is good on Windows but poor +on Linux. See my paper http://james.ahlstrom.name/serialports/index.html for measurements. +If you use Linux, try the aseqdump command for testing. + +I fixed a bug in the small screen display. + +When using "Debug sound" on Windows, I was surprised to find that clicking the console (terminal) +window froze the sound. I had to move all debug messages to an extra window and avoid writing to +the console. This is the oddest Windows "feature" I have seen. + +I did some more work on Windows "fast sound" stability, and it runs for hours at my QTH. There +may still be problems on other hardware, so please test. + +Quisk Version 4.1.68 August 2020 +================================= +I fixed a bug in the S-meter code for very small signals of less than 1E-16. I fixed a bug +in the config screen colors when Quisk can not find the correct color scheme of the system. + +I changed the Windows "fast sound" system again. This should offer better performance +and lower CPU usage. Please test by setting "Debug sound" to 1 on the Config/radio/Options +screen. + +Quisk Version 4.1.67 July 2020 +=============================== +I improved the Windows CW operation for SoftRock. Windows sound should now be stable when +using "fast sound". Please test. + +I fixed the colors on the config screens when using the "Adwaita dark" theme. The hardware +CW/PTT input now works on voice modes for HiQSDR. + +Quisk Version 4.1.66 July 2020 +=============================== +This version changes the Linux sound system. Soundio is no longer used. Instead I rewrote +the old logic and added fast sound to it. The fast sound is only used for the sidetone. +There is no longer a "fast sound" option on the config screens for Linux. Please test. +The Windows sound system was not changed. I will work on that next. + +Thanks to Dave Roberts I corrected the Soapy module for a recent API change. Thanks Dave! +Thanks to Jaroslav, OK2JRQ, I fixed two bugs in the Python2 version. Thanks Jaroslav! +I added back the waterfall_scroll_mode item from the config file. + +Quisk Version 4.1.65 July 2020 +=============================== +This version should correct the problems with opening soundcards. To open a Delta 44 use the +name "alsa:plughw:CARD=M44,Dev=0". You can use any Alsa name that libsoundio recognizes. There +are other improvements to the sound system to increase stability and reduce CPU usage. + +Quisk Version 4.1.64 July 2020 +=============================== +The favorites button now jumps to the correct screen. The Pulse device "default" now works. +This version should open "fast sound" devices that failed before. Look on the Config/Status +screen to see if all devices open successfully. If there are still problems, the new debug +messages should add more information. Turn on "Debug sound". Then start Quisk with and +without "fast sound". Save the startup messages. + +Quisk Version 4.1.63 June 2020 +=============================== +This version fixes an incompatibility with Python2 from version 4.1.61. I plan to support Python2 +indefinitely, but be advised that Python2 is obsolete. It would be best to upgrade to a 64-bit +version of Python3. + +Dave Roberts, G8KBB, provided code to add new FreeDV modes. But the newest 2020 mode requires an +additional shared library that Quisk does not provide. Quisk provides a codec2 but multiple +dependent libraries should be installed in the correct system directories. So to use 2020 +you need to install a new codec2 and all its dependencies. Then you can use 2020 with Quisk. + +Quisk Version 4.1.62 June 2020 +=============================== +This version fixes problems with fast sound on Windows. If you are not running Windows there is no +need to update. + +Quisk Version 4.1.61 June 2020 +=============================== +This version fixes a problem with the latest version of wxPython on Windows. Quisk works with all prior +versions of wxPython, but there was a problem with the newest 4.1.0. Buttons did not work properly and +stayed pressed. I think this is a problem with wxPython, but I found a workaround by re-writing all the +waterfall code in C. I do not know if this problem occurs on Linux because my Ubuntu 18 has wxPython +version 4.0.1. But now Quisk should work with any recent wxPython. + +Quisk Version 4.1.60 June 2020 +=============================== +This is a bug fix release. I fixed a problem with the file record and play buttons. +I fixed a problem with SoftRock CW operation. I make some major changes to softrock/hardware_usb.py. +If you use this file, SoftRock CW should work. If you are using a different hardware file, you should +check the changes and see if you need to modify your file. + +Quisk Version 4.1.59 June 2020 +=============================== +This is a bug fix release to correct some installation problems. + +The most recent wxPython version 4.1.0 is not compatible with Quisk on Windows. The buttons do not seem +to show the correct up/down state even though they work. For Windows, please install the last version: + +pip install wxPython==4.0.7.post2 + +For Linux, if you plan to use the new fast sound for sidetone, install libsoundio-dev before installing Quisk: + +sudo apt-get install libsoundio-dev + +Quisk Version 4.1.58 June 2020 +=============================== +This version includes a new faster sound system that enables fast CW sidetone and remote CW operation for +the Hermes-Lite2. All the old sound systems are still included, and you can turn the new sound on and off +from the Config/Radio/Timing_and_CW screen. New sound is default off. To use new sound on Linux, +install the libsoundio-dev package with "sudo apt-get install libsoundio-dev" and run "make" to rebuild Quisk. +New sound runs on Windows, but interferes with the screen update. Sometimes buttons appear to stay pressed although +they work fine. Maybe someone can offer a solution. + +When using the new sound, adjust your play latency and data poll parameters for best operation. Start with 150 +milliseconds play latency and 10000 microseconds data poll. + +I fixed a bug with the external demodulation button. I moved the sound latency timing parameter +from the sound screen to the timing screen. I removed the top level "Config/Sound" screen because it +was not useful and conflicted with the radio sound screen. The "quisk" command in Windows should now +start Quisk. + +Andrea, IW0HDV, provided a patch for the Perseus hardware. Thanks Andrea! +Eoin Mcloughlin, EI7HSB, provided a patch to speed startup time. Thanks Eoin! +Dave Roberts, G8KBB, provided code to add new FreeDV modes. Thanks Dave! +For SoftRock, Quisk will now try the USB backend libusb0 if libusb1 fails. Thanks to Ben Cahill! + +I added the "-r" or "--radio" command line option to choose the startup radio. To get a list of +command line options, use "python quisk.py --help". + +Quisk will now save and restore the various record and playback file names. You can now right-click +the temporary playback button to save the sound to a file. + +For Hamlib I added additional control items to Quisk's rig2 protocol. The mode PKTUSB sent by WSJT-X is now +recognized and interpreted as mode DGT-U. I also added PKTLSB and PKTFM. I added the level AGC to +control the AGC button, and the level AF for the audio volume. This work makes it easier to add additional +Hamlib controls in the future. + +There are a lot of changes in this release. Please report bugs to the N2ADR-SDR group at https://groups.io/g/n2adr-sdr. +A detailed description of the bug would be good. A patch would be even better. If you have problems with this release +remember that you can install the prior version: + +To install the prior version on Windows: pip install quisk==4.1.57 +To install the prior version on Linux: sudo -H pip install quisk==4.1.57 + +Quisk Version 4.1.57 May 2020 +============================== +This is a bug fix release. I fixed a bug in the small screen version. I fixed an incompatibility +with the latest wxPython version 4.1.0. I added a patch from Dave Roberts to close FreeDV properly. +Thanks Dave! + +Quisk Version 4.1.56 April 2020 +================================ +For Linux, if the file /usr/include/portaudio.h is absent, portaudio will not be included in Quisk. +This change makes portaudio optional. The change was needed because portaudio breaks wine. Quisk now +restores the correct frequency for the 60 meter band. There were some other bug fixes. + +Quisk Version 4.1.55 April 2020 +================================ +The new Sdr Micron radio was missing from the Windows version of Quisk. There are reports that the item +Hermes_BandDictTx is damaged in the quisk_settings.json file. Quisk will now fix this itself. The item is +the "Tx IO Bus" on the Hermes Bands configuration screen. + +Quisk Version 4.1.54 March 2020 +================================ +There are now two new radios available in Quisk. David Fainitski contributed code for the Sdr Micron, +and Andrea Montefusco IW0HDV contributed code for the Perseus SDR. The radios should appear on the +list of supported radios in Config/Radios screen. Check the Config/radio/Hardware screen to see if you +need any more options. Please test these radios to make sure everything is working. + +I added a general way to add new radios to the configuration screens. See docs.html under Custom Hardware. + +Quisk Version 4.1.53 March 2020 +================================ +I changed the Afedri radio module to be compatible with Python3. The graph Zoom feature now is available +on the Bandscope screen. Vladimyr Burdeiny provided a patch to make dx cluster work with Python 3. + +I extended the range of the Waterfall color controls Ys and Yz to accommodate hardware with a higher +noise floor such as PlutoSDR. You will have to readjust your waterfall colors. + +I added a hardware setting for the LNA gain during transmit for the HL2. I made some changes to the CW +and PTT hardware interface for the HL2. These work great with the HL2, but test the new code if you have +different Hermes hardware such as Red Pitaya. + +Quisk Version 4.1.52 December 2019 +=================================== +I added an On/Off button to Quisk. When Quisk starts the button is "On". When you turn it to "Off", +Quisk shuts down the sound system and closes the hardware. When you turn it "On" again, Quisk starts +with any new setting made from the Config screens. This provides a way to start Quisk with changed settings, +and to re-start your hardware after a power down. + +Martin Schaller found and fixed a bug in the Alsa sound system that produced sporadic crashes. Thanks Martin! +Max, G7UOZ, found and fixed a bug in the Python3 SoftRock hardware_net.py code. Thanks Max! +This Quisk includes a more recent FreeDV library in both 32-bit and 64-bit versions. The Windows +SoapySDR interface still requires 64-bit Quisk. + +The Config/Status screen now shows the dB level of each open sound device. The maximum level is zero dB. +This is meant as an aid to configuring the sound devices. + +Quisk Version 4.1.51 November 2019 +=================================== +I fixed a bug in the "small screen" code when using the new wx version 4. Thanks to Martin Schaller for +pointing out that underflows from the Soapy sample source should not count as errors. + +This version fixes the Linux installation errors "ImportError: No module named _quisk". It also provides +a way to run either Python2 or Python3 quisk when both are installed. To do that use "python2 -m quisk" +or "python3 -m quisk". + +The new installation instructions are here: http://james.ahlstrom.name/quisk/docs.html#Installation + +Quisk Version 4.1.50 November 2019 +=================================== +Thanks to Martin Schaller for fixing a bug in the SoftRock Tx sound level. I fixed a bug in the HermesLite +gateware program button traceback. + +This Quisk provides pre-built binaries for the Windows Python3 versions. There are now four Windows versions: +Python2 and Python3, each for either 32 or 64-bit Python. If you have a recent Python3, just run your Python3 +and use pip to install Quisk. Pip will detect the version of Python you are using, and will install the +correct version of Quisk. If you want to try Python3, install the latest 64-bit version. +The installation instructions are here: http://james.ahlstrom.name/quisk/docs.html#Installation + +Quisk Version 4.1.49 November 2019 +=================================== +Quisk can now program the Hermes Gateware (FPGA flash) over Ethernet. See the button on the radio Hardware screen. +I fixed some more Python 3 bugs. + +Quisk Version 4.1.48 November 2019 +=================================== +I fixed some more problems with the Python3 version of Quisk. I added support for reading and writing the +EEPROM in the HermesLite. Reading the EEPROM requires code version 68 or newer. The EEPROM settings are on +the radio Hardware screen. + +Quisk Version 4.1.47 October 2019 +================================== +This is a bug fix release. I fixed a problem with the Python 3 version of Quisk saving program state. I +fixed a sound device name problem on Windows. I added some code provided by Steve to support the HermesLite. + +Quisk Version 4.1.46 October 2019 +================================== +When using the bandscope screen the band buttons no longer reset the frequency and mode. Try it. +Thanks to Ed, GM3SBC, for changes to the hot key PTT logic. The hot key now works better, and is disabled +when using the config screens. I changed the FM repeater offset code to increase the level of the CTCSS tone. + +I made further progress on Python3 and fixed some resultant problems on Python2. The objective +is to have a Quisk that runs the same on Python3 as Python2 so you can use either Python. Linux users +can test the Python 3 version by entering "make quisk3" followed by "python3 quisk.py". + +Quisk Version 4.1.45 September 2019 +==================================== +I added a config screen hardware option "Hermes known IP". If you know the IP address of the Hermes +hardware, enter it here. Otherwise Quisk will search for the hardware using the usual broadcast method. + +The Python Rx samples interface can now accept bandscope data, that is, raw samples from the ADC. See +the file quisk_hardware_model.py for documentation. I fixed a bug in the bandscope that added noise. + +Quisk Version 4.1.44 September 2019 +==================================== +I made some improvements to the Python Rx sample interface. See quisk_hardware_model.py for the +new interface. I removed the duplicate decimation rates from the SDR-IQ hardware screen. +There are a number of changes to make Quisk run on Python 3, although this is not finished yet. + +Quisk Version 4.1.43 August 2019 +================================= +This release fixes a bug that resulted in calling the hardware HeartBeat() method too frequently. +This caused flickering of the HermesLite temperature reading. + +Quisk Version 4.1.42 August 2019 +================================= +I improved the SoftRock amplitude/phase correct screen. I fixed a bug in the Hamlib extended commands +thanks to James, KE4MIQ. + +I added an interface to provide Rx samples to Quisk directly from the Python hardware file. Many new SDR +hardware devices return samples by Ethernet, USB or a serial port. Since Python has these three interfaces, +it may be possible to add a new hardware device to Quisk by using Python alone. This is much easier than +adding C code to Quisk. This work was inspired by David, N7DDC, an avid SDR hardware designer. + +I am using the new interface to support the SDR-IQ by RfSpace. Change your hardware file to +quisk_hardware_sdriq.py instead of the sdriqpkg directory to use the new logic. The sdriqpkg +directory will be removed in a future release. + +Quisk Version 4.1.41 June 2019 +=============================== +I added Steve's code for the Hermes Lite 2 that moves the harmonics of the switching power supplies +out of the amateur bands. Thanks Steve! I renamed sound devices to have consistent names on all screens. +Stephen Hurd contributed changes to the SDR-IQ module and the Quisk widgets module. Thanks Stephen! +I made the configuration screens for the radios more efficient. I may have fixed the last of the bugs. +Please test. + +Quisk Version 4.1.40 June 2019 +=============================== +This is a bug fix release for assorted problems. The filter bandwidth is now saved for program start. +I fixed a buffer overflow in the DirectX code. I added "Digital output level" to the Config/Sound screen. + +I was not able to duplicate a burst of Tx noise when switching to digital modes. But I added code +to fix it anyway. Please test. + +I was not able to duplicate the problem that the Spot output frequency is correct, but the audio is +always transmitted at the center frequency instead of the tuning frequency. Please re-test with this +release, and if it is not fixed I will try again. + +Quisk Version 4.1.39 May 2019 +============================== +I fixed a bug in the VNA program quisk_vna.py. The Windows version of SoapySDR is compiled +against SoapySDR version 0.8.0. + +Quisk Version 4.1.38 April 2019 +================================ +The bandwidth of the DC removal filter on the Config/radio/Options screen now applies to all sample +sources, not just sound cards. This is useful to set the bandwidth for SoapySDR devices. For +Hermes Lite, set the bandwidth to zero or one. + +I added Rx and Tx sample rate and bandwidth to the SoapySDR hardware screen. Any value can be entered, +not just the values from a list. See the latest SoapySDR news at http://james.ahlstrom.name/quisk/soapy.html. + +Quisk Version 4.1.37 April 2019 +================================ +I added another broadcast address to the Hermes code to fix a network problem. I added an option to +the radio Options config screen to reverse the sideband on Tx. This is needed for satellite operation. +I added the 13, 9, 5 and 3cm bands to the list. I added the additional samples rates 50, 100, 250, +500 and 1250 ksps to accommodate the RedPitaya. The frequency entry box now clears its entry with +each use. You can now specify the bandwidth of the SoftRock DC removal filter on the radio Options screen. + +I added some of the SoapySDR transmit parameters on the radio screen. But I am having trouble making +transmit actually work. So transmit is disabled for now. + +Quisk Version 4.1.36 March 2019 +================================ +I added a patch by Franco Spinelli, IW2DHW, to add chk_vfo to the Hamlib control for WSJT-X. +There are further improvements to the SoapySDR radios. I added three Rx gain setting methods +and automatic DC correction. You will need to re-read the SoapySDR device. Just use the +"Change.." button and leave the device name unchanged. This is necessary to read additional +device parameters from the hardware. + +Quisk Version 4.1.35 February 2019 +=================================== +I added the Tx Level to the bands config screen for the Red Pitaya. Now you can set the power level +for each band. I changed the Hermes Lite networking so it works with Virtual Box and multiple +network interfaces. Please test. + +I am starting to add SoapySDR support to Quisk. See http://james.ahlstrom.name/quisk/soapy.html. + +Quisk Version 4.1.34 January 2019 +================================== +This fixes a bug in version 4.1.33 that caused a traceback for b_test1 when using the Small Screen +layout. I also added support for Afedri hardware. Just use the Config/Radios screen and add +a radio of type Afedri. + +Quisk Version 4.1.33 January 2019 +================================== +I improved the output level control for digital modes. This removes the lag in attaining 100% +level. For the first transmit, Quisk does not know what level to expect from the external digital +source, so there may still be a small delay. + +I added a squelch feature that works for SSB and FM. Just push the "Squelch" button. The SSB level +automatically adjusts for band noise, and the default slider value of 0.200 should be close to +what you want. + +Quisk Version 4.1.32 January 2019 +================================== +I corrected some button colors and colors on the config screens. The green Rx line is now centered +in the filter for Split mode. + +Quisk used to leave some headroom in the 16-bit samples it sends to the hardware. The maximum level +used to be about 70%. I changed the maximum level to 100%. So the PEP output for all modes will be +the same level as 100% Spot output. But remember that the PEP level is 100%, not the average level. +An average reading power meter will show a level much below PEP for SSB and digital modes. + +There is a new tuning method available. If the Shift key is held down when you left-click the graph, +the Rx filter is centered at that frequency. Normally the Tx frequency is set at the frequency. +This is useful when using narrow digital filters. It can also be used for SSB. + +Quisk Version 4.1.31 December 2018 +=================================== +When using N1MM+, I removed the 10 kHz frequency shift when adjusting frequency. The center will now +shift only if the external frequency control gets within 10% of the left or right edge. Then the +frequency is re-centered. + +I fixed the new Windows version handling of Latin-1 accented characters. I added a new "Filters" page +to the Config Radio screens for the Hermes Protocol Alex high pass and low pass filters. + +Quisk Version 4.1.30 December 2018 +=================================== +The Windows installation procedure is different. If you have an old Quisk installed, you should use +the Windows Apps screen to uninstall it. Then install Quisk using Python "pip". See the downloads page +and the new documentation. The new installation method is compatible with standard Python setup tools. +The Linux installation procedure is also different. + +I removed the "pyusb" module from Quisk. You will need to install the public version if you need it. +The inclusion interfered with the public version. See the downloads page and the new documentation. +I fixed a bug that happens with multiple displays. The zoom feature now centers in the Rx passband. +This is useful for narrow digital modes. + +There are a lot of changes in this version, and the installation method is new, as is the 64-bit +Windows version. If you have any problems or suggestions, please post to the usual groups. + +Quisk Version 4.1.26 November 2018 +=================================== +I added some narrow filters to the digital modes DGT-U and DGT-L. The narrow filters are +centered at 1500 Hertz. This makes filtering in the digital program easier, and is +useful for RTTY. As part of this project I rewrote the filter display logic. Filters are now +more accurately shown, and include the effect of RIT. + +The keyup delay on the radio Timing screen used to affect only the CW key. It now works with +the PTT button too. + +Quisk Version 4.1.25 October 2018 +================================== +I added the option of Quisk CAT control by a software serial port to accommodate N1MM+ logging software. +Set the N1MM radio to "Flex", and enter the port name on the Quisk Config/radio/Remote screen. +You need a "Virtual Serial Port" (VSP) pair. One side connects to Quisk and the other to N1MM. +For Linux, Quisk can set up these ports itself. Pick a port name like "/tmp/QuiskTTY0" on the +Config/radio/Remote screen and enter that name in the external program. On Windows +you need a VSP that is set up by an external program. This is like the "Virtual Audio Cable" +needed for samples. An Internet search will turn up HDD Software, Eltima Software and many others. +Set up a port pair, and enter one name on the Quisk Remote screen, and the other name in N1MM. +Not all of the "Flex" commands are implemented. If you get any error messages, send them to me. + +I moved the split Rx/Tx play menu from the config screen to the Split button so it is easier to change. +I added code from Steve Haynal to control the VersaClock on the Hermes-Lite2. + +Quisk Version 4.1.24 October 2018 +================================== +I changed the sound logic so that a mic device is no longer needed for digital modes or Spot. The mic +is only needed for voice communications. I added a requested feature to the Config/radio/Keys screen. +I improved the operation on the Raspberry Pi. + +Quisk Version 4.1.23 September 2018 +==================================== +I fixed some more problems with the new Ubuntu 18.04 LTS. + +Quisk Version 4.1.22 August 2018 +================================= +I fixed the FM squelch so that it works for multiple receivers. + +The record to file and play from file logic was simplified and improved. See the Config/Config +screen and the Help button. Quisk can now both record and play an IQ samples file. I removed +the 4 Gig file size limitation inherent in a WAV file so that larger files can be used. + +I fixed some of the problems with the new Linux 18.04 LTS. There is still an assertion error when accessing +the config screens. I am trying to work around it, but it seems to be a bug in the gnome desktop. + +Quisk Version 4.1.21 July 2018 +=============================== +I made additional changes for the Hermes Lite 2 project. Control the CW hang time with the +Config/Radio/Timing/Tx_delay_msecs option. I added a change for the Dx Cluster feature +from YO5RXM. Thanks! + +Quisk Version 4.1.20 June 2018 +=============================== +I made additional changes for the Hermes Lite 2 project. I added a change for the Dx Cluster feature +from YO5RXM. Thanks! I fixed a graph screen flicker problem on Windows. + +Quisk Version 4.1.19 June 2018 +=============================== +I made additional changes for the Hermes Lite 2 project. There are now separate Rx and Tx filter +settings. If multiple receivers are in use, the Rx filter is that of the highest frequency band. + +Quisk Version 4.1.18 June 2018 +=============================== +I added improved code for FreeDV to support the new 700D mode. + +Quisk Version 4.1.17 May 2018 +============================== +The Quisk VNA program now works with Hermes-Lite 2. Firmware 62 and above is required. +I made additional changes for the Hermes Lite 2 project. + +Quisk Version 4.1.16 May 2018 +============================== +I updated the built in version of FreeDV digital voice. It now includes newer modes such as 700D. +I added the column for the IO bus to the Red Pitaya radio. I fixed a bug when selecting the +"Bands" screen. I added shortcut keys to the buttons. If a button has an underlined letter, +pressing ALT and the letter will press the button. This is convenient for operation, and allows +Quisk to be controlled by another program that can generate key presses. If you have set a PTT +shortcut key, please check its validity on the radio Keys screen. The shortcut characters for the +band buttons can be changed in the config file. + +Quisk Version 4.1.15 March 2018 +================================ +I changed the logic for the PTT keyboard shortcut. If you are using a PTT shortcut key, +go to the Config/Radio/Keys screen and check the keys you are using. I made further changes +to support Hermes-Lite. And the "darwin" platform was added to defaults. I fixed the +Status screen flickering that occurred on Windows. + +Quisk Version 4.1.14 March 2018 +================================ +The use of the Spot button for testing has been removed, and proper operation restored. +There are further improvements to the Hermes-Lite power and current calculations, including a +correction to the PA current measurement from Steve. Thanks Steve! + +There is a new "Keys" tab in the configuration screens where you can set one or two hot keys +to press the PTT button. Space bar seems a popular choice. I added a toggle option so that +PTT stays on until the next key press. + +I made further improvements to wxPython 4.x Phoenix support, but feel free to +continue to use the 3.x version. + +Quisk Version 4.1.13 February 2018 +=================================== +This version is only for TEST. The Spot button now generates an audio tone from 0 to 5000 +Hertz controlled by the sidetone slider. +I added power calculations for the Hermes-Lite project. +Quisk now works with the wxPython 4.0 Phoenix release. This code is beta, so feel free to +continue to use the 3.x version. + +Quisk Version 4.1.12 December 2017 +=================================== +Sid, G3VBV, updated the documentation file. Thanks Sid! I fixed a network problem with the HiQSDR +on Windows 10. + +There is now an easy way to connect Quisk to an external digital program such as Fldigi or wsjtx by +using Linux PulseAudio. Just set the names pulse:QuiskDigitalInput and pulse:QuiskDigitalOutput +on your radio Sound configuration screen. Quisk will create the necessary loopback devices. Then +set your digital program output/input to Quisk input/output. Press the Help button for +documentation, and see the docs.html file for details. + +Quisk Version 4.1.11 November 2017 +=================================== +I added a fix for the Proficio USB frequency control from Stew, N8VET and Tony, VK4KRC. Thanks! +I changed the Config/Radio/Bands/IO Bus screen to a bit field instead of an integer for the Hermes Lite. +There is a new "Frequency round for SSB" on the radio/options screen. It rounds the left mouse click +to 1000 Hertz for voice modes on HF. This is useful when most voice stations are at a multiple of 1000 Hertz. +It defaults to zero, so enter 1000 (or other rounding frequency) to turn it on. +The graph Ys and Yz settings are now saved for each band. + +Quisk Version 4.1.10 August 2017 +================================= +I added Steve's Quisk updates for version 60 of the HL2 firmware. Thanks Steve! I fixed a bug in +the VNA code for HermesLite. But please note that quisk_vna will NOT work in HermesLite2 until I +have a working HL2 and I port the VNA firmware to it. Currently VNA requires a Hermes Lite with +firwmare version 32. + +Quisk Version 4.1.9 August 2017 +================================ +The "Display fraction" is ignored for the Bandscope so the whole spectrum is visible. I added +bandscope support for the Odyssey-2 radio. I changed the HermesLite2 logic to agree with the +new firmware. Thanks Steve! I added logic for the SoftRock to correct for rate differences +between two sound cards. Thanks to Nick, G3VNC! + +Quisk Version 4.1.8 June 2017 +============================== +This version adds a bandscope screen for the Hermes Lite. It uses the raw data +from the ADC to display a 36 MHz wide view of the spectrum. You do not need to +update to this version unless you have Hermes hardware. + +Quisk Version 4.1.7 June 2017 +============================== +This version merges all Hermes Lite 1 and 2 features with the regular Quisk distribution. +There is no longer a need for a special Hermes Lite version. If you have a Hermes Lite 2, +please use the latest firmware with version 40 and up. The extra data windows and settings for +HL2 will appear when using HL1, but will be invalid. I will fix this when everyone updates to +the latest HL2 firmware. + +Robert, DM4RW contributed a bug fix for CW. Thanks Robert! + +Quisk Version 4.1.6 May 2017 +============================= +This version fixes a bug in the small window format of the Hermes Lite 2 screen. +Do not use this version unless you have HL2 hardware. + +Quisk Version 4.1.5 April 2017 +=============================== +This version adds code for Hermes Lite Version 2 to display temperature and current. +Do not use this version unless you have HL2 hardware. + +Quisk Version 4.1.4 March 2017 +=============================== +This version adds code for Hermes Lite Version 2 that was provided by Steve, KF7O. +Do not use this version unless you have HL2 hardware. It will produce error messages if used with HL1. + +Quisk Version 4.1.3 December 2016 +================================== +The VNA program quisk_vna.py now works with either the Hermes hardware or the HiQSDR hardware. The +quisk_vna.py program is part of Quisk. You need Hermes-Lite firmware 32 or above to use VNA. + +I added some defaults for the 137k and 500k bands. These bands are experimental, so be sure +to set the lower and upper edge on the Config/Radio/Bands screen and follow the rules for your country. + +If you set the TX Level for a band to zero, the PTT button is grayed out for that band. This +is a visual reminder not to transmit on that band. + +Quisk Version 4.1.2 October 2016 +================================= +This contains a new version of the Quisk Vector Network Analyzer program quisk_vna.py. +The VNA program works with the Quisk hardware and the HiQSDR. I replaced the correction +logic, added a new Calibrate screen and improved the formatting. + +I added a feature to change the Quisk waterfall colors on the Fonts configuration screen. +The new colors were provided by David Fainitski. + +Quisk Version 4.1.1 June 2016 +============================== +Quisk can now display and tune multiple sub-receivers if your hardware has them. It currently +works with the Hermes-Lite hardware. Push the Help button for documentation. You can tune +sub-receivers to different bands and modes, and play audio from any of them. + +You can select different color schemes from the Radio/Font configuration screen. + +I added code to speed up the display of configuration pages when the Config button is pushed. + +For users of the HiQSDR, I added sample rates of 1536 and 1920 ksps. These are 16-bit samples, +and require recent DL2STG firmware. I changed the VNA program shutdown messages. + +Quisk Version 4.0.5 April 2016 +=============================== +I added a new column to the Bands screen for transverter offset. I added the favorites file path +to the Options radio screen. If you leave this blank, the default is used. I fixed a bug that +caused a difference in drawing the graph screen between wx 2.8 and wx 3.0. I added additional +sample rates 1536 and 1920 ksps. + +Quisk Version 4.0.4 February 2016 +================================== +For radios using UDP (HiQSDR, Hermes, etc.) I added the transmit IP and port number to the +radio configuration screens in order to support multiple radios on the same network. Leave +these blank for normal operation. I fixed a drawing error that seems to occur on wx 3.0. + +Quisk Version 4.0.3 December 2015 +================================== +This is mostly a bugfix release. It changes Unicode JSON strings to ASCII. +It adds additional PulseAudio features provided by Eric Thornton. Thanks Eric! + +Quisk Version 4.0.2 December 2015 +================================== +This is mostly a bugfix release. It fixes problems with the DX cluster telnet server. +It adds additional PulseAudio features provided by Eric Thornton. Thanks Eric! + +Quisk Version 4.0.1 November 2015 +================================== +This is mostly a bugfix release. It also adds the "Odyssey" radio by David Fainitski. + +Quisk Version 4.0.0 November 2015 +================================== +This is a major new release of Quisk. Comments, suggestions and bug reports are welcome. +There is a lot of new code here, but remember that the prior version is still on my web page. + +Quisk now has two new screen layouts. The "large screen" layout is the default, and is designed for +PCs. It uses the full screen width in order to show as wide a graph as possible, and to make +mouse tuning easy. The new layout "small screen" is designed for small screens such as touch screens +used for single-board computers. But "small screen" can also be used by those with sight +impairment, or by those who run Quisk at narrow widths on a PC. It has more button rows; +and the band, mode and screen select buttons are hidden behind three master buttons. To select +the band, for example, you press the band master button, and a list of bands pops up; then select +the band. Press the Help button to read the new help file. Set button_layout = "Small screen" +in your config file; but see below. + +If you have written your own quisk_widgets.py file to add custom widgets to the bottom of your +screen, you will need to change this file to accommodate both layouts. See n2adr/quisk_widgets.py +and hermes/quisk_widgets.py for examples. + +Quisk now has configuration screens to display and edit its settings. For most users, this makes +config files obsolete. Press the Config button and see the additional tabs, and be sure to read the +config help tab. Furthermore, Quisk can save different settings for different radios. For example, +you can have a block of settings named HiQSDR and a separate block named SoftRock. When Quisk starts, +it can ask which radio you want to use. This feature should appeal to those who have trouble dealing +with config files, and to advanced users with multiple radios. + +If you do nothing, Quisk operates as before, and the settings feature does nothing. You have to +turn it on by making a named block of settings; a "radio". If you make some changes that +cause Quisk to fail to start, just start Quisk with the "-a" or "--ask" option, and specify +"ConfigFileRadio" as the startup radio. The "ConfigFileRadio" is the radio as specified in the +config file, and no internal settings are used. This should not happen, but the code is very new, and +I expect that it needs more work. + +The settings screens take the place of config files, although config files can still be used. +After editing settings, it is necessary to restart Quisk to make the settings happen. Press the +"Restart" button. Some changes will always require a restart, such as the button layout. But I +expect to make many settings happen instantly without a restart. + +Quisk Version 3.7.8 November 2015 +================================== +Foreign users can have trouble with their config directory on Windows because the "Documents" +directory is in a foreign language; for example, "Mine Dokumenter". I changed Quisk to use +the registry to find the name of the "Documents" directory so Quisk should find it in any language. + +When playing a CQ loop using the "Transmit sound from WAV file" feature, pressing +hardware PTT will cancel the play loop. + +I added the hardware information to the top of the screen. This is convenient when running +multiple instances of Quisk with multiple hardwares. I removed the SoftRock status line at +the bottom of the screen. The same information is in the Config/Config screen and in the +title line at the top. + +When operating in Split mode, the RIT control used to only change the Rx channel. But +that meant that the other Tx channel did not have RIT. So in CW mode, a station exactly +on the Tx frequency would not be heard. RIT now works on both channels in Split mode. + +The "Record Rx audio" feature now records Tx audio too. This is useful to record and review +a QSO. + +Quisk Version 3.7.7 September 2015 +=================================== +Eric and I fixed some problems with the PulseAudio code, including problems displaying very +long device names. I reorganized the file quisk_conf_defaults.py to make it easier +to read. I changed the colors on the Config screen to make the controls look better. + +Quisk Version 3.7.6 September 2015 +=================================== +The PulseAudio code was completely rewritten by Eric Thornton, KM4DSJ. It now uses +the asynchronous interface. This will: +1)Improve stability/performance. We can now manage latency events how we want to and prevent +ever increasing latency due to CPU load or network delays. latency_millisecs in the quick_conf.py +file will established the target buffer size for playback. Realtime latency for pulse audio +streams is available on the config/status tab. + +2)Allow connection to two different PulseAudio servers. The default connection is to the local +machine for normal usage. A separate machine can be specified to pass IQ audio via the network. +This is useful to utilize a Softrock or Peaberry via a raspberry pi or other remote computer. +Specify IQ_Server_IP = "your.ip.here" in your quisk_conf.py to utilize this option. Be sure to +set latency_millisecs to something reasonable (300 works for me over wifi). NOTE: To keep network +congestion down, all modes except CW will "cork" the idle up/down IQ stream. +3)Quisk will attempt to utilize native 16 bit LE format if a PulseAudio sink is configured this way. +Default fallback is Float 32. This reduces network overhead for remote IQ audio streaming while not +losing resolution for any local streams that are configured as floating point. + +Additional changes: +-Remote control of Softrock receivers via usbsoftrock. Add usbsr_ip_address = "your.ip.here", +usbsr_port=port#, and import hardware_net.py in quick_conf.py to communicate with a usbsoftrock +daemon. Start usbsoftrock on the remote machine with the -D option. +-Added a macports target to the makefile to utilize dependency libraries from macports. +This option now allows use of PulseAudio on OSX. + +Eric +KM4DSJ + + +Quisk Version 3.7.5 September 2015 +=================================== +I changed the code for full-screen mode. You can now enter window sizes directly. See +window_width in the file quisk_conf_defaults.py. + +I added two more bits to tx_control for the FPGA protocol. I updated the PyUSB +package that ships with Quisk to version 1.0.0b2. + +The received characters from FreeDV now appear in the upper left of the graph. I added +the new FreeDV 700 and 700B modes. Right-click the FDV button to select the mode. + +To make it clear when buttons have added functions, I added a miniature slider to buttons +that can be right-clicked, and a circular arrow to buttons that can be clicked repeatedly. +This can be turned off in your config file. + +Quisk Version 3.7.4 August 2015 +================================ +The Spot button transmits a variable level carrier in all modes, not just SSB. The new config +file option "spot_button_keys_tx = True" will cause the Spot button to key the transmitter. The +default is False. The up button for the Favorites screen used to set the frequency in Hertz, +but the frequencies must be in Megahertz. The ctcss was also broken. This has been fixed. + +I added a new Hermes-Lite model configuration file hermes/quisk_conf2.py. This is for +use by people who can program in Python and want to add features to Quisk. It is set up +to send the Spot button status on the J16 connector. Quisk now operates in CW mode using +the new Hermes-Lite key input. For other modes, use the PTT button. On the Hermes, +use the PTT button with Spot, even for CW. The CW key always transmits full power CW even +if the mode is not CW. + +There is now an option to receive microphone samples from a UDP device. This is used for +SDR hardware that has a built-in codec. See quisk_conf_defaults.py. + +The FreeDV code was completely rewritten. It is now simpler, and it is easier to change to a +newer libcodec2. See the file freedvpkg/README.txt. If anyone is running on Apple OSX, and +has problems, email me, and look at freedv.c. + +The config file option graph_width=0.8 sets the width of the graph. If this is set to exactly 1.0, +Quisk will run in full-screen mode. This is meant for built-in screens, tablets etc. that lack +window management. + +Quisk Version 3.7.2 July 2015 +============================== +I made some changes for Hermes-Lite. You must add rx_udp_clock to your config file. You can adjust +rx_udp_clock slightly to correct the frequency display. See hermes/quisk_conf.py. +I added Hermes_BandDict to the config file. It controls the bits on the J16 connector of the Hermes-Lite. + +Quisk will now keep the same filter for a given mode when changing bands. I added RepeaterOffset() to +the softrock hardware file so that repeater offsets work. I added mode DGT-FM for digital FM modes. + + +Quisk Version 3.7.1 July 2015 +============================== +This is a bug fix release to correct an incompatibility between my FPGA firmware and that of +Stephen, DL2STG. He was using the byte in the control record that I used to send the sidetone volume. +I now send the sidetone volume as byte 17. + +I added the config file parameter use_sidetone to add/remove the Sto sidetone volume control. It +used to be necessary to use the hardware file for this. The default is no sidetone. To add the +control, add "use_sidetone=1" to your config file. + +I fixed a bug in the Audio Plackback button and Hermes-Lite. + +Quisk Version 3.7.0 July 2015 +============================== +Quisk now supports a new radio, the HermesLite. If you have a HermesLite, copy hermes/quisk_conf.py +to your config file and modify the copy. You should only need to change the soundcard names. +If you use Quisk on multiple radios, remember that you can have multiple config files. +Use "python quisk.py -c my_config_file.py" or its equivalent to choose among config files. This +code is currently under test and may have problems. + +The favorites screen now has two more columns for repeater offset and CTCSS tone. See the file +quisk_conf_defaults.py. You must set do_repeater_offset=True, and your hardware must be capable +of performing the shift. + +The FDV feature is now in "final" form. Please see freedvpkg/README.txt. Note that FDV is +a moving target. + +There is now an option to send radio sound to a UDP device. This is used to send radio sound to +SDR hardware that has a built-in codec. See quisk_conf_defaults.py. + +The SoftRock hardware file now supports the key line as a PTT button for voice modes. Use this if you +have a hardware mic button that you can connect to the key line. + +I added the sidetone volume to the HiQSDR control data to accommodate new hardware. I softened the +volume controls Vol and Sto for a smoother response. I added the new config file option cw_delay. + +Quisk Version 3.6.22 April 2015 +=============================== +FreeDV is the combination of the codec2 codec and the fdmdv modem. It provides digital voice +in 1200 Hz bandwidth suitable for HF transmission. You can use FreeDV by downloading the FreeDv +program from freedv.org, and connecting it to Quisk using the usual digital mode DGT-U. But now +there is a simpler way. Quisk has a new mode button FDV. Just push the FDV mode and talk. See +http://www.rowetel.com/blog/?page_id=452. + +Quisk will add the FDV mode button unless your config file contains the line + add_freedv_button = 0 +If there is a problem with the freedv module, the button will be grayed out. See README.txt in the +freedvpkg subdirectory for more information. The "Split" button currently fails with FDV. + +With the mouse in the frequency display, roll the mouse wheel to change the digit. + +The new config option hamlib_ip specifies the Hamlib IP address; default "localhost". + +The IMD button now has a right-click level control like the Spot button. This is more +convenient than having a few fixed levels. + +The Spot button can now transmit a carrier at zero amplitude. This is useful for testing +the output noise. If you wrote your own OnSpot() method, level -1 is now Spot button off, +and the level is now 0 to 1000. + +Quisk Version 3.6.21 March 2015 +=============================== +Quisk can now transmit a message from a WAV file. Record your message at +a high level (near clipping) at 48 ksps, 16-bit, one channel (monophonic). +Then enter the file name on the Config/Config screen. Press the "File play" +button to transmit. Quisk will press the PTT button for you, and release it +during pauses. To interrupt playback, press PTT or release FilePlay so you can answer. + +The "Split" button has been replaced with a "Splt" button and a "Rev" button. The "Splt" +button splits Rx and Tx; and if you click it with the right mouse button instead of the +left, it also locks the Tx frequency so tuning changes the Rx frequency. The "Rev" +button reverses the Tx and Rx frequencies. These features were suggested by Mario, DH5YM. + +I added a new parameter mic_agc_level to the config file to control the mic AGC. Input levels +below mic_agc_level are ignored. The default is 0.1. Increase this (up to 1.0) to reduce the +AGC range, and reduce it to increase the AGC mic gain boost. + +Philip Lee contributed a patch to the PulseAudio code to set the play buffer size. + +Quisk Version 3.6.20 December 2014 +================================== +Thanks to Graeme, ZL2APV, quisk now changes both channels of a control when using mixer_settings[]. +He reports that setting boolean values does not work, but it works on my machine. More testing +is needed. + +The new config file dictionary bandTransverterOffset[] gives the offset in Hertz for bands that are +used with a transverter; for example 144000000 - 28000000 for a two meter transverter. This +currently works for HiQSDR, SoftRock and SdrIQ. I also changed the graph X axis code so gigahertz +frequency labels are not too wide. + +Quisk Version 3.6.19 October 2014 +================================= +Mario, DL3LSM, contributed changes for MacOS support. Thanks! + +I added device names to PulseAudio. The PulseAudio name "pulse" still refers to the default +device. Otherwise, enter a PulseAudio name such as "pulse:alsa_input.pci-0000_00_1b.0.analog-stereo". +See quisk_conf_defaults.py or docs.html. PulseAudio support enables you to connect to recent +versions of wsjt-x. To turn this off, set show_pulse_audio_devices = False in your config file. + +Quisk Version 3.6.18 June 2014 +============================== +The Windows installer now works with the new WxPython 3.0. Previously it required 2.x. +I fixed an annoying sound loop that happened on Windows when quitting Quisk. + +The Rx Filter screen now displays the bandwidth of the filter at the 3 and 6 dB points. +I improved the calculation of FFT size for fft_size_multiplier==0. I added more FFT buffers +to improve performance at high data rates. + +When you right-click the S-meter, there are new options to change the S-meter time +constant, and to measure the audio voltage. + +Quisk Version 3.6.17 June 2014 +============================== +Philip G. Lee contributed code to provide native PulseAudio support for Quisk. You will need +to install the package "libpulse-dev" to compile. Thanks Philip! + +Stephen, K6BSD, provided patches to support FreeBSD. Thanks Stephen! + +The Spot button now works in CW mode with softrock USB hardware. There is a new +slider control for SoftRock transmit level on the Config/Config screen. + +The new config file options file_name_audio and file_name_samples are the initial names +for saving audio and samples on the Config/Config screen. +I added more sample rates to support different hardware. + +Quisk Version 3.6.16 March 2014 +=============================== +I re-wrote the CW transmit logic for SoftRock transmitters. It now implements semi break-in +CW operation. The config file options are key_poll_msec and key_hang_time. + +I added the config file option sample_playback_name to play the raw samples to a loopback or +VAC device. You can access the raw samples by reading them back with another program. +See quisk_conf_defaults.py. The sample rate is the same as the hardware sample rate. + +Quisk Version 3.6.14 November 2013 +================================== +I restored audio to the "Tx Audio" screen. I increased the digital (DGT-U etc.) transmit +bandwidth to 5 kHz. The Ys and Yz settings that control the waterfall colors are now +saved for each band. Just adjust Ys and Yz on a band, and these settings are restored when +you return to the band. + +I improved the receive AGC to reduce distortion. The AGC button now has separate slider values +for On and Off. Right click the AGC button to set the sliders. Push the "Help" button to read +the description of the AGC. + +I corrected some bugs related to dark color schemes, and added color_bg_txt to control the +slider text on the main screen. + +Quisk Version 3.6.13 October 2013 +================================= +I added VOX, voice operated relay. I improved the transmit audio speech processing. I +added controls for the audio clip and preemphasis levels to the Tx Audio tab of the Config +screen. Press the "Help" button to read the documentation on how to set your audio levels. + +Quisk Version 3.6.12 August 2013 +================================ +I fixed a bug that produced slight distortion in the transmitted signal. I added the Enum +type to the mixer control "mixer_settings[]". Be sure to use a Python float if you want a +decimal fraction 0.0 to 1.0; that is, use "1.0" for 100%, not "1". + +The config file now defines hot keys that will press the PTT button when the keys are pressed. + +I added a new tab to the Config Screen for testing transmit audio. You can record and play +back the processed microphone output to hear what it sounds like. I plan to add audio +controls to this screen in a future release. I think the record/playback is more useful +than a simple loopback because I find it difficult to talk and listen at the same time. + +Quisk Version 3.6.11 July 2013 +============================== +I increased the size of the SoftRock status line. + +You can now change frequency by left clicking on the digits in the frequency display. Click on +the upper part of a digit to increase it, and the lower part to decrease it. + +I added some GREAT features from Christof, DJ4CM: + Symbols for buttons. But you can change back to text with use_unicode_symbols = False. + Two new buttons for direct entry and recall of items on the favorites screen. + A new window to display saved stations, favorites and dx cluster data below the frequency axis. + A feature to query a dx cluster using telnet, and display the stations. +The symbols use Unicode, and were tested on Linux and Windows, and on computers in Germany and the USA. +But if your Windows does not support Unicode, you will need to add use_unicode_symbols = False. +Read quisk_conf_defaults.py (as usual), and look for quisk_typeface, btn_text_, use_unicode_symbols, +sym_stat_, station_display_lines, and dxClHost to see how to use these features by Christof. + +Quisk Version 3.6.10 May 2013 +============================= +I made some changes to my n2adr hardware and widget files. I increased the timeout for select() +in the UDP code. I added a patch from David Turnbull, AE9RB, to make Quisk USB control work with +the Peaberry. The problem was discovered and solved by ON7VQ. + +Windows changes the Documents folder name for different languages, and has no standard way to +find it. I added "Mine Dokumenter" to the list. + +The AGC release time and mic gain control are now parameters in the config file. + +I added some features from Christof, DJ4CM: additional color control, double click tune on favorites +screen, and format changes. + +Quisk Version 3.6.9 April 2013 +============================== +The new config file parameter digital_output_level controls the sound level to Fldigi, etc. +I fixed some small bugs in the Windows DirectX sound system. + +When using the Split feature, Quisk can now receive on both the Rx and the Tx frequencies, and play them +in stereo. When working DX, you can now hear the DX in one ear and the pileup in the other. I like +the lower frequency signal to be left, and the higher one right. This is controlled by the new config +file option split_rxtx that can be 1, 2, 3 or 4. See the file quisk_conf_defaults.py (as usual). + +This version of Quisk supports the new features in my HiQSDR FPGA firmware version 1.4. If you are +using Quisk to set your hardware IP to rx_udp_ip, please be sure that rx_udp_ip_netmask is correct. +The default is 255.255.255.0. + +Quisk Version 3.6.8 March 2013 +============================== +The new config file parameter button_font_size can be changed to reduce the size of the button font. +This is useful for a netbook when the lower screen resolution results in crowded buttons. + +There is a new button "Notch" for an automatic notch filter to get rid of carriers in SSB signals. It +works in CW too, but is less useful because the sharp filters can get rid of unwanted signals. The "Notch" +is an advanced feature based on the FFT and not the LMS algorithm. + +Quisk Version 3.6.7 February 2013 +================================= +I added a check for the correct wx version. Thanks to Mario, DH5YM. Selecting "Config/Favorites/TuneTo" +now changes to the configured default_screen instead of the waterfall screen. Thanks to Detlef, DL7IY. +The mute button and volume now controls only the radio sound, and not the digital output. Thanks to Mario, DH5YM. + +Previously, Fldigi XML-RPC control only worked if a digital mode was selected and a digital audio device +was specified. Now it is always active unless you turn it off in the config file. + +The transmit power level and the digital transmit power level can now be adjusted on the config screen. The +levels are a percentage of tx_level as set in the config file. The meaning of digital_tx_level was changed. +It is now the maximum percentage value for the slider. Thanks to Hubert, DG7MGY. + +There is a new feature to save frequencies and return to them. When you have tuned in a signal of interest, +press the "Save" button to save the frequency, band and mode. Repeat for more signals. Now press "Next", +to switch to the next saved signal, and press "Next" repeatedly to cycle through the list. To delete a +saved signal, first tune to it with "Next" and then press "Delete". If you save a large number of signals, +right click the "Next" button, and you will get a popup menu so you can jump directly to a station. Thanks +to Detlef, DL7IY. + +I had to renumber the columns for the Quisk buttons. That won't matter to you unless you add your own widgets +to the bottom of the screen by importing your own quisk_widgets. Then you will need to renumber your columns. +See n2adr/quisk_widgets.py for an example. + +Quisk Version 3.6.6 January 2013 +================================ +I corrected the frequency measurement feature so it works with RIT. This feature was successfully +verified in the November 2012 ARRL Frequency Measurement Test. I fixed a bug in Fldigi frequency +control thanks to Hubert, DG7MGY. + +The config file has a new parameter mouse_wheelmod to control the mouse wheel +sensitivity. Thanks to DG7MGY for the patch. + +I changed the digital filters so they have a bandwidth up to 20 ksps. This accommodates more digital modes. +There are now three digital modes: DGT-U and DGT-L decode the audio as upper or lower sideband. The old +DGTL mode is now DGT-U. The mode DGT-IQ does not decode. It sends the I/Q samples directly to the +sound card device. It is interesting to listen to the DGT-IQ signal, as it provides binaural reception. +Also, the PTT can be controlled by either Quisk or Fldigi, not just by Quisk. + +There is a new tab on the Config screen where you can enter the frequencies and modes for favorite +stations. This can be used to list and tune to repeaters or nets. The page works like a spreadsheet. +Fill in the rows with an arbitrary name, the frequency as integer Hertz or decimal megahertz, the mode and +an arbitrary description. Right click on the left row label for a popup menu that allows you tune to that +row. Thanks to Brian KF7WPK for suggesting this feature. + +Quisk Version 3.6.5 November 2012 +================================= +I added a waterfall method ChangeRfGain() that enables you to keep the waterfall colors constant +for changes in the RF gain control. See my n2adr directory for an example of how to use it. + +I added the ability to connect Quisk to the hamlib rigctld daemon. This enables Quisk to work +with any rig compatible with hamlib. For an example, see quisk_conf_kx3.py. Push the Help button +for documentation. + +I added a feature to measure the frequency of a continuous RF signal. Right click the S-meter window +to turn it on. Push the Help button for documentation. It is meant for precise frequency measurement +such as would be needed to characterize crystals for a filter. Precision is 0.01 Hertz. + +Quisk Version 3.6.4 September 2012 +================================== +I added Hamlib control to Quisk. Set your digital or logging program to rig2, +device localhost:4575. See the Help and docs.html. This is used to control Quisk +from other digital mode programs such as WSPR. + +I added the Y scale to the graph above the waterfall. + +Quisk can now record the speaker audio and the digital samples to a WAV file. Set the +file names using the config screen, and then use the "FileRec" button to start recording. +Press the Help button for more information. + +Quisk Version 3.6.3 July 2012 +============================= +Thanks to Steve Murphy, KB8RWQ for the patch adding additional color control, and for his +dark color design. + +I am using Quisk with my AR8600 receiver 10.7 MHz IF output as a general coverage receiver. +My config file is n2adr/quisk_conf_8600.py. This covers the VHF and UHF bands, and so +I needed to add some FM repeater and scanner features. I added a Squelch button for FM. +Right-click the button to adjust the squelch level. The Squech and AGC buttons +are combined to save space. The new configuration file items freq_spacing and +freq_base are used to round frequencies to channel spacings on VHF. There is scanner +logic in my config file. You should look at this if you use Quisk with a transverter for +the higher bands. With my hardware it is able to scan known repeater frequencies jumping +across bands as it scans. The 960 ksps rate of Quisk and HiQSDR is very useful at VHF +and higher. + +I added tabs to the config screen, and cleaned it up. + +I added a record and playback button. Press Record to start a new recording of radio +sound. The maximum recording length is set in the config file, and the default is 15 +seconds. After this limit, the most recent 15 seconds of sound is retained. To play +the recorded sound, press the Play button. If you are transmitting, the recorded sound +is transmitted provided the microphone and playback sample rate are both 48000 sps. The +transmitted recorded sound is not subjected to the usual audio processing. That means +that you can play another ham's audio back and give him/her a good idea of how it sounds. + +Quisk Version 3.6.2 May 2012 +============================ +I added a display of the filter bandwidth to the graph screen. This is based on code +provided by Terry Fox, WB4JFI. Thanks Terry! See the file quisk_conf_defaults.py. + +I added detailed information on each sound device to the config screen. The Test +button now generates AM and FM as well as CW and SSB. + +The receive filtering has been re-written to improve the shape of the filters and to +reduce the CPU time. Quisk now runs on my fan-less Shuttle Atom machine at speeds +up to 480 ksps. The CW filters are particularly nice. + +Quisk Version 3.6.1 April 2012 +============================== +There is a new "DGTL" mode to send Quisk audio to an external digital mode program +such as Fldigi. Read the file quisk_conf_defaults.py to see the new config file +options available. Use the Help button for basic information, and see docs.html. + +I changed the 60 meter operation to agree with new FCC rules (for the USA). See +the configuration file for items to control 60 meters. + +Quisk Version 3.6.0 March 2012 +============================== +There are no new user features in this release, and no changes to the HiQSDR code. +This version adds a new feature for those writing C-language extension modules +that need to access C code from the _quisk extension module. Examples are the +SDR-IQ and the Charleston extension modules. This feature was requested by +Maitland Bottoms, AA4HS, and he also provided patches. + +Previously, symbols from the _quisk module were linked to sub-modules with the +C linker. Now _quisk exports symbols using the Python CObject or Capsule +interface. The documentation is in import_quisk_api.c. Only minimal changes +to extension modules are required, as most changes are in _quisk. The linker +method still works on Linux, but the new interface is highly recommended. + +Quisk Version 3.5.12 February 2012 +================================== +There are no changes to Quisk, but this version includes the new Quisk VNA program +that enables you to use my original transceiver hardware and the newer HiQSDR +hardware as a vector network analyzer. Use "python quisk_vna.py" to run it. + +Quisk Version 3.5.11 December 2011 +================================== +I fixed a bug that caused the microphone to freeze when sending the mic sound to +the SoftRock for transmit. + +Quisk Version 3.5.10 December 2011 +================================== +Lucian Langa contributed a patch to return the primary display size for dual +displays. If you decreased graph_width for dual displays you will need to change it +back to 0.80 (or similar). + +I improved the transmit audio filters to reduce spurs and decrease processing time. +The mic sample rate can now be either 48000 (as before) or 8000 samples per second. +The plan is to make Quisk run effectively on small laptops or even tablet computers. +Remember to adjust mic_clip and mic_preemphasis in your config file. + +Quisk Version 3.5.9 November 2011 +================================= +I fixed a bug in Windows that occurs only when using the mic for transmit. + +Quisk Version 3.5.8 October 2011 +================================ +The Windows version is now equal to the Linux version, and transmits properly. + +I added a parameter agc_off_gain to the config file. It controls the audio gain +when AGC is off. Reduce it if sound with AGC off is too loud. Note that even with +AGC off, the output is limited to the clip level. I made some other improvements to AGC. + +I added FM transmit. The modulation index can be set in your config file. + +I made some improvements to demodulation, and to the SSB transmit filter. I fixed an +array out of bounds bug in transmit. + +Quisk Version 3.5.7 September 2011 +================================== +This is a quick release to fix two bugs in 3.5.6, the message "expected integer" and +faint audio for FM. I also added a new parameter agc_max_gain to the default +configuration file to control the scale of the AGC slider. + +Quisk Version 3.5.6 September 2011 +================================== +The Spot button now has a level adjustment instead of fixed values. Right-click the +button to adjust. There are now three buttons with a slider adjustment, namely AGC, +Spot and the right-most filter button. + +I added a feature to measure and remove any DC component in the UDP samples. I fixed a +problem with the waterfall display when zoomed and using band up-down. + +There is a new adjustable AGC control. Right click it to show the slider adjustment. The +full up position corresponds to the old AGC 1. + +I removed the 1650 Hertz offset when transmitting SSB. It was not necessary and cluttered the code. + +Quisk Version 3.5.5 July 2011 +============================= +These changes only affect the N2ADR 2010 transceiver and the improved version, the HiQSDR. + +I moved all the files into a new package directory hiqsdr. The old n2adr directory +has only the special files I use at my shack. Please change your config file as follows: + +from hiqsdr import quisk_hardware # Special hardware file +use_rx_udp = 1 # Use this for the N2ADR-2010 +use_rx_udp = 2 # Use this for the HiQSDR + +The sample config file quisk_conf.py in hiqsdr can be used as is for the HiQSDR. +There is a new dictionary tx_level in the config file to set the transmit level. See +quisk_conf_defaults.py for other features that can be set for the HiQSDR. + +There is a new FPGA firmware version 1.1 available to support the new HiQSDR features. +Note that your firmware version is shown on the Config screen. It is not necessary to +update your firmware unless you use the HiQSDR and you want the new HiQSDR control lines +to work. If you do update your firmware, you must run Quisk 3.5.5 or later. + +Quisk Version 3.5.4 June 2011 +============================= +I added another slider labeled "Zo" to zoom (expand) the graph screen scale +so that narrow signals can be examined. The center of the graph is changed +to the tuning frequency when zoom is turned on. To cancel zoom, move the +slider back to the bottom position. You can tune as usual even if zoom is on. + +I put the band buttons on one line so I could add more control buttons. + +Quisk Version 3.5.3 May 2011 +============================ +I added "Documents" as a possible config file location (for Windows 7). + +These changes are specific to my 2009/2010 transceiver hardware: +I now detect and display the firmware version. The files conf_transceiver.py +and hardware_transceiver.py are now the basic config and hardware files for my +transceiver. The spot button now appears without a special widgets file, so no +widgets file is necessary. The file quisk_hardware.py is still the hardware file +used in my station, but it is mostly useful as an example of what is possible, +not as a starting point for use by others. + + +Quisk Version 3.5.2 April 2011 +============================== +I added code from Ethan Blanton, KB8OJH, to provide direct frequency control +of the Si570 chip in many SoftRocks. I added AM transmit and improved AM +receive. I added FM de-emphasis to receive. I added a noise blanker. + +It is now possible to delay samples (tx_channel_delay) and correct the amplitude +and phase for the sound card play device (SoftRock transmit). Unfortunately +receive sound card corrections will need to be re-entered. + +The filter bandwidths for each mode can now be set in the config file. And +you can right-click the right-most filter button to adjust its bandwidth. + +Quisk Version 3.5.1 February 2011 +================================= +The phase correction control has been improved to allow multiple correction +points per band. Unfortunately this will require re-entering corrections. + +I added mic_preemphasis and mic_clip to the config file to control Tx audio processing. + +Quisk Version 3.5.0 January 2011 +================================ +Starting with this version, a Windows version of Quisk is available (alpha code). +I changed the amplitude/phase correction control, and added config file +options (rx_max_phase_correct) to control the maximum available correction. + +Quisk Version 3.4.14 January 2011 +================================= +The "alsa:" names can now be used for mixer settings. I added simplified +config and hardware files for my 2010 transceiver hardware. I moved the one +sample delay for some sound cards into the config file instead of using the +#define FIX_H101 (which remains for backward compatibility). I added more +buttons "GraphP2" to the Graph button to activate a peak hold function. There +are config file options graph_peak_hold_1 and _2 to control the time constant. + +Quisk Version 3.4.13 December 2010 +================================== +I decreased the microphone speech processing preemphasis and clipping. +I added a config parameter key_poll_msec to control the SoftRock USB +poll for key status. I improved the config screen. Alsa names can +now be strings like "alsa:NVidia" that match the card/device info. +Thanks to Joachim Schneider, DB6QS, I made some improvements to SoftRock +USB control. + +Quisk Version 3.4.11 November 2010 +=============================== +Thanks to Sid Boyce, G3VBV, for sending me SoftRock hardware to work with. +The "mic_play" logic was re-written so that transmit I/Q samples can be +sent from a sound card to hardware that uses QSD up-conversion. I added +USB access through pyusb to control recent SoftRock models. A new package +"softrock" directly supports several SoftRock models. + +Change Spot Button to transmit at carrier frequency. + +Add a Split button to enable split receive and transmit frequencies. + +Fix band change data for pan adapter users. + +Try to make easy_install work better. + +Quisk Version 3.4.8 August 2010 +=============================== +A new config file option "playback_rate" can set the radio sound play rate. + +I added a button to the config screen to change the decimation rate for +hardware that supports this. See the new "VarDecim" methods in +quisk_hardware_model.py. I added this feature to the SDR-IQ hardware +file sdriqpkg/quisk_hardware.py, and to n2adr/quisk_hardware.py. + +Thanks to John Nogatch AC6SL for a bug fix. + +Quisk Version 3.4.6 July 2010 +============================= +I improved the mouse tuning by eliminating a tendency to tune backward. + +I made the sdriq extension and my n2adr code into packages in the directories +"sdriqpkg" and "n2adr". The new package architecture will make it easier for +authors to write Quisk extensions. See the example config files quisk_conf_sdriq.py +and quisk_conf_n2adr.py to see how to change your imports: + + from sdriqpkg import sdriq + from sdriqpkg import quisk_hardware + from n2adr import quisk_hardware + from n2adr import quisk_widgets + +Thanks to Terry Fox, WB4JFI, for improvements to Quisk: + Code to support the Charleston hardware (libusb-dev required). + Code to add a third FFT data block. + +Quisk Version 3.4.3 June 2010 +============================= +The hardware open() method now returns a string for the config screen. If +you have a custom hardware file, create a string or return the base class string. + +I made the SDR-IQ code into a separate Python extension module "sdriq". +This module can serve as a model for other hardware extensions. It is +the model for the Charleston hardware extension module. The sdriq.so +file needs _quisk.so, so put both in the same directory. + +I corrected the decimation for sample rates greater than 240 ksps, and +improved the filters for all decimations to reduce "images". + +The following changes are only relevant if you use the SDR-IQ for capture: + + You need to add these lines to your config file (see quisk_hardware_defaults.py): + import quisk_hardware_sdriq as quisk_hardware + display_fraction = 0.85 + There is now a special hardware file for the SDR-IQ. If you have a + custom hardware file that uses the SDR-IQ you need to use + quisk_hardware_sdriq as its base class (instead of quisk_hardware_model). + +[See version 3.4.5 for further SDR-IQ changes] + +Quisk Version 3.4.2 May 2010 +============================ +The config file has a new option to add an external demodulation module. I +I added the ability to play in stereo, and corrected the sidetone logic. + +The config file has a new option to add a full duplex button. + +I added the ability to use PortAudio for sound card access. PortAudio +can also be used to connect Quisk to other programs. + +I added a key up delay to the is_key_down() serial port code and fixed a +sound card CW bug. + +A new config file entry can make amplitude/phase corrections independent +of band. This is needed for a panadapter. + +I fixed the compressed graph labels at high sample rates. + +Quisk Version 3.3.7 April 2010 +================================= +If you get samples from a UDP port, you can specify the decimation rate in the +config file. + +If you send samples to a sound card for transmit, CW now works (as does SSB). + +Quisk Version 3.3.6 February 2010 +================================= +I added BandEdge to the config file, and added code to Quisk to make the +frequency and band changes more rational. I changed the config file +attribute freqTime to bandTime (see changes). + +You can now define a class named "Hardware" in your config file, and then +you don't need a separate hardware file. This is only recommended for +simple hardware needs. See docs.html. + +If you use the microphone and send samples with UDP, the audio is now +centered at 1650 Hertz, and you must add/subtract this offset when +setting the transmit frequency. + +A number of valuable patches were submitted by Andrew Nilsson, VK6JBL, +and these were incorporated into Quisk: + + The band buttons displayed can be changed in the config file (bandLabels). + + The 6 meter band was added (change bandLabels to show it). + + Turn on add_imd_button in the config file to generate 2-tone test signals. + + The two new functions QS.capt_channels(i, q) and QS.play_channels(i, q) + will set the capture and playback channel numbers at any time. + + If you set the key method to "", the new function QS.set_key_down(1) will + set the key state up or down. This enables you change the key state using + either C or Python; for example, to add a "MOX" button. + + The microphone samples can now be output to a sound card for transmit. See + the additional items in the config file. + + The new config file parameter mouse_tune_method causes mouse drag tuning + to change the VFO frequency, not the Quisk tuning frequency. + +I moved microphone_name and tx_ip (for the microphone) to the config +file from the hardware file so that all the mic parameters are together. + +Quisk Version 3.3.1 December 2009 +================================= + +For sound card input, I added controls to correct amplitude and phase +balance. Press the new button on the config screen. A different +correction is saved for each band. See the help file. + +I added the new band "Audio". It sets the VFO frequency to +zero and is meant to be used with a sound card. I changed the WWV and CHU +bands to a new Time band. The time frequencies are named freqTime and can +be changed in your config file. + +In the file quisk_conf_defaults.py I changed the default for persistent_state +to True, and added graph_width=0.8 to specify the graph width. + +A period "." in the frequency entry box means megahertz. + +Quisk Version 3.3.0 November 2009 +================================= + +I fixed a bug in the SDR-IQ decimation that produced slight audio +distortion at decimations other than 500. + +If the play device is the null string "", Quisk no longer tunes and +demodulates the signal. This saves CPU cycles when Quisk is used +as a panadapter. + +I added decimation (reduction of sample rate) before the filters so that +Quisk can handle higher sample rates or slower computers. + +I made the waterfall into a splitter window with a graph display at the top. +There are new attributes in the config file to control this feature. + +The numeric value of Ys and Yz are now shown so that the values can be added +to the config file more easily. + +There is a new config option to save the state (band, frequency, etc.) on +exit, and restore it on startup. Only certain bits of state are saved; the +others are still taken from the config file. + +The default config file sets fft_size_multiplier to zero, and this specifies +that Quisk should calculate it for you. + +Quisk Version 3.2.3 September 2009 +================================== + +Fixed a bug that prevented tuning the SDR-IQ when using the +default hardware file. Started adding code to capture sound +from a UDP socket. + +Quisk Version 3.2.2 June 2009 +============================= + +The microphone access was re-written to make it work with more +sound cards. The config file has a new parameter "mic_channel_I" +to specify which sound card channel is used for the mic. + +Added Documentation.html. Fixed lack of poll to ReturnFrequency(). + +Quisk Version 3.2 May 2009 +========================== + +Quisk now uses wxPython instead of Tkinter for its graphical +user interface. You must install the python-wxgtk2.8 package. +Get the latest version available. If you still want to run +the Tkinter version, it is quisk_tk.py. The wxPython version +is much faster. + +Quisk now runs in two threads; a GUI thread and a sound thread. + +I moved the colors to the config file so you can change the +colors more easily. + + +Quisk Version 3.1 April 2009 +============================= + +New hardware file to control the AOR AD8600. + +I added filtering to FM audio to remove CTCSS tones and provide +-6 dB / octave de-emphasis. + +I removed the tkdirect C-language module and replaced it with a +pure Python equivalent. This reduces compilation problems. + +I improved the speed of the screen updates so that Quisk will run +without clicks on slower computers. + + +Quisk Version 3.0.0 April 2009 +=============================== + +Thanks to Leigh L. Klotz, Jr. WA5ZNU, my special hardware control was +removed to separate files so that Quisk now has a cleaner design that +is more useful to others. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..53a890c --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Quisk Software Defined Radio by N2ADR +**December 26, 2023** + +This is Quisk, a Software Defined Radio (SDR). +You supply radio hardware such as Hermes Lite2 or SoftRock to convert +the antenna voltage to I/Q samples. +Then send the samples to a computer running Quisk. +The Quisk software will read the I/Q data, tune it, filter it, +demodulate it, and send the audio to headphones or speakers. +Quisk has a microphone input and a key input so it can operate as a +complete transceiver. + +The web page for this project is [https://groups.io/g/n2adr-sdr](https://groups.io/g/n2adr-sdr). + +The change log is [here](CHANGELOG.txt). + +The documentation is [here](docs.html). + +The help file is [here](help.html). + +This site is under construction. Check back soon. diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..ee518f2 --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +#Quisk version 4.2.29 +from .quisk import main diff --git a/__main__.py b/__main__.py new file mode 100644 index 0000000..f4b3ce8 --- /dev/null +++ b/__main__.py @@ -0,0 +1,8 @@ +def main(): + import quisk + if quisk.__file__.find('__init__') >= 0: # quisk is the package + import quisk.quisk as quisk + quisk.main() + +if __name__ == "__main__": + main() diff --git a/ac2yd/Design_2022_0531.pdf b/ac2yd/Design_2022_0531.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8a54e002789b7a1f2fa78f178ec898b8fc517d0a GIT binary patch literal 371367 zcmdSAWmH|uwx~OCclU|AJHa)$ySuwPgy6v~xP{;v+}$O?gS$Jy?Ge^qd#&~MIp^Km z+HLK&$Dc9Atg2q!s?SM%HOLi2#p#(DI1ymU>t6p5V3~nTKzk!A1Xx~PMioy7Q$`U( z7egC+b4CS2b5my^%WJa|ql}@QIhCm$y{a-bkWtyi$<^3J#mUrE$===t$jI@Yd2GVenweS7ekX*nLt)H7VfvMYL=$%rcMe@re>y2rgp}!v#_$U zGW~Ai=SP4wwKI9s=l9J2Q0z@1MiF~gI~O1`JEOFviL*A4_4hC!GuPXNu=MPfUJLXVP*g0=}lW8>zgJ(wm*8ZvAwmialBpF|B%A|M;rTFn=bJ0s{NMuAF91N z?A02Ve|b#F)Y;zE>D9JZe~8=Lx%|E`1K)ID6lVec;Rtb7AoH7&jN)uS<~KcG9VX`C zBBAVJ=wkXhMnahr_=kTbl(~R!R=)O7aI!a6Hg(Zvd<`a}im8VSqonO?w1oe-i2QMp z{H^n=_f0Gfh3!4GUsYxTaxk+1Ik{PMfv;|~b9vR)8Te)%qlA;atHbMjZyE6Rz^Kfq z;$&#&?C{$vV^2mAWkyj`H%ntvB?;kIxhjT6&aYkm$b~;FcLp;3-ATU%|LG)gOB)wc zCq{9bSF1!#jqOcL8D&iE%v~&i>|ET;zehQ{IGGySBEWiN)ahzEZ;HqJorXOlUA;e% zvX~CE9>@(#ZeHYTaT}dK(U|D>Afk#xH&g_djg7DC@MHZ3&=qB-CKE@A_XsFMa@~4T zEk^QJU-i9NABgPmQ#${d%Jbrq`|R<;5N-5GU8`6vwQjG5U6Pvj_A#S_8?$ozd&+DfL_ZR#p-WG!F z1;x6|;z+AHg6y;(UvUfb3+^p#BwA|P6|>De#B}v4`(H}ipLQSIvvaqzS2~ol{aY0d zaNOwoPFzq^vDRRm!Bm;Y(i=EW^XA(G4Kl(%safiY4iN^t5FT>jjd5)Hs2Hat8grJc zxd+Ru;4|MIA>T&u($oO>djJk#fGS7aK1oV|E9|P(Ohc)Ev|MAgR7gOcEqZ7_jUnKR z2!QGA2MJ&e3=D_|9uNhKNBAibvd0QiiXR+3T}-Tz5no1*Gr}>~;ae>>sH|09Ym4!% zX`c7s8X~w@V(@*CHbHYJ*)g)I*p`9k7ak`wr0%;>>GH@Sjl&rWe;95c@^(K*h@S z;{pmwCm7U0UmEB$g=YoFoNnc_4E776Zrx&;BIC^2y9XxzF^N_6@8JpOmR(3E+T_l2 z^cK4$={FsrNF6oXRz4jQ}nPHZh*N~4y`27u14|1P^qnxIQ$liH-?}1!` z8t&ww?=l!nH2;co{1VIm%;5*KJr8+&2zd#=qv;+bv1E@Vds%bW4StG+Me@E<)>U23 zoGC_gvo3Vf6GmJc4Z{;K3N9MB5Fgf|XOnXK5-2h)0@Eg(h!Zf0u$O4v78>ul%mY2i z_Wcnf?WE<(iDTR0*LNC5N9^w`smI(BvPkJqu9upPcXxJ8Cd93(q>X&g26%bolMw4I zT>K5@@E;naZhgVmW(&lS16>_6wj7Tb*8E{#1Vqp{q7hn?GkO?#-R@F@q!W7eA?4A0 zN+#ssZcmlG8k+W&ess*=bMCX=X@(%J&-8ZMySZ6q^RKmcN@MA8{&K+4a|;@S{Z;8L zCsyrM)da)ouP!%j#UUrL_+aJSV~7%IXCC>U5`>O2gOU+*7LmfF%F~XlOoQcJ%14Qf z_9X@`aHIXBNwCS(V?ecn+gcVxy1raJEBs+jyoW*&kGe8uyoN$Uzi3&$5mLPRa>Flo z*YceNM~;#PE6~pGQsQ6KO>Sf4m6fg+Q*pD+NSkA<+}ji`s|i%`>Pj7>s9hi&GJY-| zpcFBCM-yV_EjPF}rFKOH7{`BVB)9h#wb3K4Hc+AjAm20JIMUPvH7FW7JY>dAEy&bBB4cW=mxKibj9y;k ziOFn1zL$l?5uz{6i#}MC4i`_GYUuJk)JK%(pALdsLg1M}f%8#8YN8NRAfwyLoI@+Rg@&SuM$^`(`w>8vn6d2Z*|Bc5f z*Hg2+&GAQw64ilHIag9vMDW)U*Tj#bgcYq+NduzJSay1jLGeRDd15{}z7?L;tu{3& zFd%RY>`qhVc1AT5KJg*Zd z`Ed-LA*8a;D1g=u#l$EnkkDe*U7^=2FEhxpRsY8oojB(${*lFL*doSZfg7S5$BS`O zUg~=O%1dY;-RC|E*SgH25tz~HnP2T0c0*5Ex7?Ql6-#S6O_}BObtNZeRQ;S*Eji(> zmIMil5j(`bo0Fk4P0%hB>i)phDLRK16h^~sW{cP=AwYJONUkwAx)Xb07iSd+`U-53 zbUXrA&6&KaGh(97U4e%5q%--J(=rsIan)Tv3n=pl=Il;TpPtS_9B#{s!H#9yt;1=8 zOc8*C>gaqw9^9`*P@W1zY))QUD>td6blGZnHAtfYhpC|)VX4OvrcFohHqKPB1Ps`! z&=Yu-*_|8b4W!5q5TwMFE8lTK>g`|O(Hu%aihYzO#l!v75nV^%dn+mXL&|bmTC-GD z%m+HS__IlE$&dC_!-{wU`X+x+L~Ya~h6>N7k8)jz%z-=pc#4RrD!Z^R&rPax808M} zpf)blDWLrBQFY?p0nPn$4E3YdpS7xO}?U4w_I<}_k>})c#14)LxIwHy-qlb zOE&y&dfBHUjIk9rpb}=Yim6v?v{y3`%U42j zz==p`017bb=x`g`7`+V(3V+!h;;^(1{xcCnJ}7*A>gb*-vj!h zm%hLA8}i31r<$wa#0GXGvw(OZ>Gd=_rYIumll&4QCXEkjNTE_E_yJ-Gkd#NZW5sL) zBSGvrQ{4nTQ?z};wKG=CGm7pOb)(jGj3QR-hoJO?&Lcs6!Ob8{R^^JcR&qE&QOzQk zvAt;vonGA|g{FP-nfyyrbH#TVc`UvUigc&x#hb9hf+0^0r=i!YJlecF$)Pm1NPs;7 zS3C1XG4^fp^K{AbOw}U;I`ESK1o=bG1eF_tQ05IpSj4YJ zjR*LA0CSZDMr6S*sZ9{7yafV4XLL{o`GVAD;z45fh}?&Apraor70)a;%dm7N0?3v*oNo2)dDsJj* zT`kZ1Mbt>yE*!pdk4XRd4%{0NA^kYoN{)lwt&~8KI6}+5Mf#@3w_EwdAbDzWu-;2Bfg@hmuE;1s2rYD>~WegG~95Cbo`R!>qsBh$||gh7~*QEXbIM+rC) ztt);cosQ4=u@Hu~*=z*st<0mKKiZYZs-L?!lylHObZSS=d6UTx$SIrIK7x&uaXcd& zI%&XwnM)2^<=1t#6r-o?OC#wIZ~>g+8lEI4$VOItrFUD}bNgCBTbsWgLf9|aRjR`6 zUQb<)6h$x4=yY#`Z#)G8zKY4zI)!6LDwS6KMOdGsl(=w)0$Q4BzptA&{1^n_>KUp0 z)#q2`ZN?OUO`EaOWeI24lfH7n<)t~PIEBzwGKDvm@IAGTgz5_CqYaj{KZ2Z%jj+2z zuMJkDkFLpQH6-9IhL@!xzvg0D3Qge=t~F`OM*#i%?O5G~=#3Hc;lf+YT@bE0=FFcGJbs;z zaE&-Jj*Mc_>aLlt_NVT+56w6wSLyH9zi-*FF16Z5i1$<`l1EAE-`dF;>Ujq_I)Yql z)sVq|03*FzvJ>DihmP~@4068ilZ!uRyKz1J6xjDL!@x!ZqPxezU4c23#Aa$oaP{_I zQ6krB2sLx>X7}EkLwOBUw~?-XhioN_^$1*9FAVb`#Z$%ytZie33d?cSN9*K#7hV`o zwxKWt)KsrdQ362swJWNzS}BxLvcjj(t>rX6I)NYtB|)}W?urqW@O54#_97}#3DTHuuk_{a_?NavG#iUa@>E322npr~@QIi1wZ zLg2D48&uFr<(@M^LRV>gQGjw7Elq}pY3IUL?`TLG@*&Y1=j@NNkVX9%PN7A;%IyaD4l&* z)b?N$K<_$rcCbd@3c^)84xZAF!UrrI=aJ~$0R%{61}ydW(enoZN(3~)olsJnnLgw{ z3Ukk3aNZ!$3O!?kH|dO4^->fCGizxrmpt?n(WvuRk80aG;o2B;en>L74y30!g~%*= z5DPa0u^xV1+fmEY2w|tlp1_}<9Uf$p+tqF?5=XPBPO`Y>;E6C56=3=z^ww@1zdM1J zIl2@H0&eCG+RxF`YqUaxhB1qW!>cuP%7d!R5Q_Gq0par=7dB@+=eakf8CchI04&a4S~K5qUk^}S#BPB^l!e(q z$5Eb7a+FBx?GNGqX=l7fgGO0nY^UC$s}-maw5JUR)o@k zY+I7v>xfo{URv}_(S81$nMgj;dkh;KZafU6Nmff}lir*7C4Tu0FlY~e6Bu8$#x52> z5XV)FW~*ZPASg^M=AuwsJLeZA-V_yK(>+uX+}vOaEWmt1Ai-HhHd(X@5v0)uPN21J zS<&+$5qy({Mtq#1FTYr6(K~C%Ggl9Pt^LQH-vf~)98Za>?Wd-PF=OEbXgfN=JHJzg zp7=t5a9NDT6lCUrYVT*xZ#*~Cuxf%BAiNTm7~J$|GEim0(7#~Rbu6~ZWWI~cI{^R! zw2)QhmtcT}-l+{ocLus@f(M9pud=5+4f#8du&zwTKKtQ&1`DKq{egZ0M*$}^{0Re? zv68Pj>0->CFk&tWdaH&>Xsqe^7odQw?5_^Jielp9Q@v%%m>FgSt+a$w0Ae(EgA!iL zO4HJNO^SfH1X{GcA~vdm;&@|aM=>Gs@k|*TA3^Oi0%FceW&&3Q-D+&e2)A^)W4RE< zlo`IPUcvhHo&_k}lBVXO)}}D(oyw>xvR!??)tBI4#+2hmm@f$HyW<%gU{&-};6IZ= z0FQu(ZHJz54#_2q@aZ1M8poCqgLg+!!`vANlI-GG8)3Y&AMdvmgPl$Tt&&YbVHk+!TeZ8^v@TG`vnH@Q606?TAquT#{0o)U{*w% zhRW*40AKaZL};?u9t#1zvy1xYI`icESG23kFM9eUXx2gXM?rbc2vUR@yiN%Ua=?I- zhB&?#_t8MZpI?GB(+PhV2HnDPz!~OEpY9-Hkce+siw&fCjz~T&EhtQYfeSRSBHdxf zpiPkHCs4QrJVS_0R-|Q&>Po64R7a;%KSH!w5S1B%J(>X~seTD!Vq?%`IdRV%R;90{ z=W^wHL}-Wlif%PpdWvaZT!u~~2_i%xER=(AJu+Wi4EJG|-6=c}RW2M6;#jg*Y?`RB z{#w9O(Uce;+W6Ur1Lt&lT?pmNzQM7HU3HkeVmrqUBdkwTy&ZxVpp} zp7(Vu)gsjPtQ2JwEsRIm0rZEoTF5!_?sI)=IN&2?DuT{Fx3>Dv4c5sVhyu1GA$cJR z{U5+o?<{yT?K_@2s*fYtMbktnkm^=Zwlf+qkW>y7@-cV|^SW%&9HC~snlh;U)siNv zXRQ6(+9kshGV+(O=xdA!s2^#np zqCsPWWCuS{L}Dd2!2p;m95$n(bC|adMUeAEbX_d@E6XGT4VV)-@SvTUG8Xw*8WYEe z2GaW7vRA}0eb$K@m&KvB1zCHQj|A|7)H8brkSA*&Ab?ogY0Wx|KxkCi%|NMbmL?YAuSQ2^e-B z9{T1{KA+p8rSgAu=GTU3VS;U?u$o9h*u8ivMD&qhJvUZbpmtxNjBr&{LFxDMa%q6! z_e8p`b}?8PF<=iXTa<7bm-(j+d(>F595M)hGC&2OC!^yOSG5zeSDbuZI=ulK)Ap?8#7Fi1b9 zY^L;I%j}-b`|&z^l(&Q#D`(Up?WZe!gy}e;yQ{D)v*hBpH%R|BmDlytAYUHWpPLWT zMr(5}Yf2rZs;sFz5>mnWxT&B4$$(}5=e}_Vd%&6Y*-3Rm))iiD61(j>#zOo3eABz0 zlnq9N2I6#zo7!7va6BHZ)T(Nidx=2l{2(8 zeFY7S!iLVKZ#`5jZB3o!Ox>02Z4K=dlx0;I#q3`9-!1LT8PzTAgzR2_$p88LyT7QZ zv$2zv zCHf7BylMKM+PxwI^H-rPjFPVu%F%@b_Fm7N%D~^g7KySx~qZ<^SAQBgS<=h=cGv%M z7BIiTu>UpMZ7XCQs=6_>}zS6wkboReqqpxe|uO<2~w)huaeH;3}6q`Sl`k%wW z&BXqz^z0kA?iNmC3=u{(EIIy{=3qCLkvZ z*FRV08&LfZ6aT82uZ5H8FW>)XDf)L|lr(v*J(ez>^zZ*=qKV;a_+L}N8OZzxK2@<- zwX^(F?K>qR6*f6x89+_U__Yu~c# z^ zp8kgke=q6sP9~;KuN9N(&v{-C1t^)CTfTm0c~S|z(tFe2rvzLb9BfQ&e^>2)VBxoE z{v#@X>G75i|9}2+_(yU0<6y!+Joz8B|M$6u|Bb@~f1Mfl_c4P1YljE^NR!GJTl64wVfBmYy8}kUV4u3Jf%J`V=dJ%sEVzaaB2@! z$ZqgFAZJ5KeGyHR41f!WclCSPKLMfQ?yi#LP%hB;A~+tJmDTPp()N(z_42$tfYk9Q z+o!I6aBojE?Z46>PtaMgMEcShwtTydHEVr_UTId4+q7v zTB%7YR~lA^4>xlTL#zH1)4p)}g(v%}D;@U_i=6&4{4>i&2NWgR<2>w7`d4?^_N$NJ z`j3Z;oCk$5M2TJ?veLQx^WuHdHsHk)lk_9|1(~(*(4>%tE+!&CE>w-X1BTXn{DwUG zQ(E?Hy);Q#Mpae!TdA=%)j8s*v!|;NKULlG9rmCFNyT)wa2q$0G1R*QPme3RCI$B? z6QxSE_c8a?WhXd-#ptnh6vHln??|HusXkX^H1TMhq$w3yO;iwf@t9SVciGA6DQB7e z#S;zwGJcONPlUs{v7jMfXt6!l<=S z%@v+hV3vZFek-cVy;9s!lW&Z%3hLn0^?X;CaBMXjFseBAi>1JN$8<~>;}8+*3k~QO zr3uQ)&FMhQL$J^q%0Ue}@?PAoRGW$W4!e=-QZK4=s*N2gqurBgyLP9+-V)*;l8J@H zCRng>qBn6gZPz&qq;oBVJJsJ+N56LRX`A>CN!c1qBG=B z=CNk>=X;_eu~?EZFOZrd)fe3pH#r>4JROGt&8-N>uz~la&X6cs;+{?yX5rg4GksZC z{(Zg|s4x>6+{((v6fapkoYejyZ8R8ne5#A|W>qoHs;ks0v;u*qa3xN}BA%s=4o9;3D+BN1Q z=-MS1EBy>^Q%M4zEA;fOBHI?Oqd`}>(t~I=Vx}8Np1?+<7xW-_;x=J%@{WHqHhjB5 z4)!Cu;PnZn?-O&@MnJe}9f$z)46(sca;BJY=v>F~O|cSZubguZ^zGcQcTlnn&EG!B z)G=EyewvM6B%{RISUdX+jALlJ+P-M{9ycz0=pDS02RJGq4X!^j@f_smZi1GNr-D)1 z<+Sr{lFVM9f;k%^TDkQiM&af%g0YPx8$KS|iXG54!rK-y`)N7p)%Md34!L(|OE@rX z+aX-63XFoo5*mv}9=#hq)VH9Y%fTG3jSFu-?mL{AUc1|s8J6cy8!nj(CTUc(u72if zHk1+l%z3^;|I{@KLk}C+@$nZQ(6rbOFW*LPc>2h&2ADm9#GtWveJPzB@v+b$1#FGu zjUg%mGs7ZW$Q(1Q@HCMCv0Mrfv*>3pWD}~+0@o+@;OAbDA+_l~ZT3v9iuTlBnRgwC zXp06MtgZ95OqNdlgb+)=OIx19D@`}1TCpjiR{cDnUV?JR8-Ts7Uc}})n|`?cnAY4M zbUE0?r$+Rt+m#|hK6J!XGQrrjhpV5>>E7hiQ88L6wEtE-=%>lK;-y1k|3FOE3|cPb zP<-m9Xn-&-n(!6a;U&2fy&)AOj^J6G^l4;LU_Fdgap%2!3H2#A$(IYGRJIk7j}mM) zjasQHdUOdG4lbbB8o2nh%gcvlu`moT{L#yyTykta<-x1Vm#5)@+4z1e9s-B@oAOSk zt2ZfM&I+w>hxX}PX`Y*2zWy3s*A}=TlG-mB@wqj53I65n;~GvR&wo2Nm&~8m%SU+F z0m*-&=f{)V5P$PpW*eL7vR1nzpD+XFa+Gx?Yh#4?VaZSa8x>qhxDdMo(|9~7Ak*_eF09}S?NZj2 zwB3_&!4~b<25eb2k~O9Zromj{ecO#|8*7_$qHbV&?TtX2|JYuV4xPI9CFrvw>3}g8|Ml6Fkz*O< zqV?V&(t0~@YKu)O>i(#a5`r34U#wvT!(Ty44uM&`Em}cAk@mIAN0NLWG^G&etrJtp zuzf8DNFCNg0f=}08ufjJ6Vd#}7rVn4A)!8v05UrJ(2pP!VYxB1 zYdVA@#ls)O0WK{TdUgq(*nknlg9wIglt72*sY6MdM+dXM<}OS)Kcu^LwEvDc=4k6D zAwb087ISgjQM)~^-ui&P@mmp$VhdmHg6$60Fs*rFzfGt1h_+wbQLi4MFAv;Mi9C3cHqr90Z_0Yy^^pe0UGMsbPP877o| zA61qYCWbU7cx=Qdu6A8-LC90IRvxT{kZ=i_nTM0AfLk@V&QpCt(_;53H_P#*>r)py z`tWULLMp8VRQ5Nn!0LFik878x#w4btW~KFNtmqJris;hLDFcdb_mb#v&QRS{jqUc| z0SPr1ZhRB17dtC@;^ycDei*AH5_UiHG(U>#JWUQ?d9uUp;4kLpY!kHe6l8S(6e`4o zM)15{#VOnYor3Unv{8$i3AQb|0i`u%>K;OiEsZA_7(caRCDNUuSn)s`zO!#bCak+% z_={*Yr8Z!x)Sa|dn{jXzwj|fSqm#Viv$^tdC@nn!%*m{fEq${q8Tf#@C9+cEbPZqL z?U1txgwKM%phn7q&-p0+=tz{V=x5GFWfY#>R1_AZ5tgqej}Au^4q7#-zfw_}08YCA zcFUjvFt^|yyaB8g)(^tD2FUgRs5}}vr(SBfO>5uv9+q@9;%o_(E*u{#-F=9Vue;|; zgl80z$f1|i5Bo}7we2h)yufw^#w5gTo~p7!cf^u*f&v6Nj2YDa@B6DO=W8YXb-nShD7L zU$MR1+{VD_#Wo#is&NeY1)h<7D`ED6%29~Q-?UI`h};#PHgwj!L3kyh)EG;zLz^K> zf$>Yi%u#qBNkbdjK^HcJi1V|KT=^uNn%78+qpOrio!knxr^UOm8^B)BY#XOj3m&=S z2xvkIJuZy{0^nhx<~perYEC0`s5pUBg8!t`%o|XPp0q7EXif--#MFfDWk`As&;$9b z^BBhO39pUCm9stV8`m(cWqb{2^t@nG)Q+&3J#1;t{bA>k9I{4$TW38ls9&wTT8!9} z7jjd&h0vtI5nLVmJ*48to|$1-pF?G4mlGb0Uio4Fz$Yl{Ty3v3RIvuy(Y`4_XocQ! ziIfHUkkLu#&IQP3zP5|RIAuUXt~bD3bd!tCSrGMRkAE@F7aR3M&V-}8^-I#R$S zAK$-g79R& z5+Xz6+X_2h7tYhe3>zWIbwwbT4-6wz%m(Sm=*5Z}0VNkRg&!~?}U>)8t%hYrQV2 zW>rFg{;5^aTgm=r4l4ElAysFLKCP7`E7(ZtJLM+Ll}Ibtv@jgN84|qKCpqe8mhG+~ zC=7Gl>=a}`=v9>INKb4G8!hY+CR`^KExU4zZ+NIY2%|&qV~Cmh%oV}x^vd+AN6v-b z6j#q=H{iRmdDp&kUHU?=m?i~XYa(Yvx8g{Tg@YL@ln}#LA$Kxja9)_LZvl0Pl`l*t zUmhgL6}YmYT$6{)gcRBd-BJ+p0~0Vx6}AcTxMtd+Sw=wWHZR;15c0U;*Exw~9Roos zt?IRAzFZM$i*Xy%QQEiUZXQU+opz7w%wBv|O5eR53{`H@TT!)goHaleJbeNh`(ll! zo#z98;WW!mkw|?=f7B(Fc;YQ>xf#^(*hO7q=tthSfYZ1V+^x}3AyA`r2;16e`0;re zS`b%@Z`Mx>0&W*RgfogArZZJpW4{SEppUenu|E?yojG{@DPXHBl>AK6&+v8bDl3h7XVUA*A#8{&UfZC62n{wFh6}*__FqqDD8d< zDFM6R!nYBW{w|9`T4AEn|I#xNxgK^AtGxSG2sZ)3pR?!ci8p6Gem-W?>1mXHRqcpz z2iW_b;B$8xkg87E%{bpuMN46C*CyN=^ri=;syLcZ{sCV?;E`u$(koDB~ z;rSBxP;|_o91>bv`<>AcTksuaaGR^$WR=eEa7di5KGY}DUMYKak}RR#HERZ+yn~j1 z#vG589zRbN4)pe*Yax#ms(3*4N|OhG$$w=15mXXimTqB@j|}REAL{NXNR?(=_tSoe-3TFqKJ7i; zR#4mgquk9Z2xjOap%q<^^_gt9K1t~CDYDB312o!oMq9c?h&mkQB7+WO;Qc8~imeO5 z@!SgLRWIwe*`qrb$sLtF)V5o)#W3}#@zqzpyy*`dJeM5O|gXCjOUD#IJ z*C#XpD8PfCM_Cae`X>+Zq|sJX!P9;JC!VpT``rW5KJHEiZo?;Ih!WKWvFe9}gJ@<2 z5AcxJ|BGt)2L4NU_(tgd+)R7pg#T-Mj{n|Z{P&wn|JQaJS^juW>i^7pN-TfhkbRT< zr*r=Mokn)<|6-@nezP&&&pzh`jObV_4~1GOB~v^=a;LfTh=)wM(^Xx6ua}dS?4(!<8l`i~=jY7Y)5u_SiYO&|77U5& z82<93MZY^%G$eMPFQau~`Jx!t`U$&64*?zHU>(o5Bd@P6r9EoMF(ydz%_Q{oXgwa6 zK7C!1V>IQxgYD2robtJN%$|C|$bLS2YRRUn5LoFz6L|6?6+Io=f|jS4;*E!j;dm6y zg+c)rzmocd8HbDNTb4h)i3X6E|It_Q^xos)se>Y$5C#Z?^ zrm2qQ=Zf4nN#%&XX1lK`R-l#Tg(-bogxCDgvMSE!p-*NFMcL5P+*oxq8&oO8(^#MZ zs)z9biVlWkN)j~CL22*qv}X93{M?LuKIe%iHsr(^CKE!8PIEZ*pB>f_xaNBI{Vw^Uqp?cT+NP6VuWuikOq=(A`>?li}QAx~S&_Qd66 zL`fOSBMc3VkTA<(9_RP!(P27IVi}xfdOPmn-JDn-6Y+OQc_Z1Y41Bu@wAeP=>bx%FYr=7)$F-6c1GR+> zUP08SPw+wydTVSPM#kooTp}%YR*u$`JlUh}BjnV)^>-H!D+dR+*lUc5=A-p`ZX!jz zY>xs4JAUcICL|dIEv2}~8rHoBaOaPWjSWiqyykuNW|IsoIOym&!d1r5mQZSz7LNmq z&Tef+P{=j!NehIj4y;tW8Ca`E+}Qg=-7pfW=Dl<^Ls#wvK{SBrKh+(eei+)?5m6N? zRI~FH4e*8))k|s57aX*$0Bq zoU(ok%t}hf6po1o8BF0asj9A-lg1Ksus!$}lm|q1AsKA_FoQ~BuMl<-oEaHx`$jTs z$~Y73=NKzKecq$`DQV}%a{fhMi7q^$d`ikSU8T)ST_?Pp)HO0&hwWK4?3vj#jC0q( zc7`y?oSPOWLZR@Hg#)RS7Eh}Zgy4+Ijn}lfMY|-;!j^-KpK%R$!|$t^p@5u@_3&=mPfnN<@3U1Z8V`^2~cYbX;r1o{i_=nh+n9!HzMsk!Y_TnhUAKEw}XK}dM zG#)et4Q8yOA8tvDo~R*KlCUJP7&8fCn{xQzr?U2a>5I&<|I(T2my~v)?3OK zw(&whbf8c9wJX_=h1G_e(@w$mn%3FX&nP{@uE0~UfrlWLj#WIcNlD|iC(FTG z?Q`0VR@b^1g~%oH2W5o-*QuC3*Ku5BbasAn$e_t5flf$7)lQJkfAx%-fx#^gukz<) zk91_EcEkohJ5+Sxww|);GfQPnndjY(;|5N2x_T{jNkVc>2N-29KGQ?jg7QpxVE~Dh zPd%jiLI!rW4%VYe^*HzDH`St=VWD)+7=dKisK{I}YWLYmH5ZjbFOaWL&NM|T=6Nf0 z>*VGu8ACOgh`J;|vznBdkq9ttdVsbe1%OZzRVb1s+K z&JtdJb|nMH{QRWF_Yjd#N&aQ5UO7`{(CR~Cc4)m6f^dW3ejqv(^dnlIu!N=q=rP%} z5hAFzO$y@|L2({FHsl|OR6l2qov7`OeY#sG3TaYDu^dE?<2_D*58fKWAL6z98=x?) zFh4eHHYaH4%tl)Jo#a8jz+=_>4lY{a>iw9$JinxIMD3H@yIUf@wUY!}H&>5GM|C0= zvM;TS#3_UNfpu5Yi&l*q-|B%YzF~q#(&6HaAv2P{f_Am>f@~L+21RjG>UvPkFQTG- z7;HWZHvnO)d#*;@t$&T>(*$|fQ^+u!rlNwgI&6x%^r>^IPJ+nIlV81xs$O}Q`lU|W zfV_LfxE~e$ytW>8j=!@egHI3z!s@ieYHrOQ*zf6gbfKn^D9lGJfbKYzvJ(`iu??Se z9U|4p$Y7<*-_yZZ=b>kSv6+uhdlWq|w9=hyPiT*K0itIe788x0R6`g5!y72T-@8zP zO;cc`jz@80YSvU)%2Pyt7%@0HW4cf}WhFrCUSQ4BCWSOocLrd!?dWK>DV*LodfRZ!x>=y!6Pv?3V*k{eY46b82KlZBh?RCS5-`a z)MW+@iuDl?qWZNm?o9Dqyb+UPHkt@Qn3eWNhRgu0C7$UzjU0;9y`7=g_CXS})FRr~HBF}l)&p}s|3gj1R689TmG<`%Y_ ztH+|%W9d2vn6HFTh9J*)`<9rbrs}R)VngRaO&K(4G`IECuZe&7$lznSKOAK9D5Zvv zURg&efkJg=CJQLbf7D4slBhPptAAQT^$g=GUqgF3a#Cd>Q$+_>q=;d}F-Uebz=enO z;RyyY4B_gA{2b**hTGNGzzU=QyZ9O4CSM?=*>r^0$Uuh5|1>cVWC`0!@efE{b$+*? z1S$~M=-;88dwa61M~QODnxY9xy#X-NGI<=XqT>VUq=6Xl<1WE>*vN~1F&(YMoO=HP z4fwL3asJ*?;4?KVkl4wm{)7DH_cc2$&^p}XCQLFd=Bf}f4_kUXe35Kr9#Uhs+Z@zF z@zAxK!Iu#%R9O9d%J+ou@8fAU|vf`g7w z^HmbrXPKV8Dg_j1aloaG0anZhZ-AJf#A#CZ(TAPht7?!RQ4We8roFOa1_n^4N{Y}s z%8#^>;9nH{%$=l0Y&g5om-YzvD@n4`2BF-UZXJPsO-rM%!9yd(++e+B)9w0#t?1&5 z8_fi+0xYcd1Fo(h-())0-BtyG+SQJt{Ty9xc@9ToGOM5v1 zLHz#7q)ZX2kd`X|aF>Wcv6hxP)ZFkjW2R42R4`q4n{+e+*#>1Byb-1jscJ39Pt$!P zMtX+1WsP^cDy|XZg=UgDK5aHw3OXMHCgV3K70bfm!E$sl_|3T#QAyVg!I=ntfE|~g z{p<>_k26Dt;=}MkOq-{khVPVAMo zC;^Yr5=b3S(3)MV@0lKZKd>lRAg3WJs$cHl#Bl4d#DcO3=MjTANxXND^<&SzE@d?- zheB2;d>22lGUJI4US;0?gD>!wS8sI*2#g@Injo?ye9DCh4?8X8QCCdQO{g^IGW?Y) z8%A@_U3_&R=29Ek%-u7vix#jR#sw326&QN}CCovwdkNQ>JX* zx-wn>8d@T<>J`;pt8uyUugy@9@5dl_V>ki7@W25xgp1ejyBMAZv~Ml1Wi~cSElT0! z?EC2V>n^B=C>-?;wF}M3h6AFfSmkfhVXX$KG<;;sGZ^_k9d{0)iY*8z% z7ssNd&beEYDZ{ia)!=pO6SRcH$lV908uB)`zx_fh?3D7kq>+6&sC75S_Q617z!fH= z>Jfg<4IGkHE;lA7Mha$42Lwemz#giv20MkF{5m${!HP#+^me%iQ-L)5T5+^$^yE6P z0OU!MA8s)Zkfb>Dy(hNN!YA(=3+#9aC_v40%28M}NY`U+w_Xo5a?b7=?tKGX*oUzO z*6{-fgQ-XdvRR$@uOc8s$=7`93X7R?@0a;O6?vlb>A*uyNJr#@1|&TmJMt-~dE=wv z$g**4!OZt$ewj(0rE6>4kir|Nj=2#q2fBpZidv?Qyf3uJf?9o2O6Fz> z0)^g*B%fFDqPg|LJRpzEAscid$aXC=0fmVIfU@L{QsUC4W&s3mKJUz#4sL)4bW~Bs zUg5MG>_5%&Y-d437-stZ^b9pPZ0AkhGZRfuaSoE_e>bmWh) ziy+cNni|UFw<+kiUj$INY9f|SzI8Dq|Mnh>*kcP3J8;svCyNQ-g>e2(y=V(OfUp`5 zzoljQVu+KSKd6UXU$&5dYMNP*LSzq78&uKYmNMQF5sDv#>5KXJZVUyE8yGZTo|e?h zNZcY!Q_zST0FkbHJbS~JMCQ$+{#CrypLxbtfi;sS!41EX7 z|82b)hPX9A4?lp$E38+4ji8hN@xydYta4C?2Dk7Y0T+te&)AR*`AxQkPFYm@6Nx|$Rm2&qsACd1$XdS9mnS9)GyUM_ShtB$!Bv6LUXXxy3!H#jvqrxQ|%saDkdQ6 zTL-Jj#k|**b{2HVj-NyXdY>LX>r_Vd)MGvqgm<}5fFzH$l3z z1uTM0hw6QcLKi4)(~-hDIR*^69y*Yf5idnFGw#ojO$Q!+tNKL2g>sxwFDrHCSJuiY zbiWos>0^Bo?6_Gua}eIq=6HzvGRarq=x^ocASok)_S{$1p>Oah6BX*z+Vl{M4;l%p!+#mRjHle^ z6tls}Tlko4{{(vc&GPOUq*G~G0kyF z8`6xzUcnW31Ja&;bynLLZzUXr5qE6CMG4qzPsf5Yr;b^z0=bx_TD7$UmdiA`=h``g zCv7JM<$@v`ttgWSxkC5ZABUwbm7ovwMLvvWYV5Ab=bc=?PMwbaAKuX1h&~jt51VPdX zA}$9rNJOdm&{`SPG|&LE*)ItKPYQJbs#rice6s#mg}Sg`F+3^xZZRV$WaNclUQ9Uo znlYy`t$Oe>NW#unAA;`Ma0yq!tvx`@%pM(aDQK23rowCJR&WC1QE3+nEH) z)kOY&x>bM2#c2)OKZjv6MRBd7U*buvGS}QyM?zQQYfJ_#ItWC@@t!wRGxCTtUYpPA z_zNF@rk4OVVH`fA1(H*s+DiIcDjP`&qqf=($q6cLbo2CTCgTjFX;)MPqV4d1EN%3c zn$<Z5$!6V% zrQbk*qNCF<>Q<1~ABbGLZ|Z0cc~EFWlxwNsc75CLIR|0w;@|J|gOqhE=7CG|tyV=v zbb=#xQD*`M$^Bvz=x8>C8}~&%zZp@cRcssei%*R;hj|hqi_yf;VF-10Ho#62mGGxA;r{lIwQ+vW^R5Th{hv4Oearx1ShCp$s$ZqatNILBy}vKM?ylG_2A zfDy=Pz0JURIWUB8v&~e?t=RSGMxIv{5Tu_*J7m!+pNzNW6qXK+UyKhNV>IixnW5SM zg5WdsqNnvM5%s|2psO~nq30d_=6fvvi+U&t_5MMbcTX#mDRy6-Csk-Ck6=0K$8n~? zgj{x1oyU_QMn$i6l%j|GU0;2iGBUXQ}TOG_rsmP^b;2MjBkS zYWE?nmxod9)<&0i&g;CqwR@72x(V1jHa>9gB@?#>xhsKV?yX|I>u8w(Mc6H(eWt$T z6)2Kl!hm&iN0}EwVy(<`x;O-=fD}-E1<=M%cgS9HhR4zYMsrUf6a1xHcnnuK>i53v zF@0QMJHl#)=uV>!n)*as<=u0sTip55a3WlC!A@<0C>d!k7WIR7D}n ztEJd!evh#0v8Zo=Ctn@s^pF4AwfI|i!=J+Zf5Wx-hgtD&c1qKWu@W%-eeb^5@6Mk8 z*zf+oj9z2DG1FlW$=K%U1^rf#{ifaT>e6DfUAxY8&N_JU;S@k2xQ0&nl9&VO5OZjC9z(j?uir}O#HVz5uWLTEe$E%H&5Pmm5sIp^ zB|zq8RF0{Tl8rkwG6WfJIe5I0X3R=`Z(DkuWh7b0mwQn2)E}JF=Xy#bZ zzK_$78EY#bE+;4t+Y=Zf1m%|QuNWu|tH)(|11&o+M+Xd0B#=b<>FUAvgI zCmsur$E_KS?`Yv__>Q9 i&zkAY-A$7zKd@TFCS40;j4mY3d&f5U=$20>kpbWaEv zMqigQ6->(Y%tzHEsF1)@`>X~bYx$v3^4*5s-o#QnqXygqD-rD{5Kp3DT8{VZlj1tfneL}+3xRXDjzYawov}NKGorh{JX@*(%&i0DI8 zsAh&Z6MyZ8ScAO4@(r8R9D(#3d4O6jt?_qMjEBn2?~A69vlNp7%N4eG;;fJqV{P+q z455s+O7vKzhIuPDjBF28zVGeuV1Be%hGhXiJ#nMPB$`>lB@vqnt%gK zNtxG*3f=eZlA&{W8MG|M$lL1H!y*<6lPMWYjAXSpKm_z-gH~G{P&e=Q3q{sj55^@)!!S&86_axY}f+D4a{~s zl>K69+l{Lc{768etX61mg+>604}cV9j-XuwQ-GE*DTHeQPB8l^a&WZ-5(gVpULkAIcIs)%~GeQoo3Y ziLDGyx+9*=Z0X(<_>8q~)L%!jLC_BJX3<|GJzR6l_%^l(;Q9)Jmg_yaOqI9&4LjPs z)5po&$67aJ)nr1I2(G)S=Cc)(W_#!N|NUYK*y*{qFSo4H#R9Lnk|<5(aq4HfA5&&O z+?!M5v%dUL2}v-|Q^JpPRe`&%jNW@ffJW48saWO!))C;pqB+wD9(s zAyr0fc&xnKfkK7$IT983fAD`2p`Au(QsbWg!Dkoj91lHLF8K`JaxK0CBL3s>&0l?% zB7jpJc$A(Tidi}8JddKl=*FGk>pSfi+^p7E3y1<)y}-N9_hIVlNCF1y~}EWT4PJPP6HBgZPy@t z@nX6b>c&88>_S#qbL0{-KV6$hsz#U|RLac6DnGkSKz!pq79|5q*J&JbX8{j2>F30g zkfxyWT=R#R#YfK%SYP^{ugnllI@oowFkX;@G*5X@hPH3h2hWD^i(s%}iJ(GfilGeWS}Ff$K1R_J#FVF3}1fRsnk{CcD-eJBX=o z5~whKwP#eP{nbbWbWLs2hY9%;{%Bf7;T&`v*7(myssydMHdYH=j1qdGAh#N@a;UJasgh9T{_8p~2+sEHP9Bp9ZRv=*) zcrb>r&EWi0DVgdSECQD>|3DoN^gPc?tMX*FzQS--Jas#`joa{-#@%7ooptMWCF~b0 z{`>G?IXokTB7JXSJ_fpbkb%&Rcs#xNONkhpX^M)*PE@}qwK&hM)YTxQ7v=LzH**aM zLHGd7foPFeY;~9#TAj|}5K%`XfiHZQ19>Cf*9)+BMD8_kM8gb(aQi~Pvg@D&S!}E$ zkqtS(rBR=UYVcF3^9~Y1hFtMjTprwO&zo=J!=5fgY#J1Hg!jho&2X$(^GF_$@$x24 z3-QgB$KtARl#%fSL=*Rw&+}mY2;?+k9FtC2K@HW#hKd)cFlIeWzv<(wwpxfXqc2j1uaqb~5%^o6W6eS3AiP4A9GI5@=F|J?1j(tfc(*{8V3 z@DqL(Cg|9YO!Eh4rkguc(^S6Uw!pHy(QaVa+d1fp6A{!m#q!AJS79L4@d z@89ZCIAgTiq_;WKfFKI!Hr{ww z2lP&z_QgQ8pw?-7LQdPVfEK?ONhme6ct7~39jY|Nox9BVC*4WByx()H){cGeRynDK z-ntW-OP5?Pc)(6+0aFR^#sd+g@1jH3DX7fqEE3N&RguSwM za)*F$y#l7ji%WDqD?@r_g`baUf2!JCofD4b@6A#z~XA`kG?1F?(-C#AH-Oa9wX(&y8WnoxwXD&_2dw zEo#eEa;-DT1IKy?IV)fHlDc?{_4I}LNv?$h&dd{&mN19pFRw;MRKt}oiasDKvCzNN zTT5Th;SoFcFPIrJRdZMsj!E!vyO7^wxqM6++->eYKg5Q$S>&&D&pUb(6J80&|B~sM~iXrYnAs+6L86(Iz|5&%uT;e~QQV;e2ZTs+_b9 zVMFwWc4BD(;We!-RFLltk$r6ekJGFztpU|gG8jo7DE zj06F&4BJB#fAzhw4A}9*O7SW*Fl~MtqBfD%G9396jHowu->&f?f3z09o0uU07k z{R=VCGwyX=1OgzBel-|}0uD}a)e>8f!B_*fm<7)#KYNUD=oH8(dJQaTuOz#~fTMiC zf-xvhm{-X74HW&-%J`$S$4PAkDPeK0^xG|PKR)l#L2Yk4rjxXJ|d?}2m@a~8>tY;E8*t>?-BpC>xl3+fD9lOyK zuygPC%`r0yq$vrTFFE9bI37T=pxX$nqXGHH1mz= z6uM%8-L2I9`n=MxWHo!fmupN5U^lH8=MM zosv9SN2-Ru>9`!FKM<>7lj$X?SsHTnjX{=1m^o1Lc3PhqAe$JTL(@Q>BJ_xuQz2K3 ziDkh4Iv!x_*5Am+Ez?&%%}H#fsWTDI69sL>Nrk2uHlda@4L-imrpK#fbUe|;=axXT zngnkW)U$B)`Xu=Zoc!A4Dl!nW=~k2P&;#QS-t)+Q1x@{Z_U_{)?S=whTztatz;)q} zp?er6m<-HUO4SDvPE>Yx8 z#r|nC*&X{-AL2^(K>CDhdSHIb-DYp=YDt0~0J|MK(s{Yqh3H*dAu{G;gY7qnX4}6S zp5JHM_oz=X$!FN3*QL}|0$l#q3R2fcv(UQkc%$aO%&ZC#5tGol{+;k^#yRr#a_|Gh z>9UCCtC09v(xr152zTS}Z*-cp0Ecq$HA61U&iEJeE)!6W#xcIG*&eGA@(ssmoZ~0j zU$vQDw%x!V<{X=2Hw<3hPM*K|FRhjtSM(FATmgu2z;GN-LHY$P?vze2XADJ#BM3J9 zV?MAPE!!qqL=N3mI6by19*Qg=1tBM9>R+A*cY6p!NIA;Rs)-@RbP`i5u$#_<6cQ0tse?BY>Y zSAYk=Hq~AKzDfL&{A3XWt&z-+b;>ox)h!g`y4IR6pxfgjHRECdVVy4kUjTksNHEXx z7LP_Xn*a52eS8F8DqqnV0AQ-etT_Nd9tE?BK<=dl@JO)fkSvyWJYdvVDUgfpOO|4^ z=&FFHfEh6Myn24v|U4<%&#GTy<3J%`gVBIP6(uQ6N zF3&U)zhR+8`z0MzxvZCt5F~!Wcm8Ta>{R+Ni#kwi0%_@{KWM7NHE2}>W5@1?li8UvuwQVR z&gT?I*Sjgv?ZJ%Y;muVmfqpUC9E8pv7d}v1Y5Mjt-#L-)=TSP*4&5o!#1v1(T`P0u zlNRk;AYAY!iNzEXI1{!s`wBp2YtF6`z~;?GlnF;HA8* z##QD@&YNJ5%Mjo`>CEI0>g}J2u67`i*whMXhFyYvn>5gm?*I*A=4~h8aTRLSGq!OL z@z79ndo6qoOS-`pt~TLqJvF-+1v(NE-`>(g+abiT&AUb*gZI$@hkFVFI657X^BWum z0>~Z-gD4pvN{iAyrm*rvB-8H`ho@fJVFsAH^YuRIgb=3T`^kKws_k^mOA5Na-jEv# zSS#aX%VjE&wlB%ouik#H$y?|KZyc1h>GAJfI0;hwRfRP-eYzMNy!(s`TKId=3*yC_v821DNP7t>cWdT2${GI6MD9A5HC*;>v@-QTqd#8 zNk^_9PD;(nfNXw9Qnn4t0G972Ob`7wzEiA9iHA1$D zzA#Y3ALoE9N2|9&O?G|ob-#N7r*99y3v_f$s``}oLmV%wLE0nPT3g@~Y6cf8P~Jw* zjr8$60KcvjjB;=P^dMT4;BL7(O3dAIOpvQ;7-OqIyy)YHW2QqPL2chg8Id%I`Na#CFn@9Dw5QVX91#&sf zmDm%>EQS-odc0KgQw|L2o2}oRM`K-8vE{G3f+BbwiNm=K0Erng8wfzq(>Hxvx%wPU zeQUQ7$C_6c{^aS&EQTd`9P?5JR-v8^FOPPRJFrXzmWP5$@+5ieebeuKh+=GRY+O2% zBP;bMd(Xt4DfWdzu;&c{%MC>*ofZ{w)%Nml@puYZLJ&2fV)zm-we2p%Eo04PH!B?eV0?4TU*j?si)xm~D8c5>^d8P$@Cv0` zBWwhKE@?kPLvi5C3kBB+kasl{Rxx#FDN@qf6q<%D`n^J;{Cs9-v8j}Xc92} zLAd!VW#^A~{#3;OLD~7oEdHG3|2tfV>F;qZe_Q)Mp*93ee?+(Z0oVEa-2P?ee`SdM zKf!hWIL)7{{KGQ&*LnQGruhS|^N*GPd#uxMdeFaco&JA`Yiz$ML;oPIF)}hT|8c7S zz-;|zuG8N!TWo*DtTFtJcgGq_qO-HAwC9qjvZ=Uz8f%?ia83<7~T{>VY-arJdJt zjOyJT-?Fu1;-R48+KBnt{q%5{ zO25(n#moI!sN>U%c3_HXjJc}7Dcesq9$l_xMR=A5(qy#v%1XbmdY7CG=Tv4Y>T)fH z{?|-6PybWu#F78|Q5gzZ$ZWW5WSd~Q=t)MSfB+#<90_zA7B_6q40_(7M+`nTz+nI> zJ5=mDNslO(^WD?HEHCCS$?t>!{dEvK5_#&G%GADT2FU>vKBs$X!H(*&x}@*mv2!M8 zqY90vwShfqg)lTFH1Yie2pS&)U&%Q8jf@aE<_y@@14>L+FM1r2FZJ5_qe-ucx4z>j z#Vrz%!Dszs6%u6YRObUu5y72f{a8n(ULUJ3<8)i>&aa3SE~}}K>zD^)1l7@4Z>OVh57WSIrRH%Vl}Ih8yA$#JH=t%pocmS00ShXlM*6( zru`O3Hgbx3S^<+(^iBa@`a4QtX)LxXJTPg-AQDhg#yovlJDC$PUtVH<_s2&-XUX%V1? z0P972A*V>U2+n#wBFjh!@Nsl@B}g@Hq4=mDG}!4yGI7^@=;Ts`4$OUbZlW z-7go9U?E&=DcQ_fkq>GVx4mkbSY?3Z8Z43 zB!EQO&~Qwv2QlTn?`@WVZ&TckMd%e!gN*~{#8{gPT;%-BE6d3{n!6+ToCDAU5JvXc zaa9?lGfp#wHt5;-juquJT$xQ-DD*Y-W?`pN$6DmK{XIIM6|^9N9C|RYR#=lo6Us?4 zW?ZM~obDE<9!%mY!aTz=MJ6SlmC9jtU&S`jMGhOxsKA%c;SMJeDp`T=Ai(R)d6&;Q zR4^bRaQ2WcLb18^IeM&ijdJO6x#K>a@)Wt-QE_?yaF?HO_EaGq2@@wEC0B)qFpM4@vqlHrLMePFh*# zZXCeL8)@UoR4})ERJFs}EM~JZEX22hTh8N+^Ng7-&1^wm8S5IhS@(Dg{X35{DMV@hmXBnqIgFEp>F&}@-tYMenx zm8Wb#JOYuvjGW06(3<&U#3DjmZi{Wc>_$HSGw{etEW>JZuuO<*)PbX3hc z2S|`&R1c6K;Zm=vkP&v8(}J9VXi-;(vB9UI-(;VRQ2SMX(7dT(@RBcbI^ef!5;SIR zT1Y2rmlxb5e?^iHYvh#oozr@-S<--ZBmhF^f?ph3HMOGh6yr4Td4G?QYFE8$xl zx|_YDgA^NK7H^fSa-<4i?Ho|{cf0$ovi|y3Cs+e%he{_$(17llkF6i%WA|%-lxIG6 zpqWzsGwqk~D?!nn0sz)_n-RE21Jqd=)g$JU7+6n(@*on4=*HAt0AR6Xslb~A%DcL1MP_g zLh~~!KpX6}M|+2t`2#7GrXGW=I#Q4u1k~YrX;AENyP;^zcwkR#FPS?;=63=U=fJ~a z_}45JM#4oZ^mIs^q#x;&J`QFSNZs(rJ4^GDqxyBtvg@ePO@cJyGxe;^DS2q=!!;&w z5uP?!2CqCY{6fu#2z0_$m+*H=yHCJ)$6z8z6#?^P2Cr`s3_d2}En3Gid(oIoB=4LKnCD0lux5NF=h_ITIUmW&6 zul;F~>i{%lPbH%lgGf;4U67l^(F7!fks?e^cYZ0(B5KCp3ixJ)za?~Sqc<&n6`-VE zKYIMdtwHkByX5pr;7w$@xC|jy&PAD-k~us63GCF8!PKZ6%J7#byUNgrwe(?kegQTM zJX-F|GdMq#pd8H@r6`E9kCL`B>D4z%s=Gw%dD0)yC;@9KF&`+&rnJvV=>r<@&eB9c z^V1R3S7uuH~>3z7$1>}&HAZ9JeZ0p1`x?iYp-#RNBqH@f! z7!`KSV}?A!@#V>o+eHp1NP>wTNbOTiD_+m+2Lz-XlsZ7B-a1e=x9 zc~RAW=>0%Jwj4bPPRhQ5br_3)63>E4T+U>$IV$Xeg0^8W8_2EDIHwFoXjWi2XQpKP z9rZ~JA(?WFgGT@b4OO!^u0x>Ih}}K3=+N{Xo*}-kKm1&|BR{E~d{I%^x@sB;hluAv zj_QevUO=H-kDVcm+mu(f*CJf&Ykl=VlA_+v@bAp`1bio@l;8ubXjB-;cr#e#c+*k+ zRr9UDIzCAp*V|m);$o3%KDtgyZe+zZCs{8Pbw7%ofq;>_-_Yp1JP|q`1EDGSCBGaN zo9F7HJj-rn7E8X?rONbg)h*J#u?)g;g5<=Vcud*gtbpq3_9uU2CUG4~TQ^UOq6!`pN`p*7 zf`9@ZYqVd|YXz~AfGI@{Nz$?c6pdd)o=`Hk7QR)b;G zo-xxv(TOR3phOyr)$_~SRUkN)xu9->UgeXUi^ko3Zq*xZzEl8OthV=RdYW#umCa8t z2-*Y@(Twx2P)-&%zPKv#oojRrGGHn+4R8iMms;v_3`U>m$8`DSgzsogl$W{9#5Hrz zNh;L#u<~ghMFFSxyug(VlKB&Iz`oS@1LJ4dwtr08ajTFTIDov4{uMDr+?X4jChIM= z+8cHp26vO?L)K%lGEET!2>14EJq?A2Zk51?qTYSuZUgdb+wcT7tW1~pFk4ow96JC^ zOKM)Zsd#d_o8W+b7qVy4{(Eojiamcqa}G@ynwcLzh90JecP9onjHf$r0*S28lhbnZ zguMEh&YAxGrT!PHqFemH4dAR%P#8ISp*qMs&pnZgu{J znZ_8#IeGcQTgS;+QVx`5!}C6EubkS$5h&GD2FymWzaUifr2j?sT{^jSHusm(JNq}C zM|7TdrVZaWo6cWrfMGqU#X|2jc>ZE129VC$JB!U6;rR-L)tT+5Lfq!enCCky@j+NM zy9ZfTN(EQ;JkR1{CpqY2KMH5Y%C!X@&!XZQ5n{pxj97C|fnksjiZQ9L8Du4j&(8C_ z_0(3or^togOK}9@*)uIFRG3;5Gkv?g%NQPp-!M?n`80J`A3tV-V3>F~MP}r?CS0Vu ziFR3$!yICojq~ST1Gew@h&<4;NUMs|>k}I=x^04>^Yl|<b*~02E(fGZ)H1~^wD(xAkv5D75|(PEL|W-5pk#Mb~;z;a#N4;)8r#R;)Nk1{=Sz0LBcw1}v8O&BpB_A0Cv zjNmjAqn@dK#qmULC)k-EUf_TucI8|tDh#S&(WZUyed4BPu8^U#=+KejmWkN8G;dF6 z{s>0#QNWhDrdf7@cvr6Vl}hS&-tsIO8bI!YmF|&M2{AHFp|>#1q_E$^TmPt+{&4fb z4nu8f+E7WyZ06KLo$k}ee|4VU2og>C8z zyUvxC!`v4{)<{WVGRT9m^(IoVxN&eLKT2HvErLq+1x#Io``2?~ye|v+Du_ze10c`x zmrdUL0aaiX_zT8BwC`jM#&wNt2gL`?|HyN$)MikEgtxt-&PGqj1892d zjGM`9u9qkl$QV%~$Uvk0%uTG3FLh0p&8out^#T?dJJypdtrE#>GY9BQf}FJ)rCY89 zn&|dXuTa1<{Nqd9d>6znI}rIkz2R7L^3lmqJ>r=$ILRpMcmnyOAm-Z*3>Hzv+cxFZ zsaNqwaJYDUk^e{Va8N|8r(Wp`mJ4Cph2cw)G9fXpnqqH8=Lj;p+-oIdau*Aa!iU7# zqim0m;wB4U`z-n6SB_<~@aU>Z>`v@i&pCJ|mWIIf@G*VZ8T=R^2NZ5$+{Dmpw*E1` zT58KnhQMrRVHCgaJmVg*$MZD?x9~`t7(rA|>u+Km;qDINAs`mF37~M4#I3yM0$*Or zc`4&n%a`F?7{ES|_{=?}fX+owEF`=N+eI16tdla1mWdBgJAGb22ZN6?Dr_UKya$J0 zJh?MmkD1roR)#feQHMpODlta7utfZ(Hb+4Hx`^}4A}Gc7By=~9YLlo8>0>0uYi*Hd zy#-TI;04YiK?Z#|(LzsP&0PsvWlEQ`xrL`8W4mf60D8mXm1BnE?Opj4Y1ZvvfmaFZ zPTcniYHpP~L}xr_p05%Zr{u7=Em+2bu-NX?T(%(Aw{?=r5APILuY_8gpqD~NADUsv z?e>Hm=VLu#*2IhFCfW<)=|~Tg)B($`31*mQ=2b0)-qTQet7h|D_kMfY9zMnA`|)&>pzYmd;@jnmA

m&92uV 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

p|$}=t( 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

_xK5ZJ-u7K2qppVQc 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

gw)tLxD?!mXRkL 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 self.cw_test_next_ts: + # Send one on/off pair of CW commands (with msec timestamps since start of CW phrase) + t = f'CW;1;{self.cw_test_next_msec}\n' + self.RemoteCtlSend(t) + self.cw_test_next_msec += self.dit_width + t = f'CW;0;{self.cw_test_next_msec}\n' + self.RemoteCtlSend(t) + self.cw_test_next_msec += self.space_width + self.cw_test_next_ts = self.cw_phrase_start_ts + (float(self.cw_test_next_msec) / 1000) + self.num_dits_cur_count += 1 + if self.num_dits_cur_count >= self.num_dits_in_phrase: + # Set up for next phrase + self.cw_test_next_ts = ts + (float(self.phrase_gap) / 1000) + self.cw_phrase_start_ts = self.cw_test_next_ts # Re-start beginning of phrase + self.cw_test_next_msec = 0 # Start phrase at 0 msec + self.num_dits_cur_count = 0 + + def ThreadPrinter(self, *args, **kw): + # Call this to print from (possibly) the sound thread, which must not be slowed down by a print. + # example: print (x, y, end=' ') ----> self.ThreadPrinter(x, y, end=' ') + with self.thread_lock: # Call thread_lock only once; twice will deadlock. + # If print request is from within sound thread, pass the print request to the GUI thread. + if threading.current_thread().name == "QuiskSound": + wx.CallAfter(print, *args, **kw) + else: + print(*args, **kw) + + def PollCwKey(self): # Called by the sound thread + if DEBUG_CW_SEND_DITS: + self.SendCwDits() + return + # Check Quisk key state, send to Remote Radio if change. + # NOTE: Timestamps enable Remote Radio to overcome WiFi/network jitter + ts = time.time() + if not self.cw_phrase_end_ts: + self.cw_phrase_end_ts = ts + self.cw_poll_started_ts = ts + + key_down = QS.is_cwkey_down() + if not self.cw_poll_started: + # Detect Quisk startup with CW key down + if ts - self.cw_poll_started_ts > 0.1: + # Check for key down only within first 1/10 second of running + self.cw_poll_started = True + elif key_down == 1: + # Quisk startup with key down + t = f'Quisk is starting with CW key down! Tx is on, and Rx is blocked until you release CW key.' + self.ThreadPrinter(t) + #dlg = wx.MessageDialog(self.app.main_frame, t, "Quisk start, CW key down", style=wx.OK) + #wx.CallAfter(dlg.ShowModal) + self.cw_phrase_begin_ts = ts + self.cw_poll_started = True + if key_down != self.cw_keydown: + if key_down == 1 and (ts - self.cw_phrase_end_ts) > self.cw_phrase_break_duration_secs: + # First CW key-down since a while ago, re-start timestamp sequence for new CW phrase + self.cw_phrase_begin_ts = ts + cw_event_ts_msecs = int((ts - self.cw_phrase_begin_ts) * 1000) # float secs to int msecs + self.cw_keydown = key_down + t = f'CW;{key_down};{cw_event_ts_msecs}\n' + self.RemoteCtlSend(t) + self.cw_phrase_end_ts = ts # End-of-cw-phrase-detection + if DEBUG_CW_JITTER: self.ThreadPrinter(f'{ts:10.4f} {key_down}, {cw_event_ts_msecs}') + + def HeartBeat(self): # Called at about 10 Hz by the main + if self.closing: # Don't try to connect if we are closing + return + ts = time.time() + if (ts - self.remote_ctl_timestamp) > 1.0 or self.first_heartbeat: + self.remote_ctl_timestamp = ts + if self.remote_ctl_connected: + # Send keep-alive heartbeat command + t = f'HEARTBEAT\n' + self.RemoteCtlSend(t) + else: + # Else continually try to connect + if DEBUG: print('Heartbeat Connect Attempt') + self.RemoteCtlConnect() + self.first_heartbeat = False + self.RemoteCtlRead() + + def RemoteCtlSend(self, text): + # RemoteCtlSend() may be called from sound thread or GUI thread! + # self.thread lock (also used in self.ThreadPrinter) protects against thread collisions. + if not self.remote_ctl_connected: + if DEBUG: self.ThreadPrinter('Cannot send if not TCP connected:', text) + return + if DEBUG: self.ThreadPrinter('Send: ', text, end=' ') + with self.thread_lock: # Do not call ThreadPrinter() from another thread lock! + try: + self.remote_ctl_socket.sendall(text.encode('utf-8', errors='ignore')) + except OSError as err: + errtxt = err + pass + else: + return + self.ThreadPrinter("Closing remote control socket; error in RemoteCtlSend(): {0}".format(errtxt)) + self.RemoteCtlClose() + + def GetSmeter(self): + return self.smeter_text + + def RemoteCtlRead(self): + if not self.remote_ctl_connected: + return + try: # Read any data from the socket + text = self.remote_ctl_socket.recv(1024).decode('utf-8', errors='replace') + except socket.timeout: # This does not work + pass + except socket.error: # Nothing to read + pass + else: # We got some characters + self.received += text + while '\n' in self.received: # A complete response ending with newline is available + reply, self.received = self.received.split('\n', 1) # Split off the reply, save any further characters + reply = reply.strip() # Here is our reply + if DEBUG: print('Rcvd: ', reply) + if reply[0] in 'Qq': + print('Closing Remote Control socket: Q (Quit) from remote radio') + self.RemoteCtlClose() + return + elif reply[0] in 'Mm': + # S-meter text from remote_radio + self.smeter_text = reply[2:] + #print ("Receive smeter", reply[2:]) + elif reply[0:6] == "TOKEN;": + passw = self.app.local_conf.globals.get("remote_radio_password", "") + passw = passw.strip() + if passw: + passw = passw.encode('utf-8') + H = hmac.new(passw, reply[6:].encode('utf-8'), 'sha3_256') + del passw + self.RemoteCtlSend("TOKEN;%s;%d\n" % (H.hexdigest(), self.app.data_width)) + else: + print ("Error: Missing password on control head") + elif reply[0:8] == "TOKEN_OK": + self.app.main_frame.SetConfigText("Connected to remote radio " + self.conf.remote_radio_ip) + QS.start_control_head_remote_sound(self.remote_radio_ip, self.remote_radio_sound_port, self.graph_data_port) + self.CommonInit() # Send initial parameters common to all radios + self.RadioInit() # Send initial parameters peculiar to a given radio + elif reply[0:9] == "TOKEN_BAD": + self.app.main_frame.SetConfigText("Error: Remote radio %s: Security challenge failed" % self.conf.remote_radio_ip) + elif reply[0:13] == "TOKEN_MISSING": + self.app.main_frame.SetConfigText("Error: Remote radio %s has no password" % self.conf.remote_radio_ip) + elif reply[0:9] == "HL2_TEMP;": + setattr(self, "HL2_TEMP", reply[9:]) + elif reply[:3] == 'ERR': + print('Remote Radio returned ' + reply) + else: + print ("Control head received unrecognized command", reply) + + def CommonInit(self): # Send initial frequencies, band, sample rate, etc. to remote + app = self.app + # Frequency and decimation + self.ChangeFrequency(app.txFreq + app.VFO, app.VFO, "NewDecim") + # Band + self.RemoteCtlSend("%s;1\n" % app.lastBand) + # Mode + btn = app.modeButns.GetSelectedButton() + if btn: + self.RemoteCtlSend("%s;%d\n" % (btn.idName, btn.GetIndex())) + # Filter and adjustable bandwidth + name = "Filter 6Slider" + value = app.midiControls[name][0].button.slider_value + self.RemoteCtlSend("%s;%d\n" % (name, value)) + btn = app.filterButns.GetSelectedButton() + if btn: + self.RemoteCtlSend("%s;%d\n" % (btn.idName, btn.GetIndex())) + # AGC and Squelch levels, split offset + self.RemoteCtlSend("Split;0\n") + btn = app.BtnAGC + self.RemoteCtlSend("AGCSQLCH;%d;%d;%d;%d;%d\n" % (btn.slider_value_off, btn.slider_value_on, + app.levelSquelch, app.levelSquelchSSB, app.split_offset)) + idName = "SqlchSlider" + value = app.midiControls[idName][0].button.slider_value + self.RemoteCtlSend("%s;%d\n" % (idName, value)) + # Spot slider + idName = "SpotSlider" + value = app.midiControls[idName][0].button.slider_value + self.RemoteCtlSend("%s;%d\n" % (idName, value)) + # Various buttons + 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): + if menu: + for nid in menu.id2data: + menu_item = menu.FindItemById(nid) + kind = menu_item.GetKind() + if kind == wx.ITEM_RADIO: + if menu_item.IsChecked(): + self.RemoteCtlSend('MENU;%s;%s;1\n' % (menu.menu_name, menu_item.GetItemLabelText())) + elif kind == wx.ITEM_CHECK: + checked = menu_item.IsChecked() + self.RemoteCtlSend('MENU;%s;%s;%d\n' % (menu.menu_name, menu_item.GetItemLabelText(), int(checked))) diff --git a/ac2yd/control_hermes.py b/ac2yd/control_hermes.py new file mode 100644 index 0000000..852558d --- /dev/null +++ b/ac2yd/control_hermes.py @@ -0,0 +1,54 @@ +# This provides access to a remote radio. See ac2yd/remote_common.py and .pdf files for documentation. + +from ac2yd.control_common import ControlCommon + +class Hardware(ControlCommon): + def __init__(self, app, conf): + ControlCommon.__init__(self, app, conf) + self.hermes_code_version = 40 + self.HL2_TEMP = ";;;" + self.var_rates = ['48', '96', '192', '384'] + self.var_index = 0 + #app.bandscope_clock = conf.rx_udp_clock + def ChangeLNA(self, value): + pass + def ChangeAGC(self, value): + pass + def HeartBeat(self): + ControlCommon.HeartBeat(self) + args = self.HL2_TEMP.split(';') + widg = self.app.bottom_widgets + if widg: + widg.text_temperature.SetLabel(args[0]) + widg.text_pa_current.SetLabel(args[1]) + widg.text_fwd_power.SetLabel(args[2]) + widg.text_swr.SetLabel(args[3]) + def RadioInit(self): # Send initial parameters not covered by CommonInit() + idName = "RfLna" + value = self.app.midiControls[idName][0].GetValue() + self.RemoteCtlSend("%s;%d\n" % (idName, value)) + def VarDecimGetChoices(self): # return text labels for the control + return self.var_rates + def VarDecimGetLabel(self): # return a text label for the control + return "Sample rate ksps" + def VarDecimGetIndex(self): # return the current index + return self.var_index + def VarDecimSet(self, index=None): # set decimation, return sample rate + if index is None: # initial call to set rate before the call to open() + rate = self.app.vardecim_set # May be None or from different hardware + else: + rate = int(self.var_rates[index]) * 1000 + if rate == 48000: + self.var_index = 0 + elif rate == 96000: + self.var_index = 1 + elif rate == 192000: + self.var_index = 2 + elif rate == 384000: + self.var_index = 3 + else: + self.var_index = 0 + rate = 48000 + return rate + def VarDecimRange(self): + return (48000, 384000) diff --git a/ac2yd/control_hiqsdr.py b/ac2yd/control_hiqsdr.py new file mode 100644 index 0000000..370e739 --- /dev/null +++ b/ac2yd/control_hiqsdr.py @@ -0,0 +1,40 @@ +# This provides access to a remote radio. See ac3yd/remote_common.py and .pdf files for documentation. + +from ac2yd.control_common import ControlCommon + +class Hardware(ControlCommon): + def __init__(self, app, conf): + ControlCommon.__init__(self, app, conf) + self.index = 0 + self.rx_udp_clock = 122880000 + self.decimations = [] # supported decimation rates + for dec in (40, 20, 10, 8, 5, 4, 2): + self.decimations.append(dec * 64) + self.decimations.append(80) + self.decimations.append(64) + def RadioInit(self): # Send initial parameters not covered by CommonInit() + pass + def VarDecimGetChoices(self): # return text labels for the control + clock = self.rx_udp_clock + l = [] # a list of sample rates + for dec in self.decimations: + l.append(str(int(float(clock) / dec / 1e3 + 0.5))) + return l + def VarDecimGetLabel(self): # return a text label for the control + return "Sample rate ksps" + def VarDecimGetIndex(self): # return the current index + return self.index + def VarDecimSet(self, index=None): # set decimation, return sample rate + if index is None: # initial call to set decimation before the call to open() + rate = self.application.vardecim_set # May be None or from different hardware + try: + dec = int(float(self.rx_udp_clock // rate + 0.5)) + self.index = self.decimations.index(dec) + except: + self.index = 0 + else: + self.index = index + dec = self.decimations[self.index] + return int(float(self.rx_udp_clock) / dec + 0.5) + def VarDecimRange(self): + return (48000, 960000) diff --git a/ac2yd/control_softrock.py b/ac2yd/control_softrock.py new file mode 100644 index 0000000..f9754cf --- /dev/null +++ b/ac2yd/control_softrock.py @@ -0,0 +1,9 @@ +# This provides access to a remote radio. See ac2yd/remote_common.py and .pdf files for documentation. + +from ac2yd.control_common import ControlCommon + +class Hardware(ControlCommon): + def __init__(self, app, conf): + ControlCommon.__init__(self, app, conf) + def RadioInit(self): # Send initial parameters not covered by CommonInit() + pass diff --git a/ac2yd/remote.c b/ac2yd/remote.c new file mode 100644 index 0000000..6f53e4c --- /dev/null +++ b/ac2yd/remote.c @@ -0,0 +1,748 @@ +/* + This software is Copyright (C) 2021-2022 by Ben Cahill and 2006-2022 by James C. Ahlstrom, + and is licensed for use under the GNU General Public License (GPL). + See http://www.opensource.org. + Note that there is NO WARRANTY AT ALL. USE AT YOUR OWN RISK!! +*/ + +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include +#include +#include + +#ifdef MS_WINDOWS +#include +#include +#else + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#include +#if defined(BSD) +#include +#endif +#endif + +#include +#include +#include +#include +#endif + +#include "../quisk.h" +#include "../filter.h" + +#define GRAPH_DATA_SCALE 163 +#define MAX_UDP_INT16_T 600 +#define UDP_SEND_INT16 200 + +#define REMOTE_DEBUG 0 //BMC TODO: Make this a configuration option + +static SOCKET remote_radio_sound_socket = INVALID_SOCKET; // send radio sound to control_head, receive mic samples +static SOCKET control_head_sound_socket = INVALID_SOCKET; // receive radio sound from remote_radio, send mic samples +static SOCKET remote_radio_graph_socket = INVALID_SOCKET; // send graph data to control_head +static SOCKET control_head_graph_socket = INVALID_SOCKET; // receive graph data from remote_radio +static int control_head_sound_socket_started = 0; // sound stream started on the control head +static int remote_radio_sound_socket_started = 0; // sound stream started on the remote radio +static int control_head_graph_socket_started = 0; // graph data stream started on the control head +static int remote_radio_graph_socket_started = 0; // graph data stream started on the remote radio +static int control_head_data_width; // app.data_width of control head for graph data +static int packets_sent; +static int packets_recd; + +// Receive stereo 16-bit pcm radio speaker sound on the control head via UDP +int read_remote_radio_sound_socket(complex double * cSamples) +{ + int i, bytes, nInt16, nSamples; + int16_t buf[UDP_SEND_INT16]; + double samp_r, samp_l; + struct timeval tm_wait; + fd_set fds; + static struct quisk_cHB45Filter HalfBand; + static struct quisk_cFilter cFiltInterp3; + static int init_filters=1; + + if (control_head_sound_socket == INVALID_SOCKET) + return 0; + if (init_filters) { + init_filters = 0; + memset(&HalfBand, 0, sizeof(struct quisk_cHB45Filter)); + quisk_filt_cInit(&cFiltInterp3, quiskAudio24p3Coefs, sizeof(quiskAudio24p3Coefs)/sizeof(double)); + } + // Signal far end (server) that we're ready (this sends our address/port to far end) + if (!control_head_sound_socket_started) { + QuiskPrintf("read_remote_radio_sound_socket() sending 'rr'\n"); + bytes = send(control_head_sound_socket, "rr\n", 3, 0); + if (bytes != 3) + QuiskPrintf("read_remote_radio_sound_socket(), sendto(): %s\n", strerror(errno)); + } + + // read all available packets, one per loop + nSamples = 0; + while (1) { + tm_wait.tv_sec = 0; + tm_wait.tv_usec = 0; + FD_ZERO (&fds); + FD_SET (control_head_sound_socket, &fds); + if (select(control_head_sound_socket + 1, &fds, NULL, NULL, &tm_wait) != 1) { + //BMC QuiskPrintf("read_remote_radio_sound_socket(): select returned %i\n", retval); + break; + } + bytes = recv(control_head_sound_socket, (char *)buf, UDP_SEND_INT16 * 2, 0); + if (bytes < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + QuiskPrintf("read_remote_radio_sound_socket(), recv(): %s\n", strerror(errno)); + break; + } + if (bytes > 0) { + control_head_sound_socket_started = 1; + nInt16 = bytes / 2; + for (i = 0; i < nInt16; i += 2) { + samp_r = buf[i]; + samp_l = buf[i + 1]; + cSamples[nSamples++] = (samp_r + I * samp_l) / CLIP16 * CLIP32; + } + } + } // while(1) + + nSamples = quisk_cInterpolate(cSamples, nSamples, &cFiltInterp3, 3); + nSamples = quisk_cInterp2HB45(cSamples, nSamples, &HalfBand); + + return nSamples; +} + +// Receive stereo 16-bit pcm microphone samples at the remote radio via UDP +int read_remote_mic_sound_socket(complex double * cSamples) +{ + int i, bytes, nInt16, nSamples; + int16_t buf[UDP_SEND_INT16]; + double samp_r, samp_l; + struct timeval tm_wait; + fd_set fds; + static struct quisk_cHB45Filter HalfBand; + static struct quisk_cFilter cFiltInterp3; + static int init_filters=1; + + if (remote_radio_sound_socket == INVALID_SOCKET) + return 0; + if (init_filters) { + init_filters = 0; + memset(&HalfBand, 0, sizeof(struct quisk_cHB45Filter)); + quisk_filt_cInit(&cFiltInterp3, quiskAudio24p3Coefs, sizeof(quiskAudio24p3Coefs)/sizeof(double)); + } + + // read all available packets, one per loop + nSamples = 0; + while (1) { + tm_wait.tv_sec = 0; + tm_wait.tv_usec = 0; + FD_ZERO (&fds); + FD_SET (remote_radio_sound_socket, &fds); + if (select(remote_radio_sound_socket + 1, &fds, NULL, NULL, &tm_wait) != 1) { + //BMC QuiskPrintf("read_remote_mic_sound_socket(): select returned %i\n", retval); + break; + } + bytes = recv(remote_radio_sound_socket, (char *)buf, UDP_SEND_INT16 * 2, 0); + if (bytes < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + QuiskPrintf("read_remote_mic_sound_socket(), recv(): %s\n", strerror(errno)); + break; + } + if (bytes > 0) { + nInt16 = bytes / 2; + for (i = 0; i < nInt16; i += 2) { + samp_r = buf[i]; + samp_l = buf[i + 1]; + cSamples[nSamples++] = (samp_r + I * samp_l) / CLIP16 * CLIP32; + } + } + } // while(1) + + nSamples = quisk_cInterpolate(cSamples, nSamples, &cFiltInterp3, 3); + nSamples = quisk_cInterp2HB45(cSamples, nSamples, &HalfBand); + + return nSamples; +} +// Send stereo 16-bit pcm sound via UDP +// This code acts as UDP server for radio sound (on remote radio) or mic sound (on control head) +#define MAX_SAMPLES_FOR_REMOTE_SOUND 15000 +#define RX_BUFFER_SIZE 64 + +// Send microphone samples from the control head to the remote radio +void send_remote_mic_sound_socket(complex double * cSamples, int nSamples) +{ + int i; + ssize_t sent; + static int buffer_index=0; + static int16_t buffer[UDP_SEND_INT16]; + static struct quisk_cHB45Filter HalfBand; + static struct quisk_cFilter cFiltDecim3; + static int init_filters=1, size_cBuf=0; + static complex double * cBuf=NULL; + + if (init_filters) { + init_filters = 0; + memset(&HalfBand, 0, sizeof(struct quisk_cHB45Filter)); + quisk_filt_cInit(&cFiltDecim3, quiskAudio24p3Coefs, sizeof(quiskAudio24p3Coefs)/sizeof(double)); + } + if (nSamples > size_cBuf) { + size_cBuf = nSamples; + cBuf = (complex double *)realloc(cBuf, size_cBuf * sizeof(complex double)); + } + if (control_head_sound_socket == INVALID_SOCKET) + return; + + if (!control_head_sound_socket_started) + return; + memcpy(cBuf, cSamples, nSamples * sizeof(complex double)); // Do not alter cSamples + // Reduce sample rate from 48 to 8 ksps + nSamples = quisk_cDecim2HB45(cBuf, nSamples, &HalfBand); + nSamples = quisk_cDecimate(cBuf, nSamples, &cFiltDecim3, 3); + // Convert format from complex double to stereo pairs of 16-bit PCM samples. + // Buffer samples until UDP_SEND_INT16 are available, and then send the block. + for (i = 0; i < nSamples; i++) { + buffer[buffer_index++] = (int16_t)(creal(cBuf[i]) * (double)CLIP16 / CLIP32); + buffer[buffer_index++] = (int16_t)(cimag(cBuf[i]) * (double)CLIP16 / CLIP32); + if (buffer_index >= UDP_SEND_INT16) { + sent = send(control_head_sound_socket, (const char *)buffer, buffer_index * 2, 0); + if (sent != buffer_index * 2) + QuiskPrintf("send_remote_mic_sound_socket(), send(): %s\n", strerror(errno)); + buffer_index = 0; + } + } +} + +// Send radio speaker sound from the remote radio to the control head +void send_remote_radio_sound_socket(complex double * cSamples, int nSamples) +{ // Send nSamples samples. Each sample is sent as two shorts (4 bytes) of L/R audio data. + int i, N, sent; + static int16_t sound_lr[UDP_SEND_INT16]; + static int udp_size = 0; // Keep track of UDP payload size, in shorts + char buf[RX_BUFFER_SIZE]; // For startup message + int recv_len; + int retval; + SOCKET * sock = &remote_radio_sound_socket; + static struct quisk_cHB45Filter HalfBand; + static struct quisk_cFilter cFiltDecim3; + static int init_filters=1, size_cBuf=0; + static complex double * cBuf=NULL; + +#if REMOTE_DEBUG > 0 //BMC debug + // measure/monitor tools: + static float callcount = 0; + static float sampcount = 0; + static uint64_t bunchcount = 0; + static float callcount_total = 0; + static float sampcount_total = 0; + static double prior_ts = 0; + static double delta_total = 0; +#if REMOTE_DEBUG > 1 //BMC debug + static double prior_packet_ts = 0.0; + double now; +#endif +#endif + + if (*sock == INVALID_SOCKET) + return; + + if (init_filters) { + init_filters = 0; + memset(&HalfBand, 0, sizeof(struct quisk_cHB45Filter)); + quisk_filt_cInit(&cFiltDecim3, quiskAudio24p3Coefs, sizeof(quiskAudio24p3Coefs)/sizeof(double)); + } + if (nSamples > size_cBuf) { + size_cBuf = nSamples; + cBuf = (complex double *)realloc(cBuf, size_cBuf * sizeof(complex double)); + } + +#if REMOTE_DEBUG > 0 //BMC debug + callcount++; +#endif + // Wait for far end (client) to send its opening greetings, so we can grab its network address/port + if (!remote_radio_sound_socket_started) { + struct sockaddr_in far_addr; +#ifdef MS_WINDOWS + int addr_len = sizeof(struct sockaddr_in); +#else + socklen_t addr_len = sizeof(struct sockaddr_in); +#endif + struct timeval tm_wait; + fd_set fds; + tm_wait.tv_sec = 0; + tm_wait.tv_usec = 0; + FD_ZERO (&fds); + FD_SET (*sock, &fds); + if ((retval = select(*sock + 1, &fds, NULL, NULL, &tm_wait)) != 1) { + //BMC QuiskPrintf("send_remote_sound_socket(): select returned %i\n", retval); + return; + } + // Receive short msg, grab far end address + if ((recv_len = recvfrom(*sock, buf, RX_BUFFER_SIZE, 0, (struct sockaddr *) &far_addr, &addr_len)) == -1) { + QuiskPrintf("send_remote_sound_socket(), recvfrom(): %s\n", strerror(errno)); + return; + } + else if(recv_len > 0) { + if (recv_len >= RX_BUFFER_SIZE) + buf[RX_BUFFER_SIZE - 1] = '\n'; + else + buf[recv_len] = '\n'; + QuiskPrintf("send_remote_sound_socket(): recv_len = %i, %s", recv_len, buf); + if (connect(*sock, (const struct sockaddr *)&far_addr, sizeof(far_addr)) != 0) { + QuiskPrintf("send_remote_sound_socket), connect(): %s\n", strerror(errno)); + close(*sock); + *sock = INVALID_SOCKET; + } + else + remote_radio_sound_socket_started = 1; + } + } + + memcpy(cBuf, cSamples, nSamples * sizeof(complex double)); // Do not alter cSamples + // Reduce sample rate from 48 to 8 ksps + nSamples = quisk_cDecim2HB45(cBuf, nSamples, &HalfBand); + nSamples = quisk_cDecimate(cBuf, nSamples, &cFiltDecim3, 3); + // Convert format from complex double to stereo pairs of 16-bit PCM samples, send to client + for (i = 0; i < nSamples; i++) { + sound_lr[udp_size++] = (int16_t)(creal(cBuf[i]) * (double)CLIP16 / CLIP32); + sound_lr[udp_size++] = (int16_t)(cimag(cBuf[i]) * (double)CLIP16 / CLIP32); + if (udp_size >= UDP_SEND_INT16) { + N = UDP_SEND_INT16; + udp_size = 0; + sent = send(*sock, (char *)sound_lr, N * 2, 0); + if (sent != N * 2) + QuiskPrintf("send_remote_sound_socket(), send(): %s\n", strerror(errno)); +#if REMOTE_DEBUG > 0 //BMC debug + else { + sampcount += sent/4; + packets_sent++; +#if REMOTE_DEBUG > 1 //BMC debug + now = QuiskTimeSec(); + QuiskPrintf("%f, send_remote_sound_socket(): now - prior = %f, samples = %u, sampcount = %lu\n", + now, now - prior_packet_ts, sent / 4, sampcount); + prior_packet_ts = now; +#endif + } +#endif + } +#if REMOTE_DEBUG > 1 //BMC debug + else { + now = QuiskTimeSec(); + QuiskPrintf("%f, send_remote_sound_socket(): nSamples = %i\n", now, nSamples); + } +#endif + } +#if REMOTE_DEBUG > 0 //BMC debug + if (callcount >= 200) { + double new_ts = QuiskTimeSec(); + double delta = new_ts - prior_ts; + prior_ts = new_ts; +#if REMOTE_DEBUG > 1 //BMC every 200 + QuiskPrintf("send_remote_sound_socket CURRENT calls: %lu, samples %lu, deltasec %f\n", callcount, sampcount, delta); + QuiskPrintf("%f: send_remote_sound_socket CURRENT RATES (HZ): calls %f, samples %f\n", new_ts, callcount / delta, sampcount / delta); +#endif + if (bunchcount > 0) { // skip the initial bunch; prebuf may distort some numbers(?) + callcount_total += callcount; + sampcount_total += sampcount; + delta_total += delta; + } + if (bunchcount % 10 == 0 && bunchcount > 0) { + QuiskPrintf("%f: send_remote_sound_socket SUMMARY calls: %f, samples %f, deltasec %f\n", + new_ts, callcount_total, sampcount_total, delta_total); + QuiskPrintf("%f: send_remote_sound_socket SUMMARY RATES (HZ): calls %f, samples %f\n", + new_ts, callcount_total / delta_total, sampcount_total / delta_total); + } + bunchcount++; + callcount = 0; + sampcount = 0; + } +#endif +} + +// Send graph data via UDP from the remote radio to the control head +void send_graph_data(double * fft_avg, int fft_size, double zoom, double deltaf, int fft_sample_rate, double scale) +{ + static double * pixels = NULL; + static int n_pixels = 0; + static uint8_t sequence = 0; + uint8_t flags; + int16_t buffer[MAX_UDP_INT16_T]; + int16_t block; + int pixel_index, buffer_index; + double d1, d2; + ssize_t sent; + char buf[RX_BUFFER_SIZE]; // For startup message + int recv_len; + struct sockaddr_in far_addr; +#ifdef MS_WINDOWS + int addr_len = sizeof(struct sockaddr_in); +#else + socklen_t addr_len = sizeof(struct sockaddr_in); +#endif + + if (remote_radio_graph_socket == INVALID_SOCKET) + return; + if ( !control_head_data_width) + return; + if ( !remote_radio_graph_socket_started) { + // Receive short msg, grab far end address + // Receive from control head is necessary to establish a path through NAT + if ((recv_len = recvfrom(remote_radio_graph_socket, buf, RX_BUFFER_SIZE, 0, (struct sockaddr *) &far_addr, &addr_len)) < 2) { + return; + } + else { + if (connect(remote_radio_graph_socket, (const struct sockaddr *)&far_addr, sizeof(far_addr)) != 0) { + QuiskPrintf("send_remote_graph_socket), connect(): %s\n", strerror(errno)); + close(remote_radio_graph_socket); + remote_radio_graph_socket = INVALID_SOCKET; + return; + } + else { + remote_radio_graph_socket_started = 1; + } + } + } + if (control_head_data_width > n_pixels) { + n_pixels = control_head_data_width; + if (pixels) + free(pixels); + pixels = (double *)malloc(n_pixels * sizeof(double)); + } + if ( ! fft_avg) { // send dummy graph data + send(remote_radio_graph_socket, "dum", 3, 0); + return; + } + copy2pixels(pixels, control_head_data_width, fft_avg, fft_size, zoom, deltaf, fft_sample_rate); + // Send multiple 16-bit data blocks: {flags, sequence}, block number, 16-bit graph data + // 8-bit flags: + // bit 0: clip indicator + // 8-bit sequence: 0, 1, 2, ..., 255 + block = 0; + pixel_index = 0; + while (pixel_index < control_head_data_width) { + if (quisk_get_overrange()) + flags = 0x01; + else + flags = 0x00; + buffer[0] = flags << 8 | sequence; + buffer[1] = block; + buffer_index = 2; + while (buffer_index < MAX_UDP_INT16_T && pixel_index < control_head_data_width) { + d1 = pixels[pixel_index++]; + if (fabs(d1) < 1e-40) // avoid log10(0) + d1 = 1E-40; + d2 = 20.0 * log10(d1) - scale; + if (d2 < -200) + d2 = -200; + else if (d2 > 0) + d2 = 0; + buffer[buffer_index++] = (int16_t)lround(d2 * GRAPH_DATA_SCALE); + } + sent = send(remote_radio_graph_socket, (const char *)buffer, buffer_index * 2, 0); + if (sent != buffer_index * 2) + QuiskPrintf("send_graph_data(), send(): %s\n", strerror(errno)); + block++; + } + sequence += 1; +} + +// Receive graph data via UDP on the control head +int receive_graph_data(double * fft_avg) +{ + int i, i1, i2; + ssize_t count; + uint8_t seq, flags; + int16_t buffer[MAX_UDP_INT16_T]; + int16_t block; + static int16_t * pixels = NULL; + static int n_pixels = 0; + static int total = 0; + static int16_t sequence = 0; + + if (control_head_graph_socket == INVALID_SOCKET) + return 0; + // Signal far end (server) that we're ready (this sends our address/port to far end) + if ( !control_head_graph_socket_started) { + i = send(control_head_graph_socket, "rr\n", 3, 0); + if (i != 3) + QuiskPrintf("receive_graph_data(), send(): %s\n", strerror(errno)); + } + if (n_pixels < data_width) { + n_pixels = data_width; + if (pixels) + free(pixels); + pixels = (int16_t *)malloc(n_pixels * sizeof(int16_t)); + } + count = recv(control_head_graph_socket, (char *)buffer, MAX_UDP_INT16_T * 2, 0); + count /= 2; // convert to int16_t + if (count > 2) { + control_head_graph_socket_started = 1; + flags = buffer[0] >> 8; + if (flags & 0x01) // Clip + quisk_sound_state.overrange++; + seq = buffer[0] & 0xFF; + if (seq != sequence) { // new graph data + sequence = seq; + total = 0; + } + block = buffer[1]; + count -= 2; // number of 16-bit graph data items + i1 = block * (MAX_UDP_INT16_T - 2); + i2 = i1 + count; + if (i1 >= 0 && i2 <= data_width) { + memcpy(pixels + i1, buffer + 2, count * 2); + total += count; + if (total == data_width) { + for (i = 0; i < data_width; i++) + fft_avg[i] = (double)pixels[i] / GRAPH_DATA_SCALE; + return data_width; + } + } + } + return 0; +} + + +static int start_winsock() +{ +#ifdef MS_WINDOWS + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + + if (WSAStartup(wVersionRequested, &wsaData) != 0) { + QuiskPrintf("start_winsock(): %s\n", strerror(errno)); + return 0; // failure to start winsock + } +#endif + return 1; +} + + + + + +static void open_and_bind_socket(SOCKET * sock, char * ip, int port, int sndsize, char * name, int non_block) +{ + struct sockaddr_in bind_addr; + const char enable = 1; // for sockopt +#ifndef MS_WINDOWS + int tos = 184; // DSCP "Expedite" (46) +#endif + + if (!start_winsock()) { + QuiskPrintf("open_and_bind_socket for %s: Failure to start WinSock\n", name); + return; + } + + *sock = socket(PF_INET, SOCK_DGRAM, 0); + if (*sock != INVALID_SOCKET) { + setsockopt(*sock, SOL_SOCKET, SO_SNDBUF, (char *)&sndsize, sizeof(sndsize)); + setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); +#ifndef MS_WINDOWS + setsockopt(*sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif + + // bind to this computer for receiving (and reading far-end address from client) + memset((char *) &bind_addr, 0, sizeof(bind_addr)); + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = htons(port); + bind_addr.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(*sock, (const struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0) { + QuiskPrintf("open_and_bind_socket(), bind(): %s\n", strerror(errno)); + close(*sock); + *sock = INVALID_SOCKET; + } + else if (non_block) { +#ifdef MS_WINDOWS + unsigned long one = 1; + ioctlsocket(*sock, FIONBIO, &one); // set non-blocking +#else + int flags; + flags = fcntl(*sock, F_GETFL, 0); // set non-blocking + fcntl(*sock, F_SETFL, flags | O_NONBLOCK); +#endif + } + } + if (*sock == INVALID_SOCKET) { + QuiskPrintf("open server %s: Failure to open socket\n", name); + } + else { + QuiskPrintf("open server %s: opened socket %s port %i\n", name, ip, port); + } +} + + +static void open_and_connect_socket(SOCKET * sock, char * ip, int port, int sndsize, char * name, int non_block) +{ + struct sockaddr_in Addr; + const char enable = 1; // for sockopt + + if (!start_winsock()) { + QuiskPrintf("open_and_connect_socket for %s: Failure to start WinSock\n", name); + return; + } + + *sock = socket(PF_INET, SOCK_DGRAM, 0); + if (*sock != INVALID_SOCKET) { + setsockopt(*sock, SOL_SOCKET, SO_RCVBUF, (char *)&sndsize, sizeof(sndsize)); + setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); + + // set far-end address structure to enable sending initial packet to get things started + Addr.sin_family = AF_INET; + Addr.sin_port = htons(port); +#ifdef MS_WINDOWS + Addr.sin_addr.S_un.S_addr = inet_addr(ip); +#else + inet_aton(ip, &Addr.sin_addr); +#endif + if (connect(*sock, (const struct sockaddr *)&Addr, sizeof(Addr)) != 0) { + close(*sock); + *sock = INVALID_SOCKET; + } + else if (non_block) { +#ifdef MS_WINDOWS + unsigned long one = 1; + ioctlsocket(*sock, FIONBIO, &one); // set non-blocking +#else + int flags; + flags = fcntl(*sock, F_GETFL, 0); // set non-blocking + fcntl(*sock, F_SETFL, flags | O_NONBLOCK); +#endif + } + } + if (*sock == INVALID_SOCKET) { + QuiskPrintf("open client %s: Failure to open socket\n", name); + } + else { + QuiskPrintf("open client %s: opened socket %s port %i\n", name, ip, port); + } +} + + +static void close_socket(SOCKET * sock, char * name) +{ + if (*sock != INVALID_SOCKET) { + close(*sock); + *sock = INVALID_SOCKET; +#ifdef MS_WINDOWS + WSACleanup(); +#endif + QuiskPrintf("%s: closed socket\n", name); + } + else { + QuiskPrintf("%s: socket already closed\n", name); + } +} + +// start running UDP remote sound on control_head ... +// ... receive radio sound from remote_radio, send mic sound to remote_radio +PyObject * quisk_start_control_head_remote_sound(PyObject * self, PyObject * args) +{ + int radio_sound_port; + int graph_data_port; + int sndsize = 48000; + char * remote_radio_ip; // IP address of far end + char * name; + SOCKET * sock; + + if (!PyArg_ParseTuple (args, "sii", &remote_radio_ip, &radio_sound_port, &graph_data_port)) + return NULL; + + name = "radio sound from remote_radio"; + sock = &control_head_sound_socket; + open_and_connect_socket(sock, remote_radio_ip, radio_sound_port, sndsize, name, 0); + + name = "graph data from remote_radio"; + sock = &control_head_graph_socket; + open_and_connect_socket(sock, remote_radio_ip, graph_data_port, 1024 * 8, name, 1); + + packets_sent = 0; + packets_recd = 0; + + return Py_None; +} + +// stop running UDP remote sound on control_head +PyObject * quisk_stop_control_head_remote_sound(PyObject * self, PyObject * args) +{ + char * name; + SOCKET * sock; + + if (!PyArg_ParseTuple (args, "")) + return NULL; + + name = "radio sound from remote_radio"; + sock = &control_head_sound_socket; + close_socket(sock, name); + + name = "graph data from remote_radio"; + sock = &control_head_graph_socket; + close_socket(sock, name); + + control_head_sound_socket_started = 0; // reset for next time + remote_radio_sound_socket_started = 0; + control_head_graph_socket_started = 0; + remote_radio_graph_socket_started = 0; + + QuiskPrintf("total packets sent = %i, recd = %i\n", packets_sent, packets_recd); + + return Py_None; +} + +// start running UDP remote sound on remote_radio ... +// ... send radio sound to control_head, receive mic sound from control_head +PyObject * quisk_start_remote_radio_remote_sound(PyObject * self, PyObject * args) +{ + int radio_sound_port; + int graph_data_port; + int sndsize = 48000; + char * control_head_ip; // IP address of far end + char * name; + SOCKET * sock; + + if (!PyArg_ParseTuple (args, "siii", &control_head_ip, &radio_sound_port, + &graph_data_port, &control_head_data_width)) + return NULL; + + name = "radio sound to control_head"; + sock = &remote_radio_sound_socket; + open_and_bind_socket(sock, control_head_ip, radio_sound_port, sndsize, name, 0); + + name = "graph data to control_head"; + sock = &remote_radio_graph_socket; + open_and_bind_socket(sock, control_head_ip, graph_data_port, 1024 * 8, name, 1); + + packets_sent = 0; + packets_recd = 0; + + return Py_None; +} + +// stop running UDP remote sound on remote_radio +PyObject * quisk_stop_remote_radio_remote_sound(PyObject * self, PyObject * args) +{ + char * name; + SOCKET * sock; + + if (!PyArg_ParseTuple (args, "")) + return NULL; + + name = "radio sound to control_head"; + sock = &remote_radio_sound_socket; + close_socket(sock, name); + + name = "graph data to control_head"; + sock = &remote_radio_graph_socket; + close_socket(sock, name); + + control_head_sound_socket_started = 0; // reset for next time + remote_radio_sound_socket_started = 0; + control_head_graph_socket_started = 0; + remote_radio_graph_socket_started = 0; + control_head_data_width = 0; + + QuiskPrintf("total packets sent = %i, recd = %i\n", packets_sent, packets_recd); + + return Py_None; +} diff --git a/ac2yd/remote_common.py b/ac2yd/remote_common.py new file mode 100644 index 0000000..43d996a --- /dev/null +++ b/ac2yd/remote_common.py @@ -0,0 +1,439 @@ +# THIS FILE IS FOR USE ON A REMOTELY CONTROLLED "REMOTE_RADIO" COMPUTER +# RUNNING QUISK TO CONTROL ATTACHED ACTUAL/REAL RADIO HARDWARE. +# IT CONNECTS BY NETWORK TO A SEPARATE "CONTROL_HEAD" COMPUTER ALSO RUNNING QUISK. +# +# This software is Copyright (C) 2021-2022 by Ben Cahill and 2006-2022 by James C. Ahlstrom., +# and is licensed for use under the GNU General Public License (GPL). +# See http://www.opensource.org. +# Note that there is NO WARRANTY AT ALL. USE AT YOUR OWN RISK!! +# +# This file, remote_common.py, along with a radio-specific file, e.g. +# remote_softrock.py, remote_hermes.py, or similar, allows a radio-less (control_head) Quisk, +# running on a separate computer, to connect to this (remote_radio) instance of Quisk, +# which has an actual, real radio attached. +# +# The control_head Quisk must use control_common.py, along with a radio-specific file, +# e.g. control_softrock.py, control_hermes.py, or similar, to communicate with this +# remote_radio Quisk via a network connection. +# +# This remote_radio computer should be set up with a static IP address, so that you know +# where to point the control_head. The control head computer may, however, use dynamic +# addressing; this remote_radio computer will read the control head address when the +# remote control connection is made. +# +# The main control interface between control_head and remote_radio is via a TCP port; +# this uses very low bandwidth. All functional control, including CW keying, is done via this port. +# +# There are 2 additional ports, both UDP, using low to moderate bandwidth: +# -- Send graph/waterfall data to the control_head +# -- Send radio sound to the control_head, and receive mic sound +# These use sequential port numbers based on the TCP port number self.remote_ctl_base_port. +# If you need to change the default base port number, you can edit the line in this file +# that looks like (without the #): +# +# self.remote_ctl_base_port = 4585 # Remote Control base TCP port +# self.graph_data_port = self.remote_ctl_base_port + 1 # UDP port for graph data from the remote +# self.remote_radio_sound_port = self.remote_ctl_base_port + 2 # UDP port for radio sound and mic samples +# +# +# Make sure to edit the corresponding line in control_common.py to match ports!! +# +# This remote_radio Quisk/computer is assumed to track the connected control_head Quisk/computer; +# no attempt is made by the control_head to verify the remote_radio Quisk's tuning frequency, mode, etc. +# Snap-to Rx tuning for CW works on the control_head Quisk by virtue of graph/waterfall data +# received from the remote_radio Quisk. +# +# To see detailed log output of CW key timing, set DEBUG_CW_JITTER = 1. +# To additionally see, when CW commands are pending, timestamps of when PollCwKey is called +# (e.g. to check thread scheduling behavior), set DEBUG_CW_JITTER = 2. +# To send "perfect" bursts of CW dits from the control_head, set DEBUG_CW_SEND_DITS = 1 +# in the control_head's quisk_hardware_control_head.py. + +DEBUG_CW_JITTER = 0 +DEBUG = 0 + +from collections import deque # for CW event queue +import socket, time, traceback, string, hmac, secrets, json +import _quisk as QS +from quisk_widgets import * + +class Remot: # Remote comtrol base class + def __init__(self, app, conf): + self.app = app # Access Quisk class App (Python) functions + self.conf = conf + self.token = "abc" + self.token_time = 0 + + self.control_head_ip = None # IP of control_head compter (read upon connection) + self.remote_ctl_base_port = 4585 # Base of ports for remote connection (maybe edit this) + self.remote_ctl_socket = None + self.remote_ctl_connection = None + self.remote_ctl_heartbeat_ts = None + self.remote_ctl_heartbeat_timeout = 10.0 # Close our connection if we don't hear heartbeat from Control Head + self.graph_data_port = self.remote_ctl_base_port + 1 + self.remote_radio_sound_port = self.remote_ctl_base_port + 2 + + self.cw_delay_secs = 0.020 # time delay to absorb WiFi jitter, in secs + self.cw_phrase_begin_ts = None # timestamp of beginning of cw phrase + self.cw_next_event_ts = None + self.cw_next_keydown = None + self.cw_event_queue = deque() + self.cw_key_down = 0 # Tx-enable management + self.cw_tx_enable = 0 + + self.received = '' + self.cmd_text = None # cmd received from client (remote head) + self.cmd = None # cmd received from client (remote head) + self.params = None # params = the string following the command + self.extended = None + self.split_mode = 0 + + print('Remote Overlay Initialized!') + + def open(self): + self.token = "abc" + self.remote_ctl_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.remote_ctl_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.remote_ctl_socket.bind(('', self.remote_ctl_base_port)) # '' == INADDR_ANY + self.remote_ctl_socket.settimeout(0.0) + self.remote_ctl_socket.listen(1) # listen for TCP connection from just one client + print('Remote Overlay Opened!') + # Return an informative message for the config screen. + # This method must return a string showing whether the open succeeded or failed. + # Here, we are over-writing the string set by quisk_hardware_model.py + t = f'Quisk Remote Controlled Radio {self.app.width}x{self.app.height} {self.app.graph_width} {self.app.data_width}' + return t + #BMC return ret + + def close(self): # Close the listening socket, then the connection socket + if self.remote_ctl_socket: + self.remote_ctl_socket.close() + self.remote_ctl_socket = None + self.token = "abc" + if self.remote_ctl_connection: + print('Closing Remote Control connection: close') + self.RemoteCtlClose(True) + + def RemoteCtlOpen(self): + try: + self.remote_ctl_connection, address = self.remote_ctl_socket.accept() + except: + return + else: + self.app.remote_control_slave = True + QS.set_sparams(remote_control_slave=1) + self.token = secrets.token_hex(32) + self.remote_ctl_connection.settimeout(0.0) + self.remote_ctl_heartbeat_ts = time.time() + if DEBUG: print('Remote Control connection: ', self.remote_ctl_connection, ' address: ', address) + self.control_head_ip = address[0] + print ("Remote control connection from", self.control_head_ip) + self.RemoteCtlSend("TOKEN;" + self.token + "\n") + self.token_time = time.time() + + def RemoteCtlClose(self, send_quit): + self.StopTransmit() + if self.remote_ctl_connection: + if send_quit: + self.RemoteCtlSend('Q\n') + if self.remote_ctl_connection: + self.remote_ctl_connection.close() + self.remote_ctl_connection = None + QS.stop_remote_radio_remote_sound() + self.app.remote_control_slave = False + QS.set_sparams(remote_control_slave=0) + + def RemoteCtlSend(self, text): + # Send text back to the client + if not self.remote_ctl_connection: + return + if isinstance(text, str): + text = text.encode('utf-8', errors='ignore') + try: + self.remote_ctl_connection.sendall(text) + except socket.error: + print('Closing Remote Control connection: sendall() failed. Sent text:\n ' + text.decode('utf-8')) + # NOTE: Cannot send 'Q' to Control Head here; sendall() isn't working! + self.RemoteCtlClose(False) + + def ErrParam(self): # Invalid parameter + t = 'ERR_PARAM: ' + self.cmd_text + '\n' + print(t) + self.RemoteCtlSend(t) + def ErrUnsupported(self): # Command recognized but not supported (because of either H/W or configuration) + t = 'ERR_UNSUPPORTED: ' + self.cmd_text + '\n' + print(t) + self.RemoteCtlSend(t) + def ErrUnrecognized(self): # Unrecognized command + t = 'ERR_UNRECOGNIZED_CMD: ' + self.cmd_text + '\n' + print(t) + self.RemoteCtlSend(t) + def ErrBadFormat(self): # Something wrong with format of command + t = 'ERR_BADFORMAT: ' + self.cmd_text + '\n' + print(t) + self.RemoteCtlSend(t) + + def HeartBeat(self): # Called at about 10 Hz by the GUI thread + if self.remote_ctl_connection: + # Monitor the remote connection via periodic heartbeat from Control Head + ts = time.time() + if (ts - self.remote_ctl_heartbeat_ts) > self.remote_ctl_heartbeat_timeout: + print('Closing Remote Control connection: Lost HEARTBEAT from Control Head') + self.RemoteCtlClose(True) + else: + # Continually try to connect with Control Head + self.RemoteCtlOpen() + + def FastHeartBeat(self): # Called frequently by the GUI thread + """This is the remote slave processing loop, and is called frequently. It reads and satisfies requests.""" + if not self.remote_ctl_connection: + return + try: # Read any data from the socket + text = self.remote_ctl_connection.recv(1024) + except: + #traceback.print_exc() + return + else: # We got some characters + if not isinstance(text, str): + text = text.decode('utf-8') + self.received += text + if not '\n' in self.received: # A complete command ending with newline is not available + return + while '\n' in self.received: # At least one complete command ending with newline *is* available + cmd_text, self.received = self.received.split('\n', 1) # Split off the command, save any further characters + cmd_text = cmd_text.strip() # Here is our command + if not cmd_text: + continue + self.cmd_text = cmd_text + args = cmd_text.split(';') # Split at ';' because some control names have blanks + command = args[0] + params = args[1:] + # TOKEN + if self.token: + if command == "TOKEN": + passw = self.app.local_conf.globals.get("remote_radio_password", "") + passw = passw.strip() + if not passw: + self.RemoteCtlSend("TOKEN_MISSING\n") + print ("Error: Missing password on remote radio") + continue + H = hmac.new(passw.encode('utf-8'), self.token.encode('utf-8'), 'sha3_256') + del passw + if hmac.compare_digest(H.hexdigest(), args[1]): + self.token = None + print ("Security challenge passed", args[2]) + self.control_head_data_width = int(args[2]) + self.RemoteCtlSend("TOKEN_OK\n") + self.remote_ctl_heartbeat_ts = time.time() + QS.start_remote_radio_remote_sound(self.control_head_ip, self.remote_radio_sound_port, + self.graph_data_port, self.control_head_data_width) + else: + time.sleep(1) + elif time.time() - self.token_time > 5: + self.RemoteCtlSend("TOKEN_BAD\n") + self.RemoteCtlClose(True) + print ("Security failed") + continue + # Check for Quit and Heartbeat before any other commands + if command == 'QUIT': + print('Closing Remote Control connection: QUIT from Control Head') + # NOTE: Do not send 'Q' to Control Head; sendall() will fail because Control Head already disconnected + self.RemoteCtlClose(False) + continue + # HEARTBEAT + if command == 'HEARTBEAT': + self.remote_ctl_heartbeat_ts = time.time() + continue + # Ignore the On/Off button, Help buttons, Small window pop buttons + if command in ("On", "..", "bandBtnGroup", "screenBtnGroup", "modeButns", "Scope", "Config", "RX Filter", "Help"): + continue + if DEBUG: print("Remote receive:", cmd_text) + # Look for radio buttons + if self.ProcessRadioBtn(command, self.cmd_text): + continue + # buttons in idName2Button + btn = self.app.idName2Button.get(args[0], None) + if btn: + #print ("Slave process button", cmd_text, btn.__class__) + value = int(args[1]) + btn.SetIndex(value, True) + continue + # controls in midiControls + if command in self.app.midiControls: + ctrl, func = self.app.midiControls[command] + #print ("Slave Process control", cmd_text, ctrl.__class__, func) + value = int(args[1]) + if isinstance(ctrl, WrapSlider): + ctrl.ChangeSlider(value) + else: + ctrl.SetValue(value) + func() + continue + # FREQ + if command == 'FREQ': + freq, vfo, source, band, rxFreq, var_decim_index = args[1:] + freq = int(freq) + vfo = int(vfo) + rxFreq = int(rxFreq) + if rxFreq == self.app.rxFreq: + rxFreq = None + var_decim_index = int(var_decim_index) + self.app.ChangeHwFrequency(freq - vfo, vfo, source, band, None, rxFreq) + if source == "NewDecim": + self.app.config_screen.config.btn_decimation.SetSelection(var_decim_index) + sample_rate = self.VarDecimSet(var_decim_index) + self.app.OnBtnDecimation(rate=sample_rate) + # Json function call + elif command == 'JsonAppFunc': + # Call the function self.app. + func with the specified arguments + # func = "%s.SetLabel" % self.idName + # application.Hardware.RemoteCtlSend(f'JsonAppFunc;{json.dumps((func, label, do_cmd, direction))}\n') + jargs = json.loads(params[0]) + pyobj = self.app + for nam in jargs[0].split('.'): + pyobj = getattr(pyobj, nam) + pyobj(*jargs[1:]) + # AGC and Squelch levels + elif command == 'AGCSQLCH': + ctrl = self.app.midiControls["AGCSlider"][0] + ctrl.SetSlider(value_off=int(args[1]), value_on=int(args[2])) + self.app.levelSquelch = int(args[3]) + self.app.levelSquelchSSB = int(args[4]) + self.app.split_offset = int(args[5]) + # CW Keying + elif command == 'CW': + ts = time.time() + if len(params) < 2: + self.ErrParam() + return + if params[0] in '01': + keydown = int(params[0]) + else: + print('Bad keydown value in CW command:', params[0]) + self.ErrParam() + return + cw_event_ts = float(params[1]) / 1000.0 # int msecs to float secs + if cw_event_ts == 0.0: + if keydown != 1: + # 'CW 0 0' == "Force Stop of CW"; clear all queued CW commands, and force CW key up + print('Forcing stop of CW') + while len(self.cw_event_queue): + self.cw_event_queue.popleft() + self.cw_next_event_ts = None + self.cw_next_keydown = None + self.cw_key_down = 0 + QS.set_remote_cwkey(0) + else: + # Begin new cw phrase; any prior phrase should be done by now. + # Set up first cw event to be ready to execute. + self.cw_begin_phrase_ts = ts + self.cw_delay_secs + cw_new_event_ts = self.cw_begin_phrase_ts + cw_event_ts + if not self.cw_next_event_ts: + self.cw_next_event_ts = cw_new_event_ts + self.cw_next_keydown = keydown + if DEBUG_CW_JITTER: print(f'{ts:10.4f} setting: {keydown} {cw_event_ts:2.3f} {cw_new_event_ts:10.4f}') + else: + self.cw_event_queue.append((cw_new_event_ts, keydown)) + if DEBUG_CW_JITTER: print(f'{ts:10.4f} queing: {keydown} {cw_event_ts:2.3f} {cw_new_event_ts:10.4f}') + # Menu + elif command == 'MENU': + menu_name, item_text, checked = args[1:] + if item_text == 'Reverse Rx and Tx': + continue # No need to call handler, as rxFreq and txFreq are already handled + menu = getattr(self.app, menu_name) + nid = menu.item_text2id[item_text] + menu_item = menu.FindItemById(nid) + if menu_item.IsCheckable(): + menu_item.Check(int(checked)) + menu.Handler(None, nid) + else: + t = 'ERR_UNRECOGNIZED_CMD: %s\n' % cmd_text + print(t) + self.RemoteCtlSend(t) + continue + + def PollCwKey(self): # Called periodically at HW Poll usec period (typ. 50-200 Hz) by the sound thread + cw_queue_len = len(self.cw_event_queue) + if self.cw_next_event_ts or cw_queue_len > 0: + # We have at least one CW event. If it's time to do so, set the next CW key down/up, look for next CW event. + ts = time.time() + if DEBUG_CW_JITTER > 1: print(f'{ts:10.4f}') + if not self.cw_next_event_ts: + # Nothing "on deck", but there is something on the cw event queue, so pop it off queue and put it "on deck". + self.cw_next_event_ts, self.cw_next_keydown = self.cw_event_queue.popleft() + if DEBUG_CW_JITTER: print(f'{ts:10.4f} queue len: {cw_queue_len}, popping: {self.cw_next_keydown} {self.cw_next_event_ts:10.4f}') + if ts >= self.cw_next_event_ts: + if DEBUG_CW_JITTER: print(f'{ts:10.4f} set_remote_cwkey: {self.cw_next_keydown}') + QS.set_remote_cwkey(self.cw_next_keydown) + self.cw_key_down = self.cw_next_keydown + cw_queue_len = len(self.cw_event_queue) + if cw_queue_len > 0: + self.cw_next_event_ts, self.cw_next_keydown = self.cw_event_queue.popleft() + if DEBUG_CW_JITTER: print(f'{ts:10.4f} queue len: {cw_queue_len}, popping: {self.cw_next_keydown} {self.cw_next_event_ts:10.4f}') + else: + self.cw_next_event_ts = None + self.cw_next_keydown = None + + def StopTransmit(self): + # TODO: Add code for modes other than CW + while len(self.cw_event_queue): + self.cw_event_queue.popleft() + self.cw_next_event_ts = None + self.cw_next_keydown = None + self.cw_key_down = 0 + QS.set_remote_cwkey(0) + + def ProcessRadioBtn(self, command, cmd_text): + # Large and Small format screens send different button events for radio buttons. + # Band buttons: + if command in self.conf.BandList: + self.app.bandBtnGroup.SetLabel(command, True) + return True # We processed this command + if command in ("Audio", "Time"): + self.app.bandBtnGroup.SetLabel(command, True) + return True # We processed this command + Mode = self.app.modeButns.SetLabel + Screen = self.app.screenBtnGroup.SetLabel + # Mode buttons: process both formats: + if cmd_text in ("CW U/L;0", "CWL;0", "CWL;1"): + Mode("CWL", True) + elif cmd_text in ("CW U/L;1", "CWU;0", "CWU;1"): + Mode("CWU", True) + elif cmd_text in ("SSB U/L;0", "LSB;0", "LSB;1"): + Mode("LSB", True) + elif cmd_text in ("SSB U/L;1", "USB;0", "USB;1"): + Mode("USB", True) + elif cmd_text in ("AM;0", "AM;1"): + Mode("AM", True) + elif cmd_text in ("FM;0", "FM;1"): + Mode("FM", True) + elif cmd_text in ("DGT;0", "DGT-U;0", "DGT-U;1"): + Mode("DGT-U", True) + elif cmd_text in ("DGT;1", "DGT-L;0", "DGT-L;1"): + Mode("DGT-L", True) + elif cmd_text in ("DGT;2", "DGT-FM;0", "DGT-FM;1"): + Mode("DGT-FM", True) + elif cmd_text in ("DGT;3", "DGT-IQ;0", "DGT-IQ;1"): + Mode("DGT-IQ", True) + elif cmd_text in ("FDV;0", "FDV-U;0", "FDV-U;1"): + Mode("FDV-U", True) + elif cmd_text in ("FDV;1", "FDV-L;0", "FDV-L;1"): + Mode("FDV-L", True) + # Screen buttons: process both formats: + # Due to ambiguous received commands, the setting is always "Graph" or "WFall" without "P1 or "P2". + # The P1 and P2 are handled at the control head so this shouldn't matter. + elif cmd_text in ("Graph;0", "Graph;0", "Graph;1"): + Screen("Graph", True) + elif cmd_text in ("Graph;1", "GraphP1;0", "GraphP1;1"): + Screen("Graph", True) + elif cmd_text in ("Graph;2", "GraphP2;0", "GraphP2;1"): + Screen("Graph", True) + elif cmd_text in ("WFall;0", "WFall;0", "WFall;1"): + Screen("WFall", True) + elif cmd_text in ("WFall;1", "WFallP1;0", "WFallP1;1"): + Screen("WFall", True) + elif cmd_text in ("WFall;2", "WFallP2;0", "WFallP2;1"): + Screen("WFall", True) + else: + return False + return True # We processed this command diff --git a/ac2yd/remote_hermes.py b/ac2yd/remote_hermes.py new file mode 100644 index 0000000..38b0590 --- /dev/null +++ b/ac2yd/remote_hermes.py @@ -0,0 +1,25 @@ +# This provides access to a remote radio. See ac2yd/remote_common.py and .pdf files for documentation. + +from hermes.quisk_hardware import Hardware as Radio +from ac2yd.remote_common import Remot + +class Hardware(Remot, Radio): + def __init__(self, app, conf): + Radio.__init__(self, app, conf) + Remot.__init__(self, app, conf) + def open(self): + Remot.open(self) + return "Server: " + Radio.open(self) + def close(self): + Remot.close(self) + Radio.close(self) + def HeartBeat(self): + Remot.HeartBeat(self) + Radio.HeartBeat(self) + if self.app.remote_control_slave: + widg = self.app.bottom_widgets + t = "HL2_TEMP;%s;%s;%s;%s\n" % ( + widg.text_temperature.GetLabel(), widg.text_pa_current.GetLabel(), + widg.text_fwd_power.GetLabel(), widg.text_swr.GetLabel()) + self.RemoteCtlSend(t) + diff --git a/ac2yd/remote_hiqsdr.py b/ac2yd/remote_hiqsdr.py new file mode 100644 index 0000000..393ab92 --- /dev/null +++ b/ac2yd/remote_hiqsdr.py @@ -0,0 +1,18 @@ +# This provides access to a remote radio. See ac2yd/remote_common.py and .pdf files for documentation. + +from hiqsdr.quisk_hardware import Hardware as Radio +from ac2yd.remote_common import Remot + +class Hardware(Remot, Radio): + def __init__(self, app, conf): + Radio.__init__(self, app, conf) + Remot.__init__(self, app, conf) + def open(self): + Remot.open(self) + return "Server: " + Radio.open(self) + def close(self): + Remot.close(self) + Radio.close(self) + def HeartBeat(self): + Remot.HeartBeat(self) + Radio.HeartBeat(self) diff --git a/ac2yd/remote_softrock.py b/ac2yd/remote_softrock.py new file mode 100644 index 0000000..9cf3db1 --- /dev/null +++ b/ac2yd/remote_softrock.py @@ -0,0 +1,19 @@ +# This provides access to a remote radio. See ac2yd/remote_common.py and .pdf files for documentation. + +from softrock.hardware_usb import Hardware as Radio +from ac2yd.remote_common import Remot + +class Hardware(Remot, Radio): + def __init__(self, app, conf): + Radio.__init__(self, app, conf) + Remot.__init__(self, app, conf) + def open(self): + Remot.open(self) + return "Server: " + Radio.open(self) + def close(self): + Remot.close(self) + Radio.close(self) + def HeartBeat(self): + Remot.HeartBeat(self) + Radio.HeartBeat(self) + diff --git a/afedrinet/SOURCE.txt b/afedrinet/SOURCE.txt new file mode 100644 index 0000000..7e70a2e --- /dev/null +++ b/afedrinet/SOURCE.txt @@ -0,0 +1,3 @@ +The files in this afedrinet directory came from http://www.afedri-sdr.com. Only slight +modifications were made by N2ADR. Thanks Alex!!! +N2ADR: Changes made 26 January 2019. diff --git a/afedrinet/__init__.py b/afedrinet/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/afedrinet/__init__.py @@ -0,0 +1 @@ +# diff --git a/afedrinet/af_comp.bat.makeit b/afedrinet/af_comp.bat.makeit new file mode 100644 index 0000000..5ab7034 --- /dev/null +++ b/afedrinet/af_comp.bat.makeit @@ -0,0 +1 @@ +gcc -o afedrinet_io.pyd --shared afedrinet_io.c ../is_key_down.c ../import_quisk_api.c -O3 -I"../" -I"C:\Programs\Python27\include" -L"C:\Programs\Python27\libs" -lws2_32 -lpython27 \ No newline at end of file diff --git a/afedrinet/afe_library b/afedrinet/afe_library new file mode 100644 index 0000000..ef9a493 --- /dev/null +++ b/afedrinet/afe_library @@ -0,0 +1,4 @@ +#!/bin/bash +#replace include path and library name to right one for your system +# for example for Python 2.7 it will look like -I"/usr/include/python2.7/" -lpython2.7 +gcc -o afedrinet_io.so --shared afedrinet_io.c ../is_key_down.c ../import_quisk_api.c -fPIC -O3 -I"../" -I"/usr/include/python2.7/" -lpython2.7 diff --git a/afedrinet/afe_library.mac b/afedrinet/afe_library.mac new file mode 100644 index 0000000..30eb5a0 --- /dev/null +++ b/afedrinet/afe_library.mac @@ -0,0 +1,5 @@ +#!/bin/bash +#replace include path and library name to right one for your system +# for example for Python 2.7 it will look like -I"/usr/include/python2.7/" -lpython2.7 +#/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7 +gcc -o afedrinet_io.so --shared afedrinet_io.c ../is_key_down.c ../import_quisk_api.c -I"../" -I"/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7/" -L"/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/" -lpython2.7 diff --git a/afedrinet/afedri.py b/afedrinet/afedri.py new file mode 100644 index 0000000..ede3027 --- /dev/null +++ b/afedrinet/afedri.py @@ -0,0 +1,219 @@ +#!/usr/bin/python + +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division + +################################################## +# AFEDRI Class module +# Title: afedri.py +# Author: k3it + +# Adopted to work with Quisk +# by 4Z5LV +# Last Changes: Sat Feb 02 2013 +# Version: 2.2 + +# Adapted to work with Python3 +# by N2ADR +# Last Changes: January 2020 +################################################## + +from socket import * +import wave +import sys +import struct +import time +import datetime +import string +import math + + +class afedri(object): + """ + class definition for the Afedri SDR-NET + """ + def __init__(self,sdr_address="0.0.0.0", sdr_port=50000): + if sdr_address == "0.0.0.0": + __sdr_address,self.sdr_port = self.__discover_afedri() + if __sdr_address is None: + self.s = None + return + else: + __sdr_address = sdr_address + self.sdr_port = sdr_port + + self.s = socket(AF_INET, SOCK_STREAM) + self.s.settimeout(2) + try: + self.s.connect((__sdr_address,self.sdr_port)) + #print ("Established control connection with AFEDRI") + except: + print ("Error connecting to SDR") + ##sys.exit() + self.s.close() + self.s = None + def close(self): + if self.s: + self.s.close() + self.s = None + def set_center_freq(self,target_freq): + if not self.s: return 1 + __next_freq = target_freq + __next_freq = struct.pack(">3) + return __rf_gain + + def set_gain_indx(self,indx): + if not self.s: return 1 + __gain = (indx << 3) + 1 + # special afedri calculation for the gain byte + #__gain = ((__gain+10)/3 << 3) + 1 + __set_gain_cmd = b"\x06\x00\x38\x00\x00" + struct.pack("B",__gain) + self.s.send(__set_gain_cmd) + __data = self.s.recv(6) + __rf_gain = -10 + 3 * (struct.unpack("B",__data[5:6])[0]>>3) + return __rf_gain + + def get_gain(self): + """ + NOT IMPLEMENTED IN AFEDRI?. DON'T USE + """ + if not self.s: return 1 + __get_gain_cmd = b"\x05\x20\x38\x00\x00" + self.s.send(__get_gain_cmd) + __data = self.s.recv(6) + __rf_gain = -10 + 3 * (struct.unpack("B",__data[5:])[0]>>3) + return __rf_gain + + def get_fe_clock(self): + if not self.s: return 1 + __get_lword_cmd = b"\x09\xE0\x02\x55\x00\x00\x00\x00\x00" + __get_hword_cmd = b"\x09\xE0\x02\x55\x01\x00\x00\x00\x00" + self.s.send(__get_lword_cmd) + __data_l = self.s.recv(9) + self.s.send(__get_hword_cmd) + __data_h = self.s.recv(9) + __fe_clock = struct.unpack("',__DISCOVER_SERVER_PORT)) + try: + __msg=self.sin.recv(256,0) + __devname=__msg[5:20] + __devname=__devname.decode('utf-8') + __sn=__msg[21:36] + __sn=__sn.decode('utf-8') + __ip=inet_ntoa(__msg[40:36:-1]) + __port=struct.unpack("= 0.5): + floor_div += 1 +if floor_div < 15: + floor_div = 15 + #print ("Warning: Max supported sampling rate is", math.floor(fe_main_clock_freq / (4 * floor_div))) +elif floor_div > 625: + floor_div = 625 + #print ("Warning: Min supported sampling rate is", math.floor(fe_main_clock_freq / (4 * floor_div))) + +dSR = fe_main_clock_freq / (4 * floor_div) +floor_SR = math.floor(dSR) +if (dSR - floor_SR >= 0.5): + floor_SR += 1 +if floor_SR != samp_rate: + print ("Warning: invalid sample rate selected for the AFEDRI main clock (", fe_main_clock_freq, "Hz )") + print (" setting to the next valid value", samp_rate, " => ", floor_SR) + samp_rate = floor_SR +""" diff --git a/afedrinet/afedrinet_io.c b/afedrinet/afedrinet_io.c new file mode 100644 index 0000000..af179aa --- /dev/null +++ b/afedrinet/afedrinet_io.c @@ -0,0 +1,399 @@ +#include + +#ifdef MS_WINDOWS +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifdef MS_WINDOWS +#define QUISK_SHUT_RD SD_RECEIVE +#define QUISK_SHUT_BOTH SD_BOTH +#else +#include +#include +#define SOCKET int +#define INVALID_SOCKET -1 +#define QUISK_SHUT_RD SHUT_RD +#define QUISK_SHUT_BOTH SHUT_RDWR +#endif + +#include + +#define IMPORT_QUISK_API +#include "quisk.h" +//#include "sdriq.h" + +static SOCKET rx_udp_socket = INVALID_SOCKET; // Socket for receiving ADC samples from UDP +static int rx_udp_started = 0; // Have we received any data yet? +static int rx_udp_read_blocks = 0; // Number of blocks to read for each read call +static double rx_udp_gain_correct = 1; // For decimation by 5, correct by 4096 / 5**5 +static int use_remove_dc=0; // Remove DC from samples +//static int quisk_using_udp = 0; +// This module provides access to the SDR-IQ by RfSpace. It is the source +// for the Python extension module sdriq. It can be used as a model for an +// extension module for other hardware. Read the end of this file for more +// information. This module was written by James Ahlstrom, N2ADR. + +// This module uses the Python interface to import symbols from the parent _quisk +// extension module. It must be linked with import_quisk_api.c. See the documentation +// at the start of import_quisk_api.c. + +// Start of SDR-IQ specific code: +// +#define DEBUG 0 + +// Type field for the message block header; upper 3 bits of byte +#define TYPE_HOST_SET 0 +#define TYPE_HOST_GET (1 << 5) +#define NAME_SIZE 16 + +//#define UDP_BROADCAST +#ifdef UDP_BROADCAST + #define FIRST_IQ_DATA_IDX 20 + #define RX_UDP_SIZE 1044 // Expected size of UDP samples packet +#else + #define RX_UDP_SIZE 1028 // Expected size of UDP samples packet + #define FIRST_IQ_DATA_IDX 4 +#endif +#define BROADCAST_HEADER_SIZE 16 +#define UDP_PROTCOL_ID1 0x04 +#define UDP_PROTCOL_ID2 0x18 + +#ifdef DEBUG_IO +#undef DEBUG_IO +#define DEBUG_IO 1 +#endif + +static PyObject * open_rx_udp(const char * ip, int port) +{ +// const char * ip; +// int port; + char buf[128]; + struct sockaddr_in Addr; + int recvsize; + char optval; +#if DEBUG_IO + int intbuf; +#ifdef MS_WINDOWS + int bufsize = sizeof(int); +#else + socklen_t bufsize = sizeof(int); +#endif +#endif + +#ifdef MS_WINDOWS + WORD wVersionRequested; + WSADATA wsaData; +#endif + +// if (!PyArg_ParseTuple (args, "si", &ip, &port)) +// return NULL; +// port = 50000; +#ifdef MS_WINDOWS + wVersionRequested = MAKEWORD(2, 2); + if (WSAStartup(wVersionRequested, &wsaData) != 0) { + sprintf(buf, "Failed to initialize Winsock (WSAStartup)"); + return PyString_FromString(buf); + } +#endif +// quisk_using_udp = 1; + rx_udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (rx_udp_socket != INVALID_SOCKET) + { + optval=1; + setsockopt( rx_udp_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) ); + recvsize = 256000; + setsockopt(rx_udp_socket, SOL_SOCKET, SO_RCVBUF, (char *)&recvsize, sizeof(recvsize)); + memset(&Addr, 0, sizeof(Addr)); + Addr.sin_family = AF_INET; + Addr.sin_port = htons(port); + Addr.sin_addr.s_addr = htonl(INADDR_ANY);//inet_addr("192.168.0.8"); +// Addr.sin_addr.S_un.S_addr = inet_addr(ip); +// if (connect(rx_udp_socket, (const struct sockaddr *)&Addr, sizeof(Addr)) != 0) + if (bind(rx_udp_socket, (const struct sockaddr *)&Addr, sizeof(Addr)) != 0) + { + shutdown(rx_udp_socket, QUISK_SHUT_BOTH); + close(rx_udp_socket); + rx_udp_socket = INVALID_SOCKET; + sprintf(buf, "Failed to connect to UDP %s port %u", ip, port); + } + else { + sprintf(buf, "Capture from UDP %s port %u", ip, port); +#if DEBUG_IO + if (getsockopt(rx_udp_socket, SOL_SOCKET, SO_RCVBUF, (char *)&intbuf, &bufsize) == 0) + { + printf("UDP socket receive buffer size %d\n", intbuf); + printf("address %s port %u\n", ip, port); + } + else + printf ("Failure SO_RCVBUF\n"); +#endif + } + } + else { + sprintf(buf, "Failed to open socket"); + } + return PyString_FromString(buf); +} + +static PyObject * close_rx_udp(PyObject * self, PyObject * args) +{ + short msg = 0x7373; // shutdown + + if (!PyArg_ParseTuple (args, "")) + return NULL; + + if (rx_udp_socket != INVALID_SOCKET) { + shutdown(rx_udp_socket, QUISK_SHUT_RD); + send(rx_udp_socket, (char *)&msg, 2, 0); + send(rx_udp_socket, (char *)&msg, 2, 0); + QuiskSleepMicrosec(3000000); + close(rx_udp_socket); + rx_udp_socket = INVALID_SOCKET; + } + rx_udp_started = 0; + +// if (quisk_using_udp) { +// quisk_using_udp = 0; + +#ifdef MS_WINDOWS + WSACleanup(); +#endif +// } + Py_INCREF (Py_None); + return Py_None; +} + +int afedri_read_rx_udp(complex * samp) // Read samples from UDP +{ // Size of complex sample array is SAMP_BUFFER_SIZE + ssize_t bytes; + //int SR = 0; + static int sample_rate = 0; // Sample rate such as 48000, 96000, 192000 + unsigned char buf[1500]; // Maximum Ethernet is 1500 bytes. + static unsigned short seq0; // must be 8 bits + unsigned short seq_curr = 0; +#ifdef MS_WINDOWS + __int32 i, count, nSamples, xr, xi, index; +#else + int32_t i, count, nSamples, xr, xi, index; +#endif + unsigned char * ptxr, * ptxi; + static complex dc_average = 0; // Average DC component in samples + static complex dc_sum = 0; + static int dc_count = 0; + static int dc_key_delay = 0; + + // Data from the receiver is little-endian +// if ( !rx_udp_read_blocks) + if(sample_rate != pt_quisk_sound_state->sample_rate) + { + sample_rate = pt_quisk_sound_state->sample_rate; + // "rx_udp_read_blocks" is the number of UDP blocks to read at once + rx_udp_read_blocks = (int)(pt_quisk_sound_state->data_poll_usec * 1e-6 * sample_rate + 0.5); + rx_udp_read_blocks = (rx_udp_read_blocks + (RX_UDP_SIZE / 12)) / (RX_UDP_SIZE / 6); // 6 bytes per sample + if (rx_udp_read_blocks < 1) + rx_udp_read_blocks = 1; +#if DEBUG_IO + printf("read_rx_udp: rx_udp_read_blocks %d\n", rx_udp_read_blocks); +#endif + } +/* if ( ! rx_udp_gain_correct) { + int dec; + dec = (int)(rx_udp_clock / sample_rate + 0.5); + if ((dec / 5) * 5 == dec) // Decimation by a factor of 5 + rx_udp_gain_correct = 1.31072; + else // Decimation by factors of two + rx_udp_gain_correct = 1.0; + } +*/ + nSamples = 0; + for (count = 0; count < rx_udp_read_blocks; count++) + { // read several UDP blocks +#if DEBUG_IO +// printf("Data RX Process Begin %u\n",count); +#endif + bytes = recv(rx_udp_socket, (char *)buf, RX_UDP_SIZE, 0); // blocking read + if (bytes != RX_UDP_SIZE) { // Known size of sample block + pt_quisk_sound_state->read_error++; +#if DEBUG_IO + printf("read_rx_udp: Bad block size %i\n", (int)bytes); +#endif + continue; + } + // buf[0] is the sequence number + // buf[1] is the status: + // bit 0: key up/down state + // bit 1: set for ADC overrange (clip) + seq_curr = buf[2] | (buf[3] << 8); + if (seq_curr != seq0) { +#if DEBUG_IO + printf("read_rx_udp: Bad sequence want %3d got %3d at block %d of %d\n", + (unsigned int)seq0, (unsigned int)buf[0], count, rx_udp_read_blocks); +#endif + pt_quisk_sound_state->read_error++; + } + seq0 = seq_curr + 1; // Next expected sequence number + // quisk_set_key_down(buf[1] & 0x01); // bit zero is key state + // if (buf[1] & 0x02) // bit one is ADC overrange + // quisk_sound_state.overrange++; + index = FIRST_IQ_DATA_IDX; + ptxr = (unsigned char *)&xr; + ptxi = (unsigned char *)ξ + // convert 24-bit samples to 32-bit samples; int must be 32 bits. + while (index < bytes) + { + xr = xi = 0; + memcpy (ptxr + 2, buf + index, 2); + index += 2; + memcpy (ptxi + 2, buf + index, 2); + index += 2; + samp[nSamples++] = (xr + xi * I) * rx_udp_gain_correct; + xr = xi = 0; + memcpy (ptxr + 2, buf + index, 2); + index += 2; + memcpy (ptxi + 2, buf + index, 2); + index += 2; + samp[nSamples++] = (xr + xi * I) * rx_udp_gain_correct;; + //if (nSamples == 2) printf("%12d %12d\n", xr, xi); + } + } + if (quisk_is_key_down()) { + dc_key_delay = 0; + dc_sum = 0; + dc_count = 0; + } + else if (dc_key_delay < pt_quisk_sound_state->sample_rate) { + dc_key_delay += nSamples; + } + else { + dc_count += nSamples; + for (i = 0; i < nSamples; i++) // Correction for DC offset in samples + dc_sum += samp[i]; + if (dc_count > pt_quisk_sound_state->sample_rate * 2) { + dc_average = dc_sum / dc_count; + dc_sum = 0; + dc_count = 0; + //printf("dc average %lf %lf %d\n", creal(dc_average), cimag(dc_average), dc_count); + //printf("dc polar %.0lf %d\n", cabs(dc_average), + // (int)(360.0 / 2 / M_PI * atan2(cimag(dc_average), creal(dc_average)))); + } + } + if (use_remove_dc) + for (i = 0; i < nSamples; i++) // Correction for DC offset in samples + samp[i] -= dc_average; + +// printf("%u\n", pt_quisk_sound_state->sample_rate); + return nSamples; +} + + + +// End of most AFEDRI specific code. + +/////////////////////////////////////////////////////////////////////////// +// The API requires at least two Python functions for Open and Close, plus +// additional Python functions as needed. And it requires exactly three +// C funcions for Start, Stop and Read samples. Quisk runs in two threads, +// a GUI thread and a sound thread. You must not call the GUI or any Python +// code from the sound thread. You must return promptly from functions called +// by the sound thread. +// +// The calling sequence is Open, Start, then repeated calls to Read, then +// Stop, then Close. + +// Start of Application Programming Interface (API) code: + +// Called to close the sample source; called from the GUI thread. +static PyObject * close_samples(PyObject * self, PyObject * args) +{ + if (!PyArg_ParseTuple (args, "")) + return NULL; + close_rx_udp(self, args); + Py_INCREF (Py_None); + return Py_None; +} + +// Called to open the sample source; called from the GUI thread. +static PyObject * open_samples(PyObject * self, PyObject * args) +{ + const char * ip; + int port; +// const char * name; +// char buf[128]; + + if (!PyArg_ParseTuple (args, "si", &ip, &port)) + return NULL; + +// name = QuiskGetConfigString("sdriq_name", "NoName"); +// sdriq_clock = QuiskGetConfigDouble("sdriq_clock", 66666667.0); + +// Record our C-language Start/Stop/Read functions for use by sound.c. + quisk_sample_source(NULL, NULL, &afedri_read_rx_udp); +////////////// + return open_rx_udp(ip, port); // AFEDRI specific +// return PyString_FromString(buf); // return a string message +} + +// Miscellaneous functions needed by the SDR-IQ; called from the GUI thread as +// a result of button presses. + + +// Functions callable from Python are listed here: +static PyMethodDef QuiskMethods[] = { + {"open_samples", open_samples, METH_VARARGS, "Open the AFEDRI SDR-Net."}, + {"close_samples", close_samples, METH_VARARGS, "Close the AFEDRI SDR-Net."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +#if PY_MAJOR_VERSION < 3 +// Python 2.7: +PyMODINIT_FUNC initafedrinet_io (void) +{ + if (Py_InitModule ("afedrinet_io", QuiskMethods) == NULL) { + printf("Py_InitModule failed!\n"); + return; + } + // Import pointers to functions and variables from module _quisk + if (import_quisk_api()) { + printf("Failure to import pointers from _quisk\n"); + return; //Error + } +} + +// Python 3: +#else +static struct PyModuleDef afedrinet_iomodule = { + PyModuleDef_HEAD_INIT, + "afedrinet_io", + NULL, + -1, + QuiskMethods +} ; + +PyMODINIT_FUNC PyInit_afedrinet_io(void) +{ + PyObject * m; + + m = PyModule_Create(&afedrinet_iomodule); + if (m == NULL) + return NULL; + // Import pointers to functions and variables from module _quisk + if (import_quisk_api()) { + printf("Failure to import pointers from _quisk\n"); + return m; //Error + } + return m; +} +#endif diff --git a/afedrinet/makefile b/afedrinet/makefile new file mode 100644 index 0000000..e0fb199 --- /dev/null +++ b/afedrinet/makefile @@ -0,0 +1,7 @@ + +afedrinet2: + python2 setup.py build_ext --force --inplace + +afedrinet3: + python3 setup.py build_ext --force --inplace + diff --git a/afedrinet/quisk_conf.py b/afedrinet/quisk_conf.py new file mode 100644 index 0000000..a06e908 --- /dev/null +++ b/afedrinet/quisk_conf.py @@ -0,0 +1,61 @@ +# This is a sample quisk_conf.py configuration file for Microsoft Windows. + +# For Windows, your default config file name is "My Documents/quisk_conf.py", +# but you can use a different config file by using -c or --config. Quisk creates +# an initial default config file if there is none. To control Quisk, edit +# "My Documents/quisk_conf.py" using any text editor; for example WordPad (not Notepad). + +# In Windows you can see what sound devices you have, and you can set the Primary +# Device for capture and playback by using Control Panel/Sounds and Audio Devices. +# If you have only one sound device, it should be set as "Primary". If you have +# several, find the names by using Control Panel/Sounds and Audio Devices; for +# example, you may have "SoundMAX HD Audio" in the list for "Sound playback" and +# "Sound recording". To specify this device for capture (recording) or playback, +# enter a unique part of its name using exact upper/lower case. For example: +# name_of_sound_capture = "SoundMAX" +# name_of_sound_play = "SoundMAX" + +# There are many possible options for your config file. Copy the ones you want +# from the master file quisk_conf_defaults.py (but don't change the master file). +# The master config file is located in the site-packages/quisk folder for Python 2.7. + +# This file is Python code and the comment character is "#". To ignore a line, +# start it with "#". To un-ignore a line, remove the "#". Generally you must start +# lines in column one (the left edge) except for logic blocks. +from afedrinet import quisk_hardware # Use different hardware file + +use_rx_udp = 1 # Get ADC samples from UDP +rx_udp_ip = "192.168.0.8" # Sample source IP address +rx_udp_port = 50000 # Sample source UDP port +rx_udp_clock = 79998382 # ADC sample rate in Hertz +#rx_udp_decimation = 8 * 8 * 8 # Decimation from clock to UDP sample rate +#sample_rate = int(float(rx_udp_clock) / rx_udp_decimation + 0.5) # Don't change this +data_poll_usec = 10000 +#sample_rate = 192000 # ADC hardware sample rate in Hertz +sample_rate = 740740 # ADC hardware sample rate in Hertz +playback_rate = 48000 # Radio sound play rate +name_of_sound_capt = ""#AFEDRI-SDR-Net Audio" # Name of soundcard capture hardware device. +name_of_sound_play = "Buil-in Output" # Use the same device for play back. +#name_of_sound_play = "Line 1"#Virtual Audio Cable" # Use the same device for play back. +latency_millisecs = 50 # latency time in milliseconds +display_fraction = 0.92 # The edges of the full bandwidth are not valid +default_rf_gain = 11 +# Select the default screen when Quisk starts: +#default_screen = 'Graph' +default_screen = 'WFall' + +# If you use hardware with a fixed VFO (crystal controlled SoftRock) un-comment the following: +# import quisk_hardware_fixed as quisk_hardware +# fixed_vfo_freq = 7056000 + +# If you use an SDR-IQ for capture, first install the SpectraView software +# that came with the SDR-IQ. This will install the USB driver. Then set these parameters: +# import quisk_hardware_sdriq as quisk_hardware # Use different hardware file +# use_sdriq = 1 # Capture device is the SDR-IQ +# sdriq_name = "SDR-IQ" # Name of the SDR-IQ device to open +# sdriq_clock = 66666667.0 # actual sample rate (66666667 nominal) +# sdriq_decimation = 500 # Must be 360, 500, 600, or 1250 +# sample_rate = int(float(sdriq_clock) / sdriq_decimation + 0.5) # Don't change this +# name_of_sound_capt = "" # We do not capture from the soundcard +# playback_rate = 48000 # Radio sound play rate, default 48000 +# display_fraction = 0.85 # The edges of the full bandwidth are not valid diff --git a/afedrinet/quisk_conf_linux.py b/afedrinet/quisk_conf_linux.py new file mode 100644 index 0000000..ee19885 --- /dev/null +++ b/afedrinet/quisk_conf_linux.py @@ -0,0 +1,54 @@ +# This is a sample quisk_conf.py configuration file for Linux +# For Windows, your default config file name is "/home/user/quisk_conf.py" +# but you can use a different config file by using -c or --config. Quisk creates +# an initial default config file if there is none. To control Quisk, edit +# "/home/user/quisk_conf.py" using any text editor; for example WordPad (not Notepad). +# In Windows you can see what sound devices you have, and you can set the Primary +# Device for capture and playback by using Control Panel/Sounds and Audio Devices. +# If you have only one sound device, it should be set as "Primary". If you have +# several, find the names by using Control Panel/Sounds and Audio Devices; for +# example, you may have "SoundMAX HD Audio" in the list for "Sound playback" and +# "Sound recording". To specify this device for capture (recording) or playback, +# enter a unique part of its name using exact upper/lower case. For example: +# name_of_sound_capture = "SoundMAX" +# name_of_sound_play = "SoundMAX" +# There are many possible options for your config file. Copy the ones you want +# from the master file quisk_conf_defaults.py (but don't change the master file). +# The master config file is located in the site-packages/quisk folder for Python 2.7. +# This file is Python code and the comment character is "#". To ignore a line, +# start it with "#". To un-ignore a line, remove the "#". Generally you must start +# lines in column one (the left edge) except for logic blocks. +from afedrinet import quisk_hardware # Use different hardware file +use_rx_udp = 1 # Get ADC samples from UDP +rx_udp_ip = "192.168.0.8" # Sample source IP address +rx_udp_port = 50000 # Sample source UDP port +rx_udp_clock = 80000000 # ADC sample rate in Hertz +#rx_udp_decimation = 8 * 8 * 8 # Decimation from clock to UDP sample rate +#sample_rate = int(float(rx_udp_clock) / rx_udp_decimation + 0.5) # Don't change this +data_poll_usec = 10000 +#sample_rate = 192000 # ADC hardware sample rate in Hertz +sample_rate = 740740 # ADC hardware sample rate in Hertz +playback_rate = 48000 # Radio sound play rate +name_of_sound_capt = ""#AFEDRI-SDR-Net Audio" # Name of soundcard capture hardware device. +name_of_sound_play = "portaudiodefault"#hw:0,0" # Use the same device for play back. +#name_of_sound_play = "Line 1"#Virtual Audio Cable" # Use the same device for play back. +latency_millisecs = 50 # latency time in milliseconds +display_fraction = 0.92 # The edges of the full bandwidth are not valid +default_rf_gain = 14 +# Select the default screen when Quisk starts: +#default_screen = 'Graph' +default_screen = 'WFall' +# If you use hardware with a fixed VFO (crystal controlled SoftRock) un-comment the following: +# import quisk_hardware_fixed as quisk_hardware +# fixed_vfo_freq = 7056000 +# If you use an SDR-IQ for capture, first install the SpectraView software +# that came with the SDR-IQ. This will install the USB driver. Then set these parameters: +# import quisk_hardware_sdriq as quisk_hardware # Use different hardware file +# use_sdriq = 1 # Capture device is the SDR-IQ +# sdriq_name = "SDR-IQ" # Name of the SDR-IQ device to open +# sdriq_clock = 66666667.0 # actual sample rate (66666667 nominal) +# sdriq_decimation = 500 # Must be 360, 500, 600, or 1250 +# sample_rate = int(float(sdriq_clock) / sdriq_decimation + 0.5) # Don't change this +# name_of_sound_capt = "" # We do not capture from the soundcard +# playback_rate = 48000 # Radio sound play rate, default 48000 +# display_fraction = 0.85 # The edges of the full bandwidth are not valid diff --git a/afedrinet/quisk_conf_mac.py b/afedrinet/quisk_conf_mac.py new file mode 100644 index 0000000..ee19885 --- /dev/null +++ b/afedrinet/quisk_conf_mac.py @@ -0,0 +1,54 @@ +# This is a sample quisk_conf.py configuration file for Linux +# For Windows, your default config file name is "/home/user/quisk_conf.py" +# but you can use a different config file by using -c or --config. Quisk creates +# an initial default config file if there is none. To control Quisk, edit +# "/home/user/quisk_conf.py" using any text editor; for example WordPad (not Notepad). +# In Windows you can see what sound devices you have, and you can set the Primary +# Device for capture and playback by using Control Panel/Sounds and Audio Devices. +# If you have only one sound device, it should be set as "Primary". If you have +# several, find the names by using Control Panel/Sounds and Audio Devices; for +# example, you may have "SoundMAX HD Audio" in the list for "Sound playback" and +# "Sound recording". To specify this device for capture (recording) or playback, +# enter a unique part of its name using exact upper/lower case. For example: +# name_of_sound_capture = "SoundMAX" +# name_of_sound_play = "SoundMAX" +# There are many possible options for your config file. Copy the ones you want +# from the master file quisk_conf_defaults.py (but don't change the master file). +# The master config file is located in the site-packages/quisk folder for Python 2.7. +# This file is Python code and the comment character is "#". To ignore a line, +# start it with "#". To un-ignore a line, remove the "#". Generally you must start +# lines in column one (the left edge) except for logic blocks. +from afedrinet import quisk_hardware # Use different hardware file +use_rx_udp = 1 # Get ADC samples from UDP +rx_udp_ip = "192.168.0.8" # Sample source IP address +rx_udp_port = 50000 # Sample source UDP port +rx_udp_clock = 80000000 # ADC sample rate in Hertz +#rx_udp_decimation = 8 * 8 * 8 # Decimation from clock to UDP sample rate +#sample_rate = int(float(rx_udp_clock) / rx_udp_decimation + 0.5) # Don't change this +data_poll_usec = 10000 +#sample_rate = 192000 # ADC hardware sample rate in Hertz +sample_rate = 740740 # ADC hardware sample rate in Hertz +playback_rate = 48000 # Radio sound play rate +name_of_sound_capt = ""#AFEDRI-SDR-Net Audio" # Name of soundcard capture hardware device. +name_of_sound_play = "portaudiodefault"#hw:0,0" # Use the same device for play back. +#name_of_sound_play = "Line 1"#Virtual Audio Cable" # Use the same device for play back. +latency_millisecs = 50 # latency time in milliseconds +display_fraction = 0.92 # The edges of the full bandwidth are not valid +default_rf_gain = 14 +# Select the default screen when Quisk starts: +#default_screen = 'Graph' +default_screen = 'WFall' +# If you use hardware with a fixed VFO (crystal controlled SoftRock) un-comment the following: +# import quisk_hardware_fixed as quisk_hardware +# fixed_vfo_freq = 7056000 +# If you use an SDR-IQ for capture, first install the SpectraView software +# that came with the SDR-IQ. This will install the USB driver. Then set these parameters: +# import quisk_hardware_sdriq as quisk_hardware # Use different hardware file +# use_sdriq = 1 # Capture device is the SDR-IQ +# sdriq_name = "SDR-IQ" # Name of the SDR-IQ device to open +# sdriq_clock = 66666667.0 # actual sample rate (66666667 nominal) +# sdriq_decimation = 500 # Must be 360, 500, 600, or 1250 +# sample_rate = int(float(sdriq_clock) / sdriq_decimation + 0.5) # Don't change this +# name_of_sound_capt = "" # We do not capture from the soundcard +# playback_rate = 48000 # Radio sound play rate, default 48000 +# display_fraction = 0.85 # The edges of the full bandwidth are not valid diff --git a/afedrinet/quisk_hardware.py b/afedrinet/quisk_hardware.py new file mode 100644 index 0000000..c352e49 --- /dev/null +++ b/afedrinet/quisk_hardware.py @@ -0,0 +1,94 @@ +# Please do not change this hardware control module. +# It provides support for the SDR-IQ by RfSpace. + +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division + +#import _quisk as QS +import os,sys +#sys.path.append('./afedri') +try: + import afedrinet_io as AF + from sdr_control import * +except: + from afedrinet import afedrinet_io as AF + from afedrinet.sdr_control import * +from ctypes import * +os.environ['PATH'] = os.path.dirname(__file__) + ';' + os.environ['PATH'] +#from quisk import App as parent +from quisk_hardware_model import Hardware as BaseHardware +#from quisk import * + +class Hardware(BaseHardware): + def __init__(self, app, conf): + BaseHardware.__init__(self, app, conf) + self.local_tune = 2 + self.index = 0 + self.clock = 80000000 + self.old_LO_freq = 0 + self.rf_gain_labels = ('RF -10','RF -7','RF -4','RF -1','+2','+5','+8','+11','+14','+17','+20','+23','+26','+29','+32','+35') + self.conf = conf + self.plugin = Control(self.conf.rx_udp_ip, self.conf.rx_udp_port) + self.app = app + self.decimations = [] # supported decimation rates + for dec in (53333, 96000, 133333, 185185, 192000, 370370, 740740, 1333333): + self.decimations.append(dec) + def open(self): + self.plugin.OpenHW() # Return a config message + RF_Gain_idx = int((10 + self.conf.default_rf_gain) / 3) + if not 0 <= RF_Gain_idx < len(self.rf_gain_labels): + RF_Gain_idx = 0 + self.plugin.SetAttenuator(RF_Gain_idx) + self.app.BtnRfGain.SetIndex(RF_Gain_idx) + #print ("RF Gain %i" % self.conf.default_rf_gain) + return AF.open_samples(self.conf.rx_udp_ip, self.conf.rx_udp_port) + def close(self): + self.plugin.CloseHW() + def OnButtonRfGain(self, event): + btn = event.GetEventObject() + n = btn.index + if n > -1 or n < 16 : + self.plugin.SetAttenuator(n) + else: + print ('Unknown RfGain') + def ChangeFrequency(self, tune, vfo, source='', band='', event=None): + self.local_tune = tune + if vfo: + self.plugin.SetHWLO(vfo) + return tune, vfo + def ReturnFrequency(self): # Return the current tuning and VFO frequency + return (None, None) # Return LO frequency + def GetFirmwareVersion(self): + return 226 + def HeartBeat(self): +# self.PrintStatus('Start', 'AFEDRI') + return + def VarDecimGetChoices(self): # Return a list/tuple of strings for the decimation control. + l = [] # a list of sample rates + for dec in self.decimations: + l.append(str( dec )) + return l + def VarDecimGetLabel(self): # return a text label for the control + return "Sample rate sps" + def VarDecimGetIndex(self): # return the current index + return self.index + def VarDecimSet(self, index=None): # set decimation, return sample rate + if index is None: # initial call to set decimation before the call to open() + rate = self.application.vardecim_set # May be None or from different hardware + try: + dec = rate #int(float(self.conf.rx_udp_clock / rate + 0.5)) + self.index = self.decimations.index(dec) + except: + try: + self.index = self.decimations.index(self.conf.sample_rate) + except: + self.index = 0 + else: + self.index = index + dec = self.decimations[self.index] + self.plugin.SetHWSR(dec) # Return a config message + return dec + + + diff --git a/afedrinet/readme.txt b/afedrinet/readme.txt new file mode 100644 index 0000000..3d25238 --- /dev/null +++ b/afedrinet/readme.txt @@ -0,0 +1,38 @@ +Hello All! +I want to publish here some information about possibility to use Quisk application on MAC computers running MAC OSX. +It is for experienced MAC OS users that know what is Terminal and command shell tools and how to compile executable from source code: + +1. You should download Quisk package from author's web page +2. Xcode SDK and command tools +3. Install macports to manage ports that required to be used with Quisk +4. You should install the next packages: + python27, py27-wxpython-2.8, fftw-3.0 , portaudio, pulseaudio and probably some additional +5. Be ready that packages installation will take long time. Select macports python as default python executable: +>> sudo port select --set python python27 +6. After you will extract Quisk files from archive to separate directory you must enter Quisk directory and compile Quisk for your system running next command: +>> make macports +7. After installation to use Quisk with AFEDRi SDR, you have to download additional packages: afedriusb , afedrinet extract contains of archive to separated folders afedriusb and afedrinet in main Quisk directory. +8. For network connected AFEDRI you must enter afedrinet folder and compile SDR support library running afe_library script, before you can run this script you must modify path to python library and include folders in this script, for example on my MAC it looks like: +############################################################### +gcc -o afedrinet_io.so --shared afedrinet_io.c ../is_key_down.c ../import_quisk_api.c -I"../" -I"/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7/" -L"/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/" -lpython2.7 +################################################################### +9. For USB connection you must download the sdr_commander v1.22 + Extract archive to separate folder and compile source code. + After successful compilation you will get executable sdr_commander, you must + copy it to quick/afedriusb folder +10. + 10. Edit qusik_conf.py file to define correct sound device (card) name, copy this file as .quisk_conf.py to user's home directory. + For example for portaudio devices it will look like this one: + + ################################################################# + from afedriusb import quisk_hardware + sample_rate = 185185 + name_of_sound_capt = "portaudio#2" + name_of_sound_play = "portaudiodefault" + latency_millisecs = 150 + default_rf_gain = 14 + default_screen = 'WFall' + playback_rate = 48000 + default 48000 + ########################################################## + diff --git a/afedrinet/sdr_control.py b/afedrinet/sdr_control.py new file mode 100644 index 0000000..d43b101 --- /dev/null +++ b/afedrinet/sdr_control.py @@ -0,0 +1,38 @@ +#!/usr/bin/python # This is client.py file + +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division + +import os,sys +os.environ['PATH'] = os.path.dirname(__file__) + ';' + os.environ['PATH'] + +import afedrinet +import afedrinet.afedri + +class Control: + def __init__(self, sdr_address="192.168.0.8", sdr_port=50000): + self.hw = afedrinet.afedri.afedri(sdr_address, sdr_port) + if self.hw.s is None: # Failure to find the hardware + self.hw = None + def OpenHW(self): + if not self.hw: return + data = self.hw.get_sdr_name() + print (data[4:]) + self.hw.start_capture() + def CloseHW(self): + if not self.hw: return + self.hw.stop_capture() + self.hw.close # Close the socket when done + def SetHWLO(self, vfo): + if not self.hw: return + self.hw.set_center_freq(vfo) + def SetHWSR(self, sample_rate): + if not self.hw: return + self.hw.set_samp_rate(sample_rate) + print ("Sample Rate %i" % sample_rate) + def SetAttenuator(self, indx): + if not self.hw: return + self.hw.set_gain_indx(indx) + + diff --git a/afedrinet/setup.py b/afedrinet/setup.py new file mode 100644 index 0000000..b2bd2d4 --- /dev/null +++ b/afedrinet/setup.py @@ -0,0 +1,49 @@ +from distutils.core import setup, Extension +import sys + +# Afedri hardware support added by Alex, Alex@gmail.com + +if sys.platform == "win32": + Modules = [ + Extension ('afedrinet.afedrinet_io', + libraries = ['WS2_32'], + sources = ['../import_quisk_api.c', '../is_key_down.c', 'afedrinet_io.c'], + include_dirs = ['.', '..'], + ) + ] +else: + Modules = [ + Extension ('afedrinet.afedrinet_io', + libraries = ['m'], + sources = ['../import_quisk_api.c', '../is_key_down.c', 'afedrinet_io.c'], + include_dirs = ['.', '..'], + ) + ] + +setup (name = 'afedrinet_io', + version = '0.1', + description = 'Afedri', + long_description = "Afedri.", + author = 'Alex', + author_email = 'Alex@gmail.com', + #url = 'http://', + download_url = 'http://james.ahlstrom.name/quisk/', + packages = ['afedrinet.afedrinet_io'], + package_dir = {'afedrinet' : '.'}, + ext_modules = Modules, + classifiers = [ + 'Development Status :: 6 - Mature', + 'Environment :: X11 Applications', + 'Environment :: Win32 (MS Windows)', + 'Intended Audience :: End Users/Desktop', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Natural Language :: English', + 'Operating System :: POSIX :: Linux', + 'Operating System :: Microsoft :: Windows', + 'Programming Language :: Python', + 'Programming Language :: C', + 'Topic :: Communications :: Ham Radio', + ], +) + + diff --git a/afedrinet/test.py b/afedrinet/test.py new file mode 100644 index 0000000..f1c179b --- /dev/null +++ b/afedrinet/test.py @@ -0,0 +1,16 @@ +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division + +import os,sys +from ctypes import * +os.environ['PATH'] = os.path.dirname(__file__) + ';' + os.environ['PATH'] +from sdr_control import * +#import sdr_control + +#class self(control) +cnt = Control() +cnt.OpenHW() +cnt.SetHWLO(28050000) +cnt.SetHWSR(740740) +cnt.CloseHW() diff --git a/configure.py b/configure.py new file mode 100644 index 0000000..c38f215 --- /dev/null +++ b/configure.py @@ -0,0 +1,4294 @@ + +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division + +import sys, wx, wx.lib, os, re, pickle, traceback, json +# Quisk will alter quisk_conf_defaults to include the user's config file. +import quisk_conf_defaults as conf +import _quisk as QS +from quisk_widgets import QuiskPushbutton, QuiskCheckbutton, QuiskBitField, SliderBoxH, SliderBoxHH +from quisk_widgets import wxVersion +if wxVersion in ('2', '3'): + import wx.combo as wxcombo +else: + wxcombo = wx # wxPython Phoenix +try: + from soapypkg import soapy +except: + soapy = None + +# Settings is [ +# 0: Radio_requested, a string radio name or "Ask me" or "ConfigFileRadio" +# 1: Radio in use and last used, a string radio name or "ConfigFileRadio" +# 2: List of radio names +# 3: Parallel list of radio dicts. These are all the parameters for the corresponding radio. In +# general, they are a subset of all the parameters listed in self.sections and self.receiver_data[radio_name]. +# 4: Global data common to all radios. This is similar to the radio dicts. Available as local_conf.globals. +# ] + +# radio_dict is a dictionary of variable names and text values for each radio including radio ConfigFileRadio. +# Only variable names from the specified radio and all sections are included. The data comes from the JSON file, and +# may be missing recently added config file items. Use GetValue() to get a configuration datum. + +# local_conf is the single instance of class Configuration. conf is the configuration data from quisk_conf_defaults as +# over-writen by JSON data in radio_dict. Items in the radio_dict are generally strings. We convert these strings to Python +# integers, floats, etc. and write them to conf. + +# The format for data items is read from quisk_conf_defaults.py, but there are data items not in this file. The +# dictionary name2format has formats and defaults for these additional items. +# The value is a tuple (format name, default value). +name2format = { + "digital_rx1_name":('text', ''), "digital_rx2_name":('text', ''), "digital_rx3_name":('text', ''), + "digital_rx4_name":('text', ''), "digital_rx5_name":('text', ''), "digital_rx6_name":('text', ''), + "digital_rx7_name":('text', ''), "digital_rx8_name":('text', ''), "digital_rx9_name":('text', ''), + "win_digital_rx1_name":('text', ''), "win_digital_rx2_name":('text', ''), "win_digital_rx3_name":('text', ''), + "win_digital_rx4_name":('text', ''), "win_digital_rx5_name":('text', ''), "win_digital_rx6_name":('text', ''), + "win_digital_rx7_name":('text', ''), "win_digital_rx8_name":('text', ''), "win_digital_rx9_name":('text', ''), + "lin_digital_rx1_name":('text', ''), "lin_digital_rx2_name":('text', ''), "lin_digital_rx3_name":('text', ''), + "lin_digital_rx4_name":('text', ''), "lin_digital_rx5_name":('text', ''), "lin_digital_rx6_name":('text', ''), + "lin_digital_rx7_name":('text', ''), "lin_digital_rx8_name":('text', ''), "lin_digital_rx9_name":('text', ''), +} + +# Increasing the software version will display a message to re-read the soapy device. +soapy_software_version = 3 + +def FormatKhz(dnum): # Round to 3 decimal places; remove ending ".000" + t = "%.3f" % dnum + if t[-4:] == '.000': + t = t[0:-4] + return t + +def FormatMHz(dnum): # Pretty print in MHz + t = "%.6f" % dnum + for i in range(3): + if t[-1] == '0': + t = t[0:-1] + else: + break + return t + +def SortKey(x): + try: + k = float(x) + except: + k = 0.0 + return k + +class Configuration: + def __init__(self, app, AskMe=False, Radio=''): # Called first + global application, local_conf, Settings, noname_enable, platform_ignore, platform_accept + Settings = ["ConfigFileRadio", "ConfigFileRadio", [], [], {}] + self.globals = Settings[4] # Will be replaced by quisk_settings.json + application = app + local_conf = self + noname_enable = [] + if sys.platform == 'win32': + platform_ignore = 'lin_' + platform_accept = 'win_' + else: + platform_accept = 'lin_' + platform_ignore = 'win_' + self.sections = [] + self.receiver_data = [] + self.StatePath = conf.settings_file_path + if not self.StatePath: + self.StatePath = os.path.join(app.QuiskFilesDir, "quisk_settings.json") + self.ReadState() + if AskMe == 'Same': + pass + elif Radio: + choices = Settings[2] + ["ConfigFileRadio"] + if Radio in choices: + if Settings[1] != Radio: + Settings[1] = Radio + self.settings_changed = True + else: + t = "There is no radio named %s. Radios are " % Radio + for choice in choices: + t = "%s%s, " % (t, choice) + t = t[0:-2] + '.' + dlg = wx.MessageDialog(application.main_frame, t, 'Specify Radio', wx.OK|wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + sys.exit(0) + elif AskMe or Settings[0] == "Ask me": + choices = Settings[2] + ["ConfigFileRadio"] + dlg = wx.SingleChoiceDialog(None, "", "Start Quisk with this Radio", + choices, style=wx.DEFAULT_FRAME_STYLE|wx.OK|wx.CANCEL) + dlg.SetSizeHints(dlg.GetCharWidth() * 36, -1, -1, -1) + try: + n = choices.index(Settings[1]) # Set default to last used radio + except: + pass + else: + dlg.SetSelection(n) + ok = dlg.ShowModal() + if ok != wx.ID_OK: + sys.exit(0) + select = dlg.GetStringSelection() + dlg.Destroy() + if Settings[1] != select: + Settings[1] = select + self.settings_changed = True + else: + Settings[1] = Settings[0] + if Settings[1] == "ConfigFileRadio": + Settings[2].append("ConfigFileRadio") + Settings[3].append({}) + self.ParseConfig() + self.originalBandEdge = {} # save original BandEdge + self.originalBandEdge.update(conf.BandEdge) + self.UpdateGlobals() + def RequiredValues(self, radio_dict): + radio_type = radio_dict['hardware_file_type'] + # Fill in required values + if radio_type == "SdrIQ": + radio_dict["use_sdriq"] = '1' + if radio_dict['hardware_file_name'] == "sdriqpkg/quisk_hardware.py": + radio_dict['hardware_file_name'] = "quisk_hardware_sdriq.py" + else: + radio_dict["use_sdriq"] = '0' + if radio_type == "Hermes": + radio_dict["hermes_bias_adjust"] = "False" + if radio_type == 'SoapySDR': + radio_dict["use_soapy"] = '1' + self.InitSoapyNames(radio_dict) + if radio_dict.get("soapy_file_version", 0) < soapy_software_version: + text = "Your SoapySDR device parameters are out of date. Please go to the radio configuration screen and re-read the device parameters." + dlg = wx.MessageDialog(None, text, 'Please Re-Read Device', wx.OK|wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + else: + radio_dict["use_soapy"] = '0' + if radio_type not in ("HiQSDR", "Hermes", "Red Pitaya", "Odyssey", "Odyssey2"): + radio_dict["use_rx_udp"] = '0' + if radio_type in ("Hermes", "Red Pitaya", "Odyssey2"): + if "Hermes_BandDict" not in radio_dict: + radio_dict["Hermes_BandDict"] = {} + if "Hermes_BandDictTx" not in radio_dict: + radio_dict["Hermes_BandDictTx"] = {} + def UpdateGlobals(self): + self.RadioName = Settings[1] + if self.RadioName == "ConfigFileRadio": + application.BandPlan = conf.BandPlan + elif "BandPlanColors" in Settings[4] and "BandPlan" in Settings[4]: + mode_dict = {"End":None} + for mode, color in Settings[4]["BandPlanColors"]: + mode_dict[mode] = color + plan = [] + for freq, mode in Settings[4]["BandPlan"]: + freq = int(float(freq) * 1E6 + 0.1) + try: + color = mode_dict[mode] + except: + print ("Missing color for mode", mode) + color = '#777777' + plan.append([freq, color]) + application.BandPlan = plan + else: + application.BandPlan = conf.BandPlan + if "MidiNoteDict" not in Settings[4]: + Settings[4]["MidiNoteDict"] = {} + self.settings_changed = True + self.MidiNoteDict = Settings[4]["MidiNoteDict"] + # Convert old to new format, October, 2021 + for txt_note in list(local_conf.MidiNoteDict): # txt_note is a string + int_note = int(txt_note, base=0) + if int_note < 128: + target = local_conf.MidiNoteDict[txt_note] + if len(target) > 3 and target[-3] == " " and target[-2] in "+-" and target[-1] in "0123456789": # Jog wheel + key = "0xB0%02X" % int_note + elif target in ("Vol", "STo", "Rit", "Ys", "Yz", "Zo", "Tune"): # Knob + key = "0xB0%02X" % int_note + else: # Button. Enter as the Note On message. + key = "0x90%02X" % int_note + local_conf.MidiNoteDict[key] = target + del local_conf.MidiNoteDict[txt_note] + self.settings_changed = True + def UpdateConf(self): # Called second to update the configuration for the selected radio + # Items in the radio_dict are generally strings. Convert these strings to Python integers, floats, + # etc. and write them to conf. + if Settings[1] == "ConfigFileRadio": + return + radio_dict = self.GetRadioDict() + # fill in conf from our configuration data; convert text items to Python objects + errors = '' + for k, v in list(radio_dict.items()): # radio_dict may change size during iteration + if k == 'favorites_file_path': # A null string is equivalent to "not entered" + if not v.strip(): + continue + if k in ('power_meter_local_calibrations', ): # present in configuration data but not in the config file + continue + if k[0:6] == 'soapy_': # present in configuration data but not in the config file + continue + if k[0:6] == 'Hware_': # contained in hardware file, not in configuration data nor config file + continue + try: + fmt = self.format4name[k] + except: + errors = errors + "Ignore obsolete parameter %s\n" % k + del radio_dict[k] + self.settings_changed = True + continue + k4 = k[0:4] + if k4 == platform_ignore: + continue + elif k4 == platform_accept: + k = k[4:] + fmt4 = fmt[0:4] + if fmt4 not in ('dict', 'list'): + i1 = v.find('#') + if i1 > 0: + v = v[0:i1] + try: + if fmt4 == 'text': # Note: JSON returns Unicode strings !!! + setattr(conf, k, v) + elif fmt4 == 'dict': + if isinstance(v, dict): + setattr(conf, k, v) + else: + raise ValueError() + elif fmt4 == 'list': + if isinstance(v, list): + setattr(conf, k, v) + else: + raise ValueError() + elif fmt4 == 'inte': + setattr(conf, k, int(v, base=0)) + elif fmt4 == 'numb': + setattr(conf, k, float(v)) + elif fmt4 == 'bool': + if v == "True": + setattr(conf, k, True) + else: + setattr(conf, k, False) + elif fmt4 == 'rfil': + pass + elif fmt4 == 'keyc': # key code + if v == "None": + x = None + else: + x = eval(v) + x = int(x) + if k == 'hot_key_ptt2' and not isinstance(x, int): + setattr(conf, k, wx.ACCEL_NORMAL) + else: + setattr(conf, k, x) + else: + print ("Unknown format for", k, fmt) + except: + del radio_dict[k] + 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) + self.RequiredValues(radio_dict) # Why not update conf too??? This only updates the radio_dict. + if errors: + dlg = wx.MessageDialog(None, errors, + 'Update Settings', wx.OK|wx.ICON_ERROR) + ret = dlg.ShowModal() + dlg.Destroy() + def InitSoapyNames(self, radio_dict): # Set Soapy data items, but not the hardware available lists and ranges. + if radio_dict.get('soapy_getFullDuplex_rx', 0): + radio_dict["add_fdx_button"] = '1' + else: + radio_dict["add_fdx_button"] = '0' + name = 'soapy_gain_mode_rx' + if name not in radio_dict: + radio_dict[name] = 'total' + name = 'soapy_setAntenna_rx' + if name not in radio_dict: + radio_dict[name] = '' + name = 'soapy_gain_values_rx' + if name not in radio_dict: + radio_dict[name] = {} + name = 'soapy_gain_mode_tx' + if name not in radio_dict: + radio_dict[name] = 'total' + name = 'soapy_setAntenna_tx' + if name not in radio_dict: + radio_dict[name] = '' + name = 'soapy_gain_values_tx' + if name not in radio_dict: + radio_dict[name] = {} + def NormPath(self, path): # Convert between Unix and Window file paths + if sys.platform == 'win32': + path = path.replace('/', '\\') + else: + path = path.replace('\\', '/') + return path + def GetHardware(self): # Called third to open the hardware file + if Settings[1] == "ConfigFileRadio": + return False + path = self.GetRadioDict()["hardware_file_name"] + path = self.NormPath(path) + if not os.path.isfile(path): + dlg = wx.MessageDialog(None, + "Failure for hardware file %s!" % path, + 'Hardware File', wx.OK|wx.ICON_ERROR) + ret = dlg.ShowModal() + dlg.Destroy() + path = 'quisk_hardware_model.py' + dct = {} + dct.update(conf.__dict__) # make items from conf available + if "Hardware" in dct: + del dct["Hardware"] + if 'quisk_hardware' in dct: + del dct["quisk_hardware"] + exec(compile(open(path).read(), path, 'exec'), dct) + if "Hardware" in dct: + application.Hardware = dct['Hardware'](application, conf) + return True + return False + def Initialize(self): # Called fourth to fill in our ConfigFileRadio radio from conf + if Settings[1] == "ConfigFileRadio": + radio_dict = self.GetRadioDict("ConfigFileRadio") + typ = self.GuessType() + radio_dict['hardware_file_type'] = typ + all_data = [] + all_data = all_data + self.GetReceiverData(typ) + for name, sdata in self.sections: + all_data = all_data + sdata + for data_name, text, fmt, help_text, values in all_data: + data_name4 = data_name[0:4] + if data_name4 == platform_ignore: + continue + elif data_name4 == platform_accept: + conf_name = data_name[4:] + else: + conf_name = data_name + try: + if fmt in ("dict", "list"): + radio_dict[data_name] = getattr(conf, conf_name) + else: + radio_dict[data_name] = str(getattr(conf, conf_name)) + except: + if data_name == 'playback_rate': + pass + else: + print ('No config file value for', data_name) + def GetWidgets(self, app, hardware, conf, frame, gbs, vertBox): # Called fifth + if Settings[1] == "ConfigFileRadio": + return False + path = self.GetRadioDict().get("widgets_file_name", '') + path = self.NormPath(path) + if os.path.isfile(path): + dct = {} + dct.update(conf.__dict__) # make items from conf available + exec(compile(open(path).read(), path, 'exec'), dct) + if "BottomWidgets" in dct: + app.bottom_widgets = dct['BottomWidgets'](app, hardware, conf, frame, gbs, vertBox) + return True + def OnPageChanging(self, event): # Called when the top level page changes (not RadioNotebook pages) + event.Skip() + notebook = event.GetEventObject() + index = event.GetSelection() + page = notebook.GetPage(index) + if isinstance(page, RadioNotebook): + if not page.pages: + page.MakePages() + def AddPages(self, notebk, width): # Called sixth to add pages Help, Radios, all radio names + global win_width + win_width = width + self.notebk = notebk + self.radio_page = Radios(notebk) + notebk.AddPage(self.radio_page, "Radios") + self.radios_page_start = notebk.GetPageCount() + notebk.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanging, notebk) + for name in Settings[2]: + page = RadioNotebook(notebk, name) + if name == Settings[1]: + notebk.AddPage(page, "*%s*" % name) + else: + notebk.AddPage(page, name) + def GuessType(self): + udp = conf.use_rx_udp + if conf.use_sdriq: + return 'SdrIQ' + elif udp == 1: + return 'HiQSDR' + elif udp == 2: + return 'HiQSDR' + elif udp == 10: + return 'Hermes' + elif udp > 0: + return 'HiQSDR' + return 'SoftRock USB' + def AddRadio(self, radio_name, typ): + radio_dict = {} + radio_dict['hardware_file_type'] = typ + Settings[2].append(radio_name) + Settings[3].append(radio_dict) + for data_name, text, fmt, help_text, values in self.GetReceiverData(typ): + radio_dict[data_name] = values[0] + for name, data in self.sections: + for data_name, text, fmt, help_text, values in data: + radio_dict[data_name] = values[0] + # Change some default values in quisk_conf_defaults.py based on radio type + if typ in ("HiQSDR", "Hermes", "Red Pitaya", "Odyssey", "Odyssey2"): + radio_dict["add_fdx_button"] = '1' + page = RadioNotebook(self.notebk, radio_name) + self.notebk.AddPage(page, radio_name) + return True + def RenameRadio(self, old, new): + index = Settings[2].index(old) + n = self.radios_page_start + index + if old == Settings[1]: + self.notebk.SetPageText(n, "*%s*" % new) + else: + self.notebk.SetPageText(n, new) + Settings[2][index] = new + self.notebk.GetPage(n).NewName(new) + if old == "ConfigFileRadio": + for ctrl in noname_enable: + ctrl.Enable() + return True + def DeleteRadio(self, name): + index = Settings[2].index(name) + del Settings[2][index] + del Settings[3][index] + try: + n = self.radios_page_start + index + self.notebk.DeletePage(n) + except: + pass + return True + def GetRadioDict(self, radio_name=None): # None radio_name means the current radio + if radio_name: + index = Settings[2].index(radio_name) + else: # index of radio in use + index = Settings[2].index(Settings[1]) + return Settings[3][index] + #def GetItem(self, name, deflt=None, accept=None): # return item or default. accept can be "win_" or "lin_" + # dct = self.GetRadioDict() + # if accept: + # return dct.get(accept+name, deflt) + # return dct.get(name, deflt) + def GetSectionData(self, section_name): + for sname, data in self.sections: + if sname == section_name: + return data + return None + def GetReceiverData(self, receiver_name): + for rxname, data in self.receiver_data: + if rxname == receiver_name: + return data + return None + def GetReceiverDatum(self, receiver_name, item_name): + for rxname, data in self.receiver_data: + if rxname == receiver_name: + for data_name, text, fmt, help_text, values in data: + if item_name == data_name: + return values[0] + break + return '' + def GetReceiverItemTH(self, receiver_name, item_name): + for rxname, data in self.receiver_data: + if rxname == receiver_name: + for data_name, text, fmt, help_text, values in data: + if item_name == data_name: + return text, help_text + break + return '', '' + def ReceiverHasName(self, receiver_name, item_name): + for rxname, data in self.receiver_data: + if rxname == receiver_name: + for data_name, text, fmt, help_text, values in data: + if item_name == data_name: + return True + break + return False + def ReadState(self): + self.settings_changed = False + global Settings + try: + fp = open(self.StatePath, "r") + except: + return + try: + Settings = json.load(fp) + except: + traceback.print_exc() + fp.close() + try: # Do not save settings for radio ConfigFileRadio + index = Settings[2].index("ConfigFileRadio") + except ValueError: + pass + else: + del Settings[2][index] + del Settings[3][index] + for sdict in Settings[3]: + # Fixup for prior errors that save dictionaries as strings + for name in ("tx_level", "HiQSDR_BandDict", "Hermes_BandDict", "Hermes_BandDictTx"): + if name in sdict: + if not isinstance(sdict[name], dict): + print ("Bad dictionary for", name) + sdict[name] = {} + self.settings_changed = True + # Python None is saved as "null" + if "tx_level" in sdict: + if "null" in sdict["tx_level"]: + v = sdict["tx_level"]["null"] + sdict["tx_level"][None] = v + del sdict["tx_level"]["null"] + # Add global section if it is absent + length = len(Settings) + if length < 4: # Serious error + pass + elif length == 4: # Old file without global section + Settings.append({}) + self.settings_changed = True + else: # Settings[4] must be a dict + if not isinstance(Settings[4], dict): + Settings[4] = {} + self.settings_changed = True + self.globals = Settings[4] + def SaveState(self): + if not self.settings_changed: + return + try: + fp = open(self.StatePath, "w") + except: + traceback.print_exc() + return + json.dump(Settings, fp, indent=2) + fp.close() + self.settings_changed = False + def ParseConfig(self): + # ParseConfig() fills self.sections, self.receiver_data, and + # self.format4name with the items that Configuration understands. + # Dicts and lists are Python objects. All other items are text, not Python objects. + # + # Sections start with 16 #, section name + # self.sections is a list of [section_name, section_data] + # section_data is a list of [data_name, text, fmt, help_text, values] + + # Receiver sections start with 16 #, "Receivers ", receiver name, explain + # self.receiver_data is a list of [receiver_name, receiver_data] + # receiver_data is a list of [data_name, text, fmt, help_text, values] + + # Variable names start with ## variable_name variable_text, format + # The format is integer, number, text, boolean, integer choice, text choice, rfile + # Then some help text starting with "# " + # Then a list of possible value#explain with the default first + # Then a blank line to end. + self.format4name = {} + for name in name2format: + self.format4name[name] = name2format[name][0] + self.format4name['hardware_file_type'] = 'text' + self._ParserConf('quisk_conf_defaults.py') + # Read any user-defined radio types + for dirname in os.listdir('.'): + if not os.path.isdir(dirname) or dirname[-3:] != 'pkg': + continue + if dirname in ('freedvpkg', 'sdriqpkg', 'soapypkg'): + continue + filename = os.path.join(dirname, 'quisk_hardware.py') + if not os.path.isfile(filename): + continue + try: + self._ParserConf(filename) + except: + traceback.print_exc() + def _ParserConf(self, filename): + re_AeqB = re.compile("^#?(\w+)\s*=\s*([^#]+)#*(.*)") # item values "a = b" + section = None + data_name = None + multi_line = False + fp = open(filename, "r") + for line in fp: + line = line.strip() + if not line: + data_name = None + continue + if line[0:27] == '################ Receivers ': + section = 'Receivers' + args = line[27:].split(',', 1) + rxname = args[0].strip() + section_data = [] + self.receiver_data.append((rxname, section_data)) + elif line[0:17] == '################ ': + section = line[17:].strip() + if section in ('Colors', 'Obsolete'): + section = None + continue + rxname = None + section_data = [] + self.sections.append((section, section_data)) + if not section: + continue + if line[0:3] == '## ': # item_name item_text, format + args = line[3:].split(None, 1) + data_name = args[0] + args = args[1].split(',', 1) + dspl = args[0].strip() + fmt = args[1].strip() + value_list = [] + if data_name in self.format4name: + if self.format4name[data_name] != fmt: + print (filename, ": Inconsistent format for", data_name, self.format4name[data_name], fmt) + else: + self.format4name[data_name] = fmt + section_data.append([data_name, dspl, fmt, '', value_list]) + multi_line = False + if not data_name: + continue + if multi_line: + value += line + #print ("Multi", data_name, type(value), value) + count = self._multi_count(value) + if count == 0: + value = eval(value, conf.__dict__) + value_list.append(value) + #print ("Multi done", data_name, type(value), value) + multi_line = False + continue + mo = re_AeqB.match(line) + if mo: + if data_name != mo.group(1): + print (filename, ": Parse error for", data_name) + continue + value = mo.group(2).strip() + expln = mo.group(3).strip() + if value[0] in ('"', "'"): + value = value[1:-1] + elif value[0] == '{': # item is a dictionary + if self._multi_count(value) == 0: # dictionary is complete + value = eval(value, conf.__dict__) + #print ("Single", data_name, type(value), value) + else: + multi_line = True + #print ("Start multi", data_name, type(value), value) + continue + elif value[0] == '[': # item is a list + if self._multi_count(value) == 0: # list is complete + value = eval(value, conf.__dict__) + #print ("Single", data_name, type(value), value) + else: + multi_line = True + #print ("Start multi", data_name, type(value), value) + continue + if expln: + value_list.append("%s # %s" % (value, expln)) + else: + value_list.append(value) + elif line[0:2] == '# ': + section_data[-1][3] = section_data[-1][3] + line[2:] + ' ' + fp.close() + def _multi_count(self, value): + char_start = value[0] + if char_start == '{': + char_end = '}' + elif char_start == '[': + char_end = ']' + count = 0 + for ch in value: + if ch == char_start: + count += 1 + elif ch == char_end: + count -= 1 + return count + +class xxConfigHelp(wx.html.HtmlWindow): # The "Help with Radios" first-level page + """Create the help screen for the configuration tabs.""" + def __init__(self, parent): + wx.html.HtmlWindow.__init__(self, parent, -1, size=(win_width, 100)) + if "gtk2" in wx.PlatformInfo: + self.SetStandardFonts() + self.SetFonts("", "", [10, 12, 14, 16, 18, 20, 22]) + self.SetBackgroundColour(parent.bg_color) + # read in text from file help_conf.html in the directory of this module + self.LoadFile('help_conf.html') + +class QPowerMeterCalibration(wx.Frame): + """Create a window to enter the power output and corresponding ADC value AIN1/2""" + def __init__(self, parent, local_names): + self.parent = parent + self.local_names = local_names + self.table = [] # calibration table: list of [ADC code, power watts] + try: # may be missing in wxPython 2.x + wx.Frame.__init__(self, application.main_frame, -1, "Power Meter Calibration", + pos=(50, 100), style=wx.CAPTION|wx.FRAME_FLOAT_ON_PARENT) + except AttributeError: + wx.Frame.__init__(self, application.main_frame, -1, "Power Meter Calibration", + pos=(50, 100), style=wx.CAPTION) + panel = wx.Panel(self) + self.MakeControls(panel) + self.Show() + def MakeControls(self, panel): + charx = panel.GetCharWidth() + tab1 = charx * 5 + y = 20 + # line 1 + txt = wx.StaticText(panel, -1, 'Name for new calibration table', pos=(tab1, y)) + w, h = txt.GetSize().Get() + tab2 = tab1 + w + tab1 // 2 + self.cal_name = wx.TextCtrl(panel, -1, pos=(tab2, h), size=(charx * 16, h * 13 // 10)) + y += h * 3 + # line 2 + txt = wx.StaticText(panel, -1, 'Measured power level in watts', pos=(tab1, y)) + self.cal_power = wx.TextCtrl(panel, -1, pos=(tab2, y), size=(charx * 16, h * 13 // 10)) + x = tab2 + charx * 20 + add = QuiskPushbutton(panel, self.OnBtnAdd, "Add to Table") + add.SetPosition((x, y - h * 3 // 10)) + add.SetColorGray() + ww, hh = add.GetSize().Get() + width = x + ww + tab1 + y += h * 3 + # line 3 + sv = QuiskPushbutton(panel, self.OnBtnSave, "Save") + sv.SetColorGray() + cn = QuiskPushbutton(panel, self.OnBtnCancel, "Cancel") + cn.SetColorGray() + w, h = cn.GetSize().Get() + sv.SetPosition((width // 4, y)) + cn.SetPosition((width - width // 4 - w, y)) + y += h * 12 // 10 + # help text at bottom + wx.StaticText(panel, -1, '1. Attach a 50 ohm load and power meter to the antenna connector.', pos=(tab1, y)) + w, h = txt.GetSize().Get() + h = h * 12 // 10 + y += h + wx.StaticText(panel, -1, '2. Use the Spot button to transmit at a very low power.', pos=(tab1, y)) + y += h + wx.StaticText(panel, -1, '3. Enter the measured power in the box above and press "Add to Table".', pos=(tab1, y)) + y += h + wx.StaticText(panel, -1, '4. Increase the power a small amount and repeat step 3.', pos=(tab1, y)) + y += h + wx.StaticText(panel, -1, '5. Increase power again and repeat step 3.', pos=(tab1, y)) + y += h + wx.StaticText(panel, -1, '6. Keep adding measurements to the table until you reach full power.', pos=(tab1, y)) + y += h + wx.StaticText(panel, -1, '7. Ten or twelve measurements should be enough. Then press "Save".', pos=(tab1, y)) + y += h + wx.StaticText(panel, -1, 'To delete a table, save a table with zero measurements.', pos=(tab1, y)) + y += h * 2 + self.SetClientSize(wx.Size(width, y)) + def OnBtnCancel(self, event=None): + self.parent.ChangePMcalFinished(None, None) + self.Destroy() + def OnBtnSave(self, event): + name = self.cal_name.GetValue().strip() + if not name: + dlg = wx.MessageDialog(self, + 'Please enter a name for the new calibration table.', + 'Missing Name', wx.OK|wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + elif name in conf.power_meter_std_calibrations: # known calibration names from the config file + dlg = wx.MessageDialog(self, + 'That name is reserved. Please enter a different name.', + 'Reserved Name', wx.OK|wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + elif name in self.local_names: + if self.table: + dlg = wx.MessageDialog(self, + 'That name exists. Replace the existing table?', + 'Replace Table', wx.OK|wx.CANCEL|wx.ICON_EXCLAMATION) + ret = dlg.ShowModal() + dlg.Destroy() + if ret == wx.ID_OK: + self.parent.ChangePMcalFinished(name, self.table) + self.Destroy() + else: + dlg = wx.MessageDialog(self, + 'That name exists but the table is empty. Delete the existing table?.', + 'Delete Table', wx.OK|wx.CANCEL|wx.ICON_EXCLAMATION) + ret = dlg.ShowModal() + dlg.Destroy() + if ret == wx.ID_OK: + self.parent.ChangePMcalFinished(name, None) + self.Destroy() + else: + self.parent.ChangePMcalFinished(name, self.table) + self.Destroy() + def OnBtnAdd(self, event): + power = self.cal_power.GetValue().strip() + self.cal_power.Clear() + try: + power = float(power) + except: + dlg = wx.MessageDialog(self, 'Missing or bad measured power.', 'Error in Power', wx.OK|wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + else: + ## Convert measured voltage to power + #power *= 6.388 + #power = power**2 / 50.0 + fwd = application.Hardware.hermes_fwd_power + rev = application.Hardware.hermes_rev_power + if fwd >= rev: + self.table.append([fwd, power]) # Item must use lists; sort() will fail with mixed lists and tuples + else: + self.table.append([rev, power]) + +## Note: The amplitude/phase adjustments have ideas provided by Andrew Nilsson, VK6JBL. +## October 2020: changed to make RxTx frequency and VFO two independent variables. +class QAdjustPhase(wx.Frame): + """Create a window with amplitude and phase adjustment controls""" + f_ampl = "Amplitude adjustment %.6f" + f_phase = "Phase adjustment degrees %.6f" + def __init__(self, parent, width, rx_tx): + self.width = width + self.rx_tx = rx_tx # Must be "rx" or "tx" + if rx_tx == 'tx': + self.is_tx = 1 + t = "Adjust Sound Card Transmit Amplitude and Phase" + else: + self.is_tx = 0 + t = "Adjust Sound Card Receive Amplitude and Phase" + wx.Frame.__init__(self, application.main_frame, -1, t, pos=(50, 100), style=wx.CAPTION) + self.panel = wx.Panel(self) + self.MakeControls() + self.Redraw() + self.Show() + def MakeControls(self): # Make controls for phase/amplitude adjustment + panel = self.panel + width = self.width + self.old_amplitude, self.old_phase = application.GetAmplPhase(self.rx_tx) + self.new_amplitude, self.new_phase = self.old_amplitude, self.old_phase + sl_max = width * 4 // 10 # maximum +/- value for slider + self.ampl_scale = float(conf.rx_max_amplitude_correct) / sl_max + self.phase_scale = float(conf.rx_max_phase_correct) / sl_max + font = wx.Font(conf.default_font_size, wx.FONTFAMILY_SWISS, wx.NORMAL, + wx.FONTWEIGHT_NORMAL, False, conf.quisk_typeface) + self.SetFont(font) + charx = self.GetCharWidth() + self.tab1 = tab1 = charx + chary = self.GetCharHeight() + deltay = chary * 12 // 10 + y = chary * 3 // 10 + self.controls = [] + txt = wx.StaticText(panel, -1, "No data", pos=(tab1, y)) + self.controls.append(txt) + y += txt.GetSize().GetHeight() + #y += deltay * 14 // 10 + self.t_ampl = wx.StaticText(panel, -1, self.f_ampl % self.old_amplitude, pos=(tab1, y)) + self.controls.append(self.t_ampl) + y += deltay + fine = wx.StaticText(panel, -1, 'Fine', pos=(tab1, y)) + self.controls.append(fine) + coarse = wx.StaticText(panel, -1, 'Coarse', pos=(tab1, y + deltay)) + self.controls.append(coarse) + tab2 = tab1 + coarse.GetSize().GetWidth() + charx + sliderX = width - tab2 - charx + self.ampl1 = wx.Slider(panel, -1, 0, -sl_max, sl_max, pos=(tab2, y), size=(sliderX, -1)) + self.controls.append(self.ampl1) + y += deltay + self.ampl2 = wx.Slider(panel, -1, 0, -sl_max, sl_max, pos=(tab2, y), size=(sliderX, -1)) + self.controls.append(self.ampl2) + y += deltay * 14 // 10 + self.PosAmpl(self.old_amplitude) + self.t_phase = wx.StaticText(panel, -1, self.f_phase % self.old_phase, pos=(tab1, y)) + self.controls.append(self.t_phase) + y += deltay + fine = wx.StaticText(panel, -1, 'Fine', pos=(tab1, y)) + self.controls.append(fine) + coarse = wx.StaticText(panel, -1, 'Coarse', pos=(tab1, y + deltay)) + self.controls.append(coarse) + self.phase1 = wx.Slider(panel, -1, 0, -sl_max, sl_max, pos=(tab2, y), size=(sliderX, -1)) + self.controls.append(self.phase1) + y += deltay + self.phase2 = wx.Slider(panel, -1, 0, -sl_max, sl_max, pos=(tab2, y), size=(sliderX, -1)) + self.controls.append(self.phase2) + y += deltay + sv = QuiskPushbutton(panel, self.OnBtnSave, 'Save') + self.controls.append(sv) + dv = QuiskPushbutton(panel, self.OnBtnDestroyVFO, 'Destroy VFO') + self.controls.append(dv) + ds = QuiskPushbutton(panel, self.OnBtnDestroyALL, 'Destroy ALL') + self.controls.append(ds) + cn = QuiskPushbutton(panel, self.OnBtnFinished, 'Finished') + self.controls.append(cn) + hl = QuiskPushbutton(panel, self.OnBtnHelp, 'Help') + self.controls.append(hl) + w, h = ds.GetSize().Get() + sv.SetSize((w, h)) + cn.SetSize((w, h)) + hl.SetSize((w, h)) + y += h * 5 // 10 + x = (width - w * 5) // 6 + sv.SetPosition((x, y)) + dv.SetPosition((x*2 + w, y)) + ds.SetPosition((x*3 + w*2, y)) + cn.SetPosition((x*4 + w*3, y)) + hl.SetPosition((x*5 + w*4, y)) + sv.SetBackgroundColour('light blue') + dv.SetBackgroundColour('light blue') + ds.SetBackgroundColour('light blue') + cn.SetBackgroundColour('light blue') + hl.SetBackgroundColour('light blue') + y += h + y += h * 4 // 10 + self.PosPhase(self.old_phase) + self.SetClientSize(wx.Size(width, y)) + self.ampl1.Bind(wx.EVT_SCROLL, self.OnChange) + self.ampl2.Bind(wx.EVT_SCROLL, self.OnAmpl2) + self.phase1.Bind(wx.EVT_SCROLL, self.OnChange) + self.phase2.Bind(wx.EVT_SCROLL, self.OnPhase2) + def Redraw(self): + # Print available data points + self.band = application.lastBand + data = application.bandAmplPhase.get(self.band, {}) + data = data.get(self.rx_tx, []) + if data: + t = "Band %s VFO and frequency list\n" % self.band + for vfo, items in data: + t += "%10d:" % vfo + for freq, ampl, phase in items: + t += " %7d" % freq + t += "\n" + else: + t = "Band %s: No data." % self.band + x, y = self.controls[0].GetPosition() + height = self.controls[0].GetSize().GetHeight() + self.controls[0].Destroy() + txt = wx.StaticText(self.panel, -1, t, pos=(self.tab1, y)) + self.controls[0] = txt + delta = txt.GetSize().GetHeight() - height + for ctrl in self.controls[1:]: + x, y = ctrl.GetPosition() + y += delta + ctrl.Move(x, y) + h = self.GetClientSize().GetHeight() + self.SetClientSize(wx.Size(self.width, h + delta)) + def PosAmpl(self, ampl): # set pos1, pos2 for amplitude + pos2 = round(ampl / self.ampl_scale) + remain = ampl - pos2 * self.ampl_scale + pos1 = round(remain / self.ampl_scale * 50.0) + self.ampl1.SetValue(pos1) + self.ampl2.SetValue(pos2) + def PosPhase(self, phase): # set pos1, pos2 for phase + pos2 = round(phase / self.phase_scale) + remain = phase - pos2 * self.phase_scale + pos1 = round(remain / self.phase_scale * 50.0) + self.phase1.SetValue(pos1) + self.phase2.SetValue(pos2) + def OnChange(self, event): + ampl = self.ampl_scale * self.ampl1.GetValue() / 50.0 + self.ampl_scale * self.ampl2.GetValue() + if abs(ampl) < self.ampl_scale * 3.0 / 50.0: + ampl = 0.0 + self.t_ampl.SetLabel(self.f_ampl % ampl) + phase = self.phase_scale * self.phase1.GetValue() / 50.0 + self.phase_scale * self.phase2.GetValue() + if abs(phase) < self.phase_scale * 3.0 / 50.0: + phase = 0.0 + self.t_phase.SetLabel(self.f_phase % phase) + QS.set_ampl_phase(ampl, phase, self.is_tx) + self.new_amplitude, self.new_phase = ampl, phase + def OnAmpl2(self, event): # re-center the fine slider when the coarse slider is adjusted + ampl = self.ampl_scale * self.ampl1.GetValue() / 50.0 + self.ampl_scale * self.ampl2.GetValue() + self.PosAmpl(ampl) + self.OnChange(event) + def OnPhase2(self, event): # re-center the fine slider when the coarse slider is adjusted + phase = self.phase_scale * self.phase1.GetValue() / 50.0 + self.phase_scale * self.phase2.GetValue() + self.PosPhase(phase) + self.OnChange(event) + def OnBtnDestroyVFO(self, event): # Remove entry with the application VFO + data = application.bandAmplPhase.get(self.band, {}) + data = data.get(self.rx_tx, []) + for i in range(len(data)): + if data[i][0] == application.VFO: + del data[i] + break + self.Redraw() + def OnBtnSave(self, event): + data = application.bandAmplPhase + if self.band not in data: + data[self.band] = {} + data = data[self.band] + if self.rx_tx not in data: + data[self.rx_tx] = [] + data = data[self.rx_tx] + for vfo, items in data: # All items must be lists, not tuples + if vfo == application.VFO: + for i in range(len(items)): + item = items[i] + if item[0] == application.rxFreq: + item[1] = self.new_amplitude + item[2] = self.new_phase + break + else: + items.append([application.rxFreq, self.new_amplitude, self.new_phase]) + items.sort() + break + else: + data.append([application.VFO, [[application.rxFreq, self.new_amplitude, self.new_phase]]]) + data.sort() + self.Redraw() + def OnBtnDestroyALL(self, event): + dlg = wx.MessageDialog(self, "This will destroy all data for band %s!" % self.band, + "Destroy Data", style = wx.YES|wx.NO) + if dlg.ShowModal() == wx.ID_YES: + if self.band in application.bandAmplPhase: + del application.bandAmplPhase[self.band] + self.Redraw() + def OnBtnFinished(self, event=None): + ampl, phase = application.GetAmplPhase(self.rx_tx) + QS.set_ampl_phase(ampl, phase, self.is_tx) + application.w_phase = None + self.Destroy() + def OnBtnHelp(self, event=None): + dlg = wx.MessageDialog(self, +'The "VFO" is the frequency at the center of the graph screen. The Rx or Tx frequency is the offset from the VFO. \ +Adjust the VFO and the frequency as desired. Then adjust the sliders to minimize the image. Press "Save" when satisfied. \ +To adjust the VFO, use the band Up/Down buttons, or right click the graph at the desired VFO. \ +Adjustments must be made for both receive and transmit on each band. \ +The maximum slider adjustment range can be changed on the radio Hardware screen. \ +The other buttons will delete the data for the current VFO, or for the whole band. \ +For more information, press the main "Help" button, \ +then "Documentation", then "SoftRock".' + , "Adjustment Help", style=wx.OK) + dlg.ShowModal() + +class ListEditDialog(wx.Dialog): # Display a dialog with a List-Edit control, plus Ok/Cancel + def __init__(self, parent, title, choice, choices, width): + wx.Dialog.__init__(self, parent, title=title, style=wx.CAPTION|wx.CLOSE_BOX) + cancel = wx.Button(self, wx.ID_CANCEL, "Cancel") + bsize = cancel.GetSize() + margin = bsize.height + self.combo = wx.ComboBox(self, -1, choice, pos=(margin, margin), size=(width - margin * 2, -1), choices=choices, style=wx.CB_DROPDOWN) + y = margin + self.combo.GetSize().height + margin + x = width - margin * 2 - bsize.width * 2 + x = x // 3 + ok = wx.Button(self, wx.ID_OK, "OK", pos=(margin + x, y)) + cancel.SetPosition((width - margin - x - bsize.width, y)) + self.SetClientSize(wx.Size(width, y + bsize.height * 14 // 10)) + def GetValue(self): + return self.combo.GetValue() + +class RadioNotebook(wx.Notebook): # The second-level notebook for each radio name + def __init__(self, parent, radio_name): + wx.Notebook.__init__(self, parent) + font = wx.Font(conf.config_font_size, wx.FONTFAMILY_SWISS, wx.NORMAL, + wx.FONTWEIGHT_NORMAL, False, conf.quisk_typeface) + self.SetFont(font) + self.SetBackgroundColour(parent.bg_color) + self.radio_name = radio_name + self.pages = [] + def MakePages(self): + radio_name = self.radio_name + radio_dict = local_conf.GetRadioDict(radio_name) + radio_type = radio_dict['hardware_file_type'] + if radio_type == 'SoapySDR': + page = RadioHardwareSoapySDR(self, radio_name) + else: + page = RadioHardware(self, radio_name) + self.AddPage(page, "Hardware") + self.pages.append(page) + page = RadioSound(self, radio_name) + self.AddPage(page, "Sound") + self.pages.append(page) + for section, names in local_conf.sections: + if section in ('Sound', 'Bands', 'Filters'): # There is a special page for these sections + continue + page = RadioSection(self, radio_name, section, names) + self.AddPage(page, section) + self.pages.append(page) + page = RadioBands(self, radio_name) + self.AddPage(page, "Bands") + self.pages.append(page) + #if "use_rx_udp" in radio_dict and radio_dict["use_rx_udp"] == '10': + # page = RadioFilters(self, radio_name) + # self.AddPage(page, "Filters") + # self.pages.append(page) + def NewName(self, new_name): + self.radio_name = new_name + for page in self.pages: + page.radio_name = new_name + +class ChoiceCombo(wx.Choice): + text_for_blank = "-blank-" + def __init__(self, parent, value, choices): + wx.Choice.__init__(self, parent, choices=choices) + self.choices = choices[:] + self.handler = None + try: + index = self.choices.index(value) + except: + index = 0 + self.Bind(wx.EVT_CHOICE, self.OnChoice) + self._ChangeItems(index) + def _ChangeItems(self, index): + length = len(self.choices) + if length <= 0: + self.Enable(False) + elif length == 1: + wx.Choice.SetSelection(self, 0) + self.Enable(False) + else: + wx.Choice.SetSelection(self, index) + self.Enable(True) + self._ReplaceBlank() + self.GetValue() + def _ReplaceBlank(self): + try: + n = self.choices.index('') + except: + pass + else: + self.SetString(n, self.text_for_blank) + def SetItems(self, lst): + wx.Choice.SetItems(self, lst) + self.choices = lst[:] + self._ChangeItems(0) + def SetSelection(self, n): + if 0 <= n < len(self.choices): + wx.Choice.SetSelection(self, n) + self.GetValue() + def SetText(self, text): + try: + n = self.choices.index(text) + except: + print("Failed to set choice list to", text) + else: + wx.Choice.SetSelection(self, n) + self.GetValue() + def GetValue(self): + n = self.GetSelection() + if n == wx.NOT_FOUND: + self.value = '' + else: + self.value = self.GetString(n) + if self.value == self.text_for_blank: + self.value = '' + return self.value + def OnChoice(self, event): + event.Skip() + old = self.value + self.GetValue() + if self.value != old: + if self.handler: + self.handler(self) + +class ComboCtrl(wxcombo.ComboCtrl): + def __init__(self, parent, value, choices, no_edit=False): + self.value = value + self.choices = choices[:] + self.handler = None + self.dirty = False + try: + self.bgColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_LISTBOX) + except: + self.bgColor = wx.Colour('#f6f5f4') + if no_edit: + wxcombo.ComboCtrl.__init__(self, parent, -1, style=wx.CB_READONLY) + else: + wxcombo.ComboCtrl.__init__(self, parent, -1, style=wx.TE_PROCESS_ENTER) + self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) + self.Bind(wx.EVT_TEXT, self.OnText) + self.Bind(wx.EVT_TEXT_ENTER, self.OnTextEnter) + try: + self.height = parent.quisk_height + except: + self.chary = self.GetCharHeight() + self.height = self.chary * 14 // 10 + self.SetBackgroundColour(self.bgColor) + self.Refresh() + try: + font = parent.font + except: + font = self.GetFont() + self.ctrl = ListBoxComboPopup(choices, font) + self.SetPopupControl(self.ctrl) + self.SetText(value) + self.SetSizes() + self.Bind(wx.EVT_COMBOBOX_CLOSEUP, self.OnCloseup) + def SetItems(self, lst): + self.ctrl.SetItems(lst) + self.choices = lst[:] + self.SetSizes() + def SetSizes(self): + charx = self.GetCharWidth() + wm = charx + w, h = self.GetTextExtent(self.value) + if wm < w: + wm = w + for ch in self.choices: + w, h = self.GetTextExtent(ch) + if wm < w: + wm = w + wm += charx * 5 + self.SetSizeHints(wm, self.height, 9999, self.height) + def SetSelection(self, n): # Set text to item in list box. Name conflict with wxcombo.ComboCtrl. + try: + text = self.choices[n] + except IndexError: + self.SetText('') + self.value = '' + else: + self.ctrl.SetSelection(n) + self.SetText(text) + self.value = text + def OnText(self, event): + self.dirty = True + self.SetBackgroundColour('#e0e0ff') + self.Refresh() + def OnTextEnter(self, event=None): + self.SetBackgroundColour(self.bgColor) + self.Refresh() + self.dirty = False + if event: + event.Skip() + new = self.GetValue() + if self.value != new: + self.value = new + if self.handler: + self.handler(self) + def OnKillFocus(self, event): + event.Skip() + if self.dirty: + self.OnTextEnter(None) + def OnListbox(self): + self.Dismiss() + self.OnTextEnter() + def OnButtonClick(self): + wxcombo.ComboCtrl.OnButtonClick(self) + wxcombo.ComboCtrl.SetSelection(self, 0, 0) + wxcombo.ComboCtrl.SetInsertionPointEnd(self) + def OnCloseup(self, event): + event.Skip() + wxcombo.ComboCtrl.SetSelection(self, 0, 0) + wxcombo.ComboCtrl.SetInsertionPointEnd(self) + +class ListBoxComboPopup(wxcombo.ComboPopup): + text_for_blank = "-blank-" + def __init__(self, choices, font): + wxcombo.ComboPopup.__init__(self) + self.choices = choices + self.font = font + self.lbox = None + self.index = None + def Create(self, parent): + self.lbox = wx.ListBox(parent, choices=self.choices, style=wx.LB_SINGLE|wx.LB_NEEDED_SB) + self.lbox.SetFont(self.font) + self.lbox.Bind(wx.EVT_MOTION, self.OnMotion) + self.lbox.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + self._ReplaceBlank() + return True + def SetItems(self, lst): + self.choices = lst[:] + self.lbox.Set(self.choices) + self._ReplaceBlank() + def _ReplaceBlank(self): + try: + n = self.choices.index('') + except: + pass + else: + self.lbox.SetString(n, self.text_for_blank) + def SetSelection(self, n): + self.lbox.SetSelection(n) + def GetStringValue(self): + try: + text = self.choices[self.lbox.GetSelection()] + except IndexError: + text = '' + else: + if text == self.text_for_blank: + text = '' + return text + def GetAdjustedSize(self, minWidth, prefHeight, maxHeight): + chary = self.lbox.GetCharHeight() + height = chary * len(self.choices) * 15 // 10 + chary + if height > prefHeight: + height = prefHeight + return (minWidth, height) + def OnLeftDown(self, event): + event.Skip() + self.index = self.lbox.GetSelection() # index of selected item + if wxVersion in ('2', '3'): + self.GetCombo().OnListbox() + else: + self.GetComboCtrl().OnListbox() + def OnMotion(self, event): + event.Skip() + item = self.lbox.HitTest(event.GetPosition()) + if item >= 0: + self.lbox.SetSelection(item) + def GetControl(self): + return self.lbox + +class QuiskTextCtrl(wx.TextCtrl): + def __init__(self, parent, text, style): + wx.TextCtrl.__init__(self, parent, -1, text, style=style) + self.dirty = False + self.bgColor = wx.Colour('#f6f5f4') + #self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) + if not (style & wx.TE_READONLY): + self.Bind(wx.EVT_TEXT, self.OnText) + self.Bind(wx.EVT_TEXT_ENTER, self.OnTextEnter) + def SetValue(self, value): + wx.TextCtrl.SetValue(self, value) + self.SetBackgroundColour(self.bgColor) + self.Refresh() + self.dirty = False + def OnText(self, event): + event.Skip() + self.dirty = True + self.SetBackgroundColour('#e0e0ff') + self.Refresh() + def OnTextEnter(self, event=None): + if event: + event.Skip() + self.SetBackgroundColour(self.bgColor) + self.Refresh() + self.dirty = False + #def OnKillFocus(self, event): + # event.Skip() + # if self.dirty: + # self.OnTextEnter(None) + +class QuiskControl(wx.Control): + def __init__(self, parent, text, height, pos=wx.DefaultPosition, style=0): + wx.Control.__init__(self, parent, -1, pos=pos, style=style) + self.text = text + self.handler = None + self.value = '' + if wxVersion in ('2', '3'): + self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) + else: + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + from quisk_widgets import button_font + self.SetFont(button_font) + w, h = self.GetTextExtent(text) + self.SetSizeHints(w + height * 3, height, 999, height) + self.bg_brush = wx.Brush('#dcdcdc') + self.black_brush = wx.Brush(wx.Colour(0x000000)) + self.text_color = wx.Colour(0x000000) + self.black_pen = wx.Pen(self.text_color) + self.no_pen = wx.Pen(self.text_color, style=wx.PENSTYLE_TRANSPARENT) + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + self.MakeMenu() + def OnPaint(self, event): + dc = wx.AutoBufferedPaintDC(self) + dc.SetBackground(self.bg_brush) + dc.SetTextForeground(self.text_color) + dc.Clear() + self._Glyph1(dc) + def _Glyph1(self, dc): + win_w, win_h = self.GetClientSize() + x = win_w - win_h + l = win_h * 20 // 100 + if l < 3: + l = 3 + y = win_h // 2 - 2 + pen = wx.Pen('#555555', 1) + dc.SetPen(pen) + points = [(0, 0), (l, l), (l + 1, l), (l * 2 + 2, -1)] + dc.DrawLines(points, x, y) + pen = wx.Pen('#888888', 1) + dc.SetPen(pen) + points = [(1, 0), (l, l - 1), (l + 1, l - 1), (l * 2 + 1, -1)] + dc.DrawLines(points, x, y) + w, h = dc.GetTextExtent(self.text) + x = (win_w - win_h - w) // 2 + if x < 0: + x = 0 + y = (win_h - h) // 2 + if y < 0: + y = 0 + dc.DrawText(self.text, x, y) + def _Glyph2(self, dc): # Not used + dc.SetBrush(self.black_brush) + win_w, win_h = self.GetClientSize() + spacing = 2 + rect_h = (win_h - spacing * 4) // 3 + if rect_h % 2 == 0: # Make rect_h an odd number + rect_h -= 1 + spacing = (win_h - rect_h * 3) // 4 + width = rect_h * 25 // 10 + x0 = win_w - (rect_h + 2 + width + 3) + y0 = (win_h - rect_h * 3 - spacing * 2) // 2 + x = (x0 - dc.GetCharWidth()) // 2 + y = (win_h - dc.GetCharHeight()) // 2 + dc.DrawText(u"\u2BA9", x, y) + for y in range(3): + dc.SetPen(self.no_pen) + dc.DrawRectangle(x0, y0, rect_h, rect_h) + x = x0 + rect_h + 2 + y = y0 + rect_h // 2 + dc.SetPen(self.black_pen) + dc.DrawLine(x, y, x + width, y) + y0 += rect_h + spacing + def MakeMenu(self): + self.menu = wx.Menu() + def OnLeftDown(self, event): + pos = wx.Point(0, 0) + self.PopupMenu(self.menu, pos) + +class MidiButton(QuiskControl): + def __init__(self, parent, text, height, pos=wx.DefaultPosition, style=0): + QuiskControl.__init__(self, parent, text, height, pos, style) + def MakeMenu(self): + main_names = [] + bands = wx.Menu() + filters = wx.Menu() + modes = wx.Menu() + screens = wx.Menu() + for idName in application.idName2Button: + if not idName: + item = None + elif idName in conf.BandList or idName in ("Audio", "Time"): + item = bands.Append(-1, idName) + elif idName[0:7] == 'Filter ': + item = filters.Append(-1, idName) + elif idName in ("CW U/L", "CWL", "CWU", "SSB U/L", "LSB", "USB", "AM", "FM", "DGT", + "DGT-U", "DGT-L", "DGT-FM", "DGT-IQ", "FDV", "FDV-U", "IMD", ): + item = modes.Append(-1, idName) + elif idName in ("Graph", "GraphP1", "GraphP2", "WFall", "Scope", "Config", "Audio FFT", "Bscope", "RX Filter", "Help"): + item = screens.Append(-1, idName) + else: + item = None + main_names.append(idName) + if item: + self.Bind(wx.EVT_MENU, self.OnMenu, item) + main_names.sort() + self.menu = wx.Menu() + main1 = wx.Menu() + main2 = wx.Menu() + for name in main_names: + if name[0].upper() in "ABCDEFGHIJKLMN": + item = main1.Append(-1, name) + else: + item = main2.Append(-1, name) + self.Bind(wx.EVT_MENU, self.OnMenu, item) + self.menu.AppendSubMenu(main1, "Button A-N") + self.menu.AppendSubMenu(main2, "Button O-Z") + item = self.menu.AppendSubMenu(bands, "Bands") + if bands.GetMenuItemCount() <= 0: + self.menu.Enable(item.GetId(), False) + item = self.menu.AppendSubMenu(filters, "Filters") + if filters.GetMenuItemCount() <= 0: + self.menu.Enable(item.GetId(), False) + item = self.menu.AppendSubMenu(modes, "Modes") + if modes.GetMenuItemCount() <= 0: + self.menu.Enable(item.GetId(), False) + item = self.menu.AppendSubMenu(screens, "Screens") + if screens.GetMenuItemCount() <= 0: + self.menu.Enable(item.GetId(), False) + def OnMenu(self, event): + self.value = self.menu.GetLabel(event.GetId()) + if self.handler: + self.handler(self) + +class MidiKnob(QuiskControl): + def __init__(self, parent, text, height, pos=wx.DefaultPosition, style=0): + QuiskControl.__init__(self, parent, text, height, pos, style) + def MakeMenu(self): + self.menu = wx.Menu() + for name in application.midiControls: + ctrl, func = application.midiControls[name] + if not ctrl or not func: + continue + item = self.menu.Append(-1, name) + self.Bind(wx.EVT_MENU, self.OnMenu, item) + def OnMenu(self, event): + self.value = self.menu.GetLabel(event.GetId()) + if self.handler: + self.handler(self) + +class MidiJogWheel(QuiskControl): + def __init__(self, parent, text, height, pos=wx.DefaultPosition, style=0): + self.jog_direction = "+" + self.jog_speed = "5" + self.MakeText() + QuiskControl.__init__(self, parent, self.text, height, pos, style) + def MakeText(self): + self.text = "Jog Wheel %s%s" % (self.jog_direction, self.jog_speed) + def MakeMenu(self): + self.menu = wx.Menu() + self.direc_menu = wx.Menu() + item = self.direc_menu.AppendRadioItem(-1, "Move +") + self.Bind(wx.EVT_MENU, self.OnDirecMenu, item) + item = self.direc_menu.AppendRadioItem(-1, "Move -") + self.Bind(wx.EVT_MENU, self.OnDirecMenu, item) + self.menu.AppendSubMenu(self.direc_menu, "Direction") + self.speed_menu = wx.Menu() + for i in range(10): + item = self.speed_menu.AppendRadioItem(-1, "Speed %d" % i) + self.Bind(wx.EVT_MENU, self.OnSpeedMenu, item) + if i == 5: + item.Check() + self.menu.AppendSubMenu(self.speed_menu, "Speed") + controls = wx.Menu() + for name in application.midiControls: + ctrl, func = application.midiControls[name] + if not ctrl or not func: + continue + item = controls.Append(-1, name) + self.Bind(wx.EVT_MENU, self.OnMenu, item) + self.menu.AppendSubMenu(controls, "Name") + def OnMenu(self, event): + value = self.menu.GetLabel(event.GetId()) + self.value = "%s %s%s" % (value, self.jog_direction, self.jog_speed) + if self.handler: + self.handler(self) + def OnDirecMenu(self, event): + value = self.menu.GetLabel(event.GetId()) + self.jog_direction = value[-1] + self.MakeText() + self.Refresh() + if self.handler: + self.handler(self, "%s%s" % (self.jog_direction, self.jog_speed)) + def OnSpeedMenu(self, event): + value = self.menu.GetLabel(event.GetId()) + self.jog_speed = value[-1] + self.MakeText() + self.Refresh() + if self.handler: + self.handler(self, "%s%s" % (self.jog_direction, self.jog_speed)) + def ChangeSelection(self, text): + self.jog_direction = text[0] + self.jog_speed = text[1] + self.MakeText() + self.Refresh() + if self.jog_direction == '+': + self.direc_menu.FindItemByPosition(0).Check() + else: + self.direc_menu.FindItemByPosition(1).Check() + index = int(self.jog_speed) + self.speed_menu.FindItemByPosition(index).Check() + +class ControlMixin: + def __init__(self, parent): + self.font = wx.Font(conf.config_font_size, wx.FONTFAMILY_SWISS, wx.NORMAL, + wx.FONTWEIGHT_NORMAL, False, conf.quisk_typeface) + self.SetFont(self.font) + self.row = 1 + self.charx = self.GetCharWidth() + self.chary = self.GetCharHeight() + self.quisk_height = self.chary * 14 // 10 + # GBS + self.gbs = wx.GridBagSizer(2, 2) + self.gbs.SetEmptyCellSize((self.charx, self.charx)) + self.SetSizer(self.gbs) + self.gbs.Add((self.charx, self.charx), (0, 0)) + def MarkCols(self): + for col in range(1, self.num_cols): + c = wx.StaticText(self, -1, str(col % 10)) + self.gbs.Add(c, (self.row, col)) + self.row += 1 + def NextRow(self, row=None): + if row is None: + self.row += 1 + else: + self.row = row + def AddHelpButton(self, col, text, help_text, border=1): + hbtn = QuiskPushbutton(self, self._BTnHelp, "..") + hbtn.SetColorGray() + hbtn.quisk_help_text = help_text + hbtn.quisk_caption = text + h = self.quisk_height + 2 + hbtn.SetSizeHints(h, h, h, h) + self.gbs.Add(hbtn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + def AddTextL(self, col, text, span=None): + c = wx.StaticText(self, -1, text) + if col < 0: + pass + elif span is None: + self.gbs.Add(c, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL) + else: + self.gbs.Add(c, (self.row, col), span=(1, span), flag=wx.ALIGN_CENTER_VERTICAL) + return c + def AddTextC(self, col, text, span=None, flag=wx.ALIGN_CENTER): + c = wx.StaticText(self, -1, text) + if col < 0: + pass + elif span is None: + self.gbs.Add(c, (self.row, col), flag=flag) + else: + self.gbs.Add(c, (self.row, col), span=(1, span), flag=flag) + return c + def AddTextCHelp(self, col, text, help_text, span=None): + bsizer = wx.BoxSizer(wx.HORIZONTAL) + txt = wx.StaticText(self, -1, text) + bsizer.Add(txt, flag=wx.ALIGN_CENTER_VERTICAL) + btn = QuiskPushbutton(self, self._BTnHelp, "..") + btn.SetColorGray() + btn.quisk_help_text = help_text + btn.quisk_caption = text + h = self.quisk_height + 2 + btn.SetSizeHints(h, h, h, h) + bsizer.Add(btn, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border=self.charx) + if col < 0: + pass + elif span is None: + self.gbs.Add(bsizer, (self.row, col), flag = wx.ALIGN_CENTER) + else: + self.gbs.Add(bsizer, (self.row, col), span=(1, span), flag = wx.ALIGN_CENTER) + return bsizer + def AddTextLHelp(self, col, text, help_text, span=None): + bsizer = wx.BoxSizer(wx.HORIZONTAL) + btn = QuiskPushbutton(self, self._BTnHelp, "..") + btn.SetColorGray() + btn.quisk_help_text = help_text + btn.quisk_caption = text + h = self.quisk_height + 2 + btn.SetSizeHints(h, h, h, h) + bsizer.Add(btn, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border=self.charx) + txt = wx.StaticText(self, -1, text) + bsizer.Add(txt, flag=wx.ALIGN_CENTER_VERTICAL) + if col < 0: + pass + elif span is None: + self.gbs.Add(bsizer, (self.row, col), flag = 0) + else: + self.gbs.Add(bsizer, (self.row, col), span=(1, span), flag = 0) + return bsizer + def AddTextColorChangeHelp(self, col, text, color, btn_text, handler, help_text, border=2, span1=1, span2=1, span3=1): + txt = wx.StaticText(self, -1, text) + self.gbs.Add(txt, (self.row, col), span=(1, span1), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx) + col += span1 + Cbar = wx.StaticText(self, -1, '') + h = self.quisk_height - 2 + Cbar.SetSizeHints(h, h, -1, h) + if color: + Cbar.SetBackgroundColour(color) + self.gbs.Add(Cbar, (self.row, col), span=(1, span2), + flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT, + border=self.charx*2//10) + col += span2 + btn = QuiskPushbutton(self, handler, btn_text) + btn.SetColorGray() + h = self.quisk_height + 2 + btn.SetSizeHints(-1, h, -1, h) + self.gbs.Add(btn, (self.row, col), span=(1, span3), flag = wx.EXPAND) + col += span3 + hbtn = QuiskPushbutton(self, self._BTnHelp, "..") + hbtn.SetColorGray() + hbtn.quisk_help_text = help_text + hbtn.quisk_caption = text + h = self.quisk_height + 2 + hbtn.SetSizeHints(h, h, h, h) + self.gbs.Add(hbtn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + return txt, Cbar, btn + def AddTextEditHelp(self, col, text1, text2, help_text, border=2, span1=1, span2=1, no_edit=True): + txt = wx.StaticText(self, -1, text1) + self.gbs.Add(txt, (self.row, col), span=(1, span1), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx) + col += span1 + #txt = wx.StaticText(self, -1, text2) + if no_edit: + edt = QuiskTextCtrl(self, text2, style=wx.TE_READONLY) + else: + edt = QuiskTextCtrl(self, text2, style=wx.TE_PROCESS_ENTER) + #self.gbs.Add(txt, (self.row, col), span=(1, span2), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx) + self.gbs.Add(edt, (self.row, col), span=(1, span2), + flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT, + border=self.charx*2//10) + col += span2 + btn = QuiskPushbutton(self, self._BTnHelp, "..") + btn.SetColorGray() + btn.quisk_help_text = help_text + btn.quisk_caption = text1 + h = self.quisk_height + 2 + btn.SetSizeHints(h, h, h, h) + self.gbs.Add(btn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + return txt, edt, btn + def AddTextSliderHelp(self, col, text, value, themin, themax, handler, help_text, border=2, span=1, scale=1): + display = "%" in text + sld = SliderBoxHH(self, text, value, themin, themax, handler, display, scale) + self.gbs.Add(sld, (self.row, col), span=(1, span), + flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT, + border=self.charx*2//10) + col += span + btn = QuiskPushbutton(self, self._BTnHelp, "..") + btn.SetColorGray() + btn.quisk_help_text = help_text + btn.quisk_caption = text + h = self.quisk_height + 2 + btn.SetSizeHints(h, h, h, h) + self.gbs.Add(btn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + return sld, btn + def AddPopupMenuHelp(self, Qclass, col, text, btn_text, help_text, span1=1, span2=1, border=1): + if text: + txt = wx.StaticText(self, -1, text) + self.gbs.Add(txt, (self.row, col), span=(1, span1), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx) + col += span1 + ctrl = Qclass(self, btn_text, self.quisk_height - 2, style=wx.BORDER_RAISED) + self.gbs.Add(ctrl, (self.row, col), span=(1, span2), flag = wx.EXPAND|wx.RIGHT|wx.TOP|wx.BOTTOM|wx.LEFT, border=2) + col += span2 + self.AddHelpButton(col, text, help_text, border) + return ctrl + def AddTextButtonHelp(self, col, text, butn_text, handler, help_text, span1=1, span2=1, border=1): + if text: + txt = wx.StaticText(self, -1, text) + self.gbs.Add(txt, (self.row, col), span=(1, span1), flag = 0) + col += span1 + else: + txt = None + btn = QuiskPushbutton(self, handler, butn_text) + btn.SetColorGray() + h = self.quisk_height + 2 + btn.SetSizeHints(-1, h, -1, h) + self.gbs.Add(btn, (self.row, col), span=(1, span2), flag = wx.EXPAND) + col += span2 + hbtn = QuiskPushbutton(self, self._BTnHelp, "..") + hbtn.SetColorGray() + hbtn.quisk_help_text = help_text + hbtn.quisk_caption = text + h = self.quisk_height + 2 + hbtn.SetSizeHints(h, h, h, h) + self.gbs.Add(hbtn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + return txt, btn + def AddText2CheckHelp(self, col, text, butn1_text, handler1, butn2_text, handler2, help_text, span1=1, span2=1, border=1): + if text: + txt = wx.StaticText(self, -1, text) + self.gbs.Add(txt, (self.row, col), span=(1, span1), flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) + col += span1 + else: + txt = None + btn1 = QuiskCheckbutton(self, handler1, butn1_text) + btn1.SetColorGray() + btn2 = QuiskCheckbutton(self, handler2, butn2_text) + btn2.SetColorGray() + w1 = btn1.GetSize().Width + w2 = btn2.GetSize().Width + w = max(w1, w2) + h = self.quisk_height + 2 + btn1.SetSizeHints(w, h, w, h) + btn2.SetSizeHints(w, h, w, h) + bsizer = wx.BoxSizer(wx.HORIZONTAL) + bsizer.Add(btn1, proportion=1) + bsizer.Add(2, h, proportion=0) + bsizer.Add(btn2, proportion=1) + self.gbs.Add(bsizer, (self.row, col), span=(1, span2), flag = wx.EXPAND) + col += span2 + hbtn = QuiskPushbutton(self, self._BTnHelp, "..") + hbtn.SetColorGray() + hbtn.quisk_help_text = help_text + hbtn.quisk_caption = text + h = self.quisk_height + 2 + hbtn.SetSizeHints(h, h, h, h) + self.gbs.Add(hbtn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + return txt, btn1, btn2 + def AddText2ButtonHelp(self, col, text, butn1_text, handler1, butn2_text, handler2, help_text, span1=1, span2=1, border=1): + if text: + txt = wx.StaticText(self, -1, text) + self.gbs.Add(txt, (self.row, col), span=(1, span1), flag = 0) + col += span1 + else: + txt = None + btn1 = QuiskPushbutton(self, handler1, butn1_text) + btn1.SetColorGray() + btn2 = QuiskPushbutton(self, handler2, butn2_text) + btn2.SetColorGray() + w1 = btn1.GetSize().Width + w2 = btn2.GetSize().Width + w = max(w1, w2) + h = self.quisk_height + 2 + btn1.SetSizeHints(w, h, 999, h) + btn2.SetSizeHints(w, h, 999, h) + bsizer = wx.BoxSizer(wx.HORIZONTAL) + bsizer.Add(btn1, proportion=1) + bsizer.Add(2, h, proportion=0) + bsizer.Add(btn2, proportion=1) + self.gbs.Add(bsizer, (self.row, col), span=(1, span2), flag = wx.EXPAND) + col += span2 + hbtn = QuiskPushbutton(self, self._BTnHelp, "..") + hbtn.SetColorGray() + hbtn.quisk_help_text = help_text + hbtn.quisk_caption = text + h = self.quisk_height + 2 + hbtn.SetSizeHints(h, h, h, h) + self.gbs.Add(hbtn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + return txt, btn1, btn2 + def AddTextCtrl(self, col, text, handler=None, span=None): + c = wx.TextCtrl(self, -1, text, style=wx.TE_RIGHT) + if col < 0: + pass + elif span is None: + self.gbs.Add(c, (self.row, col), flag=wx.ALIGN_CENTER) + else: + self.gbs.Add(c, (self.row, col), span=(1, span), flag=wx.ALIGN_CENTER) + if handler: + c.Bind(wx.EVT_TEXT, handler) + return c + def AddBoxSizer(self, col, span): + bsizer = wx.BoxSizer(wx.HORIZONTAL) + self.gbs.Add(bsizer, (self.row, col), span=(1, span)) + return bsizer + def AddColSpacer(self, col, width): # add a width spacer to row 0 + self.gbs.Add((width * self.charx, 1), (0, col)) # width is in characters + def AddRadioButton(self, col, text, handler=None, span=None, start=False): + if start: + c = wx.RadioButton(self, -1, text, style=wx.RB_GROUP) + else: + c = wx.RadioButton(self, -1, text) + if col < 0: + pass + elif span is None: + self.gbs.Add(c, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL) + else: + self.gbs.Add(c, (self.row, col), span=(1, span), flag=wx.ALIGN_CENTER_VERTICAL) + if handler: + c.Bind(wx.EVT_RADIOBUTTON, handler) + return c + def AddCheckBox(self, col, text, handler=None, flag=0, border=0): + btn = wx.CheckBox(self, -1, text) + h = self.quisk_height + 2 + btn.SetSizeHints(-1, h, -1, h) + if col >= 0: + self.gbs.Add(btn, (self.row, col), flag=flag, border=border*self.charx) + if self.radio_name == "ConfigFileRadio": + btn.Enable(False) + noname_enable.append(btn) + if handler: + btn.Bind(wx.EVT_CHECKBOX, handler) + return btn + def AddBitField(self, col, number, name, band, value, handler=None, span=None, border=1): + bf = QuiskBitField(self, number, value, self.quisk_height, handler) + if col < 0: + pass + elif span is None: + self.gbs.Add(bf, (self.row, col), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, border=border*self.charx) + else: + self.gbs.Add(bf, (self.row, col), span=(1, span), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, border=border*self.charx) + bf.quisk_data_name = name + bf.quisk_band = band + return bf + def AddPushButton(self, col, text, handler, border=0): + btn = QuiskPushbutton(self, handler, text) + btn.SetColorGray() + h = self.quisk_height + 2 + btn.SetSizeHints(-1, h, -1, h) + if col >= 0: + self.gbs.Add(btn, (self.row, col), flag=wx.RIGHT|wx.LEFT, border=border*self.charx) + if self.radio_name == "ConfigFileRadio": + btn.Enable(False) + noname_enable.append(btn) + return btn + def AddPushButtonR(self, col, text, handler, border=0): + btn = self.AddPushButton(-1, text, handler, border) + if col >= 0: + self.gbs.Add(btn, (self.row, col), flag=wx.ALIGN_RIGHT|wx.RIGHT|wx.LEFT, border=border*self.charx) + return btn + def AddComboCtrl(self, col, value, choices, right=False, no_edit=False, span=None, border=1): + if no_edit: + cb = ChoiceCombo(self, value, choices) + else: + cb = ComboCtrl(self, value, choices) + if col < 0: + pass + elif span is None: + self.gbs.Add(cb, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT|wx.LEFT, border=border*self.charx) + else: + self.gbs.Add(cb, (self.row, col), span=(1, span), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT|wx.LEFT, border=border*self.charx) + if self.radio_name == "ConfigFileRadio": + cb.Enable(False) + noname_enable.append(cb) + return cb + def AddComboCtrlTx(self, col, text, value, choices, right=False, no_edit=False): + c = wx.StaticText(self, -1, text) + if col >= 0: + self.gbs.Add(c, (self.row, col)) + cb = self.AddComboCtrl(col + 1, value, choices, right, no_edit) + else: + cb = self.AddComboCtrl(col, value, choices, right, no_edit) + return c, cb + def AddTextComboHelp(self, col, text, value, choices, help_text, no_edit=False, border=2, span_text=1, span_combo=1): + txt = wx.StaticText(self, -1, text) + self.gbs.Add(txt, (self.row, col), span=(1, span_text), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx) + col += span_text + cb = self.AddComboCtrl(-1, value, choices, False, no_edit) + if no_edit: + if '#' in value: + value = value[0:value.index('#')] + value = value.strip() + l = len(value) + for i in range(len(choices)): + ch = choices[i] + if '#' in ch: + ch = ch[0:ch.index('#')] + ch.strip() + if value == ch[0:l]: + cb.SetSelection(i) + break + else: + if 'fail' in value: + pass + else: + print ("Failure to set value for", text, value, choices) + self.gbs.Add(cb, (self.row, col), span=(1, span_combo), + flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT, border=self.charx*2//10) + col += span_combo + btn = QuiskPushbutton(self, self._BTnHelp, "..") + btn.SetColorGray() + btn.quisk_help_text = help_text + btn.quisk_caption = text + h = self.quisk_height + 2 + btn.SetSizeHints(h, h, h, h) + self.gbs.Add(btn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + return txt, cb, btn + def AddTextDblSpinnerHelp(self, col, text, value, dmin, dmax, dinc, help_text, border=2, span_text=1, span_spinner=1): + txt = wx.StaticText(self, -1, text) + self.gbs.Add(txt, (self.row, col), span=(1, span_text), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx) + col += span_text + spn = wx.SpinCtrlDouble(self, -1, initial=value, min=dmin, max=dmax, inc=dinc) + self.gbs.Add(spn, (self.row, col), span=(1, span_spinner), + flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT, + border=self.charx*2//10) + col += span_spinner + btn = QuiskPushbutton(self, self._BTnHelp, "..") + btn.SetColorGray() + btn.quisk_help_text = help_text + btn.quisk_caption = text + h = self.quisk_height + 2 + btn.SetSizeHints(h, h, h, h) + self.gbs.Add(btn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + return txt, spn, btn + def AddTextSpinnerHelp(self, col, text, value, imin, imax, help_text, border=2, span_text=1, span_spinner=1): + txt = wx.StaticText(self, -1, text) + self.gbs.Add(txt, (self.row, col), span=(1, span_text), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx) + col += span_text + spn = wx.SpinCtrl(self, -1, "") + spn.SetRange(imin, imax) + spn.SetValue(value) + self.gbs.Add(spn, (self.row, col), span=(1, span_spinner), + flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT, + border=self.charx*2//10) + col += span_spinner + btn = QuiskPushbutton(self, self._BTnHelp, "..") + btn.SetColorGray() + btn.quisk_help_text = help_text + btn.quisk_caption = text + h = self.quisk_height + 2 + btn.SetSizeHints(h, h, h, h) + self.gbs.Add(btn, (self.row, col), flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=self.charx*border) + return txt, spn, btn + def _BTnHelp(self, event): + btn = event.GetEventObject() + caption = btn.quisk_caption + i = caption.find('%') # remove formats %.2f + if i > 0: + caption = caption[0:i] + dlg = wx.MessageDialog(self, btn.quisk_help_text, caption, style=wx.OK|wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + def ErrorCheck(self, ctrl): # Return True for OK + name = ctrl.quisk_data_name + value = ctrl.GetValue() + #if name == 'quisk_debug_sound' and value in ("New Sound", "Debug New Sound") and not QS.get_params('have_soundio'): + # dlg = wx.MessageDialog(None, "Please install the libsoundio-dev package, and re-make", 'New Sound', wx.OK|wx.ICON_ERROR) + # dlg.ShowModal() + # dlg.Destroy() + # return False + return True + def OnChange(self, ctrl): + value = ctrl.GetValue() + self.OnChange2(ctrl, value) + def OnChange2(self, ctrl, value): + # Careful: value is Unicode + name = ctrl.quisk_data_name + try: + fmt4 = local_conf.format4name[name] + except: + if name[0:4] in ("lin_", "win_"): + fmt4 = local_conf.format4name[name[4:]] + fmt4 = fmt4[0:4] + ok, x = self.EvalItem(value, fmt4) # Only evaluates integer, number, boolean, text, rfile + if ok: + radio_dict = local_conf.GetRadioDict(self.radio_name) + if name in name2format and name2format[name][1] == value: + if name in radio_dict: + del radio_dict[name] + local_conf.settings_changed = True + else: + radio_dict[name] = value + local_conf.settings_changed = True + # Immediate changes + 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'): + setattr(conf, name, x) + application.ImmediateChange(name) + elif name[0:4] in ('lin_', 'win_'): + name = name[4:] + if name in ('quisk_serial_port', ): + setattr(conf, name, x) + application.ImmediateChange(name) + elif name == "reverse_tx_sideband": + setattr(conf, name, x) + QS.set_tx_audio(reverse_tx_sideband=x) + elif name == "dc_remove_bw": + setattr(conf, name, x) + QS.set_sparams(dc_remove_bw=x) + elif name == "digital_output_level": + setattr(conf, name, x) + QS.set_sparams(digital_output_level=x) + elif name[0:7] == 'hermes_': + if name == 'hermes_TxLNA_dB': + application.Hardware.ChangeTxLNA(x) + elif name == "hermes_bias_adjust" and self.HermesBias0: + self.HermesBias0.Enable(x) + self.HermesBias1.Enable(x) + self.HermesWriteBiasButton.Enable(x) + application.Hardware.EnableBiasChange(x) + elif hasattr(application.Hardware, "ImmediateChange"): + setattr(conf, name, x) + application.Hardware.ImmediateChange(name) + def FormatOK(self, value, fmt4): # Check formats integer, number, boolean + ok, v = self.EvalItem(value, fmt4) + return ok + def EvalItem(self, value, fmt4): # Return Python integer, number, boolean, text, rfile, keycode + # return is (item_is_ok, evaluated_item) + if fmt4 not in ('inte', 'numb', 'bool', 'keyc'): # only certain formats are evaluated + return True, value # text, rfile are returned by default + jj = value.find('#') + if jj > 0: + value = value[0:jj] + try: # only certain formats are evaluated + if fmt4 == 'inte': + v = int(value, base=0) + elif fmt4 == 'numb': + v = float(value) + elif fmt4 == 'bool': + if value == "True": + v = True + else: + v = False + elif fmt4 == 'keyc': # key code + if value == "None": + v = None + else: + v = eval(value) + v = int(v) + else: + raise ValueError + except: + #traceback.print_exc() + dlg = wx.MessageDialog(None, + "Can not set item with format %s to value %s" % (fmt4, value), + 'Change to item', wx.OK|wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + return False, None + return True, v + def GetValue(self, name, radio_dict): + try: + value = radio_dict[name] + except: + pass + else: + return value + # Value was not in radio_dict. Get it from conf. There are values for platform win_data_name and lin_data_name. + # The win_ and lin_ names are not in conf. + try: + fmt = local_conf.format4name[name] + except: + fmt = '' # not all items in conf are in section_data or receiver_data + try: + if fmt == 'dict': # make a copy for this radio + value = {} + value.update(getattr(conf, name)) + elif fmt == 'list': # make a copy for this radio + value = getattr(conf, name)[:] + else: + value = str(getattr(conf, name)) + except: + return '' + else: + return value + +class BaseWindow(wx.ScrolledCanvas, ControlMixin): + def __init__(self, parent): + wx.ScrolledCanvas.__init__(self, parent) + ControlMixin.__init__(self, parent) + +class ConfigConfig(BaseWindow): + def __init__(self, parent, width): + BaseWindow.__init__(self, parent) + self.width = width + self.SetBackgroundColour(parent.bg_color) + self.num_cols = 7 + #self.MarkCols() + self.radio_name = None + # Choice (combo) box for decimation + lst = application.Hardware.VarDecimGetChoices() + if lst: + txt = application.Hardware.VarDecimGetLabel() + index = application.Hardware.VarDecimGetIndex() + else: + txt = "Variable decimation" + lst = ["None"] + index = 0 + help_text = "If your hardware supports different sample rates, choose one here. For SoftRock, \ +change the sample rate of your sound card." + txt, cb, btn = self.AddTextComboHelp(1, txt, lst[index], lst, help_text, True) + if lst: + cb.handler = application.OnBtnDecimation + self.btn_decimation = cb + self.NextRow() + self.NextRow() + help_text = "SoftRock and other radios that use a sound card for Rx and Tx will need small corrections \ +to the amplitude and phase. Click here for an adjustment screen." + txt, btn = self.AddTextButtonHelp(1, "Adjust receive amplitude and phase", "Rx Phase..", self.OnBtnPhase, help_text) + self.rx_phase = btn + if not conf.name_of_sound_capt: + btn.Enable(0) + self.NextRow() + txt, btn = self.AddTextButtonHelp(1, "Adjust transmit amplitude and phase", "Tx Phase..", self.OnBtnPhase, help_text) + self.tx_phase = btn + if not conf.name_of_mic_play: + btn.Enable(0) + self.NextRow() + help_text = "There is a color bar under the X-axis showing the band plan. Click here for a screen to change the colors." + txt, btn = self.AddTextButtonHelp(1, "Colors on X-axis for CW, Phone", "Band plan..", self.OnBtnBandPlan, help_text) + self.NextRow() + help_text = "Click here to configure the link to WSJT-X." + txt, btn = self.AddTextButtonHelp(1, "Configure WSJT-X", "WSJT-X..", self.OnConfigureWsjtx, help_text) + self.NextRow() + lst = ("Never", "Main Rx0 on startup", "Main Rx0 now") + value = application.local_conf.globals.get("start_wsjtx", "Never") + txt, cb, btn = self.AddTextComboHelp(1, "Start WSJT-X", value, lst, help_text, True) + cb.handler = application.OnStartWsjtx + self.NextRow() + help_text = "This controls the Tx level for non-digital modes. It is usually 100%." + c1, btn = self.AddTextSliderHelp(1, "Tx level %d%% ", 100, 0, 100, self.OnTxLevel, help_text, span=2) + self.NextRow() + level = conf.digital_tx_level + help_text = "This controls the TX level for digital modes. Digital modes require greater linearity, and the digital level is often 25%." + c2, btn = self.AddTextSliderHelp(1, "Digital Tx level %d%% ", level, 0, level, self.OnDigitalTxLevel, help_text, span=2) + if not hasattr(application.Hardware, "SetTxLevel"): + c1.slider.Enable(0) + c2.slider.Enable(0) + self.NextRow() + #### Make controls SECOND column + self.row = 3 + self.AddTextL(4, "Configuration for the file record button: " + conf.Xbtn_text_file_rec, span=2) + self.NextRow() + # File for recording speaker audio + text = "Record Rx audio to WAV files 1, 2, ... " + path = application.file_name_rec_audio + help_text = 'These check buttons control what happens when you press the "File Record" button. \ +Please choose a directory for your recordings; perhaps the "Music" directory on your computer. \ +Then choose your file name. This "Record Rx audio" file is used for recording the radio speaker sound. \ +Recording stops when the button is released. When pressed again new files are created with names 001, 002, etc. \ +You will need to delete recordings you no longer need.' + self.file_button_rec_speaker = self.MakeFileButton(text, path, 0, 'rec_audio', help_text) + # File for recording samples + text = "Record I/Q samples to WAV files 1, 2, ... " + path = application.file_name_rec_samples + help_text = 'This file is used to record the I/Q samples and it works like the "Record Rx audio" button.' + self.file_button_rec_iq = self.MakeFileButton(text, path, 1, 'rec_samples', help_text) + # File for recording the microphone + text = "Record the mic to make a CQ message" + path = application.file_name_rec_mic + help_text = 'This file is used to record the microphone. It can be used to record a CQ message that can be played \ +over and over until someone answers. Change the "Play audio" to the same file to test the recording. Keep recording and \ +playing until you are satisfied, and then un-check the "Record mic" button. The file is replaced when the Record button \ +is pressed again (no 001, 002, ...).' + self.file_button_rec_mic = self.MakeFileButton(text, path, 2, 'rec_mic', help_text) + ## Play buttons + self.AddTextL(4, "Configuration for the file play button: " + conf.Xbtn_text_file_play, span=2) + self.NextRow() + # File for playing speaker audio + text = "Play audio from a WAV file" + path = application.file_name_play_audio + help_text = 'These record check buttons control what happens when you press the "File Play" button. \ +This button plays normal audio files, not samples.' + self.file_button_play_speaker = self.MakeFileButton(text, path, 10, 'play_audio', help_text) + # file for playing samples + text = "Receive saved I/Q samples from a file" + path = application.file_name_play_samples + help_text = 'This button plays I/Q samples. You can tune in different stations from the I/Q recording.' + self.file_button_play_iq = self.MakeFileButton(text, path, 11, 'play_samples', help_text) + # File for playing a file to the mic input for a CQ message + text = "Repeat a CQ message until a station answers" + path = application.file_name_play_cq + help_text = "This button enables the File Play button to start playing your CQ message." + self.file_button_play_mic = self.MakeFileButton(text, path, 12, 'play_cq', help_text) + # CQ repeat time + help_text = 'This is the amount of time to wait for someone to respond to your CQ message. \ +The CW message will then repeat. A time of zero means no repeat.' + sl, btn = self.AddTextSliderHelp(4, " Repeat secs %.1f ", 0, 0, 100, self.OnPlayFileRepeat, help_text, span=2, scale=0.1) + self.NextRow() + self.FitInside() + self.SetScrollRate(1, 1) + def MakeFileButton(self, text, path, index, name, help_text): + if index < 10: # record buttons + cb = self.AddCheckBox(4, text, self.OnCheckRecPlay) + elif index == 10: + cb = self.AddRadioButton(4, text, self.OnCheckRecPlay, start=True) + else: + cb = self.AddRadioButton(4, text, self.OnCheckRecPlay, start=False) + txt, b = self.AddTextButtonHelp(5, '', "File..", self.OnBtnFileName, help_text) + b.check_box = cb + b.index = cb.index = index + b.path = cb.path = path + b.name = 'file_name_' + name + if index < 10: # record buttons + if path: + enable = True + if index == 0: # check record audio if there is a path + cb.SetValue(True) + else: + enable = False + else: # playback buttons + enable = os.path.isfile(path) + cb.Enable(enable) + self.NextRow() + return b + def OnTxLevel(self, event): + application.tx_level = event.GetEventObject().GetValue() + application.Hardware.SetTxLevel() + def OnDigitalTxLevel(self, event): + application.digital_tx_level = event.GetEventObject().GetValue() + application.Hardware.SetTxLevel() + def OnBtnPhase(self, event): + btn = event.GetEventObject() + if btn.GetLabel()[0:2] == 'Tx': + rx_tx = 'tx' + else: + rx_tx = 'rx' + application.screenBtnGroup.SetLabel('Graph', do_cmd=True) + if application.w_phase: + application.w_phase.Raise() + else: + application.w_phase = QAdjustPhase(self, self.width, rx_tx) + def OnBtnFileName(self, event): + btn = event.GetEventObject() + dr, fn = os.path.split(btn.path) + if btn.index in (0, 1): # record audio or samples + dlg = wx.FileDialog(self, 'Choose WAV file', dr, fn, style=wx.FD_SAVE, wildcard="Wave files (*.wav)|*.wav") + elif btn.index == 2: # record mic + dlg = wx.FileDialog(self, 'Choose WAV file', dr, fn, style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT, wildcard="Wave files (*.wav)|*.wav") + else: # play buttons + dlg = wx.FileDialog(self, 'Choose WAV file', dr, fn, style=wx.FD_OPEN, wildcard="Wave files (*.wav)|*.wav") + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + if path[-4:].lower() != '.wav': + path = path + '.wav' + btn.path = path + setattr(application, btn.name, path) + btn.check_box.path = path + btn.check_box.Enable(True) + if btn.index >= 10: # play buttons + btn.check_box.SetValue(True) + application.file_play_source = btn.index + self.EnableRecPlay() + dlg.Destroy() + def ChangePlayFile(self, btn, path): + btn.path = path + setattr(application, btn.name, path) + btn.check_box.path = path + btn.check_box.Enable(True) + self.EnableRecPlay() + def OnBtnBandPlan(self, ctrl): + from configure import BandPlanDlg + dlg = BandPlanDlg(self) + dlg.ShowModal() + dlg.Destroy() + def OnConfigureWsjtx(self, ctrl): + from configure import WsjtxDlg + dlg = WsjtxDlg(self) + dlg.ShowModal() + dlg.Destroy() + def InitRecPlay(self): + for btn in (self.file_button_play_speaker, self.file_button_play_iq, self.file_button_play_mic): + if btn.index == application.file_play_source: + btn.check_box.SetValue(True) + def EnableRecPlay(self): # Enable or disable file record/play buttons on main screen + enable_rec = False + enable_play = False + for btn in (self.file_button_rec_speaker, self.file_button_rec_iq, self.file_button_rec_mic): + if btn.check_box.GetValue(): + enable_rec = True + break + for btn in (self.file_button_play_speaker, self.file_button_play_iq, self.file_button_play_mic): + if btn.check_box.GetValue() and os.path.isfile(btn.path): + enable_play = True + break + application.btn_file_record.Enable(enable_rec) + application.btnFilePlay.Enable(enable_play) + def OnCheckRecPlay(self, event): + btn = event.GetEventObject() + if btn.GetValue(): + if btn.index >= 10: # play button + application.file_play_source = btn.index + self.EnableRecPlay() + def OnPlayFileRepeat(self, event): + application.file_play_repeat = event.GetEventObject().GetValue() * 0.1 + def OnFilePlayButton(self, play): + if play: + for btn in (self.file_button_play_speaker, self.file_button_play_iq, self.file_button_play_mic): + if application.file_play_source == btn.index and btn.check_box.GetValue() and os.path.isfile(btn.path): + QS.open_wav_file_play(btn.path) + break + else: + QS.set_file_name(play_button=0) # Close all play files + def OnFileRecordButton(self, record): # The File Record button on the main screen + if record: + for btn in (self.file_button_rec_speaker, self.file_button_rec_iq, self.file_button_rec_mic): + if btn.check_box.GetValue(): # open this file + if btn.index in (0, 1) and os.path.isfile(btn.path): # Change path + direc, fname = os.path.split(btn.path) + base = os.path.splitext(fname)[0] + while base and base[-1] in '0123456789': + base = base[0:-1] + if not base: + base = "rec" + index = 0 + for dir_entry in os.scandir(direc): + if dir_entry.is_file(): + name = dir_entry.name + if len(name) > 4 and name[-4] == '.' and name.startswith(base): + ma = re.match(base + '([0-9]+)[.]', name) + if ma: + index = max(index, int(ma.group(1), base=10)) + btn.path = os.path.join(direc, "%s%03d.wav" % (base, index + 1)) + QS.set_file_name(btn.index, btn.path, record_button=1) + if btn.index == 0: # Change play files to equal record files + self.ChangePlayFile(self.file_button_play_speaker, btn.path) + elif btn.index == 1: + self.ChangePlayFile(self.file_button_play_iq, btn.path) + else: + QS.set_file_name(record_button=0) # Close all record files + +class ConfigTxAudio(BaseWindow): + def __init__(self, parent): + BaseWindow.__init__(self, parent) + self.tmp_playing = False + self.timer = wx.Timer(self) + self.Bind(wx.EVT_TIMER, self.OnTimer) + self.SetBackgroundColour(parent.bg_color) + self.num_cols = 4 + #self.MarkCols() + self.NextRow() + t = "This is a test screen for transmit audio. SSB, AM and FM have separate settings." + item = self.AddTextL(1, t, span=2) + self.NextRow() + self.NextRow() + self.NextRow() + help_text = "Record the transmit audio while adjusting clip and preemphasis; then play back the result." + txt, self.btn_record, self.btn_playback = self.AddText2CheckHelp(1, "Record transmit audio" + ' ' * 10, + "Record", self.OnBtnRecord, "Playback", self.OnBtnPlayback, help_text) + self.btn_playback.Enable(0) + if not conf.microphone_name: + self.btn_record.Enable(0) + self.NextRow() + help_text = "Audio level that triggers VOX (all modes)." + self.AddTextSliderHelp(1, "VOX level %d dB", application.levelVOX, -40, 0, application.OnLevelVOX, help_text, span=2) + self.NextRow() + help_text = "Time to hold VOX after end of audio in seconds." + self.AddTextSliderHelp(1, "VOX hold %0.2f secs", application.timeVOX, 0, 4000, application.OnTimeVOX, help_text, span=2, scale=0.001) + self.NextRow() + help_text = "Tx audio clipping level in dB for this mode." + sld, btn = self.AddTextSliderHelp(1, "Clip level %2d dB", 0, 0, 20, application.OnTxAudioClip, help_text, span=2) + application.CtrlTxAudioClip = sld + self.NextRow() + help_text = "Tx audio preemphasis of high frequencies." + sld, btn = self.AddTextSliderHelp(1, "Preemphasis %4.2f", 0, 0, 100, application.OnTxAudioPreemph, help_text, span=2, scale=0.01) + application.CtrlTxAudioPreemph = sld + self.NextRow() + self.FitInside() + self.SetScrollRate(1, 1) + def OnTimer(self, event): + if not self.tmp_playing: + self.timer.Stop() + elif QS.set_record_state(-1): # poll to see if playback is finished + self.tmp_playing = False + self.timer.Stop() + self.btn_playback.SetValue(False) + self.btn_record.Enable(1) + def OnBtnRecord(self, event): + if event.GetEventObject().GetValue(): + QS.set_kill_audio(1) + self.btn_playback.Enable(0) + QS.set_record_state(4) + else: + QS.set_kill_audio(0) + self.btn_playback.Enable(1) + QS.set_record_state(1) + def OnBtnPlayback(self, event): + if event.GetEventObject().GetValue(): + self.btn_record.Enable(0) + QS.set_record_state(2) + self.tmp_playing = True + self.timer.Start(milliseconds=200) + else: + self.btn_record.Enable(1) + QS.set_record_state(3) + self.tmp_playing = False + +class Radios(BaseWindow): # The "Radios" first-level page + def __init__(self, parent): + BaseWindow.__init__(self, parent) + self.SetBackgroundColour(parent.bg_color) + self.num_cols = 8 + self.radio_name = None + self.cur_radio_text = self.AddTextL(1, 'xx', self.num_cols - 1) + self.SetCurrentRadioText() + self.NextRow() + self.NextRow() + item = self.AddTextL(1, "When Quisk starts, use the radio") + self.start_radio = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True) + self.start_radio.handler = self.OnChoiceStartup + self.NextRow() + item = self.AddTextL(1, "Add a new radio with the general type") + choices = [] + for name, data in local_conf.receiver_data: + choices.append(name) + self.add_type = self.AddComboCtrl(2, '', choices=choices, no_edit=True) + self.add_type.SetSelection(0) + item = self.AddTextL(3, "and name the new radio") + self.add_name = self.AddComboCtrl(4, '', choices=["My Radio", "SR with XVtr", "SoftRock"]) + item = self.AddPushButton(5, "Add", self.OnBtnAdd) + self.NextRow() + item = self.AddTextL(1, "Rename the radio named") + self.rename_old = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True) + item = self.AddTextL(3, "to the new name") + self.rename_new = self.AddComboCtrl(4, '', choices=["My Radio", "SR with XVtr", "SoftRock"]) + item = self.AddPushButton(5, "Rename", self.OnBtnRename) + self.NextRow() + item = self.AddTextL(1, "Delete the radio named") + self.delete_name = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True) + item = self.AddPushButton(3, "Delete", self.OnBtnDelete) + self.NextRow() + self.FitInside() + self.SetScrollRate(1, 1) + self.NewRadioNames() + def SetCurrentRadioText(self): + radio_dict = local_conf.GetRadioDict(self.radio_name) + radio_type = radio_dict['hardware_file_type'] + if Settings[1] == "ConfigFileRadio": + text = 'The current radio is ConfigFileRadio, so all settings come from the config file. The hardware type is %s.' % radio_type + else: + text = "Quisk is running with settings from the radio %s. The hardware type is %s." % (Settings[1], radio_type) + self.cur_radio_text.SetLabel(text) + def DuplicateName(self, name): + if name in Settings[2] or name == "ConfigFileRadio": + dlg = wx.MessageDialog(self, "The name already exists. Please choose a different name.", + 'Quisk', wx.OK) + dlg.ShowModal() + dlg.Destroy() + return True + return False + def OnBtnAdd(self, event): + name = self.add_name.GetValue().strip() + if not name or self.DuplicateName(name): + return + self.add_name.SetValue('') + typ = self.add_type.GetValue().strip() + if local_conf.AddRadio(name, typ): + if Settings[0] != "Ask me": + Settings[0] = name + self.NewRadioNames() + local_conf.settings_changed = True + def OnBtnRename(self, event): + old = self.rename_old.GetValue() + new = self.rename_new.GetValue().strip() + if not old or not new or self.DuplicateName(new): + return + self.rename_new.SetValue('') + if local_conf.RenameRadio(old, new): + if old == 'ConfigFileRadio' and Settings[1] == "ConfigFileRadio": + Settings[1] = new + elif Settings[1] == old: + Settings[1] = new + self.SetCurrentRadioText() + if Settings[0] != "Ask me": + Settings[0] = new + self.NewRadioNames() + local_conf.settings_changed = True + def OnBtnDelete(self, event): + name = self.delete_name.GetValue() + if not name: + return + dlg = wx.MessageDialog(self, + "Are you sure you want to permanently delete the radio %s?" % name, + 'Quisk', wx.OK|wx.CANCEL|wx.ICON_EXCLAMATION) + ret = dlg.ShowModal() + dlg.Destroy() + if ret == wx.ID_OK and local_conf.DeleteRadio(name): + self.NewRadioNames() + local_conf.settings_changed = True + def OnChoiceStartup(self, ctrl): + choice = self.start_radio.GetValue() + if Settings[0] != choice: + Settings[0] = choice + local_conf.settings_changed = True + def NewRadioNames(self): # Correct all choice lists for changed radio names + choices = Settings[2][:] # can rename any available radio + self.rename_old.SetItems(choices) + self.rename_old.SetSelection(0) + if "ConfigFileRadio" in choices: + choices.remove("ConfigFileRadio") + if Settings[1] in choices: + choices.remove(Settings[1]) + self.delete_name.SetItems(choices) # can not delete ConfigFileRadio nor the current radio + self.delete_name.SetSelection(0) + choices = Settings[2] + ["Ask me"] + if "ConfigFileRadio" not in choices: + choices.append("ConfigFileRadio") + self.start_radio.SetItems(choices) # can start any radio, plus "Ask me" and "ConfigFileRadio" + try: # Set text in control + index = choices.index(Settings[0]) # last used radio, or new or renamed radio + except: + num = len(Settings[2]) + if len == 0: + index = 1 + elif num == 1: + index = 0 + else: + index = len(choices) - 2 + Settings[0] = choices[index] + self.start_radio.SetSelection(index) + +class RadioSection(BaseWindow): # The pages for each section in the second-level notebook for each radio + help_cw = \ + 'If your CW key is not connected to your radio hardware, you can connect the key to Quisk using a serial port or MIDI. '\ + 'For the serial port, enter the serial port name and either CTS or DSR for the key. '\ + 'You can use MIDI for keying. See the "Keys" screen . '\ + 'If you turn on the Quisk internal sidetone, be sure to use the Fast Sound setting for Windows. '\ + 'And for Linux, use Alsa for the radio sound output. '\ + 'Reduce the hardware poll time for faster response.' + def __init__(self, parent, radio_name, section, names): + BaseWindow.__init__(self, parent) + self.radio_name = radio_name + self.section = section + self.names = names + self.MakeControls() + def MakeControls(self): + self.num_cols = 8 + #self.MarkCols() + self.NextRow(3) + Keys = self.section == "Keys" + if Keys: + col = 5 + else: + col = 1 + start_row = self.row + radio_dict = local_conf.GetRadioDict(self.radio_name) + radio_type = radio_dict['hardware_file_type'] + for name, text, fmt, help_text, values in self.names: + if name == 'remote_radio_password': + self.AddTextButtonHelp(col, text, "Change", self.OnChangePassword, help_text) + if col == 1: + col = 4 + else: + col = 1 + self.NextRow() + elif name == 'remote_radio_ip' and radio_type != "Control Head": + continue + elif name == 'favorites_file_path': + self.favorites_path = radio_dict.get('favorites_file_path', '') + row = self.row + self.row = 1 + item, self.favorites_combo, btn = self.AddTextComboHelp(1, text, self.favorites_path, values, help_text, False, span_text=1, span_combo=4) + self.favorites_combo.handler = self.OnButtonChangeFavorites + item = self.AddPushButtonR(7, "Change..", self.OnButtonChangeFavorites, border=0) + self.row = row + else: + if name == "keyupDelay": + if col != 1: + col = 1 + self.NextRow() + self.NextRow() + self.NextRow() + self.AddTextCHelp(1, "CW Settings for Remote and Local Operation", self.help_cw, 8) + col = 1 + self.NextRow() + self.NextRow() + if fmt[0:4] in ('dict', 'list'): + continue + if name[0:4] == platform_ignore: + continue + if name == 'use_fast_sound' and sys.platform != 'win32': + continue + value = self.GetValue(name, radio_dict) + no_edit = "choice" in fmt or fmt == 'boolean' + if name == "midi_cwkey_device": + # Start of MIDI + values = QS.control_midi(get_in_names=1) + values.insert(0, '') + txt, cb, btn = self.AddTextComboHelp(col, text, value, values, help_text, no_edit) + cb.handler = self.OnChange + cb.quisk_data_name = name + if Keys: + self.NextRow() + elif col == 1: + col = 4 + else: + col = 1 + self.NextRow() + if self.section == "Keys": + if self.radio_name == Settings[1]: # Current radio in use + application.config_midi_window = self + charx = self.charx + self.list_ctrl = list_ctrl = wx.ListCtrl(self, size=(charx * 19 + 5, self.quisk_height * 8), + style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.BORDER_SIMPLE) + list_ctrl.InsertColumn(0, 'Midi', width=charx*10) + list_ctrl.InsertColumn(1, 'Quisk', width=charx*9) + list_ctrl.SetBackgroundColour(wx.Colour(0xeaeaea)) + list_ctrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnMidiListSelected) + index = 0 + for txt_note in local_conf.MidiNoteDict: # txt_note is a string + int_note = int(txt_note, base=0) + self.list_ctrl.InsertItem(index, txt_note) + self.list_ctrl.SetItemData(index, int_note) + self.list_ctrl.SetItem(index, 1, local_conf.MidiNoteDict[txt_note]) + index += 1 + list_ctrl.SortItems(lambda x, y: x - y) + rows = self.row - start_row + 1 + self.row = start_row + self.gbs.Add(list_ctrl, (start_row, 1), span=(rows, 1), flag=wx.RIGHT, border=charx * 15 // 10) + #self.gbs.Add (charx, self.chary // 2, wx.GBPosition(start_row + rows, 1)) # Spacer at the bottom + hlp = \ + 'You can use a MIDI keyboard or other MIDI device to control Quisk. '\ + 'These Midi devices generate a control number and value for buttons, knobs and jog wheels. '\ + 'You can assign the number to a Quisk button or slider. '\ + 'Operate the control and look here to see the note or control number received by Quisk. '\ + 'Then press a button below to assign the control.' + txt, self.midi_edt, btn = self.AddTextEditHelp(2, "Midi control", '', hlp, border=2, span1=1, span2=1, no_edit=True) + self.NextRow() + hlp = 'Assign the Midi button to a Quisk button.' + ctrl = self.AddPopupMenuHelp(MidiButton, 2, "Assign Midi control to", "Button", hlp) + ctrl.handler = self.OnMidiMenu + self.NextRow() + hlp = \ + 'A "knob" is a Midi control that rotates left and right one turn. It sends a value from 0 to 127. '\ + 'Assign the Midi knob to a Quisk control.' + ctrl = self.AddPopupMenuHelp(MidiKnob, 2, "Assign Midi control to", "Knob", hlp) + ctrl.handler = self.OnMidiMenu + self.NextRow() + hlp = \ + 'A "jog wheel" is a Midi control that rotates around and around. It sends continuous high or low values. '\ + 'Assign the Midi jog wheel to a Quisk control.' + self.midiJogWheel = self.AddPopupMenuHelp(MidiJogWheel, 2, "Assign Midi control to", "Jog Wheel", hlp) + self.midiJogWheel.handler = self.OnMidiMenu + self.NextRow() + hlp = "To delete a row, select it and press this button." + self.AddTextButtonHelp(2, "Delete Midi row", "Delete", self.OnMidiDelete, hlp) + self.NextRow() + if self.section == "Timing and CW": + if col != 1: + col = 1 + self.NextRow() + self.NextRow() + self.AddTextL(1, 'Midi CW key moved to "Keys"', span=4) + if not Keys: + self.AddColSpacer(2, 20) + self.AddColSpacer(5, 20) + self.FitInside() + self.SetScrollRate(1, 1) + def OnButtonChangeFavorites(self, event): + if isinstance(event, (ChoiceCombo, ComboCtrl)): + path = event.GetValue() + else: + direc, fname = os.path.split(getattr(conf, 'favorites_file_in_use')) + dlg = wx.FileDialog(None, "Choose Favorites File", direc, fname, "*.txt", wx.FD_OPEN) + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + self.favorites_combo.SetText(path) + dlg.Destroy() + else: + dlg.Destroy() + return + path = path.strip() + self.favorites_path = path + local_conf.GetRadioDict(self.radio_name)["favorites_file_path"] = path + local_conf.settings_changed = True + def OnMidiDelete(self, event): + ctrl = self.list_ctrl + index = ctrl.GetFirstSelected() + if index < 0: + return + txt_note = ctrl.GetItem(index, 0).GetText() + ctrl.DeleteItem(index) + del local_conf.MidiNoteDict[txt_note] + local_conf.settings_changed = True + if index > 0: + index -= 1 + if ctrl.GetItemCount() > 0: + ctrl.Select(index) + ctrl.EnsureVisible(index) + def MidiAddNote(self): + note = self.midi_edt.GetValue() + self.midi_edt.Clear() + if not note: + return -1 + txt_note = note.split()[0] + if txt_note[2] == '8': + txt_note = "0x9%s" % txt_note[3:] # Change Note Off to store as Note On + int_note = int(txt_note, base=0) + ctrl = self.list_ctrl + index = ctrl.FindItem(-1, txt_note) + if index < 0: # Item is not in the list. Add it. + ctrl.Append((txt_note, '')) + ctrl.SetItemData(ctrl.GetItemCount() - 1, int_note) + ctrl.SortItems(lambda x, y: x-y) + index = ctrl.FindItem(-1, txt_note) + local_conf.MidiNoteDict[txt_note] = '' + local_conf.settings_changed = True + if index >= 0: + ctrl.Select(index) + ctrl.EnsureVisible(index) + return index + def OnMidiMenu(self, ctrl, jog_tag=None): + if jog_tag: # Change the jog tag on the current line + index = self.list_ctrl.GetFirstSelected() + if index < 0: + return + text = self.list_ctrl.GetItem(index, 1).GetText() + if len(text) > 3 and text[-3] == ' ' and text[-2] in "+-" and text[-1] in '0123456789': # Item is a jog wheel item + text = text[0:-2] + jog_tag + else: # There is no jog tag on the current item + return + else: # Assign the Midi message to a Quisk control + index = self.MidiAddNote() + if index < 0: + return + text = ctrl.value + self.list_ctrl.SetItem(index, 1, text) + txt_note = self.list_ctrl.GetItem(index, 0).GetText() + local_conf.MidiNoteDict[txt_note] = text + local_conf.settings_changed = True + def OnMidiListSelected(self, event): + event.Skip() + self.midi_edt.Clear() + index = self.list_ctrl.GetFirstSelected() + text = self.list_ctrl.GetItem(index, 1).GetText() + if len(text) > 3 and text[-3] == ' ' and text[-2] in "+-" and text[-1] in '0123456789': # Item is a jog wheel item + self.midiJogWheel.ChangeSelection(text[-2:]) + def OnNewMidiNote(self, message): + txt_note = "0x%02X%02X %d" % tuple(message) + self.midi_edt.SetValue(txt_note) + if txt_note[2] == '8': # Note off. We already saw the Note On message. + return + txt_note = txt_note.split()[0] + index = self.list_ctrl.FindItem(-1, txt_note) + if index >= 0: + self.list_ctrl.Select(index) + self.list_ctrl.EnsureVisible(index) + else: + index = self.list_ctrl.GetFirstSelected() + if index >= 0: + self.list_ctrl.Select(index, 0) + def OnChangePassword(self, event): + text = local_conf.globals.get('remote_radio_password', '') + dlg = wx.TextEntryDialog(self, "Please enter a password for remote access", + "Password Entry", text) + if dlg.ShowModal() == wx.ID_OK: + local_conf.globals["remote_radio_password"] = dlg.GetValue() + local_conf.settings_changed = True + local_conf.SaveState() + +class RadioHardwareBase(BaseWindow): # The Hardware page in the second-level notebook for each radio + def __init__(self, parent, radio_name): + BaseWindow.__init__(self, parent) + self.radio_name = radio_name + self.num_cols = 8 + self.PMcalDialog = None + #self.MarkCols() + def AlwaysMakeControls(self): + radio_dict = local_conf.GetRadioDict(self.radio_name) + radio_type = radio_dict['hardware_file_type'] + data_names = local_conf.GetReceiverData(radio_type) + self.AddTextL(1, "These are the hardware settings for a radio of type %s" % radio_type, self.num_cols-1) + for name, text, fmt, help_text, values in data_names: + if name == 'hardware_file_name': + self.hware_path = self.GetValue(name, radio_dict) + row = self.row + self.row = 3 + item, self.hware_combo, btn = self.AddTextComboHelp(1, text, self.hware_path, values, help_text, False, span_text=1, span_combo=4) + self.hware_combo.handler = self.OnButtonChangeHardware + item = self.AddPushButtonR(7, "Change..", self.OnButtonChangeHardware, border=0) + elif name == 'widgets_file_name': + self.widgets_path = self.GetValue(name, radio_dict) + row = self.row + self.row = 5 + item, self.widgets_combo, btn = self.AddTextComboHelp(1, text, self.widgets_path, values, help_text, False, span_text=1, span_combo=4) + self.widgets_combo.handler = self.OnButtonChangeWidgets + item = self.AddPushButtonR(7, "Change..", self.OnButtonChangeWidgets, border=0) + self.NextRow(7) + self.AddColSpacer(2, 20) + self.AddColSpacer(5, 20) + self.SetScrollRate(1, 1) + def OnButtonChangeHardware(self, event): + if isinstance(event, (ChoiceCombo, ComboCtrl)): + path = event.GetValue() + else: + direc, fname = os.path.split(self.hware_path) + dlg = wx.FileDialog(None, "Choose Hardware File", direc, fname, "*.py", wx.FD_OPEN) + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + self.hware_combo.SetText(path) + dlg.Destroy() + else: + dlg.Destroy() + return + path = path.strip() + self.hware_path = path + local_conf.GetRadioDict(self.radio_name)["hardware_file_name"] = path + local_conf.settings_changed = True + def OnButtonChangeWidgets(self, event): + if isinstance(event, (ChoiceCombo, ComboCtrl)): + path = event.GetValue() + else: + direc, fname = os.path.split(self.widgets_path) + dlg = wx.FileDialog(None, "Choose Widgets File", direc, fname, "*.py", wx.FD_OPEN) + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + self.widgets_combo.SetText(path) + dlg.Destroy() + else: + dlg.Destroy() + return + path = path.strip() + self.widgets_path = path + local_conf.GetRadioDict(self.radio_name)["widgets_file_name"] = path + local_conf.settings_changed = True + +class RadioHardware(RadioHardwareBase): # The Hardware page in the second-level notebook for each radio + def __init__(self, parent, radio_name): + RadioHardwareBase.__init__(self, parent, radio_name) + self.AlwaysMakeControls() + self.HermesBias0 = None + self.HermesBias1 = None + radio_dict = local_conf.GetRadioDict(radio_name) + radio_type = radio_dict['hardware_file_type'] + data_names = local_conf.GetReceiverData(radio_type) + col = 1 + border = 2 + hermes_board_id = 0 + if radio_type == "Hermes": + try: + hermes_board_id = application.Hardware.hermes_board_id + except: + pass + if radio_name == Settings[1] and hasattr(application.Hardware, "ProgramGateware"): + help_text = "Choose an RBF file and program the Gateware (FPGA software) over Ethernet." + self.AddTextButtonHelp(1, "Gateware Update", "Program from RBF file..", application.Hardware.ProgramGateware, help_text) + col = 1 + self.NextRow(self.row + 2) + for name, text, fmt, help_text, values in data_names: + if name in ('hardware_file_name', 'widgets_file_name'): + pass + elif name[0:4] == platform_ignore: + pass + elif name in ('Hermes_BandDictEnTx', ): + pass + elif 'Hl2_' in name and hermes_board_id != 6: + pass + elif fmt[0:4] in ('dict', 'list'): + pass + else: + if name[0:6] == 'Hware_': # value comes from the hardware file + value = application.Hardware.GetValue(name) + else: + value = self.GetValue(name, radio_dict) + no_edit = "choice" in fmt or fmt == 'boolean' + if name == 'power_meter_calib_name': + values = self.PowerMeterCalChoices() + txt, cb, btn = self.AddTextComboHelp(col, text, value, values, help_text, no_edit, border=border) + cb.handler = self.OnButtonChangePMcal + self.power_meter_cal_choices = cb + else: + txt, cb, btn = self.AddTextComboHelp(col, text, value, values, help_text, no_edit, border=border) + if name[0:6] == 'Hware_': + cb.handler = application.Hardware.SetValue + else: + cb.handler = self.OnChange + cb.quisk_data_name = name + if col == 1: + col = 4 + border = 0 + else: + col = 1 + border = 2 + self.NextRow() + if hermes_board_id == 6: + if col == 4: + self.NextRow() + help_text = ('This controls the bias level for transistors in the final power amplifier. Enter a level from 0 to 255.' + ' These changes are temporary. Press the "Write" button to write the value to the hardware and make it permanent.') + ## Bias is 0 indexed to match schematic + txt, self.HermesBias0, btn = self.AddTextSpinnerHelp(1, "Power amp bias 0", 0, 0, 255, help_text) + txt, self.HermesBias1, btn = self.AddTextSpinnerHelp(4, "Power amp bias 1", 0, 0, 255, help_text) + enbl = radio_dict["hermes_bias_adjust"] == "True" + self.HermesBias0.Enable(enbl) + self.HermesBias1.Enable(enbl) + self.HermesBias0.Bind(wx.EVT_SPINCTRL, self.OnHermesChangeBias0) + self.HermesBias1.Bind(wx.EVT_SPINCTRL, self.OnHermesChangeBias1) + self.HermesWriteBiasButton = self.AddPushButton(7, "Write", self.OnButtonHermesWriteBias, border=0) + self.HermesWriteBiasButton.Enable(enbl) + self.FitInside() + self.SetScrollRate(1, 1) + def OnHermesChangeBias0(self, event): + value = self.HermesBias0.GetValue() + application.Hardware.ChangeBias0(value) + def OnHermesChangeBias1(self, event): + value = self.HermesBias1.GetValue() + application.Hardware.ChangeBias1(value) + def OnButtonHermesWriteBias(self, event): + value0 = self.HermesBias0.GetValue() + value1 = self.HermesBias1.GetValue() + application.Hardware.WriteBias(value0, value1) + def PowerMeterCalChoices(self): + values = list(conf.power_meter_std_calibrations) # known calibration names from the config file + radio_dict = local_conf.GetRadioDict(self.radio_name) + values += list(radio_dict.get('power_meter_local_calibrations', {})) # local calibrations + values.sort() + values.append('New') + return values + def OnButtonChangePMcal(self, ctrl): + value = ctrl.GetValue() + name = ctrl.quisk_data_name + radio_dict = local_conf.GetRadioDict(self.radio_name) + local_cal = radio_dict.get('power_meter_local_calibrations', {}) + if value == 'New': + if not self.PMcalDialog: + self.PMcalDialog = QPowerMeterCalibration(self, list(local_cal)) + else: + setattr(conf, name, value) + radio_dict[name] = value + local_conf.settings_changed = True + application.Hardware.MakePowerCalibration() + def ChangePMcalFinished(self, name, table): + self.PMcalDialog = None + radio_dict = local_conf.GetRadioDict(self.radio_name) + local_cal = radio_dict.get('power_meter_local_calibrations', {}) + if name is None: # Cancel + name = conf.power_meter_calib_name + values = self.PowerMeterCalChoices() + else: + if table is None: # delete name + del local_cal[name] + name = list(conf.power_meter_std_calibrations)[0] # replacement name + else: # new entry + local_cal[name] = table + conf.power_meter_calib_name = name + radio_dict['power_meter_calib_name'] = name + radio_dict['power_meter_local_calibrations'] = local_cal + local_conf.settings_changed = True + values = self.PowerMeterCalChoices() + self.power_meter_cal_choices.SetItems(values) + application.Hardware.MakePowerCalibration() + try: + index = values.index(name) + except: + index = 0 + self.power_meter_cal_choices.SetSelection(index) + +class RadioHardwareSoapySDR(RadioHardwareBase): # The Hardware page in the second-level notebook for the SoapySDR radios + name_text = { +'soapy_gain_mode_rx' : 'Rx gain mode', +'soapy_setAntenna_rx' : 'Rx antenna name', +'soapy_setBandwidth_rx' : 'Rx bandwidth kHz', +'soapy_setSampleRate_rx' : 'Rx sample rate kHz', +'soapy_device' : 'Device name', +'soapy_gain_mode_tx' : 'Tx gain mode', +'soapy_setAntenna_tx' : 'Tx antenna name', +'soapy_setBandwidth_tx' : 'Tx bandwidth kHz', +'soapy_setSampleRate_tx' : 'Tx sample rate kHz', +} + + help_text = { +'soapy_gain_mode_rx' : 'Choose "total" to set the total gain, "detailed" to set multiple gain elements individually, \ +or "automatic" for automatic gain control. The "detailed" or "automatic" may not be available depending on your hardware.', + +'soapy_setAntenna_rx' : 'Choose the antenna to use for receive.', + +'soapy_device' : "SoapySDR provides an interface to various radio hardware. The device name specifies \ +the hardware device. Create a new radio for each hardware you have. Changing the device \ +name requires re-entering all the hardware settings because different hardware has \ +different settings. Also, the hardware device must be turned on when you change the \ +device name so that Quisk can read the available settings.", + +'soapy_gain_mode_tx' : 'Choose "total" to set the total gain, "detailed" to set multiple gain elements individually, \ +or "automatic" for automatic gain control. The "detailed" or "automatic" may not be available depending on your hardware.', + +'soapy_setAntenna_tx' : 'Choose the antenna to use for transmit.', + +} + def __init__(self, parent, radio_name): + RadioHardwareBase.__init__(self, parent, radio_name) + self.no_device = "No device specified" + if soapy: + self.AlwaysMakeControls() + self.MakeSoapyControls() + else: + radio_dict = local_conf.GetRadioDict(self.radio_name) + radio_type = radio_dict['hardware_file_type'] + self.AddTextL(1, "These are the hardware settings for a radio of type %s" % radio_type, self.num_cols-1) + self.NextRow() + self.AddTextL(1, "The shared library from the SoapySDR project is not available.") + self.NextRow() + self.AddTextL(1, "The shared library is not installed or is not compatible (perhaps 32 versus 64 bit versions).") + self.NextRow() + return + #self.MarkCols() + def NextCol(self): + if self.col == 1: + self.col = 4 + self.border = 0 + else: + self.col = 1 + self.border = 2 + self.NextRow() + def MakeSoapyControls(self): + self.gains_rx = [] + self.gains_tx = [] + radio_dict = local_conf.GetRadioDict(self.radio_name) + local_conf.InitSoapyNames(radio_dict) + self.border = 2 + name = 'soapy_device' + device = radio_dict.get(name, self.no_device) + txt, self.edit_soapy_device, btn = self.AddTextEditHelp(1, self.name_text[name], device, self.help_text[name], span1=1, span2=4) + self.AddPushButtonR(7, "Change..", self.OnButtonChangeSoapyDevice, border=0) + self.NextRow() + self.NextRow() + self.col = 1 + if device == self.no_device: + self.FitInside() + return + + if radio_dict.get("soapy_file_version", 0) < soapy_software_version: + text = "Please re-enter the device name. This will read additional parameters from the hardware." + self.AddTextL(self.col, text, span=6) + self.FitInside() + return + + # Receive parameters + name = 'soapy_setSampleRate_rx' + help_text = 'Available sample rates: ' + rates = ['48', '50', '240', '250', '960', '1000'] + for dmin, dmax, dstep in radio_dict.get('soapy_getSampleRateRange_rx', ()): + tmin = FormatKhz(dmin * 1E-3) + if tmin not in rates: + rates.append(tmin) + if abs(dmin - dmax) < 0.5: + help_text = help_text + '%s; ' % tmin + elif dstep < 0.5: + help_text = help_text + '%s to %s; ' % (tmin, FormatKhz(dmax * 1E-3)) + else: + help_text = help_text + '%s to %s by %s; ' % (tmin, FormatKhz(dmax * 1E-3), FormatKhz(dstep * 1E-3)) + help_text = help_text[0:-2] + '.' + if rates: + rates.sort(key=SortKey) + rate = radio_dict.get(name, '') + txt, cb, btn = self.AddTextComboHelp(self.col, self.name_text[name], rate, rates, help_text, False, border=self.border) + cb.handler = self.OnChange + cb.quisk_data_name = name + self.NextCol() + + len_gain_names = len(radio_dict.get('soapy_listGainsValues_rx', ())) + name = 'soapy_gain_mode_rx' + gain_mode = radio_dict[name] + choices = ['total'] + if len_gain_names >= 3: + choices.append('detailed') + if radio_dict.get('soapy_hasGainMode_rx', 0): + choices.append('automatic') + if gain_mode not in choices: + gain_mode = radio_dict[name] = 'total' + local_conf.settings_changed = True + txt, cb, btn = self.AddTextComboHelp(self.col, self.name_text[name], gain_mode, choices, self.help_text[name], True, border=self.border) + cb.handler = self.OnChange + cb.quisk_data_name = name + self.NextCol() + + name = 'soapy_gain_values_rx' + values = radio_dict[name] + for name2, dmin, dmax, dstep in radio_dict.get('soapy_listGainsValues_rx', ()): + if dstep < 1E-4: + dstep = 0.5 + text = "Rx gain %s" % name2 + help_text = 'Rf gain min %f, max %f, step %f' % (dmin, dmax, dstep) + value = values.get(name2, '0') + value = float(value) + txt, spn, btn = self.AddTextDblSpinnerHelp(self.col, text, value, dmin, dmax, dstep, help_text, border=self.border) + spn.quisk_data_name = name + spn.quisk_data_name2 = name2 + spn.Bind(wx.EVT_SPINCTRLDOUBLE, self.OnGain) + self.gains_rx.append(spn) + self.NextCol() + if len_gain_names < 3: # for 1 or 2 names, just show total gain item + break + self.FixGainButtons('soapy_gain_mode_rx') + + name = 'soapy_setAntenna_rx' + antenna = radio_dict[name] + antennas = radio_dict.get('soapy_listAntennas_rx', ()) + if antenna not in antennas: + if antennas: + antenna = antennas[0] + else: + antenna = '' + radio_dict[name] = antenna + local_conf.settings_changed = True + if antennas: + txt, cb, btn = self.AddTextComboHelp(self.col, self.name_text[name], antenna, antennas, self.help_text[name], True, border=self.border) + cb.handler = self.OnChange + cb.quisk_data_name = name + self.NextCol() + + name = 'soapy_setBandwidth_rx' + help_text = 'Available bandwidth: ' + bandwidths = [] + for dmin, dmax, dstep in radio_dict.get('soapy_getBandwidthRange_rx', ()): + tmin = FormatKhz(dmin * 1E-3) + bandwidths.append(tmin) + if abs(dmin - dmax) < 0.5: + help_text = help_text + '%s; ' % tmin + elif dstep < 0.5: + help_text = help_text + '%s to %s; ' % (tmin, FormatKhz(dmax * 1E-3)) + else: + help_text = help_text + '%s to %s by %s; ' % (tmin, FormatKhz(dmax * 1E-3), FormatKhz(dstep * 1E-3)) + help_text = help_text[0:-2] + '.' + if bandwidths: + bandwidth = radio_dict.get(name, '') + txt, cb, btn = self.AddTextComboHelp(self.col, self.name_text[name], bandwidth, bandwidths, help_text, False, border=self.border) + cb.handler = self.OnChange + cb.quisk_data_name = name + self.NextCol() + + # Transmit parameters + if self.col != 1: + self.NextCol() + name = 'soapy_enable_tx' + enable = radio_dict.get(name, 'Disable') + help_text = 'This will enable or disable the transmit function. If changed, you must restart Quisk.' + txt, cb, btn = self.AddTextComboHelp(self.col, 'Tx enable', enable, ['Enable', 'Disable'], help_text, True, border=self.border) + cb.handler = self.OnChange + cb.quisk_data_name = name + self.NextCol() + + name = 'soapy_setSampleRate_tx' + help_text = 'Available sample rates: ' + rates = [] + for dmin, dmax, dstep in radio_dict.get('soapy_getSampleRateRange_tx', ()): + tmin = FormatKhz(dmin * 1E-3) + rates.append(tmin) + if abs(dmin - dmax) < 0.5: + help_text = help_text + '%s; ' % tmin + elif dstep < 0.5: + help_text = help_text + '%s to %s; ' % (tmin, FormatKhz(dmax * 1E-3)) + else: + help_text = help_text + '%s to %s by %s; ' % (tmin, FormatKhz(dmax * 1E-3), FormatKhz(dstep * 1E-3)) + help_text = help_text[0:-2] + '.' + if rates: + rate = radio_dict.get(name, '') + rates = ('48', '50', '96', '100', '192') + txt, cb, btn = self.AddTextComboHelp(self.col, self.name_text[name], rate, rates, help_text, True, border=self.border) + cb.handler = self.OnChange + cb.quisk_data_name = name + self.NextCol() + + len_gain_names = len(radio_dict.get('soapy_listGainsValues_tx', ())) + name = 'soapy_gain_mode_tx' + gain_mode = radio_dict[name] + choices = ['total'] + if len_gain_names >= 3: + choices.append('detailed') + if radio_dict.get('soapy_hasGainMode_tx', 0): + choices.append('automatic') + if gain_mode not in choices: + gain_mode = radio_dict[name] = 'total' + local_conf.settings_changed = True + txt, cb, btn = self.AddTextComboHelp(self.col, self.name_text[name], gain_mode, choices, self.help_text[name], True, border=self.border) + cb.handler = self.OnChange + cb.quisk_data_name = name + self.NextCol() + + name = 'soapy_gain_values_tx' + values = radio_dict[name] + for name2, dmin, dmax, dstep in radio_dict.get('soapy_listGainsValues_tx', ()): + if dstep < 1E-4: + dstep = 0.5 + text = "Tx gain %s" % name2 + help_text = 'Rf gain min %f, max %f, step %f' % (dmin, dmax, dstep) + value = values.get(name2, '0') + value = float(value) + txt, spn, btn = self.AddTextDblSpinnerHelp(self.col, text, value, dmin, dmax, dstep, help_text, border=self.border) + spn.quisk_data_name = name + spn.quisk_data_name2 = name2 + spn.Bind(wx.EVT_SPINCTRLDOUBLE, self.OnGain) + self.gains_tx.append(spn) + self.NextCol() + if len_gain_names < 3: # for 1 or 2 names, just show total gain item + break + self.FixGainButtons('soapy_gain_mode_tx') + + name = 'soapy_setAntenna_tx' + antenna = radio_dict[name] + antennas = radio_dict.get('soapy_listAntennas_tx', ()) + if antenna not in antennas: + if antennas: + antenna = antennas[0] + else: + antenna = '' + radio_dict[name] = antenna + local_conf.settings_changed = True + if antennas: + txt, cb, btn = self.AddTextComboHelp(self.col, self.name_text[name], antenna, antennas, self.help_text[name], True, border=self.border) + cb.handler = self.OnChange + cb.quisk_data_name = name + self.NextCol() + + name = 'soapy_setBandwidth_tx' + help_text = 'Available bandwidths: ' + bandwidths = [] + for dmin, dmax, dstep in radio_dict.get('soapy_getBandwidthRange_tx', ()): + tmin = FormatKhz(dmin * 1E-3) + bandwidths.append(tmin) + if abs(dmin - dmax) < 0.5: + help_text = help_text + '%s; ' % tmin + elif dstep < 0.5: + help_text = help_text + '%s to %s; ' % (tmin, FormatKhz(dmax * 1E-3)) + else: + help_text = help_text + '%s to %s by %s; ' % (tmin, FormatKhz(dmax * 1E-3), FormatKhz(dstep * 1E-3)) + help_text = help_text[0:-2] + '.' + if bandwidths: + bandwidth = radio_dict.get(name, '') + txt, cb, btn = self.AddTextComboHelp(self.col, self.name_text[name], bandwidth, bandwidths, help_text, False, border=self.border) + cb.handler = self.OnChange + cb.quisk_data_name = name + self.NextCol() + + self.FitInside() + def FixGainButtons(self, name): + radio_dict = local_conf.GetRadioDict(self.radio_name) + gain_mode = radio_dict[name] + if name[-3:] == '_tx': + controls = self.gains_tx + else: + controls = self.gains_rx + for i in range(len(controls)): + ctrl = controls[i] + if gain_mode == "automatic": + ctrl.Enable(False) + elif gain_mode == "total": + if i == 0: + ctrl.Enable(True) + else: + ctrl.Enable(False) + else: # gain_mode is "detailed" + if i == 0: + ctrl.Enable(False) + else: + ctrl.Enable(True) + def OnButtonChangeSoapyDevice(self, event): + if not soapy: + txt = "Soapy shared library (DLL) is not available." + msg = wx.MessageDialog(None, txt, 'SoapySDR Error', wx.OK|wx.ICON_ERROR) + msg.ShowModal() + msg.Destroy() + return + try: + choices = self.GetSoapyDevices() + except: + #traceback.print_exc() + choices = [] + if not choices: + choices = ['No devices were found.'] + device = self.edit_soapy_device.GetValue() + width = application.main_frame.GetSize().width + width = width * 50 // 100 + parent = self.edit_soapy_device.GetParent() + dlg = ListEditDialog(parent, "Change Soapy Device", device, choices, width) + ok = dlg.ShowModal() + if ok != wx.ID_OK: + dlg.Destroy() + return + device = dlg.GetValue() + dlg.Destroy() + if device == self.no_device: + return + if Settings[1] == self.radio_name: + txt = "Changing the active radio requires a shutdown and restart. Proceed?" + msg = wx.MessageDialog(None, txt, 'SoapySDR Change to Active Radio', wx.OK|wx.CANCEL|wx.ICON_INFORMATION) + ok = msg.ShowModal() + msg.Destroy() + if ok == wx.ID_OK: + soapy.close_device(1) + else: + return + txt = soapy.open_device(device, 0, 0) + if txt[0:8] == 'Capture ': + radio_dict = local_conf.GetRadioDict(self.radio_name) + radio_dict['soapy_device'] = device + radio_dict['soapy_file_version'] = soapy_software_version + self.edit_soapy_device.ChangeValue(device) + # Record the new SoapySDR parameters for the new device. Do not change the old data values yet. + for name in ('soapy_listAntennas_rx', 'soapy_hasGainMode_rx', 'soapy_listGainsValues_rx', + 'soapy_listAntennas_tx', 'soapy_hasGainMode_tx', 'soapy_listGainsValues_tx', + 'soapy_getFullDuplex_rx', 'soapy_getSampleRateRange_rx', 'soapy_getSampleRateRange_tx', + 'soapy_getBandwidthRange_rx', 'soapy_getBandwidthRange_tx', + ): + radio_dict[name] = soapy.get_parameter(name, 0) + soapy.close_device(0) + local_conf.settings_changed = True + # Clear our sizer and re-create all the controls + self.gbs.Clear(True) + self.gbs.Add((self.charx, self.charx), (0, 0)) + self.row = 1 + RadioHardwareBase.AlwaysMakeControls(self) + self.MakeSoapyControls() + txt = "Please check the settings for the new hardware device." + msg = wx.MessageDialog(None, txt, 'SoapySDR Change to Radio', wx.OK|wx.ICON_INFORMATION) + msg.ShowModal() + msg.Destroy() + else: + msg = wx.MessageDialog(None, txt, 'SoapySDR Device Error', wx.OK|wx.ICON_ERROR) + msg.ShowModal() + msg.Destroy() + def GetSoapyDevices(self): + choices = [] + for dct in soapy.get_device_list(): + text = '' + try: + driver = dct["driver"] + except: + pass + else: + text = 'driver=%s' % driver + try: + label = dct["label"] + except: + pass + else: + text = text + ', label=%s' % label + choices.append(text) + return choices + def OnChange(self, ctrl): + name = ctrl.quisk_data_name + value = ctrl.GetValue() + radio_dict = local_conf.GetRadioDict(self.radio_name) + radio_dict[name] = value + local_conf.settings_changed = True + # Immediate changes + if name in ('soapy_gain_mode_rx', 'soapy_gain_mode_tx'): + self.FixGainButtons(name) + if soapy and self.radio_name == Settings[1]: # changed for current radio + application.Hardware.ImmediateChange(name, value) + def OnGain(self, event): + radio_dict = local_conf.GetRadioDict(self.radio_name) + obj = event.GetEventObject() + value = obj.GetValue() + name = obj.quisk_data_name + radio_dict[name][obj.quisk_data_name2] = value + local_conf.settings_changed = True + if soapy and self.radio_name == Settings[1]: # changed for current radio + application.Hardware.ChangeGain(name[-3:]) + +class RadioSound(BaseWindow): # The Sound page in the second-level notebook for each radio + """Configure the available sound devices.""" + sound_names = ( # same order as label_help + ('playback_rate', '', '', '', 'name_of_sound_play'), + ('mic_sample_rate', 'mic_channel_I', 'mic_channel_Q', '', 'microphone_name'), + ('sample_rate', 'channel_i', 'channel_q', 'channel_delay', 'name_of_sound_capt'), + ('mic_playback_rate', 'mic_play_chan_I', 'mic_play_chan_Q', 'tx_channel_delay', 'name_of_mic_play'), + ('', '', '', '', 'sample_playback_name'), + ('', '', '', '', 'digital_input_name'), + ('', '', '', '', 'digital_output_name'), + ('', '', '', '', 'digital_rx1_name'), + ('', '', '', '', 'digital_rx2_name'), + ('', '', '', '', 'digital_rx3_name'), + ('', '', '', '', 'digital_rx4_name'), + ('', '', '', '', 'digital_rx5_name'), + ('', '', '', '', 'digital_rx6_name'), + ('', '', '', '', 'digital_rx7_name'), + ('', '', '', '', 'digital_rx8_name'), + ('', '', '', '', 'digital_rx9_name'), + ) + 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."), + (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."), + (1, "Digital Rx4 Output", "This is the sub-receiver 4 audio going to a digital mode program."), + (1, "Digital Rx5 Output", "This is the sub-receiver 5 audio going to a digital mode program."), + (1, "Digital Rx6 Output", "This is the sub-receiver 6 audio going to a digital mode program."), + (1, "Digital Rx7 Output", "This is the sub-receiver 7 audio going to a digital mode program."), + (1, "Digital Rx8 Output", "This is the sub-receiver 8 audio going to a digital mode program."), + (1, "Digital Rx9 Output", "This is the sub-receiver 9 audio going to a digital mode program."), + ) + def __init__(self, parent, radio_name): + BaseWindow.__init__(self, parent) + self.radio_name = radio_name + self.MakeControls() + def MakeControls(self): + self.radio_dict = local_conf.GetRadioDict(self.radio_name) + self.num_cols = 8 + for name, text, fmt, help_text, values in local_conf.GetSectionData('Sound'): + if name == 'digital_output_level': + value = self.GetValue(name, self.radio_dict) + no_edit = "choice" in fmt or fmt == 'boolean' + txt, cb, btn = self.AddTextComboHelp(1, text, value, values, help_text, no_edit) + cb.handler = self.OnChange + cb.quisk_data_name = name + break + self.NextRow() + # Add the grid for the sound settings + sizer = wx.GridBagSizer(2, 2) + sizer.SetEmptyCellSize((self.charx, self.charx)) + self.gbs.Add(sizer, (self.row, 0), span=(1, self.num_cols)) + gbs = self.gbs + self.gbs = sizer + self.row = 1 + help_chan = "For the usual stereo device enter 0 for the I channel and 1 for the Q channel. Reversing the 0 and 1 switches the left\ + and right channels. For a monophonic device, enter 0 for both I and Q. If you have more channels enter the channel number. Channels\ + are numbered from zero, so a four channel device has channels 0, 1, 2 and 3." + self.AddTextC(1, "Stream") + self.AddTextCHelp(2, "Rate", +"This is the sample rate for the device in Hertz." "Some devices have fixed rates that can not be changed.") + self.AddTextCHelp(3, "Ch I", "This is the in-phase channel for devices with I/Q data. " + help_chan) + self.AddTextCHelp(4, "Ch Q", "This is the quadrature channel for devices with I/Q data. " + help_chan) + self.AddTextCHelp(5, "Delay", "Some older devices have a one sample channel delay between channels. " +"This must be corrected for devices with I/Q data. Enter the channel number to delay; either the I or Q channel number. " +"For no delay, leave this blank.") + self.AddTextCHelp(6, "Sound Device", "This is the name of the sound device. For Windows, this is the Wasapi name. " +"For Linux you can use the Alsa device, the PortAudio device or the PulseAudio device. " +"The Alsa device are recommended because they have lower latency. See the documentation for more information.") + self.NextRow() + choices = (("48000", "96000", "192000"), ("0", "1"), ("0", "1"), (" ", "0", "1")) + r = 0 + if "SoftRock" in self.radio_dict['hardware_file_type']: # Samples come from sound card + softrock = True + else: + softrock = False + last_row = 8 + for is_output, label, helptxt in self.label_help: + self.AddTextLHelp(1, label, helptxt) + # Add col 0 + value = self.ItemValue(r, 0) + if value is None: + value = '' + data_name = self.sound_names[r][0] + if r == 0: + cb = self.AddComboCtrl(2, value, choices=("48000", "96000", "192000"), right=True) + if r == 1: + cb = self.AddComboCtrl(2, value, choices=("48000", "8000"), right=True, no_edit=True) + if softrock: + if r == 2: + cb = self.AddComboCtrl(2, value, choices=("48000", "96000", "192000"), right=True) + if r == 3: + cb = self.AddComboCtrl(2, value, choices=("48000", "96000", "192000"), right=True) + else: + if r == 2: + cb = self.AddComboCtrl(2, '', choices=("",), right=True) + cb.Enable(False) + if r == 3: + cb = self.AddComboCtrl(2, '', choices=("",), right=True) + cb.Enable(False) + if r >= 4: + cb = self.AddComboCtrl(2, "48000", choices=("48000",), right=True, no_edit=True) + cb.Enable(False) + cb.handler = self.OnChange + cb.quisk_data_name = data_name + # Add col 1, 2, 3 + for col in range(1, 4): + value = self.ItemValue(r, col) + data_name = self.sound_names[r][col] + if value is None: + cb = self.AddComboCtrl(col + 2, ' ', choices=[], right=True) + cb.Enable(False) + else: + cb = self.AddComboCtrl(col + 2, value, choices=choices[col], right=True) + cb.handler = self.OnChange + cb.quisk_data_name = self.sound_names[r][col] + # Add col 4 + if not softrock and r in (2, 3): + cb = self.AddComboCtrl(6, self.ItemValue(r, 4), choices=['']) + elif is_output: + if label == "Digital Rx0 Output" and sys.platform != 'win32': + play_names = application.dev_play[:] + play_names += ["pulse: Use name QuiskDigitalOutput.monitor"] + else: + play_names = application.dev_play + cb = self.AddComboCtrl(6, self.ItemValue(r, 4), choices=play_names) + else: + if label == "Digital Tx0 Input" and sys.platform != 'win32': + capt_names = application.dev_capt[:] + capt_names += ["pulse: Use name QuiskDigitalInput"] + else: + capt_names = application.dev_capt + cb = self.AddComboCtrl(6, self.ItemValue(r, 4), choices=capt_names) + cb.handler = self.OnChange + cb.quisk_data_name = platform_accept + self.sound_names[r][4] + self.NextRow() + r += 1 + if r >= last_row: + break + self.gbs = gbs + self.FitInside() + self.SetScrollRate(1, 1) + def ItemValue(self, row, col): + data_name = self.sound_names[row][col] + if col == 4: # Device names + data_name = platform_accept + data_name + value = self.GetValue(data_name, self.radio_dict) + return value + elif data_name: + value = self.GetValue(data_name, self.radio_dict) + if col == 3: # Delay + if value == "-1": + value = '' + return value + return None + def OnChange(self, ctrl): + data_name = ctrl.quisk_data_name + value = ctrl.GetValue() + #index = ctrl.ctrl.lbox.GetSelections()[0] + #print (value, index, ctrl.choices[index]) + if data_name in ('channel_delay', 'tx_channel_delay'): + value = value.strip() + if not value: + value = "-1" + self.OnChange2(ctrl, value) + +class RadioBands(BaseWindow): # The Bands page in the second-level notebook for each radio + def __init__(self, parent, radio_name): + BaseWindow.__init__(self, parent) + self.radio_name = radio_name + self.parent = parent + self.MakeControls() + def MakeControls(self): + radio_dict = local_conf.GetRadioDict(self.radio_name) + radio_type = radio_dict['hardware_file_type'] + self.num_cols = 8 + #self.MarkCols() + self.NextRow() + self.AddTextCHelp(1, "Bands", +"This is a list of the bands that Quisk understands. A check mark means that the band button is displayed. A maximum of " +"14 bands may be displayed.") + self.AddTextCHelp(2, " Start MHz", +"This is the start of the band in megahertz.") + self.AddTextCHelp(3, " End MHz", +"This is the end of the band in megahertz.") + heading_row = self.row + self.NextRow() + band_labels = radio_dict['bandLabels'][:] + for i in range(len(band_labels)): + if isinstance(band_labels[i], (list, tuple)): + band_labels[i] = band_labels[i][0] + band_edge = radio_dict['BandEdge'] + # band_list is a list of all known bands + band_list = conf.BandList[:] + band_list.append('Time') + if local_conf.ReceiverHasName(radio_type, 'tx_level'): # Must show and edit tx_level + if 'tx_level' in radio_dict: + tx_level = radio_dict['tx_level'] + else: + tx_level = {} + radio_dict['tx_level'] = {} + local_conf.settings_changed = True + else: + tx_level = None + try: + transverter_offset = radio_dict['bandTransverterOffset'] + except: + transverter_offset = {} + radio_dict['bandTransverterOffset'] = transverter_offset # Make sure the dictionary is in radio_dict + try: + hiqsdr_bus = radio_dict['HiQSDR_BandDict'] + except: + hiqsdr_bus = None + try: + hermes_bus = radio_dict['Hermes_BandDict'] + except: + hermes_bus = None + self.band_checks = [] + # Add the Audio band. This must be first to allow for column labels. + cb = self.AddCheckBox(1, 'Audio', self.OnChangeBands) + self.band_checks.append(cb) + if 'Audio' in band_labels: + cb.SetValue(True) + self.NextRow() + start_row = self.row + # Add check box, start, end + for band in band_list: + cb = self.AddCheckBox(1, band, self.OnChangeBands) + self.band_checks.append(cb) + if band in band_labels: + cb.SetValue(True) + try: + start, end = band_edge[band] + start = "%.3f" % (start * 1E-6) + end = "%.3f" % (end * 1E-6) + except: + try: + start, end = local_conf.originalBandEdge[band] + start = "%.3f" % (start * 1E-6) + end = "%.3f" % (end * 1E-6) + except: + start = '' + end = '' + cb = self.AddComboCtrl(2, start, choices=(start, ), right=True) + cb.handler = self.OnChangeBandStart + cb.quisk_band = band + cb = self.AddComboCtrl(3, end, choices=(end, ), right=True) + cb.handler = self.OnChangeBandEnd + cb.quisk_band = band + self.NextRow() + col = 3 + # Add tx_level + if tx_level is not None: + col += 1 + self.row = heading_row + text, help_text = local_conf.GetReceiverItemTH(radio_type, 'tx_level') + self.AddTextCHelp(col, " %s" % text, help_text) + self.row = start_row + for band in band_list: + try: + level = tx_level[band] + level = str(level) + except: + try: + level = tx_level[None] + tx_level[band] = level # Fill in tx_level for each band + level = str(level) + except: + tx_level[band] = 70 + level = '70' + cb = self.AddComboCtrl(col, level, choices=(level, ), right=True) + cb.handler = self.OnChangeDict + cb.quisk_data_name = 'tx_level' + cb.quisk_band = band + self.NextRow() + # Add transverter offset + if isinstance(transverter_offset, dict): + col += 1 + self.row = heading_row + self.AddTextCHelp(col, " Transverter Offset", +"If you use a transverter, you need to tune your hardware to a frequency lower than\ + the frequency displayed by Quisk. For example, if you have a 2 meter transverter,\ + you may need to tune your hardware from 28 to 30 MHz to receive 144 to 146 MHz.\ + Enter the transverter offset in Hertz. For this to work, your\ + hardware must support it. Currently, the HiQSDR, SDR-IQ and SoftRock are supported.") + self.row = start_row + for band in band_list: + try: + offset = transverter_offset[band] + except: + offset = '' + else: + offset = str(offset) + cb = self.AddComboCtrl(col, offset, choices=(offset, ), right=True) + cb.handler = self.OnChangeDictBlank + cb.quisk_data_name = 'bandTransverterOffset' + cb.quisk_band = band + self.NextRow() + # Add hiqsdr_bus + if hiqsdr_bus is not None: + bus_text = 'The IO bus is used to select filters for each band. Refer to the documentation for your filter board to see what number to enter.' + col += 1 + self.row = heading_row + self.AddTextCHelp(col, " IO Bus", bus_text) + self.row = start_row + for band in band_list: + try: + bus = hiqsdr_bus[band] + except: + bus = '' + bus_choice = ('11', ) + else: + bus = str(bus) + bus_choice = (bus, ) + cb = self.AddComboCtrl(col, bus, bus_choice, right=True) + cb.handler = self.OnChangeDict + cb.quisk_data_name = 'HiQSDR_BandDict' + cb.quisk_band = band + self.NextRow() + # Add hermes_bus + if hermes_bus is not None: + rx_bus_text = 'The IO bus is used to select filters for each band. Check the bit for a "1", and uncheck the bit for a "0".\ + Bits are shown in binary number order. For example, decimal 9 is 0b1001, so check bits 3 and 0.\ + Changes are immediate (no need to restart).\ + Refer to the documentation for your filter board to see which bits to set. For the Hermes Lite 2 N2ADR filter set:\n\n\ +160: 0000001\n\ +80: 1000010\n\ +60: 1000100\n\ +40: 1000100\n\ +30: 1001000\n\ +20: 1001000\n\ +17: 1010000\n\ +15: 1010000\n\ +12: 1100000\n\ +10: 1100000\n\ +\n\ +If multiple receivers are in use, the Rx filter will be that of the highest frequency band.\ + The Rx bits are sent as the "Alex" filters (Protocol 1, address 9) and are sent to the J16 interface.' + tx_bus_text = 'The Rx bits are used for both receive and transmit unless the "Enable" box is checked.\ + Then you can specify different filters for Rx and Tx.\n\n\ +The Tx bits are sent as the "Alex" filters and are sent to the J16 interface.' + col += 1 + self.row = heading_row + self.AddTextCHelp(col, " Rx IO Bus", rx_bus_text) + self.AddTextCHelp(col + 1, " Tx IO Bus", tx_bus_text) + self.row += 1 + self.AddTextC(col, "6...Bits...0") + btn = self.AddCheckBox(col + 1, " Enable", self.ChangeIOTxEnable, flag=wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL) + value = self.GetValue("Hermes_BandDictEnTx", radio_dict) + value = value == 'True' + btn.SetValue(value) + self.row = start_row + try: + hermes_tx_bus = radio_dict['Hermes_BandDictTx'] + except: + hermes_tx_bus = {} + for band in band_list: + try: + bus = int(hermes_bus[band]) + except: + bus = 0 + self.AddBitField(col, 7, 'Hermes_BandDict', band, bus, self.ChangeIO) + try: + bus = int(hermes_tx_bus[band]) + except: + bus = 0 + self.AddBitField(col + 1, 7, 'Hermes_BandDictTx', band, bus, self.ChangeIO) + self.NextRow() + self.FitInside() + self.SetScrollRate(1, 1) + def SortCmp(self, item1): + # Numerical conversion of band name to megahertz + try: + if item1[-2:] == 'cm': + item1 = float(item1[0:-2]) * .01 + item1 = 300.0 / item1 + elif item1[-1] == 'k': + item1 = float(item1[0:-1]) * .001 + else: + item1 = float(item1) + item1 = 300.0 / item1 + except: + item1 = 50000.0 + return item1 + def OnChangeBands(self, ctrl): + band_list = [] + count = 0 + for cb in self.band_checks: + if cb.IsChecked(): + band = cb.GetLabel() + count += 1 + if band == '60' and len(conf.freq60) > 1: + band_list.append(('60', ) * len(conf.freq60)) + elif band == 'Time' and len(conf.bandTime) > 1: + band_list.append(('Time', ) * len(conf.bandTime)) + else: + band_list.append(band) + if count > 14: + dlg = wx.MessageDialog(None, + "There are more than the maximum of 14 bands checked. Please remove some checks.", + 'List of Bands', wx.OK|wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + else: + radio_dict = local_conf.GetRadioDict(self.radio_name) + radio_dict['bandLabels'] = band_list + local_conf.settings_changed = True + def OnChangeBandStart(self, ctrl): + radio_dict = local_conf.GetRadioDict(self.radio_name) + band_edge = radio_dict['BandEdge'] + band = ctrl.quisk_band + start, end = band_edge.get(band, (0, 9999)) + value = ctrl.GetValue() + if self.FormatOK(value, 'numb'): + start = int(float(value) * 1E6 + 0.1) + band_edge[band] = (start, end) + local_conf.settings_changed = True + def OnChangeBandEnd(self, ctrl): + radio_dict = local_conf.GetRadioDict(self.radio_name) + band_edge = radio_dict['BandEdge'] + band = ctrl.quisk_band + start, end = band_edge.get(band, (0, 9999)) + value = ctrl.GetValue() + if self.FormatOK(value, 'numb'): + end = int(float(value) * 1E6 + 0.1) + band_edge[band] = (start, end) + local_conf.settings_changed = True + def OnChangeDict(self, ctrl): + radio_dict = local_conf.GetRadioDict(self.radio_name) + dct = radio_dict[ctrl.quisk_data_name] + band = ctrl.quisk_band + value = ctrl.GetValue() + if self.FormatOK(value, 'inte'): + value = int(value) + dct[band] = value + local_conf.settings_changed = True + if ctrl.quisk_data_name == 'tx_level' and hasattr(application.Hardware, "SetTxLevel"): + application.Hardware.SetTxLevel() + def OnChangeDictBlank(self, ctrl): + radio_dict = local_conf.GetRadioDict(self.radio_name) + dct = radio_dict[ctrl.quisk_data_name] + band = ctrl.quisk_band + value = ctrl.GetValue() + value = value.strip() + if not value: + if band in dct: + del dct[band] + local_conf.settings_changed = True + elif self.FormatOK(value, 'inte'): + value = int(value) + dct[band] = value + local_conf.settings_changed = True + def ChangeIO(self, control): + radio_dict = local_conf.GetRadioDict(self.radio_name) + dct = radio_dict[control.quisk_data_name] + band = control.quisk_band + dct[band] = control.value + local_conf.settings_changed = True + if hasattr(application.Hardware, "ChangeBandFilters"): + application.Hardware.ChangeBandFilters() + def ChangeIOTxEnable(self, event): + name = "Hermes_BandDictEnTx" + radio_dict = local_conf.GetRadioDict(self.radio_name) + if event.IsChecked(): + radio_dict[name] = "True" + setattr(conf, name, True) + else: + radio_dict[name] = "False" + setattr(conf, name, False) + local_conf.settings_changed = True + if hasattr(application.Hardware, "ImmediateChange"): + application.Hardware.ImmediateChange(name) + +class xxRadioFilters(BaseWindow): # The Filters page in the second-level notebook for each radio + def __init__(self, parent, radio_name): + BaseWindow.__init__(self, parent) + self.radio_name = radio_name + self.MakeControls() + def MakeControls(self): + radio_dict = local_conf.GetRadioDict(self.radio_name) + self.num_cols = 8 + self.NextRow() + bus_text = 'These high-pass and low-pass filters are only available for radios that support the Hermes protocol.\ + Enter a frequency range and the control bits for that range. Leave the frequencies blank for unused ranges.\ + Place whole bands within the frequency ranges because filters are only changed when changing bands.\ + Check the bit for a "1", and uncheck the bit for a "0".\ + Bits are shown in binary number order. For example, decimal 9 is 0b1001, so check bits 3 and 0.\ + Changes are immediate (no need to restart).\ + Refer to the documentation for your filter board to see which bits to set.\ + The Rx bits are used for both receive and transmit, unless the "Tx Enable" box is checked.\ + Then you can specify different filters for Rx and Tx.\ + If multiple receivers are in use, the filters will accommodate the highest and lowest frequencies of all receivers.' + self.AddTextCHelp(1, 'Hermes Protocol: Alex High and Low Pass Filters', bus_text, span=self.num_cols) + self.NextRow() + self.AddTextC(1, 'Start MHz') + self.AddTextC(2, 'End MHz') + self.AddTextC(3, "Alex HPF Rx") + btn = self.AddCheckBox(4, "Alex HPF Tx", self.ChangeEnable, flag=wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL) + btn.quisk_data_name = "AlexHPF_TxEn" + value = self.GetValue("AlexHPF_TxEn", radio_dict) + value = value == 'True' + btn.SetValue(value) + self.AddTextC(5, 'Start MHz') + self.AddTextC(6, 'End MHz') + self.AddTextC(7, "Alex LPF Rx") + btn = self.AddCheckBox(8, "Alex LPF Tx", self.ChangeEnable, flag=wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL) + btn.quisk_data_name = "AlexLPF_TxEn" + value = self.GetValue("AlexLPF_TxEn", radio_dict) + value = value == 'True' + btn.SetValue(value) + self.NextRow() + hp_filters = self.GetValue("AlexHPF", radio_dict) + lp_filters = self.GetValue("AlexLPF", radio_dict) + row = self.row + for index in range(len(hp_filters)): + f1, f2, rx, tx = hp_filters[index] # f1 and f2 are strings; rx and tx are integers + cb = self.AddTextCtrl(1, f1, self.OnChangeFreq) + cb.quisk_data_name = "AlexHPF" + cb.index = (index, 0) + cb = self.AddTextCtrl(2, f2, self.OnChangeFreq) + cb.quisk_data_name = "AlexHPF" + cb.index = (index, 1) + bf = self.AddBitField(3, 8, 'AlexHPF', None, rx, self.ChangeBits) + bf.index = (index, 2) + bf = self.AddBitField(4, 8, 'AlexHPF', None, tx, self.ChangeBits) + bf.index = (index, 3) + self.NextRow() + index += 1 + self.row = row + for index in range(len(lp_filters)): + f1, f2, rx, tx = lp_filters[index] # f1 and f2 are strings; rx and tx are integers + cb = self.AddTextCtrl(5, f1, self.OnChangeFreq) + cb.quisk_data_name = "AlexLPF" + cb.index = (index, 0) + cb = self.AddTextCtrl(6, f2, self.OnChangeFreq) + cb.quisk_data_name = "AlexLPF" + cb.index = (index, 1) + bf = self.AddBitField(7, 8, 'AlexLPF', None, rx, self.ChangeBits) + bf.index = (index, 2) + bf = self.AddBitField(8, 8, 'AlexLPF', None, tx, self.ChangeBits) + bf.index = (index, 3) + self.NextRow() + index += 1 + self.FitInside() + self.SetScrollRate(1, 1) + def OnChangeFreq(self, event): + freq = event.GetString() + radio_dict = local_conf.GetRadioDict(self.radio_name) + ctrl = event.GetEventObject() + name = ctrl.quisk_data_name + filters = self.GetValue(name, radio_dict) + filters[ctrl.index[0]][ctrl.index[1]] = freq + setattr(conf, name, filters) + radio_dict[name] = filters + local_conf.settings_changed = True + def ChangeBits(self, control): + radio_dict = local_conf.GetRadioDict(self.radio_name) + name = control.quisk_data_name + filters = self.GetValue(name, radio_dict) + filters[control.index[0]][control.index[1]] = control.value + setattr(conf, name, filters) + radio_dict[name] = filters + local_conf.settings_changed = True + def ChangeEnable(self, event): + btn = event.GetEventObject() + name = btn.quisk_data_name + radio_dict = local_conf.GetRadioDict(self.radio_name) + if event.IsChecked(): + radio_dict[name] = "True" + setattr(conf, name, True) + else: + radio_dict[name] = "False" + setattr(conf, name, False) + local_conf.settings_changed = True + +class BandPlanDlg(wx.Dialog, ControlMixin): + # BandPlan in the application and the config file is a list of [integer hertz, color] and the color is None for "End". + # BandPlan in Settings[4] (global settings) is a list of [string freq in MHz, mode]. + # BandPlanColors is always a list of [mode, color] and the mode "End" is not in the list. + # The color is a string "#aa88cc". The mode is a string "DxData", etc. + def __init__(self, parent): + txt = 'Color Bars on the Frequency X-axis Mark the Band Plan' + wx.Dialog.__init__(self, None, -1, txt, size=(-1, 100)) + ControlMixin.__init__(self, parent) + self.Bind(wx.EVT_SHOW, self.OnBug) + bg_color = parent.GetBackgroundColour() + self.SetBackgroundColour(bg_color) + self.parent = parent + self.radio_name = "_Global_" + self.select_index = 0 + if "BandPlanColors" in Settings[4] and "BandPlan" in Settings[4]: + colors = Settings[4]["BandPlanColors"] + else: + Get = conf.__dict__.get # Get colors from the user's config file, if any. + dflt = "#555555" + colors = [ + ['CW, General', Get("CW", dflt)], + ['CW, Extra', Get("eCW", dflt)], + ['Phone, General', Get("Phone", dflt)], + ['Phone, Extra', Get("ePhone", dflt)], + ['AM', Get("AM", dflt)], + ['Data', Get("Data", dflt)], + ['DxData', Get("DxData", dflt)], + ['RTTY', Get("RTTY", dflt)], + ['SSTV', Get("SSTV", dflt)], + ['Packet', Get("Packet", dflt)], + ['Beacons', Get("Beacons", dflt)], + ['Satellite', Get("Satellite",dflt)], + ['Repeater out', Get("Repeater", dflt)], + ['Repeater in', Get("RepInput", dflt)], + ['Simplex', Get("Simplex", dflt)], + ['Rx only', Get("RxOnly", dflt)], + ['Special', Get("Special", dflt)], + ['Other', Get("Other", dflt)] + ] + self.modes = [] + self.color_dict = {None:"End"} + self.mode_dict = {"End":None} + for mode, color in colors: + self.modes.append(mode) + self.color_dict[color] = mode + self.mode_dict[mode] = color + self.modes.append("End") + self.MakeControls() + def OnBug(self, event): # Bug 16088 + self.gbs.Fit(self) + self.Unbind(wx.EVT_SHOW, handler=self.OnBug) + def MakeControls(self): + self.num_cols = 7 + #self.MarkCols() + self.BgColor1 = wx.Colour(0xf8f8f8) + self.BgColor2 = wx.Colour(0xe0e0e0) + charx = self.charx + chary = self.chary + self.gbs.Add (charx, chary // 2, wx.GBPosition(self.row, 6)) # Spacer at the right ends control expansion + start_row = self.row + self.list_ctrl = list_ctrl = wx.ListCtrl(self, size=(charx * 36, application.screen_height * 6 // 10), + style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.BORDER_SIMPLE | wx.LC_NO_HEADER) + list_ctrl.InsertColumn(0, '', width=charx*16) + list_ctrl.InsertColumn(1, '', width=charx*20) + list_ctrl.SetBackgroundColour(self.BgColor1) + list_ctrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelected) + hlp = '\ +The graph X-axis has colored bars to mark band segments; CW, phone etc. \ +Colors start at each frequency and continue until the next frequency or "End". \ +To add a row, enter the frequency that starts the segment. Then press the Enter key. \ +It is easier to add all the frequencies for a band first and then correct the modes later.' + txt, edt, btn = self.AddTextEditHelp(3, "New frequency MHz", "", hlp, no_edit=False, border=0) + self.Bind(wx.EVT_TEXT_ENTER, self.OnNewFreq, source=edt) + self.NextRow() + hlp = 'This changes the mode on the selected row. \ +The mode begins at the start frequency and ends at the next frequency.' + txt, self.ComboMode, btn = self.AddTextComboHelp(3, "Mode at start frequency", "AM", self.modes, hlp, no_edit=True, border=0) + self.ComboMode.handler = self.OnChangeMode + self.NextRow() + hlp = 'To delete a row, click the row and press this button.' + self.AddTextButtonHelp(3, "Delete frequency", "Delete", self.OnDelete, hlp, border=0) + self.NextRow() + hlp = 'Press "Save" to save your changes. Press "Cancel" to discard your changes.' + self.AddText2ButtonHelp(3, "Save changes", "Save", self.OnSave, "Cancel", self.OnCancel, hlp, border=0) + self.NextRow() + self.gbs.Add (charx, chary, wx.GBPosition(self.row, 3)) # Spacer before color table + self.NextRow() + width = 0 + buttons = [] + colors = [] + for mode in self.modes: + if mode != "End": + colors.append(self.mode_dict[mode]) + btn = QuiskPushbutton(self, self.OnColorButton, mode) + btn.SetColorGray() + buttons.append(btn) + width = max(width, btn.GetSize().Width) + h = self.quisk_height + 2 + for btn in buttons: # Make all buttons the same size + btn.SetSizeHints(width, h, width, h) + h = self.quisk_height + for i in range(0, len(buttons), 2): # Must be an even number + btn1 = buttons[i] + btn2 = buttons[i + 1] + color1 = wx.StaticText(self, -1, '') + color1.SetSizeHints(h, h, -1, h) + color1.SetBackgroundColour(wx.Colour(colors[i])) + btn1.color_ctrl = color1 + color2 = wx.StaticText(self, -1, '') + color2.SetSizeHints(h, h, -1, h) + color2.SetBackgroundColour(wx.Colour(colors[i + 1])) + btn2.color_ctrl = color2 + bsizer = wx.BoxSizer(wx.HORIZONTAL) + bsizer.Add(btn1, proportion=0) + bsizer.Add(color1, proportion=1, border=3, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT) + bsizer.Add(color2, proportion=1, border=3, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT) + bsizer.Add(btn2, proportion=0) + self.gbs.Add(bsizer, (self.row, 3), span=(1, 3), flag=wx.EXPAND) + self.NextRow() + self.NextRow() + self.gbs.Add(list_ctrl, (start_row, 1), span=(self.row, 1)) + self.gbs.Add (charx, chary // 2, wx.GBPosition(start_row + self.row, 1)) # Spacer at the bottom + self.DrawPlan() + self.gbs.Fit(self) + def DrawPlan(self): + index = 0 + sel = 0 + if "BandPlanColors" in Settings[4] and "BandPlan" in Settings[4]: + for freq, mode in Settings[4]["BandPlan"]: + self.list_ctrl.InsertItem(index, freq) + self.list_ctrl.SetItem(index, 1, mode) + if freq == "3.500": + sel = index + index += 1 + else: + for freq, color in conf.BandPlan: + mode = self.color_dict.get(color, "Other") + self.list_ctrl.InsertItem(index, FormatMHz(freq * 1E-6)) + self.list_ctrl.SetItem(index, 1, mode) + if freq == 3500000: + sel = index + index += 1 + self.MakeBackground() + self.list_ctrl.Select(sel) + self.list_ctrl.EnsureVisible(sel) + def OnSelected(self, event): + self.select_index = event.GetIndex() + mode = self.list_ctrl.GetItem(self.select_index, 1).GetText() + j = self.modes.index(mode) + self.ComboMode.SetSelection(j) + def OnChangeMode(self, ctrl): + self.list_ctrl.EnsureVisible(self.select_index) + mode = self.ComboMode.GetValue() + self.list_ctrl.SetItem(self.select_index, 1, mode) + self.MakeBackground() + def MakeBackground(self): + ctrl = self.list_ctrl + color = self.BgColor1 + for index in range(ctrl.GetItemCount()): + mode = self.list_ctrl.GetItem(index, 1).GetText() + self.list_ctrl.SetItemBackgroundColour(index, color) + if mode == "End": + if color is self.BgColor1: + color = self.BgColor2 + else: + color = self.BgColor1 + def OnNewFreq(self, event): + freq = event.GetString() + win = event.GetEventObject() + win.Clear() + try: + freq = float(freq) + hertz = int(freq * 1E6 + 0.1) + except: + return + i1, i2 = self.SearchBandPlan(hertz) + if i1 != i2: # Frequency is not in the list. Add it at i2. + mode = self.ComboMode.GetValue() + self.list_ctrl.InsertItem(i2, FormatMHz(hertz * 1E-6)) + self.list_ctrl.SetItem(i2, 1, mode) + self.MakeBackground() + self.list_ctrl.Select(i2) + self.list_ctrl.EnsureVisible(i2) + def SearchBandPlan(self, hertz): + # Binary search for the bracket frequency + ctrl = self.list_ctrl + i1 = 0 + i2 = length = ctrl.GetItemCount() + index = (i1 + i2) // 2 + for i in range(length): + freq = ctrl.GetItem(index, 0).GetText() + freq = int(float(freq) * 1E6 + 0.1) + diff = freq - hertz + if diff < 0: + i1 = index + elif diff > 0: + i2 = index + else: # equal to an item in the list + return index, index + if i2 - i1 <= 1: + break + index = (i1 + i2) // 2 + return i1, i2 + def OnDelete(self, event): + self.list_ctrl.DeleteItem(self.select_index) + self.MakeBackground() + if self.select_index > 0: + self.select_index -= 1 + self.list_ctrl.Select(self.select_index) + self.list_ctrl.EnsureVisible(self.select_index) + def OnColorButton(self, event): + btn = event.GetEventObject() + mode = btn.GetLabel() + color = wx.Colour(self.mode_dict[mode]) + data = wx.ColourData() + data.SetColour(color) + dlg = wx.ColourDialog(self, data) + dlg.GetColourData().SetChooseFull(True) + if dlg.ShowModal() == wx.ID_OK: + color = dlg.GetColourData() + color = color.GetColour() + btn.color_ctrl.SetBackgroundColour(color) + btn.color_ctrl.Refresh() + color = color.GetAsString(wx.C2S_HTML_SYNTAX) + self.mode_dict[mode] = color + self.color_dict[color] = mode + dlg.Destroy() + def OnSave(self, event): + mode_color = [] + for mode in self.modes: + if mode != "End": + mode_color.append([mode, self.mode_dict[mode]]) + Settings[4]["BandPlanColors"] = mode_color + freq_mode = [] + plan = [] + ctrl = self.list_ctrl + for index in range(ctrl.GetItemCount()): + freq = ctrl.GetItem(index, 0).GetText() + mode = ctrl.GetItem(index, 1).GetText() + freq_mode.append([freq, mode]) + hertz = int(float(freq) * 1E6 + 0.1) + color = self.mode_dict[mode] + plan.append([hertz, color]) + Settings[4]["BandPlan"] = freq_mode + application.BandPlan = plan + local_conf.settings_changed = True + self.EndModal(wx.ID_OK) + def OnCancel(self, event): + self.EndModal(wx.ID_CANCEL) + +class WsjtxDlg(wx.Dialog, ControlMixin): + def __init__(self, parent): + txt = 'Configure WSJT-X' + wx.Dialog.__init__(self, None, -1, txt) + ControlMixin.__init__(self, parent) + self.Bind(wx.EVT_SHOW, self.OnBug) + bg_color = parent.GetBackgroundColour() + self.SetBackgroundColour(bg_color) + self.parent = parent + self.radio_name = "_Global_" + self.MakeControls() + def OnBug(self, event): # Bug 16088 + self.gbs.Fit(self) + self.Unbind(wx.EVT_SHOW, handler=self.OnBug) + def MakeControls(self): + self.num_cols = 10 + #self.MarkCols() + charx = self.charx + chary = self.chary + self.gbs.Add (charx * 3, chary * 1, wx.GBPosition(self.row, 9)) # Spacer at the top right + self.NextRow() + # Path to WSJT-X + path = local_conf.globals.get('path_to_wsjtx', '') + help_text = "Leave blank for the usual installation path to WSJT-X. If WSJT-X is not in the usual place, enter the path." + txt, self.edit_path, btn = self.AddTextEditHelp(1, "Path to Wsjt-x", path, help_text, no_edit=False, span2=5, border=0) + self.Bind(wx.EVT_TEXT_ENTER, self.OnEditPath, source=self.edit_path) + item = self.AddPushButtonR(8, "Change..", self.OnChangePath, border=0) + self.NextRow() + self.NextRow() + # Configuration name option + value = local_conf.globals.get('config_wsjtx', '') + hlp = '\ +This is the "--config" option used to specify a configuration when WSJT-X starts. It is normally left blank.' + txt, edt, btn = self.AddTextEditHelp(1, "Config name option", value, hlp, no_edit=False, border=0) + edt.SetSizeHints(charx * 20, -1, -1, -1) + self.Bind(wx.EVT_TEXT_ENTER, self.OnConfigName, source=edt) + self.gbs.Add (charx * 3, chary // 2, wx.GBPosition(self.row, 4)) # Middle spacer + # Rig name option + value = local_conf.globals.get('rig_name_wsjtx', 'quisk') + hlp = '\ +When WSJT-X starts, it uses a rig name to keep different instances separate. \ +This is the "--rig-name" option, and it defaults to "quisk".' + txt, edt, btn = self.AddTextEditHelp(5, "Rig name option", value, hlp, no_edit=False, border=0) + self.Bind(wx.EVT_TEXT_ENTER, self.OnConfigRigName, source=edt) + self.NextRow() + self.NextRow() + # End of controls + self.gbs.Add (charx, chary * 2, wx.GBPosition(self.row, 1)) # Spacer at the bottom + self.gbs.Fit(self) + def OnConfigName(self, event): + value = event.GetString() + value = value.strip() + local_conf.globals['config_wsjtx'] = value + local_conf.settings_changed = True + def OnConfigRigName(self, event): + value = event.GetString() + value = value.strip() + local_conf.globals['rig_name_wsjtx'] = value + local_conf.settings_changed = True + def OnChangePath(self, event): + path = self.edit_path.GetValue() + path = path.strip() + if not path: + if sys.platform == 'win32': + path = "C:\\WSJT\\wsjtx\\bin\\wsjtx.exe" + else: + path = "/usr/bin/wsjtx" + direc, fname = os.path.split(path) + dlg = wx.FileDialog(application.main_frame, "Path to WSJT-X", direc, fname, "", wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + path = path.strip() + self.edit_path.SetValue(path) + local_conf.globals['path_to_wsjtx'] = path + local_conf.settings_changed = True + dlg.Destroy() + def OnEditPath(self, event): + path = self.edit_path.GetValue() + path = path.strip() + local_conf.globals['path_to_wsjtx'] = path + local_conf.settings_changed = True diff --git a/defaults.html b/defaults.html new file mode 100644 index 0000000..635831c --- /dev/null +++ b/defaults.html @@ -0,0 +1,33 @@ + + + +QUISK Configuration Defaults: quisk_conf_defaults.py + + + + + + + + +

+ + diff --git a/docs.html b/docs.html new file mode 100644 index 0000000..e53ca47 --- /dev/null +++ b/docs.html @@ -0,0 +1,1766 @@ + + + + + + + + +Documentation for Quisk + + + + + + + + + +
+

+ +Welcome to QUISK (December 2023) +

+ +

+This is Quisk, a Software Defined Radio (SDR). You supply an antenna and +a complex (I/Q) mixer to convert the radio spectrum to a low IF. Then send +that IF to your computer using the sound card, Ethernet or USB. +The Quisk software will read the I/Q data, tune it, filter it, +demodulate it, and send the audio to headphones or speakers. +Quisk has a microphone input and a key input so it can operate as a +complete transceiver. Quisk works with this hardware: +

+
+
    +
  • SoftRock connected to the sound card
  • +
  • Many other SDR's connected to the sound card
  • +
  • SDR-IQ connected by USB
  • +
  • Perseus connected by USB
  • +
  • N2ADR hardware connected by Ethernet and IP
  • +
  • HiQSDR hardware connected by Ethernet and IP
  • +
  • The Hermes-Lite project at hermeslite.com
  • +
  • Quisk can be used as a pan adapter, and can control some radios
  • +
+
+ +

+Quisk is small and simple, and has been designed so that it is easy to +change Quisk to suit your own hardware. Quisk rhymes with +"brisk", and is QSK plus a few letters to make it +easier to pronounce. QSK is a Q signal meaning full breakin CW +operation, +and Quisk has been designed for low latency. Quisk includes an input +keying signal that can mute the audio and substitute a sidetone. +
+ +
+Please read the file CHANGELOG.txt +for changes. +
+ +
+When running Quisk for the first time, please press the "Help" +button on the lower right. +
+ +
+

+

News

+

+The newest versions of Linux and Raspberry Pi OS do not allow installing Quisk in the system Python package directory. +I am working on a solution for this. +

+
+

+Python 2 is no longer supported. +Python 2 was obsolete as of January 1, 2020. And the new code by Ben, AC2YD, needs Python 3. It is troublesome to write +code that runs on both Python 2 and Python 3. So it is time to stop supporting Python 2 in Quisk. Please upgrade to +Python 3. If you have both versions, use Python 3 for Quisk. +

+
+

Credits

+

+Quisk was originally written by James Ahlstrom, N2ADR. +
+ +
+Thanks to Leigh L. Klotz, Jr. WA5ZNU for configuration improvements, +factoring +out my eccentric hardware control, and adding panadapter and other +hardware +support. +
+ +
+Thanks to Franco Spinelli for a fix for the H101 hardware. +
+ +
+Thanks to Andrew Nilsson VK6JBL for adding support for SoftRock Rx and +Tx. +
+ +
+Thanks to Terry Fox, WB4JFI, for code to support the Charleston +hardware. +
+ +
+Thanks to Maitland Bottoms, AA4HS, for the sub-module linkage patches. +
+ +
+Thanks to Philip G. Lee for adding native support for PulseAudio. +
+ +
+Thanks to Eric Thornton, KM4DSJ, for adding async support for PulseAudio. +
+ +
+Many others contributed to Quisk, and are mentioned in comments in the source code. +
+
+

+ +

Installation

+

+Quisk is free open source software written in Python and C. It is hosted on the PyPi repository +https://pypi.org. +It can be installed using the standard Python setup tools. See specific directions below. +

+
+

+If you have Python 3 installed on your computer, just continue to use that version. +Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11. +If you want to install a more recent version, 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. +

+
+

Windows Initial Installation

+

+Windows does not include Python, so you must first install the most recent version of 64-bit Python 3. +If you already have 64-bit Python 3 installed, just keep using it. +Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11. +There may be newer versions of Python, but until support is added, use the versions listed above. +Python is available from http://www.python.org. +There is an option to Add Python to PATH. You MUST select +this option so that you can start python by just typing "python" instead of the whole path +to your install directory. My install directory is: +
+C:\users\jim\AppData\Local\Programs\Python\Python39 +
+And "AppData" is invisible. To avoid problems, check Add Python to PATH. +If you forget, it is worth it to uninstall and reinstall Python. +Quisk is really designed for curious people who want to play with the code, but Windows +tries to shield users from program details. +

+
+

+Next open Windows PowerShell (not PowerShell ISE). This is on the Start button menu on Windows 10; or use the +search bar. Enter "python --version" to make sure that Python is installed +and what version you have. If "python" is not found, it might not be on your Path. +You will need to keep typing your_install_directory\python instead of "python". +

+
+

+Next upgrade some Python modules to the newest version, then install Quisk. Enter these commands: +
+python -m pip install --upgrade pip +

+python -m pip install --upgrade setuptools +

+python -m pip install --upgrade wxPython +

+python -m pip install --upgrade pyserial +

+python -m pip install --upgrade quisk +

+You should then be able to start Quisk with the command "quisk". +You can also start Quisk with "python -m quisk". +To create a Quisk shortcut on your desktop, right-click an empty space and select "New" and "Shortcut". +Use "quisk.exe" as the command and "Quisk" as the name. +The "quisk.exe" program is in your_install_directory\Scripts. +If you are curious, Quisk and its Python source files are installed in "your_install_directory\Lib\site-packages\quisk". +If you change to that directory you can run Quisk with "python quisk.py". +

+
+

+To get started you must tell Quisk what kind of radio hardware you have. Press the Config button and select Radios. +Then set your sound devices for that radio; the device for the radio speakers, the microphone and so forth. All configuration +is (mostly) from the Config button. Ignore old directions and don't bother with a config file. +

+
+

Windows Quisk Upgrade

+

+To upgrade to a newer version of Quisk, use pip. Remember that if Python is not on your Path, you will +need to type out the whole path. +You should check for newer versions of the other modules twice a year. +
+
+python -m pip install --upgrade quisk +
+
+You can also install an older version of Quisk. You may need to do that if the most recent version fails for some reason. Use: +
+
+python -m pip install quisk==4.1.51 +
+
+

+

Windows Uninstall Quisk

+

+python -m pip uninstall quisk +
+
+

+

Linux Initial Installation

+

+
+The libsoundio-dev module is NO LONGER required! +
+
+Linux runs on a wide variety of computers with different processors and versions of Linux. +Therefore Quisk is compiled for each one. To do this, Python and a number of other packages are required. +Most likely both Python2 and Python3 are already installed on your computer. Either the 32-bit or 64-bit +version will work. On my Ubuntu machine, "python2" starts Python2 and "python3" starts Python3. +Just "python" starts Python2, but this will change as Python2 is phased out. +Python2 is obsolete, so you should only install Quisk to Python3, and use "python3" to start it. +Install these packages. Some of these can be installed from Python using "pip", +but it is better to use your package manager because pip and the package manager do not coordinate. +If newer versions of these packages become available, Linux will notify you. +Use the most recent version of python3-wxgtk available. +

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Package DescriptionPython3 Package
Discrete fourier transformsudo apt-get install libfftw3-dev
Alsa sound librarysudo apt-get install libasound2-dev
Portaudio sound librarysudo apt-get install portaudio19-dev
Pulseaudio sound librarysudo apt-get install libpulse-dev
Python developmentsudo apt-get install python3-dev
Python library developmentsudo apt-get install libpython3-dev
wxPython GUI librarysudo apt-get install python3-wxgtk4.0
Python USBsudo apt-get install python3-usb
Python serial portsudo apt-get install python3-serial
Python installation toolssudo apt-get install python3-setuptools
Pip package installersudo apt-get install python3-pip
+

+

+If you want to use the SoapySDR module make sure that SoapySDR is installed from its Pothosware site before you build Quisk. +Otherwise Quisk will not have the files it needs. You can install SoapySDR later, but then you +will need to reinstall Quisk. This is also true of any other external packages that have their own C source. +
+
+

+

Linux Pip Installation

+

+You can use the Python program "pip" to install Quisk into the Python package directory. This is the easiest +method, but it will not install the source files. If you want the source files see below. +
+
+sudo -H python3 -m pip install --upgrade quisk +
+
+This will install the Python files and all other files except for the C source. This is the easiest method +if you do not want to work in C. +To run Quisk, use this command from a terminal: +
+
+python3 -m quisk +
+
+You can create a panel launcher on your desktop with the command to run Quisk. +
+
+If the installation of Quisk fails, you can force re-installation: +
+
+

+
+sudo -H python3 -m pip uninstall quisk
+sudo -H python3 -m pip install  --no-cache-dir  --force-reinstall  quisk
+
+

+
+You can also install an older version of Quisk. You may need to do that if the most recent version fails for some reason. Use: +
+
+sudo -H python3 -m pip install quisk==4.1.51 +
+
+To get started you must tell Quisk what kind of radio hardware you have. Press the Config button and select Radios. +Then set your sound devices for that radio; the device for the radio speakers, the microphone and so forth. All configuration +is (mostly) from the Config button. Ignore old directions and don't bother with a config file. +
+
+To edit the Quisk Python files you need to know where they are. To find out, import quisk and print it: +
+
+

+
+jim@IntelNUC:~$ python3
+Python 3.6.9 (default, Oct  8 2020, 12:12:24) 
+[GCC 8.4.0] on linux
+Type "help", "copyright", "credits" or "license" for more information.
+--- import quisk
+--- quisk
+module 'quisk' from '/usr/local/lib/python3.6/dist-packages/quisk/__init__.py'
+--- 
+
+
+
+

Linux Pip Upgrade

+

+To upgrade to a newer version of Quisk, use pip: +
+
+sudo python3 -m pip install --upgrade quisk +
+
+

+

Linux Uninstall Quisk

+

+sudo python3 -m pip uninstall quisk +

+
+
+

Linux Source Installation

+

+The previous method using pip is simple, but will not install the C source files. If you want those proceed as follows. +Go to https://pypi.org/project/quisk and under "Navigation" +select "Download files". Download the quisk-x-x-xx.tar.gz file. This is the source file. The ".whl" files are for Windows. +Change directory to where the file was downloaded, probably "Downloads", and uncompress and untar it. +

+cd ~/Downloads +
+tar zxf quisk-x.x.xx.tar.gz +

+Change directories to the Quisk directory: +

+cd quisk-x.x.xx +

+Now compile Quisk with the "make" command: +

+make quisk3 +

+If "make" fails, you probably have missing packages or missing "-dev" packages. Try to figure out what is missing from the error messages. +Now you can run quisk with the command: +

+python3 quisk.py +

+At this point you have a choice of where to install Quisk. You could just rename the quisk-x.x.xx directory +to a more convenient name, like "quisk" in your home directory, and run Quisk from there. +This is convenient if you want to +alter the Quisk code. Or you could install Quisk into the Python system. +

+python2 setup.py build +
+sudo python2 setup.py install +

+

+

Quisk Files

+

+
+These are the Quisk files in the distribution: +
+ +
+

+
    + +
  • quisk.py is the main program and is written in the Python +language. +Python is a powerful but easy to learn language, and I hope you have +fun changing Quisk to make it do what you want. Python is also useful +for general electronics calculations such as complex arithmetic. See +www.python.org. Quisk.py uses the wxPython Python package to make the +screen interface.
  • +
+
    + +
  • help.html is the help file for quisk. Press the "Help" button.
  • +
+
    + +
  • _quisk.so is the _quisk extension module for Linux, and _quisk.pyd is the extension module DLL used by Windows. +
    +
  • +
+
    + +
  • sdriq.so is the extension module needed for the SDR-IQ. It needs +_quisk.so to be available when it starts.
  • +
+
    + +
  • makefile is the makefile, and you must run "make" to create a new +_quisk.so unless you use a Python installer that creates _quisk.so +itself.
  • +
+
    + +
  • setup.py is used by makefile and the Python installers.
  • +
+
    + +
  • quisk_conf_defaults.py is the basic configuration file imported +into +all other configuration files. Read it (but don't change it) to see +what you can change in your own quisk_conf.py.
  • +
+
    + +
  • quisk_conf_*.py are various Quisk configuration files. Copy one +of them +to your own .quisk_conf.py and edit that file. I may publish new model +files in the future, and you don't want your changes to be overwritten. +
  • +
+
    + +
  • quisk_hardware_*.py are various quisk hardware control programs. +If you +have custom hardware, import one of these files into your +quisk_conf.py. Or copy one of them to your own quisk_hardware.py, edit +that file, and import it in .quisk_conf.py.
  • +
+
    + +
  • quisk.c, quisk.h are the files for the _quisk extension module +used by +quisk.py. The other C-language files are linked with these to make +_quisk.so and _quisk.pyd. +
    +
  • +
+
    + +
  • sound.c is the general purpose sound code for all sources.
  • +
+
    + +
  • sound_portaudio.c is the sound card access code for PortAudio. PortAudio is optional.
  • +
+
    + +
  • sound_pulseaudio.c is the sound card access code for PulseAudio.
  • +
+
    + +
  • sound_alsa.c is the sound card access code for the ALSA drivers. +
    +
  • +
+
    + +
  • sound_directx.c is the sound card access code for DirectX.
  • +
+
    + +
  • is_key_down.c is the hardware key checker for the PC. I use +Ethernet to +send the key status, but there is code for the parallel port and dummy +code too.
  • +
+
    + +
  • sdriq.c, sdriq.h are the files that make sdriq.so and support the +SDR-IQ.
  • +
+
    + +
  • microphone.c reads the microphone audio and sends it to your +hardware +using Ethernet. Change it for other sound access.
  • +
+
    +
+
    + +
  • docs.html is Quisk documentation. Look for other *.html and *.txt too.
  • +
+
    + +
  • portaudio.py is a utility program. Run it to list your PortAudio +devices. It is not used by the Quisk program.
  • +
+
+

Configuration

+

+The Quisk "Config" button brings up a number of status and configuration screens. +Quisk supports multiple types of radio hardware and each type has different parameters. +Each block of parameters is called a "radio". It is a named block of settings Quisk uses +to control a specific kind of hardware. +So a single Quisk can have parameters for a SoftRock and an HL2. +You specfy the radio you want when starting Quisk. +
+
+ +When you first install Quisk, you will not have any settings for your radio. +Press the Config button and go to the Radios screen. +Then create a radio by specifying the general hardware type and give it a name of your choosing. +For a Hermes-Lite, specify "Hermes" as the hardware type and call it "HL2" (or some other name). +Press "Add" and a new tab for your radio will appear. Look through the various settings on the HL2 tab. +The parameters for the radios are stored in the file quisk_settings.json. +
+
+ +A special radio called "ConfigFileRadio" is always available. +It takes its parameters from a configuration file. +You can set almost everything with the screens, but you can have a configuration file if you want. +Most users will not need a configuration file. +For Linux, the default configuration file name is ".quisk_conf.py" in your home +directory; that is, "~/.quisk_conf.py". For Windows, the default +configuration file name is quisk_conf.py in your My Documents folder. + + + + +
+
+

+

Sound Cards

+

+If you use a sound card for input, the quality of your sound card is +critical; +but you can start with the sound card you have. Check the Graph screen +with no input to see the noise floor. It should be as flat and as low +as +possible, +with no bump near zero Hertz. The 0dB line at the top of the Graph +screen +is the +maximum level, so if your noise floor is at -90 dB, you have that much +dynamic range. The IF (sound) input to the sound card should raise the +noise +floor only slightly to avoid losing dynamic range. +
+ +
+The sample rate determines how much of the band you can see on the +screen. My 96 kHz card shows a little over 80 kHz of bandwidth, from +-40 kHz to + 40 kHz centered at the VFO frequency. Generally you +would choose the +highest +rate available to get the most visible bandwidth. Be aware that a card +claiming to work at (say) 192 kHz may in fact play at that rate, but +only capture (record) audio at a lower rate. It is the capture rate +that matters. +Enter only the sample rate you know your raw hardware supports for +capture. +
+ +
+If you use the SDR-IQ or other hardware for input, you still need a +sound card for sound output. The quality of this card is not so +important, so try the one you have. Be aware that most sound +cards require the capture and playback rate to be the same when used +for both. Here are some sample configurations: +
+

+
    + +
  • SoftRock Rx/Tx: Receive to card 1, Transmit to card 1 at the same +rate, radio sound to card 2 at 48 kHz, microphone input from card 2 or +3 at 48 kHz.
  • + +
  • SoftRock Rx: Receive to card 1, radio sound to card1 at the same +rate; OR radio sound to card 2 at 48 kHz.
  • + +
  • Other: Receive from SDR-IQ or other hardware, radio sound to card +1 at 48 kHz. Add a microphone to card1 at 48 kHz, or to card2 at +48 kHz.
  • + +
  • Panadapter: There is no radio sound. Enter a null name "" +for the play device. +
    +
  • +
+

+If you buy a new sound card, make sure you know the +capture (recording) sample rates and the noise level. Sound cards +are usually specified over +the audio range up to 24 kHz or so. But we need low noise and +distortion +over the whole range. +

+
+

Linux Names

+

+Quisk can use PulseAudio, PortAudio or ALSA to access your sound card. +Names can be a fragment of text from the device description. It is +better to use this text search rather than an index number, because the +index number can change if you plug and unplug USB sound cards. +
+ +
+The ALSA drivers use different names for the same sound card +to provide different access. The names "hw:0" and "hw:1" refer +to the raw hardware devices of the first and second sound card. +You should use the raw hardware if possible. If the raw devices don't +work, +use the "plughw" name. The ALSA name can also be a string +name. Here are some ALSA names: +
+

+
+
"hw:0"		# First sound card
+"hw:1"		# Second sound card, etc.
+"plughw"	# plug device
+"default"	# alsa default device
+"alsa:NVidia"	# Search for the name in the alsa device description
+
+
+

+Alsa names starting with "alsa:" are an extension to the normal alsa +names. They search for the text after the colon in the alsa +device +name. The alsa device names are shown on the config screen. +Or you +can start a terminal window and enter "aplay -l" for a list of play +devices, or "arecord -l" for a list of capture devices. See alsa_names for +more information. +
+ +
+The PortAudio interface is now optional. Many users are changing to PulseAudio. +You can run "python portaudio.py" in a terminal window to +see a list of available PortAudio names. Here are some PortAudio names: +
+

+
"portaudio:(hw:0,0)"    First sound card.
+"portaudio:(hw:1,0)"    Second sound card, etc.
+"portaudio:NVidia"      Search for the name in the portaudio device description.
+"portaudio#1"           Directly specified index.
+"portaudiodefault"      May give poor performance on capture.
+
+
+

Linux Sound Servers

+

+Newer Linux systems are now shipping with PulseAudio enabled. +PulseAudio is a sound server, a program that takes control of your +sound cards, and controls usage by applications. The idea is that +your applications talk to PulseAudio, and PulseAudio talks to the sound +cards. Another example of a sound server is JACK. +You can control the +sound routing with the pavucontrol program. Remarkably, this is +not included with PulseAudio, and you will need to install the +pavucontrol package first. +
+ +
+Thanks to Philip G. Lee and Eric Thornton, KM4DSJ, Quisk now has native support for PulseAudio. +For PulseAudio devices, use the name "pulse:name" and connect the streams +to your hardware devices using a PulseAudio control program like pavucontrol. The name "pulse" +alone refers to the "default" device. The PulseAudio names are quite long; +for example "alsa_output.pci-0000_00_1b.0.analog-stereo". Look on the screen +Config/Sound to see the device names. There is a description, a PulseAudio name, +and for ALSA devices, the ALSA name. +Instead of the long PulseAudio name, you can enter a substring of any of these three strings. +An example is: + +

+
+
+# As seen on the Config/Sound screen:
+     CM106 Like Sound Device Analog Stereo
+     alsa_output.usb-0d8c_USB_Sound_Device-00-Device.analog-stereo
+     USB Sound Device USB Audio (hw:1,0)
+
+# Use the default pulse device for radio sound:
+   "pulse"
+# Use a PulseAudio name for radio sound:
+   "pulse:alsa_output.usb-0d8c_USB_Sound_Device-00-Device.analog-stereo"
+# Abbreviate the PulseAudio name:
+   "pulse:alsa_output.usb"
+# Another abbreviation:
+   "pulse:CM106"
+
+
+ +

+The PulseAudio code should not cause problems, but I am not sure what happens if PulseAudio is not +installed, or if you replace it with JACK. This config file option will turn off all but directly +entered "pulse:" names: +

+
show_pulse_audio_devices = False
+
+ + +
+ +

Linux Problems

+ +

+If Quisk appears to run but you get no sound input or output, you +may be having trouble +with your settings. Start Quisk and look at the graph. You should get a +moving +line display. Look at the Config screen. Interrupts should be +increasing and latencies +should fluctuate. If all this looks normal, but you get no sound +output, or you get only +white noise output, then you may need to change your settings with a +mixer program. +
+ +
+If you capture data with the sound card (no SDR-IQ) then you need +to set the "capture +device" to the line-in jack, and set the volume of the line-in to 100%. +To play sound, +you need to increase the volume of the playback device. Since a typical +sound card has +ten or twenty controls for all its analog and digital inputs and +outputs, it is a guessing +game to figure out which control to adjust. +
+ +
+Basically you start the alsamixer program (use "man alsamixer" first) +and adjust the volume +controls and capture device until Quisk works. It is wise to reduce or +mute unwanted inputs +to avoid adding extra noise. +Quisk does not do this for you. But once you have the controls set, +they will stay the same +and Quisk should keep working until you run another audio program that +changes them. +
+ +
+To make Quisk adjust the mixer controls when it starts, you need to +know the control id number. +Run the command "amixer -c 0 contents" (for card zero) and look at the +control ids, names +and values of all your controls. Figure out the control you need to +adjust. For a setable +option (on/off) the control value is one or zero. For a volume it is a +number from 0.0 to +1.0. Make a list of (device_name, numid, value) and add it to +mixer_settings in your +.quisk_conf.py file (see quisk_conf_defaults.py). I don't need to do +this on my computer +except for the microphone input on my second sound card. +
+ +
+If you really get stuck, try one of these commands (see the "man" +page): +
+

+
    + +
  • alsamixer An ALSA mixer program with a curses interface.
  • +
+
    + +
  • amixer A character ALSA mixer.
  • +
+
    + +
  • aplay Play sound.
  • +
+
    + +
  • arecord Capture sound.
  • +
+
    + +
  • speaker-test Play sound to test your speakers.
  • +
+

+
+And try to play an audio CD or run some other Linux audio program just +to see that you +have a working sound system. +If you can't get ALSA to work, you could try the PortAudio or PulseAudio interface by +just +changing the sound card names. +
+ +
+

+

Windows Names

+

+To see what sound cards you have, use the Control Panel item Sound +Devices. There is a separate list for capture (recording) and +playback devices, and a specified default device for each. The +name of the default device is "Primary". To specify your sound +card name, use either "Primary" or a substring of the device +name. The search is case sensitive. +
+
+

+

SoftRock

+

+SoftRock radios use an analog mixer to change the RF signal to stereo audio, and then use a sound card to digitize it. +A high quality sound card is advisable. The analog mixer is not perfect, and it will be necessary to adjust the I and Q +signals to equal amplitude and 90 degrees phase difference. Use the Config/Config screen buttons to +bring up an adjustment screen. Adjustments must be made for each band, and separately for transmit and receive. +The adjustment depends on both the VFO frequency and the tuning offset from the VFO. See my paper +http://james.ahlstrom.name/phase_corr.html for more information. +A good strategy is to pick a VFO near the band center, and record corrections at an Rx frequency of -15000, -1000, 1000 and 15000 Hertz. +If the corrections are sensitive to VFO, record the corrections for these same Rx frequencies at VFOs equal to +or slightly outside the upper and lower band edges. +To effectively adjust for multiple VFO frequencies, the VFOs must have the same table of Rx frequencies. +You can add as many correction points as desired. The corrections are saved in the file quisk_init.json. This file can be edited +by hand if you are a Python expert. Otherwise you can just read the values. +
+
+To create a Receive correction point for a given VFO and frequency, attach a signal generator to the SoftRock through an attenuator, +and look at the image on the graph screen. If the signal is 3500 Hertz above the VFO, the image is 3500 Hertz below it. +If you don't have a signal generator, find a strong station on the band and look at its image. Minimize the image by adjusting +the amplitude and phase sliders on the adjustment screen, and then press "Save". +
+
+To create a Transmit correction point for a given VFO and frequency, attach the SoftRock RF output to a spectrum analyzer through an attenuator, +and look at the image. If you don't have a spectrum analyzer use a second receiver tuned to the image. Minimize the image by adjusting +the amplitude and phase sliders on the adjustment screen, and then press "Save". +
+
+

+

SDR-IQ as Input

+

+Quisk can use an SDR-IQ from RfSpace instead of a sound card as input. +Set up a radio of type SdrIQ. The SDR-IQ uses a serial port to connect to Quisk. When you plug it in, +it will create a USB serial port and connect to it. +

+
+

+On Linux the serial port has a name like /dev/ttyUSB0. Look in /dev or use "dmesg | tail" to figure out what port it is using. +Then enter that port as the "Serial port" on the Config/radio/Hardware screen. The serial ports are part of the "dialout" group. +Add yourself to the "dialout" group so you have permission to use the serial port. You also need a serial port USB +driver for the ft245 chip in the SDR-IQ, but Linux generally comes with a suitable driver. +

+
+

+On Windows the "Serial port" name is not used, and Quisk will search for the port in use. +On Windows 10, you should see a device "SDR-IQ" in Device Manager in the "View/Devices by Container" tab. +In earlier versions of Windows, port names are COM1, COM2 etc. and use the "USB Serial Converter" driver. +Windows should find this driver by itself. +

+
+

Perseus as Input

+

+Quisk can use an Perseus HF receiver from Microtelecom instead of a sound card as input. +Set up a radio of type Perseus. The Perseus uses a native USB interface to connect to Quisk. +The Quisk perseuspkg extension relies on libperseus-sdr + open source library to manage Perseus hardware and receive the I/Q samples stream. +

+
+

+Follow the instruction into GitHub repository to compile and install the library. +On Suse distribution the library is available as binary package. +Next compile the perseuspkg using the command: +

+
+
+make perseus3
+
+
+

+The several sample rates can be selected opening Config panel: in +the Config tab there is the Samples rates dropdown. +The input analog filter can be switched in using the button Wideband.
+The input attenuator is operate via the button RF, that allows to select +the four attenuator steps.
+The ADC commands for dithering and preamplifier are found on +left bottom corner as ADC Dither and ADC Preamp.
+

+
+

Timing

+

+There are several configuration parameters devoted to tuning; read the +file quisk_conf_defaults.py for documentation. For most users, Quisk +should run fine with the default settings. But if you use Quisk as part +of a QSK CW transmitter, you should reduce latency_millisecs to as low +a +value as possible. This will reduce latency, but increase the +likelihood of clicks and pops due to sound buffer underruns. +
+ +
+

+

USB Control

+

+Many radio devices are now controlled through a USB interface. In +many cases, the interface is actually a serial port, and an external or +internal USB to serial converter is used. In other cases, the USB +is native, but requires a custom device driver. In still other +cases, the USB device announces itself as a standard device such as a +sound device or human interface device, and uses a standard operating +system built-in driver. +
+ +
+

+

Linux

+

+Default USB permissions do not allow a non-root user to write to the +bus. You may find that Quisk will complain about lack of +permission to access the USB. You could test this by running +Quisk as root and seeing if that works; but this is not acceptable +except for testing. To change USB permissions, add a rule to +/etc/udev/rules.d/local.rules (for SoftRock on Debian and Ubuntu) like +this: +
+ +
+ SUBSYSTEM=="usb", ATTR{idVendor}=="16c0" , +ATTR{idProduct}=="05dc", MODE="0666", GROUP="dialout" +
+ +
+This changes the USB device permissions to read/write for all users, +and changes the group to the "dialout" group. Default group +permissions are read/write, so if you are in the "dialout" group, you +don't need "MODE"; modify as appropriate. To load the new rule, you can either reboot or on Ubuntu use +
+ +
+ sudo udevadm control --reload-rules +
+ +
+

+

Custom Hardware

+

+Quisk comes with hardware files for many types of radio. See the various quisk_hardware_*.py files. +But if you have a radio that Quisk does not support, or if you want to customize an included hardware file, you +can write your own hardware file and enter the name on the Config/radio/Hardware screen. +A model hardware file is included as quisk_hardware_model.py. +It is useful as a starting point and as documentation. Hardware files use Python class inheritance. +That is, all hardware files inherit methods from a parent file and then add their custom methods. +Hardware files can control other hardware too. +At my shack, I control an AT-200PC antenna tuner, my SDR-IQ, my filter +boxes and my SSB transceiver (using Ethernet) all with Quisk. +Take a look at my n2adr subdirectory. +

+
+

+The quisk_hardware_model.py file shows the basics of hardware control. +There is an open() and close() function called once on startup and +shutdown. The ChangeMode() and ChangeBand() functions are called when +the user changes the mode or band with the corresponding buttons. +The HeartBeat() function is called at about 10 Hz by Quisk. You +can put code there to poll a serial port or to perform other +housekeeping functions. +

+
+

+Here is the start of the SDR-IQ hardware file: +

+
+
+from quisk_hardware_model import Hardware as BaseHardware
+
+class Hardware(BaseHardware):
+  def __init__(self, app, conf):
+    BaseHardware.__init__(self, app, conf)
+    # etc.
+  def ChangeBand(self, band):
+    # etc.
+
+
+

+The file imports the Hardware from quisk_hardware_model and uses it as the basis of the SDR-IQ Hardware class. +It calls the base init function and then adds its own methods for ChangeBand() and other methods. If +it does not define a method, the method from quisk_hardware_model is used. Please refer to Python documentation if +you are not familiar with inheritance. +

+
+

+If you want to write a hardware file from scratch, your file would start the same way. But if you have a radio like +the SDR-IQ but want to customize it, your hardware file would look like this: +

+
+
+from quisk_hardware_sdriq import Hardware as BaseHardware
+
+class Hardware(BaseHardware):
+  def __init__(self, app, conf):
+    BaseHardware.__init__(self, app, conf)
+    # etc.
+  def ChangeBand(self, band):
+    # etc.
+
+
+

+This file uses all the methods from the SDR-IQ file except ones that are defined here. +The HL2 hardware file is in the hermes subdirectory, so to create a custom file you would use: +

+
+
+from hermes.quisk_hardware import Hardware as BaseHardware
+
+
+
+

+Alternatively, you can define a class named "Hardware" in your config +file, +and that class will be used instead of a hardware file. This is +recommended +only for simple hardware needs. The class should start like this: +
+
+

+ +
from quisk_hardware_model import Hardware as BaseHardware
+class Hardware(BaseHardware):
+    def __init__(self, app, conf):
+        BaseHardware.__init__(self, app, conf)
+        # Start your hardware control here.
+        # For ideas, see one of the other hardware modules.
+
+ +
+ +
+

+Both the config file and your hardware file are written in the Python +language. Python is an easy to learn but powerful computer +language. Quisk can be adapted to different hardware because of +the power of Python. +
+
+

+

ChangeFrequency(self, tune, vfo, source='', band='', event=None)

+

+Quisk calls the ChangeFrequency() function when the user changes the Tx +frequency with a mouse click on the graph or waterfall, with the entry +box, with the band Up/Down buttons, etc. The "source" is a string +giving the reason for the change: +
+ +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BtnBand A band button was pressed (the +string band is in the band argument)
BtnUpDownThe band Up/Down buttons were +pressed
FreqEntryThe user entered a frequency in +the +box
MouseBtn1Left mouse button was pressed +(for the mouse, "event" is the handler event) +
+
MouseBtn3Right mouse button was pressed
MouseMotionThe user is dragging with the +left button
MouseWheelThe mouse wheel up/down was used
+
+

+Most of the time you will not care about the "source". You just +need to react to the user's action, perhaps by changing the hardware +VFO frequency. It is not necessary to actually make the change +requested. Just +adjust your hardware as required, and return the actual (tune, vfo) +that you want. Quisk will ignore its requested values and use +your actual values instead. +
+ +
+For example, suppose you have a crystal controlled SoftRock. The +VFO frequency is fixed at (say) 7.025 MHz. Then when +ChangeFrequency() is called, return (tune, 7025000). This will +fix your VFO frequency to the only one available. +
+ +
+Suppose Quisk calls ChangeFrequency() with vfo=7050000 and +tune=7100000, so the tune is 50 kHz above the VFO. Suppose that +is unacceptable because of (say) bandwidth limitations, so you want the +VFO closer to the tune. Set your hardware VFO to 7090000 instead, +and return (tune, 7090000). +
+ +
+Suppose Quisk is just controlling a receiver and the audio is +demodulated by the receiver and not by Quisk. Then the center +frequency is always the tuning frequency, and you would set the +receiver frequency to tune, and return (tune, +tune). +
+ +
+

+

ReturnFrequency(self)

+

+When Quisk starts, it calls ReturnFrequency() to get the initial tune +and VFO. To display an initial frequency, return (tune, vfo) on +the first call. +
+ +
+Thereafter, Quisk calls ReturnFrequency() at a 10 Hz rate to poll for +frequency changes. You should almost always return (None, None) +to indicate that the frequencies have not changed since the last time +ReturnFrequency() or ChangeFrequency() was called. Returning +(None, None) is slightly more efficient than returning the actual +frequencies, and thus forcing Quisk to see if its frequencies are out +of date. +
+ +
+The only reason to return something other than (None, None) is if your +hardware can change frequency by itself; that is, other than in +response to ChangeFrequency(). For example, if your hardware is a +receiver with a tuning knob, and the user turns the knob, you must +return the new frequencies from ReturnFrequency() or else Quisk will be +unaware of the change. +
+ +
+

+

Adding Custom Hardware to the Config/Radios Screen

+

+If you have unique hardware and want to add it to the +list of available radio types and add its configuration options to its Config/radios/Hardware screen, +create a subdirectory of the Quisk directory with a name ending in "pkg"; for example, +"myradiopkg". Then put your hardware file in this subdirectory with the name "quisk_hardware.py". +You will need at least one configuration option to specify the hardware file name. +Add this code (all comments) near the top of quisk_hardware.py: +

+
+
+# Define the name of the hardware and the items on the hardware screen (see quisk_conf_defaults.py):
+################ Receivers MyRadio, The special radio that I own
+## hardware_file_name		Hardware file path, rfile
+# This is the file that contains the control logic for each radio.
+#hardware_file_name = 'myradiopkg/quisk_hardware.py'
+
+
+

+Of course, change the names of the radio and subdirectory as appropriate. Your radio of general type "MyRadio" +will now appear in the list of radios on the Config/Radios screen, and its configuration items will appear +on the Config/radio/Hardware screen. You can add additional items. See quisk_conf_defaults.py and the *pkg +subdirectories for examples. + +
+
+

+

Extension Packages

+

+Quisk comes with two extension packages. The freedvpkg package +supports FreeDV digital voice. The n2adr package +supports the hardware in my shack. There are other extension +packages available from third parties. +
+ +
+All extension packages are directories (folders) in the Quisk root; +that is, in the directory where quisk.py is located. This enables +Quisk to find extension modules, and extension modules to find each +other. You can install in a different place, but you will need to +know what you are doing. +
+ +
+Starting with Quisk 3.6 C-language extension modules are not linked +with _quisk.so. Certain symbols from _quisk.so are exported using +the Python CObject or Capsule interface. That simplifies linkage +and eliminates problems with module search paths. See the +documentation in import_quisk_api.c. This change was suggested by +Maitland Bottoms, AA4HS, and he also provided patches. +
+ +
+

+

Shared Libraries

+ +

+The main Python extension module for Quisk is _quisk.so or _quisk.pyd. +It is a shared +library. To import it, it must be on the Python path. There are other +Python extension modules (shared libraries) for other hardware, for +example, +sdriq.so. Quisk works fine when all these modules are in package +subdirectories. If you want to put them somewhere else, be sure that the Python import mechanism can find them. +
+ +
+If you link your sub-packages against _quisk so you can use _quisk functions, be aware that your sub-package must be able +to find +_quisk.so at both compile and run time. You need to follow the Linux rules for searching +for +shared libraries. Try using the "ldd sdriq.so" command to see your +library +dependencies. Also try readelf -d sdriq.so. +
+ +
+For Quisk version 3.6 and newer, you should use the Python CObject or +Capsule mechanism instead of using the C linker to access _quisk +functions and data. Quisk will prepare an array of function and +data pointers and transfer them to your sub-module without using the C +linker. Only minimal changes to your sub-module are +required. The SDR-IQ module sdriq.so uses this method, and you +can use it as a model. See the file import_quisk_api.c for +documentation. +

+
+

New Packages

+

+If you have more complex needs or want to distribute your code more +widely, you need to create a new Quisk package. That is easily +done by modeling your code after the existing packages. To create +a new package you need a subdirectory of the Quisk root to hold it, +perhaps "mypak". Then create these files in mypak: +
+ +
+

+
    + +
  • __init__.py This file just consists of +the +character "#". Its existence identifies mypak as a Python package. +
    +
  • +
+
    + +
  • makefile Only needed if you have C-language extensions. +
    +
  • +
+
    + +
  • MANIFEST.in A list of files you want to +distribute in your package. This file often consists of just one +line: include *.c *.h *.py *.txt +*.html +*.so makefile +
    +
  • +
+
    + +
  • README.txt This file is expected to +be +present.
  • +
+ +
+

+To these files, you add all your Python files, C-language files and any +other files you need. If you have a hardware or widget file, they +should be named quisk_hardware.py and quisk_widgets.py. Longer +names are not needed because you are within a package. You should +include a sample quisk_conf.py too. +
+ +
+To compile C-language extensions (if you have any) enter "make". +To import your hardware and widgets files from other modules, use: +
+ +
+ from mypak import quisk_hardware +
+ from mypak import quisk_widgets +
+ from mypak import myext as EXT +
+ +
+The setup.py file describes how to build your package. But it is +also used to distribute it. To create a mypak-1.0.tar.gz file in +the "dist" subdirectory, use: +
+ +
+ python setup.py sdist +
+ +
+You can then put the file on your web page (for example). To make +your package available on PyPi.Python.org, first register with PyPi and +then use: +
+ +
+ python setup.py register sdist upload +
+ +
+Python supports quite complicated packages; see the distutils +documentation. +
+ +
+

+

Installing Packages

+

+Your package mypak will run on your machine as is. But when +another user gets mypak-1.0.tar.gz they need to install it. +Basically, they just put it in the Quisk root with the same name as on +your machine. Here is an INSTALL.txt: +
+ +
+Unzip and untar this archive at the root of the Quisk directory; that +is, where the file quisk.py is located. In this example, the +archive is named "mypak" and the path to quisk.py is +/home/jim/quisk/quisk.py. +
+ +
+ mv mypak-1.0.tar.gz /home/jim/quisk +
+ cd /home/jim/quisk +
+ gunzip mypak-1.0.tar.gz +
+ tar xf mypak-1.0.tar +
+ # Make sure that directory mypak-1.0 exists before removing the +archive. +
+ rm mypak-1.0.tar # tar file +is no longer needed +
+ mv mypak-1.0 mypak # change to the correct name +
+ +
+

+

Digital Modes

+

+Quisk has a number of modes "DGT-" to receive and transmit digital +signals. The modes "DGT-U" and "DGT-L" decode the signal as upper +or lower sideband, and send the stereo audio to the digital sound +device. The left and right channel are the same. +The bandwidth is set with the filter buttons as +usual, and the filter center is 1500 Hertz. +The mode "DGT-IQ" does not decode the audio; the I/Q +samples are sent directly to the stereo digital sound device. +
+ +
+Digital modes require an external digital program such as Fldigi or WSJT-X to decode the received +audio and to generate transmit audio. There are two aspects, rig +control and audio transfer. Rig control is needed to synchronize +the transmit frequency between Quisk and WSJT-X and +to operate the PTT (push to talk). You can control Quisk using +XML-RPC, Hamlib or a serial port. +See Rig Control and Logging below. +
+
+Quisk has additional audio inputs and outputs for digital programs. +On the radio Sound screen "Digital Tx0 Input" is the Quisk input for transmitted audio. +The "Digital Rx0 Output" is the Quisk output of the received digital signals. +You need to set these names to a sound device. + +The sound device is not a real sound card; it is +some sort of loopback device, and is only needed because there is no +standard way of sending digital samples between two programs (yet). +The method to use for Quisk is the same as for other programs, and is +on the web. It works for any digital program. +
+
+If you use Windows, you need to purchase a Virtual Audio Cable +(VAC). Connect Quisk to one side, and your digital program to the other. +The name of the sound device depends on which VAC you use. +
+
+If you use Linux, you can use the ALSA loopback device, or use +PortAudio, PulseAudio or Jack to route your audio. Using PulseAudio is the +easiest method because Quisk can set up the loopback devices when it starts. +Set "Digital Tx0 Input" to "pulse: Use name QuiskDigitalInput". +Set "Digital Rx0 Output" to "pulse: Use name QuiskDigitalOutput.monitor". +These names are on the drop down list for the sound device. +In your digital program, connect the digital +input to QuiskDigitalOutput.monitor and the digital output to QuiskDigitalInput. +These names will be on the sound menu of the digital program, and you should be receiving and transmitting digital data. +Remember to select one of the DGT-* modes. +
+
+Fldigi only has a PulseAudio check box, and there is no way to set the proper device. +In this case, first install the program pavucontrol to control PulseAudio. This is a useful program to +control and understand PulseAudio even if you are not using digital. Set the Quisk devices as above. +Now start both Quisk and Fldigi, and then pavucontrol. The Playback and Recording screens in pavucontrol will +show the devices being used. Change the Fldigi playback to QuiskDigitalInput, and the Fldigi recording to +Monitor of QuiskDigitalOutput. Then everything should work. You do not need to use pavucontrol again because PulseAudio will +remember the settings. +
+ +
+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. +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 +another side that connects to your digital program. For the Quisk +side connect both Digital Input and Digital Output to Loopback,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". +Your audio is now connected and you +should be able to receive digital signals. Be sure to test your +transmit signal off the air. You may need to reduce power to +improve linearity. +
+ +
+

+

Rig Control and Logging

+

+Digital mode and logging programs need to control Quisk to read and set the frequency and mode and to operate PTT. +Quisk has three options for external control and they can all be used together to connect to multiple programs. +See the Config/radio/Remote screen. +See http://james.ahlstrom.name/hamlib.html for more information. +

+To connect an external program to Quisk using Hamlib, configure your program to use "Hamlib NET rigctl" (rig 2). +Then go to the Quisk "Remote" config screen for your radio and set +"IP address for Hamlib Rig 2" to "localhost", and set +"IP port for Hamlib" to 4532. This assumes you are not using the rigctld daemon program. If you are, +set the Quisk port to 4575 and tell rigctld to control quisk on port 4575. +Now changing the frequency on one program will change the other. +Keying Quisk to key down (however you do that with your hardware) will +set the external program to Transmit. Pressing the PTT control in the external program will +also press the PTT button in Quisk. +

+You can also control Quisk from another program by using the XML-RPC method if this is available +in your program. Fldigi can use this method. +

+If your program only uses a serial port (N1MM+) then +use Hamlib with the rig set to "Flex" and connect to the Quisk serial port set on the Remote screen. +For Linux, Quisk can set up these ports itself, and they have names like "/tmp/QuiskTTY0". On Windows +you need a "Virtual Serial Port" that is set up by an external program. This is like the "Virtual Audio Cable" +needed for samples. An Internet search will turn up HDD Software, Eltima Software and many others. Set up a port pair, +and enter one name on the Quisk Remote screen and the other name in the external program. +
+
+

+

WSJT-X

+

+Quisk has special support for the digital program WSJT-X. +There is a button to start WSJT-X on the Config/Config screen. Look for "Start WSJT-X" and select "Main Rx0 now". +This starts WSJT-X with radio "quisk" and you will see "WSJT-X - quisk" in the title bar. +Then under WSJT-X File/Settings set up the Radio and Audio as described above. +For Linux, use the pulseaudio devices: +

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Quisk NameQuisk ValueWSJT-X NameWSJT-X Value
IP address for Hamlib Rig 2localhostRadio RigHamlib NET rigctl
IP port for Hamlib4532Network Serverlocalhost:4532
Digital Tx0 InputUse name QuiskDigitalInputAudio Soundcard OutputQuiskDigitalInput
Digital Rx0 OutputUse name QuiskDigitalOutput.monitorAudio Soundcard InputQuiskDigitalOutput.monitor
+

+

Vector Network Analyzer

+

+If you have my transceiver hardware from 2010 QEX, or the newer HiQSDR +hardware you can use it as a vector network analyzer by using a +special program. As of 2021 the HL2 no longer works as a VNA. This may change in the future. +Run the VNA program with "python quisk_vna.py" or use a +shortcut. The VNA program will not work with SoftRock or other hardware. +This VNA program enables you to analyze your antennas without additional expense. +
+ +
+A calibration run must be taken before any data can be obtained. +The calibrations request a scan of data points every 15 killohertz from +zero to 60 megahertz, or a little over 4000 points. These data +are saved so that the scan frequencies can be changed without a new +calibration. For any start and end scan frequency the user +chooses, these saved calibrations are used with linear interpolation. +
+ +
+

+

HiQSDR Internals

+

+When running in VNA mode the two control bytes [18:20] are the 16-bit +non-zero VNA count "vna_count", the number of data points to +send. This locks the transmit and receive frequency to the +same value. The phases are also equal except for a fixed time +delay, which causes a linear change of phase with frequency. The +starting frequency is the receive frequency (actually phase) +rx_phase. Subsequent points have the transmit phase tx_phase +added to create a frequency scan. Specifically, after each data +point, the tx_phase is added to create the RF output at the next +frequency; then there is a pause of 65 microseconds to allow the +external device under test to stabilize; then 4096 data points are +added together to create the sample; then the sample is added to the +block of data to send by UDP. A sample of zero is sent after the +last data point, and the process repeats. The receiving software +must look for the zero sample that marks the start of a new scan. +The total number of points in the scan is vna_count, and blocks +received with a different length should be rejected. +Since the transmit and receive frequency are the same, the data points +are I/Q values at DC; that is, a complex number representing a voltage +and phase. +If vna_count is zero, the firmware is operating normally, and not in VNA mode. +
+
+ +

+

Remote Operation

+

+Ben, AC2YD, contributed a feature that enables you to run a radio at a remote location from a control +head. The remote radio could be on your local area network or somewhere on the Internet. The remote radio +is connected to a computer running Quisk, the computer could run Linux or Windows, and it could be +a single-board computer such as Raspberry Pi. The two computers communicate using the TCP and UDP protocols. +You can use either a wired connection or WiFi. +The feature uses TCP port 4585 and two UDP ports 4586 and 4587. These must be open at the remote radio. +It should not be necessary to open ports at the control head as it initiates all communication. +A paper describing the original design is here. +
+
+Nigel, G4ZAL wrote a quick guide here. +

+
+

+The version of Quisk running on the control head must exactly match the version at the remote radio. +Also, each type of radio has its own remote hardware file and control head hardware file. The supported +radios are softrock, hermes and hiqsdr. +

+
+

+If you have dropouts in the sound, try increasing the "Play latency msec" on the Config/Timing screen. Quisk +buffers sound, and the buffer size can be adjusted. +

+
+

+Create a radio of type "Control Head", and on the Radio/Remote screen enter the IP address or host name +of the remote radio and a password. Change the hardware file to ac2yd/control_softrock.py or +ac2yd/control_hermes.py or ac2yd/control_hiqsdr.py. Enter the usual widget file for that radio if any. +Create sound devices for radio sound and microphone sound. Do not create devices for samples or digital modes. +

+
+

+Create a radio of the correct type at the remote site. This must be softrock, hermes or hiqsdr. +Configure the radio to correctly control the hardware. You should be able to operate successfully at the remote. +Then change the hardware file to ac2yd/remote_softrock.py or ac2yd/remote_hermes.py or ac2yd/remote_hiqsdr.py. Enter the usual +widget file if any. The radio will still operate normally unless the control head is connected. Enter +the same password on the Radio/Remote screen. The password only needs to be entered once at each end. +It is a "shared secret". Use a somewhat lengthy pass phrase of random words, at least 20 characters long. +

+
+

+You can use Windows, Mac or Linux at either end. But for Windows, the Windows Firewall will interfere. +Do not turn the firewall off. Change the network profile type from the default Public to Private if +you are on a private home network. The setting is under Settings, Network, WiFi or Ethernet, Network Profile. +If you are on a public network, you should use a VPN for security. +

+
+

+For security, Quisk will not accept remote operation unless the special hardware files are used. And the +password or pass phrase must match. But you must (as usual) make sure both computers are secure and can not +be broken into. You must (as usual) have a good router password and secure settings to protect your home network. +

+
+

+The AC2YD remote feature was designed to send back demodulated audio, not samples, in order to reduce the +bandwidth to a minimum. Running digital modes on the control head would require more data streams for +digital in/out. Also, note that recording samples to a file fails for the same reason. And most hardware +config settings are inoperative on the control head. I can add some more interaction like a clip indicator, +but complete control, such as adding recording samples, is a never-ending list. +

+
+

+Because of the low bandwidth and the time stamps on the CW keying, the AC2YD feature achieves fast classic ham +radio over a network. For digital modes, it seems workable to run everything on the remote and control it with +remote terminal software because fast response is not required. +

+
+
+ + + + + diff --git a/dxcluster.py b/dxcluster.py new file mode 100644 index 0000000..eff52d7 --- /dev/null +++ b/dxcluster.py @@ -0,0 +1,141 @@ +from __future__ import absolute_import +# This code was contributed by Christof, DJ4CM. Many Thanks!! + +import threading +import time +import telnetlib +import quisk_conf_defaults as conf + +class DxEntry: + def __init__(self): + self.info = [] + + def getFreq(self): + return self.freq + + def getDX(self): + return self.dx + + def getSpotter(self, index): + return self.info[index][0] + + def getTime(self, index): + return self.info[index][1] + + def getLocation(self, index): + return self.info[index][2] + + def getComment(self, index): + return self.info[index][3] + + def getLen(self): + return len(self.info) + + def equal(self, element): + if element.getDX() == self.dx: + return True + else: + return False + + def join (self, element): + for i in range (0, len(element.info)): + self.info.insert(0, element.info[i]) + length = len(self.info) + # limit to max history + if length > 3: + del (self.info[length-1]) + self.timestamp = max (self.timestamp, element.timestamp) + + def isExpired(self): + return time.time()-self.timestamp > conf.dxClExpireTime * 60 + + def parseMessage(self, message): + words = message.split() + sTime = '' + locator = '' + comment = '' + if len(words) > 3 and words[0].lower() == 'dx' and words[1].lower() == 'de': + spotter = words[2].strip(':') + self.freq = int(float(words[3])*1000) + self.dx = words[4] + for index in range (5, len(words)): + word = words[index] + try: + if sTime != '': + locator = word.strip('\07') + #search time + if word[0:3].isdigit() and word[4].isalpha(): + sTime = word.strip('\07') + sTime = sTime[0:2]+':'+sTime[2:4]+ ' UTC' + if sTime == '': + if comment != '': + comment += ' ' + comment += word + except: + pass + self.info.insert(0, (spotter, sTime, locator, comment)) + self.timestamp = time.time() + #print(self.dx, self.freq, spotter, sTime, locator, comment) + return True + return False + +class DxCluster(threading.Thread): + def __init__(self): + self.do_init = 1 + threading.Thread.__init__(self) + self.doQuit = threading.Event() + self.dxSpots = [] + self.doQuit.clear() + + def run(self): + self.telnetInit() + self.telnetConnect() + while not self.doQuit.isSet(): + try: + self.telnetRead() + except: + self.tn.close() + time.sleep(20) + if not self.doQuit.isSet(): + self.telnetConnect() + self.tn.close() + + def setListener (self, listener): + self.listener = listener + + def telnetInit(self): + self.tn = telnetlib.Telnet() + + def telnetConnect(self): + for i in range(10): + try: + self.tn.open(conf.dxClHost, conf.dxClPort, 10) + self.tn.read_until(b"login:", 10) + self.tn.write(conf.user_call_sign.encode('utf-8', errors='ignore') + b"\n") # user_call_sign may be Unicode + break + except: + time.sleep(0.5) + if conf.dxClPassword: + self.tn.read_until(b"Password: ") + self.tn.write(conf.dxClPassword.encode('utf-8', errors='ignore') + b"\n") + + def telnetRead(self): + message = self.tn.read_until(b'\n', 60).decode(encoding='utf-8', errors='replace') + if self.doQuit.isSet() == False: + dxEntry = DxEntry(); + if dxEntry.parseMessage(message): + for i, listElement in enumerate(self.dxSpots): + if (listElement.equal(dxEntry)): + listElement.join (dxEntry) + return + if listElement.isExpired(): + del (self.dxSpots[i]) + self.dxSpots.append(dxEntry) + if self.listener: + self.listener() + + def getHost(self): + return self.tn.host + ':' + str(self.tn.port) + + def stop(self): + self.doQuit.set() diff --git a/extdemod.c b/extdemod.c new file mode 100644 index 0000000..26636d4 --- /dev/null +++ b/extdemod.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include "quisk.h" + +// If you set add_extern_demod in your config file, you will get another +// button that will call this module. Change it to what you want. Save +// a copy because new releases of Quisk will overwrite this file. +// +// NOTE: NEW RELEASES OF QUISK WILL OVERWRITE THIS FILE! + +int quisk_extern_demod(complex double * cSamples, int nSamples, double decim) +{ // Filter and demodulate the I/Q samples into audio play samples. +// cSamples: The input I/Q samples, and the output stereo play samples. +// nSamples: The number of input samples; maximum is SAMP_BUFFER_SIZE. +// decim: The decimation needed (1.0 for no decimation). +// The output play samples are stereo, and are placed into cSamples. +// The return value is the number of output samples = nSamples / decim. +// See quisk.h for useful data in quisk_sound_state. For example, the +// sample rate is quisk_sound_state.sample_rate. If you need decimation, +// look at iDecimate() and fDecimate() in quisk.c. + + int i; + double d, di; + complex double cx; + static complex double fm_1 = 10; // Sample delayed by one + static complex double fm_2 = 10; // Sample delayed by two + + if (fabs (decim - 1.0) > 0.001) // no provision for decimation + return 0; + + for (i = 0; i < nSamples; i++) { // narrow FM + cx = cSamples[i]; + di = creal(fm_1) * (cimag(cx) - cimag(fm_2)) - + cimag(fm_1) * (creal(cx) - creal(fm_2)); + d = creal(fm_1) * creal(fm_1) + cimag(fm_1) * cimag(fm_1); + if (d == 0) // I don't think this can happen + di = 0; + else + di = di / d * quisk_sound_state.sample_rate; + fm_2 = fm_1; // fm_2 is sample cSamples[i - 2] + fm_1 = cx; // fm_1 is sample cSamples[i - 1] + cSamples[i] = di + I * di; // monophonic sound, two channels + } + return nSamples; // Number of play samples +} diff --git a/filter.c b/filter.c new file mode 100644 index 0000000..670d2c9 --- /dev/null +++ b/filter.c @@ -0,0 +1,489 @@ +#include +#include +#include +#include // Use native C99 complex type for fftw3 +#include "quisk.h" +#include "filter.h" +#include "filters.h" + +void quisk_filt_cInit(struct quisk_cFilter * filter, double * coefs, int taps) +{ // Prepare a new filter using coefs and taps. Samples are complex. + filter->dCoefs = coefs; + filter->cpxCoefs = NULL; + filter->cSamples = (complex double *)malloc(taps * sizeof(complex double)); + memset(filter->cSamples, 0, taps * sizeof(complex double)); + filter->ptcSamp = filter->cSamples; + filter->nTaps = taps; + filter->decim_index = 0; + filter->cBuf = NULL; + filter->nBuf = 0; +} + +void quisk_filt_dInit(struct quisk_dFilter * filter, double * coefs, int taps) +{ // Prepare a new filter using coefs and taps. Samples are double. + filter->dCoefs = coefs; + filter->cpxCoefs = NULL; + filter->dSamples = (double *)malloc(taps * sizeof(double)); + memset(filter->dSamples, 0, taps * sizeof(double)); + filter->ptdSamp = filter->dSamples; + filter->nTaps = taps; + filter->decim_index = 0; + filter->dBuf = NULL; + filter->nBuf = 0; +} + +void quisk_filt_differInit(struct quisk_dFilter * filter, int taps) +{ // Prepare a new classic differentiating filter. taps must be odd. + int j, k; + + filter->dCoefs = (double *)malloc(taps * sizeof(double)); + for (k = - (taps - 1) / 2; k <= (taps - 1) / 2; k++) { + j = (taps - 1) / 2 + k; + if (k == 0) + filter->dCoefs[j] = 0; + else + filter->dCoefs[j] = pow(-1, k) / k; + printf("%4d taps %8.4lf\n", j, filter->dCoefs[j]); + } + filter->cpxCoefs = NULL; + filter->dSamples = (double *)malloc(taps * sizeof(double)); + memset(filter->dSamples, 0, taps * sizeof(double)); + filter->ptdSamp = filter->dSamples; + filter->nTaps = taps; + filter->decim_index = 0; + filter->dBuf = NULL; + filter->nBuf = 0; +} + +void quisk_filt_tune(struct quisk_dFilter * filter, double freq, int ssb_upper) +{ // Tune a filter into an analytic I/Q filter with complex coefficients. + // freq is the center frequency / sample rate. Reverse coef if ssb_upper == 0. + // This is used for both quisk_dFilter and quisk_cFilter with a cast. + // Filter can be re-tuned repeatedly. + // + // The tuned low pass filter has a loss of 0.5 when applied to real signals. + // There is no loss applied to complex signals. Coeffs of the tuned filter are not symetric(??). + int i; + complex double coef, tune; + double D; + + if ( ! filter->cpxCoefs) + filter->cpxCoefs = (complex double *)malloc(filter->nTaps * sizeof(complex double)); + tune = I * 2.0 * M_PI * freq; + D = (filter->nTaps - 1.0) / 2.0; + for (i = 0; i < filter->nTaps; i++) { + coef = cexp(tune * (i - D)) * filter->dCoefs[i]; + if (ssb_upper) + filter->cpxCoefs[i] = coef; + else + filter->cpxCoefs[i] = cimag(coef) + I * creal(coef); + } +} + +complex double quisk_dC_out(double sample, struct quisk_dFilter * filter) +{ + complex double csample; + complex double * ptCoef; + double * ptSample; + int k; + + // FIR bandpass filter; separate double sample into I and Q. + // Put samples into buffer left to right. Use samples right to left. + ptSample = filter->ptdSamp; + *ptSample = sample; + ptCoef = filter->cpxCoefs; + csample = 0; + for (k = 0; k < filter->nTaps; k++, ptCoef++) { + csample += *ptSample * *ptCoef; + if (--ptSample < filter->dSamples) + ptSample = filter->dSamples + filter->nTaps - 1; + } + if (++filter->ptdSamp >= filter->dSamples + filter->nTaps) + filter->ptdSamp = filter->dSamples; + return csample; +} + +#if 0 +complex double quisk_cC_out(complex double sample, struct quisk_cFilter * filter) +{ + complex double csample; + complex double * ptCoef; + complex double * ptSample; + int k; + + // FIR bandpass filter; filter complex samples by complex coeffs. + // Put samples into buffer left to right. Use samples right to left. + ptSample = filter->ptcSamp; + *ptSample = sample; + ptCoef = filter->cpxCoefs; + csample = 0; + for (k = 0; k < filter->nTaps; k++, ptCoef++) { + csample += *ptSample * *ptCoef; + if (--ptSample < filter->cSamples) + ptSample = filter->cSamples + filter->nTaps - 1; + } + if (++filter->ptcSamp >= filter->cSamples + filter->nTaps) + filter->ptcSamp = filter->cSamples; + return csample; +} +#endif + +int quisk_cInterpolate(complex double * cSamples, int count, struct quisk_cFilter * filter, int interp) +{ // This uses the double coefficients of filter (not the complex). Samples are complex. + int i, j, k, nOut; + double * ptCoef; + complex double * ptSample; + complex double csample; + + if (count > filter->nBuf) { // increase size of sample buffer + filter->nBuf = count * 2; + if (filter->cBuf) + free(filter->cBuf); + filter->cBuf = (complex double *)malloc(filter->nBuf * sizeof(complex double)); + } + memcpy(filter->cBuf, cSamples, count * sizeof(complex double)); + nOut = 0; + for (i = 0; i < count; i++) { + // Put samples into buffer left to right. Use samples right to left. + *filter->ptcSamp = filter->cBuf[i]; + for (j = 0; j < interp; j++) { + ptSample = filter->ptcSamp; + ptCoef = filter->dCoefs + j; + csample = 0; + for (k = 0; k < filter->nTaps / interp; k++, ptCoef += interp) { + csample += *ptSample * *ptCoef; + if (--ptSample < filter->cSamples) + ptSample = filter->cSamples + filter->nTaps - 1; + } + if (nOut < SAMP_BUFFER_SIZE * 8 / 10) + cSamples[nOut++] = csample * interp; + } + if (++filter->ptcSamp >= filter->cSamples + filter->nTaps) + filter->ptcSamp = filter->cSamples; + } + return nOut; +} + +int quisk_dInterpolate(double * dSamples, int count, struct quisk_dFilter * filter, int interp) +{ // This uses the double coefficients of filter (not the complex). Samples are double. + int i, j, k, nOut; + double * ptCoef; + double * ptSample; + double dsample; + + if (count > filter->nBuf) { // increase size of sample buffer + filter->nBuf = count * 2; + if (filter->dBuf) + free(filter->dBuf); + filter->dBuf = (double *)malloc(filter->nBuf * sizeof(double)); + } + memcpy(filter->dBuf, dSamples, count * sizeof(double)); + nOut = 0; + for (i = 0; i < count; i++) { + // Put samples into buffer left to right. Use samples right to left. + *filter->ptdSamp = filter->dBuf[i]; + for (j = 0; j < interp; j++) { + ptSample = filter->ptdSamp; + ptCoef = filter->dCoefs + j; + dsample = 0; + for (k = 0; k < filter->nTaps / interp; k++, ptCoef += interp) { + dsample += *ptSample * *ptCoef; + if (--ptSample < filter->dSamples) + ptSample = filter->dSamples + filter->nTaps - 1; + } + if (nOut < SAMP_BUFFER_SIZE * 8 / 10) + dSamples[nOut++] = dsample * interp; + } + if (++filter->ptdSamp >= filter->dSamples + filter->nTaps) + filter->ptdSamp = filter->dSamples; + } + return nOut; +} + +int quisk_cDecimate(complex double * cSamples, int count, struct quisk_cFilter * filter, int decim) +{ // This uses the double coefficients of filter (not the complex). + int i, k, nOut; + complex double * ptSample; + double * ptCoef; + complex double csample; + + nOut = 0; + for (i = 0; i < count; i++) { + *filter->ptcSamp = cSamples[i]; + if (++filter->decim_index >= decim) { + filter->decim_index = 0; // output a sample + csample = 0; + ptSample = filter->ptcSamp; + ptCoef = filter->dCoefs; + for (k = 0; k < filter->nTaps; k++, ptCoef++) { + csample += *ptSample * *ptCoef; + if (--ptSample < filter->cSamples) + ptSample = filter->cSamples + filter->nTaps - 1; + } + cSamples[nOut++] = csample; + } + if (++filter->ptcSamp >= filter->cSamples + filter->nTaps) + filter->ptcSamp = filter->cSamples; + } + return nOut; +} + +int quisk_cCDecimate(complex double * cSamples, int count, struct quisk_cFilter * filter, int decim) +{ // This uses the complex coefficients of filter (not the double). Call quisk_filt_tune() first. + int i, k, nOut; + complex double * ptSample; + complex double * ptCoef; + complex double csample; + + nOut = 0; + for (i = 0; i < count; i++) { + *filter->ptcSamp = cSamples[i]; + if (++filter->decim_index >= decim) { + filter->decim_index = 0; // output a sample + csample = 0; + ptSample = filter->ptcSamp; + ptCoef = filter->cpxCoefs; + for (k = 0; k < filter->nTaps; k++, ptCoef++) { + csample += *ptSample * *ptCoef; + if (--ptSample < filter->cSamples) + ptSample = filter->cSamples + filter->nTaps - 1; + } + cSamples[nOut++] = csample; + } + if (++filter->ptcSamp >= filter->cSamples + filter->nTaps) + filter->ptcSamp = filter->cSamples; + } + return nOut; +} + +int quisk_dDecimate(double * dSamples, int count, struct quisk_dFilter * filter, int decim) +{ // This uses the double coefficients of filter (not the complex). + int i, k, nOut; + double * ptSample; + double * ptCoef; + double dsample; + + nOut = 0; + for (i = 0; i < count; i++) { + *filter->ptdSamp = dSamples[i]; + if (++filter->decim_index >= decim) { + filter->decim_index = 0; // output a sample + dsample = 0; + ptSample = filter->ptdSamp; + ptCoef = filter->dCoefs; + for (k = 0; k < filter->nTaps; k++, ptCoef++) { + dsample += *ptSample * *ptCoef; + if (--ptSample < filter->dSamples) + ptSample = filter->dSamples + filter->nTaps - 1; + } + dSamples[nOut++] = dsample; + } + if (++filter->ptdSamp >= filter->dSamples + filter->nTaps) + filter->ptdSamp = filter->dSamples; + } + return nOut; +} + +int quisk_cInterpDecim(complex double * cSamples, int count, struct quisk_cFilter * filter, int interp, int decim) +{ // Interpolate by interp, and then decimate by decim. + // This uses the double coefficients of filter (not the complex). Samples are complex. + int i, k, nOut; + double * ptCoef; + complex double * ptSample; + complex double csample; + + if (count > filter->nBuf) { // increase size of sample buffer + filter->nBuf = count * 2; + if (filter->cBuf) + free(filter->cBuf); + filter->cBuf = (complex double *)malloc(filter->nBuf * sizeof(complex double)); + } + memcpy(filter->cBuf, cSamples, count * sizeof(complex double)); + nOut = 0; + for (i = 0; i < count; i++) { + // Put samples into buffer left to right. Use samples right to left. + *filter->ptcSamp = filter->cBuf[i]; + while (filter->decim_index < interp) { + ptSample = filter->ptcSamp; + ptCoef = filter->dCoefs + filter->decim_index; + csample = 0; + for (k = 0; k < filter->nTaps / interp; k++, ptCoef += interp) { + csample += *ptSample * *ptCoef; + if (--ptSample < filter->cSamples) + ptSample = filter->cSamples + filter->nTaps - 1; + } + if (nOut < SAMP_BUFFER_SIZE * 8 / 10) + cSamples[nOut++] = csample * interp; + filter->decim_index += decim; + } + if (++filter->ptcSamp >= filter->cSamples + filter->nTaps) + filter->ptcSamp = filter->cSamples; + filter->decim_index = filter->decim_index - interp; + } + return nOut; +} + +double quisk_dD_out(double samp, struct quisk_dFilter * filter) +{ // Filter double samples. + int k; + double * ptSample; + double * ptCoef; + double dsample; + + *filter->ptdSamp = samp; + dsample = 0; + ptSample = filter->ptdSamp; + ptCoef = filter->dCoefs; + for (k = 0; k < filter->nTaps; k++, ptCoef++) { + dsample += *ptSample * *ptCoef; + if (--ptSample < filter->dSamples) + ptSample = filter->dSamples + filter->nTaps - 1; + } + if (++filter->ptdSamp >= filter->dSamples + filter->nTaps) + filter->ptdSamp = filter->dSamples; + return dsample; +} + +int quisk_dFilter(double * dSamples, int count, struct quisk_dFilter * filter) +{ // Filter double samples. + int i, k, nOut; + double * ptSample; + double * ptCoef; + double dsample; + + nOut = 0; + for (i = 0; i < count; i++) { + *filter->ptdSamp = dSamples[i]; + dsample = 0; + ptSample = filter->ptdSamp; + ptCoef = filter->dCoefs; + for (k = 0; k < filter->nTaps; k++, ptCoef++) { + dsample += *ptSample * *ptCoef; + if (--ptSample < filter->dSamples) + ptSample = filter->dSamples + filter->nTaps - 1; + } + dSamples[nOut++] = dsample; + if (++filter->ptdSamp >= filter->dSamples + filter->nTaps) + filter->ptdSamp = filter->dSamples; + } + return nOut; +} + +int quisk_cFilter(complex double * cSamples, int count, struct quisk_cFilter * filter) +{ // Filter complex samples using the double coefficients of filter (not the complex). + return quisk_cDecimate(cSamples, count, filter, 1); +} + +int quisk_cDecim2HB45(complex double * cSamples, int count, struct quisk_cHB45Filter * filter) +{ // This uses the double coefficients of filter (not the complex). +// Half band filter, sample rate 96 Hz, pass 16, center 24, stop 32, good BW 2/3, 45 taps. + int i, nOut; + complex double * samples, * center; + static double coef[12] = { 0.000018566625444266, -0.000118469698701817, 0.000457318798253456, + -0.001347840471412094, 0.003321838571445455, -0.007198422696929033, 0.014211106939802483, + -0.026424776824073383, 0.048414810444971007, -0.096214669073304823, 0.314881034738348550, + 0.500000000000000000 }; // Rate 96, cutoff 16-24-32, atten 120 dB. Coef[0] and [44] are zero. + + nOut = 0; + samples = filter->samples; + center = filter->center; + for (i = 0; i < count; i++) { + if (filter->toggle == 0){ + filter->toggle = 1; + memmove(center + 1, center, sizeof(complex double) * 10); + center[0] = cSamples[i]; + } + else { + filter->toggle = 0; + memmove(samples + 1, samples, sizeof(complex double) * 21); + samples[0] = cSamples[i]; + // output a sample + cSamples[nOut++] = + (samples[ 0] + samples[21]) * coef[0] + + (samples[ 1] + samples[20]) * coef[1] + + (samples[ 2] + samples[19]) * coef[2] + + (samples[ 3] + samples[18]) * coef[3] + + (samples[ 4] + samples[17]) * coef[4] + + (samples[ 5] + samples[16]) * coef[5] + + (samples[ 6] + samples[15]) * coef[6] + + (samples[ 7] + samples[14]) * coef[7] + + (samples[ 8] + samples[13]) * coef[8] + + (samples[ 9] + samples[12]) * coef[9] + + (samples[10] + samples[11]) * coef[10] + + center[10] * coef[11]; + } + } + return nOut; +} + + +int quisk_dInterp2HB45(double * dsamples, int count, struct quisk_dHB45Filter * filter) +{ // Half-Band interpolation by 2 + int i, k, nOut, nCoef, nSamp; + double out; + double * samples; + static double coef[12] = { 0.000018566625444266, -0.000118469698701817, 0.000457318798253456, + -0.001347840471412094, 0.003321838571445455, -0.007198422696929033, 0.014211106939802483, + -0.026424776824073383, 0.048414810444971007, -0.096214669073304823, 0.314881034738348550, + 0.500000000000000000 }; // Rate 96, cutoff 16-24-32, atten 120 dB. Coef[0] and [44] are zero. + + if (count > filter->nBuf) { // increase size of sample buffer + filter->nBuf = count * 2; + if (filter->dBuf) + free(filter->dBuf); + filter->dBuf = (double *)malloc(filter->nBuf * sizeof(double)); + } + nCoef = 12; + nSamp = (nCoef - 1) * 2; + memcpy(filter->dBuf, dsamples, count * sizeof(double)); + samples = filter->samples; + nOut = 0; + for (i = 0; i < count; i++) { + memmove(samples + 1, samples, (nSamp - 1) * sizeof(double)); + samples[0] = filter->dBuf[i]; + if (nOut > SAMP_BUFFER_SIZE * 8 / 10) + continue; + dsamples[nOut++] = samples[nCoef - 1] * coef[nCoef - 1] * 2; + out = 0; + for (k = 0; k < nSamp / 2; k++) + out += (samples[k] + samples[nSamp - 1 - k]) * coef[k]; + dsamples[nOut++] = out * 2; + } + return nOut; +} + +int quisk_cInterp2HB45(complex double * cSamples, int count, struct quisk_cHB45Filter * filter) +{ // Half-Band interpolation by 2 + int i, k, nOut, nCoef, nSamp; + complex double out; + complex double * samples; + static double coef[12] = { 0.000018566625444266, -0.000118469698701817, 0.000457318798253456, + -0.001347840471412094, 0.003321838571445455, -0.007198422696929033, 0.014211106939802483, + -0.026424776824073383, 0.048414810444971007, -0.096214669073304823, 0.314881034738348550, + 0.500000000000000000 }; // Rate 96, cutoff 16-24-32, atten 120 dB. Coef[0] and [44] are zero. + + if (count > filter->nBuf) { // increase size of sample buffer + filter->nBuf = count * 2; + if (filter->cBuf) + free(filter->cBuf); + filter->cBuf = (complex double *)malloc(filter->nBuf * sizeof(complex double)); + } + nCoef = 12; + nSamp = (nCoef - 1) * 2; + memcpy(filter->cBuf, cSamples, count * sizeof(complex double)); + samples = filter->samples; + nOut = 0; + for (i = 0; i < count; i++) { + memmove(samples + 1, samples, (nSamp - 1) * sizeof(complex double)); + samples[0] = filter->cBuf[i]; + if (nOut > SAMP_BUFFER_SIZE * 8 / 10) + continue; + cSamples[nOut++] = samples[nCoef - 1] * coef[nCoef - 1] * 2; + out = 0; + for (k = 0; k < nSamp / 2; k++) + out += (samples[k] + samples[nSamp - 1 - k]) * coef[k]; + cSamples[nOut++] = out * 2; + } + return nOut; +} + diff --git a/filter.h b/filter.h new file mode 100644 index 0000000..f806f25 --- /dev/null +++ b/filter.h @@ -0,0 +1,86 @@ +struct quisk_cFilter { + double * dCoefs; // filter coefficients + complex double * cpxCoefs; // make the complex coefficients from dCoefs + int nBuf; // dimension of cBuf + int nTaps; // dimension of dSamples, cSamples, dCoefs and cpxCoefs + int decim_index; // used to count samples for decimation + complex double * cSamples; // storage for old samples + complex double * ptcSamp; // next available position in cSamples + complex double * cBuf; // auxillary buffer for interpolation +} ; + +struct quisk_dFilter { + double * dCoefs; // filter coefficients + complex double * cpxCoefs; // make the complex coefficients from dCoefs + int nBuf; // dimension of dBuf + int nTaps; // dimension of dSamples, cSamples, dCoefs and cpxCoefs + int decim_index; // used to count samples for decimation + double * dSamples; // storage for old samples + double * ptdSamp; // next available position in dSamples + double * dBuf; // auxillary buffer for interpolation +} ; + +struct quisk_cHB45Filter { // Complex half band decimate by 2 filter with 45 coefficients + complex double * cBuf; // auxillary buffer for interpolation + int nBuf; // dimension of cBuf + int toggle; + complex double samples[22]; + complex double center[11]; +} ; + +struct quisk_dHB45Filter { // Real half band decimate by 2 filter with 45 coefficients + double * dBuf; // auxillary buffer for interpolation + int nBuf; // dimension of dBuf + int toggle; + double samples[22]; + double center[11]; +} ; + +void quisk_filt_cInit(struct quisk_cFilter *, double *, int); +void quisk_filt_dInit(struct quisk_dFilter *, double *, int); +void quisk_filt_differInit(struct quisk_dFilter *, int); +void quisk_filt_tune(struct quisk_dFilter *, double, int); +complex double quisk_dC_out(double, struct quisk_dFilter *); +double quisk_dD_out(double, struct quisk_dFilter *); +int quisk_cInterpolate(complex double *, int, struct quisk_cFilter *, int); +int quisk_dInterpolate(double *, int, struct quisk_dFilter *, int); +int quisk_cDecimate(complex double *, int, struct quisk_cFilter *, int); +int quisk_cCDecimate(complex double *, int, struct quisk_cFilter *, int); +int quisk_dDecimate(double *, int, struct quisk_dFilter *, int); +int quisk_cInterpDecim(complex double *, int, struct quisk_cFilter *, int, int); +int quisk_cDecim2HB45(complex double *, int, struct quisk_cHB45Filter *); +int quisk_dInterp2HB45(double *, int, struct quisk_dHB45Filter *); +int quisk_cInterp2HB45(complex double *, int, struct quisk_cHB45Filter *); +int quisk_dFilter(double *, int, struct quisk_dFilter *); +int quisk_cFilter(complex double *, int, struct quisk_cFilter *); + +extern double quiskMicFilt48Coefs[325]; +extern double quiskMic5Filt48Coefs[424]; +extern double quiskMicFilt8Coefs[93]; +extern double quiskLpFilt48Coefs[186]; +extern double quiskFilt12_19Coefs[64]; +extern double quiskFilt185D3Coefs[189]; +extern double quiskFilt133D2Coefs[136]; +extern double quiskFilt167D3Coefs[174]; +extern double quiskFilt111D2Coefs[114]; +extern double quiskFilt53D1Coefs[55]; +extern double quiskFilt53D2Coefs[93]; +extern double quiskFilt144D3Coefs[147]; +extern double quiskFilt240D5Coefs[115]; +extern double quiskFilt240D5CoefsSharp[245]; +extern double quiskFilt48dec24Coefs[98]; +extern double quiskAudio24p6Coefs[36]; +extern double quiskAudio48p6Coefs[71]; +extern double quiskAudio96Coefs[11]; +extern double quiskAudio24p4Coefs[50]; +extern double quiskAudioFmHpCoefs[309]; +extern double quiskAudio24p3Coefs[100]; +extern double quiskFiltTx8kAudioB[168]; +extern double quiskFilt16dec8Coefs[62]; +extern double quiskFilt120s03[480]; +extern double quiskFiltI3D25Coefs[825]; +extern double quiskDgtFilt48Coefs[520]; +extern double quiskFilt300D5Coefs[125]; +extern double quiskFilt300D6Coefs[248]; +extern double quiskFilt240D4Coefs[100]; +extern double quiskDiff48Coefs[38]; diff --git a/filters.h b/filters.h new file mode 100644 index 0000000..957ef7f --- /dev/null +++ b/filters.h @@ -0,0 +1,1308 @@ +// Sample 48000 Hz, pass 1350, stop 1750, ripple 1 dB, atten 80 dB (coef_tx). Stop 0.036458. +double quiskMicFilt48Coefs[325] = { + 0.000070000950338734, 0.000060542525732639, 0.000084830915167248, 0.000113820318991206, 0.000147523658260692, + 0.000185727097247136, 0.000228017694264326, 0.000273714746744222, 0.000321902242967596, 0.000371355069437399, + 0.000420595492180569, 0.000467865412686539, 0.000511235564174107, 0.000548584697295435, 0.000577683697404093, + 0.000596168573595124, 0.000601725499419238, 0.000592142083607333, 0.000565459097743531, 0.000519884471131262, + 0.000454031497680723, 0.000367015380281216, 0.000258599355817496, 0.000128888056076112, -0.000021074826181109, +-0.000189585802186493, -0.000374389987374961, -0.000572207780852182, -0.000779359325040342, -0.000991335380833019, +-0.001203244280115714, -0.001409670281637694, -0.001605003575053789, -0.001783471817219780, -0.001939438753998784, +-0.002067470485014850, -0.002162628068669760, -0.002220597743425130, -0.002237960051673301, -0.002212264773625852, +-0.002142247186138010, -0.002027907322210780, -0.001870648667784210, -0.001673216059663119, -0.001439782952032160, +-0.001175849407968695, -0.000888138367181945, -0.000584400564491252, -0.000273350274681290, 0.000035800336978808, + 0.000333410168961052, 0.000609868204837889, 0.000855845198751289, 0.001062645743133108, 0.001222567778915907, + 0.001329178561171123, 0.001377619146269160, 0.001364836763234778, 0.001289791422827911, 0.001153569049793089, + 0.000959442606127223, 0.000712858984801055, 0.000421343499696318, 0.000094311703309987, -0.000257178812726900, +-0.000620740024523200, -0.000983067401076951, -0.001330378208545664, -0.001648868642029554, -0.001925231739749957, +-0.002147185427808426, -0.002303924955683100, -0.002386617358290471, -0.002388809260074332, -0.002306735130576522, +-0.002139640086196955, -0.001889862283127962, -0.001562946712603551, -0.001167542853751166, -0.000715240442693352, +-0.000220259485535331, 0.000300952307259904, 0.000830234179165372, 0.001348316435464076, 0.001835484518420602, + 0.002272286892056382, 0.002640275270224598, 0.002922747186895021, 0.003105429025669433, 0.003177121318055631, + 0.003130264761140124, 0.002961377569599093, 0.002671367231884301, 0.002265711108816848, 0.001754448044185524, + 0.001152016769339380, 0.000476936676175902, -0.000248718444031171, -0.000999948124398612, -0.001749629130166096, +-0.002469433330814752, -0.003130790556013717, -0.003705967375692800, -0.004169105103972317, -0.004497290920890501, +-0.004671516289830527, -0.004677586380416906, -0.004506861602367489, -0.004156859083959776, -0.003631621917386205, +-0.002941916481868308, -0.002105153002563826, -0.001145089028022731, -0.000091277027194512, 0.001021707212306502, + 0.002155280663061357, 0.003268024493993296, 0.004317048090841251, 0.005259445594087794, 0.006053837753595639, + 0.006661931665353417, 0.007050060676035087, 0.007190601958065676, 0.007063321534553406, 0.006656461303944376, + 0.005967650471713226, 0.005004501271125813, 0.003784934033421286, 0.002337151835923401, 0.000699290786282728, +-0.001081281658450751, -0.002948980083931280, -0.004841350286243770, -0.006690670765283031, -0.008425801537676034, +-0.009974215633219711, -0.011264158097802190, -0.012226858155753686, -0.012798749185527288, -0.012923592591893964, +-0.012554478278462186, -0.011655599268700838, -0.010203756484326235, -0.008189534541107277, -0.005618124746137401, +-0.002509708641553183, 0.001100549166042612, 0.005162968475167027, 0.009614243563845803, 0.014378746109223314, + 0.019370216567322070, 0.024493786616406266, 0.029648291853908371, 0.034728809072115958, 0.039629330964538544, + 0.044245520769433347, 0.048477462256357018, 0.052232311099492307, 0.055426788736225432, 0.057989428195401226, + 0.059862511141804443, 0.061003649799348830, 0.061386934931288717, 0.061003649799348830, 0.059862511141804443, + 0.057989428195401226, 0.055426788736225432, 0.052232311099492307, 0.048477462256357018, 0.044245520769433347, + 0.039629330964538544, 0.034728809072115958, 0.029648291853908371, 0.024493786616406266, 0.019370216567322070, + 0.014378746109223314, 0.009614243563845803, 0.005162968475167027, 0.001100549166042612, -0.002509708641553183, +-0.005618124746137401, -0.008189534541107277, -0.010203756484326235, -0.011655599268700838, -0.012554478278462186, +-0.012923592591893964, -0.012798749185527288, -0.012226858155753686, -0.011264158097802190, -0.009974215633219711, +-0.008425801537676034, -0.006690670765283031, -0.004841350286243770, -0.002948980083931280, -0.001081281658450751, + 0.000699290786282728, 0.002337151835923401, 0.003784934033421286, 0.005004501271125813, 0.005967650471713226, + 0.006656461303944376, 0.007063321534553406, 0.007190601958065676, 0.007050060676035087, 0.006661931665353417, + 0.006053837753595639, 0.005259445594087794, 0.004317048090841251, 0.003268024493993296, 0.002155280663061357, + 0.001021707212306502, -0.000091277027194512, -0.001145089028022731, -0.002105153002563826, -0.002941916481868308, +-0.003631621917386205, -0.004156859083959776, -0.004506861602367489, -0.004677586380416906, -0.004671516289830527, +-0.004497290920890501, -0.004169105103972317, -0.003705967375692800, -0.003130790556013717, -0.002469433330814752, +-0.001749629130166096, -0.000999948124398612, -0.000248718444031171, 0.000476936676175902, 0.001152016769339380, + 0.001754448044185524, 0.002265711108816848, 0.002671367231884301, 0.002961377569599093, 0.003130264761140124, + 0.003177121318055631, 0.003105429025669433, 0.002922747186895021, 0.002640275270224598, 0.002272286892056382, + 0.001835484518420602, 0.001348316435464076, 0.000830234179165372, 0.000300952307259904, -0.000220259485535331, +-0.000715240442693352, -0.001167542853751166, -0.001562946712603551, -0.001889862283127962, -0.002139640086196955, +-0.002306735130576522, -0.002388809260074332, -0.002386617358290471, -0.002303924955683100, -0.002147185427808426, +-0.001925231739749957, -0.001648868642029554, -0.001330378208545664, -0.000983067401076951, -0.000620740024523200, +-0.000257178812726900, 0.000094311703309987, 0.000421343499696318, 0.000712858984801055, 0.000959442606127223, + 0.001153569049793089, 0.001289791422827911, 0.001364836763234778, 0.001377619146269160, 0.001329178561171123, + 0.001222567778915907, 0.001062645743133108, 0.000855845198751289, 0.000609868204837889, 0.000333410168961052, + 0.000035800336978808, -0.000273350274681290, -0.000584400564491252, -0.000888138367181945, -0.001175849407968695, +-0.001439782952032160, -0.001673216059663119, -0.001870648667784210, -0.002027907322210780, -0.002142247186138010, +-0.002212264773625852, -0.002237960051673301, -0.002220597743425130, -0.002162628068669760, -0.002067470485014850, +-0.001939438753998784, -0.001783471817219780, -0.001605003575053789, -0.001409670281637694, -0.001203244280115714, +-0.000991335380833019, -0.000779359325040342, -0.000572207780852182, -0.000374389987374961, -0.000189585802186493, +-0.000021074826181109, 0.000128888056076112, 0.000258599355817496, 0.000367015380281216, 0.000454031497680723, + 0.000519884471131262, 0.000565459097743531, 0.000592142083607333, 0.000601725499419238, 0.000596168573595124, + 0.000577683697404093, 0.000548584697295435, 0.000511235564174107, 0.000467865412686539, 0.000420595492180569, + 0.000371355069437399, 0.000321902242967596, 0.000273714746744222, 0.000228017694264326, 0.000185727097247136, + 0.000147523658260692, 0.000113820318991206, 0.000084830915167248, 0.000060542525732639, 0.000070000950338734} ; + +// Rate 8000, pass 1350, stop 1700, ripple 0.2 dB, Atten 100 dB, taps 93 (tune 1650). Stop 0.2125 +double quiskMicFilt8Coefs[93] = { -0.000048361334397216, -0.000162172832480051, -0.000233937595698212, +-0.000006327103457766, 0.000686150429731403, 0.001555572669911020, 0.001852235322638049, 0.001010518676959113, +-0.000533703037637750, -0.001395527281294679, -0.000547805689731835, 0.001291819787342756, 0.002005877810728077, + 0.000349773034952121, -0.002188174349636707, -0.002546652809598079, 0.000326796204088075, 0.003522458156729808, + 0.002929815624806958, -0.001643263026946036, -0.005188443834846179, -0.002794087489903680, 0.003828812643532208, + 0.006979098861533383, 0.001700757602284474, -0.007013801827881641, -0.008485813922195549, 0.000867966129209818, + 0.011177153889901327, 0.009069558799223736, -0.005488279673121778, -0.016106658511052186, -0.007822070151870978, + 0.012835116756370880, 0.021388877055372072, 0.003393318192541889, -0.023987911353715568, -0.026461891098409809, + 0.006724804206592470, 0.041707244257452522, 0.030690541453250592, -0.029955120082834141, -0.077739106243522968, +-0.033499374091835024, 0.116943716609774510, 0.290907261177841880, 0.367819184749133500, 0.290907261177841880, + 0.116943716609774510, -0.033499374091835024, -0.077739106243522968, -0.029955120082834141, 0.030690541453250592, + 0.041707244257452522, 0.006724804206592470, -0.026461891098409809, -0.023987911353715568, 0.003393318192541889, + 0.021388877055372072, 0.012835116756370880, -0.007822070151870978, -0.016106658511052186, -0.005488279673121778, + 0.009069558799223736, 0.011177153889901327, 0.000867966129209818, -0.008485813922195549, -0.007013801827881641, + 0.001700757602284474, 0.006979098861533383, 0.003828812643532208, -0.002794087489903680, -0.005188443834846179, +-0.001643263026946036, 0.002929815624806958, 0.003522458156729808, 0.000326796204088075, -0.002546652809598079, +-0.002188174349636707, 0.000349773034952121, 0.002005877810728077, 0.001291819787342756, -0.000547805689731835, +-0.001395527281294679, -0.000533703037637750, 0.001010518676959113, 0.001852235322638049, 0.001555572669911020, + 0.000686150429731403, -0.000006327103457766, -0.000233937595698212, -0.000162172832480051, -0.000048361334397216} ; + +// Rate 48000, pass 3000, stop 4000, ripple 0.2 dB, atten 100 dB, taps 186. Pass 0.0625. Stop .083333. +double quiskLpFilt48Coefs[186] = { -0.000013634802929206, -0.000025833607451468, -0.000045098917291799, +-0.000067397454184632, -0.000087934612849518, -0.000099336288085176, -0.000092164590568194, -0.000056185338095760, + 0.000017628479128830, 0.000134695138796130, 0.000294364688920149, 0.000487930816326543, 0.000697781352809774, + 0.000898146218636447, 0.001057686434085801, 0.001143917302878051, 0.001128977827853932, 0.000995827794007123, + 0.000743730207286422, 0.000391756255003027, -0.000020838483133667, -0.000438064704829056, -0.000794512795634084, +-0.001025788840201078, -0.001080548087342285, -0.000932013104946299, -0.000586606665219995, -0.000087502631308000, + 0.000488407444073969, 0.001040518103766967, 0.001460541797297978, 0.001652270668043200, 0.001551661149061061, + 0.001143287935426198, 0.000469406269084863, -0.000371161499314815, -0.001235417370813682, -0.001958835103416428, +-0.002385048283250022, -0.002397529299276367, -0.001947179493277411, -0.001069838763968274, 0.000110976961796973, + 0.001398395929344110, 0.002552691150332272, 0.003333738458450593, 0.003548332671239537, 0.003093481207411224, + 0.001986606612550283, 0.000375208969545557, -0.001478173505667034, -0.003235390501589682, -0.004541286966345233, +-0.005091921692691575, -0.004699905483508720, -0.003343628882412480, -0.001188973425908869, 0.001423632771579898, + 0.004027852785840577, 0.006109889449352044, 0.007204411806930103, 0.006990964143613923, 0.005372067993842730, + 0.002515924626355224, -0.001148191299404085, -0.004985775758010277, -0.008258611238961837, -0.010257070307501979, +-0.010440987284724979, -0.008563091952746314, -0.004749779955204285, 0.000480411506971036, 0.006270496250094757, + 0.011548336869209398, 0.015203957673842774, 0.016294017929596276, 0.014239284159958419, 0.008979207551576943, + 0.001052260094328930, -0.008418875271295619, -0.017843305879886966, -0.025368239712008800, -0.029148572862344568, +-0.027640215600942798, -0.019871118305655480, -0.005644876301707345, 0.014359687674705773, 0.038614566536650477, + 0.064896650800270142, 0.090551963489257925, 0.112834114097579010, 0.129268306000616030, 0.137987044444314420, + 0.137987044444314420, 0.129268306000616030, 0.112834114097579010, 0.090551963489257925, 0.064896650800270142, + 0.038614566536650477, 0.014359687674705773, -0.005644876301707345, -0.019871118305655480, -0.027640215600942798, +-0.029148572862344568, -0.025368239712008800, -0.017843305879886966, -0.008418875271295619, 0.001052260094328930, + 0.008979207551576943, 0.014239284159958419, 0.016294017929596276, 0.015203957673842774, 0.011548336869209398, + 0.006270496250094757, 0.000480411506971036, -0.004749779955204285, -0.008563091952746314, -0.010440987284724979, +-0.010257070307501979, -0.008258611238961837, -0.004985775758010277, -0.001148191299404085, 0.002515924626355224, + 0.005372067993842730, 0.006990964143613923, 0.007204411806930103, 0.006109889449352044, 0.004027852785840577, + 0.001423632771579898, -0.001188973425908869, -0.003343628882412480, -0.004699905483508720, -0.005091921692691575, +-0.004541286966345233, -0.003235390501589682, -0.001478173505667034, 0.000375208969545557, 0.001986606612550283, + 0.003093481207411224, 0.003548332671239537, 0.003333738458450593, 0.002552691150332272, 0.001398395929344110, + 0.000110976961796973, -0.001069838763968274, -0.001947179493277411, -0.002397529299276367, -0.002385048283250022, +-0.001958835103416428, -0.001235417370813682, -0.000371161499314815, 0.000469406269084863, 0.001143287935426198, + 0.001551661149061061, 0.001652270668043200, 0.001460541797297978, 0.001040518103766967, 0.000488407444073969, +-0.000087502631308000, -0.000586606665219995, -0.000932013104946299, -0.001080548087342285, -0.001025788840201078, +-0.000794512795634084, -0.000438064704829056, -0.000020838483133667, 0.000391756255003027, 0.000743730207286422, + 0.000995827794007123, 0.001128977827853932, 0.001143917302878051, 0.001057686434085801, 0.000898146218636447, + 0.000697781352809774, 0.000487930816326543, 0.000294364688920149, 0.000134695138796130, 0.000017628479128830, +-0.000056185338095760, -0.000092164590568194, -0.000099336288085176, -0.000087934612849518, -0.000067397454184632, +-0.000045098917291799, -0.000025833607451468, -0.000013634802929206 } ; + +// Sample 1 Hz, pass 0.125, stop 0.192, ripple 0.1 dB, atten 100 dB, taps 64. Stop 0.192. +double quiskFilt12_19Coefs[64] = { 0.000043810351462319, 0.000138863869398690, 0.000206835545025070, + 0.000047730531591317, -0.000514727559529336, -0.001353635480056038, -0.001881242409327435, -0.001336759882352925, + 0.000480762137654505, 0.002595795305500315, 0.003157538223935716, 0.000883738175457450, -0.003304786290465149, +-0.006137600456523736, -0.004232173904239833, 0.002529546544381186, 0.009496143474996962, 0.009949625572871518, + 0.000962602913419665, -0.012260655539427043, -0.018443416827456323, -0.008986096201165510, 0.012737225552167929, + 0.030249086308101754, 0.024812387650292519, -0.007810917817793795, -0.047781081559953899, -0.059071807532626572, +-0.012800932712041477, 0.089119808681493592, 0.208124268210933110, 0.287998295491409540, 0.287998295491409540, + 0.208124268210933110, 0.089119808681493592, -0.012800932712041477, -0.059071807532626572, -0.047781081559953899, +-0.007810917817793795, 0.024812387650292519, 0.030249086308101754, 0.012737225552167929, -0.008986096201165510, +-0.018443416827456323, -0.012260655539427043, 0.000962602913419665, 0.009949625572871518, 0.009496143474996962, + 0.002529546544381186, -0.004232173904239833, -0.006137600456523736, -0.003304786290465149, 0.000883738175457450, + 0.003157538223935716, 0.002595795305500315, 0.000480762137654505, -0.001336759882352925, -0.001881242409327435, +-0.001353635480056038, -0.000514727559529336, 0.000047730531591317, 0.000206835545025070, 0.000138863869398690, + 0.000043810351462319} ; + +// Sample 185185 Hz, pass 20000, stop 24000, ripple 0.1 dB, atten 100 dB. For SDR-IQ. Stop 0.12960. +double quiskFilt185D3Coefs[189] = { 0.000012775222963944, 0.000016983455496025, 0.000005695459401072, -0.000043370392322925, +-0.000147289810253534, -0.000306853192747712, -0.000495824111042255, -0.000659912449094600, -0.000730624820807555, +-0.000652527190094691, -0.000414108542407609, -0.000067101230547892, 0.000279651977919746, 0.000496266897694305, + 0.000487147757737141, 0.000242333355062937, -0.000142550724009829, -0.000494575786766818, -0.000635523916496215, +-0.000469056286907829, -0.000040928295181758, 0.000463663137093341, 0.000794834769348999, 0.000757740301889996, + 0.000321720777800376, -0.000340193393344642, -0.000912913559618510, -0.001084751128773843, -0.000711940589185640, + 0.000082299572584560, 0.000936277365105903, 0.001408757213795883, 0.001201831453599291, 0.000337664985017852, +-0.000811409064230295, -0.001675111110230421, -0.001762493246689817, -0.000932484695478804, 0.000487612811028567, + 0.001818232361534366, 0.002345552396132496, 0.001696550268334318, 0.000077777468070599, -0.001766041325684469, +-0.002884900730916659, -0.002604595228251389, -0.000916585198089093, 0.001441898300187086, 0.003295669370079313, + 0.003607178283399026, 0.002043134080945332, -0.000770834928466311, -0.003477680777975640, -0.004630423337101654, +-0.003451243412347569, -0.000316897327995463, 0.003317404994207552, 0.005575542061462892, 0.005112123782225341, + 0.001883844314927795, -0.002688463188924373, -0.006318001179294125, -0.006973801349612467, -0.003986753493372796, + 0.001448254061321833, 0.006704745658002290, 0.008962905591378809, 0.006685267872330669, 0.000575241526335665, +-0.006544512593532594, -0.010988781408054678, -0.010065358566522303, -0.003619687890287124, 0.005579382505096557, + 0.012949729773207512, 0.014296503321222344, 0.008083093875296470, -0.003403672893464083, -0.014738780224596465, +-0.019777086918256612, -0.014784814209033215, -0.000774387161930971, 0.016257202240672342, 0.027605140528859175, + 0.025878721397717495, 0.009050451134125328, -0.017412070606886515, -0.041603859087594751, -0.049367067487597505, +-0.030106928037341840, 0.018134551394911973, 0.086605309563255489, 0.157907419743824910, 0.211651319263293920, + 0.231619508265925700, 0.211651319263293920, 0.157907419743824910, 0.086605309563255489, 0.018134551394911973, +-0.030106928037341840, -0.049367067487597505, -0.041603859087594751, -0.017412070606886515, 0.009050451134125328, + 0.025878721397717495, 0.027605140528859175, 0.016257202240672342, -0.000774387161930971, -0.014784814209033215, +-0.019777086918256612, -0.014738780224596465, -0.003403672893464083, 0.008083093875296470, 0.014296503321222344, + 0.012949729773207512, 0.005579382505096557, -0.003619687890287124, -0.010065358566522303, -0.010988781408054678, +-0.006544512593532594, 0.000575241526335665, 0.006685267872330669, 0.008962905591378809, 0.006704745658002290, + 0.001448254061321833, -0.003986753493372796, -0.006973801349612467, -0.006318001179294125, -0.002688463188924373, + 0.001883844314927795, 0.005112123782225341, 0.005575542061462892, 0.003317404994207552, -0.000316897327995463, +-0.003451243412347569, -0.004630423337101654, -0.003477680777975640, -0.000770834928466311, 0.002043134080945332, + 0.003607178283399026, 0.003295669370079313, 0.001441898300187086, -0.000916585198089093, -0.002604595228251389, +-0.002884900730916659, -0.001766041325684469, 0.000077777468070599, 0.001696550268334318, 0.002345552396132496, + 0.001818232361534366, 0.000487612811028567, -0.000932484695478804, -0.001762493246689817, -0.001675111110230421, +-0.000811409064230295, 0.000337664985017852, 0.001201831453599291, 0.001408757213795883, 0.000936277365105903, + 0.000082299572584560, -0.000711940589185640, -0.001084751128773843, -0.000912913559618510, -0.000340193393344642, + 0.000321720777800376, 0.000757740301889996, 0.000794834769348999, 0.000463663137093341, -0.000040928295181758, +-0.000469056286907829, -0.000635523916496215, -0.000494575786766818, -0.000142550724009829, 0.000242333355062937, + 0.000487147757737141, 0.000496266897694305, 0.000279651977919746, -0.000067101230547892, -0.000414108542407609, +-0.000652527190094691, -0.000730624820807555, -0.000659912449094600, -0.000495824111042255, -0.000306853192747712, +-0.000147289810253534, -0.000043370392322925, 0.000005695459401072, 0.000016983455496025, 0.000012775222963944} ; + +// Sample 185185 Hz, pass 15000, stop 23900, ripple 0.1 dB, atten 100 dB. For SDR-IQ. Stop 0.129060. +double quiskFilt185D3XCoefs[88] = { -0.000026016801458962, -0.000079343977811083, -0.000167897725115826, -0.000272158478205181, +-0.000343785933253942, -0.000310796788289393, -0.000102311891399797, 0.000311300138822939, 0.000877228005105512, 0.001440583394256035, + 0.001767836749778265, 0.001621580974541500, 0.000871040137123456, -0.000402149294308734, -0.001857385353819294, -0.002952994474321905, +-0.003118971956742465, -0.002002468636632821, 0.000304795748571584, 0.003168465665729574, 0.005527734084410955, 0.006236194024480606, + 0.004538088065486723, 0.000504287301538830, -0.004776061900406583, -0.009396166048199123, -0.011236677659113514, -0.008814076686757433, +-0.002068388398230007, 0.007257051941983628, 0.015896603006282241, 0.020037182698727726, 0.016744338396284194, 0.005368795820044257, +-0.011616528320034477, -0.028774969855905743, -0.038977373476370786, -0.035506674315576942, -0.014373032297997279, 0.023936198172814224, + 0.073933459676699551, 0.126134168736811850, 0.169387884611201690, 0.193878581402395330, 0.193878581402395330, 0.169387884611201690, + 0.126134168736811850, 0.073933459676699551, 0.023936198172814224, -0.014373032297997279, -0.035506674315576942, -0.038977373476370786, +-0.028774969855905743, -0.011616528320034477, 0.005368795820044257, 0.016744338396284194, 0.020037182698727726, 0.015896603006282241, + 0.007257051941983628, -0.002068388398230007, -0.008814076686757433, -0.011236677659113514, -0.009396166048199123, -0.004776061900406583, + 0.000504287301538830, 0.004538088065486723, 0.006236194024480606, 0.005527734084410955, 0.003168465665729574, 0.000304795748571584, +-0.002002468636632821, -0.003118971956742465, -0.002952994474321905, -0.001857385353819294, -0.000402149294308734, 0.000871040137123456, + 0.001621580974541500, 0.001767836749778265, 0.001440583394256035, 0.000877228005105512, 0.000311300138822939, -0.000102311891399797, +-0.000310796788289393, -0.000343785933253942, -0.000272158478205181, -0.000167897725115826, -0.000079343977811083, -0.000026016801458962} ; + +// Sample 185185 Hz, pass 10000, stop 12000, ripple 0.5 dB, atten 100 dB. For SDR-IQ. Stop 0.064800. +double quiskFilt185D7Coefs[325] = { 0.000011585872492535, 0.000021202775655724, 0.000038515581433427, 0.000062668164659740, + 0.000093940890103130, 0.000131718743859808, 0.000174225065604974, 0.000218342986026828, 0.000259582942086064, 0.000292202414812531, + 0.000309501208323850, 0.000304347853118504, 0.000269885084834102, 0.000200329336288352, 0.000091873319388286, +-0.000056460226867932, -0.000242180306367212, -0.000458826972767189, -0.000695972007921572, -0.000939539009494425, +-0.001172660992563646, -0.001376927020458812, -0.001534012395041686, -0.001627510712907692, -0.001644810967208332, +-0.001578811291153747, -0.001429231737210363, -0.001203341468259860, -0.000915956952592136, -0.000588604281778632, +-0.000247881153416102, 0.000076883069806609, 0.000356496498766536, 0.000564970346134735, 0.000682503931650803, + 0.000697978266499694, 0.000610677107413264, 0.000430929848100204, 0.000179551442826917, -0.000113968106450125, +-0.000414397628381283, -0.000684546545073360, -0.000889508690226669, -0.001000889233610915, -0.001000492619169160, +-0.000883003638604998, -0.000657261341613736, -0.000345878903792753, 0.000016835572177804, 0.000388523086839818, + 0.000723605124037493, 0.000978796711705906, 0.001118627280774081, 0.001120302957105695, 0.000977241071844119, + 0.000700761818102867, 0.000319615424985736, -0.000122724655145581, -0.000572787713987942, -0.000973362887973448, +-0.001270584863164685, -0.001420984914880352, -0.001397605040893653, -0.001194338356762529, -0.000827834100503333, +-0.000336591061237865, 0.000222813863757337, 0.000782022394777827, 0.001269065085645792, 0.001617440270226089, + 0.001774989493019493, 0.001711406052073067, 0.001423329284260602, 0.000936230433482062, 0.000302669877122975, +-0.000403051367712102, -0.001093347917242359, -0.001677989133371222, -0.002075657051446509, -0.002224973164684223, +-0.002093532018815395, -0.001683654864926617, -0.001033944903045330, -0.000216202715201031, 0.000672136237235640, + 0.001519308622149400, 0.002212998394841567, 0.002654922443391980, 0.002774368358702789, 0.002538866483308035, + 0.001960451088476113, 0.001096459360528461, 0.000044471086033573, -0.001068263405090781, -0.002099826730828226, +-0.002911349453455988, -0.003385273847682783, -0.003441820002543952, -0.003051407043926876, -0.002241202920048492, +-0.001094628195429446, 0.000256492779785520, 0.001646511036377237, 0.002895722994780784, 0.003833041920244146, + 0.004318777856326995, 0.004264570929820472, 0.003647733587396600, 0.002517835568167883, 0.000994251746281646, +-0.000745501220005664, -0.002485634335945939, -0.003998039828567652, -0.005071301821004867, -0.005539183731069824, +-0.005304874120435773, -0.004357624572350711, -0.002779195755674417, -0.000738704575887045, 0.001524133036686280, + 0.003725875479734138, 0.005573480900438330, 0.006801730588660005, 0.007209350069135231, 0.006689128326667765, + 0.005247829163413888, 0.003012735394874103, 0.000223174064082399, -0.002792831962376837, -0.005654763344137692, +-0.007974895792849587, -0.009407569536250765, -0.009696510630814705, -0.008714169676261001, -0.006487651574925092, +-0.003207133255888027, 0.000785450700374519, 0.005027410607157849, 0.008983570118297410, 0.012107159427564776, + 0.013906909894651421, 0.014012809617679676, 0.012232695276105182, 0.008592471450916649, 0.003354235296550607, +-0.002991194159451697, -0.009758117856276788, -0.016123999816583842, -0.021206724919405923, -0.024153695524852702, +-0.024233748891356845, -0.020922161341036132, -0.013969353698339257, -0.003445298078014795, 0.010246061695767921, + 0.026385065799221116, 0.043986586663902316, 0.061878202104145755, 0.078799201858789497, 0.093511010803744687, + 0.104908248570820360, 0.112119405527806140, 0.114587041362813750, 0.112119405527806140, 0.104908248570820360, + 0.093511010803744687, 0.078799201858789497, 0.061878202104145755, 0.043986586663902316, 0.026385065799221116, + 0.010246061695767921, -0.003445298078014795, -0.013969353698339257, -0.020922161341036132, -0.024233748891356845, +-0.024153695524852702, -0.021206724919405923, -0.016123999816583842, -0.009758117856276788, -0.002991194159451697, + 0.003354235296550607, 0.008592471450916649, 0.012232695276105182, 0.014012809617679676, 0.013906909894651421, + 0.012107159427564776, 0.008983570118297410, 0.005027410607157849, 0.000785450700374519, -0.003207133255888027, +-0.006487651574925092, -0.008714169676261001, -0.009696510630814705, -0.009407569536250765, -0.007974895792849587, +-0.005654763344137692, -0.002792831962376837, 0.000223174064082399, 0.003012735394874103, 0.005247829163413888, + 0.006689128326667765, 0.007209350069135231, 0.006801730588660005, 0.005573480900438330, 0.003725875479734138, + 0.001524133036686280, -0.000738704575887045, -0.002779195755674417, -0.004357624572350711, -0.005304874120435773, +-0.005539183731069824, -0.005071301821004867, -0.003998039828567652, -0.002485634335945939, -0.000745501220005664, + 0.000994251746281646, 0.002517835568167883, 0.003647733587396600, 0.004264570929820472, 0.004318777856326995, + 0.003833041920244146, 0.002895722994780784, 0.001646511036377237, 0.000256492779785520, -0.001094628195429446, +-0.002241202920048492, -0.003051407043926876, -0.003441820002543952, -0.003385273847682783, -0.002911349453455988, +-0.002099826730828226, -0.001068263405090781, 0.000044471086033573, 0.001096459360528461, 0.001960451088476113, + 0.002538866483308035, 0.002774368358702789, 0.002654922443391980, 0.002212998394841567, 0.001519308622149400, + 0.000672136237235640, -0.000216202715201031, -0.001033944903045330, -0.001683654864926617, -0.002093532018815395, +-0.002224973164684223, -0.002075657051446509, -0.001677989133371222, -0.001093347917242359, -0.000403051367712102, + 0.000302669877122975, 0.000936230433482062, 0.001423329284260602, 0.001711406052073067, 0.001774989493019493, + 0.001617440270226089, 0.001269065085645792, 0.000782022394777827, 0.000222813863757337, -0.000336591061237865, +-0.000827834100503333, -0.001194338356762529, -0.001397605040893653, -0.001420984914880352, -0.001270584863164685, +-0.000973362887973448, -0.000572787713987942, -0.000122724655145581, 0.000319615424985736, 0.000700761818102867, + 0.000977241071844119, 0.001120302957105695, 0.001118627280774081, 0.000978796711705906, 0.000723605124037493, + 0.000388523086839818, 0.000016835572177804, -0.000345878903792753, -0.000657261341613736, -0.000883003638604998, +-0.001000492619169160, -0.001000889233610915, -0.000889508690226669, -0.000684546545073360, -0.000414397628381283, +-0.000113968106450125, 0.000179551442826917, 0.000430929848100204, 0.000610677107413264, 0.000697978266499694, + 0.000682503931650803, 0.000564970346134735, 0.000356496498766536, 0.000076883069806609, -0.000247881153416102, +-0.000588604281778632, -0.000915956952592136, -0.001203341468259860, -0.001429231737210363, -0.001578811291153747, +-0.001644810967208332, -0.001627510712907692, -0.001534012395041686, -0.001376927020458812, -0.001172660992563646, +-0.000939539009494425, -0.000695972007921572, -0.000458826972767189, -0.000242180306367212, -0.000056460226867932, + 0.000091873319388286, 0.000200329336288352, 0.000269885084834102, 0.000304347853118504, 0.000309501208323850, + 0.000292202414812531, 0.000259582942086064, 0.000218342986026828, 0.000174225065604974, 0.000131718743859808, + 0.000093940890103130, 0.000062668164659740, 0.000038515581433427, 0.000021202775655724, 0.000011585872492535} ; + +// Sample 133333 Hz, pass 20000, stop 24000, ripple 0.1 dB, atten 100 dB. For SDR-IQ. Stop 0.18000. +double quiskFilt133D2Coefs[136] = { 0.000017194140195307, 0.000013031580406032, -0.000070982819622986, -0.000297799318127961, +-0.000645057684247744, -0.000951723373793975, -0.000976667009837673, -0.000579840699091521, 0.000098129751533427, + 0.000644977424009104, 0.000648591932388761, 0.000063466245825653, -0.000655952785922865, -0.000858530950797784, +-0.000263313568128542, 0.000697280609267464, 0.001152025473909408, 0.000556416972642962, -0.000702600426682260, +-0.001503408593721275, -0.000962293487226091, 0.000635424413378249, 0.001893865760533597, 0.001494654948692980, +-0.000464212878209573, -0.002304129661798076, -0.002165029121341371, 0.000156986911460916, 0.002712310672598236, + 0.002984071948396753, 0.000321449210044334, -0.003091821762536665, -0.003961473137247651, -0.001010870707262950, + 0.003410242877909513, 0.005106330989341096, 0.001957278141537391, -0.003628441800145526, -0.006428886895772534, +-0.003216116999687284, 0.003699894436571663, 0.007945926308831793, 0.004862613142073883, -0.003562107867854992, +-0.009680253224586733, -0.006996529395398682, 0.003139846233471389, 0.011684868441375082, 0.009783362061267441, +-0.002313232717655680, -0.014049355073607583, -0.013495163730537300, 0.000895100102567855, 0.016959635420547809, + 0.018648538187471329, 0.001461901498462892, -0.020821848411902603, -0.026360133080398199, -0.005525410959722330, + 0.026648719554864163, 0.039549486288596822, 0.013454287175328376, -0.037723817929648518, -0.068973339495709857, +-0.034792487725714472, 0.073207163331834649, 0.211614628197181350, 0.308128392192117740, 0.308128392192117740, + 0.211614628197181350, 0.073207163331834649, -0.034792487725714472, -0.068973339495709857, -0.037723817929648518, + 0.013454287175328376, 0.039549486288596822, 0.026648719554864163, -0.005525410959722330, -0.026360133080398199, +-0.020821848411902603, 0.001461901498462892, 0.018648538187471329, 0.016959635420547809, 0.000895100102567855, +-0.013495163730537300, -0.014049355073607583, -0.002313232717655680, 0.009783362061267441, 0.011684868441375082, + 0.003139846233471389, -0.006996529395398682, -0.009680253224586733, -0.003562107867854992, 0.004862613142073883, + 0.007945926308831793, 0.003699894436571663, -0.003216116999687284, -0.006428886895772534, -0.003628441800145526, + 0.001957278141537391, 0.005106330989341096, 0.003410242877909513, -0.001010870707262950, -0.003961473137247651, +-0.003091821762536665, 0.000321449210044334, 0.002984071948396753, 0.002712310672598236, 0.000156986911460916, +-0.002165029121341371, -0.002304129661798076, -0.000464212878209573, 0.001494654948692980, 0.001893865760533597, + 0.000635424413378249, -0.000962293487226091, -0.001503408593721275, -0.000702600426682260, 0.000556416972642962, + 0.001152025473909408, 0.000697280609267464, -0.000263313568128542, -0.000858530950797784, -0.000655952785922865, + 0.000063466245825653, 0.000648591932388761, 0.000644977424009104, 0.000098129751533427, -0.000579840699091521, +-0.000976667009837673, -0.000951723373793975, -0.000645057684247744, -0.000297799318127961, -0.000070982819622986, + 0.000013031580406032, 0.000017194140195307 } ; + +// Sample 133333 Hz, pass 10000, stop 12000, ripple 0.5 dB, atten 100 dB. For SDR-IQ. Stop 0.09000. +double quiskFilt133D5Coefs[235] = { 0.000017189993342429, 0.000044738485582654, 0.000095017992345093, 0.000171520768649681, + 0.000274754514100238, 0.000398861594001340, 0.000529984999270100, 0.000646117397525726, 0.000718766415753168, + 0.000716925008973219, 0.000612844047432097, 0.000388876459995003, 0.000043995168792169, -0.000401638582561071, +-0.000905542666180542, -0.001406385530252347, -0.001832160018119073, -0.002112253709578303, -0.002191408150519588, +-0.002042816719676478, -0.001677284024812651, -0.001145752378681760, -0.000533679761891523, 0.000052653624322135, + 0.000505907329874479, 0.000739851765907619, 0.000709229648947976, 0.000422523833395244, -0.000056020452981720, +-0.000616720304461352, -0.001125962440173316, -0.001454995787757606, -0.001509950907384681, -0.001256131267758628, +-0.000730326866325590, -0.000037158838317771, 0.000671036130692201, 0.001227356473019533, 0.001489601586267346, + 0.001376632327061763, 0.000892728515088217, 0.000132987785167989, -0.000733102122237588, -0.001497662325285410, +-0.001962695382200209, -0.001989694482613183, -0.001538410312914189, -0.000684278577781320, 0.000391518648789513, + 0.001439060262231111, 0.002196666221903917, 0.002455206790769118, 0.002114248789295101, 0.001215358226109379, +-0.000057550388771950, -0.001413462921977421, -0.002519018104662746, -0.003078793094584098, -0.002912162814601297, +-0.002007172281996295, -0.000536145063816962, 0.001173803706907478, 0.002711347438868158, 0.003677986766436793, + 0.003789135200490261, 0.002954134059121999, 0.001313489128052297, -0.000779246120784695, -0.002830527021932420, +-0.004320552674014772, -0.004832115109577898, -0.004163289510814260, -0.002394421340991988, 0.000110859904206459, + 0.002775804501582138, 0.004939612978506710, 0.006017429310863970, 0.005653427023669567, 0.003828044237650523, + 0.000888978423738288, -0.002507663812902237, -0.005538235553289072, -0.007405523728182901, -0.007541454609909858, +-0.005768568026570733, -0.002376693049722654, 0.001911054897098118, 0.006083411898142144, 0.009070202108022866, + 0.010005711453736786, 0.008463803744478828, 0.004604287191332431, -0.000813142277372015, -0.006562286180605113, +-0.011211419856533258, -0.013459517260663504, -0.012472773254562211, -0.008139991258903686, -0.001177765649827518, + 0.006953975184466299, 0.014324290436841271, 0.018945676347138025, 0.019248150626647365, 0.014509895663648657, + 0.005142708852136439, -0.007245145517153711, -0.020042796993989549, -0.030067767509750015, -0.034161762145356343, +-0.029838715290132833, -0.015854346901954634, 0.007424778279719944, 0.037939728171909451, 0.072177533053372794, + 0.105698112853527980, 0.133859311332239410, 0.152607173067114270, 0.159182344236760560, 0.152607173067114270, + 0.133859311332239410, 0.105698112853527980, 0.072177533053372794, 0.037939728171909451, 0.007424778279719944, +-0.015854346901954634, -0.029838715290132833, -0.034161762145356343, -0.030067767509750015, -0.020042796993989549, +-0.007245145517153711, 0.005142708852136439, 0.014509895663648657, 0.019248150626647365, 0.018945676347138025, + 0.014324290436841271, 0.006953975184466299, -0.001177765649827518, -0.008139991258903686, -0.012472773254562211, +-0.013459517260663504, -0.011211419856533258, -0.006562286180605113, -0.000813142277372015, 0.004604287191332431, + 0.008463803744478828, 0.010005711453736786, 0.009070202108022866, 0.006083411898142144, 0.001911054897098118, +-0.002376693049722654, -0.005768568026570733, -0.007541454609909858, -0.007405523728182901, -0.005538235553289072, +-0.002507663812902237, 0.000888978423738288, 0.003828044237650523, 0.005653427023669567, 0.006017429310863970, + 0.004939612978506710, 0.002775804501582138, 0.000110859904206459, -0.002394421340991988, -0.004163289510814260, +-0.004832115109577898, -0.004320552674014772, -0.002830527021932420, -0.000779246120784695, 0.001313489128052297, + 0.002954134059121999, 0.003789135200490261, 0.003677986766436793, 0.002711347438868158, 0.001173803706907478, +-0.000536145063816962, -0.002007172281996295, -0.002912162814601297, -0.003078793094584098, -0.002519018104662746, +-0.001413462921977421, -0.000057550388771950, 0.001215358226109379, 0.002114248789295101, 0.002455206790769118, + 0.002196666221903917, 0.001439060262231111, 0.000391518648789513, -0.000684278577781320, -0.001538410312914189, +-0.001989694482613183, -0.001962695382200209, -0.001497662325285410, -0.000733102122237588, 0.000132987785167989, + 0.000892728515088217, 0.001376632327061763, 0.001489601586267346, 0.001227356473019533, 0.000671036130692201, +-0.000037158838317771, -0.000730326866325590, -0.001256131267758628, -0.001509950907384681, -0.001454995787757606, +-0.001125962440173316, -0.000616720304461352, -0.000056020452981720, 0.000422523833395244, 0.000709229648947976, + 0.000739851765907619, 0.000505907329874479, 0.000052653624322135, -0.000533679761891523, -0.001145752378681760, +-0.001677284024812651, -0.002042816719676478, -0.002191408150519588, -0.002112253709578303, -0.001832160018119073, +-0.001406385530252347, -0.000905542666180542, -0.000401638582561071, 0.000043995168792169, 0.000388876459995003, + 0.000612844047432097, 0.000716925008973219, 0.000718766415753168, 0.000646117397525726, 0.000529984999270100, + 0.000398861594001340, 0.000274754514100238, 0.000171520768649681, 0.000095017992345093, 0.000044738485582654, + 0.000017189993342429} ; + +// Sample 111111 Hz, pass 20000, stop 24000, ripple 0.1 dB, atten 100 dB. For SDR-IQ. Stop 0.21600. +double quiskFilt111D2Coefs[114] = { 0.000039619752517087, 0.000089439819783633, 0.000017353012666121, -0.000334808345968833, +-0.000892257134326630, -0.001221700854092078, -0.000859742027523152, 0.000086110163390211, 0.000794431841612731, + 0.000492910288706401, -0.000542730604541516, -0.001057738392194920, -0.000232619264909279, 0.001101735902638522, + 0.001208303693873524, -0.000351788978766190, -0.001781186751638964, -0.001038581835235810, 0.001330430232082531, + 0.002355496074120870, 0.000292615997120782, -0.002623102550864884, -0.002462643018753817, 0.001194053806308097, + 0.003912675164153385, 0.001670607202119238, -0.003361036335001390, -0.004638914091859385, 0.000363767953875138, + 0.005797757906125993, 0.004088942448683545, -0.003689094083671128, -0.007701448612726584, -0.001601464053307583, + 0.007864313939600763, 0.007963248868264130, -0.003156323310881499, -0.011852746523309575, -0.005390083465132761, + 0.009915212572859983, 0.014053676129843581, -0.000996935620086296, -0.017619302590712586, -0.012439281622203873, + 0.011722997082370391, 0.024364733312011930, 0.004596957308944789, -0.026911691600293927, -0.027288530345555617, + 0.013072844783675014, 0.047281998734662697, 0.021354076339639792, -0.051099079289920336, -0.080397920778604998, + 0.013794073914731197, 0.205298935938689170, 0.362799183591728360, 0.362799183591728360, 0.205298935938689170, + 0.013794073914731197, -0.080397920778604998, -0.051099079289920336, 0.021354076339639792, 0.047281998734662697, + 0.013072844783675014, -0.027288530345555617, -0.026911691600293927, 0.004596957308944789, 0.024364733312011930, + 0.011722997082370391, -0.012439281622203873, -0.017619302590712586, -0.000996935620086296, 0.014053676129843581, + 0.009915212572859983, -0.005390083465132761, -0.011852746523309575, -0.003156323310881499, 0.007963248868264130, + 0.007864313939600763, -0.001601464053307583, -0.007701448612726584, -0.003689094083671128, 0.004088942448683545, + 0.005797757906125993, 0.000363767953875138, -0.004638914091859385, -0.003361036335001390, 0.001670607202119238, + 0.003912675164153385, 0.001194053806308097, -0.002462643018753817, -0.002623102550864884, 0.000292615997120782, + 0.002355496074120870, 0.001330430232082531, -0.001038581835235810, -0.001781186751638964, -0.000351788978766190, + 0.001208303693873524, 0.001101735902638522, -0.000232619264909279, -0.001057738392194920, -0.000542730604541516, + 0.000492910288706401, 0.000794431841612731, 0.000086110163390211, -0.000859742027523152, -0.001221700854092078, +-0.000892257134326630, -0.000334808345968833, 0.000017353012666121, 0.000089439819783633, 0.000039619752517087 } ; + +// Sample 111111 Hz, pass 10000, stop 12000, ripple 0.5 dB, atten 100 dB. For SDR-IQ. Stop 0.10800. +double quiskFilt111D4Coefs[196] = { 0.000022860383861326, 0.000070265173417164, 0.000160689233204503, 0.000301317969124407, + 0.000487289901409396, 0.000694724116305488, 0.000878636067621920, 0.000977522977076171, 0.000925771316840867, + 0.000672305339588912, 0.000201226295657756, -0.000452162019544330, -0.001196628260487459, -0.001896523042546486, +-0.002399782411882304, -0.002577406131356652, -0.002364065883391685, -0.001786962447427500, -0.000971468216463463, +-0.000117441349581108, 0.000550823899522796, 0.000848990131462632, 0.000692513431543379, 0.000131676598678132, +-0.000652871765004500, -0.001397429828707858, -0.001834711252195214, -0.001782286112376423, -0.001211048928870428, +-0.000268185005007021, 0.000757996926666718, 0.001525273309644227, 0.001751649817944780, 0.001319320688138531, + 0.000330934715806419, -0.000906405873937246, -0.001970890581915849, -0.002464861139454794, -0.002158210203051176, +-0.001085620699944977, 0.000441507737096167, 0.001920949956624443, 0.002821890381744697, 0.002772521251916338, + 0.001707992629296951, -0.000078236168178539, -0.002007203892070514, -0.003396752476301502, -0.003697747220029350, +-0.002703958879036829, -0.000657405952388967, 0.001798494644242414, 0.003814960168125246, 0.004625841420163100, + 0.003831836297877268, 0.001577797389627854, -0.001450651164746532, -0.004226329291915980, -0.005721230469088120, +-0.005278368569659456, -0.002883121089582856, 0.000773112126565357, 0.004478776308038774, 0.006893091575659842, + 0.007019574634915599, 0.004598796615908167, 0.000271268071355489, -0.004569531219948477, -0.008209861014252150, +-0.009205194241390222, -0.006929272243366950, -0.001881659896440290, 0.004379813721900100, 0.009694113972501929, + 0.012015064656800249, 0.010162418707817443, 0.004341783259066366, -0.003775178431738174, -0.011484866194632987, +-0.015901979746403339, -0.014972424881419052, -0.008312775352876528, 0.002435156726078577, 0.013913027108919607, + 0.021990371341685223, 0.023101099895195237, 0.015573244973319158, 0.000514891807100321, -0.018080409101544084, +-0.034128539734629829, -0.040997556950638683, -0.033400949657662792, -0.009105399838522790, 0.030066051833635116, + 0.078279007079479407, 0.126836610192946620, 0.166170707028785000, 0.188161385669205480, 0.188161385669205480, + 0.166170707028785000, 0.126836610192946620, 0.078279007079479407, 0.030066051833635116, -0.009105399838522790, +-0.033400949657662792, -0.040997556950638683, -0.034128539734629829, -0.018080409101544084, 0.000514891807100321, + 0.015573244973319158, 0.023101099895195237, 0.021990371341685223, 0.013913027108919607, 0.002435156726078577, +-0.008312775352876528, -0.014972424881419052, -0.015901979746403339, -0.011484866194632987, -0.003775178431738174, + 0.004341783259066366, 0.010162418707817443, 0.012015064656800249, 0.009694113972501929, 0.004379813721900100, +-0.001881659896440290, -0.006929272243366950, -0.009205194241390222, -0.008209861014252150, -0.004569531219948477, + 0.000271268071355489, 0.004598796615908167, 0.007019574634915599, 0.006893091575659842, 0.004478776308038774, + 0.000773112126565357, -0.002883121089582856, -0.005278368569659456, -0.005721230469088120, -0.004226329291915980, +-0.001450651164746532, 0.001577797389627854, 0.003831836297877268, 0.004625841420163100, 0.003814960168125246, + 0.001798494644242414, -0.000657405952388967, -0.002703958879036829, -0.003697747220029350, -0.003396752476301502, +-0.002007203892070514, -0.000078236168178539, 0.001707992629296951, 0.002772521251916338, 0.002821890381744697, + 0.001920949956624443, 0.000441507737096167, -0.001085620699944977, -0.002158210203051176, -0.002464861139454794, +-0.001970890581915849, -0.000906405873937246, 0.000330934715806419, 0.001319320688138531, 0.001751649817944780, + 0.001525273309644227, 0.000757996926666718, -0.000268185005007021, -0.001211048928870428, -0.001782286112376423, +-0.001834711252195214, -0.001397429828707858, -0.000652871765004500, 0.000131676598678132, 0.000692513431543379, + 0.000848990131462632, 0.000550823899522796, -0.000117441349581108, -0.000971468216463463, -0.001786962447427500, +-0.002364065883391685, -0.002577406131356652, -0.002399782411882304, -0.001896523042546486, -0.001196628260487459, +-0.000452162019544330, 0.000201226295657756, 0.000672305339588912, 0.000925771316840867, 0.000977522977076171, + 0.000878636067621920, 0.000694724116305488, 0.000487289901409396, 0.000301317969124407, 0.000160689233204503, + 0.000070265173417164, 0.000022860383861326} ; + +// Sample 53333 Hz, pass 20000, stop 24000, ripple 0.1 dB, atten 100 dB. For SDR-IQ. Stop 0.45000. +double quiskFilt53D1Coefs[55] = { 0.000318858391362187, 0.001662116369387873, -0.000065237353639775, -0.000854885424783294, + 0.001693126335254083, -0.002034310867663472, 0.001354648789538254, 0.000509693525155950, -0.003079059747940221, 0.005242349925004462, +-0.005626507739484119, 0.003235398612833476, 0.001883583294529978, -0.008273775245721798, 0.013282431836060808, -0.013924391581141530, + 0.008176677795586052, 0.003763659697675038, -0.018814370186261472, 0.031357304759835808, -0.034684131608746831, 0.023184334016039702, + 0.005435239957731122, -0.048724194253317150, 0.099481724552805198, -0.147334801628740130, 0.181502639813268140, 0.806108236192275450, + 0.181502639813268140, -0.147334801628740130, 0.099481724552805198, -0.048724194253317150, 0.005435239957731122, 0.023184334016039702, +-0.034684131608746831, 0.031357304759835808, -0.018814370186261472, 0.003763659697675038, 0.008176677795586052, -0.013924391581141530, + 0.013282431836060808, -0.008273775245721798, 0.001883583294529978, 0.003235398612833476, -0.005626507739484119, 0.005242349925004462, +-0.003079059747940221, 0.000509693525155950, 0.001354648789538254, -0.002034310867663472, 0.001693126335254083, -0.000854885424783294, +-0.000065237353639775, 0.001662116369387873, 0.000318858391362187 } ; + +// Sample 53333 Hz, pass 10000, stop 12000, ripple 0.5 dB, atten 100 dB. For SDR-IQ. Stop 0.22500. +double quiskFilt53D2Coefs[93] = { 0.000107966318729964, 0.000421475598772498, 0.000731343669484184, 0.000328611865414609, +-0.001457272471508304, -0.004143803290279123, -0.005736802939367581, -0.004303861070721504, -0.000415676176666607, + 0.002561521351129964, 0.001705074918770091, -0.001794491466202784, -0.003398011312785532, -0.000595401366072839, + 0.003389160538244070, 0.003117806598779015, -0.001738195724034938, -0.004960858372597238, -0.001539269216218705, + 0.004783351138955084, 0.005278243767957266, -0.001906381347753796, -0.007653356303201065, -0.003190874189611138, + 0.006849740993527085, 0.008664448318071482, -0.002035800124581497, -0.011780892066463697, -0.005873932705860416, + 0.009972743922291657, 0.014072286848767549, -0.002113998573380457, -0.018521807368841001, -0.010490752431554192, + 0.015285369742599874, 0.023814301314901452, -0.002148815334259815, -0.031770485009434718, -0.020238958138733353, + 0.027248391304817831, 0.048071554306549469, -0.002165663523025174, -0.075199791717139086, -0.060076551864238638, + 0.094944856808714839, 0.301769455144326630, 0.397829555404577600, 0.301769455144326630, 0.094944856808714839, +-0.060076551864238638, -0.075199791717139086, -0.002165663523025174, 0.048071554306549469, 0.027248391304817831, +-0.020238958138733353, -0.031770485009434718, -0.002148815334259815, 0.023814301314901452, 0.015285369742599874, +-0.010490752431554192, -0.018521807368841001, -0.002113998573380457, 0.014072286848767549, 0.009972743922291657, +-0.005873932705860416, -0.011780892066463697, -0.002035800124581497, 0.008664448318071482, 0.006849740993527085, +-0.003190874189611138, -0.007653356303201065, -0.001906381347753796, 0.005278243767957266, 0.004783351138955084, +-0.001539269216218705, -0.004960858372597238, -0.001738195724034938, 0.003117806598779015, 0.003389160538244070, +-0.000595401366072839, -0.003398011312785532, -0.001794491466202784, 0.001705074918770091, 0.002561521351129964, +-0.000415676176666607, -0.004303861070721504, -0.005736802939367581, -0.004143803290279123, -0.001457272471508304, + 0.000328611865414609, 0.000731343669484184, 0.000421475598772498, 0.000107966318729964} ; + + +// Sample 240 kHz, pass 15, stop 23.9, ripple 0. 1dB, atten 100 dB, taps 114. For 240 to 48 decimation by 5. Stop 0.09958. +double quiskFilt240D5Coefs[115] = { -0.000016728727123248, -0.000044087357718719, -0.000091924762762279, -0.000159511110578360, +-0.000239892740115764, -0.000317037848665795, -0.000366082222804719, -0.000356867696341249, -0.000260799073810572, +-0.000060362275313048, 0.000240882695147526, 0.000610784476390585, 0.000986747708265456, 0.001282232501802643, + 0.001402382039117427, 0.001267163059859552, 0.000837554837630180, 0.000138176060202843, -0.000731120724040292, +-0.001601062409512967, -0.002259171778682862, -0.002494407062419913, -0.002152575146141896, -0.001190005644388707, + 0.000289463534037753, 0.002027990436998251, 0.003646223170245011, 0.004714602533497222, 0.004852837355866500, + 0.003837221343174296, 0.001689352142256304, -0.001280461340842228, -0.004495043698374529, -0.007204192122448914, +-0.008639958370167617, -0.008205272191961563, -0.005654509302706373, -0.001219595475489302, 0.004358910000877759, + 0.009919482807219961, 0.014084647220259008, 0.015553522670126217, 0.013426967081734104, 0.007494973982180793, +-0.001584790943077927, -0.012272742073297286, -0.022322599151574144, -0.029132041059785355, -0.030214716745057827, +-0.023707788648535934, -0.008813567526928788, 0.013920929398671823, 0.042556246756071010, 0.073971248970453499, + 0.104307827587028490, 0.129576402798269570, 0.146313667429180170, 0.152169870450184460, 0.146313667429180170, + 0.129576402798269570, 0.104307827587028490, 0.073971248970453499, 0.042556246756071010, 0.013920929398671823, +-0.008813567526928788, -0.023707788648535934, -0.030214716745057827, -0.029132041059785355, -0.022322599151574144, +-0.012272742073297286, -0.001584790943077927, 0.007494973982180793, 0.013426967081734104, 0.015553522670126217, + 0.014084647220259008, 0.009919482807219961, 0.004358910000877759, -0.001219595475489302, -0.005654509302706373, +-0.008205272191961563, -0.008639958370167617, -0.007204192122448914, -0.004495043698374529, -0.001280461340842228, + 0.001689352142256304, 0.003837221343174296, 0.004852837355866500, 0.004714602533497222, 0.003646223170245011, + 0.002027990436998251, 0.000289463534037753, -0.001190005644388707, -0.002152575146141896, -0.002494407062419913, +-0.002259171778682862, -0.001601062409512967, -0.000731120724040292, 0.000138176060202843, 0.000837554837630180, + 0.001267163059859552, 0.001402382039117427, 0.001282232501802643, 0.000986747708265456, 0.000610784476390585, + 0.000240882695147526, -0.000060362275313048, -0.000260799073810572, -0.000356867696341249, -0.000366082222804719, +-0.000317037848665795, -0.000239892740115764, -0.000159511110578360, -0.000091924762762279, -0.000044087357718719, +-0.000016728727123248} ; + +// Sample 240 kHz, pass 20, stop 24, ripple 0. 1dB, atten 100 dB. For 240 to 48 decimation by 5. Pass 0.083333. Stop 0.1000. +double quiskFilt240D5CoefsSharp[245] = {0.000010509517951006, 0.000013600436904199, 0.000014114646487651, + 0.000003107336183074, -0.000027062229851180, -0.000082599263842674, -0.000165408687939920, -0.000270459677107609, +-0.000384429386564009, -0.000486513762577162, -0.000551912068282870, -0.000557575961050952, -0.000489097193751572, +-0.000346866350142718, -0.000149400346186253, 0.000067944169519721, 0.000259321418469537, 0.000379109945031964, + 0.000394500403136929, 0.000296443107959613, 0.000105514360701591, -0.000130126194004534, -0.000345130836201596, +-0.000473961146691502, -0.000470132844463270, -0.000322205344194264, -0.000061120176458703, 0.000244508351024915, + 0.000505224922624527, 0.000635967434817550, 0.000582869672082906, 0.000343410640475203, -0.000027211329865173, +-0.000427051494167815, -0.000734325622577685, -0.000843497343068510, -0.000700190684320865, -0.000323704342054218, + 0.000191371674406576, 0.000697200136082736, 0.001033942501059566, 0.001079068090563369, 0.000789896649978617, + 0.000225095253699184, -0.000463869809966486, -0.001071638023123453, -0.001398840064879135, -0.001315934285574502, +-0.000810990907316290, -0.000004610004754826, 0.000875943963655002, 0.001559026923347157, 0.001810835543451637, + 0.001513186414012800, 0.000711563768288702, -0.000384610343377712, -0.001453941368175342, -0.002155359985238479, +-0.002234880299660999, -0.001614005062810786, -0.000429922430657965, 0.000989560561609744, 0.002214005025736412, + 0.002838581123665033, 0.002615068817313840, 0.001544038090357682, -0.000104494434130949, -0.001854539656237390, +-0.003158534255089867, -0.003565698051769338, -0.002873486424864839, -0.001212438364790582, 0.000968078677012544, + 0.003016832609770853, 0.004271507641806298, 0.004268577543475138, 0.002906991659042924, 0.000509839095017221, +-0.002243915738203439, -0.004506439605309381, -0.005516963680092404, -0.004850900074620331, -0.002582471594385039, + 0.000697817320287101, 0.004028242154893376, 0.006350748353554076, 0.006839211177606702, 0.005181509596969096, + 0.001722314463339247, -0.002590160933654113, -0.006453095775103189, -0.008591647653124229, -0.008165982840134313, +-0.005076656227616793, -0.000063471506879233, 0.005451413373916889, 0.009749704157369357, 0.011332344600275311, + 0.009414890577675568, 0.004246680386224628, -0.002862043391342491, -0.009841507474776875, -0.014431368776294761, +-0.014868975905553507, -0.010498438296855372, -0.002106968919934075, 0.008141920985575500, 0.017201630945497853, + 0.021962093122837603, 0.020191237691200150, 0.011339390591466089, -0.003037076058820319, -0.019391273155317083, +-0.032862864929577094, -0.038387061811562206, -0.031979063119985378, -0.011872162453893439, 0.020795139196288157, + 0.061948624205024894, 0.105252254728618600, 0.143357336865708950, 0.169447992824642290, 0.178721289066174270, + 0.169447992824642290, 0.143357336865708950, 0.105252254728618600, 0.061948624205024894, 0.020795139196288157, +-0.011872162453893439, -0.031979063119985378, -0.038387061811562206, -0.032862864929577094, -0.019391273155317083, +-0.003037076058820319, 0.011339390591466089, 0.020191237691200150, 0.021962093122837603, 0.017201630945497853, + 0.008141920985575500, -0.002106968919934075, -0.010498438296855372, -0.014868975905553507, -0.014431368776294761, +-0.009841507474776875, -0.002862043391342491, 0.004246680386224628, 0.009414890577675568, 0.011332344600275311, + 0.009749704157369357, 0.005451413373916889, -0.000063471506879233, -0.005076656227616793, -0.008165982840134313, +-0.008591647653124229, -0.006453095775103189, -0.002590160933654113, 0.001722314463339247, 0.005181509596969096, + 0.006839211177606702, 0.006350748353554076, 0.004028242154893376, 0.000697817320287101, -0.002582471594385039, +-0.004850900074620331, -0.005516963680092404, -0.004506439605309381, -0.002243915738203439, 0.000509839095017221, + 0.002906991659042924, 0.004268577543475138, 0.004271507641806298, 0.003016832609770853, 0.000968078677012544, +-0.001212438364790582, -0.002873486424864839, -0.003565698051769338, -0.003158534255089867, -0.001854539656237390, +-0.000104494434130949, 0.001544038090357682, 0.002615068817313840, 0.002838581123665033, 0.002214005025736412, + 0.000989560561609744, -0.000429922430657965, -0.001614005062810786, -0.002234880299660999, -0.002155359985238479, +-0.001453941368175342, -0.000384610343377712, 0.000711563768288702, 0.001513186414012800, 0.001810835543451637, + 0.001559026923347157, 0.000875943963655002, -0.000004610004754826, -0.000810990907316290, -0.001315934285574502, +-0.001398840064879135, -0.001071638023123453, -0.000463869809966486, 0.000225095253699184, 0.000789896649978617, + 0.001079068090563369, 0.001033942501059566, 0.000697200136082736, 0.000191371674406576, -0.000323704342054218, +-0.000700190684320865, -0.000843497343068510, -0.000734325622577685, -0.000427051494167815, -0.000027211329865173, + 0.000343410640475203, 0.000582869672082906, 0.000635967434817550, 0.000505224922624527, 0.000244508351024915, +-0.000061120176458703, -0.000322205344194264, -0.000470132844463270, -0.000473961146691502, -0.000345130836201596, +-0.000130126194004534, 0.000105514360701591, 0.000296443107959613, 0.000394500403136929, 0.000379109945031964, + 0.000259321418469537, 0.000067944169519721, -0.000149400346186253, -0.000346866350142718, -0.000489097193751572, +-0.000557575961050952, -0.000551912068282870, -0.000486513762577162, -0.000384429386564009, -0.000270459677107609, +-0.000165408687939920, -0.000082599263842674, -0.000027062229851180, 0.000003107336183074, 0.000014114646487651, + 0.000013600436904199, 0.000010509517951006} ; + +// Sample 48 kHz, pass 10, stop 12, ripple 0.1 dB, atten 100 dB. Pass 0.20833. Stop 0.25000. +double quiskFilt48dec24Coefs[98] = { 0.000036864882767612, 0.000009858596392836, -0.000330770380800406, -0.001009174072411182, +-0.001404963853116591, -0.000770236458542885, 0.000523955998497470, 0.000947009978368078, -0.000170647420729277, -0.001215337186275615, +-0.000339882761263432, 0.001380697691347411, 0.001052816245779061, -0.001289457483737270, -0.001905995022750999, + 0.000806360758757266, 0.002752294066736399, 0.000151371304906695, -0.003382080182092562, -0.001586160863198252, + 0.003550356871637037, 0.003397108074666923, -0.003014198882506884, -0.005368247151920206, 0.001578030229641401, + 0.007172700113962798, 0.000859017219495951, -0.008394441438252027, -0.004260927420042382, 0.008565389049746911, + 0.008423570232799422, -0.007213182430375741, -0.012960608818773384, 0.003906137602881253, 0.017299646590834120, + 0.001710647317106962, -0.020687435471124054, -0.009911496013769150, 0.022192116474879110, 0.020997544995749250, +-0.020577450597980520, -0.035567363858049900, 0.013892294138831605, 0.055472458661532512, 0.002296277239682308, +-0.087929779622300100, -0.045452533452794319, 0.182125569604839360, 0.410917417297433360, 0.410917417297433360, + 0.182125569604839360, -0.045452533452794319, -0.087929779622300100, 0.002296277239682308, 0.055472458661532512, + 0.013892294138831605, -0.035567363858049900, -0.020577450597980520, 0.020997544995749250, 0.022192116474879110, +-0.009911496013769150, -0.020687435471124054, 0.001710647317106962, 0.017299646590834120, 0.003906137602881253, +-0.012960608818773384, -0.007213182430375741, 0.008423570232799422, 0.008565389049746911, -0.004260927420042382, +-0.008394441438252027, 0.000859017219495951, 0.007172700113962798, 0.001578030229641401, -0.005368247151920206, +-0.003014198882506884, 0.003397108074666923, 0.003550356871637037, -0.001586160863198252, -0.003382080182092562, + 0.000151371304906695, 0.002752294066736399, 0.000806360758757266, -0.001905995022750999, -0.001289457483737270, + 0.001052816245779061, 0.001380697691347411, -0.000339882761263432, -0.001215337186275615, -0.000170647420729277, + 0.000947009978368078, 0.000523955998497470, -0.000770236458542885, -0.001404963853116591, -0.001009174072411182, +-0.000330770380800406, 0.000009858596392836, 0.000036864882767612} ; + +// Sample 16 kHz, pass 3, stop 4, ripple 0.2 dB, atten 100 dB. Stop 0.25000. +double quiskFilt16dec8Coefs[62] = { -0.000124177474244557, -0.000430523605276357, -0.000493631990211104, 0.000494123472239155, + 0.002450538023220896, 0.003434046886116560, 0.001430964941031747, -0.002160470594532627, -0.002743908756760310, + 0.001447692179523467, 0.005035286516949985, 0.001559830515735925, -0.005832171392166522, -0.005961983009450190, + 0.004090355806650406, 0.010727470109490524, 0.001295745938037953, -0.013610977881243281, -0.010206097747183476, + 0.011968191835149432, 0.021092443344418406, -0.003254546341513048, -0.030789280172024487, -0.014459667926688211, + 0.034543627845998542, 0.042879451356168191, -0.025057567311424923, -0.087496290237351254, -0.017166635170977933, + 0.194607349925601910, 0.387989214201396540, 0.387989214201396540, 0.194607349925601910, -0.017166635170977933, +-0.087496290237351254, -0.025057567311424923, 0.042879451356168191, 0.034543627845998542, -0.014459667926688211, +-0.030789280172024487, -0.003254546341513048, 0.021092443344418406, 0.011968191835149432, -0.010206097747183476, +-0.013610977881243281, 0.001295745938037953, 0.010727470109490524, 0.004090355806650406, -0.005961983009450190, +-0.005832171392166522, 0.001559830515735925, 0.005035286516949985, 0.001447692179523467, -0.002743908756760310, +-0.002160470594532627, 0.001430964941031747, 0.003434046886116560, 0.002450538023220896, 0.000494123472239155, +-0.000493631990211104, -0.000430523605276357, -0.000124177474244557} ; + + +// Sample 24 kHz, pass 4, stop 6, ripple 0.1 dB, atten 100 dB. Stop 0.25000. +double quiskAudio24p4Coefs[50] = { 0.000157528548309112, 0.000724067233656635, 0.001494063979696902, 0.001415398667382404, +-0.000379600362177000, -0.002568955219195720, -0.001955131144563022, 0.002290648015950155, 0.005318866809894723, + 0.001269605748864858, -0.007123779005412860, -0.008160775426007658, 0.003744714029953210, 0.015299947817479908, + 0.007159589230094381, -0.016608567214549036, -0.024058672575662729, 0.004961464480378132, 0.040021693556365548, + 0.025370838603802327, -0.042357473426880975, -0.080147218603820458, 0.005640775946851126, 0.201560113392096980, + 0.369146036302117400, 0.369146036302117400, 0.201560113392096980, 0.005640775946851126, -0.080147218603820458, +-0.042357473426880975, 0.025370838603802327, 0.040021693556365548, 0.004961464480378132, -0.024058672575662729, +-0.016608567214549036, 0.007159589230094381, 0.015299947817479908, 0.003744714029953210, -0.008160775426007658, +-0.007123779005412860, 0.001269605748864858, 0.005318866809894723, 0.002290648015950155, -0.001955131144563022, +-0.002568955219195720, -0.000379600362177000, 0.001415398667382404, 0.001494063979696902, 0.000724067233656635, + 0.000157528548309112} ; + +// Sample 24 kHz, pass 6, stop 8, ripple 0.5 dB, atten 80 dB. Stop 0.33333. +double quiskAudio24p6Coefs[36] = { 0.001199140008010727, 0.005953815908571521, 0.008621055763448699, -0.000319602525571569, +-0.008665733839154772, 0.003122601697996235, 0.011839435384729619, -0.008973143363910490, -0.014884256392667099, 0.019451226278492342, + 0.015232717446420085, -0.036364345084318225, -0.008723687718470995, 0.063232433222709966, -0.014104739941611717, -0.115885442639735490, + 0.103975832351096060, 0.487646324142562260, 0.487646324142562260, 0.103975832351096060, -0.115885442639735490, -0.014104739941611717, + 0.063232433222709966, -0.008723687718470995, -0.036364345084318225, 0.015232717446420085, 0.019451226278492342, -0.014884256392667099, +-0.008973143363910490, 0.011839435384729619, 0.003122601697996235, -0.008665733839154772, -0.000319602525571569, 0.008621055763448699, + 0.005953815908571521, 0.001199140008010727} ; + +// Sample 48 kHz, pass 6, stop 8, ripple 0.5 dB, atten 80 dB. Stop 0.16667. +double quiskAudio48p6Coefs[71] = { 0.000324677779792651, 0.001038192405843851, 0.002082337464742640, 0.002938055116822665, + 0.002811674671240099, 0.001132278163758895, -0.001835394694865431, -0.004766409947196539, -0.005833007003128954, -0.003914015660218843, + 0.000296099005521212, 0.004239593489554206, 0.004918390640978499, 0.001100872619673516, -0.005241634309386784, +-0.009658192522859132, -0.008117324326241930, -0.000324815284776402, 0.009102631659469559, 0.013098226740520415, + 0.007176675441219364, -0.006349840429806648, -0.018522542400385748, -0.019189364957006558, -0.004749023128788906, + 0.017303403623348305, 0.031306220127229140, 0.023400239035184393, -0.007229519056042042, -0.043717246110810550, +-0.058176618846235130, -0.027325746983863020, 0.051711450483087174, 0.155566868220561340, 0.243667260926508620, + 0.278152523812557280, 0.243667260926508620, 0.155566868220561340, 0.051711450483087174, -0.027325746983863020, +-0.058176618846235130, -0.043717246110810550, -0.007229519056042042, 0.023400239035184393, 0.031306220127229140, + 0.017303403623348305, -0.004749023128788906, -0.019189364957006558, -0.018522542400385748, -0.006349840429806648, + 0.007176675441219364, 0.013098226740520415, 0.009102631659469559, -0.000324815284776402, -0.008117324326241930, +-0.009658192522859132, -0.005241634309386784, 0.001100872619673516, 0.004918390640978499, 0.004239593489554206, + 0.000296099005521212, -0.003914015660218843, -0.005833007003128954, -0.004766409947196539, -0.001835394694865431, + 0.001132278163758895, 0.002811674671240099, 0.002938055116822665, 0.002082337464742640, 0.001038192405843851, + 0.000324677779792651} ; + +// Sample 96 kHz, pass 6, stop 40, ripple 0.1 dB, atten 100 dB. Stop 0.41667. +double quiskAudio96Coefs[11] = { -0.004756607210954994, -0.022893760473075838, -0.023609616122334920, + 0.078161168368381481, 0.277880235815005590, 0.388489826271597950, 0.277880235815005590, + 0.078161168368381481, -0.023609616122334920, -0.022893760473075838, -0.004756607210954994} ; + + +// Sample 12000 kHz, high pass, stop 180, pass 300, ripple 0.2 dB, atten 80 dB. +double quiskAudioFmHpCoefs[309] = { 0.004847574947800705, -0.001211970386842814, -0.001072277374239886, -0.000949749961336003, +-0.000841696354699768, -0.000745515693376396, -0.000658731927598885, -0.000579117971409598, -0.000504808183463150, -0.000434052387522716, +-0.000365311474947399, -0.000297420057921267, -0.000229505695217796, -0.000160920338573639, -0.000091306103977099, -0.000020600671979957, + 0.000051022545303487, 0.000123144667419350, 0.000195091536211946, 0.000265999359621072, 0.000334844261309097, 0.000400429077199748, + 0.000461460673600523, 0.000516639603401830, 0.000564613893906762, 0.000603966827912324, 0.000633383885155741, 0.000651723486505544, + 0.000657887414710607, 0.000650908078289207, 0.000630221666830102, 0.000595435888069700, 0.000546108904674455, 0.000484276489349049, + 0.000401543327440382, 0.000321419603506285, 0.000213106256109367, 0.000098353002391692, -0.000019740287652141, -0.000144911454925575, +-0.000277381849908065, -0.000413476038173425, -0.000548427361576298, -0.000678490099554352, -0.000801069365710719, -0.000914049078670187, +-0.001015286248346293, -0.001102331400542105, -0.001172396297916355, -0.001222632870457348, -0.001250498303855702, -0.001253983783014675, +-0.001231724573461644, -0.001182999511712966, -0.001107609474592948, -0.001005802502109255, -0.000878333102915519, -0.000726459269042778, +-0.000551839538058223, -0.000356598632546370, -0.000143511677076383, 0.000084082690787220, 0.000322510489680255, 0.000567640643123008, + 0.000814725473802231, 0.001058851063175263, 0.001295215851931261, 0.001517809497406495, 0.001723717744529424, 0.001902986849115314, + 0.002058196188280081, 0.002178885915107971, 0.002262004073121292, 0.002306568447922007, 0.002308817130050681, 0.002265197627398382, + 0.002174415827424258, 0.002036784086082496, 0.001852852576328433, 0.001623326794876901, 0.001349852467618926, 0.001035442578833791, + 0.000684336173595900, 0.000301699601011732, -0.000106613479977550, -0.000534173868829619, -0.000973944167066713, -0.001418238552947833, +-0.001858773604862586, -0.002286765126902860, -0.002693131197271969, -0.003068872591348129, -0.003405360512862820, -0.003694294048148267, +-0.003927679645897309, -0.004098170195163846, -0.004199334823619039, -0.004225516845056660, -0.004171926884864147, -0.004035318109791245, +-0.003814086552001789, -0.003507185822613928, -0.003116677958603595, -0.002643536244472083, -0.002094553515010935, -0.001473198308268938, +-0.000787663079055384, -0.000048057914319065, 0.000736224014748010, 0.001553680380245313, 0.002390615203692300, 0.003232587312827377, + 0.004064917562862491, 0.004872099692091644, 0.005637649839955575, 0.006344792681250736, 0.006977074214765010, 0.007518456060848391, + 0.007953298539805269, 0.008266566568951741, 0.008444193362869966, 0.008473441636160825, 0.008343141792745356, 0.008043815615010122, + 0.007567858472781280, 0.006909789777373219, 0.006066331643824790, 0.005036377728498742, 0.003821244456796700, 0.002425048400384444, + 0.000854549919626146, -0.000881361873217043, -0.002771584559469569, -0.004802304296173217, -0.006957451254254373, -0.009219821548686471, +-0.011569065498728633, -0.013984260863667768, -0.016442032516463558, -0.018919305853375375, -0.021391356610149178, -0.023832595189340611, +-0.026218718241990988, -0.028524575634244292, -0.030725193547532524, -0.032797022202043544, -0.034717887030001365, -0.036466683292335302, +-0.038023870249327039, -0.039372161079335152, -0.040496648982101487, -0.041384719119703033, -0.042026270620733261, -0.042414057053836371, + 0.957456210208300410, -0.042414057053836371, -0.042026270620733261, -0.041384719119703033, -0.040496648982101487, -0.039372161079335152, +-0.038023870249327039, -0.036466683292335302, -0.034717887030001365, -0.032797022202043544, -0.030725193547532524, -0.028524575634244292, +-0.026218718241990988, -0.023832595189340611, -0.021391356610149178, -0.018919305853375375, -0.016442032516463558, -0.013984260863667768, +-0.011569065498728633, -0.009219821548686471, -0.006957451254254373, -0.004802304296173217, -0.002771584559469569, -0.000881361873217043, + 0.000854549919626146, 0.002425048400384444, 0.003821244456796700, 0.005036377728498742, 0.006066331643824790, 0.006909789777373219, + 0.007567858472781280, 0.008043815615010122, 0.008343141792745356, 0.008473441636160825, 0.008444193362869966, 0.008266566568951741, + 0.007953298539805269, 0.007518456060848391, 0.006977074214765010, 0.006344792681250736, 0.005637649839955575, 0.004872099692091644, + 0.004064917562862491, 0.003232587312827377, 0.002390615203692300, 0.001553680380245313, 0.000736224014748010, -0.000048057914319065, +-0.000787663079055384, -0.001473198308268938, -0.002094553515010935, -0.002643536244472083, -0.003116677958603595, -0.003507185822613928, +-0.003814086552001789, -0.004035318109791245, -0.004171926884864147, -0.004225516845056660, -0.004199334823619039, -0.004098170195163846, +-0.003927679645897309, -0.003694294048148267, -0.003405360512862820, -0.003068872591348129, -0.002693131197271969, -0.002286765126902860, +-0.001858773604862586, -0.001418238552947833, -0.000973944167066713, -0.000534173868829619, -0.000106613479977550, 0.000301699601011732, + 0.000684336173595900, 0.001035442578833791, 0.001349852467618926, 0.001623326794876901, 0.001852852576328433, 0.002036784086082496, + 0.002174415827424258, 0.002265197627398382, 0.002308817130050681, 0.002306568447922007, 0.002262004073121292, 0.002178885915107971, + 0.002058196188280081, 0.001902986849115314, 0.001723717744529424, 0.001517809497406495, 0.001295215851931261, 0.001058851063175263, + 0.000814725473802231, 0.000567640643123008, 0.000322510489680255, 0.000084082690787220, -0.000143511677076383, -0.000356598632546370, +-0.000551839538058223, -0.000726459269042778, -0.000878333102915519, -0.001005802502109255, -0.001107609474592948, -0.001182999511712966, +-0.001231724573461644, -0.001253983783014675, -0.001250498303855702, -0.001222632870457348, -0.001172396297916355, -0.001102331400542105, +-0.001015286248346293, -0.000914049078670187, -0.000801069365710719, -0.000678490099554352, -0.000548427361576298, -0.000413476038173425, +-0.000277381849908065, -0.000144911454925575, -0.000019740287652141, 0.000098353002391692, 0.000213106256109367, 0.000321419603506285, + 0.000401543327440382, 0.000484276489349049, 0.000546108904674455, 0.000595435888069700, 0.000630221666830102, 0.000650908078289207, + 0.000657887414710607, 0.000651723486505544, 0.000633383885155741, 0.000603966827912324, 0.000564613893906762, 0.000516639603401830, + 0.000461460673600523, 0.000400429077199748, 0.000334844261309097, 0.000265999359621072, 0.000195091536211946, 0.000123144667419350, + 0.000051022545303487, -0.000020600671979957, -0.000091306103977099, -0.000160920338573639, -0.000229505695217796, -0.000297420057921267, +-0.000365311474947399, -0.000434052387522716, -0.000504808183463150, -0.000579117971409598, -0.000658731927598885, -0.000745515693376396, +-0.000841696354699768, -0.000949749961336003, -0.001072277374239886, -0.001211970386842814, 0.004847574947800705} ; + +// Sample 24 kHz, pass 3, stop 4, ripple 0.1 dB, atten 100 dB. Stop 0.166667. +double quiskAudio24p3Coefs[100] = { 0.000040101217607380, 0.000144142945632828, 0.000313272623844654, 0.000472482653992188, + 0.000478145332060253, 0.000196098255702949, -0.000370577202968848, -0.000996638165520275, -0.001297816111521122, +-0.000964536466628102, -0.000042557313824444, 0.000962997869327471, 0.001323534221298773, 0.000594056697947640, +-0.000938434034616708, -0.002262533962755747, -0.002248428515927852, -0.000532704303549466, 0.001971754193404763, + 0.003472380925898466, 0.002523293296727912, -0.000709634306372636, -0.004178686614322947, -0.005154383468615377, +-0.002273829954161664, 0.003035630406357807, 0.007068054286328254, 0.006315276617742645, 0.000339547080891264, +-0.007198952625282303, -0.010594445062611715, -0.006237154402398545, 0.004041031055408734, 0.013268415977346956, + 0.013808366838176838, 0.003317264370573068, -0.012230819216356600, -0.021457570785737779, -0.015397758077551751, + 0.004833972586296557, 0.026490252025494231, 0.032271888015886901, 0.012604612531251926, -0.024801184242399594, +-0.055520053090148611, -0.050680798756218122, 0.005272625250748675, 0.102090078311359670, 0.205131072151264500, + 0.271331067474994470, 0.271331067474994470, 0.205131072151264500, 0.102090078311359670, 0.005272625250748675, +-0.050680798756218122, -0.055520053090148611, -0.024801184242399594, 0.012604612531251926, 0.032271888015886901, + 0.026490252025494231, 0.004833972586296557, -0.015397758077551751, -0.021457570785737779, -0.012230819216356600, + 0.003317264370573068, 0.013808366838176838, 0.013268415977346956, 0.004041031055408734, -0.006237154402398545, +-0.010594445062611715, -0.007198952625282303, 0.000339547080891264, 0.006315276617742645, 0.007068054286328254, + 0.003035630406357807, -0.002273829954161664, -0.005154383468615377, -0.004178686614322947, -0.000709634306372636, + 0.002523293296727912, 0.003472380925898466, 0.001971754193404763, -0.000532704303549466, -0.002248428515927852, +-0.002262533962755747, -0.000938434034616708, 0.000594056697947640, 0.001323534221298773, 0.000962997869327471, +-0.000042557313824444, -0.000964536466628102, -0.001297816111521122, -0.000996638165520275, -0.000370577202968848, + 0.000196098255702949, 0.000478145332060253, 0.000472482653992188, 0.000313272623844654, 0.000144142945632828, + 0.000040101217607380} ; + +// Sample 166.667 kHz, pass 20.000, stop 23.900, ripple 0.1dB, atten 100 dB. Stop 0.143400. +double quiskFilt167D3Coefs[174] = { 0.000030576791453527, 0.000100024508790516, 0.000219490573577180, 0.000367267217464130, + 0.000487951714515836, 0.000506290951844816, 0.000363119613743102, 0.000058217319683104, -0.000325800746668442, +-0.000641682936184189, -0.000741147106205377, -0.000558069498091234, -0.000164677374303788, 0.000241795422764969, + 0.000430784306851279, 0.000269110113911442, -0.000183008351848305, -0.000678061334270815, -0.000905480029500586, +-0.000676768412048412, -0.000064217209958982, 0.000604041196303946, 0.000913992713709960, 0.000618300484733981, +-0.000178595029186343, -0.001034200731311528, -0.001402993970581089, -0.000974211666713609, 0.000092170056602371, + 0.001200780364176702, 0.001644807535570492, 0.001049188500933230, -0.000339340877146445, -0.001726815143299963, +-0.002213609343829470, -0.001365082350867275, 0.000444074067558948, 0.002167326762557576, 0.002679800165208473, + 0.001499801473272823, -0.000816400722026263, -0.002906147604258461, -0.003386440545728839, -0.001750807523066417, + 0.001194559262667584, 0.003696427504621467, 0.004080958623548870, 0.001851416220932293, -0.001849699778520349, +-0.004790658557602842, -0.004973805523919799, -0.001961292686306448, 0.002659261470241232, 0.006072015731008707, + 0.005920856423841668, 0.001893698122810163, -0.003838202172739613, -0.007743253601342105, -0.007066127284542626, +-0.001708831992923160, 0.005393163352848182, 0.009818853048919281, 0.008356595075109337, 0.001237190414144524, +-0.007594080720116375, -0.012578375307378011, -0.009950345325599911, -0.000416149899767642, 0.010713667739836119, + 0.016347877720485654, 0.011969545448953062, -0.001060994469278557, -0.015508256818753343, -0.022033387392024387, +-0.014893466276995419, 0.003733297630878139, 0.023713405546777432, 0.031919145545518959, 0.019968942416937793, +-0.009436963490412478, -0.041522182058827371, -0.055167482103195595, -0.033140893435705220, 0.028140747437596390, + 0.114536627613187850, 0.198460066797957520, 0.250031620638361150, 0.250031620638361150, 0.198460066797957520, + 0.114536627613187850, 0.028140747437596390, -0.033140893435705220, -0.055167482103195595, -0.041522182058827371, +-0.009436963490412478, 0.019968942416937793, 0.031919145545518959, 0.023713405546777432, 0.003733297630878139, +-0.014893466276995419, -0.022033387392024387, -0.015508256818753343, -0.001060994469278557, 0.011969545448953062, + 0.016347877720485654, 0.010713667739836119, -0.000416149899767642, -0.009950345325599911, -0.012578375307378011, +-0.007594080720116375, 0.001237190414144524, 0.008356595075109337, 0.009818853048919281, 0.005393163352848182, +-0.001708831992923160, -0.007066127284542626, -0.007743253601342105, -0.003838202172739613, 0.001893698122810163, + 0.005920856423841668, 0.006072015731008707, 0.002659261470241232, -0.001961292686306448, -0.004973805523919799, +-0.004790658557602842, -0.001849699778520349, 0.001851416220932293, 0.004080958623548870, 0.003696427504621467, + 0.001194559262667584, -0.001750807523066417, -0.003386440545728839, -0.002906147604258461, -0.000816400722026263, + 0.001499801473272823, 0.002679800165208473, 0.002167326762557576, 0.000444074067558948, -0.001365082350867275, +-0.002213609343829470, -0.001726815143299963, -0.000339340877146445, 0.001049188500933230, 0.001644807535570492, + 0.001200780364176702, 0.000092170056602371, -0.000974211666713609, -0.001402993970581089, -0.001034200731311528, +-0.000178595029186343, 0.000618300484733981, 0.000913992713709960, 0.000604041196303946, -0.000064217209958982, +-0.000676768412048412, -0.000905480029500586, -0.000678061334270815, -0.000183008351848305, 0.000269110113911442, + 0.000430784306851279, 0.000241795422764969, -0.000164677374303788, -0.000558069498091234, -0.000741147106205377, +-0.000641682936184189, -0.000325800746668442, 0.000058217319683104, 0.000363119613743102, 0.000506290951844816, + 0.000487951714515836, 0.000367267217464130, 0.000219490573577180, 0.000100024508790516, 0.000030576791453527} ; + +// Sample 8000 Hz, stop 0 to 120, pass 300 to 2700, stop 2900 to 4000, ripple 0.1dB, atten 100 dB. +double quiskFiltTx8kAudioB[168] = { 0.000382863369135811, 0.001125903700328483, 0.000925837447276999, -0.000374734941094989, +-0.000888985517068820, -0.000361216817648551, -0.000190964056763804, -0.000521640215250387, -0.000810984560171181, +-0.000586556150016652, -0.000283591051604510, -0.000870238259790123, -0.000852903625374087, 0.000024159967210229, +-0.000630232168612451, -0.000785665131620066, 0.000569610938158391, -0.000138017351197930, -0.000362220277410620, + 0.001366339327936554, 0.000470327395373754, 0.000284347316273074, 0.002255344271539108, 0.000942593074105167, + 0.000895615209137272, 0.002941734992852122, 0.000961093640197151, 0.001177319952191160, 0.003099108113624071, + 0.000249508373597955, 0.000915783375976514, 0.002501346935887695, -0.001290867165414506, 0.000106788350951055, + 0.001148919726851922, -0.003476112709865105, -0.000973631179709113, -0.000674076242033701, -0.005811280332409654, +-0.001784274911171613, -0.002440004582391299, -0.007574097052077259, -0.001651967564004321, -0.003534348839131837, +-0.008009304387660080, 0.000005086297602882, -0.003482125314769407, -0.006580016297377269, 0.003414827979152467, +-0.002192304121179012, -0.003207666910599826, 0.008239448308648542, -0.000120276288831242, 0.001601392930489681, + 0.013506207426436229, 0.001723883024608883, 0.006793369654665180, 0.017715945730523756, 0.001956966866997749, + 0.011004187010827985, 0.019134477971688185, -0.000836412988146811, 0.012945050783659821, 0.016195220864669575, +-0.007677125676925340, 0.011863897915831447, 0.007909156115753185, -0.018792331215007903, 0.007957114843116177, +-0.005919014059116164, -0.033405211299572850, 0.002675271476407882, -0.024817610540172161, -0.049749838513716206, +-0.000915002981442826, -0.048502552862820653, -0.065383587718200784, 0.003175445066804758, -0.080194918286764838, +-0.077682133405468937, 0.033727063680071466, -0.150760186145271510, -0.084457338944868982, 0.505660603990009560, + 0.505660603990009560, -0.084457338944868982, -0.150760186145271510, 0.033727063680071466, -0.077682133405468937, +-0.080194918286764838, 0.003175445066804758, -0.065383587718200784, -0.048502552862820653, -0.000915002981442826, +-0.049749838513716206, -0.024817610540172161, 0.002675271476407882, -0.033405211299572850, -0.005919014059116164, + 0.007957114843116177, -0.018792331215007903, 0.007909156115753185, 0.011863897915831447, -0.007677125676925340, + 0.016195220864669575, 0.012945050783659821, -0.000836412988146811, 0.019134477971688185, 0.011004187010827985, + 0.001956966866997749, 0.017715945730523756, 0.006793369654665180, 0.001723883024608883, 0.013506207426436229, + 0.001601392930489681, -0.000120276288831242, 0.008239448308648542, -0.003207666910599826, -0.002192304121179012, + 0.003414827979152467, -0.006580016297377269, -0.003482125314769407, 0.000005086297602882, -0.008009304387660080, +-0.003534348839131837, -0.001651967564004321, -0.007574097052077259, -0.002440004582391299, -0.001784274911171613, +-0.005811280332409654, -0.000674076242033701, -0.000973631179709113, -0.003476112709865105, 0.001148919726851922, + 0.000106788350951055, -0.001290867165414506, 0.002501346935887695, 0.000915783375976514, 0.000249508373597955, + 0.003099108113624071, 0.001177319952191160, 0.000961093640197151, 0.002941734992852122, 0.000895615209137272, + 0.000942593074105167, 0.002255344271539108, 0.000284347316273074, 0.000470327395373754, 0.001366339327936554, +-0.000362220277410620, -0.000138017351197930, 0.000569610938158391, -0.000785665131620066, -0.000630232168612451, + 0.000024159967210229, -0.000852903625374087, -0.000870238259790123, -0.000283591051604510, -0.000586556150016652, +-0.000810984560171181, -0.000521640215250387, -0.000190964056763804, -0.000361216817648551, -0.000888985517068820, +-0.000374734941094989, 0.000925837447276999, 0.001125903700328483, 0.000382863369135811 } ; +// Sample 48000 Hz, pass 2350, stop 2650, ripple 1 dB, atten 80 dB. Stop 0.05521. +double quiskMic5Filt48Coefs[424] = { +-0.000087601196745517, -0.000119178480453066, -0.000192494530734137, -0.000287398500897765, -0.000404455107555149, +-0.000542187350923721, -0.000697354821092750, -0.000864361814542882, -0.001035675409027330, -0.001201670675683178, +-0.001351260760102592, -0.001472389090721280, -0.001552963425946309, -0.001581622501567850, -0.001548788902291486, +-0.001447577149496593, -0.001274757191818737, -0.001031365182705646, -0.000723138798454071, -0.000360540808884225, + 0.000041472210381792, 0.000464151153253057, 0.000885978417045319, 0.001284080850383070, 0.001635786964228304, + 0.001920322587244723, 0.002120431736364362, 0.002223894227313585, 0.002224702189457267, 0.002123834204231122, + 0.001929455560771270, 0.001656578667677397, 0.001326124950893504, 0.000963492246325779, 0.000596656779876184, + 0.000254003709475283, -0.000037984405595945, -0.000256935860609037, -0.000386544706976665, -0.000418046328997344, +-0.000351100298847373, -0.000193939445451714, 0.000037212952694738, 0.000319460819342812, 0.000625164037239141, + 0.000924363583767082, 0.001187470307244314, 0.001388000318914036, 0.001505069987035192, 0.001525437805536890, + 0.001444870661224978, 0.001268714805496182, 0.001011568778937986, 0.000696080786323511, 0.000350937369521106, + 0.000008234786560396, -0.000299551487706380, -0.000542701008001189, -0.000697204883792079, -0.000747233312787443, +-0.000686876298204384, -0.000520952456912102, -0.000264798296929030, 0.000056986807722520, 0.000412734842368625, + 0.000766669398901066, 0.001082346669587512, 0.001326263088537886, 0.001471265194417923, 0.001499431895309172, + 0.001404113830652116, 0.001190910569750263, 0.000877443544384873, 0.000491924577078300, 0.000070618732748754, +-0.000345567500376679, -0.000715070316355716, -0.000999834598355945, -0.001169257920406941, -0.001203497801923855, +-0.001095797960360223, -0.000853559853825754, -0.000498020971159122, -0.000062520041312805, 0.000410511052497376, + 0.000873571510876301, 0.001278748950006680, 0.001582567444665569, 0.001750535054942567, 0.001760927137558775, + 0.001607388781175447, 0.001300061195708352, 0.000865065069005933, 0.000342348399080757, -0.000217942303300231, +-0.000760241586711499, -0.001229053244090960, -0.001574604672635160, -0.001758102209059370, -0.001756034066558804, +-0.001563058384174837, -0.001193131136885269, -0.000678706705799908, -0.000068024295664869, 0.000579300435978082, + 0.001197989750585395, 0.001723580332124720, 0.002099032072900320, 0.002280794678156941, 0.002243699390601673, + 0.001984133400353534, 0.001521121064322945, 0.000895127993320633, 0.000164637922390302, -0.000599227139535745, +-0.001319555291352399, -0.001921314886803284, -0.002339087231340391, -0.002524079501171732, -0.002449675079358536, +-0.002114908771525688, -0.001545437314172064, -0.000791823925944463, 0.000074782605168353, 0.000969218460672265, + 0.001800534718138737, 0.002481100933662590, 0.002935686794045762, 0.003109591237339856, 0.002974959532860613, + 0.002534577733743369, 0.001822671724082916, 0.000902520745944448, -0.000138980947793396, -0.001199369993895262, +-0.002170536435942518, -0.002949581024448342, -0.003449532389579513, -0.003608829916851618, -0.003398558758097474, +-0.002826619977722426, -0.001938296425724648, -0.000813026544735210, 0.000442428219163585, 0.001703859853903780, + 0.002841646272526556, 0.003733795631607910, 0.004278716742047143, 0.004406405339454943, 0.004086851240208961, + 0.003334703369161471, 0.002209574959936082, 0.000811784328342671, -0.000726216440571004, -0.002252031018207481, +-0.003608037091401704, -0.004647249273905110, -0.005248768326240763, -0.005331240183248048, -0.004862881229352157, +-0.003866915473379464, -0.002421678784759232, -0.000655151171295565, 0.001265777761167976, 0.003150462759634698, + 0.004802753890816374, 0.006040653489191535, 0.006715477432817280, 0.006728582425712883, 0.006043878643663059, + 0.004694681402232655, 0.002783951074178680, 0.000477576037627513, -0.002008976414542753, -0.004429653284750409, +-0.006530865070729735, -0.008076534379704757, -0.008872792811204462, -0.008789897699281934, -0.007779088447247421, +-0.005882478096374164, -0.003234645557614322, -0.000055318960121547, 0.003366648103157717, 0.006696974329608038, + 0.009585521827024799, 0.011699303862532495, 0.012756057470065627, 0.012555316826801740, 0.011004006112499458, + 0.008133925792679506, 0.004109116367295407, -0.000778103172178875, -0.006122670687011962, -0.011433128819157286, +-0.016167043119046529, -0.019772887723249422, -0.021735132723275978, -0.021618869254173001, -0.019110214094446909, +-0.014048964546840230, -0.006450511384232603, 0.003485174039118999, 0.015378649609086421, 0.028690243785521201, + 0.042752300360393465, 0.056812206812071039, 0.070083061994479681, 0.081798161497452851, 0.091265110934848792, + 0.097915351884672430, 0.101345208406692710, 0.101345208406692710, 0.097915351884672430, 0.091265110934848792, + 0.081798161497452851, 0.070083061994479681, 0.056812206812071039, 0.042752300360393465, 0.028690243785521201, + 0.015378649609086421, 0.003485174039118999, -0.006450511384232603, -0.014048964546840230, -0.019110214094446909, +-0.021618869254173001, -0.021735132723275978, -0.019772887723249422, -0.016167043119046529, -0.011433128819157286, +-0.006122670687011962, -0.000778103172178875, 0.004109116367295407, 0.008133925792679506, 0.011004006112499458, + 0.012555316826801740, 0.012756057470065627, 0.011699303862532495, 0.009585521827024799, 0.006696974329608038, + 0.003366648103157717, -0.000055318960121547, -0.003234645557614322, -0.005882478096374164, -0.007779088447247421, +-0.008789897699281934, -0.008872792811204462, -0.008076534379704757, -0.006530865070729735, -0.004429653284750409, +-0.002008976414542753, 0.000477576037627513, 0.002783951074178680, 0.004694681402232655, 0.006043878643663059, + 0.006728582425712883, 0.006715477432817280, 0.006040653489191535, 0.004802753890816374, 0.003150462759634698, + 0.001265777761167976, -0.000655151171295565, -0.002421678784759232, -0.003866915473379464, -0.004862881229352157, +-0.005331240183248048, -0.005248768326240763, -0.004647249273905110, -0.003608037091401704, -0.002252031018207481, +-0.000726216440571004, 0.000811784328342671, 0.002209574959936082, 0.003334703369161471, 0.004086851240208961, + 0.004406405339454943, 0.004278716742047143, 0.003733795631607910, 0.002841646272526556, 0.001703859853903780, + 0.000442428219163585, -0.000813026544735210, -0.001938296425724648, -0.002826619977722426, -0.003398558758097474, +-0.003608829916851618, -0.003449532389579513, -0.002949581024448342, -0.002170536435942518, -0.001199369993895262, +-0.000138980947793396, 0.000902520745944448, 0.001822671724082916, 0.002534577733743369, 0.002974959532860613, + 0.003109591237339856, 0.002935686794045762, 0.002481100933662590, 0.001800534718138737, 0.000969218460672265, + 0.000074782605168353, -0.000791823925944463, -0.001545437314172064, -0.002114908771525688, -0.002449675079358536, +-0.002524079501171732, -0.002339087231340391, -0.001921314886803284, -0.001319555291352399, -0.000599227139535745, + 0.000164637922390302, 0.000895127993320633, 0.001521121064322945, 0.001984133400353534, 0.002243699390601673, + 0.002280794678156941, 0.002099032072900320, 0.001723580332124720, 0.001197989750585395, 0.000579300435978082, +-0.000068024295664869, -0.000678706705799908, -0.001193131136885269, -0.001563058384174837, -0.001756034066558804, +-0.001758102209059370, -0.001574604672635160, -0.001229053244090960, -0.000760241586711499, -0.000217942303300231, + 0.000342348399080757, 0.000865065069005933, 0.001300061195708352, 0.001607388781175447, 0.001760927137558775, + 0.001750535054942567, 0.001582567444665569, 0.001278748950006680, 0.000873571510876301, 0.000410511052497376, +-0.000062520041312805, -0.000498020971159122, -0.000853559853825754, -0.001095797960360223, -0.001203497801923855, +-0.001169257920406941, -0.000999834598355945, -0.000715070316355716, -0.000345567500376679, 0.000070618732748754, + 0.000491924577078300, 0.000877443544384873, 0.001190910569750263, 0.001404113830652116, 0.001499431895309172, + 0.001471265194417923, 0.001326263088537886, 0.001082346669587512, 0.000766669398901066, 0.000412734842368625, + 0.000056986807722520, -0.000264798296929030, -0.000520952456912102, -0.000686876298204384, -0.000747233312787443, +-0.000697204883792079, -0.000542701008001189, -0.000299551487706380, 0.000008234786560396, 0.000350937369521106, + 0.000696080786323511, 0.001011568778937986, 0.001268714805496182, 0.001444870661224978, 0.001525437805536890, + 0.001505069987035192, 0.001388000318914036, 0.001187470307244314, 0.000924363583767082, 0.000625164037239141, + 0.000319460819342812, 0.000037212952694738, -0.000193939445451714, -0.000351100298847373, -0.000418046328997344, +-0.000386544706976665, -0.000256935860609037, -0.000037984405595945, 0.000254003709475283, 0.000596656779876184, + 0.000963492246325779, 0.001326124950893504, 0.001656578667677397, 0.001929455560771270, 0.002123834204231122, + 0.002224702189457267, 0.002223894227313585, 0.002120431736364362, 0.001920322587244723, 0.001635786964228304, + 0.001284080850383070, 0.000885978417045319, 0.000464151153253057, 0.000041472210381792, -0.000360540808884225, +-0.000723138798454071, -0.001031365182705646, -0.001274757191818737, -0.001447577149496593, -0.001548788902291486, +-0.001581622501567850, -0.001552963425946309, -0.001472389090721280, -0.001351260760102592, -0.001201670675683178, +-0.001035675409027330, -0.000864361814542882, -0.000697354821092750, -0.000542187350923721, -0.000404455107555149, +-0.000287398500897765, -0.000192494530734137, -0.000119178480453066, -0.000087601196745517} ; + +// Sample 144 kHz, pass 20.0, stop 24.0, ripple 0.1dB, atten 100 dB. Pass 0.138889. Stop 0.166667. Decimate by 3. +double quiskFilt144D3Coefs[147] = { + 0.000016780987039824, 0.000019929721906467, -0.000029812334087366, -0.000185957342855632, -0.000457595041742833, -0.000763020582249048, +-0.000935502330460281, -0.000810648972696192, -0.000358803671633800, 0.000234868974461967, 0.000635518410854574, 0.000568649052653316, + 0.000046290780734364, -0.000576848135417221, -0.000812569262690978, -0.000406382962866972, 0.000409530745262834, 0.001018717022433781, + 0.000868514981759032, -0.000047834218816234, -0.001083548438467626, -0.001372913494385146, -0.000536984649454124, 0.000901238288475838, + 0.001808128325743618, 0.001313343518974492, -0.000390643142762094, -0.002034153793379463, -0.002190363787369149, -0.000486785830211702, + 0.001899971815554939, 0.003016393051189844, 0.001703802959800975, -0.001272713184907955, -0.003592060794645169, -0.003153839927509408, + 0.000067273868535848, 0.003691997080030787, 0.004646339876200064, 0.001725059542379199, -0.003095000160661851, -0.005913611355699069, +-0.004013555322861903, 0.001618893140735101, 0.006628860694469696, 0.006593761434451985, 0.000843918175071128, -0.006434294798483291, +-0.009145937757863428, -0.004298379230455153, 0.004970723844275416, 0.011242560431712569, 0.008630896022305587, -0.001897430256163395, +-0.012354817079092976, -0.013606356255629011, -0.003111995792617545, 0.011842459244895697, 0.018885015598445000, 0.010442018188565504, +-0.008869010528864909, -0.024054103081211958, -0.020781522872984903, 0.002077600796052109, 0.028673899750479920, 0.035969215939495834, + 0.011698610672543283, -0.032323400359080219, -0.062850340377290967, -0.044670144014306225, 0.034665193983578813, 0.151715188318120200, + 0.256129260910589850, 0.297861169542637920, 0.256129260910589850, 0.151715188318120200, 0.034665193983578813, -0.044670144014306225, +-0.062850340377290967, -0.032323400359080219, 0.011698610672543283, 0.035969215939495834, 0.028673899750479920, 0.002077600796052109, +-0.020781522872984903, -0.024054103081211958, -0.008869010528864909, 0.010442018188565504, 0.018885015598445000, 0.011842459244895697, +-0.003111995792617545, -0.013606356255629011, -0.012354817079092976, -0.001897430256163395, 0.008630896022305587, 0.011242560431712569, + 0.004970723844275416, -0.004298379230455153, -0.009145937757863428, -0.006434294798483291, 0.000843918175071128, 0.006593761434451985, + 0.006628860694469696, 0.001618893140735101, -0.004013555322861903, -0.005913611355699069, -0.003095000160661851, 0.001725059542379199, + 0.004646339876200064, 0.003691997080030787, 0.000067273868535848, -0.003153839927509408, -0.003592060794645169, -0.001272713184907955, + 0.001703802959800975, 0.003016393051189844, 0.001899971815554939, -0.000486785830211702, -0.002190363787369149, -0.002034153793379463, +-0.000390643142762094, 0.001313343518974492, 0.001808128325743618, 0.000901238288475838, -0.000536984649454124, -0.001372913494385146, +-0.001083548438467626, -0.000047834218816234, 0.000868514981759032, 0.001018717022433781, 0.000409530745262834, -0.000406382962866972, +-0.000812569262690978, -0.000576848135417221, 0.000046290780734364, 0.000568649052653316, 0.000635518410854574, 0.000234868974461967, +-0.000358803671633800, -0.000810648972696192, -0.000935502330460281, -0.000763020582249048, -0.000457595041742833, -0.000185957342855632, +-0.000029812334087366, 0.000019929721906467, 0.000016780987039824 +} ; + +// Sample 120000 Hz, pass 2700, stop 3730, ripple 0.1dB, atten 100 dB. Stop 0.03108. +double quiskFilt120s03[480] = { -0.000005050567303837, -0.000000267011791999, 0.000000197734700398, 0.000001038946634000, + 0.000002322193058869, 0.000004115682735322, 0.000006499942123311, 0.000009551098482930, 0.000013350669444763, + 0.000017966192635412, 0.000023463361155584, 0.000029885221425020, 0.000037271082107518, 0.000045630720487935, + 0.000054970017069384, 0.000065233162392019, 0.000076360900545177, 0.000088271373315159, 0.000100818605854714, + 0.000113853476544409, 0.000127174196746337, 0.000140558396336177, 0.000153744508371709, 0.000166450784469067, + 0.000178368313347299, 0.000189176709991702, 0.000198541881389953, 0.000206128795372885, 0.000211604878787747, + 0.000214655997661182, 0.000214994859281552, 0.000212358734245594, 0.000206539880117977, 0.000197379393194548, + 0.000184780318878738, 0.000168719942655099, 0.000149250512353807, 0.000126511346757621, 0.000100726393185629, + 0.000072210925236429, 0.000041365841965015, 0.000008680571408025, -0.000025277165852799, -0.000059865389594949, +-0.000094384355854646, -0.000128080670195777, -0.000160170174848483, -0.000189854272533545, -0.000216333899003825, +-0.000238836419299503, -0.000256632149501508, -0.000269058714331757, -0.000275541485292432, -0.000275614059005332, +-0.000268937472718753, -0.000255317038867589, -0.000234717772155001, -0.000207273956099563, -0.000173297342436372, +-0.000133280012107173, -0.000087895370243821, -0.000037986085678081, 0.000015440388211825, 0.000071232572821451, + 0.000128114399130489, 0.000184710477990398, 0.000239577162514028, 0.000291234779803098, 0.000338204791740229, + 0.000379047713684221, 0.000412403761615261, 0.000437031818051652, 0.000451848709179591, 0.000455966225408344, + 0.000448726371643413, 0.000429729020814434, 0.000398857326863837, 0.000356297600912998, 0.000302547334727027, + 0.000238422248479072, 0.000165048886226905, 0.000083853091464077, -0.000003462782744354, -0.000094949813106744, +-0.000188451833293202, -0.000281651282503015, -0.000372121907291206, -0.000457387566635848, -0.000534985542936898, +-0.000602532044011899, -0.000657788245032425, -0.000698728981427767, -0.000723604675185869, -0.000731002305621048, +-0.000719899536922384, -0.000689709694056092, -0.000640319946685634, -0.000572115873292030, -0.000485996080304965, +-0.000383371840261246, -0.000266155252511831, -0.000136731311264191, 0.000002082667095075, 0.000147092077716480, + 0.000294790953130229, 0.000441441918072383, 0.000583164190168290, 0.000716029226064227, 0.000836164238172957, + 0.000939856052624227, 0.001023657909064450, 0.001084492755093968, 0.001119751426837743, 0.001127383039339373, + 0.001105974243787613, 0.001054815583369999, 0.000973950761085690, 0.000864209315714227, 0.000727219011746881, + 0.000565398080608305, 0.000381924396468366, 0.000180685902835315, -0.000033793183292569, -0.000256444114966522, +-0.000481764526566339, -0.000703946352348464, -0.000917016099829735, -0.001114986581270253, -0.001292014799874503, +-0.001442563411804926, -0.001561559957317790, -0.001644551048567398, -0.001687846581475964, -0.001688649703502788, +-0.001645167889846890, -0.001556702802350076, -0.001423714708648073, -0.001247857669697092, -0.001031986722557201, +-0.000780131048444402, -0.000497436825078657, -0.000190077210351809, 0.000134868279325909, 0.000469563533327739, + 0.000805591531546815, 0.001134152328775355, 0.001446279849797673, 0.001733071409562941, 0.001985924997799762, + 0.002196778054604388, 0.002358342626407065, 0.002464328098407475, 0.002509648218888532, 0.002490604086803692, + 0.002405037734357425, 0.002252452724297770, 0.002034094661603120, 0.001752990365583534, 0.001413941154886139, + 0.001023470495638453, 0.000589723521647734, 0.000122320866350319, -0.000367832138027160, -0.000868777013398284, +-0.001367771151677059, -0.001851587344265625, -0.002306838088978190, -0.002720317947026380, -0.003079353614002113, +-0.003372155891804708, -0.003588162376578369, -0.003718362558663737, -0.003755596511143005, -0.003694818131674599, +-0.003533315298404129, -0.003270878754553819, -0.002909914962857412, -0.002455496391464944, -0.001915346645364514, +-0.001299757227227888, -0.000621437066532776, 0.000104706515738248, 0.000861849931067767, 0.001631595707499856, + 0.002394368911341672, 0.003129858565588139, 0.003817496679992245, 0.004436963307209760, 0.004968707287606522, + 0.005394469536085115, 0.005697797543539088, 0.005864537618023589, 0.005883292537600076, 0.005745832319314692, + 0.005447447099071761, 0.004987231255534477, 0.004368289529377007, 0.003597859022418248, 0.002687338851256991, + 0.001652226293162047, 0.000511956075882180, -0.000710356149138656, -0.001988263330091648, -0.003292424566049982, +-0.004591123342747130, -0.005850857852106148, -0.007036991266043732, -0.008114450164977267, -0.009048456200082230, +-0.009805276478965942, -0.010352975302354198, -0.010662152577592631, -0.010706650669328861, -0.010464214075017983, +-0.009917087295446811, -0.009052534679222271, -0.007863270920348924, -0.006347789704693751, -0.004510582323649121, +-0.002362238055733795, 0.000080576968834213, 0.002795265196543707, 0.005753566158586979, 0.008921944932552510, + 0.012262093950265378, 0.015731539846483594, 0.019284344624007944, 0.022871886384520687, 0.026443706729191677, + 0.029948406200633094, 0.033334570666910354, 0.036551709955124537, 0.039551189200810140, 0.042287133974308874, + 0.044717290029466283, 0.046803820535016104, 0.048514022996355009, 0.049820951883635139, 0.050703932928426454, + 0.051148959210315710, 0.051148959210315710, 0.050703932928426454, 0.049820951883635139, 0.048514022996355009, + 0.046803820535016104, 0.044717290029466283, 0.042287133974308874, 0.039551189200810140, 0.036551709955124537, + 0.033334570666910354, 0.029948406200633094, 0.026443706729191677, 0.022871886384520687, 0.019284344624007944, + 0.015731539846483594, 0.012262093950265378, 0.008921944932552510, 0.005753566158586979, 0.002795265196543707, + 0.000080576968834213, -0.002362238055733795, -0.004510582323649121, -0.006347789704693751, -0.007863270920348924, +-0.009052534679222271, -0.009917087295446811, -0.010464214075017983, -0.010706650669328861, -0.010662152577592631, +-0.010352975302354198, -0.009805276478965942, -0.009048456200082230, -0.008114450164977267, -0.007036991266043732, +-0.005850857852106148, -0.004591123342747130, -0.003292424566049982, -0.001988263330091648, -0.000710356149138656, + 0.000511956075882180, 0.001652226293162047, 0.002687338851256991, 0.003597859022418248, 0.004368289529377007, + 0.004987231255534477, 0.005447447099071761, 0.005745832319314692, 0.005883292537600076, 0.005864537618023589, + 0.005697797543539088, 0.005394469536085115, 0.004968707287606522, 0.004436963307209760, 0.003817496679992245, + 0.003129858565588139, 0.002394368911341672, 0.001631595707499856, 0.000861849931067767, 0.000104706515738248, +-0.000621437066532776, -0.001299757227227888, -0.001915346645364514, -0.002455496391464944, -0.002909914962857412, +-0.003270878754553819, -0.003533315298404129, -0.003694818131674599, -0.003755596511143005, -0.003718362558663737, +-0.003588162376578369, -0.003372155891804708, -0.003079353614002113, -0.002720317947026380, -0.002306838088978190, +-0.001851587344265625, -0.001367771151677059, -0.000868777013398284, -0.000367832138027160, 0.000122320866350319, + 0.000589723521647734, 0.001023470495638453, 0.001413941154886139, 0.001752990365583534, 0.002034094661603120, + 0.002252452724297770, 0.002405037734357425, 0.002490604086803692, 0.002509648218888532, 0.002464328098407475, + 0.002358342626407065, 0.002196778054604388, 0.001985924997799762, 0.001733071409562941, 0.001446279849797673, + 0.001134152328775355, 0.000805591531546815, 0.000469563533327739, 0.000134868279325909, -0.000190077210351809, +-0.000497436825078657, -0.000780131048444402, -0.001031986722557201, -0.001247857669697092, -0.001423714708648073, +-0.001556702802350076, -0.001645167889846890, -0.001688649703502788, -0.001687846581475964, -0.001644551048567398, +-0.001561559957317790, -0.001442563411804926, -0.001292014799874503, -0.001114986581270253, -0.000917016099829735, +-0.000703946352348464, -0.000481764526566339, -0.000256444114966522, -0.000033793183292569, 0.000180685902835315, + 0.000381924396468366, 0.000565398080608305, 0.000727219011746881, 0.000864209315714227, 0.000973950761085690, + 0.001054815583369999, 0.001105974243787613, 0.001127383039339373, 0.001119751426837743, 0.001084492755093968, + 0.001023657909064450, 0.000939856052624227, 0.000836164238172957, 0.000716029226064227, 0.000583164190168290, + 0.000441441918072383, 0.000294790953130229, 0.000147092077716480, 0.000002082667095075, -0.000136731311264191, +-0.000266155252511831, -0.000383371840261246, -0.000485996080304965, -0.000572115873292030, -0.000640319946685634, +-0.000689709694056092, -0.000719899536922384, -0.000731002305621048, -0.000723604675185869, -0.000698728981427767, +-0.000657788245032425, -0.000602532044011899, -0.000534985542936898, -0.000457387566635848, -0.000372121907291206, +-0.000281651282503015, -0.000188451833293202, -0.000094949813106744, -0.000003462782744354, 0.000083853091464077, + 0.000165048886226905, 0.000238422248479072, 0.000302547334727027, 0.000356297600912998, 0.000398857326863837, + 0.000429729020814434, 0.000448726371643413, 0.000455966225408344, 0.000451848709179591, 0.000437031818051652, + 0.000412403761615261, 0.000379047713684221, 0.000338204791740229, 0.000291234779803098, 0.000239577162514028, + 0.000184710477990398, 0.000128114399130489, 0.000071232572821451, 0.000015440388211825, -0.000037986085678081, +-0.000087895370243821, -0.000133280012107173, -0.000173297342436372, -0.000207273956099563, -0.000234717772155001, +-0.000255317038867589, -0.000268937472718753, -0.000275614059005332, -0.000275541485292432, -0.000269058714331757, +-0.000256632149501508, -0.000238836419299503, -0.000216333899003825, -0.000189854272533545, -0.000160170174848483, +-0.000128080670195777, -0.000094384355854646, -0.000059865389594949, -0.000025277165852799, 0.000008680571408025, + 0.000041365841965015, 0.000072210925236429, 0.000100726393185629, 0.000126511346757621, 0.000149250512353807, + 0.000168719942655099, 0.000184780318878738, 0.000197379393194548, 0.000206539880117977, 0.000212358734245594, + 0.000214994859281552, 0.000214655997661182, 0.000211604878787747, 0.000206128795372885, 0.000198541881389953, + 0.000189176709991702, 0.000178368313347299, 0.000166450784469067, 0.000153744508371709, 0.000140558396336177, + 0.000127174196746337, 0.000113853476544409, 0.000100818605854714, 0.000088271373315159, 0.000076360900545177, + 0.000065233162392019, 0.000054970017069384, 0.000045630720487935, 0.000037271082107518, 0.000029885221425020, + 0.000023463361155584, 0.000017966192635412, 0.000013350669444763, 0.000009551098482930, 0.000006499942123311, + 0.000004115682735322, 0.000002322193058869, 0.000001038946634000, 0.000000197734700398, -0.000000267011791999, +-0.000005050567303837 }; + +// Sample 9600 kHz, pass 168, stop 216, ripple 0.1dB, atten 100 dB. Stop 0.0225. +double quiskFiltI3D25Coefs[825] = { 0.000005956787452187, 0.000003211705784635, 0.000004034862817142, 0.000004961356794279, + 0.000005992480684896, 0.000007125011282928, 0.000008356176996335, 0.000009678674512241, 0.000011085681450866, 0.000012565356814181, + 0.000014105577722777, 0.000015688094611533, 0.000017293987798534, 0.000018898513368975, 0.000020477970947099, 0.000022003882695500, + 0.000023449026651569, 0.000024779370194270, 0.000025960975613092, 0.000026954037148028, 0.000027724129005342, 0.000028233620872516, + 0.000028448168208646, 0.000028322403611347, 0.000027819244560919, 0.000026904364434149, 0.000025555768460688, 0.000023718858672597, + 0.000021388236737303, 0.000018531461504295, 0.000015128617900530, 0.000011172338433941, 0.000006650682808194, 0.000001567980147682, +-0.000004071844887927, -0.000010251946885753, -0.000016952037416704, -0.000024139403703601, -0.000031775674847193, -0.000039811520245419, +-0.000048192148911461, -0.000056852971113359, -0.000065722611018575, -0.000074720205221454, -0.000083759727095511, -0.000092749188319604, +-0.000101592108032084, -0.000110185878000537, -0.000118425032569519, -0.000126204064875885, -0.000133416362597982, -0.000139953974896786, +-0.000145712082126712, -0.000150594676074443, -0.000154502739904809, -0.000157349878306252, -0.000159058476336748, -0.000159555772217867, +-0.000158788109935490, -0.000156707360410909, -0.000153284964905293, -0.000148502970885125, -0.000142363405313636, -0.000134882362974756, +-0.000126095843217766, -0.000116055019098236, -0.000104831203935920, -0.000092512000120117, -0.000079204203578124, -0.000065029325935631, +-0.000050126352194410, -0.000034647988869857, -0.000018761566915687, -0.000002643867645482, 0.000013517370747292, 0.000029526982476978, + 0.000045184046634717, 0.000060287289500742, 0.000074633464306207, 0.000088022237529899, 0.000100260387268166, 0.000111162183255553, + 0.000120552183081292, 0.000128273257824200, 0.000134179871751258, 0.000138151310658245, 0.000140084636985629, 0.000139904660813329, + 0.000137559905089999, 0.000133028662869780, 0.000126316758019544, 0.000117461929793833, 0.000106531164007165, 0.000093623364714925, + 0.000078866698566106, 0.000062420736245776, 0.000044472363393569, 0.000025236122848728, 0.000004949997449017, -0.000016124117496096, +-0.000037706322052971, -0.000059500304426378, -0.000081198420104433, -0.000102483313141189, -0.000123035628711338, -0.000142535319778232, +-0.000160667780360279, -0.000177128235455148, -0.000191627620690743, -0.000203893426905360, -0.000213680605929445, -0.000220768612480065, +-0.000224972129527046, -0.000226139442542857, -0.000224159617516556, -0.000218962999255533, -0.000210525248496711, -0.000198867299743239, +-0.000184058116066018, -0.000166214040676592, -0.000145499890142001, -0.000122126241762541, -0.000096349500708075, -0.000068468267762019, +-0.000038821745842297, -0.000007783989333822, 0.000024238365952557, 0.000056812590122291, 0.000089484319755483, 0.000121785090745373, + 0.000153236391205647, 0.000183359023230085, 0.000211678221012634, 0.000237731645367386, 0.000261075639702519, 0.000281294681250729, + 0.000298004342402298, 0.000310862516511249, 0.000319571315012658, 0.000323886296653843, 0.000323619563118675, 0.000318644983901538, + 0.000308901513454268, 0.000294396418133417, 0.000275206841101241, 0.000251480887099541, 0.000223437061305126, 0.000191363947212061, + 0.000155617045621414, 0.000116616164620783, 0.000074840231468239, 0.000030823184154666, -0.000014853764966253, -0.000061568272389658, +-0.000108665640335580, -0.000155466780716653, -0.000201278972426392, -0.000245403805143397, -0.000287148930939394, -0.000325837733962965, +-0.000360820345491574, -0.000391482737253097, -0.000417258980581691, -0.000437638220339685, -0.000452176524646696, -0.000460503187462496, +-0.000462329168633824, -0.000457453739285960, -0.000445769357864568, -0.000427266209021117, -0.000402034633782136, -0.000370266824699425, +-0.000332256748568874, -0.000288398092983417, -0.000239181635064980, -0.000185190057199916, -0.000127092351239062, -0.000065635351677973, +-0.000001635768489257, 0.000064031032789795, 0.000130441926615597, 0.000196638895244753, 0.000261641460484809, 0.000324462014025233, + 0.000384119062216776, 0.000439652650855628, 0.000490138798319519, 0.000534704796699404, 0.000572542368876905, 0.000602922923466479, + 0.000625208686170919, 0.000638865887614407, 0.000643474954073381, 0.000638739227035361, 0.000624493258921266, 0.000600707953030737, + 0.000567495007786956, 0.000525108546609422, 0.000473944885437249, 0.000414540661406180, 0.000347567762721358, 0.000273827417009147, + 0.000194240866662439, 0.000109839511430035, 0.000021751281014785, -0.000068812726758276, -0.000160574821442717, -0.000252207643694647, +-0.000342353358610352, -0.000429641983841077, -0.000512712588279566, -0.000590233000458980, -0.000660920833710300, -0.000723563310391309, +-0.000777037688231366, -0.000820329138707098, -0.000852549805275905, -0.000872954045876351, -0.000880953408348413, -0.000876129480105976, +-0.000858243707443438, -0.000827245823239233, -0.000783278920419950, -0.000726681966249080, -0.000657989929078930, -0.000577929796159890, +-0.000487415411907342, -0.000387537349196916, -0.000279551948319666, -0.000164865572977560, -0.000045018545421742, 0.000078335772017527, + 0.000203451977405086, 0.000328516992293320, 0.000451674636902284, 0.000571053266050113, 0.000684792094554321, 0.000791070116123563, + 0.000888133372727545, 0.000974323218404695, 0.001048102631468540, 0.001108082857136429, 0.001153046414612510, 0.001181970380085016, + 0.001194045545471417, 0.001188693681255879, 0.001165581726339971, 0.001124632477690236, 0.001066031623671160, 0.000990231761272084, + 0.000897951111087878, 0.000790170354647780, 0.000668122820361005, 0.000533283635452978, 0.000387352203340500, 0.000232233621309843, + 0.000070013789254570, -0.000097065849154538, -0.000266639011630557, -0.000436245878669300, -0.000603368261275017, -0.000765464501726597, +-0.000920007831966061, -0.001064523015142339, -0.001196625296357533, -0.001314057093625971, -0.001414725206513849, -0.001496734827620223, +-0.001558423502125369, -0.001598390052570974, -0.001615522526442618, -0.001609020914211984, -0.001578416762563850, -0.001523587643370717, +-0.001444767829815729, -0.001342552379906827, -0.001217898207865990, -0.001072116979449732, -0.000906866009268772, -0.000724130257712046, +-0.000526202440125150, -0.000315655017075901, -0.000095310483896663, 0.000131795797362412, 0.000362454044567802, 0.000593324198824254, + 0.000820981122794087, 0.001041964098270290, 0.001252825798400345, 0.001450184706684350, 0.001630775350917507, 0.001791500206060708, + 0.001929478553733191, 0.002042095133402677, 0.002127043989719768, 0.002182371279443224, 0.002206511463226233, 0.002198320858196307, + 0.002157104185387105, 0.002082636609166360, 0.001975177863452211, 0.001835481726247019, 0.001664795879174717, 0.001464857801720407, + 0.001237879727116529, 0.000986530138212679, 0.000713904673185216, 0.000423493241763419, 0.000119137628223145, -0.000195014201816123, +-0.000514561915304833, -0.000834909513284288, -0.001151328068004269, -0.001459020234601911, -0.001753189335167306, -0.002029107895048567, +-0.002282189352141815, -0.002508057311799787, -0.002702615502811452, -0.002862113710271713, -0.002983212400539942, -0.003063040853327989, +-0.003099252599280703, -0.003090072441393503, -0.003034338569084416, -0.002931535299727895, -0.002781819336183325, -0.002586034995156839, +-0.002345722740096574, -0.002063114928211339, -0.001741125267586707, -0.001383324779597604, -0.000993911106418735, -0.000577665400414888, +-0.000139902377919259, 0.000313590758783057, 0.000776621606795215, 0.001242669040263383, 0.001704964962659825, 0.002156583049235837, + 0.002590530627198782, 0.002999845936290586, 0.003377695514868402, 0.003717474718472581, 0.004012906048525476, 0.004258137513089033, + 0.004447836307400812, 0.004577280134783838, 0.004642440654159172, 0.004640062324902708, 0.004567731152875479, 0.004423936073887462, + 0.004208118356932086, 0.003920711899156275, 0.003563169368207032, 0.003137978634478423, 0.002648663135226872, 0.002099772065464075, + 0.001496854451285576, 0.000846422020958481, 0.000155897036473891, -0.000566451115784383, -0.001311583033678882, -0.002069776837875344, +-0.002830727368610444, -0.003583654241941997, -0.004317419512253912, -0.005020651106998095, -0.005681873967977548, -0.006289643577342038, +-0.006832684074428672, -0.007300025943397813, -0.007681144614133184, -0.007966095174260545, -0.008145645068345550, -0.008211399133170209, +-0.008155919326273169, -0.007972833886033631, -0.007656937873393390, -0.007204280391407546, -0.006612240797414353, -0.005879588674156967, +-0.005006531452725929, -0.003994743792822069, -0.002847382842898682, -0.001569085180199714, -0.000165948105466916, 0.001354506897970326, + 0.002983386500934207, 0.004710494837761955, 0.006524417826120845, 0.008412622862724265, 0.010361571520526983, 0.012356846115947544, + 0.014383285875718958, 0.016425134848742497, 0.018466196154810609, 0.020489994296242191, 0.022479940752782465, 0.024419503808109731, + 0.026292377563203666, 0.028082651706093722, 0.029774976416515504, 0.031354724068567270, 0.032808142535659224, 0.034122501353439978, + 0.035286226053449159, 0.036289022437609730, 0.037121985402646829, 0.037777695264168000, 0.038250296541390075, 0.038535561348862886, + 0.038630934515923344, 0.038535561348862886, 0.038250296541390075, 0.037777695264168000, 0.037121985402646829, 0.036289022437609730, + 0.035286226053449159, 0.034122501353439978, 0.032808142535659224, 0.031354724068567270, 0.029774976416515504, 0.028082651706093722, + 0.026292377563203666, 0.024419503808109731, 0.022479940752782465, 0.020489994296242191, 0.018466196154810609, 0.016425134848742497, + 0.014383285875718958, 0.012356846115947544, 0.010361571520526983, 0.008412622862724265, 0.006524417826120845, 0.004710494837761955, + 0.002983386500934207, 0.001354506897970326, -0.000165948105466916, -0.001569085180199714, -0.002847382842898682, -0.003994743792822069, +-0.005006531452725929, -0.005879588674156967, -0.006612240797414353, -0.007204280391407546, -0.007656937873393390, -0.007972833886033631, +-0.008155919326273169, -0.008211399133170209, -0.008145645068345550, -0.007966095174260545, -0.007681144614133184, -0.007300025943397813, +-0.006832684074428672, -0.006289643577342038, -0.005681873967977548, -0.005020651106998095, -0.004317419512253912, -0.003583654241941997, +-0.002830727368610444, -0.002069776837875344, -0.001311583033678882, -0.000566451115784383, 0.000155897036473891, 0.000846422020958481, + 0.001496854451285576, 0.002099772065464075, 0.002648663135226872, 0.003137978634478423, 0.003563169368207032, 0.003920711899156275, + 0.004208118356932086, 0.004423936073887462, 0.004567731152875479, 0.004640062324902708, 0.004642440654159172, 0.004577280134783838, + 0.004447836307400812, 0.004258137513089033, 0.004012906048525476, 0.003717474718472581, 0.003377695514868402, 0.002999845936290586, + 0.002590530627198782, 0.002156583049235837, 0.001704964962659825, 0.001242669040263383, 0.000776621606795215, 0.000313590758783057, +-0.000139902377919259, -0.000577665400414888, -0.000993911106418735, -0.001383324779597604, -0.001741125267586707, -0.002063114928211339, +-0.002345722740096574, -0.002586034995156839, -0.002781819336183325, -0.002931535299727895, -0.003034338569084416, -0.003090072441393503, +-0.003099252599280703, -0.003063040853327989, -0.002983212400539942, -0.002862113710271713, -0.002702615502811452, -0.002508057311799787, +-0.002282189352141815, -0.002029107895048567, -0.001753189335167306, -0.001459020234601911, -0.001151328068004269, -0.000834909513284288, +-0.000514561915304833, -0.000195014201816123, 0.000119137628223145, 0.000423493241763419, 0.000713904673185216, 0.000986530138212679, + 0.001237879727116529, 0.001464857801720407, 0.001664795879174717, 0.001835481726247019, 0.001975177863452211, 0.002082636609166360, + 0.002157104185387105, 0.002198320858196307, 0.002206511463226233, 0.002182371279443224, 0.002127043989719768, 0.002042095133402677, + 0.001929478553733191, 0.001791500206060708, 0.001630775350917507, 0.001450184706684350, 0.001252825798400345, 0.001041964098270290, + 0.000820981122794087, 0.000593324198824254, 0.000362454044567802, 0.000131795797362412, -0.000095310483896663, -0.000315655017075901, +-0.000526202440125150, -0.000724130257712046, -0.000906866009268772, -0.001072116979449732, -0.001217898207865990, -0.001342552379906827, +-0.001444767829815729, -0.001523587643370717, -0.001578416762563850, -0.001609020914211984, -0.001615522526442618, -0.001598390052570974, +-0.001558423502125369, -0.001496734827620223, -0.001414725206513849, -0.001314057093625971, -0.001196625296357533, -0.001064523015142339, +-0.000920007831966061, -0.000765464501726597, -0.000603368261275017, -0.000436245878669300, -0.000266639011630557, -0.000097065849154538, + 0.000070013789254570, 0.000232233621309843, 0.000387352203340500, 0.000533283635452978, 0.000668122820361005, 0.000790170354647780, + 0.000897951111087878, 0.000990231761272084, 0.001066031623671160, 0.001124632477690236, 0.001165581726339971, 0.001188693681255879, + 0.001194045545471417, 0.001181970380085016, 0.001153046414612510, 0.001108082857136429, 0.001048102631468540, 0.000974323218404695, + 0.000888133372727545, 0.000791070116123563, 0.000684792094554321, 0.000571053266050113, 0.000451674636902284, 0.000328516992293320, + 0.000203451977405086, 0.000078335772017527, -0.000045018545421742, -0.000164865572977560, -0.000279551948319666, -0.000387537349196916, +-0.000487415411907342, -0.000577929796159890, -0.000657989929078930, -0.000726681966249080, -0.000783278920419950, -0.000827245823239233, +-0.000858243707443438, -0.000876129480105976, -0.000880953408348413, -0.000872954045876351, -0.000852549805275905, -0.000820329138707098, +-0.000777037688231366, -0.000723563310391309, -0.000660920833710300, -0.000590233000458980, -0.000512712588279566, -0.000429641983841077, +-0.000342353358610352, -0.000252207643694647, -0.000160574821442717, -0.000068812726758276, 0.000021751281014785, 0.000109839511430035, + 0.000194240866662439, 0.000273827417009147, 0.000347567762721358, 0.000414540661406180, 0.000473944885437249, 0.000525108546609422, + 0.000567495007786956, 0.000600707953030737, 0.000624493258921266, 0.000638739227035361, 0.000643474954073381, 0.000638865887614407, + 0.000625208686170919, 0.000602922923466479, 0.000572542368876905, 0.000534704796699404, 0.000490138798319519, 0.000439652650855628, + 0.000384119062216776, 0.000324462014025233, 0.000261641460484809, 0.000196638895244753, 0.000130441926615597, 0.000064031032789795, +-0.000001635768489257, -0.000065635351677973, -0.000127092351239062, -0.000185190057199916, -0.000239181635064980, -0.000288398092983417, +-0.000332256748568874, -0.000370266824699425, -0.000402034633782136, -0.000427266209021117, -0.000445769357864568, -0.000457453739285960, +-0.000462329168633824, -0.000460503187462496, -0.000452176524646696, -0.000437638220339685, -0.000417258980581691, -0.000391482737253097, +-0.000360820345491574, -0.000325837733962965, -0.000287148930939394, -0.000245403805143397, -0.000201278972426392, -0.000155466780716653, +-0.000108665640335580, -0.000061568272389658, -0.000014853764966253, 0.000030823184154666, 0.000074840231468239, 0.000116616164620783, + 0.000155617045621414, 0.000191363947212061, 0.000223437061305126, 0.000251480887099541, 0.000275206841101241, 0.000294396418133417, + 0.000308901513454268, 0.000318644983901538, 0.000323619563118675, 0.000323886296653843, 0.000319571315012658, 0.000310862516511249, + 0.000298004342402298, 0.000281294681250729, 0.000261075639702519, 0.000237731645367386, 0.000211678221012634, 0.000183359023230085, + 0.000153236391205647, 0.000121785090745373, 0.000089484319755483, 0.000056812590122291, 0.000024238365952557, -0.000007783989333822, +-0.000038821745842297, -0.000068468267762019, -0.000096349500708075, -0.000122126241762541, -0.000145499890142001, -0.000166214040676592, +-0.000184058116066018, -0.000198867299743239, -0.000210525248496711, -0.000218962999255533, -0.000224159617516556, -0.000226139442542857, +-0.000224972129527046, -0.000220768612480065, -0.000213680605929445, -0.000203893426905360, -0.000191627620690743, -0.000177128235455148, +-0.000160667780360279, -0.000142535319778232, -0.000123035628711338, -0.000102483313141189, -0.000081198420104433, -0.000059500304426378, +-0.000037706322052971, -0.000016124117496096, 0.000004949997449017, 0.000025236122848728, 0.000044472363393569, 0.000062420736245776, + 0.000078866698566106, 0.000093623364714925, 0.000106531164007165, 0.000117461929793833, 0.000126316758019544, 0.000133028662869780, + 0.000137559905089999, 0.000139904660813329, 0.000140084636985629, 0.000138151310658245, 0.000134179871751258, 0.000128273257824200, + 0.000120552183081292, 0.000111162183255553, 0.000100260387268166, 0.000088022237529899, 0.000074633464306207, 0.000060287289500742, + 0.000045184046634717, 0.000029526982476978, 0.000013517370747292, -0.000002643867645482, -0.000018761566915687, -0.000034647988869857, +-0.000050126352194410, -0.000065029325935631, -0.000079204203578124, -0.000092512000120117, -0.000104831203935920, -0.000116055019098236, +-0.000126095843217766, -0.000134882362974756, -0.000142363405313636, -0.000148502970885125, -0.000153284964905293, -0.000156707360410909, +-0.000158788109935490, -0.000159555772217867, -0.000159058476336748, -0.000157349878306252, -0.000154502739904809, -0.000150594676074443, +-0.000145712082126712, -0.000139953974896786, -0.000133416362597982, -0.000126204064875885, -0.000118425032569519, -0.000110185878000537, +-0.000101592108032084, -0.000092749188319604, -0.000083759727095511, -0.000074720205221454, -0.000065722611018575, -0.000056852971113359, +-0.000048192148911461, -0.000039811520245419, -0.000031775674847193, -0.000024139403703601, -0.000016952037416704, -0.000010251946885753, +-0.000004071844887927, 0.000001567980147682, 0.000006650682808194, 0.000011172338433941, 0.000015128617900530, 0.000018531461504295, + 0.000021388236737303, 0.000023718858672597, 0.000025555768460688, 0.000026904364434149, 0.000027819244560919, 0.000028322403611347, + 0.000028448168208646, 0.000028233620872516, 0.000027724129005342, 0.000026954037148028, 0.000025960975613092, 0.000024779370194270, + 0.000023449026651569, 0.000022003882695500, 0.000020477970947099, 0.000018898513368975, 0.000017293987798534, 0.000015688094611533, + 0.000014105577722777, 0.000012565356814181, 0.000011085681450866, 0.000009678674512241, 0.000008356176996335, 0.000007125011282928, + 0.000005992480684896, 0.000004961356794279, 0.000004034862817142, 0.000003211705784635, 0.000005956787452187} ; + +// Sample 48000 Hz, pass 1350, stop 1650, ripple 0.2 dB, atten 80 dB. Stop 0.034375. +double quiskDgtFilt48Coefs[520] = { + 0.000061210247545999, 0.000043746409775174, 0.000058212225303250, 0.000074753467634881, 0.000093266105141811, 0.000113585644687065, + 0.000135423663054955, 0.000158328870775841, 0.000181891356788712, 0.000205599601545496, 0.000228645026826478, 0.000250549753299610, + 0.000270439656117594, 0.000287634273171333, 0.000301382803316157, 0.000310952112247671, 0.000315671378571221, 0.000314971116764541, + 0.000308384566541330, 0.000295546908455563, 0.000276344800843569, 0.000250714121608637, 0.000218894247891487, 0.000181247345217867, + 0.000138379308460797, 0.000091069145057693, 0.000040318367692028, -0.000012743757610817, -0.000066815793862360, -0.000120497624901022, +-0.000172340560901390, -0.000220842779844105, -0.000264545759556513, -0.000302058112897356, -0.000332100314633963, -0.000353574884310516, +-0.000365597749511169, -0.000367549444935919, -0.000359095079853050, -0.000340248103712097, -0.000311318683345800, -0.000273001982059955, +-0.000226290390127120, -0.000172520816677827, -0.000113294404001585, -0.000050482110010595, 0.000013879538580977, 0.000077596226431135, + 0.000138433205925659, 0.000194162617679696, 0.000242652729081166, 0.000281945254408166, 0.000310336367628720, 0.000326432713773731, + 0.000329234359797551, 0.000318166980387218, 0.000293134593766962, 0.000254529215078380, 0.000203257753881511, 0.000140696086207818, + 0.000068704477919520, -0.000010469460386413, -0.000094210675757463, -0.000179655354658535, -0.000263748377143012, -0.000343385270986054, +-0.000415481708883424, -0.000477127323461832, -0.000525666549407324, -0.000558827052389178, -0.000574804339799704, -0.000572357024711863, +-0.000550859745957779, -0.000510367338359310, -0.000451622691749228, -0.000376069966292593, -0.000285818679043561, -0.000183604586405879, +-0.000072694519055085, 0.000043186973280323, 0.000160008355381864, 0.000273537318290895, 0.000379516624120779, 0.000473794395644265, + 0.000552507414263523, 0.000612205254874264, 0.000650022772194762, 0.000663781194005598, 0.000652114106223388, 0.000614534587819213, + 0.000551495995704878, 0.000464399444796304, 0.000355588960580530, 0.000228287725906860, 0.000086520645722337, -0.000065011393951899, +-0.000221075064189407, -0.000376087180263476, -0.000524295599695647, -0.000660002965562596, -0.000777761692100941, -0.000872600928845689, +-0.000940212655599070, -0.000977149734388767, -0.000980967014779459, -0.000950366012125329, -0.000885265628490655, -0.000786862352853491, +-0.000657621051291283, -0.000501234399294955, -0.000322523208728928, -0.000127303145013843, 0.000077802378562603, 0.000285575230029221, + 0.000488450821113931, 0.000678786843882911, 0.000849143685814374, 0.000992564685207953, 0.001102855954848265, 0.001174835509697094, + 0.001204566114728847, 0.001189538527501501, 0.001128817899509618, 0.001023125083534072, 0.000874874171711282, 0.000688130789013763, + 0.000468524483432417, 0.000223087853969189, -0.000039955342366039, -0.000311455733838924, -0.000581646129972180, -0.000840483383595002, +-0.001078012372713286, -0.001284734478075700, -0.001451980683838214, -0.001572253684600511, -0.001639550394473688, -0.001649630237634784, +-0.001600232998004774, -0.001491225820499409, -0.001324681151893289, -0.001104869603259574, -0.000838175895475649, -0.000532931937919764, +-0.000199170729113807, 0.000151688054986733, 0.000507214917161548, 0.000854400443433744, 0.001180113894521864, 0.001471590125046728, + 0.001716909717985989, 0.001905473631337625, 0.002028431534072068, 0.002079076641927828, 0.002053153126803861, 0.001949105340232015, + 0.001768214443346741, 0.001514653053460190, 0.001195418338212109, 0.000820174992671957, 0.000400977596085337, -0.000048087891083484, +-0.000511363582024283, -0.000972141266167522, -0.001413248730385190, -0.001817663457069480, -0.002169152608635768, -0.002452890782327930, +-0.002656062485219405, -0.002768394343164597, -0.002782628014346268, -0.002694879964376376, -0.002504910400199462, -0.002216242707999800, +-0.001836175428756279, -0.001375629705238660, -0.000848883420232441, -0.000273150191053543, 0.000331940653919483, 0.000944993743897393, + 0.001543560510622334, 0.002104937222707049, 0.002606980311279288, 0.003028946231109706, 0.003352293573213900, 0.003561449536441622, + 0.003644476822562617, 0.003593651107671871, 0.003405886703965055, 0.003083027382420723, 0.002631950003670605, 0.002064510134633659, + 0.001397281869751727, 0.000651143584554952, -0.000149333524704116, -0.000976626519557435, -0.001801160279185895, -0.002592289416964058, +-0.003319380795725522, -0.003952910020607082, -0.004465585199017764, -0.004833410512420773, -0.005036693218115415, -0.005060918229289793, +-0.004897490298049392, -0.004544277445509641, -0.004005965505833022, -0.003294168725949424, -0.002427317265456230, -0.001430285373802798, +-0.000333795576405943, 0.000826424465452056, 0.002010672384642099, 0.003176505000933111, 0.004280095526479507, 0.005277718036143528, + 0.006127269407596632, 0.006789824315509249, 0.007231125920759553, 0.007423007691652700, 0.007344651831210159, 0.006983677069065356, + 0.006336979883000709, 0.005411322883488758, 0.004223614512558328, 0.002800887653706634, 0.001179941307064418, -0.000593330853561824, +-0.002464935213428108, -0.004374055521365783, -0.006254603585906442, -0.008036977828199395, -0.009650011275619909, -0.011023022782754674, +-0.012087943155882672, -0.012781422483705883, -0.013046885087532611, -0.012836436176880656, -0.012112592214956674, -0.010849746583647199, +-0.009035348406797636, -0.006670730134745213, -0.003771569342382457, -0.000367947895237729, 0.003495987348277634, 0.007762773423868341, + 0.012362764963184806, 0.017215633993121138, 0.022232193813321942, 0.027316504310074905, 0.032368195227461656, 0.037284954065171724, + 0.041965097700029354, 0.046310167209203890, 0.050227461840328891, 0.053632450203056022, 0.056450977859688306, 0.058621221189980630, + 0.060095317119274780, 0.060840633157909045, 0.060840633157909045, 0.060095317119274780, 0.058621221189980630, 0.056450977859688306, + 0.053632450203056022, 0.050227461840328891, 0.046310167209203890, 0.041965097700029354, 0.037284954065171724, 0.032368195227461656, + 0.027316504310074905, 0.022232193813321942, 0.017215633993121138, 0.012362764963184806, 0.007762773423868341, 0.003495987348277634, +-0.000367947895237729, -0.003771569342382457, -0.006670730134745213, -0.009035348406797636, -0.010849746583647199, -0.012112592214956674, +-0.012836436176880656, -0.013046885087532611, -0.012781422483705883, -0.012087943155882672, -0.011023022782754674, -0.009650011275619909, +-0.008036977828199395, -0.006254603585906442, -0.004374055521365783, -0.002464935213428108, -0.000593330853561824, 0.001179941307064418, + 0.002800887653706634, 0.004223614512558328, 0.005411322883488758, 0.006336979883000709, 0.006983677069065356, 0.007344651831210159, + 0.007423007691652700, 0.007231125920759553, 0.006789824315509249, 0.006127269407596632, 0.005277718036143528, 0.004280095526479507, + 0.003176505000933111, 0.002010672384642099, 0.000826424465452056, -0.000333795576405943, -0.001430285373802798, -0.002427317265456230, +-0.003294168725949424, -0.004005965505833022, -0.004544277445509641, -0.004897490298049392, -0.005060918229289793, -0.005036693218115415, +-0.004833410512420773, -0.004465585199017764, -0.003952910020607082, -0.003319380795725522, -0.002592289416964058, -0.001801160279185895, +-0.000976626519557435, -0.000149333524704116, 0.000651143584554952, 0.001397281869751727, 0.002064510134633659, 0.002631950003670605, + 0.003083027382420723, 0.003405886703965055, 0.003593651107671871, 0.003644476822562617, 0.003561449536441622, 0.003352293573213900, + 0.003028946231109706, 0.002606980311279288, 0.002104937222707049, 0.001543560510622334, 0.000944993743897393, 0.000331940653919483, +-0.000273150191053543, -0.000848883420232441, -0.001375629705238660, -0.001836175428756279, -0.002216242707999800, -0.002504910400199462, +-0.002694879964376376, -0.002782628014346268, -0.002768394343164597, -0.002656062485219405, -0.002452890782327930, -0.002169152608635768, +-0.001817663457069480, -0.001413248730385190, -0.000972141266167522, -0.000511363582024283, -0.000048087891083484, 0.000400977596085337, + 0.000820174992671957, 0.001195418338212109, 0.001514653053460190, 0.001768214443346741, 0.001949105340232015, 0.002053153126803861, + 0.002079076641927828, 0.002028431534072068, 0.001905473631337625, 0.001716909717985989, 0.001471590125046728, 0.001180113894521864, + 0.000854400443433744, 0.000507214917161548, 0.000151688054986733, -0.000199170729113807, -0.000532931937919764, -0.000838175895475649, +-0.001104869603259574, -0.001324681151893289, -0.001491225820499409, -0.001600232998004774, -0.001649630237634784, -0.001639550394473688, +-0.001572253684600511, -0.001451980683838214, -0.001284734478075700, -0.001078012372713286, -0.000840483383595002, -0.000581646129972180, +-0.000311455733838924, -0.000039955342366039, 0.000223087853969189, 0.000468524483432417, 0.000688130789013763, 0.000874874171711282, + 0.001023125083534072, 0.001128817899509618, 0.001189538527501501, 0.001204566114728847, 0.001174835509697094, 0.001102855954848265, + 0.000992564685207953, 0.000849143685814374, 0.000678786843882911, 0.000488450821113931, 0.000285575230029221, 0.000077802378562603, +-0.000127303145013843, -0.000322523208728928, -0.000501234399294955, -0.000657621051291283, -0.000786862352853491, -0.000885265628490655, +-0.000950366012125329, -0.000980967014779459, -0.000977149734388767, -0.000940212655599070, -0.000872600928845689, -0.000777761692100941, +-0.000660002965562596, -0.000524295599695647, -0.000376087180263476, -0.000221075064189407, -0.000065011393951899, 0.000086520645722337, + 0.000228287725906860, 0.000355588960580530, 0.000464399444796304, 0.000551495995704878, 0.000614534587819213, 0.000652114106223388, + 0.000663781194005598, 0.000650022772194762, 0.000612205254874264, 0.000552507414263523, 0.000473794395644265, 0.000379516624120779, + 0.000273537318290895, 0.000160008355381864, 0.000043186973280323, -0.000072694519055085, -0.000183604586405879, -0.000285818679043561, +-0.000376069966292593, -0.000451622691749228, -0.000510367338359310, -0.000550859745957779, -0.000572357024711863, -0.000574804339799704, +-0.000558827052389178, -0.000525666549407324, -0.000477127323461832, -0.000415481708883424, -0.000343385270986054, -0.000263748377143012, +-0.000179655354658535, -0.000094210675757463, -0.000010469460386413, 0.000068704477919520, 0.000140696086207818, 0.000203257753881511, + 0.000254529215078380, 0.000293134593766962, 0.000318166980387218, 0.000329234359797551, 0.000326432713773731, 0.000310336367628720, + 0.000281945254408166, 0.000242652729081166, 0.000194162617679696, 0.000138433205925659, 0.000077596226431135, 0.000013879538580977, +-0.000050482110010595, -0.000113294404001585, -0.000172520816677827, -0.000226290390127120, -0.000273001982059955, -0.000311318683345800, +-0.000340248103712097, -0.000359095079853050, -0.000367549444935919, -0.000365597749511169, -0.000353574884310516, -0.000332100314633963, +-0.000302058112897356, -0.000264545759556513, -0.000220842779844105, -0.000172340560901390, -0.000120497624901022, -0.000066815793862360, +-0.000012743757610817, 0.000040318367692028, 0.000091069145057693, 0.000138379308460797, 0.000181247345217867, 0.000218894247891487, + 0.000250714121608637, 0.000276344800843569, 0.000295546908455563, 0.000308384566541330, 0.000314971116764541, 0.000315671378571221, + 0.000310952112247671, 0.000301382803316157, 0.000287634273171333, 0.000270439656117594, 0.000250549753299610, 0.000228645026826478, + 0.000205599601545496, 0.000181891356788712, 0.000158328870775841, 0.000135423663054955, 0.000113585644687065, 0.000093266105141811, + 0.000074753467634881, 0.000058212225303250, 0.000043746409775174, 0.000061210247545999 +} ; + +// Sample 300 kHz, pass 20, stop 30, ripple 0. 1dB, atten 100 dB. For 300 to 60 decimation by 5. Pass 0.0667. Stop 0.1000. +double quiskFilt300D5Coefs[125] = { 0.000018530898985935, 0.000048715296542540, 0.000101487713663108, 0.000176187904864114, + 0.000265595698505904, 0.000353014002024914, 0.000412706642676480, 0.000413703988255270, 0.000327138262335125, 0.000135887617633139, +-0.000155548315125899, -0.000514148133587695, -0.000877608176001011, -0.001162065155160332, -0.001278607497803621, -0.001156314863202252, +-0.000766878845626526, -0.000144004619502309, 0.000609514309871448, 0.001332304107659656, 0.001833903451046474, 0.001939453692881414, + 0.001540470537753584, 0.000638747246876731, -0.000630485750733283, -0.002007091925274773, -0.003150529629631723, -0.003715315152624679, +-0.003442798119033104, -0.002247149655474171, -0.000270888264728748, 0.002110922295378516, 0.004348312328871565, 0.005831372793382018, + 0.006041592691834349, 0.004705198784696960, 0.001907765739476663, -0.001866929002333326, -0.005796648417844732, -0.008879322633277557, +-0.010164870856853636, -0.009011364543686401, -0.005302992316722601, 0.000432213665085220, 0.007050324283081295, 0.012980232879613959, + 0.016550056909769441, 0.016390975010318803, 0.011831378367146397, 0.003185612579985462, -0.008144878931421762, -0.019805426351182119, +-0.028854993458539563, -0.032310238090111396, -0.027765474190972032, -0.013961139511732867, 0.008825649334690966, 0.038665457076335182, + 0.072179830176428442, 0.105042015460381060, 0.132689511350380370, 0.151114898773723890, 0.157580579198349920, 0.151114898773723890, + 0.132689511350380370, 0.105042015460381060, 0.072179830176428442, 0.038665457076335182, 0.008825649334690966, -0.013961139511732867, +-0.027765474190972032, -0.032310238090111396, -0.028854993458539563, -0.019805426351182119, -0.008144878931421762, 0.003185612579985462, + 0.011831378367146397, 0.016390975010318803, 0.016550056909769441, 0.012980232879613959, 0.007050324283081295, 0.000432213665085220, +-0.005302992316722601, -0.009011364543686401, -0.010164870856853636, -0.008879322633277557, -0.005796648417844732, -0.001866929002333326, + 0.001907765739476663, 0.004705198784696960, 0.006041592691834349, 0.005831372793382018, 0.004348312328871565, 0.002110922295378516, +-0.000270888264728748, -0.002247149655474171, -0.003442798119033104, -0.003715315152624679, -0.003150529629631723, -0.002007091925274773, +-0.000630485750733283, 0.000638747246876731, 0.001540470537753584, 0.001939453692881414, 0.001833903451046474, 0.001332304107659656, + 0.000609514309871448, -0.000144004619502309, -0.000766878845626526, -0.001156314863202252, -0.001278607497803621, -0.001162065155160332, +-0.000877608176001011, -0.000514148133587695, -0.000155548315125899, 0.000135887617633139, 0.000327138262335125, 0.000413703988255270, + 0.000412706642676480, 0.000353014002024914, 0.000265595698505904, 0.000176187904864114, 0.000101487713663108, 0.000048715296542540, + 0.000018530898985935 +} ; + +// Sample 240 kHz, pass 20, stop 30, ripple 0. 1dB, atten 100 dB. For 240 to 60 decimation by 4. Pass 0.0833. Stop 0.125. +double quiskFilt240D4Coefs[100] = { 0.000026427748038218, 0.000083044892122331, 0.000183911274066307, 0.000320349110829452, + 0.000457388985778469, 0.000532620344169036, 0.000470872819082771, 0.000214313769983376, -0.000240081570728570, -0.000811566364292325, +-0.001333777016038777, -0.001592688419586267, -0.001402284132170216, -0.000696434758718897, 0.000400157800248161, 0.001562276075007405, + 0.002340457068600146, 0.002316140009670930, 0.001289626552449154, -0.000573307985418704, -0.002713127282322421, -0.004311143720675439, +-0.004570909733165466, -0.003061204879955289, 0.000013040647807287, 0.003746101513420969, 0.006749294361298744, 0.007623049432174923, + 0.005539556386039066, 0.000714048117939475, -0.005457094353399323, -0.010719487844933161, -0.012706806783966153, -0.009888987455409530, +-0.002375961043231839, 0.007777042412925690, 0.016982700467123999, 0.021228739037755177, 0.017579411755468045, 0.005569962317355060, +-0.012047196260398848, -0.029586753168515320, -0.039804931920814045, -0.036039635084008419, -0.014481769698543458, 0.024172310628567603, + 0.074299385648554650, 0.126409178605880910, 0.169462317675936840, 0.193798358783223160, 0.193798358783223160, 0.169462317675936840, + 0.126409178605880910, 0.074299385648554650, 0.024172310628567603, -0.014481769698543458, -0.036039635084008419, -0.039804931920814045, +-0.029586753168515320, -0.012047196260398848, 0.005569962317355060, 0.017579411755468045, 0.021228739037755177, 0.016982700467123999, + 0.007777042412925690, -0.002375961043231839, -0.009888987455409530, -0.012706806783966153, -0.010719487844933161, -0.005457094353399323, + 0.000714048117939475, 0.005539556386039066, 0.007623049432174923, 0.006749294361298744, 0.003746101513420969, 0.000013040647807287, +-0.003061204879955289, -0.004570909733165466, -0.004311143720675439, -0.002713127282322421, -0.000573307985418704, 0.001289626552449154, + 0.002316140009670930, 0.002340457068600146, 0.001562276075007405, 0.000400157800248161, -0.000696434758718897, -0.001402284132170216, +-0.001592688419586267, -0.001333777016038777, -0.000811566364292325, -0.000240081570728570, 0.000214313769983376, 0.000470872819082771, + 0.000532620344169036, 0.000457388985778469, 0.000320349110829452, 0.000183911274066307, 0.000083044892122331, 0.000026427748038218 +} ; + +// Sample 300 kHz, pass 20, stop 25, ripple 0. 1dB, atten 100 dB. For 300 to 50 decimation by 6. Pass 0.0667. Stop 0.08333. +double quiskFilt300D6Coefs[248] = { + 0.000011910887251416, 0.000022105099505161, 0.000038528164880090, 0.000058057974756687, 0.000077529348136962, 0.000092122417522252, + 0.000095773826807872, 0.000082150606579247, 0.000045941802546827, -0.000015655964463738, -0.000101508560175109, -0.000205623901768475, +-0.000317020911117816, -0.000420598298959303, -0.000499105467381654, -0.000536009730130428, -0.000518834322912359, -0.000442349591905865, +-0.000310909623154438, -0.000139289407787980, 0.000048450839869391, 0.000222182374045520, 0.000350656872582151, 0.000407443247027791, + 0.000376767722059760, 0.000258013352391244, 0.000067733888072331, -0.000161597279767678, -0.000386276495667759, -0.000558969004494543, +-0.000638008043834271, -0.000596639687427168, -0.000430244947175777, -0.000159760966769872, 0.000169879976202206, 0.000496544732562163, + 0.000752090455258835, 0.000876070391837635, 0.000829307042381563, 0.000604420257362057, 0.000230722859227965, -0.000228175424415781, +-0.000684317047397475, -0.001041562295798296, -0.001215154241382078, -0.001150839763823582, -0.000839401588636812, -0.000323015778972820, + 0.000308742853689313, 0.000933405036813186, 0.001418532658612045, 0.001648958563352753, 0.001552907570724076, 0.001121286921437812, + 0.000415365221846381, -0.000439881924253269, -0.001276648649153385, -0.001916215180556943, -0.002206052212475925, -0.002054620852694224, +-0.001456203929560064, -0.000499569542945491, 0.000642800486076826, 0.001744295867778604, 0.002567858832132124, 0.002915611567235574, + 0.002674313538272378, 0.001846569533541176, 0.000559918306471771, -0.000949983449361535, -0.002380635105445952, -0.003421623936658742, +-0.003820082151371851, -0.003439232264380022, -0.002296966067370011, -0.000574611485798243, 0.001408391062695743, 0.003251259943133494, + 0.004550539113003901, 0.004985688082005479, 0.004393958051961603, 0.002817791913037669, 0.000512488271545885, -0.002090680856085413, +-0.004461658971355160, -0.006076001180326797, -0.006526596735626234, -0.005619716454301121, -0.003433859382364719, -0.000325983426037548, + 0.003120481512082456, 0.006199690635806164, 0.008221988623961972, 0.008660926799975453, 0.007278698204181967, 0.004202043225935329, +-0.000071349813179933, -0.004743225902744831, -0.008854023235662239, -0.011468359077217744, -0.011872788367371184, -0.009746561766167391, +-0.005268271950313840, 0.000868984112618413, 0.007545384655588967, 0.013397186502100830, 0.017067367815119631, 0.017481531706887495, + 0.014098209764418178, 0.007083578133321482, -0.002628980063776467, -0.013415472624288912, -0.023171404153239539, -0.029628134880484221, +-0.030727216816581020, -0.024991056056960267, -0.011825325274110144, 0.008302942014898000, 0.033848791339949939, 0.062345366630597851, + 0.090714512362454230, 0.115687137518083420, 0.134268728793426880, 0.144176283697368230, 0.144176283697368230, 0.134268728793426880, + 0.115687137518083420, 0.090714512362454230, 0.062345366630597851, 0.033848791339949939, 0.008302942014898000, -0.011825325274110144, +-0.024991056056960267, -0.030727216816581020, -0.029628134880484221, -0.023171404153239539, -0.013415472624288912, -0.002628980063776467, + 0.007083578133321482, 0.014098209764418178, 0.017481531706887495, 0.017067367815119631, 0.013397186502100830, 0.007545384655588967, + 0.000868984112618413, -0.005268271950313840, -0.009746561766167391, -0.011872788367371184, -0.011468359077217744, -0.008854023235662239, +-0.004743225902744831, -0.000071349813179933, 0.004202043225935329, 0.007278698204181967, 0.008660926799975453, 0.008221988623961972, + 0.006199690635806164, 0.003120481512082456, -0.000325983426037548, -0.003433859382364719, -0.005619716454301121, -0.006526596735626234, +-0.006076001180326797, -0.004461658971355160, -0.002090680856085413, 0.000512488271545885, 0.002817791913037669, 0.004393958051961603, + 0.004985688082005479, 0.004550539113003901, 0.003251259943133494, 0.001408391062695743, -0.000574611485798243, -0.002296966067370011, +-0.003439232264380022, -0.003820082151371851, -0.003421623936658742, -0.002380635105445952, -0.000949983449361535, 0.000559918306471771, + 0.001846569533541176, 0.002674313538272378, 0.002915611567235574, 0.002567858832132124, 0.001744295867778604, 0.000642800486076826, +-0.000499569542945491, -0.001456203929560064, -0.002054620852694224, -0.002206052212475925, -0.001916215180556943, -0.001276648649153385, +-0.000439881924253269, 0.000415365221846381, 0.001121286921437812, 0.001552907570724076, 0.001648958563352753, 0.001418532658612045, + 0.000933405036813186, 0.000308742853689313, -0.000323015778972820, -0.000839401588636812, -0.001150839763823582, -0.001215154241382078, +-0.001041562295798296, -0.000684317047397475, -0.000228175424415781, 0.000230722859227965, 0.000604420257362057, 0.000829307042381563, + 0.000876070391837635, 0.000752090455258835, 0.000496544732562163, 0.000169879976202206, -0.000159760966769872, -0.000430244947175777, +-0.000596639687427168, -0.000638008043834271, -0.000558969004494543, -0.000386276495667759, -0.000161597279767678, 0.000067733888072331, + 0.000258013352391244, 0.000376767722059760, 0.000407443247027791, 0.000350656872582151, 0.000222182374045520, 0.000048450839869391, +-0.000139289407787980, -0.000310909623154438, -0.000442349591905865, -0.000518834322912359, -0.000536009730130428, -0.000499105467381654, +-0.000420598298959303, -0.000317020911117816, -0.000205623901768475, -0.000101508560175109, -0.000015655964463738, 0.000045941802546827, + 0.000082150606579247, 0.000095773826807872, 0.000092122417522252, 0.000077529348136962, 0.000058057974756687, 0.000038528164880090, + 0.000022105099505161, 0.000011910887251416 +} ; + +// Sample 48 kHz, differentiator, pass 21000 ripple 5db, stop 23999 to 24000 atten 20 dB +double quiskDiff48Coefs[38] = { -0.000346604486329388, 0.000872060526788775, -0.001377888428055412, 0.002143851727666205, +-0.003140241321402629, 0.004374640214997344, -0.005845903317976450, 0.007539093249300473, -0.009422827615507629, 0.011447847797822517, +-0.013545904360670370, 0.015627972401100334, -0.017577926784897915, 0.019235081888911390, -0.020342464109632617, + 0.020366421977821021, -0.017707206191343410, 0.004204607038114273, 0.175457827152511820, -0.175457827152511820, +-0.004204607038114273, 0.017707206191343410, -0.020366421977821021, 0.020342464109632617, -0.019235081888911390, + 0.017577926784897915, -0.015627972401100334, 0.013545904360670370, -0.011447847797822517, 0.009422827615507629, +-0.007539093249300473, 0.005845903317976450, -0.004374640214997344, 0.003140241321402629, -0.002143851727666205, + 0.001377888428055412, -0.000872060526788775, 0.000346604486329388 +} ; diff --git a/filters.py b/filters.py new file mode 100644 index 0000000..bcf066c --- /dev/null +++ b/filters.py @@ -0,0 +1,770 @@ +# Rate 24000 sps, ripple 0.2 dB, atten 100 dB, shape 1.2 +# Filters key is the bandwidth at 24000 sps. +# key = bw * 24000 / rate / 2 +# bw = key * 2 * rate / 24000 +Filters = { + +8500: # 55 taps +[ -0.001578241201420500, -0.002488309592652162, 0.001185909979889287, 0.000286041612735020, -0.001914552184024928, 0.002760744242385052, +-0.001796589368523962, -0.001056335106849282, 0.004390770806455585, -0.005824325923037834, 0.003465568616258560, 0.002462568821267293, +-0.008988281812658983, 0.011629434198087422, -0.007027876977623796, -0.004272818661253936, 0.016890540812408691, -0.022619963224181760, + 0.014949848668575091, 0.006115840936585570, -0.031813780331403710, 0.046915611796058194, -0.036226209362519565, -0.007500120805011563, + 0.078216492123247466, -0.156996838729026640, 0.218668091485032380, 0.758014834761081340, 0.218668091485032380, -0.156996838729026640, + 0.078216492123247466, -0.007500120805011563, -0.036226209362519565, 0.046915611796058194, -0.031813780331403710, 0.006115840936585570, + 0.014949848668575091, -0.022619963224181760, 0.016890540812408691, -0.004272818661253936, -0.007027876977623796, 0.011629434198087422, +-0.008988281812658983, 0.002462568821267293, 0.003465568616258560, -0.005824325923037834, 0.004390770806455585, -0.001056335106849282, +-0.001796589368523962, 0.002760744242385052, -0.001914552184024928, 0.000286041612735020, 0.001185909979889287, -0.002488309592652162, +-0.001578241201420500], + +7500: # 62 taps +[ 0.000094561136776712, 0.001408783281963872, 0.003252521294198745, 0.001088470223697677, -0.002238006126669353, 0.000797427337925380, + 0.001818354390057951, -0.002971083246008131, 0.000737271087741229, 0.003304967641132078, -0.004678221609224611, 0.000618143813431047, + 0.005732278632558854, -0.007264716193352521, 0.000382151456262394, 0.009375552017313197, -0.011107789354244654, 0.000048810931888440, + 0.014797494013431127, -0.016988015917755381, -0.000322746345617388, 0.023289857616213484, -0.026716501910571362, -0.000681860486120778, + 0.038508535988246995, -0.045999278172093885, -0.000961050429347091, 0.076884527279961243, -0.108006033576806130, -0.001113929105132611, + 0.551548401015432340, 0.551548401015432340, -0.001113929105132611, -0.108006033576806130, 0.076884527279961243, -0.000961050429347091, +-0.045999278172093885, 0.038508535988246995, -0.000681860486120778, -0.026716501910571362, 0.023289857616213484, -0.000322746345617388, +-0.016988015917755381, 0.014797494013431127, 0.000048810931888440, -0.011107789354244654, 0.009375552017313197, 0.000382151456262394, +-0.007264716193352521, 0.005732278632558854, 0.000618143813431047, -0.004678221609224611, 0.003304967641132078, 0.000737271087741229, +-0.002971083246008131, 0.001818354390057951, 0.000797427337925380, -0.002238006126669353, 0.001088470223697677, 0.003252521294198745, + 0.001408783281963872, 0.000094561136776712], + +6000: # 77 taps + [0.000208183216794497, 0.001160220144497454, 0.002498877019501132, 0.002217684703504632, -0.000364985361618348, -0.001823661861823501, + 0.000356579314286080, 0.002005946277037424, -0.000685757071824683, -0.002428321330453817, 0.001256816376812629, 0.002943833745660115, +-0.002130949481744204, -0.003446793644558851, 0.003380457505759830, 0.003807055791440830, -0.005074958590792311, -0.003869772827804518, + 0.007242784565398008, 0.003418656761533474, -0.009894698971107840, -0.002211451952907241, 0.012977467451293289, -0.000058006701182926, +-0.016390443243429843, 0.003773319671211690, 0.019989716564592076, -0.009449626980637388, -0.023581188491464997, 0.017927337095200193, + 0.026945533578336647, -0.030887338584165511, -0.029856365801280219, 0.052657593695444822, 0.032108551641510746, -0.099253908185103573, +-0.033533139787951992, 0.315983886115891840, 0.534019780032233380, 0.315983886115891840, -0.033533139787951992, -0.099253908185103573, + 0.032108551641510746, 0.052657593695444822, -0.029856365801280219, -0.030887338584165511, 0.026945533578336647, 0.017927337095200193, +-0.023581188491464997, -0.009449626980637388, 0.019989716564592076, 0.003773319671211690, -0.016390443243429843, -0.000058006701182926, + 0.012977467451293289, -0.002211451952907241, -0.009894698971107840, 0.003418656761533474, 0.007242784565398008, -0.003869772827804518, +-0.005074958590792311, 0.003807055791440830, 0.003380457505759830, -0.003446793644558851, -0.002130949481744204, 0.002943833745660115, + 0.001256816376812629, -0.002428321330453817, -0.000685757071824683, 0.002005946277037424, 0.000356579314286080, -0.001823661861823501, +-0.000364985361618348, 0.002217684703504632, 0.002498877019501132, 0.001160220144497454, 0.000208183216794497], + +5000: # 92 taps +[ 0.000172282027074915, 0.000805722137099302, 0.001789488027434626, 0.002208898476835740, 0.001133555920432830, -0.000798012231727563, +-0.001468876483570138, 0.000053213574826391, 0.001640044205108085, 0.000693959429133929, -0.001663633271521857, -0.001609719846438075, + 0.001334915862232435, 0.002601113060240987, -0.000511327252542249, -0.003459777069001452, -0.000863807590293483, 0.003915575549171324, + 0.002725579328876624, -0.003678783180717698, -0.004875619345633069, 0.002500119680365451, 0.006978228022424931, -0.000223624762857588, +-0.008586543420189311, -0.003156720729060025, 0.009180028261871051, 0.007460886285077675, -0.008222908809263846, -0.012298402032805727, + 0.005219135899689294, 0.017069401572755501, 0.000240426913514277, -0.020971624610692834, -0.008474182212803629, 0.022996901553415793, + 0.019787130769856574, -0.021836278313112276, -0.034759648853466663, 0.015468734639474426, 0.055194479055695952, 0.000590985958335975, +-0.088238504731419823, -0.043831925654670714, 0.182996493758220890, 0.409587845444253250, 0.409587845444253250, 0.182996493758220890, +-0.043831925654670714, -0.088238504731419823, 0.000590985958335975, 0.055194479055695952, 0.015468734639474426, -0.034759648853466663, +-0.021836278313112276, 0.019787130769856574, 0.022996901553415793, -0.008474182212803629, -0.020971624610692834, 0.000240426913514277, + 0.017069401572755501, 0.005219135899689294, -0.012298402032805727, -0.008222908809263846, 0.007460886285077675, 0.009180028261871051, +-0.003156720729060025, -0.008586543420189311, -0.000223624762857588, 0.006978228022424931, 0.002500119680365451, -0.004875619345633069, +-0.003678783180717698, 0.002725579328876624, 0.003915575549171324, -0.000863807590293483, -0.003459777069001452, -0.000511327252542249, + 0.002601113060240987, 0.001334915862232435, -0.001609719846438075, -0.001663633271521857, 0.000693959429133929, 0.001640044205108085, + 0.000053213574826391, -0.001468876483570138, -0.000798012231727563, 0.001133555920432830, 0.002208898476835740, 0.001789488027434626, + 0.000805722137099302, 0.000172282027074915], + +4500: # 102 taps +[ 0.000109626786353929, 0.000462685347095325, 0.000976091511950423, 0.001146819725082014, 0.000393473830731237, -0.001141511548465342, +-0.002282834370334400, -0.001829207175666342, -0.000101528442900756, 0.000987382913028173, 0.000039751448950354, -0.001822373509053446, +-0.001985247021210946, 0.000232895236822806, 0.002153431919758378, 0.000930723811183338, -0.002248491432658168, -0.003017055190706798, + 0.000342078561845510, 0.003721285661215643, 0.002085819103613060, -0.003140571194897081, -0.004835117960215140, 0.000288263563014682, + 0.005924235485555640, 0.003715592386573005, -0.004558690834977428, -0.007633320493226938, 0.000139066056400393, 0.009165154333957292, + 0.006093793617689697, -0.006780247949818048, -0.011926916343123978, -0.000057228900700871, 0.014232549280021243, 0.009771685994328400, +-0.010499186884061604, -0.019033587881754202, -0.000249830231388923, 0.023228171670193941, 0.016360728644062135, -0.017855007053639658, +-0.033458833271228797, -0.000405885453398987, 0.044679917304132288, 0.033455780397647107, -0.040439521683945157, -0.085684078775375178, +-0.000493182012170114, 0.201251835014007290, 0.374516101600616320, 0.374516101600616320, 0.201251835014007290, -0.000493182012170114, +-0.085684078775375178, -0.040439521683945157, 0.033455780397647107, 0.044679917304132288, -0.000405885453398987, -0.033458833271228797, +-0.017855007053639658, 0.016360728644062135, 0.023228171670193941, -0.000249830231388923, -0.019033587881754202, -0.010499186884061604, + 0.009771685994328400, 0.014232549280021243, -0.000057228900700871, -0.011926916343123978, -0.006780247949818048, 0.006093793617689697, + 0.009165154333957292, 0.000139066056400393, -0.007633320493226938, -0.004558690834977428, 0.003715592386573005, 0.005924235485555640, + 0.000288263563014682, -0.004835117960215140, -0.003140571194897081, 0.002085819103613060, 0.003721285661215643, 0.000342078561845510, +-0.003017055190706798, -0.002248491432658168, 0.000930723811183338, 0.002153431919758378, 0.000232895236822806, -0.001985247021210946, +-0.001822373509053446, 0.000039751448950354, 0.000987382913028173, -0.000101528442900756, -0.001829207175666342, -0.002282834370334400, +-0.001141511548465342, 0.000393473830731237, 0.001146819725082014, 0.000976091511950423, 0.000462685347095325, 0.000109626786353929], + +4000: # 115 taps +[ 0.000078524780320715, 0.000329316620713670, 0.000756688762644958, 0.001124260123708105, 0.001012255791778806, 0.000167097630541898, +-0.001099970608704988, -0.001940975178324101, -0.001625045960154994, -0.000333634994172134, 0.000774600995458535, 0.000561151578352762, +-0.000854751315214004, -0.001984988321940357, -0.001407739886101419, 0.000563473972774177, 0.001900424989770745, 0.000925528523083090, +-0.001609328373038515, -0.002935359144493341, -0.001199176692593675, 0.002118199425756780, 0.003340065554812114, 0.000621160360920014, +-0.003467567745617676, -0.004224605385298791, -0.000122849763708023, 0.004757557369118008, 0.004664298923525774, -0.001148493552322016, +-0.006629807337962404, -0.005080846185003255, 0.002831233307698604, 0.008609924128977818, 0.004891354675929344, -0.005425659232916345, +-0.010937452229276256, -0.004116901228197529, 0.008877312794209294, 0.013300805492116527, 0.002232134062032589, -0.013614806007146969, +-0.015720726837983701, 0.001140172406302503, 0.019985979932262889, 0.017946437617680795, -0.007018595519087580, -0.029113241143045584, +-0.019901010720465118, 0.017502959326722210, 0.043879416112787749, 0.021395356319346116, -0.039872421015430898, -0.076438935865810770, +-0.022353637195009195, 0.124776719393937450, 0.286085089477746360, 0.356006518780215160, 0.286085089477746360, 0.124776719393937450, +-0.022353637195009195, -0.076438935865810770, -0.039872421015430898, 0.021395356319346116, 0.043879416112787749, 0.017502959326722210, +-0.019901010720465118, -0.029113241143045584, -0.007018595519087580, 0.017946437617680795, 0.019985979932262889, 0.001140172406302503, +-0.015720726837983701, -0.013614806007146969, 0.002232134062032589, 0.013300805492116527, 0.008877312794209294, -0.004116901228197529, +-0.010937452229276256, -0.005425659232916345, 0.004891354675929344, 0.008609924128977818, 0.002831233307698604, -0.005080846185003255, +-0.006629807337962404, -0.001148493552322016, 0.004664298923525774, 0.004757557369118008, -0.000122849763708023, -0.004224605385298791, +-0.003467567745617676, 0.000621160360920014, 0.003340065554812114, 0.002118199425756780, -0.001199176692593675, -0.002935359144493341, +-0.001609328373038515, 0.000925528523083090, 0.001900424989770745, 0.000563473972774177, -0.001407739886101419, -0.001984988321940357, +-0.000854751315214004, 0.000561151578352762, 0.000774600995458535, -0.000333634994172134, -0.001625045960154994, -0.001940975178324101, +-0.001099970608704988, 0.000167097630541898, 0.001012255791778806, 0.001124260123708105, 0.000756688762644958, 0.000329316620713670, + 0.000078524780320715], + +3000: # 153 taps +[ 0.000035516842253053, 0.000120241356692793, 0.000262655558849812, 0.000421822903927101, 0.000504577556110132, 0.000393748344899348, + 0.000013887896365045, -0.000595749610102644, -0.001250516069080062, -0.001672797229233531, -0.001624666112532619, -0.001059802835950466, +-0.000199611602796516, 0.000539126532493226, 0.000756552265297489, 0.000316087930054194, -0.000530090257617794, -0.001244496438584232, +-0.001312107606392913, -0.000583465448463906, 0.000572285419075017, 0.001440987738923421, 0.001399231913182619, 0.000355359654986622, +-0.001108071194955440, -0.002036249585608732, -0.001707924634389252, -0.000169594756520662, 0.001685676765584473, 0.002618141236597755, + 0.001855681542593165, -0.000301881949096732, -0.002542398236828585, -0.003317624887398237, -0.001892391320644168, 0.001031582912020721, + 0.003613502326574886, 0.004009329165673526, 0.001661239094822322, -0.002143832449766766, -0.004935146076383144, -0.004634572876828250, +-0.001061541395886805, 0.003698344992855291, 0.006465358367957256, 0.005053382115343493, -0.000073525601278193, -0.005791007488487868, +-0.008166064459464053, -0.005109407447857288, 0.001926336767092539, 0.008511258651897639, 0.009958456933858797, 0.004574761558815073, +-0.004759500034603159, -0.012000283350062089, -0.011748002733961208, -0.003120526928071337, 0.008993494047071084, 0.016529385910476514, + 0.013426861274101313, 0.000181178754148492, -0.015466003330041472, -0.022748666494653846, -0.014877802849879123, 0.005502737892245722, + 0.026345716718994539, 0.032621748472455206, 0.016002483863926359, -0.017887767208878101, -0.049636964594866217, -0.054713319818683277, +-0.016713474537575129, 0.061906959876918494, 0.157995584096340960, 0.236648015289099320, 0.266956938993864910, 0.236648015289099320, + 0.157995584096340960, 0.061906959876918494, -0.016713474537575129, -0.054713319818683277, -0.049636964594866217, -0.017887767208878101, + 0.016002483863926359, 0.032621748472455206, 0.026345716718994539, 0.005502737892245722, -0.014877802849879123, -0.022748666494653846, +-0.015466003330041472, 0.000181178754148492, 0.013426861274101313, 0.016529385910476514, 0.008993494047071084, -0.003120526928071337, +-0.011748002733961208, -0.012000283350062089, -0.004759500034603159, 0.004574761558815073, 0.009958456933858797, 0.008511258651897639, + 0.001926336767092539, -0.005109407447857288, -0.008166064459464053, -0.005791007488487868, -0.000073525601278193, 0.005053382115343493, + 0.006465358367957256, 0.003698344992855291, -0.001061541395886805, -0.004634572876828250, -0.004935146076383144, -0.002143832449766766, + 0.001661239094822322, 0.004009329165673526, 0.003613502326574886, 0.001031582912020721, -0.001892391320644168, -0.003317624887398237, +-0.002542398236828585, -0.000301881949096732, 0.001855681542593165, 0.002618141236597755, 0.001685676765584473, -0.000169594756520662, +-0.001707924634389252, -0.002036249585608732, -0.001108071194955440, 0.000355359654986622, 0.001399231913182619, 0.001440987738923421, + 0.000572285419075017, -0.000583465448463906, -0.001312107606392913, -0.001244496438584232, -0.000530090257617794, 0.000316087930054194, + 0.000756552265297489, 0.000539126532493226, -0.000199611602796516, -0.001059802835950466, -0.001624666112532619, -0.001672797229233531, +-0.001250516069080062, -0.000595749610102644, 0.000013887896365045, 0.000393748344899348, 0.000504577556110132, 0.000421822903927101, + 0.000262655558849812, 0.000120241356692793, 0.000035516842253053], + +2800: # 164 taps +[ 0.000031131729843669, 0.000101363194753254, 0.000221445426297415, 0.000366876574918002, 0.000474767078312489, 0.000455842035531617, + 0.000232260566090893, -0.000211274148558287, -0.000789599231892209, -0.001321558408924163, -0.001590694231360794, -0.001445876584766002, +-0.000895790759735873, -0.000140741015448872, 0.000492974178868593, 0.000701994832615354, 0.000371048524479058, -0.000337331657814867, +-0.001034893398244673, -0.001296923457098872, -0.000901505495786621, 0.000013737161230886, 0.000983722390445291, 0.001446949255083495, + 0.001067648443145340, -0.000031354774249279, -0.001279007610852184, -0.001938862367504847, -0.001531888866790246, -0.000161657161535566, + 0.001470243306104189, 0.002410991575715065, 0.002003292184824320, 0.000324608046693798, -0.001764616004515830, -0.003045092340783782, +-0.002637146414877572, -0.000580772176139985, 0.002073712223135724, 0.003782183003317882, 0.003389875946583850, 0.000885046737439423, +-0.002455184150014141, -0.004690784048082864, -0.004326357662532086, -0.001279167957787954, 0.002902671922490944, 0.005795008254325585, + 0.005478283962467929, 0.001772012477148658, -0.003450331904449436, -0.007166940301855534, -0.006922766471023288, -0.002399780547747377, + 0.004134972375816635, 0.008909643621809859, 0.008776960071400969, 0.003217389082858829, -0.005021094247026112, -0.011201040385085154, +-0.011246577131734643, -0.004323921802028225, 0.006225285499714047, 0.014372818914766290, 0.014725605621083649, 0.005913959753070873, +-0.007987043798634774, -0.019127577707865114, -0.020080950001885520, -0.008435384367710340, 0.010879472951485192, 0.027244661531034900, + 0.029649597250285761, 0.013188790214990415, -0.016693663421477496, -0.044885495280403948, -0.052572421004629472, -0.026098663081897257, + 0.035426680640922303, 0.117691081437968410, 0.195575709828410660, 0.242828423889182740, 0.242828423889182740, 0.195575709828410660, + 0.117691081437968410, 0.035426680640922303, -0.026098663081897257, -0.052572421004629472, -0.044885495280403948, -0.016693663421477496, + 0.013188790214990415, 0.029649597250285761, 0.027244661531034900, 0.010879472951485192, -0.008435384367710340, -0.020080950001885520, +-0.019127577707865114, -0.007987043798634774, 0.005913959753070873, 0.014725605621083649, 0.014372818914766290, 0.006225285499714047, +-0.004323921802028225, -0.011246577131734643, -0.011201040385085154, -0.005021094247026112, 0.003217389082858829, 0.008776960071400969, + 0.008909643621809859, 0.004134972375816635, -0.002399780547747377, -0.006922766471023288, -0.007166940301855534, -0.003450331904449436, + 0.001772012477148658, 0.005478283962467929, 0.005795008254325585, 0.002902671922490944, -0.001279167957787954, -0.004326357662532086, +-0.004690784048082864, -0.002455184150014141, 0.000885046737439423, 0.003389875946583850, 0.003782183003317882, 0.002073712223135724, +-0.000580772176139985, -0.002637146414877572, -0.003045092340783782, -0.001764616004515830, 0.000324608046693798, 0.002003292184824320, + 0.002410991575715065, 0.001470243306104189, -0.000161657161535566, -0.001531888866790246, -0.001938862367504847, -0.001279007610852184, +-0.000031354774249279, 0.001067648443145340, 0.001446949255083495, 0.000983722390445291, 0.000013737161230886, -0.000901505495786621, +-0.001296923457098872, -0.001034893398244673, -0.000337331657814867, 0.000371048524479058, 0.000701994832615354, 0.000492974178868593, +-0.000140741015448872, -0.000895790759735873, -0.001445876584766002, -0.001590694231360794, -0.001321558408924163, -0.000789599231892209, +-0.000211274148558287, 0.000232260566090893, 0.000455842035531617, 0.000474767078312489, 0.000366876574918002, 0.000221445426297415, + 0.000101363194753254, 0.000031131729843669], + +2500: # 184 taps +[ 0.000025917992771962, 0.000080053245121681, 0.000175970492512707, 0.000306338738553062, 0.000441565642149420, 0.000529323030925432, + 0.000506924615738917, 0.000324946604356638, -0.000025129491557388, -0.000491406291157732, -0.000962621820235116, -0.001296070805120129, +-0.001367486032675225, -0.001125819560345427, -0.000629044038382859, -0.000039974702137716, 0.000424508165741649, 0.000577252836600570, + 0.000345217289298406, -0.000184075454087644, -0.000781983763019378, -0.001165504636264451, -0.001120965614204760, -0.000611972955861357, + 0.000181852749867249, 0.000923465749187075, 0.001259585458205595, 0.000987840637523650, 0.000172679353454093, -0.000852839379782549, +-0.001614970097646428, -0.001711408440970664, -0.001014931729801465, 0.000231871248756494, 0.001490613857222986, 0.002154309898448981, + 0.001835132214640792, 0.000579040055238457, -0.001106680482118841, -0.002448432281636389, -0.002754083239929045, -0.001760402090267228, + 0.000190461179667856, 0.002251447413131553, 0.003429705849477585, 0.003056701038439272, 0.001150595364468159, -0.001518817798865299, +-0.003726827835919960, -0.004345414044232734, -0.002906143603126905, 0.000100825920300210, 0.003366376342127399, 0.005318377282596896, + 0.004864845763651887, 0.001979300066273931, -0.002175246374000726, -0.005692516278767321, -0.006780045002879058, -0.004649909783330805, +-0.000010396809644141, 0.005123662380431661, 0.008280363892442090, 0.007693898397635463, 0.003246147289573304, -0.003296547115364492, +-0.008942285826077026, -0.010803588267982528, -0.007523983582706444, -0.000118131836810526, 0.008247900700950585, 0.013549640285344118, + 0.012768247956349992, 0.005492469173933735, -0.005544176969275239, -0.015378525898323159, -0.018928522076093565, -0.013447400254072768, +-0.000204681888673014, 0.015499492179231059, 0.026221846254622386, 0.025562428680330915, 0.011389219733612054, -0.012327402868838598, +-0.036056520568517325, -0.047698955925824099, -0.037076675237560336, -0.000252599132841553, 0.058062325226935912, 0.125125149137395140, + 0.183789926776792010, 0.217965757483672360, 0.217965757483672360, 0.183789926776792010, 0.125125149137395140, 0.058062325226935912, +-0.000252599132841553, -0.037076675237560336, -0.047698955925824099, -0.036056520568517325, -0.012327402868838598, 0.011389219733612054, + 0.025562428680330915, 0.026221846254622386, 0.015499492179231059, -0.000204681888673014, -0.013447400254072768, -0.018928522076093565, +-0.015378525898323159, -0.005544176969275239, 0.005492469173933735, 0.012768247956349992, 0.013549640285344118, 0.008247900700950585, +-0.000118131836810526, -0.007523983582706444, -0.010803588267982528, -0.008942285826077026, -0.003296547115364492, 0.003246147289573304, + 0.007693898397635463, 0.008280363892442090, 0.005123662380431661, -0.000010396809644141, -0.004649909783330805, -0.006780045002879058, +-0.005692516278767321, -0.002175246374000726, 0.001979300066273931, 0.004864845763651887, 0.005318377282596896, 0.003366376342127399, + 0.000100825920300210, -0.002906143603126905, -0.004345414044232734, -0.003726827835919960, -0.001518817798865299, 0.001150595364468159, + 0.003056701038439272, 0.003429705849477585, 0.002251447413131553, 0.000190461179667856, -0.001760402090267228, -0.002754083239929045, +-0.002448432281636389, -0.001106680482118841, 0.000579040055238457, 0.001835132214640792, 0.002154309898448981, 0.001490613857222986, + 0.000231871248756494, -0.001014931729801465, -0.001711408440970664, -0.001614970097646428, -0.000852839379782549, 0.000172679353454093, + 0.000987840637523650, 0.001259585458205595, 0.000923465749187075, 0.000181852749867249, -0.000611972955861357, -0.001120965614204760, +-0.001165504636264451, -0.000781983763019378, -0.000184075454087644, 0.000345217289298406, 0.000577252836600570, 0.000424508165741649, +-0.000039974702137716, -0.000629044038382859, -0.001125819560345427, -0.001367486032675225, -0.001296070805120129, -0.000962621820235116, +-0.000491406291157732, -0.000025129491557388, 0.000324946604356638, 0.000506924615738917, 0.000529323030925432, 0.000441565642149420, + 0.000306338738553062, 0.000175970492512707, 0.000080053245121681, 0.000025917992771962], + +2250: # 204 taps +[ 0.000021235929725912, 0.000058222413723091, 0.000120890002831670, 0.000203732920433964, 0.000289842861102114, 0.000349648687783014, + 0.000345987806871638, 0.000244741113281256, 0.000028683359304148, -0.000289767552139220, -0.000662562723513035, -0.001011139906426526, +-0.001244168321321761, -0.001284537444429324, -0.001097853884402359, -0.000712241533727792, -0.000220016931252946, 0.000242881192519691, + 0.000536533139642942, 0.000564406769439812, 0.000312158569581760, -0.000137882856713157, -0.000625674547956830, -0.000963565458358823, +-0.001002878434444682, -0.000693839038801721, -0.000115120712337792, 0.000542271936437241, 0.001036221937634474, 0.001161519435408656, + 0.000833656052982527, 0.000135090190320360, -0.000698564960603252, -0.001354001229403848, -0.001554644716587147, -0.001173297796072647, +-0.000298257519547670, 0.000778476775888895, 0.001654851153636767, 0.001966028879000262, 0.001532563461927731, 0.000450688659966846, +-0.000917352451094544, -0.002059631204322221, -0.002502298531213019, -0.002001396043186186, -0.000662678470838726, 0.001065622033389926, + 0.002537010973466811, 0.003144134768937308, 0.002563969753555681, 0.000913777569993959, -0.001253108006318142, -0.003125545369287028, +-0.003932636886255475, -0.003254230145379617, -0.001221262259301159, 0.001484407116176422, 0.003849603664148359, 0.004902048797404353, + 0.004101215157545026, 0.001593866974392046, -0.001780147556263722, -0.004757480195317273, -0.006114639178429671, -0.005158848135225734, +-0.002053747031888304, 0.002164989106816366, 0.005919348898632932, 0.007665501534134797, 0.006511589945553144, 0.002635680468215082, +-0.002680807848145089, -0.007454309167215744, -0.009718217353005822, -0.008306681703332177, -0.003400058938312392, 0.003406734158392871, + 0.009587959427131761, 0.012589212182082522, 0.010837597719690596, 0.004476080310267183, -0.004494123792760167, -0.012783165957938696, +-0.016952789434463451, -0.014752125292065949, -0.006159094027769298, 0.006304669250227038, 0.018192572127496920, 0.024568955795679859, + 0.021832946865353555, 0.009314262398551900, -0.009958208720588032, -0.029683337395096002, -0.041898689176258344, -0.039357322455930641, +-0.017957925042069021, 0.021558306071190737, 0.073246545073647112, 0.127141924217462950, 0.171715605172332860, 0.196918159377366730, + 0.196918159377366730, 0.171715605172332860, 0.127141924217462950, 0.073246545073647112, 0.021558306071190737, -0.017957925042069021, +-0.039357322455930641, -0.041898689176258344, -0.029683337395096002, -0.009958208720588032, 0.009314262398551900, 0.021832946865353555, + 0.024568955795679859, 0.018192572127496920, 0.006304669250227038, -0.006159094027769298, -0.014752125292065949, -0.016952789434463451, +-0.012783165957938696, -0.004494123792760167, 0.004476080310267183, 0.010837597719690596, 0.012589212182082522, 0.009587959427131761, + 0.003406734158392871, -0.003400058938312392, -0.008306681703332177, -0.009718217353005822, -0.007454309167215744, -0.002680807848145089, + 0.002635680468215082, 0.006511589945553144, 0.007665501534134797, 0.005919348898632932, 0.002164989106816366, -0.002053747031888304, +-0.005158848135225734, -0.006114639178429671, -0.004757480195317273, -0.001780147556263722, 0.001593866974392046, 0.004101215157545026, + 0.004902048797404353, 0.003849603664148359, 0.001484407116176422, -0.001221262259301159, -0.003254230145379617, -0.003932636886255475, +-0.003125545369287028, -0.001253108006318142, 0.000913777569993959, 0.002563969753555681, 0.003144134768937308, 0.002537010973466811, + 0.001065622033389926, -0.000662678470838726, -0.002001396043186186, -0.002502298531213019, -0.002059631204322221, -0.000917352451094544, + 0.000450688659966846, 0.001532563461927731, 0.001966028879000262, 0.001654851153636767, 0.000778476775888895, -0.000298257519547670, +-0.001173297796072647, -0.001554644716587147, -0.001354001229403848, -0.000698564960603252, 0.000135090190320360, 0.000833656052982527, + 0.001161519435408656, 0.001036221937634474, 0.000542271936437241, -0.000115120712337792, -0.000693839038801721, -0.001002878434444682, +-0.000963565458358823, -0.000625674547956830, -0.000137882856713157, 0.000312158569581760, 0.000564406769439812, 0.000536533139642942, + 0.000242881192519691, -0.000220016931252946, -0.000712241533727792, -0.001097853884402359, -0.001284537444429324, -0.001244168321321761, +-0.001011139906426526, -0.000662562723513035, -0.000289767552139220, 0.000028683359304148, 0.000244741113281256, 0.000345987806871638, + 0.000349648687783014, 0.000289842861102114, 0.000203732920433964, 0.000120890002831670, 0.000058222413723091, 0.000021235929725912], + +2200: #208 taps +[ 0.000019773973500698, 0.000050300702719923, 0.000099108356371034, 0.000158194404233062, 0.000210525538441445, 0.000229815943545636, + 0.000185844232898202, 0.000053925802103604, -0.000173777821813056, -0.000478713240844808, -0.000812707500835325, -0.001104087723445511, +-0.001273942998532759, -0.001259278130931017, -0.001036517683714317, -0.000637135855849719, -0.000147905388336950, 0.000307371053002897, + 0.000601888175399474, 0.000646481239850203, 0.000423598448328852, 0.000001477795930511, -0.000479511456398659, -0.000847542574860787, +-0.000956181777710176, -0.000738934578391376, -0.000241404176917473, 0.000383518196967566, 0.000920251953818672, 0.001163208934048226, + 0.000993921481591226, 0.000434388361798019, -0.000346030182304409, -0.001078079759008518, -0.001482742622315739, -0.001374843351537245, +-0.000742498373847161, 0.000230275873300524, 0.001212510588437732, 0.001835971920260912, 0.001830429895718975, 0.001135824361158899, +-0.000053708874146025, -0.001337763115758486, -0.002240751224958943, -0.002384735614959788, -0.001642386517013215, -0.000209813690831605, + 0.001436458922346993, 0.002693390382420342, 0.003048363283850710, 0.002282591868736866, 0.000582131982803615, -0.001494683726987109, +-0.003194641328318874, -0.003837926603637492, -0.003083544121084786, -0.001090420131933939, 0.001496937564140111, 0.003749027796032780, + 0.004778960997800515, 0.004083937494744029, 0.001771796479680179, -0.001424175363221383, -0.004367185919670888, -0.005912778052177615, +-0.005343837748934002, -0.002682898015250491, 0.001248211259214371, 0.005067191417250770, 0.007305293729039890, 0.006958279310681562, + 0.003912446091368335, -0.000927394507475861, -0.005884363717650123, -0.009072440354829070, -0.009093145110623519, -0.005615743824657722, + 0.000389138616762466, 0.006884468473346532, 0.011428522924352827, 0.012058034647345471, 0.008084684337356606, 0.000498409327693868, +-0.008214286092348189, -0.014832606629260737, -0.016526271123526743, -0.011969567139734955, -0.002035744936384088, 0.010229678038446030, + 0.020433030409654745, 0.024256700710114647, 0.019055511377788287, 0.005093243716641166, -0.014044229049919777, -0.032091634104820632, +-0.041707387370849711, -0.036619723239870061, -0.013689386302707808, 0.025758635108220899, 0.075797616878918297, 0.127078744612693170, + 0.169056056140688250, 0.192658606871644660, 0.192658606871644660, 0.169056056140688250, 0.127078744612693170, 0.075797616878918297, + 0.025758635108220899, -0.013689386302707808, -0.036619723239870061, -0.041707387370849711, -0.032091634104820632, -0.014044229049919777, + 0.005093243716641166, 0.019055511377788287, 0.024256700710114647, 0.020433030409654745, 0.010229678038446030, -0.002035744936384088, +-0.011969567139734955, -0.016526271123526743, -0.014832606629260737, -0.008214286092348189, 0.000498409327693868, 0.008084684337356606, + 0.012058034647345471, 0.011428522924352827, 0.006884468473346532, 0.000389138616762466, -0.005615743824657722, -0.009093145110623519, +-0.009072440354829070, -0.005884363717650123, -0.000927394507475861, 0.003912446091368335, 0.006958279310681562, 0.007305293729039890, + 0.005067191417250770, 0.001248211259214371, -0.002682898015250491, -0.005343837748934002, -0.005912778052177615, -0.004367185919670888, +-0.001424175363221383, 0.001771796479680179, 0.004083937494744029, 0.004778960997800515, 0.003749027796032780, 0.001496937564140111, +-0.001090420131933939, -0.003083544121084786, -0.003837926603637492, -0.003194641328318874, -0.001494683726987109, 0.000582131982803615, + 0.002282591868736866, 0.003048363283850710, 0.002693390382420342, 0.001436458922346993, -0.000209813690831605, -0.001642386517013215, +-0.002384735614959788, -0.002240751224958943, -0.001337763115758486, -0.000053708874146025, 0.001135824361158899, 0.001830429895718975, + 0.001835971920260912, 0.001212510588437732, 0.000230275873300524, -0.000742498373847161, -0.001374843351537245, -0.001482742622315739, +-0.001078079759008518, -0.000346030182304409, 0.000434388361798019, 0.000993921481591226, 0.001163208934048226, 0.000920251953818672, + 0.000383518196967566, -0.000241404176917473, -0.000738934578391376, -0.000956181777710176, -0.000847542574860787, -0.000479511456398659, + 0.000001477795930511, 0.000423598448328852, 0.000646481239850203, 0.000601888175399474, 0.000307371053002897, -0.000147905388336950, +-0.000637135855849719, -0.001036517683714317, -0.001259278130931017, -0.001273942998532759, -0.001104087723445511, -0.000812707500835325, +-0.000478713240844808, -0.000173777821813056, 0.000053925802103604, 0.000185844232898202, 0.000229815943545636, 0.000210525538441445, + 0.000158194404233062, 0.000099108356371034, 0.000050300702719923, 0.000019773973500698], + +2000: # 230 taps +[ 0.000018263332334824, 0.000047149978647285, 0.000097406241243338, 0.000168379817473922, 0.000253967146823514, 0.000340023431940833, + 0.000404952972379638, 0.000422974737428721, 0.000369902505736477, 0.000230539284736893, 0.000005684130024729, -0.000283317092531425, +-0.000594762485225633, -0.000872546941267996, -0.001057521343918215, -0.001102228302856328, -0.000985222996371773, -0.000720881682050162, +-0.000360902436481765, 0.000014352678995945, 0.000313854754768513, 0.000459361314264358, 0.000408357201159471, 0.000169601665199639, +-0.000194309748856567, -0.000579769259034295, -0.000868450605965827, -0.000960850467769566, -0.000807759775150557, -0.000429923379846805, + 0.000081456338895501, 0.000585585033204507, 0.000930280363007829, 0.000997553630123726, 0.000743304377770887, 0.000218142932019430, +-0.000439021999187466, -0.001035464584506922, -0.001379274582853274, -0.001339118806894442, -0.000890898584125663, -0.000135426700809927, + 0.000721291534689607, 0.001422586528925900, 0.001736709146196323, 0.001531678957580429, 0.000825809348346554, -0.000204332584606896, +-0.001264953954035041, -0.002026681147110289, -0.002224767812024736, -0.001747989347578871, -0.000687126008900549, 0.000675855233958752, + 0.001939810141526326, 0.002700254906709327, 0.002676337670079708, 0.001809809227907344, 0.000302346385971505, -0.001425894059131038, +-0.002850111164443048, -0.003497344177823653, -0.003099785097153715, -0.001696282588251145, 0.000354381431382131, 0.002459975729847483, + 0.003962117890550910, 0.004338291351008917, 0.003377760857642002, 0.001273722107127891, -0.001404260811957126, -0.003858729944018619, +-0.005296868477641261, -0.005180794578143301, -0.003418238131210470, -0.000428690403667462, 0.002946095146143898, 0.005671970307130408, + 0.006832059383649537, 0.005925580242615656, 0.003061135525037048, -0.001025055462746871, -0.005146493168885704, -0.008000447007066858, +-0.008567643322000875, -0.006458387751298429, -0.002092389293377035, 0.003358433615988978, 0.008267542643372157, 0.011021009169962534, + 0.010525039504447272, 0.006600477461859877, 0.000131874225722409, -0.007098295196117369, -0.012870086951713777, -0.015169746940300757, +-0.012839241513492812, -0.006035689378457242, 0.003657448927275838, 0.013537787301098309, 0.020464471653522786, 0.021753420096582588, + 0.016052407125049746, 0.003944898543127921, -0.011915101650258511, -0.027222337492958711, -0.036934437155297005, -0.036483323022197825, +-0.022992339899140058, 0.003814121297142671, 0.041261656200314752, 0.084061156150449762, 0.125284573669860120, 0.157774677964436120, + 0.175667194419142110, 0.175667194419142110, 0.157774677964436120, 0.125284573669860120, 0.084061156150449762, 0.041261656200314752, + 0.003814121297142671, -0.022992339899140058, -0.036483323022197825, -0.036934437155297005, -0.027222337492958711, -0.011915101650258511, + 0.003944898543127921, 0.016052407125049746, 0.021753420096582588, 0.020464471653522786, 0.013537787301098309, 0.003657448927275838, +-0.006035689378457242, -0.012839241513492812, -0.015169746940300757, -0.012870086951713777, -0.007098295196117369, 0.000131874225722409, + 0.006600477461859877, 0.010525039504447272, 0.011021009169962534, 0.008267542643372157, 0.003358433615988978, -0.002092389293377035, +-0.006458387751298429, -0.008567643322000875, -0.008000447007066858, -0.005146493168885704, -0.001025055462746871, 0.003061135525037048, + 0.005925580242615656, 0.006832059383649537, 0.005671970307130408, 0.002946095146143898, -0.000428690403667462, -0.003418238131210470, +-0.005180794578143301, -0.005296868477641261, -0.003858729944018619, -0.001404260811957126, 0.001273722107127891, 0.003377760857642002, + 0.004338291351008917, 0.003962117890550910, 0.002459975729847483, 0.000354381431382131, -0.001696282588251145, -0.003099785097153715, +-0.003497344177823653, -0.002850111164443048, -0.001425894059131038, 0.000302346385971505, 0.001809809227907344, 0.002676337670079708, + 0.002700254906709327, 0.001939810141526326, 0.000675855233958752, -0.000687126008900549, -0.001747989347578871, -0.002224767812024736, +-0.002026681147110289, -0.001264953954035041, -0.000204332584606896, 0.000825809348346554, 0.001531678957580429, 0.001736709146196323, + 0.001422586528925900, 0.000721291534689607, -0.000135426700809927, -0.000890898584125663, -0.001339118806894442, -0.001379274582853274, +-0.001035464584506922, -0.000439021999187466, 0.000218142932019430, 0.000743304377770887, 0.000997553630123726, 0.000930280363007829, + 0.000585585033204507, 0.000081456338895501, -0.000429923379846805, -0.000807759775150557, -0.000960850467769566, -0.000868450605965827, +-0.000579769259034295, -0.000194309748856567, 0.000169601665199639, 0.000408357201159471, 0.000459361314264358, 0.000313854754768513, + 0.000014352678995945, -0.000360902436481765, -0.000720881682050162, -0.000985222996371773, -0.001102228302856328, -0.001057521343918215, +-0.000872546941267996, -0.000594762485225633, -0.000283317092531425, 0.000005684130024729, 0.000230539284736893, 0.000369902505736477, + 0.000422974737428721, 0.000404952972379638, 0.000340023431940833, 0.000253967146823514, 0.000168379817473922, 0.000097406241243338, + 0.000047149978647285, 0.000018263332334824], + +1200: # 390 taps +[ -0.000004331430434813, 0.000000895178754483, 0.000004819206934844, 0.000012387135981227, 0.000024702874344864, 0.000042904801446151, + 0.000067985064859609, 0.000100627030615644, 0.000141025873402660, 0.000188738905083740, 0.000242545741476270, 0.000300399529662751, + 0.000359433330154281, 0.000416062388387887, 0.000466131771574125, 0.000505276256945394, 0.000529201721306850, 0.000534142706869689, + 0.000517270879752382, 0.000477091889474874, 0.000413779420856055, 0.000329374266276144, 0.000227844055373822, 0.000114956874548630, +-0.000002037964985788, -0.000114915331771079, -0.000215124321927526, -0.000294538502564660, -0.000346261198238618, -0.000365357958551737, +-0.000349492174517184, -0.000299346594446952, -0.000218781210350266, -0.000114696259371792, 0.000003439467944363, 0.000124342943768293, + 0.000235923038322712, 0.000326439472488279, 0.000385716174095538, 0.000406270345323467, 0.000384244892423745, 0.000320026589486952, + 0.000218451713387220, 0.000088560889198400, -0.000057113100623293, -0.000203679685243764, -0.000335341513050654, -0.000437048860128537, +-0.000496163931220179, -0.000503976472887228, -0.000456879139659229, -0.000357064175200153, -0.000212619491029857, -0.000036988477009561, + 0.000152196470731637, 0.000334813551887072, 0.000490329017689163, 0.000600045160004744, 0.000649282295414473, 0.000629236561431697, + 0.000538304340843976, 0.000382670778203303, 0.000176071280391891, -0.000061311579103233, -0.000304731538051063, -0.000527388822625111, +-0.000703310824877695, -0.000810309677712974, -0.000832681816046785, -0.000763333939111765, -0.000605059771056946, -0.000370777976208385, +-0.000082637093413831, 0.000229962373724818, 0.000533282556646377, 0.000792807201162301, 0.000977090407652497, 0.001061465563886132, + 0.001031195280476629, 0.000883666665500652, 0.000629337435771274, 0.000291251466919408, -0.000096894708513463, -0.000493996123025552, +-0.000855794020410478, -0.001139717153414092, -0.001309782074955667, -0.001341010390430392, -0.001222824367769252, -0.000960990590585683, +-0.000577807284515617, -0.000110428182918688, 0.000392587860975165, 0.000876197024190483, 0.001284780403728217, 0.001568419571371085, + 0.001688844037687690, 0.001624356624161237, 0.001373126810656710, 0.000954389812320343, 0.000407316271842430, -0.000212443049446438, +-0.000838254589610985, -0.001399522686014101, -0.001829507546348522, -0.002073092512801241, -0.002093612886275790, -0.001877924713394541, +-0.001439048979367570, -0.000815971265919441, -0.000070480695977658, 0.000718736847294852, 0.001464215661002276, 0.002079218519234449, + 0.002487627013869813, 0.002633104169545422, 0.002486455189532861, 0.002050261072858978, 0.001360126142354939, 0.000482234298234895, +-0.000492687418387142, -0.001458485408297803, -0.002304891424791415, -0.002929849705482365, -0.003251510669015779, -0.003218513620108414, +-0.002817308401190046, -0.002075543578763564, -0.001060944493273319, 0.000124409621994965, 0.001353984612741832, 0.002489950284201132, + 0.003398266860636230, 0.003964087731928019, 0.004105749968228098, 0.003785703607610913, 0.003016991173328030, 0.001864327522378759, + 0.000439388406465501, -0.001109448711112102, -0.002612032761559656, -0.003894589690524808, -0.004799215003224437, -0.005202582868054837, +-0.005031731058566960, -0.004274996752467488, -0.002986628326221043, -0.001284225000364678, 0.000661081413588171, 0.002640990158453115, + 0.004431457793095885, 0.005817070614785085, 0.006615805917154524, 0.006701450960612391, 0.006021062146242219, 0.004605255832862724, + 0.002569791244804701, 0.000107781839820803, -0.002527142979701336, -0.005045394324250144, -0.007151459446961864, -0.008576455016121923, +-0.009109805876143171, -0.008626557809676316, -0.007107110348255605, -0.004646780506669336, -0.001453545516455117, 0.002166532839260334, + 0.005835432981396591, 0.009138830213137229, 0.011668164700843125, 0.013065169962452085, 0.013064440018490608, 0.011529579182106710, + 0.008478886219133458, 0.004097368728152922, -0.001266921998658053, -0.007122790428060381, -0.012871021717463436, -0.017852189458389456, +-0.021403262319433757, -0.022918110896739993, -0.021906440359153793, -0.018045605353792606, -0.011220225882222989, -0.001545481108326266, + 0.010628660576177286, 0.024733344647961189, 0.040016116357312823, 0.055593053292742431, 0.070513617769391357, 0.083832869405100041, + 0.094684919293335654, 0.102351302009811070, 0.106318317517167070, 0.106318317517167070, 0.102351302009811070, 0.094684919293335654, + 0.083832869405100041, 0.070513617769391357, 0.055593053292742431, 0.040016116357312823, 0.024733344647961189, 0.010628660576177286, +-0.001545481108326266, -0.011220225882222989, -0.018045605353792606, -0.021906440359153793, -0.022918110896739993, -0.021403262319433757, +-0.017852189458389456, -0.012871021717463436, -0.007122790428060381, -0.001266921998658053, 0.004097368728152922, 0.008478886219133458, + 0.011529579182106710, 0.013064440018490608, 0.013065169962452085, 0.011668164700843125, 0.009138830213137229, 0.005835432981396591, + 0.002166532839260334, -0.001453545516455117, -0.004646780506669336, -0.007107110348255605, -0.008626557809676316, -0.009109805876143171, +-0.008576455016121923, -0.007151459446961864, -0.005045394324250144, -0.002527142979701336, 0.000107781839820803, 0.002569791244804701, + 0.004605255832862724, 0.006021062146242219, 0.006701450960612391, 0.006615805917154524, 0.005817070614785085, 0.004431457793095885, + 0.002640990158453115, 0.000661081413588171, -0.001284225000364678, -0.002986628326221043, -0.004274996752467488, -0.005031731058566960, +-0.005202582868054837, -0.004799215003224437, -0.003894589690524808, -0.002612032761559656, -0.001109448711112102, 0.000439388406465501, + 0.001864327522378759, 0.003016991173328030, 0.003785703607610913, 0.004105749968228098, 0.003964087731928019, 0.003398266860636230, + 0.002489950284201132, 0.001353984612741832, 0.000124409621994965, -0.001060944493273319, -0.002075543578763564, -0.002817308401190046, +-0.003218513620108414, -0.003251510669015779, -0.002929849705482365, -0.002304891424791415, -0.001458485408297803, -0.000492687418387142, + 0.000482234298234895, 0.001360126142354939, 0.002050261072858978, 0.002486455189532861, 0.002633104169545422, 0.002487627013869813, + 0.002079218519234449, 0.001464215661002276, 0.000718736847294852, -0.000070480695977658, -0.000815971265919441, -0.001439048979367570, +-0.001877924713394541, -0.002093612886275790, -0.002073092512801241, -0.001829507546348522, -0.001399522686014101, -0.000838254589610985, +-0.000212443049446438, 0.000407316271842430, 0.000954389812320343, 0.001373126810656710, 0.001624356624161237, 0.001688844037687690, + 0.001568419571371085, 0.001284780403728217, 0.000876197024190483, 0.000392587860975165, -0.000110428182918688, -0.000577807284515617, +-0.000960990590585683, -0.001222824367769252, -0.001341010390430392, -0.001309782074955667, -0.001139717153414092, -0.000855794020410478, +-0.000493996123025552, -0.000096894708513463, 0.000291251466919408, 0.000629337435771274, 0.000883666665500652, 0.001031195280476629, + 0.001061465563886132, 0.000977090407652497, 0.000792807201162301, 0.000533282556646377, 0.000229962373724818, -0.000082637093413831, +-0.000370777976208385, -0.000605059771056946, -0.000763333939111765, -0.000832681816046785, -0.000810309677712974, -0.000703310824877695, +-0.000527388822625111, -0.000304731538051063, -0.000061311579103233, 0.000176071280391891, 0.000382670778203303, 0.000538304340843976, + 0.000629236561431697, 0.000649282295414473, 0.000600045160004744, 0.000490329017689163, 0.000334813551887072, 0.000152196470731637, +-0.000036988477009561, -0.000212619491029857, -0.000357064175200153, -0.000456879139659229, -0.000503976472887228, -0.000496163931220179, +-0.000437048860128537, -0.000335341513050654, -0.000203679685243764, -0.000057113100623293, 0.000088560889198400, 0.000218451713387220, + 0.000320026589486952, 0.000384244892423745, 0.000406270345323467, 0.000385716174095538, 0.000326439472488279, 0.000235923038322712, + 0.000124342943768293, 0.000003439467944363, -0.000114696259371792, -0.000218781210350266, -0.000299346594446952, -0.000349492174517184, +-0.000365357958551737, -0.000346261198238618, -0.000294538502564660, -0.000215124321927526, -0.000114915331771079, -0.000002037964985788, + 0.000114956874548630, 0.000227844055373822, 0.000329374266276144, 0.000413779420856055, 0.000477091889474874, 0.000517270879752382, + 0.000534142706869689, 0.000529201721306850, 0.000505276256945394, 0.000466131771574125, 0.000416062388387887, 0.000359433330154281, + 0.000300399529662751, 0.000242545741476270, 0.000188738905083740, 0.000141025873402660, 0.000100627030615644, 0.000067985064859609, + 0.000042904801446151, 0.000024702874344864, 0.000012387135981227, 0.000004819206934844, 0.000000895178754483, +-0.000004331430434813], + +1050: # 436 taps +[ 0.000009448479205375, 0.000012696465277652, 0.000020370172970180, 0.000030184991625035, 0.000042106693955817, 0.000055852072599971, + 0.000070887005271560, 0.000086420158465092, 0.000101355825900618, 0.000114363268643203, 0.000123887834821102, 0.000128274519298202, + 0.000125832586450277, 0.000114986590640600, 0.000094403728281619, 0.000063154754823051, 0.000020845988244699, -0.000032265535133044, +-0.000095180544123547, -0.000166116860144892, -0.000242513979646880, -0.000321114777409093, -0.000398098262223401, -0.000469277292459888, +-0.000530343005106164, -0.000577150816605647, -0.000606030552867816, -0.000614088176959215, -0.000599488135042163, -0.000561676165437471, +-0.000501537574188219, -0.000421453273437500, -0.000325256102322617, -0.000218069429252057, -0.000106038676866598, 0.000004038733321948, + 0.000105163201801783, 0.000190629395147399, 0.000254537701320533, 0.000292276183024851, 0.000300940504739341, 0.000279647367467038, + 0.000229712182001491, 0.000154664133559244, 0.000060087287885032, -0.000046708916188273, -0.000157178278901246, -0.000262150604318790, +-0.000352532643004546, -0.000420038843447295, -0.000457900791044766, -0.000461490220027360, -0.000428802562294159, -0.000360749682491985, +-0.000261227783830262, -0.000136942941254748, 0.000003006863095541, 0.000147769782466689, 0.000285560076794725, 0.000404597750694492, + 0.000494097819748234, 0.000545227040891837, 0.000551946387992179, 0.000511661803626807, 0.000425615907406962, 0.000298975923041507, + 0.000140591237099924, -0.000037572806623586, -0.000221297040657601, -0.000395204977572457, -0.000544014582709044, -0.000653844664412656, +-0.000713468032359235, -0.000715401015700932, -0.000656726220245329, -0.000539565358551840, -0.000371142263662183, -0.000163412087825843, + 0.000067734912716773, 0.000303646478822936, 0.000524376688359045, 0.000710337247559984, 0.000843997687273033, 0.000911491282666008, + 0.000903981565989860, 0.000818661581901103, 0.000659279571163340, 0.000436124965687766, 0.000165448090041410, -0.000131662114271814, +-0.000430871860061819, -0.000706551570190628, -0.000933927661174878, -0.001091260873927019, -0.001161862605544963, -0.001135768011733947, +-0.001010903443574116, -0.000793624567958766, -0.000498548786522930, -0.000147664383619282, 0.000231239373696082, 0.000606716500508849, + 0.000946221796915500, 0.001218879442233109, 0.001398230278516649, 0.001464719587961910, 0.001407696084540016, 0.001226727670795367, + 0.000932088534687613, 0.000544338945220889, 0.000092993997809168, -0.000385643796196990, -0.000851335887551482, -0.001263225987480693, +-0.001583356294077108, -0.001780085545791869, -0.001831106846925676, -0.001725785932705194, -0.001466586997062531, -0.001069422391909016, +-0.000562849829295198, 0.000013863768127086, 0.000613691289313864, 0.001185595579630164, 0.001678788016865224, 0.002047148219684201, + 0.002253427125685210, 0.002272859461156746, 0.002095846355480220, 0.001729435227991120, 0.001197417642361670, 0.000538977181181343, +-0.000194056307018250, -0.000941167802354134, -0.001638040755694804, -0.002221979108638823, -0.002637419881293469, -0.002841061961880444, +-0.002806150254550888, -0.002525507242008391, -0.002012996612957378, -0.001303224500472842, -0.000449429868884502, 0.000480331461610398, + 0.001408451066145862, 0.002254087235765462, 0.002940037601874124, 0.003399594478686270, 0.003582789834066899, 0.003461463426614760, + 0.003032665933951381, 0.002320030370388926, 0.001372905661370613, 0.000263228943665216, -0.000919692494408471, -0.002076132077760251, +-0.003104105713267222, -0.003908095577429846, -0.004407611415610481, -0.004544845892385366, -0.004290726778249251, -0.003648773776410054, +-0.002656330572825240, -0.001382946510100638, 0.000074084090594797, 0.001596775046947397, 0.003055612326596284, 0.004320145989574181, + 0.005270175821128589, 0.005806607129742208, 0.005861039774711523, 0.005403216045538973, 0.004445593651759434, 0.003044516829901583, + 0.001297719390662469, -0.000661812043684862, -0.002675281726881974, -0.004570210544203085, -0.006174367519919625, -0.007330437981090785, +-0.007910248018541601, -0.007827342889142773, -0.007046794045290511, -0.005591280073358511, -0.003542743024648185, -0.001039245490340182, + 0.001732978782646685, 0.004551902468552846, 0.007175761837551073, 0.009361715988035382, 0.010885774625277341, 0.011562497118639900, + 0.011262913292836945, 0.009929181902797219, 0.007584685668611571, 0.004338552786979482, 0.000383973096667143, -0.004009880384869925, +-0.008512042746918275, -0.012749310695913854, -0.016329981037081505, -0.018870242377519306, -0.020021437259417553, -0.019496284710171480, +-0.017092162548070899, -0.012709698776261012, -0.006365201717689276, 0.001804145292512615, 0.011542917413936767, 0.022487965990827675, + 0.034186326021837028, 0.046119492420907078, 0.057732581500000407, 0.068466529917194638, 0.077791244144850280, 0.085237520364008251, + 0.090425612489785534, 0.093088535620340640, 0.093088535620340640, 0.090425612489785534, 0.085237520364008251, 0.077791244144850280, + 0.068466529917194638, 0.057732581500000407, 0.046119492420907078, 0.034186326021837028, 0.022487965990827675, 0.011542917413936767, + 0.001804145292512615, -0.006365201717689276, -0.012709698776261012, -0.017092162548070899, -0.019496284710171480, -0.020021437259417553, +-0.018870242377519306, -0.016329981037081505, -0.012749310695913854, -0.008512042746918275, -0.004009880384869925, 0.000383973096667143, + 0.004338552786979482, 0.007584685668611571, 0.009929181902797219, 0.011262913292836945, 0.011562497118639900, 0.010885774625277341, + 0.009361715988035382, 0.007175761837551073, 0.004551902468552846, 0.001732978782646685, -0.001039245490340182, -0.003542743024648185, +-0.005591280073358511, -0.007046794045290511, -0.007827342889142773, -0.007910248018541601, -0.007330437981090785, -0.006174367519919625, +-0.004570210544203085, -0.002675281726881974, -0.000661812043684862, 0.001297719390662469, 0.003044516829901583, 0.004445593651759434, + 0.005403216045538973, 0.005861039774711523, 0.005806607129742208, 0.005270175821128589, 0.004320145989574181, 0.003055612326596284, + 0.001596775046947397, 0.000074084090594797, -0.001382946510100638, -0.002656330572825240, -0.003648773776410054, -0.004290726778249251, +-0.004544845892385366, -0.004407611415610481, -0.003908095577429846, -0.003104105713267222, -0.002076132077760251, -0.000919692494408471, + 0.000263228943665216, 0.001372905661370613, 0.002320030370388926, 0.003032665933951381, 0.003461463426614760, 0.003582789834066899, + 0.003399594478686270, 0.002940037601874124, 0.002254087235765462, 0.001408451066145862, 0.000480331461610398, -0.000449429868884502, +-0.001303224500472842, -0.002012996612957378, -0.002525507242008391, -0.002806150254550888, -0.002841061961880444, -0.002637419881293469, +-0.002221979108638823, -0.001638040755694804, -0.000941167802354134, -0.000194056307018250, 0.000538977181181343, 0.001197417642361670, + 0.001729435227991120, 0.002095846355480220, 0.002272859461156746, 0.002253427125685210, 0.002047148219684201, 0.001678788016865224, + 0.001185595579630164, 0.000613691289313864, 0.000013863768127086, -0.000562849829295198, -0.001069422391909016, -0.001466586997062531, +-0.001725785932705194, -0.001831106846925676, -0.001780085545791869, -0.001583356294077108, -0.001263225987480693, -0.000851335887551482, +-0.000385643796196990, 0.000092993997809168, 0.000544338945220889, 0.000932088534687613, 0.001226727670795367, 0.001407696084540016, + 0.001464719587961910, 0.001398230278516649, 0.001218879442233109, 0.000946221796915500, 0.000606716500508849, 0.000231239373696082, +-0.000147664383619282, -0.000498548786522930, -0.000793624567958766, -0.001010903443574116, -0.001135768011733947, -0.001161862605544963, +-0.001091260873927019, -0.000933927661174878, -0.000706551570190628, -0.000430871860061819, -0.000131662114271814, 0.000165448090041410, + 0.000436124965687766, 0.000659279571163340, 0.000818661581901103, 0.000903981565989860, 0.000911491282666008, 0.000843997687273033, + 0.000710337247559984, 0.000524376688359045, 0.000303646478822936, 0.000067734912716773, -0.000163412087825843, -0.000371142263662183, +-0.000539565358551840, -0.000656726220245329, -0.000715401015700932, -0.000713468032359235, -0.000653844664412656, -0.000544014582709044, +-0.000395204977572457, -0.000221297040657601, -0.000037572806623586, 0.000140591237099924, 0.000298975923041507, 0.000425615907406962, + 0.000511661803626807, 0.000551946387992179, 0.000545227040891837, 0.000494097819748234, 0.000404597750694492, 0.000285560076794725, + 0.000147769782466689, 0.000003006863095541, -0.000136942941254748, -0.000261227783830262, -0.000360749682491985, -0.000428802562294159, +-0.000461490220027360, -0.000457900791044766, -0.000420038843447295, -0.000352532643004546, -0.000262150604318790, -0.000157178278901246, +-0.000046708916188273, 0.000060087287885032, 0.000154664133559244, 0.000229712182001491, 0.000279647367467038, 0.000300940504739341, + 0.000292276183024851, 0.000254537701320533, 0.000190629395147399, 0.000105163201801783, 0.000004038733321948, -0.000106038676866598, +-0.000218069429252057, -0.000325256102322617, -0.000421453273437500, -0.000501537574188219, -0.000561676165437471, -0.000599488135042163, +-0.000614088176959215, -0.000606030552867816, -0.000577150816605647, -0.000530343005106164, -0.000469277292459888, -0.000398098262223401, +-0.000321114777409093, -0.000242513979646880, -0.000166116860144892, -0.000095180544123547, -0.000032265535133044, 0.000020845988244699, + 0.000063154754823051, 0.000094403728281619, 0.000114986590640600, 0.000125832586450277, 0.000128274519298202, 0.000123887834821102, + 0.000114363268643203, 0.000101355825900618, 0.000086420158465092, 0.000070887005271560, 0.000055852072599971, 0.000042106693955817, + 0.000030184991625035, 0.000020370172970180, 0.000012696465277652, 0.000009448479205375 ], + +900: # 507 taps +[ 0.000008584643858565, 0.000009418988876053, 0.000014080571079703, 0.000019768330546629, 0.000026407938202815, 0.000033835554436160, + 0.000041775447258916, 0.000049840688763229, 0.000057525397104827, 0.000064222904381994, 0.000069229653331724, 0.000071774979399342, + 0.000071044828173283, 0.000066239730112684, 0.000056611414853593, 0.000041517869163367, 0.000020462723568921, -0.000006825518135279, +-0.000040362137084292, -0.000079866391614059, -0.000124740944604127, -0.000174014477804677, -0.000226421731946560, -0.000280359514663211, +-0.000333959777524947, -0.000385165398367582, -0.000431779497772114, -0.000471602831913601, -0.000502508714800933, -0.000522587601943708, +-0.000530244985591953, -0.000524326246532992, -0.000504199216087390, -0.000469843487184555, -0.000421891912076977, -0.000361653837423190, +-0.000291090671787090, -0.000212772068148448, -0.000129775708505696, -0.000045562167590561, 0.000036181038541454, 0.000111715248667983, + 0.000177457791675865, 0.000230168155042476, 0.000267145694421862, 0.000286408115238973, 0.000286822428035563, 0.000268225243572190, + 0.000231462231911356, 0.000178399161339807, 0.000111858949442223, 0.000035506782941005, -0.000046322038661538, -0.000128840476454010, +-0.000207064338646769, -0.000276095967153748, -0.000331410885812673, -0.000369138410006413, -0.000386317254135060, -0.000381111227069818, +-0.000352960919521696, -0.000302673831926362, -0.000232432591548855, -0.000145719497401035, -0.000047160366530124, 0.000057705644340284, + 0.000162731306278391, 0.000261502601122459, 0.000347728839468377, 0.000415628506871784, 0.000460307676423158, 0.000478101121808084, + 0.000466848645480576, 0.000426095215060785, 0.000357188814988566, 0.000263277817392097, 0.000149189135268233, 0.000021201177134175, +-0.000113285445089856, -0.000246159846025947, -0.000369078758947477, -0.000473974817394057, -0.000553573238187010, -0.000601879769614216, +-0.000614609034335983, -0.000589524295786869, -0.000526664670957786, -0.000428434330673012, -0.000299553787970989, -0.000146857332295172, + 0.000021049474537857, 0.000194254912976452, 0.000362113995846136, 0.000513885517380605, 0.000639407293886249, 0.000729757323783321, + 0.000777868837348859, 0.000779050717176191, 0.000731379294218185, 0.000635932028877494, 0.000496840283261992, 0.000321150767788200, + 0.000118496954373743, -0.000099406083911376, -0.000319420735859412, -0.000527759843251443, -0.000710834560643461, -0.000856122360814998, +-0.000953000983271033, -0.000993496322190293, -0.000972892522850171, -0.000890155314802122, -0.000748144004291994, -0.000553578640569728, +-0.000316765531771492, -0.000051080037307638, 0.000227762682493944, 0.000502615360611366, 0.000755932794801656, 0.000970869777260266, + 0.001132378198325256, 0.001228230831826114, 0.001249905234811852, 0.001193265605753869, 0.001058991562763383, 0.000852715731034719, + 0.000584852833903113, 0.000270116979072993, -0.000073253806782210, -0.000424524625176341, -0.000761689644626737, -0.001062825395605405, +-0.001307487536115730, -0.001478067477842081, -0.001561017661066179, -0.001547863924535660, -0.001435933022484648, -0.001228735487931008, +-0.000935971757611233, -0.000573140492907433, -0.000160766721791647, 0.000276722077340107, 0.000712407276705890, 0.001118502589392694, + 0.001468091403060663, 0.001736873763731978, 0.001904808530758137, 0.001957546402581353, 0.001887551700267456, 0.001694830920708386, + 0.001387207697842587, 0.000980110478290227, 0.000495866308669078, -0.000037468525946086, -0.000587687238352686, -0.001120332197278825, +-0.001600807439724522, -0.001996577848597569, -0.002279316251738701, -0.002426862344229953, -0.002424859679572050, -0.002267956637237035, +-0.001960478287612480, -0.001516506229070831, -0.000959342574003313, -0.000320365808444990, 0.000362667830279848, 0.001047791955533175, + 0.001691344168593488, 0.002250683068847439, 0.002686938772729870, 0.002967628123966539, 0.003068960407414939, 0.002977678084347548, + 0.002692297230438709, 0.002223648489886749, 0.001594655540313720, 0.000839340735107497, 0.000001086828787463, -0.000869758241867305, +-0.001718811255366889, -0.002490949831033119, -0.003133783611782112, -0.003601086428852005, -0.003855971828191669, -0.003873602941247202, +-0.003643247691842578, -0.003169524021369342, -0.002472727031941658, -0.001588180215901802, -0.000564613998354494, 0.000538365561072565, + 0.001653594591084052, 0.002710281216575659, 0.003638323695477434, 0.004372765950231630, 0.004858115780200945, 0.005052252604909589, + 0.004929664405729861, 0.004483785715272022, 0.003728252839970840, 0.002696955568587015, 0.001442828675796044, 0.000035407607162536, +-0.001442758217956277, -0.002900658243916532, -0.004244107302544382, -0.005381470691504941, -0.006229500241120763, -0.006718928923729580, +-0.006799475746570288, -0.006443928763042613, -0.005651020748503490, -0.004446866818108242, -0.002884813426831026, -0.001043632901391050, + 0.000975905251500855, 0.003055961712387741, 0.005067831037893753, 0.006879122081429573, 0.008361541203410547, 0.009398858161408168, + 0.009894604643011696, 0.009779054490283380, 0.009015056125786116, 0.007602336538990519, 0.005579966604992522, 0.003026770185465134, + 0.000059562008821989, -0.003170779553680069, -0.006485285707743206, -0.009684675209522759, -0.012558594763991068, -0.014895884693277550, +-0.016495367870764655, -0.017176610952606329, -0.016790098400516502, -0.015226268406232360, -0.012422909599680294, -0.008370486626783004, +-0.003115063791803408, 0.003241392133040441, 0.010543411361223271, 0.018587073767192364, 0.027127521275133942, 0.035888613808298719, + 0.044574291632219959, 0.052881118780651611, 0.060511410978741485, 0.067186317405564938, 0.072658219055817971, 0.076721836801003668, + 0.079223499565701364, 0.080068115845938093, 0.079223499565701364, 0.076721836801003668, 0.072658219055817971, 0.067186317405564938, + 0.060511410978741485, 0.052881118780651611, 0.044574291632219959, 0.035888613808298719, 0.027127521275133942, 0.018587073767192364, + 0.010543411361223271, 0.003241392133040441, -0.003115063791803408, -0.008370486626783004, -0.012422909599680294, -0.015226268406232360, +-0.016790098400516502, -0.017176610952606329, -0.016495367870764655, -0.014895884693277550, -0.012558594763991068, -0.009684675209522759, +-0.006485285707743206, -0.003170779553680069, 0.000059562008821989, 0.003026770185465134, 0.005579966604992522, 0.007602336538990519, + 0.009015056125786116, 0.009779054490283380, 0.009894604643011696, 0.009398858161408168, 0.008361541203410547, 0.006879122081429573, + 0.005067831037893753, 0.003055961712387741, 0.000975905251500855, -0.001043632901391050, -0.002884813426831026, -0.004446866818108242, +-0.005651020748503490, -0.006443928763042613, -0.006799475746570288, -0.006718928923729580, -0.006229500241120763, -0.005381470691504941, +-0.004244107302544382, -0.002900658243916532, -0.001442758217956277, 0.000035407607162536, 0.001442828675796044, 0.002696955568587015, + 0.003728252839970840, 0.004483785715272022, 0.004929664405729861, 0.005052252604909589, 0.004858115780200945, 0.004372765950231630, + 0.003638323695477434, 0.002710281216575659, 0.001653594591084052, 0.000538365561072565, -0.000564613998354494, -0.001588180215901802, +-0.002472727031941658, -0.003169524021369342, -0.003643247691842578, -0.003873602941247202, -0.003855971828191669, -0.003601086428852005, +-0.003133783611782112, -0.002490949831033119, -0.001718811255366889, -0.000869758241867305, 0.000001086828787463, 0.000839340735107497, + 0.001594655540313720, 0.002223648489886749, 0.002692297230438709, 0.002977678084347548, 0.003068960407414939, 0.002967628123966539, + 0.002686938772729870, 0.002250683068847439, 0.001691344168593488, 0.001047791955533175, 0.000362667830279848, -0.000320365808444990, +-0.000959342574003313, -0.001516506229070831, -0.001960478287612480, -0.002267956637237035, -0.002424859679572050, -0.002426862344229953, +-0.002279316251738701, -0.001996577848597569, -0.001600807439724522, -0.001120332197278825, -0.000587687238352686, -0.000037468525946086, + 0.000495866308669078, 0.000980110478290227, 0.001387207697842587, 0.001694830920708386, 0.001887551700267456, 0.001957546402581353, + 0.001904808530758137, 0.001736873763731978, 0.001468091403060663, 0.001118502589392694, 0.000712407276705890, 0.000276722077340107, +-0.000160766721791647, -0.000573140492907433, -0.000935971757611233, -0.001228735487931008, -0.001435933022484648, -0.001547863924535660, +-0.001561017661066179, -0.001478067477842081, -0.001307487536115730, -0.001062825395605405, -0.000761689644626737, -0.000424524625176341, +-0.000073253806782210, 0.000270116979072993, 0.000584852833903113, 0.000852715731034719, 0.001058991562763383, 0.001193265605753869, + 0.001249905234811852, 0.001228230831826114, 0.001132378198325256, 0.000970869777260266, 0.000755932794801656, 0.000502615360611366, + 0.000227762682493944, -0.000051080037307638, -0.000316765531771492, -0.000553578640569728, -0.000748144004291994, -0.000890155314802122, +-0.000972892522850171, -0.000993496322190293, -0.000953000983271033, -0.000856122360814998, -0.000710834560643461, -0.000527759843251443, +-0.000319420735859412, -0.000099406083911376, 0.000118496954373743, 0.000321150767788200, 0.000496840283261992, 0.000635932028877494, + 0.000731379294218185, 0.000779050717176191, 0.000777868837348859, 0.000729757323783321, 0.000639407293886249, 0.000513885517380605, + 0.000362113995846136, 0.000194254912976452, 0.000021049474537857, -0.000146857332295172, -0.000299553787970989, -0.000428434330673012, +-0.000526664670957786, -0.000589524295786869, -0.000614609034335983, -0.000601879769614216, -0.000553573238187010, -0.000473974817394057, +-0.000369078758947477, -0.000246159846025947, -0.000113285445089856, 0.000021201177134175, 0.000149189135268233, 0.000263277817392097, + 0.000357188814988566, 0.000426095215060785, 0.000466848645480576, 0.000478101121808084, 0.000460307676423158, 0.000415628506871784, + 0.000347728839468377, 0.000261502601122459, 0.000162731306278391, 0.000057705644340284, -0.000047160366530124, -0.000145719497401035, +-0.000232432591548855, -0.000302673831926362, -0.000352960919521696, -0.000381111227069818, -0.000386317254135060, -0.000369138410006413, +-0.000331410885812673, -0.000276095967153748, -0.000207064338646769, -0.000128840476454010, -0.000046322038661538, 0.000035506782941005, + 0.000111858949442223, 0.000178399161339807, 0.000231462231911356, 0.000268225243572190, 0.000286822428035563, 0.000286408115238973, + 0.000267145694421862, 0.000230168155042476, 0.000177457791675865, 0.000111715248667983, 0.000036181038541454, -0.000045562167590561, +-0.000129775708505696, -0.000212772068148448, -0.000291090671787090, -0.000361653837423190, -0.000421891912076977, -0.000469843487184555, +-0.000504199216087390, -0.000524326246532992, -0.000530244985591953, -0.000522587601943708, -0.000502508714800933, -0.000471602831913601, +-0.000431779497772114, -0.000385165398367582, -0.000333959777524947, -0.000280359514663211, -0.000226421731946560, -0.000174014477804677, +-0.000124740944604127, -0.000079866391614059, -0.000040362137084292, -0.000006825518135279, 0.000020462723568921, 0.000041517869163367, + 0.000056611414853593, 0.000066239730112684, 0.000071044828173283, 0.000071774979399342, 0.000069229653331724, 0.000064222904381994, + 0.000057525397104827, 0.000049840688763229, 0.000041775447258916, 0.000033835554436160, 0.000026407938202815, 0.000019768330546629, + 0.000014080571079703, 0.000009418988876053, 0.000008584643858565 ], + +800: # 576 taps +[ 0.000007812421315026, 0.000008939491599758, 0.000013730623424634, 0.000019922294330328, 0.000027648080357536, 0.000037033583075960, + 0.000048143861498476, 0.000060975611534136, 0.000075478577997365, 0.000091486545323856, 0.000108759338791293, 0.000126959389238696, + 0.000145652693573148, 0.000164310512299333, 0.000182322037177544, 0.000198999590569024, 0.000213614009898736, 0.000225404773677130, + 0.000233610866318186, 0.000237501949870309, 0.000236413452609260, 0.000229779689874953, 0.000217172712546861, 0.000198326327006374, + 0.000173169245803356, 0.000141846034715806, 0.000104732554871058, 0.000062442756567725, 0.000015827588463025, -0.000034041343381570, +-0.000085892061029301, -0.000138290085401831, -0.000189681915226441, -0.000238447248218383, -0.000282958234132596, -0.000321645514236948, +-0.000353063703221184, -0.000375960779331289, -0.000389338959490572, -0.000392510765617404, -0.000385145111580360, -0.000367301005108244, +-0.000339445383884735, -0.000302456824898826, -0.000257608154338197, -0.000206533574886596, -0.000151176196094362, -0.000093718946756075, +-0.000036501336346782, 0.000018073847262639, 0.000067643276268719, 0.000109986899426513, 0.000143132709269975, 0.000165454349492432, + 0.000175757544777044, 0.000173350787020117, 0.000158097036687462, 0.000130440616590328, 0.000091410233747216, 0.000042593436932362, +-0.000013915345466225, -0.000075592647414163, -0.000139587283955501, -0.000202842130917589, -0.000262229388149273, -0.000314696298480236, +-0.000357412309140960, -0.000387912413334777, -0.000404229097174175, -0.000405005829251745, -0.000389585053939050, -0.000358067160688509, +-0.000311334118688652, -0.000251037234855322, -0.000179546871425107, -0.000099865220473760, -0.000015504805729649, 0.000069662241126261, + 0.000151579558482804, 0.000226197017031803, 0.000289669081062027, 0.000338550686637535, 0.000369981142430884, 0.000381846581094768, + 0.000372912653196213, 0.000342918280693206, 0.000292625889758754, 0.000223822598023746, 0.000139271063990380, 0.000042610318846710, +-0.000061790478444653, -0.000169020516263524, -0.000273849135305279, -0.000370972701089329, -0.000455274194431906, -0.000522083581325246, +-0.000567425902673365, -0.000588244271105343, -0.000582585214321533, -0.000549736803809912, -0.000490309633189676, -0.000406255267998045, +-0.000300818490773199, -0.000178423294758992, -0.000044495794702522, 0.000094768978920233, 0.000232687846388931, 0.000362404482394644, + 0.000477224887882656, 0.000570957019366285, 0.000638237711550962, 0.000674829917199715, 0.000677874702997924, 0.000646082835685789, + 0.000579855135491155, 0.000481322426719260, 0.000354300348814663, 0.000204158180969291, 0.000037605243749942, -0.000137597792476766, +-0.000312990053930070, -0.000479809451013147, -0.000629420667419594, -0.000753751787384093, -0.000845717937205157, -0.000899610318070534, +-0.000911429428339170, -0.000879144467901314, -0.000802862554846735, -0.000684896491503848, -0.000529724099269400, -0.000343837417747543, +-0.000135485422990712, 0.000085680046384777, 0.000309041306180360, 0.000523514975880865, 0.000718092538346410, 0.000882395437488790, + 0.001007217243930078, 0.001085025011663583, 0.001110393602574907, 0.001080348246990215, 0.000994595533680270, 0.000855627164276610, + 0.000668686966519433, 0.000441598292681654, 0.000184456030731509, -0.000090806124488888, -0.000370954941565034, -0.000642080313861205, +-0.000890272488296022, -0.001102321495571061, -0.001266404272056495, -0.001372724600470066, -0.001414071700002426, -0.001386267150663963, +-0.001288473577308051, -0.001123345195626614, -0.000897007516317755, -0.000618861629989936, -0.000301217045862462, 0.000041233610152644, + 0.000392077118279518, 0.000733950102097711, 0.001049382185659160, 0.001321671816520764, 0.001535751682495805, 0.001678999613916214, + 0.001741952717366617, 0.001718885253761411, 0.001608217075415199, 0.001412726517149060, 0.001139550679396572, 0.000799966281315092, + 0.000408955257020119, -0.000015430291445883, -0.000452877901224665, -0.000881764419386712, -0.001280202174740378, -0.001627132056108478, +-0.001903410054728627, -0.002092832630669335, -0.002183047066222210, -0.002166297669634002, -0.002039964832379242, -0.001806863227100527, +-0.001475276496367705, -0.001058718218567234, -0.000575422161543602, -0.000047579005659538, 0.000499650640507829, 0.001039304486976568, + 0.001543915361236087, 0.001986875765177565, 0.002343803978935238, 0.002593843111794459, 0.002720826151891805, 0.002714244052422841, + 0.002569962094668674, 0.002290640442950059, 0.001885828071941918, 0.001371714610702517, 0.000770541414234860, 0.000109689735095918, +-0.000579518937674694, -0.001263260162418279, -0.001906817708604015, -0.002476294031416823, -0.002940338141748221, -0.003271805587759476, +-0.003449265353836316, -0.003458273739088719, -0.003292343903957135, -0.002953552492773730, -0.002452740833751403, -0.001809286792967657, +-0.001050443545598396, -0.000210263213371804, 0.000671856047428268, 0.001552940847463649, 0.002388454943494185, 0.003134462521134076, + 0.003749841173565993, 0.004198437225205720, 0.004451055565195895, 0.004487180085620033, 0.004296330509189801, 0.003878975870600702, + 0.003246943568337220, 0.002423285251527749, 0.001441585983790830, 0.000344729182561098, -0.000816843070689627, -0.001987309118898022, +-0.003107997569512837, -0.004120159055022873, -0.004967855525593480, -0.005600832763225632, -0.005977237320460713, -0.006066041703895594, +-0.005849050345318046, -0.005322373876566778, -0.004497280230451145, -0.003400356983323659, -0.002072948915619172, -0.000569867563016818, + 0.001042597072372037, 0.002689299061586182, 0.004289398058621653, 0.005759936423629122, 0.007019685172079656, 0.007993094199877881, + 0.008614171947670925, 0.008830116409127314, 0.008604524292277297, 0.007920018129346281, 0.006780151368661940, 0.005210478633443022, + 0.003258711582163722, 0.000993917994210585, -0.001495237701498300, -0.004103172616953404, -0.006710901704621617, -0.009190272912439044, +-0.011408767543032211, -0.013234683860549021, -0.014542500326924340, -0.015218201853231224, -0.015164348354310610, -0.014304669860257387, +-0.012587987008943695, -0.009991279084291772, -0.006521752749437525, -0.002217802857491734, 0.002851200149507507, 0.008586315129089878, + 0.014861597832277761, 0.021527754009268797, 0.028416755733892799, 0.035347257703852740, 0.042130616026199795, 0.048577285227121583, + 0.054503351348228368, 0.059736951256086870, 0.064124330411942668, 0.067535303652784234, 0.069867905879939018, 0.071052050289491700, + 0.071052050289491700, 0.069867905879939018, 0.067535303652784234, 0.064124330411942668, 0.059736951256086870, 0.054503351348228368, + 0.048577285227121583, 0.042130616026199795, 0.035347257703852740, 0.028416755733892799, 0.021527754009268797, 0.014861597832277761, + 0.008586315129089878, 0.002851200149507507, -0.002217802857491734, -0.006521752749437525, -0.009991279084291772, -0.012587987008943695, +-0.014304669860257387, -0.015164348354310610, -0.015218201853231224, -0.014542500326924340, -0.013234683860549021, -0.011408767543032211, +-0.009190272912439044, -0.006710901704621617, -0.004103172616953404, -0.001495237701498300, 0.000993917994210585, 0.003258711582163722, + 0.005210478633443022, 0.006780151368661940, 0.007920018129346281, 0.008604524292277297, 0.008830116409127314, 0.008614171947670925, + 0.007993094199877881, 0.007019685172079656, 0.005759936423629122, 0.004289398058621653, 0.002689299061586182, 0.001042597072372037, +-0.000569867563016818, -0.002072948915619172, -0.003400356983323659, -0.004497280230451145, -0.005322373876566778, -0.005849050345318046, +-0.006066041703895594, -0.005977237320460713, -0.005600832763225632, -0.004967855525593480, -0.004120159055022873, -0.003107997569512837, +-0.001987309118898022, -0.000816843070689627, 0.000344729182561098, 0.001441585983790830, 0.002423285251527749, 0.003246943568337220, + 0.003878975870600702, 0.004296330509189801, 0.004487180085620033, 0.004451055565195895, 0.004198437225205720, 0.003749841173565993, + 0.003134462521134076, 0.002388454943494185, 0.001552940847463649, 0.000671856047428268, -0.000210263213371804, -0.001050443545598396, +-0.001809286792967657, -0.002452740833751403, -0.002953552492773730, -0.003292343903957135, -0.003458273739088719, -0.003449265353836316, +-0.003271805587759476, -0.002940338141748221, -0.002476294031416823, -0.001906817708604015, -0.001263260162418279, -0.000579518937674694, + 0.000109689735095918, 0.000770541414234860, 0.001371714610702517, 0.001885828071941918, 0.002290640442950059, 0.002569962094668674, + 0.002714244052422841, 0.002720826151891805, 0.002593843111794459, 0.002343803978935238, 0.001986875765177565, 0.001543915361236087, + 0.001039304486976568, 0.000499650640507829, -0.000047579005659538, -0.000575422161543602, -0.001058718218567234, -0.001475276496367705, +-0.001806863227100527, -0.002039964832379242, -0.002166297669634002, -0.002183047066222210, -0.002092832630669335, -0.001903410054728627, +-0.001627132056108478, -0.001280202174740378, -0.000881764419386712, -0.000452877901224665, -0.000015430291445883, 0.000408955257020119, + 0.000799966281315092, 0.001139550679396572, 0.001412726517149060, 0.001608217075415199, 0.001718885253761411, 0.001741952717366617, + 0.001678999613916214, 0.001535751682495805, 0.001321671816520764, 0.001049382185659160, 0.000733950102097711, 0.000392077118279518, + 0.000041233610152644, -0.000301217045862462, -0.000618861629989936, -0.000897007516317755, -0.001123345195626614, -0.001288473577308051, +-0.001386267150663963, -0.001414071700002426, -0.001372724600470066, -0.001266404272056495, -0.001102321495571061, -0.000890272488296022, +-0.000642080313861205, -0.000370954941565034, -0.000090806124488888, 0.000184456030731509, 0.000441598292681654, 0.000668686966519433, + 0.000855627164276610, 0.000994595533680270, 0.001080348246990215, 0.001110393602574907, 0.001085025011663583, 0.001007217243930078, + 0.000882395437488790, 0.000718092538346410, 0.000523514975880865, 0.000309041306180360, 0.000085680046384777, -0.000135485422990712, +-0.000343837417747543, -0.000529724099269400, -0.000684896491503848, -0.000802862554846735, -0.000879144467901314, -0.000911429428339170, +-0.000899610318070534, -0.000845717937205157, -0.000753751787384093, -0.000629420667419594, -0.000479809451013147, -0.000312990053930070, +-0.000137597792476766, 0.000037605243749942, 0.000204158180969291, 0.000354300348814663, 0.000481322426719260, 0.000579855135491155, + 0.000646082835685789, 0.000677874702997924, 0.000674829917199715, 0.000638237711550962, 0.000570957019366285, 0.000477224887882656, + 0.000362404482394644, 0.000232687846388931, 0.000094768978920233, -0.000044495794702522, -0.000178423294758992, -0.000300818490773199, +-0.000406255267998045, -0.000490309633189676, -0.000549736803809912, -0.000582585214321533, -0.000588244271105343, -0.000567425902673365, +-0.000522083581325246, -0.000455274194431906, -0.000370972701089329, -0.000273849135305279, -0.000169020516263524, -0.000061790478444653, + 0.000042610318846710, 0.000139271063990380, 0.000223822598023746, 0.000292625889758754, 0.000342918280693206, 0.000372912653196213, + 0.000381846581094768, 0.000369981142430884, 0.000338550686637535, 0.000289669081062027, 0.000226197017031803, 0.000151579558482804, + 0.000069662241126261, -0.000015504805729649, -0.000099865220473760, -0.000179546871425107, -0.000251037234855322, -0.000311334118688652, +-0.000358067160688509, -0.000389585053939050, -0.000405005829251745, -0.000404229097174175, -0.000387912413334777, -0.000357412309140960, +-0.000314696298480236, -0.000262229388149273, -0.000202842130917589, -0.000139587283955501, -0.000075592647414163, -0.000013915345466225, + 0.000042593436932362, 0.000091410233747216, 0.000130440616590328, 0.000158097036687462, 0.000173350787020117, 0.000175757544777044, + 0.000165454349492432, 0.000143132709269975, 0.000109986899426513, 0.000067643276268719, 0.000018073847262639, -0.000036501336346782, +-0.000093718946756075, -0.000151176196094362, -0.000206533574886596, -0.000257608154338197, -0.000302456824898826, -0.000339445383884735, +-0.000367301005108244, -0.000385145111580360, -0.000392510765617404, -0.000389338959490572, -0.000375960779331289, -0.000353063703221184, +-0.000321645514236948, -0.000282958234132596, -0.000238447248218383, -0.000189681915226441, -0.000138290085401831, -0.000085892061029301, +-0.000034041343381570, 0.000015827588463025, 0.000062442756567725, 0.000104732554871058, 0.000141846034715806, 0.000173169245803356, + 0.000198326327006374, 0.000217172712546861, 0.000229779689874953, 0.000236413452609260, 0.000237501949870309, 0.000233610866318186, + 0.000225404773677130, 0.000213614009898736, 0.000198999590569024, 0.000182322037177544, 0.000164310512299333, 0.000145652693573148, + 0.000126959389238696, 0.000108759338791293, 0.000091486545323856, 0.000075478577997365, 0.000060975611534136, 0.000048143861498476, + 0.000037033583075960, 0.000027648080357536, 0.000019922294330328, 0.000013730623424634, 0.000008939491599758, 0.000007812421315026], + +400: # Shape 1.3, 770 taps +[ 0.000006365988874769, 0.000003794338487480, 0.000004876278267121, 0.000006131564967683, 0.000007569854655698, 0.000009199819153557, + 0.000011019782182642, 0.000013040101237056, 0.000015265995566942, 0.000017696391039940, 0.000020312791765427, 0.000023135950087517, + 0.000026132051354026, 0.000029299266022519, 0.000032613010658575, 0.000036050466832082, 0.000039588439426462, 0.000043195256918759, + 0.000046833693036583, 0.000050464843382682, 0.000054046630064787, 0.000057526210900478, 0.000060855931076401, 0.000063977534092402, + 0.000066834004766232, 0.000069367495918698, 0.000071512066722409, 0.000073206600728293, 0.000074384990732719, 0.000074985154083054, + 0.000074941358848070, 0.000074196098488127, 0.000072687036065854, 0.000070364017870395, 0.000067174711830892, 0.000063076338216280, + 0.000058031241355323, 0.000052010623717769, 0.000044992453647518, 0.000036966078084084, 0.000027928525061234, 0.000017889642287636, + 0.000006870052370296, -0.000005097430373119, -0.000017968467835226, -0.000031683467914261, -0.000046174009079182, -0.000061354520788796, +-0.000077131717460578, -0.000093396057473154, -0.000110030073586572, -0.000126901644954483, -0.000143873358110243, -0.000160793959726774, +-0.000177509258936783, -0.000193855013323854, -0.000209665383215103, -0.000224769517767120, -0.000238997508310048, -0.000252178063434561, +-0.000264145812850823, -0.000274737753978821, -0.000283800555901376, -0.000291188041391623, -0.000296767743750952, -0.000300418295008671, +-0.000302037282504393, -0.000301535929224389, -0.000298849141904324, -0.000293929053180242, -0.000286753991998278, -0.000277322604938755, +-0.000265661916507038, -0.000251820837747345, -0.000235878209412169, -0.000217935329921142, -0.000198122592158803, -0.000176593305324287, +-0.000153527761718214, -0.000129127285942292, -0.000103617728527825, -0.000077242169252941, -0.000050264484742360, -0.000022961141775614, + 0.000004376321843836, 0.000031449535456591, 0.000057950921452100, 0.000083573577884305, 0.000108007974814302, 0.000130953051335089, + 0.000152112926634605, 0.000171207503556438, 0.000187970143706098, 0.000202157681598790, 0.000213547707697876, 0.000221948537710715, + 0.000227196325735486, 0.000229163803316040, 0.000227757838181676, 0.000222926398781727, 0.000214655798042235, 0.000202977177552243, + 0.000187962083392261, 0.000169728357656893, 0.000148434669356957, 0.000124285090373991, 0.000097522588191792, 0.000068432637827430, + 0.000037334851940437, 0.000004586327504349, -0.000029428168903046, -0.000064294238370152, -0.000099577998322800, -0.000134825630330724, +-0.000169573965195115, -0.000203350785393334, -0.000235686105255552, -0.000266113076153881, -0.000294178911934326, -0.000319446762108148, +-0.000341506152453346, -0.000359974720676106, -0.000374508618833672, -0.000384803219892303, -0.000390603203520662, -0.000391702472886875, +-0.000387952968439801, -0.000379263512998934, -0.000365607622249019, -0.000347020514221526, -0.000323605716996244, -0.000295530392174600, +-0.000263029891857937, -0.000226402015568579, -0.000186009246146028, -0.000142271402525828, -0.000095666125277552, -0.000046719850734115, + 0.000003993583197103, 0.000055863130913344, 0.000108243891748510, 0.000160468312893279, 0.000211850586288707, 0.000261699269396572, + 0.000309322128759128, 0.000354039461924243, 0.000395189349018748, 0.000432140920160531, 0.000464299583558709, 0.000491120046640221, + 0.000512110607671310, 0.000526845446928034, 0.000534967981519684, 0.000536201343488612, 0.000530350597696912, 0.000517311013160068, + 0.000497068439035188, 0.000469705445435111, 0.000435399334882175, 0.000394425628337960, 0.000347153886365670, 0.000294048338105669, + 0.000235661140309264, 0.000172630559665850, 0.000105671355374271, 0.000035570791815593, -0.000036823410279226, -0.000110609450083053, +-0.000184845354264341, -0.000258557173028134, -0.000330754528114594, -0.000400439970264790, -0.000466625524492906, -0.000528342888003039, +-0.000584659866982658, -0.000634690947746067, -0.000677613119135274, -0.000712675673752122, -0.000739214923907549, -0.000756662495413132, +-0.000764558104223763, -0.000762555872583250, -0.000750434429795932, -0.000728100396603541, -0.000695595696462148, -0.000653097503960073, +-0.000600922257516729, -0.000539522021491309, -0.000469484695604341, -0.000391526816237233, -0.000306489974421006, -0.000215329834691466, +-0.000119109058134576, -0.000018982981184245, 0.000083810915235651, 0.000187970000243908, 0.000292139959571129, 0.000394933986966016, + 0.000494948142664746, 0.000590782205985295, 0.000681056090464857, 0.000764431197740454, 0.000839627050452169, 0.000905442225580665, + 0.000960769995583447, 0.001004618039369409, 0.001036121906273736, 0.001054562391583931, 0.001059376177251162, 0.001050169688173629, + 0.001026726061735900, 0.000989014843850052, 0.000937194466007411, 0.000871617237374737, 0.000792826925935815, 0.000701558421554040, + 0.000598730377991698, 0.000485439514972828, 0.000362948081805026, 0.000232673195056634, 0.000096169337651215, -0.000044886850392886, +-0.000188718302856941, -0.000333466707544108, -0.000477217660879214, -0.000618022612530573, -0.000753926550301084, -0.000882991568244702, +-0.001003325671471952, -0.001113106854174517, -0.001210611462788910, -0.001294237402501427, -0.001362530796885569, -0.001414206728098591, +-0.001448172776524704, -0.001463546100508022, -0.001459672417038066, -0.001436138251655395, -0.001392784297634421, -0.001329711615112041, +-0.001247288727402393, -0.001146150989584800, -0.001027200706007495, -0.000891599407168167, -0.000740760728594229, -0.000576335492878354, +-0.000400197621159931, -0.000214422239224392, -0.000021265147290331, 0.000176865113062629, 0.000377433546776307, 0.000577811529416548, + 0.000775307530646278, 0.000967203529925253, 0.001150788527481832, 0.001323396897145278, 0.001482443038707630, 0.001625459660097671, + 0.001750131670233381, 0.001854332615855901, 0.001936155690016456, 0.001993946486300650, 0.002026329220986863, 0.002032233882826800, + 0.002010916100718580, 0.001961977029179007, 0.001885375282542786, 0.001781438385022068, 0.001650865633292049, 0.001494730107495952, + 0.001314471906401099, 0.001111890114781271, 0.000889126269781853, 0.000648646430391355, 0.000393215009447102, 0.000125867561738431, +-0.000150124130738416, -0.000431287566747790, -0.000713991806927365, -0.000994489713483095, -0.001268965941124358, -0.001533583756312415, +-0.001784536663195940, -0.002018097583665120, -0.002230671541941067, -0.002418844734804261, -0.002579435657364371, -0.002709541359817244, +-0.002806584464746950, -0.002868353953987823, -0.002893045404115256, -0.002879294007683593, -0.002826205616681453, -0.002733379783299579, +-0.002600929724527880, -0.002429493528198460, -0.002220241603361130, -0.001974874872112559, -0.001695618645272131, -0.001385207220497454, +-0.001046863974406666, -0.000684272287342287, -0.000301542320981976, 0.000096831014522634, 0.000506013584333645, 0.000920885468043068, + 0.001336097138497067, 0.001746132300635148, 0.002145372272540894, 0.002528165649885475, 0.002888898066467300, 0.003222065542166644, + 0.003522346486582096, 0.003784675282650529, 0.004004312987512708, 0.004176917749158860, 0.004298610545483354, 0.004366038868235595, + 0.004376434088990949, 0.004327664995106265, 0.004218283602051056, 0.004047565581181025, 0.003815541611827637, 0.003523022274162837, + 0.003171612798693589, 0.002763720451889364, 0.002302551278585364, 0.001792098861036779, 0.001237122342212990, 0.000643116381836866, + 0.000016270569630469, -0.000636578845765101, -0.001308006968347628, -0.001990065317450888, -0.002674355367539247, -0.003352107995747014, +-0.004014269925046706, -0.004651594401548673, -0.005254737118938589, -0.005814354519325696, -0.006321205162109337, -0.006766251436254966, +-0.007140762118819473, -0.007436412979114623, -0.007645386018101693, -0.007760464454403432, -0.007775124017275518, -0.007683617984862557, +-0.007481056258606499, -0.007163476264301046, -0.006727906019989577, -0.006172417333684170, -0.005496169589494688, -0.004699442443010971, +-0.003783657807377434, -0.002751390001364686, -0.001606364515685684, -0.000353444465756940, 0.001001394411574224, 0.002451100972703334, + 0.003987597007902468, 0.005601837062643716, 0.007283878572786960, 0.009022962344277727, 0.010807601882596756, 0.012625681525580169, + 0.014464561665273282, 0.016311190660485246, 0.018152221825464991, 0.019974134681837181, 0.021763358913199277, 0.023506400111173612, + 0.025189965611159389, 0.026801089460580949, 0.028327254981021979, 0.029756513765852269, 0.031077599771363505, 0.032280037413131318, + 0.033354242302380432, 0.034291613871121174, 0.035084618575605728, 0.035726863022388727, 0.036213156095256299, 0.036539559528566301, + 0.036703426247133436, 0.036703426247133436, 0.036539559528566301, 0.036213156095256299, 0.035726863022388727, 0.035084618575605728, + 0.034291613871121174, 0.033354242302380432, 0.032280037413131318, 0.031077599771363505, 0.029756513765852269, 0.028327254981021979, + 0.026801089460580949, 0.025189965611159389, 0.023506400111173612, 0.021763358913199277, 0.019974134681837181, 0.018152221825464991, + 0.016311190660485246, 0.014464561665273282, 0.012625681525580169, 0.010807601882596756, 0.009022962344277727, 0.007283878572786960, + 0.005601837062643716, 0.003987597007902468, 0.002451100972703334, 0.001001394411574224, -0.000353444465756940, -0.001606364515685684, +-0.002751390001364686, -0.003783657807377434, -0.004699442443010971, -0.005496169589494688, -0.006172417333684170, -0.006727906019989577, +-0.007163476264301046, -0.007481056258606499, -0.007683617984862557, -0.007775124017275518, -0.007760464454403432, -0.007645386018101693, +-0.007436412979114623, -0.007140762118819473, -0.006766251436254966, -0.006321205162109337, -0.005814354519325696, -0.005254737118938589, +-0.004651594401548673, -0.004014269925046706, -0.003352107995747014, -0.002674355367539247, -0.001990065317450888, -0.001308006968347628, +-0.000636578845765101, 0.000016270569630469, 0.000643116381836866, 0.001237122342212990, 0.001792098861036779, 0.002302551278585364, + 0.002763720451889364, 0.003171612798693589, 0.003523022274162837, 0.003815541611827637, 0.004047565581181025, 0.004218283602051056, + 0.004327664995106265, 0.004376434088990949, 0.004366038868235595, 0.004298610545483354, 0.004176917749158860, 0.004004312987512708, + 0.003784675282650529, 0.003522346486582096, 0.003222065542166644, 0.002888898066467300, 0.002528165649885475, 0.002145372272540894, + 0.001746132300635148, 0.001336097138497067, 0.000920885468043068, 0.000506013584333645, 0.000096831014522634, -0.000301542320981976, +-0.000684272287342287, -0.001046863974406666, -0.001385207220497454, -0.001695618645272131, -0.001974874872112559, -0.002220241603361130, +-0.002429493528198460, -0.002600929724527880, -0.002733379783299579, -0.002826205616681453, -0.002879294007683593, -0.002893045404115256, +-0.002868353953987823, -0.002806584464746950, -0.002709541359817244, -0.002579435657364371, -0.002418844734804261, -0.002230671541941067, +-0.002018097583665120, -0.001784536663195940, -0.001533583756312415, -0.001268965941124358, -0.000994489713483095, -0.000713991806927365, +-0.000431287566747790, -0.000150124130738416, 0.000125867561738431, 0.000393215009447102, 0.000648646430391355, 0.000889126269781853, + 0.001111890114781271, 0.001314471906401099, 0.001494730107495952, 0.001650865633292049, 0.001781438385022068, 0.001885375282542786, + 0.001961977029179007, 0.002010916100718580, 0.002032233882826800, 0.002026329220986863, 0.001993946486300650, 0.001936155690016456, + 0.001854332615855901, 0.001750131670233381, 0.001625459660097671, 0.001482443038707630, 0.001323396897145278, 0.001150788527481832, + 0.000967203529925253, 0.000775307530646278, 0.000577811529416548, 0.000377433546776307, 0.000176865113062629, -0.000021265147290331, +-0.000214422239224392, -0.000400197621159931, -0.000576335492878354, -0.000740760728594229, -0.000891599407168167, -0.001027200706007495, +-0.001146150989584800, -0.001247288727402393, -0.001329711615112041, -0.001392784297634421, -0.001436138251655395, -0.001459672417038066, +-0.001463546100508022, -0.001448172776524704, -0.001414206728098591, -0.001362530796885569, -0.001294237402501427, -0.001210611462788910, +-0.001113106854174517, -0.001003325671471952, -0.000882991568244702, -0.000753926550301084, -0.000618022612530573, -0.000477217660879214, +-0.000333466707544108, -0.000188718302856941, -0.000044886850392886, 0.000096169337651215, 0.000232673195056634, 0.000362948081805026, + 0.000485439514972828, 0.000598730377991698, 0.000701558421554040, 0.000792826925935815, 0.000871617237374737, 0.000937194466007411, + 0.000989014843850052, 0.001026726061735900, 0.001050169688173629, 0.001059376177251162, 0.001054562391583931, 0.001036121906273736, + 0.001004618039369409, 0.000960769995583447, 0.000905442225580665, 0.000839627050452169, 0.000764431197740454, 0.000681056090464857, + 0.000590782205985295, 0.000494948142664746, 0.000394933986966016, 0.000292139959571129, 0.000187970000243908, 0.000083810915235651, +-0.000018982981184245, -0.000119109058134576, -0.000215329834691466, -0.000306489974421006, -0.000391526816237233, -0.000469484695604341, +-0.000539522021491309, -0.000600922257516729, -0.000653097503960073, -0.000695595696462148, -0.000728100396603541, -0.000750434429795932, +-0.000762555872583250, -0.000764558104223763, -0.000756662495413132, -0.000739214923907549, -0.000712675673752122, -0.000677613119135274, +-0.000634690947746067, -0.000584659866982658, -0.000528342888003039, -0.000466625524492906, -0.000400439970264790, -0.000330754528114594, +-0.000258557173028134, -0.000184845354264341, -0.000110609450083053, -0.000036823410279226, 0.000035570791815593, 0.000105671355374271, + 0.000172630559665850, 0.000235661140309264, 0.000294048338105669, 0.000347153886365670, 0.000394425628337960, 0.000435399334882175, + 0.000469705445435111, 0.000497068439035188, 0.000517311013160068, 0.000530350597696912, 0.000536201343488612, 0.000534967981519684, + 0.000526845446928034, 0.000512110607671310, 0.000491120046640221, 0.000464299583558709, 0.000432140920160531, 0.000395189349018748, + 0.000354039461924243, 0.000309322128759128, 0.000261699269396572, 0.000211850586288707, 0.000160468312893279, 0.000108243891748510, + 0.000055863130913344, 0.000003993583197103, -0.000046719850734115, -0.000095666125277552, -0.000142271402525828, -0.000186009246146028, +-0.000226402015568579, -0.000263029891857937, -0.000295530392174600, -0.000323605716996244, -0.000347020514221526, -0.000365607622249019, +-0.000379263512998934, -0.000387952968439801, -0.000391702472886875, -0.000390603203520662, -0.000384803219892303, -0.000374508618833672, +-0.000359974720676106, -0.000341506152453346, -0.000319446762108148, -0.000294178911934326, -0.000266113076153881, -0.000235686105255552, +-0.000203350785393334, -0.000169573965195115, -0.000134825630330724, -0.000099577998322800, -0.000064294238370152, -0.000029428168903046, + 0.000004586327504349, 0.000037334851940437, 0.000068432637827430, 0.000097522588191792, 0.000124285090373991, 0.000148434669356957, + 0.000169728357656893, 0.000187962083392261, 0.000202977177552243, 0.000214655798042235, 0.000222926398781727, 0.000227757838181676, + 0.000229163803316040, 0.000227196325735486, 0.000221948537710715, 0.000213547707697876, 0.000202157681598790, 0.000187970143706098, + 0.000171207503556438, 0.000152112926634605, 0.000130953051335089, 0.000108007974814302, 0.000083573577884305, 0.000057950921452100, + 0.000031449535456591, 0.000004376321843836, -0.000022961141775614, -0.000050264484742360, -0.000077242169252941, -0.000103617728527825, +-0.000129127285942292, -0.000153527761718214, -0.000176593305324287, -0.000198122592158803, -0.000217935329921142, -0.000235878209412169, +-0.000251820837747345, -0.000265661916507038, -0.000277322604938755, -0.000286753991998278, -0.000293929053180242, -0.000298849141904324, +-0.000301535929224389, -0.000302037282504393, -0.000300418295008671, -0.000296767743750952, -0.000291188041391623, -0.000283800555901376, +-0.000274737753978821, -0.000264145812850823, -0.000252178063434561, -0.000238997508310048, -0.000224769517767120, -0.000209665383215103, +-0.000193855013323854, -0.000177509258936783, -0.000160793959726774, -0.000143873358110243, -0.000126901644954483, -0.000110030073586572, +-0.000093396057473154, -0.000077131717460578, -0.000061354520788796, -0.000046174009079182, -0.000031683467914261, -0.000017968467835226, +-0.000005097430373119, 0.000006870052370296, 0.000017889642287636, 0.000027928525061234, 0.000036966078084084, 0.000044992453647518, + 0.000052010623717769, 0.000058031241355323, 0.000063076338216280, 0.000067174711830892, 0.000070364017870395, 0.000072687036065854, + 0.000074196098488127, 0.000074941358848070, 0.000074985154083054, 0.000074384990732719, 0.000073206600728293, 0.000071512066722409, + 0.000069367495918698, 0.000066834004766232, 0.000063977534092402, 0.000060855931076401, 0.000057526210900478, 0.000054046630064787, + 0.000050464843382682, 0.000046833693036583, 0.000043195256918759, 0.000039588439426462, 0.000036050466832082, 0.000032613010658575, + 0.000029299266022519, 0.000026132051354026, 0.000023135950087517, 0.000020312791765427, 0.000017696391039940, 0.000015265995566942, + 0.000013040101237056, 0.000011019782182642, 0.000009199819153557, 0.000007569854655698, 0.000006131564967683, 0.000004876278267121, + 0.000003794338487480, 0.000006365988874769], + +} diff --git a/freedv.c b/freedv.c new file mode 100644 index 0000000..23d3cf7 --- /dev/null +++ b/freedv.c @@ -0,0 +1,707 @@ +#include +#include +#include +#include // Use native C99 complex type for fftw3 +#include + +#include "quisk.h" +#include "freedv.h" +#include + +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 { // from freedv_api.h + int interleave_frames; +}; + +#ifdef MS_WINDOWS +#include +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 +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; + +#define SPEECH_BUF_SIZE 12000 // speech buffer size increased from 3000 +static struct _rx_channel{ + struct freedv * hFreedv; + COMP * 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 +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 = 3; + GET_HANDLE3; + 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(complex double * cSamples, double * dsamples, int count, int bank) // Called from the sound thread. +{ // Input digital modulation is cSamples; decoded voice is dsamples. Each "bank" is a stream of audio. + // 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; + complex double cx; + double scale = (double)CLIP32 / CLIP16; // convert 32 bits to 16 bits + struct freedv * hF; + struct _rx_channel * pCh; + int nCountOut = 1; // In case we have a higher (or lower) value for o/p count + + if (cSamples == 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++) { + cx = cRxFilterOut(cSamples[i], bank, 0); + if (rxMode == FDV_L) // lower sideband + cx = conj(cx); +#if 1 // Try it the other way and remove scaling factor + pCh->demod_in[pCh->rxdata_index].real = creal(cx); + pCh->demod_in[pCh->rxdata_index].imag = cimag(cx); +#else + pCh->demod_in[pCh->rxdata_index].real = (creal(cx) - cimag(cx)); + pCh->demod_in[pCh->rxdata_index].imag = 0; +#endif + pCh->rxdata_index++; + if (pCh->rxdata_index >= need) { + if (pCh->speech_available + n_speech_samples < SPEECH_BUF_SIZE) { // check for buffer space + have = freedv_comprx(hF, pCh->speech_out + pCh->speech_available, pCh->demod_in); + 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] * scale * 0.7; + 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) // 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 + if (mod_out) + free(mod_out); + mod_out = NULL; + if (real_mod_out) + free(real_mod_out); + real_mod_out = NULL; + if (speech_in) + free(speech_in); + speech_in = NULL; + return 0; + } + if ( ! rx_channel[0].hFreedv) + 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) * n_nom_modem_samples); + memset(mod_out, 0, sizeof(COMP) * n_nom_modem_samples); + speech_in = (short*)malloc(sizeof(short) * n_speech_samples); + speech_index=0; + mod_index=0; + real_mod_out = (short *)malloc(sizeof(short) * n_nom_modem_samples); + memset(real_mod_out, 0, sizeof(short) * n_nom_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( freedv_current_mode == FREEDV_MODE_800XA ) { + 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= 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; + } +#if 0 // g8kbb - unnecessary - quisk_freedv_rx() will do it + if (rx_channel[i].demod_in) { + free(rx_channel[i].demod_in); + rx_channel[i].demod_in = NULL; + } +#endif + } + quisk_freedv_rx(NULL, NULL, 0, 0); + quisk_freedv_tx(NULL, NULL, 0); + freedv_current_mode = -1; +} + +static int OpenFreedv(void) // Called from the GUI thread or sound thread +{ + int i, n_max_modem_samples; + 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_32|64.dll|so 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_700E) { + if (handle_index >= 3) { // Quisk provided codec2 + CloseFreedv(); + requested_mode = -1; + if (DEBUG) + printf("freedv_open: Failure because mode 700E requires a system installation of codec2\n"); + return 0; // failure + } + } + + if ((requested_mode == FREEDV_MODE_700D || requested_mode == FREEDV_MODE_700E) && freedv_open_advanced) { + struct freedv_advanced 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 = (COMP *)malloc(sizeof(COMP) * 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); +} + + diff --git a/freedv.h b/freedv.h new file mode 100644 index 0000000..d1932f9 --- /dev/null +++ b/freedv.h @@ -0,0 +1,12 @@ +extern int freedv_current_mode; // the current FreeDV running mode + +#define FREEDV_MODE_1600 0 +#define FREEDV_MODE_700 1 +#define FREEDV_MODE_700B 2 +#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_2020 8 +#define FREEDV_MODE_700E 13 diff --git a/freedvpkg/README.txt b/freedvpkg/README.txt new file mode 100644 index 0000000..bf20708 --- /dev/null +++ b/freedvpkg/README.txt @@ -0,0 +1,129 @@ + +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 + diff --git a/freedvpkg/__init__.py b/freedvpkg/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/freedvpkg/__init__.py @@ -0,0 +1 @@ +# diff --git a/freedvpkg/libcodec2_32.dll b/freedvpkg/libcodec2_32.dll new file mode 100644 index 0000000000000000000000000000000000000000..540842cfc90a203cfdc0fe724c5e9cbdb0fc1821 GIT binary patch literal 875574 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≤Fj={vDy!!DZM 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ⅇ+GjR`^7Hx#Pp9Uw*1DF=8MV^I=+;x`@ zED|jdL#x+7)L3gapceB-T#|V9a()F&4eA$T_A1c}p?j(idQN@P^}*2(z^NJPozOYg zy?2})C)?)z4(T(YIp72Z^l~3~rsR0q;4t;q$d^X&YxAOga;_m8hk*9JO>YI{Er;or zeL+A~anRoXX~2|*LmdlIRNX%p;s90sT`=ZGKk1r2I(;@`R=aF^n#Sk;xI!j3d9cGE*gTab>EVP4FqH1Pbq&NOkqPdQgvSor{Z&x8SZ+xMI%3 z|EsNU7|yb|-w?HnE+)Hr)brtE!Ed9a|B51VoYEl2Gp!+37b!F6;}5EAhcFZQZ=G_! zA)+$YOPTXgpH~Ej8W-9-Pk-G|yf!skEl06r+>ldjL!Cc0#oP2gj9BWerXg?BTQp~( zd1yqAnLp3f7app)){;Qfr%bn1Llksn-A>%$wvPa zBUi>(E)BowZ9ambYc9zKi)5)@bCF?|#XNIjevgtjKRo95VDPB9B-bpvD<$s*-pI+F z1Bi0N)nnaP)QzO~b@f6j8CT-D00SPmD3X%VKywpMOqUkXMFdg3{WEM6Qz9H!?Xn2RUy z@Ejkaco^ox6hk!m`818EGQWXGgc3{PhJNuVBFjFH_`k(t`1U;HeenrAAj*KJZ4Dp5hTY6MrObofcc5WI&jXwBWr;r6dzd+L~P`vT@fzFA92rC z1NrdVm}DvWoRCM!=cGJJK4;{S`Ctt{gDfQ<5Axy0R|I@vFX3^#Z?P+McwXoYcjE_C zy1e19knP}tqlB+1K@EkL6L=6>M)4rDoWcXARgmC_P1J|u9WfMfl9K{XS2)SpdZ#O#}0^MVAH>aaTsqp8h`Ul=&-x-eNLL*rf12yeX&n)B8K?{MDRPYz@e|4wOoDBP&{V- zIAVlDoV!(~A1EF>D7d=J^-Cutc*Xf4!Ix-YN1A-1PmNEkpQpy2~ohX6HkdUTKFfX{N-ke~Yq;9`|*SEg*Rb@+)(WL*Tjs^JYYazx+QWu+F=rz%0Am4GP%T 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 z z+`upk#9jZ{^fR|q?apEBGdFRoZ&C2wDm>l;d5;@7??TY3YcLB&l94>j{kg;G`wo*0 zON47JsOr7zM*gM;J>gZJV`c3|;lgkSNyeII0RpZSUtTBfEh&$7qC#looWq8Xmu;+D6;_^4+O&~~lPN6Rc zGOMIC`*)40+v$%6IcmHFRpD1+tR5%4S~u{>uGc4|x=89fUaczl^(RUn+iSezYIsNw zaPIyjQ}K*7cZ6Ol3cvX}p7T6-+@M25_GFLMRIA?NNPqPLcn=sViz!n0W~Px!st z@hE$dZ#~X^(Dmq*cFX*B4)po6oz@OUUZMv^|d5Ih}h+)lpCvoAnquQs@_2%ebccnAjI_qV|(7_20^(f;;i`rUnC;raMC>cPA25pi|w*bB*=J7GqFu~*3fJl44L ziJ5;l(KdF`fA%6jHB}yQU5uW!QJWusPP>;n2n-NH&R`wyI9Q%O`WOy2mDx5nWLa*~ z+wNyg^_Kk2ij@5V)w`aH&nuQU-zB-;uZQFvj0_%yZajEUHuu zKB%d(a;FeOViLVPv5e z;jh|8zBG@C_h>9yu~M*NyyM2G4S-zzfLii!S5`wy>W< zMbe2O`1vCB1EuSMLDq8CtRVigXt_+;ErCZn=6FF1I!$~@+?H9g~VQ=E&u`3)ua3CJY*KoDr)O{!6 z;JFkn?5y71b&)_%gAdQIj)c}>{l$Z^FQJjn#v@i(PhY-E|8ge^U)@4x#-vj_9l*ce zd3H^~F`KoI=Cpwa)yUq=a`4MR5qC2_XFM& zd-SVKR=&qz?wRH7tLNy{9mwFMqUoRFb0vYba)_&|D;SM;nBjFZ;r@y!`zbzX5qXrA zOT5@*rSbhp7V~(xH)G0a=FqJ%YEMp3&u=Xzb8-y~bAar344-!lzNSg?KuX0i zmH_yvF!ZsZoGHwo&+~va|AiUUR(#VJ?YtY_vPS&7H&_>i;Ayv`$A0CUE=De@9=bz1 zo``OWEFCkHJHbgbfVZ8GZ=;SP+pCYvo=5Lho8Ea3oY83XyK!pJSzGk-cY5VBC*fsA zQ-}P(Rhz~4j^oLeoR(ShN?Tr37)u>5O_oL>Iuyk@Ka z|58*{&g@cfJ00Mxyr`{qLiMZ(Fa1_pv$YT7R+0ONPQkklwx;&$kx3L_TwLqa9?S`r=*ROYWj0y{;p=XRPu0PXqMA#^gUV zIEM%L96OO&T}1wD5p#0BFgp^QyskdE*TMJ*GPvWTIy1c@=>-zV_e^HS)l%kpePj+; zWBB?^vUwhyKXLdMUs>-&HVzHnjV}9vOz>SelTqq<&{}3Hc=Gv|ssA`S>#!=;t`F0w zpdbcfiydG$duAT&!0ztA?pBZxF%c021Pl}r3vAgFV)tuzcXu4?y@x-(>-vu8m9v>W z&wAGS)%ta2hl*znj^G#1Wk2&-7kH=|WNf5^yAMZS+6T>U6?8J?$s3TUnNQ?;8&;#) zeo9TQDE~Mz5lz)_>e77`j~BB=j^*jaO4NyO>bioHxCc6cKkw%`XLE*5Sh5{^=sCl> zQ1g>`&#r(yD{_I3JEd{Y$IzcoWj{kW_3(o>wdFACa1c5STV=6Wh_~w^{pL8H(T-e< zX6~mOCjM*vpZ9IGM{-lN6X;CqbO%?wC)s7M!*AG!d#a!IdP)LXlw;i0t@+=qQ@T`c zk6trGPplgXKV1i1!v>9a9A9m)U(E0ba;KW0MX87G;s-d~U&+iJN$n89XTL9Zd+f;f z-iEB4kxIkjKkzi{wItT{XU=p5lNiU(UXE6CEqzk}y2k)!AT=Il?ufF8a*-}3>oKJVjg8^e8G7+gD(Y>Yel#DR5{kym5kVuo8fkGTbgut|FN zI!OI`yQgLQXm4hfWoBL?KIA59m&$Qyn$R(V`J7K5n9H?4NL&v`AIA5Rg{OTsdc-Ag zo8e$Choz1?pU^j&8E${F6S_~x)nof)euc}MK>pov>FUf`P?LM*OE%ujqD#>;tft@d zhJSX|GfH~v#bWk=qr}1Wc!C!n0xSKA9&5V3{>NavfrI4EW8&b2Qo%eHS(5$Il`nsu znD-Kx*FQMArqs76c<)sHyf8Ckh&u0Lw2BWEop68V@AI6e3f$xARy;2f{|vVlYUbyF zh2P|?J1U+xmvRReL3iT_uksk&SOJ6gR{9KcLnGcAUu|FA%+>#yvW5Ky`>d7ZXvO`U+XC?5)C-4}tvffu`mW}3aPSw2sOt*I2IS38^1@7jBU_txv!+KkO zHL1n(cnYRko^xK)53dV3SIxxU&5OW@!kCSB>9wle#8xA`5|<#gqtOJC*2hoxw3!t}aF2cseBu5G(;RYeP{lbyx8 zN0#Zx?dZSW!JkE$9vkcFI_`R(VXwhn1-sF+@PY-RMG92O)Id{K68*1@HSF~T-7PU1 z{IMszOCoup-8JVAMbYDwKnokH;4ugPYlQ!EsT8}?p4t1U)Uk9OaFl57pmS+xX2C?cM9C{JtyUMJ!;8&6xvBfrC8Ls%S&Kr{33>K*Giqbitn}qn06&ntIU(H zGCTg$vs^>aaF16^k873rwqjrC6FJ|g2>RV+%z0kI?3+eq<)dy5(=XY4!@GZzJM93v zxcA(*hEk!*Ex6Qn_+{)QmzICv(Sx~9UPv)lywLoI(;FR!2bs!!I@HA5z!n0)KitSI z3&M+4PBAk|EH(W2=k|*K1%G39>MAmr8<<&7_^oR2`!0Z=D`oOy>gDXmmgUoG)2~1B z%k=d{;~EA&@LjrIua$DQLJ!XASv1+LmBZ0p&!v;!w8@84oH&_U6>W(IJo@Vlyre|=T zJWet5W~3M6@YFSjgR#??x72s9$Lf^9NT*=+)1(va!OvZO;rP`6XT7x`*NHllvxv%P5 z;iRm6zIyQtBDgOiz!w{kCAk-E=3eU3QmySAd&>jW7rp)kv}BXuV8<$TW_T$Fe}9MH zXd%}NdPwivn0$%ZXc7~_wgc$b&!}?;UsCT@n68=kY`*K;IA0N3-V$}tYWjgUY(n33 z6RgaQ{`4B(r7QShG;W_5*l$(8zx3_lc&F-0%cm{oZnXpd*$x+!1m^aLjER$0 z?>{a1{fDE;PtfLseAlkTH=#xbkC2}xa025ybmks2g;BsU7!BRi~8|HYthOZOue1f zrOh9Dz0+WfG2EL`=*``!jR(*fKLi`Rseckt`lSg5na(zxrK8H9ZwX+)2c<-^=SO^Z zFfn%KZUxQhEuPa~^c}unNFULg{zR)}P`7)i^JE|Nz0>G%!>C)A(Uk}AzP7@`jN za6hzLWtpw-GaE<2>3D-7zF}tht$Z$XUSFBwW{~MBjl9|wEk-x`PIq{MYIvEBaSy!U z{QBh1fvFu7;G5fnR>GOPKAd-TvwqzpQj2Tz@5>p*rL01qAyLve}T9AW;rm*y=cTA;4x`}FFSTb_oM+8 z;nfOjV6%8!zGy-FYvpp?cPl&t|A=KCxWPW|qi8gSk@F#;ec6k~c?kX6eEP1^VDvxf zq1xhsaIr4E{R2JI3q06WIfL`aUTQ~wn~pbVKAFZJ!GhkwwJv85<1p^|x9EFkabKQB z1MyETR;i5gvQiZIbsn{N=hp?&q~BX#l7%pzd-XW_lL+vkK5#kj z`8#d(^g&<9$oQY7wKkYLJeJ=06nEYTyux1OLiNN0#qM<1NODWzR9vRYCk{oJx`=GY zW69(Wj?3kI@FrS(n)O5HGYGDu2maK`iua$*ax42r(!xhQ;icQ70k1(n*qXU)b#`dd zTk`z#9`5aVpFHL~a4BA7(e?qyS)1#_a%ovndYpQhx)}pDm(4zcXgKMA+AjMk_;H4i z(eQ}AIE&|hjeL;-%+>kHyzyj?_JgY^sr3080Iq3z20Tn(D&O0AX1;35+x$Vw*u0C; zmRDycE(6}@j>nNbRfWo;XZ0oDEs^)Q5L%_GR?~-vzM6Ww(K_Ie5Bj!V@KU>_EiZPF z+uay_a~^H_gTiQa7Ng@j#942{T{npw`G)Wu&$y>c>!!ELEnliJGCMwqy)Jt9n7 zOdua{CB5-o_=Vg2yGWTV4>S-SWb-_tFLXk)xeCs=Fz-TXFoQw*p*F7EH5IKUZ+AZ3 z%rAKWHi2{YcojI4we-AI;9}Y z9`#>G?KwhESOHDs1Uymg^l`@|=}yab{P!k<-;Dy3Dg*9#fqbd8a8qFtx$R*5ao`9t zJ@Hkzp4aEmJF01~ZQaU)x5{)R`PYiF$il{rII1evA5YMW>2d!= ztq@yCx$xc0B=IrdL;2GsdUPpGI{149xe=qudm6z`=bLDpa%2;mu61|@-hUr@?rYq$ zo8hpl(uX|cXWvjqHm}OuS)BLJ4ty&RZfi1H{W5TdA!?`JC7EGA$#C{+*z4LRmaFx> zjQ@95GG3SQ;DxnJ?2GT88MNXw=3o2}vn!He|AAV04G+X+t=@)O^nq^Z=p32Z&Tx*WNebQTxkY6$v9(oo%@!Z_^1dR~=6FYAF%-p?#)Us0Oz%uC# zMpzG&w%`%{%$eyAhnl0det1dy@@KU={edf7&m%C;ChSeAf(^8Rvh;QyFr1xecq;4U zilH6qt!ylB!F#`;X+*%tH4>pxW8<4&;9GQ z&&yr#iI+092k1_Eqpw~;J!%N!3-G{~G9nu%>){fu# z!yRQ&OufUd$uq&-+L;(E&&H3e#=}yL;g0a5YtdF-P#23ha+Lz8Q|r-DMuCqNrjH8d zeYKN%nxDDN(Q*ga6B|~(0CM}R<2U4P3hT1BFXhydqZhfXy?+GX!a9_%@?9xH1Pd|Y{F^WlX)imqz4w6kJi zKKDxgcRj&b-8G*~t3+-X{IQdObFw(rD?(n$NM^xFoQ0yw*qLX!%jT2cQJ30&f?VNp zWG`)kmo9~0^N!Y9OV;!L+(!-UhKC{@jW4^KgAU*YY6}Jvp!*L>mALDvIXh%&)=bWy zssD3O{tlm!&ELV(ynB zbVS=|1$QkE&hZmHTRr+X7wXI&-qp6?lI{8Z0>DbMz;+5y-`OQS$cpbGNG?%xuiWnB zbNG&j^fI=5-gP{`A7+jsyhcs5AJug;FA7Z-+NVL}{=SlZS60Z?@$)W#?=>d7GakOG zJly*e^2Y4RN_dAy&n8!wdg1$J>1Cz-^nX>j#}@MaKO~DH*fOjsIl`BU((9i>|Fnkw zpddO8SLT{P-np~#N#|o`4m!`EBp7xSS+xS4Oq1kvq-Y|!hRgQ$q;Q6ho_eETQ;*~nvecRzSALj_rt2}Q%bU;CkNZDfo7&OT4MvO zXA#)|_4Lw3m*cH3MlZaMIkO-hhd|~(8SP&Ov{D+q{42@qY5lBDdWZl053TSNG&uKE zmkKA@62>OBn8o?Hb65FP3_je@6OR1#4(fP0|RwNqjZnE zCYGG!chab=(s+D=n49q(j;jb45iWN(sz~O{WM(n9vcF;WIBJF1SiZkSczGPrzg3}T zmY|m%Ouyxzn_3&P>fz8A;zOCL-5FID{n{Qh(}&?)uX3lR^ypez0B`| z=Q{|!mA6Ljw^^6K_o`)^IoM4ncJPZ_-c388c$*m!mcV;L-ZEF|P` z;J)k8=j_wuo>}NohJxc?AbaJc?B%doo}pG(Out@}@~^0)6Sbqeuc4*+2JV$i9`tT{ z=r7c`#dzHQCp&+L2ahcP@0E%#H4W~eCU|!ZaIWb*7Z3C$AL;8_ql*qUHNx<jK< z?6v8J@2NkWa8de@7R;Yz;eO_T0j#8_%0q8?I`_<)nU5vsHfAT$53qy-8oesMzh-z} zBgpochW~7ZR5Z{>t2nKPZWo`ydzz1)D~`LODj60{s57%od>if87MY9`JVp~^jQs~TvmX5fup zPliW+eN6le>PbWR)T8Ru3d!2s!M^Co>upyaKO5&S0u;$rnaly-wEY zNOTp=@ozW5*HoHbB8*ug3JvmgDed?fFy&~q)G0@@HujNs6AQK$4?dM?V$Wo|{Nn!b zf|ngaZ(R-SIDy_GhniU*FaLMdyrZ9ltdYqpz;oCF>@f=8M|aKa_bR76B-=Ag&2oK6 zo=FV-@n3kV!6yGk{#sKsDLr))?}ulhhd#^iUW1u662Ev$a0(YZzO~um(F!cTgudXH z6ZPvWy?GA3gB`WV6^}qO_76P(L$WpTIr942GM5_kM(-_l@rB`73Nly3OD_Vm;Q2e5 z{zI#oSrGB%F}mRO_^_*4(X>lwz|kTuwwl={Z}f*?Cad)V>MG7hL++|nZD3q`{73K5 z)_;=Be2R>c)$lMUf|q)OC!T<#|BszKYh^s?WWzlHv#zaNvKfyLD1v;44Pf5Y*`HNZ zBOixn+=P6`_0-vE@EkF4`ghn-l$o0y%nk%Ng5%uZEzpi{GBe)LUZvsz{jRlpRgAg6 zqF!;@1~g0UO?`^G>Hd7FOeji2Yf7!zLtQ+snRv6Q>wi*s8ktOw=ow?smnJaV9H#c3q877H z^7#h&>BTh83SNX#RheH#%gNi0;i>6D?rAA90e-Ho#37hnG8D{YKFtEhav-HI%&nRrf0$u;j*;Oskrg?9ya`b2+}K;Q9=XO@9CWPvrcLmC>u zV(1$h=?~Ov^f))<9tFnXZE}FKx`+qqC%G@b!GTKXo9=f(uh>MIIB-An@&`0e{`8}R z@UJ@Xy;Wq-)*o<=OlBB6G!u@>Gr74In*TbQaDORhSh@_BL|y3uK3GVk)~Jj|`H=N4v_l#&sQ53Cy%IkFUzKh+tC*NhX$=Ve$YD9`up%J5ti{Ax02`Z z8XeVI`MQTU^Z9Z1@+N>09|8Aj2o@~Bl;h;qS}GiBGQ9mFwd=hE;uXZ_#+@fB>Pe+!h)d);IvtgO6pn5f);;KE(- zLN&era4_hS{K-d|g|@(%`jMdZ{~LmQF^SnF1Wm;yeB^h`K3D7XyAv39GIL}liM==Y z^L)wg{7*7__MBn)(AqeolPtn>Z_Q4mU^I12nBP13b?ed-A9OzS4X3#GmxG)40E65? z9Vv;Hxh*=-v+{wRZeYZtvSY5iEvpccbl`=Q{WxS@qdc!3L{1Kkf1L{g76sT;zAoL67uCWw#dj?ak;RTEfd@ zbM76;W0}O909S?I77sCgJ1`WV?%XAL*kv#Wt+vdZ{}9bk9z3Tnm{WH!m#+f{dkD|m z4eY!N_+DLlwcdDj53^^Xjb>_l*y+hEKsMTw(P+C)&@(vm-$~KSirRQ$X2PH3r6-(> zruLII>t$&;I7jaIMd(0Et21ZU2Dg07ZrcR1KmSRdH_wx0Fx5hqCV0*a*~H4X|Cfk1 zFr9oWCxtz4ybs=J?>)%&JR=>t<;2W75X^o*oTx9p_Wa82$Z7Pex5@64&HO-k#i`QT z!>bf}JF-WAs(F9@fQPI=Z);2c;ZIGjOwI9Sc0a~<7RCHfR&ySB5-xQaIA#hMaH8B` z+fsaUx8YiQ%5irKp{JaUmS`vM@d%|^$uoSODx=T(rIC53nto#Ve0o_$$r-kR^Vkf%Mt%CLmSohF#9L-CFLlI=(FIP3tpk8*_F$-jmK~ zq+OVyl9}VCaXx2*BaCI%a=>5Sotb|CzN{cLm^Ns#znj`S)5A`U8${1{O*PMeSXdey zHHsRM1~&Oa1>=-#?=6J$S;n57CdzQH)9irF1fS`wO&pko-^Q$g?cj3<MLT2XCXsb(P-K zJS>?RIp~h@CwE~l$vy6FTk3Q*JlosR-!34F<%$%&bqF|AB%03B8ao|j^Uv@@laNAk|>2h$tGT#$yxYaclMX1wUzo6dB}!=9Yc^eUFzn%AR1 z4B1B$q{Edv!r^+Enhcd(1~P_XwVMN{oBBDkp0Ht`B$dHsGgWi{ ztC=5{+Ce8@<6v&jZ=4qRoO*-H4AwW6ug@$zmmN3Z++#92?-==B^gnd(_IMw%$wP{P zZ>oa#a1@wriUz2K-a?hx<8!#^FBfp8IC#)>^pDQ)C;jlQeS@D{fwrm%+-^1*Szhc| z@+4cdhi-bGyBjv-k7P>eA5Wvzh(Onpf#*I%HFJvS2dL%E(GCQ&zpgwO@I>kLqXyCu z&&}$-&*zbnBWa3Q;Aa8Xn_}oTo#Fv4bmw(_pThZO_;eCt4r?Sw} zYO)1fXgr?P&Ga8D!R~5m7n@&%+aCrG{Q|A554j9w;LA&bu~s3cARcbe{9b~k@Kfe) z-^FtN2V3!u4OQPJRRN3Lgg?q1E{lHs$XoUeJS7+60cSS{{bL<+RuefBzu_+zOXOVe zoLcieRKx>TM{l`$z8+kuIOo^~J}l1K)-@DeP)BB5k==4%melw1Ty_cW5$*Bf4kDYUHhwE16)BaUccvPj&x>r07b-Ic`pZf3 z!`X$o`_6$a6<{Yt3v>msS3_6-+HmAW3IetePG zM`i7iTpcfc62IRmD?BOL#{GA+y8gL{!m~9=r z*Zt9|Z$~qoOy2fN=FFNV*F&z!G4z4&$ZBFIN7Pa>aeC%r1*SLk-P|(dt2)wqb_KT| z#a!^(#I^mv;CNR{=zEJa1Giep^Ztp~V;@?Rp7=_;pw+03mSF@~k-1Z*nMA$;_{21= zo5y{>1WZ+8 z-_|FK$=P;$d`x9mO0HM2wN`{&sWRYbA=ab;z0sxL#J3v7TH-_h(sU&Yp zWk2Z~$+2ER_yh-Y7moSdh3S{IhlJPmYzvvHqp_dy@k7uLif~{J%qEsF7l}lUC`-y*k`x6aRA*; zO`YB0-~;yNS)?bf!t_u@U1L`T9M;rb-o6H%7yc`>NY)@U; zTsJV~t#|qffaqDmw((+mpqtIhV}md%4<9 zw8&&YZzh915{=DYH2Uq(k%f^9)Q~)@5CtzY`3)DikI!?rhv_McJMl~t$Wn_$+ZW9q z^rsTp4c4X;?eOnSP!o?OWL^>?40Oh#hqYx!13<(8PZQbeBcjR2~V_I)g19xRAjcl2WDKKjD&e+R){nz z>KK?$AN_QLiFf%ice-K z3;s}iR@K1`i|c9i9r5$U!r`Z(W8JLK)1$2$A~6%GZCx|LGSko?Wx~5}(b{j$M+Rp( zaN0WLq&*?)`VQKlkMzgQ;4aSbKIHh3OMpI%{NVco&0GO==7X7mTf(WK_pzPnVZnn z)PkvaeBg!avZLw@GfOa8Jjd|{^d_5T1KC%x^!)}o*X7l{MLg;4SCZ*kRf$~wf@iS? z?_e}D%}(Z{ew^o7U?=C`*qqsII+5JDD{9@kpUKErrPeE$1iv^MyV!)O$1Uvmo#2M>vicawYQfF7JTv`9#mgJ+slnYGy|V282+b>frw{GZmx2 ztSf;VH={pV!i;~D9GLI2IiGsoyoUz)7kRE*$&S5c?QZnq=dO@DmafJ;8A@hCGqfY) z@dLk8hrY{1v%gGBDP96il)Z7T=^C_}>&PgP*+~>Z=KB$yOnfkaSg_M$%nhSW@0Bv_ z>~*xJt2v9;=~YwFsTD*wSPNV^lir{OT7yGq+n2MSI)AR_#{K1CdSo-#El`hgD=Vhi z%?GD+<>$qK-MVsTe}PMIM_Yak{%Nz+_}VyjuNG$?Q)T9<$LMcz$d1VbKiEh9XQ&e0 z8FE7Hje0>1E$p@B_g2x)$I~O-A~(cKdA8#&e*WWHxiW9?4SCSdH^aZznYwlr zt}q#ly#?O+6{c4mUG@<5?-h2v&-ssBoX7MVKhe!D)-$%R(vOcLm-BWC+)6OLMn|$n z>f^tNlT99xEFLmV){$YqUz;51g!Z5?_u5`O8?NNo+2dm^r{lxGCl`zECWk(^CO%4c z`qJv?H?!E;<_(WzOTV`ntm~4g!v!1MMW){xaH?WxvIf)VZ^P@|ih9@^{aU=#`e7ex zQfqegm1Ra4#MzmTKY0>4CDWNR`p}d0Vh2e_&cH6-m9D%?1@Q6)(WiLh)mW>Vc@UfP zP1eC*)nb+QGkaaaW7?5=mO)>11dYHFxCux0aXN$B)kE_dMBYbRV{pz41CJJGq>#wi zUPW{)-G_O;FPc} z?7}-5g1Zz3vpS}dC80Gd`P$UONT&CC%Zrmzt@hzyCHbgb6UaiU&T}s>u>(RfXYgH< zU#K^NL(oG{;ye4t8J(hAJ|dY`+r2}AFe zh(^1=nF~T@bw=*qoWu;Bf8G;;CRzM|em5@r*R$yi{aw(@4139ps6W1jApZI|Xk+ zCt-uG{f;)^P%!?P<6tFS;at-AKHShMF}Hjy#QEEb@4Y1%6svOebitK8;Ap4AZPlTd z+^N2AR3DG@4zRAqDmkTOp|504AF0XPdd3s*=}xLo)9wvUNBUrViyb=emPF+NL3&tst}DPc+R= zc(AXkW_}m@Ezr1bCGYT(sf$D7*@_H~y5#n}qD~LB>U~X}Xu4Wh_J)TEfXh0P9h$!~ zb;V0R`|c(Azda>(O`{plga7XdeeY||`V%}wDR8w5IiL6G5zC^l%}bBllRZ2G@ytfk zlWa5b7y5@n?6aw)Ss#a@pA6$&KW;_e!Mt#R{^1jMy3VXn4Q^(ZVrnENN2>4@m)W^G z2@E}xGaXJ3vmPD&3Vab`;CZc_^><`wFOj3b*!hBNC-zdq2y{*k7igd0A>V5!}! z!eAGx$kiN!hpq>D1YhM>zj0{iYJ!_oHnXc_k5r4r%-%j+*-hJ8NM|qL3w7V;tIWFg z?8B7sZkJ?Mcz~v`3wb(?6rW}l(C?JDd=eM93oDbCMV4{LSZX+)iW3jC;pvfJrK8Nh z3m!B^J63iO`0@d@=&y}n%mHwVYr!!(P`8^gD>JJ)YGYH2Ib=Kh;t1a7z1$NEg^EATlbs!5X#Ra=vM<#Jel2w0e~rnZU!=5eSOhNax}}Y8Wwb#{^&#%*VCzxp zfObpKB?)?yDa;TJ&?beG>k&c^Gn=_=hjcY>3Y?%fz1UAOdW*u5)dTlY$UW`BXWI;3 z{+JwtBqgPIG92brvTeudQ(GR#cbyjwuK?fmc{ut5++XZtxj7rZMkwBk1bukAD|(7+ z@TB{AH)DBEMzhB(MY%b!0~v$9ika_s;d>T44gTTTuSTv(GhW=_@mR;;_3ps= zi?Xi&u~lR50$Mm*d{8;mzk+C0+|iwMz>B(54ZN3E2b;mK{u^%D0gc-Ra2QW^L^Q&u zS&q3q+RENsvZY+ne@p@=SuQ%3sw?~z9vOQ4~ItN3Aq#{ax)0? zPZ~@9UMTrcW4Ut{prP0XmTAE^Fb=*wlX;*qwJscvz!~lCc@sm`BEtOf!Zbzya2GvS zO>IQPHgKnSJRuXvmnjGKJdOMBE*Qa2HQ%UJ^g8p=IeOu_>yEa)Z#FsqWL|lg{Uw}U zRoUEQf_{(lw$SviO4W-!MGtZZ>^mL}+70fE#ytBo@N)avv6Vt^RvetC3LdXiW{Nj( zb2HEYMvzZkOL;ojmfSo~&h2Yvy#KTkNe#HmHi9FjfCp~?+xlv9wdS5uavIk$?*@Zo z=jVNxPR2!dB_ixU=FqQTOr4k)|6|U!F-B#1!0Se{-|h)I^JsRVmO?i%2JOgc$(%p6 zdC6!NJ@Eka1e?pkpOl%q*Od&_COcXga^n{E%tl?Pw8+>$@v!E8M+ zpgi~1EbjHQaEM#+pY4JN#y?u0eY|#!nZJ`IvM}h8lEB86gO!%2C-uhDum&An8@PZ6 z=wD`n_dl@gvLA?sE=Ie2FGz*YW*1ExbnUhZ{xf_rxl;Suv~j>7jvGY_gYIH>lR*CCs%aBal*6mZiZX0r}xMlI6rPEE-m2w;a)06TY!Q*Vk< zORQ*qEc&DpOYq=&8g|uPjPV=ugL%oKt7BhsPWFNYhUaE4{OH<_&omYu$1nT!BM)@S zjoD4Sou0dpa%@FGdN5yj+8~RoUC-RUPw$(87Ssn_Otkd7SSxlttzc#D{z3?)4qGMakO!b^IXyptak_yWMOw5^m3hzv8jGb7I$fZ3flfl6(bA`P8 z1?Zb5kYi-WoR(@?TP;mX>$w5#;AKn1&~?%QxRUAP*I7*OF!O~R z5SpL9Sl6@yu3-Ha$fBsm9HvX2t<&&f{bbIL1us2<&uWbPM$aLiZ?RM_Xc76|wdpz6 zq6<^ed~U*LKZN_IDjdZMbll73f8~5kkB#aY-3BdTQ+n~qN}cY#xWAg`YU6p|HR=qz z2~W^JwZQ`_u|pw-9F{ccS3e)TP=)xNeR)1!^eqF-9fahV)T0kx12&b2mZ+d^W^CWx z>k3Zc!!xabKHu4|K*vS!IGf>hC&TH*S>xJIBR|6%{%&&aE{}VYUhz8(R5~gya92_2 zJ0t1KN}&fjV~PFuLmPHB23_Js=2km>O!X0X(>~GLEu_!2n7iN5;W=~959hO$X09H{ zcXN_VoN#>o>*aD~Tyu8`+GUr6-|=H7b6*SICcLTT&>c1*7sOX@^y5E`tS{5s&rJW2 zdYM4Rizjmr%@p=2yn zGc{7^9g^^WE|%}vCE%^8fKH*aITMiCw2Hk1gB0oxHJ~?o=Llx;PSnb2cq6x|eZH>e z-MLTJp)1*Q70BH#gP*F8Ud1h$nj23JS*(5}JeEGGG#Phq;0l+L0r&uK@G)uqNPDoG zw&)wiTi|-=8^gI*qS?vu*K+lF2mIvGrstjcs1z7pF*3pe*jv6HKj2=p6vxOZC&+-q%Pb=58Z)hyg|Kql|4Vfci55GS^j%v6#QZyatunEsrbPt$1RD(>J5XeR5DDc*qZ zc^|lTIj!dSuDSj0kFH(O2gHHXO@OE9Ouk!9?aNqSGy-Nm%?k9HWt0_9?BFkb=mDDY zoeZNUjzbeXiT7qb+Rw7wNekho?t?ph%gx2aUuaqI=v&md zZFop+z|a)ViVd7vPqNDPTRmG3qsQ(ijkeh-gOh;~7le0?kQx@xgElIfyqv-CQkU?? zh2slYNY7c=awB*s+Rke10e*vD2DbV?6Gz<%_qY>3@MyFRtLdc_g`Kf#-J`bnMjbg5 z`Sj1rn}IKUMQ>A@^Kx0f@VzU!YERHpC%~Z$rnhOYv~|sEB>OE#2UiVjYCGOdAM$lt zqdlpnP}|f)ZEE0Mjgpm3R&eLy(&;*7@!C}4Jt#mef1rMMs!8U;JIk7KPV5uFCSUEL z?6UV6Io|H*IBZSdu{Nt$F?83P@E={UCcSS6?)IOyCNq;eeVyb%3Lq zpcN_g5v*hYxlhN?`|Y$gwOxjn1MU3f!}N&@(H%4)XYQO|i&nSD%9Qxs9m(|gM@B?9 zc%brV0&U3)a6ubYo%(r;T}s~Q-*fl9SMetnmLaHx;J`Y78G$f$zJ6iOpG>+D5Shtq=25jAZ(MpO>m^VZS)% z%7$6B3U%6_bJY&r&~G@bHPpA7+=u3FgYV468a2}VJz`XICk^|;wCNp}D=sakk@+vk zc8@_TI10Yv3+FnLp0*Tu61LWBJ?4_JIG)`Eb=U>7(8N!f%Nme5_lb8|XiA~=zC|jNtN!58iWoih#ol--Eb=Pco2Hb2QquC`TTZh9* z;;me!PqNrCug$>=zY`v&Hu_zatb(iXw3Del|Kw@WH_+j3hQIp7jMxU;8gBzVEt)^D zJ_p|2bQPZ@`%XrJ2i(r>bIrY;ZOx5?K>N@N=Z% zv54m0XhJ^C2(&H{X!VaV7mi_1?O6#wC39SDc1avYqcxH|&<6Bc&+vefSsL7p3<_Vk zqS<6LEm3#0s*3(+gc@0j zeGBUS1kSK*h1!* zK=_=Ya3SB+keZv&X^z2ru?o)Y23Y%OefPtc(xaEN&=M!(nW>Byr>&gxHJuEq%ABLG z=t*be@wsDtoO&Bi!XY)-p+3Fpa=qm~4ew<t#qpSoK3RpYEeGG|3g)(s+Na`& zDrjb1lY3uB$^Rf2k6l#mu9T}YW5Cbm$?QR;4%CEuG-uyXYU46`_9(KBmQoWvz!>+S zV_mI3{nVAa=PY}<`Z6mtLA%t;ipPLFlt6xmaQ4Y~;{)8Son3I!(qz>yX2`+p7wK-T zKcEZw0Mpr#=#9T;F1tJgGsJjuXuF}A{{#N?iW#^A?|(nZ`GXx=m>ljN2XKeK^wu4~ zR7ZfrY$MyG4;kvo)UAKa)Khq-OX)Fck$sX5R#S&;+*KBHS9G23*-CMJF*-_DYR7MK z>Pn)+O0>>SJp@*{JXgn)RzHg~k%`7~jpBH$F7L!WG^H^z8Mf>=Z)j|Jk%pG`2HM!t z^z~^HGfegj)kWMq?@Wea9(u3+U}_ie-DOg5Y&aw9wZMCel{flYIOBEnd@^&yLW{YJ zqW=40^oq~rQ3xlJ-t)P(&St;7vy~k<{4$#y=?w1EO!D~hpv})ghtU>{UvM9kLlfSJe2`(f z>7gZun;u}1{PfdxJh-3nFBzL|) z(eLymi@Y9pvI<{WkZdVm&GY0+1AQLY^lbcQP5GXRvDc&u7~p;K?nA7XY>K1(-X^ar zSc-q|%#PeS?9SVYzR{N+uxn6-tS> zNwvh6KXXO7UF*chp`qf?tsoJfXTBIy{RO_kMeK#v$uVzjx)=JjTxy2V=p_5a!jE#*ePSMwzMqGp>6Cu}f$wnQ$ApcY@%~>(b92M_1KP zAs-CA>P>db6d^ybu}&_4S%<(jN|1+ER_q@#hkaVUcnmg4J6cWCJLQiw-X^^hc79t$ z?8^2cXILlkxy&JQ`zN3^EUu`@#idIVqWDhJ$VBrKrMD#NL*MD_Rd6#}KSb*@y_&Ie zb!(%=jSQpP&{c-^=aA89S%Px>&PH@Z|5M6dDkUrlGsMAVYebDJW5tPfHN@t8t;EWd zK>}X_9{2I!`X!7h*LxYkF+oO=QvHlYTeh=GPi~Jt+2bqF(Cq;~ z+{V0r08U^SePtrOPZD={Q#hWA)R{}{liaF|o?t_L*V&Wy3{7Bp{@x*d)tUGD^b4nq zfzB#3?gRSs>SFb`DdOycapW15$l-Zq!24DauF(-9H2-MPBl$M@js?I-A4)B5 zOhp^uF6M-+;QqRSHoTBgCM=(EV5barn$HN)iyDu5UB-X*&Pc6MTHKqo6wF{0I7|Q< zxEQ5+v1oP`n7O-?53gPnKrb*8Jja~H`{4c9ir>T8n7Vf(zw>OpYQOu`xg7KshW;-z z*@(OQ+2kff<7)xp^1Todb7j0>S3KYGP~}E2y9#@Z(%|ls42;?x-NFexSby}{Cn_3G2k+*d%4D`FXVf2X z&3O7rlOE3gEk}H*PHx$0@FO=-^jCXvX+j?Xr!BU;s4L>yrF}_}YAs=9azsruh5{zk;(Afw>Ec;n^fHO8HmOAXt5 ze|7e=8rr<2hKxqn{Jdv)e_KB;X^_{}|fT5q;}oS_w1CVG@f=pZa%bb+>yn0C{{Wc8d-7PmOQ|(_D&TVLC5zYVPslJrYL*c_ z{wxviPaWj_kL3NkNiOA8_EG*oGq6hD+t3e8;kL4Ob1D47FWD32W>l`-O>Z=^Ir)fw z#?`zrM%8|8`LmJ+vjBClkSOz|jA&TAwKzC8MeNo>B7kBa4$qYi9mi?2~FG!uu=|GhKYBC6&P}7Qxdb(xc|+wc5X;|5>g-NL)-t z^(1_>^>j1y>U6hY`De-nc;E}1>jTE55*@|&Cl$oC9<9Mf^C_ku-qbIL&AkWD=q#>2 ze?nf)L9!f1!~HBk=d%v)e+IsWmik89-)d0nSh6(I(Ja{;{tpfsi;a`iy5*v7T{qFA zfRDm`E1CN8=KopX){3AHNKyknk4Kkf;Vud$ONaeUK?&S(tEi#N;Kdr?V_d< zlf*6raGHunpJ5Y?amSh&=i2W#>c5voy$u~j{*Mu2o}3|ylv*Sj{qPaoVcfZS#Pp6G zg}HC3@wL0?d%7Bz1450Wfh+mhVa8Ga62`daIb?;o8dcBS(U-j0X{`UzUQF;9Eso#m zAzlPN1^Y5{23ym6c*AQZq8mSKwcqgjb^;?)>0zocubu(F zY-ZFL{@fT`rz%;w?s!ROkh^vSouaSUx@nPUm^VXgT-8o|Jy?Wy(^CXy2XKa#E9TCk z?5VNjsJhcH)iYi;Ty1>Vl4ZEL9OUkNXcTNvRe*bNh6jl@JEG_{hSOiT@ZGFaj{1km zFIyKy@Ae$ObQB(-_4H-+jOL>|b0%sUcdiTF)vlOPWNCRLcI8$5`l=fSJDE8TQ9S=1 zVt9ICQC7r@dO>}}?Z^?r%fXxP=>}TSIpoAvWS-ur_q|-i__S-FaV5Ti(cQ36U%F_o zLN^#|GFuq4M#mYIo&+m(k47u|LVu72bQ_&jQBnCwIUzmA>;Sf?r?)EwTvar`2CWVx^-tLgX_46 z&&zEE`ZGMw5%l&u^_P`m>B-}n#~#y%{HJ!@X~WJ6KQbBygW+}t%WJQEaLh8c|L03x znJQB9MhR_RD{-uB4Rn2L@$WxEpLdL!X5jlcsBa(LQrG)Na!1=L?RxLxt_?-2_L)7f ztHH(hfMdqEw&yBJN!z&#Q@QBW$ zcAgA0i5I8?D)k`@4p|s+<69XqSA2~Qk6s#~`Qt?1pBu!;?kh#)E>BVF)LJlyZIU)G zgxX|pO!(kteECzt$kp=>~UzpJLxJ;*V(6B_FHB|42^}? zybL~)Og{f>dIEQ9Oc>c4&zR4CNi%1!2j}}hzjPG+?sWRGboLIOvOaWqsCTcm0ZrLk z6;CG|rKIiqtQ+qe^%Lw+!7rIkEuY0~JWom7kOg+`FR@o!$EU#GO9I1efd9D)S+I5Z z=gHjXDtR!jXw5Rr{ZHsrI`f%gjpTD}#mw1bg>f)gcrT9;y&4Y@vHu=%UZ#Q@mximG zkM?7^UR-aex7GrbIfZ@YUuQgwek;6&l1^R zGDP3qLq!9>s^WCF>)^@;+Au$1q&bNetp?zQt7mL2G{;y%FW^6D6g6)pcWDH^VQxG1D~Sci#>swT#TeMBbI(;6pQvTN#z9aVyAC zxdbOyM)VHvAnteUOh(^UJYD+*lWN&A55KnQ}lWD^AAM z5lG(24Qr1M#u@%@k95yY;4}3^Ly`_x6|VVTn2oOIC%MPXc(&*9uLN+HtRr{jJ{ajs zebt@S#^V#q>GKlAr(I{n^?GXsJQmN=Q+x=^K_fYqccv-(7
M6fz