commit dc6b29d625b56b578b5077a5a30c6bd0e7221d2a Author: Jonathan Naylor Date: Thu May 10 19:30:05 2018 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f42248 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.o +*.obj +x86 +Debug +Release +*.user +*~ +*.bak +.vs + diff --git a/Arduino/RepeaterControl/RepeaterControl.ino b/Arduino/RepeaterControl/RepeaterControl.ino new file mode 100644 index 0000000..ca82a04 --- /dev/null +++ b/Arduino/RepeaterControl/RepeaterControl.ino @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +// 0 and 1 are used for serial +const int RFTRANSMIT = 2; +const int EXTTRANSMIT = 3; +const int HEARTBEAT1 = 4; +const int HEARTBEAT2 = 13; +const int ACTIVE = 5; +const int OUTPUT1 = 6; +const int OUTPUT2 = 7; +const int OUTPUT3 = 8; +const int OUTPUT4 = 9; + + +const int RFSQUELCH1 = A0; +const int RFSQUELCH2 = A1; +const int EXTSQUELCH = A2; +const int BATTERY = A3; +const int DISABLE = A4; + + +void setup() { + // Set up the outut pins with pull ups + pinMode(RFSQUELCH1, INPUT); + pinMode(RFSQUELCH2, INPUT); + pinMode(EXTSQUELCH, INPUT); + pinMode(BATTERY, INPUT); + pinMode(DISABLE, INPUT); + + digitalWrite(RFSQUELCH1, HIGH); + digitalWrite(RFSQUELCH2, HIGH); + digitalWrite(EXTSQUELCH, HIGH); + digitalWrite(BATTERY, HIGH); + digitalWrite(DISABLE, HIGH); + + // Set up the output pins and set low + pinMode(RFTRANSMIT, OUTPUT); + pinMode(EXTTRANSMIT, OUTPUT); + pinMode(HEARTBEAT1, OUTPUT); + pinMode(HEARTBEAT2, OUTPUT); + pinMode(ACTIVE, OUTPUT); + pinMode(OUTPUT1, OUTPUT); + pinMode(OUTPUT2, OUTPUT); + pinMode(OUTPUT3, OUTPUT); + pinMode(OUTPUT4, OUTPUT); + + digitalWrite(RFTRANSMIT, LOW); + digitalWrite(EXTTRANSMIT, LOW); + digitalWrite(HEARTBEAT1, LOW); + digitalWrite(HEARTBEAT2, HIGH); + digitalWrite(ACTIVE, LOW); + digitalWrite(OUTPUT1, LOW); + digitalWrite(OUTPUT2, LOW); + digitalWrite(OUTPUT3, LOW); + digitalWrite(OUTPUT4, LOW); + + // Set up the serial port + Serial.begin(19200); +} + +void loop() { + if (Serial.available() > 0) { + int out = Serial.read(); + + digitalWrite(RFTRANSMIT, (out & 0x01) == 0x01 ? HIGH : LOW); + digitalWrite(EXTTRANSMIT, (out & 0x02) == 0x02 ? HIGH : LOW); + digitalWrite(HEARTBEAT1, (out & 0x04) == 0x04 ? HIGH : LOW); + digitalWrite(HEARTBEAT2, (out & 0x04) == 0x04 ? HIGH : LOW); + digitalWrite(ACTIVE, (out & 0x08) == 0x08 ? HIGH : LOW); + digitalWrite(OUTPUT1, (out & 0x10) == 0x10 ? HIGH : LOW); + digitalWrite(OUTPUT2, (out & 0x20) == 0x20 ? HIGH : LOW); + digitalWrite(OUTPUT3, (out & 0x40) == 0x40 ? HIGH : LOW); + digitalWrite(OUTPUT4, (out & 0x80) == 0x80 ? HIGH : LOW); + } + + byte val = 0x00; + val |= (digitalRead(RFSQUELCH1) == LOW) ? 0x01 : 0x00; + val |= (digitalRead(RFSQUELCH2) == LOW) ? 0x02 : 0x00; + val |= (digitalRead(EXTSQUELCH) == LOW) ? 0x04 : 0x00; + val |= (digitalRead(BATTERY) == LOW) ? 0x08 : 0x00; + val |= (digitalRead(DISABLE) == LOW) ? 0x10 : 0x00; + + Serial.write(val); + + delay(20); // 20ms +} + diff --git a/BUILD.txt b/BUILD.txt new file mode 100644 index 0000000..5140a51 --- /dev/null +++ b/BUILD.txt @@ -0,0 +1,77 @@ +Repeater - 20180510 +=================== + +Windows +------- + +To use the Repeater control software you will first need to build the latest +version of wxWidgets (http://www.wxwidgets.org), the version I used was 2.8.12, +I also installed it in the default location which is C:\wxWidgets-2.8.12. + +You will also need a copy of PortAudio (http://www.portaudio.com), I used the +latest stable version which is currently 20071207 and it appears to be fine. I +put that into the "Visual Studio 2008\Projects" folder alongside the source code +for Repeater itself. A skeleton version of PortAudio suitable for using to build +Repeater is available from the pcrepeatercontroller Yahoo group in the +Files/Windows Extras folder. + +For compiling I use Visual C++ 2008 Express Edition downloaded from Microsoft +for free. I recommend that you use the same. + +To build wxWidgets, you simply need to open Visual Studio 2008 using the File -> +Open -> Projects/Solutions and load the wx_dll.dsw file to be found in +wxWidgets-2.8.12\build\msw directory and then go into Batch Build and select the +DLL Unicode Debug and DLL Uncode Release entries for every one, this take a +little time! Then build them. + +Unfortunately building the software under Windows also requires the downloading +of a very large file from Microsoft, the WDK. Adding this to Visual Studio +causes a lot of problems, so I created an extract of the required files from the +kit and used that instead. If you do want to build this software from scratch, +then download the files HID.zip and WinUSB.zip from the Yahoo! group. Otherwise +simply use the supplied binaries. + +The path names for things like wxWidgets and PortAudio are embedded within the +Solution and Project preferences, and will need changing if anything other than +these default locations are used. The first pass through the compiler will no +doubt tell you all that you need to know if there are problems. + +Once you have built the executables, you will need to copy the +portaudio_x86.dll, wxbase28u_vc_custom.dll, wxmsw28u_adv_vc_custom.dll, +and wxmsw28u_core_vc_custom.dll to the same directory as your newly +built executables in order for it to run. If you are running in debug mode then +the required wxWidgets files have the names xxx28ud_xxxx instead. These can be +found in the wxWidgets-2.8.12\lib\vc_dll directory. + +It is also probable that you'll need to install a copy of the latest Visual C++ +run-time libraries from Microsoft, if you are not running the Repeater software +on the same machine as the development/compilation was done on. To do this you +need to copy and run the Vcredist_x86.exe file which is found at + + +Linux +----- + +You need to ensure that wxGTK, ALSA and libusb-1.0 are already +installed on your machine, under Ubuntu these are available from the standard +repositories, the version of wxWidgets is adequate. However you should get the +latest version of PortAudio from its home and build it from scratch. + +To install them from scratch, you need to get wxGTK from +, and libusb-1.0 from . +If you do a "make install" on all of them then they'll be installed in the +right places and nothing more needs to be done. + +On the Raspberry Pi you will need to build and install the wiringPi GPIO library +which is available from . +This will allow use the GPIO pins as controller pins for the different repeaters +if needed. + +The top level Makefile includes platform specific settings from the file +"settings.mk". The default file is for x86 Linux systems. Samples for other +platforms are provided by other developers, copy a required version to +"settings.mk" when needed. + +To actually build the software, type "make" in the same directory as this file +and all should build without errors, there may be a warning or two though. Once +compiled log in as root or use the sudo command, and do "make install". diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..df1da31 --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,2088 @@ +Repeater - 20180510 +=================== + +20090530 +-------- + +Added audio filtering and optional pre- and de-emphasis. Also modified the +configuration to have less pages as well as add the new settings. + +20090607 +-------- + +Silly bug in the through audio fixed. +Added the option to use a sound file as an input, but only in a debug build. + +20090608 +-------- + +Added debugging information in the tone decoder. +Not generally released. + +20090609 +-------- + +Added a simple VOGAD with reporting in the GUI. +Reworked the filters and output levels. +Not generally released. + +20090610 +-------- + +Added the audio level value to the GUI. +Re-arranged the GUI layout. +Not generally released. + +20090613 +-------- +Added Linux support, but not tested. +Changed the name of the executable and directories. +Changed how the K8055 parameters are set. +Allow for COM ports higher than COM10. +Not generally released. + +20090616 +-------- + +Added the D-Star Repeater, but unfinished. +Optimised access to the K8055. +Not generally released. + +20090617 +-------- + +The D-Star Repeater now compiles cleanly on Windows and Linux, so a good time to +do a release. + +20090618 +-------- + +Removed the icons from the programs. +Increased the audio output for the external output. +Not generally released. + +20090619 +-------- + +Added a VOGAD unit, and made the VOGAD on both the Radio and External audio. +Not generally released. + +20090626 +-------- + +Converted the pre-emphasis, de-emphasis and audio filters to IIRs for efficiency +and better shapes. +Show VOGAD levels for the Radio and External on the main screen. +Not generally released. + +20090629 +-------- + +Switched off filters when not transmitting to reduce CPU load. +Moved CTCSS insertion after pre-emphasis filter. +Not generally released. + +200900630 +--------- + +Changed the main filter back to the FIR for performance reasons. +First version to be used on a real repeater, GB3IN on 2m. + +20090812 +-------- + +Fixed CTCSS output tones. +Widened the CTCSS detector from 2Hz to 5Hz and from 500ms to 200ms. +Numerous internal improvements. +Not generally released. + +20090813 +-------- + +Fixed audio out bug. +CTCSS output is now for the whole time the repeater is accessed. + +20090824 +-------- + +Audio levels are now reset before each received transmission instead of at the +Ack. +Added controls to toggle the unused output lines to control external hardware. +Delay renamed to Ack in the analogue repeater GUI. +Fixed bugs in the state machine causing timeout resets and a couple of missing +state transitions. + +20090909 +-------- + +Turn off the CTCSS early when the end callsign is enabled because of slow +responding decoders in radios allowing some of the callsign through. +Added an optional audio delay on the input to synchronise with the squelch +input(s). +Make GUI updates optional. + +20090910 +-------- + +Allow for an external CTCSS decoder. +Not generally released. + +20090911 +-------- + +Add a battery input and a change of Ack to signify battery operation. +Not generally released. + +20090916 +-------- + +The External Transmit is only set when relaying radio audio. +First version of the D-Star Controller. +Optimised the K8055 communication. + +20090917 +-------- + +Linux cleanups. +Changes to the D-Star Controller. +Not generally released. + +20090923 +-------- + +Validate the RPT2 callsign. +Fixed bit errors on the D-Star Ack. +Improved the documentation about the D-Star Repeater. +Added a PTT delay for D-Star. + +20091002 +-------- + +Removed RPT2 validation. +Added PTT and Squelch inversion for the D-Star Repeater. +Added G2 Gateway support. +Tightened the filter before the GMSK democulator. +Not generally released. + +20091008 +-------- + +Changes in the G2 protocol handler. +Not generally released. + +20091015 +-------- + +Allow for named configurations to allow multiple repeaters on one machine. +Increased the maximum PTT Delay to 500ms from 200ms. +Added the PTT Delay and PTT and Squelch inversion to the Analogue Repeater. +More changes to the G2 protocol handler. +Not generally released. + +20091016 +-------- + +Added a network watchdog timer to handle the loss of the end packet. + +20091017 +-------- + +Allow for the configuration of the local IP address to bind to. +Not generally released. + +20091020 +-------- + +Lots of Linux GUI cleanups. +Added the Watchdog timer to the D-Star GUI. +Added a simplex mode to the D-Star repeater. +Added an output level to the D-Star repeater to allow for sharing with the +analogue controller. +Not generally released. + +20091021 +-------- + +Removed the confirmation prompt when exiting the repeaters. +Not generally released. + +20091025 +-------- + +Changed to the GMSK modulator to be more conventional. +Added command line versions of the repeaters. +Major updates to the G2 protocol. +Not generally released. + +20091026 +-------- + +Added a new bandpass filter to the analogue repeater to remove the CTCSS tones. +More G2 changes. + +20091027 +-------- + +Removed RPT2 validation from network received headers. +More G2 changes. +Not generally released. + +20091029 +-------- + +Implemented the "Open G2" network protocol. +Not generally released. + +20091030 +-------- + +More changes to the "Open G2" network protocol. +Not generally released. + +20091031 +-------- + +More changes to the "Open G2" network protocol. +Allow network data relaying while waiting for the ack. +Not generally released. + +20091103 +-------- + +Move the log files to the users home directory. + +20091104 +-------- + +Changes to the protocol handlers to handle odd cases. +Not generally released. + +20091106 +-------- + +A cleanup of the 20091104 release because it works so well and deserves to be +released :-) + +20091117 +-------- + +Added support for serial ports as I/O ports for both repeaters. +Removed the ack when in simplex mode. +Added all the EIA CTCSS tones to the analogue repeater. +Changes to the "Open G2" network protocol. + +20091120 +-------- + +Merged the callsign and beacon timers in the analogue repeater. +Support the URI USB. + +20091125 +-------- + +Changes to the "Open G2" network protocol. +Added the network delay for the D-Star repeater. +Not generally released. + +20091126 +-------- + +Bug fix release for network delay. +Don't pass through RF data with the RPT2 entry set to blanks. +Not generally released. + +20091212 +-------- + +Added the Gateway mode to the D-Star repeater. +Don't pass through RF data with the RPT2 entry set to my callsign. +Brought the K8055 drivers internal for both Windows and Linux. +Put the ack back in simplex mode. +Added the callsign on latch to the analogue repeater. +Added a choice of timeout tones to the analogue repeater. + +20091219 +-------- + +Added DTMF and local commands to the analogue repeater. +Added remote and local commands to the D-Star repeater. +Added Callsign Holdoff to the analogue repeater. +Removed the RPT2 validation that was added for the last release. +Added new programs to configure the repeaters and removed configuration from the +main programs. +Added two new programs, rmk8055drv and rmuridrv, to remove the Linux usbhid +driver. +Fixed Simplex/Duplex choice bug. +Not generally released. + +20091226 +-------- + +Added Parrot mode to the D-Star repeater. +Added DTMF display to the analogue repeater. +Not generally released. + +20091228 +-------- + +Tweaks to the DTMF controller. + +20091229 +-------- + +Fixed Linux build issues with 20091228. +Included incomplete Parrot Controller. + +20091230 +-------- + +First version of the Parrot Controller. +Small change to the G2 protocols. +Not generally released. + +20091231 +-------- + +Fixed bugs in the Parrot Controller. +Added adjustable Turnaround Time to the Parrot Controller configuration. + +20100106 +-------- + +Added Watchdog timer to the Parrot Controller. +Added suffix D for 10m to the D-Star Repeater. +Added two external commands to both repeaters. +Added the four outputs to the D-Star control to match the analogue Repeater. +Not generally released. + +20100107 +-------- + +Added a command to be run at a period after the D-Star Repeater closes. +Not generally released. + +20100108 +-------- + +Fixed Linux compilation issues. + +20100111 +-------- + +Added menu commands to run Command 1 and 2 on the analogue repeater. +Removed the timed command added in 20100107. +Added a Disable input to shutdown and startup the repeaters. +Modified the Active output to use with the new Disable input to allow cross linking of repeaters leading to true dual-mode operation. +Fixed bug with K8055s with addresses greater than zero. +Not generally released. + +20100112 +-------- + +Fixed Active output bug in 20100111 in the D-Star repeater. +Added a Disable input to the URI USB. +Allows both external commands for Startup and Shutdown and the Disable input. +Not generally released. + +20100114 +-------- + +Bug fixes for 20100112. + +20100222 +-------- + +Added a gateway callsign to the D-Star repeater for messages. +Removed the Icom G2 Protocol Hander as it wasn't used. +Allow for a -nolog command line option to disable all logging. +Simplified the mode selection and added an ack option for D-Star. +Change network validation to only be on the IP address. +Not generally relased. + +20100223 +-------- + +Bug fixes for 20100222. + +20100412 +-------- + +Changed ack selection to free form text in the analogue repeater. +Removed the need for a squelch input or the audio delay setting for D-Star. +Now takes the header from slow data if the header was missed. +Removed the ack timer. +Better handling of missing sync bytes on both the radio and network sides. + +20100415 +-------- + +Re-added the ack timer, but it can now handle shorter times than before. +Allow for the manual setting of the D-Star gateway callsign. +Fixed a major bug in 20100412. + +20100419 +-------- + +Special test release for GB3IN. +Not generally released. + +20100423 +-------- + +Moved output controls to the menu bar instead of buttons. +Display the last three lines of the log on the repeater screen. +Not generally released. + +20100424 +-------- + +Threading bug in 20100423 fixed. +Not generally released. + +20100510 +-------- + +A bit of a stinker, best forgotten. +Not generally released. + +20100516 +-------- + +Extra D-Star header validation. + +20100603 +-------- + +Bug fixes. +Use the ack timer to pause the D-Star repeater in gateway mode between overs. +Added an optional audio delay to the D-Star output audio. +Not generally released. + +20100604 +-------- + +Bug fixes. +Re-implement the audio delay as negative PTT delay. +Not generally released. + +20100610 +-------- + +Added the end of transmission senc at the end of the network transmision. +Lengthened the D-Star transmit period. +Not generally released. + +20100712 +-------- + +Better handling of error conditions at startup on the repeaters. +Added new time to limit the timeout tones on the analogue repeater. +Change sound card handling times. +Not generally released. + +20100719 +-------- + +A bug fix on the previous release. + +20100720 +-------- + +More bug fixes. +CTCSS goes off before sending the callsign in lockout. + +20100726 +-------- + +Added new CTCSS output modes. +Changed the parameter of PortAudio which appears to make a big difference under +Linux. +Not generally released. + +20100808 +-------- + +Fix to the DVTool files. +Fixed shutdown command. +PortAudio parameter depends on the operating system, new parameter for Linux, +old for Windows. + +20100810 +-------- + +Added 79.7 Hz as a CTCSS tone. +Added an RX Level setting to the D-Star repeater for when running alongside +analogue for adjusting input levels. +Bug in the slow data decoder fixed. +Not generally released. + +20100811 +-------- + +Improvements to the slow data decoder. + +20100813 +-------- + +Added a beacon to the D-Star repeater. +Fixed bug in the new slow data decoder. + +20100815 +-------- + +Re-integrated the configuration for both repeaters back into the main +repeater programs. +Fix startup and shutdown when PTT inversion is set. +Not generally released. + +20100818 +-------- + +Added the GMSK repeater. +Not generally released. + +20100820 +-------- + +Added the DExtra Gateway. +Added hourly usage statistics to all of the repeaters. +Not generally released. + +20100823 +-------- + +Added the CTCSS hang timer. +Not generally released. + +20100825 +-------- + +First working version of the DExtra Gateway. +Not generally released. + +20100825a +--------- + +Bug fixes to the DExtra Gateway. +Not generally released. + +20100826 +-------- + +Linux support for the DExtra Gateway. +Written documentation for the DExtra Gateway. + +20100901 +-------- + +Added optional callsign restriction to both D-Star repeaters. +Added repeater callsign to the DExtra Gateway configuration, as this fixed a +bug. +Not generally relased. + +20100903 +-------- + +Modify the slow data when in gateway mode. +Not generally released. + +20100907 +-------- + +The GMSK Repeater now works on a Satoshi 7.08 system. +Not generally released. + +20100915 +-------- + +Allow for the choice of the GMSK modem type, Satoshi or Dutch*Star. Only the +Satoshi option works as expected. +Added text from the DExtra Reflector to appear in the repeater ack and beacon. +More changes for the GMSK Modem, it appears even better now. +A number of changes to the transmitted callsigns made. +Not generally released. + +20100917 +-------- + +More callsign changes. +More work on the GMSK Modem support. +Not generally released. + +20100922 +-------- + +More callsign changes in the repeaters and Parrot Controller. +Changes timings for the GMSK Modem. +Not generally released. + +20100924 +-------- + +Added display of the ack text to the repeater GUIs. +Use Fred PA4YBR's modem support library for GMSK Modems. + +20101016 +-------- + +Allow use of shared libraries on Linux. +Fixed header transmission for the GMSK Repeater. +Reject relayed gateway-ed data to stop confusing G2. + +20101026 +-------- + +Changed the way the header is received in the GMSK Repeater. +Increased the watchdog timer to two seconds from half second. + +20101104 +-------- + +Changes to the GMSK Repeater. +Pass Flag2 and Flag2 through the D-Star Repeater. +The GMSK Repeater can now build on an AMD64 Linux machine. + +20101107 +-------- + +More changes to the GMSK Repeater. +Add the -gui command line flag to all the programs. + +20101109 +-------- + +More changes to the GMSK Repeater. + +20101109a +--------- + +Plan B for getting full duplex working with the GMSK Repeater, a seperate thread for the transmitter. + +20101110 +-------- + +Another try, but still not working properly + +20101112 +-------- + +Logs are now written into the /var/log directory under Linux. +Removed the writer thread and gone back to single threading. +Added the Intel.o file for i386 to allow the GMSK Repeater to build. + +20101113 +-------- + +Changed the timing within the GMSK Repeater. +Fix a problem with receiving text from the latest ircDDB Gateway. + +20101121 +-------- + +Added the Active output for the URI USB at the expense of GUI Output 2. +Changed the end of transmission network packet to be more standard. +More changes to the GMSK Repeater. + +20101202 +-------- + +Fix the statistics for all the repeaters, hopefully. +Changed the callsigns in the acks to be Icom compatible. +Added more intelligent silence insertion. + +20101203 +-------- + +Another stab at full-duplex on the GMSK Modem. +Handle out-of-order data on the network traffic. + +20101203a +--------- + +Changed to wxWidgets 2.8.11. +Fixed half/full duplex modem setting. + +20101208 +-------- + +Converted the GMSK Repeater to use libusb instead of NAtools. +Changed to a new extension protocol to pass data to/from a gateway. +Collect extra statistics and put the AMBE BER on the GUI. +Added a statistics packet. + +20101211 +-------- + +The returned BER was wrong, it should have been the number of error bits. +The DExtra Gateway now reports the link status correctly to the repeaters. +Added French, German, Danish, Italian and Polish to the DExtra Gateway ack languages. +Added the XRF055 reflector. +Back to using NAtools. + +20101212 +-------- + +Test a new version of the libusb GMSK Repeater. +Clean up the GUI a little for the Flags and BER. + +20110105 +-------- + +Added the PTT Hang parameter to the D-Star Repeater. +Added the XRF019, XRF026, XRF027, and XRF044 reflectors. +Revert to using libusb. + +20110107 +-------- + +Removed the statistics from the GMSK Repeater. +Fixed the Linux compile bug from 20110105. +Added a PTT maintainer for the GMSK Repeater. + +20110107a +--------- + +A change to the PTT maintainer which may be more effective. +Increased the buffer size for RF data. + +20110107b +--------- + +Return to the PTT system of 20110107. +Increase the buffer size for the RF data again, it is now 50 frames. +Not generally released. + +20110108 +-------- + +Replaced the DExtra_Hosts.txt with the one from the ircDDB Gateway package. +More changes to buffering and timing in the GMSK Repeater. +Not generally released. + +20110108a +--------- + +More changes to buffering and timing in the GMSK Repeater. +Not generally released. + +20110109 +-------- + +Allow for the use of both the libUSB and NAtools interface. +Change how the internal timing operates. +Not generally released. + +20110109a +--------- + +Increase the buffer size and put in a cycle of silence to start it off. +Not generally released. + +20110114 +-------- + +Swap RPT1 and RPT2 for the libUSB modem driver. +Different GMSK modem buffer filling routines. +Not generally released. + +20110114a +--------- + +Same as 20110114 but with additional debugging information. +Not generally released. + +20110116 +-------- + +Swap RPT1 and RPT2 for the NAtools modem driver. +Increase the polling rate in the GMSK Repeater. +Not generally released. + +20110120 +-------- + +Added XRF000 for the DExtra Gateway. +DExtra protocol has been upgraded. +Another timing change for the GMSK Repeater receiver. + +20110121 +-------- + +More timing changes for the GMSK Repeater. + +20110123 +-------- + +More timing changes for the GMSK Repeater. + +20110205 +-------- + +Fixed the callsigns in the acks of the GMSK Repeater. +Change the timings, particularly when handling receive on the modem. +Change the default ports to match those of ircDDB Gateway. + +20110208 +-------- + +More timing changes to the GMSK Repeater. + +20110210 +-------- + +Handle errors in the GMSK modem better. +More timing changes to the GMSK Repeater. + +20110213 +-------- + +Internal restructuring of the GMSK Repeater. +Removed the RX State from the GMSK Repeater GUI. + +20110215 +-------- + +Added XRF020 to DExtra Gateway. +Added Dutch*Star 1.00.0 firmware compatibility again. + +20110217 +-------- + +Fixed bug in DExtraGateway with incoming headers disrupting communications. +Change the maximum Parrot Controller beacon timer to 20 minutes. +More GMSK Repeater internal changes. + +20110219 +-------- + +Swapped the callsigns in the D-Star Repeater to match the GMSK Repeater. +Removed support for Dutch*Star 1.00.0 firmware. + +20110219a +--------- + +Re-arranged the transmitted callsigns in both of the digital repeaters. +Validate the incoming network header for both digital repeaters. + +20110222 +-------- + +Added Swedish and Spanish translations to the DExtra Gateway. +Increased the USB timeout when using libUSB with the GMSK modem. +Increased the packet silence timeout from 30ms to 40ms. +Run the network processing from the timer thread in the GMSK Repeater. + +20110222a +--------- + +Small change to the GMSK Repeater to stop lock-ups. + +20110227 +-------- + +Added optional RPT1 validation. +Relaxed the timing when talking to the GMSK modem. + +20110301 +-------- + +Remove a delay added to the NAtools driver. +Fixed Linux compile error with the previous release. + +20110308 +-------- + +Added daily logs. +Added the -logdir command line option to the programs. +Added XRF007 and XRF021 to the DExtra_Hosts.txt file. +Added the sound card demodulator bias patch from Eric NS7DQ. + +20110308a +--------- + +Timing changes to the GMSK Repeater closer to the settings of 20110219a. + +20110327 +-------- + +Removed the bias patch which was added to the 20110308 release. +Removed the silly GMSK Repeater error messages that I added. +Updated the IP address of XRF008. +Added extra my callsign validation to the repeaters. + +20110329 +-------- + +Relax the header checksum testing under certain circumstances. +Check the incoming my call with a regular expression. +More buffering of network data in the GMSK Repeater. + +20110331 +-------- + +Timing changes in the GMSK Repeater. +Put the networking back into the second thread on the GMSK Repeater. + +20110402 +-------- + +Refinements to 20110331 based on feedback. + +20110403 +-------- + +Mutex locking in the last couple of releases was a little wrong. + +20110404 +-------- + +More mutex locking changes in the GMSK Repeater. +Timing changes in the GMSK Repeater. + +20110411 +-------- + +Different method of silence insertion used. + +20110413 +-------- + +A small tidy up of the previous release. + +20110417 +-------- + +Changed from wxWidgets 2.8.11 to 2.8.12. +Small internal timing tweak for the GMSK Repeater. + +20110418 +-------- + +Added XRF033. +Updated the IP address of XRF013. +Changed the timing in the main loop. +Not generally released. + +20110420 +-------- + +Changed the timing of feeding the data to the GMSK Modem. + +20110421 +-------- + +Fixed an obscure bug in the Parrot Controller. +Updated the IP address of XRF021. +Change timings and data feed to the GMSK Modem. + +20110506 +-------- + +Added XRF031. +Added the ack time to the minimum time for the analogue repeater controller. +Allow outgoing DExtra links to have random ports. +Change timings in the GMSK Repeater. + +20110508 +-------- + +Added -daemon option to create proper daemons on Linux. +Not generally released. + +20110510 +-------- + +Fundamental change to the GMSK Modem handling. +Cleaned up 20110508. + +20110512 +-------- + +Updated the USAGE.txt file to include the -daemon option. +The pid of the programs is written to /var/run for Linux versions. +Added the -confdir option to the daemon versions of the programs. + +20110525 +-------- + +Updated the IP address of XRF020. +Changed poll format to report the repeater type to the gateway. +Small change in timing of the GMSK Repeater. + +20110529 +-------- + +Tweaks to the software type/version reporting to the gateway. + +20110529a +--------- + +Small GMSK modem change, rolls back a change made in 20110529. + +20110601 +-------- + +Small GMSK modem change, rolls back another change made in 20110529. + +20110609 +-------- + +Add the cycle time setting to the GMSK modem settings. +Added different serial port configs to allow for different serial port settings. + +20110610 +-------- + +Added more cycle times for the GMSK modem. +Added an extra serial port config. + +20110613 +-------- + +Display cycle overruns more intelligently. +Display time taken for the networking to complete. +Stop polling the URI USB in the D-Star Repeater, it's not needed. + +20110621 +-------- + +Increased the max cycle time value to 20ms. +Now write 12 bytes per cycle instead of 8. + +20110621a +--------- + +Changed from the broken node_putdta to node_io. + +20110622 +-------- + +Clean ups and removal of some logging messages. +Added extra delays for libUSB. + +20110628 +-------- + +Remove the need to have NAtools installed on Windows. + +20110702 +-------- + +Remove the need to have LibUSB installed on Windows. + +20110703 +-------- + +Trying to handle unreasonable combinations of modem and USB chipset. + +20110704 +-------- + +Allow for compilation without NAtools being available. +Added XRF028. + +20110706 +-------- + +Changed timings when dealing with LibUSB and relaying from the modem. +Changed Modem Type to Firmware Type. + +20110708 +-------- + +Upgraded the LibUSB driver from libusb-0.1 to libusb-1.0 under Linux. +Upgraded the NAtools version under Windows. +Removed NAtools on the Linux side. + +20110710 +-------- + +Turned the logging upside down to match the gateway, and common sense. +Improvements to the NAtools drivers. + +20110711 +-------- + +Handle DUTCH*Star 0.1.00 firmware explicitely. + +20110712 +-------- + +Improve the clock accuracy in the GMSK Repeater. + +20110714 +-------- + +Insert a 100ms delay before sending the data after sending the header. +Updated XRF033. + +20110716 +-------- + +Added Nederlands as a language option to the DExtra Gateway. +Removed the cycle time option for the GMSK Repeater. +First version that is stable with DUTCH*Star 0.1.00 firmware. + +20110717 +-------- + +Cleans-ups in all the repeaters. +More work on 0.1.00 stability. + +20110719 +-------- + +Removed the Network Delay from the D-Star Repeater. +Added extra delay and buffering to the D-Star Repeater for network traffic. + +20110720 +-------- + +Fix PTT bug in 20110719. + +20110808 +-------- + +First version of the DVAP Node program. +Allow incoming network data during wait periods. +Clean up GMSK Repeater local repeating. + +20110808a +--------- + +Serial port selection bug fixed. + +20110808b +--------- + +Windows serial port changes. + +20110811 +-------- + +Minor internal changes to all of the repeaters. +Improved timing for the DExtra Gateway and Parrot Controller. +Added fuzzy matching for the frame sync to the D-Star Repeater. + +20110811a +--------- + +Fix for GMSK Repeater full duplex mode. +Identify the OS and wxWidgets version in the log for all programs. + +20110814 +-------- + +Added the noise squelch to the D-Star Repeater. +More work on GMSK full duplex mode. + +20110816 +-------- + +Allow the timers to handle long timeouts correctly. +The Squelch Level affects the D-Star Repeater in real-time. +Change the serial port handling for the Analogue and D-Star repeaters. + +20110818 +-------- + +The RX and TX Levels affect the D-Star Repeater in real-time. +The levels and thresholds in the Analogue Repeater operate in real-time. +Increased the Analogue Repeater hang timer maximum to 30 seconds. +All of the IDs and Acks in the Analogue Repeater can now be WAV files. + +20110822 +-------- + +The Battery Ack is now configurable in the Analogue Repeater. +Fix to the Windows SerialLineController.cpp +Add more buffering to the D-Star Repeater. + +20110823 +-------- + +Fixed a bug in the DExtra Gateway. +Allow the opening callsign of the Analogue Repeater to be different from the closing callsign. +Fixed the minimum time for ack timer in the Analogue Repeater. +Added XRF073. + +20110825 +-------- + +Added a noise level display to the D-Star Repeater. +Added the Squelch Mode to the D-Star Repeater. + +20110826 +-------- + +Allow for alternative GMSK Modem addresses in NAtools. +Use the new silence insertion in the D-Star Repeater. + +20110830 +-------- + +Fix Squelch and Noise display when transmitting in the D-Star Repeater. +First untested version of the DV-RPTR Repeater. + +20110831 +-------- + +More work on the DV-RPTR Repeater. + +20110902 +-------- + +Seperate the closdown ID from the periodic ID in the Analogue Repeater. +Lots of internal changes to the DVAP Node. +More work on the DV-RPTR Repeater. + +20110905 +-------- + +Considerable internal changes to the output queueing. +Bug in the silence inseration fixed. + +20110907 +-------- + +More internal changes to handle extreme situations. + +20110913 +-------- + +Use the official FTDI interface to access the DVAP on Windows. +Changed XRF002 IP address. + +20110915 +-------- + +Revert the FTDI change for the DVAP on Windows. +Changes to the queueing. +Enable the debug flag on Linux builds. +More changes to the DV-RPTR Repeater. + +20110923 +-------- + +More changes to the DV-RPTR Repeater. +Changed XRF073 IP address. + +20111004 +-------- + +Changed how data is fed to the DVAP and DV-RPTR. +Incorporated the new DV-RPTR data formats. + +20111016 +-------- + +Added XRF069. +Reverted external input handling for the Analogue Repeater. + +20111022 +-------- + +Changes to the DV-RPTR protocol. +Added packet logging for local traffic for all DV repeaters. +Pass the headers from the DV repeaters through properly. +Increase the size and number of the network queues. +Changed XRF069 IP address. +Changes to the repeater to gateway protocol. + +20111102 +-------- + +More changes to the DV-RPTR protocol. +The default log directory is now the home directory under Linux. +Bug fix to the DExtra Gateway. +Increased network security for the repeaters. +Added XRF123. + +20111109 +-------- + +Output buffer changes. +Added support for the reporting of the two status messages. + +20111112 +-------- + +Fix to the DExtra Gateway. +Minor bug fix. + +20111118 +-------- + +Increased the GUI width on Linux to accomodate the new status messages. +Allow for the DV-RPTR TX Delay up to 500ms from 250ms. +Fixed the config file name for the DV-RPTR daemon version. +Fixed the DV-RPTR communications protocol. +Fixed DV-RPTR version string. +Updated XRF069 IP address. +Added XRF038. + +20111120 +-------- + +Changes to the DVAP Node and DV-RPTR Repeater handling. + +20111122 +-------- + +Allow more choices for the analogue repeater Ack delay. +Cleaned up the DV-RPTR Modem settings. +Updated the Windows NATools DLL. +Updated XRF002 IP address. +DExtra Gateway fix. + +20111125 +-------- + +Handle startup of the DV-RPTR modem and DVAP better. + +20111126 +-------- + +Changes to the DV-RPTR modem startup for slow serial subsystem starts. +Dummy gateway handling added. + +20111204 +-------- + +Allow headers received when in full duplex and transmitting network data to be passed to the gateway. + +20111207 +-------- + +Acks changed from the link status to the transmission time and BER. +Allow up to five status messages. + +20111208 +-------- + +Fix bug in the D-Star Repeater for the new status messages. +Similar fix to the DVAP Node. +Small change to the new ack format. + +20111209 +-------- + +Change the ack text to include the reflector instead of the time. +Updated XRF019s IP address. + +20111212 +-------- + +In the Analogue Repeater send radio audio to the external port when relaying from the external port. +Removal of the USAGE.txt file. +Fix an ancient bug with the output switching in the D-Star Repeater. + +20111213 +-------- + +More work on the Analogue Repeater radio audio to the external port. + +20111215 +-------- + +More work on the Analogue Repeater radio audio to the external port. + +20111220 +-------- + +More work on the Analogue Repeater radio audio to the external port. +Updated XRF031s IP address. + +20111221 +-------- + +More work on the Analogue Repeater radio audio to the external port. + +20111222 +-------- + +Allow the ack on the D-Star repeaters to be selectable for BER or status. + +20111231 +-------- + +Added the upgraded Dummy Repeater to the repeater package. +Allow channel D in the DExtra Gateway. + +20120111 +-------- + +Increase maximum D-Star Repeater hang time to 3000ms from 2000ms. +Improve gateway mode rejection rules. +Allow the Dummy Repeater to be controlled from external hardware. +Added XRF119. + +20120116 +-------- + +Updated XRF033s IP addeess. +Fixed GUI PTT bug in the Dummy Repeater. +Add an audio resampler to the Dummy Repeater. + +20120125 +-------- + +Allow for the settings of the beacon text for the D-Star repeaters. +Equalised the audio levels in the Dummy Repeater; +Updated XRF019 IPs address. +Added XRF110. + +20120205 +-------- + +New configuration system for non-Windows platforms. +Added XRF040. + +20120206 +-------- + +Bug fixes for 20120205. + +20120210 +-------- + +Updated XRF021s IP address. +Small cleanups. + +20120213 +-------- + +Fixed D-Star Repeater Windows configuration bug. + +20120221 +-------- + +Changed the lower frequency limits of the analogue repeater callsign and ack. +Added the E (Echo) and I (Info) commands to the DExtra Gateway. +Updated XRF069s IP address. + +20120223 +-------- + +Blank any DTMF tones from being transmitted over RF. +Added audio delay and pre-, de-emphasis to the Analogue Repeater external port. + +20120228 +-------- + +Restore GUI window position on startup. +New repeater to gateway protocol implemented. +Allow background audio data to be transmitted. +Added a receive hang time to the Dummy Repeater. + +20120303 +-------- + +Added AMBE regeneration to background data. +Added receive-only and transmit-only modes to the repeaters. + +20120308 +-------- + +Changes to the DV-RPTR driver. +Updated XRF021s and XRF044s IP address. +Added XRF222, XRF353, and XRF777. +Not generally released. + +20120309 +-------- + +Changes to support the DCS reflector system. +More changes to the DV-RPTR driver. + +20120310 +-------- + +Updated the DV-RPTR driver again. +Binary only release for Windows users. + +20120310a +--------- + +Updated the DV-RPTR and DVAP drivers. + +20120312 +-------- + +Added CM119A product id for latest URI USB hardware. +Added K8055 support to the GMSK and DV-RPTR repeaters. + +20120320 +-------- + +Updated XRF021s IP address. +Added TX output to the K8055 with the GMSK and DV-RPTR repeaters. +Added the DCS Gateway. + +20120322 +-------- + +Added XRF030. +Added the Split Repeater. +Allow minimum ack time in the Analogue Repeater to be 100, 300, and 500ms. +Add up to 100ms of squelch delay in the Analogue Repeater. + +20120324 +-------- + +Added "TX and RX" mode to all of the D-Star repeaters except the DVAP Node. +More changes to the Split Repeater. +Added DCS009. + +20120326 +-------- + +Fixed bugs in the RF D-Star repeaters when used with the Split Repeater. +Fixed bugs in the use of the Split Repeater with the RF repeaters. +Added DCS005. + +20120327 +-------- + +Add extra logging to the Split Repeater for unknown IP addresses and ports. +Modify the Linux configuration system to fix a bug. +Updated XRF017s IP address. +Added DCS008. + +20120328 +-------- + +Fixed ack bug in the Split Repeater. +Added DCS006. + +20120330 +-------- + +Updated the DCS and DExtra hosts files to the latest versions. +Convert the main log files to use UTC instead of local time. +More changes to the Split Repeater. + +20120402 +-------- + +Better error handling of the DV-Dongle. +More changes to the Split Repeater. + +20120403 +-------- + +Sort the callsign entries in the Dummy Repeater. +Minor cleanups. + +20120411 +-------- + +Added Norwegian as a language to the DExtra and DCS Gateway. +Added an optional voice beacon to all of the D-Star repeaters. + +20120412 +-------- + +Fixed BER display on all of the repeater GUIs. +Fixed header transmission bug in diversity modes. +More changes to the Split Repeater. +Not generally released. + +20120414 +-------- + +Added DCS007. +Handle reopening of the GMSK modem if it disconnects. + +20120419 +-------- + +Allow a greater range of GMSK modem addresses. +Replaced NAtools with direct calls to WinUSB :-) +Handle reopening of the DV-RPTR modem if it disconnects, Linux only for now. + +20120420 +-------- + +Upgrade the reopening of the DV-RPTR modem on Linux. +Remove the first poll from the repeaters to the gateway. + +20120420a +--------- + +Tweaks to the GMSK modem LibUSB driver to reduce the logging. +Changes to the GMSK modem WinUSB driver. + +20120424 +-------- + +Remove the architecture setting in the top level Makefile. +Added DCS004. + +20120428 +-------- + +Small revision to the DVAP driver. +Add the -nolog command line option to the Dummy Repeater. + +20120511 +-------- + +Fix the -nolog option for the Dummy Repeater. + +20120524 +-------- + +Upgraded network error reporting. +Added DCS010. +Removed debug statements from the Slit Repeater. +Add One-Touch Reply to the Dummy Repeater. + +20120526 +-------- + +Run the GMSK Modem in a seperate thread. + +20120527 +-------- + +Improvements to the changes made in 20120526. + +20120527a +--------- + +Minor tweaks to 20120527 relating to Windows mostly. +Not generally released. + +20120602 +-------- + +Remove the LibUsb option for the GMSK modem in Windows. +Infill data gaps with the last transmitted audio or silence. +More GMSK repeater changes. + +20120603 +-------- + +Lot of debugging statements in the GMSK drivers. +More GMSK repeater changes. +Removed XRF009, XRF010, XRF011, XRF030, XRF038, and XRF110. +Updated XRF031s and XRF069s IP address. +Added DCS012. + +20120604 +-------- + +Remove the GMSK debugging statements. +More GMSK repeater changes. +Fix bug in TX and RX mode in all but the D-Star Repeater. + +20120615 +-------- + +Added DCS013. +Roll back GMSK repeater changes regarding driving the modem. +Fixed background data in the GMSK Repeater. + +20120706 +-------- + +More GMSK Repeater rollbacks. +Allow cross compilation to ARM processors. +Added DCS011. + +20120802 +-------- + +Added DCS014 and DCS015. +Removed XRF100. +Fixed ARM cross compilation location of data files. +Changed silence insertion parameter. +Allow for up to four commands. + +20120826 +-------- + +Upgraded to Visual C++ 2010 on Windows. +Platform dependent settings moved to Makefile includes. +Changed the D-Star Repeater to the Sound Card Repeater. +Upgraded to the latest version of the DCS protocol. +Increased the default TX delay in the DV-RPTR Repeater to 150ms from 30ms. + +20120826a +--------- + +Reverted to using Visual C++ 2008 on Windows. + +20120901 +-------- + +Allow module E on all of the repeaters. +Re-added Windows LibUSB support to the GMSK Repeater. + +20120920 +-------- + +Make LibUsb on both Windows and Linux more agressive at transferring data. +Remove the need for the extra thread in the Linux command line versions. +Initial support for the XDV-RPTR (V2) protocol. +Removed the DCS and DExtra Gateways. +Added SVN revsion version to the log file. +Add AMBE logging to non-GUI versions of the repeaters. +Add -audiodir command line option to change the location of the AMBE data logging. + +20121004 +-------- + +Put the DCS and DExtra Gateways back into the package. +Updated DCS protocol. +Added support for the 70cms DVAP. +Use the DVAP to validate its own frequency. +Add a pause in the GMSK Repeater before PTT is set to on. + +20121027 +-------- + +Added DCS016, DCS017, and DCS018. +Changed the announcement audio for B and D for UK English. +Removed the beacon in Gateway mode. + +20121112 +-------- + +Removed XRF018 and XRF250. +Updated DCS007s IP address. +Added new XDVRPTR data transfer mode for v0.72 firmware or later. + +20121220 +-------- + +Added DCS020. +Updated XRF008s IP address. +Add Ethernet support for the DVRPTR-V2 hardware. +Add support for the Raspberry Pi GPIO port. + +20130111 +-------- + +Make the frame wait time configurable in the Split Repeater. +Allow for empty beacons in the Analogue Repeater to suppress them. +Convert old DV-RPTR configuration values (Windows only). +Added DCS019. + +20130127 +-------- + +Allow for temporary ack text. +Convert the DCS hosts files to use hostnames. +Increase DVAP polling speed to increase reliability on slow machines. +Fixed bug in status reporting. + +20130131 +-------- + +Upgrade the DCS Gateway to the latest protocol version. +Revert DVAP polling change. + +20130203 +-------- + +Remove DVAP frequency offset setting. + +20130219 +-------- + +Updated XRF008s and XRF123s IP addresses. +Reset the DVAP into GMSK mode if FM data is received. +Fix for GMSK modems request for space. +Added DCS021. +Added XRF333. + +20130302 +-------- + +Make the DVAP initialisation less aggressive. +Updated to match ircDDB Gateway 20130302. +Added XRF150 + +20130309 +-------- + +Allow STN* callsigns as the my callsign. +Allow for switching DTMF blanking off for duplex. + +20130315 +-------- + +Added XRF038. +Allow for the new format of Danish callsigns. + +20130323 +-------- + +Updated XRF003s IP address. +Added the recording and playback of announcements. + +20130423 +-------- + +Change to the wiringPi library rather than bcm2835 library for controlling the RPi. +Added the generic D-Star Repeater and configuration program. +Allow incoming radio audio during the valid_wait period. +Don't reset the announcement timer on transmissions. +Added XRF851. + +20130503 +-------- + +Tweaks to the DVAP handling. +Made opening the serial port more aggressive for the DVAP. +Included the D-Star Repeater and Config program in the Windows package for the first time. + +20130508 +-------- + +Revert most of the serial port changes. + +20130515 +-------- + +Removed aggressive DVAP serial port opening. +Remove support for the DUTCH*Star 0.1.00 firmware. +Add GMSK modem support to the D-Star Repeater. +Added Arduino hardware support. + +20130523 +-------- + +Fixed GMSK Modem bug in the D-Star Repeater. +Updated XRF008s IP addresses. +Added XRF727. + +20130615 +-------- + +Added XRF250. +Add an optional startup delay for the DV-RPTR V1. +Add a sound card interface to the D-Star Repeater. + +20130622 +-------- + +Added XRF310. +Try overlapped serial I/O under Windows again. +Add the serial port PTT control to the D-Star Repeater. +Add /dev/ttyS* devices to the serial control list. +Added a receive level setting to the sound card driver in the D-Star Repeater. +Fixed bugs in the D-Star Repeater regarding end of incoming transmissions. + +20130702 +-------- + +Added daemon info to the log. +Changes to the internal buffering. +Remove the Analogue Repeater from the Repeater package. +Remove the DCSGateway, DExtraGateway, and ParrotController from the Repeater package. +Add a DC blocker to the sound card GMSK demodulator. +Implement GMSK PLL locking. + +20130726 +-------- + +Restore D-Star Repeater window position on startup. +Allow for the error reply to be disabled. + +20130831 +-------- + +Make DV-RPTR V2 handling better. +Catch exceptions and other C++ changes in the D-Star Repeater. +Add TX Delay to the DV-RPTR V2 modem in the D-Star Repeater. + +20130903 +-------- + +Removed the DC blocker from the sound card based repeaters. +Remove some of the C++ changes. + +20130904 +-------- + +Remove even more of the C++ changes. + +20131231 +-------- + +More cleanups. +Removed the white list functionality. +Added explicit support for the DV-RPTR V3 modem. + +20140117 +-------- + +Removed the DVAP Node and DV-RPTR Repeater. +Block Australian foundation class and French novice callsigns from using the repeaters. +Removed the optional delay on the DV-RPTR V1 modem. +Added the DVMEGA hardware. + +20140118 +-------- + +Fix endian-ness of the DVMEGA frequency command. + +20140128 +-------- + +Tweaks to the GMSK Modem controller in the D-Star Repeater. +More work on buffering in the D-Star Repeater for all modem types. + +20140129 +-------- + +Move the D-Star Repeater to its own package. +More GMSK Modem changes for better locking. + +20140208 +-------- + +GMSK Modem changes. +Improve the DVAP transmit handling. +Added the Split controller. + +20140217 +-------- + +Add extra logging to the Split driver. +Remove the FEC restoration in RX only and RX and TX modes. +Added TX Tail to the sound card driver. +Changed the timings in the GMSK driver. +Added GMSK driver modem reopen. +Write the configuration to a file under Windows. + +20140303 +-------- + +Experimental DC offset removal in the sound card driver. +Use names in the Split driver to allow for easier matching of addresses and ports. +Increase the number of receivers in the Split driver to five from three. +Added a modem watchdog to the DV-RPTR, DVAP and DVMEGA modem drivers. +Hold off writing the header until the TX is off for the DV-RPTR and DVMEGA modems. + +20140306 +-------- + +Removed the modem watchdog. +Fixed the DV-RPTR V2 and V3 modem drivers. +Fixed the DVMEGA driver. +Fix the network name going missing the config program. + +20140410 +-------- + +The statistics in the Split driver include all configured receivers. +Put the type of the repeater into the title bar of the GUI. +The first registration packet is after 10 seconds, and 30 seconds thereafter. +Fix many bugs in the Split driver. + +20140415 +-------- + +Change the DV-RPTR V2 & V3 handling back to the way it is done in the DV-RPTR Repeater. +Fixed watchdog timer handling in TX Only and TX and RX modes. +Reduce buffering in TX Only and TX and RX modes for local connections. + +20140424 +-------- + +Added /dev/ttyACM* for all serial devices under Linux. +Added PTT Inversion control for external hardware controllers. +Hold off the next D-Star transmission until driver is clear of previous data. + +20140520 +-------- + +Switch on internal and external speakers for XDVRPTR firmware/hardware. +DVMEGA messages say DVMega now. +Added power setting to the DVMEGA radio. +Add extra TX and RX entries for the Split driver, but not in the GUI. +Add two extra commands. + +20140521 +-------- + +Added DVAP error dump. + +20150208 +-------- + +Added ALSA for use on Linux, PortAudio is used elsewhere. +Dynamic serial port selection on Linux. +Allow for cross-band operating on the DVMEGA. +GPIO is potentially available on more platforms. +Assert the RTS pin for the DVMEGA at startup. +Getting the version at the startup of a DVMEGA is now more aggressive. +Added a sanity check to the reply from the DVMEGA. +Improved configuration and validation for the DVMEGA. + +20150210 +-------- + +Updated the dynamic serial port selection for /dev/ttyACM* +Updated the ALSA handing. + +20150213 +-------- + +Updated the ALSA handling once again. +Fixed a compile bug when defining GPIO. + +20150308 +-------- + +Added a .mk file for the Pi2. +Increased the network timeout from one second to two seconds. +More changes to the ALSA interface. + +20150404 +-------- + +Added method to ALSA interface for TX control in the sound card controller. + +20150615 +-------- + +Rearrange the source code slightly. +Allow a DVMEGA to be even slower at responding the first time. + +20150709 +-------- + +Allow for a late arriving data sync in the sound card repeater controller. +Add support for D-Star operation with the MMDVM. +Fix bugs in the DV-RPTR V1, V2, V3, and DVMEGA drivers. + +20151001 +-------- + +Added the RX and TX levels to the MMDVM driver and configuration. +Change the slow data header replacement in Gateway mode to be more intelligent. +Add sync regeneration to the sound card repeater driver. +Change the slipped sync detection in the sound card repeater driver. +Use a new matched filter in the GMSK modem. +More MMDVM driver changes. + +20151012 +-------- + +Change to the Windows serial controller for Windows 10. +Add overflow warnings for the MMDVM. + +2015xxxx +-------- + +Fix the TX Delay setting for the MMDVM. + +20180510 +-------- + +Port to wxWIdgets-3.0.x diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Common/AMBEFEC.cpp b/Common/AMBEFEC.cpp new file mode 100644 index 0000000..a672b51 --- /dev/null +++ b/Common/AMBEFEC.cpp @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2010,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "AMBEFEC.h" + +#include + +static const unsigned int PRNG_TABLE[] = { + 0x42CC47U, 0x19D6FEU, 0x304729U, 0x6B2CD0U, 0x60BF47U, 0x39650EU, 0x7354F1U, 0xEACF60U, 0x819C9FU, 0xDE25CEU, + 0xD7B745U, 0x8CC8B8U, 0x8D592BU, 0xF71257U, 0xBCA084U, 0xA5B329U, 0xEE6AFAU, 0xF7D9A7U, 0xBCC21CU, 0x4712D9U, + 0x4F2922U, 0x14FA37U, 0x5D43ECU, 0x564115U, 0x299A92U, 0x20A9EBU, 0x7B707DU, 0x3BE3A4U, 0x20D95BU, 0x6B085AU, + 0x5233A5U, 0x99A474U, 0xC0EDCBU, 0xCB5F12U, 0x918455U, 0xF897ECU, 0xE32E3BU, 0xAA7CC2U, 0xB1E7C9U, 0xFC561DU, + 0xA70DE6U, 0x8DBE73U, 0xD4F608U, 0x57658DU, 0x0E5E56U, 0x458DABU, 0x7E15B8U, 0x376645U, 0x2DFD86U, 0x64EC3BU, + 0x3F1F60U, 0x3481B4U, 0x4DA00FU, 0x067BCEU, 0x1B68B1U, 0xD19328U, 0xCA03FFU, 0xA31856U, 0xF8EB81U, 0xF9F2F8U, + 0xA26067U, 0xA91BB6U, 0xF19A59U, 0x9A6148U, 0x8372B6U, 0xC8E86FU, 0x9399DCU, 0x1A0291U, 0x619142U, 0x6DE9FFU, + 0x367A2CU, 0x7D2511U, 0x6484DAU, 0x2F1F0FU, 0x1E6DB4U, 0x55F6E1U, 0x0EA70AU, 0x061C96U, 0xDD0E45U, 0xB4D738U, + 0xAF64ABU, 0xE47F42U, 0xFDBE9DU, 0xB684ACU, 0xFE5773U, 0xC1E4A2U, 0x8AFD0DU, 0x932ED4U, 0xD814E3U, 0x81853AU, + 0x225EECU, 0x7A6945U, 0x31A112U, 0x2AB2EBU, 0x630974U, 0x785AB5U, 0x11E3CEU, 0x4A715BU, 0x402AA0U, 0x199B7DU, + 0x16C05EU, 0x6F5283U, 0xA4FB10U, 0xBFA8ECU, 0xF633B7U, 0xEC4012U, 0xADD8C9U, 0xD6EB1CU, 0xDD3027U, 0x84A1FAU, + 0xCF9E19U, 0xD64C80U, 0xBC4557U, 0xA7B62EU, 0x6E2DA1U, 0x311F50U, 0x38C68EU, 0x63D5BFU, 0x486E60U, 0x10BFE1U, + 0x5BAD1EU, 0x4A4647U, 0x0157F0U, 0x7ACC29U, 0x73BEEAU, 0x2825D7U, 0xA0940CU, 0xFBCFF9U, 0xB05C62U, 0x892426U, + 0xC6B3DDU, 0xDF3840U, 0x9449B3U, 0xCED3BEU, 0xE7804DU, 0xBC3B90U, 0xF5AA0BU, 0xE6D17EU, 0x2D43B5U, 0x345A04U, + 0x5EA9DBU, 0x07A202U, 0x0C7134U, 0x45C9FDU, 0x5EDA0AU, 0x310193U, 0x6830C4U, 0x62AA3DU, 0x3B59B2U, 0xB04043U, + 0xEB975CU, 0x82BCADU, 0x912E62U, 0xD8F7FBU, 0x82C489U, 0x895F54U, 0xF00FE7U, 0xFBBC2AU, 0xA2E771U, 0xE956C4U, + 0xF6CD1FU, 0x3F8FEAU, 0x0534E1U, 0x4C653CU, 0x17FE8FU, 0x1C4C52U, 0x4515A1U, 0x2E86A9U, 0x3FBD56U, 0x756C87U, + 0x6ED218U, 0x279179U, 0x7C0AA6U, 0xD53B17U, 0x8EE0C8U, 0x85F291U, 0xD94B36U, 0x9298EFU, 0xAB8318U, 0xE07301U, + 0xBB68DFU, 0xB2CB7CU, 0xE910A5U, 0xE101D2U, 0x92BB4BU, 0x59E8B4U, 0x407175U, 0x0B026AU, 0x12989BU, 0x792944U, + 0x2376EDU, 0x2EF5BAU, 0x758663U, 0x7C1ED5U, 0x078D0CU, 0x4EF6ABU, 0x5567F2U, 0x9F7C29U, 0xC68E9CU, 0xC51747U, + 0xBC6422U, 0xB7EFB9U, 0xECFD44U, 0xA50497U, 0xAF178AU, 0xD68C69U, 0xD97DB5U, 0x82670EU, 0xCBB45BU, 0x508D90U, + 0x190A25U, 0x63F0FEU, 0x68E3C7U, 0x317A10U, 0x3A09D9U, 0x6B926EU, 0x004237U, 0x1B79C8U, 0x53EA59U, 0x48B3B7U, + 0x811166U, 0xDE4A79U, 0xF5F988U, 0xAC6057U, 0xE733FEU, 0xFF89ADU, 0xB49830U, 0x8F4BC3U, 0xC6F00EU, 0x9DA135U, + 0x942FE0U, 0xC71C3BU, 0x4DC78FU, 0x3476C4U, 0x7F6C39U, 0x66BFAAU, 0x298657U, 0x725504U, 0x5B4E89U, 0x01FE72U, + 0x0835A3U, 0x53269CU, 0x189D4DU, 0x01CDC2U, 0xEA763BU, 0xF3A56DU, 0xB0BCD4U, 0xE80F13U, 0xE355CAU, 0x98C47DU, + 0x91AB24U, 0xCE38DBU, 0x87A35AU, 0x9CD3A5U, 0xD648F4U, 0xAF7B6FU, 0x24A292U, 0x7D3011U, 0x764B6DU, 0x2DDABEU, + 0x44D123U, 0x5E22D8U, 0x1FB09DU, 0x04A926U, 0x4F5AF3U, 0x064128U, 0x3DB105U, 0x70AAD6U, 0xAA392FU, 0xA1C4B8U, + 0xF8C7C0U, 0xD35D0FU, 0x8A2E9EU, 0xC1B761U, 0xDA44F0U, 0x925E8FU, 0x89CF4EU, 0xE8B4D1U, 0xB32728U, 0xB8FE7FU, + 0x61DCC6U, 0x2A4701U, 0x1614D8U, 0x5DADE2U, 0x46BE37U, 0x0F44DCU, 0x54D549U, 0x5D8E32U, 0x263DAFU, 0x2C237CU, + 0x75E291U, 0xBE5982U, 0xA74A7FU, 0xC493A4U, 0xDFA131U, 0x967A5AU, 0xCCCB8EU, 0xC1D835U, 0x9A02ECU, 0xF331BBU, + 0xE8B812U, 0xA3EBC5U, 0xBA507CU, 0x7080ABU, 0x099BC2U, 0x02285DU, 0x59718CU, 0x50C273U, 0x0B1862U, 0x4A1F8CU, + 0x70A655U, 0x3BF5C2U, 0x666FBBU, 0x6DDE68U, 0x3485C5U, 0x9F161EU, 0xC46F4BU, 0x8CFDF0U, 0x97C625U, 0xDE058EU, + 0xC59CD3U, 0xAEAE20U, 0xF775BCU, 0xFC647FU, 0xBD9F02U, 0xE70C91U, 0xCC1468U, 0x11E7B7U, 0x1AFC36U, 0x435B49U, + 0x080398U, 0x139027U, 0x7B63FEU, 0x607AF9U, 0x29E900U, 0x7293D6U, 0x79026FU, 0x00D930U, 0x0BEAF1U, 0xD3614EU, + 0x90119FU, 0x8B8AE4U, 0xC61969U, 0xBD609AU, 0xB4F247U, 0xEFA954U, 0xE518A9U, 0xBC0362U, 0xD7D0D6U, 0xCE7E8DU, + 0x856F18U, 0x1C94E3U, 0x578726U, 0x0D5F1DU, 0x24ECC0U, 0x7FF713U, 0x3E26AAU, 0x251D6DU, 0x6A8F14U, 0x53648BU, + 0x19757AU, 0x40AEB4U, 0xCB9CA5U, 0x90055AU, 0x9956C3U, 0xE2ED34U, 0xAB3C7DU, 0xB126EAU, 0xFA9513U, 0xA3D2C8U, + 0x886BFDU, 0xD9F836U, 0xD2A2E3U, 0x8D1359U, 0x454804U, 0x5EDBF7U, 0x37637AU, 0x2C3089U, 0x67ABD4U, 0x3E8847U, + 0x3551BAU, 0x4D6331U, 0x46B8C4U, 0x1D299FU, 0x54120EU, 0x5FC0E1U, 0x86D93BU, 0xE56A0EU, 0xFBB1D5U, 0xB2B600U, + 0xA94EABU, 0xE05DF6U, 0x9BE605U, 0x90B798U, 0xC92C6BU, 0xC3DE66U, 0x9AC7BDU, 0xD15448U, 0x6A3FD3U, 0x23ADA3U, + 0x78346CU, 0x7147F5U, 0x2BDC02U, 0x0EAD5BU, 0x553FFCU, 0x1EA425U, 0x07D5F2U, 0x4C4ECBU, 0x554C14U, 0x3EB3F5U, + 0xE4A26AU, 0xED799BU, 0xB6CA85U, 0xFFD25CU, 0xC421BFU, 0x8F3A22U, 0x96AB51U, 0xDC518CU, 0x895217U, 0x8289F2U, + 0xF9B8A9U, 0xF0231CU, 0x2BF1C7U, 0x62C80AU, 0x781B39U, 0x1320E5U, 0x4AB156U, 0x41EB8FU, 0x1848E0U, 0x13D771U, + 0x4886AEU, 0x203C5FU, 0x3B6F40U, 0x76F6A1U, 0xE5457EU, 0xAE1EE7U, 0xD7AC10U, 0xDCB549U, 0x8476EFU, 0x8FC536U, + 0xD49DE9U, 0x9D0ED8U, 0xA63513U, 0xEFE4A6U, 0xB4DF7DU, 0x3E0D00U, 0x779693U, 0x4CA75EU, 0x0568ADU, 0x527BB0U, + 0x59C34BU, 0x00109FU, 0x0A0B14U, 0x73FA61U, 0x38E0BAU, 0x23530FU, 0x6A88D4U, 0xB199DDU, 0x98322AU, 0xC260F3U, + 0xCBF944U, 0x908A0DU, 0xDB11F2U, 0xC28163U, 0xADFABDU, 0xBC694CU, 0xF65243U, 0xAD83BAU, 0xA40D6DU, 0x5F7EF4U, + 0x16E787U, 0x0DF44AU, 0x460EF1U, 0x5E1F24U, 0x15CC3FU, 0x6C77CAU, 0x676401U, 0x3C9CBDU, 0x359FEEU, 0x6A0413U, + 0x02F590U, 0x91EE4DU, 0xDA3C3EU, 0xC305A3U, 0x889658U, 0xF14D99U, 0xFA7F86U, 0xA1E677U, 0xE981E8U, 0xF21A10U, + 0xBB4BD7U, 0x80F1CEU, 0xCB6239U, 0x123BE0U, 0x1D885FU, 0x45921EU, 0x6641E1U, 0x3DE870U, 0x74BBAFU, 0x6F00C6U, + 0x261055U, 0x7DCBA8U, 0x57787AU, 0x0E2167U, 0x05B28CU, 0xCC8819U, 0x975BE2U, 0xBC52B7U, 0xE5E52CU, 0xEB37C9U, + 0xB20E12U, 0xF9DD2FU, 0xE8C6FCU, 0x837701U, 0xD8AD82U, 0xD1BE5AU, 0x0B0525U, 0x0244B4U, 0x79FE5BU, 0x322DCAU, + 0x2B3495U, 0x60876CU, 0x79DCFBU, 0x334C12U, 0x4C7745U, 0x45A4DCU, 0x1E3F23U, 0x175FF2U, 0xC4C0D8U, 0xAFF30DU, + 0xB72AF6U, 0xFCB96BU, 0xA5C338U, 0xAE5295U, 0xF54946U, 0xDCBABBU, 0x87A1A8U, 0xCF2165U, 0xD4DA9EU, 0x9FC90BU, + 0x223070U, 0x6922A4U, 0x30B92FU, 0x3348D6U, 0x695B01U, 0x20C038U, 0x1BB2EFU, 0x523B06U, 0x49EC99U, 0x02D7C8U, + 0x5B4777U, 0x713CA6U, 0xA8AF49U, 0xA3B650U, 0xF84586U, 0xB5DF7FU, 0xAE8CF8U, 0xC72581U, 0x9D3652U, 0x9EEDCFU, + 0xC75D34U, 0xCC0671U, 0xB5B5CAU, 0xFEAC1FU, 0x677EA4U, 0x2DC5F9U, 0x26D63AU, 0x7F1F86U, 0x142855U, 0x0DF2A8U, + 0x42E3B3U, 0x195872U, 0x108B8DU, 0x6AB31CU, 0x632063U, 0x307BAAU, 0xFBC83DU, 0xE201C4U, 0xA91393U, 0x90A82AU, + 0xDAF9E4U, 0x816A55U, 0x88D00AU, 0xD383DBU, 0xFA3A64U, 0xA569A5U, 0xEEE2DEU, 0x76D243U, 0x3D0D90U, 0x649E6DU, + 0x47E76EU, 0x1C7491U, 0x156E49U, 0x4E9DDEU, 0x0604B7U, 0x3D3720U, 0x76FDD9U, 0x6FEC06U, 0x2417B7U, 0xFD04F8U, + 0xF29D29U, 0x886F92U, 0xC1744FU, 0xDAC73CU, 0x939EB1U, 0x880C63U, 0xEBE79EU, 0xB2F285U, 0xB86970U, 0xE11ABBU, + 0xEA822EU, 0x311155U, 0x586AC0U, 0x43F92BU, 0x0A81F6U, 0x5412C5U, 0x5D111CU, 0x26E8CBU, 0x2D7B63U, 0x74213CU, + 0x3F90CDU, 0x2E8B52U, 0x645883U, 0xDFE36CU, 0x96F375U, 0xDD0882U, 0xC40B1BU, 0x8FD6CCU, 0xB464A5U, 0xFC7F3EU, + 0xA7AECBU, 0xAA9511U, 0xF10634U, 0xBA5CEFU, 0x83ED32U, 0x483681U, 0x5015DCU, 0x138D3FU, 0x48DEA2U, 0x616571U, + 0x3AF40CU, 0x33AF97U, 0x681D72U, 0x2246E9U, 0x3BD7B9U, 0x506C46U, 0x0D2FDFU, 0x869338U, 0xDDC061U, 0xD45BD6U, + 0xAF6A0FU, 0xE7B8C0U, 0xFC2371U, 0xBF102EU, 0xA6C9DFU, 0xEDDA40U, 0x943089U, 0x9FA1BFU, 0x459A66U, 0x0C4995U, + 0x175108U, 0x7AE243U, 0x6139B6U, 0x2A2A2DU, 0x73D3D8U, 0x79C183U, 0x204A26U, 0x0B3FFDU, 0x5AA420U, 0x111613U, + 0x8A4FDFU, 0xC3DC2CU, 0xF9A7B5U, 0xB034EAU, 0xEBAC5BU, 0xE0CF94U, 0xBD5465U, 0xF605FAU, 0xCFBEA3U, 0x85AC54U, + 0x9E55DDU, 0xD7C62AU, 0x0CDD73U, 0x252FCDU, 0x76361CU, 0x7DF5D3U, 0x3546E2U, 0x6E5B39U, 0x67A98CU, 0x1CB247U, + 0x57231AU, 0x4AD8A9U, 0x01CA74U, 0x191187U, 0xF2208AU, 0xA9AB50U, 0xA0F8A5U, 0xFB403EU, 0xF2D34BU, 0xA9A880U, + 0xCB393DU, 0xD262EEU, 0x99D0B7U, 0xC04B00U, 0xCB1AC9U, 0xB0B176U, 0x39E3A7U, 0x677EF8U, 0x2ECD58U, 0x359687U, + 0x7E277EU, 0x473D69U, 0x0CEEB0U, 0x55D557U, 0x5F04CEU, 0x0C8EBDU, 0x25BD60U, 0x7E64DBU, 0xB7771EU, 0xACCC05U, + 0xE51CF0U, 0xBF2F2AU, 0x90F497U, 0xC9E7D4U, 0xC25F09U, 0x9B9CBAU, 0xD08767U, 0xEB320CU, 0xA36999U, 0x38FB42U, + 0x7180B3U, 0x22112CU, 0x29AA45U, 0x50F9D2U, 0x1B610AU, 0x0202FDU, 0x4899E4U, 0x57080BU, 0x3E72DAU, 0x65E165U, + 0x6CFA34U, 0xB70BEBU, 0xBC104AU, 0xE4E295U, 0x8F7BECU, 0x96787FU, 0xD583B2U, 0x9E9740U, 0x870C5DU, 0xECFFA6U, + 0xF4E433U, 0xBF35F8U, 0xE00F8DU, 0x699C16U, 0x3265EBU, 0x1B6638U, 0x40F515U, 0x0A8DC6U, 0x131E1BU, 0x5845A0U, + 0x21F670U, 0x2A6E1FU, 0x791D8EU, 0x708651U, 0x2AD7E8U, 0xE37CAFU, 0xD8EE56U, 0x97B3C1U, 0x8E0018U, 0xC51B6FU, + 0x9CC9E6U, 0xB67019U, 0xEF23C8U, 0xE498F2U, 0xBF9927U, 0xF643ECU, 0xCD7051U, 0x04E902U, 0x563AFFU, 0x5D006CU, + 0x04D3A1U, 0x0FCA9AU, 0x72794FU, 0x39A2B4U, 0x228231U, 0x6A19EAU, 0x714E96U, 0x18F705U, 0x4324FCU, 0xC83E3BU, + 0x918D02U, 0xDADCD5U, 0xC2470CU, 0xA135B3U, 0xBABCF2U, 0xF30F4DU, 0xA8549EU, 0xA1C543U, 0xDEFF78U, 0xD42CBCU, + 0x0DB747U, 0x46C6D2U, 0x5F5C89U, 0x144F60U, 0x6FA6F7U, 0x66350EU, 0x2C0A59U, 0x35DAE0U, 0x7EC12FU, 0x0D32FEU, + 0x0429C1U, 0x5FB911U, 0xD642AEU, 0x895167U, 0xC3D8B0U, 0xFAAB89U, 0xB1315AU, 0xA8C0A7U, 0xE3DB24U, 0xB84879U, + 0x913382U, 0xCBA317U, 0x82F8FCU, 0x994BA9U, 0x50C213U, 0x4390CEU, 0x282F5DU, 0x713E30U, 0x7FCDE3U, 0x26565EU, + 0x2D0485U, 0x56BDD4U, 0x1FAE7BU, 0x0475AAU, 0x4DD555U, 0x17CE4CU, 0x9C1D9BU, 0xE52473U, 0xEEF7E4U, 0xB7CD1DU, + 0xF45E42U, 0xEF87E3U, 0x87B43CU, 0x986FADU, 0xD16FD2U, 0x8AD403U, 0x8103A8U, 0xD83A75U, 0x33A826U, 0x2BF39BU, + 0x604049U, 0x7B99A4U, 0x328ABFU, 0x49306AU, 0x407191U, 0x1BEA04U, 0x19D96FU, 0x4001F2U, 0x0FB201U, 0x36E9DCU, + 0xFD7ADFU, 0xE64326U, 0xAF91F9U, 0xF51249U, 0xDC2B16U, 0x87F8D7U, 0xCCE668U, 0xC517B1U, 0x9E8C46U, 0x97BF5FU, + 0xED6498U, 0xA67461U, 0x378FF6U, 0x788C8FU, 0x611514U, 0x0AE6F1U, 0x53FC2BU, 0x596F3EU, 0x0216C5U, 0x4B8508U, + 0x507FBBU, 0x396EE6U, 0x22F535U, 0xE99688U, 0xB10F43U, 0xBA1D36U, 0xC3E2ADU, 0xC07178U, 0x9B28C3U, 0xD69A8BU, + 0xCD817CU, 0x8570E5U, 0xFEEB12U, 0xF5E8CBU, 0xAC10C4U, 0x270335U, 0x7ED8EAU, 0x156B5BU, 0x0E7A14U, 0x46A0C5U, + 0x5D937AU, 0x144AA3U, 0x4F79D5U, 0x6CF35CU, 0x31228FU, 0x7A1932U, 0x628E69U, 0xA9D59CU, 0x926517U, 0xDBBEE2U, + 0x80ADB9U, 0x891424U, 0xD246D7U, 0xD8ED1AU, 0xA17C28U, 0xEA27F5U, 0xF3942EU, 0xB8CE8FU, 0xAB5FD0U, 0x466461U, + 0x1CB7BEU, 0x152F6FU, 0x4E1CC0U, 0x05D799U, 0x1CE66EU, 0x773DF7U, 0x7EAB00U, 0x249048U, 0x6D41D7U, 0x765A26U, + 0x1DA9F9U, 0x8431C8U, 0xCF0203U, 0x96C1DEU, 0x90D86DU, 0xCB6A30U, 0xA23193U, 0xB9A24EU, 0xF05B95U, 0xEB48A0U, + 0xA0D27AU, 0xD8A39FU, 0xD33804U, 0x0A9B79U, 0x01C3AAU, 0x5A5437U, 0x132FD4U, 0x28BC0DU, 0x60253AU, 0x3F57E3U, + 0x3CCC7CU, 0x65DD9DU, 0x4E26C2U, 0x172572U, 0xDCDDADU, 0xC64E64U, 0x8F5553U, 0x94A68AU, 0xFDBE7DU, 0xA66DE4U, + 0xADD68BU, 0xF4C75AU, 0xFE0CC1U, 0x873E34U, 0xC8A72FU, 0xDBD0C2U, 0x124B10U, 0x49998DU, 0x40A8FEU, 0x3A3323U, + 0x316088U, 0x68D95DU, 0x235B06U, 0x3A00B3U, 0x51B178U, 0x4AEA89U, 0x025816U, 0x59C36FU, 0xD092B8U, 0x8B2930U, + 0xE43AC7U, 0xF5E2DEU, 0xBEC121U, 0xA71AF0U, 0xED8B7FU, 0x94B40EU, 0x9F66D1U, 0xD45D68U, 0xCD8CBFU, 0x8617F6U, + 0x5F2545U, 0x75FC98U, 0x2EFF62U, 0x674467U, 0x7C959CU, 0x318F09U, 0x0A7CD2U, 0x4967AFU, 0x11D62CU, 0x1A8CD1U, + 0x431F02U, 0x48A69DU, 0xB3E5ECU, 0xFA7623U, 0xE10E9AU, 0xA99948U, 0xB20215U, 0xD971A6U, 0x80E86BU, 0x8BDA90U, + 0xD60185U, 0x9D907EU, 0x8FFBFBU, 0xE66920U, 0x7D705DU, 0x3483CEU, 0x6F9833U, 0x646BF1U, 0x1DF3E8U, 0x17E017U, + 0x4E1BC6U, 0x050A79U, 0x1E8038U, 0x5773E7U, 0x2C685EU, 0xA1BD89U, 0xFB86B0U, 0xF01477U, 0xA16D8EU, 0xCAFE19U, + 0xD365C1U, 0x9815AEU, 0x839E3FU, 0xCBCDC4U, 0x907611U, 0xB9E70AU, 0xE2BDE7U, 0x2B0E34U, 0x301789U, 0x7BE4DAU, + 0x477707U, 0x0C2FACU, 0x558C79U, 0x5E9743U, 0x0D4496U, 0x04786DU, 0x7FABE0U, 0x3730B3U, 0x3C014AU, 0xE7DADDU, + 0xEEE834U, 0x956163U, 0xDCB2FAU, 0xC78905U, 0x8D5BD4U, 0xD0427BU, 0xDBF12BU, 0xA22AB4U, 0xA93B4DU, 0xFA819AU, + 0xB3D2B3U, 0x287B64U, 0x40289DU, 0x5BB206U, 0x100153U, 0x495CB8U, 0x42CF2DU, 0x3BF4D6U, 0x70248BU, 0x6ABF19U, + 0x23CCF4U, 0x3C4527U, 0x75761AU, 0x8EACC1U, 0x853F44U, 0xD44EBFU, 0xDED5EEU, 0x87C751U, 0xEC3E80U, 0xF72D6FU, + 0xBEB676U, 0xE557A1U, 0xEC4D59U, 0xB6BECEU, 0x9DA527U, 0x443078U, 0x0BCAE9U, 0x12D916U, 0x594087U, 0x6033E8U, + 0x22A831U, 0x7948A2U, 0x70535FU, 0x2BC01CU, 0x62BBA1U, 0x592A7BU, 0x92308EU, 0x8AC395U, 0xC15A50U, 0x9809ABU, + 0xB3B336U, 0xECB245U, 0xE54998U, 0xBEDA1BU, 0xF681E6U, 0xED35F5U, 0x8E2E0CU, 0x87FDD3U, 0x5CC453U, 0x1556ACU, + 0x0E85FDU, 0x64AC42U, 0x3D7F8BU, 0x36447CU, 0x6FD665U, 0x640FB2U, 0x3B3C4BU, 0x52A7C4U, 0x48F7B5U, 0x014C2EU, + 0x9A9FFBU, 0xD19601U, 0xA0250CU, 0xAB7FFFU, 0xF2C822U, 0xB8D1B1U, 0xA302CCU, 0xEAB907U, 0xD1E9B2U, 0x987269U, + 0xC3411CU, 0xCC8897U, 0x141A42U, 0x3F61B8U, 0x66F2A1U, 0x2DCB56U, 0x3618DFU, 0x778208U, 0x2CB3F1U, 0x0468EEU, + 0x5F7B1FU, 0x5693D0U, 0x0D8041U, 0x461B3EU, 0xFFECE7U, 0xB4FD50U, 0xA94798U, 0xE314CFU, 0xB88D76U, 0xB17EADU, + 0xCA7508U, 0xC3E553U, 0x989EA6U, 0xDB0D3DU, 0xC396E8U, 0xA8E683U, 0x717D1EU, 0x7A0EEDU, 0x219730U, 0x288422U, + 0x736ECFU, 0x1BFF14U, 0x04A4A1U, 0x4F177AU, 0x56092BU, 0x1DD884U, 0x64635DU, 0xEF70EAU, 0xA589B3U, 0xF49B54U, + 0xFF50CDU, 0xA66312U, 0x8DFA62U, 0xD628FDU, 0x9F131CU, 0x8582C3U, 0xCCF9DAU, 0xF36A29U, 0xB8B2F4U, 0x618157U, + 0x6A020AU, 0x335999U, 0x79E864U, 0x4272BFU, 0x03259AU, 0x189C40U, 0x51CFB5U, 0x0A752EU, 0x216463U, 0x79BF90U, + 0x721C0DU, 0xAB47FEU, 0xE4D727U, 0xFDEC28U, 0x963FD9U, 0x8DA646U, 0xC594B7U, 0x9E4FE8U, 0x977E60U, 0xECA597U, + 0xAF264EU, 0xB61C79U, 0xFDCDA0U, 0x65D64FU, 0x2E61DCU, 0x553881U, 0x5CAA72U, 0x0351FBU, 0x0A400CU, 0x51FB55U, + 0x3BB9CAU, 0x22223AU, 0x6993B5U, 0x30C8C4U, 0x3B5B1BU, 0xE02B82U, 0xC1B075U, 0x9B23BCU, 0xD25A8BU, 0xC9C852U, + 0x82A3A9U, 0xBB303CU, 0xF42977U, 0xADDA82U, 0xA64418U, 0xFC55E5U, 0xB5AEE6U, 0x0EBD3BU, 0x4765C8U, 0x4CD655U, + 0x17DD2EU, 0x562EEBU, 0x6C3770U, 0x25A585U, 0x3E5EDEU, 0x754F6FU, 0x2C94A1U, 0x23A758U, 0x5A3F4FU, 0xD07C96U, + 0x8BC761U, 0xC254E8U, 0xD92C97U, 0xB0BF06U, 0xEBE0D9U, 0xE25138U, 0xB8CAA7U, 0xBB98DEU, 0xE22109U, 0x896291U, + 0x10F172U, 0x5BCB2FU, 0x401A94U, 0x0CA141U, 0x77B2BAU, 0x7E6BBFU, 0x255964U, 0x6E82D9U, 0x77130AU, 0x3C3877U, + 0x04EAF4U, 0x4FD129U, 0x9C40DBU, 0x959BC6U, 0xCEAC2DU, 0xE774FCU, 0xBC6763U, 0xF6DC12U, 0xEB8DCDU, 0xA00664U, + 0xF9F4B3U, 0xD2EF4AU, 0x895E5DU, 0x800584U, 0x5A972BU, 0x132EFBU, 0x287D84U, 0x63E615U, 0x7297CEU, 0x391D23U, + 0x608E30U, 0x6AF5CDU, 0x11641EU, 0x5C5E93U, 0x4789E0U, 0x0E903DU, 0x956386U, 0xFEF053U, 0xB6E879U, 0xAD0BACU, + 0xE41077U, 0xFF83CAU, 0xB47A99U, 0xCD6870U, 0xCE93E7U, 0x96823EU, 0x9D1941U, 0xC4EBD0U, 0x2BF23FU, 0x3031EEU, + 0x790A71U, 0x229909U, 0x2AC1CEU, 0x717677U, 0x5AEDA0U, 0x039C99U, 0x480646U, 0x515587U, 0x1AEC3CU, 0x296F69U, + 0xE13492U, 0xBA8607U, 0xB39FCCU, 0xEC4CB1U, 0xA77723U, 0x9EA7DEU, 0xD51C0DU, 0xCD0F00U, 0x86D4FBU, 0xDDF56EU, + 0xF46F95U, 0x2FBCD4U, 0x268D6BU, 0x7D52B2U, 0x374165U, 0x26F9DCU, 0x4D2A9BU, 0x141163U, 0x1FD2FCU, 0x40CA2DU, + 0x497952U, 0x3322D3U, 0x7AB32CU, 0xE108F5U, 0xAA5AE2U, 0xB3E31BU, 0xF8B098U, 0x812B65U, 0x8B8936U, 0xD0D08AU, + 0xD94341U, 0x8A7894U, 0xE3A9AFU, 0xF8377AU, 0xB74481U, 0x6FDD0CU, 0x64EE5FU, 0x3D35A2U, 0x163731U, 0x5F8ECCU, + 0x045DC7U, 0x0F4616U, 0x57B6E8U, 0x7CAD79U, 0x253E86U, 0x6EC7CFU, 0x7DD478U, 0xB426A1U, 0xCF2D76U, 0xC3BC5FU, + 0x984780U, 0x935571U, 0xCACCEEU, 0x81BBBFU, 0xB82054U, 0xF371C0U, 0xE9CB3BU, 0xA05826U, 0xFB33F5U, 0x52A218U, + 0x09B88BU, 0x424BF6U, 0x53D22DU, 0x198198U, 0x043A53U, 0x6F2A06U, 0x34F1BDU, 0x3DC260U, 0x664982U, 0x6FB81BU, + 0x15A24CU, 0xDE71F5U, 0xC7482AU, 0x8CDFCBU, 0x9505D4U, 0xDE3405U, 0xA5EFFAU, 0xA4FC63U, 0xFE5704U, 0xB387DDU, + 0xA8BC6AU, 0xC32FB2U, 0x5A7EE5U, 0x11C44CU, 0x489797U, 0x420E62U, 0x19BD79U, 0x30E6BCU, 0x6B6407U, 0x225DDAU, + 0x398EA9U, 0x703534U, 0x0A64F7U, 0x09FA0AU, 0xD4C910U, 0xDF10E5U, 0x86833EU, 0xCDB99BU, 0xE67A40U, 0xBE631BU, + 0xB590AEU, 0xEC8B75U, 0xA73BD0U, 0x9CE08BU, 0xD5F35EU, 0x8E0AE5U, 0x061828U, 0x5D835AU, 0x5660C7U, 0x277914U, + 0x68CAE9U, 0x7190E2U, 0x3A0113U, 0x20FECCU, 0x49ED7DU, 0x127522U, 0x1B06ABU, 0x40855CU, 0x8B9E85U, 0x926FB2U, + 0xF8F56AU, 0xE186A5U, 0xAA1F14U, 0xF10CCBU, 0xF0F7BAU, 0x8F6735U, 0x867CECU, 0xDC9F1FU, 0x978402U, 0x8E54F1U, + 0x45EF3CU, 0x7CFC8FU, 0x3705D2U, 0x6C1248U, 0x64C8BDU, 0x3FF976U, 0x566243U, 0x4DA198U, 0x069B45U, 0x1F0AF6U, + 0x5851BBU, 0x00E248U, 0xAB3BD1U, 0xF2090EU, 0xF9926FU, 0xA2C3F1U, 0xEB7800U, 0xD07B9FU, 0x98A1E6U, 0xC31021U, + 0xC84BB8U, 0x91D84FU, 0x9AEC96U, 0x6337A9U, 0x288468U, 0x369FB3U, 0x774E06U, 0x6C645DU, 0x05B7A9U, 0x4E2E22U, + 0x551DFFU, 0x1CC78CU, 0x47D611U, 0x4F2DF2U, 0x343E6FU, 0xBF8514U, 0xE655C1U, 0xAD5E5AU, 0xB4EDBFU, 0xDFB4E4U, + 0xC1265DU, 0x80DD8BU, 0xDBC852U, 0xD25375U, 0x8920ACU, 0xA2BA53U, 0xFB0BC2U, 0x31401DU, 0x28D33CU, 0x63AAE3U, + 0x18381AU, 0x11238DU, 0x4AD2E4U, 0x434933U, 0x195BABU, 0x56A058U, 0x6FB105U, 0x2C5AAEU, 0x35C97BU, 0xFED9A0U, + 0xA52295U, 0x8D314EU, 0xD6ECA3U, 0x9F5E30U, 0x84456DU, 0xCFB6DEU, 0xD6AF03U, 0xBD2CE9U, 0xE556FCU, 0xEEC707U, + 0xB71CD6U, 0x382F59U, 0x43B720U, 0x02E4F7U, 0x195F4EU, 0x51CC99U, 0x0AA550U, 0x013767U, 0x786CBEU, 0x73DD01U, + 0x2AC6D1U, 0x61159EU, 0x7BA92FU, 0x92BAF4U, 0x896109U, 0xC0521AU, 0x9F9AF7U, 0x942924U, 0xC532B9U, 0xEFE3C2U, + 0xA6D807U, 0xFD0ABCU, 0xF69369U, 0xAFA033U, 0x44738EU, 0x5D694DU, 0x17C8F0U, 0x0C93A3U, 0x45207AU, 0x1EF9C5U, + 0x37EB04U, 0x6850FBU, 0x6305EAU, 0x3B9E15U, 0x782DC4U, 0x41774BU, 0x8AF633U, 0xD18DE4U, 0xD81E5DU, 0x83A69AU, + 0x8AF583U, 0xF06E7CU, 0xBB5FADU, 0xA28416U, 0xE99653U, 0xF06D88U, 0x9FEC35U, 0xC4F7E6U, 0x4C059AU, 0x1F1C19U, + 0x56EFC4U, 0x4D743FU, 0x24612AU, 0x3F9BD1U, 0x748814U, 0x2C13AFU, 0x27F276U, 0x5EE861U, 0x553B88U, 0x0E0A5FU, + 0xC791E6U, 0xD8E2B0U, 0x907A69U, 0xABE9C6U, 0xE09217U, 0xB10168U, 0xBA48F9U, 0xE3FA26U, 0x8861CFU, 0x9230D8U, + 0xDB8B21U, 0xC099B2U, 0x09644FU, 0x52F704U, 0x79AC90U, 0x201F6BU, 0x2E17BEU, 0x77C495U, 0x3CFF48U, 0x172E9BU, + 0x4E9426U, 0x0D8775U, 0x145E98U, 0x5E6D03U, 0xC5F6D6U, 0xAC242DU, 0xF70D3CU, 0xFEDED2U, 0xA5C543U, 0xAE74BCU, + 0xD62EE5U, 0x9D9D72U, 0x80029BU, 0xCB534CU, 0x90E175U, 0x19BAAAU, 0x6A3B6BU, 0x6280D4U, 0x39D385U, 0x724B7AU, + 0x6B78E2U, 0x00A321U, 0x19101CU, 0x5248CFU, 0x0ADB30U, 0x01F0A9U, 0x5A21CEU, 0xB73A17U, 0xACC880U, 0xE55179U, + 0xFE42A6U, 0xB4B987U, 0xC5AF58U, 0xCE1688U, 0x97C533U, 0x9CCE76U, 0xC73F8DU, 0x8E2510U, 0xB4B6C3U, 0x7D4FFEU, + 0x665C3DU, 0x2DC7C0U, 0x70B55BU, 0x5B2C2EU, 0x025FF5U, 0x49D470U, 0x53448AU, 0x1A3FD7U, 0x09AC64U, 0x60BDBDU, + 0x3B467AU, 0xB0D043U, 0xE98B9CU, 0xE33A2DU, 0x9A21E2U, 0xD1C3B3U, 0xCA5A0CU, 0x8709DDU, 0xDCB222U, 0xF5A3AAU, + 0xBF79DDU, 0xA44A04U, 0xEDD193U, 0x3E006AU, 0x373B21U, 0x4CF994U, 0x47C04FU, 0x1F53DAU, 0x5488A1U, 0x4DB86CU, + 0x2623DFU, 0x7D7402U, 0x70CF50U, 0x2B9EFDU, 0x232426U, 0xF8A7D3U, 0x91FEC8U, 0x8A4D39U, 0xC117F6U, 0xD0866FU, + 0x9B3D18U, 0xE36EC1U, 0xE8F576U, 0xB3C5BFU, 0xBA1629U, 0xE1BD50U, 0xA8EC8FU, 0x17763EU, 0x5D45F1U, 0x049CA0U, + 0x0F8F1FU, 0x5630C6U, 0x7DE225U, 0x26FB38U, 0x6F08CBU, 0x7D0316U, 0x34B28DU, 0x2F68E9U, 0xC47B72U, 0x9DC287U, + 0x96915CU, 0xCF0B41U, 0x85F8A2U, 0xBAE17FU, 0xF372CCU, 0xE81991U, 0xA1894AU, 0xFAF2EBU, 0xF16134U, 0x89F845U, + 0x0A8ADBU, 0x53153AU, 0x1806E5U, 0x03FF7CU, 0x6A7C0BU, 0x312692U, 0x399775U, 0x628CACU, 0x6D7FB3U, 0x34EE42U, + 0x5FF49DU, 0x56073CU, 0x8D1C67U, 0x87CDBBU, 0xDEE708U, 0xB574D5U, 0xA4ADB6U, 0xEF9E2BU, 0xF605D0U, 0xBD7545U, + 0xE6EE0EU, 0xCE39FBU, 0x950260U, 0xD8929DU, 0x43D9CEU, 0x086A47U, 0x31B3B1U, 0x7AA068U, 0x221ADFU, 0x294B86U, + 0x72F049U, 0x73E3F8U, 0x083927U, 0x418856U, 0x5AC3C9U, 0x105020U, 0xC969B7U, 0xE2BBEEU, 0xBF2019U, 0xB41181U, + 0xEFCA6AU, 0xA6FD3FU, 0xBC27A4U, 0xD53651U, 0xCE9D9AU, 0x854EA7U, 0xDC5E74U, 0xDFE5A9U, 0x26B61AU, 0x6C0D57U, + 0x77DCECU, 0x3EC639U, 0x2575C3U, 0x682CD6U, 0x13AF1DU, 0x1855ECU, 0x404473U, 0x4BDF8AU, 0x12ACDDU, 0xF93754U, + 0xE207A3U, 0xABD87AU, 0xF04B45U, 0xF03284U, 0xABB05BU, 0x80ABEBU, 0xD95AB4U, 0x92C10DU, 0x8FD2CEU, 0xC42833U, + 0xEC3920U, 0x37C2FDU, 0x7C5106U, 0x654883U, 0x2EAAF8U, 0x37B12DU, 0x5C20B6U, 0x065B42U, 0x07C909U, 0x5C12B4U, + 0x152367U, 0x2EB4FAU, 0x65CF19U, 0xFC5F40U, 0xB294FFU, 0xEBA72EU, 0xE03ED1U, 0x9B6CD0U, 0x92D70FU, 0xC944F6U, + 0x801D60U, 0x9AAE19U, 0xF1F4DEU, 0xA85547U, 0xAB4EB8U, 0x729DE9U, 0x792456U, 0x223697U, 0x4BED0CU, 0x55DE71U, + 0x1C03A2U, 0x07910FU, 0x4CAADCU, 0x356BA0U, 0x3E5033U, 0x67C3EEU, 0x2D9B05U, 0xB62810U, 0xFFF3EBU, 0xC4E03EU, + 0x8558A5U, 0xDE0B48U, 0xD5905BU, 0x8D71A2U, 0xA26A75U, 0xFBD8ECU, 0xB08982U, 0xAB1253U, 0xE2A1ECU, 0x79FB3FU, + 0x116E52U, 0x4A15C9U, 0x43861CU, 0x188FE7U, 0x537DF2U, 0x62E619U, 0x29D7C0U, 0x310C57U, 0x7A1F2EU, 0x25E5B8U, + 0xAC7451U, 0xC76F86U, 0xDE9C9FU, 0x959460U, 0xCF27B1U, 0xC6FC1EU, 0xBDEDCFU, 0xF416B0U, 0xEF0429U, 0xA49FEEU, + 0xBDEA17U, 0xFF7104U, 0x06A3F8U, 0x0D8A63U, 0x5219A6U, 0x5B62DDU, 0x00F348U, 0x6969B3U, 0x731A6EU, 0x38816DU, + 0x61D090U, 0x6A6343U, 0x33F9FEU, 0x18B8A5U, 0xC30340U, 0x8B10DAU, 0x98E80BU, 0xD1FB74U, 0xEA20F5U, 0xA5930AU, + 0xFC8E93U, 0xF75CC4U, 0xAF673DU, 0xA4E6BAU, 0xDF3D43U, 0x960F9CU, 0x0DD68DU, 0x44E572U, 0x1F7EB2U, 0x35AD09U, + 0x6C9554U, 0x6746A7U, 0x365D3AU, 0x7DFCF9U, 0x64A6C4U, 0x0B351FU, 0x118CEAU, 0x58DF61U, 0x836434U, 0x8A36CFU, + 0xF1AB5BU, 0xBA18A0U, 0xA343EDU, 0xE8C27EU, 0xF0F887U, 0xBB2B50U, 0xC03A69U, 0xC9C1A6U, 0x9A5317U, 0x9368C8U, + 0x5CB919U, 0x26A226U, 0x2F01EFU, 0x74D919U, 0x3DCA80U, 0x2631D7U, 0x6D223EU, 0x54BAA1U, 0x1E4950U, 0x47520BU, + 0x4CA79EU, 0x97BC75U, 0xBE3EA8U, 0xED479BU, 0xA4D446U, 0xBA4FF5U, 0xF13C39U, 0xE8A46AU, 0x83D7D7U, 0xDA4C0CU, + 0xD1DDF9U, 0x8AA7F2U, 0xC22427U, 0x793DDCU, 0x30CE45U, 0x2B5522U, 0x6007FBU, 0x39BE6CU, 0x32AD95U, 0x42560BU, + 0x4D426AU, 0x16D1B5U, 0x5F3A04U, 0x442BDBU, 0x2DF082U, 0xF6C225U, 0xFE59FCU, 0xA5880FU, 0xAEB312U, 0xF761C9U, + 0x9C582CU, 0x85CBB7U, 0xCE00C3U, 0xD43118U, 0x9DAB9DU, 0xEAF866U, 0xE3437BU, 0x381288U, 0x738955U, 0x6A3BF6U, + 0x2066ABU, 0x19D570U, 0x52DEC1U, 0x090E1EU, 0x00B5FFU, 0x5BE6E1U, 0x727D38U, 0x284CCFU, 0x639656U, 0xFA8531U, + 0xBD3CA8U, 0xD4EF77U, 0xCFC586U, 0x841489U, 0x9C0F78U, 0xD7BCA7U, 0x8E671EU, 0xA5774DU, 0xFE8481U, 0xF79F32U, + 0xAC0AEFU, 0x65F09CU, 0x5FF301U, 0x144ACAU, 0x0D193FU, 0x468224U, 0x13F0D1U, 0x18694AU, 0x63FA87U, 0x2B81F4U, + 0x30106DU, 0x790A9BU, 0xE2E952U, 0x8970CDU, 0xD003BCU, 0xDB9963U, 0x838AD2U, 0x88731DU, 0xD1E064U, 0xBAFFF3U, + 0xA10F2AU, 0xEC049DU, 0xBFD7D4U, 0xB7EE2BU, 0x4C7CBBU, 0x478760U, 0x1E9415U, 0x554D9EU, 0x4C7E6BU, 0x07E4B0U, + 0x3D35ADU, 0x741E4EU, 0x2F8D93U, 0x26FC20U, 0x7D667DU, 0x16B586U, 0x8B8E02U, 0xC91FD9U, 0xD0456CU, 0x9BF237U, + 0xC0EBCEU, 0xE92849U, 0xB29390U, 0xBBC3E7U, 0xE1787EU, 0xAA6B81U, 0x93B040U, 0xD8005FU, 0x411BAEU, 0x0AC870U, + 0x51F1D1U, 0x5D328EU, 0x362837U, 0x6799E0U, 0x6C4239U, 0x37711AU, 0x3EABC7U, 0x45BA3CU, 0x0D01A9U, 0x16D6F2U, + 0xDDCF17U, 0xC46D8CU, 0x8F3670U, 0xF6A723U, 0xFD5CBCU, 0xA74F5DU, 0xEAF582U, 0xF1A43BU, 0x903768U, 0x8B0CC5U, + 0xC0DC16U, 0x9957CBU, 0x1324F0U, 0x4ABD25U, 0x61AECEU, 0x38545AU, 0x73C701U, 0x68FEF4U, 0x212D6FU, 0x5B3382U, + 0x52C2D1U, 0x09494CU, 0x065ABFU, 0xDFA126U, 0x9CB149U, 0xA56A98U, 0xEE5927U, 0xF4C0F6U, 0xBD33B8U, 0xE62901U, + 0xCFB8D6U, 0x94D32FU, 0x9F40B8U, 0xC69AF1U, 0x8CAB0EU, 0x15309FU, 0x7E6360U, 0x21DA31U, 0x2848BAU, 0x733747U, + 0x72A6D4U, 0x08EDA8U, 0x435F7BU, 0x5A4CD6U, 0x119505U, 0x082658U, 0x433DE3U, 0xB8ED26U, 0xB0D6DDU, 0xEB05C8U, + 0xA2BC13U, 0xA9BEEAU, 0xD6656DU, 0xDF5614U, 0x848F82U, 0xC41C5BU, 0xDF26A4U, 0x94F7A5U, 0xADCC5AU, 0x665B8BU, + 0x3F1234U, 0x34A0EDU, 0x6E7BAAU, 0x076813U, 0x1CD1C4U, 0x55833DU, 0x4E1836U, 0x03A9E2U, 0x58F219U, 0x72418CU, + 0x2B09F7U, 0xA89A72U, 0xF1A1A9U, 0xBA7254U, 0x81EA47U, 0xC899BAU, 0xD20279U, 0x9B13C4U, 0xC0E09FU, 0xCB7E4BU, + 0xB25FF0U, 0xF98431U, 0xE4974EU, 0x2E6CD7U, 0x35FC00U, 0x5CE7A9U, 0x07147EU, 0x060D07U, 0x5D9F98U, 0x56E449U, + 0x0E65A6U, 0x659EB7U, 0x7C8D49U, 0x371790U, 0x6C6623U, 0xE5FD6EU, 0x9E6EBDU, 0x921600U, 0xC985D3U, 0x82DAEEU, + 0x9B7B25U, 0xD0E0F0U, 0xE1924BU, 0xAA091EU, 0xF158F5U, 0xF9E369U, 0x22F1BAU, 0x4B28C7U, 0x509B54U, 0x1B80BDU, + 0x024162U, 0x497B53U, 0x01A88CU, 0x3E1B5DU, 0x7502F2U, 0x6CD12BU, 0x27EB1CU, 0x7E7AC5U, 0xDDA113U, 0x8596BAU, + 0xCE5EEDU, 0xD54D14U, 0x9CF68BU, 0x87A54AU, 0xEE1C31U, 0xB58EA4U, 0xBFD55FU, 0xE66482U, 0xE93FA1U, 0x90AD7CU, + 0x5B04EFU, 0x405713U, 0x09CC48U, 0x13BFEDU, 0x522736U, 0x2914E3U, 0x22CFD8U, 0x7B5E05U, 0x3061E6U, 0x29B37FU, + 0x43BAA8U, 0x5849D1U, 0x91D25EU, 0xCEE0AFU, 0xC73971U, 0x9C2A40U, 0xB7919FU, 0xEF401EU, 0xA452E1U, 0xB5B9B8U, + 0xFEA80FU, 0x8533D6U, 0x8C4115U, 0xD7DA28U, 0x5F6BF3U, 0x043006U, 0x4FA39DU, 0x76DBD9U, 0x394C22U, 0x20C7BFU, + 0x6BB64CU, 0x312C41U, 0x187FB2U, 0x43C46FU, 0x0A55F4U, 0x192E81U, 0xD2BC4AU, 0xCBA5FBU, 0xA15624U, 0xF85DFDU, + 0xF38ECBU, 0xBA3602U, 0xA125F5U, 0xCEFE6CU, 0x97CF3BU, 0x9D55C2U, 0xC4A64DU, 0x4FBFBCU, 0x1468A3U, 0x7D4352U, + 0x6ED19DU, 0x270804U, 0x7D3B76U, 0x76A0ABU, 0x0FF018U, 0x0443D5U, 0x5D188EU, 0x16A93BU, 0x0932E0U, 0xC07015U, + 0xFACB1EU, 0xB39AC3U, 0xE80170U, 0xE3B3ADU, 0xBAEA5EU, 0xD17956U, 0xC042A9U, 0x8A9378U, 0x912DE7U, 0xD86E86U, + 0x83F559U, 0x2AC4E8U, 0x711F37U, 0x7A0D6EU, 0x26B4C9U, 0x6D6710U, 0x547CE7U, 0x1F8CFEU, 0x449720U, 0x4D3483U, + 0x16EF5AU, 0x1EFE2DU, 0x6D44B4U, 0xA6174BU, 0xBF8E8AU, 0xF4FD95U, 0xED6764U, 0x86D6BBU, 0xDC8912U, 0xD10A45U, + 0x8A799CU, 0x83E12AU, 0xF872F3U, 0xB10954U, 0xAA980DU, 0x6083D6U, 0x397163U, 0x3AE8B8U, 0x439BDDU, 0x481046U, + 0x1302BBU, 0x5AFB68U, 0x50E875U, 0x297396U, 0x26824AU, 0x7D98F1U, 0x344BA4U, 0xAF726FU, 0xE6F5DAU, 0x9C0F01U, + 0x971C38U, 0xCE85EFU, 0xC5F626U, 0x946D91U, 0xFFBDC8U, 0xE48637U, 0xAC15A6U, 0xB74C48U, 0x7EEE99U, 0x21B586U, + 0x0A0677U, 0x539FA8U, 0x18CC01U, 0x007652U, 0x4B67CFU, 0x70B43CU, 0x390FF1U, 0x625ECAU, 0x6BD01FU, 0x38E3C4U, + 0xB23870U, 0xCB893BU, 0x8093C6U, 0x994055U, 0xD679A8U, 0x8DAAFBU, 0xA4B176U, 0xFE018DU, 0xF7CA5CU, 0xACD963U, + 0xE762B2U, 0xFE323DU, 0x1589C4U, 0x0C5A92U, 0x4F432BU, 0x17F0ECU, 0x1CAA35U, 0x673B82U, 0x6E54DBU, 0x31C724U, + 0x785CA5U, 0x632C5AU, 0x29B70BU, 0x508490U, 0xDB5D6DU, 0x82CFEEU, 0x89B492U, 0xD22541U, 0xBB2EDCU, 0xA1DD27U, + 0xE04F62U, 0xFB56D9U, 0xB0A50CU, 0xF9BED7U, 0xC24EFAU, 0x8F5529U, 0x55C6D0U, 0x5E3B47U, 0x07383FU, 0x2CA2F0U, + 0x75D161U, 0x3E489EU, 0x25BB0FU, 0x6DA170U, 0x7630B1U, 0x174B2EU, 0x4CD8D7U, 0x470180U, 0x9E2339U, 0xD5B8FEU, + 0xE9EB27U, 0xA2521DU, 0xB941C8U, 0xF0BB23U, 0xAB2AB6U, 0xA271CDU, 0xD9C250U, 0xD3DC83U, 0x8A1D6EU, 0x41A67DU, + 0x58B580U, 0x3B6C5BU, 0x205ECEU, 0x6985A5U, 0x333471U, 0x3E27CAU, 0x65FD13U, 0x0CCE44U, 0x1747EDU, 0x5C143AU, + 0x45AF83U, 0x8F7F54U, 0xF6643DU, 0xFDD7A2U, 0xA68E73U, 0xAF3D8CU, 0xF4E79DU, 0xB5E073U, 0x8F59AAU, 0xC40A3DU, + 0x999044U, 0x922197U, 0xCB7A3AU, 0x60E9E1U, 0x3B90B4U, 0x73020FU, 0x6839DAU, 0x21FA71U, 0x3A632CU, 0x5151DFU, + 0x088A43U, 0x039B80U, 0x4260FDU, 0x18F36EU, 0x33EB97U, 0xEE1848U, 0xE503C9U, 0xBCA4B6U, 0xF7FC67U, 0xEC6FD8U, + 0x849C01U, 0x9F8506U, 0xD616FFU, 0x8D6C29U, 0x86FD90U, 0xFF26CFU, 0xF4150EU, 0x2C9EB1U, 0x6FEE60U, 0x74751BU, + 0x39E696U, 0x429F65U, 0x4B0DB8U, 0x1056ABU, 0x1AE756U, 0x43FC9DU, 0x282F29U, 0x318172U, 0x7A90E7U, 0xE36B1CU, + 0xA878D9U, 0xF2A0E2U, 0xDB133FU, 0x8008ECU, 0xC1D955U, 0xDAE292U, 0x9570EBU, 0xAC9B74U, 0xE68A85U, 0xBF514BU, + 0x34635AU, 0x6FFAA5U, 0x66A93CU, 0x1D12CBU, 0x54C382U, 0x4ED915U, 0x056AECU, 0x5C2D37U, 0x779402U, 0x2607C9U, + 0x2D5D1CU, 0x72ECA6U, 0xBAB7FBU, 0xA12408U, 0xC89C85U, 0xD3CF76U, 0x98542BU, 0xC177B8U, 0xCAAE45U, 0xB29CCEU, + 0xB9473BU, 0xE2D660U, 0xABEDF1U, 0xA03F1EU, 0x7926C4U, 0x1A95F1U, 0x044E2AU, 0x4D49FFU, 0x56B154U, 0x1FA209U, + 0x6419FAU, 0x6F4867U, 0x36D394U, 0x3C2199U, 0x653842U, 0x2EABB7U, 0x95C02CU, 0xDC525CU, 0x87CB93U, 0x8EB80AU, + 0xD423FDU, 0xF152A4U, 0xAAC003U, 0xE15BDAU, 0xF82A0DU, 0xB3B134U, 0xAAB3EBU, 0xC14C0AU, 0x1B5D95U, 0x128664U, + 0x49357AU, 0x002DA3U, 0x3BDE40U, 0x70C5DDU, 0x6954AEU, 0x23AE73U, 0x76ADE8U, 0x7D760DU, 0x064756U, 0x0FDCE3U, + 0xD40E38U, 0x9D37F5U, 0x87E4C6U, 0xECDF1AU, 0xB54EA9U, 0xBE1470U, 0xE7B71FU, 0xEC288EU, 0xB77951U, 0xDFC3A0U, + 0xC490BFU, 0x89095EU, 0x1ABA81U, 0x51E118U, 0x2853EFU, 0x234AB6U, 0x7B8910U, 0x703AC9U, 0x2B6216U, 0x62F127U, + 0x59CAECU, 0x101B59U, 0x4B2082U, 0xC1F2FFU, 0x88696CU, 0xB358A1U, 0xFA9752U, 0xAD844FU, 0xA63CB4U, 0xFFEF60U, + 0xF5F4EBU, 0x8C059EU, 0xC71F45U, 0xDCACF0U, 0x95772BU, 0x4E6622U, 0x67CDD5U, 0x3D9F0CU, 0x3406BBU, 0x6F75F2U, + 0x24EE0DU, 0x3D7E9CU, 0x520542U, 0x4396B3U, 0x09ADBCU, 0x527C45U, 0x5BF292U, 0xA0810BU, 0xE91878U, 0xF20BB5U, + 0xB9F10EU, 0xA1E0DBU, 0xEA33C0U, 0x938835U, 0x989BFEU, 0xC36342U, 0xCA6011U, 0x95FBECU, 0xFD0A6FU, 0x6E11B2U, + 0x25C3C1U, 0x3CFA5CU, 0x7769A7U, 0x0EB266U, 0x058079U, 0x5E1988U, 0x167E17U, 0x0DE5EFU, 0x44B428U, 0x7F0E31U, + 0x349DC6U, 0xEDC41FU, 0xE277A0U, 0xBA6DE1U, 0x99BE1EU, 0xC2178FU, 0x8B4450U, 0x90FF39U, 0xD9EFAAU, 0x823457U, + 0xA88785U, 0xF1DE98U, 0xFA4D73U, 0x3377E6U, 0x68A41DU, 0x43AD48U, 0x1A1AD3U, 0x14C836U, 0x4DF1EDU, 0x0622D0U, + 0x173903U, 0x7C88FEU, 0x27527DU, 0x2E41A5U, 0xF4FADAU, 0xFDBB4BU, 0x8601A4U, 0xCDD235U, 0xD4CB6AU, 0x9F7893U, + 0x862304U, 0xCCB3EDU, 0xB388BAU, 0xBA5B23U, 0xE1C0DCU, 0xE8A00DU, 0x3B3F27U, 0x500CF2U, 0x48D509U, 0x034694U, + 0x5A3CC7U, 0x51AD6AU, 0x0AB6B9U, 0x234544U, 0x785E57U, 0x30DE9AU, 0x2B2561U, 0x6036F4U, 0xDDCF8FU, 0x96DD5BU, + 0xCF46D0U, 0xCCB729U, 0x96A4FEU, 0xDF3FC7U, 0xE44D10U, 0xADC4F9U, 0xB61366U, 0xFD2837U, 0xA4B888U, 0x8EC359U, + 0x5750B6U, 0x5C49AFU, 0x07BA79U, 0x4A2080U, 0x517307U, 0x38DA7EU, 0x62C9ADU, 0x611230U, 0x38A2CBU, 0x33F98EU, + 0x4A4A35U, 0x0153E0U, 0x98815BU, 0xD23A06U, 0xD929C5U, 0x80E079U, 0xEBD7AAU, 0xF20D57U, 0xBD1C4CU, 0xE6A78DU, + 0xEF7472U, 0x954CE3U, 0x9CDF9CU, 0xCF8455U, 0x0437C2U, 0x1DFE3BU, 0x56EC6CU, 0x6F57D5U, 0x25061BU, 0x7E95AAU, + 0x772FF5U, 0x2C7C24U, 0x05C59BU, 0x5A965AU, 0x111D21U, 0x892DBCU, 0xC2F26FU, 0x9B6192U, 0xB81891U, 0xE38B6EU, + 0xEA91B6U, 0xB16221U, 0xF9FB48U, 0xC2C8DFU, 0x890226U, 0x9013F9U, 0xDBE848U, 0x02FB07U, 0x0D62D6U, 0x77906DU, + 0x3E8BB0U, 0x2538C3U, 0x6C614EU, 0x77F39CU, 0x141861U, 0x4D0D7AU, 0x47968FU, 0x1EE544U, 0x157DD1U, 0xCEEEAAU, + 0xA7953FU, 0xBC06D4U, 0xF57E09U, 0xABED3AU, 0xA2EEE3U, 0xD91734U, 0xD2849CU, 0x8BDEC3U, 0xC06F32U, 0xD174ADU, + 0x9BA77CU, 0x201C93U, 0x690C8AU, 0x22F77DU, 0x3BF4E4U, 0x702933U, 0x4B9B5AU, 0x0380C1U, 0x585134U, 0x556AEEU, + 0x0EF9CBU, 0x45A310U, 0x7C12CDU, 0xB7C97EU, 0xAFEA23U, 0xEC72C0U, 0xB7215DU, 0x9E9A8EU, 0xC50BF3U, 0xCC5068U, + 0x97E28DU, 0xDDB916U, 0xC42846U, 0xAF93B9U, 0xF2D020U, 0x796CC7U, 0x223F9EU, 0x2BA429U, 0x5095F0U, 0x18473FU, + 0x03DC8EU, 0x40EFD1U, 0x593620U, 0x1225BFU, 0x6BCF76U, 0x605E40U, 0xBA6599U, 0xF3B66AU, 0xE8AEF7U, 0x851DBCU, + 0x9EC649U, 0xD5D5D2U, 0x8C2C27U, 0x863E7CU, 0xDFB5D9U, 0xF4C002U, 0xA55BDFU, 0xEEE9ECU, 0x75B020U, 0x3C23D3U, + 0x06584AU, 0x4FCB15U, 0x1453A4U, 0x1F306BU, 0x42AB9AU, 0x09FA05U, 0x30415CU, 0x7A53ABU, 0x61AA22U, 0x2839D5U, + 0xF3228CU, 0xDAD032U, 0x89C9E3U, 0x820A2CU, 0xCAB91DU, 0x91A4C6U, 0x985673U, 0xE34DB8U, 0xA8DCE5U, 0xB52756U, + 0xFE358BU, 0xE6EE78U, 0x0DDF75U, 0x5654AFU, 0x5F075AU, 0x04BFC1U, 0x0D2CB4U, 0x56577FU, 0x34C6C2U, 0x2D9D11U, + 0x662F48U, 0x3FB4FFU, 0x34E536U, 0x4F4E89U, 0xC61C58U, 0x988107U, 0xD132A7U, 0xCA6978U, 0x81D881U, 0xB8C296U, + 0xF3114FU, 0xAA2AA8U, 0xA0FB31U, 0xF37142U, 0xDA429FU, 0x819B24U, 0x4888E1U, 0x5333FAU, 0x1AE30FU, 0x40D0D5U, + 0x6F0B68U, 0x36182BU, 0x3DA0F6U, 0x646345U, 0x2F7898U, 0x14CDF3U, 0x5C9666U, 0xC704BDU, 0x8E7F4CU, 0xDDEED3U, + 0xD655BAU, 0xAF062DU, 0xE49EF5U, 0xFDFD02U, 0xB7661BU, 0xA8F7F4U, 0xC18D25U, 0x9A1E9AU, 0x9305CBU, 0x48F414U, + 0x43EFB5U, 0x1B1D6AU, 0x708413U, 0x698780U, 0x2A7C4DU, 0x6168BFU, 0x78F3A2U, 0x130059U, 0x0B1BCCU, 0x40CA07U, + 0x1FF072U, 0x9663E9U, 0xCD9A14U, 0xE499C7U, 0xBF0AEAU, 0xF57239U, 0xECE1E4U, 0xA7BA5FU, 0xDE098FU, 0xD591E0U, + 0x86E271U, 0x8F79AEU, 0xD52817U, 0x1C8350U, 0x2711A9U, 0x684C3EU, 0x71FFE7U, 0x3AE490U, 0x633619U, 0x498FE6U, + 0x10DC37U, 0x1B670DU, 0x4066D8U, 0x09BC13U, 0x328FAEU, 0xFB16FDU, 0xA9C500U, 0xA2FF93U, 0xFB2C5EU, 0xF03565U, + 0x8D86B0U, 0xC65D4BU, 0xDD7DCEU, 0x95E615U, 0x8EB169U, 0xE708FAU, 0xBCDB03U, 0x37C1C4U, 0x6E72FDU, 0x25232AU, + 0x3DB8F3U, 0x5ECA4CU, 0x45430DU, 0x0CF0B2U, 0x57AB61U, 0x5E3ABCU, 0x210087U, 0x2BD343U, 0xF248B8U, 0xB9392DU, + 0xA0A376U, 0xEBB09FU, 0x905908U, 0x99CAF1U, 0xD3F5A6U, 0xCA251FU, 0x813ED0U, 0xF2CD01U, 0xFBD63EU, 0xA046EEU, + 0x29BD51U, 0x76AE98U, 0x3C274FU, 0x055476U, 0x4ECEA5U, 0x573F58U, 0x1C24DBU, 0x47B786U, 0x6ECC7DU, 0x345CE8U, + 0x7D0703U, 0x66B456U, 0xAF3DECU, 0xBC6F31U, 0xD7D0A2U, 0x8EC1CFU, 0x80321CU, 0xD9A9A1U, 0xD2FB7AU, 0xA9422BU, + 0xE05184U, 0xFB8A55U, 0xB22AAAU, 0xE831B3U, 0x63E264U, 0x1ADB8CU, 0x11081BU, 0x4832E2U, 0x0BA1BDU, 0x10781CU, + 0x784BC3U, 0x679052U, 0x2E902DU, 0x752BFCU, 0x7EFC57U, 0x27C58AU, 0xCC57D9U, 0xD40C64U, 0x9FBFB6U, 0x84665BU, + 0xCD7540U, 0xB6CF95U, 0xBF8E6EU, 0xE415FBU, 0xE62690U, 0xBFFE0DU, 0xF04DFEU, 0xC91623U, 0x028520U, 0x19BCD9U, + 0x506E06U, 0x0AEDB6U, 0x23D4E9U, 0x780728U, 0x331997U, 0x3AE84EU, 0x6173B9U, 0x6840A0U, 0x129B67U, 0x598B9EU, + 0xC87009U, 0x877370U, 0x9EEAEBU, 0xF5190EU, 0xAC03D4U, 0xA690C1U, 0xFDE93AU, 0xB47AF7U, 0xAF8044U, 0xC69119U, + 0xDD0ACAU, 0x166977U, 0x4EF0BCU, 0x45E2C9U, 0x3C1D52U, 0x3F8E87U, 0x64D73CU, 0x296574U, 0x327E83U, 0x7A8F1AU, + 0x0114EDU, 0x0A1734U, 0x53EF3BU, 0xD8FCCAU, 0x812715U, 0xEA94A4U, 0xF185EBU, 0xB95F3AU, 0xA26C85U, 0xEBB55CU, + 0xB0862AU, 0x930CA3U, 0xCEDD70U, 0x85E6CDU, 0x9D7196U, 0x562A63U, 0x6D9AE8U, 0x24411DU, 0x7F5246U, 0x76EBDBU, + 0x2DB928U, 0x2712E5U, 0x5E83D7U, 0x15D80AU, 0x0C6BD1U, 0x473170U, 0x54A02FU, 0xB99B9EU, 0xE34841U, 0xEAD090U, + 0xB1E33FU, 0xFA2866U, 0xE31991U, 0x88C208U, 0x8154FFU, 0xDB6FB7U, 0x92BE28U, 0x89A5D9U, 0xE25606U, 0x7BCE37U, + 0x30FDFCU, 0x693E21U, 0x6F2792U, 0x3495CFU, 0x5DCE6CU, 0x465DB1U, 0x0FA46AU, 0x14B75FU, 0x5F2D85U, 0x275C60U, + 0x2CC7FBU, 0xF56486U, 0xFE3C55U, 0xA5ABC8U, 0xECD02BU, 0xD743F2U, 0x9FDAC5U, 0xC0A81CU, 0xC33383U, 0x9A2262U, + 0xB1D93DU, 0xE8DA8DU, 0x232252U, 0x39B19BU, 0x70AAACU, 0x6B5975U, 0x024182U, 0x59921BU, 0x522974U, 0x0B38A5U, + 0x01F33EU, 0x78C1CBU, 0x3758D0U, 0x242F3DU, 0xEDB4EFU, 0xB66672U, 0xBF5701U, 0xC5CCDCU, 0xCE9F77U, 0x9726A2U, + 0xDCA4F9U, 0xC5FF4CU, 0xAE4E87U, 0xB51576U, 0xFDA7E9U, 0xA63C90U, 0x2F6D47U, 0x74D6CFU, 0x1BC538U, 0x0A1D21U, + 0x413EDEU, 0x58E50FU, 0x127480U, 0x6B4BF1U, 0x60992EU, 0x2BA297U, 0x327340U, 0x79E809U, 0xA0DABAU, 0x8A0367U, + 0xD1009DU, 0x98BB98U, 0x836A63U, 0xCE70F6U, 0xF5832DU, 0xB69850U, 0xEE29D3U, 0xE5732EU, 0xBCE0FDU, 0xB75962U, + 0x4C1A13U, 0x0589DCU, 0x1EF165U, 0x5666B7U, 0x4DFDEAU, 0x268E59U, 0x7F1794U, 0x74256FU, 0x29FE7AU, 0x626F81U, + 0x700404U, 0x1996DFU, 0x828FA2U, 0xCB7C31U, 0x9067CCU, 0x9B940EU, 0xE20C17U, 0xE81FE8U, 0xB1E439U, 0xFAF586U, + 0xE17FC7U, 0xA88C18U, 0xD397A1U, 0x5E4276U, 0x04794FU, 0x0FEB88U, 0x5E9271U, 0x3501E6U, 0x2C9A3EU, 0x67EA51U, + 0x7C61C0U, 0x34323BU, 0x6F89EEU, 0x4618F5U, 0x1D4218U, 0xD4F1CBU, 0xCFE876U, 0x841B25U, 0xB888F8U, 0xF3D053U, + 0xAA7386U, 0xA168BCU, 0xF2BB69U, 0xFB8792U, 0x80541FU, 0xC8CF4CU, 0xC3FEB5U, 0x182522U, 0x1117CBU, 0x6A9E9CU, + 0x234D05U, 0x3876FAU, 0x72A42BU, 0x2FBD84U, 0x240ED4U, 0x5DD54BU, 0x56C4B2U, 0x057E65U, 0x4C2D4CU, 0xD7849BU, + 0xBFD762U, 0xA44DF9U, 0xEFFEACU, 0xB6A347U, 0xBD30D2U, 0xC40B29U, 0x8FDB74U, 0x9540E6U, 0xDC330BU, 0xC3BAD8U, + 0x8A89E5U, 0x71533EU, 0x7AC0BBU, 0x2BB140U, 0x212A11U, 0x7838AEU, 0x13C17FU, 0x08D290U, 0x414989U, 0x1AA85EU, + 0x13B2A6U, 0x494131U, 0x625AD8U, 0xBBCF87U, 0xF43516U, 0xED26E9U, 0xA6BF78U, 0x9FCC17U, 0xDD57CEU, 0x86B75DU, + 0x8FACA0U, 0xD43FE3U, 0x9D445EU, 0xA6D584U, 0x6DCF71U, 0x753C6AU, 0x3EA5AFU, 0x67F654U, 0x4C4CC9U, 0x134DBAU, + 0x1AB667U, 0x4125E4U, 0x097E19U, 0x12CA0AU, 0x71D1F3U, 0x78022CU, 0xA33BACU, 0xEAA953U, 0xF17A02U, 0x9B53BDU, + 0xC28074U, 0xC9BB83U, 0x90299AU, 0x9BF04DU, 0xC4C3B4U, 0xAD583BU, 0xB7084AU, 0xFEB3D1U, 0x656004U, 0x2E69FEU, + 0x5FDAF3U, 0x548000U, 0x0D37DDU, 0x472E4EU, 0x5CFD33U, 0x1546F8U, 0x2E164DU, 0x678D96U, 0x3CBEE3U, 0x337768U, + 0xEBE5BDU, 0xC09E47U, 0x990D5EU, 0xD234A9U, 0xC9E720U, 0x887DF7U, 0xD34C0EU, 0xFB9711U, 0xA084E0U, 0xA96C2FU, + 0xF27FBEU, 0xB9E4C1U, 0x001318U, 0x4B02AFU, 0x56B867U, 0x1CEB30U, 0x477289U, 0x4E8152U, 0x358AF7U, 0x3C1AACU, + 0x676159U, 0x24F2C2U, 0x3C6917U, 0x57197CU, 0x8E82E1U, 0x85F112U, 0xDE68CFU, 0xD77BDDU, 0x8C9130U, 0xE400EBU, + 0xFB5B5EU, 0xB0E885U, 0xA9F6D4U, 0xE2277BU, 0x9B9CA2U, 0x108F15U, 0x5A764CU, 0x0B64ABU, 0x00AF32U, 0x599CEDU, + 0x72059DU, 0x29D702U, 0x60ECE3U, 0x7A7D3CU, 0x330625U, 0x0C95D6U, 0x474D0BU, 0x9E7EA8U, 0x95FDF5U, 0xCCA666U, + 0x86179BU, 0xBD8D40U, 0xFCDA65U, 0xE763BFU, 0xAE304AU, 0xF58AD1U, 0xDE9B9CU, 0x86406FU, 0x8DE3F2U, 0x54B801U, + 0x1B28D8U, 0x0213D7U, 0x69C026U, 0x7259B9U, 0x3A6B48U, 0x61B017U, 0x68819FU, 0x135A68U, 0x50D9B1U, 0x49E386U, + 0x02325FU, 0x9A29B0U, 0xD19E23U, 0xAAC77EU, 0xA3558DU, 0xFCAE04U, 0xF5BFF3U, 0xAE04AAU, 0xC44635U, 0xDDDDC5U, + 0x966C4AU, 0xCF373BU, 0xC4A4E4U, 0x1FD47DU, 0x3E4F8AU, 0x64DC43U, 0x2DA574U, 0x3637ADU, 0x7D5C56U, 0x44CFC3U, + 0x0BD688U, 0x52257DU, 0x59BBE7U, 0x03AA1AU, 0x4A5119U, 0xF142C4U, 0xB89A37U, 0xB329AAU, 0xE822D1U, 0xA9D114U, + 0x93C88FU, 0xDA5A7AU, 0xC1A121U, 0x8AB090U, 0xD36B5EU, 0xDC58A7U, 0xA5C0B0U, 0x2F8369U, 0x74389EU, 0x3DAB17U, + 0x26D368U, 0x4F40F9U, 0x141F26U, 0x1DAEC7U, 0x473558U, 0x446721U, 0x1DDEF6U, 0x769D6EU, 0xEF0E8DU, 0xA434D0U, + 0xBFE56BU, 0xF35EBEU, 0x884D45U, 0x819440U, 0xDAA69BU, 0x917D26U, 0x88ECF5U, 0xC3C788U, 0xFB150BU, 0xB02ED6U, + 0x63BF24U, 0x6A6439U, 0x3153D2U, 0x188B03U, 0x43989CU, 0x0923EDU, 0x147232U, 0x5FF99BU, 0x060B4CU, 0x2D10B5U, + 0x76A1A2U, 0x7FFA7BU, 0xA568D4U, 0xECD104U, 0xD7827BU, 0x9C19EAU, 0x8D6831U, 0xC6E2DCU, 0x9F71CFU, 0x950A32U, + 0xEE9BE1U, 0xA3A16CU, 0xB8761FU, 0xF16FC2U, 0x6A9C79U, 0x010FACU, 0x491786U, 0x52F453U, 0x1BEF88U, 0x007C35U, + 0x4B8566U, 0x32978FU, 0x316C18U, 0x697DC1U, 0x62E6BEU, 0x3B142FU, 0xD40DC0U, 0xCFCE11U, 0x86F58EU, 0xDD66F6U, + 0xD53E31U, 0x8E8988U, 0xA5125FU, 0xFC6366U, 0xB7F9B9U, 0xAEAA78U, 0xE513C3U, 0xD69096U, 0x1ECB6DU, 0x4579F8U, + 0x4C6033U, 0x13B34EU, 0x5888DCU, 0x615821U, 0x2AE3F2U, 0x32F0FFU, 0x792B04U, 0x220A91U, 0x0B906AU, 0xD0432BU, + 0xD97294U, 0x82AD4DU, 0xC8BE9AU, 0xD90623U, 0xB2D564U, 0xEBEE9CU, 0xE02D03U, 0xBF35D2U, 0xB686ADU, 0xCCDD2CU, + 0x854CD3U, 0x1EF70AU, 0x55A51DU, 0x4C1CE4U, 0x074F67U, 0x7ED49AU, 0x7476C9U, 0x2F2F75U, 0x26BCBEU, 0x75876BU, + 0x1C5650U, 0x07C885U, 0x48BB7EU, 0x9022F3U, 0x9B11A0U, 0xC2CA5DU, 0xE9C8CEU, 0xA07133U, 0xFBA238U, 0xF0B9E9U, + 0xA84917U, 0x835286U, 0xDAC179U, 0x913830U, 0x822B87U, 0x4BD95EU, 0x30D289U, 0x3C43A0U, 0x67B87FU, 0x6CAA8EU, + 0x353311U, 0x7E4440U, 0x47DFABU, 0x0C8E3FU, 0x1634C4U, 0x5FA7D9U, 0x04CC0AU, 0xAD5DE7U, 0xF64774U, 0xBDB409U, + 0xAC2DD2U, 0xE67E67U, 0xFBC5ACU, 0x90D5F9U, 0xCB0E42U, 0xC23D9FU, 0x99B67DU, 0x9047E4U, 0xEA5DB3U, 0x218E0AU, + 0x38B7D5U, 0x732034U, 0x6AFA2BU, 0x21CBFAU, 0x5A1005U, 0x5B039CU, 0x01A8FBU, 0x4C7822U, 0x574395U, 0x3CD04DU, + 0xA5811AU, 0xEE3BB3U, 0xB76868U, 0xBDF19DU, 0xE64286U, 0xCF1943U, 0x949BF8U, 0xDDA225U, 0xC67156U, 0x8FCACBU, + 0xF59B08U, 0xF605F5U, 0x2B36EFU, 0x20EF1AU, 0x797CC1U, 0x324664U, 0x1985BFU, 0x419CE4U, 0x4A6F51U, 0x13748AU, + 0x58C42FU, 0x631F74U, 0x2A0CA1U, 0x71F51AU, 0xF9E7D7U, 0xA27CA5U, 0xA99F38U, 0xD886EBU, 0x973516U, 0x8E6F1DU, + 0xC5FEECU, 0xDF0133U, 0xB61282U, 0xED8ADDU, 0xE4F954U, 0xBF7AA3U, 0x74617AU, 0x6D904DU, 0x070A95U, 0x1E795AU, + 0x55E0EBU, 0x0EF334U, 0x0F0845U, 0x7098CAU, 0x798313U, 0x2360E0U, 0x687BFDU, 0x71AB0EU, 0xBA10C3U, 0x830370U, + 0xC8FA2DU, 0x93EDB7U, 0x9B3742U, 0xC00689U, 0xA99DBCU, 0xB25E67U, 0xF964BAU, 0xE0F509U, 0xA7AE44U, 0xFF1DB7U, + 0x54C42EU, 0x0DF6F1U, 0x066D90U, 0x5D3C0EU, 0x1487FFU, 0x2F8460U, 0x675E19U, 0x3CEFDEU, 0x37B447U, 0x6E27B0U, + 0x651369U, 0x9CC856U, 0xD77B97U, 0xC9604CU, 0x88B1F9U, 0x939BA2U, 0xFA4856U, 0xB1D1DDU, 0xAAE200U, 0xE33873U, + 0xB829EEU, 0xB0D20DU, 0xCBC190U, 0x407AEBU, 0x19AA3EU, 0x52A1A5U, 0x4B1240U, 0x204B1BU, 0x3ED9A2U, 0x7F2274U, + 0x2437ADU, 0x2DAC8AU, 0x76DF53U, 0x5D45ACU, 0x04F43DU, 0xCEBFE2U, 0xD72CC3U, 0x9C551CU, 0xE7C7E5U, 0xEEDC72U, + 0xB52D1BU, 0xBCB6CCU, 0xE6A454U, 0xA95FA7U, 0x904EFAU, 0xD3A551U, 0xCA3684U, 0x01265FU, 0x5ADD6AU, 0x72CEB1U, + 0x29135CU, 0x60A1CFU, 0x7BBA92U, 0x304921U, 0x2950FCU, 0x42D316U, 0x1AA903U, 0x1138F8U, 0x48E329U, 0xC7D0A6U, + 0xBC48DFU, 0xFD1B08U, 0xE6A0B1U, 0xAE3366U, 0xF55AAFU, 0xFEC898U, 0x879341U, 0x8C22FEU, 0xD5392EU, 0x9EEA61U, + 0x8456D0U, 0x6D450BU, 0x769EF6U, 0x3FADE5U, 0x606508U, 0x6BD6DBU, 0x3ACD46U, 0x101C3DU, 0x5927F8U, 0x02F543U, + 0x096C96U, 0x505FCCU, 0xBB8C71U, 0xA296B2U, 0xE8370FU, 0xF36C5CU, 0xBADF85U, 0xE1063AU, 0xC814FBU, 0x97AF04U, + 0x9CFA15U, 0xC461EAU, 0x87D23BU, 0xBE88B4U, 0x7509CCU, 0x2E721BU, 0x27E1A2U, 0x7C5965U, 0x750A7CU, 0x0F9183U, + 0x44A052U, 0x5D7BE9U, 0x1669ACU, 0x0F9277U, 0x6013CAU, 0x3B0819U, 0xB3FA65U, 0xE0E3E6U, 0xA9103BU, 0xB28BC0U, + 0xDB9ED5U, 0xC0642EU, 0x8B77EBU, 0xD3EC50U, 0xD80D89U, 0xA1179EU, 0xAAC477U, 0xF1F5A0U, 0x386E19U, 0x271D4FU, + 0x6F8596U, 0x541639U, 0x1F6DE8U, 0x4EFE97U, 0x45B706U, 0x1C05D9U, 0x779E30U, 0x6DCF27U, 0x2474DEU, 0x3F664DU, + 0xF69BB0U, 0xAD08FBU, 0x86536FU, 0xDFE094U, 0xD1E841U, 0x883B6AU, 0xC300B7U, 0xE8D164U, 0xB16BD9U, 0xF2788AU, + 0xEBA167U, 0xA192FCU, 0x3A0929U, 0x53DBD2U, 0x08F2C3U, 0x01212DU, 0x5A3ABCU, 0x518B43U, 0x29D11AU, 0x62628DU, + 0x7FFD64U, 0x34ACB3U, 0x6F1E8AU, 0xE64555U, 0x95C494U, 0x9D7F2BU, 0xC62C7AU, 0x8DB485U, 0x94871DU, 0xFF5CDEU, + 0xE6EFE3U, 0xADB730U, 0xF524CFU, 0xFE0F56U, 0xA5DE31U, 0x48C5E8U, 0x53377FU, 0x1AAE86U, 0x01BD59U, 0x4B4678U, + 0x3A50A7U, 0x31E977U, 0x683ACCU, 0x633189U, 0x38C072U, 0x71DAEFU, 0x4B493CU, 0x82B001U, 0x99A3C2U, 0xD2383FU, + 0x8F4AA4U, 0xA4D3D1U, 0xFDA00AU, 0xB62B8FU, 0xACBB75U, 0xE5C028U, 0xF6539BU, 0x9F4242U, 0xC4B985U, 0x4F2FBCU, + 0x167463U, 0x1CC5D2U, 0x65DE1DU, 0x2E3C4CU, 0x35A5F3U, 0x78F622U, 0x234DDDU, 0x0A5C55U, 0x408622U, 0x5BB5FBU, + 0x122E6CU, 0xC1FF95U, 0xC8C4DEU, 0xB3066BU, 0xB83FB0U, 0xE0AC25U, 0xAB775EU, 0xB24793U, 0xD9DC20U, 0x828BFDU, + 0x8F30AFU, 0xD46102U, 0xDCDBD9U, 0x07582CU, 0x6E0137U, 0x75B2C6U, 0x3EE809U, 0x2F7990U, 0x64C2E7U, 0x1C913EU, + 0x170A89U, 0x4C3A40U, 0x45E9D6U, 0x1E42AFU, 0x571370U, 0xE889C1U, 0xA2BA0EU, 0xFB635FU, 0xF070E0U, 0xA9CF39U, + 0x821DDAU, 0xD904C7U, 0x90F734U, 0x82FCE9U, 0xCB4D72U, 0xD09716U, 0x3B848DU, 0x623D78U, 0x696EA3U, 0x30F4BEU, + 0x7A075DU, 0x451E80U, 0x0C8D33U, 0x17E66EU, 0x5E76B5U, 0x050D14U, 0x0E9ECBU, 0x7607BAU, 0xF57524U, 0xACEAC5U, + 0xE7F91AU, 0xFC0083U, 0x9583F4U, 0xCED96DU, 0xC6688AU, 0x9D7353U, 0x92804CU, 0xCB11BDU, 0xA00B62U, 0xA9F8C3U, + 0x72E398U, 0x783244U, 0x2118F7U, 0x4A8B2AU, 0x5B5249U, 0x1061D4U, 0x09FA2FU, 0x428ABAU, 0x1911F1U, 0x31C604U, + 0x6AFD9FU, 0x276D62U, 0xBC2631U, 0xF795B8U, 0xCE4C4EU, 0x855F97U, 0xDDE520U, 0xD6B479U, 0x8D0FB6U, 0x8C1C07U, + 0xF7C6D8U, 0xBE77A9U, 0xA53C36U, 0xEFAFDFU, 0x369648U, 0x1D4411U, 0x40DFE6U, 0x4BEE7EU, 0x103595U, 0x5902C0U, + 0x43D85BU, 0x2AC9AEU, 0x316265U, 0x7AB158U, 0x23A18BU, 0x201A56U, 0xD949E5U, 0x93F2A8U, 0x882313U, 0xC139C6U, + 0xDA8A3CU, 0x97D329U, 0xEC50E2U, 0xE7AA13U, 0xBFBB8CU, 0xB42075U, 0xED5322U, 0x06C8ABU, 0x1DF85CU, 0x542785U, + 0x0FB4BAU, 0x0FCD7BU, 0x544FA4U, 0x7F5414U, 0x26A54BU, 0x6D3EF2U, 0x702D31U, 0x3BD7CCU, 0x13C6DFU, 0xC83D02U, + 0x83AEF9U, 0x9AB77CU, 0xD15507U, 0xC84ED2U, 0xA3DF49U, 0xF9A4BDU, 0xF836F6U, 0xA3ED4BU, 0xEADC98U, 0xD14B05U, + 0x9A30E6U, 0x03A0BFU, 0x4D6B00U, 0x1458D1U, 0x1FC12EU, 0x64932FU, 0x6D28F0U, 0x36BB09U, 0x7FE29FU, 0x6551E6U, + 0x0E0B21U, 0x57AAB8U, 0x54B147U, 0x8D6216U, 0x86DBA9U, 0xDDC968U, 0xB412F3U, 0xAA218EU, 0xE3FC5DU, 0xF86EF0U, + 0xB35523U, 0xCA945FU, 0xC1AFCCU, 0x983C11U, 0xD264FAU, 0x49D7EFU, 0x000C14U, 0x3B1FC1U, 0x7AA75AU, 0x21F4B7U, + 0x2A6FA4U, 0x728E5DU, 0x5D958AU, 0x042713U, 0x4F767DU, 0x54EDACU, 0x1D5E13U, 0x8604C0U, 0xEE91ADU, 0xB5EA36U, + 0xBC79E3U, 0xE77018U, 0xAC820DU, 0x9D19E6U, 0xD6283FU, 0xCEF3A8U, 0x85E0D1U, 0xDA1A47U, 0x538BAEU, 0x389079U, + 0x216360U, 0x6A6B9FU, 0x30D84EU, 0x3903E1U, 0x421230U, 0x0BE94FU, 0x10FBD6U, 0x5B6011U, 0x4215E8U, 0x008EFBU, + 0xF95C07U, 0xF2759CU, 0xADE659U, 0xA49D22U, 0xFF0CB7U, 0x96964CU, 0x8CE591U, 0xC77E92U, 0x9E2F6FU, 0x959CBCU, + 0xCC0601U, 0xE7475AU, 0x3CFCBFU, 0x74EF25U, 0x6717F4U, 0x2E048BU, 0x15DF0AU, 0x5A6CF5U, 0x03716CU, 0x08A33BU, + 0x5098C2U, 0x5B1945U, 0x20C2BCU, 0x69F063U, 0xF22972U, 0xBB1A8DU, 0xE0814DU, 0xCA52F6U, 0x936AABU, 0x98B958U, + 0xC9A2C5U, 0x820306U, 0x9B593BU, 0xF4CAE0U, 0xEE7315U, 0xA7209EU, 0x7C9BCBU, 0x75C930U, 0x0E54A4U, 0x45E75FU, + 0x5CBC12U, 0x173D81U, 0x0F0778U, 0x44D4AFU, 0x3FC596U, 0x363E59U, 0x65ACE8U, 0x6C9737U, 0xA346E6U, 0xD95DD9U, + 0xD0FE10U, 0x8B26E6U, 0xC2357FU, 0xD9CE28U, 0x92DDC1U, 0xAB455EU, 0xE1B6AFU, 0xB8ADF4U, 0xB35861U, 0x68438AU, + 0x41C157U, 0x12B864U, 0x5B2BB9U, 0x45B00AU, 0x0EC3C6U, 0x175B95U, 0x7C2828U, 0x25B3F3U, 0x2E2206U, 0x75580DU, + 0x3DDBD8U, 0x86C223U, 0xCF31BAU, 0xD4AADDU, 0x9FF804U, 0xC64193U, 0xCD526AU, 0xBDA9F4U, 0xB2BD95U, 0xE92E4AU, + 0xA0C5FBU, 0xBBD424U, 0xD20F7DU, 0x093DDAU, 0x01A603U, 0x5A77F0U, 0x514CEDU, 0x089E36U, 0x63A7D3U, 0x7A3448U, + 0x31FF3CU, 0x2BCEE7U, 0x625462U, 0x150799U, 0x1CBC84U, 0xC7ED77U, 0x8C76AAU, 0x95C409U, 0xDF9954U, 0xE62A8FU, + 0xAD213EU, 0xF6F1E1U, 0xFF4A00U, 0xA4191EU, 0x8D82C7U, 0xD7B330U, 0x9C69A9U, 0x057ACEU, 0x42C357U, 0x2B1088U, + 0x303A79U, 0x7BEB76U, 0x63F087U, 0x284358U, 0x7198E1U, 0x5A88B2U, 0x017B7EU, 0x0860CDU, 0x53F510U, 0x9A0F63U, + 0xA00CFEU, 0xEBB535U, 0xF2E6C0U, 0xB97DDBU, 0xEC0F2EU, 0xE796B5U, 0x9C0578U, 0xD47E0BU, 0xCFEF92U, 0x86F564U, + 0x1D16ADU, 0x768F32U, 0x2FFC43U, 0x24669CU, 0x7C752DU, 0x778CE2U, 0x2E1F9BU, 0x45000CU, 0x5EF0D5U, 0x13FB62U, + 0x40282BU, 0x4811D4U, 0xB38344U, 0xB8789FU, 0xE16BEAU, 0xAAB261U, 0xB38194U, 0xF81B4FU, 0xC2CA52U, 0x8BE1B1U, + 0xD0726CU, 0xD903DFU, 0x829982U, 0xE94A79U, 0x7471FDU, 0x36E026U, 0x2FBA93U, 0x640DC8U, 0x3F1431U, 0x16D7B6U, + 0x4D6C6FU, 0x443C18U, 0x1E8781U, 0x55947EU, 0x6C4FBFU, 0x27FFA0U, 0xBEE451U, 0xF5378FU, 0xAE0E2EU, 0xA2CD71U, + 0xC9D7C8U, 0x98661FU, 0x93BDC6U, 0xC88EE5U, 0xC15438U, 0xBA45C3U, 0xF2FE56U, 0xE9290DU, 0x2230E8U, 0x3B9273U, + 0x70C98FU, 0x0958DCU, 0x02A343U, 0x58B0A2U, 0x150A7DU, 0x0E5BC4U, 0x6FC897U, 0x74F33AU, 0x3F23E9U, 0x66A834U, + 0xECDB0FU, 0xB542DAU, 0x9E5131U, 0xC7ABA5U, 0x8C38FEU, 0x97010BU, 0xDED290U, 0xA4CC7DU, 0xAD3D2EU, 0xF6B6B3U, + 0xF9A540U, 0x205ED9U, 0x634EB6U, 0x5A9567U, 0x11A6D8U, 0x0B3F09U}; + +CAMBEFEC::CAMBEFEC() +{ +} + +CAMBEFEC::~CAMBEFEC() +{ +} + +unsigned int CAMBEFEC::regenerate(unsigned char* bytes) const +{ + wxASSERT(bytes != NULL); + + unsigned int a = ((bytes[0] & 0x80) ? 0x800000 : 0x000000) | ((bytes[0] & 0x02) ? 0x400000 : 0x000000) | + ((bytes[1] & 0x08) ? 0x200000 : 0x000000) | ((bytes[2] & 0x20) ? 0x100000 : 0x000000) | + ((bytes[3] & 0x80) ? 0x080000 : 0x000000) | ((bytes[3] & 0x02) ? 0x040000 : 0x000000) | + ((bytes[4] & 0x08) ? 0x020000 : 0x000000) | ((bytes[5] & 0x20) ? 0x010000 : 0x000000) | + ((bytes[6] & 0x80) ? 0x008000 : 0x000000) | ((bytes[6] & 0x02) ? 0x004000 : 0x000000) | + ((bytes[7] & 0x08) ? 0x002000 : 0x000000) | ((bytes[8] & 0x20) ? 0x001000 : 0x000000) | + ((bytes[0] & 0x40) ? 0x000800 : 0x000000) | ((bytes[0] & 0x01) ? 0x000400 : 0x000000) | + ((bytes[1] & 0x04) ? 0x000200 : 0x000000) | ((bytes[2] & 0x10) ? 0x000100 : 0x000000) | + ((bytes[3] & 0x40) ? 0x000080 : 0x000000) | ((bytes[3] & 0x01) ? 0x000040 : 0x000000) | + ((bytes[4] & 0x04) ? 0x000020 : 0x000000) | ((bytes[5] & 0x10) ? 0x000010 : 0x000000) | + ((bytes[6] & 0x40) ? 0x000008 : 0x000000) | ((bytes[6] & 0x01) ? 0x000004 : 0x000000) | + ((bytes[7] & 0x04) ? 0x000002 : 0x000000) | ((bytes[8] & 0x10) ? 0x000001 : 0x000000); + + unsigned int b = ((bytes[0] & 0x20) ? 0x800000 : 0x000000) | ((bytes[1] & 0x80) ? 0x400000 : 0x000000) | + ((bytes[1] & 0x02) ? 0x200000 : 0x000000) | ((bytes[2] & 0x08) ? 0x100000 : 0x000000) | + ((bytes[3] & 0x20) ? 0x080000 : 0x000000) | ((bytes[4] & 0x80) ? 0x040000 : 0x000000) | + ((bytes[4] & 0x02) ? 0x020000 : 0x000000) | ((bytes[5] & 0x08) ? 0x010000 : 0x000000) | + ((bytes[6] & 0x20) ? 0x008000 : 0x000000) | ((bytes[7] & 0x80) ? 0x004000 : 0x000000) | + ((bytes[7] & 0x02) ? 0x002000 : 0x000000) | ((bytes[8] & 0x08) ? 0x001000 : 0x000000) | + ((bytes[0] & 0x10) ? 0x000800 : 0x000000) | ((bytes[1] & 0x40) ? 0x000400 : 0x000000) | + ((bytes[1] & 0x01) ? 0x000200 : 0x000000) | ((bytes[2] & 0x04) ? 0x000100 : 0x000000) | + ((bytes[3] & 0x10) ? 0x000080 : 0x000000) | ((bytes[4] & 0x40) ? 0x000040 : 0x000000) | + ((bytes[4] & 0x01) ? 0x000020 : 0x000000) | ((bytes[5] & 0x04) ? 0x000010 : 0x000000) | + ((bytes[6] & 0x10) ? 0x000008 : 0x000000) | ((bytes[7] & 0x40) ? 0x000004 : 0x000000) | + ((bytes[7] & 0x01) ? 0x000002 : 0x000000) | ((bytes[8] & 0x04) ? 0x000001 : 0x000000); + + unsigned int c = ((bytes[0] & 0x08) ? 0x800000 : 0x000000) | ((bytes[1] & 0x20) ? 0x400000 : 0x000000) | + ((bytes[2] & 0x80) ? 0x200000 : 0x000000) | ((bytes[2] & 0x02) ? 0x100000 : 0x000000) | + ((bytes[3] & 0x08) ? 0x080000 : 0x000000) | ((bytes[4] & 0x20) ? 0x040000 : 0x000000) | + ((bytes[5] & 0x80) ? 0x020000 : 0x000000) | ((bytes[5] & 0x02) ? 0x010000 : 0x000000) | + ((bytes[6] & 0x08) ? 0x008000 : 0x000000) | ((bytes[7] & 0x20) ? 0x004000 : 0x000000) | + ((bytes[8] & 0x80) ? 0x002000 : 0x000000) | ((bytes[8] & 0x02) ? 0x001000 : 0x000000) | + ((bytes[0] & 0x04) ? 0x000800 : 0x000000) | ((bytes[1] & 0x10) ? 0x000400 : 0x000000) | + ((bytes[2] & 0x40) ? 0x000200 : 0x000000) | ((bytes[2] & 0x01) ? 0x000100 : 0x000000) | + ((bytes[3] & 0x04) ? 0x000080 : 0x000000) | ((bytes[4] & 0x10) ? 0x000040 : 0x000000) | + ((bytes[5] & 0x40) ? 0x000020 : 0x000000) | ((bytes[5] & 0x01) ? 0x000010 : 0x000000) | + ((bytes[6] & 0x04) ? 0x000008 : 0x000000) | ((bytes[7] & 0x10) ? 0x000004 : 0x000000) | + ((bytes[8] & 0x40) ? 0x000002 : 0x000000) | ((bytes[8] & 0x01) ? 0x000001 : 0x000000); + + unsigned int errors = regenerate(a, b, c); + + bytes[0] = ((a & 0x800000) ? 0x80 : 0x00) | ((a & 0x000800) ? 0x40 : 0x00) | ((b & 0x800000) ? 0x20 : 0x00) | + ((b & 0x000800) ? 0x10 : 0x00) | ((c & 0x800000) ? 0x08 : 0x00) | ((c & 0x000800) ? 0x04 : 0x00) | + ((a & 0x400000) ? 0x02 : 0x00) | ((a & 0x000400) ? 0x01 : 0x00); + + bytes[1] = ((b & 0x400000) ? 0x80 : 0x00) | ((b & 0x000400) ? 0x40 : 0x00) | ((c & 0x400000) ? 0x20 : 0x00) | + ((c & 0x000400) ? 0x10 : 0x00) | ((a & 0x200000) ? 0x08 : 0x00) | ((a & 0x000200) ? 0x04 : 0x00) | + ((b & 0x200000) ? 0x02 : 0x00) | ((b & 0x000200) ? 0x01 : 0x00); + + bytes[2] = ((c & 0x200000) ? 0x80 : 0x00) | ((c & 0x000200) ? 0x40 : 0x00) | ((a & 0x100000) ? 0x20 : 0x00) | + ((a & 0x000100) ? 0x10 : 0x00) | ((b & 0x100000) ? 0x08 : 0x00) | ((b & 0x000100) ? 0x04 : 0x00) | + ((c & 0x100000) ? 0x02 : 0x00) | ((c & 0x000100) ? 0x01 : 0x00); + + bytes[3] = ((a & 0x080000) ? 0x80 : 0x00) | ((a & 0x000080) ? 0x40 : 0x00) | ((b & 0x080000) ? 0x20 : 0x00) | + ((b & 0x000080) ? 0x10 : 0x00) | ((c & 0x080000) ? 0x08 : 0x00) | ((c & 0x000080) ? 0x04 : 0x00) | + ((a & 0x040000) ? 0x02 : 0x00) | ((a & 0x000040) ? 0x01 : 0x00); + + bytes[4] = ((b & 0x040000) ? 0x80 : 0x00) | ((b & 0x000040) ? 0x40 : 0x00) | ((c & 0x040000) ? 0x20 : 0x00) | + ((c & 0x000040) ? 0x10 : 0x00) | ((a & 0x020000) ? 0x08 : 0x00) | ((a & 0x000020) ? 0x04 : 0x00) | + ((b & 0x020000) ? 0x02 : 0x00) | ((b & 0x000020) ? 0x01 : 0x00); + + bytes[5] = ((c & 0x020000) ? 0x80 : 0x00) | ((c & 0x000020) ? 0x40 : 0x00) | ((a & 0x010000) ? 0x20 : 0x00) | + ((a & 0x000010) ? 0x10 : 0x00) | ((b & 0x010000) ? 0x08 : 0x00) | ((b & 0x000010) ? 0x04 : 0x00) | + ((c & 0x010000) ? 0x02 : 0x00) | ((c & 0x000010) ? 0x01 : 0x00); + + bytes[6] = ((a & 0x008000) ? 0x80 : 0x00) | ((a & 0x000008) ? 0x40 : 0x00) | ((b & 0x008000) ? 0x20 : 0x00) | + ((b & 0x000008) ? 0x10 : 0x00) | ((c & 0x008000) ? 0x08 : 0x00) | ((c & 0x000008) ? 0x04 : 0x00) | + ((a & 0x004000) ? 0x02 : 0x00) | ((a & 0x000004) ? 0x01 : 0x00); + + bytes[7] = ((b & 0x004000) ? 0x80 : 0x00) | ((b & 0x000004) ? 0x40 : 0x00) | ((c & 0x004000) ? 0x20 : 0x00) | + ((c & 0x000004) ? 0x10 : 0x00) | ((a & 0x002000) ? 0x08 : 0x00) | ((a & 0x000002) ? 0x04 : 0x00) | + ((b & 0x002000) ? 0x02 : 0x00) | ((b & 0x000002) ? 0x01 : 0x00); + + bytes[8] = ((c & 0x002000) ? 0x80 : 0x00) | ((c & 0x000002) ? 0x40 : 0x00) | ((a & 0x001000) ? 0x20 : 0x00) | + ((a & 0x000001) ? 0x10 : 0x00) | ((b & 0x001000) ? 0x08 : 0x00) | ((b & 0x000001) ? 0x04 : 0x00) | + ((c & 0x001000) ? 0x02 : 0x00) | ((c & 0x000001) ? 0x01 : 0x00); + + return errors; +} + +unsigned int CAMBEFEC::count(const unsigned char* bytes) const +{ + wxASSERT(bytes != NULL); + + unsigned int a = ((bytes[0] & 0x80) ? 0x800000 : 0x000000) | ((bytes[0] & 0x02) ? 0x400000 : 0x000000) | + ((bytes[1] & 0x08) ? 0x200000 : 0x000000) | ((bytes[2] & 0x20) ? 0x100000 : 0x000000) | + ((bytes[3] & 0x80) ? 0x080000 : 0x000000) | ((bytes[3] & 0x02) ? 0x040000 : 0x000000) | + ((bytes[4] & 0x08) ? 0x020000 : 0x000000) | ((bytes[5] & 0x20) ? 0x010000 : 0x000000) | + ((bytes[6] & 0x80) ? 0x008000 : 0x000000) | ((bytes[6] & 0x02) ? 0x004000 : 0x000000) | + ((bytes[7] & 0x08) ? 0x002000 : 0x000000) | ((bytes[8] & 0x20) ? 0x001000 : 0x000000) | + ((bytes[0] & 0x40) ? 0x000800 : 0x000000) | ((bytes[0] & 0x01) ? 0x000400 : 0x000000) | + ((bytes[1] & 0x04) ? 0x000200 : 0x000000) | ((bytes[2] & 0x10) ? 0x000100 : 0x000000) | + ((bytes[3] & 0x40) ? 0x000080 : 0x000000) | ((bytes[3] & 0x01) ? 0x000040 : 0x000000) | + ((bytes[4] & 0x04) ? 0x000020 : 0x000000) | ((bytes[5] & 0x10) ? 0x000010 : 0x000000) | + ((bytes[6] & 0x40) ? 0x000008 : 0x000000) | ((bytes[6] & 0x01) ? 0x000004 : 0x000000) | + ((bytes[7] & 0x04) ? 0x000002 : 0x000000) | ((bytes[8] & 0x10) ? 0x000001 : 0x000000); + + unsigned int b = ((bytes[0] & 0x20) ? 0x800000 : 0x000000) | ((bytes[1] & 0x80) ? 0x400000 : 0x000000) | + ((bytes[1] & 0x02) ? 0x200000 : 0x000000) | ((bytes[2] & 0x08) ? 0x100000 : 0x000000) | + ((bytes[3] & 0x20) ? 0x080000 : 0x000000) | ((bytes[4] & 0x80) ? 0x040000 : 0x000000) | + ((bytes[4] & 0x02) ? 0x020000 : 0x000000) | ((bytes[5] & 0x08) ? 0x010000 : 0x000000) | + ((bytes[6] & 0x20) ? 0x008000 : 0x000000) | ((bytes[7] & 0x80) ? 0x004000 : 0x000000) | + ((bytes[7] & 0x02) ? 0x002000 : 0x000000) | ((bytes[8] & 0x08) ? 0x001000 : 0x000000) | + ((bytes[0] & 0x10) ? 0x000800 : 0x000000) | ((bytes[1] & 0x40) ? 0x000400 : 0x000000) | + ((bytes[1] & 0x01) ? 0x000200 : 0x000000) | ((bytes[2] & 0x04) ? 0x000100 : 0x000000) | + ((bytes[3] & 0x10) ? 0x000080 : 0x000000) | ((bytes[4] & 0x40) ? 0x000040 : 0x000000) | + ((bytes[4] & 0x01) ? 0x000020 : 0x000000) | ((bytes[5] & 0x04) ? 0x000010 : 0x000000) | + ((bytes[6] & 0x10) ? 0x000008 : 0x000000) | ((bytes[7] & 0x40) ? 0x000004 : 0x000000) | + ((bytes[7] & 0x01) ? 0x000002 : 0x000000) | ((bytes[8] & 0x04) ? 0x000001 : 0x000000); + + unsigned int c = ((bytes[0] & 0x08) ? 0x800000 : 0x000000) | ((bytes[1] & 0x20) ? 0x400000 : 0x000000) | + ((bytes[2] & 0x80) ? 0x200000 : 0x000000) | ((bytes[2] & 0x02) ? 0x100000 : 0x000000) | + ((bytes[3] & 0x08) ? 0x080000 : 0x000000) | ((bytes[4] & 0x20) ? 0x040000 : 0x000000) | + ((bytes[5] & 0x80) ? 0x020000 : 0x000000) | ((bytes[5] & 0x02) ? 0x010000 : 0x000000) | + ((bytes[6] & 0x08) ? 0x008000 : 0x000000) | ((bytes[7] & 0x20) ? 0x004000 : 0x000000) | + ((bytes[8] & 0x80) ? 0x002000 : 0x000000) | ((bytes[8] & 0x02) ? 0x001000 : 0x000000) | + ((bytes[0] & 0x04) ? 0x000800 : 0x000000) | ((bytes[1] & 0x10) ? 0x000400 : 0x000000) | + ((bytes[2] & 0x40) ? 0x000200 : 0x000000) | ((bytes[2] & 0x01) ? 0x000100 : 0x000000) | + ((bytes[3] & 0x04) ? 0x000080 : 0x000000) | ((bytes[4] & 0x10) ? 0x000040 : 0x000000) | + ((bytes[5] & 0x40) ? 0x000020 : 0x000000) | ((bytes[5] & 0x01) ? 0x000010 : 0x000000) | + ((bytes[6] & 0x04) ? 0x000008 : 0x000000) | ((bytes[7] & 0x10) ? 0x000004 : 0x000000) | + ((bytes[8] & 0x40) ? 0x000002 : 0x000000) | ((bytes[8] & 0x01) ? 0x000001 : 0x000000); + + return count(a, b, c); +} + +unsigned int CAMBEFEC::regenerate(unsigned int& a, unsigned int& b, unsigned int&) const +{ + unsigned int old_a = a; + unsigned int old_b = b; + + unsigned int data = CGolay::decode24128(a); + + unsigned int new_a = CGolay::encode24128(data); + + // The PRNG + unsigned int p = PRNG_TABLE[data]; + + b ^= p; + + unsigned int datb = CGolay::decode24128(b); + + unsigned int new_b = CGolay::encode24128(datb); + + new_b ^= p; + + unsigned int errors = 0U; + unsigned int mask = 0x01; + for (unsigned int i = 0U; i < 24U; i++, mask <<= 1) { + if ((old_a & mask) != (new_a & mask)) + errors++; + if ((old_b & mask) != (new_b & mask)) + errors++; + } + + a = new_a; + b = new_b; + + return errors; +} + +unsigned int CAMBEFEC::count(unsigned int a, unsigned int b, unsigned int) const +{ + unsigned int old_a = a; + unsigned int old_b = b; + + unsigned int data = CGolay::decode24128(a); + + unsigned int new_a = CGolay::encode24128(data); + + // The PRNG + unsigned int p = PRNG_TABLE[data]; + + b ^= p; + + unsigned int datb = CGolay::decode24128(b); + + unsigned int new_b = CGolay::encode24128(datb); + + new_b ^= p; + + unsigned int errors = 0U; + unsigned int mask = 0x01; + for (unsigned int i = 0U; i < 24U; i++, mask <<= 1) { + if ((old_a & mask) != (new_a & mask)) + errors++; + if ((old_b & mask) != (new_b & mask)) + errors++; + } + + return errors; +} diff --git a/Common/AMBEFEC.h b/Common/AMBEFEC.h new file mode 100644 index 0000000..8f9cf8c --- /dev/null +++ b/Common/AMBEFEC.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef AMBEFEC_H +#define AMBEFEC_H + +#include "Golay.h" + +class CAMBEFEC { +public: + CAMBEFEC(); + ~CAMBEFEC(); + + unsigned int regenerate(unsigned char* bytes) const; + unsigned int count(const unsigned char* bytes) const; + +private: + unsigned int regenerate(unsigned int& a, unsigned int& b, unsigned int& c) const; + unsigned int count(unsigned int a, unsigned int b, unsigned int c) const; +}; + +#endif diff --git a/Common/AnnouncementCallback.h b/Common/AnnouncementCallback.h new file mode 100644 index 0000000..eef53f2 --- /dev/null +++ b/Common/AnnouncementCallback.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef AnnouncementCallback_H +#define AnnouncementCallback_H + +#include "HeaderData.h" + +class IAnnouncementCallback { +public: + virtual void transmitAnnouncementHeader(CHeaderData* header) = 0; + + virtual void transmitAnnouncementData(const unsigned char* data, unsigned int length, bool end) = 0; + +private: +}; + +#endif diff --git a/Common/AnnouncementUnit.cpp b/Common/AnnouncementUnit.cpp new file mode 100644 index 0000000..89430cf --- /dev/null +++ b/Common/AnnouncementUnit.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "AnnouncementUnit.h" + +#include +#include + +const wxString GLOBAL_FILE_NAME = wxT("Announce"); + +CAnnouncementUnit::CAnnouncementUnit(IAnnouncementCallback* handler, const wxString& callsign) : +m_handler(handler), +m_localFileName(), +m_reader(), +m_writer(), +m_time(), +m_out(0U), +m_sending(false) +{ + wxASSERT(handler != NULL); + + m_localFileName.Printf(wxT("Announce %s"), callsign.c_str()); +#if !defined(__WINDOWS__) + m_localFileName.Replace(wxT(" "), wxT("_")); +#endif + + m_writer.setDirectory(wxFileName::GetHomeDir()); +} + +CAnnouncementUnit::~CAnnouncementUnit() +{ +} + +bool CAnnouncementUnit::writeHeader(const CHeaderData& header) +{ + return m_writer.open(m_localFileName, header); +} + +bool CAnnouncementUnit::writeData(const unsigned char* data, unsigned int length, bool end) +{ + bool ret = m_writer.write(data, length); + if (!ret) + return false; + + if (end) + m_writer.close(); + + return true; +} + +void CAnnouncementUnit::deleteAnnouncement() +{ + wxFileName fileName(wxFileName::GetHomeDir(), m_localFileName, wxT("dvtool")); + + if (wxFile::Exists(fileName.GetFullPath())) + ::wxRemoveFile(fileName.GetFullPath()); +} + +void CAnnouncementUnit::startAnnouncement() +{ + wxFileName fileName1(wxFileName::GetHomeDir(), m_localFileName, wxT("dvtool")); + wxFileName fileName2(wxFileName::GetHomeDir(), GLOBAL_FILE_NAME, wxT("dvtool")); + + if (wxFile::Exists(fileName1.GetFullPath())) { + bool ret = m_reader.open(fileName1.GetFullPath()); + if (!ret) + return; + } else if (wxFile::Exists(fileName2.GetFullPath())) { + bool ret = m_reader.open(fileName2.GetFullPath()); + if (!ret) + return; + } else { + return; + } + + DVTFR_TYPE type = m_reader.read(); + if (type != DVTFR_HEADER) { + wxLogError(wxT("Invalid header element in the file - %d"), int(type)); + m_reader.close(); + return; + } + + CHeaderData* header = m_reader.readHeader(); + if (header == NULL) { + wxLogError(wxT("NULL header element in the file")); + m_reader.close(); + return; + } + + // Remove the repeater bit + header->setFlag1(header->getFlag1() & ~REPEATER_MASK); + + m_handler->transmitAnnouncementHeader(header); + + m_time.Start(); + + m_out = 0U; + m_sending = true; +} + +void CAnnouncementUnit::clock() +{ + if (!m_sending) + return; + + unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS; + + while (m_out < needed) { + DVTFR_TYPE type = m_reader.read(); + if (type != DVTFR_DATA) { + wxLogError(wxT("Invalid data element in the file - %d"), int(type)); + m_handler->transmitAnnouncementData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + m_reader.close(); + m_sending = false; + m_out = 0U; + return; + } + + unsigned char data[DV_FRAME_MAX_LENGTH_BYTES]; + bool end; + m_reader.readData(data, DV_FRAME_MAX_LENGTH_BYTES, end); + + m_handler->transmitAnnouncementData(data, DV_FRAME_LENGTH_BYTES, false); + m_out++; + + if (end) { + m_handler->transmitAnnouncementData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + m_reader.close(); + m_sending = false; + m_out = 0U; + return; + } + } +} diff --git a/Common/AnnouncementUnit.h b/Common/AnnouncementUnit.h new file mode 100644 index 0000000..0823214 --- /dev/null +++ b/Common/AnnouncementUnit.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef AnnouncementUnit_H +#define AnnouncementUnit_H + +#include "AnnouncementCallback.h" +#include "DVTOOLFileWriter.h" +#include "DVTOOLFileReader.h" +#include "DStarDefines.h" +#include "HeaderData.h" + +#include + +class CAnnouncementUnit { +public: + CAnnouncementUnit(IAnnouncementCallback* handler, const wxString& callsign); + ~CAnnouncementUnit(); + + bool writeHeader(const CHeaderData& header); + bool writeData(const unsigned char* data, unsigned int length, bool end); + + void deleteAnnouncement(); + + void startAnnouncement(); + + void clock(); + +private: + IAnnouncementCallback* m_handler; + wxString m_localFileName; + CDVTOOLFileReader m_reader; + CDVTOOLFileWriter m_writer; + wxStopWatch m_time; + unsigned int m_out; + bool m_sending; +}; + +#endif diff --git a/Common/ArduinoController.cpp b/Common/ArduinoController.cpp new file mode 100644 index 0000000..8b4ef04 --- /dev/null +++ b/Common/ArduinoController.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ArduinoController.h" + + +const char IN_PORT1 = 0x01U; +const char IN_PORT2 = 0x02U; +const char IN_PORT3 = 0x04U; +const char IN_PORT4 = 0x08U; +const char IN_PORT5 = 0x10U; + +const char OUT_PORT1 = 0x01U; +const char OUT_PORT2 = 0x02U; +const char OUT_PORT3 = 0x04U; +const char OUT_PORT4 = 0x08U; +const char OUT_PORT5 = 0x10U; +const char OUT_PORT6 = 0x20U; +const char OUT_PORT7 = 0x40U; +const char OUT_PORT8 = 0x80U; + + +CArduinoController::CArduinoController(const wxString& port) : +m_serial(port, SERIAL_19200), +m_out(0x00U), +m_in(0x00U) +{ +} + +CArduinoController::~CArduinoController() +{ +} + +bool CArduinoController::open() +{ + bool ret = m_serial.open(); + if (!ret) + return false; + + unsigned char buffer; + while (m_serial.read(&buffer, 1U) == 1) + ; + + m_out = 0x00U; + m_in = 0x00U; + + return true; +} + +void CArduinoController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + unsigned char buffer; + int ret = m_serial.read(&buffer, 1U); + if (ret == 1) { + m_in = buffer; + m_serial.write(&m_out, 1U); + } + + inp1 = (m_in & IN_PORT1) == IN_PORT1; + inp2 = (m_in & IN_PORT2) == IN_PORT2; + inp3 = (m_in & IN_PORT3) == IN_PORT3; + inp4 = (m_in & IN_PORT4) == IN_PORT4; + inp5 = (m_in & IN_PORT5) == IN_PORT5; +} + +void CArduinoController::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8) +{ + m_out = 0x00U; + m_out |= outp1 ? OUT_PORT1 : 0x00U; + m_out |= outp2 ? OUT_PORT2 : 0x00U; + m_out |= outp3 ? OUT_PORT3 : 0x00U; + m_out |= outp4 ? OUT_PORT4 : 0x00U; + m_out |= outp5 ? OUT_PORT5 : 0x00U; + m_out |= outp6 ? OUT_PORT6 : 0x00U; + m_out |= outp7 ? OUT_PORT7 : 0x00U; + m_out |= outp8 ? OUT_PORT8 : 0x00U; + + unsigned char buffer; + int ret = m_serial.read(&buffer, 1U); + if (ret == 1) { + m_in = buffer; + m_serial.write(&m_out, 1U); + } +} + +void CArduinoController::close() +{ + unsigned char buffer = 0x00U; + m_serial.write(&buffer, 1U); + + m_serial.close(); +} diff --git a/Common/ArduinoController.h b/Common/ArduinoController.h new file mode 100644 index 0000000..bbf0889 --- /dev/null +++ b/Common/ArduinoController.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ArduinoController_H +#define ArduinoController_H + +#include "SerialDataController.h" +#include "HardwareController.h" + +#include + + +class CArduinoController : public IHardwareController { +public: + CArduinoController(const wxString& port); + virtual ~CArduinoController(); + + virtual bool open(); + + virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5); + + virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8); + + virtual void close(); + +private: + CSerialDataController m_serial; + unsigned char m_out; + unsigned char m_in; +}; + +#endif diff --git a/Common/AudioCallback.h b/Common/AudioCallback.h new file mode 100644 index 0000000..fb37a1d --- /dev/null +++ b/Common/AudioCallback.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2009,2015 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef AudioCallback_H +#define AudioCallback_H + +#include + +class IAudioCallback { +public: + virtual void readCallback(const wxFloat32* input, unsigned int nSamples, int id) = 0; + virtual void writeCallback(wxFloat32* output, unsigned int& nSamples, int id) = 0; + +private: +}; + +#endif diff --git a/Common/BeaconCallback.h b/Common/BeaconCallback.h new file mode 100644 index 0000000..2ba6aa8 --- /dev/null +++ b/Common/BeaconCallback.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef BeaconCallback_H +#define BeaconCallback_H + +class IBeaconCallback { +public: + virtual void transmitBeaconHeader() = 0; + + virtual void transmitBeaconData(const unsigned char* data, unsigned int length, bool end) = 0; + +private: +}; + +#endif diff --git a/Common/BeaconUnit.cpp b/Common/BeaconUnit.cpp new file mode 100644 index 0000000..3b591ca --- /dev/null +++ b/Common/BeaconUnit.cpp @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2011,2012 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "BeaconUnit.h" + +#include +#include +#include +#include + +const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC; + +const unsigned int SILENCE_LENGTH = 10U; + +CBeaconUnit::CBeaconUnit(IBeaconCallback* handler, const wxString& callsign, const wxString& text, bool voice, TEXT_LANG language) : +m_ambe(NULL), +m_ambeLength(0U), +m_data(NULL), +m_dataLength(0U), +m_index(), +m_language(language), +m_handler(handler), +m_callsign(callsign), +m_encoder(), +m_in(0U), +m_out(0U), +m_seqNo(0U), +m_time(), +m_sending(false) +{ + wxASSERT(handler != NULL); + + wxString slowData = text; + slowData.resize(20U, wxT(' ')); + m_encoder.setTextData(slowData); + + m_data = new unsigned char[MAX_FRAMES * VOICE_FRAME_LENGTH_BYTES]; + ::memset(m_data, 0x00U, MAX_FRAMES * VOICE_FRAME_LENGTH_BYTES); + + if (!voice) + return; + + wxString ambeFileName; + wxString indxFileName; + + switch (m_language) { + case TL_DEUTSCH: + ambeFileName = wxT("de_DE.ambe"); + indxFileName = wxT("de_DE.indx"); + break; + case TL_DANSK: + ambeFileName = wxT("dk_DK.ambe"); + indxFileName = wxT("dk_DK.indx"); + break; + case TL_ITALIANO: + ambeFileName = wxT("it_IT.ambe"); + indxFileName = wxT("it_IT.indx"); + break; + case TL_FRANCAIS: + ambeFileName = wxT("fr_FR.ambe"); + indxFileName = wxT("fr_FR.indx"); + break; + case TL_ESPANOL: + ambeFileName = wxT("es_ES.ambe"); + indxFileName = wxT("es_ES.indx"); + break; + case TL_SVENSKA: + ambeFileName = wxT("se_SE.ambe"); + indxFileName = wxT("se_SE.indx"); + break; + case TL_POLSKI: + ambeFileName = wxT("pl_PL.ambe"); + indxFileName = wxT("pl_PL.indx"); + break; + case TL_ENGLISH_US: + ambeFileName = wxT("en_US.ambe"); + indxFileName = wxT("en_US.indx"); + break; + case TL_NORSK: + ambeFileName = wxT("no_NO.ambe"); + indxFileName = wxT("no_NO.indx"); + break; +// case TL_NEDERLANDS_NL: +// ambeFileName = wxT("nl_NL.ambe"); +// indxFileName = wxT("nl_NL.indx"); +// break; +// case TL_NEDERLANDS_BE: +// ambeFileName = wxT("nl_BE.ambe"); +// indxFileName = wxT("nl_BE.indx"); +// break; + default: + ambeFileName = wxT("en_GB.ambe"); + indxFileName = wxT("en_GB.indx"); + break; + } + + bool ret = readAMBE(ambeFileName); + if (!ret) + return; + + readIndex(indxFileName); +} + +CBeaconUnit::~CBeaconUnit() +{ + for (CIndexList_t::iterator it = m_index.begin(); it != m_index.end(); ++it) + delete it->second; + + delete[] m_ambe; + delete[] m_data; +} + +void CBeaconUnit::sendBeacon() +{ + m_handler->transmitBeaconHeader(); + + m_sending = true; + + m_time.Start(); + + m_in = 0U; + m_out = 0U; + m_seqNo = 0U; + m_dataLength = 0U; + + if (m_ambe == NULL) { + for (unsigned int i = 0U; i < 21U; i++) { + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + + if (i == 0U) { + m_encoder.sync(); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + } else { + unsigned char text[DATA_FRAME_LENGTH_BYTES]; + m_encoder.getTextData(text); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, text, DATA_FRAME_LENGTH_BYTES); + } + + ::memcpy(m_data + m_dataLength, buffer, DV_FRAME_LENGTH_BYTES); + m_dataLength += DV_FRAME_LENGTH_BYTES; + m_in++; + } + } else { + lookup(wxT(" ")); + lookup(wxT(" ")); + lookup(wxT(" ")); + lookup(wxT(" ")); + + spellCallsign(m_callsign); + + lookup(wxT(" ")); + lookup(wxT(" ")); + lookup(wxT(" ")); + lookup(wxT(" ")); + } +} + +void CBeaconUnit::clock() +{ + if (!m_sending) + return; + + unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS; + + while (m_out < needed) { + m_handler->transmitBeaconData(m_data + m_out * DV_FRAME_LENGTH_BYTES, DV_FRAME_LENGTH_BYTES, false); + m_out++; + + if (m_in == m_out) { + m_handler->transmitBeaconData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + m_sending = false; + m_in = 0U; + m_out = 0U; + return; + } + } +} + +bool CBeaconUnit::lookup(const wxString &id) +{ + CIndexRecord* info = m_index[id]; + if (info == NULL) { + // wxLogError(wxT("Cannot find the AMBE index for *%s*"), id.c_str()); + return false; + } + + unsigned int start = info->getStart(); + unsigned int length = info->getLength(); + + for (unsigned int i = 0U; i < length; i++) { + unsigned char* dataIn = m_ambe + (start + i) * VOICE_FRAME_LENGTH_BYTES; + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, dataIn, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (m_seqNo == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + m_encoder.sync(); + } else { + m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); + } + + ::memcpy(m_data + m_dataLength, buffer, DV_FRAME_LENGTH_BYTES); + m_dataLength += DV_FRAME_LENGTH_BYTES; + m_in++; + + m_seqNo++; + if (m_seqNo == 21U) + m_seqNo = 0U; + } + + return true; +} + +void CBeaconUnit::spellCallsign(const wxString &callsign) +{ + unsigned int length = callsign.Len(); + + for (unsigned int i = 0U; i < (length - 1U); i++) { + wxString c = callsign.Mid(i, 1U); + + if (!c.IsSameAs(wxT(" "))) + lookup(c); + } + + wxChar c = callsign.GetChar(length - 1U); + + switch (c) { + case wxT('A'): + lookup(wxT("alpha")); + break; + case wxT('B'): + lookup(wxT("bravo")); + break; + case wxT('C'): + lookup(wxT("charlie")); + break; + case wxT('D'): + lookup(wxT("delta")); + break; + default: + lookup(wxString(c)); + break; + } +} + +bool CBeaconUnit::readAMBE(const wxString& name) +{ + wxFileName fileName(wxFileName::GetHomeDir(), name); + + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), name); +#else + fileName.Assign(wxT(DATA_DIR), name); +#endif + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + return false; + } + } + + wxFFile file; + + bool ret = file.Open(fileName.GetFullPath().c_str(), wxT("rb")); + if (!ret) { + wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str()); + return false; + } + + wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str()); + + unsigned char buffer[VOICE_FRAME_LENGTH_BYTES]; + + size_t n = file.Read(buffer, 4U); + if (n != 4U) { + wxLogMessage(wxT("Unable to read the header from %s"), fileName.GetFullPath().c_str()); + file.Close(); + return false; + } + + if (::memcmp(buffer, "AMBE", 4U) != 0) { + wxLogMessage(wxT("Invalid header from %s"), fileName.GetFullPath().c_str()); + file.Close(); + return false; + } + + // Length of the file minus the header + unsigned int length = file.Length() - 4U; + + // Hold the file data plus silence at the end + m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES]; + m_ambeLength = length / VOICE_FRAME_LENGTH_BYTES; + + // Add silence to the beginning of the buffer + unsigned char* p = m_ambe; + for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES) + ::memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + n = file.Read(p, length); + if (n != length) { + wxLogMessage(wxT("Unable to read the AMBE data from %s"), fileName.GetFullPath().c_str()); + file.Close(); + delete[] m_ambe; + m_ambe = NULL; + return false; + } + + file.Close(); + + return true; +} + +bool CBeaconUnit::readIndex(const wxString& name) +{ + wxFileName fileName(wxFileName::GetHomeDir(), name); + + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), name); +#else + fileName.Assign(wxT(DATA_DIR), name); +#endif + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + return false; + } + } + + wxTextFile file; + + bool ret = file.Open(fileName.GetFullPath()); + if (!ret) { + wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str()); + return false; + } + + // Add a silence entry at the beginning + m_index[wxT(" ")] = new CIndexRecord(wxT(" "), 0U, SILENCE_LENGTH); + + wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str()); + + unsigned int nLines = file.GetLineCount(); + + for (unsigned int i = 0; i < nLines; i++) { + wxString line = file.GetLine(i); + + if (line.length() > 0 && line.GetChar(0) != wxT('#')) { + wxStringTokenizer t(line, wxT(" \t\r\n"), wxTOKEN_STRTOK); + wxString name = t.GetNextToken(); + wxString startTxt = t.GetNextToken(); + wxString lengthTxt = t.GetNextToken(); + + if (!name.IsEmpty() && !startTxt.IsEmpty() && !lengthTxt.IsEmpty()) { + unsigned long start; + startTxt.ToULong(&start); + + unsigned long length; + lengthTxt.ToULong(&length); + + if (start >= m_ambeLength || (start + length) >= m_ambeLength) + wxLogError(wxT("The start or end for *%s* is out of range, start: %lu, end: %lu"), name.c_str(), start, start + length); + else + m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length); + } + } + } + + file.Close(); + + return true; +} diff --git a/Common/BeaconUnit.h b/Common/BeaconUnit.h new file mode 100644 index 0000000..84f3cd5 --- /dev/null +++ b/Common/BeaconUnit.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011,2012 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef BeaconUnit_H +#define BeaconUnit_H + +#include "SlowDataEncoder.h" +#include "BeaconCallback.h" +#include "DStarDefines.h" + +#include + +class CIndexRecord { +public: + CIndexRecord(const wxString& name, unsigned int start, unsigned int length) : + m_name(name), + m_start(start), + m_length(length) + { + } + + wxString getName() const + { + return m_name; + } + + unsigned int getStart() const + { + return m_start; + } + + unsigned int getLength() const + { + return m_length; + } + +private: + wxString m_name; + unsigned int m_start; + unsigned int m_length; +}; + +WX_DECLARE_STRING_HASH_MAP(CIndexRecord*, CIndexList_t); + +class CBeaconUnit { +public: + CBeaconUnit(IBeaconCallback* handler, const wxString& callsign, const wxString& text, bool voice, TEXT_LANG language); + ~CBeaconUnit(); + + void sendBeacon(); + + void clock(); + +private: + unsigned char* m_ambe; + unsigned int m_ambeLength; + unsigned char* m_data; + unsigned int m_dataLength; + CIndexList_t m_index; + TEXT_LANG m_language; + IBeaconCallback* m_handler; + wxString m_callsign; + CSlowDataEncoder m_encoder; + unsigned int m_in; + unsigned int m_out; + unsigned int m_seqNo; + wxStopWatch m_time; + bool m_sending; + + bool lookup(const wxString& id); + void spellCallsign(const wxString& callsign); + + bool readAMBE(const wxString& name); + bool readIndex(const wxString& name); +}; + +#endif diff --git a/Common/CCITTChecksum.cpp b/Common/CCITTChecksum.cpp new file mode 100644 index 0000000..62baf10 --- /dev/null +++ b/Common/CCITTChecksum.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2009 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CCITTChecksum.h" + +#include "Utils.h" + +static const unsigned short ccittTab[] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0}; + +CCCITTChecksum::CCCITTChecksum() : +m_crc16(0x0000U) +{ +} + +CCCITTChecksum::~CCCITTChecksum() +{ +} + +void CCCITTChecksum::update(const unsigned char* data, unsigned int length) +{ + wxASSERT(data != NULL); + + for (unsigned int i = 0U; i < length; i++) + m_crc16 = (wxUint16(m_crc8[0U]) << 8) ^ ccittTab[m_crc8[1U] ^ data[i]]; +} + +void CCCITTChecksum::result(unsigned char* data) +{ + wxASSERT(data != NULL); + + data[0U] = m_crc8[1U]; + data[1U] = m_crc8[0U]; +} + +bool CCCITTChecksum::check(const unsigned char* data) +{ + wxASSERT(data != NULL); + + unsigned char sum[2U]; + result(sum); + + return sum[0U] == data[0U] && sum[1U] == data[1U]; +} + +void CCCITTChecksum::reset() +{ + m_crc16 = 0x0000U; +} diff --git a/Common/CCITTChecksum.h b/Common/CCITTChecksum.h new file mode 100644 index 0000000..3e202cd --- /dev/null +++ b/Common/CCITTChecksum.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009,2011 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCITTChecksum_H +#define CCITTChecksum_H + +#include + +class CCCITTChecksum { +public: + CCCITTChecksum(); + ~CCCITTChecksum(); + + void update(const unsigned char* data, unsigned int length); + + void result(unsigned char* data); + + bool check(const unsigned char* data); + + void reset(); + +private: + union { + wxUint16 m_crc16; + wxUint8 m_crc8[2U]; + }; +}; + +#endif diff --git a/Common/CCITTChecksumReverse.cpp b/Common/CCITTChecksumReverse.cpp new file mode 100644 index 0000000..076d164 --- /dev/null +++ b/Common/CCITTChecksumReverse.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009,2011,2014 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CCITTChecksumReverse.h" + +#include "Utils.h" + +static const unsigned short ccittTab[] = { + 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf, + 0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7, + 0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e, + 0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876, + 0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd, + 0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5, + 0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c, + 0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974, + 0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb, + 0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3, + 0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a, + 0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72, + 0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9, + 0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1, + 0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738, + 0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70, + 0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7, + 0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff, + 0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036, + 0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e, + 0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5, + 0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd, + 0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134, + 0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c, + 0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3, + 0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb, + 0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232, + 0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a, + 0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1, + 0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9, + 0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330, + 0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78}; + +CCCITTChecksumReverse::CCCITTChecksumReverse() : +m_crc16(0xFFFFU) +{ +} + +CCCITTChecksumReverse::~CCCITTChecksumReverse() +{ +} + +void CCCITTChecksumReverse::update(const unsigned char* data, unsigned int length) +{ + wxASSERT(data != NULL); + + for (unsigned int i = 0U; i < length; i++) + m_crc16 = wxUint16(m_crc8[1U]) ^ ccittTab[m_crc8[0U] ^ data[i]]; +} + +void CCCITTChecksumReverse::result(unsigned char* data) +{ + wxASSERT(data != NULL); + + m_crc16 = ~m_crc16; + + data[0U] = m_crc8[0U]; + data[1U] = m_crc8[1U]; +} + +bool CCCITTChecksumReverse::check(const unsigned char* data) +{ + wxASSERT(data != NULL); + + unsigned char sum[2U]; + result(sum); + + return sum[0U] == data[0U] && sum[1U] == data[1U]; +} + +void CCCITTChecksumReverse::reset() +{ + m_crc16 = 0xFFFFU; +} diff --git a/Common/CCITTChecksumReverse.h b/Common/CCITTChecksumReverse.h new file mode 100644 index 0000000..febba08 --- /dev/null +++ b/Common/CCITTChecksumReverse.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009,2011,2014 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCITTChecksumReverse_H +#define CCITTChecksumReverse_H + +#include + +class CCCITTChecksumReverse { +public: + CCCITTChecksumReverse(); + ~CCCITTChecksumReverse(); + + void update(const unsigned char* data, unsigned int length); + + void result(unsigned char* data); + + bool check(const unsigned char* data); + + void reset(); + +private: + union { + wxUint16 m_crc16; + wxUint8 m_crc8[2U]; + }; +}; + +#endif diff --git a/Common/CallsignList.cpp b/Common/CallsignList.cpp new file mode 100644 index 0000000..f55a1f5 --- /dev/null +++ b/Common/CallsignList.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CallsignList.h" +#include "DStarDefines.h" + +#include + +CCallsignList::CCallsignList(const wxString& filename) : +m_filename(filename), +m_callsigns() +{ +} + + +CCallsignList::~CCallsignList() +{ +} + +bool CCallsignList::load() +{ + wxTextFile file; + + bool res = file.Open(m_filename); + if (!res) + return false; + + unsigned int lines = file.GetLineCount(); + if (lines == 0U) { + file.Close(); + return true; + } + + m_callsigns.Alloc(lines); + + wxString callsign = file.GetFirstLine(); + + while (!file.Eof()) { + callsign.MakeUpper(); + callsign.Append(wxT(" ")); + callsign.Truncate(LONG_CALLSIGN_LENGTH); + + m_callsigns.Add(callsign); + + callsign = file.GetNextLine(); + } + + file.Close(); + + return true; +} + +unsigned int CCallsignList::getCount() const +{ + return m_callsigns.GetCount(); +} + +bool CCallsignList::isInList(const wxString& callsign) const +{ + return m_callsigns.Index(callsign) != wxNOT_FOUND; +} diff --git a/Common/CallsignList.h b/Common/CallsignList.h new file mode 100644 index 0000000..2a8d06c --- /dev/null +++ b/Common/CallsignList.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef CallsignList_H +#define CallsignList_H + +#include + +class CCallsignList { +public: + CCallsignList(const wxString& filename); + ~CCallsignList(); + + bool load(); + + unsigned int getCount() const; + + bool isInList(const wxString& callsign) const; + +private: + wxString m_filename; + wxArrayString m_callsigns; +}; + +#endif diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj new file mode 100644 index 0000000..628c50c --- /dev/null +++ b/Common/Common.vcxproj @@ -0,0 +1,264 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {3753EF20-2330-415E-B933-2560A474498B} + Common + Win32Proj + 10.0.17134.0 + + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + + + StaticLibrary + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include + + + $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_x64_dll\mswud;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include + + + $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_x64_dll\mswu;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include + + + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) + + + + + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) + + + + + + + + MaxSpeed + true + $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + + + + + + + MaxSpeed + true + $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters new file mode 100644 index 0000000..df153d8 --- /dev/null +++ b/Common/Common.vcxproj.filters @@ -0,0 +1,326 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/Common/DStarDefines.h b/Common/DStarDefines.h new file mode 100644 index 0000000..a21f17d --- /dev/null +++ b/Common/DStarDefines.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2009-2015 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef DStarDefines_H +#define DStarDefines_H + +#include + +const unsigned int DSTAR_GMSK_SYMBOL_RATE = 4800U; +const float DSTAR_GMSK_BT = 0.5F; + +const unsigned char DATA_SYNC_BYTES[] = {0x55, 0x2D, 0x16}; + +const unsigned char END_PATTERN_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const unsigned int END_PATTERN_LENGTH_BYTES = 6U; + +const unsigned char NULL_AMBE_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8}; + +// Note that these are already scrambled, 0x66 0x66 0x66 otherwise +const unsigned char NULL_SLOW_DATA_BYTES[] = {0x16, 0x29, 0xF5}; + +const unsigned char NULL_FRAME_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x16, 0x29, 0xF5}; + +const unsigned int VOICE_FRAME_LENGTH_BYTES = 9U; + +const unsigned int DATA_FRAME_LENGTH_BYTES = 3U; + +const unsigned int DV_FRAME_LENGTH_BYTES = VOICE_FRAME_LENGTH_BYTES + DATA_FRAME_LENGTH_BYTES; + +// The length of the end frame, three bytes extra +const unsigned int DV_FRAME_MAX_LENGTH_BYTES = DV_FRAME_LENGTH_BYTES + 3U; + +const unsigned int FEC_SECTION_LENGTH_BYTES = 83U; + +const unsigned int RADIO_HEADER_LENGTH_BYTES = 41U; + +const unsigned int DATA_BLOCK_SIZE_BYTES = 21U * DV_FRAME_LENGTH_BYTES; + +const unsigned int DSTAR_RADIO_SAMPLE_RATE = 48000U; +const unsigned int DSTAR_RADIO_BLOCK_SIZE = 960U; +const unsigned int DSTAR_AUDIO_BLOCK_SIZE = 160U; + +const unsigned int LOCATOR_LENGTH = 6U; +const unsigned int LONG_CALLSIGN_LENGTH = 8U; +const unsigned int SHORT_CALLSIGN_LENGTH = 4U; + +const unsigned char SLOW_DATA_TYPE_MASK = 0xF0U; +const unsigned char SLOW_DATA_TYPE_GPSDATA = 0x30U; +const unsigned char SLOW_DATA_TYPE_TEXT = 0x40U; +const unsigned char SLOW_DATA_TYPE_HEADER = 0x50U; +const unsigned char SLOW_DATA_TYPE_SQUELCH = 0xC0U; +const unsigned char SLOW_DATA_LENGTH_MASK = 0x0FU; + +const unsigned int SLOW_DATA_TEXT_LENGTH = 20U; + +const unsigned char SCRAMBLER_BYTE1 = 0x70U; +const unsigned char SCRAMBLER_BYTE2 = 0x4FU; +const unsigned char SCRAMBLER_BYTE3 = 0x93U; + +const unsigned char DATA_MASK = 0x80U; +const unsigned char REPEATER_MASK = 0x40U; +const unsigned char INTERRUPTED_MASK = 0x20U; +const unsigned char CONTROL_SIGNAL_MASK = 0x10U; +const unsigned char URGENT_MASK = 0x08U; + +const unsigned char REPEATER_CONTROL_MASK = 0x07U; +const unsigned char REPEATER_CONTROL = 0x07U; +const unsigned char AUTO_REPLY = 0x06U; +const unsigned char RESEND_REQUESTED = 0x04U; +const unsigned char ACK_FLAG = 0x03U; +const unsigned char NO_RESPONSE = 0x02U; +const unsigned char RELAY_UNAVAILABLE = 0x01U; + +const unsigned int DSTAR_RADIO_BIT_LENGTH = DSTAR_RADIO_SAMPLE_RATE / DSTAR_GMSK_SYMBOL_RATE; + +const unsigned int DSTAR_FRAME_TIME_MS = 20U; +const unsigned int DSTAR_FRAMES_PER_SEC = 50U; + +const unsigned int DSTAR_TICKS_PER_SEC = DSTAR_RADIO_SAMPLE_RATE / DSTAR_RADIO_BLOCK_SIZE; + +const unsigned int DEXTRA_PORT = 30001U; +const unsigned int DCS_PORT = 30051U; + +const unsigned int GMSK_MODEM_DATA_LENGTH = 8U; + +const unsigned int RADIO_RUN_FRAME_COUNT = 5U; +const unsigned int LOCAL_RUN_FRAME_COUNT = 1U; +const unsigned int NETWORK_RUN_FRAME_COUNT = 25U; + +const unsigned int DSTAR_BLEEP_FREQ = 2000U; +const unsigned int DSTAR_BLEEP_LENGTH = 100U; +const float DSTAR_BLEEP_AMPL = 0.5F; + +const unsigned int NETWORK_TIMEOUT = 2U; + +const unsigned int SPLIT_RX_GUI_COUNT = 5U; +const unsigned int SPLIT_RX_COUNT = 25U; +const unsigned int SPLIT_TX_GUI_COUNT = 3U; +const unsigned int SPLIT_TX_COUNT = 5U; + +enum DSTAR_RPT_STATE { + DSRS_SHUTDOWN, + DSRS_LISTENING, + DSRS_VALID, + DSRS_VALID_WAIT, + DSRS_INVALID, + DSRS_INVALID_WAIT, + DSRS_TIMEOUT, + DSRS_TIMEOUT_WAIT, + DSRS_NETWORK +}; + +enum NETWORK_TYPE { + NETWORK_NONE, + NETWORK_HEADER, + NETWORK_DATA, + NETWORK_TEXT, + NETWORK_TEMPTEXT, + NETWORK_STATUS1, + NETWORK_STATUS2, + NETWORK_STATUS3, + NETWORK_STATUS4, + NETWORK_STATUS5, + NETWORK_REGISTER +}; + +enum DSTAR_MODE { + MODE_DUPLEX, + MODE_SIMPLEX, + MODE_GATEWAY, + MODE_TXONLY, + MODE_RXONLY, + MODE_TXANDRX +}; + +enum LINK_STATUS { + LS_NONE, + LS_PENDING_IRCDDB, + LS_LINKING_LOOPBACK, + LS_LINKING_DEXTRA, + LS_LINKING_DPLUS, + LS_LINKING_DCS, + LS_LINKING_CCS, + LS_LINKED_LOOPBACK, + LS_LINKED_DEXTRA, + LS_LINKED_DPLUS, + LS_LINKED_DCS, + LS_LINKED_CCS +}; + +enum ACK_TYPE { + AT_NONE, + AT_BER, + AT_STATUS +}; + +enum TEXT_LANG { + TL_ENGLISH_UK, + TL_DEUTSCH, + TL_DANSK, + TL_FRANCAIS, + TL_ITALIANO, + TL_POLSKI, + TL_ESPANOL, + TL_SVENSKA, + TL_NEDERLANDS, + TL_ENGLISH_US, + TL_NORSK +}; + +enum CONNECTION_TYPE { + CT_USB, + CT_NETWORK +}; + +enum USB_INTERFACE { + UI_LIBUSB, + UI_WINUSB +}; + +enum DVMEGA_VARIANT { + DVMV_MODEM, + DVMV_RADIO_2M, + DVMV_RADIO_70CM, + DVMV_RADIO_2M_70CM +}; + +#endif diff --git a/Common/DStarGMSKDemodulator.cpp b/Common/DStarGMSKDemodulator.cpp new file mode 100644 index 0000000..2b67897 --- /dev/null +++ b/Common/DStarGMSKDemodulator.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2009,2010,2013,2014,2015 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "DStarGMSKDemodulator.h" + +// Generated by gaussfir(0.5, 4, 10) +const wxFloat32 COEFFS_TABLE[] = { + 0.000000000000003F, 0.000000000000065F, 0.000000000001037F, 0.000000000014448F, 0.000000000174579F, + 0.000000001829471F, 0.000000016627294F, 0.000000131062698F, 0.000000895979719F, 0.000005312253663F, 0.000027316243802F, + 0.000121821714020F, 0.000471183399421F, 0.001580581180127F, 0.004598383433830F, 0.011602594308899F, 0.025390226926262F, + 0.048188078330624F, 0.079318443411643F, 0.113232294527059F, 0.140193533802410F, 0.150538369557851F, 0.140193533802410F, + 0.113232294527059F, 0.079318443411643F, 0.048188078330623F, 0.025390226926262F, 0.011602594308899F, 0.004598383433830F, + 0.001580581180127F, 0.000471183399421F, 0.000121821714020F, 0.000027316243802F, 0.000005312253663F, 0.000000895979719F, + 0.000000131062698F, 0.000000016627294F, 0.000000001829471F, 0.000000000174579F, 0.000000000014448F, 0.000000000001037F, + 0.000000000000065F, 0.000000000000003F}; + +const unsigned int COEFFS_LENGTH = 43U; + +const unsigned int DCOFFSET_COUNT = 4800U; // 5 seconds + +const unsigned int PLLMAX = 0x10000U; +const unsigned int PLLINC = PLLMAX / DSTAR_RADIO_BIT_LENGTH; +const unsigned int INC_LOCK = PLLINC / 64U; +const unsigned int INC_UNLOCK = PLLINC / 32U; + +CDStarGMSKDemodulator::CDStarGMSKDemodulator() : +m_filter(COEFFS_TABLE, COEFFS_LENGTH), +m_invert(false), +m_pll(0U), +m_prev(false), +m_inc(INC_UNLOCK), +m_locked(false), +m_offset(0.0F), +m_accum(0.0F), +m_count(0U) +{ +} + +CDStarGMSKDemodulator::~CDStarGMSKDemodulator() +{ +} + +TRISTATE CDStarGMSKDemodulator::decode(wxFloat32 val) +{ + TRISTATE state = STATE_UNKNOWN; + + // Calculate the DC offset when we are locked + if (m_locked) { + m_accum += val; + m_count++; + + if (m_count >= DCOFFSET_COUNT) { + m_accum /= wxFloat32(DCOFFSET_COUNT); + + m_offset = m_offset * 0.9F + m_accum * 0.1F; + + m_accum = 0.0F; + m_count = 0U; + } + } + + wxFloat32 out = m_filter.process(val - m_offset); + + bool bit = out > 0.0F; + + if (bit != m_prev) { + if (m_pll < (PLLMAX / 2U)) + m_pll += m_inc; + else + m_pll -= m_inc; + } + + m_prev = bit; + + m_pll += PLLINC; + + if (m_pll >= PLLMAX) { + if (m_invert) + state = bit ? STATE_TRUE : STATE_FALSE; + else + state = bit ? STATE_FALSE : STATE_TRUE; + + m_pll -= PLLMAX; + } + + return state; +} + +void CDStarGMSKDemodulator::reset() +{ + m_pll = 0U; + m_prev = false; + m_inc = INC_UNLOCK; + m_locked = false; +} + +void CDStarGMSKDemodulator::setInvert(bool set) +{ + m_invert = set; +} + +void CDStarGMSKDemodulator::lock(bool on) +{ + // Debugging only XXX + wxLogMessage(wxT("Current DC offset: %f"), m_offset); + + m_locked = on; + + m_inc = on ? INC_LOCK : INC_UNLOCK; +} diff --git a/Common/DStarGMSKDemodulator.h b/Common/DStarGMSKDemodulator.h new file mode 100644 index 0000000..38ba850 --- /dev/null +++ b/Common/DStarGMSKDemodulator.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009,2010,2013,2014 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef DStarGMSKDemodulator_H +#define DStarGMSKDemodulator_H + +#include "DStarDefines.h" +#include "FIRFilter.h" +#include "Utils.h" + +class CDStarGMSKDemodulator { +public: + CDStarGMSKDemodulator(); + ~CDStarGMSKDemodulator(); + + TRISTATE decode(wxFloat32 val); + + void setInvert(bool set); + + void reset(); + + void lock(bool on); + +private: + CFIRFilter m_filter; + bool m_invert; + unsigned int m_pll; + bool m_prev; + unsigned int m_inc; + bool m_locked; + wxFloat32 m_offset; + wxFloat32 m_accum; + unsigned int m_count; +}; + +#endif diff --git a/Common/DStarGMSKModulator.cpp b/Common/DStarGMSKModulator.cpp new file mode 100644 index 0000000..37c6c0d --- /dev/null +++ b/Common/DStarGMSKModulator.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009,2015 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "DStarGMSKModulator.h" + +#include "DStarDefines.h" + +// Generated by gaussfir(0.5, 4, 10) +const wxFloat32 COEFFS_TABLE[] = { + 0.000000000000003F, 0.000000000000065F, 0.000000000001037F, 0.000000000014448F, 0.000000000174579F, + 0.000000001829471F, 0.000000016627294F, 0.000000131062698F, 0.000000895979719F, 0.000005312253663F, 0.000027316243802F, + 0.000121821714020F, 0.000471183399421F, 0.001580581180127F, 0.004598383433830F, 0.011602594308899F, 0.025390226926262F, + 0.048188078330624F, 0.079318443411643F, 0.113232294527059F, 0.140193533802410F, 0.150538369557851F, 0.140193533802410F, + 0.113232294527059F, 0.079318443411643F, 0.048188078330623F, 0.025390226926262F, 0.011602594308899F, 0.004598383433830F, + 0.001580581180127F, 0.000471183399421F, 0.000121821714020F, 0.000027316243802F, 0.000005312253663F, 0.000000895979719F, + 0.000000131062698F, 0.000000016627294F, 0.000000001829471F, 0.000000000174579F, 0.000000000014448F, 0.000000000001037F, + 0.000000000000065F, 0.000000000000003F}; + +const unsigned int COEFFS_LENGTH = 43U; + +CDStarGMSKModulator::CDStarGMSKModulator() : +m_filter(COEFFS_TABLE, COEFFS_LENGTH), +m_invert(false) +{ +} + +CDStarGMSKModulator::~CDStarGMSKModulator() +{ +} + +unsigned int CDStarGMSKModulator::code(bool bit, wxFloat32* buffer, unsigned int length) +{ + wxASSERT(buffer != NULL); + wxASSERT(length == DSTAR_RADIO_BIT_LENGTH); + + if (m_invert) + bit = !bit; + + for (unsigned int i = 0U; i < DSTAR_RADIO_BIT_LENGTH; i++) { + if (bit) + buffer[i] = m_filter.process(-0.5F); + else + buffer[i] = m_filter.process(0.5F); + } + + return DSTAR_RADIO_BIT_LENGTH; +} + +void CDStarGMSKModulator::setInvert(bool set) +{ + m_invert = set; +} diff --git a/Common/DStarGMSKModulator.h b/Common/DStarGMSKModulator.h new file mode 100644 index 0000000..1a1b401 --- /dev/null +++ b/Common/DStarGMSKModulator.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef DStarGMSKModulator_H +#define DStarGMSKModulator_H + +#include "FIRFilter.h" + +class CDStarGMSKModulator { +public: + CDStarGMSKModulator(); + ~CDStarGMSKModulator(); + + unsigned int code(bool bit, wxFloat32* buffer, unsigned int length); + + void setInvert(bool set); + +private: + CFIRFilter m_filter; + bool m_invert; +}; + +#endif diff --git a/Common/DStarRepeaterConfig.cpp b/Common/DStarRepeaterConfig.cpp new file mode 100644 index 0000000..2a1fb33 --- /dev/null +++ b/Common/DStarRepeaterConfig.cpp @@ -0,0 +1,1906 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include + +#include "DStarRepeaterConfig.h" + +const wxString KEY_CALLSIGN = wxT("callsign"); +const wxString KEY_GATEWAY = wxT("gateway"); +const wxString KEY_MODE = wxT("mode"); +const wxString KEY_ACK = wxT("ack"); +const wxString KEY_RESTRICTION = wxT("restriction"); +const wxString KEY_RPT1_VALIDATION = wxT("rpt1Validation"); +const wxString KEY_DTMF_BLANKING = wxT("dtmfBlanking"); +const wxString KEY_ERROR_REPLY = wxT("errorReply"); +const wxString KEY_GATEWAY_ADDRESS = wxT("gatewayAddress"); +const wxString KEY_GATEWAY_PORT = wxT("gatewayPort"); +const wxString KEY_LOCAL_ADDRESS = wxT("localAddress"); +const wxString KEY_LOCAL_PORT = wxT("localPort"); +const wxString KEY_NETWORK_NAME = wxT("networkName"); +const wxString KEY_MODEM_TYPE = wxT("modemType"); +const wxString KEY_TIMEOUT = wxT("timeout"); +const wxString KEY_ACK_TIME = wxT("ackTime"); +const wxString KEY_BEACON_TIME = wxT("beaconTime"); +const wxString KEY_BEACON_TEXT = wxT("beaconText"); +const wxString KEY_BEACON_VOICE = wxT("beaconVoice"); +const wxString KEY_LANGUAGE = wxT("language"); +const wxString KEY_ANNOUNCEMENT_ENABLED = wxT("announcementEnabled"); +const wxString KEY_ANNOUNCEMENT_TIME = wxT("announcementTime"); +const wxString KEY_ANNOUNCEMENT_RECORD_RPT1 = wxT("announcementRecordRPT1"); +const wxString KEY_ANNOUNCEMENT_RECORD_RPT2 = wxT("announcementRecordRPT2"); +const wxString KEY_ANNOUNCEMENT_DELETE_RPT1 = wxT("announcementDeleteRPT1"); +const wxString KEY_ANNOUNCEMENT_DELETE_RPT2 = wxT("announcementDeleteRPT2"); +const wxString KEY_CONTROL_ENABLED = wxT("controlEnabled"); +const wxString KEY_CONTROL_RPT1 = wxT("controlRPT1"); +const wxString KEY_CONTROL_RPT2 = wxT("controlRPT2"); +const wxString KEY_CONTROL_SHUTDOWN = wxT("controlShutdown"); +const wxString KEY_CONTROL_STARTUP = wxT("controlStartup"); +const wxString KEY_CONTROL_STATUS1 = wxT("controlStatus1"); +const wxString KEY_CONTROL_STATUS2 = wxT("controlStatus2"); +const wxString KEY_CONTROL_STATUS3 = wxT("controlStatus3"); +const wxString KEY_CONTROL_STATUS4 = wxT("controlStatus4"); +const wxString KEY_CONTROL_STATUS5 = wxT("controlStatus5"); +const wxString KEY_CONTROL_COMMAND1 = wxT("controlCommand1"); +const wxString KEY_CONTROL_COMMAND1_LINE = wxT("controlCommand1Line"); +const wxString KEY_CONTROL_COMMAND2 = wxT("controlCommand2"); +const wxString KEY_CONTROL_COMMAND2_LINE = wxT("controlCommand2Line"); +const wxString KEY_CONTROL_COMMAND3 = wxT("controlCommand3"); +const wxString KEY_CONTROL_COMMAND3_LINE = wxT("controlCommand3Line"); +const wxString KEY_CONTROL_COMMAND4 = wxT("controlCommand4"); +const wxString KEY_CONTROL_COMMAND4_LINE = wxT("controlCommand4Line"); +const wxString KEY_CONTROL_COMMAND5 = wxT("controlCommand5"); +const wxString KEY_CONTROL_COMMAND5_LINE = wxT("controlCommand5Line"); +const wxString KEY_CONTROL_COMMAND6 = wxT("controlCommand6"); +const wxString KEY_CONTROL_COMMAND6_LINE = wxT("controlCommand6Line"); +const wxString KEY_CONTROL_OUTPUT1 = wxT("controlOutput1"); +const wxString KEY_CONTROL_OUTPUT2 = wxT("controlOutput2"); +const wxString KEY_CONTROL_OUTPUT3 = wxT("controlOutput3"); +const wxString KEY_CONTROL_OUTPUT4 = wxT("controlOutput4"); +const wxString KEY_CONTROLLER_TYPE = wxT("controllerType"); +const wxString KEY_SERIAL_CONFIG = wxT("serialConfig"); +const wxString KEY_PTT_INVERT = wxT("pttInvert"); +const wxString KEY_ACTIVE_HANG_TIME = wxT("activeHangTime"); +const wxString KEY_OUTPUT1 = wxT("output1"); +const wxString KEY_OUTPUT2 = wxT("output2"); +const wxString KEY_OUTPUT3 = wxT("output3"); +const wxString KEY_OUTPUT4 = wxT("output4"); +const wxString KEY_LOGGING = wxT("logging"); +const wxString KEY_WINDOW_X = wxT("windowX"); +const wxString KEY_WINDOW_Y = wxT("windowY"); + +const wxString KEY_DVAP_PORT = wxT("dvapPort"); +const wxString KEY_DVAP_FREQUENCY = wxT("dvapFrequency"); +const wxString KEY_DVAP_POWER = wxT("dvapPower"); +const wxString KEY_DVAP_SQUELCH = wxT("dvapSquelch"); + +const wxString KEY_GMSK_INTERFACE = wxT("gmskInterfaceType"); +const wxString KEY_GMSK_ADDRESS = wxT("gmskAddress"); + +const wxString KEY_DVRPTR1_PORT = wxT("dvrptr1Port"); +const wxString KEY_DVRPTR1_RXINVERT = wxT("dvrptr1RXInvert"); +const wxString KEY_DVRPTR1_TXINVERT = wxT("dvrptr1TXInvert"); +const wxString KEY_DVRPTR1_CHANNEL = wxT("dvrptr1Channel"); +const wxString KEY_DVRPTR1_MODLEVEL = wxT("dvrptr1ModLevel"); +const wxString KEY_DVRPTR1_TXDELAY = wxT("dvrptr1TXDelay"); + +const wxString KEY_DVRPTR2_CONNECTION = wxT("dvrptr2Connection"); +const wxString KEY_DVRPTR2_USBPORT = wxT("dvrptr2USBPort"); +const wxString KEY_DVRPTR2_ADDRESS = wxT("dvrptr2Address"); +const wxString KEY_DVRPTR2_PORT = wxT("dvrptr2Port"); +const wxString KEY_DVRPTR2_TXINVERT = wxT("dvrptr2TXInvert"); +const wxString KEY_DVRPTR2_MODLEVEL = wxT("dvrptr2ModLevel"); +const wxString KEY_DVRPTR2_TXDELAY = wxT("dvrptr2TXDelay"); + +const wxString KEY_DVRPTR3_CONNECTION = wxT("dvrptr3Connection"); +const wxString KEY_DVRPTR3_USBPORT = wxT("dvrptr3USBPort"); +const wxString KEY_DVRPTR3_ADDRESS = wxT("dvrptr3Address"); +const wxString KEY_DVRPTR3_PORT = wxT("dvrptr3Port"); +const wxString KEY_DVRPTR3_TXINVERT = wxT("dvrptr3TXInvert"); +const wxString KEY_DVRPTR3_MODLEVEL = wxT("dvrptr3ModLevel"); +const wxString KEY_DVRPTR3_TXDELAY = wxT("dvrptr3TXDelay"); + +const wxString KEY_DVMEGA_PORT = wxT("dvmegaPort"); +const wxString KEY_DVMEGA_VARIANT = wxT("dvmegaVariant"); +const wxString KEY_DVMEGA_RXINVERT = wxT("dvmegaRXInvert"); +const wxString KEY_DVMEGA_TXINVERT = wxT("dvmegaTXInvert"); +const wxString KEY_DVMEGA_TXDELAY = wxT("dvmegaTXDelay"); +const wxString KEY_DVMEGA_RXFREQUENCY = wxT("dvmegaRXFrequency"); +const wxString KEY_DVMEGA_TXFREQUENCY = wxT("dvmegaTXFrequency"); +const wxString KEY_DVMEGA_POWER = wxT("dvmegaPower"); + +const wxString KEY_MMDVM_PORT = wxT("mmdvmPort"); +const wxString KEY_MMDVM_RXINVERT = wxT("mmdvmRXInvert"); +const wxString KEY_MMDVM_TXINVERT = wxT("mmdvmTXInvert"); +const wxString KEY_MMDVM_PTTINVERT = wxT("mmdvmPTTInvert"); +const wxString KEY_MMDVM_TXDELAY = wxT("mmdvmTXDelay"); +const wxString KEY_MMDVM_RXLEVEL = wxT("mmdvmRXLevel"); +const wxString KEY_MMDVM_TXLEVEL = wxT("mmdvmTXLevel"); + +const wxString KEY_SOUNDCARD_RXDEVICE = wxT("soundCardRXDevice"); +const wxString KEY_SOUNDCARD_TXDEVICE = wxT("soundCardTXDevice"); +const wxString KEY_SOUNDCARD_RXINVERT = wxT("soundCardRXInvert"); +const wxString KEY_SOUNDCARD_TXINVERT = wxT("soundCardTXInvert"); +const wxString KEY_SOUNDCARD_RXLEVEL = wxT("soundCardRXLevel"); +const wxString KEY_SOUNDCARD_TXLEVEL = wxT("soundCardTXLevel"); +const wxString KEY_SOUNDCARD_TXDELAY = wxT("soundCardTXDelay"); +const wxString KEY_SOUNDCARD_TXTAIL = wxT("soundCardTXTail"); + +const wxString KEY_SPLIT_LOCALADDRESS = wxT("splitLocalAddress"); +const wxString KEY_SPLIT_LOCALPORT = wxT("splitLocalPort"); +const wxString KEY_SPLIT_TXNAME = wxT("splitTXName"); +const wxString KEY_SPLIT_RXNAME = wxT("splitRXName"); +const wxString KEY_SPLIT_TIMEOUT = wxT("splitTimeout"); + + +const wxString DEFAULT_CALLSIGN = wxT("GB3IN C"); +const wxString DEFAULT_GATEWAY = wxEmptyString; +const DSTAR_MODE DEFAULT_MODE = MODE_DUPLEX; +const ACK_TYPE DEFAULT_ACK = AT_BER; +const bool DEFAULT_RESTRICTION = false; +const bool DEFAULT_RPT1_VALIDATION = true; +const bool DEFAULT_DTMF_BLANKING = true; +const bool DEFAULT_ERROR_REPLY = true; +const wxString DEFAULT_GATEWAY_ADDRESS = wxT("127.0.0.1"); +const unsigned int DEFAULT_GATEWAY_PORT = 20010U; +const wxString DEFAULT_LOCAL_ADDRESS = wxT("127.0.0.1"); +const unsigned int DEFAULT_LOCAL_PORT = 20011U; +const wxString DEFAULT_NETWORK_NAME = wxEmptyString; +const wxString DEFAULT_MODEM_TYPE = wxT("DVAP"); +const unsigned int DEFAULT_TIMEOUT = 180U; +const unsigned int DEFAULT_ACK_TIME = 500U; +const unsigned int DEFAULT_BEACON_TIME = 600U; +const wxString DEFAULT_BEACON_TEXT = wxT("D-Star Repeater"); +const bool DEFAULT_BEACON_VOICE = false; +const TEXT_LANG DEFAULT_LANGUAGE = TL_ENGLISH_UK; +const bool DEFAULT_ANNOUNCEMENT_ENABLED = false; +const unsigned int DEFAULT_ANNOUNCEMENT_TIME = 500U; +const wxString DEFAULT_ANNOUNCEMENT_RECORD_RPT1 = wxEmptyString; +const wxString DEFAULT_ANNOUNCEMENT_RECORD_RPT2 = wxEmptyString; +const wxString DEFAULT_ANNOUNCEMENT_DELETE_RPT1 = wxEmptyString; +const wxString DEFAULT_ANNOUNCEMENT_DELETE_RPT2 = wxEmptyString; +const bool DEFAULT_CONTROL_ENABLED = false; +const wxString DEFAULT_CONTROL_RPT1 = wxEmptyString; +const wxString DEFAULT_CONTROL_RPT2 = wxEmptyString; +const wxString DEFAULT_CONTROL_SHUTDOWN = wxEmptyString; +const wxString DEFAULT_CONTROL_STARTUP = wxEmptyString; +const wxString DEFAULT_CONTROL_STATUS = wxEmptyString; +const wxString DEFAULT_CONTROL_COMMAND = wxEmptyString; +const wxString DEFAULT_CONTROL_COMMAND_LINE = wxEmptyString; +const wxString DEFAULT_CONTROL_OUTPUT = wxEmptyString; +const wxString DEFAULT_CONTROLLER_TYPE = wxEmptyString; +const unsigned int DEFAULT_SERIAL_CONFIG = 1U; +const bool DEFAULT_PTT_INVERT = false; +const unsigned int DEFAULT_ACTIVE_HANG_TIME = 0U; +const bool DEFAULT_OUTPUT = false; +const bool DEFAULT_LOGGING = false; +const int DEFAULT_WINDOW_X = -1; +const int DEFAULT_WINDOW_Y = -1; + +const wxString DEFAULT_DVAP_PORT = wxEmptyString; +const unsigned int DEFAULT_DVAP_FREQUENCY = 145500000U; +const int DEFAULT_DVAP_POWER = 10; +const int DEFAULT_DVAP_SQUELCH = -100; + +#if defined(WIN32) +const USB_INTERFACE DEFAULT_GMSK_INTERFACE = UI_WINUSB; +#else +const USB_INTERFACE DEFAULT_GMSK_INTERFACE = UI_LIBUSB; +#endif +const unsigned int DEFAULT_GMSK_ADDRESS = 0x0300U; + +const wxString DEFAULT_DVRPTR1_PORT = wxEmptyString; +const bool DEFAULT_DVRPTR1_RXINVERT = false; +const bool DEFAULT_DVRPTR1_TXINVERT = false; +const bool DEFAULT_DVRPTR1_CHANNEL = false; +const unsigned int DEFAULT_DVRPTR1_MODLEVEL = 20U; +const unsigned int DEFAULT_DVRPTR1_TXDELAY = 150U; + +const CONNECTION_TYPE DEFAULT_DVRPTR2_CONNECTION = CT_USB; +const wxString DEFAULT_DVRPTR2_USBPORT = wxEmptyString; +const wxString DEFAULT_DVRPTR2_ADDRESS = wxT("127.0.0.1"); +const unsigned int DEFAULT_DVRPTR2_PORT = 0U; +const bool DEFAULT_DVRPTR2_TXINVERT = false; +const unsigned int DEFAULT_DVRPTR2_MODLEVEL = 20U; +const unsigned int DEFAULT_DVRPTR2_TXDELAY = 150U; + +const CONNECTION_TYPE DEFAULT_DVRPTR3_CONNECTION = CT_USB; +const wxString DEFAULT_DVRPTR3_USBPORT = wxEmptyString; +const wxString DEFAULT_DVRPTR3_ADDRESS = wxT("127.0.0.1"); +const unsigned int DEFAULT_DVRPTR3_PORT = 0U; +const bool DEFAULT_DVRPTR3_TXINVERT = false; +const unsigned int DEFAULT_DVRPTR3_MODLEVEL = 20U; +const unsigned int DEFAULT_DVRPTR3_TXDELAY = 150U; + +const wxString DEFAULT_DVMEGA_PORT = wxEmptyString; +const DVMEGA_VARIANT DEFAULT_DVMEGA_VARIANT = DVMV_MODEM; +const bool DEFAULT_DVMEGA_RXINVERT = false; +const bool DEFAULT_DVMEGA_TXINVERT = false; +const unsigned int DEFAULT_DVMEGA_TXDELAY = 150U; +const unsigned int DEFAULT_DVMEGA_RXFREQUENCY = 145500000U; +const unsigned int DEFAULT_DVMEGA_TXFREQUENCY = 145500000U; +const unsigned int DEFAULT_DVMEGA_POWER = 100U; + +const wxString DEFAULT_MMDVM_PORT = wxEmptyString; +const bool DEFAULT_MMDVM_RXINVERT = false; +const bool DEFAULT_MMDVM_TXINVERT = false; +const bool DEFAULT_MMDVM_PTTINVERT = false; +const unsigned int DEFAULT_MMDVM_TXDELAY = 50U; +const unsigned int DEFAULT_MMDVM_RXLEVEL = 100U; +const unsigned int DEFAULT_MMDVM_TXLEVEL = 100U; + +const wxString DEFAULT_SOUNDCARD_RXDEVICE = wxEmptyString; +const wxString DEFAULT_SOUNDCARD_TXDEVICE = wxEmptyString; +const bool DEFAULT_SOUNDCARD_RXINVERT = false; +const bool DEFAULT_SOUNDCARD_TXINVERT = false; +const wxFloat32 DEFAULT_SOUNDCARD_RXLEVEL = 1.0F; +const wxFloat32 DEFAULT_SOUNDCARD_TXLEVEL = 1.0F; +const unsigned int DEFAULT_SOUNDCARD_TXDELAY = 150U; +const unsigned int DEFAULT_SOUNDCARD_TXTAIL = 50U; + +const wxString DEFAULT_SPLIT_LOCALADDRESS = wxEmptyString; +const unsigned int DEFAULT_SPLIT_LOCALPORT = 0U; +const wxString DEFAULT_SPLIT_TXNAME = wxEmptyString; +const wxString DEFAULT_SPLIT_RXNAME = wxEmptyString; +const unsigned int DEFAULT_SPLIT_TIMEOUT = 0U; + +#if defined(__WINDOWS__) + +CDStarRepeaterConfig::CDStarRepeaterConfig(wxConfigBase* config, const wxString& dir, const wxString& configName, const wxString& name) : +m_config(config), +m_name(wxT("/")), +m_fileName(), +m_callsign(DEFAULT_CALLSIGN), +m_gateway(DEFAULT_GATEWAY), +m_mode(DEFAULT_MODE), +m_ack(DEFAULT_ACK), +m_restriction(DEFAULT_RESTRICTION), +m_rpt1Validation(DEFAULT_RPT1_VALIDATION), +m_dtmfBlanking(DEFAULT_DTMF_BLANKING), +m_errorReply(DEFAULT_ERROR_REPLY), +m_gatewayAddress(DEFAULT_GATEWAY_ADDRESS), +m_gatewayPort(DEFAULT_GATEWAY_PORT), +m_localAddress(DEFAULT_LOCAL_ADDRESS), +m_localPort(DEFAULT_LOCAL_PORT), +m_networkName(DEFAULT_NETWORK_NAME), +m_modemType(DEFAULT_MODEM_TYPE), +m_timeout(DEFAULT_TIMEOUT), +m_ackTime(DEFAULT_ACK_TIME), +m_beaconTime(DEFAULT_BEACON_TIME), +m_beaconText(DEFAULT_BEACON_TEXT), +m_beaconVoice(DEFAULT_BEACON_VOICE), +m_language(DEFAULT_LANGUAGE), +m_announcementEnabled(DEFAULT_ANNOUNCEMENT_ENABLED), +m_announcementTime(DEFAULT_ANNOUNCEMENT_TIME), +m_announcementRecordRPT1(DEFAULT_ANNOUNCEMENT_RECORD_RPT1), +m_announcementRecordRPT2(DEFAULT_ANNOUNCEMENT_RECORD_RPT2), +m_announcementDeleteRPT1(DEFAULT_ANNOUNCEMENT_DELETE_RPT1), +m_announcementDeleteRPT2(DEFAULT_ANNOUNCEMENT_DELETE_RPT2), +m_controlEnabled(DEFAULT_CONTROL_ENABLED), +m_controlRpt1Callsign(DEFAULT_CONTROL_RPT1), +m_controlRpt2Callsign(DEFAULT_CONTROL_RPT2), +m_controlShutdown(DEFAULT_CONTROL_SHUTDOWN), +m_controlStartup(DEFAULT_CONTROL_STARTUP), +m_controlStatus1(DEFAULT_CONTROL_STATUS), +m_controlStatus2(DEFAULT_CONTROL_STATUS), +m_controlStatus3(DEFAULT_CONTROL_STATUS), +m_controlStatus4(DEFAULT_CONTROL_STATUS), +m_controlStatus5(DEFAULT_CONTROL_STATUS), +m_controlCommand1(DEFAULT_CONTROL_COMMAND), +m_controlCommand1Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand2(DEFAULT_CONTROL_COMMAND), +m_controlCommand2Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand3(DEFAULT_CONTROL_COMMAND), +m_controlCommand3Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand4(DEFAULT_CONTROL_COMMAND), +m_controlCommand4Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand5(DEFAULT_CONTROL_COMMAND), +m_controlCommand5Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand6(DEFAULT_CONTROL_COMMAND), +m_controlCommand6Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlOutput1(DEFAULT_CONTROL_OUTPUT), +m_controlOutput2(DEFAULT_CONTROL_OUTPUT), +m_controlOutput3(DEFAULT_CONTROL_OUTPUT), +m_controlOutput4(DEFAULT_CONTROL_OUTPUT), +m_controllerType(DEFAULT_CONTROLLER_TYPE), +m_serialConfig(DEFAULT_SERIAL_CONFIG), +m_pttInvert(DEFAULT_PTT_INVERT), +m_activeHangTime(DEFAULT_ACTIVE_HANG_TIME), +m_output1(DEFAULT_OUTPUT), +m_output2(DEFAULT_OUTPUT), +m_output3(DEFAULT_OUTPUT), +m_output4(DEFAULT_OUTPUT), +m_logging(DEFAULT_LOGGING), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y), +m_dvapPort(DEFAULT_DVAP_PORT), +m_dvapFrequency(DEFAULT_DVAP_FREQUENCY), +m_dvapPower(DEFAULT_DVAP_POWER), +m_dvapSquelch(DEFAULT_DVAP_SQUELCH), +m_gmskInterface(DEFAULT_GMSK_INTERFACE), +m_gmskAddress(DEFAULT_GMSK_ADDRESS), +m_dvrptr1Port(DEFAULT_DVRPTR1_PORT), +m_dvrptr1RXInvert(DEFAULT_DVRPTR1_RXINVERT), +m_dvrptr1TXInvert(DEFAULT_DVRPTR1_TXINVERT), +m_dvrptr1Channel(DEFAULT_DVRPTR1_CHANNEL), +m_dvrptr1ModLevel(DEFAULT_DVRPTR1_MODLEVEL), +m_dvrptr1TXDelay(DEFAULT_DVRPTR1_TXDELAY), +m_dvrptr2Connection(DEFAULT_DVRPTR2_CONNECTION), +m_dvrptr2USBPort(DEFAULT_DVRPTR2_USBPORT), +m_dvrptr2Address(DEFAULT_DVRPTR2_ADDRESS), +m_dvrptr2Port(DEFAULT_DVRPTR2_PORT), +m_dvrptr2TXInvert(DEFAULT_DVRPTR2_TXINVERT), +m_dvrptr2ModLevel(DEFAULT_DVRPTR2_MODLEVEL), +m_dvrptr2TXDelay(DEFAULT_DVRPTR2_TXDELAY), +m_dvrptr3Connection(DEFAULT_DVRPTR3_CONNECTION), +m_dvrptr3USBPort(DEFAULT_DVRPTR3_USBPORT), +m_dvrptr3Address(DEFAULT_DVRPTR3_ADDRESS), +m_dvrptr3Port(DEFAULT_DVRPTR3_PORT), +m_dvrptr3TXInvert(DEFAULT_DVRPTR3_TXINVERT), +m_dvrptr3ModLevel(DEFAULT_DVRPTR3_MODLEVEL), +m_dvrptr3TXDelay(DEFAULT_DVRPTR3_TXDELAY), +m_dvmegaPort(DEFAULT_DVMEGA_PORT), +m_dvmegaVariant(DEFAULT_DVMEGA_VARIANT), +m_dvmegaRXInvert(DEFAULT_DVMEGA_RXINVERT), +m_dvmegaTXInvert(DEFAULT_DVMEGA_TXINVERT), +m_dvmegaTXDelay(DEFAULT_DVMEGA_TXDELAY), +m_dvmegaRXFrequency(DEFAULT_DVMEGA_RXFREQUENCY), +m_dvmegaTXFrequency(DEFAULT_DVMEGA_TXFREQUENCY), +m_dvmegaPower(DEFAULT_DVMEGA_POWER), +m_mmdvmPort(DEFAULT_MMDVM_PORT), +m_mmdvmRXInvert(DEFAULT_MMDVM_RXINVERT), +m_mmdvmTXInvert(DEFAULT_MMDVM_TXINVERT), +m_mmdvmPTTInvert(DEFAULT_MMDVM_PTTINVERT), +m_mmdvmTXDelay(DEFAULT_MMDVM_TXDELAY), +m_mmdvmRXLevel(DEFAULT_MMDVM_RXLEVEL), +m_mmdvmTXLevel(DEFAULT_MMDVM_TXLEVEL), +m_soundCardRXDevice(DEFAULT_SOUNDCARD_RXDEVICE), +m_soundCardTXDevice(DEFAULT_SOUNDCARD_TXDEVICE), +m_soundCardRXInvert(DEFAULT_SOUNDCARD_RXINVERT), +m_soundCardTXInvert(DEFAULT_SOUNDCARD_TXINVERT), +m_soundCardRXLevel(DEFAULT_SOUNDCARD_RXLEVEL), +m_soundCardTXLevel(DEFAULT_SOUNDCARD_TXLEVEL), +m_soundCardTXDelay(DEFAULT_SOUNDCARD_TXDELAY), +m_soundCardTXTail(DEFAULT_SOUNDCARD_TXTAIL), +m_splitLocalAddress(DEFAULT_SPLIT_LOCALADDRESS), +m_splitLocalPort(DEFAULT_SPLIT_LOCALPORT), +m_splitTXNames(), +m_splitRXNames(), +m_splitTimeout(DEFAULT_SPLIT_TIMEOUT) +{ + wxASSERT(config != NULL); + wxASSERT(!dir.IsEmpty()); + + wxString fileName = configName; + + if (!name.IsEmpty()) { + fileName = configName + wxT("_") + name; + m_name = wxT("/") + name + wxT("/"); + } + + m_fileName.Assign(dir, fileName); + + long temp; + double temp1; + + m_config->Read(m_name + KEY_CALLSIGN, &m_callsign, DEFAULT_CALLSIGN); + + m_config->Read(m_name + KEY_GATEWAY, &m_gateway, DEFAULT_GATEWAY); + + m_config->Read(m_name + KEY_MODE, &temp, long(DEFAULT_MODE)); + m_mode = DSTAR_MODE(temp); + + m_config->Read(m_name + KEY_ACK, &temp, long(DEFAULT_ACK)); + m_ack = ACK_TYPE(temp); + + m_config->Read(m_name + KEY_RESTRICTION, &m_restriction, DEFAULT_RESTRICTION); + + m_config->Read(m_name + KEY_RPT1_VALIDATION, &m_rpt1Validation, DEFAULT_RPT1_VALIDATION); + + m_config->Read(m_name + KEY_DTMF_BLANKING, &m_dtmfBlanking, DEFAULT_DTMF_BLANKING); + + m_config->Read(m_name + KEY_ERROR_REPLY, &m_errorReply, DEFAULT_ERROR_REPLY); + + m_config->Read(m_name + KEY_GATEWAY_ADDRESS, &m_gatewayAddress, DEFAULT_GATEWAY_ADDRESS); + + m_config->Read(m_name + KEY_GATEWAY_PORT, &temp, long(DEFAULT_GATEWAY_PORT)); + m_gatewayPort = (unsigned int)temp; + + m_config->Read(m_name + KEY_LOCAL_ADDRESS, &m_localAddress, DEFAULT_LOCAL_ADDRESS); + + m_config->Read(m_name + KEY_LOCAL_PORT, &temp, long(DEFAULT_LOCAL_PORT)); + m_localPort = (unsigned int)temp; + + m_config->Read(m_name + KEY_NETWORK_NAME, &m_networkName, DEFAULT_NETWORK_NAME); + + m_config->Read(m_name + KEY_MODEM_TYPE, &m_modemType, DEFAULT_MODEM_TYPE); + + m_config->Read(m_name + KEY_TIMEOUT, &temp, long(DEFAULT_TIMEOUT)); + m_timeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_ACK_TIME, &temp, long(DEFAULT_ACK_TIME)); + m_ackTime = (unsigned int)temp; + + m_config->Read(m_name + KEY_BEACON_TIME, &temp, long(DEFAULT_BEACON_TIME)); + m_beaconTime = (unsigned int)temp; + + m_config->Read(m_name + KEY_BEACON_TEXT, &m_beaconText, DEFAULT_BEACON_TEXT); + + m_config->Read(m_name + KEY_BEACON_VOICE, &m_beaconVoice, DEFAULT_BEACON_VOICE); + + m_config->Read(m_name + KEY_LANGUAGE, &temp, long(DEFAULT_LANGUAGE)); + m_language = TEXT_LANG(temp); + + m_config->Read(m_name + KEY_ANNOUNCEMENT_ENABLED, &m_announcementEnabled, DEFAULT_ANNOUNCEMENT_ENABLED); + + m_config->Read(m_name + KEY_ANNOUNCEMENT_TIME, &temp, long(DEFAULT_ANNOUNCEMENT_TIME)); + m_announcementTime = (unsigned int)temp; + + m_config->Read(m_name + KEY_ANNOUNCEMENT_RECORD_RPT1, &m_announcementRecordRPT1, DEFAULT_ANNOUNCEMENT_RECORD_RPT1); + + m_config->Read(m_name + KEY_ANNOUNCEMENT_RECORD_RPT2, &m_announcementRecordRPT2, DEFAULT_ANNOUNCEMENT_RECORD_RPT2); + + m_config->Read(m_name + KEY_ANNOUNCEMENT_DELETE_RPT1, &m_announcementDeleteRPT1, DEFAULT_ANNOUNCEMENT_DELETE_RPT1); + + m_config->Read(m_name + KEY_ANNOUNCEMENT_DELETE_RPT2, &m_announcementDeleteRPT2, DEFAULT_ANNOUNCEMENT_DELETE_RPT2); + + m_config->Read(m_name + KEY_CONTROL_ENABLED, &m_controlEnabled, DEFAULT_CONTROL_ENABLED); + + m_config->Read(m_name + KEY_CONTROL_RPT1, &m_controlRpt1Callsign, DEFAULT_CONTROL_RPT1); + + m_config->Read(m_name + KEY_CONTROL_RPT2, &m_controlRpt2Callsign, DEFAULT_CONTROL_RPT2); + + m_config->Read(m_name + KEY_CONTROL_SHUTDOWN, &m_controlShutdown, DEFAULT_CONTROL_SHUTDOWN); + + m_config->Read(m_name + KEY_CONTROL_STARTUP, &m_controlStartup, DEFAULT_CONTROL_STARTUP); + + m_config->Read(m_name + KEY_CONTROL_STATUS1, &m_controlStatus1, DEFAULT_CONTROL_STATUS); + + m_config->Read(m_name + KEY_CONTROL_STATUS2, &m_controlStatus2, DEFAULT_CONTROL_STATUS); + + m_config->Read(m_name + KEY_CONTROL_STATUS3, &m_controlStatus3, DEFAULT_CONTROL_STATUS); + + m_config->Read(m_name + KEY_CONTROL_STATUS4, &m_controlStatus4, DEFAULT_CONTROL_STATUS); + + m_config->Read(m_name + KEY_CONTROL_STATUS5, &m_controlStatus5, DEFAULT_CONTROL_STATUS); + + m_config->Read(m_name + KEY_CONTROL_COMMAND1, &m_controlCommand1, DEFAULT_CONTROL_COMMAND); + + m_config->Read(m_name + KEY_CONTROL_COMMAND1_LINE, &m_controlCommand1Line, DEFAULT_CONTROL_COMMAND_LINE); + + m_config->Read(m_name + KEY_CONTROL_COMMAND2, &m_controlCommand2, DEFAULT_CONTROL_COMMAND); + + m_config->Read(m_name + KEY_CONTROL_COMMAND2_LINE, &m_controlCommand2Line, DEFAULT_CONTROL_COMMAND_LINE); + + m_config->Read(m_name + KEY_CONTROL_COMMAND3, &m_controlCommand3, DEFAULT_CONTROL_COMMAND); + + m_config->Read(m_name + KEY_CONTROL_COMMAND3_LINE, &m_controlCommand3Line, DEFAULT_CONTROL_COMMAND_LINE); + + m_config->Read(m_name + KEY_CONTROL_COMMAND4, &m_controlCommand4, DEFAULT_CONTROL_COMMAND); + + m_config->Read(m_name + KEY_CONTROL_COMMAND4_LINE, &m_controlCommand4Line, DEFAULT_CONTROL_COMMAND_LINE); + + m_config->Read(m_name + KEY_CONTROL_COMMAND5, &m_controlCommand5, DEFAULT_CONTROL_COMMAND); + + m_config->Read(m_name + KEY_CONTROL_COMMAND5_LINE, &m_controlCommand5Line, DEFAULT_CONTROL_COMMAND_LINE); + + m_config->Read(m_name + KEY_CONTROL_COMMAND6, &m_controlCommand6, DEFAULT_CONTROL_COMMAND); + + m_config->Read(m_name + KEY_CONTROL_COMMAND6_LINE, &m_controlCommand6Line, DEFAULT_CONTROL_COMMAND_LINE); + + m_config->Read(m_name + KEY_CONTROL_OUTPUT1, &m_controlOutput1, DEFAULT_CONTROL_OUTPUT); + + m_config->Read(m_name + KEY_CONTROL_OUTPUT2, &m_controlOutput2, DEFAULT_CONTROL_OUTPUT); + + m_config->Read(m_name + KEY_CONTROL_OUTPUT3, &m_controlOutput3, DEFAULT_CONTROL_OUTPUT); + + m_config->Read(m_name + KEY_CONTROL_OUTPUT4, &m_controlOutput4, DEFAULT_CONTROL_OUTPUT); + + m_config->Read(m_name + KEY_CONTROLLER_TYPE, &m_controllerType, DEFAULT_CONTROLLER_TYPE); + + m_config->Read(m_name + KEY_SERIAL_CONFIG, &temp, long(DEFAULT_SERIAL_CONFIG)); + m_serialConfig = (unsigned int)temp; + + m_config->Read(m_name + KEY_PTT_INVERT, &m_pttInvert, DEFAULT_PTT_INVERT); + + m_config->Read(m_name + KEY_ACTIVE_HANG_TIME, &temp, long(DEFAULT_ACTIVE_HANG_TIME)); + m_activeHangTime = (unsigned int)temp; + + m_config->Read(m_name + KEY_OUTPUT1, &m_output1, DEFAULT_OUTPUT); + + m_config->Read(m_name + KEY_OUTPUT2, &m_output2, DEFAULT_OUTPUT); + + m_config->Read(m_name + KEY_OUTPUT3, &m_output3, DEFAULT_OUTPUT); + + m_config->Read(m_name + KEY_OUTPUT4, &m_output4, DEFAULT_OUTPUT); + + m_config->Read(m_name + KEY_LOGGING, &m_logging, DEFAULT_LOGGING); + + m_config->Read(m_name + KEY_WINDOW_X, &temp, long(DEFAULT_WINDOW_X)); + m_x = int(temp); + + m_config->Read(m_name + KEY_WINDOW_Y, &temp, long(DEFAULT_WINDOW_Y)); + m_y = int(temp); + + m_config->Read(m_name + KEY_DVAP_PORT, &m_dvapPort, DEFAULT_DVAP_PORT); + + m_config->Read(m_name + KEY_DVAP_FREQUENCY, &temp, long(DEFAULT_DVAP_FREQUENCY)); + m_dvapFrequency = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVAP_POWER, &temp, long(DEFAULT_DVAP_POWER)); + m_dvapPower = int(temp); + + m_config->Read(m_name + KEY_DVAP_SQUELCH, &temp, long(DEFAULT_DVAP_SQUELCH)); + m_dvapSquelch = int(temp); + + m_config->Read(m_name + KEY_GMSK_INTERFACE, &temp, long(DEFAULT_GMSK_INTERFACE)); + m_gmskInterface = USB_INTERFACE(temp); + + m_config->Read(m_name + KEY_GMSK_ADDRESS, &temp, long(DEFAULT_GMSK_ADDRESS)); + m_gmskAddress = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVRPTR1_PORT, &m_dvrptr1Port, DEFAULT_DVRPTR1_PORT); + + m_config->Read(m_name + KEY_DVRPTR1_RXINVERT, &m_dvrptr1RXInvert, DEFAULT_DVRPTR1_RXINVERT); + + m_config->Read(m_name + KEY_DVRPTR1_TXINVERT, &m_dvrptr1TXInvert, DEFAULT_DVRPTR1_TXINVERT); + + m_config->Read(m_name + KEY_DVRPTR1_CHANNEL, &m_dvrptr1Channel, DEFAULT_DVRPTR1_CHANNEL); + + m_config->Read(m_name + KEY_DVRPTR1_MODLEVEL, &temp, long(DEFAULT_DVRPTR1_MODLEVEL)); + m_dvrptr1ModLevel = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVRPTR1_TXDELAY, &temp, long(DEFAULT_DVRPTR1_TXDELAY)); + m_dvrptr1TXDelay = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVRPTR2_CONNECTION, &temp, long(DEFAULT_DVRPTR2_CONNECTION)); + m_dvrptr2Connection = CONNECTION_TYPE(temp); + + m_config->Read(m_name + KEY_DVRPTR2_USBPORT, &m_dvrptr2USBPort, DEFAULT_DVRPTR2_USBPORT); + + m_config->Read(m_name + KEY_DVRPTR2_ADDRESS, &m_dvrptr2Address, DEFAULT_DVRPTR2_ADDRESS); + + m_config->Read(m_name + KEY_DVRPTR2_PORT, &temp, long(DEFAULT_DVRPTR2_PORT)); + m_dvrptr2Port = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVRPTR2_TXINVERT, &m_dvrptr2TXInvert, DEFAULT_DVRPTR2_TXINVERT); + + m_config->Read(m_name + KEY_DVRPTR2_MODLEVEL, &temp, long(DEFAULT_DVRPTR2_MODLEVEL)); + m_dvrptr2ModLevel = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVRPTR2_TXDELAY, &temp, long(DEFAULT_DVRPTR2_TXDELAY)); + m_dvrptr2TXDelay = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVRPTR3_CONNECTION, &temp, long(DEFAULT_DVRPTR3_CONNECTION)); + m_dvrptr3Connection = CONNECTION_TYPE(temp); + + m_config->Read(m_name + KEY_DVRPTR3_USBPORT, &m_dvrptr3USBPort, DEFAULT_DVRPTR3_USBPORT); + + m_config->Read(m_name + KEY_DVRPTR3_ADDRESS, &m_dvrptr3Address, DEFAULT_DVRPTR3_ADDRESS); + + m_config->Read(m_name + KEY_DVRPTR3_PORT, &temp, long(DEFAULT_DVRPTR3_PORT)); + m_dvrptr3Port = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVRPTR3_TXINVERT, &m_dvrptr3TXInvert, DEFAULT_DVRPTR3_TXINVERT); + + m_config->Read(m_name + KEY_DVRPTR3_MODLEVEL, &temp, long(DEFAULT_DVRPTR3_MODLEVEL)); + m_dvrptr3ModLevel = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVRPTR3_TXDELAY, &temp, long(DEFAULT_DVRPTR3_TXDELAY)); + m_dvrptr3TXDelay = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVMEGA_PORT, &m_dvmegaPort, DEFAULT_DVMEGA_PORT); + + m_config->Read(m_name + KEY_DVMEGA_VARIANT, &temp, long(DEFAULT_DVMEGA_VARIANT)); + m_dvmegaVariant = DVMEGA_VARIANT(temp); + + m_config->Read(m_name + KEY_DVMEGA_RXINVERT, &m_dvmegaRXInvert, DEFAULT_DVMEGA_RXINVERT); + + m_config->Read(m_name + KEY_DVMEGA_TXINVERT, &m_dvmegaTXInvert, DEFAULT_DVMEGA_TXINVERT); + + m_config->Read(m_name + KEY_DVMEGA_TXDELAY, &temp, long(DEFAULT_DVMEGA_TXDELAY)); + m_dvmegaTXDelay = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVMEGA_RXFREQUENCY, &temp, long(DEFAULT_DVMEGA_RXFREQUENCY)); + m_dvmegaRXFrequency = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVMEGA_TXFREQUENCY, &temp, long(DEFAULT_DVMEGA_TXFREQUENCY)); + m_dvmegaTXFrequency = (unsigned int)temp; + + m_config->Read(m_name + KEY_DVMEGA_POWER, &temp, long(DEFAULT_DVMEGA_POWER)); + m_dvmegaPower = (unsigned int)temp; + + m_config->Read(m_name + KEY_MMDVM_PORT, &m_mmdvmPort, DEFAULT_MMDVM_PORT); + + m_config->Read(m_name + KEY_MMDVM_RXINVERT, &m_mmdvmRXInvert, DEFAULT_MMDVM_RXINVERT); + + m_config->Read(m_name + KEY_MMDVM_TXINVERT, &m_mmdvmTXInvert, DEFAULT_MMDVM_TXINVERT); + + m_config->Read(m_name + KEY_MMDVM_PTTINVERT, &m_mmdvmPTTInvert, DEFAULT_MMDVM_PTTINVERT); + + m_config->Read(m_name + KEY_MMDVM_TXDELAY, &temp, long(DEFAULT_MMDVM_TXDELAY)); + m_mmdvmTXDelay = (unsigned int)temp; + + m_config->Read(m_name + KEY_MMDVM_RXLEVEL, &temp, long(DEFAULT_MMDVM_RXLEVEL)); + m_mmdvmRXLevel = (unsigned int)temp; + + m_config->Read(m_name + KEY_MMDVM_TXLEVEL, &temp, long(DEFAULT_MMDVM_TXLEVEL)); + m_mmdvmTXLevel = (unsigned int)temp; + + m_config->Read(m_name + KEY_SOUNDCARD_RXDEVICE, &m_soundCardRXDevice, DEFAULT_SOUNDCARD_RXDEVICE); + + m_config->Read(m_name + KEY_SOUNDCARD_TXDEVICE, &m_soundCardTXDevice, DEFAULT_SOUNDCARD_TXDEVICE); + + m_config->Read(m_name + KEY_SOUNDCARD_RXINVERT, &m_soundCardRXInvert, DEFAULT_SOUNDCARD_RXINVERT); + + m_config->Read(m_name + KEY_SOUNDCARD_TXINVERT, &m_soundCardTXInvert, DEFAULT_SOUNDCARD_TXINVERT); + + m_config->Read(m_name + KEY_SOUNDCARD_RXLEVEL, &temp1, double(DEFAULT_SOUNDCARD_RXLEVEL)); + m_soundCardRXLevel = wxFloat32(temp1); + + m_config->Read(m_name + KEY_SOUNDCARD_TXLEVEL, &temp1, double(DEFAULT_SOUNDCARD_TXLEVEL)); + m_soundCardTXLevel = wxFloat32(temp1); + + m_config->Read(m_name + KEY_SOUNDCARD_TXDELAY, &temp, long(DEFAULT_SOUNDCARD_TXDELAY)); + m_soundCardTXDelay = (unsigned int)temp; + + m_config->Read(m_name + KEY_SOUNDCARD_TXTAIL, &temp, long(DEFAULT_SOUNDCARD_TXTAIL)); + m_soundCardTXTail = (unsigned int)temp; + + m_config->Read(m_name + KEY_SPLIT_LOCALADDRESS, &m_splitLocalAddress, DEFAULT_SPLIT_LOCALADDRESS); + + m_config->Read(m_name + KEY_SPLIT_LOCALPORT, &temp, long(DEFAULT_SPLIT_LOCALPORT)); + m_splitLocalPort = (unsigned int)temp; + + for (unsigned int i = 0U; i < SPLIT_TX_COUNT; i++) { + wxString name; + name.Printf(wxT("%s%u"), KEY_SPLIT_TXNAME.c_str(), i); + + wxString value; + m_config->Read(m_name + name, &value, DEFAULT_SPLIT_TXNAME); + m_splitTXNames.Add(value); + } + + for (unsigned int i = 0U; i < SPLIT_RX_COUNT; i++) { + wxString name; + name.Printf(wxT("%s%u"), KEY_SPLIT_RXNAME.c_str(), i); + + wxString value; + m_config->Read(m_name + name, &value, DEFAULT_SPLIT_RXNAME); + m_splitRXNames.Add(value); + } + + m_config->Read(m_name + KEY_SPLIT_TIMEOUT, &temp, long(DEFAULT_SPLIT_TIMEOUT)); + m_splitTimeout = (unsigned int)temp; +} + +CDStarRepeaterConfig::~CDStarRepeaterConfig() +{ + delete m_config; +} + +#else + +CDStarRepeaterConfig::CDStarRepeaterConfig(const wxString& dir, const wxString& configName, const wxString& name, const bool mustExist) : +m_fileName(), +m_callsign(DEFAULT_CALLSIGN), +m_gateway(DEFAULT_GATEWAY), +m_mode(DEFAULT_MODE), +m_ack(DEFAULT_ACK), +m_restriction(DEFAULT_RESTRICTION), +m_rpt1Validation(DEFAULT_RPT1_VALIDATION), +m_dtmfBlanking(DEFAULT_DTMF_BLANKING), +m_errorReply(DEFAULT_ERROR_REPLY), +m_gatewayAddress(DEFAULT_GATEWAY_ADDRESS), +m_gatewayPort(DEFAULT_GATEWAY_PORT), +m_localAddress(DEFAULT_LOCAL_ADDRESS), +m_localPort(DEFAULT_LOCAL_PORT), +m_networkName(DEFAULT_NETWORK_NAME), +m_modemType(DEFAULT_MODEM_TYPE), +m_timeout(DEFAULT_TIMEOUT), +m_ackTime(DEFAULT_ACK_TIME), +m_beaconTime(DEFAULT_BEACON_TIME), +m_beaconText(DEFAULT_BEACON_TEXT), +m_beaconVoice(DEFAULT_BEACON_VOICE), +m_language(DEFAULT_LANGUAGE), +m_announcementEnabled(DEFAULT_ANNOUNCEMENT_ENABLED), +m_announcementTime(DEFAULT_ANNOUNCEMENT_TIME), +m_announcementRecordRPT1(DEFAULT_ANNOUNCEMENT_RECORD_RPT1), +m_announcementRecordRPT2(DEFAULT_ANNOUNCEMENT_RECORD_RPT2), +m_announcementDeleteRPT1(DEFAULT_ANNOUNCEMENT_DELETE_RPT1), +m_announcementDeleteRPT2(DEFAULT_ANNOUNCEMENT_DELETE_RPT2), +m_controlEnabled(DEFAULT_CONTROL_ENABLED), +m_controlRpt1Callsign(DEFAULT_CONTROL_RPT1), +m_controlRpt2Callsign(DEFAULT_CONTROL_RPT2), +m_controlShutdown(DEFAULT_CONTROL_SHUTDOWN), +m_controlStartup(DEFAULT_CONTROL_STARTUP), +m_controlStatus1(DEFAULT_CONTROL_STATUS), +m_controlStatus2(DEFAULT_CONTROL_STATUS), +m_controlStatus3(DEFAULT_CONTROL_STATUS), +m_controlStatus4(DEFAULT_CONTROL_STATUS), +m_controlStatus5(DEFAULT_CONTROL_STATUS), +m_controlCommand1(DEFAULT_CONTROL_COMMAND), +m_controlCommand1Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand2(DEFAULT_CONTROL_COMMAND), +m_controlCommand2Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand3(DEFAULT_CONTROL_COMMAND), +m_controlCommand3Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand4(DEFAULT_CONTROL_COMMAND), +m_controlCommand4Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand5(DEFAULT_CONTROL_COMMAND), +m_controlCommand5Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlCommand6(DEFAULT_CONTROL_COMMAND), +m_controlCommand6Line(DEFAULT_CONTROL_COMMAND_LINE), +m_controlOutput1(DEFAULT_CONTROL_OUTPUT), +m_controlOutput2(DEFAULT_CONTROL_OUTPUT), +m_controlOutput3(DEFAULT_CONTROL_OUTPUT), +m_controlOutput4(DEFAULT_CONTROL_OUTPUT), +m_controllerType(DEFAULT_CONTROLLER_TYPE), +m_serialConfig(DEFAULT_SERIAL_CONFIG), +m_pttInvert(DEFAULT_PTT_INVERT), +m_activeHangTime(DEFAULT_ACTIVE_HANG_TIME), +m_output1(DEFAULT_OUTPUT), +m_output2(DEFAULT_OUTPUT), +m_output3(DEFAULT_OUTPUT), +m_output4(DEFAULT_OUTPUT), +m_logging(DEFAULT_LOGGING), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y), +m_dvapPort(DEFAULT_DVAP_PORT), +m_dvapFrequency(DEFAULT_DVAP_FREQUENCY), +m_dvapPower(DEFAULT_DVAP_POWER), +m_dvapSquelch(DEFAULT_DVAP_SQUELCH), +m_gmskInterface(DEFAULT_GMSK_INTERFACE), +m_gmskAddress(DEFAULT_GMSK_ADDRESS), +m_dvrptr1Port(DEFAULT_DVRPTR1_PORT), +m_dvrptr1RXInvert(DEFAULT_DVRPTR1_RXINVERT), +m_dvrptr1TXInvert(DEFAULT_DVRPTR1_TXINVERT), +m_dvrptr1Channel(DEFAULT_DVRPTR1_CHANNEL), +m_dvrptr1ModLevel(DEFAULT_DVRPTR1_MODLEVEL), +m_dvrptr1TXDelay(DEFAULT_DVRPTR1_TXDELAY), +m_dvrptr2Connection(DEFAULT_DVRPTR2_CONNECTION), +m_dvrptr2USBPort(DEFAULT_DVRPTR2_USBPORT), +m_dvrptr2Address(DEFAULT_DVRPTR2_ADDRESS), +m_dvrptr2Port(DEFAULT_DVRPTR2_PORT), +m_dvrptr2TXInvert(DEFAULT_DVRPTR2_TXINVERT), +m_dvrptr2ModLevel(DEFAULT_DVRPTR2_MODLEVEL), +m_dvrptr2TXDelay(DEFAULT_DVRPTR2_TXDELAY), +m_dvrptr3Connection(DEFAULT_DVRPTR3_CONNECTION), +m_dvrptr3USBPort(DEFAULT_DVRPTR3_USBPORT), +m_dvrptr3Address(DEFAULT_DVRPTR3_ADDRESS), +m_dvrptr3Port(DEFAULT_DVRPTR3_PORT), +m_dvrptr3TXInvert(DEFAULT_DVRPTR3_TXINVERT), +m_dvrptr3ModLevel(DEFAULT_DVRPTR3_MODLEVEL), +m_dvrptr3TXDelay(DEFAULT_DVRPTR3_TXDELAY), +m_dvmegaPort(DEFAULT_DVMEGA_PORT), +m_dvmegaVariant(DEFAULT_DVMEGA_VARIANT), +m_dvmegaRXInvert(DEFAULT_DVMEGA_RXINVERT), +m_dvmegaTXInvert(DEFAULT_DVMEGA_TXINVERT), +m_dvmegaTXDelay(DEFAULT_DVMEGA_TXDELAY), +m_dvmegaRXFrequency(DEFAULT_DVMEGA_RXFREQUENCY), +m_dvmegaTXFrequency(DEFAULT_DVMEGA_TXFREQUENCY), +m_dvmegaPower(DEFAULT_DVMEGA_POWER), +m_mmdvmPort(DEFAULT_MMDVM_PORT), +m_mmdvmRXInvert(DEFAULT_MMDVM_RXINVERT), +m_mmdvmTXInvert(DEFAULT_MMDVM_TXINVERT), +m_mmdvmPTTInvert(DEFAULT_MMDVM_PTTINVERT), +m_mmdvmTXDelay(DEFAULT_MMDVM_TXDELAY), +m_mmdvmRXLevel(DEFAULT_MMDVM_RXLEVEL), +m_mmdvmTXLevel(DEFAULT_MMDVM_TXLEVEL), +m_soundCardRXDevice(DEFAULT_SOUNDCARD_RXDEVICE), +m_soundCardTXDevice(DEFAULT_SOUNDCARD_TXDEVICE), +m_soundCardRXInvert(DEFAULT_SOUNDCARD_RXINVERT), +m_soundCardTXInvert(DEFAULT_SOUNDCARD_TXINVERT), +m_soundCardRXLevel(DEFAULT_SOUNDCARD_RXLEVEL), +m_soundCardTXLevel(DEFAULT_SOUNDCARD_TXLEVEL), +m_soundCardTXDelay(DEFAULT_SOUNDCARD_TXDELAY), +m_soundCardTXTail(DEFAULT_SOUNDCARD_TXTAIL), +m_splitLocalAddress(DEFAULT_SPLIT_LOCALADDRESS), +m_splitLocalPort(DEFAULT_SPLIT_LOCALPORT), +m_splitTXNames(), +m_splitRXNames(), +m_splitTimeout(DEFAULT_SPLIT_TIMEOUT) +{ + wxASSERT(!dir.IsEmpty()); + + wxString fileName = configName; + if (!name.IsEmpty()) + fileName = configName + wxT("_") + name; + + m_fileName.Assign(dir, fileName); + + wxTextFile file(m_fileName.GetFullPath()); + + if (!file.Exists()) + if(mustExist) + throw std::runtime_error("Configuration file does not exist"); + else + return; + + if (!file.Open()) + throw std::runtime_error("Cannot open the configuration file"); + + wxString* splitTXName = new wxString[SPLIT_TX_COUNT]; + wxString* splitRXName = new wxString[SPLIT_RX_COUNT]; + + long temp1; + unsigned long temp2; + double temp3; + + for (wxString str = file.GetFirstLine(); + !file.Eof(); + str = file.GetNextLine()) { + if (str.GetChar(0U) == wxT('#')) { + str = file.GetNextLine(); + continue; + } + + int n = str.Find(wxT('=')); + if (n == wxNOT_FOUND) { + str = file.GetNextLine(); + continue; + } + + wxString key = str.Left(n); + wxString val = str.Mid(n + 1U); + + if (key.IsSameAs(KEY_CALLSIGN)) { + m_callsign = val; + } else if (key.IsSameAs(KEY_GATEWAY)) { + m_gateway = val; + } else if (key.IsSameAs(KEY_MODE)) { + val.ToLong(&temp1); + m_mode = DSTAR_MODE(temp1); + } else if (key.IsSameAs(KEY_ACK)) { + val.ToLong(&temp1); + m_ack = ACK_TYPE(temp1); + } else if (key.IsSameAs(KEY_RESTRICTION)) { + val.ToLong(&temp1); + m_restriction = temp1 == 1L; + } else if (key.IsSameAs(KEY_RPT1_VALIDATION)) { + val.ToLong(&temp1); + m_rpt1Validation = temp1 == 1L; + } else if (key.IsSameAs(KEY_DTMF_BLANKING)) { + val.ToLong(&temp1); + m_dtmfBlanking = temp1 == 1L; + } else if (key.IsSameAs(KEY_ERROR_REPLY)) { + val.ToLong(&temp1); + m_errorReply = temp1 == 1L; + } else if (key.IsSameAs(KEY_GATEWAY_ADDRESS)) { + m_gatewayAddress = val; + } else if (key.IsSameAs(KEY_GATEWAY_PORT)) { + val.ToULong(&temp2); + m_gatewayPort = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_LOCAL_ADDRESS)) { + m_localAddress = val; + } else if (key.IsSameAs(KEY_LOCAL_PORT)) { + val.ToULong(&temp2); + m_localPort = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_NETWORK_NAME)) { + m_networkName = val; + } else if (key.IsSameAs(KEY_MODEM_TYPE)) { + m_modemType = val; + } else if (key.IsSameAs(KEY_TIMEOUT)) { + val.ToULong(&temp2); + m_timeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_ACK_TIME)) { + val.ToULong(&temp2); + m_ackTime = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_BEACON_TIME)) { + val.ToULong(&temp2); + m_beaconTime = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_BEACON_TEXT)) { + m_beaconText = val; + } else if (key.IsSameAs(KEY_BEACON_VOICE)) { + val.ToLong(&temp1); + m_beaconVoice = temp1 == 1L; + } else if (key.IsSameAs(KEY_LANGUAGE)) { + val.ToLong(&temp1); + m_language = TEXT_LANG(temp1); + } else if (key.IsSameAs(KEY_ANNOUNCEMENT_ENABLED)) { + val.ToLong(&temp1); + m_announcementEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_ANNOUNCEMENT_TIME)) { + val.ToULong(&temp2); + m_announcementTime = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_ANNOUNCEMENT_RECORD_RPT1)) { + m_announcementRecordRPT1 = val; + } else if (key.IsSameAs(KEY_ANNOUNCEMENT_RECORD_RPT2)) { + m_announcementRecordRPT2 = val; + } else if (key.IsSameAs(KEY_ANNOUNCEMENT_DELETE_RPT1)) { + m_announcementDeleteRPT1 = val; + } else if (key.IsSameAs(KEY_ANNOUNCEMENT_DELETE_RPT2)) { + m_announcementDeleteRPT2 = val; + } else if (key.IsSameAs(KEY_CONTROL_ENABLED)) { + val.ToLong(&temp1); + m_controlEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_CONTROL_RPT1)) { + m_controlRpt1Callsign = val; + } else if (key.IsSameAs(KEY_CONTROL_RPT2)) { + m_controlRpt2Callsign = val; + } else if (key.IsSameAs(KEY_CONTROL_SHUTDOWN)) { + m_controlShutdown = val; + } else if (key.IsSameAs(KEY_CONTROL_STARTUP)) { + m_controlStartup = val; + } else if (key.IsSameAs(KEY_CONTROL_STATUS1)) { + m_controlStatus1 = val; + } else if (key.IsSameAs(KEY_CONTROL_STATUS2)) { + m_controlStatus2 = val; + } else if (key.IsSameAs(KEY_CONTROL_STATUS3)) { + m_controlStatus3 = val; + } else if (key.IsSameAs(KEY_CONTROL_STATUS4)) { + m_controlStatus4 = val; + } else if (key.IsSameAs(KEY_CONTROL_STATUS5)) { + m_controlStatus5 = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND1)) { + m_controlCommand1 = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND1_LINE)) { + m_controlCommand1Line = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND2)) { + m_controlCommand2 = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND2_LINE)) { + m_controlCommand2Line = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND3)) { + m_controlCommand3 = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND3_LINE)) { + m_controlCommand3Line = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND4)) { + m_controlCommand4 = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND4_LINE)) { + m_controlCommand4Line = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND5)) { + m_controlCommand5 = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND5_LINE)) { + m_controlCommand5Line = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND6)) { + m_controlCommand6 = val; + } else if (key.IsSameAs(KEY_CONTROL_COMMAND6_LINE)) { + m_controlCommand6Line = val; + } else if (key.IsSameAs(KEY_CONTROL_OUTPUT1)) { + m_controlOutput1 = val; + } else if (key.IsSameAs(KEY_CONTROL_OUTPUT2)) { + m_controlOutput2 = val; + } else if (key.IsSameAs(KEY_CONTROL_OUTPUT3)) { + m_controlOutput3 = val; + } else if (key.IsSameAs(KEY_CONTROL_OUTPUT4)) { + m_controlOutput4 = val; + } else if (key.IsSameAs(KEY_CONTROLLER_TYPE)) { + m_controllerType = val; + } else if (key.IsSameAs(KEY_SERIAL_CONFIG)) { + val.ToULong(&temp2); + m_serialConfig = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_PTT_INVERT)) { + val.ToLong(&temp1); + m_pttInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_ACTIVE_HANG_TIME)) { + val.ToULong(&temp2); + m_activeHangTime = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_OUTPUT1)) { + val.ToLong(&temp1); + m_output1 = temp1 == 1L; + } else if (key.IsSameAs(KEY_OUTPUT2)) { + val.ToLong(&temp1); + m_output2 = temp1 == 1L; + } else if (key.IsSameAs(KEY_OUTPUT3)) { + val.ToLong(&temp1); + m_output3 = temp1 == 1L; + } else if (key.IsSameAs(KEY_OUTPUT4)) { + val.ToLong(&temp1); + m_output4 = temp1 == 1L; + } else if (key.IsSameAs(KEY_LOGGING)) { + val.ToLong(&temp1); + m_logging = temp1 == 1L; + } else if (key.IsSameAs(KEY_WINDOW_X)) { + val.ToLong(&temp1); + m_x = int(temp1); + } else if (key.IsSameAs(KEY_WINDOW_Y)) { + val.ToLong(&temp1); + m_y = int(temp1); + } else if (key.IsSameAs(KEY_DVAP_PORT)) { + m_dvapPort = val; + } else if (key.IsSameAs(KEY_DVAP_FREQUENCY)) { + val.ToULong(&temp2); + m_dvapFrequency = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVAP_POWER)) { + val.ToLong(&temp1); + m_dvapPower = int(temp1); + } else if (key.IsSameAs(KEY_DVAP_SQUELCH)) { + val.ToLong(&temp1); + m_dvapSquelch = int(temp1); + } else if (key.IsSameAs(KEY_GMSK_ADDRESS)) { + val.ToULong(&temp2); + m_gmskAddress = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVRPTR1_PORT)) { + m_dvrptr1Port = val; + } else if (key.IsSameAs(KEY_DVRPTR1_RXINVERT)) { + val.ToLong(&temp1); + m_dvrptr1RXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_DVRPTR1_TXINVERT)) { + val.ToLong(&temp1); + m_dvrptr1TXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_DVRPTR1_CHANNEL)) { + val.ToLong(&temp1); + m_dvrptr1Channel = temp1 == 1L; + } else if (key.IsSameAs(KEY_DVRPTR1_MODLEVEL)) { + val.ToULong(&temp2); + m_dvrptr1ModLevel = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVRPTR1_TXDELAY)) { + val.ToULong(&temp2); + m_dvrptr1TXDelay = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVRPTR2_CONNECTION)) { + val.ToLong(&temp1); + m_dvrptr2Connection = CONNECTION_TYPE(temp1); + } else if (key.IsSameAs(KEY_DVRPTR2_USBPORT)) { + m_dvrptr2USBPort = val; + } else if (key.IsSameAs(KEY_DVRPTR2_ADDRESS)) { + m_dvrptr2Address = val; + } else if (key.IsSameAs(KEY_DVRPTR2_PORT)) { + val.ToULong(&temp2); + m_dvrptr2Port = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVRPTR2_TXINVERT)) { + val.ToLong(&temp1); + m_dvrptr2TXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_DVRPTR2_MODLEVEL)) { + val.ToULong(&temp2); + m_dvrptr2ModLevel = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVRPTR2_TXDELAY)) { + val.ToULong(&temp2); + m_dvrptr2TXDelay = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVRPTR3_CONNECTION)) { + val.ToLong(&temp1); + m_dvrptr3Connection = CONNECTION_TYPE(temp1); + } else if (key.IsSameAs(KEY_DVRPTR3_USBPORT)) { + m_dvrptr3USBPort = val; + } else if (key.IsSameAs(KEY_DVRPTR3_ADDRESS)) { + m_dvrptr3Address = val; + } else if (key.IsSameAs(KEY_DVRPTR3_PORT)) { + val.ToULong(&temp2); + m_dvrptr3Port = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVRPTR3_TXINVERT)) { + val.ToLong(&temp1); + m_dvrptr3TXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_DVRPTR3_MODLEVEL)) { + val.ToULong(&temp2); + m_dvrptr3ModLevel = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVRPTR3_TXDELAY)) { + val.ToULong(&temp2); + m_dvrptr3TXDelay = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVMEGA_PORT)) { + m_dvmegaPort = val; + } else if (key.IsSameAs(KEY_DVMEGA_VARIANT)) { + val.ToLong(&temp1); + m_dvmegaVariant = DVMEGA_VARIANT(temp1); + } else if (key.IsSameAs(KEY_DVMEGA_RXINVERT)) { + val.ToLong(&temp1); + m_dvmegaRXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_DVMEGA_TXINVERT)) { + val.ToLong(&temp1); + m_dvmegaTXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_DVMEGA_TXDELAY)) { + val.ToULong(&temp2); + m_dvmegaTXDelay = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVMEGA_RXFREQUENCY)) { + val.ToULong(&temp2); + m_dvmegaRXFrequency = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVMEGA_TXFREQUENCY)) { + val.ToULong(&temp2); + m_dvmegaTXFrequency = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DVMEGA_POWER)) { + val.ToULong(&temp2); + m_dvmegaPower = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_MMDVM_PORT)) { + m_mmdvmPort = val; + } else if (key.IsSameAs(KEY_MMDVM_RXINVERT)) { + val.ToLong(&temp1); + m_mmdvmRXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_MMDVM_TXINVERT)) { + val.ToLong(&temp1); + m_mmdvmTXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_MMDVM_PTTINVERT)) { + val.ToLong(&temp1); + m_mmdvmPTTInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_MMDVM_TXDELAY)) { + val.ToULong(&temp2); + m_mmdvmTXDelay = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_MMDVM_RXLEVEL)) { + val.ToULong(&temp2); + m_mmdvmRXLevel = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_MMDVM_TXLEVEL)) { + val.ToULong(&temp2); + m_mmdvmTXLevel = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_SOUNDCARD_RXDEVICE)) { + m_soundCardRXDevice = val; + } else if (key.IsSameAs(KEY_SOUNDCARD_TXDEVICE)) { + m_soundCardTXDevice = val; + } else if (key.IsSameAs(KEY_SOUNDCARD_RXINVERT)) { + val.ToLong(&temp1); + m_soundCardRXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_SOUNDCARD_TXINVERT)) { + val.ToLong(&temp1); + m_soundCardTXInvert = temp1 == 1L; + } else if (key.IsSameAs(KEY_SOUNDCARD_RXLEVEL)) { + val.ToDouble(&temp3); + m_soundCardRXLevel = wxFloat32(temp3); + } else if (key.IsSameAs(KEY_SOUNDCARD_TXLEVEL)) { + val.ToDouble(&temp3); + m_soundCardTXLevel = wxFloat32(temp3); + } else if (key.IsSameAs(KEY_SOUNDCARD_TXDELAY)) { + val.ToULong(&temp2); + m_soundCardTXDelay = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_SOUNDCARD_TXTAIL)) { + val.ToULong(&temp2); + m_soundCardTXTail = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_SPLIT_LOCALADDRESS)) { + m_splitLocalAddress = val; + } else if (key.IsSameAs(KEY_SPLIT_LOCALPORT)) { + val.ToULong(&temp2); + m_splitLocalPort = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_SPLIT_TIMEOUT)) { + val.ToULong(&temp2); + m_splitTimeout = (unsigned int)temp2; + } else { + for (unsigned int i = 0U; i < SPLIT_TX_COUNT; i++) { + wxString name; + name.Printf(wxT("%s%u"), KEY_SPLIT_TXNAME.c_str(), i); + if (key.IsSameAs(name)) + splitTXName[i] = val; + } + + for (unsigned int i = 0U; i < SPLIT_RX_COUNT; i++) { + wxString name; + name.Printf(wxT("%s%u"), KEY_SPLIT_RXNAME.c_str(), i); + if (key.IsSameAs(name)) + splitRXName[i] = val; + } + } + } + + file.Close(); + + // Move the array of wxStrings into the wxArrayString + for (unsigned int i = 0U; i < SPLIT_TX_COUNT; i++) + m_splitTXNames.Add(splitTXName[i]); + for (unsigned int i = 0U; i < SPLIT_RX_COUNT; i++) + m_splitRXNames.Add(splitRXName[i]); + + delete[] splitTXName; + delete[] splitRXName; +} + +CDStarRepeaterConfig::~CDStarRepeaterConfig() +{ +} + +#endif + +void CDStarRepeaterConfig::getCallsign(wxString& callsign, wxString& gateway, DSTAR_MODE& mode, ACK_TYPE& ack, bool& restriction, bool& rpt1Validation, bool& dtmfBlanking, bool& errorReply) const +{ + callsign = m_callsign; + gateway = m_gateway; + mode = m_mode; + ack = m_ack; + restriction = m_restriction; + rpt1Validation = m_rpt1Validation; + dtmfBlanking = m_dtmfBlanking; + errorReply = m_errorReply; +} + +void CDStarRepeaterConfig::setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) +{ + m_callsign = callsign; + m_gateway = gateway; + m_mode = mode; + m_ack = ack; + m_restriction = restriction; + m_rpt1Validation = rpt1Validation; + m_dtmfBlanking = dtmfBlanking; + m_errorReply = errorReply; +} + +void CDStarRepeaterConfig::getNetwork(wxString& gatewayAddress, unsigned int& gatewayPort, wxString& localAddress, unsigned int& localPort, wxString& name) const +{ + gatewayAddress = m_gatewayAddress; + gatewayPort = m_gatewayPort; + localAddress = m_localAddress; + localPort = m_localPort; + name = m_networkName; +} + +void CDStarRepeaterConfig::setNetwork(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name) +{ + m_gatewayAddress = gatewayAddress; + m_gatewayPort = gatewayPort; + m_localAddress = localAddress; + m_localPort = localPort; + m_networkName = name; +} + +void CDStarRepeaterConfig::getModem(wxString& type) const +{ + type = m_modemType; +} + +void CDStarRepeaterConfig::setModem(const wxString& type) +{ + m_modemType = type; +} + +void CDStarRepeaterConfig::getTimes(unsigned int& timeout, unsigned int& ackTime) const +{ + timeout = m_timeout; + ackTime = m_ackTime; +} + +void CDStarRepeaterConfig::setTimes(unsigned int timeout, unsigned int ackTime) +{ + m_timeout = timeout; + m_ackTime = ackTime; +} + +void CDStarRepeaterConfig::getBeacon(unsigned int& time, wxString& text, bool& voice, TEXT_LANG& language) const +{ + time = m_beaconTime; + text = m_beaconText; + voice = m_beaconVoice; + language = m_language; +} + +void CDStarRepeaterConfig::setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language) +{ + m_beaconTime = time; + m_beaconText = text; + m_beaconVoice = voice; + m_language = language; +} + +void CDStarRepeaterConfig::getAnnouncement(bool& enabled, unsigned int& time, wxString& recordRPT1, wxString& recordRPT2, wxString& deleteRPT1, wxString& deleteRPT2) const +{ + enabled = m_announcementEnabled; + time = m_announcementTime; + recordRPT1 = m_announcementRecordRPT1; + recordRPT2 = m_announcementRecordRPT2; + deleteRPT1 = m_announcementDeleteRPT1; + deleteRPT2 = m_announcementDeleteRPT2; +} + +void CDStarRepeaterConfig::setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2) +{ + m_announcementEnabled = enabled; + m_announcementTime = time; + m_announcementRecordRPT1 = recordRPT1; + m_announcementRecordRPT2 = recordRPT2; + m_announcementDeleteRPT1 = deleteRPT1; + m_announcementDeleteRPT2 = deleteRPT2; +} + +void CDStarRepeaterConfig::getControl(bool& enabled, wxString& rpt1Callsign, wxString& rpt2Callsign, wxString& shutdown, wxString& startup, wxString& status1, wxString& status2, wxString& status3, wxString& status4, wxString& status5, wxString& command1, wxString& command1Line, wxString& command2, wxString& command2Line, wxString& command3, wxString& command3Line, wxString& command4, wxString& command4Line, wxString& command5, wxString& command5Line, wxString& command6, wxString& command6Line, wxString& output1, wxString& output2, wxString& output3, wxString& output4) const +{ + enabled = m_controlEnabled; + rpt1Callsign = m_controlRpt1Callsign; + rpt2Callsign = m_controlRpt2Callsign; + shutdown = m_controlShutdown; + startup = m_controlStartup; + + status1 = m_controlStatus1; + status2 = m_controlStatus2; + status3 = m_controlStatus3; + status4 = m_controlStatus4; + status5 = m_controlStatus5; + + command1 = m_controlCommand1; + command1Line = m_controlCommand1Line; + command2 = m_controlCommand2; + command2Line = m_controlCommand2Line; + command3 = m_controlCommand3; + command3Line = m_controlCommand3Line; + command4 = m_controlCommand4; + command4Line = m_controlCommand4Line; + command5 = m_controlCommand5; + command5Line = m_controlCommand5Line; + command6 = m_controlCommand6; + command6Line = m_controlCommand6Line; + + output1 = m_controlOutput1; + output2 = m_controlOutput2; + output3 = m_controlOutput3; + output4 = m_controlOutput4; +} + +void CDStarRepeaterConfig::setControl(bool enabled, const wxString& rpt1Callsign, const wxString& rpt2Callsign, const wxString& shutdown, const wxString& startup, const wxString& status1, const wxString& status2, const wxString& status3, const wxString& status4, const wxString& status5, const wxString& command1, const wxString& command1Line, const wxString& command2, const wxString& command2Line, const wxString& command3, const wxString& command3Line, const wxString& command4, const wxString& command4Line, const wxString& command5, const wxString& command5Line, const wxString& command6, const wxString& command6Line, const wxString& output1, const wxString& output2, const wxString& output3, const wxString& output4) +{ + m_controlEnabled = enabled; + m_controlRpt1Callsign = rpt1Callsign; + m_controlRpt2Callsign = rpt2Callsign; + m_controlShutdown = shutdown; + m_controlStartup = startup; + m_controlStatus1 = status1; + m_controlStatus2 = status2; + m_controlStatus3 = status3; + m_controlStatus4 = status4; + m_controlStatus5 = status5; + m_controlCommand1 = command1; + m_controlCommand1Line = command1Line; + m_controlCommand2 = command2; + m_controlCommand2Line = command2Line; + m_controlCommand3 = command3; + m_controlCommand3Line = command3Line; + m_controlCommand4 = command4; + m_controlCommand4Line = command4Line; + m_controlCommand5 = command5; + m_controlCommand5Line = command5Line; + m_controlCommand6 = command6; + m_controlCommand6Line = command6Line; + m_controlOutput1 = output1; + m_controlOutput2 = output2; + m_controlOutput3 = output3; + m_controlOutput4 = output4; +} + +void CDStarRepeaterConfig::getController(wxString& type, unsigned int& serialConfig, bool& pttInvert, unsigned int& activeHangTime) const +{ + type = m_controllerType; + serialConfig = m_serialConfig; + pttInvert = m_pttInvert; + activeHangTime = m_activeHangTime; +} + +void CDStarRepeaterConfig::setController(const wxString& type, unsigned int serialConfig, bool pttInvert, unsigned int activeHangTime) +{ + m_controllerType = type; + m_serialConfig = serialConfig; + m_pttInvert = pttInvert; + m_activeHangTime = activeHangTime; +} + +void CDStarRepeaterConfig::getOutputs(bool& out1, bool& out2, bool& out3, bool& out4) const +{ + out1 = m_output1; + out2 = m_output2; + out3 = m_output3; + out4 = m_output4; +} + +void CDStarRepeaterConfig::setOutputs(bool out1, bool out2, bool out3, bool out4) +{ + m_output1 = out1; + m_output2 = out2; + m_output3 = out3; + m_output4 = out4; +} + +void CDStarRepeaterConfig::getLogging(bool& logging) const +{ + logging = m_logging; +} + +void CDStarRepeaterConfig::setLogging(bool logging) +{ + m_logging = logging; +} + +void CDStarRepeaterConfig::getPosition(int& x, int& y) const +{ + x = m_x; + y = m_y; +} + +void CDStarRepeaterConfig::setPosition(int x, int y) +{ + m_x = x; + m_y = y; +} + +void CDStarRepeaterConfig::getDVAP(wxString& port, unsigned int& frequency, int& power, int& squelch) const +{ + port = m_dvapPort; + frequency = m_dvapFrequency; + power = m_dvapPower; + squelch = m_dvapSquelch; +} + +void CDStarRepeaterConfig::setDVAP(const wxString& port, unsigned int frequency, int power, int squelch) +{ + m_dvapPort = port; + m_dvapFrequency = frequency; + m_dvapPower = power; + m_dvapSquelch = squelch; +} + +void CDStarRepeaterConfig::getGMSK(USB_INTERFACE& type, unsigned int& address) const +{ + type = m_gmskInterface; + address = m_gmskAddress; +} + +void CDStarRepeaterConfig::setGMSK(USB_INTERFACE type, unsigned int address) +{ + m_gmskInterface = type; + m_gmskAddress = address; +} + +void CDStarRepeaterConfig::getDVRPTR1(wxString& port, bool& rxInvert, bool& txInvert, bool& channel, unsigned int& modLevel, unsigned int& txDelay) const +{ + port = m_dvrptr1Port; + rxInvert = m_dvrptr1RXInvert; + txInvert = m_dvrptr1TXInvert; + channel = m_dvrptr1Channel; + modLevel = m_dvrptr1ModLevel; + txDelay = m_dvrptr1TXDelay; +} + +void CDStarRepeaterConfig::setDVRPTR1(const wxString& port, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay) +{ + m_dvrptr1Port = port; + m_dvrptr1RXInvert = rxInvert; + m_dvrptr1TXInvert = txInvert; + m_dvrptr1Channel = channel; + m_dvrptr1ModLevel = modLevel; + m_dvrptr1TXDelay = txDelay; +} + +void CDStarRepeaterConfig::getDVRPTR2(CONNECTION_TYPE& connection, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const +{ + connection = m_dvrptr2Connection; + usbPort = m_dvrptr2USBPort; + address = m_dvrptr2Address; + port = m_dvrptr2Port; + txInvert = m_dvrptr2TXInvert; + modLevel = m_dvrptr2ModLevel; + txDelay = m_dvrptr2TXDelay; +} + +void CDStarRepeaterConfig::setDVRPTR2(CONNECTION_TYPE connection, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay) +{ + m_dvrptr2Connection = connection; + m_dvrptr2USBPort = usbPort; + m_dvrptr2Address = address; + m_dvrptr2Port = port; + m_dvrptr2TXInvert = txInvert; + m_dvrptr2ModLevel = modLevel; + m_dvrptr2TXDelay = txDelay; +} + +void CDStarRepeaterConfig::getDVRPTR3(CONNECTION_TYPE& connection, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const +{ + connection = m_dvrptr3Connection; + usbPort = m_dvrptr3USBPort; + address = m_dvrptr3Address; + port = m_dvrptr3Port; + txInvert = m_dvrptr3TXInvert; + modLevel = m_dvrptr3ModLevel; + txDelay = m_dvrptr3TXDelay; +} + +void CDStarRepeaterConfig::setDVRPTR3(CONNECTION_TYPE connection, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay) +{ + m_dvrptr3Connection = connection; + m_dvrptr3USBPort = usbPort; + m_dvrptr3Address = address; + m_dvrptr3Port = port; + m_dvrptr3TXInvert = txInvert; + m_dvrptr3ModLevel = modLevel; + m_dvrptr3TXDelay = txDelay; +} + +void CDStarRepeaterConfig::getDVMEGA(wxString& port, DVMEGA_VARIANT& variant, bool& rxInvert, bool& txInvert, unsigned int& txDelay, unsigned int& rxFrequency, unsigned int& txFrequency, unsigned int& power) const +{ + port = m_dvmegaPort; + variant = m_dvmegaVariant; + rxInvert = m_dvmegaRXInvert; + txInvert = m_dvmegaTXInvert; + txDelay = m_dvmegaTXDelay; + rxFrequency = m_dvmegaRXFrequency; + txFrequency = m_dvmegaTXFrequency; + power = m_dvmegaPower; +} + +void CDStarRepeaterConfig::setDVMEGA(const wxString& port, DVMEGA_VARIANT variant, bool rxInvert, bool txInvert, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power) +{ + m_dvmegaPort = port; + m_dvmegaVariant = variant; + m_dvmegaRXInvert = rxInvert; + m_dvmegaTXInvert = txInvert; + m_dvmegaTXDelay = txDelay; + m_dvmegaRXFrequency = rxFrequency; + m_dvmegaTXFrequency = txFrequency; + m_dvmegaPower = power; +} + +void CDStarRepeaterConfig::getMMDVM(wxString& port, bool& rxInvert, bool& txInvert, bool& pttInvert, unsigned int& txDelay, unsigned int& rxLevel, unsigned int& txLevel) const +{ + port = m_mmdvmPort; + rxInvert = m_mmdvmRXInvert; + txInvert = m_mmdvmTXInvert; + pttInvert = m_mmdvmPTTInvert; + txDelay = m_mmdvmTXDelay; + rxLevel = m_mmdvmRXLevel; + txLevel = m_mmdvmTXLevel; +} + +void CDStarRepeaterConfig::setMMDVM(const wxString& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel) +{ + m_mmdvmPort = port; + m_mmdvmRXInvert = rxInvert; + m_mmdvmTXInvert = txInvert; + m_mmdvmPTTInvert = pttInvert; + m_mmdvmTXDelay = txDelay; + m_mmdvmRXLevel = rxLevel; + m_mmdvmTXLevel = txLevel; +} + +void CDStarRepeaterConfig::getSoundCard(wxString& rxDevice, wxString& txDevice, bool& rxInvert, bool& txInvert, wxFloat32& rxLevel, wxFloat32& txLevel, unsigned int& txDelay, unsigned int& txTail) const +{ + rxDevice = m_soundCardRXDevice; + txDevice = m_soundCardTXDevice; + rxInvert = m_soundCardRXInvert; + txInvert = m_soundCardTXInvert; + rxLevel = m_soundCardRXLevel; + txLevel = m_soundCardTXLevel; + txDelay = m_soundCardTXDelay; + txTail = m_soundCardTXTail; +} + +void CDStarRepeaterConfig::setSoundCard(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail) +{ + m_soundCardRXDevice = rxDevice; + m_soundCardTXDevice = txDevice; + m_soundCardRXInvert = rxInvert; + m_soundCardTXInvert = txInvert; + m_soundCardRXLevel = rxLevel; + m_soundCardTXLevel = txLevel; + m_soundCardTXDelay = txDelay; + m_soundCardTXTail = txTail; +} + +void CDStarRepeaterConfig::getSplit(wxString& localAddress, unsigned int& localPort, wxArrayString& transmitterNames, wxArrayString& receiverNames, unsigned int& timeout) const +{ + localAddress = m_splitLocalAddress; + localPort = m_splitLocalPort; + transmitterNames = m_splitTXNames; + receiverNames = m_splitRXNames; + timeout = m_splitTimeout; +} + +void CDStarRepeaterConfig::setSplit(const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout) +{ + m_splitLocalAddress = localAddress; + m_splitLocalPort = localPort; + m_splitTXNames = transmitterNames; + m_splitRXNames = receiverNames; + m_splitTimeout = timeout; +} + +bool CDStarRepeaterConfig::write() +{ +#if defined(__WINDOWS__) + m_config->Write(m_name + KEY_CALLSIGN, m_callsign); + m_config->Write(m_name + KEY_GATEWAY, m_gateway); + m_config->Write(m_name + KEY_MODE, long(m_mode)); + m_config->Write(m_name + KEY_ACK, long(m_ack)); + m_config->Write(m_name + KEY_RESTRICTION, m_restriction); + m_config->Write(m_name + KEY_RPT1_VALIDATION, m_rpt1Validation); + m_config->Write(m_name + KEY_DTMF_BLANKING, m_dtmfBlanking); + m_config->Write(m_name + KEY_ERROR_REPLY, m_errorReply); + m_config->Write(m_name + KEY_GATEWAY_ADDRESS, m_gatewayAddress); + m_config->Write(m_name + KEY_GATEWAY_PORT, long(m_gatewayPort)); + m_config->Write(m_name + KEY_LOCAL_ADDRESS, m_localAddress); + m_config->Write(m_name + KEY_LOCAL_PORT, long(m_localPort)); + m_config->Write(m_name + KEY_NETWORK_NAME, m_networkName); + m_config->Write(m_name + KEY_MODEM_TYPE, m_modemType); + m_config->Write(m_name + KEY_TIMEOUT, long(m_timeout)); + m_config->Write(m_name + KEY_ACK_TIME, long(m_ackTime)); + m_config->Write(m_name + KEY_BEACON_TIME, long(m_beaconTime)); + m_config->Write(m_name + KEY_BEACON_TEXT, m_beaconText); + m_config->Write(m_name + KEY_BEACON_VOICE, m_beaconVoice); + m_config->Write(m_name + KEY_LANGUAGE, long(m_language)); + m_config->Write(m_name + KEY_ANNOUNCEMENT_ENABLED, m_announcementEnabled); + m_config->Write(m_name + KEY_ANNOUNCEMENT_TIME, long(m_announcementTime)); + m_config->Write(m_name + KEY_ANNOUNCEMENT_RECORD_RPT1, m_announcementRecordRPT1); + m_config->Write(m_name + KEY_ANNOUNCEMENT_RECORD_RPT2, m_announcementRecordRPT2); + m_config->Write(m_name + KEY_ANNOUNCEMENT_DELETE_RPT1, m_announcementDeleteRPT1); + m_config->Write(m_name + KEY_ANNOUNCEMENT_DELETE_RPT2, m_announcementDeleteRPT2); + m_config->Write(m_name + KEY_CONTROL_ENABLED, m_controlEnabled); + m_config->Write(m_name + KEY_CONTROL_RPT1, m_controlRpt1Callsign); + m_config->Write(m_name + KEY_CONTROL_RPT2, m_controlRpt2Callsign); + m_config->Write(m_name + KEY_CONTROL_SHUTDOWN, m_controlShutdown); + m_config->Write(m_name + KEY_CONTROL_STARTUP, m_controlStartup); + m_config->Write(m_name + KEY_CONTROL_STATUS1, m_controlStatus1); + m_config->Write(m_name + KEY_CONTROL_STATUS2, m_controlStatus2); + m_config->Write(m_name + KEY_CONTROL_STATUS3, m_controlStatus3); + m_config->Write(m_name + KEY_CONTROL_STATUS4, m_controlStatus4); + m_config->Write(m_name + KEY_CONTROL_STATUS5, m_controlStatus5); + m_config->Write(m_name + KEY_CONTROL_COMMAND1, m_controlCommand1); + m_config->Write(m_name + KEY_CONTROL_COMMAND1_LINE, m_controlCommand1Line); + m_config->Write(m_name + KEY_CONTROL_COMMAND2, m_controlCommand2); + m_config->Write(m_name + KEY_CONTROL_COMMAND2_LINE, m_controlCommand2Line); + m_config->Write(m_name + KEY_CONTROL_COMMAND3, m_controlCommand3); + m_config->Write(m_name + KEY_CONTROL_COMMAND3_LINE, m_controlCommand3Line); + m_config->Write(m_name + KEY_CONTROL_COMMAND4, m_controlCommand4); + m_config->Write(m_name + KEY_CONTROL_COMMAND4_LINE, m_controlCommand4Line); + m_config->Write(m_name + KEY_CONTROL_COMMAND5, m_controlCommand5); + m_config->Write(m_name + KEY_CONTROL_COMMAND5_LINE, m_controlCommand5Line); + m_config->Write(m_name + KEY_CONTROL_COMMAND6, m_controlCommand6); + m_config->Write(m_name + KEY_CONTROL_COMMAND6_LINE, m_controlCommand6Line); + m_config->Write(m_name + KEY_CONTROL_OUTPUT1, m_controlOutput1); + m_config->Write(m_name + KEY_CONTROL_OUTPUT2, m_controlOutput2); + m_config->Write(m_name + KEY_CONTROL_OUTPUT3, m_controlOutput3); + m_config->Write(m_name + KEY_CONTROL_OUTPUT4, m_controlOutput4); + m_config->Write(m_name + KEY_CONTROLLER_TYPE, m_controllerType); + m_config->Write(m_name + KEY_SERIAL_CONFIG, long(m_serialConfig)); + m_config->Write(m_name + KEY_PTT_INVERT, m_pttInvert); + m_config->Write(m_name + KEY_ACTIVE_HANG_TIME, long(m_activeHangTime)); + m_config->Write(m_name + KEY_OUTPUT1, m_output1); + m_config->Write(m_name + KEY_OUTPUT2, m_output2); + m_config->Write(m_name + KEY_OUTPUT3, m_output3); + m_config->Write(m_name + KEY_OUTPUT4, m_output4); + m_config->Write(m_name + KEY_LOGGING, m_logging); + m_config->Write(m_name + KEY_WINDOW_X, long(m_x)); + m_config->Write(m_name + KEY_WINDOW_Y, long(m_y)); + + m_config->Write(m_name + KEY_DVAP_PORT, m_dvapPort); + m_config->Write(m_name + KEY_DVAP_FREQUENCY, long(m_dvapFrequency)); + m_config->Write(m_name + KEY_DVAP_POWER, long(m_dvapPower)); + m_config->Write(m_name + KEY_DVAP_SQUELCH, long(m_dvapSquelch)); + + m_config->Write(m_name + KEY_GMSK_INTERFACE, long(m_gmskInterface)); + m_config->Write(m_name + KEY_GMSK_ADDRESS, long(m_gmskAddress)); + + m_config->Write(m_name + KEY_DVRPTR1_PORT, m_dvrptr1Port); + m_config->Write(m_name + KEY_DVRPTR1_RXINVERT, m_dvrptr1RXInvert); + m_config->Write(m_name + KEY_DVRPTR1_TXINVERT, m_dvrptr1TXInvert); + m_config->Write(m_name + KEY_DVRPTR1_CHANNEL, m_dvrptr1Channel); + m_config->Write(m_name + KEY_DVRPTR1_MODLEVEL, long(m_dvrptr1ModLevel)); + m_config->Write(m_name + KEY_DVRPTR1_TXDELAY, long(m_dvrptr1TXDelay)); + + m_config->Write(m_name + KEY_DVRPTR2_CONNECTION, long(m_dvrptr2Connection)); + m_config->Write(m_name + KEY_DVRPTR2_USBPORT, m_dvrptr2USBPort); + m_config->Write(m_name + KEY_DVRPTR2_ADDRESS, m_dvrptr2Address); + m_config->Write(m_name + KEY_DVRPTR2_PORT, long(m_dvrptr2Port)); + m_config->Write(m_name + KEY_DVRPTR2_TXINVERT, m_dvrptr2TXInvert); + m_config->Write(m_name + KEY_DVRPTR2_MODLEVEL, long(m_dvrptr2ModLevel)); + m_config->Write(m_name + KEY_DVRPTR2_TXDELAY, long(m_dvrptr2TXDelay)); + + m_config->Write(m_name + KEY_DVRPTR3_CONNECTION, long(m_dvrptr3Connection)); + m_config->Write(m_name + KEY_DVRPTR3_USBPORT, m_dvrptr3USBPort); + m_config->Write(m_name + KEY_DVRPTR3_ADDRESS, m_dvrptr3Address); + m_config->Write(m_name + KEY_DVRPTR3_PORT, long(m_dvrptr3Port)); + m_config->Write(m_name + KEY_DVRPTR3_TXINVERT, m_dvrptr3TXInvert); + m_config->Write(m_name + KEY_DVRPTR3_MODLEVEL, long(m_dvrptr3ModLevel)); + m_config->Write(m_name + KEY_DVRPTR3_TXDELAY, long(m_dvrptr3TXDelay)); + + m_config->Write(m_name + KEY_DVMEGA_PORT, m_dvmegaPort); + m_config->Write(m_name + KEY_DVMEGA_VARIANT, long(m_dvmegaVariant)); + m_config->Write(m_name + KEY_DVMEGA_RXINVERT, m_dvmegaRXInvert); + m_config->Write(m_name + KEY_DVMEGA_TXINVERT, m_dvmegaTXInvert); + m_config->Write(m_name + KEY_DVMEGA_TXDELAY, long(m_dvmegaTXDelay)); + m_config->Write(m_name + KEY_DVMEGA_RXFREQUENCY, long(m_dvmegaRXFrequency)); + m_config->Write(m_name + KEY_DVMEGA_TXFREQUENCY, long(m_dvmegaTXFrequency)); + m_config->Write(m_name + KEY_DVMEGA_POWER, long(m_dvmegaPower)); + + m_config->Write(m_name + KEY_MMDVM_PORT, m_mmdvmPort); + m_config->Write(m_name + KEY_MMDVM_RXINVERT, m_mmdvmRXInvert); + m_config->Write(m_name + KEY_MMDVM_TXINVERT, m_mmdvmTXInvert); + m_config->Write(m_name + KEY_MMDVM_PTTINVERT, m_mmdvmPTTInvert); + m_config->Write(m_name + KEY_MMDVM_TXDELAY, long(m_mmdvmTXDelay)); + m_config->Write(m_name + KEY_MMDVM_RXLEVEL, long(m_mmdvmRXLevel)); + m_config->Write(m_name + KEY_MMDVM_TXLEVEL, long(m_mmdvmTXLevel)); + + m_config->Write(m_name + KEY_SOUNDCARD_RXDEVICE, m_soundCardRXDevice); + m_config->Write(m_name + KEY_SOUNDCARD_TXDEVICE, m_soundCardTXDevice); + m_config->Write(m_name + KEY_SOUNDCARD_RXINVERT, m_soundCardRXInvert); + m_config->Write(m_name + KEY_SOUNDCARD_TXINVERT, m_soundCardTXInvert); + m_config->Write(m_name + KEY_SOUNDCARD_RXLEVEL, double(m_soundCardRXLevel)); + m_config->Write(m_name + KEY_SOUNDCARD_TXLEVEL, double(m_soundCardTXLevel)); + m_config->Write(m_name + KEY_SOUNDCARD_TXDELAY, long(m_soundCardTXDelay)); + m_config->Write(m_name + KEY_SOUNDCARD_TXTAIL, long(m_soundCardTXTail)); + + m_config->Write(m_name + KEY_SPLIT_LOCALADDRESS, m_splitLocalAddress); + m_config->Write(m_name + KEY_SPLIT_LOCALPORT, long(m_splitLocalPort)); + + for (unsigned int i = 0U; i < m_splitTXNames.GetCount(); i++) { + wxString name; + name.Printf(wxT("%s%u"), KEY_SPLIT_TXNAME.c_str(), i); + + m_config->Write(m_name + name, m_splitTXNames.Item(i)); + } + + for (unsigned int i = 0U; i < m_splitRXNames.GetCount(); i++) { + wxString name; + name.Printf(wxT("%s%u"), KEY_SPLIT_RXNAME.c_str(), i); + + m_config->Write(m_name + name, m_splitRXNames.Item(i)); + } + + m_config->Write(m_name + KEY_SPLIT_TIMEOUT, long(m_splitTimeout)); + + m_config->Flush(); +#endif + + wxTextFile file(m_fileName.GetFullPath()); + + bool exists = file.Exists(); + if (exists) { + bool ret = file.Open(); + if (!ret) { + wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + // Remove the existing file entries + file.Clear(); + } else { + bool ret = file.Create(); + if (!ret) { + wxLogError(wxT("Cannot create the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + } + + wxString buffer; + buffer.Printf(wxT("%s=%s"), KEY_CALLSIGN.c_str(), m_callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_GATEWAY.c_str(), m_gateway.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_MODE.c_str(), int(m_mode)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_ACK.c_str(), int(m_ack)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_RESTRICTION.c_str(), m_restriction ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_RPT1_VALIDATION.c_str(), m_rpt1Validation ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DTMF_BLANKING.c_str(), m_dtmfBlanking ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_ERROR_REPLY.c_str(), m_errorReply ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_GATEWAY_ADDRESS.c_str(), m_gatewayAddress.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_GATEWAY_PORT.c_str(), m_gatewayPort); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_LOCAL_ADDRESS.c_str(), m_localAddress.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_LOCAL_PORT.c_str(), m_localPort); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_NETWORK_NAME.c_str(), m_networkName.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_MODEM_TYPE.c_str(), m_modemType.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_TIMEOUT.c_str(), m_timeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_ACK_TIME.c_str(), m_ackTime); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BEACON_TIME.c_str(), m_beaconTime); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_BEACON_TEXT.c_str(), m_beaconText.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_BEACON_VOICE.c_str(), m_beaconVoice ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_LANGUAGE.c_str(), int(m_language)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_ANNOUNCEMENT_ENABLED.c_str(), m_announcementEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_ANNOUNCEMENT_TIME.c_str(), m_announcementTime); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_ANNOUNCEMENT_RECORD_RPT1.c_str(), m_announcementRecordRPT1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_ANNOUNCEMENT_RECORD_RPT2.c_str(), m_announcementRecordRPT2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_ANNOUNCEMENT_DELETE_RPT1.c_str(), m_announcementDeleteRPT1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_ANNOUNCEMENT_DELETE_RPT2.c_str(), m_announcementDeleteRPT2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_CONTROL_ENABLED.c_str(), m_controlEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_RPT1.c_str(), m_controlRpt1Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_RPT2.c_str(), m_controlRpt2Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_SHUTDOWN.c_str(), m_controlShutdown.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STARTUP.c_str(), m_controlStartup.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS1.c_str(), m_controlStatus1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS2.c_str(), m_controlStatus2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS3.c_str(), m_controlStatus3.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS4.c_str(), m_controlStatus4.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS5.c_str(), m_controlStatus5.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND1.c_str(), m_controlCommand1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND1_LINE.c_str(), m_controlCommand1Line.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND2.c_str(), m_controlCommand2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND2_LINE.c_str(), m_controlCommand2Line.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND3.c_str(), m_controlCommand3.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND3_LINE.c_str(), m_controlCommand3Line.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND4.c_str(), m_controlCommand4.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND4_LINE.c_str(), m_controlCommand4Line.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND5.c_str(), m_controlCommand5.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND5_LINE.c_str(), m_controlCommand5Line.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND6.c_str(), m_controlCommand6.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND6_LINE.c_str(), m_controlCommand6Line.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_OUTPUT1.c_str(), m_controlOutput1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_OUTPUT2.c_str(), m_controlOutput2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_OUTPUT3.c_str(), m_controlOutput3.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROL_OUTPUT4.c_str(), m_controlOutput4.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CONTROLLER_TYPE.c_str(), m_controllerType.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_SERIAL_CONFIG.c_str(), m_serialConfig); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_PTT_INVERT.c_str(), m_pttInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_ACTIVE_HANG_TIME.c_str(), m_activeHangTime); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_OUTPUT1.c_str(), m_output1 ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_OUTPUT2.c_str(), m_output2 ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_OUTPUT3.c_str(), m_output3 ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_OUTPUT4.c_str(), m_output4 ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_LOGGING.c_str(), m_logging ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_X.c_str(), m_x); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_Y.c_str(), m_y); file.AddLine(buffer); + + buffer.Printf(wxT("%s=%s"), KEY_DVAP_PORT.c_str(), m_dvapPort.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVAP_FREQUENCY.c_str(), m_dvapFrequency); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVAP_POWER.c_str(), m_dvapPower); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVAP_SQUELCH.c_str(), m_dvapSquelch); file.AddLine(buffer); + + buffer.Printf(wxT("%s=%u"), KEY_GMSK_ADDRESS.c_str(), m_gmskAddress); file.AddLine(buffer); + + buffer.Printf(wxT("%s=%s"), KEY_DVRPTR1_PORT.c_str(), m_dvrptr1Port.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVRPTR1_RXINVERT.c_str(), m_dvrptr1RXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVRPTR1_TXINVERT.c_str(), m_dvrptr1TXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVRPTR1_CHANNEL.c_str(), m_dvrptr1Channel ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVRPTR1_MODLEVEL.c_str(), m_dvrptr1ModLevel); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVRPTR1_TXDELAY.c_str(), m_dvrptr1TXDelay); file.AddLine(buffer); + + buffer.Printf(wxT("%s=%d"), KEY_DVRPTR2_CONNECTION.c_str(), int(m_dvrptr2Connection)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DVRPTR2_USBPORT.c_str(), m_dvrptr2USBPort.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DVRPTR2_ADDRESS.c_str(), m_dvrptr2Address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVRPTR2_PORT.c_str(), m_dvrptr2Port); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVRPTR2_TXINVERT.c_str(), m_dvrptr2TXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVRPTR2_MODLEVEL.c_str(), m_dvrptr2ModLevel); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVRPTR2_TXDELAY.c_str(), m_dvrptr2TXDelay); file.AddLine(buffer); + + buffer.Printf(wxT("%s=%d"), KEY_DVRPTR3_CONNECTION.c_str(), int(m_dvrptr3Connection)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DVRPTR3_USBPORT.c_str(), m_dvrptr3USBPort.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DVRPTR3_ADDRESS.c_str(), m_dvrptr3Address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVRPTR3_PORT.c_str(), m_dvrptr3Port); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVRPTR3_TXINVERT.c_str(), m_dvrptr3TXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVRPTR3_MODLEVEL.c_str(), m_dvrptr3ModLevel); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVRPTR3_TXDELAY.c_str(), m_dvrptr3TXDelay); file.AddLine(buffer); + + buffer.Printf(wxT("%s=%s"), KEY_DVMEGA_PORT.c_str(), m_dvmegaPort.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVMEGA_VARIANT.c_str(), int(m_dvmegaVariant)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVMEGA_RXINVERT.c_str(), m_dvmegaRXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DVMEGA_TXINVERT.c_str(), m_dvmegaTXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVMEGA_TXDELAY.c_str(), m_dvmegaTXDelay); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVMEGA_RXFREQUENCY.c_str(), m_dvmegaRXFrequency); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVMEGA_TXFREQUENCY.c_str(), m_dvmegaTXFrequency); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DVMEGA_POWER.c_str(), m_dvmegaPower); file.AddLine(buffer); + + buffer.Printf(wxT("%s=%s"), KEY_MMDVM_PORT.c_str(), m_mmdvmPort.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_MMDVM_RXINVERT.c_str(), m_mmdvmRXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_MMDVM_TXINVERT.c_str(), m_mmdvmTXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_MMDVM_PTTINVERT.c_str(), m_mmdvmPTTInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_MMDVM_TXDELAY.c_str(), m_mmdvmTXDelay); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_MMDVM_RXLEVEL.c_str(), m_mmdvmRXLevel); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_MMDVM_TXLEVEL.c_str(), m_mmdvmTXLevel); file.AddLine(buffer); + + buffer.Printf(wxT("%s=%s"), KEY_SOUNDCARD_RXDEVICE.c_str(), m_soundCardRXDevice.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_SOUNDCARD_TXDEVICE.c_str(), m_soundCardTXDevice.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_SOUNDCARD_RXINVERT.c_str(), m_soundCardRXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_SOUNDCARD_TXINVERT.c_str(), m_soundCardTXInvert ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.4f"), KEY_SOUNDCARD_RXLEVEL.c_str(), m_soundCardRXLevel); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.4f"), KEY_SOUNDCARD_TXLEVEL.c_str(), m_soundCardTXLevel); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_SOUNDCARD_TXDELAY.c_str(), m_soundCardTXDelay); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_SOUNDCARD_TXTAIL.c_str(), m_soundCardTXTail); file.AddLine(buffer); + + buffer.Printf(wxT("%s=%s"), KEY_SPLIT_LOCALADDRESS.c_str(), m_splitLocalAddress.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_SPLIT_LOCALPORT.c_str(), m_splitLocalPort); file.AddLine(buffer); + + for (unsigned int i = 0U; i < m_splitTXNames.GetCount(); i++) { + wxString name; + name.Printf(wxT("%s%u"), KEY_SPLIT_TXNAME.c_str(), i); + buffer.Printf(wxT("%s=%s"), name.c_str(), m_splitTXNames.Item(i).c_str()); + file.AddLine(buffer); + } + + for (unsigned int i = 0U; i < m_splitRXNames.GetCount(); i++) { + wxString name; + name.Printf(wxT("%s%u"), KEY_SPLIT_RXNAME.c_str(), i); + buffer.Printf(wxT("%s=%s"), name.c_str(), m_splitRXNames.Item(i).c_str()); + file.AddLine(buffer); + } + + buffer.Printf(wxT("%s=%u"), KEY_SPLIT_TIMEOUT.c_str(), m_splitTimeout); file.AddLine(buffer); + + bool ret = file.Write(); + if (!ret) { + file.Close(); + wxLogError(wxT("Cannot write the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + file.Close(); + + return true; +} diff --git a/Common/DStarRepeaterConfig.h b/Common/DStarRepeaterConfig.h new file mode 100644 index 0000000..2f36d98 --- /dev/null +++ b/Common/DStarRepeaterConfig.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfig_H +#define DStarRepeaterConfig_H + +#include "DStarDefines.h" + +#include +#include +#include + +class CDStarRepeaterConfig { +public: +#if defined(__WINDOWS__) + CDStarRepeaterConfig(wxConfigBase* config, const wxString& dir, const wxString& configName, const wxString& name); +#else + CDStarRepeaterConfig(const wxString& dir, const wxString& configName, const wxString& name, const bool mustExist=false); +#endif + ~CDStarRepeaterConfig(); + + void getCallsign(wxString& callsign, wxString& gateway, DSTAR_MODE& mode, ACK_TYPE& ack, bool& restriction, bool& rpt1Validation, bool& dtmfBlanking, bool& errorReply) const; + void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + + void getNetwork(wxString& gatewayAddress, unsigned int& gatewayPort, wxString& localAddress, unsigned int& localPort, wxString& name) const; + void setNetwork(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name); + + void getModem(wxString& type) const; + void setModem(const wxString& type); + + void getTimes(unsigned int& timeout, unsigned int& ackTime) const; + void setTimes(unsigned int timeout, unsigned int ackTime); + + void getBeacon(unsigned int& time, wxString& text, bool& voice, TEXT_LANG& language) const; + void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); + + void getAnnouncement(bool& enabled, unsigned int& time, wxString& recordRPT1, wxString& recordRPT2, wxString& deleteRPT1, wxString& deleteRPT2) const; + void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + + void getControl(bool& enabled, wxString& rpt1Callsign, wxString& rpt2Callsign, wxString& shutdown, wxString& startup, wxString& status1, wxString& status2, wxString& status3, wxString& status4, wxString& status5, wxString& command1, wxString& command1Line, wxString& command2, wxString& command2Line, wxString& command5, wxString& command5Line, wxString& command6, wxString& command6Line, wxString& command3, wxString& command3Line, wxString& command4, wxString& command4Line, wxString& output1, wxString& output2, wxString& output3, wxString& output4) const; + + void setControl(bool enabled, const wxString& rpt1Callsign, const wxString& rpt2Callsign, const wxString& shutdown, const wxString& startup, const wxString& status1, const wxString& status2, const wxString& status3, const wxString& status4, const wxString& status5, const wxString& command1, const wxString& command1Line, const wxString& command2, const wxString& command2Line, const wxString& command3, const wxString& command3Line, const wxString& command4, const wxString& command4Line, const wxString& command5, const wxString& command5Line, const wxString& command6, const wxString& command6Line, const wxString& output1, const wxString& output2, const wxString& output3, const wxString& output4); + + void getController(wxString& type, unsigned int& serialConfig, bool& pttInvert, unsigned int& activeHangTime) const; + void setController(const wxString& type, unsigned int serialConfig, bool pttInvert, unsigned int activeHangTime); + + void getOutputs(bool& out1, bool& out2, bool& out3, bool& out4) const; + void setOutputs(bool out1, bool out2, bool out3, bool out4); + + void getLogging(bool& logging) const; + void setLogging(bool logging); + + void getPosition(int& x, int& y) const; + void setPosition(int x, int y); + + void getDVAP(wxString& port, unsigned int& frequency, int& power, int& squelch) const; + void setDVAP(const wxString& port, unsigned int frequency, int power, int squelch); + + void getGMSK(USB_INTERFACE& type, unsigned int& address) const; + void setGMSK(USB_INTERFACE type, unsigned int address); + + void getDVRPTR1(wxString& port, bool& rxInvert, bool& txInvert, bool& channel, unsigned int& modLevel, unsigned int& txDelay) const; + void setDVRPTR1(const wxString& port, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay); + + void getDVRPTR2(CONNECTION_TYPE& connectionType, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const; + void setDVRPTR2(CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay); + + void getDVRPTR3(CONNECTION_TYPE& connectionType, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const; + void setDVRPTR3(CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay); + + void getDVMEGA(wxString& port, DVMEGA_VARIANT& variant, bool& rxInvert, bool& txInvert, unsigned int& txDelay, unsigned int& rxFrequency, unsigned int& txFrequency, unsigned int& power) const; + void setDVMEGA(const wxString& port, DVMEGA_VARIANT variant, bool rxInvert, bool txInvert, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power); + + void getMMDVM(wxString& port, bool& rxInvert, bool& txInvert, bool& pttInvert, unsigned int& txDelay, unsigned int& rxLevel, unsigned int& txLevel) const; + void setMMDVM(const wxString& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel); + + void getSoundCard(wxString& rxDevice, wxString& txDevice, bool& rxInvert, bool& txInvert, wxFloat32& rxLevel, wxFloat32& txLevel, unsigned int& txDelay, unsigned int& txTail) const; + void setSoundCard(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail); + + void getSplit(wxString& localAddress, unsigned int& localPort, wxArrayString& transmitterNames, wxArrayString& receiverNames, unsigned int& timeout) const; + void setSplit(const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout); + + bool write(); + +private: +#if defined(__WINDOWS__) + wxConfigBase* m_config; + wxString m_name; +#endif + wxFileName m_fileName; + wxString m_callsign; + wxString m_gateway; + DSTAR_MODE m_mode; + ACK_TYPE m_ack; + bool m_restriction; + bool m_rpt1Validation; + bool m_dtmfBlanking; + bool m_errorReply; + wxString m_gatewayAddress; + unsigned int m_gatewayPort; + wxString m_localAddress; + unsigned int m_localPort; + wxString m_networkName; + wxString m_modemType; + unsigned int m_timeout; + unsigned int m_ackTime; + unsigned int m_beaconTime; + wxString m_beaconText; + bool m_beaconVoice; + TEXT_LANG m_language; + bool m_announcementEnabled; + unsigned int m_announcementTime; + wxString m_announcementRecordRPT1; + wxString m_announcementRecordRPT2; + wxString m_announcementDeleteRPT1; + wxString m_announcementDeleteRPT2; + bool m_controlEnabled; + wxString m_controlRpt1Callsign; + wxString m_controlRpt2Callsign; + wxString m_controlShutdown; + wxString m_controlStartup; + wxString m_controlStatus1; + wxString m_controlStatus2; + wxString m_controlStatus3; + wxString m_controlStatus4; + wxString m_controlStatus5; + wxString m_controlCommand1; + wxString m_controlCommand1Line; + wxString m_controlCommand2; + wxString m_controlCommand2Line; + wxString m_controlCommand3; + wxString m_controlCommand3Line; + wxString m_controlCommand4; + wxString m_controlCommand4Line; + wxString m_controlCommand5; + wxString m_controlCommand5Line; + wxString m_controlCommand6; + wxString m_controlCommand6Line; + wxString m_controlOutput1; + wxString m_controlOutput2; + wxString m_controlOutput3; + wxString m_controlOutput4; + wxString m_controllerType; + unsigned int m_serialConfig; + bool m_pttInvert; + unsigned int m_activeHangTime; + bool m_output1; + bool m_output2; + bool m_output3; + bool m_output4; + bool m_logging; + int m_x; + int m_y; + + // DVAP + wxString m_dvapPort; + unsigned int m_dvapFrequency; + int m_dvapPower; + int m_dvapSquelch; + + // GMSK + USB_INTERFACE m_gmskInterface; + unsigned int m_gmskAddress; + + // DV-RPTR 1 + wxString m_dvrptr1Port; + bool m_dvrptr1RXInvert; + bool m_dvrptr1TXInvert; + bool m_dvrptr1Channel; + unsigned int m_dvrptr1ModLevel; + unsigned int m_dvrptr1TXDelay; + + // DV-RPTR 2 + CONNECTION_TYPE m_dvrptr2Connection; + wxString m_dvrptr2USBPort; + wxString m_dvrptr2Address; + unsigned int m_dvrptr2Port; + bool m_dvrptr2TXInvert; + unsigned int m_dvrptr2ModLevel; + unsigned int m_dvrptr2TXDelay; + + // DV-RPTR 3 + CONNECTION_TYPE m_dvrptr3Connection; + wxString m_dvrptr3USBPort; + wxString m_dvrptr3Address; + unsigned int m_dvrptr3Port; + bool m_dvrptr3TXInvert; + unsigned int m_dvrptr3ModLevel; + unsigned int m_dvrptr3TXDelay; + + // DVMEGA + wxString m_dvmegaPort; + DVMEGA_VARIANT m_dvmegaVariant; + bool m_dvmegaRXInvert; + bool m_dvmegaTXInvert; + unsigned int m_dvmegaTXDelay; + unsigned int m_dvmegaRXFrequency; + unsigned int m_dvmegaTXFrequency; + unsigned int m_dvmegaPower; + + // MMDVM + wxString m_mmdvmPort; + bool m_mmdvmRXInvert; + bool m_mmdvmTXInvert; + bool m_mmdvmPTTInvert; + unsigned int m_mmdvmTXDelay; + unsigned int m_mmdvmRXLevel; + unsigned int m_mmdvmTXLevel; + + // Sound Card + wxString m_soundCardRXDevice; + wxString m_soundCardTXDevice; + bool m_soundCardRXInvert; + bool m_soundCardTXInvert; + wxFloat32 m_soundCardRXLevel; + wxFloat32 m_soundCardTXLevel; + unsigned int m_soundCardTXDelay; + unsigned int m_soundCardTXTail; + + // Split + wxString m_splitLocalAddress; + unsigned int m_splitLocalPort; + wxArrayString m_splitTXNames; + wxArrayString m_splitRXNames; + unsigned int m_splitTimeout; +}; + +#endif diff --git a/Common/DStarScrambler.cpp b/Common/DStarScrambler.cpp new file mode 100644 index 0000000..ad1c735 --- /dev/null +++ b/Common/DStarScrambler.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2009 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "DStarScrambler.h" + +#include + +static const bool SCRAMBLER_TABLE_BITS[] = { + false, false, false, false, true, true, true, false, true, true, true, true, false, false, true, false, + true, true, false, false, true, false, false, true, false, false, false, false, false, false, true, false, + false, false, true, false, false, true, true, false, false, false, true, false, true, true, true, false, + true, false, true, true, false, true, true, false, false, false, false, false, true, true, false, false, + true, true, false, true, false, true, false, false, true, true, true, false, false, true, true, true, + true, false, true, true, false, true, false, false, false, false, true, false, true, false, true, false, + true, true, true, true, true, false, true, false, false, true, false, true, false, false, false, true, + true, false, true, true, true, false, false, false, true, true, true, true, true, true, true, false, + false, false, false, true, true, true, false, true, true, true, true, false, false, true, false, true, + true, false, false, true, false, false, true, false, false, false, false, false, false, true, false, false, + false, true, false, false, true, true, false, false, false, true, false, true, true, true, false, true, + false, true, true, false, true, true, false, false, false, false, false, true, true, false, false, true, + true, false, true, false, true, false, false, true, true, true, false, false, true, true, true, true, + false, true, true, false, true, false, false, false, false, true, false, true, false, true, false, true, + true, true, true, true, false, true, false, false, true, false, true, false, false, false, true, true, + false, true, true, true, false, false, false, true, true, true, true, true, true, true, false, false, + false, false, true, true, true, false, true, true, true, true, false, false, true, false, true, true, + false, false, true, false, false, true, false, false, false, false, false, false, true, false, false, false, + true, false, false, true, true, false, false, false, true, false, true, true, true, false, true, false, + true, true, false, true, true, false, false, false, false, false, true, true, false, false, true, true, + false, true, false, true, false, false, true, true, true, false, false, true, true, true, true, false, + true, true, false, true, false, false, false, false, true, false, true, false, true, false, true, true, + true, true, true, false, true, false, false, true, false, true, false, false, false, true, true, false, + true, true, true, false, false, false, true, true, true, true, true, true, true, false, false, false, + false, true, true, true, false, true, true, true, true, false, false, true, false, true, true, false, + false, true, false, false, true, false, false, false, false, false, false, true, false, false, false, true, + false, false, true, true, false, false, false, true, false, true, true, true, false, true, false, true, + true, false, true, true, false, false, false, false, false, true, true, false, false, true, true, false, + true, false, true, false, false, true, true, true, false, false, true, true, true, true, false, true, + true, false, true, false, false, false, false, true, false, true, false, true, false, true, true, true, + true, true, false, true, false, false, true, false, true, false, false, false, true, true, false, true, + true, true, false, false, false, true, true, true, true, true, true, true, false, false, false, false, + true, true, true, false, true, true, true, true, false, false, true, false, true, true, false, false, + true, false, false, true, false, false, false, false, false, false, true, false, false, false, true, false, + false, true, true, false, false, false, true, false, true, true, true, false, true, false, true, true, + false, true, true, false, false, false, false, false, true, true, false, false, true, true, false, true, + false, true, false, false, true, true, true, false, false, true, true, true, true, false, true, true, + false, true, false, false, false, false, true, false, true, false, true, false, true, true, true, true, + true, false, true, false, false, true, false, true, false, false, false, true, true, false, true, true, + true, false, false, false, true, true, true, true, true, true, true, false, false, false, false, true, + true, true, false, true, true, true, true, false, false, true, false, true, true, false, false, true, + false, false, true, false, false, false, false, false, false, true, false, false, false, true, false, false, + true, true, false, false, false, true, false, true, true, true, false, true, false, true, true, false, + true, true, false, false, false, false, false, true, true, false, false, true, true, false, true, false, + true, false, false, true, true, true, false, false, true, true, true, true, false, true, true, false}; + +static const unsigned int SCRAMBLER_TABLE_BITS_LENGTH = 720U; + +static const unsigned char SCRAMBLER_TABLE_BYTES[] = { + 0x0e, 0xf2, 0xc9, 0x02, 0x26, 0x2e, 0xb6, 0x0c, 0xd4, 0xe7, 0xb4, 0x2a, 0xfa, 0x51, 0xb8, 0xfe, + 0x1d, 0xe5, 0x92, 0x04, 0x4c, 0x5d, 0x6c, 0x19, 0xa9, 0xcf, 0x68, 0x55, 0xf4, 0xa3, 0x71, 0xfc, + 0x3b, 0xcb, 0x24, 0x08, 0x98, 0xba, 0xd8, 0x33, 0x53, 0x9e, 0xd0, 0xab, 0xe9, 0x46, 0xe3, 0xf8, + 0x77, 0x96, 0x48, 0x11, 0x31, 0x75, 0xb0, 0x66, 0xa7, 0x3d, 0xa1, 0x57, 0xd2, 0x8d, 0xc7, 0xf0, + 0xef, 0x2c, 0x90, 0x22, 0x62, 0xeb, 0x60, 0xcd, 0x4e, 0x7b, 0x42, 0xaf, 0xa5, 0x1b, 0x8f, 0xe1, + 0xde, 0x59, 0x20, 0x44, 0xc5, 0xd6, 0xc1, 0x9a, 0x9c, 0xf6}; + +static const unsigned int SCRAMBLER_TABLE_BYTES_LENGTH = 90U; + +CDStarScrambler::CDStarScrambler() : +m_count(0U) +{ +} + +CDStarScrambler::~CDStarScrambler() +{ +} + +void CDStarScrambler::process(bool* inOut, unsigned int length) +{ + wxASSERT(inOut != 0); + + for (unsigned int i = 0U; i < length; i++) { + inOut[i] ^= SCRAMBLER_TABLE_BITS[m_count++]; + + if (m_count >= SCRAMBLER_TABLE_BITS_LENGTH) + m_count = 0U; + } +} + +void CDStarScrambler::process(const bool* in, bool* out, unsigned int length) +{ + wxASSERT(in != 0); + wxASSERT(out != 0); + + for (unsigned int i = 0U; i < length; i++) { + out[i] = in[i] ^ SCRAMBLER_TABLE_BITS[m_count++]; + + if (m_count >= SCRAMBLER_TABLE_BITS_LENGTH) + m_count = 0U; + } +} + +void CDStarScrambler::process(unsigned char* inOut, unsigned int length) +{ + wxASSERT(inOut != 0); + + for (unsigned int i = 0U; i < length; i++) { + inOut[i] ^= SCRAMBLER_TABLE_BYTES[m_count++]; + + if (m_count >= SCRAMBLER_TABLE_BYTES_LENGTH) + m_count = 0U; + } +} + +void CDStarScrambler::process(const unsigned char* in, unsigned char* out, unsigned int length) +{ + wxASSERT(in != 0); + wxASSERT(out != 0); + + for (unsigned int i = 0U; i < length; i++) { + out[i] = in[i] ^ SCRAMBLER_TABLE_BYTES[m_count++]; + + if (m_count >= SCRAMBLER_TABLE_BYTES_LENGTH) + m_count = 0U; + } +} + +void CDStarScrambler::reset() +{ + m_count = 0U; +} diff --git a/Common/DStarScrambler.h b/Common/DStarScrambler.h new file mode 100644 index 0000000..40db775 --- /dev/null +++ b/Common/DStarScrambler.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef DStarScrambler_H +#define DStarScrambler_H + +class CDStarScrambler { +public: + CDStarScrambler(); + ~CDStarScrambler(); + + void process(bool* inOut, unsigned int length); + void process(const bool* in, bool* out, unsigned int length); + + void process(unsigned char* inOut, unsigned int length); + void process(const unsigned char* in, unsigned char* out, unsigned int length); + + void reset(); + +private: + unsigned int m_count; +}; + +#endif diff --git a/Common/DVAPController.cpp b/Common/DVAPController.cpp new file mode 100644 index 0000000..e8a0fee --- /dev/null +++ b/Common/DVAPController.cpp @@ -0,0 +1,1026 @@ +/* + * Copyright (C) 2011-2014,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CCITTChecksumReverse.h" +#include "DVAPController.h" +#include "DStarDefines.h" +#include "Timer.h" + +const unsigned char DVAP_REQ_NAME[] = {0x04, 0x20, 0x01, 0x00}; +const unsigned int DVAP_REQ_NAME_LEN = 4U; + +const unsigned char DVAP_RESP_NAME[] = {0x10, 0x00, 0x01, 0x00, 'D', 'V', 'A', 'P', ' ', 'D', 'o', 'n', 'g', 'l', 'e', 0x00}; +const unsigned int DVAP_RESP_NAME_LEN = 16U; + +const unsigned char DVAP_REQ_SERIAL[] = {0x04, 0x20, 0x02, 0x00}; +const unsigned int DVAP_REQ_SERIAL_LEN = 4U; + +const unsigned char DVAP_RESP_SERIAL[] = {0x0C, 0x00, 0x02, 0x00}; +const unsigned int DVAP_RESP_SERIAL_LEN = 4U; + +const unsigned char DVAP_REQ_FIRMWARE[] = {0x05, 0x20, 0x04, 0x00, 0x01}; +const unsigned int DVAP_REQ_FIRMWARE_LEN = 5U; + +const unsigned char DVAP_RESP_FIRMWARE[] = {0x07, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}; +const unsigned int DVAP_RESP_FIRMWARE_LEN = 7U; + +const unsigned char DVAP_REQ_MODULATION[] = {0x05, 0x00, 0x28, 0x00, 0x01}; +const unsigned int DVAP_REQ_MODULATION_LEN = 5U; + +const unsigned char DVAP_RESP_MODULATION[] = {0x05, 0x00, 0x28, 0x00, 0x01}; +const unsigned int DVAP_RESP_MODULATION_LEN = 5U; + +const unsigned char DVAP_REQ_MODE[] = {0x05, 0x00, 0x2A, 0x00, 0x00}; +const unsigned int DVAP_REQ_MODE_LEN = 5U; + +const unsigned char DVAP_RESP_MODE[] = {0x05, 0x00, 0x2A, 0x00, 0x00}; +const unsigned int DVAP_RESP_MODE_LEN = 5U; + +const unsigned char DVAP_REQ_SQUELCH[] = {0x05, 0x00, 0x80, 0x00, 0x00}; +const unsigned int DVAP_REQ_SQUELCH_LEN = 5U; + +const unsigned char DVAP_RESP_SQUELCH[] = {0x05, 0x00, 0x80, 0x00, 0x00}; +const unsigned int DVAP_RESP_SQUELCH_LEN = 5U; + +const unsigned char DVAP_REQ_POWER[] = {0x06, 0x00, 0x38, 0x01, 0x00, 0x00}; +const unsigned int DVAP_REQ_POWER_LEN = 6U; + +const unsigned char DVAP_RESP_POWER[] = {0x06, 0x00, 0x38, 0x01, 0x00, 0x00}; +const unsigned int DVAP_RESP_POWER_LEN = 6U; + +const unsigned char DVAP_REQ_FREQUENCY[] = {0x08, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00}; +const unsigned int DVAP_REQ_FREQUENCY_LEN = 8U; + +const unsigned char DVAP_RESP_FREQUENCY[] = {0x08, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00}; +const unsigned int DVAP_RESP_FREQUENCY_LEN = 8U; + +const unsigned char DVAP_REQ_FREQLIMITS[] = {0x04, 0x20, 0x30, 0x02}; +const unsigned int DVAP_REQ_FREQLIMITS_LEN = 4U; + +const unsigned char DVAP_RESP_FREQLIMITS[] = {0x0C, 0x00, 0x30, 0x02}; +const unsigned int DVAP_RESP_FREQLIMITS_LEN = 4U; + +const unsigned char DVAP_REQ_START[] = {0x05, 0x00, 0x18, 0x00, 0x01}; +const unsigned int DVAP_REQ_START_LEN = 5U; + +const unsigned char DVAP_RESP_START[] = {0x05, 0x00, 0x18, 0x00, 0x01}; +const unsigned int DVAP_RESP_START_LEN = 5U; + +const unsigned char DVAP_REQ_STOP[] = {0x05, 0x00, 0x18, 0x00, 0x00}; +const unsigned int DVAP_REQ_STOP_LEN = 5U; + +const unsigned char DVAP_RESP_STOP[] = {0x05, 0x00, 0x18, 0x00, 0x00}; +const unsigned int DVAP_RESP_STOP_LEN = 5U; + +const unsigned char DVAP_HEADER[] = {0x2F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const unsigned int DVAP_HEADER_LEN = 47U; + +const unsigned char DVAP_RESP_HEADER[] = {0x2F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const unsigned int DVAP_RESP_HEADER_LEN = 47U; + +const unsigned char DVAP_RESP_PTT[] = {0x05, 0x20, 0x18, 0x01, 0x00}; +const unsigned int DVAP_RESP_PTT_LEN = 5U; + +const unsigned char DVAP_GMSK_DATA[] = {0x12, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const unsigned int DVAP_GMSK_DATA_LEN = 18U; + +const unsigned char DVAP_STATUS[] = {0x07, 0x20, 0x90, 0x00, 0x00, 0x00, 0x00}; +const unsigned int DVAP_STATUS_LEN = 7U; + +const unsigned char DVAP_ACK[] = {0x03, 0x60, 0x00}; +const unsigned int DVAP_ACK_LEN = 3U; + +const unsigned char DVAP_FM_DATA[] = {0x42, 0x81}; +const unsigned int DVAP_FM_DATA_LEN = 2U; + +const unsigned int DVAP_HEADER_LENGTH = 2U; + +const unsigned int MAX_RESPONSES = 20U; + +const unsigned int BUFFER_LENGTH = 200U; + +const unsigned int DVAP_DUMP_LENGTH = 30U; + +CDVAPController::CDVAPController(const wxString& port, unsigned int frequency, int power, int squelch) : +CModem(), +m_serial(port, SERIAL_230400), +m_frequency(frequency), +m_power(power), +m_squelch(squelch), +m_squelchOpen(false), +m_signal(0), +m_buffer(NULL), +m_streamId(0U), +m_framePos(0U), +m_seq(0U), +m_txData(1000U) +#if defined(DVAP_DUMP) +, +m_dvapData(NULL), +m_dvapLength(NULL), +m_dvapIndex(0U) +#endif +{ + wxASSERT(!port.IsEmpty()); + wxASSERT((frequency >= 144000000U && frequency <= 148000000U) || + (frequency >= 220000000U && frequency <= 225000000U) || + (frequency >= 420000000U && frequency <= 450000000U)); + wxASSERT(power >= -12 && power <= 10); + wxASSERT(squelch >= -128 && squelch <= -45); + + m_buffer = new unsigned char[BUFFER_LENGTH]; + +#if defined(DVAP_DUMP) + m_dvapData = new unsigned char*[DVAP_DUMP_LENGTH]; + for (unsigned int i = 0U; i < DVAP_DUMP_LENGTH; i++) + m_dvapData[i] = new unsigned char[100U]; + + m_dvapLength = new unsigned int[DVAP_DUMP_LENGTH]; + for (unsigned int i = 0U; i < DVAP_DUMP_LENGTH; i++) + m_dvapLength[i] = 0U; +#endif +} + +CDVAPController::~CDVAPController() +{ + delete[] m_buffer; + +#if defined(DVAP_DUMP) + for (unsigned int i = 0U; i < DVAP_DUMP_LENGTH; i++) + delete[] m_dvapData[i]; + delete[] m_dvapData; + + delete[] m_dvapLength; +#endif +} + +bool CDVAPController::start() +{ + bool res = m_serial.open(); + if (!res) + return false; + + res = getName(); + if (!res) { + m_serial.close(); + return false; + } + + res = getFirmware(); + if (!res) { + m_serial.close(); + return false; + } + + res = getSerial(); + if (!res) { + m_serial.close(); + return false; + } + + res = setModulation(); + if (!res) { + m_serial.close(); + return false; + } + + res = setMode(); + if (!res) { + m_serial.close(); + return false; + } + + res = setSquelch(); + if (!res) { + m_serial.close(); + return false; + } + + res = setPower(); + if (!res) { + m_serial.close(); + return false; + } + + res = setFrequency(); + if (!res) { + m_serial.close(); + return false; + } + + res = startDVAP(); + if (!res) { + m_serial.close(); + return false; + } + + Create(); + SetPriority(100U); + Run(); + + return true; +} + +void* CDVAPController::Entry() +{ + wxLogMessage(wxT("Starting DVAP Controller thread")); + + // Clock every 5ms-ish + CTimer pollTimer(200U, 2U); + pollTimer.start(); + + unsigned char writeLength = 0U; + unsigned char* writeBuffer = new unsigned char[BUFFER_LENGTH]; + + unsigned int space = 0U; + + while (!m_stopped) { + // Poll the modem every 2s + if (pollTimer.hasExpired()) { + writePoll(); + pollTimer.start(); + } + + unsigned int length; + RESP_TYPE type = getResponse(m_buffer, length); + + switch (type) { + case RT_TIMEOUT: + break; + case RT_ERROR: + wxLogMessage(wxT("Stopping DVAP Controller thread")); +#if defined(DVAP_DUMP) + dumpPackets(); +#endif + m_serial.close(); + return NULL; + case RT_STATE: + m_signal = int(m_buffer[4U]) - 256; + m_squelchOpen = m_buffer[5U] == 0x01U; + space = m_buffer[6U]; + break; + case RT_PTT: + m_tx = m_buffer[4U] == 0x01U; + break; + case RT_START: + break; + case RT_STOP: + wxLogWarning(wxT("DVAP has stopped, restarting")); +#if defined(DVAP_DUMP) + dumpPackets(); +#endif + startDVAP(); + break; + case RT_HEADER: { + wxMutexLocker locker(m_mutex); + + unsigned char hdr[2U]; + hdr[0U] = DSMTT_HEADER; + hdr[1U] = RADIO_HEADER_LENGTH_BYTES; + m_rxData.addData(hdr, 2U); + + m_rxData.addData(m_buffer + 6U, RADIO_HEADER_LENGTH_BYTES); + } + break; + case RT_HEADER_ACK: + break; + case RT_GMSK_DATA: { + wxMutexLocker locker(m_mutex); + + bool end = (m_buffer[4U] & 0x40U) == 0x40U; + if (end) { + unsigned char hdr[2U]; + hdr[0U] = DSMTT_EOT; + hdr[1U] = 0U; + m_rxData.addData(hdr, 2U); + } else { + unsigned char hdr[2U]; + hdr[0U] = DSMTT_DATA; + hdr[1U] = length - 6U; + m_rxData.addData(hdr, 2U); + + m_rxData.addData(m_buffer + 6U, length - 6U); + } + } + break; + case RT_FM_DATA: + wxLogWarning(wxT("The DVAP has gone into FM mode, restarting the DVAP")); +#if defined(DVAP_DUMP) + dumpPackets(); +#endif + stopDVAP(); + setModulation(); + startDVAP(); + break; + default: + wxLogMessage(wxT("Unknown message")); + CUtils::dump(wxT("Buffer dump"), m_buffer, length); +#if defined(DVAP_DUMP) + dumpPackets(); +#endif + break; + } + + // Use the status packet every 20ms to trigger the sending of data to the DVAP + if (space > 0U && type == RT_STATE) { + if (writeLength == 0U && m_txData.hasData()) { + wxMutexLocker locker(m_mutex); + + m_txData.getData(&writeLength, 1U); + m_txData.getData(writeBuffer, writeLength); + } + + // Only send the header when the TX is off + if (!m_tx && writeLength == DVAP_HEADER_LEN) { + // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + +#if defined(DVAP_DUMP) + storePacket(writeBuffer, writeLength); +#endif + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing the header to the DVAP")); + + writeLength = 0U; + space--; + } + + if (writeLength == DVAP_GMSK_DATA_LEN) { + // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + +#if defined(DVAP_DUMP) + storePacket(writeBuffer, writeLength); +#endif + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing data to the DVAP")); + + writeLength = 0U; + space--; + } + } + + Sleep(5UL); + + pollTimer.clock(); + } + + wxLogMessage(wxT("Stopping DVAP Controller thread")); + + stopDVAP(); + + delete[] writeBuffer; + + m_serial.close(); + + return NULL; +} + +bool CDVAPController::writeHeader(const CHeaderData& header) +{ + bool ret = m_txData.hasSpace(DVAP_HEADER_LEN + 1U); + if (!ret) { + wxLogWarning(wxT("No space to write the header")); + return false; + } + + m_streamId++; + + unsigned char buffer[50U]; + + ::memcpy(buffer, DVAP_HEADER, DVAP_HEADER_LEN); + + wxUint16 sid = wxUINT16_SWAP_ON_BE(m_streamId); + ::memcpy(buffer + 2U, &sid, sizeof(wxUint16)); + + buffer[4U] = 0x80U; + buffer[5U] = 0U; + + ::memset(buffer + 6U, ' ', RADIO_HEADER_LENGTH_BYTES); + + buffer[6U] = header.getFlag1(); + buffer[7U] = header.getFlag2(); + buffer[8U] = header.getFlag3(); + + wxString rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 9U] = rpt2.GetChar(i); + + wxString rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 17U] = rpt1.GetChar(i); + + wxString your = header.getYourCall(); + for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 25U] = your.GetChar(i); + + wxString my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 33U] = my1.GetChar(i); + + wxString my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 41U] = my2.GetChar(i); + + CCCITTChecksumReverse cksum; + cksum.update(buffer + 6U, RADIO_HEADER_LENGTH_BYTES - 2U); + cksum.result(buffer + 45U); + + m_framePos = 0U; + m_seq = 0U; + + wxMutexLocker locker(m_mutex); + + unsigned char len = DVAP_HEADER_LEN; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, DVAP_HEADER_LEN); + + return true; +} + +bool CDVAPController::writeData(const unsigned char* data, unsigned int, bool end) +{ + bool ret = m_txData.hasSpace(DVAP_GMSK_DATA_LEN + 1U); + if (!ret) { + wxLogWarning(wxT("No space to write data")); + return false; + } + + unsigned char buffer[20U]; + + ::memcpy(buffer + 0U, DVAP_GMSK_DATA, DVAP_GMSK_DATA_LEN); + + if (::memcmp(data + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES) == 0) + m_framePos = 0U; + + wxUint16 sid = wxUINT16_SWAP_ON_BE(m_streamId); + ::memcpy(buffer + 2U, &sid, sizeof(wxUint16)); + + buffer[4U] = m_framePos; + buffer[5U] = m_seq; + + if (end) + buffer[4U] |= 0x40U; + + ::memcpy(buffer + 6U, data, DV_FRAME_LENGTH_BYTES); + + wxMutexLocker locker(m_mutex); + + unsigned char len = DVAP_GMSK_DATA_LEN; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, DVAP_GMSK_DATA_LEN); + + m_framePos++; + m_seq++; + + return true; +} + +unsigned int CDVAPController::getSpace() +{ + return m_txData.freeSpace() / (DVAP_GMSK_DATA_LEN + 1U); +} + +bool CDVAPController::isTXReady() +{ + if (m_tx) + return false; + + return m_txData.isEmpty(); +} + +bool CDVAPController::getSquelch() const +{ + return m_squelchOpen; +} + +int CDVAPController::getSignal() const +{ + return m_signal; +} + +void CDVAPController::writePoll() +{ +#if defined(DVAP_DUMP) + storePacket(DVAP_ACK, DVAP_ACK_LEN); +#endif + m_serial.write(DVAP_ACK, DVAP_ACK_LEN); +} + +bool CDVAPController::getName() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + int ret = m_serial.write(DVAP_REQ_NAME, DVAP_REQ_NAME_LEN); + if (ret != int(DVAP_REQ_NAME_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_NAME) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The Dongle is not replying with its name")); + return false; + } + } + } while (resp != RT_NAME); + + bool cmp = ::memcmp(m_buffer, DVAP_RESP_NAME, length) == 0; + if (!cmp) { + wxLogError(wxT("The Dongle is not responding as a DVAP")); + return false; + } + + return true; +} + +bool CDVAPController::getFirmware() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + int ret = m_serial.write(DVAP_REQ_FIRMWARE, DVAP_REQ_FIRMWARE_LEN); + if (ret != int(DVAP_REQ_FIRMWARE_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_FIRMWARE) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding with its firmware version")); + return false; + } + } + } while (resp != RT_FIRMWARE); + + unsigned int version = m_buffer[6U] * 256U + m_buffer[5U]; + wxLogInfo(wxT("DVAP Firmware version: %u.%u"), version / 100U, version % 100U); + + return true; +} + +bool CDVAPController::getSerial() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + int ret = m_serial.write(DVAP_REQ_SERIAL, DVAP_REQ_SERIAL_LEN); + if (ret != int(DVAP_REQ_SERIAL_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_SERIAL) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding with its serial number")); + return false; + } + } + } while (resp != RT_SERIAL); + + wxString serial((char*)(m_buffer + 4U), wxConvLocal, length - 5U); + wxLogInfo(wxT("DVAP Serial number: %s"), serial.c_str()); + + return true; +} + +bool CDVAPController::startDVAP() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + int ret = m_serial.write(DVAP_REQ_START, DVAP_REQ_START_LEN); + if (ret != int(DVAP_REQ_START_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_START) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding to the start command")); + return false; + } + } + } while (resp != RT_START); + + return true; +} + +bool CDVAPController::stopDVAP() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + int ret = m_serial.write(DVAP_REQ_STOP, DVAP_REQ_STOP_LEN); + if (ret != int(DVAP_REQ_STOP_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_STOP) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding to the stop command")); + return false; + } + } + } while (resp != RT_STOP); + + return true; +} + +bool CDVAPController::setModulation() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + int ret = m_serial.write(DVAP_REQ_MODULATION, DVAP_REQ_MODULATION_LEN); + if (ret != int(DVAP_REQ_MODULATION_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_MODULATION) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding to the modulation command")); + return false; + } + } + } while (resp != RT_MODULATION); + + return true; +} + +bool CDVAPController::setMode() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + int ret = m_serial.write(DVAP_REQ_MODE, DVAP_REQ_MODE_LEN); + if (ret != int(DVAP_REQ_MODE_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_MODE) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding to the mode command")); + return false; + } + } + } while (resp != RT_MODE); + + return true; +} + +bool CDVAPController::setSquelch() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + unsigned char buffer[10U]; + ::memcpy(buffer, DVAP_REQ_SQUELCH, DVAP_REQ_SQUELCH_LEN); + ::memcpy(buffer + 4U, &m_squelch, sizeof(wxInt8)); + + int ret = m_serial.write(buffer, DVAP_REQ_SQUELCH_LEN); + if (ret != int(DVAP_REQ_SQUELCH_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_SQUELCH) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding to the squelch command")); + return false; + } + } + } while (resp != RT_SQUELCH); + + return true; +} + +bool CDVAPController::setPower() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + unsigned char buffer[10U]; + ::memcpy(buffer, DVAP_REQ_POWER, DVAP_REQ_POWER_LEN); + + wxInt16 power = wxINT16_SWAP_ON_BE(m_power); + ::memcpy(buffer + 4U, &power, sizeof(wxInt16)); + + int ret = m_serial.write(buffer, DVAP_REQ_POWER_LEN); + if (ret != int(DVAP_REQ_POWER_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_POWER) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding to the power command")); + return false; + } + } + } while (resp != RT_POWER); + + return true; +} + +bool CDVAPController::setFrequency() +{ + unsigned int count = 0U; + unsigned int length; + RESP_TYPE resp; + do { + int ret = m_serial.write(DVAP_REQ_FREQLIMITS, DVAP_REQ_FREQLIMITS_LEN); + if (ret != int(DVAP_REQ_FREQLIMITS_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_FREQLIMITS) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding to the frequency limits command")); + return false; + } + } + } while (resp != RT_FREQLIMITS); + + wxUint32* pFreq1 = (wxUint32*)(m_buffer + 4U); + wxUint32* pFreq2 = (wxUint32*)(m_buffer + 8U); + + wxUint32 lower = wxUINT32_SWAP_ON_BE(*pFreq1); + wxUint32 upper = wxUINT32_SWAP_ON_BE(*pFreq2); + + wxLogInfo(wxT("DVAP frequency limits are %u Hz to %u Hz"), lower, upper); + + if (m_frequency < lower || m_frequency > upper) { + wxLogError(wxT("The required frequency is out of the range of the DVAP hardware")); + m_serial.close(); + return false; + } + + count = 0U; + do { + unsigned char buffer[10U]; + ::memcpy(buffer, DVAP_REQ_FREQUENCY, DVAP_REQ_FREQUENCY_LEN); + + wxUint32 frequency = wxUINT32_SWAP_ON_BE(m_frequency); + ::memcpy(buffer + 4U, &frequency, sizeof(wxUint32)); + + int ret = m_serial.write(buffer, DVAP_REQ_FREQUENCY_LEN); + if (ret != int(DVAP_REQ_FREQUENCY_LEN)) { + m_serial.close(); + return false; + } + + ::wxMilliSleep(50UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT_FREQUENCY) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVAP is not responding to the frequency command")); + return false; + } + } + } while (resp != RT_FREQUENCY); + + return true; +} + +RESP_TYPE CDVAPController::getResponse(unsigned char *buffer, unsigned int& length) +{ + int ret = m_serial.read(buffer, DVAP_HEADER_LENGTH); + if (ret == 0) + return RT_TIMEOUT; + if (ret != int(DVAP_HEADER_LENGTH)) + return RT_ERROR; + + unsigned int offset = DVAP_HEADER_LENGTH; + + // First byte has gone missing from the operation status message, fix it + if (buffer[0U] == 0x20U && buffer[1U] == 0x90U) { + buffer[0U] = 0x07U; + buffer[1U] = 0x20U; + buffer[2U] = 0x90U; + offset = DVAP_HEADER_LENGTH + 1U; + } + + length = buffer[0U] + (buffer[1U] & 0x1FU) * 256U; + + // Check for silliness + if (length > 50U) { + CUtils::dump(wxT("Bad DVAP header"), buffer, DVAP_HEADER_LENGTH); +#if defined(DVAP_DUMP) + dumpPackets(); +#endif + resync(); + return RT_TIMEOUT; + } + + while (offset < length) { + ret = m_serial.read(buffer + offset, length - offset); + if (ret < 0) + return RT_ERROR; + if (ret > 0) + offset += ret; + if (ret == 0) + Sleep(5UL); + } + + // CUtils::dump(wxT("Received"), buffer, length); + + if (::memcmp(buffer, DVAP_STATUS, 4U) == 0) + return RT_STATE; + else if (::memcmp(buffer, DVAP_GMSK_DATA, 2U) == 0) + return RT_GMSK_DATA; + else if (::memcmp(buffer, DVAP_HEADER, 2U) == 0) + return RT_HEADER; + else if (::memcmp(buffer, DVAP_RESP_HEADER, 2U) == 0) + return RT_HEADER_ACK; + else if (::memcmp(buffer, DVAP_RESP_PTT, 4U) == 0) + return RT_PTT; + else if (::memcmp(buffer, DVAP_ACK, DVAP_ACK_LEN) == 0) + return RT_ACK; + else if (::memcmp(buffer, DVAP_FM_DATA, 2U) == 0) + return RT_FM_DATA; + else if (::memcmp(buffer, DVAP_RESP_START, DVAP_RESP_START_LEN) == 0) + return RT_START; + else if (::memcmp(buffer, DVAP_RESP_STOP, DVAP_RESP_STOP_LEN) == 0) + return RT_STOP; + else if (::memcmp(buffer, DVAP_RESP_NAME, 4U) == 0) + return RT_NAME; + else if (::memcmp(buffer + 1U, DVAP_RESP_SERIAL + 1U, 3U) == 0) + return RT_SERIAL; + else if (::memcmp(buffer, DVAP_RESP_FIRMWARE, 5U) == 0) + return RT_FIRMWARE; + else if (::memcmp(buffer, DVAP_RESP_FREQUENCY, 4U) == 0) + return RT_FREQUENCY; + else if (::memcmp(buffer, DVAP_RESP_FREQLIMITS, 4U) == 0) + return RT_FREQLIMITS; + else if (::memcmp(buffer, DVAP_RESP_MODULATION, DVAP_RESP_MODULATION_LEN) == 0) + return RT_MODULATION; + else if (::memcmp(buffer, DVAP_RESP_MODE, DVAP_RESP_MODE_LEN) == 0) + return RT_MODE; + else if (::memcmp(buffer, DVAP_RESP_POWER, 4U) == 0) + return RT_POWER; + else if (::memcmp(buffer, DVAP_RESP_SQUELCH, 4U) == 0) + return RT_SQUELCH; + else { + CUtils::dump(wxT("Bad DVAP data"), buffer, length); +#if defined(DVAP_DUMP) + dumpPackets(); +#endif + resync(); + return RT_TIMEOUT; + } +} + +void CDVAPController::resync() +{ + wxLogWarning(wxT("Resynchronising the DVAP data stream")); + + unsigned char data[DVAP_STATUS_LEN]; + ::memset(data, 0x00U, DVAP_STATUS_LEN); + + while (::memcmp(data, DVAP_STATUS, 4U) != 0) { + unsigned char c; + int n = m_serial.read(&c, 1U); + if (n > 0) { + data[0U] = data[1U]; + data[1U] = data[2U]; + data[2U] = data[3U]; + data[3U] = data[4U]; + data[4U] = data[5U]; + data[5U] = data[6U]; + data[6U] = c; + + // CUtils::dump(wxT("Resync buffer"), data, DVAP_STATUS_LEN); + } + } + + wxLogMessage(wxT("End resynchronising")); +} + +#if defined(DVAP_DUMP) + +void CDVAPController::storePacket(const unsigned char* data, unsigned int length) +{ + ::memcpy(m_dvapData[m_dvapIndex], data, length); + m_dvapLength[m_dvapIndex] = length; + + m_dvapIndex++; + if (m_dvapIndex >= DVAP_DUMP_LENGTH) + m_dvapIndex = 0U; +} + +void CDVAPController::dumpPackets() +{ + unsigned int n = m_dvapIndex; + unsigned int i = 0U; + while (n != m_dvapIndex) { + if (m_dvapLength[n] > 0U) { + wxString text; + text.Printf(wxT("Packet: %u"), i); + + CUtils::dump(text, m_dvapData[n], m_dvapLength[n]); + } + + i++; + n++; + if (n >= DVAP_DUMP_LENGTH) + n = 0U; + } + + m_dvapIndex = 0U; +} + +#endif diff --git a/Common/DVAPController.h b/Common/DVAPController.h new file mode 100644 index 0000000..0e5881b --- /dev/null +++ b/Common/DVAPController.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DVAPController_H +#define DVAPController_H + +#define DVAP_DUMP + +#include "SerialDataController.h" +#include "RingBuffer.h" +#include "HeaderData.h" +#include "Modem.h" +#include "Utils.h" + +#include + +enum RESP_TYPE { + RT_TIMEOUT, + RT_ERROR, + RT_UNKNOWN, + RT_NAME, + RT_SERIAL, + RT_FIRMWARE, + RT_START, + RT_STOP, + RT_MODULATION, + RT_MODE, + RT_SQUELCH, + RT_POWER, + RT_FREQUENCY, + RT_FREQLIMITS, + RT_STATE, + RT_PTT, + RT_ACK, + RT_HEADER, + RT_HEADER_ACK, + RT_GMSK_DATA, + RT_FM_DATA +}; + +class CDVAPController : public CModem { +public: + CDVAPController(const wxString& port, unsigned int frequency, int power, int squelch); + virtual ~CDVAPController(); + + virtual void* Entry(); + + virtual bool start(); + + virtual unsigned int getSpace(); + virtual bool isTXReady(); + + virtual bool getSquelch() const; + virtual int getSignal() const; + + virtual bool writeHeader(const CHeaderData& header); + virtual bool writeData(const unsigned char* data, unsigned int length, bool end); + +private: + CSerialDataController m_serial; + wxUint32 m_frequency; + wxInt16 m_power; + wxInt8 m_squelch; + bool m_squelchOpen; + int m_signal; + unsigned char* m_buffer; + wxUint16 m_streamId; + wxUint8 m_framePos; + wxUint8 m_seq; + CRingBuffer m_txData; +#if defined(DVAP_DUMP) + unsigned char** m_dvapData; + unsigned int* m_dvapLength; + unsigned int m_dvapIndex; +#endif + + bool getName(); + bool getFirmware(); + bool getSerial(); + + bool setModulation(); + bool setMode(); + + bool setSquelch(); + bool setPower(); + bool setFrequency(); + + bool startDVAP(); + bool stopDVAP(); + + void writePoll(); + + void resync(); + + RESP_TYPE getResponse(unsigned char* buffer, unsigned int& length); + +#if defined(DVAP_DUMP) + void storePacket(const unsigned char* data, unsigned int length); + void dumpPackets(); +#endif +}; + +#endif diff --git a/Common/DVMegaController.cpp b/Common/DVMegaController.cpp new file mode 100644 index 0000000..f5f1e02 --- /dev/null +++ b/Common/DVMegaController.cpp @@ -0,0 +1,1149 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CCITTChecksumReverse.h" +#include "DVMegaController.h" +#include "CCITTChecksum.h" +#include "DStarDefines.h" +#include "Timer.h" + +#if defined(__WINDOWS__) +#include +#else +#include +#endif + +const unsigned char DVRPTR_HEADER_LENGTH = 5U; + +const unsigned char DVRPTR_FRAME_START = 0xD0U; + +const unsigned char DVRPTR_GET_STATUS = 0x10U; +const unsigned char DVRPTR_GET_VERSION = 0x11U; +const unsigned char DVRPTR_GET_SERIAL = 0x12U; +const unsigned char DVRPTR_GET_CONFIG = 0x13U; +const unsigned char DVRPTR_SET_CONFIG = 0x14U; +const unsigned char DVRPTR_RXPREAMBLE = 0x15U; +const unsigned char DVRPTR_START = 0x16U; +const unsigned char DVRPTR_HEADER = 0x17U; +const unsigned char DVRPTR_RXSYNC = 0x18U; +const unsigned char DVRPTR_DATA = 0x19U; +const unsigned char DVRPTR_EOT = 0x1AU; +const unsigned char DVRPTR_RXLOST = 0x1BU; +const unsigned char DVRPTR_MSG_RSVD1 = 0x1CU; +const unsigned char DVRPTR_MSG_RSVD2 = 0x1DU; +const unsigned char DVRPTR_MSG_RSVD3 = 0x1EU; +const unsigned char DVRPTR_SET_TESTMDE = 0x1FU; + +const unsigned char DVRPTR_ACK = 0x06U; +const unsigned char DVRPTR_NAK = 0x15U; + +const unsigned int MAX_RESPONSES = 30U; + +const unsigned int BUFFER_LENGTH = 200U; + +CDVMegaController::CDVMegaController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, unsigned int txDelay) : +CModem(), +m_port(port), +m_path(path), +m_rxInvert(rxInvert), +m_txInvert(txInvert), +m_txDelay(txDelay), +m_rxFrequency(0U), +m_txFrequency(0U), +m_power(0U), +m_serial(port, SERIAL_115200, true), +m_buffer(NULL), +m_txData(1000U), +m_txCounter(0U), +m_pktCounter(0U), +m_rx(false), +m_txSpace(0U), +m_txEnabled(false), +m_checksum(false) +{ + wxASSERT(!port.IsEmpty()); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDVMegaController::CDVMegaController(const wxString& port, const wxString& path, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power) : +CModem(), +m_port(port), +m_path(path), +m_rxInvert(false), +m_txInvert(false), +m_txDelay(txDelay), +m_rxFrequency(rxFrequency), +m_txFrequency(txFrequency), +m_power(power), +m_serial(port, SERIAL_115200, true), +m_buffer(NULL), +m_txData(1000U), +m_txCounter(0U), +m_pktCounter(0U), +m_rx(false), +m_txSpace(0U), +m_txEnabled(false), +m_checksum(false) +{ + wxASSERT(!port.IsEmpty()); + wxASSERT((rxFrequency >= 144000000U && rxFrequency <= 148000000U) || + (rxFrequency >= 420000000U && rxFrequency <= 450000000U)); + wxASSERT((txFrequency >= 144000000U && txFrequency <= 148000000U) || + (txFrequency >= 420000000U && txFrequency <= 450000000U)); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDVMegaController::~CDVMegaController() +{ + delete[] m_buffer; +} + +bool CDVMegaController::start() +{ + findPort(); + + bool ret = openModem(); + if (!ret) + return false; + + findPath(); + + Create(); + SetPriority(100U); + Run(); + + return true; +} + +void* CDVMegaController::Entry() +{ + wxLogMessage(wxT("Starting DVMEGA Controller thread")); + + // Clock every 5ms-ish + CTimer pollTimer(200U, 0U, 100U); + pollTimer.start(); + + unsigned char writeType = DSMTT_NONE; + unsigned char writeLength = 0U; + unsigned char* writeBuffer = new unsigned char[BUFFER_LENGTH]; + + unsigned int space = 0U; + + while (!m_stopped) { + // Poll the modem status every 100ms + if (pollTimer.hasExpired()) { + bool ret = readStatus(); + if (!ret) { + ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DVMEGA Controller thread")); + return NULL; + } + } + + pollTimer.start(); + } + + unsigned int length; + RESP_TYPE_MEGA type = getResponse(m_buffer, length); + + switch (type) { + case RTM_TIMEOUT: + break; + + case RTM_ERROR: { + bool ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DVMEGA Controller thread")); + return NULL; + } + } + break; + + case RTM_RXPREAMBLE: + // wxLogMessage(wxT("RT_PREAMBLE")); + break; + + case RTM_START: + // wxLogMessage(wxT("RT_START")); + break; + + case RTM_HEADER: + // CUtils::dump(wxT("RT_HEADER"), m_buffer, length); + if (length == 7U) { + if (m_buffer[4U] == DVRPTR_NAK) + wxLogWarning(wxT("Received a header NAK from the DVMEGA")); + } else { + bool correct = (m_buffer[5U] & 0x80U) == 0x00U; + if (correct) { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); + + m_rx = true; + } + } + break; + + case RTM_RXSYNC: + // wxLogMessage(wxT("RT_RXSYNC")); + break; + + case RTM_DATA: + // CUtils::dump(wxT("RT_DATA"), m_buffer, length); + if (length == 7U) { + if (m_buffer[4U] == DVRPTR_NAK) + wxLogWarning(wxT("Received a data NAK from the DVMEGA")); + } else { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 8U, DV_FRAME_LENGTH_BYTES); + + m_rx = true; + } + break; + + case RTM_EOT: { + // wxLogMessage(wxT("RT_EOT")); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + break; + + case RTM_RXLOST: { + // wxLogMessage(wxT("RT_LOST")); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_LOST; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + break; + + case RTM_GET_STATUS: { + m_txEnabled = (m_buffer[4U] & 0x02U) == 0x02U; + m_checksum = (m_buffer[4U] & 0x08U) == 0x08U; + m_tx = (m_buffer[5U] & 0x02U) == 0x02U; + m_txSpace = m_buffer[8U]; + space = m_txSpace - m_buffer[9U]; + // CUtils::dump(wxT("GET_STATUS"), m_buffer, length); + // wxLogMessage(wxT("PTT=%d tx=%u space=%u cksum=%d, tx enabled=%d"), int(m_tx), m_txSpace, space, int(m_checksum), int(m_txEnabled)); + } + break; + + // These should not be received in this loop, but don't complain if we do + case RTM_GET_VERSION: + case RTM_GET_SERIAL: + case RTM_GET_CONFIG: + break; + + default: + wxLogMessage(wxT("Unknown message, type: %02X"), m_buffer[3U]); + CUtils::dump(wxT("Buffer dump"), m_buffer, length); + break; + } + + if (space > 0U) { + if (writeType == DSMTT_NONE && m_txData.hasData()) { + wxMutexLocker locker(m_mutex); + + m_txData.getData(&writeType, 1U); + m_txData.getData(&writeLength, 1U); + m_txData.getData(writeBuffer, writeLength); + } + + // Only send the start when the TX is off + if (!m_tx && writeType == DSMTT_START) { + // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing the header to the DVMEGA")); + + writeType = DSMTT_NONE; + space--; + } + + if (space > 4U && writeType == DSMTT_HEADER) { + // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing the header to the DVMEGA")); + + writeType = DSMTT_NONE; + space -= 4U; + } + + if (writeType == DSMTT_DATA || writeType == DSMTT_EOT) { + // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing data to the DVMEGA")); + + writeType = DSMTT_NONE; + space--; + } + } + + Sleep(5UL); + + pollTimer.clock(); + } + + wxLogMessage(wxT("Stopping DVMEGA Controller thread")); + + setEnabled(false); + + delete[] writeBuffer; + + m_serial.close(); + + return NULL; +} + +bool CDVMegaController::writeHeader(const CHeaderData& header) +{ + if (!m_txEnabled) + return false; + + bool ret = m_txData.hasSpace(64U); + if (!ret) { + wxLogWarning(wxT("No space to write the header")); + return false; + } + + m_txCounter++; + if (m_txCounter == 0U) + m_txCounter = 1U; + + unsigned char buffer1[10U]; + + buffer1[0U] = DVRPTR_FRAME_START; + + buffer1[1U] = 0x03U; + buffer1[2U] = 0x00U; + + buffer1[3U] = DVRPTR_START; + + buffer1[4U] = m_txCounter; + buffer1[5U] = 0x00U; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer1 + 0U, 6U); + cksum.result(buffer1 + 6U); + } else { + buffer1[6U] = 0x00U; + buffer1[7U] = 0x0BU; + } + + unsigned char buffer2[60U]; + + buffer2[0U] = DVRPTR_FRAME_START; + + buffer2[1U] = 0x2FU; + buffer2[2U] = 0x00U; + + buffer2[3U] = DVRPTR_HEADER; + + buffer2[4U] = m_txCounter; + buffer2[5U] = 0x00U; + + buffer2[6U] = 0x00U; + buffer2[7U] = 0x00U; + + ::memset(buffer2 + 8U, ' ', RADIO_HEADER_LENGTH_BYTES); + + buffer2[8U] = header.getFlag1(); + buffer2[9U] = header.getFlag2(); + buffer2[10U] = header.getFlag3(); + + wxString rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 11U] = rpt2.GetChar(i); + + wxString rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 19U] = rpt1.GetChar(i); + + wxString your = header.getYourCall(); + for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 27U] = your.GetChar(i); + + wxString my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 35U] = my1.GetChar(i); + + wxString my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer2[i + 43U] = my2.GetChar(i); + + CCCITTChecksumReverse cksum1; + cksum1.update(buffer2 + 8U, RADIO_HEADER_LENGTH_BYTES - 2U); + cksum1.result(buffer2 + 47U); + + buffer2[49U] = 0x00U; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer2 + 0U, 50U); + cksum.result(buffer2 + 50U); + } else { + buffer2[50U] = 0x00U; + buffer2[51U] = 0x0BU; + } + + m_pktCounter = 0U; + + wxMutexLocker locker(m_mutex); + + unsigned char type1 = DSMTT_START; + m_txData.addData(&type1, 1U); + + unsigned char len1 = 8U; + m_txData.addData(&len1, 1U); + + m_txData.addData(buffer1, 8U); + + unsigned char type2 = DSMTT_HEADER; + m_txData.addData(&type2, 1U); + + unsigned char len2 = 52U; + m_txData.addData(&len2, 1U); + + m_txData.addData(buffer2, 52U); + + return true; +} + +bool CDVMegaController::writeData(const unsigned char* data, unsigned int, bool end) +{ + if (!m_txEnabled) + return false; + + bool ret = m_txData.hasSpace(26U); + if (!ret) { + wxLogWarning(wxT("No space to write data")); + return false; + } + + unsigned char buffer[30U]; + + if (end) { + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x03U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_EOT; + + buffer[4U] = m_txCounter; + buffer[5U] = 0xFFU; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 6U); + cksum.result(buffer + 6U); + } else { + buffer[6U] = 0x00U; + buffer[7U] = 0x0BU; + } + + wxMutexLocker locker(m_mutex); + + unsigned char type = DSMTT_EOT; + m_txData.addData(&type, 1U); + + unsigned char len = 8U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, 8U); + + return true; + } + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x13U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_DATA; + + buffer[4U] = m_txCounter; + buffer[5U] = m_pktCounter; + + m_pktCounter++; + if (m_pktCounter >= m_txSpace) + m_pktCounter = 0U; + + buffer[6U] = 0x00U; + buffer[7U] = 0x00U; + + ::memcpy(buffer + 8U, data, DV_FRAME_LENGTH_BYTES); + + buffer[20U] = 0x00U; + buffer[21U] = 0x00U; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 22U); + cksum.result(buffer + 22U); + } else { + buffer[22U] = 0x00U; + buffer[23U] = 0x0BU; + } + + wxMutexLocker locker(m_mutex); + + unsigned char type = DSMTT_DATA; + m_txData.addData(&type, 1U); + + unsigned char len = 24U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, 24U); + + return true; +} + +unsigned int CDVMegaController::getSpace() +{ + return m_txData.freeSpace() / 26U; +} + +bool CDVMegaController::isTXReady() +{ + if (m_tx) + return false; + + return m_txData.isEmpty(); +} + +bool CDVMegaController::readVersion() +{ + for (unsigned int i = 0U; i < 6U; i++) { + unsigned char buffer[10U]; + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x01U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_GET_VERSION; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 4U); + cksum.result(buffer + 4U); + } else { + buffer[4U] = 0x00U; + buffer[5U] = 0x0BU; + } + + // CUtils::dump(wxT("Written"), buffer, 6U); + + int ret = m_serial.write(buffer, 6U); + if (ret != 6) + return false; + + for (unsigned int count = 0U; count < MAX_RESPONSES; count++) { + ::wxMilliSleep(10UL); + + unsigned int length; + RESP_TYPE_MEGA resp = getResponse(m_buffer, length); + if (resp == RTM_GET_VERSION) { + wxString firmware; + if ((m_buffer[4U] & 0x0FU) > 0x00U) + firmware.Printf(wxT("%u.%u%u%c"), (m_buffer[5U] & 0xF0U) >> 4, m_buffer[5U] & 0x0FU, (m_buffer[4U] & 0xF0U) >> 4, (m_buffer[4U] & 0x0FU) + wxT('a') - 1U); + else + firmware.Printf(wxT("%u.%u%u"), (m_buffer[5U] & 0xF0U) >> 4, m_buffer[5U] & 0x0FU, (m_buffer[4U] & 0xF0U) >> 4); + + wxString hardware((char*)(m_buffer + 6U), wxConvLocal, length - DVRPTR_HEADER_LENGTH - 3U); + + wxLogInfo(wxT("DVMEGA Firmware version: %s, hardware: %s"), firmware.c_str(), hardware.c_str()); + + return true; + } + } + + ::wxSleep(1); + } + + wxLogError(wxT("Unable to read the firmware version after six attempts")); + + return false; +} + +bool CDVMegaController::readStatus() +{ + unsigned char buffer[10U]; + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x01U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_GET_STATUS; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 4U); + cksum.result(buffer + 4U); + } else { + buffer[4U] = 0x00U; + buffer[5U] = 0x0BU; + } + + return m_serial.write(buffer, 6U) == 6; +} + +bool CDVMegaController::setConfig() +{ + unsigned char buffer[20U]; + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x07U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_SET_CONFIG; + + buffer[4U] = 0xC0U; // Physical layer + + buffer[5U] = 0x04U; // Block length + + buffer[6U] = 0x00U; + if (m_rxInvert) + buffer[6U] |= 0x01U; + if (m_txInvert) + buffer[6U] |= 0x02U; + + wxUint16* txDelay = (wxUint16*)(buffer + 8U); + *txDelay = wxUINT16_SWAP_ON_BE(m_txDelay); + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 10U); + cksum.result(buffer + 10U); + } else { + buffer[10U] = 0x00U; + buffer[11U] = 0x0BU; + } + + // CUtils::dump(wxT("Written"), buffer, 12U); + + int ret = m_serial.write(buffer, 12U); + if (ret != 12) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_MEGA resp; + do { + + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RTM_SET_CONFIG) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVMEGA is not responding to the SET_CONFIG command")); + return false; + } + } + } while (resp != RTM_SET_CONFIG); + + // CUtils::dump(wxT("Response"), m_buffer, length); + + unsigned char type = m_buffer[4U]; + if (type != DVRPTR_ACK) { + wxLogError(wxT("Received a NAK to the SET_CONFIG command from the modem")); + return false; + } + + return true; +} + +bool CDVMegaController::setFrequencyAndPower() +{ + unsigned char buffer[25U]; + + ::memset(buffer, 0x00U, 21U); + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x10U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_SET_CONFIG; + + buffer[4U] = 0xC1U; // RF layer + + buffer[5U] = 0x0CU; // Block length + + wxUint32 rxFreq = wxUINT32_SWAP_ON_BE(wxUint32(m_rxFrequency)); + wxUint32 txFreq = wxUINT32_SWAP_ON_BE(wxUint32(m_txFrequency)); + + ::memcpy(buffer + 7U, &rxFreq, sizeof(wxUint32)); + ::memcpy(buffer + 11U, &txFreq, sizeof(wxUint32)); + + buffer[16U] = (m_power * 64U) / 100U; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 19U); + cksum.result(buffer + 19U); + } else { + buffer[19U] = 0x00U; + buffer[20U] = 0x0BU; + } + + // CUtils::dump(wxT("Written"), buffer, 21U); + + int ret = m_serial.write(buffer, 21U); + if (ret != 21) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_MEGA resp; + do { + + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RTM_SET_CONFIG) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVMEGA is not responding to the SET_CONFIG command")); + return false; + } + } + } while (resp != RTM_SET_CONFIG); + + // CUtils::dump(wxT("Response"), m_buffer, length); + + unsigned char type = m_buffer[4U]; + if (type != DVRPTR_ACK) { + wxLogError(wxT("Received a NAK to the SET_CONFIG command from the modem")); + return false; + } + + return true; +} + +bool CDVMegaController::setEnabled(bool enable) +{ + unsigned char buffer[10U]; + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x02U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_GET_STATUS; + + // Enable RX, TX, and Watchdog + if (enable) + buffer[4U] = 0x01U | 0x02U | 0x04U; + else + buffer[4U] = 0x00U; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 5U); + cksum.result(buffer + 5U); + } else { + buffer[5U] = 0x00U; + buffer[6U] = 0x0BU; + } + + // CUtils::dump(wxT("Written"), buffer, 7U); + + int ret = m_serial.write(buffer, 7U); + if (ret != 7) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_MEGA resp; + do { + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RTM_GET_STATUS) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DVMEGA is not responding to the SET_STATUS command")); + return false; + } + } + } while (resp != RTM_GET_STATUS); + + // CUtils::dump(wxT("Response"), m_buffer, length); + + unsigned char type = m_buffer[4U]; + if (type != DVRPTR_ACK) { + wxLogError(wxT("Received a NAK to the SET_STATUS command from the modem")); + return false; + } + + return true; +} + +RESP_TYPE_MEGA CDVMegaController::getResponse(unsigned char *buffer, unsigned int& length) +{ + // Get the start of the frame or nothing at all + int ret = m_serial.read(buffer, 1U); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DVMEGA")); + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + if (buffer[0U] != DVRPTR_FRAME_START) + return RTM_TIMEOUT; + + unsigned int offset = 1U; + + while (offset < DVRPTR_HEADER_LENGTH) { + ret = m_serial.read(buffer + offset, DVRPTR_HEADER_LENGTH - offset); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DVMEGA")); + return RTM_ERROR; + } + + if (ret > 0) + offset += ret; + + if (ret == 0) + Sleep(5UL); + } + + length = buffer[1U] + buffer[2U] * 256U; + + if (length >= 100U) { + wxLogError(wxT("Invalid data received from the DVMEGA")); + return RTM_ERROR; + } + + // Remove the response bit + unsigned int type = buffer[3U] & 0x7FU; + + offset = 0U; + + while (offset < length) { + ret = m_serial.read(buffer + offset + DVRPTR_HEADER_LENGTH, length - offset); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DVMEGA")); + return RTM_ERROR; + } + + if (ret > 0) + offset += ret; + + if (ret == 0) + Sleep(5UL); + } + + length += DVRPTR_HEADER_LENGTH; + + // CUtils::dump(wxT("Received"), buffer, length); + + switch (type) { + case DVRPTR_GET_STATUS: + return RTM_GET_STATUS; + case DVRPTR_GET_VERSION: + return RTM_GET_VERSION; + case DVRPTR_GET_SERIAL: + return RTM_GET_SERIAL; + case DVRPTR_GET_CONFIG: + return RTM_GET_CONFIG; + case DVRPTR_SET_CONFIG: + return RTM_SET_CONFIG; + case DVRPTR_RXPREAMBLE: + return RTM_RXPREAMBLE; + case DVRPTR_START: + return RTM_START; + case DVRPTR_HEADER: + return RTM_HEADER; + case DVRPTR_RXSYNC: + return RTM_RXSYNC; + case DVRPTR_DATA: + return RTM_DATA; + case DVRPTR_EOT: + return RTM_EOT; + case DVRPTR_RXLOST: + return RTM_RXLOST; + case DVRPTR_SET_TESTMDE: + return RTM_SET_TESTMDE; + default: + return RTM_UNKNOWN; + } +} + +wxString CDVMegaController::getPath() const +{ + return m_path; +} + +bool CDVMegaController::findPort() +{ + if (m_path.IsEmpty()) + return false; + +#if defined(__WINDOWS__) +#else + wxDir dir; + bool ret1 = dir.Open(wxT("/sys/class/tty")); + if (!ret1) { + wxLogError(wxT("Cannot open directory /sys/class/tty")); + return false; + } + + wxString fileName; + ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); + while (ret1) { + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + ::strcat(cpath, "/device"); + ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret2); + } else { + // Get all but the last section + wxString fullPath = wxString(symlink, wxConvLocal, ret2); + path = fullPath.BeforeLast(wxT('/')); + } + + if (path.IsSameAs(m_path)) { + m_port.Printf(wxT("/dev/%s"), fileName.c_str()); + + wxLogMessage(wxT("Found modem port of %s based on the path"), m_port.c_str()); + + return true; + } + + ret1 = dir.GetNext(&fileName); + } +#endif + + return false; +} + +bool CDVMegaController::findPath() +{ +#if defined(__WINDOWS__) +#ifdef notdef + GUID guids[5U]; + + DWORD count; + BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); + if (!res) { + wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); + return false; + } + + for (DWORD i = 0U; i < count; i++) { + HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + if (devInfo == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); + return false; + } + + SP_DEVICE_INTERFACE_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { + // Find the required length of the device structure + DWORD length; + ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Get the detailed data into the newly allocated device structure + DWORD required; + res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); + if (!res) { + wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ::free(detailData); + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + } + + return false; +#endif +#else + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), m_port.Mid(5U).c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + ::strcat(cpath, "/device"); + ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret); + } else { + wxString fullPath = wxString(symlink, wxConvLocal, ret); + path = fullPath.BeforeLast(wxT('/')); + } + + if (m_path.IsEmpty()) + wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + + m_path = path; +#endif + + return true; +} + +bool CDVMegaController::findModem() +{ + m_serial.close(); + + // Tell the repeater that the signal has gone away + if (m_rx) { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + + unsigned int count = 0U; + + // Purge the transmit buffer every 500ms to avoid overflow, but only try and reopen the modem every 2s + while (!m_stopped) { + count++; + if (count >= 4U) { + wxLogMessage(wxT("Trying to reopen the modem")); + + bool ret = findPort(); + if (ret) { + ret = openModem(); + if (ret) + return true; + } + + count = 0U; + } + + Sleep(500UL); + } + + return false; +} + +bool CDVMegaController::openModem() +{ + bool ret = m_serial.open(); + if (!ret) + return false; + + ret = readVersion(); + if (!ret) { + m_serial.close(); + return false; + } + + ret = setConfig(); + if (!ret) { + m_serial.close(); + return false; + } + + if (m_rxFrequency != 0U && m_txFrequency != 0U) { + ret = setFrequencyAndPower(); + if (!ret) { + m_serial.close(); + return false; + } + } + + ret = setEnabled(true); + if (!ret) { + m_serial.close(); + return false; + } + + return true; +} + diff --git a/Common/DVMegaController.h b/Common/DVMegaController.h new file mode 100644 index 0000000..9ae29ee --- /dev/null +++ b/Common/DVMegaController.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DVMegaController_H +#define DVMegaController_H + +#include "SerialDataController.h" +#include "RingBuffer.h" +#include "Modem.h" +#include "Utils.h" + +#include + +enum RESP_TYPE_MEGA { + RTM_TIMEOUT, + RTM_ERROR, + RTM_UNKNOWN, + RTM_GET_STATUS, + RTM_GET_VERSION, + RTM_GET_SERIAL, + RTM_GET_CONFIG, + RTM_SET_CONFIG, + RTM_RXPREAMBLE, + RTM_START, + RTM_HEADER, + RTM_RXSYNC, + RTM_DATA, + RTM_EOT, + RTM_RXLOST, + RTM_SET_TESTMDE +}; + +class CDVMegaController : public CModem { +public: + CDVMegaController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, unsigned int txDelay); + CDVMegaController(const wxString& port, const wxString& path, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power); + virtual ~CDVMegaController(); + + virtual void* Entry(); + + virtual bool start(); + + virtual unsigned int getSpace(); + virtual bool isTXReady(); + + virtual bool writeHeader(const CHeaderData& header); + virtual bool writeData(const unsigned char* data, unsigned int length, bool end); + + virtual wxString getPath() const; + +private: + wxString m_port; + wxString m_path; + bool m_rxInvert; + bool m_txInvert; + unsigned int m_txDelay; + unsigned int m_rxFrequency; + unsigned int m_txFrequency; + unsigned int m_power; + CSerialDataController m_serial; + unsigned char* m_buffer; + CRingBuffer m_txData; + unsigned char m_txCounter; + unsigned char m_pktCounter; + bool m_rx; + unsigned int m_txSpace; + bool m_txEnabled; + bool m_checksum; + + bool readVersion(); + bool readStatus(); + bool setConfig(); + bool setFrequencyAndPower(); + bool setEnabled(bool enable); + + RESP_TYPE_MEGA getResponse(unsigned char* buffer, unsigned int& length); + + bool findPort(); + bool findPath(); + + bool findModem(); + bool openModem(); +}; + +#endif + diff --git a/Common/DVRPTRV1Controller.cpp b/Common/DVRPTRV1Controller.cpp new file mode 100644 index 0000000..f0cbff1 --- /dev/null +++ b/Common/DVRPTRV1Controller.cpp @@ -0,0 +1,1042 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CCITTChecksumReverse.h" +#include "DVRPTRV1Controller.h" +#include "CCITTChecksum.h" +#include "DStarDefines.h" +#include "Timer.h" + +#if defined(__WINDOWS__) +#include +#else +#include +#endif + +const unsigned char DVRPTR_HEADER_LENGTH = 5U; + +const unsigned char DVRPTR_FRAME_START = 0xD0U; + +const unsigned char DVRPTR_GET_STATUS = 0x10U; +const unsigned char DVRPTR_GET_VERSION = 0x11U; +const unsigned char DVRPTR_GET_SERIAL = 0x12U; +const unsigned char DVRPTR_GET_CONFIG = 0x13U; +const unsigned char DVRPTR_SET_CONFIG = 0x14U; +const unsigned char DVRPTR_RXPREAMBLE = 0x15U; +const unsigned char DVRPTR_START = 0x16U; +const unsigned char DVRPTR_HEADER = 0x17U; +const unsigned char DVRPTR_RXSYNC = 0x18U; +const unsigned char DVRPTR_DATA = 0x19U; +const unsigned char DVRPTR_EOT = 0x1AU; +const unsigned char DVRPTR_RXLOST = 0x1BU; +const unsigned char DVRPTR_MSG_RSVD1 = 0x1CU; +const unsigned char DVRPTR_MSG_RSVD2 = 0x1DU; +const unsigned char DVRPTR_MSG_RSVD3 = 0x1EU; +const unsigned char DVRPTR_SET_TESTMDE = 0x1FU; + +const unsigned char DVRPTR_ACK = 0x06U; +const unsigned char DVRPTR_NAK = 0x15U; + +const unsigned int MAX_RESPONSES = 30U; + +const unsigned int BUFFER_LENGTH = 200U; + +CDVRPTRV1Controller::CDVRPTRV1Controller(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay) : +CModem(), +m_port(port), +m_path(path), +m_rxInvert(rxInvert), +m_txInvert(txInvert), +m_channel(channel), +m_modLevel(modLevel), +m_txDelay(txDelay), +m_serial(port, SERIAL_115200), +m_buffer(NULL), +m_txData(1000U), +m_txCounter(0U), +m_pktCounter(0U), +m_rx(false), +m_txSpace(0U), +m_txEnabled(false), +m_checksum(false) +{ + wxASSERT(!port.IsEmpty()); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDVRPTRV1Controller::~CDVRPTRV1Controller() +{ + delete[] m_buffer; +} + +bool CDVRPTRV1Controller::start() +{ + findPort(); + + bool ret = openModem(); + if (!ret) + return false; + + findPath(); + + Create(); + SetPriority(100U); + Run(); + + return true; +} + +void* CDVRPTRV1Controller::Entry() +{ + wxLogMessage(wxT("Starting DV-RPTR1 Modem Controller thread")); + + // Clock every 5ms-ish + CTimer pollTimer(200U, 0U, 100U); + pollTimer.start(); + + unsigned char writeType = DSMTT_NONE; + unsigned char writeLength = 0U; + unsigned char* writeBuffer = new unsigned char[BUFFER_LENGTH]; + + unsigned int space = 0U; + + while (!m_stopped) { + // Poll the modem status every 100ms + if (pollTimer.hasExpired()) { + bool ret = readStatus(); + if (!ret) { + ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DV-RPTR1 Modem Controller thread")); + return NULL; + } + } + + pollTimer.start(); + } + + unsigned int length; + RESP_TYPE_V1 type = getResponse(m_buffer, length); + + switch (type) { + case RT1_TIMEOUT: + break; + + case RT1_ERROR: { + bool ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DV-RPTR1 Modem Controller thread")); + return NULL; + } + } + break; + + case RT1_RXPREAMBLE: + // wxLogMessage(wxT("RT_PREAMBLE")); + break; + + case RT1_START: + // wxLogMessage(wxT("RT_START")); + break; + + case RT1_HEADER: + // CUtils::dump(wxT("RT_HEADER"), m_buffer, length); + if (length == 7U) { + if (m_buffer[4U] == DVRPTR_NAK) + wxLogWarning(wxT("Received a header NAK from the modem")); + } else { + bool correct = (m_buffer[5U] & 0x80U) == 0x00U; + if (correct) { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); + + m_rx = true; + } + } + break; + + case RT1_RXSYNC: + // wxLogMessage(wxT("RT_RXSYNC")); + break; + + case RT1_DATA: + // CUtils::dump(wxT("RT_DATA"), m_buffer, length); + if (length == 7U) { + if (m_buffer[4U] == DVRPTR_NAK) + wxLogWarning(wxT("Received a data NAK from the modem")); + } else { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 8U, DV_FRAME_LENGTH_BYTES); + + m_rx = true; + } + break; + + case RT1_EOT: { + // wxLogMessage(wxT("RT_EOT")); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + break; + + case RT1_RXLOST: { + // wxLogMessage(wxT("RT_LOST")); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_LOST; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + break; + + case RT1_GET_STATUS: { + m_txEnabled = (m_buffer[4U] & 0x02U) == 0x02U; + m_checksum = (m_buffer[4U] & 0x08U) == 0x08U; + m_tx = (m_buffer[5U] & 0x02U) == 0x02U; + m_txSpace = m_buffer[8U]; + space = m_txSpace - m_buffer[9U]; + // CUtils::dump(wxT("GET_STATUS"), m_buffer, length); + // wxLogMessage(wxT("PTT=%d tx=%u space=%u cksum=%d, tx enabled=%d"), int(m_tx), m_txSpace, space, int(m_checksum), int(m_txEnabled)); + } + break; + + // These should not be received in this loop, but don't complain if we do + case RT1_GET_VERSION: + case RT1_GET_SERIAL: + case RT1_GET_CONFIG: + break; + + default: + wxLogMessage(wxT("Unknown message, type: %02X"), m_buffer[3U]); + CUtils::dump(wxT("Buffer dump"), m_buffer, length); + break; + } + + if (space > 0U) { + if (writeType == DSMTT_NONE && m_txData.hasData()) { + wxMutexLocker locker(m_mutex); + + m_txData.getData(&writeType, 1U); + m_txData.getData(&writeLength, 1U); + m_txData.getData(writeBuffer, writeLength); + } + + // Only send the start when the TX is off + if (!m_tx && writeType == DSMTT_START) { + // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing the header to the DV-RPTR modem")); + + writeType = DSMTT_NONE; + space--; + } + + if (space > 4U && writeType == DSMTT_HEADER) { + // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing the header to the DV-RPTR modem")); + + writeType = DSMTT_NONE; + space -= 4U; + } + + if (writeType == DSMTT_DATA || writeType == DSMTT_EOT) { + // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing data to the DV-RPTR modem")); + + writeType = DSMTT_NONE; + space--; + } + } + + Sleep(5UL); + + pollTimer.clock(); + } + + wxLogMessage(wxT("Stopping DV-RPTR1 Modem Controller thread")); + + setEnabled(false); + + delete[] writeBuffer; + + m_serial.close(); + + return NULL; +} + +bool CDVRPTRV1Controller::writeHeader(const CHeaderData& header) +{ + if (!m_txEnabled) + return false; + + bool ret = m_txData.hasSpace(64U); + if (!ret) { + wxLogWarning(wxT("No space to write the header")); + return false; + } + + m_txCounter++; + if (m_txCounter == 0U) + m_txCounter = 1U; + + unsigned char buffer1[10U]; + + buffer1[0U] = DVRPTR_FRAME_START; + + buffer1[1U] = 0x03U; + buffer1[2U] = 0x00U; + + buffer1[3U] = DVRPTR_START; + + buffer1[4U] = m_txCounter; + buffer1[5U] = 0x00U; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer1 + 0U, 6U); + cksum.result(buffer1 + 6U); + } else { + buffer1[6U] = 0x00U; + buffer1[7U] = 0x0BU; + } + + unsigned char buffer2[60U]; + + buffer2[0U] = DVRPTR_FRAME_START; + + buffer2[1U] = 0x2FU; + buffer2[2U] = 0x00U; + + buffer2[3U] = DVRPTR_HEADER; + + buffer2[4U] = m_txCounter; + buffer2[5U] = 0x00U; + + buffer2[6U] = 0x00U; + buffer2[7U] = 0x00U; + + ::memset(buffer2 + 8U, ' ', RADIO_HEADER_LENGTH_BYTES); + + buffer2[8U] = header.getFlag1(); + buffer2[9U] = header.getFlag2(); + buffer2[10U] = header.getFlag3(); + + wxString rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 11U] = rpt2.GetChar(i); + + wxString rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 19U] = rpt1.GetChar(i); + + wxString your = header.getYourCall(); + for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 27U] = your.GetChar(i); + + wxString my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 35U] = my1.GetChar(i); + + wxString my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer2[i + 43U] = my2.GetChar(i); + + CCCITTChecksumReverse cksum1; + cksum1.update(buffer2 + 8U, RADIO_HEADER_LENGTH_BYTES - 2U); + cksum1.result(buffer2 + 47U); + + buffer2[49U] = 0x00U; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer2 + 0U, 50U); + cksum.result(buffer2 + 50U); + } else { + buffer2[50U] = 0x00U; + buffer2[51U] = 0x0BU; + } + + m_pktCounter = 0U; + + wxMutexLocker locker(m_mutex); + + unsigned char type1 = DSMTT_START; + m_txData.addData(&type1, 1U); + + unsigned char len1 = 8U; + m_txData.addData(&len1, 1U); + + m_txData.addData(buffer1, 8U); + + unsigned char type2 = DSMTT_HEADER; + m_txData.addData(&type2, 1U); + + unsigned char len2 = 52U; + m_txData.addData(&len2, 1U); + + m_txData.addData(buffer2, 52U); + + return true; +} + +bool CDVRPTRV1Controller::writeData(const unsigned char* data, unsigned int, bool end) +{ + if (!m_txEnabled) + return false; + + bool ret = m_txData.hasSpace(26U); + if (!ret) { + wxLogWarning(wxT("No space to write data")); + return false; + } + + unsigned char buffer[30U]; + + if (end) { + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x03U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_EOT; + + buffer[4U] = m_txCounter; + buffer[5U] = 0xFFU; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 6U); + cksum.result(buffer + 6U); + } else { + buffer[6U] = 0x00U; + buffer[7U] = 0x0BU; + } + + wxMutexLocker locker(m_mutex); + + unsigned char type = DSMTT_EOT; + m_txData.addData(&type, 1U); + + unsigned char len = 8U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, 8U); + + return true; + } + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x13U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_DATA; + + buffer[4U] = m_txCounter; + buffer[5U] = m_pktCounter; + + m_pktCounter++; + if (m_pktCounter >= m_txSpace) + m_pktCounter = 0U; + + buffer[6U] = 0x00U; + buffer[7U] = 0x00U; + + ::memcpy(buffer + 8U, data, DV_FRAME_LENGTH_BYTES); + + buffer[20U] = 0x00U; + buffer[21U] = 0x00U; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 22U); + cksum.result(buffer + 22U); + } else { + buffer[22U] = 0x00U; + buffer[23U] = 0x0BU; + } + + wxMutexLocker locker(m_mutex); + + unsigned char type = DSMTT_DATA; + m_txData.addData(&type, 1U); + + unsigned char len = 24U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, 24U); + + return true; +} + +unsigned int CDVRPTRV1Controller::getSpace() +{ + return m_txData.freeSpace() / 26U; +} + +bool CDVRPTRV1Controller::isTXReady() +{ + if (m_tx) + return false; + + return m_txData.isEmpty(); +} + +bool CDVRPTRV1Controller::readVersion() +{ + unsigned char buffer[10U]; + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x01U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_GET_VERSION; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 4U); + cksum.result(buffer + 4U); + } else { + buffer[4U] = 0x00U; + buffer[5U] = 0x0BU; + } + + // CUtils::dump(wxT("Written"), buffer, 6U); + + int ret = m_serial.write(buffer, 6U); + if (ret != 6) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_V1 resp; + do { + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT1_GET_VERSION) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DV-RPTR modem is not responding to the version command")); + return false; + } + } + } while (resp != RT1_GET_VERSION); + + wxString firmware; + if ((m_buffer[4U] & 0x0FU) > 0x00U) + firmware.Printf(wxT("%u.%u%u%c"), (m_buffer[5U] & 0xF0U) >> 4, m_buffer[5U] & 0x0FU, (m_buffer[4U] & 0xF0U) >> 4, (m_buffer[4U] & 0x0FU) + wxT('a') - 1U); + else + firmware.Printf(wxT("%u.%u%u"), (m_buffer[5U] & 0xF0U) >> 4, m_buffer[5U] & 0x0FU, (m_buffer[4U] & 0xF0U) >> 4); + + wxString hardware((char*)(m_buffer + 6U), wxConvLocal, length - DVRPTR_HEADER_LENGTH - 3U); + + wxLogInfo(wxT("DV-RPTR Modem Firmware version: %s, hardware: %s"), firmware.c_str(), hardware.c_str()); + + return true; +} + +bool CDVRPTRV1Controller::readStatus() +{ + unsigned char buffer[10U]; + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x01U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_GET_STATUS; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 4U); + cksum.result(buffer + 4U); + } else { + buffer[4U] = 0x00U; + buffer[5U] = 0x0BU; + } + + return m_serial.write(buffer, 6U) == 6; +} + +bool CDVRPTRV1Controller::setConfig() +{ + unsigned char buffer[20U]; + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x07U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_SET_CONFIG; + + buffer[4U] = 0xC0U; // Physical layer + + buffer[5U] = 0x04U; // Block length + + buffer[6U] = 0x00U; + if (m_rxInvert) + buffer[6U] |= 0x01U; + if (m_txInvert) + buffer[6U] |= 0x02U; + if (m_channel) + buffer[6U] |= 0x04U; + + buffer[7U] = (m_modLevel * 256U) / 100U; + + wxUint16* txDelay = (wxUint16*)(buffer + 8U); + *txDelay = wxUINT16_SWAP_ON_BE(m_txDelay); + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 10U); + cksum.result(buffer + 10U); + } else { + buffer[10U] = 0x00U; + buffer[11U] = 0x0BU; + } + + // CUtils::dump(wxT("Written"), buffer, 12U); + + int ret = m_serial.write(buffer, 12U); + if (ret != 12) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_V1 resp; + do { + + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT1_SET_CONFIG) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DV-RPTR modem is not responding to the SET_CONFIG command")); + return false; + } + } + } while (resp != RT1_SET_CONFIG); + + // CUtils::dump(wxT("Response"), m_buffer, length); + + unsigned char type = m_buffer[4U]; + if (type != DVRPTR_ACK) { + wxLogError(wxT("Received a NAK to the SET_CONFIG command from the modem")); + return false; + } + + return true; +} + +bool CDVRPTRV1Controller::setEnabled(bool enable) +{ + unsigned char buffer[10U]; + + buffer[0U] = DVRPTR_FRAME_START; + + buffer[1U] = 0x02U; + buffer[2U] = 0x00U; + + buffer[3U] = DVRPTR_GET_STATUS; + + // Enable RX, TX, and Watchdog + if (enable) + buffer[4U] = 0x01U | 0x02U | 0x04U; + else + buffer[4U] = 0x00U; + + if (m_checksum) { + CCCITTChecksum cksum; + cksum.update(buffer + 0U, 5U); + cksum.result(buffer + 5U); + } else { + buffer[5U] = 0x00U; + buffer[6U] = 0x0BU; + } + + // CUtils::dump(wxT("Written"), buffer, 7U); + + int ret = m_serial.write(buffer, 7U); + if (ret != 7) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_V1 resp; + do { + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT1_GET_STATUS) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DV-RPTR modem is not responding to the SET_STATUS command")); + return false; + } + } + } while (resp != RT1_GET_STATUS); + + // CUtils::dump(wxT("Response"), m_buffer, length); + + unsigned char type = m_buffer[4U]; + if (type != DVRPTR_ACK) { + wxLogError(wxT("Received a NAK to the SET_STATUS command from the modem")); + return false; + } + + return true; +} + +RESP_TYPE_V1 CDVRPTRV1Controller::getResponse(unsigned char *buffer, unsigned int& length) +{ + // Get the start of the frame or nothing at all + int ret = m_serial.read(buffer, 1U); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DV-RPTR")); + return RT1_ERROR; + } + + if (ret == 0) + return RT1_TIMEOUT; + + if (buffer[0U] != DVRPTR_FRAME_START) + return RT1_TIMEOUT; + + unsigned int offset = 1U; + + while (offset < DVRPTR_HEADER_LENGTH) { + ret = m_serial.read(buffer + offset, DVRPTR_HEADER_LENGTH - offset); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DV-RPTR")); + return RT1_ERROR; + } + + if (ret > 0) + offset += ret; + + if (ret == 0) + Sleep(5UL); + } + + length = buffer[1U] + buffer[2U] * 256U; + + // Remove the response bit + unsigned int type = buffer[3U] & 0x7FU; + + offset = 0U; + + while (offset < length) { + ret = m_serial.read(buffer + offset + DVRPTR_HEADER_LENGTH, length - offset); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DV-RPTR")); + return RT1_ERROR; + } + + if (ret > 0) + offset += ret; + + if (ret == 0) + Sleep(5UL); + } + + length += DVRPTR_HEADER_LENGTH; + + // CUtils::dump(wxT("Received"), buffer, length); + + switch (type) { + case DVRPTR_GET_STATUS: + return RT1_GET_STATUS; + case DVRPTR_GET_VERSION: + return RT1_GET_VERSION; + case DVRPTR_GET_SERIAL: + return RT1_GET_SERIAL; + case DVRPTR_GET_CONFIG: + return RT1_GET_CONFIG; + case DVRPTR_SET_CONFIG: + return RT1_SET_CONFIG; + case DVRPTR_RXPREAMBLE: + return RT1_RXPREAMBLE; + case DVRPTR_START: + return RT1_START; + case DVRPTR_HEADER: + return RT1_HEADER; + case DVRPTR_RXSYNC: + return RT1_RXSYNC; + case DVRPTR_DATA: + return RT1_DATA; + case DVRPTR_EOT: + return RT1_EOT; + case DVRPTR_RXLOST: + return RT1_RXLOST; + case DVRPTR_SET_TESTMDE: + return RT1_SET_TESTMDE; + default: + return RT1_UNKNOWN; + } +} + +wxString CDVRPTRV1Controller::getPath() const +{ + return m_path; +} + +bool CDVRPTRV1Controller::findPort() +{ + if (m_path.IsEmpty()) + return false; + +#if defined(__WINDOWS__) +#else + wxDir dir; + bool ret1 = dir.Open(wxT("/sys/class/tty")); + if (!ret1) { + wxLogError(wxT("Cannot open directory /sys/class/tty")); + return false; + } + + wxString fileName; + ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); + while (ret1) { + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + ::strcat(cpath, "/device"); + ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret2); + } else { + // Get all but the last section + wxString fullPath = wxString(symlink, wxConvLocal, ret2); + path = fullPath.BeforeLast(wxT('/')); + } + + if (path.IsSameAs(m_path)) { + m_port.Printf(wxT("/dev/%s"), fileName.c_str()); + + wxLogMessage(wxT("Found modem port of %s based on the path"), m_port.c_str()); + + return true; + } + + ret1 = dir.GetNext(&fileName); + } +#endif + + return false; +} + +bool CDVRPTRV1Controller::findPath() +{ +#if defined(__WINDOWS__) +#ifdef notdef + GUID guids[5U]; + + DWORD count; + BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); + if (!res) { + wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); + return false; + } + + for (DWORD i = 0U; i < count; i++) { + HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + if (devInfo == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); + return false; + } + + SP_DEVICE_INTERFACE_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { + // Find the required length of the device structure + DWORD length; + ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Get the detailed data into the newly allocated device structure + DWORD required; + res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); + if (!res) { + wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ::free(detailData); + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + } + + return false; +#endif +#else + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), m_port.Mid(5U).c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + ::strcat(cpath, "/device"); + ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret); + } else { + wxString fullPath = wxString(symlink, wxConvLocal, ret); + path = fullPath.BeforeLast(wxT('/')); + } + + if (m_path.IsEmpty()) + wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + + m_path = path; +#endif + + return true; +} + +bool CDVRPTRV1Controller::findModem() +{ + m_serial.close(); + + // Tell the repeater that the signal has gone away + if (m_rx) { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + + unsigned int count = 0U; + + // Purge the transmit buffer every 500ms to avoid overflow, but only try and reopen the modem every 2s + while (!m_stopped) { + count++; + if (count >= 4U) { + wxLogMessage(wxT("Trying to reopen the modem")); + + bool ret = findPort(); + if (ret) { + ret = openModem(); + if (ret) + return true; + } + + count = 0U; + } + + Sleep(500UL); + } + + return false; +} + +bool CDVRPTRV1Controller::openModem() +{ + bool ret = m_serial.open(); + if (!ret) + return false; + + ret = readVersion(); + if (!ret) { + m_serial.close(); + return false; + } + + ret = setConfig(); + if (!ret) { + m_serial.close(); + return false; + } + + ret = setEnabled(true); + if (!ret) { + m_serial.close(); + return false; + } + + return true; +} + diff --git a/Common/DVRPTRV1Controller.h b/Common/DVRPTRV1Controller.h new file mode 100644 index 0000000..ef93af5 --- /dev/null +++ b/Common/DVRPTRV1Controller.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DVRPTRV1Controller_H +#define DVRPTRV1Controller_H + +#include "SerialDataController.h" +#include "RingBuffer.h" +#include "Modem.h" +#include "Utils.h" + +#include + +enum RESP_TYPE_V1 { + RT1_TIMEOUT, + RT1_ERROR, + RT1_UNKNOWN, + RT1_GET_STATUS, + RT1_GET_VERSION, + RT1_GET_SERIAL, + RT1_GET_CONFIG, + RT1_SET_CONFIG, + RT1_RXPREAMBLE, + RT1_START, + RT1_HEADER, + RT1_RXSYNC, + RT1_DATA, + RT1_EOT, + RT1_RXLOST, + RT1_SET_TESTMDE +}; + +class CDVRPTRV1Controller : public CModem { +public: + CDVRPTRV1Controller(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay); + virtual ~CDVRPTRV1Controller(); + + virtual void* Entry(); + + virtual bool start(); + + virtual unsigned int getSpace(); + virtual bool isTXReady(); + + virtual bool writeHeader(const CHeaderData& header); + virtual bool writeData(const unsigned char* data, unsigned int length, bool end); + + virtual wxString getPath() const; + +private: + wxString m_port; + wxString m_path; + bool m_rxInvert; + bool m_txInvert; + bool m_channel; + unsigned int m_modLevel; + unsigned int m_txDelay; + CSerialDataController m_serial; + unsigned char* m_buffer; + CRingBuffer m_txData; + unsigned char m_txCounter; + unsigned char m_pktCounter; + bool m_rx; + unsigned int m_txSpace; + bool m_txEnabled; + bool m_checksum; + + bool readVersion(); + bool readStatus(); + bool setConfig(); + bool setEnabled(bool enable); + + RESP_TYPE_V1 getResponse(unsigned char* buffer, unsigned int& length); + + bool findPort(); + bool findPath(); + + bool findModem(); + bool openModem(); +}; + +#endif + diff --git a/Common/DVRPTRV2Controller.cpp b/Common/DVRPTRV2Controller.cpp new file mode 100644 index 0000000..3a75117 --- /dev/null +++ b/Common/DVRPTRV2Controller.cpp @@ -0,0 +1,841 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DVRPTRV2Controller.h" +#include "DStarDefines.h" +#include "Timer.h" + +#if defined(__WINDOWS__) +#include +#else +#include +#endif + +const unsigned int MAX_RESPONSES = 30U; + +const unsigned int BUFFER_LENGTH = 200U; + +CDVRPTRV2Controller::CDVRPTRV2Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) : +CModem(), +m_connection(CT_USB), +m_usbPort(port), +m_usbPath(path), +m_address(), +m_port(0U), +m_txInvert(txInvert), +m_modLevel(modLevel), +m_duplex(duplex), +m_callsign(callsign), +m_txDelay(txDelay), +m_usb(NULL), +m_network(NULL), +m_buffer(NULL), +m_txData(1000U), +m_rx(false) +{ + wxASSERT(!port.IsEmpty()); + + m_usb = new CSerialDataController(port, SERIAL_115200); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDVRPTRV2Controller::CDVRPTRV2Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) : +CModem(), +m_connection(CT_NETWORK), +m_usbPort(), +m_usbPath(), +m_address(address), +m_port(port), +m_txInvert(txInvert), +m_modLevel(modLevel), +m_duplex(duplex), +m_callsign(callsign), +m_txDelay(txDelay), +m_usb(NULL), +m_network(NULL), +m_buffer(NULL), +m_txData(1000U), +m_rx(false) +{ + wxASSERT(!address.IsEmpty()); + wxASSERT(port > 0U); + + m_network = new CTCPReaderWriter(address, port); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDVRPTRV2Controller::~CDVRPTRV2Controller() +{ + delete m_usb; + delete m_network; + + delete[] m_buffer; +} + +bool CDVRPTRV2Controller::start() +{ + findPort(); + + bool ret = openModem(); + if (!ret) + return false; + + findPath(); + + Create(); + SetPriority(100U); + Run(); + + return true; +} + +void* CDVRPTRV2Controller::Entry() +{ + wxLogMessage(wxT("Starting DV-RPTR2 Modem Controller thread")); + + // Clock every 5ms-ish + CTimer pollTimer(200U, 0U, 250U); + pollTimer.start(); + + unsigned int space = 0U; + + while (!m_stopped) { + // Poll the modem status every 250ms + if (pollTimer.hasExpired()) { + bool ret = readSpace(); + if (!ret) { + ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread")); + return NULL; + } + } + + pollTimer.start(); + } + + unsigned int length; + RESP_TYPE_V2 type = getResponse(m_buffer, length); + + switch (type) { + case RT2_TIMEOUT: + break; + + case RT2_ERROR: { + bool ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread")); + return NULL; + } + } + break; + + case RT2_HEADER: { + // CUtils::dump(wxT("RT2_HEADER"), m_buffer, length); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 9U, RADIO_HEADER_LENGTH_BYTES - 2U); + + // Dummy checksum + data[0U] = 0xFFU; + data[1U] = 0xFFU; + m_rxData.addData(data, 2U); + + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 51U, DV_FRAME_LENGTH_BYTES); + + m_rx = true; + } + break; + + case RT2_DATA: { + // CUtils::dump(wxT("RT2_DATA"), m_buffer, length); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 5U, DV_FRAME_LENGTH_BYTES); + + m_rx = true; + + // End of transmission? + bool end = (m_buffer[19U] & 0x40U) == 0x40U; + if (end) { + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + } + break; + + case RT2_SPACE: + space = m_buffer[9U]; + // CUtils::dump(wxT("RT2_SPACE"), m_buffer, length); + break; + + // These should not be received in this loop, but don't complain if we do + case RT2_QUERY: + case RT2_CONFIG: + break; + + default: + wxLogMessage(wxT("Unknown DV-RPTR2 message, type")); + CUtils::dump(wxT("Buffer dump"), m_buffer, length); + break; + } + + if (space >= 4U) { + if (m_txData.hasData()) { + unsigned char len = 0U; + unsigned char data[200U]; + + { + wxMutexLocker locker(m_mutex); + + m_txData.getData(&len, 1U); + m_txData.getData(data, len); + } + + // CUtils::dump(wxT("Write"), data, len); + + bool ret = writeModem(data, len); + if (!ret) { + ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread")); + return NULL; + } + } else { + if (len > 100U) + space -= 4U; + else + space--; + } + } + } + + Sleep(5UL); + + pollTimer.clock(); + } + + wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread")); + + closeModem(); + + return NULL; +} + +bool CDVRPTRV2Controller::writeHeader(const CHeaderData& header) +{ + bool ret = m_txData.hasSpace(106U); + if (!ret) { + wxLogWarning(wxT("No space to write the header")); + return false; + } + + unsigned char buffer[105U]; + + ::memset(buffer, 0x00U, 105U); + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'X'; + buffer[5U] = '0'; + buffer[6U] = '0'; + buffer[7U] = '0'; + buffer[8U] = '2'; + + ::memset(buffer + 9U, ' ', RADIO_HEADER_LENGTH_BYTES); + + buffer[9U] = header.getFlag1(); + buffer[10U] = header.getFlag2(); + buffer[11U] = header.getFlag3(); + + wxString rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 12U] = rpt2.GetChar(i); + + wxString rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 20U] = rpt1.GetChar(i); + + wxString your = header.getYourCall(); + for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 28U] = your.GetChar(i); + + wxString my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 36U] = my1.GetChar(i); + + wxString my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 44U] = my2.GetChar(i); + + wxMutexLocker locker(m_mutex); + + unsigned char len = 105U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, 105U); + + m_tx = true; + + return true; +} + +bool CDVRPTRV2Controller::writeData(const unsigned char* data, unsigned int, bool end) +{ + bool ret = m_txData.hasSpace(18U); + if (!ret) { + wxLogWarning(wxT("No space to write data")); + return false; + } + + unsigned char buffer[17U]; + + ::memset(buffer, 0x00U, 17U); + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'Z'; + + ::memcpy(buffer + 5U, data, DV_FRAME_LENGTH_BYTES); + + if (end) { + buffer[14U] = 0x55U; + buffer[15U] = 0x55U; + buffer[16U] = 0x55U; + + m_tx = false; + } + + wxMutexLocker locker(m_mutex); + + unsigned char len = 17U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, 17U); + + return true; +} + +unsigned int CDVRPTRV2Controller::getSpace() +{ + return m_txData.freeSpace() / 18U; +} + +bool CDVRPTRV2Controller::isTXReady() +{ + return m_txData.isEmpty(); +} + +bool CDVRPTRV2Controller::readSerial() +{ + unsigned char buffer[105U]; + + ::memset(buffer, 0x00U, 105U); + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'X'; + buffer[5U] = '9'; + buffer[6U] = '0'; + buffer[7U] = '0'; + buffer[8U] = '0'; + + // CUtils::dump(wxT("Written"), buffer, 105U); + + bool ret = writeModem(buffer, 105U); + if (!ret) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_V2 resp; + do { + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT2_QUERY) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DV-RPTR modem is not responding to the query command")); + return false; + } + } + } while (resp != RT2_QUERY); + + wxLogInfo(wxT("DV-RPTR Modem Hardware serial: 0x%02X%02X%02x%02X"), m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U]); + + return true; +} + +bool CDVRPTRV2Controller::readSpace() +{ + unsigned char buffer[10U]; + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'Y'; + buffer[5U] = '9'; + buffer[6U] = '0'; + buffer[7U] = '1'; + buffer[8U] = '1'; + buffer[9U] = 0x00U; + + // CUtils::dump(wxT("Written"), buffer, 10U); + + return writeModem(buffer, 10U); +} + +bool CDVRPTRV2Controller::setConfig() +{ + unsigned char buffer[105U]; + + ::memset(buffer, 0x00U, 105U); + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'X'; + buffer[5U] = '9'; + buffer[6U] = '0'; + buffer[7U] = '0'; + buffer[8U] = '1'; + + ::memset(buffer + 9U, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH && i < m_callsign.Len(); i++) + buffer[9U + i] = m_callsign.GetChar(i); + + buffer[65U] = m_duplex ? 0x03U : 0x00U; + + buffer[66U] = m_txInvert ? 0x01U : 0x00U; + + buffer[73U] = (m_modLevel * 256U) / 100U; + + // Internal and external speaker on + buffer[86U] = 0x03U; + + buffer[87U] = 0x01U; + + if (m_txDelay < 100U) + m_txDelay = 100U; + if (m_txDelay > 850U) + m_txDelay = 850U; + + buffer[89U] = m_txDelay / 10U; + + // CUtils::dump(wxT("Written"), buffer, 105U); + + bool ret = writeModem(buffer, 105U); + if (!ret) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_V2 resp; + do { + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT2_CONFIG) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DV-RPTR modem is not responding to the configure command")); + return false; + } + } + } while (resp != RT2_CONFIG); + + // CUtils::dump(wxT("Response"), m_buffer, length); + + wxString firmware((char*)(m_buffer + 9U), wxConvLocal); + + wxLogInfo(wxT("DV-RPTR Modem Firmware version: %s"), firmware.c_str()); + + return true; +} + +RESP_TYPE_V2 CDVRPTRV2Controller::getResponse(unsigned char *buffer, unsigned int& length) +{ + // Get the start of the frame or nothing at all + int ret = readModem(buffer + 0U, 5U); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DV-RPTR")); + return RT2_ERROR; + } + + if (ret == 0) + return RT2_TIMEOUT; + + while (::memcmp(buffer + 0U, "HEAD", 4U) != 0) { + buffer[0U] = buffer[1U]; + buffer[1U] = buffer[2U]; + buffer[2U] = buffer[3U]; + buffer[3U] = buffer[4U]; + + ret = readModem(buffer + 4U, 1U); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DV-RPTR")); + return RT2_ERROR; + } + + if (ret == 0) + return RT2_TIMEOUT; + } + + switch (buffer[4U]) { + case 'X': + length = 105U; + break; + case 'Y': + length = 10U; + break; + case 'Z': + length = 20U; + break; + default: + wxLogError(wxT("DV-RPTR frame type is incorrect - 0x%02X"), buffer[4U]); + return RT2_UNKNOWN; + } + + unsigned int offset = 5U; + + while (offset < length) { + ret = readModem(buffer + offset, length - offset); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DV-RPTR")); + return RT2_ERROR; + } + + if (ret > 0) + offset += ret; + + if (ret == 0) + Sleep(5UL); + } + + // CUtils::dump(wxT("Received"), buffer, length); + + if (::memcmp(buffer + 0U, "HEADZ", 5U) == 0) { + return RT2_DATA; + } else if (::memcmp(buffer + 5U, "0001", 4U) == 0) { + if (buffer[104U] == 0x01U) + return RT2_HEADER; + } else if (::memcmp(buffer + 5U, "9900", 4U) == 0) { + return RT2_QUERY; + } else if (::memcmp(buffer + 5U, "9001", 4U) == 0) { + return RT2_CONFIG; + } else if (::memcmp(buffer + 5U, "9011", 4U) == 0) { + return RT2_SPACE; + } else if (::memcmp(buffer + 5U, "9021", 4U) == 0) { + return RT2_TIMEOUT; + } + + return RT2_UNKNOWN; +} + +wxString CDVRPTRV2Controller::getPath() const +{ + return m_usbPath; +} + +bool CDVRPTRV2Controller::findPort() +{ + if (m_connection != CT_USB) + return true; + + if (m_usbPath.IsEmpty()) + return false; + +#if defined(__WINDOWS__) +#else + wxDir dir; + bool ret1 = dir.Open(wxT("/sys/class/tty")); + if (!ret1) { + wxLogError(wxT("Cannot open directory /sys/class/tty")); + return false; + } + + wxString fileName; + ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); + while (ret1) { + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + ::strcat(cpath, "/device"); + ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret2); + } else { + // Get all but the last section + wxString fullPath = wxString(symlink, wxConvLocal, ret2); + path = fullPath.BeforeLast(wxT('/')); + } + + if (path.IsSameAs(m_usbPath)) { + m_usbPort.Printf(wxT("/dev/%s"), fileName.c_str()); + + wxLogMessage(wxT("Found modem port of %s based on the path"), m_usbPort.c_str()); + + return true; + } + + ret1 = dir.GetNext(&fileName); + } +#endif + + return false; +} + +bool CDVRPTRV2Controller::findPath() +{ + if (m_connection != CT_USB) + return true; + +#if defined(__WINDOWS__) +#ifdef notdef + GUID guids[5U]; + + DWORD count; + BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); + if (!res) { + wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); + return false; + } + + for (DWORD i = 0U; i < count; i++) { + HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + if (devInfo == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); + return false; + } + + SP_DEVICE_INTERFACE_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { + // Find the required length of the device structure + DWORD length; + ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Get the detailed data into the newly allocated device structure + DWORD required; + res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); + if (!res) { + wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ::free(detailData); + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + } + + return false; +#endif +#else + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), m_usbPort.Mid(5U).c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + ::strcat(cpath, "/device"); + ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret); + } else { + wxString fullPath = wxString(symlink, wxConvLocal, ret); + path = fullPath.BeforeLast(wxT('/')); + } + + if (m_usbPath.IsEmpty()) + wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + + m_usbPath = path; +#endif + + return true; +} + +bool CDVRPTRV2Controller::findModem() +{ + closeModem(); + + // Tell the repeater that the signal has gone away + if (m_rx) { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + + unsigned int count = 0U; + + // Purge the transmit buffer every 500ms to avoid overflow, but only try and reopen the modem every 2s + while (!m_stopped) { + count++; + if (count >= 4U) { + wxLogMessage(wxT("Trying to reopen the modem")); + + bool ret = findPort(); + if (ret) { + ret = openModem(); + if (ret) + return true; + } + + count = 0U; + } + + Sleep(500UL); + } + + return false; +} + +bool CDVRPTRV2Controller::openModem() +{ + bool ret = false; + + switch (m_connection) { + case CT_USB: + ret = m_usb->open(); + break; + case CT_NETWORK: + ret = m_network->open(); + break; + default: + wxLogError(wxT("Invalid connection type: %d"), int(m_connection)); + break; + } + + if (!ret) + return false; + + ret = readSerial(); + if (!ret) { + closeModem(); + return false; + } + + ret = setConfig(); + if (!ret) { + closeModem(); + return false; + } + + return true; +} + +int CDVRPTRV2Controller::readModem(unsigned char* buffer, unsigned int length) +{ + switch (m_connection) { + case CT_USB: + return m_usb->read(buffer, length); + case CT_NETWORK: + return m_network->read(buffer, length, 0U); + default: + return -1; + } +} + +bool CDVRPTRV2Controller::writeModem(const unsigned char* buffer, unsigned int length) +{ + switch (m_connection) { + case CT_USB: + return m_usb->write(buffer, length) == int(length); + case CT_NETWORK: + return m_network->write(buffer, length); + default: + return false; + } +} + +void CDVRPTRV2Controller::closeModem() +{ + switch (m_connection) { + case CT_USB: + return m_usb->close(); + case CT_NETWORK: + return m_network->close(); + default: + return; + } +} diff --git a/Common/DVRPTRV2Controller.h b/Common/DVRPTRV2Controller.h new file mode 100644 index 0000000..8c35f7d --- /dev/null +++ b/Common/DVRPTRV2Controller.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DVRPTRV2Controller_H +#define DVRPTRV2Controller_H + +#include "SerialDataController.h" +#include "TCPReaderWriter.h" +#include "DStarDefines.h" +#include "RingBuffer.h" +#include "Modem.h" +#include "Utils.h" + +#include + +enum RESP_TYPE_V2 { + RT2_TIMEOUT, + RT2_ERROR, + RT2_UNKNOWN, + RT2_SPACE, + RT2_QUERY, + RT2_CONFIG, + RT2_HEADER, + RT2_DATA +}; + +class CDVRPTRV2Controller : public CModem { +public: + CDVRPTRV2Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay); + CDVRPTRV2Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay); + virtual ~CDVRPTRV2Controller(); + + virtual void* Entry(); + + virtual bool start(); + + virtual unsigned int getSpace(); + virtual bool isTXReady(); + + virtual bool writeHeader(const CHeaderData& header); + virtual bool writeData(const unsigned char* data, unsigned int length, bool end); + + virtual wxString getPath() const; + +private: + CONNECTION_TYPE m_connection; + wxString m_usbPort; + wxString m_usbPath; + wxString m_address; + unsigned int m_port; + bool m_txInvert; + unsigned int m_modLevel; + bool m_duplex; + wxString m_callsign; + unsigned int m_txDelay; + CSerialDataController* m_usb; + CTCPReaderWriter* m_network; + unsigned char* m_buffer; + CRingBuffer m_txData; + bool m_rx; + + bool readSerial(); + bool setConfig(); + bool readSpace(); + + RESP_TYPE_V2 getResponse(unsigned char* buffer, unsigned int& length); + + bool findPort(); + bool findPath(); + + bool findModem(); + bool openModem(); + + int readModem(unsigned char* buffer, unsigned int length); + bool writeModem(const unsigned char* buffer, unsigned int length); + void closeModem(); +}; + +#endif diff --git a/Common/DVRPTRV3Controller.cpp b/Common/DVRPTRV3Controller.cpp new file mode 100644 index 0000000..79fbf09 --- /dev/null +++ b/Common/DVRPTRV3Controller.cpp @@ -0,0 +1,841 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DVRPTRV3Controller.h" +#include "DStarDefines.h" +#include "Timer.h" + +#if defined(__WINDOWS__) +#include +#else +#include +#endif + +const unsigned int MAX_RESPONSES = 30U; + +const unsigned int BUFFER_LENGTH = 200U; + +CDVRPTRV3Controller::CDVRPTRV3Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) : +CModem(), +m_connection(CT_USB), +m_usbPort(port), +m_usbPath(path), +m_address(), +m_port(0U), +m_txInvert(txInvert), +m_modLevel(modLevel), +m_duplex(duplex), +m_callsign(callsign), +m_txDelay(txDelay), +m_usb(NULL), +m_network(NULL), +m_buffer(NULL), +m_txData(1000U), +m_rx(false) +{ + wxASSERT(!port.IsEmpty()); + + m_usb = new CSerialDataController(port, SERIAL_115200); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDVRPTRV3Controller::CDVRPTRV3Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) : +CModem(), +m_connection(CT_NETWORK), +m_usbPort(), +m_usbPath(), +m_address(address), +m_port(port), +m_txInvert(txInvert), +m_modLevel(modLevel), +m_duplex(duplex), +m_callsign(callsign), +m_txDelay(txDelay), +m_usb(NULL), +m_network(NULL), +m_buffer(NULL), +m_txData(1000U), +m_rx(false) +{ + wxASSERT(!address.IsEmpty()); + wxASSERT(port > 0U); + + m_network = new CTCPReaderWriter(address, port); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDVRPTRV3Controller::~CDVRPTRV3Controller() +{ + delete m_usb; + delete m_network; + + delete[] m_buffer; +} + +bool CDVRPTRV3Controller::start() +{ + findPort(); + + bool ret = openModem(); + if (!ret) + return false; + + findPath(); + + Create(); + SetPriority(100U); + Run(); + + return true; +} + +void* CDVRPTRV3Controller::Entry() +{ + wxLogMessage(wxT("Starting DV-RPTR3 Modem Controller thread")); + + // Clock every 5ms-ish + CTimer pollTimer(200U, 0U, 250U); + pollTimer.start(); + + unsigned int space = 0U; + + while (!m_stopped) { + // Poll the modem status every 250ms + if (pollTimer.hasExpired()) { + bool ret = readSpace(); + if (!ret) { + ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread")); + return NULL; + } + } + + pollTimer.start(); + } + + unsigned int length; + RESP_TYPE_V3 type = getResponse(m_buffer, length); + + switch (type) { + case RT3_TIMEOUT: + break; + + case RT3_ERROR: { + bool ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread")); + return NULL; + } + } + break; + + case RT3_HEADER: { + // CUtils::dump(wxT("RT3_HEADER"), m_buffer, length); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 9U, RADIO_HEADER_LENGTH_BYTES - 2U); + + // Dummy checksum + data[0U] = 0xFFU; + data[1U] = 0xFFU; + m_rxData.addData(data, 2U); + + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 51U, DV_FRAME_LENGTH_BYTES); + + m_rx = true; + } + break; + + case RT3_DATA: { + // CUtils::dump(wxT("RT3_DATA"), m_buffer, length); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 5U, DV_FRAME_LENGTH_BYTES); + + m_rx = true; + + // End of transmission? + bool end = (m_buffer[19U] & 0x40U) == 0x40U; + if (end) { + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + } + break; + + case RT3_SPACE: + space = m_buffer[9U]; + // CUtils::dump(wxT("RT3_SPACE"), m_buffer, length); + break; + + // These should not be received in this loop, but don't complain if we do + case RT3_QUERY: + case RT3_CONFIG: + break; + + default: + wxLogMessage(wxT("Unknown DV-RPTR3 message, type")); + CUtils::dump(wxT("Buffer dump"), m_buffer, length); + break; + } + + if (space >= 4U) { + if (m_txData.hasData()) { + unsigned char len = 0U; + unsigned char data[200U]; + + { + wxMutexLocker locker(m_mutex); + + m_txData.getData(&len, 1U); + m_txData.getData(data, len); + } + + // CUtils::dump(wxT("Write"), data, len); + + bool ret = writeModem(data, len); + if (!ret) { + ret = findModem(); + if (!ret) { + wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread")); + return NULL; + } + } else { + if (len > 100U) + space -= 4U; + else + space--; + } + } + } + + Sleep(5UL); + + pollTimer.clock(); + } + + wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread")); + + closeModem(); + + return NULL; +} + +bool CDVRPTRV3Controller::writeHeader(const CHeaderData& header) +{ + bool ret = m_txData.hasSpace(106U); + if (!ret) { + wxLogWarning(wxT("No space to write the header")); + return false; + } + + unsigned char buffer[105U]; + + ::memset(buffer, 0x00U, 105U); + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'X'; + buffer[5U] = '0'; + buffer[6U] = '0'; + buffer[7U] = '0'; + buffer[8U] = '2'; + + ::memset(buffer + 9U, ' ', RADIO_HEADER_LENGTH_BYTES); + + buffer[9U] = header.getFlag1(); + buffer[10U] = header.getFlag2(); + buffer[11U] = header.getFlag3(); + + wxString rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 12U] = rpt2.GetChar(i); + + wxString rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 20U] = rpt1.GetChar(i); + + wxString your = header.getYourCall(); + for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 28U] = your.GetChar(i); + + wxString my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 36U] = my1.GetChar(i); + + wxString my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 44U] = my2.GetChar(i); + + wxMutexLocker locker(m_mutex); + + unsigned char len = 105U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, 105U); + + m_tx = true; + + return true; +} + +bool CDVRPTRV3Controller::writeData(const unsigned char* data, unsigned int, bool end) +{ + bool ret = m_txData.hasSpace(18U); + if (!ret) { + wxLogWarning(wxT("No space to write data")); + return false; + } + + unsigned char buffer[17U]; + + ::memset(buffer, 0x00U, 17U); + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'Z'; + + ::memcpy(buffer + 5U, data, DV_FRAME_LENGTH_BYTES); + + if (end) { + buffer[14U] = 0x55U; + buffer[15U] = 0x55U; + buffer[16U] = 0x55U; + + m_tx = false; + } + + wxMutexLocker locker(m_mutex); + + unsigned char len = 17U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, 17U); + + return true; +} + +unsigned int CDVRPTRV3Controller::getSpace() +{ + return m_txData.freeSpace() / 18U; +} + +bool CDVRPTRV3Controller::isTXReady() +{ + return m_txData.isEmpty(); +} + +bool CDVRPTRV3Controller::readSerial() +{ + unsigned char buffer[105U]; + + ::memset(buffer, 0x00U, 105U); + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'X'; + buffer[5U] = '9'; + buffer[6U] = '0'; + buffer[7U] = '0'; + buffer[8U] = '0'; + + // CUtils::dump(wxT("Written"), buffer, 105U); + + bool ret = writeModem(buffer, 105U); + if (!ret) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_V3 resp; + do { + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT3_QUERY) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DV-RPTR modem is not responding to the query command")); + return false; + } + } + } while (resp != RT3_QUERY); + + wxLogInfo(wxT("DV-RPTR Modem Hardware serial: 0x%02X%02X%02x%02X"), m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U]); + + return true; +} + +bool CDVRPTRV3Controller::readSpace() +{ + unsigned char buffer[10U]; + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'Y'; + buffer[5U] = '9'; + buffer[6U] = '0'; + buffer[7U] = '1'; + buffer[8U] = '1'; + buffer[9U] = 0x00U; + + // CUtils::dump(wxT("Written"), buffer, 10U); + + return writeModem(buffer, 10U); +} + +bool CDVRPTRV3Controller::setConfig() +{ + unsigned char buffer[105U]; + + ::memset(buffer, 0x00U, 105U); + + buffer[0U] = 'H'; + buffer[1U] = 'E'; + buffer[2U] = 'A'; + buffer[3U] = 'D'; + buffer[4U] = 'X'; + buffer[5U] = '9'; + buffer[6U] = '0'; + buffer[7U] = '0'; + buffer[8U] = '1'; + + ::memset(buffer + 9U, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH && i < m_callsign.Len(); i++) + buffer[9U + i] = m_callsign.GetChar(i); + + buffer[65U] = m_duplex ? 0x03U : 0x00U; + + buffer[66U] = m_txInvert ? 0x01U : 0x00U; + + buffer[73U] = (m_modLevel * 256U) / 100U; + + // Internal and external speaker on + buffer[86U] = 0x03U; + + buffer[87U] = 0x01U; + + if (m_txDelay < 100U) + m_txDelay = 100U; + if (m_txDelay > 850U) + m_txDelay = 850U; + + buffer[89U] = m_txDelay / 10U; + + // CUtils::dump(wxT("Written"), buffer, 105U); + + bool ret = writeModem(buffer, 105U); + if (!ret) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_V3 resp; + do { + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RT3_CONFIG) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The DV-RPTR modem is not responding to the configure command")); + return false; + } + } + } while (resp != RT3_CONFIG); + + // CUtils::dump(wxT("Response"), m_buffer, length); + + wxString firmware((char*)(m_buffer + 9U), wxConvLocal); + + wxLogInfo(wxT("DV-RPTR Modem Firmware version: %s"), firmware.c_str()); + + return true; +} + +RESP_TYPE_V3 CDVRPTRV3Controller::getResponse(unsigned char *buffer, unsigned int& length) +{ + // Get the start of the frame or nothing at all + int ret = readModem(buffer + 0U, 5U); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DV-RPTR")); + return RT3_ERROR; + } + + if (ret == 0) + return RT3_TIMEOUT; + + while (::memcmp(buffer + 0U, "HEAD", 4U) != 0) { + buffer[0U] = buffer[1U]; + buffer[1U] = buffer[2U]; + buffer[2U] = buffer[3U]; + buffer[3U] = buffer[4U]; + + ret = readModem(buffer + 4U, 1U); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DV-RPTR")); + return RT3_ERROR; + } + + if (ret == 0) + return RT3_TIMEOUT; + } + + switch (buffer[4U]) { + case 'X': + length = 105U; + break; + case 'Y': + length = 10U; + break; + case 'Z': + length = 20U; + break; + default: + wxLogError(wxT("DV-RPTR frame type is incorrect - 0x%02X"), buffer[4U]); + return RT3_UNKNOWN; + } + + unsigned int offset = 5U; + + while (offset < length) { + ret = readModem(buffer + offset, length - offset); + if (ret < 0) { + wxLogError(wxT("Error when reading from the DV-RPTR")); + return RT3_ERROR; + } + + if (ret > 0) + offset += ret; + + if (ret == 0) + Sleep(5UL); + } + + // CUtils::dump(wxT("Received"), buffer, length); + + if (::memcmp(buffer + 0U, "HEADZ", 5U) == 0) { + return RT3_DATA; + } else if (::memcmp(buffer + 5U, "0001", 4U) == 0) { + if (buffer[104U] == 0x01U) + return RT3_HEADER; + } else if (::memcmp(buffer + 5U, "9900", 4U) == 0) { + return RT3_QUERY; + } else if (::memcmp(buffer + 5U, "9001", 4U) == 0) { + return RT3_CONFIG; + } else if (::memcmp(buffer + 5U, "9011", 4U) == 0) { + return RT3_SPACE; + } else if (::memcmp(buffer + 5U, "9021", 4U) == 0) { + return RT3_TIMEOUT; + } + + return RT3_UNKNOWN; +} + +wxString CDVRPTRV3Controller::getPath() const +{ + return m_usbPath; +} + +bool CDVRPTRV3Controller::findPort() +{ + if (m_connection != CT_USB) + return true; + + if (m_usbPath.IsEmpty()) + return false; + +#if defined(__WINDOWS__) +#else + wxDir dir; + bool ret1 = dir.Open(wxT("/sys/class/tty")); + if (!ret1) { + wxLogError(wxT("Cannot open directory /sys/class/tty")); + return false; + } + + wxString fileName; + ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); + while (ret1) { + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + ::strcat(cpath, "/device"); + ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret2); + } else { + // Get all but the last section + wxString fullPath = wxString(symlink, wxConvLocal, ret2); + path = fullPath.BeforeLast(wxT('/')); + } + + if (path.IsSameAs(m_usbPath)) { + m_usbPort.Printf(wxT("/dev/%s"), fileName.c_str()); + + wxLogMessage(wxT("Found modem port of %s based on the path"), m_usbPort.c_str()); + + return true; + } + + ret1 = dir.GetNext(&fileName); + } +#endif + + return false; +} + +bool CDVRPTRV3Controller::findPath() +{ + if (m_connection != CT_USB) + return true; + +#if defined(__WINDOWS__) +#ifdef notdef + GUID guids[5U]; + + DWORD count; + BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); + if (!res) { + wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); + return false; + } + + for (DWORD i = 0U; i < count; i++) { + HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + if (devInfo == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); + return false; + } + + SP_DEVICE_INTERFACE_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { + // Find the required length of the device structure + DWORD length; + ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Get the detailed data into the newly allocated device structure + DWORD required; + res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); + if (!res) { + wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ::free(detailData); + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + } + + return false; +#endif +#else + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), m_usbPort.Mid(5U).c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + ::strcat(cpath, "/device"); + ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret); + } else { + wxString fullPath = wxString(symlink, wxConvLocal, ret); + path = fullPath.BeforeLast(wxT('/')); + } + + if (m_usbPath.IsEmpty()) + wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + + m_usbPath = path; +#endif + + return true; +} + +bool CDVRPTRV3Controller::findModem() +{ + closeModem(); + + // Tell the repeater that the signal has gone away + if (m_rx) { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + + unsigned int count = 0U; + + // Purge the transmit buffer every 500ms to avoid overflow, but only try and reopen the modem every 2s + while (!m_stopped) { + count++; + if (count >= 4U) { + wxLogMessage(wxT("Trying to reopen the modem")); + + bool ret = findPort(); + if (ret) { + ret = openModem(); + if (ret) + return true; + } + + count = 0U; + } + + Sleep(500UL); + } + + return false; +} + +bool CDVRPTRV3Controller::openModem() +{ + bool ret = false; + + switch (m_connection) { + case CT_USB: + ret = m_usb->open(); + break; + case CT_NETWORK: + ret = m_network->open(); + break; + default: + wxLogError(wxT("Invalid connection type: %d"), int(m_connection)); + break; + } + + if (!ret) + return false; + + ret = readSerial(); + if (!ret) { + closeModem(); + return false; + } + + ret = setConfig(); + if (!ret) { + closeModem(); + return false; + } + + return true; +} + +int CDVRPTRV3Controller::readModem(unsigned char* buffer, unsigned int length) +{ + switch (m_connection) { + case CT_USB: + return m_usb->read(buffer, length); + case CT_NETWORK: + return m_network->read(buffer, length, 0U); + default: + return -1; + } +} + +bool CDVRPTRV3Controller::writeModem(const unsigned char* buffer, unsigned int length) +{ + switch (m_connection) { + case CT_USB: + return m_usb->write(buffer, length) == int(length); + case CT_NETWORK: + return m_network->write(buffer, length); + default: + return false; + } +} + +void CDVRPTRV3Controller::closeModem() +{ + switch (m_connection) { + case CT_USB: + return m_usb->close(); + case CT_NETWORK: + return m_network->close(); + default: + return; + } +} diff --git a/Common/DVRPTRV3Controller.h b/Common/DVRPTRV3Controller.h new file mode 100644 index 0000000..4829c61 --- /dev/null +++ b/Common/DVRPTRV3Controller.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DVRPTRV3Controller_H +#define DVRPTRV3Controller_H + +#include "SerialDataController.h" +#include "TCPReaderWriter.h" +#include "DStarDefines.h" +#include "RingBuffer.h" +#include "Modem.h" +#include "Utils.h" + +#include + +enum RESP_TYPE_V3 { + RT3_TIMEOUT, + RT3_ERROR, + RT3_UNKNOWN, + RT3_SPACE, + RT3_QUERY, + RT3_CONFIG, + RT3_HEADER, + RT3_DATA +}; + +class CDVRPTRV3Controller : public CModem { +public: + CDVRPTRV3Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay); + CDVRPTRV3Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay); + virtual ~CDVRPTRV3Controller(); + + virtual void* Entry(); + + virtual bool start(); + + virtual unsigned int getSpace(); + virtual bool isTXReady(); + + virtual bool writeHeader(const CHeaderData& header); + virtual bool writeData(const unsigned char* data, unsigned int length, bool end); + + virtual wxString getPath() const; + +private: + CONNECTION_TYPE m_connection; + wxString m_usbPort; + wxString m_usbPath; + wxString m_address; + unsigned int m_port; + bool m_txInvert; + unsigned int m_modLevel; + bool m_duplex; + wxString m_callsign; + unsigned int m_txDelay; + CSerialDataController* m_usb; + CTCPReaderWriter* m_network; + unsigned char* m_buffer; + CRingBuffer m_txData; + bool m_rx; + + bool readSerial(); + bool setConfig(); + bool readSpace(); + + RESP_TYPE_V3 getResponse(unsigned char* buffer, unsigned int& length); + + bool findPort(); + bool findPath(); + + bool findModem(); + bool openModem(); + + int readModem(unsigned char* buffer, unsigned int length); + bool writeModem(const unsigned char* buffer, unsigned int length); + void closeModem(); +}; + +#endif diff --git a/Common/DVTOOLFileReader.cpp b/Common/DVTOOLFileReader.cpp new file mode 100644 index 0000000..0a2550b --- /dev/null +++ b/Common/DVTOOLFileReader.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2009,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DVTOOLFileReader.h" +#include "DStarDefines.h" +#include "Utils.h" + +#include + +static const char DVTOOL_SIGNATURE[] = "DVTOOL"; +static const unsigned int DVTOOL_SIGNATURE_LENGTH = 6U; + +static const char DSVT_SIGNATURE[] = "DSVT"; +static const unsigned int DSVT_SIGNATURE_LENGTH = 4U; + +static const unsigned int FIXED_DATA_LENGTH = 9U; + +static const unsigned char HEADER_FLAG = 0x10; +static const unsigned char DATA_FLAG = 0x20; + +static const unsigned char HEADER_MASK = 0x80; +static const unsigned char TRAILER_MASK = 0x40; + +const unsigned int BUFFER_LENGTH = 255U; + + +CDVTOOLFileReader::CDVTOOLFileReader() : +m_fileName(), +m_file(), +m_records(0U), +m_type(DVTFR_NONE), +m_buffer(NULL), +m_length(0U), +m_end(false) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDVTOOLFileReader::~CDVTOOLFileReader() +{ + delete[] m_buffer; +} + +wxString CDVTOOLFileReader::getFileName() const +{ + return m_fileName; +} + +unsigned int CDVTOOLFileReader::getRecords() const +{ + return m_records; +} + +bool CDVTOOLFileReader::open(const wxString& fileName) +{ + m_fileName = fileName; + + bool res = m_file.Open(fileName, wxT("rb")); + if (!res) + return false; + + unsigned char buffer[DVTOOL_SIGNATURE_LENGTH]; + size_t n = m_file.Read(buffer, DVTOOL_SIGNATURE_LENGTH); + if (n != DVTOOL_SIGNATURE_LENGTH) { + m_file.Close(); + return false; + } + + if (::memcmp(buffer, DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH) != 0) { + m_file.Close(); + return false; + } + + wxUint32 uint32; + n = m_file.Read(&uint32, sizeof(wxUint32)); + if (n != sizeof(wxUint32)) { + m_file.Close(); + return false; + } + + m_records = wxUINT32_SWAP_ON_LE(uint32); + m_end = false; + + return true; +} + +DVTFR_TYPE CDVTOOLFileReader::read() +{ + wxUint16 uint16; + size_t n = m_file.Read(&uint16, sizeof(wxUint16)); + if (n != sizeof(wxUint16)) + return DVTFR_NONE; + + m_length = wxUINT16_SWAP_ON_BE(uint16) - 15U; + + unsigned char bytes[FIXED_DATA_LENGTH]; + n = m_file.Read(bytes, DSVT_SIGNATURE_LENGTH); + if (n != DSVT_SIGNATURE_LENGTH) + return DVTFR_NONE; + + if (::memcmp(bytes, DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH) != 0) + return DVTFR_NONE; + + char flag; + n = m_file.Read(&flag, 1U); + if (n != 1U) + return DVTFR_NONE; + + m_type = (flag == HEADER_FLAG) ? DVTFR_HEADER : DVTFR_DATA; + + n = m_file.Read(bytes, FIXED_DATA_LENGTH); + if (n != FIXED_DATA_LENGTH) + return DVTFR_NONE; + + n = m_file.Read(&flag, 1U); + if (n != 1U) + return DVTFR_NONE; + + if (m_type == DVTFR_DATA) { + if ((flag & TRAILER_MASK) == TRAILER_MASK) + m_end = true; + } + + n = m_file.Read(m_buffer, m_length); + if (n != m_length) + return DVTFR_NONE; + + return m_type; +} + +CHeaderData* CDVTOOLFileReader::readHeader() +{ + if (m_type != DVTFR_HEADER) + return NULL; + + if (m_buffer[39U] == 0xFFU && m_buffer[40U] == 0xFFU) + return new CHeaderData(m_buffer, RADIO_HEADER_LENGTH_BYTES, false); + + // Header checksum testing is enabled + CHeaderData* header = new CHeaderData(m_buffer, RADIO_HEADER_LENGTH_BYTES, true); + + if (!header->isValid()) { + CUtils::dump(wxT("Header checksum failure"), m_buffer, RADIO_HEADER_LENGTH_BYTES); + delete header; + return NULL; + } + + return header; +} + +unsigned int CDVTOOLFileReader::readData(unsigned char* buffer, unsigned int length, bool& end) +{ + wxASSERT(buffer != NULL); + wxASSERT(length > 0U); + + if (m_type != DVTFR_DATA) + return 0U; + + if (length > m_length) + length = m_length; + + end = m_end; + + ::memcpy(buffer, m_buffer, length); + + return length; +} + +void CDVTOOLFileReader::close() +{ + m_file.Close(); +} diff --git a/Common/DVTOOLFileReader.h b/Common/DVTOOLFileReader.h new file mode 100644 index 0000000..13ba0f1 --- /dev/null +++ b/Common/DVTOOLFileReader.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DVTOOLFileReader_H +#define DVTOOLFileReader_H + +#include "HeaderData.h" + +enum DVTFR_TYPE { + DVTFR_NONE, + DVTFR_HEADER, + DVTFR_DATA +}; + +#include +#include + +class CDVTOOLFileReader { +public: + CDVTOOLFileReader(); + ~CDVTOOLFileReader(); + + wxString getFileName() const; + unsigned int getRecords() const; + + bool open(const wxString& fileName); + DVTFR_TYPE read(); + CHeaderData* readHeader(); + unsigned int readData(unsigned char* buffer, unsigned int length, bool& end); + void close(); + +private: + wxString m_fileName; + wxFFile m_file; + wxUint32 m_records; + DVTFR_TYPE m_type; + unsigned char* m_buffer; + unsigned int m_length; + bool m_end; +}; + +#endif diff --git a/Common/DVTOOLFileWriter.cpp b/Common/DVTOOLFileWriter.cpp new file mode 100644 index 0000000..f642e35 --- /dev/null +++ b/Common/DVTOOLFileWriter.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2009,2011,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CCITTChecksumReverse.h" +#include "DVTOOLFileWriter.h" +#include "DStarDefines.h" + +#include +#include + +static const char DVTOOL_SIGNATURE[] = "DVTOOL"; +static unsigned int DVTOOL_SIGNATURE_LENGTH = 6U; + +static const char DSVT_SIGNATURE[] = "DSVT"; +static unsigned int DSVT_SIGNATURE_LENGTH = 4U; + +static const unsigned char HEADER_FLAG = 0x10; +static const unsigned char DATA_FLAG = 0x20; + +static const unsigned char FIXED_DATA[] = {0x00, 0x81, 0x00, 0x20, 0x00, 0x01, 0x02, 0xC0, 0xDE}; +static unsigned int FIXED_DATA_LENGTH = 9U; + +static const unsigned char TRAILER_DATA[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static unsigned int TRAILER_DATA_LENGTH = 12U; + +static const unsigned char HEADER_MASK = 0x80; +static const unsigned char TRAILER_MASK = 0x40; + +wxString CDVTOOLFileWriter::m_dirName = wxEmptyString; + +CDVTOOLFileWriter::CDVTOOLFileWriter() : +m_fileName(), +m_file(), +m_count(0U), +m_sequence(0U), +m_offset(0) +{ +} + +CDVTOOLFileWriter::~CDVTOOLFileWriter() +{ +} + +void CDVTOOLFileWriter::setDirectory(const wxString& dirName) +{ + m_dirName = dirName; +} + +wxString CDVTOOLFileWriter::getFileName() const +{ + return m_fileName; +} + +bool CDVTOOLFileWriter::open(const wxString& filename, const CHeaderData& header) +{ + if (m_file.IsOpened()) + close(); + + wxString name = filename; +#if !defined(__WINDOWS__) + name.Replace(wxT(" "), wxT("_")); +#endif + + wxFileName fileName(m_dirName, name, wxT("dvtool")); + m_fileName = fileName.GetFullPath(); + + bool res = m_file.Open(m_fileName, wxT("wb")); + if (!res) + return false; + + size_t n = m_file.Write(DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH); + if (n != DVTOOL_SIGNATURE_LENGTH) { + m_file.Close(); + return false; + } + + m_offset = m_file.Tell(); + + wxUint32 dummy = 0U; + n = m_file.Write(&dummy, sizeof(wxUint32)); + if (n != sizeof(wxUint32)) { + m_file.Close(); + return false; + } + + m_sequence = 0U; + m_count = 0U; + + res = writeHeader(header); + if (!res) { + m_file.Close(); + return false; + } + + return true; +} + +bool CDVTOOLFileWriter::open(const CHeaderData& header) +{ + if (m_file.IsOpened()) + close(); + + wxDateTime time; + time.SetToCurrent(); + + wxString name = time.Format(wxT("%Y%m%d-%H%M%S-")); + + name.Append(header.getRptCall1()); + name.Append(header.getRptCall2()); + name.Append(header.getYourCall()); + name.Append(header.getMyCall1()); + name.Append(header.getMyCall2()); + +#if !defined(__WINDOWS__) + name.Replace(wxT(" "), wxT("_")); +#endif + name.Replace(wxT("/"), wxT("-")); + + wxFileName fileName(m_dirName, name, wxT("dvtool")); + m_fileName = fileName.GetFullPath(); + + bool res = m_file.Open(m_fileName, wxT("wb")); + if (!res) + return false; + + size_t n = m_file.Write(DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH); + if (n != DVTOOL_SIGNATURE_LENGTH) { + m_file.Close(); + return false; + } + + m_offset = m_file.Tell(); + + wxUint32 dummy = 0U; + n = m_file.Write(&dummy, sizeof(wxUint32)); + if (n != sizeof(wxUint32)) { + m_file.Close(); + return false; + } + + m_sequence = 0U; + m_count = 0U; + + res = writeHeader(header); + if (!res) { + m_file.Close(); + return false; + } + + return true; +} + +bool CDVTOOLFileWriter::write(const unsigned char* buffer, unsigned int length) +{ + wxASSERT(buffer != 0); + wxASSERT(length > 0U); + + wxUint16 len = wxUINT16_SWAP_ON_BE(length + 15U); + size_t n = m_file.Write(&len, sizeof(wxUint16)); + if (n != sizeof(wxUint16)) { + m_file.Close(); + return false; + } + + n = m_file.Write(DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH); + if (n != DSVT_SIGNATURE_LENGTH) { + m_file.Close(); + return false; + } + + char byte = DATA_FLAG; + n = m_file.Write(&byte, 1U); + if (n != 1U) { + m_file.Close(); + return false; + } + + n = m_file.Write(FIXED_DATA, FIXED_DATA_LENGTH); + if (n != FIXED_DATA_LENGTH) { + m_file.Close(); + return false; + } + + byte = m_sequence; + n = m_file.Write(&byte, 1U); + if (n != 1U) { + m_file.Close(); + return false; + } + + n = m_file.Write(buffer, length); + if (n != length) { + m_file.Close(); + return false; + } + + m_count++; + m_sequence++; + if (m_sequence >= 0x15U) + m_sequence = 0U; + + return true; +} + +void CDVTOOLFileWriter::close() +{ + writeTrailer(); + + m_file.Seek(m_offset); + + wxUint32 count = wxUINT32_SWAP_ON_LE(m_count); + m_file.Write(&count, sizeof(wxUint32)); + + m_file.Close(); +} + +bool CDVTOOLFileWriter::writeHeader(const CHeaderData& header) +{ + unsigned char buffer[RADIO_HEADER_LENGTH_BYTES]; + + buffer[0] = header.getFlag1(); + buffer[1] = header.getFlag2(); + buffer[2] = header.getFlag3(); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[3 + i] = header.getRptCall1().GetChar(i); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[11 + i] = header.getRptCall2().GetChar(i); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[19 + i] = header.getYourCall().GetChar(i); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[27 + i] = header.getMyCall1().GetChar(i); + + for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++) + buffer[35 + i] = header.getMyCall2().GetChar(i); + + // Get the checksum for the header + CCCITTChecksumReverse csum; + csum.update(buffer, RADIO_HEADER_LENGTH_BYTES - 2U); + csum.result(buffer + 39U); + + wxUint16 len = wxUINT16_SWAP_ON_BE(RADIO_HEADER_LENGTH_BYTES + 15U); + size_t n = m_file.Write(&len, sizeof(wxUint16)); + if (n != sizeof(wxUint16)) { + m_file.Close(); + return false; + } + + n = m_file.Write(DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH); + if (n != DSVT_SIGNATURE_LENGTH) { + m_file.Close(); + return false; + } + + char byte = HEADER_FLAG; + n = m_file.Write(&byte, 1U); + if (n != 1U) { + m_file.Close(); + return false; + } + + n = m_file.Write(FIXED_DATA, FIXED_DATA_LENGTH); + if (n != FIXED_DATA_LENGTH) { + m_file.Close(); + return false; + } + + byte = HEADER_MASK; + n = m_file.Write(&byte, 1U); + if (n != 1U) { + m_file.Close(); + return false; + } + + n = m_file.Write(buffer, RADIO_HEADER_LENGTH_BYTES); + if (n != RADIO_HEADER_LENGTH_BYTES) { + m_file.Close(); + return false; + } + + m_count++; + + return true; +} + +bool CDVTOOLFileWriter::writeTrailer() +{ + wxUint16 len = wxUINT16_SWAP_ON_BE(27U); + size_t n = m_file.Write(&len, sizeof(wxUint16)); + if (n != sizeof(wxUint16)) { + m_file.Close(); + return false; + } + + n = m_file.Write(DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH); + if (n != DSVT_SIGNATURE_LENGTH) { + m_file.Close(); + return false; + } + + char byte = DATA_FLAG; + n = m_file.Write(&byte, 1U); + if (n != 1U) { + m_file.Close(); + return false; + } + + n = m_file.Write(FIXED_DATA, FIXED_DATA_LENGTH); + if (n != FIXED_DATA_LENGTH) { + m_file.Close(); + return false; + } + + byte = TRAILER_MASK | m_sequence; + n = m_file.Write(&byte, 1U); + if (n != 1U) { + m_file.Close(); + return false; + } + + n = m_file.Write(TRAILER_DATA, TRAILER_DATA_LENGTH); + if (n != TRAILER_DATA_LENGTH) { + m_file.Close(); + return false; + } + + return true; +} diff --git a/Common/DVTOOLFileWriter.h b/Common/DVTOOLFileWriter.h new file mode 100644 index 0000000..d771614 --- /dev/null +++ b/Common/DVTOOLFileWriter.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DVTOOLFileWriter_H +#define DVTOOLFileWriter_H + +#include "HeaderData.h" + +#include +#include + +class CDVTOOLFileWriter { +public: + CDVTOOLFileWriter(); + ~CDVTOOLFileWriter(); + + static void setDirectory(const wxString& dirName); + + wxString getFileName() const; + + bool open(const CHeaderData& header); + bool open(const wxString& filename, const CHeaderData& header); + bool write(const unsigned char* buffer, unsigned int length); + void close(); + +private: + static wxString m_dirName; + + wxString m_fileName; + wxFFile m_file; + wxUint32 m_count; + unsigned int m_sequence; + wxFileOffset m_offset; + + bool writeHeader(const CHeaderData& header); + bool writeTrailer(); +}; + +#endif diff --git a/Common/DummyController.cpp b/Common/DummyController.cpp new file mode 100644 index 0000000..3f14c0b --- /dev/null +++ b/Common/DummyController.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "DummyController.h" + +#include + +CDummyController::CDummyController() +{ +} + +CDummyController::~CDummyController() +{ +} + +bool CDummyController::open() +{ + return true; +} + +void CDummyController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + inp1 = false; + inp2 = false; + inp3 = false; + inp4 = false; + inp5 = false; +} + +void CDummyController::setDigitalOutputs(bool, bool, bool, bool, bool, bool, bool, bool) +{ +} + +void CDummyController::close() +{ +} diff --git a/Common/DummyController.h b/Common/DummyController.h new file mode 100644 index 0000000..f54604e --- /dev/null +++ b/Common/DummyController.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef DummyController_H +#define DummyController_H + +#include "HardwareController.h" + +class CDummyController : public IHardwareController { +public: + CDummyController(); + virtual ~CDummyController(); + + virtual bool open(); + + virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5); + + virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8); + + virtual void close(); + +private: +}; + +#endif diff --git a/Common/ExternalController.cpp b/Common/ExternalController.cpp new file mode 100644 index 0000000..fdb12be --- /dev/null +++ b/Common/ExternalController.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2009,2010,2014 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ExternalController.h" + +#include "DStarDefines.h" + +CExternalController::CExternalController(IHardwareController* controller, bool pttInvert) : +wxThread(wxTHREAD_JOINABLE), +m_controller(controller), +m_pttInvert(pttInvert), +m_disable(false), +m_heartbeat(false), +m_active(false), +m_radioTX(false), +m_out1(false), +m_out2(false), +m_out3(false), +m_out4(false), +m_kill(false) +{ + // wxASSERT(controller != NULL); + + if (m_pttInvert) + m_radioTX = true; +} + +CExternalController::~CExternalController() +{ + delete m_controller; +} + +bool CExternalController::open() +{ + bool res = m_controller->open(); + if (!res) + return false; + + Create(); + Run(); + + return true; +} + +void* CExternalController::Entry() +{ + wxASSERT(m_controller != NULL); + + bool dummy1, dummy2, dummy3, dummy4; + + while (!m_kill) { + m_controller->setDigitalOutputs(m_radioTX, false, m_heartbeat, m_active, m_out1, m_out2, m_out3, m_out4); + m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, m_disable); + + Sleep(DSTAR_FRAME_TIME_MS / 2U); + } + + if (m_pttInvert) + m_controller->setDigitalOutputs(true, false, false, false, false, false, false, false); + else + m_controller->setDigitalOutputs(false, false, false, false, false, false, false, false); + m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, m_disable); + + Sleep(DSTAR_FRAME_TIME_MS * 3U); + + if (m_pttInvert) + m_controller->setDigitalOutputs(true, false, false, false, false, false, false, false); + else + m_controller->setDigitalOutputs(false, false, false, false, false, false, false, false); + m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, m_disable); + + m_controller->close(); + + return NULL; +} + +bool CExternalController::getDisable() const +{ + return m_disable; +} + +void CExternalController::setRadioTransmit(bool value) +{ + if (m_pttInvert) + value = !value; + + m_radioTX = value; +} + +void CExternalController::setHeartbeat() +{ + m_heartbeat = !m_heartbeat; +} + +void CExternalController::setActive(bool value) +{ + m_active = value; +} + +void CExternalController::setOutput1(bool value) +{ + m_out1 = value; +} + +void CExternalController::setOutput2(bool value) +{ + m_out2 = value; +} + +void CExternalController::setOutput3(bool value) +{ + m_out3 = value; +} + +void CExternalController::setOutput4(bool value) +{ + m_out4 = value; +} + +void CExternalController::close() +{ + m_kill = true; + + Wait(); +} diff --git a/Common/ExternalController.h b/Common/ExternalController.h new file mode 100644 index 0000000..6a804f5 --- /dev/null +++ b/Common/ExternalController.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009,2010,2013,2014 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ExternalController_H +#define ExternalController_H + +#include "HardwareController.h" + +#include + +class CExternalController : public wxThread { +public: + CExternalController(IHardwareController* controller, bool pttInvert); + virtual ~CExternalController(); + + virtual bool open(); + + virtual bool getDisable() const; + + virtual void setRadioTransmit(bool value); + virtual void setHeartbeat(); + virtual void setActive(bool value); + virtual void setOutput1(bool value); + virtual void setOutput2(bool value); + virtual void setOutput3(bool value); + virtual void setOutput4(bool value); + + virtual void close(); + + virtual void* Entry(); + +private: + IHardwareController* m_controller; + bool m_pttInvert; + bool m_disable; + bool m_heartbeat; + bool m_active; + bool m_radioTX; + bool m_out1; + bool m_out2; + bool m_out3; + bool m_out4; + bool m_kill; +}; + +#endif diff --git a/Common/FIRFilter.cpp b/Common/FIRFilter.cpp new file mode 100644 index 0000000..c397b49 --- /dev/null +++ b/Common/FIRFilter.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2001, 2002, 2003 by Tomi Manninen, OH2BNS + * Copyright (C) 2009 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "FIRFilter.h" + +CFIRFilter::CFIRFilter(const wxFloat32* taps, unsigned int length) : +m_taps(NULL), +m_length(length), +m_buffer(NULL), +m_bufLen(20U * m_length), +m_pointer(length) +{ + wxASSERT(taps != NULL); + wxASSERT(length > 0U); + + m_taps = new wxFloat32[m_length]; + m_buffer = new wxFloat32[m_bufLen]; + + ::memcpy(m_taps, taps, m_length * sizeof(wxFloat32)); + ::memset(m_buffer, 0x00, m_bufLen * sizeof(wxFloat32)); +} + +CFIRFilter::CFIRFilter() : +m_taps(NULL), +m_length(0U), +m_buffer(NULL), +m_bufLen(0U), +m_pointer(0U) +{ +} + +CFIRFilter::~CFIRFilter() +{ + delete[] m_taps; + delete[] m_buffer; +} + +void CFIRFilter::setTaps(const wxFloat32* taps, unsigned int length) +{ + wxASSERT(taps != NULL); + wxASSERT(length > 0U); + + delete[] m_taps; + delete[] m_buffer; + + m_length = length; + m_pointer = length; + m_bufLen = 20U * m_length; + + m_taps = new wxFloat32[m_length]; + m_buffer = new wxFloat32[m_bufLen]; + + ::memcpy(m_taps, taps, m_length * sizeof(wxFloat32)); + ::memset(m_buffer, 0x00, m_bufLen * sizeof(wxFloat32)); +} + +wxFloat32 CFIRFilter::process(wxFloat32 val) +{ + wxFloat32* ptr = m_buffer + m_pointer++; + + *ptr = val; + + wxFloat32* a = ptr - m_length; + wxFloat32* b = m_taps; + + wxFloat32 out = 0.0F; + for (unsigned int i = 0U; i < m_length; i++) + out += (*a++) * (*b++); + + if (m_pointer == m_bufLen) { + ::memcpy(m_buffer, m_buffer + m_bufLen - m_length, m_length * sizeof(wxFloat32)); + m_pointer = m_length; + } + + return out; +} + +void CFIRFilter::process(wxFloat32* inOut, unsigned int length) +{ + wxASSERT(inOut != NULL); + + for (unsigned int i = 0U; i < length; i++) + inOut[i] = process(inOut[i]); +} + +void CFIRFilter::process(const wxFloat32* in, wxFloat32* out, unsigned int length) +{ + wxASSERT(in != NULL); + wxASSERT(out != NULL); + + for (unsigned int i = 0U; i < length; i++) + out[i] = process(in[i]); +} + +void CFIRFilter::reset() +{ + ::memset(m_buffer, 0x00, m_bufLen * sizeof(wxFloat32));; +} diff --git a/Common/FIRFilter.h b/Common/FIRFilter.h new file mode 100644 index 0000000..b164212 --- /dev/null +++ b/Common/FIRFilter.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef FIRFilter_H +#define FIRFilter_H + +#include + +class CFIRFilter { +public: + CFIRFilter(const wxFloat32* taps, unsigned int length); + CFIRFilter(); + ~CFIRFilter(); + + void setTaps(const wxFloat32* taps, unsigned int length); + + wxFloat32 process(wxFloat32 val); + void process(wxFloat32* inOut, unsigned int length); + void process(const wxFloat32* in, wxFloat32* out, unsigned int length); + + void reset(); + +private: + wxFloat32* m_taps; + unsigned int m_length; + wxFloat32* m_buffer; + unsigned int m_bufLen; + unsigned int m_pointer; +}; + +#endif diff --git a/Common/GMSKController.cpp b/Common/GMSKController.cpp new file mode 100644 index 0000000..00d8162 --- /dev/null +++ b/Common/GMSKController.cpp @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "GMSKController.h" +#if defined(__WINDOWS__) +#include "GMSKModemWinUSB.h" +#endif +#include "GMSKModemLibUsb.h" +#include "Timer.h" + +const unsigned char DVRPTR_HEADER_LENGTH = 5U; + +const unsigned int BUFFER_LENGTH = 200U; + +const unsigned int CYCLE_TIME = 15U; + +CGMSKController::CGMSKController(USB_INTERFACE iface, unsigned int address, bool duplex) : +CModem(), +m_modem(NULL), +m_duplex(duplex), +m_buffer(NULL), +m_txData(1000U) +{ + wxASSERT(address > 0U); + + m_buffer = new unsigned char[BUFFER_LENGTH]; + +#if defined(__WINDOWS__) + switch (iface) { + case UI_LIBUSB: + m_modem = new CGMSKModemLibUsb(address); + break; + case UI_WINUSB: + m_modem = new CGMSKModemWinUSB(address); + break; + default: + wxLogError(wxT("Unknown GMSM modem driver type - %d"), int(iface)); + break; + } +#else + m_modem = new CGMSKModemLibUsb(address); +#endif +} + +CGMSKController::~CGMSKController() +{ + delete[] m_buffer; +} + +bool CGMSKController::start() +{ + if (m_modem == NULL) + return false; + + bool ret = m_modem->open(); + if (!ret) { + delete m_modem; + return false; + } + + Create(); + SetPriority(100U); + Run(); + + return true; +} + +void* CGMSKController::Entry() +{ + wxLogMessage(wxT("Starting GMSK Modem Controller thread")); + + CTimer hdrTimer(1000U, 0U, 100U); + hdrTimer.start(); + + CTimer dataTimer(1000U, 0U, 100U); + + bool rx = false; + + DSMT_TYPE writeType = DSMTT_HEADER; + unsigned char writeLength = 0U; + unsigned char* writeBuffer = new unsigned char[BUFFER_LENGTH]; + + unsigned char readLength = 0U; + unsigned char* readBuffer = new unsigned char[DV_FRAME_LENGTH_BYTES]; + + wxStopWatch stopWatch; + + while (!m_stopped) { + stopWatch.Start(); + + // Only receive when not transmitting or when in duplex mode + if (!m_tx || m_duplex) { + if (rx) { + unsigned char buffer[GMSK_MODEM_DATA_LENGTH]; + bool end; + int ret = m_modem->readData(buffer, GMSK_MODEM_DATA_LENGTH, end); + if (ret >= 0) { + // CUtils::dump(wxT("Read Data"), buffer, ret); + + if (end) { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + hdrTimer.start(); + readLength = 0U; + rx = false; + } else { + for (int i = 0; i < ret; i++) { + readBuffer[readLength] = buffer[i]; + + readLength++; + if (readLength >= DV_FRAME_LENGTH_BYTES) { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(readBuffer, DV_FRAME_LENGTH_BYTES); + readLength = 0U; + } + } + } + } else { + ret = reopenModem(); + if (!ret) + break; + } + } else { + // Check for a header every 100ms or so + if (hdrTimer.isRunning() && hdrTimer.hasExpired()) { + unsigned char buffer[90U]; + bool ret = m_modem->readHeader(buffer, 90U); + if (ret) { + // CUtils::dump(wxT("Read Header"), buffer, RADIO_HEADER_LENGTH_BYTES); + + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES - 2U; + m_rxData.addData(data, 2U); + + m_rxData.addData(buffer, RADIO_HEADER_LENGTH_BYTES - 2U); + + hdrTimer.stop(); + readLength = 0U; + rx = true; + } else { + hdrTimer.start(); + } + } + } + } + + // Only transmit when not receiving or when in duplex mode + if (!rx || m_duplex) { + if (writeLength == 0U && m_txData.hasData()) { + wxMutexLocker locker(m_mutex); + + unsigned char type = DSMTT_NONE; + m_txData.getData(&type, 1U); + writeType = DSMT_TYPE(type); + + m_txData.getData(&writeLength, 1U); + m_txData.getData(writeBuffer, writeLength); + } + + if (writeLength > 0U) { + if (writeType == DSMTT_HEADER) { + TRISTATE tx = m_modem->getPTT(); + + // Check that the modem isn't still transmitting before sending the new header + if (tx == STATE_FALSE) { + // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + m_modem->writeHeader(writeBuffer, writeLength); + m_modem->setPTT(true); + dataTimer.start(); + writeLength = 0U; + m_tx = true; + } else if (tx == STATE_UNKNOWN) { + bool ret = reopenModem(); + if (!ret) + break; + } + } else { + // Don't start sending data until the header has been gone for 100ms or so + if (!dataTimer.isRunning() || dataTimer.hasExpired()) { + dataTimer.stop(); + + TRISTATE ret = m_modem->hasSpace(); + + // Check that there is space in the modem buffer + if (ret == STATE_TRUE) { + // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + int ret = m_modem->writeData(writeBuffer, writeLength); + if (ret > 0) { + writeLength -= ret; + + if (writeType == DSMTT_EOT) { + m_modem->setPTT(false); + m_tx = false; + } + } else if (ret < 0) { + ret = reopenModem(); + if (!ret) + break; + } + } else if (ret == STATE_UNKNOWN) { + bool ret = reopenModem(); + if (!ret) + break; + } + } + } + } + } + + unsigned long ms = stopWatch.Time(); + + // Don't sleep when reading from the modem + if (!rx) { + if (ms < CYCLE_TIME) + Sleep(CYCLE_TIME - ms); + + ms = stopWatch.Time(); + } + + // Catch up with the clock + dataTimer.clock(ms); + hdrTimer.clock(ms); + } + + wxLogMessage(wxT("Stopping GMSK Modem Controller thread")); + + if (m_modem != NULL) { + m_modem->close(); + delete m_modem; + } + + delete[] writeBuffer; + delete[] readBuffer; + + return NULL; +} + +bool CGMSKController::writeHeader(const CHeaderData& header) +{ + bool ret = m_txData.hasSpace(RADIO_HEADER_LENGTH_BYTES); + if (!ret) { + wxLogWarning(wxT("No space to write the header")); + return false; + } + + unsigned char buffer[50U]; + + ::memset(buffer, ' ', RADIO_HEADER_LENGTH_BYTES - 2U); + + buffer[0U] = header.getFlag1(); + buffer[1U] = header.getFlag2(); + buffer[2U] = header.getFlag3(); + + wxString rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 3U] = rpt2.GetChar(i); + + wxString rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 11U] = rpt1.GetChar(i); + + wxString your = header.getYourCall(); + for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 19U] = your.GetChar(i); + + wxString my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 27U] = my1.GetChar(i); + + wxString my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 35U] = my2.GetChar(i); + + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES - 2U; + m_txData.addData(data, 2U); + + m_txData.addData(buffer, RADIO_HEADER_LENGTH_BYTES - 2U); + + return true; +} + +bool CGMSKController::writeData(const unsigned char* data, unsigned int, bool end) +{ + bool ret = m_txData.hasSpace(DV_FRAME_LENGTH_BYTES + 2U); + if (!ret) { + wxLogWarning(wxT("No space to write data")); + return false; + } + + wxMutexLocker locker(m_mutex); + + unsigned char buffer[2U]; + buffer[0U] = end ? DSMTT_EOT : DSMTT_DATA; + buffer[1U] = DV_FRAME_LENGTH_BYTES; + m_txData.addData(buffer, 2U); + + m_txData.addData(data, DV_FRAME_LENGTH_BYTES); + + return true; +} + +unsigned int CGMSKController::getSpace() +{ + return m_txData.freeSpace() / (DV_FRAME_LENGTH_BYTES + 2U); +} + +bool CGMSKController::isTXReady() +{ + if (m_tx) + return false; + + return m_txData.isEmpty(); +} + +bool CGMSKController::reopenModem() +{ + wxLogMessage(wxT("Connection to the GMSK modem has been lost")); + + m_modem->close(); + + while (!m_stopped) { + bool ret = m_modem->open(); + if (ret) + return true; + + // Reset the drivers state + m_mutex.Lock(); + m_txData.clear(); + m_mutex.Unlock(); + + m_tx = false; + + Sleep(1000UL); + } + + delete m_modem; + m_modem = NULL; + + return false; + +} diff --git a/Common/GMSKController.h b/Common/GMSKController.h new file mode 100644 index 0000000..6eab923 --- /dev/null +++ b/Common/GMSKController.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef GMSKController_H +#define GMSKController_H + +#include "DStarDefines.h" +#include "RingBuffer.h" +#include "GMSKModem.h" +#include "Modem.h" +#include "Utils.h" + +#include + +class CGMSKController : public CModem { +public: + CGMSKController(USB_INTERFACE iface, unsigned int address, bool duplex); + virtual ~CGMSKController(); + + virtual void* Entry(); + + virtual bool start(); + + virtual unsigned int getSpace(); + virtual bool isTXReady(); + + virtual bool writeHeader(const CHeaderData& header); + virtual bool writeData(const unsigned char* data, unsigned int length, bool end); + +private: + IGMSKModem* m_modem; + bool m_duplex; + unsigned char* m_buffer; + CRingBuffer m_txData; + + bool reopenModem(); +}; + +#endif diff --git a/Common/GMSKModem.cpp b/Common/GMSKModem.cpp new file mode 100644 index 0000000..b78ee8a --- /dev/null +++ b/Common/GMSKModem.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "GMSKModem.h" + +IGMSKModem::~IGMSKModem() +{ +} diff --git a/Common/GMSKModem.h b/Common/GMSKModem.h new file mode 100644 index 0000000..7c1a8c3 --- /dev/null +++ b/Common/GMSKModem.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef GMSKModem_H +#define GMSKModem_H + +#include "Utils.h" + +#include + +class IGMSKModem { +public: + virtual ~IGMSKModem() = 0; + + virtual bool open() = 0; + + virtual bool readHeader(unsigned char* header, unsigned int length) = 0; + virtual int readData(unsigned char* data, unsigned int length, bool& end) = 0; + + virtual TRISTATE getPTT() = 0; + virtual void setPTT(bool on) = 0; + + virtual TRISTATE hasSpace() = 0; + + virtual void writeHeader(unsigned char* data, unsigned int length) = 0; + virtual int writeData(unsigned char* data, unsigned int length) = 0; + + virtual void close() = 0; + +private: +}; + +#endif + diff --git a/Common/GMSKModemLibUsb.cpp b/Common/GMSKModemLibUsb.cpp new file mode 100644 index 0000000..d827090 --- /dev/null +++ b/Common/GMSKModemLibUsb.cpp @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2010-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "GMSKModemLibUsb.h" +#include "DStarDefines.h" + +const unsigned int VENDOR_ID = 0x04D8U; + +const int USB_TIMEOUT = 1000; + +const int SET_AD_INIT = 0x00; +const int SET_PTT = 0x05; +const int PUT_DATA = 0x10; +const int GET_DATA = 0x11; +const int GET_HEADER = 0x21; +const int GET_AD_STATUS = 0x30; +const int SET_MyCALL = 0x40; +const int SET_MyCALL2 = 0x41; +const int SET_YourCALL = 0x42; +const int SET_RPT1CALL = 0x43; +const int SET_RPT2CALL = 0x44; +const int SET_FLAGS = 0x45; +const int GET_REMAINSPACE = 0x50; +const int GET_VERSION = 0xFF; + +const char COS_OnOff = 0x02; +const char CRC_ERROR = 0x04; +const char LAST_FRAME = 0x08; +const char PTT_OnOff = 0x20; + +const int PTT_ON = 1; +const int PTT_OFF = 0; + +#if defined(WIN32) +const wxString LIBNAME = wxT("libusb0"); + +wxDynamicLibrary* CGMSKModemLibUsb::m_library = NULL; +bool CGMSKModemLibUsb::m_loaded = false; + +void (*CGMSKModemLibUsb::m_usbInit)() = NULL; +int (*CGMSKModemLibUsb::m_usbFindBusses)() = NULL; +int (*CGMSKModemLibUsb::m_usbFindDevices)() = NULL; +usb_bus* (*CGMSKModemLibUsb::m_usbGetBusses)() = NULL; +usb_dev_handle* (*CGMSKModemLibUsb::m_usbOpen)(struct usb_device*) = NULL; +int (*CGMSKModemLibUsb::m_usbSetConfiguration)(usb_dev_handle*, int) = NULL; +int (*CGMSKModemLibUsb::m_usbControlMsg)(usb_dev_handle*, int, int, int, int, unsigned char*, int, int) = NULL; +char* (*CGMSKModemLibUsb::m_usbStrerror)() = NULL; +int (*CGMSKModemLibUsb::m_usbClose)(usb_dev_handle*) = NULL; +#endif + +static void libUsbLogError(int ret, const char *message) { +#if defined(WIN32) + wxString errorText(CGMSKModemLibUsb::m_usbStrerror(), wxConvLocal); +#else + wxString errorText(libusb_error_name(ret), wxConvLocal); +#endif + wxLogMessage(wxT("%s, ret: %d, err=%s"), message, ret, errorText.c_str()); +} + +CGMSKModemLibUsb::CGMSKModemLibUsb(unsigned int address) : +m_address(address), +m_dev(NULL), +m_brokenSpace(false) +#if !defined(WIN32) +,m_context(NULL) +#endif +{ + +#if defined(WIN32) + if (m_library == NULL) + m_library = new wxDynamicLibrary(LIBNAME); + + if (!m_library->IsLoaded()) { + wxLogError(wxT("Unable to load shared library %s"), LIBNAME.c_str()); + return; + } + + void* ptr1 = m_library->GetSymbol(wxT("usb_init")); + void* ptr2 = m_library->GetSymbol(wxT("usb_find_busses")); + void* ptr3 = m_library->GetSymbol(wxT("usb_find_devices")); + void* ptr4 = m_library->GetSymbol(wxT("usb_get_busses")); + void* ptr5 = m_library->GetSymbol(wxT("usb_open")); + void* ptr6 = m_library->GetSymbol(wxT("usb_set_configuration")); + void* ptr7 = m_library->GetSymbol(wxT("usb_control_msg")); + void* ptr8 = m_library->GetSymbol(wxT("usb_strerror")); + void* ptr9 = m_library->GetSymbol(wxT("usb_close")); + + if (ptr1 == NULL || ptr2 == NULL || ptr3 == NULL || + ptr4 == NULL || ptr5 == NULL || ptr6 == NULL || + ptr7 == NULL || ptr8 == NULL || ptr9 == NULL) { + wxLogError(wxT("Unable to get symbols from %s"), LIBNAME.c_str()); + return; + } + + m_usbInit = (void (*)())ptr1; + m_usbFindBusses = (int (*)())ptr2; + m_usbFindDevices = (int (*)())ptr3; + m_usbGetBusses = (usb_bus* (*)())ptr4; + m_usbOpen = (usb_dev_handle* (*)(struct usb_device*))ptr5; + m_usbSetConfiguration = (int (*)(usb_dev_handle*, int))ptr6; + m_usbControlMsg = (int (*)(usb_dev_handle*, int, int, int, int, unsigned char*, int, int))ptr7; + m_usbStrerror = (char* (*)())ptr8; + m_usbClose = (int (*)(usb_dev_handle*))ptr9; + + wxLogMessage(wxT("Successfully loaded library %s"), LIBNAME.c_str()); + + m_loaded = true; +#else + ::libusb_init(&m_context); +#endif +} + + +CGMSKModemLibUsb::~CGMSKModemLibUsb() +{ +#if !defined(WIN32) + wxASSERT(m_context != NULL); + ::libusb_exit(m_context); +#endif +} + +bool CGMSKModemLibUsb::open() +{ +#if !defined(WIN32) + wxASSERT(m_context != NULL); +#endif + wxASSERT(m_dev == NULL); + + bool ret1 = openModem(); + if (!ret1) { + wxLogError(wxT("Cannot find the GMSK Modem with address: 0x%04X"), m_address); + return false; + } + + wxLogInfo(wxT("Found the GMSK Modem with address: 0x%04X"), m_address); + + wxString version; + + int ret2; + do { + unsigned char buffer[GMSK_MODEM_DATA_LENGTH]; + ret2 = io(0xC0, GET_VERSION, 0, 0, buffer, GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT); + if (ret2 > 0) { + wxString text((char *) buffer, wxConvLocal, ret2); + version.Append(text); + } else if (ret2 < 0) { + ::libUsbLogError(ret2, "GET_VERSION"); + close(); + return false; + } + } while (ret2 == int(GMSK_MODEM_DATA_LENGTH)); + + wxLogInfo(wxT("Firmware version: %s"), version.c_str()); + + // Trap firmware version 0.1.00 of DUTCH*Star and complain loudly + if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND && version.Find(wxT("0.1.00")) != wxNOT_FOUND) { + wxLogWarning(wxT("This modem firmware is not supported by the repeater")); + wxLogWarning(wxT("Please upgrade to a newer version")); + close(); + return false; + } + + // DUTCH*Star firmware has a broken concept of free space + if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND) + m_brokenSpace = true; + + return true; +} + +bool CGMSKModemLibUsb::readHeader(unsigned char* header, unsigned int length) +{ + wxASSERT(header != NULL); + wxASSERT(length > (RADIO_HEADER_LENGTH_BYTES * 2U)); + + unsigned int offset = 0U; + + while (offset < RADIO_HEADER_LENGTH_BYTES) { + int ret = io(0xC0, GET_HEADER, 0, 0, (header + offset), 8, USB_TIMEOUT); + if (ret < 0) { + ::libUsbLogError(ret, "GET_HEADER"); + + if (ret == -19) // -ENODEV + return false; + + ::wxMilliSleep(10UL); + } else if (ret == 0) { + if (offset == 0U) + return false; + + ::wxMilliSleep(10UL); + + unsigned char status; + int ret = io(0xC0, GET_AD_STATUS, 0, 0, &status, 1, USB_TIMEOUT); + if (ret < 0) { + ::libUsbLogError(ret, "GET_COS"); + + if (ret == -19) // -ENODEV + return false; + + ::wxMilliSleep(10UL); + } else if (ret > 0) { + if ((status & COS_OnOff) == COS_OnOff) + offset = 0U; + } + } else { + offset += ret; + } + } + + unsigned char status; + int ret = io(0xC0, GET_AD_STATUS, 0, 0, &status, 1, USB_TIMEOUT); + if (ret < 0) { + ::libUsbLogError(ret, "GET_CRC"); + return false; + } + + if ((status & CRC_ERROR) == CRC_ERROR) { + wxLogMessage(wxT("Header - CRC Error")); + return false; + } + + return true; +} + +int CGMSKModemLibUsb::readData(unsigned char* data, unsigned int length, bool& end) +{ + wxASSERT(data != NULL); + wxASSERT(length > 0U); + + end = false; + + int ret = io(0xC0, GET_DATA, 0, 0, data, GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT); + if (ret < 0) { + if (ret == -19) { // -ENODEV + ::libUsbLogError(ret, "GET_DATA"); + return ret; + } + + return 0; + } else if (ret == 0) { + unsigned char status; + int ret = io(0xC0, GET_AD_STATUS, 0, 0, &status, 1, USB_TIMEOUT); + if (ret < 0) { + ::libUsbLogError(ret, "LAST_FRAME"); + + if (ret == -19) // -ENODEV + return ret; + + return 0; + } + + if ((status & LAST_FRAME) == LAST_FRAME) + end = true; + } + + return ret; +} + +void CGMSKModemLibUsb::writeHeader(unsigned char* header, unsigned int length) +{ + wxASSERT(header != NULL); + wxASSERT(length >= (RADIO_HEADER_LENGTH_BYTES - 2U)); + + io(0x40, SET_MyCALL2, 0, 0, (header + 35U), SHORT_CALLSIGN_LENGTH, USB_TIMEOUT); + io(0x40, SET_MyCALL, 0, 0, (header + 27U), LONG_CALLSIGN_LENGTH, USB_TIMEOUT); + io(0x40, SET_YourCALL, 0, 0, (header + 19U), LONG_CALLSIGN_LENGTH, USB_TIMEOUT); + io(0x40, SET_RPT1CALL, 0, 0, (header + 11U), LONG_CALLSIGN_LENGTH, USB_TIMEOUT); + io(0x40, SET_RPT2CALL, 0, 0, (header + 3U), LONG_CALLSIGN_LENGTH, USB_TIMEOUT); + io(0x40, SET_FLAGS, 0, 0, (header + 0U), 3U, USB_TIMEOUT); +} + +TRISTATE CGMSKModemLibUsb::hasSpace() +{ + unsigned char space; + int rc = io(0xC0, GET_REMAINSPACE, 0, 0, &space, 1, USB_TIMEOUT); + if (rc != 1) { + ::libUsbLogError(rc, "GET_REMAINSPACE"); + return STATE_UNKNOWN; + } + + if (space >= DV_FRAME_LENGTH_BYTES) + return STATE_TRUE; + else + return STATE_FALSE; +} + +TRISTATE CGMSKModemLibUsb::getPTT() +{ + unsigned char status; + int rc = io(0xC0, GET_AD_STATUS, 0, 0, &status, 1, USB_TIMEOUT); + if (rc < 1) { + ::libUsbLogError(rc, "GET_PTT"); + return STATE_UNKNOWN; + } + + if ((status & PTT_OnOff) == PTT_OnOff) + return STATE_TRUE; + else + return STATE_FALSE; +} + +void CGMSKModemLibUsb::setPTT(bool on) +{ + unsigned char c; + io(0x40, SET_PTT, on ? PTT_ON : PTT_OFF, 0, &c, 0, USB_TIMEOUT); +} + +int CGMSKModemLibUsb::writeData(unsigned char* data, unsigned int length) +{ + wxASSERT(data != NULL); + wxASSERT(length > 0U && length <= DV_FRAME_LENGTH_BYTES); + + if (length > GMSK_MODEM_DATA_LENGTH) { + int ret = io(0x40, PUT_DATA, 0, 0, data, GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT); + if (ret < 0) { + if (ret == -19) { // -ENODEV + ::libUsbLogError(ret, "PUT_DATA 1"); + return ret; + } + + return 0; + } + + // Give libUSB some recovery time + ::wxMilliSleep(3UL); + + ret = io(0x40, PUT_DATA, 0, 0, (data + GMSK_MODEM_DATA_LENGTH), length - GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT); + if (ret < 0) { + if (ret == -19) { // -ENODEV + ::libUsbLogError(ret, "PUT_DATA 2"); + return ret; + } + + return int(GMSK_MODEM_DATA_LENGTH); + } + + return length; + } else { + int ret = io(0x40, PUT_DATA, 0, 0, data, length, USB_TIMEOUT); + if (ret < 0) { + if (ret == -19) { // -ENODEV + ::libUsbLogError(ret, "PUT_DATA"); + return ret; + } + + return 0; + } + + return length; + } +} + +void CGMSKModemLibUsb::close() +{ + wxASSERT(m_dev != NULL); + +#if defined(WIN32) + m_usbClose(m_dev); +#else + libusb_close(m_dev); +#endif + m_dev = NULL; +} + +bool CGMSKModemLibUsb::openModem() +{ +#if defined(WIN32) + if (!m_loaded) + return false; + + m_usbInit(); + m_usbFindBusses(); + m_usbFindDevices(); + + for (struct usb_bus* bus = m_usbGetBusses(); bus != NULL; bus = bus->next) { + for (struct usb_device* dev = bus->devices; dev != NULL; dev = dev->next) { + if (dev->descriptor.idVendor == VENDOR_ID && dev->descriptor.idProduct == m_address) { + m_dev = m_usbOpen(dev); + break; + } + } + } + + if (m_dev == NULL) + return false; + + m_usbSetConfiguration(m_dev, 1); +#else + m_dev = ::libusb_open_device_with_vid_pid(m_context, VENDOR_ID, m_address); + if (m_dev == NULL) + return false; + + ::libusb_set_configuration(m_dev, 1); +#endif + + unsigned char c; + io(0x40, SET_AD_INIT, 0, 0, &c, 0, USB_TIMEOUT); + + setPTT(false); + + return true; +} + +int CGMSKModemLibUsb::io(uint8_t requestType, uint8_t request, uint16_t value, + uint16_t index, unsigned char* data, uint16_t length, + unsigned int timeout) +{ + wxASSERT(m_dev != NULL); + wxASSERT(data != NULL); + + int ret = 0; + for (unsigned int i = 0U; i < 4U; i++) { +#if defined(WIN32) + ret = m_usbControlMsg(m_dev, requestType, request, value, index, data, length, timeout); +#else + ret = ::libusb_control_transfer(m_dev, requestType, request, value, index, data, length, timeout); +#endif + if (ret >= 0) + return ret; + + if (ret == -19) // ENODEV + return ret; + + ::wxMilliSleep(5UL); + } + + return ret; +} \ No newline at end of file diff --git a/Common/GMSKModemLibUsb.h b/Common/GMSKModemLibUsb.h new file mode 100644 index 0000000..ccc01e5 --- /dev/null +++ b/Common/GMSKModemLibUsb.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2010-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef GMSKModemLibUsb_H +#define GMSKModemLibUsb_H + +#include "GMSKModem.h" +#include "Utils.h" + +#include +#if defined(WIN32) +#include +#if _MSC_VER == 1900 +#undef __USB_H__ +#include +#else +#include "lusb0_usb.h" +#endif +#else +#include +#endif + +class CGMSKModemLibUsb : public IGMSKModem { +public: + CGMSKModemLibUsb(unsigned int address); + virtual ~CGMSKModemLibUsb(); + + virtual bool open(); + + virtual bool readHeader(unsigned char* header, unsigned int length); + virtual int readData(unsigned char* data, unsigned int length, bool& end); + + virtual TRISTATE getPTT(); + virtual void setPTT(bool on); + + virtual TRISTATE hasSpace(); + + virtual void writeHeader(unsigned char* data, unsigned int length); + virtual int writeData(unsigned char* data, unsigned int length); + + virtual void close(); +#if defined(WIN32) + static char* (*m_usbStrerror)(); +#endif + +private: + unsigned int m_address; +#if defined(WIN32) + struct usb_dev_handle* m_dev; + + static wxDynamicLibrary* m_library; + static bool m_loaded; + + static void (*m_usbInit)(); + static int (*m_usbFindBusses)(); + static int (*m_usbFindDevices)(); + static struct usb_bus* (*m_usbGetBusses)(); + static usb_dev_handle* (*m_usbOpen)(struct usb_device*); + static int (*m_usbSetConfiguration)(usb_dev_handle*, int); + static int (*m_usbControlMsg)(usb_dev_handle*, int, int, int, int, unsigned char*, int, int); + static int (*m_usbClose)(usb_dev_handle*); + +#else + libusb_context* m_context; + libusb_device_handle* m_dev; + +#endif + int io(uint8_t requestType, uint8_t request, uint16_t value, uint16_t index, unsigned char* data, uint16_t length, unsigned int timeout); + + bool m_brokenSpace; + + bool openModem(); +}; + +#endif diff --git a/Common/GMSKModemWinUSB.cpp b/Common/GMSKModemWinUSB.cpp new file mode 100644 index 0000000..40bc13e --- /dev/null +++ b/Common/GMSKModemWinUSB.cpp @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2010-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "GMSKModemWinUSB.h" +#include "DStarDefines.h" + +#include + +const unsigned char SET_AD_INIT = 0x00U; +const unsigned char SET_PTT = 0x05U; +const unsigned char PUT_DATA = 0x10U; +const unsigned char GET_DATA = 0x11U; +const unsigned char GET_HEADER = 0x21U; +const unsigned char GET_AD_STATUS = 0x30U; +const unsigned char SET_MyCALL = 0x40U; +const unsigned char SET_MyCALL2 = 0x41U; +const unsigned char SET_YourCALL = 0x42U; +const unsigned char SET_RPT1CALL = 0x43U; +const unsigned char SET_RPT2CALL = 0x44U; +const unsigned char SET_FLAGS = 0x45U; +const unsigned char GET_REMAINSPACE = 0x50U; +const unsigned char GET_VERSION = 0xFFU; + +const unsigned char COS_OnOff = 0x02U; +const unsigned char CRC_ERROR = 0x04U; +const unsigned char LAST_FRAME = 0x08U; +const unsigned char PTT_OnOff = 0x20U; + +const unsigned char PTT_ON = 1U; +const unsigned char PTT_OFF = 0U; + +CGMSKModemWinUSB::CGMSKModemWinUSB(unsigned int address) : +m_address(address), +m_file(INVALID_HANDLE_VALUE), +m_handle(INVALID_HANDLE_VALUE), +m_brokenSpace(false) +{ +} + +CGMSKModemWinUSB::~CGMSKModemWinUSB() +{ +} + +bool CGMSKModemWinUSB::open() +{ + wxASSERT(m_handle == INVALID_HANDLE_VALUE); + + bool res = openModem(); + if (!res) { + wxLogError(wxT("Cannot find the GMSK Modem with address: 0x%04X"), m_address); + return false; + } + + wxLogInfo(wxT("Found the GMSK Modem with address: 0x%04X"), m_address); + + wxString version; + + int ret; + do { + unsigned char buffer[GMSK_MODEM_DATA_LENGTH]; + ret = io(GET_VERSION, 0xC0U, 0U, buffer, GMSK_MODEM_DATA_LENGTH); + if (ret > 0) { + wxString text((char*)buffer, wxConvLocal, ret); + version.Append(text); + } else if (ret < 0) { + wxLogError(wxT("GET_VERSION returned %d"), -ret); + close(); + return false; + } + } while (ret == int(GMSK_MODEM_DATA_LENGTH)); + + wxLogInfo(wxT("Firmware version: %s"), version.c_str()); + + // Trap firmware version 0.1.00 of DUTCH*Star and complain loudly + if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND && version.Find(wxT("0.1.00")) != wxNOT_FOUND) { + wxLogWarning(wxT("This modem firmware is not supported by the repeater")); + wxLogWarning(wxT("Please upgrade to a newer version")); + close(); + return false; + } + + // DUTCH*Star firmware has a broken concept of free space + if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND) + m_brokenSpace = true; + + return true; +} + +bool CGMSKModemWinUSB::readHeader(unsigned char* header, unsigned int length) +{ + wxASSERT(header != NULL); + wxASSERT(length > (RADIO_HEADER_LENGTH_BYTES * 2U)); + + unsigned int offset = 0U; + + while (offset < RADIO_HEADER_LENGTH_BYTES) { + int ret = io(GET_HEADER, 0xC0U, 0U, header + offset, GMSK_MODEM_DATA_LENGTH); + if (ret < 0) { + wxLogError(wxT("GET_HEADER returned %d"), -ret); + return false; + } else if (ret == 0) { + if (offset == 0U) + return false; + + ::wxMilliSleep(10UL); + + unsigned char status; + ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U); + if (ret < 0) { + wxLogError(wxT("GET_COS returned %d"), -ret); + return false; + } else if (ret > 0) { + if ((status & COS_OnOff) == COS_OnOff) + offset = 0U; + } + } else { + offset += ret; + } + } + + unsigned char status; + int ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U); + if (ret < 0) { + wxLogError(wxT("GET_CRC returned %d"), -ret); + return false; + } + + if ((status & CRC_ERROR) == CRC_ERROR) { + wxLogMessage(wxT("Invalid CRC on header")); + return false; + } + + return true; +} + +int CGMSKModemWinUSB::readData(unsigned char* data, unsigned int length, bool& end) +{ + wxASSERT(data != NULL); + wxASSERT(length > 0U); + + end = false; + + int ret = io(GET_DATA, 0xC0U, 0U, data, GMSK_MODEM_DATA_LENGTH); + if (ret < 0) { + wxLogError(wxT("GET_DATA returned %d"), -ret); + return ret; + } else if (ret == 0) { + unsigned char status; + int ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U); + if (ret < 0) { + wxLogError(wxT("LAST_FRAME returned %d"), -ret); + return ret; + } + + if ((status & LAST_FRAME) == LAST_FRAME) + end = true; + } + + return ret; +} + +void CGMSKModemWinUSB::writeHeader(unsigned char* header, unsigned int length) +{ + wxASSERT(header != NULL); + wxASSERT(length >= (RADIO_HEADER_LENGTH_BYTES - 2U)); + + io(SET_MyCALL2, 0x40U, 0U, header + 35U, SHORT_CALLSIGN_LENGTH); + io(SET_MyCALL, 0x40U, 0U, header + 27U, LONG_CALLSIGN_LENGTH); + io(SET_YourCALL, 0x40U, 0U, header + 19U, LONG_CALLSIGN_LENGTH); + io(SET_RPT1CALL, 0x40U, 0U, header + 11U, LONG_CALLSIGN_LENGTH); + io(SET_RPT2CALL, 0x40U, 0U, header + 3U, LONG_CALLSIGN_LENGTH); + io(SET_FLAGS, 0x40U, 0U, header + 0U, 3U); +} + +TRISTATE CGMSKModemWinUSB::getPTT() +{ + unsigned char status; + int ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U); + if (ret != 1) { + wxLogError(wxT("GET_PTT returned %d"), -ret); + return STATE_UNKNOWN; + } + + if ((status & PTT_OnOff) == PTT_OnOff) + return STATE_TRUE; + else + return STATE_FALSE; +} + +void CGMSKModemWinUSB::setPTT(bool on) +{ + unsigned char c; + io(SET_PTT, 0x40U, on ? PTT_ON : PTT_OFF, &c, 0U); +} + +TRISTATE CGMSKModemWinUSB::hasSpace() +{ + unsigned char space; + int ret = io(GET_REMAINSPACE, 0xC0U, 0U, &space, 1U); + if (ret != 1) { + wxLogError(wxT("GET_REMAINSPACE returned %d"), -ret); + return STATE_UNKNOWN; + } + + if (space >= DV_FRAME_LENGTH_BYTES) + return STATE_TRUE; + else + return STATE_FALSE; +} + +int CGMSKModemWinUSB::writeData(unsigned char* data, unsigned int length) +{ + wxASSERT(data != NULL); + wxASSERT(length > 0U && length <= DV_FRAME_LENGTH_BYTES); + + if (length > GMSK_MODEM_DATA_LENGTH) { + int ret = io(PUT_DATA, 0x40U, 0U, data, GMSK_MODEM_DATA_LENGTH); + if (ret < 0) { + if (ret == -22) { + wxLogError(wxT("PUT_DATA 1, returned %d"), -ret); + return ret; + } + + return 0; + } + + ret = io(PUT_DATA, 0x40U, 0U, data + GMSK_MODEM_DATA_LENGTH, length - GMSK_MODEM_DATA_LENGTH); + if (ret < 0) { + if (ret == -22) { + wxLogError(wxT("PUT_DATA 2, returned %d"), -ret); + return ret; + } + + return int(GMSK_MODEM_DATA_LENGTH); + } + + return length; + } else { + int ret = io(PUT_DATA, 0x40U, 0U, data, length); + if (ret < 0) { + if (ret == -22) { + wxLogError(wxT("PUT_DATA returned %d"), -ret); + return ret; + } + + return 0; + } + + return length; + } +} + +void CGMSKModemWinUSB::close() +{ + wxASSERT(m_file != INVALID_HANDLE_VALUE); + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + ::CloseHandle(m_file); + ::WinUsb_Free(m_handle); + + m_file = INVALID_HANDLE_VALUE; + m_handle = INVALID_HANDLE_VALUE; +} + +bool CGMSKModemWinUSB::openModem() +{ + WCHAR id1[15U], id2[15U]; + ::swprintf(id1, L"pid_%04x", m_address); + ::swprintf(id2, L"pid_%04X", m_address); + + wxString wxId1(id1, wxConvLocal); + wxString wxId2(id2, wxConvLocal); + + CLSID clsId; + LPOLESTR str = OLESTR("{136C76EF-3F4E-4030-A7E3-E1003EF0A715}"); + HRESULT result = ::CLSIDFromString(str, &clsId); + if (result != NOERROR) { + wxLogError(wxT("Error from CLSIDFromString: err=%lu"), ::GetLastError()); + return false; + } + + HDEVINFO devInfo = ::SetupDiGetClassDevs(&clsId, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (devInfo == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from SetupDiGetClassDevs: err=%lu"), ::GetLastError()); + return false; + } + + SP_DEVICE_INTERFACE_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + for (unsigned int index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &clsId, index, &devInfoData); index++) { + DWORD length; + ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + DWORD required; + BOOL ret1 = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); + if (!ret1) { + wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%lu"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + // Check the name to see if it's the correct vendor id and address + if (::wcsstr(detailData->DevicePath, id1) == NULL && ::wcsstr(detailData->DevicePath, id2) == NULL) { + ::free(detailData); + continue; + } + + m_file = ::CreateFile(detailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (m_file == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from CreateFile: err=%lu"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ret1 = ::WinUsb_Initialize(m_file, &m_handle); + if (!ret1) { + wxLogError(wxT("Error from WinUsb_Initialize: err=%lu"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::CloseHandle(m_file); + ::free(detailData); + return false; + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + + unsigned char c; + io(SET_AD_INIT, 0x40U, 0U, &c, 0U); + + setPTT(false); + + return true; + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + + return false; +} + +int CGMSKModemWinUSB::io(unsigned char type, unsigned char n1, unsigned char n2, unsigned char* buffer, unsigned int length) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + WINUSB_SETUP_PACKET packet; + packet.RequestType = n1; + packet.Request = type; + packet.Value = n2; + packet.Index = 0U; + packet.Length = length; + + ULONG transferred; + BOOL ret = ::WinUsb_ControlTransfer(m_handle, packet, buffer, length, &transferred, 0); + if (!ret) { + long error = ::GetLastError(); + return -error; + } + + return transferred; +} + diff --git a/Common/GMSKModemWinUSB.h b/Common/GMSKModemWinUSB.h new file mode 100644 index 0000000..789061d --- /dev/null +++ b/Common/GMSKModemWinUSB.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef GMSKModemWinUSB_H +#define GMSKModemWinUSB_H + +#include "GMSKModem.h" +#include "Utils.h" + +#include + +#include "winusb.h" + +class CGMSKModemWinUSB : public IGMSKModem { +public: + CGMSKModemWinUSB(unsigned int address); + virtual ~CGMSKModemWinUSB(); + + virtual bool open(); + + virtual bool readHeader(unsigned char* header, unsigned int length); + virtual int readData(unsigned char* data, unsigned int length, bool& end); + + virtual TRISTATE getPTT(); + virtual void setPTT(bool on); + + virtual TRISTATE hasSpace(); + + virtual void writeHeader(unsigned char* data, unsigned int length); + virtual int writeData(unsigned char* data, unsigned int length); + + virtual void close(); + +private: + unsigned int m_address; + HANDLE m_file; + WINUSB_INTERFACE_HANDLE m_handle; + bool m_brokenSpace; + + bool openModem(); + int io(unsigned char type, unsigned char n1, unsigned char n2, unsigned char* buffer, unsigned int length); +}; + +#endif + diff --git a/Common/GPIOController.cpp b/Common/GPIOController.cpp new file mode 100644 index 0000000..d98ccfc --- /dev/null +++ b/Common/GPIOController.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2012,2013,2015 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "GPIOController.h" + +CGPIOController::CGPIOController(unsigned int config) : +m_config(config), +m_outp1(false), +m_outp2(false), +m_outp3(false), +m_outp4(false), +m_outp5(false), +m_outp6(false), +m_outp7(false), +m_outp8(false) +{ +} + +CGPIOController::~CGPIOController() +{ +} + +#include + +bool CGPIOController::open() +{ + bool ret = ::wiringPiSetup() != -1; + if (!ret) { + wxLogError(wxT("Unable to initialise wiringPi")); + return false; + } + + ::pinMode(8, INPUT); + ::pinMode(9, INPUT); + ::pinMode(7, INPUT); + ::pinMode(0, INPUT); + ::pinMode(2, INPUT); + + // Set pull ups on the input pins + ::pullUpDnControl(8, PUD_UP); + ::pullUpDnControl(9, PUD_UP); + ::pullUpDnControl(7, PUD_UP); + ::pullUpDnControl(0, PUD_UP); + ::pullUpDnControl(2, PUD_UP); + + ::pinMode(12, OUTPUT); + ::pinMode(13, OUTPUT); + ::pinMode(14, OUTPUT); + ::pinMode(11, OUTPUT); + ::pinMode(10, OUTPUT); + ::pinMode(6, OUTPUT); + ::pinMode(5, OUTPUT); + ::pinMode(4, OUTPUT); + + setDigitalOutputs(false, false, false, false, false, false, false, false); + + return true; +} + +void CGPIOController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + inp1 = ::digitalRead(8) == LOW; + + inp2 = ::digitalRead(9) == LOW; + + inp3 = ::digitalRead(7) == LOW; + + inp4 = ::digitalRead(0) == LOW; + + inp5 = ::digitalRead(2) == LOW; +} + +void CGPIOController::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8) +{ + if (m_config == 1U) { + if (outp1 != m_outp1) { + ::digitalWrite(12, outp1 ? HIGH : LOW); + m_outp1 = outp1; + } + + if (outp2 != m_outp2) { + ::digitalWrite(13, outp2 ? HIGH : LOW); + m_outp2 = outp2; + } + + if (outp3 != m_outp3) { + ::digitalWrite(14, outp3 ? HIGH : LOW); + m_outp3 = outp3; + } + + if (outp4 != m_outp4) { + ::digitalWrite(11, outp4 ? HIGH : LOW); + m_outp4 = outp4; + } + + if (outp5 != m_outp5) { + ::digitalWrite(10, outp5 ? HIGH : LOW); + m_outp5 = outp5; + } + + if (outp6 != m_outp6) { + ::digitalWrite(6, outp6 ? HIGH : LOW); + m_outp6 = outp6; + } + + if (outp7 != m_outp7) { + ::digitalWrite(5, outp7 ? HIGH : LOW); + m_outp7 = outp7; + } + + if (outp8 != m_outp8) { + ::digitalWrite(4, outp8 ? HIGH : LOW); + m_outp8 = outp8; + } + } else { + if (outp1 != m_outp1) { + ::digitalWrite(4, outp1 ? HIGH : LOW); + m_outp1 = outp1; + } + + if (outp2 != m_outp2) { + ::digitalWrite(5, outp2 ? HIGH : LOW); + m_outp2 = outp2; + } + + if (outp3 != m_outp3) { + ::digitalWrite(6, outp3 ? HIGH : LOW); + m_outp3 = outp3; + } + + if (outp4 != m_outp4) { + ::digitalWrite(10, outp4 ? HIGH : LOW); + m_outp4 = outp4; + } + + if (outp5 != m_outp5) { + ::digitalWrite(11, outp5 ? HIGH : LOW); + m_outp5 = outp5; + } + + if (outp6 != m_outp6) { + ::digitalWrite(14, outp6 ? HIGH : LOW); + m_outp6 = outp6; + } + + if (outp7 != m_outp7) { + ::digitalWrite(13, outp7 ? HIGH : LOW); + m_outp7 = outp7; + } + + if (outp8 != m_outp8) { + ::digitalWrite(12, outp8 ? HIGH : LOW); + m_outp8 = outp8; + } + } +} + +void CGPIOController::close() +{ +} diff --git a/Common/GPIOController.h b/Common/GPIOController.h new file mode 100644 index 0000000..aa170f0 --- /dev/null +++ b/Common/GPIOController.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012,2015 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef GPIOController_H +#define GPIOController_H + +#include "HardwareController.h" + +#include + +class CGPIOController : public IHardwareController { +public: + CGPIOController(unsigned int config); + virtual ~CGPIOController(); + + virtual bool open(); + + virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5); + + virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8); + + virtual void close(); + +private: + unsigned int m_config; + bool m_outp1; + bool m_outp2; + bool m_outp3; + bool m_outp4; + bool m_outp5; + bool m_outp6; + bool m_outp7; + bool m_outp8; +}; + +#endif diff --git a/Common/GatewayProtocolHandler.cpp b/Common/GatewayProtocolHandler.cpp new file mode 100644 index 0000000..d0b11d9 --- /dev/null +++ b/Common/GatewayProtocolHandler.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2009-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "GatewayProtocolHandler.h" +#include "CCITTChecksumReverse.h" +#include "DStarDefines.h" +#include "Utils.h" + +// #define DUMP_TX + +const unsigned int BUFFER_LENGTH = 255U; + +CGatewayProtocolHandler::CGatewayProtocolHandler(const wxString& localAddress, unsigned int localPort) : +m_socket(localAddress, localPort), +m_type(NETWORK_NONE), +m_buffer(NULL), +m_length(0U) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; + + wxDateTime now = wxDateTime::UNow(); + ::srand(now.GetMillisecond()); +} + +CGatewayProtocolHandler::~CGatewayProtocolHandler() +{ + delete[] m_buffer; +} + +bool CGatewayProtocolHandler::open() +{ + return m_socket.open(); +} + +bool CGatewayProtocolHandler::writeHeader(const unsigned char* header, wxUint16 id, const in_addr& address, unsigned int port) +{ + unsigned char buffer[50U]; + + buffer[0] = 'D'; + buffer[1] = 'S'; + buffer[2] = 'R'; + buffer[3] = 'P'; + + buffer[4] = 0x20U; + + buffer[5] = id / 256U; // Unique session id + buffer[6] = id % 256U; + + buffer[7] = 0U; + + ::memcpy(buffer + 8U, header + 0U, RADIO_HEADER_LENGTH_BYTES - 2U); + + // Get the checksum for the header + CCCITTChecksumReverse csum; + csum.update(buffer + 8U, RADIO_HEADER_LENGTH_BYTES - 2U); + csum.result(buffer + 8U + RADIO_HEADER_LENGTH_BYTES - 2U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Header"), buffer, 49U); +#endif + + for (unsigned int i = 0U; i < 4U; i++) { + bool ret = m_socket.write(buffer, 49U, address, port); + if (!ret) + return false; + } + + return true; +} + +bool CGatewayProtocolHandler::writeData(const unsigned char* data, unsigned int length, wxUint16 id, wxUint8 seqNo, const in_addr& address, unsigned int port) +{ + wxASSERT(data != NULL); + wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES); + + unsigned char buffer[30U]; + + buffer[0] = 'D'; + buffer[1] = 'S'; + buffer[2] = 'R'; + buffer[3] = 'P'; + + buffer[4] = 0x21U; + + buffer[5] = id / 256U; // Unique session id + buffer[6] = id % 256U; + + buffer[7] = seqNo; + + buffer[8] = 0U; + + ::memcpy(buffer + 9U, data, length); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Data"), buffer, length + 9U); +#endif + + return m_socket.write(buffer, length + 9U, address, port); +} + +NETWORK_TYPE CGatewayProtocolHandler::read(wxUint16& id, in_addr& address, unsigned int& port) +{ + bool res = true; + + // Loop until we have no more data from the socket or we have data for the higher layers + while (res) + res = readPackets(id, address, port); + + return m_type; +} + +bool CGatewayProtocolHandler::readPackets(wxUint16& id, in_addr& address, unsigned int& port) +{ + m_type = NETWORK_NONE; + + // No more data? + int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return false; + + m_length = length; + + // Invalid packet type? + if (m_buffer[0] == 'D' && m_buffer[1] == 'S' && m_buffer[2] == 'R' && m_buffer[3] == 'P') { + // Header data + if (m_buffer[4] == 0x20U) { + id = m_buffer[5U] * 256U + m_buffer[6U]; + m_type = NETWORK_HEADER; + return false; + } + + // User data + else if (m_buffer[4] == 0x21U) { + id = m_buffer[5U] * 256U + m_buffer[6U]; + m_type = NETWORK_DATA; + return false; + } + + // Register data + else if (m_buffer[4] == 0x0BU) { + m_type = NETWORK_REGISTER; + return false; + } + } + + CUtils::dump(wxT("Unknown packet from the Repeater"), m_buffer, m_length); + + return true; +} + +unsigned int CGatewayProtocolHandler::readHeader(unsigned char* buffer, unsigned int length) +{ + if (m_type != NETWORK_HEADER) + return 0U; + + // If the checksum is 0xFFFF then we accept the header without testing the checksum + if (m_buffer[47U] == 0xFFU && m_buffer[48U] == 0xFFU) { + ::memcpy(buffer, m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); + return RADIO_HEADER_LENGTH_BYTES; + } + + // Get the checksum for the header + CCCITTChecksumReverse csum; + csum.update(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES - 2U); + + bool check = csum.check(m_buffer + 8U + RADIO_HEADER_LENGTH_BYTES - 2U); + if (!check) { + CUtils::dump(wxT("Header checksum failure from the Repeater"), m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); + return 0U; + } + + ::memcpy(buffer, m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); + + return RADIO_HEADER_LENGTH_BYTES; +} + +unsigned int CGatewayProtocolHandler::readData(unsigned char* buffer, unsigned int length, wxUint8& seqNo, unsigned int& errors) +{ + if (m_type != NETWORK_DATA) + return 0U; + + unsigned int dataLen = m_length - 9U; + + // Is our buffer too small? + if (dataLen > length) + dataLen = length; + + seqNo = m_buffer[7U]; + + errors = m_buffer[8U]; + + ::memcpy(buffer, m_buffer + 9U, dataLen); + + // Simple sanity checks of the incoming sync bits + if (seqNo == 0U) { + // Regenerate sync bytes + buffer[9U] = DATA_SYNC_BYTES[0U]; + buffer[10U] = DATA_SYNC_BYTES[1U]; + buffer[11U] = DATA_SYNC_BYTES[2U]; + } else if (::memcmp(buffer + 9U, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES) == 0) { + // Sync bytes appearing where they shouldn't! + buffer[9U] = 0x70U; + buffer[10U] = 0x4FU; + buffer[11U] = 0x93U; + } + + return dataLen; +} + +unsigned int CGatewayProtocolHandler::readRegister(wxString& name) +{ + if (m_type != NETWORK_REGISTER) + return 0U; + + name = wxString((char*)(m_buffer + 5U), wxConvLocal); + + return m_length - 6U; +} + +void CGatewayProtocolHandler::close() +{ + m_socket.close(); +} diff --git a/Common/GatewayProtocolHandler.h b/Common/GatewayProtocolHandler.h new file mode 100644 index 0000000..107080e --- /dev/null +++ b/Common/GatewayProtocolHandler.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef GatewayProtocolHander_H +#define GatewayProtocolHander_H + +#include "UDPReaderWriter.h" +#include "DStarDefines.h" + +#include +#include + +class CGatewayProtocolHandler { +public: + CGatewayProtocolHandler(const wxString& localAddress, unsigned int localPort); + ~CGatewayProtocolHandler(); + + bool open(); + + bool writeHeader(const unsigned char* header, wxUint16 id, const in_addr& address, unsigned int port); + bool writeData(const unsigned char* data, unsigned int length, wxUint16 id, wxUint8 seqNo, const in_addr& address, unsigned int port); + + NETWORK_TYPE read(wxUint16& id, in_addr& address, unsigned int& port); + unsigned int readHeader(unsigned char* data, unsigned int length); + unsigned int readData(unsigned char* data, unsigned int length, wxUint8& seqNo, unsigned int& errors); + unsigned int readRegister(wxString& name); + + void close(); + +private: + CUDPReaderWriter m_socket; + NETWORK_TYPE m_type; + unsigned char* m_buffer; + unsigned int m_length; + + bool readPackets(wxUint16& id, in_addr& address, unsigned int& port); +}; + +#endif diff --git a/Common/Golay.cpp b/Common/Golay.cpp new file mode 100644 index 0000000..3ab026b --- /dev/null +++ b/Common/Golay.cpp @@ -0,0 +1,1092 @@ +/* + * Copyright (C) 2010 by Jonathan Naylor G4KLX + * Copyright (C) 2002 by Robert H. Morelos-Zaragoza. All rights reserved. + */ + +#include "Golay.h" + +static const unsigned int ENCODING_TABLE_23127[] = { + 0x000000U, 0x0018EAU, 0x00293EU, 0x0031D4U, 0x004A96U, 0x00527CU, 0x0063A8U, 0x007B42U, 0x008DC6U, 0x00952CU, + 0x00A4F8U, 0x00BC12U, 0x00C750U, 0x00DFBAU, 0x00EE6EU, 0x00F684U, 0x010366U, 0x011B8CU, 0x012A58U, 0x0132B2U, + 0x0149F0U, 0x01511AU, 0x0160CEU, 0x017824U, 0x018EA0U, 0x01964AU, 0x01A79EU, 0x01BF74U, 0x01C436U, 0x01DCDCU, + 0x01ED08U, 0x01F5E2U, 0x0206CCU, 0x021E26U, 0x022FF2U, 0x023718U, 0x024C5AU, 0x0254B0U, 0x026564U, 0x027D8EU, + 0x028B0AU, 0x0293E0U, 0x02A234U, 0x02BADEU, 0x02C19CU, 0x02D976U, 0x02E8A2U, 0x02F048U, 0x0305AAU, 0x031D40U, + 0x032C94U, 0x03347EU, 0x034F3CU, 0x0357D6U, 0x036602U, 0x037EE8U, 0x03886CU, 0x039086U, 0x03A152U, 0x03B9B8U, + 0x03C2FAU, 0x03DA10U, 0x03EBC4U, 0x03F32EU, 0x040D98U, 0x041572U, 0x0424A6U, 0x043C4CU, 0x04470EU, 0x045FE4U, + 0x046E30U, 0x0476DAU, 0x04805EU, 0x0498B4U, 0x04A960U, 0x04B18AU, 0x04CAC8U, 0x04D222U, 0x04E3F6U, 0x04FB1CU, + 0x050EFEU, 0x051614U, 0x0527C0U, 0x053F2AU, 0x054468U, 0x055C82U, 0x056D56U, 0x0575BCU, 0x058338U, 0x059BD2U, + 0x05AA06U, 0x05B2ECU, 0x05C9AEU, 0x05D144U, 0x05E090U, 0x05F87AU, 0x060B54U, 0x0613BEU, 0x06226AU, 0x063A80U, + 0x0641C2U, 0x065928U, 0x0668FCU, 0x067016U, 0x068692U, 0x069E78U, 0x06AFACU, 0x06B746U, 0x06CC04U, 0x06D4EEU, + 0x06E53AU, 0x06FDD0U, 0x070832U, 0x0710D8U, 0x07210CU, 0x0739E6U, 0x0742A4U, 0x075A4EU, 0x076B9AU, 0x077370U, + 0x0785F4U, 0x079D1EU, 0x07ACCAU, 0x07B420U, 0x07CF62U, 0x07D788U, 0x07E65CU, 0x07FEB6U, 0x0803DAU, 0x081B30U, + 0x082AE4U, 0x08320EU, 0x08494CU, 0x0851A6U, 0x086072U, 0x087898U, 0x088E1CU, 0x0896F6U, 0x08A722U, 0x08BFC8U, + 0x08C48AU, 0x08DC60U, 0x08EDB4U, 0x08F55EU, 0x0900BCU, 0x091856U, 0x092982U, 0x093168U, 0x094A2AU, 0x0952C0U, + 0x096314U, 0x097BFEU, 0x098D7AU, 0x099590U, 0x09A444U, 0x09BCAEU, 0x09C7ECU, 0x09DF06U, 0x09EED2U, 0x09F638U, + 0x0A0516U, 0x0A1DFCU, 0x0A2C28U, 0x0A34C2U, 0x0A4F80U, 0x0A576AU, 0x0A66BEU, 0x0A7E54U, 0x0A88D0U, 0x0A903AU, + 0x0AA1EEU, 0x0AB904U, 0x0AC246U, 0x0ADAACU, 0x0AEB78U, 0x0AF392U, 0x0B0670U, 0x0B1E9AU, 0x0B2F4EU, 0x0B37A4U, + 0x0B4CE6U, 0x0B540CU, 0x0B65D8U, 0x0B7D32U, 0x0B8BB6U, 0x0B935CU, 0x0BA288U, 0x0BBA62U, 0x0BC120U, 0x0BD9CAU, + 0x0BE81EU, 0x0BF0F4U, 0x0C0E42U, 0x0C16A8U, 0x0C277CU, 0x0C3F96U, 0x0C44D4U, 0x0C5C3EU, 0x0C6DEAU, 0x0C7500U, + 0x0C8384U, 0x0C9B6EU, 0x0CAABAU, 0x0CB250U, 0x0CC912U, 0x0CD1F8U, 0x0CE02CU, 0x0CF8C6U, 0x0D0D24U, 0x0D15CEU, + 0x0D241AU, 0x0D3CF0U, 0x0D47B2U, 0x0D5F58U, 0x0D6E8CU, 0x0D7666U, 0x0D80E2U, 0x0D9808U, 0x0DA9DCU, 0x0DB136U, + 0x0DCA74U, 0x0DD29EU, 0x0DE34AU, 0x0DFBA0U, 0x0E088EU, 0x0E1064U, 0x0E21B0U, 0x0E395AU, 0x0E4218U, 0x0E5AF2U, + 0x0E6B26U, 0x0E73CCU, 0x0E8548U, 0x0E9DA2U, 0x0EAC76U, 0x0EB49CU, 0x0ECFDEU, 0x0ED734U, 0x0EE6E0U, 0x0EFE0AU, + 0x0F0BE8U, 0x0F1302U, 0x0F22D6U, 0x0F3A3CU, 0x0F417EU, 0x0F5994U, 0x0F6840U, 0x0F70AAU, 0x0F862EU, 0x0F9EC4U, + 0x0FAF10U, 0x0FB7FAU, 0x0FCCB8U, 0x0FD452U, 0x0FE586U, 0x0FFD6CU, 0x1007B4U, 0x101F5EU, 0x102E8AU, 0x103660U, + 0x104D22U, 0x1055C8U, 0x10641CU, 0x107CF6U, 0x108A72U, 0x109298U, 0x10A34CU, 0x10BBA6U, 0x10C0E4U, 0x10D80EU, + 0x10E9DAU, 0x10F130U, 0x1104D2U, 0x111C38U, 0x112DECU, 0x113506U, 0x114E44U, 0x1156AEU, 0x11677AU, 0x117F90U, + 0x118914U, 0x1191FEU, 0x11A02AU, 0x11B8C0U, 0x11C382U, 0x11DB68U, 0x11EABCU, 0x11F256U, 0x120178U, 0x121992U, + 0x122846U, 0x1230ACU, 0x124BEEU, 0x125304U, 0x1262D0U, 0x127A3AU, 0x128CBEU, 0x129454U, 0x12A580U, 0x12BD6AU, + 0x12C628U, 0x12DEC2U, 0x12EF16U, 0x12F7FCU, 0x13021EU, 0x131AF4U, 0x132B20U, 0x1333CAU, 0x134888U, 0x135062U, + 0x1361B6U, 0x13795CU, 0x138FD8U, 0x139732U, 0x13A6E6U, 0x13BE0CU, 0x13C54EU, 0x13DDA4U, 0x13EC70U, 0x13F49AU, + 0x140A2CU, 0x1412C6U, 0x142312U, 0x143BF8U, 0x1440BAU, 0x145850U, 0x146984U, 0x14716EU, 0x1487EAU, 0x149F00U, + 0x14AED4U, 0x14B63EU, 0x14CD7CU, 0x14D596U, 0x14E442U, 0x14FCA8U, 0x15094AU, 0x1511A0U, 0x152074U, 0x15389EU, + 0x1543DCU, 0x155B36U, 0x156AE2U, 0x157208U, 0x15848CU, 0x159C66U, 0x15ADB2U, 0x15B558U, 0x15CE1AU, 0x15D6F0U, + 0x15E724U, 0x15FFCEU, 0x160CE0U, 0x16140AU, 0x1625DEU, 0x163D34U, 0x164676U, 0x165E9CU, 0x166F48U, 0x1677A2U, + 0x168126U, 0x1699CCU, 0x16A818U, 0x16B0F2U, 0x16CBB0U, 0x16D35AU, 0x16E28EU, 0x16FA64U, 0x170F86U, 0x17176CU, + 0x1726B8U, 0x173E52U, 0x174510U, 0x175DFAU, 0x176C2EU, 0x1774C4U, 0x178240U, 0x179AAAU, 0x17AB7EU, 0x17B394U, + 0x17C8D6U, 0x17D03CU, 0x17E1E8U, 0x17F902U, 0x18046EU, 0x181C84U, 0x182D50U, 0x1835BAU, 0x184EF8U, 0x185612U, + 0x1867C6U, 0x187F2CU, 0x1889A8U, 0x189142U, 0x18A096U, 0x18B87CU, 0x18C33EU, 0x18DBD4U, 0x18EA00U, 0x18F2EAU, + 0x190708U, 0x191FE2U, 0x192E36U, 0x1936DCU, 0x194D9EU, 0x195574U, 0x1964A0U, 0x197C4AU, 0x198ACEU, 0x199224U, + 0x19A3F0U, 0x19BB1AU, 0x19C058U, 0x19D8B2U, 0x19E966U, 0x19F18CU, 0x1A02A2U, 0x1A1A48U, 0x1A2B9CU, 0x1A3376U, + 0x1A4834U, 0x1A50DEU, 0x1A610AU, 0x1A79E0U, 0x1A8F64U, 0x1A978EU, 0x1AA65AU, 0x1ABEB0U, 0x1AC5F2U, 0x1ADD18U, + 0x1AECCCU, 0x1AF426U, 0x1B01C4U, 0x1B192EU, 0x1B28FAU, 0x1B3010U, 0x1B4B52U, 0x1B53B8U, 0x1B626CU, 0x1B7A86U, + 0x1B8C02U, 0x1B94E8U, 0x1BA53CU, 0x1BBDD6U, 0x1BC694U, 0x1BDE7EU, 0x1BEFAAU, 0x1BF740U, 0x1C09F6U, 0x1C111CU, + 0x1C20C8U, 0x1C3822U, 0x1C4360U, 0x1C5B8AU, 0x1C6A5EU, 0x1C72B4U, 0x1C8430U, 0x1C9CDAU, 0x1CAD0EU, 0x1CB5E4U, + 0x1CCEA6U, 0x1CD64CU, 0x1CE798U, 0x1CFF72U, 0x1D0A90U, 0x1D127AU, 0x1D23AEU, 0x1D3B44U, 0x1D4006U, 0x1D58ECU, + 0x1D6938U, 0x1D71D2U, 0x1D8756U, 0x1D9FBCU, 0x1DAE68U, 0x1DB682U, 0x1DCDC0U, 0x1DD52AU, 0x1DE4FEU, 0x1DFC14U, + 0x1E0F3AU, 0x1E17D0U, 0x1E2604U, 0x1E3EEEU, 0x1E45ACU, 0x1E5D46U, 0x1E6C92U, 0x1E7478U, 0x1E82FCU, 0x1E9A16U, + 0x1EABC2U, 0x1EB328U, 0x1EC86AU, 0x1ED080U, 0x1EE154U, 0x1EF9BEU, 0x1F0C5CU, 0x1F14B6U, 0x1F2562U, 0x1F3D88U, + 0x1F46CAU, 0x1F5E20U, 0x1F6FF4U, 0x1F771EU, 0x1F819AU, 0x1F9970U, 0x1FA8A4U, 0x1FB04EU, 0x1FCB0CU, 0x1FD3E6U, + 0x1FE232U, 0x1FFAD8U, 0x200F68U, 0x201782U, 0x202656U, 0x203EBCU, 0x2045FEU, 0x205D14U, 0x206CC0U, 0x20742AU, + 0x2082AEU, 0x209A44U, 0x20AB90U, 0x20B37AU, 0x20C838U, 0x20D0D2U, 0x20E106U, 0x20F9ECU, 0x210C0EU, 0x2114E4U, + 0x212530U, 0x213DDAU, 0x214698U, 0x215E72U, 0x216FA6U, 0x21774CU, 0x2181C8U, 0x219922U, 0x21A8F6U, 0x21B01CU, + 0x21CB5EU, 0x21D3B4U, 0x21E260U, 0x21FA8AU, 0x2209A4U, 0x22114EU, 0x22209AU, 0x223870U, 0x224332U, 0x225BD8U, + 0x226A0CU, 0x2272E6U, 0x228462U, 0x229C88U, 0x22AD5CU, 0x22B5B6U, 0x22CEF4U, 0x22D61EU, 0x22E7CAU, 0x22FF20U, + 0x230AC2U, 0x231228U, 0x2323FCU, 0x233B16U, 0x234054U, 0x2358BEU, 0x23696AU, 0x237180U, 0x238704U, 0x239FEEU, + 0x23AE3AU, 0x23B6D0U, 0x23CD92U, 0x23D578U, 0x23E4ACU, 0x23FC46U, 0x2402F0U, 0x241A1AU, 0x242BCEU, 0x243324U, + 0x244866U, 0x24508CU, 0x246158U, 0x2479B2U, 0x248F36U, 0x2497DCU, 0x24A608U, 0x24BEE2U, 0x24C5A0U, 0x24DD4AU, + 0x24EC9EU, 0x24F474U, 0x250196U, 0x25197CU, 0x2528A8U, 0x253042U, 0x254B00U, 0x2553EAU, 0x25623EU, 0x257AD4U, + 0x258C50U, 0x2594BAU, 0x25A56EU, 0x25BD84U, 0x25C6C6U, 0x25DE2CU, 0x25EFF8U, 0x25F712U, 0x26043CU, 0x261CD6U, + 0x262D02U, 0x2635E8U, 0x264EAAU, 0x265640U, 0x266794U, 0x267F7EU, 0x2689FAU, 0x269110U, 0x26A0C4U, 0x26B82EU, + 0x26C36CU, 0x26DB86U, 0x26EA52U, 0x26F2B8U, 0x27075AU, 0x271FB0U, 0x272E64U, 0x27368EU, 0x274DCCU, 0x275526U, + 0x2764F2U, 0x277C18U, 0x278A9CU, 0x279276U, 0x27A3A2U, 0x27BB48U, 0x27C00AU, 0x27D8E0U, 0x27E934U, 0x27F1DEU, + 0x280CB2U, 0x281458U, 0x28258CU, 0x283D66U, 0x284624U, 0x285ECEU, 0x286F1AU, 0x2877F0U, 0x288174U, 0x28999EU, + 0x28A84AU, 0x28B0A0U, 0x28CBE2U, 0x28D308U, 0x28E2DCU, 0x28FA36U, 0x290FD4U, 0x29173EU, 0x2926EAU, 0x293E00U, + 0x294542U, 0x295DA8U, 0x296C7CU, 0x297496U, 0x298212U, 0x299AF8U, 0x29AB2CU, 0x29B3C6U, 0x29C884U, 0x29D06EU, + 0x29E1BAU, 0x29F950U, 0x2A0A7EU, 0x2A1294U, 0x2A2340U, 0x2A3BAAU, 0x2A40E8U, 0x2A5802U, 0x2A69D6U, 0x2A713CU, + 0x2A87B8U, 0x2A9F52U, 0x2AAE86U, 0x2AB66CU, 0x2ACD2EU, 0x2AD5C4U, 0x2AE410U, 0x2AFCFAU, 0x2B0918U, 0x2B11F2U, + 0x2B2026U, 0x2B38CCU, 0x2B438EU, 0x2B5B64U, 0x2B6AB0U, 0x2B725AU, 0x2B84DEU, 0x2B9C34U, 0x2BADE0U, 0x2BB50AU, + 0x2BCE48U, 0x2BD6A2U, 0x2BE776U, 0x2BFF9CU, 0x2C012AU, 0x2C19C0U, 0x2C2814U, 0x2C30FEU, 0x2C4BBCU, 0x2C5356U, + 0x2C6282U, 0x2C7A68U, 0x2C8CECU, 0x2C9406U, 0x2CA5D2U, 0x2CBD38U, 0x2CC67AU, 0x2CDE90U, 0x2CEF44U, 0x2CF7AEU, + 0x2D024CU, 0x2D1AA6U, 0x2D2B72U, 0x2D3398U, 0x2D48DAU, 0x2D5030U, 0x2D61E4U, 0x2D790EU, 0x2D8F8AU, 0x2D9760U, + 0x2DA6B4U, 0x2DBE5EU, 0x2DC51CU, 0x2DDDF6U, 0x2DEC22U, 0x2DF4C8U, 0x2E07E6U, 0x2E1F0CU, 0x2E2ED8U, 0x2E3632U, + 0x2E4D70U, 0x2E559AU, 0x2E644EU, 0x2E7CA4U, 0x2E8A20U, 0x2E92CAU, 0x2EA31EU, 0x2EBBF4U, 0x2EC0B6U, 0x2ED85CU, + 0x2EE988U, 0x2EF162U, 0x2F0480U, 0x2F1C6AU, 0x2F2DBEU, 0x2F3554U, 0x2F4E16U, 0x2F56FCU, 0x2F6728U, 0x2F7FC2U, + 0x2F8946U, 0x2F91ACU, 0x2FA078U, 0x2FB892U, 0x2FC3D0U, 0x2FDB3AU, 0x2FEAEEU, 0x2FF204U, 0x3008DCU, 0x301036U, + 0x3021E2U, 0x303908U, 0x30424AU, 0x305AA0U, 0x306B74U, 0x30739EU, 0x30851AU, 0x309DF0U, 0x30AC24U, 0x30B4CEU, + 0x30CF8CU, 0x30D766U, 0x30E6B2U, 0x30FE58U, 0x310BBAU, 0x311350U, 0x312284U, 0x313A6EU, 0x31412CU, 0x3159C6U, + 0x316812U, 0x3170F8U, 0x31867CU, 0x319E96U, 0x31AF42U, 0x31B7A8U, 0x31CCEAU, 0x31D400U, 0x31E5D4U, 0x31FD3EU, + 0x320E10U, 0x3216FAU, 0x32272EU, 0x323FC4U, 0x324486U, 0x325C6CU, 0x326DB8U, 0x327552U, 0x3283D6U, 0x329B3CU, + 0x32AAE8U, 0x32B202U, 0x32C940U, 0x32D1AAU, 0x32E07EU, 0x32F894U, 0x330D76U, 0x33159CU, 0x332448U, 0x333CA2U, + 0x3347E0U, 0x335F0AU, 0x336EDEU, 0x337634U, 0x3380B0U, 0x33985AU, 0x33A98EU, 0x33B164U, 0x33CA26U, 0x33D2CCU, + 0x33E318U, 0x33FBF2U, 0x340544U, 0x341DAEU, 0x342C7AU, 0x343490U, 0x344FD2U, 0x345738U, 0x3466ECU, 0x347E06U, + 0x348882U, 0x349068U, 0x34A1BCU, 0x34B956U, 0x34C214U, 0x34DAFEU, 0x34EB2AU, 0x34F3C0U, 0x350622U, 0x351EC8U, + 0x352F1CU, 0x3537F6U, 0x354CB4U, 0x35545EU, 0x35658AU, 0x357D60U, 0x358BE4U, 0x35930EU, 0x35A2DAU, 0x35BA30U, + 0x35C172U, 0x35D998U, 0x35E84CU, 0x35F0A6U, 0x360388U, 0x361B62U, 0x362AB6U, 0x36325CU, 0x36491EU, 0x3651F4U, + 0x366020U, 0x3678CAU, 0x368E4EU, 0x3696A4U, 0x36A770U, 0x36BF9AU, 0x36C4D8U, 0x36DC32U, 0x36EDE6U, 0x36F50CU, + 0x3700EEU, 0x371804U, 0x3729D0U, 0x37313AU, 0x374A78U, 0x375292U, 0x376346U, 0x377BACU, 0x378D28U, 0x3795C2U, + 0x37A416U, 0x37BCFCU, 0x37C7BEU, 0x37DF54U, 0x37EE80U, 0x37F66AU, 0x380B06U, 0x3813ECU, 0x382238U, 0x383AD2U, + 0x384190U, 0x38597AU, 0x3868AEU, 0x387044U, 0x3886C0U, 0x389E2AU, 0x38AFFEU, 0x38B714U, 0x38CC56U, 0x38D4BCU, + 0x38E568U, 0x38FD82U, 0x390860U, 0x39108AU, 0x39215EU, 0x3939B4U, 0x3942F6U, 0x395A1CU, 0x396BC8U, 0x397322U, + 0x3985A6U, 0x399D4CU, 0x39AC98U, 0x39B472U, 0x39CF30U, 0x39D7DAU, 0x39E60EU, 0x39FEE4U, 0x3A0DCAU, 0x3A1520U, + 0x3A24F4U, 0x3A3C1EU, 0x3A475CU, 0x3A5FB6U, 0x3A6E62U, 0x3A7688U, 0x3A800CU, 0x3A98E6U, 0x3AA932U, 0x3AB1D8U, + 0x3ACA9AU, 0x3AD270U, 0x3AE3A4U, 0x3AFB4EU, 0x3B0EACU, 0x3B1646U, 0x3B2792U, 0x3B3F78U, 0x3B443AU, 0x3B5CD0U, + 0x3B6D04U, 0x3B75EEU, 0x3B836AU, 0x3B9B80U, 0x3BAA54U, 0x3BB2BEU, 0x3BC9FCU, 0x3BD116U, 0x3BE0C2U, 0x3BF828U, + 0x3C069EU, 0x3C1E74U, 0x3C2FA0U, 0x3C374AU, 0x3C4C08U, 0x3C54E2U, 0x3C6536U, 0x3C7DDCU, 0x3C8B58U, 0x3C93B2U, + 0x3CA266U, 0x3CBA8CU, 0x3CC1CEU, 0x3CD924U, 0x3CE8F0U, 0x3CF01AU, 0x3D05F8U, 0x3D1D12U, 0x3D2CC6U, 0x3D342CU, + 0x3D4F6EU, 0x3D5784U, 0x3D6650U, 0x3D7EBAU, 0x3D883EU, 0x3D90D4U, 0x3DA100U, 0x3DB9EAU, 0x3DC2A8U, 0x3DDA42U, + 0x3DEB96U, 0x3DF37CU, 0x3E0052U, 0x3E18B8U, 0x3E296CU, 0x3E3186U, 0x3E4AC4U, 0x3E522EU, 0x3E63FAU, 0x3E7B10U, + 0x3E8D94U, 0x3E957EU, 0x3EA4AAU, 0x3EBC40U, 0x3EC702U, 0x3EDFE8U, 0x3EEE3CU, 0x3EF6D6U, 0x3F0334U, 0x3F1BDEU, + 0x3F2A0AU, 0x3F32E0U, 0x3F49A2U, 0x3F5148U, 0x3F609CU, 0x3F7876U, 0x3F8EF2U, 0x3F9618U, 0x3FA7CCU, 0x3FBF26U, + 0x3FC464U, 0x3FDC8EU, 0x3FED5AU, 0x3FF5B0U, 0x40063AU, 0x401ED0U, 0x402F04U, 0x4037EEU, 0x404CACU, 0x405446U, + 0x406592U, 0x407D78U, 0x408BFCU, 0x409316U, 0x40A2C2U, 0x40BA28U, 0x40C16AU, 0x40D980U, 0x40E854U, 0x40F0BEU, + 0x41055CU, 0x411DB6U, 0x412C62U, 0x413488U, 0x414FCAU, 0x415720U, 0x4166F4U, 0x417E1EU, 0x41889AU, 0x419070U, + 0x41A1A4U, 0x41B94EU, 0x41C20CU, 0x41DAE6U, 0x41EB32U, 0x41F3D8U, 0x4200F6U, 0x42181CU, 0x4229C8U, 0x423122U, + 0x424A60U, 0x42528AU, 0x42635EU, 0x427BB4U, 0x428D30U, 0x4295DAU, 0x42A40EU, 0x42BCE4U, 0x42C7A6U, 0x42DF4CU, + 0x42EE98U, 0x42F672U, 0x430390U, 0x431B7AU, 0x432AAEU, 0x433244U, 0x434906U, 0x4351ECU, 0x436038U, 0x4378D2U, + 0x438E56U, 0x4396BCU, 0x43A768U, 0x43BF82U, 0x43C4C0U, 0x43DC2AU, 0x43EDFEU, 0x43F514U, 0x440BA2U, 0x441348U, + 0x44229CU, 0x443A76U, 0x444134U, 0x4459DEU, 0x44680AU, 0x4470E0U, 0x448664U, 0x449E8EU, 0x44AF5AU, 0x44B7B0U, + 0x44CCF2U, 0x44D418U, 0x44E5CCU, 0x44FD26U, 0x4508C4U, 0x45102EU, 0x4521FAU, 0x453910U, 0x454252U, 0x455AB8U, + 0x456B6CU, 0x457386U, 0x458502U, 0x459DE8U, 0x45AC3CU, 0x45B4D6U, 0x45CF94U, 0x45D77EU, 0x45E6AAU, 0x45FE40U, + 0x460D6EU, 0x461584U, 0x462450U, 0x463CBAU, 0x4647F8U, 0x465F12U, 0x466EC6U, 0x46762CU, 0x4680A8U, 0x469842U, + 0x46A996U, 0x46B17CU, 0x46CA3EU, 0x46D2D4U, 0x46E300U, 0x46FBEAU, 0x470E08U, 0x4716E2U, 0x472736U, 0x473FDCU, + 0x47449EU, 0x475C74U, 0x476DA0U, 0x47754AU, 0x4783CEU, 0x479B24U, 0x47AAF0U, 0x47B21AU, 0x47C958U, 0x47D1B2U, + 0x47E066U, 0x47F88CU, 0x4805E0U, 0x481D0AU, 0x482CDEU, 0x483434U, 0x484F76U, 0x48579CU, 0x486648U, 0x487EA2U, + 0x488826U, 0x4890CCU, 0x48A118U, 0x48B9F2U, 0x48C2B0U, 0x48DA5AU, 0x48EB8EU, 0x48F364U, 0x490686U, 0x491E6CU, + 0x492FB8U, 0x493752U, 0x494C10U, 0x4954FAU, 0x49652EU, 0x497DC4U, 0x498B40U, 0x4993AAU, 0x49A27EU, 0x49BA94U, + 0x49C1D6U, 0x49D93CU, 0x49E8E8U, 0x49F002U, 0x4A032CU, 0x4A1BC6U, 0x4A2A12U, 0x4A32F8U, 0x4A49BAU, 0x4A5150U, + 0x4A6084U, 0x4A786EU, 0x4A8EEAU, 0x4A9600U, 0x4AA7D4U, 0x4ABF3EU, 0x4AC47CU, 0x4ADC96U, 0x4AED42U, 0x4AF5A8U, + 0x4B004AU, 0x4B18A0U, 0x4B2974U, 0x4B319EU, 0x4B4ADCU, 0x4B5236U, 0x4B63E2U, 0x4B7B08U, 0x4B8D8CU, 0x4B9566U, + 0x4BA4B2U, 0x4BBC58U, 0x4BC71AU, 0x4BDFF0U, 0x4BEE24U, 0x4BF6CEU, 0x4C0878U, 0x4C1092U, 0x4C2146U, 0x4C39ACU, + 0x4C42EEU, 0x4C5A04U, 0x4C6BD0U, 0x4C733AU, 0x4C85BEU, 0x4C9D54U, 0x4CAC80U, 0x4CB46AU, 0x4CCF28U, 0x4CD7C2U, + 0x4CE616U, 0x4CFEFCU, 0x4D0B1EU, 0x4D13F4U, 0x4D2220U, 0x4D3ACAU, 0x4D4188U, 0x4D5962U, 0x4D68B6U, 0x4D705CU, + 0x4D86D8U, 0x4D9E32U, 0x4DAFE6U, 0x4DB70CU, 0x4DCC4EU, 0x4DD4A4U, 0x4DE570U, 0x4DFD9AU, 0x4E0EB4U, 0x4E165EU, + 0x4E278AU, 0x4E3F60U, 0x4E4422U, 0x4E5CC8U, 0x4E6D1CU, 0x4E75F6U, 0x4E8372U, 0x4E9B98U, 0x4EAA4CU, 0x4EB2A6U, + 0x4EC9E4U, 0x4ED10EU, 0x4EE0DAU, 0x4EF830U, 0x4F0DD2U, 0x4F1538U, 0x4F24ECU, 0x4F3C06U, 0x4F4744U, 0x4F5FAEU, + 0x4F6E7AU, 0x4F7690U, 0x4F8014U, 0x4F98FEU, 0x4FA92AU, 0x4FB1C0U, 0x4FCA82U, 0x4FD268U, 0x4FE3BCU, 0x4FFB56U, + 0x50018EU, 0x501964U, 0x5028B0U, 0x50305AU, 0x504B18U, 0x5053F2U, 0x506226U, 0x507ACCU, 0x508C48U, 0x5094A2U, + 0x50A576U, 0x50BD9CU, 0x50C6DEU, 0x50DE34U, 0x50EFE0U, 0x50F70AU, 0x5102E8U, 0x511A02U, 0x512BD6U, 0x51333CU, + 0x51487EU, 0x515094U, 0x516140U, 0x5179AAU, 0x518F2EU, 0x5197C4U, 0x51A610U, 0x51BEFAU, 0x51C5B8U, 0x51DD52U, + 0x51EC86U, 0x51F46CU, 0x520742U, 0x521FA8U, 0x522E7CU, 0x523696U, 0x524DD4U, 0x52553EU, 0x5264EAU, 0x527C00U, + 0x528A84U, 0x52926EU, 0x52A3BAU, 0x52BB50U, 0x52C012U, 0x52D8F8U, 0x52E92CU, 0x52F1C6U, 0x530424U, 0x531CCEU, + 0x532D1AU, 0x5335F0U, 0x534EB2U, 0x535658U, 0x53678CU, 0x537F66U, 0x5389E2U, 0x539108U, 0x53A0DCU, 0x53B836U, + 0x53C374U, 0x53DB9EU, 0x53EA4AU, 0x53F2A0U, 0x540C16U, 0x5414FCU, 0x542528U, 0x543DC2U, 0x544680U, 0x545E6AU, + 0x546FBEU, 0x547754U, 0x5481D0U, 0x54993AU, 0x54A8EEU, 0x54B004U, 0x54CB46U, 0x54D3ACU, 0x54E278U, 0x54FA92U, + 0x550F70U, 0x55179AU, 0x55264EU, 0x553EA4U, 0x5545E6U, 0x555D0CU, 0x556CD8U, 0x557432U, 0x5582B6U, 0x559A5CU, + 0x55AB88U, 0x55B362U, 0x55C820U, 0x55D0CAU, 0x55E11EU, 0x55F9F4U, 0x560ADAU, 0x561230U, 0x5623E4U, 0x563B0EU, + 0x56404CU, 0x5658A6U, 0x566972U, 0x567198U, 0x56871CU, 0x569FF6U, 0x56AE22U, 0x56B6C8U, 0x56CD8AU, 0x56D560U, + 0x56E4B4U, 0x56FC5EU, 0x5709BCU, 0x571156U, 0x572082U, 0x573868U, 0x57432AU, 0x575BC0U, 0x576A14U, 0x5772FEU, + 0x57847AU, 0x579C90U, 0x57AD44U, 0x57B5AEU, 0x57CEECU, 0x57D606U, 0x57E7D2U, 0x57FF38U, 0x580254U, 0x581ABEU, + 0x582B6AU, 0x583380U, 0x5848C2U, 0x585028U, 0x5861FCU, 0x587916U, 0x588F92U, 0x589778U, 0x58A6ACU, 0x58BE46U, + 0x58C504U, 0x58DDEEU, 0x58EC3AU, 0x58F4D0U, 0x590132U, 0x5919D8U, 0x59280CU, 0x5930E6U, 0x594BA4U, 0x59534EU, + 0x59629AU, 0x597A70U, 0x598CF4U, 0x59941EU, 0x59A5CAU, 0x59BD20U, 0x59C662U, 0x59DE88U, 0x59EF5CU, 0x59F7B6U, + 0x5A0498U, 0x5A1C72U, 0x5A2DA6U, 0x5A354CU, 0x5A4E0EU, 0x5A56E4U, 0x5A6730U, 0x5A7FDAU, 0x5A895EU, 0x5A91B4U, + 0x5AA060U, 0x5AB88AU, 0x5AC3C8U, 0x5ADB22U, 0x5AEAF6U, 0x5AF21CU, 0x5B07FEU, 0x5B1F14U, 0x5B2EC0U, 0x5B362AU, + 0x5B4D68U, 0x5B5582U, 0x5B6456U, 0x5B7CBCU, 0x5B8A38U, 0x5B92D2U, 0x5BA306U, 0x5BBBECU, 0x5BC0AEU, 0x5BD844U, + 0x5BE990U, 0x5BF17AU, 0x5C0FCCU, 0x5C1726U, 0x5C26F2U, 0x5C3E18U, 0x5C455AU, 0x5C5DB0U, 0x5C6C64U, 0x5C748EU, + 0x5C820AU, 0x5C9AE0U, 0x5CAB34U, 0x5CB3DEU, 0x5CC89CU, 0x5CD076U, 0x5CE1A2U, 0x5CF948U, 0x5D0CAAU, 0x5D1440U, + 0x5D2594U, 0x5D3D7EU, 0x5D463CU, 0x5D5ED6U, 0x5D6F02U, 0x5D77E8U, 0x5D816CU, 0x5D9986U, 0x5DA852U, 0x5DB0B8U, + 0x5DCBFAU, 0x5DD310U, 0x5DE2C4U, 0x5DFA2EU, 0x5E0900U, 0x5E11EAU, 0x5E203EU, 0x5E38D4U, 0x5E4396U, 0x5E5B7CU, + 0x5E6AA8U, 0x5E7242U, 0x5E84C6U, 0x5E9C2CU, 0x5EADF8U, 0x5EB512U, 0x5ECE50U, 0x5ED6BAU, 0x5EE76EU, 0x5EFF84U, + 0x5F0A66U, 0x5F128CU, 0x5F2358U, 0x5F3BB2U, 0x5F40F0U, 0x5F581AU, 0x5F69CEU, 0x5F7124U, 0x5F87A0U, 0x5F9F4AU, + 0x5FAE9EU, 0x5FB674U, 0x5FCD36U, 0x5FD5DCU, 0x5FE408U, 0x5FFCE2U, 0x600952U, 0x6011B8U, 0x60206CU, 0x603886U, + 0x6043C4U, 0x605B2EU, 0x606AFAU, 0x607210U, 0x608494U, 0x609C7EU, 0x60ADAAU, 0x60B540U, 0x60CE02U, 0x60D6E8U, + 0x60E73CU, 0x60FFD6U, 0x610A34U, 0x6112DEU, 0x61230AU, 0x613BE0U, 0x6140A2U, 0x615848U, 0x61699CU, 0x617176U, + 0x6187F2U, 0x619F18U, 0x61AECCU, 0x61B626U, 0x61CD64U, 0x61D58EU, 0x61E45AU, 0x61FCB0U, 0x620F9EU, 0x621774U, + 0x6226A0U, 0x623E4AU, 0x624508U, 0x625DE2U, 0x626C36U, 0x6274DCU, 0x628258U, 0x629AB2U, 0x62AB66U, 0x62B38CU, + 0x62C8CEU, 0x62D024U, 0x62E1F0U, 0x62F91AU, 0x630CF8U, 0x631412U, 0x6325C6U, 0x633D2CU, 0x63466EU, 0x635E84U, + 0x636F50U, 0x6377BAU, 0x63813EU, 0x6399D4U, 0x63A800U, 0x63B0EAU, 0x63CBA8U, 0x63D342U, 0x63E296U, 0x63FA7CU, + 0x6404CAU, 0x641C20U, 0x642DF4U, 0x64351EU, 0x644E5CU, 0x6456B6U, 0x646762U, 0x647F88U, 0x64890CU, 0x6491E6U, + 0x64A032U, 0x64B8D8U, 0x64C39AU, 0x64DB70U, 0x64EAA4U, 0x64F24EU, 0x6507ACU, 0x651F46U, 0x652E92U, 0x653678U, + 0x654D3AU, 0x6555D0U, 0x656404U, 0x657CEEU, 0x658A6AU, 0x659280U, 0x65A354U, 0x65BBBEU, 0x65C0FCU, 0x65D816U, + 0x65E9C2U, 0x65F128U, 0x660206U, 0x661AECU, 0x662B38U, 0x6633D2U, 0x664890U, 0x66507AU, 0x6661AEU, 0x667944U, + 0x668FC0U, 0x66972AU, 0x66A6FEU, 0x66BE14U, 0x66C556U, 0x66DDBCU, 0x66EC68U, 0x66F482U, 0x670160U, 0x67198AU, + 0x67285EU, 0x6730B4U, 0x674BF6U, 0x67531CU, 0x6762C8U, 0x677A22U, 0x678CA6U, 0x67944CU, 0x67A598U, 0x67BD72U, + 0x67C630U, 0x67DEDAU, 0x67EF0EU, 0x67F7E4U, 0x680A88U, 0x681262U, 0x6823B6U, 0x683B5CU, 0x68401EU, 0x6858F4U, + 0x686920U, 0x6871CAU, 0x68874EU, 0x689FA4U, 0x68AE70U, 0x68B69AU, 0x68CDD8U, 0x68D532U, 0x68E4E6U, 0x68FC0CU, + 0x6909EEU, 0x691104U, 0x6920D0U, 0x69383AU, 0x694378U, 0x695B92U, 0x696A46U, 0x6972ACU, 0x698428U, 0x699CC2U, + 0x69AD16U, 0x69B5FCU, 0x69CEBEU, 0x69D654U, 0x69E780U, 0x69FF6AU, 0x6A0C44U, 0x6A14AEU, 0x6A257AU, 0x6A3D90U, + 0x6A46D2U, 0x6A5E38U, 0x6A6FECU, 0x6A7706U, 0x6A8182U, 0x6A9968U, 0x6AA8BCU, 0x6AB056U, 0x6ACB14U, 0x6AD3FEU, + 0x6AE22AU, 0x6AFAC0U, 0x6B0F22U, 0x6B17C8U, 0x6B261CU, 0x6B3EF6U, 0x6B45B4U, 0x6B5D5EU, 0x6B6C8AU, 0x6B7460U, + 0x6B82E4U, 0x6B9A0EU, 0x6BABDAU, 0x6BB330U, 0x6BC872U, 0x6BD098U, 0x6BE14CU, 0x6BF9A6U, 0x6C0710U, 0x6C1FFAU, + 0x6C2E2EU, 0x6C36C4U, 0x6C4D86U, 0x6C556CU, 0x6C64B8U, 0x6C7C52U, 0x6C8AD6U, 0x6C923CU, 0x6CA3E8U, 0x6CBB02U, + 0x6CC040U, 0x6CD8AAU, 0x6CE97EU, 0x6CF194U, 0x6D0476U, 0x6D1C9CU, 0x6D2D48U, 0x6D35A2U, 0x6D4EE0U, 0x6D560AU, + 0x6D67DEU, 0x6D7F34U, 0x6D89B0U, 0x6D915AU, 0x6DA08EU, 0x6DB864U, 0x6DC326U, 0x6DDBCCU, 0x6DEA18U, 0x6DF2F2U, + 0x6E01DCU, 0x6E1936U, 0x6E28E2U, 0x6E3008U, 0x6E4B4AU, 0x6E53A0U, 0x6E6274U, 0x6E7A9EU, 0x6E8C1AU, 0x6E94F0U, + 0x6EA524U, 0x6EBDCEU, 0x6EC68CU, 0x6EDE66U, 0x6EEFB2U, 0x6EF758U, 0x6F02BAU, 0x6F1A50U, 0x6F2B84U, 0x6F336EU, + 0x6F482CU, 0x6F50C6U, 0x6F6112U, 0x6F79F8U, 0x6F8F7CU, 0x6F9796U, 0x6FA642U, 0x6FBEA8U, 0x6FC5EAU, 0x6FDD00U, + 0x6FECD4U, 0x6FF43EU, 0x700EE6U, 0x70160CU, 0x7027D8U, 0x703F32U, 0x704470U, 0x705C9AU, 0x706D4EU, 0x7075A4U, + 0x708320U, 0x709BCAU, 0x70AA1EU, 0x70B2F4U, 0x70C9B6U, 0x70D15CU, 0x70E088U, 0x70F862U, 0x710D80U, 0x71156AU, + 0x7124BEU, 0x713C54U, 0x714716U, 0x715FFCU, 0x716E28U, 0x7176C2U, 0x718046U, 0x7198ACU, 0x71A978U, 0x71B192U, + 0x71CAD0U, 0x71D23AU, 0x71E3EEU, 0x71FB04U, 0x72082AU, 0x7210C0U, 0x722114U, 0x7239FEU, 0x7242BCU, 0x725A56U, + 0x726B82U, 0x727368U, 0x7285ECU, 0x729D06U, 0x72ACD2U, 0x72B438U, 0x72CF7AU, 0x72D790U, 0x72E644U, 0x72FEAEU, + 0x730B4CU, 0x7313A6U, 0x732272U, 0x733A98U, 0x7341DAU, 0x735930U, 0x7368E4U, 0x73700EU, 0x73868AU, 0x739E60U, + 0x73AFB4U, 0x73B75EU, 0x73CC1CU, 0x73D4F6U, 0x73E522U, 0x73FDC8U, 0x74037EU, 0x741B94U, 0x742A40U, 0x7432AAU, + 0x7449E8U, 0x745102U, 0x7460D6U, 0x74783CU, 0x748EB8U, 0x749652U, 0x74A786U, 0x74BF6CU, 0x74C42EU, 0x74DCC4U, + 0x74ED10U, 0x74F5FAU, 0x750018U, 0x7518F2U, 0x752926U, 0x7531CCU, 0x754A8EU, 0x755264U, 0x7563B0U, 0x757B5AU, + 0x758DDEU, 0x759534U, 0x75A4E0U, 0x75BC0AU, 0x75C748U, 0x75DFA2U, 0x75EE76U, 0x75F69CU, 0x7605B2U, 0x761D58U, + 0x762C8CU, 0x763466U, 0x764F24U, 0x7657CEU, 0x76661AU, 0x767EF0U, 0x768874U, 0x76909EU, 0x76A14AU, 0x76B9A0U, + 0x76C2E2U, 0x76DA08U, 0x76EBDCU, 0x76F336U, 0x7706D4U, 0x771E3EU, 0x772FEAU, 0x773700U, 0x774C42U, 0x7754A8U, + 0x77657CU, 0x777D96U, 0x778B12U, 0x7793F8U, 0x77A22CU, 0x77BAC6U, 0x77C184U, 0x77D96EU, 0x77E8BAU, 0x77F050U, + 0x780D3CU, 0x7815D6U, 0x782402U, 0x783CE8U, 0x7847AAU, 0x785F40U, 0x786E94U, 0x78767EU, 0x7880FAU, 0x789810U, + 0x78A9C4U, 0x78B12EU, 0x78CA6CU, 0x78D286U, 0x78E352U, 0x78FBB8U, 0x790E5AU, 0x7916B0U, 0x792764U, 0x793F8EU, + 0x7944CCU, 0x795C26U, 0x796DF2U, 0x797518U, 0x79839CU, 0x799B76U, 0x79AAA2U, 0x79B248U, 0x79C90AU, 0x79D1E0U, + 0x79E034U, 0x79F8DEU, 0x7A0BF0U, 0x7A131AU, 0x7A22CEU, 0x7A3A24U, 0x7A4166U, 0x7A598CU, 0x7A6858U, 0x7A70B2U, + 0x7A8636U, 0x7A9EDCU, 0x7AAF08U, 0x7AB7E2U, 0x7ACCA0U, 0x7AD44AU, 0x7AE59EU, 0x7AFD74U, 0x7B0896U, 0x7B107CU, + 0x7B21A8U, 0x7B3942U, 0x7B4200U, 0x7B5AEAU, 0x7B6B3EU, 0x7B73D4U, 0x7B8550U, 0x7B9DBAU, 0x7BAC6EU, 0x7BB484U, + 0x7BCFC6U, 0x7BD72CU, 0x7BE6F8U, 0x7BFE12U, 0x7C00A4U, 0x7C184EU, 0x7C299AU, 0x7C3170U, 0x7C4A32U, 0x7C52D8U, + 0x7C630CU, 0x7C7BE6U, 0x7C8D62U, 0x7C9588U, 0x7CA45CU, 0x7CBCB6U, 0x7CC7F4U, 0x7CDF1EU, 0x7CEECAU, 0x7CF620U, + 0x7D03C2U, 0x7D1B28U, 0x7D2AFCU, 0x7D3216U, 0x7D4954U, 0x7D51BEU, 0x7D606AU, 0x7D7880U, 0x7D8E04U, 0x7D96EEU, + 0x7DA73AU, 0x7DBFD0U, 0x7DC492U, 0x7DDC78U, 0x7DEDACU, 0x7DF546U, 0x7E0668U, 0x7E1E82U, 0x7E2F56U, 0x7E37BCU, + 0x7E4CFEU, 0x7E5414U, 0x7E65C0U, 0x7E7D2AU, 0x7E8BAEU, 0x7E9344U, 0x7EA290U, 0x7EBA7AU, 0x7EC138U, 0x7ED9D2U, + 0x7EE806U, 0x7EF0ECU, 0x7F050EU, 0x7F1DE4U, 0x7F2C30U, 0x7F34DAU, 0x7F4F98U, 0x7F5772U, 0x7F66A6U, 0x7F7E4CU, + 0x7F88C8U, 0x7F9022U, 0x7FA1F6U, 0x7FB91CU, 0x7FC25EU, 0x7FDAB4U, 0x7FEB60U, 0x7FF38AU, 0x800C74U, 0x80149EU, + 0x80254AU, 0x803DA0U, 0x8046E2U, 0x805E08U, 0x806FDCU, 0x807736U, 0x8081B2U, 0x809958U, 0x80A88CU, 0x80B066U, + 0x80CB24U, 0x80D3CEU, 0x80E21AU, 0x80FAF0U, 0x810F12U, 0x8117F8U, 0x81262CU, 0x813EC6U, 0x814584U, 0x815D6EU, + 0x816CBAU, 0x817450U, 0x8182D4U, 0x819A3EU, 0x81ABEAU, 0x81B300U, 0x81C842U, 0x81D0A8U, 0x81E17CU, 0x81F996U, + 0x820AB8U, 0x821252U, 0x822386U, 0x823B6CU, 0x82402EU, 0x8258C4U, 0x826910U, 0x8271FAU, 0x82877EU, 0x829F94U, + 0x82AE40U, 0x82B6AAU, 0x82CDE8U, 0x82D502U, 0x82E4D6U, 0x82FC3CU, 0x8309DEU, 0x831134U, 0x8320E0U, 0x83380AU, + 0x834348U, 0x835BA2U, 0x836A76U, 0x83729CU, 0x838418U, 0x839CF2U, 0x83AD26U, 0x83B5CCU, 0x83CE8EU, 0x83D664U, + 0x83E7B0U, 0x83FF5AU, 0x8401ECU, 0x841906U, 0x8428D2U, 0x843038U, 0x844B7AU, 0x845390U, 0x846244U, 0x847AAEU, + 0x848C2AU, 0x8494C0U, 0x84A514U, 0x84BDFEU, 0x84C6BCU, 0x84DE56U, 0x84EF82U, 0x84F768U, 0x85028AU, 0x851A60U, + 0x852BB4U, 0x85335EU, 0x85481CU, 0x8550F6U, 0x856122U, 0x8579C8U, 0x858F4CU, 0x8597A6U, 0x85A672U, 0x85BE98U, + 0x85C5DAU, 0x85DD30U, 0x85ECE4U, 0x85F40EU, 0x860720U, 0x861FCAU, 0x862E1EU, 0x8636F4U, 0x864DB6U, 0x86555CU, + 0x866488U, 0x867C62U, 0x868AE6U, 0x86920CU, 0x86A3D8U, 0x86BB32U, 0x86C070U, 0x86D89AU, 0x86E94EU, 0x86F1A4U, + 0x870446U, 0x871CACU, 0x872D78U, 0x873592U, 0x874ED0U, 0x87563AU, 0x8767EEU, 0x877F04U, 0x878980U, 0x87916AU, + 0x87A0BEU, 0x87B854U, 0x87C316U, 0x87DBFCU, 0x87EA28U, 0x87F2C2U, 0x880FAEU, 0x881744U, 0x882690U, 0x883E7AU, + 0x884538U, 0x885DD2U, 0x886C06U, 0x8874ECU, 0x888268U, 0x889A82U, 0x88AB56U, 0x88B3BCU, 0x88C8FEU, 0x88D014U, + 0x88E1C0U, 0x88F92AU, 0x890CC8U, 0x891422U, 0x8925F6U, 0x893D1CU, 0x89465EU, 0x895EB4U, 0x896F60U, 0x89778AU, + 0x89810EU, 0x8999E4U, 0x89A830U, 0x89B0DAU, 0x89CB98U, 0x89D372U, 0x89E2A6U, 0x89FA4CU, 0x8A0962U, 0x8A1188U, + 0x8A205CU, 0x8A38B6U, 0x8A43F4U, 0x8A5B1EU, 0x8A6ACAU, 0x8A7220U, 0x8A84A4U, 0x8A9C4EU, 0x8AAD9AU, 0x8AB570U, + 0x8ACE32U, 0x8AD6D8U, 0x8AE70CU, 0x8AFFE6U, 0x8B0A04U, 0x8B12EEU, 0x8B233AU, 0x8B3BD0U, 0x8B4092U, 0x8B5878U, + 0x8B69ACU, 0x8B7146U, 0x8B87C2U, 0x8B9F28U, 0x8BAEFCU, 0x8BB616U, 0x8BCD54U, 0x8BD5BEU, 0x8BE46AU, 0x8BFC80U, + 0x8C0236U, 0x8C1ADCU, 0x8C2B08U, 0x8C33E2U, 0x8C48A0U, 0x8C504AU, 0x8C619EU, 0x8C7974U, 0x8C8FF0U, 0x8C971AU, + 0x8CA6CEU, 0x8CBE24U, 0x8CC566U, 0x8CDD8CU, 0x8CEC58U, 0x8CF4B2U, 0x8D0150U, 0x8D19BAU, 0x8D286EU, 0x8D3084U, + 0x8D4BC6U, 0x8D532CU, 0x8D62F8U, 0x8D7A12U, 0x8D8C96U, 0x8D947CU, 0x8DA5A8U, 0x8DBD42U, 0x8DC600U, 0x8DDEEAU, + 0x8DEF3EU, 0x8DF7D4U, 0x8E04FAU, 0x8E1C10U, 0x8E2DC4U, 0x8E352EU, 0x8E4E6CU, 0x8E5686U, 0x8E6752U, 0x8E7FB8U, + 0x8E893CU, 0x8E91D6U, 0x8EA002U, 0x8EB8E8U, 0x8EC3AAU, 0x8EDB40U, 0x8EEA94U, 0x8EF27EU, 0x8F079CU, 0x8F1F76U, + 0x8F2EA2U, 0x8F3648U, 0x8F4D0AU, 0x8F55E0U, 0x8F6434U, 0x8F7CDEU, 0x8F8A5AU, 0x8F92B0U, 0x8FA364U, 0x8FBB8EU, + 0x8FC0CCU, 0x8FD826U, 0x8FE9F2U, 0x8FF118U, 0x900BC0U, 0x90132AU, 0x9022FEU, 0x903A14U, 0x904156U, 0x9059BCU, + 0x906868U, 0x907082U, 0x908606U, 0x909EECU, 0x90AF38U, 0x90B7D2U, 0x90CC90U, 0x90D47AU, 0x90E5AEU, 0x90FD44U, + 0x9108A6U, 0x91104CU, 0x912198U, 0x913972U, 0x914230U, 0x915ADAU, 0x916B0EU, 0x9173E4U, 0x918560U, 0x919D8AU, + 0x91AC5EU, 0x91B4B4U, 0x91CFF6U, 0x91D71CU, 0x91E6C8U, 0x91FE22U, 0x920D0CU, 0x9215E6U, 0x922432U, 0x923CD8U, + 0x92479AU, 0x925F70U, 0x926EA4U, 0x92764EU, 0x9280CAU, 0x929820U, 0x92A9F4U, 0x92B11EU, 0x92CA5CU, 0x92D2B6U, + 0x92E362U, 0x92FB88U, 0x930E6AU, 0x931680U, 0x932754U, 0x933FBEU, 0x9344FCU, 0x935C16U, 0x936DC2U, 0x937528U, + 0x9383ACU, 0x939B46U, 0x93AA92U, 0x93B278U, 0x93C93AU, 0x93D1D0U, 0x93E004U, 0x93F8EEU, 0x940658U, 0x941EB2U, + 0x942F66U, 0x94378CU, 0x944CCEU, 0x945424U, 0x9465F0U, 0x947D1AU, 0x948B9EU, 0x949374U, 0x94A2A0U, 0x94BA4AU, + 0x94C108U, 0x94D9E2U, 0x94E836U, 0x94F0DCU, 0x95053EU, 0x951DD4U, 0x952C00U, 0x9534EAU, 0x954FA8U, 0x955742U, + 0x956696U, 0x957E7CU, 0x9588F8U, 0x959012U, 0x95A1C6U, 0x95B92CU, 0x95C26EU, 0x95DA84U, 0x95EB50U, 0x95F3BAU, + 0x960094U, 0x96187EU, 0x9629AAU, 0x963140U, 0x964A02U, 0x9652E8U, 0x96633CU, 0x967BD6U, 0x968D52U, 0x9695B8U, + 0x96A46CU, 0x96BC86U, 0x96C7C4U, 0x96DF2EU, 0x96EEFAU, 0x96F610U, 0x9703F2U, 0x971B18U, 0x972ACCU, 0x973226U, + 0x974964U, 0x97518EU, 0x97605AU, 0x9778B0U, 0x978E34U, 0x9796DEU, 0x97A70AU, 0x97BFE0U, 0x97C4A2U, 0x97DC48U, + 0x97ED9CU, 0x97F576U, 0x98081AU, 0x9810F0U, 0x982124U, 0x9839CEU, 0x98428CU, 0x985A66U, 0x986BB2U, 0x987358U, + 0x9885DCU, 0x989D36U, 0x98ACE2U, 0x98B408U, 0x98CF4AU, 0x98D7A0U, 0x98E674U, 0x98FE9EU, 0x990B7CU, 0x991396U, + 0x992242U, 0x993AA8U, 0x9941EAU, 0x995900U, 0x9968D4U, 0x99703EU, 0x9986BAU, 0x999E50U, 0x99AF84U, 0x99B76EU, + 0x99CC2CU, 0x99D4C6U, 0x99E512U, 0x99FDF8U, 0x9A0ED6U, 0x9A163CU, 0x9A27E8U, 0x9A3F02U, 0x9A4440U, 0x9A5CAAU, + 0x9A6D7EU, 0x9A7594U, 0x9A8310U, 0x9A9BFAU, 0x9AAA2EU, 0x9AB2C4U, 0x9AC986U, 0x9AD16CU, 0x9AE0B8U, 0x9AF852U, + 0x9B0DB0U, 0x9B155AU, 0x9B248EU, 0x9B3C64U, 0x9B4726U, 0x9B5FCCU, 0x9B6E18U, 0x9B76F2U, 0x9B8076U, 0x9B989CU, + 0x9BA948U, 0x9BB1A2U, 0x9BCAE0U, 0x9BD20AU, 0x9BE3DEU, 0x9BFB34U, 0x9C0582U, 0x9C1D68U, 0x9C2CBCU, 0x9C3456U, + 0x9C4F14U, 0x9C57FEU, 0x9C662AU, 0x9C7EC0U, 0x9C8844U, 0x9C90AEU, 0x9CA17AU, 0x9CB990U, 0x9CC2D2U, 0x9CDA38U, + 0x9CEBECU, 0x9CF306U, 0x9D06E4U, 0x9D1E0EU, 0x9D2FDAU, 0x9D3730U, 0x9D4C72U, 0x9D5498U, 0x9D654CU, 0x9D7DA6U, + 0x9D8B22U, 0x9D93C8U, 0x9DA21CU, 0x9DBAF6U, 0x9DC1B4U, 0x9DD95EU, 0x9DE88AU, 0x9DF060U, 0x9E034EU, 0x9E1BA4U, + 0x9E2A70U, 0x9E329AU, 0x9E49D8U, 0x9E5132U, 0x9E60E6U, 0x9E780CU, 0x9E8E88U, 0x9E9662U, 0x9EA7B6U, 0x9EBF5CU, + 0x9EC41EU, 0x9EDCF4U, 0x9EED20U, 0x9EF5CAU, 0x9F0028U, 0x9F18C2U, 0x9F2916U, 0x9F31FCU, 0x9F4ABEU, 0x9F5254U, + 0x9F6380U, 0x9F7B6AU, 0x9F8DEEU, 0x9F9504U, 0x9FA4D0U, 0x9FBC3AU, 0x9FC778U, 0x9FDF92U, 0x9FEE46U, 0x9FF6ACU, + 0xA0031CU, 0xA01BF6U, 0xA02A22U, 0xA032C8U, 0xA0498AU, 0xA05160U, 0xA060B4U, 0xA0785EU, 0xA08EDAU, 0xA09630U, + 0xA0A7E4U, 0xA0BF0EU, 0xA0C44CU, 0xA0DCA6U, 0xA0ED72U, 0xA0F598U, 0xA1007AU, 0xA11890U, 0xA12944U, 0xA131AEU, + 0xA14AECU, 0xA15206U, 0xA163D2U, 0xA17B38U, 0xA18DBCU, 0xA19556U, 0xA1A482U, 0xA1BC68U, 0xA1C72AU, 0xA1DFC0U, + 0xA1EE14U, 0xA1F6FEU, 0xA205D0U, 0xA21D3AU, 0xA22CEEU, 0xA23404U, 0xA24F46U, 0xA257ACU, 0xA26678U, 0xA27E92U, + 0xA28816U, 0xA290FCU, 0xA2A128U, 0xA2B9C2U, 0xA2C280U, 0xA2DA6AU, 0xA2EBBEU, 0xA2F354U, 0xA306B6U, 0xA31E5CU, + 0xA32F88U, 0xA33762U, 0xA34C20U, 0xA354CAU, 0xA3651EU, 0xA37DF4U, 0xA38B70U, 0xA3939AU, 0xA3A24EU, 0xA3BAA4U, + 0xA3C1E6U, 0xA3D90CU, 0xA3E8D8U, 0xA3F032U, 0xA40E84U, 0xA4166EU, 0xA427BAU, 0xA43F50U, 0xA44412U, 0xA45CF8U, + 0xA46D2CU, 0xA475C6U, 0xA48342U, 0xA49BA8U, 0xA4AA7CU, 0xA4B296U, 0xA4C9D4U, 0xA4D13EU, 0xA4E0EAU, 0xA4F800U, + 0xA50DE2U, 0xA51508U, 0xA524DCU, 0xA53C36U, 0xA54774U, 0xA55F9EU, 0xA56E4AU, 0xA576A0U, 0xA58024U, 0xA598CEU, + 0xA5A91AU, 0xA5B1F0U, 0xA5CAB2U, 0xA5D258U, 0xA5E38CU, 0xA5FB66U, 0xA60848U, 0xA610A2U, 0xA62176U, 0xA6399CU, + 0xA642DEU, 0xA65A34U, 0xA66BE0U, 0xA6730AU, 0xA6858EU, 0xA69D64U, 0xA6ACB0U, 0xA6B45AU, 0xA6CF18U, 0xA6D7F2U, + 0xA6E626U, 0xA6FECCU, 0xA70B2EU, 0xA713C4U, 0xA72210U, 0xA73AFAU, 0xA741B8U, 0xA75952U, 0xA76886U, 0xA7706CU, + 0xA786E8U, 0xA79E02U, 0xA7AFD6U, 0xA7B73CU, 0xA7CC7EU, 0xA7D494U, 0xA7E540U, 0xA7FDAAU, 0xA800C6U, 0xA8182CU, + 0xA829F8U, 0xA83112U, 0xA84A50U, 0xA852BAU, 0xA8636EU, 0xA87B84U, 0xA88D00U, 0xA895EAU, 0xA8A43EU, 0xA8BCD4U, + 0xA8C796U, 0xA8DF7CU, 0xA8EEA8U, 0xA8F642U, 0xA903A0U, 0xA91B4AU, 0xA92A9EU, 0xA93274U, 0xA94936U, 0xA951DCU, + 0xA96008U, 0xA978E2U, 0xA98E66U, 0xA9968CU, 0xA9A758U, 0xA9BFB2U, 0xA9C4F0U, 0xA9DC1AU, 0xA9EDCEU, 0xA9F524U, + 0xAA060AU, 0xAA1EE0U, 0xAA2F34U, 0xAA37DEU, 0xAA4C9CU, 0xAA5476U, 0xAA65A2U, 0xAA7D48U, 0xAA8BCCU, 0xAA9326U, + 0xAAA2F2U, 0xAABA18U, 0xAAC15AU, 0xAAD9B0U, 0xAAE864U, 0xAAF08EU, 0xAB056CU, 0xAB1D86U, 0xAB2C52U, 0xAB34B8U, + 0xAB4FFAU, 0xAB5710U, 0xAB66C4U, 0xAB7E2EU, 0xAB88AAU, 0xAB9040U, 0xABA194U, 0xABB97EU, 0xABC23CU, 0xABDAD6U, + 0xABEB02U, 0xABF3E8U, 0xAC0D5EU, 0xAC15B4U, 0xAC2460U, 0xAC3C8AU, 0xAC47C8U, 0xAC5F22U, 0xAC6EF6U, 0xAC761CU, + 0xAC8098U, 0xAC9872U, 0xACA9A6U, 0xACB14CU, 0xACCA0EU, 0xACD2E4U, 0xACE330U, 0xACFBDAU, 0xAD0E38U, 0xAD16D2U, + 0xAD2706U, 0xAD3FECU, 0xAD44AEU, 0xAD5C44U, 0xAD6D90U, 0xAD757AU, 0xAD83FEU, 0xAD9B14U, 0xADAAC0U, 0xADB22AU, + 0xADC968U, 0xADD182U, 0xADE056U, 0xADF8BCU, 0xAE0B92U, 0xAE1378U, 0xAE22ACU, 0xAE3A46U, 0xAE4104U, 0xAE59EEU, + 0xAE683AU, 0xAE70D0U, 0xAE8654U, 0xAE9EBEU, 0xAEAF6AU, 0xAEB780U, 0xAECCC2U, 0xAED428U, 0xAEE5FCU, 0xAEFD16U, + 0xAF08F4U, 0xAF101EU, 0xAF21CAU, 0xAF3920U, 0xAF4262U, 0xAF5A88U, 0xAF6B5CU, 0xAF73B6U, 0xAF8532U, 0xAF9DD8U, + 0xAFAC0CU, 0xAFB4E6U, 0xAFCFA4U, 0xAFD74EU, 0xAFE69AU, 0xAFFE70U, 0xB004A8U, 0xB01C42U, 0xB02D96U, 0xB0357CU, + 0xB04E3EU, 0xB056D4U, 0xB06700U, 0xB07FEAU, 0xB0896EU, 0xB09184U, 0xB0A050U, 0xB0B8BAU, 0xB0C3F8U, 0xB0DB12U, + 0xB0EAC6U, 0xB0F22CU, 0xB107CEU, 0xB11F24U, 0xB12EF0U, 0xB1361AU, 0xB14D58U, 0xB155B2U, 0xB16466U, 0xB17C8CU, + 0xB18A08U, 0xB192E2U, 0xB1A336U, 0xB1BBDCU, 0xB1C09EU, 0xB1D874U, 0xB1E9A0U, 0xB1F14AU, 0xB20264U, 0xB21A8EU, + 0xB22B5AU, 0xB233B0U, 0xB248F2U, 0xB25018U, 0xB261CCU, 0xB27926U, 0xB28FA2U, 0xB29748U, 0xB2A69CU, 0xB2BE76U, + 0xB2C534U, 0xB2DDDEU, 0xB2EC0AU, 0xB2F4E0U, 0xB30102U, 0xB319E8U, 0xB3283CU, 0xB330D6U, 0xB34B94U, 0xB3537EU, + 0xB362AAU, 0xB37A40U, 0xB38CC4U, 0xB3942EU, 0xB3A5FAU, 0xB3BD10U, 0xB3C652U, 0xB3DEB8U, 0xB3EF6CU, 0xB3F786U, + 0xB40930U, 0xB411DAU, 0xB4200EU, 0xB438E4U, 0xB443A6U, 0xB45B4CU, 0xB46A98U, 0xB47272U, 0xB484F6U, 0xB49C1CU, + 0xB4ADC8U, 0xB4B522U, 0xB4CE60U, 0xB4D68AU, 0xB4E75EU, 0xB4FFB4U, 0xB50A56U, 0xB512BCU, 0xB52368U, 0xB53B82U, + 0xB540C0U, 0xB5582AU, 0xB569FEU, 0xB57114U, 0xB58790U, 0xB59F7AU, 0xB5AEAEU, 0xB5B644U, 0xB5CD06U, 0xB5D5ECU, + 0xB5E438U, 0xB5FCD2U, 0xB60FFCU, 0xB61716U, 0xB626C2U, 0xB63E28U, 0xB6456AU, 0xB65D80U, 0xB66C54U, 0xB674BEU, + 0xB6823AU, 0xB69AD0U, 0xB6AB04U, 0xB6B3EEU, 0xB6C8ACU, 0xB6D046U, 0xB6E192U, 0xB6F978U, 0xB70C9AU, 0xB71470U, + 0xB725A4U, 0xB73D4EU, 0xB7460CU, 0xB75EE6U, 0xB76F32U, 0xB777D8U, 0xB7815CU, 0xB799B6U, 0xB7A862U, 0xB7B088U, + 0xB7CBCAU, 0xB7D320U, 0xB7E2F4U, 0xB7FA1EU, 0xB80772U, 0xB81F98U, 0xB82E4CU, 0xB836A6U, 0xB84DE4U, 0xB8550EU, + 0xB864DAU, 0xB87C30U, 0xB88AB4U, 0xB8925EU, 0xB8A38AU, 0xB8BB60U, 0xB8C022U, 0xB8D8C8U, 0xB8E91CU, 0xB8F1F6U, + 0xB90414U, 0xB91CFEU, 0xB92D2AU, 0xB935C0U, 0xB94E82U, 0xB95668U, 0xB967BCU, 0xB97F56U, 0xB989D2U, 0xB99138U, + 0xB9A0ECU, 0xB9B806U, 0xB9C344U, 0xB9DBAEU, 0xB9EA7AU, 0xB9F290U, 0xBA01BEU, 0xBA1954U, 0xBA2880U, 0xBA306AU, + 0xBA4B28U, 0xBA53C2U, 0xBA6216U, 0xBA7AFCU, 0xBA8C78U, 0xBA9492U, 0xBAA546U, 0xBABDACU, 0xBAC6EEU, 0xBADE04U, + 0xBAEFD0U, 0xBAF73AU, 0xBB02D8U, 0xBB1A32U, 0xBB2BE6U, 0xBB330CU, 0xBB484EU, 0xBB50A4U, 0xBB6170U, 0xBB799AU, + 0xBB8F1EU, 0xBB97F4U, 0xBBA620U, 0xBBBECAU, 0xBBC588U, 0xBBDD62U, 0xBBECB6U, 0xBBF45CU, 0xBC0AEAU, 0xBC1200U, + 0xBC23D4U, 0xBC3B3EU, 0xBC407CU, 0xBC5896U, 0xBC6942U, 0xBC71A8U, 0xBC872CU, 0xBC9FC6U, 0xBCAE12U, 0xBCB6F8U, + 0xBCCDBAU, 0xBCD550U, 0xBCE484U, 0xBCFC6EU, 0xBD098CU, 0xBD1166U, 0xBD20B2U, 0xBD3858U, 0xBD431AU, 0xBD5BF0U, + 0xBD6A24U, 0xBD72CEU, 0xBD844AU, 0xBD9CA0U, 0xBDAD74U, 0xBDB59EU, 0xBDCEDCU, 0xBDD636U, 0xBDE7E2U, 0xBDFF08U, + 0xBE0C26U, 0xBE14CCU, 0xBE2518U, 0xBE3DF2U, 0xBE46B0U, 0xBE5E5AU, 0xBE6F8EU, 0xBE7764U, 0xBE81E0U, 0xBE990AU, + 0xBEA8DEU, 0xBEB034U, 0xBECB76U, 0xBED39CU, 0xBEE248U, 0xBEFAA2U, 0xBF0F40U, 0xBF17AAU, 0xBF267EU, 0xBF3E94U, + 0xBF45D6U, 0xBF5D3CU, 0xBF6CE8U, 0xBF7402U, 0xBF8286U, 0xBF9A6CU, 0xBFABB8U, 0xBFB352U, 0xBFC810U, 0xBFD0FAU, + 0xBFE12EU, 0xBFF9C4U, 0xC00A4EU, 0xC012A4U, 0xC02370U, 0xC03B9AU, 0xC040D8U, 0xC05832U, 0xC069E6U, 0xC0710CU, + 0xC08788U, 0xC09F62U, 0xC0AEB6U, 0xC0B65CU, 0xC0CD1EU, 0xC0D5F4U, 0xC0E420U, 0xC0FCCAU, 0xC10928U, 0xC111C2U, + 0xC12016U, 0xC138FCU, 0xC143BEU, 0xC15B54U, 0xC16A80U, 0xC1726AU, 0xC184EEU, 0xC19C04U, 0xC1ADD0U, 0xC1B53AU, + 0xC1CE78U, 0xC1D692U, 0xC1E746U, 0xC1FFACU, 0xC20C82U, 0xC21468U, 0xC225BCU, 0xC23D56U, 0xC24614U, 0xC25EFEU, + 0xC26F2AU, 0xC277C0U, 0xC28144U, 0xC299AEU, 0xC2A87AU, 0xC2B090U, 0xC2CBD2U, 0xC2D338U, 0xC2E2ECU, 0xC2FA06U, + 0xC30FE4U, 0xC3170EU, 0xC326DAU, 0xC33E30U, 0xC34572U, 0xC35D98U, 0xC36C4CU, 0xC374A6U, 0xC38222U, 0xC39AC8U, + 0xC3AB1CU, 0xC3B3F6U, 0xC3C8B4U, 0xC3D05EU, 0xC3E18AU, 0xC3F960U, 0xC407D6U, 0xC41F3CU, 0xC42EE8U, 0xC43602U, + 0xC44D40U, 0xC455AAU, 0xC4647EU, 0xC47C94U, 0xC48A10U, 0xC492FAU, 0xC4A32EU, 0xC4BBC4U, 0xC4C086U, 0xC4D86CU, + 0xC4E9B8U, 0xC4F152U, 0xC504B0U, 0xC51C5AU, 0xC52D8EU, 0xC53564U, 0xC54E26U, 0xC556CCU, 0xC56718U, 0xC57FF2U, + 0xC58976U, 0xC5919CU, 0xC5A048U, 0xC5B8A2U, 0xC5C3E0U, 0xC5DB0AU, 0xC5EADEU, 0xC5F234U, 0xC6011AU, 0xC619F0U, + 0xC62824U, 0xC630CEU, 0xC64B8CU, 0xC65366U, 0xC662B2U, 0xC67A58U, 0xC68CDCU, 0xC69436U, 0xC6A5E2U, 0xC6BD08U, + 0xC6C64AU, 0xC6DEA0U, 0xC6EF74U, 0xC6F79EU, 0xC7027CU, 0xC71A96U, 0xC72B42U, 0xC733A8U, 0xC748EAU, 0xC75000U, + 0xC761D4U, 0xC7793EU, 0xC78FBAU, 0xC79750U, 0xC7A684U, 0xC7BE6EU, 0xC7C52CU, 0xC7DDC6U, 0xC7EC12U, 0xC7F4F8U, + 0xC80994U, 0xC8117EU, 0xC820AAU, 0xC83840U, 0xC84302U, 0xC85BE8U, 0xC86A3CU, 0xC872D6U, 0xC88452U, 0xC89CB8U, + 0xC8AD6CU, 0xC8B586U, 0xC8CEC4U, 0xC8D62EU, 0xC8E7FAU, 0xC8FF10U, 0xC90AF2U, 0xC91218U, 0xC923CCU, 0xC93B26U, + 0xC94064U, 0xC9588EU, 0xC9695AU, 0xC971B0U, 0xC98734U, 0xC99FDEU, 0xC9AE0AU, 0xC9B6E0U, 0xC9CDA2U, 0xC9D548U, + 0xC9E49CU, 0xC9FC76U, 0xCA0F58U, 0xCA17B2U, 0xCA2666U, 0xCA3E8CU, 0xCA45CEU, 0xCA5D24U, 0xCA6CF0U, 0xCA741AU, + 0xCA829EU, 0xCA9A74U, 0xCAABA0U, 0xCAB34AU, 0xCAC808U, 0xCAD0E2U, 0xCAE136U, 0xCAF9DCU, 0xCB0C3EU, 0xCB14D4U, + 0xCB2500U, 0xCB3DEAU, 0xCB46A8U, 0xCB5E42U, 0xCB6F96U, 0xCB777CU, 0xCB81F8U, 0xCB9912U, 0xCBA8C6U, 0xCBB02CU, + 0xCBCB6EU, 0xCBD384U, 0xCBE250U, 0xCBFABAU, 0xCC040CU, 0xCC1CE6U, 0xCC2D32U, 0xCC35D8U, 0xCC4E9AU, 0xCC5670U, + 0xCC67A4U, 0xCC7F4EU, 0xCC89CAU, 0xCC9120U, 0xCCA0F4U, 0xCCB81EU, 0xCCC35CU, 0xCCDBB6U, 0xCCEA62U, 0xCCF288U, + 0xCD076AU, 0xCD1F80U, 0xCD2E54U, 0xCD36BEU, 0xCD4DFCU, 0xCD5516U, 0xCD64C2U, 0xCD7C28U, 0xCD8AACU, 0xCD9246U, + 0xCDA392U, 0xCDBB78U, 0xCDC03AU, 0xCDD8D0U, 0xCDE904U, 0xCDF1EEU, 0xCE02C0U, 0xCE1A2AU, 0xCE2BFEU, 0xCE3314U, + 0xCE4856U, 0xCE50BCU, 0xCE6168U, 0xCE7982U, 0xCE8F06U, 0xCE97ECU, 0xCEA638U, 0xCEBED2U, 0xCEC590U, 0xCEDD7AU, + 0xCEECAEU, 0xCEF444U, 0xCF01A6U, 0xCF194CU, 0xCF2898U, 0xCF3072U, 0xCF4B30U, 0xCF53DAU, 0xCF620EU, 0xCF7AE4U, + 0xCF8C60U, 0xCF948AU, 0xCFA55EU, 0xCFBDB4U, 0xCFC6F6U, 0xCFDE1CU, 0xCFEFC8U, 0xCFF722U, 0xD00DFAU, 0xD01510U, + 0xD024C4U, 0xD03C2EU, 0xD0476CU, 0xD05F86U, 0xD06E52U, 0xD076B8U, 0xD0803CU, 0xD098D6U, 0xD0A902U, 0xD0B1E8U, + 0xD0CAAAU, 0xD0D240U, 0xD0E394U, 0xD0FB7EU, 0xD10E9CU, 0xD11676U, 0xD127A2U, 0xD13F48U, 0xD1440AU, 0xD15CE0U, + 0xD16D34U, 0xD175DEU, 0xD1835AU, 0xD19BB0U, 0xD1AA64U, 0xD1B28EU, 0xD1C9CCU, 0xD1D126U, 0xD1E0F2U, 0xD1F818U, + 0xD20B36U, 0xD213DCU, 0xD22208U, 0xD23AE2U, 0xD241A0U, 0xD2594AU, 0xD2689EU, 0xD27074U, 0xD286F0U, 0xD29E1AU, + 0xD2AFCEU, 0xD2B724U, 0xD2CC66U, 0xD2D48CU, 0xD2E558U, 0xD2FDB2U, 0xD30850U, 0xD310BAU, 0xD3216EU, 0xD33984U, + 0xD342C6U, 0xD35A2CU, 0xD36BF8U, 0xD37312U, 0xD38596U, 0xD39D7CU, 0xD3ACA8U, 0xD3B442U, 0xD3CF00U, 0xD3D7EAU, + 0xD3E63EU, 0xD3FED4U, 0xD40062U, 0xD41888U, 0xD4295CU, 0xD431B6U, 0xD44AF4U, 0xD4521EU, 0xD463CAU, 0xD47B20U, + 0xD48DA4U, 0xD4954EU, 0xD4A49AU, 0xD4BC70U, 0xD4C732U, 0xD4DFD8U, 0xD4EE0CU, 0xD4F6E6U, 0xD50304U, 0xD51BEEU, + 0xD52A3AU, 0xD532D0U, 0xD54992U, 0xD55178U, 0xD560ACU, 0xD57846U, 0xD58EC2U, 0xD59628U, 0xD5A7FCU, 0xD5BF16U, + 0xD5C454U, 0xD5DCBEU, 0xD5ED6AU, 0xD5F580U, 0xD606AEU, 0xD61E44U, 0xD62F90U, 0xD6377AU, 0xD64C38U, 0xD654D2U, + 0xD66506U, 0xD67DECU, 0xD68B68U, 0xD69382U, 0xD6A256U, 0xD6BABCU, 0xD6C1FEU, 0xD6D914U, 0xD6E8C0U, 0xD6F02AU, + 0xD705C8U, 0xD71D22U, 0xD72CF6U, 0xD7341CU, 0xD74F5EU, 0xD757B4U, 0xD76660U, 0xD77E8AU, 0xD7880EU, 0xD790E4U, + 0xD7A130U, 0xD7B9DAU, 0xD7C298U, 0xD7DA72U, 0xD7EBA6U, 0xD7F34CU, 0xD80E20U, 0xD816CAU, 0xD8271EU, 0xD83FF4U, + 0xD844B6U, 0xD85C5CU, 0xD86D88U, 0xD87562U, 0xD883E6U, 0xD89B0CU, 0xD8AAD8U, 0xD8B232U, 0xD8C970U, 0xD8D19AU, + 0xD8E04EU, 0xD8F8A4U, 0xD90D46U, 0xD915ACU, 0xD92478U, 0xD93C92U, 0xD947D0U, 0xD95F3AU, 0xD96EEEU, 0xD97604U, + 0xD98080U, 0xD9986AU, 0xD9A9BEU, 0xD9B154U, 0xD9CA16U, 0xD9D2FCU, 0xD9E328U, 0xD9FBC2U, 0xDA08ECU, 0xDA1006U, + 0xDA21D2U, 0xDA3938U, 0xDA427AU, 0xDA5A90U, 0xDA6B44U, 0xDA73AEU, 0xDA852AU, 0xDA9DC0U, 0xDAAC14U, 0xDAB4FEU, + 0xDACFBCU, 0xDAD756U, 0xDAE682U, 0xDAFE68U, 0xDB0B8AU, 0xDB1360U, 0xDB22B4U, 0xDB3A5EU, 0xDB411CU, 0xDB59F6U, + 0xDB6822U, 0xDB70C8U, 0xDB864CU, 0xDB9EA6U, 0xDBAF72U, 0xDBB798U, 0xDBCCDAU, 0xDBD430U, 0xDBE5E4U, 0xDBFD0EU, + 0xDC03B8U, 0xDC1B52U, 0xDC2A86U, 0xDC326CU, 0xDC492EU, 0xDC51C4U, 0xDC6010U, 0xDC78FAU, 0xDC8E7EU, 0xDC9694U, + 0xDCA740U, 0xDCBFAAU, 0xDCC4E8U, 0xDCDC02U, 0xDCEDD6U, 0xDCF53CU, 0xDD00DEU, 0xDD1834U, 0xDD29E0U, 0xDD310AU, + 0xDD4A48U, 0xDD52A2U, 0xDD6376U, 0xDD7B9CU, 0xDD8D18U, 0xDD95F2U, 0xDDA426U, 0xDDBCCCU, 0xDDC78EU, 0xDDDF64U, + 0xDDEEB0U, 0xDDF65AU, 0xDE0574U, 0xDE1D9EU, 0xDE2C4AU, 0xDE34A0U, 0xDE4FE2U, 0xDE5708U, 0xDE66DCU, 0xDE7E36U, + 0xDE88B2U, 0xDE9058U, 0xDEA18CU, 0xDEB966U, 0xDEC224U, 0xDEDACEU, 0xDEEB1AU, 0xDEF3F0U, 0xDF0612U, 0xDF1EF8U, + 0xDF2F2CU, 0xDF37C6U, 0xDF4C84U, 0xDF546EU, 0xDF65BAU, 0xDF7D50U, 0xDF8BD4U, 0xDF933EU, 0xDFA2EAU, 0xDFBA00U, + 0xDFC142U, 0xDFD9A8U, 0xDFE87CU, 0xDFF096U, 0xE00526U, 0xE01DCCU, 0xE02C18U, 0xE034F2U, 0xE04FB0U, 0xE0575AU, + 0xE0668EU, 0xE07E64U, 0xE088E0U, 0xE0900AU, 0xE0A1DEU, 0xE0B934U, 0xE0C276U, 0xE0DA9CU, 0xE0EB48U, 0xE0F3A2U, + 0xE10640U, 0xE11EAAU, 0xE12F7EU, 0xE13794U, 0xE14CD6U, 0xE1543CU, 0xE165E8U, 0xE17D02U, 0xE18B86U, 0xE1936CU, + 0xE1A2B8U, 0xE1BA52U, 0xE1C110U, 0xE1D9FAU, 0xE1E82EU, 0xE1F0C4U, 0xE203EAU, 0xE21B00U, 0xE22AD4U, 0xE2323EU, + 0xE2497CU, 0xE25196U, 0xE26042U, 0xE278A8U, 0xE28E2CU, 0xE296C6U, 0xE2A712U, 0xE2BFF8U, 0xE2C4BAU, 0xE2DC50U, + 0xE2ED84U, 0xE2F56EU, 0xE3008CU, 0xE31866U, 0xE329B2U, 0xE33158U, 0xE34A1AU, 0xE352F0U, 0xE36324U, 0xE37BCEU, + 0xE38D4AU, 0xE395A0U, 0xE3A474U, 0xE3BC9EU, 0xE3C7DCU, 0xE3DF36U, 0xE3EEE2U, 0xE3F608U, 0xE408BEU, 0xE41054U, + 0xE42180U, 0xE4396AU, 0xE44228U, 0xE45AC2U, 0xE46B16U, 0xE473FCU, 0xE48578U, 0xE49D92U, 0xE4AC46U, 0xE4B4ACU, + 0xE4CFEEU, 0xE4D704U, 0xE4E6D0U, 0xE4FE3AU, 0xE50BD8U, 0xE51332U, 0xE522E6U, 0xE53A0CU, 0xE5414EU, 0xE559A4U, + 0xE56870U, 0xE5709AU, 0xE5861EU, 0xE59EF4U, 0xE5AF20U, 0xE5B7CAU, 0xE5CC88U, 0xE5D462U, 0xE5E5B6U, 0xE5FD5CU, + 0xE60E72U, 0xE61698U, 0xE6274CU, 0xE63FA6U, 0xE644E4U, 0xE65C0EU, 0xE66DDAU, 0xE67530U, 0xE683B4U, 0xE69B5EU, + 0xE6AA8AU, 0xE6B260U, 0xE6C922U, 0xE6D1C8U, 0xE6E01CU, 0xE6F8F6U, 0xE70D14U, 0xE715FEU, 0xE7242AU, 0xE73CC0U, + 0xE74782U, 0xE75F68U, 0xE76EBCU, 0xE77656U, 0xE780D2U, 0xE79838U, 0xE7A9ECU, 0xE7B106U, 0xE7CA44U, 0xE7D2AEU, + 0xE7E37AU, 0xE7FB90U, 0xE806FCU, 0xE81E16U, 0xE82FC2U, 0xE83728U, 0xE84C6AU, 0xE85480U, 0xE86554U, 0xE87DBEU, + 0xE88B3AU, 0xE893D0U, 0xE8A204U, 0xE8BAEEU, 0xE8C1ACU, 0xE8D946U, 0xE8E892U, 0xE8F078U, 0xE9059AU, 0xE91D70U, + 0xE92CA4U, 0xE9344EU, 0xE94F0CU, 0xE957E6U, 0xE96632U, 0xE97ED8U, 0xE9885CU, 0xE990B6U, 0xE9A162U, 0xE9B988U, + 0xE9C2CAU, 0xE9DA20U, 0xE9EBF4U, 0xE9F31EU, 0xEA0030U, 0xEA18DAU, 0xEA290EU, 0xEA31E4U, 0xEA4AA6U, 0xEA524CU, + 0xEA6398U, 0xEA7B72U, 0xEA8DF6U, 0xEA951CU, 0xEAA4C8U, 0xEABC22U, 0xEAC760U, 0xEADF8AU, 0xEAEE5EU, 0xEAF6B4U, + 0xEB0356U, 0xEB1BBCU, 0xEB2A68U, 0xEB3282U, 0xEB49C0U, 0xEB512AU, 0xEB60FEU, 0xEB7814U, 0xEB8E90U, 0xEB967AU, + 0xEBA7AEU, 0xEBBF44U, 0xEBC406U, 0xEBDCECU, 0xEBED38U, 0xEBF5D2U, 0xEC0B64U, 0xEC138EU, 0xEC225AU, 0xEC3AB0U, + 0xEC41F2U, 0xEC5918U, 0xEC68CCU, 0xEC7026U, 0xEC86A2U, 0xEC9E48U, 0xECAF9CU, 0xECB776U, 0xECCC34U, 0xECD4DEU, + 0xECE50AU, 0xECFDE0U, 0xED0802U, 0xED10E8U, 0xED213CU, 0xED39D6U, 0xED4294U, 0xED5A7EU, 0xED6BAAU, 0xED7340U, + 0xED85C4U, 0xED9D2EU, 0xEDACFAU, 0xEDB410U, 0xEDCF52U, 0xEDD7B8U, 0xEDE66CU, 0xEDFE86U, 0xEE0DA8U, 0xEE1542U, + 0xEE2496U, 0xEE3C7CU, 0xEE473EU, 0xEE5FD4U, 0xEE6E00U, 0xEE76EAU, 0xEE806EU, 0xEE9884U, 0xEEA950U, 0xEEB1BAU, + 0xEECAF8U, 0xEED212U, 0xEEE3C6U, 0xEEFB2CU, 0xEF0ECEU, 0xEF1624U, 0xEF27F0U, 0xEF3F1AU, 0xEF4458U, 0xEF5CB2U, + 0xEF6D66U, 0xEF758CU, 0xEF8308U, 0xEF9BE2U, 0xEFAA36U, 0xEFB2DCU, 0xEFC99EU, 0xEFD174U, 0xEFE0A0U, 0xEFF84AU, + 0xF00292U, 0xF01A78U, 0xF02BACU, 0xF03346U, 0xF04804U, 0xF050EEU, 0xF0613AU, 0xF079D0U, 0xF08F54U, 0xF097BEU, + 0xF0A66AU, 0xF0BE80U, 0xF0C5C2U, 0xF0DD28U, 0xF0ECFCU, 0xF0F416U, 0xF101F4U, 0xF1191EU, 0xF128CAU, 0xF13020U, + 0xF14B62U, 0xF15388U, 0xF1625CU, 0xF17AB6U, 0xF18C32U, 0xF194D8U, 0xF1A50CU, 0xF1BDE6U, 0xF1C6A4U, 0xF1DE4EU, + 0xF1EF9AU, 0xF1F770U, 0xF2045EU, 0xF21CB4U, 0xF22D60U, 0xF2358AU, 0xF24EC8U, 0xF25622U, 0xF267F6U, 0xF27F1CU, + 0xF28998U, 0xF29172U, 0xF2A0A6U, 0xF2B84CU, 0xF2C30EU, 0xF2DBE4U, 0xF2EA30U, 0xF2F2DAU, 0xF30738U, 0xF31FD2U, + 0xF32E06U, 0xF336ECU, 0xF34DAEU, 0xF35544U, 0xF36490U, 0xF37C7AU, 0xF38AFEU, 0xF39214U, 0xF3A3C0U, 0xF3BB2AU, + 0xF3C068U, 0xF3D882U, 0xF3E956U, 0xF3F1BCU, 0xF40F0AU, 0xF417E0U, 0xF42634U, 0xF43EDEU, 0xF4459CU, 0xF45D76U, + 0xF46CA2U, 0xF47448U, 0xF482CCU, 0xF49A26U, 0xF4ABF2U, 0xF4B318U, 0xF4C85AU, 0xF4D0B0U, 0xF4E164U, 0xF4F98EU, + 0xF50C6CU, 0xF51486U, 0xF52552U, 0xF53DB8U, 0xF546FAU, 0xF55E10U, 0xF56FC4U, 0xF5772EU, 0xF581AAU, 0xF59940U, + 0xF5A894U, 0xF5B07EU, 0xF5CB3CU, 0xF5D3D6U, 0xF5E202U, 0xF5FAE8U, 0xF609C6U, 0xF6112CU, 0xF620F8U, 0xF63812U, + 0xF64350U, 0xF65BBAU, 0xF66A6EU, 0xF67284U, 0xF68400U, 0xF69CEAU, 0xF6AD3EU, 0xF6B5D4U, 0xF6CE96U, 0xF6D67CU, + 0xF6E7A8U, 0xF6FF42U, 0xF70AA0U, 0xF7124AU, 0xF7239EU, 0xF73B74U, 0xF74036U, 0xF758DCU, 0xF76908U, 0xF771E2U, + 0xF78766U, 0xF79F8CU, 0xF7AE58U, 0xF7B6B2U, 0xF7CDF0U, 0xF7D51AU, 0xF7E4CEU, 0xF7FC24U, 0xF80148U, 0xF819A2U, + 0xF82876U, 0xF8309CU, 0xF84BDEU, 0xF85334U, 0xF862E0U, 0xF87A0AU, 0xF88C8EU, 0xF89464U, 0xF8A5B0U, 0xF8BD5AU, + 0xF8C618U, 0xF8DEF2U, 0xF8EF26U, 0xF8F7CCU, 0xF9022EU, 0xF91AC4U, 0xF92B10U, 0xF933FAU, 0xF948B8U, 0xF95052U, + 0xF96186U, 0xF9796CU, 0xF98FE8U, 0xF99702U, 0xF9A6D6U, 0xF9BE3CU, 0xF9C57EU, 0xF9DD94U, 0xF9EC40U, 0xF9F4AAU, + 0xFA0784U, 0xFA1F6EU, 0xFA2EBAU, 0xFA3650U, 0xFA4D12U, 0xFA55F8U, 0xFA642CU, 0xFA7CC6U, 0xFA8A42U, 0xFA92A8U, + 0xFAA37CU, 0xFABB96U, 0xFAC0D4U, 0xFAD83EU, 0xFAE9EAU, 0xFAF100U, 0xFB04E2U, 0xFB1C08U, 0xFB2DDCU, 0xFB3536U, + 0xFB4E74U, 0xFB569EU, 0xFB674AU, 0xFB7FA0U, 0xFB8924U, 0xFB91CEU, 0xFBA01AU, 0xFBB8F0U, 0xFBC3B2U, 0xFBDB58U, + 0xFBEA8CU, 0xFBF266U, 0xFC0CD0U, 0xFC143AU, 0xFC25EEU, 0xFC3D04U, 0xFC4646U, 0xFC5EACU, 0xFC6F78U, 0xFC7792U, + 0xFC8116U, 0xFC99FCU, 0xFCA828U, 0xFCB0C2U, 0xFCCB80U, 0xFCD36AU, 0xFCE2BEU, 0xFCFA54U, 0xFD0FB6U, 0xFD175CU, + 0xFD2688U, 0xFD3E62U, 0xFD4520U, 0xFD5DCAU, 0xFD6C1EU, 0xFD74F4U, 0xFD8270U, 0xFD9A9AU, 0xFDAB4EU, 0xFDB3A4U, + 0xFDC8E6U, 0xFDD00CU, 0xFDE1D8U, 0xFDF932U, 0xFE0A1CU, 0xFE12F6U, 0xFE2322U, 0xFE3BC8U, 0xFE408AU, 0xFE5860U, + 0xFE69B4U, 0xFE715EU, 0xFE87DAU, 0xFE9F30U, 0xFEAEE4U, 0xFEB60EU, 0xFECD4CU, 0xFED5A6U, 0xFEE472U, 0xFEFC98U, + 0xFF097AU, 0xFF1190U, 0xFF2044U, 0xFF38AEU, 0xFF43ECU, 0xFF5B06U, 0xFF6AD2U, 0xFF7238U, 0xFF84BCU, 0xFF9C56U, + 0xFFAD82U, 0xFFB568U, 0xFFCE2AU, 0xFFD6C0U, 0xFFE714U, 0xFFFFFEU}; + +static const unsigned int ENCODING_TABLE_24128[] = { + 0x000000U, 0x0018EBU, 0x00293EU, 0x0031D5U, 0x004A97U, 0x00527CU, 0x0063A9U, 0x007B42U, 0x008DC6U, 0x00952DU, + 0x00A4F8U, 0x00BC13U, 0x00C751U, 0x00DFBAU, 0x00EE6FU, 0x00F684U, 0x010367U, 0x011B8CU, 0x012A59U, 0x0132B2U, + 0x0149F0U, 0x01511BU, 0x0160CEU, 0x017825U, 0x018EA1U, 0x01964AU, 0x01A79FU, 0x01BF74U, 0x01C436U, 0x01DCDDU, + 0x01ED08U, 0x01F5E3U, 0x0206CDU, 0x021E26U, 0x022FF3U, 0x023718U, 0x024C5AU, 0x0254B1U, 0x026564U, 0x027D8FU, + 0x028B0BU, 0x0293E0U, 0x02A235U, 0x02BADEU, 0x02C19CU, 0x02D977U, 0x02E8A2U, 0x02F049U, 0x0305AAU, 0x031D41U, + 0x032C94U, 0x03347FU, 0x034F3DU, 0x0357D6U, 0x036603U, 0x037EE8U, 0x03886CU, 0x039087U, 0x03A152U, 0x03B9B9U, + 0x03C2FBU, 0x03DA10U, 0x03EBC5U, 0x03F32EU, 0x040D99U, 0x041572U, 0x0424A7U, 0x043C4CU, 0x04470EU, 0x045FE5U, + 0x046E30U, 0x0476DBU, 0x04805FU, 0x0498B4U, 0x04A961U, 0x04B18AU, 0x04CAC8U, 0x04D223U, 0x04E3F6U, 0x04FB1DU, + 0x050EFEU, 0x051615U, 0x0527C0U, 0x053F2BU, 0x054469U, 0x055C82U, 0x056D57U, 0x0575BCU, 0x058338U, 0x059BD3U, + 0x05AA06U, 0x05B2EDU, 0x05C9AFU, 0x05D144U, 0x05E091U, 0x05F87AU, 0x060B54U, 0x0613BFU, 0x06226AU, 0x063A81U, + 0x0641C3U, 0x065928U, 0x0668FDU, 0x067016U, 0x068692U, 0x069E79U, 0x06AFACU, 0x06B747U, 0x06CC05U, 0x06D4EEU, + 0x06E53BU, 0x06FDD0U, 0x070833U, 0x0710D8U, 0x07210DU, 0x0739E6U, 0x0742A4U, 0x075A4FU, 0x076B9AU, 0x077371U, + 0x0785F5U, 0x079D1EU, 0x07ACCBU, 0x07B420U, 0x07CF62U, 0x07D789U, 0x07E65CU, 0x07FEB7U, 0x0803DAU, 0x081B31U, + 0x082AE4U, 0x08320FU, 0x08494DU, 0x0851A6U, 0x086073U, 0x087898U, 0x088E1CU, 0x0896F7U, 0x08A722U, 0x08BFC9U, + 0x08C48BU, 0x08DC60U, 0x08EDB5U, 0x08F55EU, 0x0900BDU, 0x091856U, 0x092983U, 0x093168U, 0x094A2AU, 0x0952C1U, + 0x096314U, 0x097BFFU, 0x098D7BU, 0x099590U, 0x09A445U, 0x09BCAEU, 0x09C7ECU, 0x09DF07U, 0x09EED2U, 0x09F639U, + 0x0A0517U, 0x0A1DFCU, 0x0A2C29U, 0x0A34C2U, 0x0A4F80U, 0x0A576BU, 0x0A66BEU, 0x0A7E55U, 0x0A88D1U, 0x0A903AU, + 0x0AA1EFU, 0x0AB904U, 0x0AC246U, 0x0ADAADU, 0x0AEB78U, 0x0AF393U, 0x0B0670U, 0x0B1E9BU, 0x0B2F4EU, 0x0B37A5U, + 0x0B4CE7U, 0x0B540CU, 0x0B65D9U, 0x0B7D32U, 0x0B8BB6U, 0x0B935DU, 0x0BA288U, 0x0BBA63U, 0x0BC121U, 0x0BD9CAU, + 0x0BE81FU, 0x0BF0F4U, 0x0C0E43U, 0x0C16A8U, 0x0C277DU, 0x0C3F96U, 0x0C44D4U, 0x0C5C3FU, 0x0C6DEAU, 0x0C7501U, + 0x0C8385U, 0x0C9B6EU, 0x0CAABBU, 0x0CB250U, 0x0CC912U, 0x0CD1F9U, 0x0CE02CU, 0x0CF8C7U, 0x0D0D24U, 0x0D15CFU, + 0x0D241AU, 0x0D3CF1U, 0x0D47B3U, 0x0D5F58U, 0x0D6E8DU, 0x0D7666U, 0x0D80E2U, 0x0D9809U, 0x0DA9DCU, 0x0DB137U, + 0x0DCA75U, 0x0DD29EU, 0x0DE34BU, 0x0DFBA0U, 0x0E088EU, 0x0E1065U, 0x0E21B0U, 0x0E395BU, 0x0E4219U, 0x0E5AF2U, + 0x0E6B27U, 0x0E73CCU, 0x0E8548U, 0x0E9DA3U, 0x0EAC76U, 0x0EB49DU, 0x0ECFDFU, 0x0ED734U, 0x0EE6E1U, 0x0EFE0AU, + 0x0F0BE9U, 0x0F1302U, 0x0F22D7U, 0x0F3A3CU, 0x0F417EU, 0x0F5995U, 0x0F6840U, 0x0F70ABU, 0x0F862FU, 0x0F9EC4U, + 0x0FAF11U, 0x0FB7FAU, 0x0FCCB8U, 0x0FD453U, 0x0FE586U, 0x0FFD6DU, 0x1007B4U, 0x101F5FU, 0x102E8AU, 0x103661U, + 0x104D23U, 0x1055C8U, 0x10641DU, 0x107CF6U, 0x108A72U, 0x109299U, 0x10A34CU, 0x10BBA7U, 0x10C0E5U, 0x10D80EU, + 0x10E9DBU, 0x10F130U, 0x1104D3U, 0x111C38U, 0x112DEDU, 0x113506U, 0x114E44U, 0x1156AFU, 0x11677AU, 0x117F91U, + 0x118915U, 0x1191FEU, 0x11A02BU, 0x11B8C0U, 0x11C382U, 0x11DB69U, 0x11EABCU, 0x11F257U, 0x120179U, 0x121992U, + 0x122847U, 0x1230ACU, 0x124BEEU, 0x125305U, 0x1262D0U, 0x127A3BU, 0x128CBFU, 0x129454U, 0x12A581U, 0x12BD6AU, + 0x12C628U, 0x12DEC3U, 0x12EF16U, 0x12F7FDU, 0x13021EU, 0x131AF5U, 0x132B20U, 0x1333CBU, 0x134889U, 0x135062U, + 0x1361B7U, 0x13795CU, 0x138FD8U, 0x139733U, 0x13A6E6U, 0x13BE0DU, 0x13C54FU, 0x13DDA4U, 0x13EC71U, 0x13F49AU, + 0x140A2DU, 0x1412C6U, 0x142313U, 0x143BF8U, 0x1440BAU, 0x145851U, 0x146984U, 0x14716FU, 0x1487EBU, 0x149F00U, + 0x14AED5U, 0x14B63EU, 0x14CD7CU, 0x14D597U, 0x14E442U, 0x14FCA9U, 0x15094AU, 0x1511A1U, 0x152074U, 0x15389FU, + 0x1543DDU, 0x155B36U, 0x156AE3U, 0x157208U, 0x15848CU, 0x159C67U, 0x15ADB2U, 0x15B559U, 0x15CE1BU, 0x15D6F0U, + 0x15E725U, 0x15FFCEU, 0x160CE0U, 0x16140BU, 0x1625DEU, 0x163D35U, 0x164677U, 0x165E9CU, 0x166F49U, 0x1677A2U, + 0x168126U, 0x1699CDU, 0x16A818U, 0x16B0F3U, 0x16CBB1U, 0x16D35AU, 0x16E28FU, 0x16FA64U, 0x170F87U, 0x17176CU, + 0x1726B9U, 0x173E52U, 0x174510U, 0x175DFBU, 0x176C2EU, 0x1774C5U, 0x178241U, 0x179AAAU, 0x17AB7FU, 0x17B394U, + 0x17C8D6U, 0x17D03DU, 0x17E1E8U, 0x17F903U, 0x18046EU, 0x181C85U, 0x182D50U, 0x1835BBU, 0x184EF9U, 0x185612U, + 0x1867C7U, 0x187F2CU, 0x1889A8U, 0x189143U, 0x18A096U, 0x18B87DU, 0x18C33FU, 0x18DBD4U, 0x18EA01U, 0x18F2EAU, + 0x190709U, 0x191FE2U, 0x192E37U, 0x1936DCU, 0x194D9EU, 0x195575U, 0x1964A0U, 0x197C4BU, 0x198ACFU, 0x199224U, + 0x19A3F1U, 0x19BB1AU, 0x19C058U, 0x19D8B3U, 0x19E966U, 0x19F18DU, 0x1A02A3U, 0x1A1A48U, 0x1A2B9DU, 0x1A3376U, + 0x1A4834U, 0x1A50DFU, 0x1A610AU, 0x1A79E1U, 0x1A8F65U, 0x1A978EU, 0x1AA65BU, 0x1ABEB0U, 0x1AC5F2U, 0x1ADD19U, + 0x1AECCCU, 0x1AF427U, 0x1B01C4U, 0x1B192FU, 0x1B28FAU, 0x1B3011U, 0x1B4B53U, 0x1B53B8U, 0x1B626DU, 0x1B7A86U, + 0x1B8C02U, 0x1B94E9U, 0x1BA53CU, 0x1BBDD7U, 0x1BC695U, 0x1BDE7EU, 0x1BEFABU, 0x1BF740U, 0x1C09F7U, 0x1C111CU, + 0x1C20C9U, 0x1C3822U, 0x1C4360U, 0x1C5B8BU, 0x1C6A5EU, 0x1C72B5U, 0x1C8431U, 0x1C9CDAU, 0x1CAD0FU, 0x1CB5E4U, + 0x1CCEA6U, 0x1CD64DU, 0x1CE798U, 0x1CFF73U, 0x1D0A90U, 0x1D127BU, 0x1D23AEU, 0x1D3B45U, 0x1D4007U, 0x1D58ECU, + 0x1D6939U, 0x1D71D2U, 0x1D8756U, 0x1D9FBDU, 0x1DAE68U, 0x1DB683U, 0x1DCDC1U, 0x1DD52AU, 0x1DE4FFU, 0x1DFC14U, + 0x1E0F3AU, 0x1E17D1U, 0x1E2604U, 0x1E3EEFU, 0x1E45ADU, 0x1E5D46U, 0x1E6C93U, 0x1E7478U, 0x1E82FCU, 0x1E9A17U, + 0x1EABC2U, 0x1EB329U, 0x1EC86BU, 0x1ED080U, 0x1EE155U, 0x1EF9BEU, 0x1F0C5DU, 0x1F14B6U, 0x1F2563U, 0x1F3D88U, + 0x1F46CAU, 0x1F5E21U, 0x1F6FF4U, 0x1F771FU, 0x1F819BU, 0x1F9970U, 0x1FA8A5U, 0x1FB04EU, 0x1FCB0CU, 0x1FD3E7U, + 0x1FE232U, 0x1FFAD9U, 0x200F68U, 0x201783U, 0x202656U, 0x203EBDU, 0x2045FFU, 0x205D14U, 0x206CC1U, 0x20742AU, + 0x2082AEU, 0x209A45U, 0x20AB90U, 0x20B37BU, 0x20C839U, 0x20D0D2U, 0x20E107U, 0x20F9ECU, 0x210C0FU, 0x2114E4U, + 0x212531U, 0x213DDAU, 0x214698U, 0x215E73U, 0x216FA6U, 0x21774DU, 0x2181C9U, 0x219922U, 0x21A8F7U, 0x21B01CU, + 0x21CB5EU, 0x21D3B5U, 0x21E260U, 0x21FA8BU, 0x2209A5U, 0x22114EU, 0x22209BU, 0x223870U, 0x224332U, 0x225BD9U, + 0x226A0CU, 0x2272E7U, 0x228463U, 0x229C88U, 0x22AD5DU, 0x22B5B6U, 0x22CEF4U, 0x22D61FU, 0x22E7CAU, 0x22FF21U, + 0x230AC2U, 0x231229U, 0x2323FCU, 0x233B17U, 0x234055U, 0x2358BEU, 0x23696BU, 0x237180U, 0x238704U, 0x239FEFU, + 0x23AE3AU, 0x23B6D1U, 0x23CD93U, 0x23D578U, 0x23E4ADU, 0x23FC46U, 0x2402F1U, 0x241A1AU, 0x242BCFU, 0x243324U, + 0x244866U, 0x24508DU, 0x246158U, 0x2479B3U, 0x248F37U, 0x2497DCU, 0x24A609U, 0x24BEE2U, 0x24C5A0U, 0x24DD4BU, + 0x24EC9EU, 0x24F475U, 0x250196U, 0x25197DU, 0x2528A8U, 0x253043U, 0x254B01U, 0x2553EAU, 0x25623FU, 0x257AD4U, + 0x258C50U, 0x2594BBU, 0x25A56EU, 0x25BD85U, 0x25C6C7U, 0x25DE2CU, 0x25EFF9U, 0x25F712U, 0x26043CU, 0x261CD7U, + 0x262D02U, 0x2635E9U, 0x264EABU, 0x265640U, 0x266795U, 0x267F7EU, 0x2689FAU, 0x269111U, 0x26A0C4U, 0x26B82FU, + 0x26C36DU, 0x26DB86U, 0x26EA53U, 0x26F2B8U, 0x27075BU, 0x271FB0U, 0x272E65U, 0x27368EU, 0x274DCCU, 0x275527U, + 0x2764F2U, 0x277C19U, 0x278A9DU, 0x279276U, 0x27A3A3U, 0x27BB48U, 0x27C00AU, 0x27D8E1U, 0x27E934U, 0x27F1DFU, + 0x280CB2U, 0x281459U, 0x28258CU, 0x283D67U, 0x284625U, 0x285ECEU, 0x286F1BU, 0x2877F0U, 0x288174U, 0x28999FU, + 0x28A84AU, 0x28B0A1U, 0x28CBE3U, 0x28D308U, 0x28E2DDU, 0x28FA36U, 0x290FD5U, 0x29173EU, 0x2926EBU, 0x293E00U, + 0x294542U, 0x295DA9U, 0x296C7CU, 0x297497U, 0x298213U, 0x299AF8U, 0x29AB2DU, 0x29B3C6U, 0x29C884U, 0x29D06FU, + 0x29E1BAU, 0x29F951U, 0x2A0A7FU, 0x2A1294U, 0x2A2341U, 0x2A3BAAU, 0x2A40E8U, 0x2A5803U, 0x2A69D6U, 0x2A713DU, + 0x2A87B9U, 0x2A9F52U, 0x2AAE87U, 0x2AB66CU, 0x2ACD2EU, 0x2AD5C5U, 0x2AE410U, 0x2AFCFBU, 0x2B0918U, 0x2B11F3U, + 0x2B2026U, 0x2B38CDU, 0x2B438FU, 0x2B5B64U, 0x2B6AB1U, 0x2B725AU, 0x2B84DEU, 0x2B9C35U, 0x2BADE0U, 0x2BB50BU, + 0x2BCE49U, 0x2BD6A2U, 0x2BE777U, 0x2BFF9CU, 0x2C012BU, 0x2C19C0U, 0x2C2815U, 0x2C30FEU, 0x2C4BBCU, 0x2C5357U, + 0x2C6282U, 0x2C7A69U, 0x2C8CEDU, 0x2C9406U, 0x2CA5D3U, 0x2CBD38U, 0x2CC67AU, 0x2CDE91U, 0x2CEF44U, 0x2CF7AFU, + 0x2D024CU, 0x2D1AA7U, 0x2D2B72U, 0x2D3399U, 0x2D48DBU, 0x2D5030U, 0x2D61E5U, 0x2D790EU, 0x2D8F8AU, 0x2D9761U, + 0x2DA6B4U, 0x2DBE5FU, 0x2DC51DU, 0x2DDDF6U, 0x2DEC23U, 0x2DF4C8U, 0x2E07E6U, 0x2E1F0DU, 0x2E2ED8U, 0x2E3633U, + 0x2E4D71U, 0x2E559AU, 0x2E644FU, 0x2E7CA4U, 0x2E8A20U, 0x2E92CBU, 0x2EA31EU, 0x2EBBF5U, 0x2EC0B7U, 0x2ED85CU, + 0x2EE989U, 0x2EF162U, 0x2F0481U, 0x2F1C6AU, 0x2F2DBFU, 0x2F3554U, 0x2F4E16U, 0x2F56FDU, 0x2F6728U, 0x2F7FC3U, + 0x2F8947U, 0x2F91ACU, 0x2FA079U, 0x2FB892U, 0x2FC3D0U, 0x2FDB3BU, 0x2FEAEEU, 0x2FF205U, 0x3008DCU, 0x301037U, + 0x3021E2U, 0x303909U, 0x30424BU, 0x305AA0U, 0x306B75U, 0x30739EU, 0x30851AU, 0x309DF1U, 0x30AC24U, 0x30B4CFU, + 0x30CF8DU, 0x30D766U, 0x30E6B3U, 0x30FE58U, 0x310BBBU, 0x311350U, 0x312285U, 0x313A6EU, 0x31412CU, 0x3159C7U, + 0x316812U, 0x3170F9U, 0x31867DU, 0x319E96U, 0x31AF43U, 0x31B7A8U, 0x31CCEAU, 0x31D401U, 0x31E5D4U, 0x31FD3FU, + 0x320E11U, 0x3216FAU, 0x32272FU, 0x323FC4U, 0x324486U, 0x325C6DU, 0x326DB8U, 0x327553U, 0x3283D7U, 0x329B3CU, + 0x32AAE9U, 0x32B202U, 0x32C940U, 0x32D1ABU, 0x32E07EU, 0x32F895U, 0x330D76U, 0x33159DU, 0x332448U, 0x333CA3U, + 0x3347E1U, 0x335F0AU, 0x336EDFU, 0x337634U, 0x3380B0U, 0x33985BU, 0x33A98EU, 0x33B165U, 0x33CA27U, 0x33D2CCU, + 0x33E319U, 0x33FBF2U, 0x340545U, 0x341DAEU, 0x342C7BU, 0x343490U, 0x344FD2U, 0x345739U, 0x3466ECU, 0x347E07U, + 0x348883U, 0x349068U, 0x34A1BDU, 0x34B956U, 0x34C214U, 0x34DAFFU, 0x34EB2AU, 0x34F3C1U, 0x350622U, 0x351EC9U, + 0x352F1CU, 0x3537F7U, 0x354CB5U, 0x35545EU, 0x35658BU, 0x357D60U, 0x358BE4U, 0x35930FU, 0x35A2DAU, 0x35BA31U, + 0x35C173U, 0x35D998U, 0x35E84DU, 0x35F0A6U, 0x360388U, 0x361B63U, 0x362AB6U, 0x36325DU, 0x36491FU, 0x3651F4U, + 0x366021U, 0x3678CAU, 0x368E4EU, 0x3696A5U, 0x36A770U, 0x36BF9BU, 0x36C4D9U, 0x36DC32U, 0x36EDE7U, 0x36F50CU, + 0x3700EFU, 0x371804U, 0x3729D1U, 0x37313AU, 0x374A78U, 0x375293U, 0x376346U, 0x377BADU, 0x378D29U, 0x3795C2U, + 0x37A417U, 0x37BCFCU, 0x37C7BEU, 0x37DF55U, 0x37EE80U, 0x37F66BU, 0x380B06U, 0x3813EDU, 0x382238U, 0x383AD3U, + 0x384191U, 0x38597AU, 0x3868AFU, 0x387044U, 0x3886C0U, 0x389E2BU, 0x38AFFEU, 0x38B715U, 0x38CC57U, 0x38D4BCU, + 0x38E569U, 0x38FD82U, 0x390861U, 0x39108AU, 0x39215FU, 0x3939B4U, 0x3942F6U, 0x395A1DU, 0x396BC8U, 0x397323U, + 0x3985A7U, 0x399D4CU, 0x39AC99U, 0x39B472U, 0x39CF30U, 0x39D7DBU, 0x39E60EU, 0x39FEE5U, 0x3A0DCBU, 0x3A1520U, + 0x3A24F5U, 0x3A3C1EU, 0x3A475CU, 0x3A5FB7U, 0x3A6E62U, 0x3A7689U, 0x3A800DU, 0x3A98E6U, 0x3AA933U, 0x3AB1D8U, + 0x3ACA9AU, 0x3AD271U, 0x3AE3A4U, 0x3AFB4FU, 0x3B0EACU, 0x3B1647U, 0x3B2792U, 0x3B3F79U, 0x3B443BU, 0x3B5CD0U, + 0x3B6D05U, 0x3B75EEU, 0x3B836AU, 0x3B9B81U, 0x3BAA54U, 0x3BB2BFU, 0x3BC9FDU, 0x3BD116U, 0x3BE0C3U, 0x3BF828U, + 0x3C069FU, 0x3C1E74U, 0x3C2FA1U, 0x3C374AU, 0x3C4C08U, 0x3C54E3U, 0x3C6536U, 0x3C7DDDU, 0x3C8B59U, 0x3C93B2U, + 0x3CA267U, 0x3CBA8CU, 0x3CC1CEU, 0x3CD925U, 0x3CE8F0U, 0x3CF01BU, 0x3D05F8U, 0x3D1D13U, 0x3D2CC6U, 0x3D342DU, + 0x3D4F6FU, 0x3D5784U, 0x3D6651U, 0x3D7EBAU, 0x3D883EU, 0x3D90D5U, 0x3DA100U, 0x3DB9EBU, 0x3DC2A9U, 0x3DDA42U, + 0x3DEB97U, 0x3DF37CU, 0x3E0052U, 0x3E18B9U, 0x3E296CU, 0x3E3187U, 0x3E4AC5U, 0x3E522EU, 0x3E63FBU, 0x3E7B10U, + 0x3E8D94U, 0x3E957FU, 0x3EA4AAU, 0x3EBC41U, 0x3EC703U, 0x3EDFE8U, 0x3EEE3DU, 0x3EF6D6U, 0x3F0335U, 0x3F1BDEU, + 0x3F2A0BU, 0x3F32E0U, 0x3F49A2U, 0x3F5149U, 0x3F609CU, 0x3F7877U, 0x3F8EF3U, 0x3F9618U, 0x3FA7CDU, 0x3FBF26U, + 0x3FC464U, 0x3FDC8FU, 0x3FED5AU, 0x3FF5B1U, 0x40063BU, 0x401ED0U, 0x402F05U, 0x4037EEU, 0x404CACU, 0x405447U, + 0x406592U, 0x407D79U, 0x408BFDU, 0x409316U, 0x40A2C3U, 0x40BA28U, 0x40C16AU, 0x40D981U, 0x40E854U, 0x40F0BFU, + 0x41055CU, 0x411DB7U, 0x412C62U, 0x413489U, 0x414FCBU, 0x415720U, 0x4166F5U, 0x417E1EU, 0x41889AU, 0x419071U, + 0x41A1A4U, 0x41B94FU, 0x41C20DU, 0x41DAE6U, 0x41EB33U, 0x41F3D8U, 0x4200F6U, 0x42181DU, 0x4229C8U, 0x423123U, + 0x424A61U, 0x42528AU, 0x42635FU, 0x427BB4U, 0x428D30U, 0x4295DBU, 0x42A40EU, 0x42BCE5U, 0x42C7A7U, 0x42DF4CU, + 0x42EE99U, 0x42F672U, 0x430391U, 0x431B7AU, 0x432AAFU, 0x433244U, 0x434906U, 0x4351EDU, 0x436038U, 0x4378D3U, + 0x438E57U, 0x4396BCU, 0x43A769U, 0x43BF82U, 0x43C4C0U, 0x43DC2BU, 0x43EDFEU, 0x43F515U, 0x440BA2U, 0x441349U, + 0x44229CU, 0x443A77U, 0x444135U, 0x4459DEU, 0x44680BU, 0x4470E0U, 0x448664U, 0x449E8FU, 0x44AF5AU, 0x44B7B1U, + 0x44CCF3U, 0x44D418U, 0x44E5CDU, 0x44FD26U, 0x4508C5U, 0x45102EU, 0x4521FBU, 0x453910U, 0x454252U, 0x455AB9U, + 0x456B6CU, 0x457387U, 0x458503U, 0x459DE8U, 0x45AC3DU, 0x45B4D6U, 0x45CF94U, 0x45D77FU, 0x45E6AAU, 0x45FE41U, + 0x460D6FU, 0x461584U, 0x462451U, 0x463CBAU, 0x4647F8U, 0x465F13U, 0x466EC6U, 0x46762DU, 0x4680A9U, 0x469842U, + 0x46A997U, 0x46B17CU, 0x46CA3EU, 0x46D2D5U, 0x46E300U, 0x46FBEBU, 0x470E08U, 0x4716E3U, 0x472736U, 0x473FDDU, + 0x47449FU, 0x475C74U, 0x476DA1U, 0x47754AU, 0x4783CEU, 0x479B25U, 0x47AAF0U, 0x47B21BU, 0x47C959U, 0x47D1B2U, + 0x47E067U, 0x47F88CU, 0x4805E1U, 0x481D0AU, 0x482CDFU, 0x483434U, 0x484F76U, 0x48579DU, 0x486648U, 0x487EA3U, + 0x488827U, 0x4890CCU, 0x48A119U, 0x48B9F2U, 0x48C2B0U, 0x48DA5BU, 0x48EB8EU, 0x48F365U, 0x490686U, 0x491E6DU, + 0x492FB8U, 0x493753U, 0x494C11U, 0x4954FAU, 0x49652FU, 0x497DC4U, 0x498B40U, 0x4993ABU, 0x49A27EU, 0x49BA95U, + 0x49C1D7U, 0x49D93CU, 0x49E8E9U, 0x49F002U, 0x4A032CU, 0x4A1BC7U, 0x4A2A12U, 0x4A32F9U, 0x4A49BBU, 0x4A5150U, + 0x4A6085U, 0x4A786EU, 0x4A8EEAU, 0x4A9601U, 0x4AA7D4U, 0x4ABF3FU, 0x4AC47DU, 0x4ADC96U, 0x4AED43U, 0x4AF5A8U, + 0x4B004BU, 0x4B18A0U, 0x4B2975U, 0x4B319EU, 0x4B4ADCU, 0x4B5237U, 0x4B63E2U, 0x4B7B09U, 0x4B8D8DU, 0x4B9566U, + 0x4BA4B3U, 0x4BBC58U, 0x4BC71AU, 0x4BDFF1U, 0x4BEE24U, 0x4BF6CFU, 0x4C0878U, 0x4C1093U, 0x4C2146U, 0x4C39ADU, + 0x4C42EFU, 0x4C5A04U, 0x4C6BD1U, 0x4C733AU, 0x4C85BEU, 0x4C9D55U, 0x4CAC80U, 0x4CB46BU, 0x4CCF29U, 0x4CD7C2U, + 0x4CE617U, 0x4CFEFCU, 0x4D0B1FU, 0x4D13F4U, 0x4D2221U, 0x4D3ACAU, 0x4D4188U, 0x4D5963U, 0x4D68B6U, 0x4D705DU, + 0x4D86D9U, 0x4D9E32U, 0x4DAFE7U, 0x4DB70CU, 0x4DCC4EU, 0x4DD4A5U, 0x4DE570U, 0x4DFD9BU, 0x4E0EB5U, 0x4E165EU, + 0x4E278BU, 0x4E3F60U, 0x4E4422U, 0x4E5CC9U, 0x4E6D1CU, 0x4E75F7U, 0x4E8373U, 0x4E9B98U, 0x4EAA4DU, 0x4EB2A6U, + 0x4EC9E4U, 0x4ED10FU, 0x4EE0DAU, 0x4EF831U, 0x4F0DD2U, 0x4F1539U, 0x4F24ECU, 0x4F3C07U, 0x4F4745U, 0x4F5FAEU, + 0x4F6E7BU, 0x4F7690U, 0x4F8014U, 0x4F98FFU, 0x4FA92AU, 0x4FB1C1U, 0x4FCA83U, 0x4FD268U, 0x4FE3BDU, 0x4FFB56U, + 0x50018FU, 0x501964U, 0x5028B1U, 0x50305AU, 0x504B18U, 0x5053F3U, 0x506226U, 0x507ACDU, 0x508C49U, 0x5094A2U, + 0x50A577U, 0x50BD9CU, 0x50C6DEU, 0x50DE35U, 0x50EFE0U, 0x50F70BU, 0x5102E8U, 0x511A03U, 0x512BD6U, 0x51333DU, + 0x51487FU, 0x515094U, 0x516141U, 0x5179AAU, 0x518F2EU, 0x5197C5U, 0x51A610U, 0x51BEFBU, 0x51C5B9U, 0x51DD52U, + 0x51EC87U, 0x51F46CU, 0x520742U, 0x521FA9U, 0x522E7CU, 0x523697U, 0x524DD5U, 0x52553EU, 0x5264EBU, 0x527C00U, + 0x528A84U, 0x52926FU, 0x52A3BAU, 0x52BB51U, 0x52C013U, 0x52D8F8U, 0x52E92DU, 0x52F1C6U, 0x530425U, 0x531CCEU, + 0x532D1BU, 0x5335F0U, 0x534EB2U, 0x535659U, 0x53678CU, 0x537F67U, 0x5389E3U, 0x539108U, 0x53A0DDU, 0x53B836U, + 0x53C374U, 0x53DB9FU, 0x53EA4AU, 0x53F2A1U, 0x540C16U, 0x5414FDU, 0x542528U, 0x543DC3U, 0x544681U, 0x545E6AU, + 0x546FBFU, 0x547754U, 0x5481D0U, 0x54993BU, 0x54A8EEU, 0x54B005U, 0x54CB47U, 0x54D3ACU, 0x54E279U, 0x54FA92U, + 0x550F71U, 0x55179AU, 0x55264FU, 0x553EA4U, 0x5545E6U, 0x555D0DU, 0x556CD8U, 0x557433U, 0x5582B7U, 0x559A5CU, + 0x55AB89U, 0x55B362U, 0x55C820U, 0x55D0CBU, 0x55E11EU, 0x55F9F5U, 0x560ADBU, 0x561230U, 0x5623E5U, 0x563B0EU, + 0x56404CU, 0x5658A7U, 0x566972U, 0x567199U, 0x56871DU, 0x569FF6U, 0x56AE23U, 0x56B6C8U, 0x56CD8AU, 0x56D561U, + 0x56E4B4U, 0x56FC5FU, 0x5709BCU, 0x571157U, 0x572082U, 0x573869U, 0x57432BU, 0x575BC0U, 0x576A15U, 0x5772FEU, + 0x57847AU, 0x579C91U, 0x57AD44U, 0x57B5AFU, 0x57CEEDU, 0x57D606U, 0x57E7D3U, 0x57FF38U, 0x580255U, 0x581ABEU, + 0x582B6BU, 0x583380U, 0x5848C2U, 0x585029U, 0x5861FCU, 0x587917U, 0x588F93U, 0x589778U, 0x58A6ADU, 0x58BE46U, + 0x58C504U, 0x58DDEFU, 0x58EC3AU, 0x58F4D1U, 0x590132U, 0x5919D9U, 0x59280CU, 0x5930E7U, 0x594BA5U, 0x59534EU, + 0x59629BU, 0x597A70U, 0x598CF4U, 0x59941FU, 0x59A5CAU, 0x59BD21U, 0x59C663U, 0x59DE88U, 0x59EF5DU, 0x59F7B6U, + 0x5A0498U, 0x5A1C73U, 0x5A2DA6U, 0x5A354DU, 0x5A4E0FU, 0x5A56E4U, 0x5A6731U, 0x5A7FDAU, 0x5A895EU, 0x5A91B5U, + 0x5AA060U, 0x5AB88BU, 0x5AC3C9U, 0x5ADB22U, 0x5AEAF7U, 0x5AF21CU, 0x5B07FFU, 0x5B1F14U, 0x5B2EC1U, 0x5B362AU, + 0x5B4D68U, 0x5B5583U, 0x5B6456U, 0x5B7CBDU, 0x5B8A39U, 0x5B92D2U, 0x5BA307U, 0x5BBBECU, 0x5BC0AEU, 0x5BD845U, + 0x5BE990U, 0x5BF17BU, 0x5C0FCCU, 0x5C1727U, 0x5C26F2U, 0x5C3E19U, 0x5C455BU, 0x5C5DB0U, 0x5C6C65U, 0x5C748EU, + 0x5C820AU, 0x5C9AE1U, 0x5CAB34U, 0x5CB3DFU, 0x5CC89DU, 0x5CD076U, 0x5CE1A3U, 0x5CF948U, 0x5D0CABU, 0x5D1440U, + 0x5D2595U, 0x5D3D7EU, 0x5D463CU, 0x5D5ED7U, 0x5D6F02U, 0x5D77E9U, 0x5D816DU, 0x5D9986U, 0x5DA853U, 0x5DB0B8U, + 0x5DCBFAU, 0x5DD311U, 0x5DE2C4U, 0x5DFA2FU, 0x5E0901U, 0x5E11EAU, 0x5E203FU, 0x5E38D4U, 0x5E4396U, 0x5E5B7DU, + 0x5E6AA8U, 0x5E7243U, 0x5E84C7U, 0x5E9C2CU, 0x5EADF9U, 0x5EB512U, 0x5ECE50U, 0x5ED6BBU, 0x5EE76EU, 0x5EFF85U, + 0x5F0A66U, 0x5F128DU, 0x5F2358U, 0x5F3BB3U, 0x5F40F1U, 0x5F581AU, 0x5F69CFU, 0x5F7124U, 0x5F87A0U, 0x5F9F4BU, + 0x5FAE9EU, 0x5FB675U, 0x5FCD37U, 0x5FD5DCU, 0x5FE409U, 0x5FFCE2U, 0x600953U, 0x6011B8U, 0x60206DU, 0x603886U, + 0x6043C4U, 0x605B2FU, 0x606AFAU, 0x607211U, 0x608495U, 0x609C7EU, 0x60ADABU, 0x60B540U, 0x60CE02U, 0x60D6E9U, + 0x60E73CU, 0x60FFD7U, 0x610A34U, 0x6112DFU, 0x61230AU, 0x613BE1U, 0x6140A3U, 0x615848U, 0x61699DU, 0x617176U, + 0x6187F2U, 0x619F19U, 0x61AECCU, 0x61B627U, 0x61CD65U, 0x61D58EU, 0x61E45BU, 0x61FCB0U, 0x620F9EU, 0x621775U, + 0x6226A0U, 0x623E4BU, 0x624509U, 0x625DE2U, 0x626C37U, 0x6274DCU, 0x628258U, 0x629AB3U, 0x62AB66U, 0x62B38DU, + 0x62C8CFU, 0x62D024U, 0x62E1F1U, 0x62F91AU, 0x630CF9U, 0x631412U, 0x6325C7U, 0x633D2CU, 0x63466EU, 0x635E85U, + 0x636F50U, 0x6377BBU, 0x63813FU, 0x6399D4U, 0x63A801U, 0x63B0EAU, 0x63CBA8U, 0x63D343U, 0x63E296U, 0x63FA7DU, + 0x6404CAU, 0x641C21U, 0x642DF4U, 0x64351FU, 0x644E5DU, 0x6456B6U, 0x646763U, 0x647F88U, 0x64890CU, 0x6491E7U, + 0x64A032U, 0x64B8D9U, 0x64C39BU, 0x64DB70U, 0x64EAA5U, 0x64F24EU, 0x6507ADU, 0x651F46U, 0x652E93U, 0x653678U, + 0x654D3AU, 0x6555D1U, 0x656404U, 0x657CEFU, 0x658A6BU, 0x659280U, 0x65A355U, 0x65BBBEU, 0x65C0FCU, 0x65D817U, + 0x65E9C2U, 0x65F129U, 0x660207U, 0x661AECU, 0x662B39U, 0x6633D2U, 0x664890U, 0x66507BU, 0x6661AEU, 0x667945U, + 0x668FC1U, 0x66972AU, 0x66A6FFU, 0x66BE14U, 0x66C556U, 0x66DDBDU, 0x66EC68U, 0x66F483U, 0x670160U, 0x67198BU, + 0x67285EU, 0x6730B5U, 0x674BF7U, 0x67531CU, 0x6762C9U, 0x677A22U, 0x678CA6U, 0x67944DU, 0x67A598U, 0x67BD73U, + 0x67C631U, 0x67DEDAU, 0x67EF0FU, 0x67F7E4U, 0x680A89U, 0x681262U, 0x6823B7U, 0x683B5CU, 0x68401EU, 0x6858F5U, + 0x686920U, 0x6871CBU, 0x68874FU, 0x689FA4U, 0x68AE71U, 0x68B69AU, 0x68CDD8U, 0x68D533U, 0x68E4E6U, 0x68FC0DU, + 0x6909EEU, 0x691105U, 0x6920D0U, 0x69383BU, 0x694379U, 0x695B92U, 0x696A47U, 0x6972ACU, 0x698428U, 0x699CC3U, + 0x69AD16U, 0x69B5FDU, 0x69CEBFU, 0x69D654U, 0x69E781U, 0x69FF6AU, 0x6A0C44U, 0x6A14AFU, 0x6A257AU, 0x6A3D91U, + 0x6A46D3U, 0x6A5E38U, 0x6A6FEDU, 0x6A7706U, 0x6A8182U, 0x6A9969U, 0x6AA8BCU, 0x6AB057U, 0x6ACB15U, 0x6AD3FEU, + 0x6AE22BU, 0x6AFAC0U, 0x6B0F23U, 0x6B17C8U, 0x6B261DU, 0x6B3EF6U, 0x6B45B4U, 0x6B5D5FU, 0x6B6C8AU, 0x6B7461U, + 0x6B82E5U, 0x6B9A0EU, 0x6BABDBU, 0x6BB330U, 0x6BC872U, 0x6BD099U, 0x6BE14CU, 0x6BF9A7U, 0x6C0710U, 0x6C1FFBU, + 0x6C2E2EU, 0x6C36C5U, 0x6C4D87U, 0x6C556CU, 0x6C64B9U, 0x6C7C52U, 0x6C8AD6U, 0x6C923DU, 0x6CA3E8U, 0x6CBB03U, + 0x6CC041U, 0x6CD8AAU, 0x6CE97FU, 0x6CF194U, 0x6D0477U, 0x6D1C9CU, 0x6D2D49U, 0x6D35A2U, 0x6D4EE0U, 0x6D560BU, + 0x6D67DEU, 0x6D7F35U, 0x6D89B1U, 0x6D915AU, 0x6DA08FU, 0x6DB864U, 0x6DC326U, 0x6DDBCDU, 0x6DEA18U, 0x6DF2F3U, + 0x6E01DDU, 0x6E1936U, 0x6E28E3U, 0x6E3008U, 0x6E4B4AU, 0x6E53A1U, 0x6E6274U, 0x6E7A9FU, 0x6E8C1BU, 0x6E94F0U, + 0x6EA525U, 0x6EBDCEU, 0x6EC68CU, 0x6EDE67U, 0x6EEFB2U, 0x6EF759U, 0x6F02BAU, 0x6F1A51U, 0x6F2B84U, 0x6F336FU, + 0x6F482DU, 0x6F50C6U, 0x6F6113U, 0x6F79F8U, 0x6F8F7CU, 0x6F9797U, 0x6FA642U, 0x6FBEA9U, 0x6FC5EBU, 0x6FDD00U, + 0x6FECD5U, 0x6FF43EU, 0x700EE7U, 0x70160CU, 0x7027D9U, 0x703F32U, 0x704470U, 0x705C9BU, 0x706D4EU, 0x7075A5U, + 0x708321U, 0x709BCAU, 0x70AA1FU, 0x70B2F4U, 0x70C9B6U, 0x70D15DU, 0x70E088U, 0x70F863U, 0x710D80U, 0x71156BU, + 0x7124BEU, 0x713C55U, 0x714717U, 0x715FFCU, 0x716E29U, 0x7176C2U, 0x718046U, 0x7198ADU, 0x71A978U, 0x71B193U, + 0x71CAD1U, 0x71D23AU, 0x71E3EFU, 0x71FB04U, 0x72082AU, 0x7210C1U, 0x722114U, 0x7239FFU, 0x7242BDU, 0x725A56U, + 0x726B83U, 0x727368U, 0x7285ECU, 0x729D07U, 0x72ACD2U, 0x72B439U, 0x72CF7BU, 0x72D790U, 0x72E645U, 0x72FEAEU, + 0x730B4DU, 0x7313A6U, 0x732273U, 0x733A98U, 0x7341DAU, 0x735931U, 0x7368E4U, 0x73700FU, 0x73868BU, 0x739E60U, + 0x73AFB5U, 0x73B75EU, 0x73CC1CU, 0x73D4F7U, 0x73E522U, 0x73FDC9U, 0x74037EU, 0x741B95U, 0x742A40U, 0x7432ABU, + 0x7449E9U, 0x745102U, 0x7460D7U, 0x74783CU, 0x748EB8U, 0x749653U, 0x74A786U, 0x74BF6DU, 0x74C42FU, 0x74DCC4U, + 0x74ED11U, 0x74F5FAU, 0x750019U, 0x7518F2U, 0x752927U, 0x7531CCU, 0x754A8EU, 0x755265U, 0x7563B0U, 0x757B5BU, + 0x758DDFU, 0x759534U, 0x75A4E1U, 0x75BC0AU, 0x75C748U, 0x75DFA3U, 0x75EE76U, 0x75F69DU, 0x7605B3U, 0x761D58U, + 0x762C8DU, 0x763466U, 0x764F24U, 0x7657CFU, 0x76661AU, 0x767EF1U, 0x768875U, 0x76909EU, 0x76A14BU, 0x76B9A0U, + 0x76C2E2U, 0x76DA09U, 0x76EBDCU, 0x76F337U, 0x7706D4U, 0x771E3FU, 0x772FEAU, 0x773701U, 0x774C43U, 0x7754A8U, + 0x77657DU, 0x777D96U, 0x778B12U, 0x7793F9U, 0x77A22CU, 0x77BAC7U, 0x77C185U, 0x77D96EU, 0x77E8BBU, 0x77F050U, + 0x780D3DU, 0x7815D6U, 0x782403U, 0x783CE8U, 0x7847AAU, 0x785F41U, 0x786E94U, 0x78767FU, 0x7880FBU, 0x789810U, + 0x78A9C5U, 0x78B12EU, 0x78CA6CU, 0x78D287U, 0x78E352U, 0x78FBB9U, 0x790E5AU, 0x7916B1U, 0x792764U, 0x793F8FU, + 0x7944CDU, 0x795C26U, 0x796DF3U, 0x797518U, 0x79839CU, 0x799B77U, 0x79AAA2U, 0x79B249U, 0x79C90BU, 0x79D1E0U, + 0x79E035U, 0x79F8DEU, 0x7A0BF0U, 0x7A131BU, 0x7A22CEU, 0x7A3A25U, 0x7A4167U, 0x7A598CU, 0x7A6859U, 0x7A70B2U, + 0x7A8636U, 0x7A9EDDU, 0x7AAF08U, 0x7AB7E3U, 0x7ACCA1U, 0x7AD44AU, 0x7AE59FU, 0x7AFD74U, 0x7B0897U, 0x7B107CU, + 0x7B21A9U, 0x7B3942U, 0x7B4200U, 0x7B5AEBU, 0x7B6B3EU, 0x7B73D5U, 0x7B8551U, 0x7B9DBAU, 0x7BAC6FU, 0x7BB484U, + 0x7BCFC6U, 0x7BD72DU, 0x7BE6F8U, 0x7BFE13U, 0x7C00A4U, 0x7C184FU, 0x7C299AU, 0x7C3171U, 0x7C4A33U, 0x7C52D8U, + 0x7C630DU, 0x7C7BE6U, 0x7C8D62U, 0x7C9589U, 0x7CA45CU, 0x7CBCB7U, 0x7CC7F5U, 0x7CDF1EU, 0x7CEECBU, 0x7CF620U, + 0x7D03C3U, 0x7D1B28U, 0x7D2AFDU, 0x7D3216U, 0x7D4954U, 0x7D51BFU, 0x7D606AU, 0x7D7881U, 0x7D8E05U, 0x7D96EEU, + 0x7DA73BU, 0x7DBFD0U, 0x7DC492U, 0x7DDC79U, 0x7DEDACU, 0x7DF547U, 0x7E0669U, 0x7E1E82U, 0x7E2F57U, 0x7E37BCU, + 0x7E4CFEU, 0x7E5415U, 0x7E65C0U, 0x7E7D2BU, 0x7E8BAFU, 0x7E9344U, 0x7EA291U, 0x7EBA7AU, 0x7EC138U, 0x7ED9D3U, + 0x7EE806U, 0x7EF0EDU, 0x7F050EU, 0x7F1DE5U, 0x7F2C30U, 0x7F34DBU, 0x7F4F99U, 0x7F5772U, 0x7F66A7U, 0x7F7E4CU, + 0x7F88C8U, 0x7F9023U, 0x7FA1F6U, 0x7FB91DU, 0x7FC25FU, 0x7FDAB4U, 0x7FEB61U, 0x7FF38AU, 0x800C75U, 0x80149EU, + 0x80254BU, 0x803DA0U, 0x8046E2U, 0x805E09U, 0x806FDCU, 0x807737U, 0x8081B3U, 0x809958U, 0x80A88DU, 0x80B066U, + 0x80CB24U, 0x80D3CFU, 0x80E21AU, 0x80FAF1U, 0x810F12U, 0x8117F9U, 0x81262CU, 0x813EC7U, 0x814585U, 0x815D6EU, + 0x816CBBU, 0x817450U, 0x8182D4U, 0x819A3FU, 0x81ABEAU, 0x81B301U, 0x81C843U, 0x81D0A8U, 0x81E17DU, 0x81F996U, + 0x820AB8U, 0x821253U, 0x822386U, 0x823B6DU, 0x82402FU, 0x8258C4U, 0x826911U, 0x8271FAU, 0x82877EU, 0x829F95U, + 0x82AE40U, 0x82B6ABU, 0x82CDE9U, 0x82D502U, 0x82E4D7U, 0x82FC3CU, 0x8309DFU, 0x831134U, 0x8320E1U, 0x83380AU, + 0x834348U, 0x835BA3U, 0x836A76U, 0x83729DU, 0x838419U, 0x839CF2U, 0x83AD27U, 0x83B5CCU, 0x83CE8EU, 0x83D665U, + 0x83E7B0U, 0x83FF5BU, 0x8401ECU, 0x841907U, 0x8428D2U, 0x843039U, 0x844B7BU, 0x845390U, 0x846245U, 0x847AAEU, + 0x848C2AU, 0x8494C1U, 0x84A514U, 0x84BDFFU, 0x84C6BDU, 0x84DE56U, 0x84EF83U, 0x84F768U, 0x85028BU, 0x851A60U, + 0x852BB5U, 0x85335EU, 0x85481CU, 0x8550F7U, 0x856122U, 0x8579C9U, 0x858F4DU, 0x8597A6U, 0x85A673U, 0x85BE98U, + 0x85C5DAU, 0x85DD31U, 0x85ECE4U, 0x85F40FU, 0x860721U, 0x861FCAU, 0x862E1FU, 0x8636F4U, 0x864DB6U, 0x86555DU, + 0x866488U, 0x867C63U, 0x868AE7U, 0x86920CU, 0x86A3D9U, 0x86BB32U, 0x86C070U, 0x86D89BU, 0x86E94EU, 0x86F1A5U, + 0x870446U, 0x871CADU, 0x872D78U, 0x873593U, 0x874ED1U, 0x87563AU, 0x8767EFU, 0x877F04U, 0x878980U, 0x87916BU, + 0x87A0BEU, 0x87B855U, 0x87C317U, 0x87DBFCU, 0x87EA29U, 0x87F2C2U, 0x880FAFU, 0x881744U, 0x882691U, 0x883E7AU, + 0x884538U, 0x885DD3U, 0x886C06U, 0x8874EDU, 0x888269U, 0x889A82U, 0x88AB57U, 0x88B3BCU, 0x88C8FEU, 0x88D015U, + 0x88E1C0U, 0x88F92BU, 0x890CC8U, 0x891423U, 0x8925F6U, 0x893D1DU, 0x89465FU, 0x895EB4U, 0x896F61U, 0x89778AU, + 0x89810EU, 0x8999E5U, 0x89A830U, 0x89B0DBU, 0x89CB99U, 0x89D372U, 0x89E2A7U, 0x89FA4CU, 0x8A0962U, 0x8A1189U, + 0x8A205CU, 0x8A38B7U, 0x8A43F5U, 0x8A5B1EU, 0x8A6ACBU, 0x8A7220U, 0x8A84A4U, 0x8A9C4FU, 0x8AAD9AU, 0x8AB571U, + 0x8ACE33U, 0x8AD6D8U, 0x8AE70DU, 0x8AFFE6U, 0x8B0A05U, 0x8B12EEU, 0x8B233BU, 0x8B3BD0U, 0x8B4092U, 0x8B5879U, + 0x8B69ACU, 0x8B7147U, 0x8B87C3U, 0x8B9F28U, 0x8BAEFDU, 0x8BB616U, 0x8BCD54U, 0x8BD5BFU, 0x8BE46AU, 0x8BFC81U, + 0x8C0236U, 0x8C1ADDU, 0x8C2B08U, 0x8C33E3U, 0x8C48A1U, 0x8C504AU, 0x8C619FU, 0x8C7974U, 0x8C8FF0U, 0x8C971BU, + 0x8CA6CEU, 0x8CBE25U, 0x8CC567U, 0x8CDD8CU, 0x8CEC59U, 0x8CF4B2U, 0x8D0151U, 0x8D19BAU, 0x8D286FU, 0x8D3084U, + 0x8D4BC6U, 0x8D532DU, 0x8D62F8U, 0x8D7A13U, 0x8D8C97U, 0x8D947CU, 0x8DA5A9U, 0x8DBD42U, 0x8DC600U, 0x8DDEEBU, + 0x8DEF3EU, 0x8DF7D5U, 0x8E04FBU, 0x8E1C10U, 0x8E2DC5U, 0x8E352EU, 0x8E4E6CU, 0x8E5687U, 0x8E6752U, 0x8E7FB9U, + 0x8E893DU, 0x8E91D6U, 0x8EA003U, 0x8EB8E8U, 0x8EC3AAU, 0x8EDB41U, 0x8EEA94U, 0x8EF27FU, 0x8F079CU, 0x8F1F77U, + 0x8F2EA2U, 0x8F3649U, 0x8F4D0BU, 0x8F55E0U, 0x8F6435U, 0x8F7CDEU, 0x8F8A5AU, 0x8F92B1U, 0x8FA364U, 0x8FBB8FU, + 0x8FC0CDU, 0x8FD826U, 0x8FE9F3U, 0x8FF118U, 0x900BC1U, 0x90132AU, 0x9022FFU, 0x903A14U, 0x904156U, 0x9059BDU, + 0x906868U, 0x907083U, 0x908607U, 0x909EECU, 0x90AF39U, 0x90B7D2U, 0x90CC90U, 0x90D47BU, 0x90E5AEU, 0x90FD45U, + 0x9108A6U, 0x91104DU, 0x912198U, 0x913973U, 0x914231U, 0x915ADAU, 0x916B0FU, 0x9173E4U, 0x918560U, 0x919D8BU, + 0x91AC5EU, 0x91B4B5U, 0x91CFF7U, 0x91D71CU, 0x91E6C9U, 0x91FE22U, 0x920D0CU, 0x9215E7U, 0x922432U, 0x923CD9U, + 0x92479BU, 0x925F70U, 0x926EA5U, 0x92764EU, 0x9280CAU, 0x929821U, 0x92A9F4U, 0x92B11FU, 0x92CA5DU, 0x92D2B6U, + 0x92E363U, 0x92FB88U, 0x930E6BU, 0x931680U, 0x932755U, 0x933FBEU, 0x9344FCU, 0x935C17U, 0x936DC2U, 0x937529U, + 0x9383ADU, 0x939B46U, 0x93AA93U, 0x93B278U, 0x93C93AU, 0x93D1D1U, 0x93E004U, 0x93F8EFU, 0x940658U, 0x941EB3U, + 0x942F66U, 0x94378DU, 0x944CCFU, 0x945424U, 0x9465F1U, 0x947D1AU, 0x948B9EU, 0x949375U, 0x94A2A0U, 0x94BA4BU, + 0x94C109U, 0x94D9E2U, 0x94E837U, 0x94F0DCU, 0x95053FU, 0x951DD4U, 0x952C01U, 0x9534EAU, 0x954FA8U, 0x955743U, + 0x956696U, 0x957E7DU, 0x9588F9U, 0x959012U, 0x95A1C7U, 0x95B92CU, 0x95C26EU, 0x95DA85U, 0x95EB50U, 0x95F3BBU, + 0x960095U, 0x96187EU, 0x9629ABU, 0x963140U, 0x964A02U, 0x9652E9U, 0x96633CU, 0x967BD7U, 0x968D53U, 0x9695B8U, + 0x96A46DU, 0x96BC86U, 0x96C7C4U, 0x96DF2FU, 0x96EEFAU, 0x96F611U, 0x9703F2U, 0x971B19U, 0x972ACCU, 0x973227U, + 0x974965U, 0x97518EU, 0x97605BU, 0x9778B0U, 0x978E34U, 0x9796DFU, 0x97A70AU, 0x97BFE1U, 0x97C4A3U, 0x97DC48U, + 0x97ED9DU, 0x97F576U, 0x98081BU, 0x9810F0U, 0x982125U, 0x9839CEU, 0x98428CU, 0x985A67U, 0x986BB2U, 0x987359U, + 0x9885DDU, 0x989D36U, 0x98ACE3U, 0x98B408U, 0x98CF4AU, 0x98D7A1U, 0x98E674U, 0x98FE9FU, 0x990B7CU, 0x991397U, + 0x992242U, 0x993AA9U, 0x9941EBU, 0x995900U, 0x9968D5U, 0x99703EU, 0x9986BAU, 0x999E51U, 0x99AF84U, 0x99B76FU, + 0x99CC2DU, 0x99D4C6U, 0x99E513U, 0x99FDF8U, 0x9A0ED6U, 0x9A163DU, 0x9A27E8U, 0x9A3F03U, 0x9A4441U, 0x9A5CAAU, + 0x9A6D7FU, 0x9A7594U, 0x9A8310U, 0x9A9BFBU, 0x9AAA2EU, 0x9AB2C5U, 0x9AC987U, 0x9AD16CU, 0x9AE0B9U, 0x9AF852U, + 0x9B0DB1U, 0x9B155AU, 0x9B248FU, 0x9B3C64U, 0x9B4726U, 0x9B5FCDU, 0x9B6E18U, 0x9B76F3U, 0x9B8077U, 0x9B989CU, + 0x9BA949U, 0x9BB1A2U, 0x9BCAE0U, 0x9BD20BU, 0x9BE3DEU, 0x9BFB35U, 0x9C0582U, 0x9C1D69U, 0x9C2CBCU, 0x9C3457U, + 0x9C4F15U, 0x9C57FEU, 0x9C662BU, 0x9C7EC0U, 0x9C8844U, 0x9C90AFU, 0x9CA17AU, 0x9CB991U, 0x9CC2D3U, 0x9CDA38U, + 0x9CEBEDU, 0x9CF306U, 0x9D06E5U, 0x9D1E0EU, 0x9D2FDBU, 0x9D3730U, 0x9D4C72U, 0x9D5499U, 0x9D654CU, 0x9D7DA7U, + 0x9D8B23U, 0x9D93C8U, 0x9DA21DU, 0x9DBAF6U, 0x9DC1B4U, 0x9DD95FU, 0x9DE88AU, 0x9DF061U, 0x9E034FU, 0x9E1BA4U, + 0x9E2A71U, 0x9E329AU, 0x9E49D8U, 0x9E5133U, 0x9E60E6U, 0x9E780DU, 0x9E8E89U, 0x9E9662U, 0x9EA7B7U, 0x9EBF5CU, + 0x9EC41EU, 0x9EDCF5U, 0x9EED20U, 0x9EF5CBU, 0x9F0028U, 0x9F18C3U, 0x9F2916U, 0x9F31FDU, 0x9F4ABFU, 0x9F5254U, + 0x9F6381U, 0x9F7B6AU, 0x9F8DEEU, 0x9F9505U, 0x9FA4D0U, 0x9FBC3BU, 0x9FC779U, 0x9FDF92U, 0x9FEE47U, 0x9FF6ACU, + 0xA0031DU, 0xA01BF6U, 0xA02A23U, 0xA032C8U, 0xA0498AU, 0xA05161U, 0xA060B4U, 0xA0785FU, 0xA08EDBU, 0xA09630U, + 0xA0A7E5U, 0xA0BF0EU, 0xA0C44CU, 0xA0DCA7U, 0xA0ED72U, 0xA0F599U, 0xA1007AU, 0xA11891U, 0xA12944U, 0xA131AFU, + 0xA14AEDU, 0xA15206U, 0xA163D3U, 0xA17B38U, 0xA18DBCU, 0xA19557U, 0xA1A482U, 0xA1BC69U, 0xA1C72BU, 0xA1DFC0U, + 0xA1EE15U, 0xA1F6FEU, 0xA205D0U, 0xA21D3BU, 0xA22CEEU, 0xA23405U, 0xA24F47U, 0xA257ACU, 0xA26679U, 0xA27E92U, + 0xA28816U, 0xA290FDU, 0xA2A128U, 0xA2B9C3U, 0xA2C281U, 0xA2DA6AU, 0xA2EBBFU, 0xA2F354U, 0xA306B7U, 0xA31E5CU, + 0xA32F89U, 0xA33762U, 0xA34C20U, 0xA354CBU, 0xA3651EU, 0xA37DF5U, 0xA38B71U, 0xA3939AU, 0xA3A24FU, 0xA3BAA4U, + 0xA3C1E6U, 0xA3D90DU, 0xA3E8D8U, 0xA3F033U, 0xA40E84U, 0xA4166FU, 0xA427BAU, 0xA43F51U, 0xA44413U, 0xA45CF8U, + 0xA46D2DU, 0xA475C6U, 0xA48342U, 0xA49BA9U, 0xA4AA7CU, 0xA4B297U, 0xA4C9D5U, 0xA4D13EU, 0xA4E0EBU, 0xA4F800U, + 0xA50DE3U, 0xA51508U, 0xA524DDU, 0xA53C36U, 0xA54774U, 0xA55F9FU, 0xA56E4AU, 0xA576A1U, 0xA58025U, 0xA598CEU, + 0xA5A91BU, 0xA5B1F0U, 0xA5CAB2U, 0xA5D259U, 0xA5E38CU, 0xA5FB67U, 0xA60849U, 0xA610A2U, 0xA62177U, 0xA6399CU, + 0xA642DEU, 0xA65A35U, 0xA66BE0U, 0xA6730BU, 0xA6858FU, 0xA69D64U, 0xA6ACB1U, 0xA6B45AU, 0xA6CF18U, 0xA6D7F3U, + 0xA6E626U, 0xA6FECDU, 0xA70B2EU, 0xA713C5U, 0xA72210U, 0xA73AFBU, 0xA741B9U, 0xA75952U, 0xA76887U, 0xA7706CU, + 0xA786E8U, 0xA79E03U, 0xA7AFD6U, 0xA7B73DU, 0xA7CC7FU, 0xA7D494U, 0xA7E541U, 0xA7FDAAU, 0xA800C7U, 0xA8182CU, + 0xA829F9U, 0xA83112U, 0xA84A50U, 0xA852BBU, 0xA8636EU, 0xA87B85U, 0xA88D01U, 0xA895EAU, 0xA8A43FU, 0xA8BCD4U, + 0xA8C796U, 0xA8DF7DU, 0xA8EEA8U, 0xA8F643U, 0xA903A0U, 0xA91B4BU, 0xA92A9EU, 0xA93275U, 0xA94937U, 0xA951DCU, + 0xA96009U, 0xA978E2U, 0xA98E66U, 0xA9968DU, 0xA9A758U, 0xA9BFB3U, 0xA9C4F1U, 0xA9DC1AU, 0xA9EDCFU, 0xA9F524U, + 0xAA060AU, 0xAA1EE1U, 0xAA2F34U, 0xAA37DFU, 0xAA4C9DU, 0xAA5476U, 0xAA65A3U, 0xAA7D48U, 0xAA8BCCU, 0xAA9327U, + 0xAAA2F2U, 0xAABA19U, 0xAAC15BU, 0xAAD9B0U, 0xAAE865U, 0xAAF08EU, 0xAB056DU, 0xAB1D86U, 0xAB2C53U, 0xAB34B8U, + 0xAB4FFAU, 0xAB5711U, 0xAB66C4U, 0xAB7E2FU, 0xAB88ABU, 0xAB9040U, 0xABA195U, 0xABB97EU, 0xABC23CU, 0xABDAD7U, + 0xABEB02U, 0xABF3E9U, 0xAC0D5EU, 0xAC15B5U, 0xAC2460U, 0xAC3C8BU, 0xAC47C9U, 0xAC5F22U, 0xAC6EF7U, 0xAC761CU, + 0xAC8098U, 0xAC9873U, 0xACA9A6U, 0xACB14DU, 0xACCA0FU, 0xACD2E4U, 0xACE331U, 0xACFBDAU, 0xAD0E39U, 0xAD16D2U, + 0xAD2707U, 0xAD3FECU, 0xAD44AEU, 0xAD5C45U, 0xAD6D90U, 0xAD757BU, 0xAD83FFU, 0xAD9B14U, 0xADAAC1U, 0xADB22AU, + 0xADC968U, 0xADD183U, 0xADE056U, 0xADF8BDU, 0xAE0B93U, 0xAE1378U, 0xAE22ADU, 0xAE3A46U, 0xAE4104U, 0xAE59EFU, + 0xAE683AU, 0xAE70D1U, 0xAE8655U, 0xAE9EBEU, 0xAEAF6BU, 0xAEB780U, 0xAECCC2U, 0xAED429U, 0xAEE5FCU, 0xAEFD17U, + 0xAF08F4U, 0xAF101FU, 0xAF21CAU, 0xAF3921U, 0xAF4263U, 0xAF5A88U, 0xAF6B5DU, 0xAF73B6U, 0xAF8532U, 0xAF9DD9U, + 0xAFAC0CU, 0xAFB4E7U, 0xAFCFA5U, 0xAFD74EU, 0xAFE69BU, 0xAFFE70U, 0xB004A9U, 0xB01C42U, 0xB02D97U, 0xB0357CU, + 0xB04E3EU, 0xB056D5U, 0xB06700U, 0xB07FEBU, 0xB0896FU, 0xB09184U, 0xB0A051U, 0xB0B8BAU, 0xB0C3F8U, 0xB0DB13U, + 0xB0EAC6U, 0xB0F22DU, 0xB107CEU, 0xB11F25U, 0xB12EF0U, 0xB1361BU, 0xB14D59U, 0xB155B2U, 0xB16467U, 0xB17C8CU, + 0xB18A08U, 0xB192E3U, 0xB1A336U, 0xB1BBDDU, 0xB1C09FU, 0xB1D874U, 0xB1E9A1U, 0xB1F14AU, 0xB20264U, 0xB21A8FU, + 0xB22B5AU, 0xB233B1U, 0xB248F3U, 0xB25018U, 0xB261CDU, 0xB27926U, 0xB28FA2U, 0xB29749U, 0xB2A69CU, 0xB2BE77U, + 0xB2C535U, 0xB2DDDEU, 0xB2EC0BU, 0xB2F4E0U, 0xB30103U, 0xB319E8U, 0xB3283DU, 0xB330D6U, 0xB34B94U, 0xB3537FU, + 0xB362AAU, 0xB37A41U, 0xB38CC5U, 0xB3942EU, 0xB3A5FBU, 0xB3BD10U, 0xB3C652U, 0xB3DEB9U, 0xB3EF6CU, 0xB3F787U, + 0xB40930U, 0xB411DBU, 0xB4200EU, 0xB438E5U, 0xB443A7U, 0xB45B4CU, 0xB46A99U, 0xB47272U, 0xB484F6U, 0xB49C1DU, + 0xB4ADC8U, 0xB4B523U, 0xB4CE61U, 0xB4D68AU, 0xB4E75FU, 0xB4FFB4U, 0xB50A57U, 0xB512BCU, 0xB52369U, 0xB53B82U, + 0xB540C0U, 0xB5582BU, 0xB569FEU, 0xB57115U, 0xB58791U, 0xB59F7AU, 0xB5AEAFU, 0xB5B644U, 0xB5CD06U, 0xB5D5EDU, + 0xB5E438U, 0xB5FCD3U, 0xB60FFDU, 0xB61716U, 0xB626C3U, 0xB63E28U, 0xB6456AU, 0xB65D81U, 0xB66C54U, 0xB674BFU, + 0xB6823BU, 0xB69AD0U, 0xB6AB05U, 0xB6B3EEU, 0xB6C8ACU, 0xB6D047U, 0xB6E192U, 0xB6F979U, 0xB70C9AU, 0xB71471U, + 0xB725A4U, 0xB73D4FU, 0xB7460DU, 0xB75EE6U, 0xB76F33U, 0xB777D8U, 0xB7815CU, 0xB799B7U, 0xB7A862U, 0xB7B089U, + 0xB7CBCBU, 0xB7D320U, 0xB7E2F5U, 0xB7FA1EU, 0xB80773U, 0xB81F98U, 0xB82E4DU, 0xB836A6U, 0xB84DE4U, 0xB8550FU, + 0xB864DAU, 0xB87C31U, 0xB88AB5U, 0xB8925EU, 0xB8A38BU, 0xB8BB60U, 0xB8C022U, 0xB8D8C9U, 0xB8E91CU, 0xB8F1F7U, + 0xB90414U, 0xB91CFFU, 0xB92D2AU, 0xB935C1U, 0xB94E83U, 0xB95668U, 0xB967BDU, 0xB97F56U, 0xB989D2U, 0xB99139U, + 0xB9A0ECU, 0xB9B807U, 0xB9C345U, 0xB9DBAEU, 0xB9EA7BU, 0xB9F290U, 0xBA01BEU, 0xBA1955U, 0xBA2880U, 0xBA306BU, + 0xBA4B29U, 0xBA53C2U, 0xBA6217U, 0xBA7AFCU, 0xBA8C78U, 0xBA9493U, 0xBAA546U, 0xBABDADU, 0xBAC6EFU, 0xBADE04U, + 0xBAEFD1U, 0xBAF73AU, 0xBB02D9U, 0xBB1A32U, 0xBB2BE7U, 0xBB330CU, 0xBB484EU, 0xBB50A5U, 0xBB6170U, 0xBB799BU, + 0xBB8F1FU, 0xBB97F4U, 0xBBA621U, 0xBBBECAU, 0xBBC588U, 0xBBDD63U, 0xBBECB6U, 0xBBF45DU, 0xBC0AEAU, 0xBC1201U, + 0xBC23D4U, 0xBC3B3FU, 0xBC407DU, 0xBC5896U, 0xBC6943U, 0xBC71A8U, 0xBC872CU, 0xBC9FC7U, 0xBCAE12U, 0xBCB6F9U, + 0xBCCDBBU, 0xBCD550U, 0xBCE485U, 0xBCFC6EU, 0xBD098DU, 0xBD1166U, 0xBD20B3U, 0xBD3858U, 0xBD431AU, 0xBD5BF1U, + 0xBD6A24U, 0xBD72CFU, 0xBD844BU, 0xBD9CA0U, 0xBDAD75U, 0xBDB59EU, 0xBDCEDCU, 0xBDD637U, 0xBDE7E2U, 0xBDFF09U, + 0xBE0C27U, 0xBE14CCU, 0xBE2519U, 0xBE3DF2U, 0xBE46B0U, 0xBE5E5BU, 0xBE6F8EU, 0xBE7765U, 0xBE81E1U, 0xBE990AU, + 0xBEA8DFU, 0xBEB034U, 0xBECB76U, 0xBED39DU, 0xBEE248U, 0xBEFAA3U, 0xBF0F40U, 0xBF17ABU, 0xBF267EU, 0xBF3E95U, + 0xBF45D7U, 0xBF5D3CU, 0xBF6CE9U, 0xBF7402U, 0xBF8286U, 0xBF9A6DU, 0xBFABB8U, 0xBFB353U, 0xBFC811U, 0xBFD0FAU, + 0xBFE12FU, 0xBFF9C4U, 0xC00A4EU, 0xC012A5U, 0xC02370U, 0xC03B9BU, 0xC040D9U, 0xC05832U, 0xC069E7U, 0xC0710CU, + 0xC08788U, 0xC09F63U, 0xC0AEB6U, 0xC0B65DU, 0xC0CD1FU, 0xC0D5F4U, 0xC0E421U, 0xC0FCCAU, 0xC10929U, 0xC111C2U, + 0xC12017U, 0xC138FCU, 0xC143BEU, 0xC15B55U, 0xC16A80U, 0xC1726BU, 0xC184EFU, 0xC19C04U, 0xC1ADD1U, 0xC1B53AU, + 0xC1CE78U, 0xC1D693U, 0xC1E746U, 0xC1FFADU, 0xC20C83U, 0xC21468U, 0xC225BDU, 0xC23D56U, 0xC24614U, 0xC25EFFU, + 0xC26F2AU, 0xC277C1U, 0xC28145U, 0xC299AEU, 0xC2A87BU, 0xC2B090U, 0xC2CBD2U, 0xC2D339U, 0xC2E2ECU, 0xC2FA07U, + 0xC30FE4U, 0xC3170FU, 0xC326DAU, 0xC33E31U, 0xC34573U, 0xC35D98U, 0xC36C4DU, 0xC374A6U, 0xC38222U, 0xC39AC9U, + 0xC3AB1CU, 0xC3B3F7U, 0xC3C8B5U, 0xC3D05EU, 0xC3E18BU, 0xC3F960U, 0xC407D7U, 0xC41F3CU, 0xC42EE9U, 0xC43602U, + 0xC44D40U, 0xC455ABU, 0xC4647EU, 0xC47C95U, 0xC48A11U, 0xC492FAU, 0xC4A32FU, 0xC4BBC4U, 0xC4C086U, 0xC4D86DU, + 0xC4E9B8U, 0xC4F153U, 0xC504B0U, 0xC51C5BU, 0xC52D8EU, 0xC53565U, 0xC54E27U, 0xC556CCU, 0xC56719U, 0xC57FF2U, + 0xC58976U, 0xC5919DU, 0xC5A048U, 0xC5B8A3U, 0xC5C3E1U, 0xC5DB0AU, 0xC5EADFU, 0xC5F234U, 0xC6011AU, 0xC619F1U, + 0xC62824U, 0xC630CFU, 0xC64B8DU, 0xC65366U, 0xC662B3U, 0xC67A58U, 0xC68CDCU, 0xC69437U, 0xC6A5E2U, 0xC6BD09U, + 0xC6C64BU, 0xC6DEA0U, 0xC6EF75U, 0xC6F79EU, 0xC7027DU, 0xC71A96U, 0xC72B43U, 0xC733A8U, 0xC748EAU, 0xC75001U, + 0xC761D4U, 0xC7793FU, 0xC78FBBU, 0xC79750U, 0xC7A685U, 0xC7BE6EU, 0xC7C52CU, 0xC7DDC7U, 0xC7EC12U, 0xC7F4F9U, + 0xC80994U, 0xC8117FU, 0xC820AAU, 0xC83841U, 0xC84303U, 0xC85BE8U, 0xC86A3DU, 0xC872D6U, 0xC88452U, 0xC89CB9U, + 0xC8AD6CU, 0xC8B587U, 0xC8CEC5U, 0xC8D62EU, 0xC8E7FBU, 0xC8FF10U, 0xC90AF3U, 0xC91218U, 0xC923CDU, 0xC93B26U, + 0xC94064U, 0xC9588FU, 0xC9695AU, 0xC971B1U, 0xC98735U, 0xC99FDEU, 0xC9AE0BU, 0xC9B6E0U, 0xC9CDA2U, 0xC9D549U, + 0xC9E49CU, 0xC9FC77U, 0xCA0F59U, 0xCA17B2U, 0xCA2667U, 0xCA3E8CU, 0xCA45CEU, 0xCA5D25U, 0xCA6CF0U, 0xCA741BU, + 0xCA829FU, 0xCA9A74U, 0xCAABA1U, 0xCAB34AU, 0xCAC808U, 0xCAD0E3U, 0xCAE136U, 0xCAF9DDU, 0xCB0C3EU, 0xCB14D5U, + 0xCB2500U, 0xCB3DEBU, 0xCB46A9U, 0xCB5E42U, 0xCB6F97U, 0xCB777CU, 0xCB81F8U, 0xCB9913U, 0xCBA8C6U, 0xCBB02DU, + 0xCBCB6FU, 0xCBD384U, 0xCBE251U, 0xCBFABAU, 0xCC040DU, 0xCC1CE6U, 0xCC2D33U, 0xCC35D8U, 0xCC4E9AU, 0xCC5671U, + 0xCC67A4U, 0xCC7F4FU, 0xCC89CBU, 0xCC9120U, 0xCCA0F5U, 0xCCB81EU, 0xCCC35CU, 0xCCDBB7U, 0xCCEA62U, 0xCCF289U, + 0xCD076AU, 0xCD1F81U, 0xCD2E54U, 0xCD36BFU, 0xCD4DFDU, 0xCD5516U, 0xCD64C3U, 0xCD7C28U, 0xCD8AACU, 0xCD9247U, + 0xCDA392U, 0xCDBB79U, 0xCDC03BU, 0xCDD8D0U, 0xCDE905U, 0xCDF1EEU, 0xCE02C0U, 0xCE1A2BU, 0xCE2BFEU, 0xCE3315U, + 0xCE4857U, 0xCE50BCU, 0xCE6169U, 0xCE7982U, 0xCE8F06U, 0xCE97EDU, 0xCEA638U, 0xCEBED3U, 0xCEC591U, 0xCEDD7AU, + 0xCEECAFU, 0xCEF444U, 0xCF01A7U, 0xCF194CU, 0xCF2899U, 0xCF3072U, 0xCF4B30U, 0xCF53DBU, 0xCF620EU, 0xCF7AE5U, + 0xCF8C61U, 0xCF948AU, 0xCFA55FU, 0xCFBDB4U, 0xCFC6F6U, 0xCFDE1DU, 0xCFEFC8U, 0xCFF723U, 0xD00DFAU, 0xD01511U, + 0xD024C4U, 0xD03C2FU, 0xD0476DU, 0xD05F86U, 0xD06E53U, 0xD076B8U, 0xD0803CU, 0xD098D7U, 0xD0A902U, 0xD0B1E9U, + 0xD0CAABU, 0xD0D240U, 0xD0E395U, 0xD0FB7EU, 0xD10E9DU, 0xD11676U, 0xD127A3U, 0xD13F48U, 0xD1440AU, 0xD15CE1U, + 0xD16D34U, 0xD175DFU, 0xD1835BU, 0xD19BB0U, 0xD1AA65U, 0xD1B28EU, 0xD1C9CCU, 0xD1D127U, 0xD1E0F2U, 0xD1F819U, + 0xD20B37U, 0xD213DCU, 0xD22209U, 0xD23AE2U, 0xD241A0U, 0xD2594BU, 0xD2689EU, 0xD27075U, 0xD286F1U, 0xD29E1AU, + 0xD2AFCFU, 0xD2B724U, 0xD2CC66U, 0xD2D48DU, 0xD2E558U, 0xD2FDB3U, 0xD30850U, 0xD310BBU, 0xD3216EU, 0xD33985U, + 0xD342C7U, 0xD35A2CU, 0xD36BF9U, 0xD37312U, 0xD38596U, 0xD39D7DU, 0xD3ACA8U, 0xD3B443U, 0xD3CF01U, 0xD3D7EAU, + 0xD3E63FU, 0xD3FED4U, 0xD40063U, 0xD41888U, 0xD4295DU, 0xD431B6U, 0xD44AF4U, 0xD4521FU, 0xD463CAU, 0xD47B21U, + 0xD48DA5U, 0xD4954EU, 0xD4A49BU, 0xD4BC70U, 0xD4C732U, 0xD4DFD9U, 0xD4EE0CU, 0xD4F6E7U, 0xD50304U, 0xD51BEFU, + 0xD52A3AU, 0xD532D1U, 0xD54993U, 0xD55178U, 0xD560ADU, 0xD57846U, 0xD58EC2U, 0xD59629U, 0xD5A7FCU, 0xD5BF17U, + 0xD5C455U, 0xD5DCBEU, 0xD5ED6BU, 0xD5F580U, 0xD606AEU, 0xD61E45U, 0xD62F90U, 0xD6377BU, 0xD64C39U, 0xD654D2U, + 0xD66507U, 0xD67DECU, 0xD68B68U, 0xD69383U, 0xD6A256U, 0xD6BABDU, 0xD6C1FFU, 0xD6D914U, 0xD6E8C1U, 0xD6F02AU, + 0xD705C9U, 0xD71D22U, 0xD72CF7U, 0xD7341CU, 0xD74F5EU, 0xD757B5U, 0xD76660U, 0xD77E8BU, 0xD7880FU, 0xD790E4U, + 0xD7A131U, 0xD7B9DAU, 0xD7C298U, 0xD7DA73U, 0xD7EBA6U, 0xD7F34DU, 0xD80E20U, 0xD816CBU, 0xD8271EU, 0xD83FF5U, + 0xD844B7U, 0xD85C5CU, 0xD86D89U, 0xD87562U, 0xD883E6U, 0xD89B0DU, 0xD8AAD8U, 0xD8B233U, 0xD8C971U, 0xD8D19AU, + 0xD8E04FU, 0xD8F8A4U, 0xD90D47U, 0xD915ACU, 0xD92479U, 0xD93C92U, 0xD947D0U, 0xD95F3BU, 0xD96EEEU, 0xD97605U, + 0xD98081U, 0xD9986AU, 0xD9A9BFU, 0xD9B154U, 0xD9CA16U, 0xD9D2FDU, 0xD9E328U, 0xD9FBC3U, 0xDA08EDU, 0xDA1006U, + 0xDA21D3U, 0xDA3938U, 0xDA427AU, 0xDA5A91U, 0xDA6B44U, 0xDA73AFU, 0xDA852BU, 0xDA9DC0U, 0xDAAC15U, 0xDAB4FEU, + 0xDACFBCU, 0xDAD757U, 0xDAE682U, 0xDAFE69U, 0xDB0B8AU, 0xDB1361U, 0xDB22B4U, 0xDB3A5FU, 0xDB411DU, 0xDB59F6U, + 0xDB6823U, 0xDB70C8U, 0xDB864CU, 0xDB9EA7U, 0xDBAF72U, 0xDBB799U, 0xDBCCDBU, 0xDBD430U, 0xDBE5E5U, 0xDBFD0EU, + 0xDC03B9U, 0xDC1B52U, 0xDC2A87U, 0xDC326CU, 0xDC492EU, 0xDC51C5U, 0xDC6010U, 0xDC78FBU, 0xDC8E7FU, 0xDC9694U, + 0xDCA741U, 0xDCBFAAU, 0xDCC4E8U, 0xDCDC03U, 0xDCEDD6U, 0xDCF53DU, 0xDD00DEU, 0xDD1835U, 0xDD29E0U, 0xDD310BU, + 0xDD4A49U, 0xDD52A2U, 0xDD6377U, 0xDD7B9CU, 0xDD8D18U, 0xDD95F3U, 0xDDA426U, 0xDDBCCDU, 0xDDC78FU, 0xDDDF64U, + 0xDDEEB1U, 0xDDF65AU, 0xDE0574U, 0xDE1D9FU, 0xDE2C4AU, 0xDE34A1U, 0xDE4FE3U, 0xDE5708U, 0xDE66DDU, 0xDE7E36U, + 0xDE88B2U, 0xDE9059U, 0xDEA18CU, 0xDEB967U, 0xDEC225U, 0xDEDACEU, 0xDEEB1BU, 0xDEF3F0U, 0xDF0613U, 0xDF1EF8U, + 0xDF2F2DU, 0xDF37C6U, 0xDF4C84U, 0xDF546FU, 0xDF65BAU, 0xDF7D51U, 0xDF8BD5U, 0xDF933EU, 0xDFA2EBU, 0xDFBA00U, + 0xDFC142U, 0xDFD9A9U, 0xDFE87CU, 0xDFF097U, 0xE00526U, 0xE01DCDU, 0xE02C18U, 0xE034F3U, 0xE04FB1U, 0xE0575AU, + 0xE0668FU, 0xE07E64U, 0xE088E0U, 0xE0900BU, 0xE0A1DEU, 0xE0B935U, 0xE0C277U, 0xE0DA9CU, 0xE0EB49U, 0xE0F3A2U, + 0xE10641U, 0xE11EAAU, 0xE12F7FU, 0xE13794U, 0xE14CD6U, 0xE1543DU, 0xE165E8U, 0xE17D03U, 0xE18B87U, 0xE1936CU, + 0xE1A2B9U, 0xE1BA52U, 0xE1C110U, 0xE1D9FBU, 0xE1E82EU, 0xE1F0C5U, 0xE203EBU, 0xE21B00U, 0xE22AD5U, 0xE2323EU, + 0xE2497CU, 0xE25197U, 0xE26042U, 0xE278A9U, 0xE28E2DU, 0xE296C6U, 0xE2A713U, 0xE2BFF8U, 0xE2C4BAU, 0xE2DC51U, + 0xE2ED84U, 0xE2F56FU, 0xE3008CU, 0xE31867U, 0xE329B2U, 0xE33159U, 0xE34A1BU, 0xE352F0U, 0xE36325U, 0xE37BCEU, + 0xE38D4AU, 0xE395A1U, 0xE3A474U, 0xE3BC9FU, 0xE3C7DDU, 0xE3DF36U, 0xE3EEE3U, 0xE3F608U, 0xE408BFU, 0xE41054U, + 0xE42181U, 0xE4396AU, 0xE44228U, 0xE45AC3U, 0xE46B16U, 0xE473FDU, 0xE48579U, 0xE49D92U, 0xE4AC47U, 0xE4B4ACU, + 0xE4CFEEU, 0xE4D705U, 0xE4E6D0U, 0xE4FE3BU, 0xE50BD8U, 0xE51333U, 0xE522E6U, 0xE53A0DU, 0xE5414FU, 0xE559A4U, + 0xE56871U, 0xE5709AU, 0xE5861EU, 0xE59EF5U, 0xE5AF20U, 0xE5B7CBU, 0xE5CC89U, 0xE5D462U, 0xE5E5B7U, 0xE5FD5CU, + 0xE60E72U, 0xE61699U, 0xE6274CU, 0xE63FA7U, 0xE644E5U, 0xE65C0EU, 0xE66DDBU, 0xE67530U, 0xE683B4U, 0xE69B5FU, + 0xE6AA8AU, 0xE6B261U, 0xE6C923U, 0xE6D1C8U, 0xE6E01DU, 0xE6F8F6U, 0xE70D15U, 0xE715FEU, 0xE7242BU, 0xE73CC0U, + 0xE74782U, 0xE75F69U, 0xE76EBCU, 0xE77657U, 0xE780D3U, 0xE79838U, 0xE7A9EDU, 0xE7B106U, 0xE7CA44U, 0xE7D2AFU, + 0xE7E37AU, 0xE7FB91U, 0xE806FCU, 0xE81E17U, 0xE82FC2U, 0xE83729U, 0xE84C6BU, 0xE85480U, 0xE86555U, 0xE87DBEU, + 0xE88B3AU, 0xE893D1U, 0xE8A204U, 0xE8BAEFU, 0xE8C1ADU, 0xE8D946U, 0xE8E893U, 0xE8F078U, 0xE9059BU, 0xE91D70U, + 0xE92CA5U, 0xE9344EU, 0xE94F0CU, 0xE957E7U, 0xE96632U, 0xE97ED9U, 0xE9885DU, 0xE990B6U, 0xE9A163U, 0xE9B988U, + 0xE9C2CAU, 0xE9DA21U, 0xE9EBF4U, 0xE9F31FU, 0xEA0031U, 0xEA18DAU, 0xEA290FU, 0xEA31E4U, 0xEA4AA6U, 0xEA524DU, + 0xEA6398U, 0xEA7B73U, 0xEA8DF7U, 0xEA951CU, 0xEAA4C9U, 0xEABC22U, 0xEAC760U, 0xEADF8BU, 0xEAEE5EU, 0xEAF6B5U, + 0xEB0356U, 0xEB1BBDU, 0xEB2A68U, 0xEB3283U, 0xEB49C1U, 0xEB512AU, 0xEB60FFU, 0xEB7814U, 0xEB8E90U, 0xEB967BU, + 0xEBA7AEU, 0xEBBF45U, 0xEBC407U, 0xEBDCECU, 0xEBED39U, 0xEBF5D2U, 0xEC0B65U, 0xEC138EU, 0xEC225BU, 0xEC3AB0U, + 0xEC41F2U, 0xEC5919U, 0xEC68CCU, 0xEC7027U, 0xEC86A3U, 0xEC9E48U, 0xECAF9DU, 0xECB776U, 0xECCC34U, 0xECD4DFU, + 0xECE50AU, 0xECFDE1U, 0xED0802U, 0xED10E9U, 0xED213CU, 0xED39D7U, 0xED4295U, 0xED5A7EU, 0xED6BABU, 0xED7340U, + 0xED85C4U, 0xED9D2FU, 0xEDACFAU, 0xEDB411U, 0xEDCF53U, 0xEDD7B8U, 0xEDE66DU, 0xEDFE86U, 0xEE0DA8U, 0xEE1543U, + 0xEE2496U, 0xEE3C7DU, 0xEE473FU, 0xEE5FD4U, 0xEE6E01U, 0xEE76EAU, 0xEE806EU, 0xEE9885U, 0xEEA950U, 0xEEB1BBU, + 0xEECAF9U, 0xEED212U, 0xEEE3C7U, 0xEEFB2CU, 0xEF0ECFU, 0xEF1624U, 0xEF27F1U, 0xEF3F1AU, 0xEF4458U, 0xEF5CB3U, + 0xEF6D66U, 0xEF758DU, 0xEF8309U, 0xEF9BE2U, 0xEFAA37U, 0xEFB2DCU, 0xEFC99EU, 0xEFD175U, 0xEFE0A0U, 0xEFF84BU, + 0xF00292U, 0xF01A79U, 0xF02BACU, 0xF03347U, 0xF04805U, 0xF050EEU, 0xF0613BU, 0xF079D0U, 0xF08F54U, 0xF097BFU, + 0xF0A66AU, 0xF0BE81U, 0xF0C5C3U, 0xF0DD28U, 0xF0ECFDU, 0xF0F416U, 0xF101F5U, 0xF1191EU, 0xF128CBU, 0xF13020U, + 0xF14B62U, 0xF15389U, 0xF1625CU, 0xF17AB7U, 0xF18C33U, 0xF194D8U, 0xF1A50DU, 0xF1BDE6U, 0xF1C6A4U, 0xF1DE4FU, + 0xF1EF9AU, 0xF1F771U, 0xF2045FU, 0xF21CB4U, 0xF22D61U, 0xF2358AU, 0xF24EC8U, 0xF25623U, 0xF267F6U, 0xF27F1DU, + 0xF28999U, 0xF29172U, 0xF2A0A7U, 0xF2B84CU, 0xF2C30EU, 0xF2DBE5U, 0xF2EA30U, 0xF2F2DBU, 0xF30738U, 0xF31FD3U, + 0xF32E06U, 0xF336EDU, 0xF34DAFU, 0xF35544U, 0xF36491U, 0xF37C7AU, 0xF38AFEU, 0xF39215U, 0xF3A3C0U, 0xF3BB2BU, + 0xF3C069U, 0xF3D882U, 0xF3E957U, 0xF3F1BCU, 0xF40F0BU, 0xF417E0U, 0xF42635U, 0xF43EDEU, 0xF4459CU, 0xF45D77U, + 0xF46CA2U, 0xF47449U, 0xF482CDU, 0xF49A26U, 0xF4ABF3U, 0xF4B318U, 0xF4C85AU, 0xF4D0B1U, 0xF4E164U, 0xF4F98FU, + 0xF50C6CU, 0xF51487U, 0xF52552U, 0xF53DB9U, 0xF546FBU, 0xF55E10U, 0xF56FC5U, 0xF5772EU, 0xF581AAU, 0xF59941U, + 0xF5A894U, 0xF5B07FU, 0xF5CB3DU, 0xF5D3D6U, 0xF5E203U, 0xF5FAE8U, 0xF609C6U, 0xF6112DU, 0xF620F8U, 0xF63813U, + 0xF64351U, 0xF65BBAU, 0xF66A6FU, 0xF67284U, 0xF68400U, 0xF69CEBU, 0xF6AD3EU, 0xF6B5D5U, 0xF6CE97U, 0xF6D67CU, + 0xF6E7A9U, 0xF6FF42U, 0xF70AA1U, 0xF7124AU, 0xF7239FU, 0xF73B74U, 0xF74036U, 0xF758DDU, 0xF76908U, 0xF771E3U, + 0xF78767U, 0xF79F8CU, 0xF7AE59U, 0xF7B6B2U, 0xF7CDF0U, 0xF7D51BU, 0xF7E4CEU, 0xF7FC25U, 0xF80148U, 0xF819A3U, + 0xF82876U, 0xF8309DU, 0xF84BDFU, 0xF85334U, 0xF862E1U, 0xF87A0AU, 0xF88C8EU, 0xF89465U, 0xF8A5B0U, 0xF8BD5BU, + 0xF8C619U, 0xF8DEF2U, 0xF8EF27U, 0xF8F7CCU, 0xF9022FU, 0xF91AC4U, 0xF92B11U, 0xF933FAU, 0xF948B8U, 0xF95053U, + 0xF96186U, 0xF9796DU, 0xF98FE9U, 0xF99702U, 0xF9A6D7U, 0xF9BE3CU, 0xF9C57EU, 0xF9DD95U, 0xF9EC40U, 0xF9F4ABU, + 0xFA0785U, 0xFA1F6EU, 0xFA2EBBU, 0xFA3650U, 0xFA4D12U, 0xFA55F9U, 0xFA642CU, 0xFA7CC7U, 0xFA8A43U, 0xFA92A8U, + 0xFAA37DU, 0xFABB96U, 0xFAC0D4U, 0xFAD83FU, 0xFAE9EAU, 0xFAF101U, 0xFB04E2U, 0xFB1C09U, 0xFB2DDCU, 0xFB3537U, + 0xFB4E75U, 0xFB569EU, 0xFB674BU, 0xFB7FA0U, 0xFB8924U, 0xFB91CFU, 0xFBA01AU, 0xFBB8F1U, 0xFBC3B3U, 0xFBDB58U, + 0xFBEA8DU, 0xFBF266U, 0xFC0CD1U, 0xFC143AU, 0xFC25EFU, 0xFC3D04U, 0xFC4646U, 0xFC5EADU, 0xFC6F78U, 0xFC7793U, + 0xFC8117U, 0xFC99FCU, 0xFCA829U, 0xFCB0C2U, 0xFCCB80U, 0xFCD36BU, 0xFCE2BEU, 0xFCFA55U, 0xFD0FB6U, 0xFD175DU, + 0xFD2688U, 0xFD3E63U, 0xFD4521U, 0xFD5DCAU, 0xFD6C1FU, 0xFD74F4U, 0xFD8270U, 0xFD9A9BU, 0xFDAB4EU, 0xFDB3A5U, + 0xFDC8E7U, 0xFDD00CU, 0xFDE1D9U, 0xFDF932U, 0xFE0A1CU, 0xFE12F7U, 0xFE2322U, 0xFE3BC9U, 0xFE408BU, 0xFE5860U, + 0xFE69B5U, 0xFE715EU, 0xFE87DAU, 0xFE9F31U, 0xFEAEE4U, 0xFEB60FU, 0xFECD4DU, 0xFED5A6U, 0xFEE473U, 0xFEFC98U, + 0xFF097BU, 0xFF1190U, 0xFF2045U, 0xFF38AEU, 0xFF43ECU, 0xFF5B07U, 0xFF6AD2U, 0xFF7239U, 0xFF84BDU, 0xFF9C56U, + 0xFFAD83U, 0xFFB568U, 0xFFCE2AU, 0xFFD6C1U, 0xFFE714U, 0xFFFFFFU}; + +static const unsigned int DECODING_TABLE_23127[] = { + 0x000000U, 0x000001U, 0x000002U, 0x000003U, 0x000004U, 0x000005U, 0x000006U, 0x000007U, 0x000008U, 0x000009U, + 0x00000AU, 0x00000BU, 0x00000CU, 0x00000DU, 0x00000EU, 0x024020U, 0x000010U, 0x000011U, 0x000012U, 0x000013U, + 0x000014U, 0x000015U, 0x000016U, 0x412000U, 0x000018U, 0x000019U, 0x00001AU, 0x180800U, 0x00001CU, 0x200300U, + 0x048040U, 0x001480U, 0x000020U, 0x000021U, 0x000022U, 0x000023U, 0x000024U, 0x000025U, 0x000026U, 0x024008U, + 0x000028U, 0x000029U, 0x00002AU, 0x024004U, 0x00002CU, 0x024002U, 0x024001U, 0x024000U, 0x000030U, 0x000031U, + 0x000032U, 0x008180U, 0x000034U, 0x000C40U, 0x301000U, 0x0C0200U, 0x000038U, 0x043000U, 0x400600U, 0x210040U, + 0x090080U, 0x508000U, 0x002900U, 0x024010U, 0x000040U, 0x000041U, 0x000042U, 0x000043U, 0x000044U, 0x000045U, + 0x000046U, 0x280080U, 0x000048U, 0x000049U, 0x00004AU, 0x002500U, 0x00004CU, 0x111000U, 0x048010U, 0x400A00U, + 0x000050U, 0x000051U, 0x000052U, 0x021200U, 0x000054U, 0x000C20U, 0x048008U, 0x104100U, 0x000058U, 0x404080U, + 0x048004U, 0x210020U, 0x048002U, 0x0A2000U, 0x048000U, 0x048001U, 0x000060U, 0x000061U, 0x000062U, 0x540000U, + 0x000064U, 0x000C10U, 0x010300U, 0x00B000U, 0x000068U, 0x088200U, 0x001880U, 0x210010U, 0x602000U, 0x040180U, + 0x180400U, 0x024040U, 0x000070U, 0x000C04U, 0x086000U, 0x210008U, 0x000C01U, 0x000C00U, 0x420080U, 0x000C02U, + 0x120100U, 0x210002U, 0x210001U, 0x210000U, 0x005200U, 0x000C08U, 0x048020U, 0x210004U, 0x000080U, 0x000081U, + 0x000082U, 0x000083U, 0x000084U, 0x000085U, 0x000086U, 0x280040U, 0x000088U, 0x000089U, 0x00008AU, 0x050200U, + 0x00008CU, 0x00A800U, 0x500100U, 0x001410U, 0x000090U, 0x000091U, 0x000092U, 0x008120U, 0x000094U, 0x160000U, + 0x004A00U, 0x001408U, 0x000098U, 0x404040U, 0x222000U, 0x001404U, 0x090020U, 0x001402U, 0x001401U, 0x001400U, + 0x0000A0U, 0x0000A1U, 0x0000A2U, 0x008110U, 0x0000A4U, 0x401200U, 0x042400U, 0x110800U, 0x0000A8U, 0x300400U, + 0x001840U, 0x482000U, 0x090010U, 0x040140U, 0x208200U, 0x024080U, 0x0000B0U, 0x008102U, 0x008101U, 0x008100U, + 0x090008U, 0x206000U, 0x420040U, 0x008104U, 0x090004U, 0x020A00U, 0x144000U, 0x008108U, 0x090000U, 0x090001U, + 0x090002U, 0x001420U, 0x0000C0U, 0x0000C1U, 0x0000C2U, 0x280004U, 0x0000C4U, 0x280002U, 0x280001U, 0x280000U, + 0x0000C8U, 0x404010U, 0x001820U, 0x128000U, 0x020600U, 0x040120U, 0x016000U, 0x280008U, 0x0000D0U, 0x404008U, + 0x110400U, 0x042800U, 0x003100U, 0x018200U, 0x420020U, 0x280010U, 0x404001U, 0x404000U, 0x080300U, 0x404002U, + 0x300800U, 0x404004U, 0x048080U, 0x001440U, 0x0000E0U, 0x032000U, 0x001808U, 0x004600U, 0x10C000U, 0x040108U, + 0x420010U, 0x280020U, 0x001802U, 0x040104U, 0x001800U, 0x001801U, 0x040101U, 0x040100U, 0x001804U, 0x040102U, + 0x240200U, 0x181000U, 0x420004U, 0x008140U, 0x420002U, 0x000C80U, 0x420000U, 0x420001U, 0x00A400U, 0x404020U, + 0x001810U, 0x210080U, 0x090040U, 0x040110U, 0x420008U, 0x102200U, 0x000100U, 0x000101U, 0x000102U, 0x000103U, + 0x000104U, 0x000105U, 0x000106U, 0x041800U, 0x000108U, 0x000109U, 0x00010AU, 0x002440U, 0x00010CU, 0x200210U, + 0x500080U, 0x098000U, 0x000110U, 0x000111U, 0x000112U, 0x0080A0U, 0x000114U, 0x200208U, 0x0A0400U, 0x104040U, + 0x000118U, 0x200204U, 0x015000U, 0x460000U, 0x200201U, 0x200200U, 0x002820U, 0x200202U, 0x000120U, 0x000121U, + 0x000122U, 0x008090U, 0x000124U, 0x182000U, 0x010240U, 0x600400U, 0x000128U, 0x410800U, 0x2C0000U, 0x101200U, + 0x009400U, 0x0400C0U, 0x002810U, 0x024100U, 0x000130U, 0x008082U, 0x008081U, 0x008080U, 0x444000U, 0x031000U, + 0x002808U, 0x008084U, 0x120040U, 0x084400U, 0x002804U, 0x008088U, 0x002802U, 0x200220U, 0x002800U, 0x002801U, + 0x000140U, 0x000141U, 0x000142U, 0x002408U, 0x000144U, 0x428000U, 0x010220U, 0x104010U, 0x000148U, 0x002402U, + 0x002401U, 0x002400U, 0x084800U, 0x0400A0U, 0x221000U, 0x002404U, 0x000150U, 0x0D0000U, 0x600800U, 0x104004U, + 0x003080U, 0x104002U, 0x104001U, 0x104000U, 0x120020U, 0x009800U, 0x080280U, 0x002410U, 0x410400U, 0x200240U, + 0x048100U, 0x104008U, 0x000160U, 0x205000U, 0x010204U, 0x0A0800U, 0x010202U, 0x040088U, 0x010200U, 0x010201U, + 0x120010U, 0x040084U, 0x40C000U, 0x002420U, 0x040081U, 0x040080U, 0x010208U, 0x040082U, 0x120008U, 0x402200U, + 0x041400U, 0x0080C0U, 0x288000U, 0x000D00U, 0x010210U, 0x104020U, 0x120000U, 0x120001U, 0x120002U, 0x210100U, + 0x120004U, 0x040090U, 0x002840U, 0x481000U, 0x000180U, 0x000181U, 0x000182U, 0x008030U, 0x000184U, 0x014400U, + 0x500008U, 0x022200U, 0x000188U, 0x0A1000U, 0x500004U, 0x204800U, 0x500002U, 0x040060U, 0x500000U, 0x500001U, + 0x000190U, 0x008022U, 0x008021U, 0x008020U, 0x003040U, 0x480800U, 0x250000U, 0x008024U, 0x040C00U, 0x112000U, + 0x080240U, 0x008028U, 0x02C000U, 0x200280U, 0x500010U, 0x001500U, 0x0001A0U, 0x008012U, 0x008011U, 0x008010U, + 0x220800U, 0x040048U, 0x085000U, 0x008014U, 0x006200U, 0x040044U, 0x030400U, 0x008018U, 0x040041U, 0x040040U, + 0x500020U, 0x040042U, 0x008003U, 0x008002U, 0x008001U, 0x008000U, 0x100600U, 0x008006U, 0x008005U, 0x008004U, + 0x601000U, 0x00800AU, 0x008009U, 0x008008U, 0x090100U, 0x040050U, 0x002880U, 0x00800CU, 0x0001C0U, 0x100A00U, + 0x064000U, 0x411000U, 0x003010U, 0x040028U, 0x008C00U, 0x280100U, 0x218000U, 0x040024U, 0x080210U, 0x002480U, + 0x040021U, 0x040020U, 0x500040U, 0x040022U, 0x003004U, 0x220400U, 0x080208U, 0x008060U, 0x003000U, 0x003001U, + 0x003002U, 0x104080U, 0x080202U, 0x404100U, 0x080200U, 0x080201U, 0x003008U, 0x040030U, 0x080204U, 0x030800U, + 0x480400U, 0x04000CU, 0x302000U, 0x008050U, 0x040009U, 0x040008U, 0x010280U, 0x04000AU, 0x040005U, 0x040004U, + 0x001900U, 0x040006U, 0x040001U, 0x040000U, 0x040003U, 0x040002U, 0x014800U, 0x008042U, 0x008041U, 0x008040U, + 0x003020U, 0x040018U, 0x420100U, 0x008044U, 0x120080U, 0x040014U, 0x080220U, 0x008048U, 0x040011U, 0x040010U, + 0x204400U, 0x040012U, 0x000200U, 0x000201U, 0x000202U, 0x000203U, 0x000204U, 0x000205U, 0x000206U, 0x108400U, + 0x000208U, 0x000209U, 0x00020AU, 0x050080U, 0x00020CU, 0x200110U, 0x083000U, 0x400840U, 0x000210U, 0x000211U, + 0x000212U, 0x021040U, 0x000214U, 0x200108U, 0x004880U, 0x0C0020U, 0x000218U, 0x200104U, 0x400420U, 0x00E000U, + 0x200101U, 0x200100U, 0x130000U, 0x200102U, 0x000220U, 0x000221U, 0x000222U, 0x202800U, 0x000224U, 0x401080U, + 0x010140U, 0x0C0010U, 0x000228U, 0x088040U, 0x400410U, 0x101100U, 0x140800U, 0x012400U, 0x208080U, 0x024200U, + 0x000230U, 0x114000U, 0x400408U, 0x0C0004U, 0x02A000U, 0x0C0002U, 0x0C0001U, 0x0C0000U, 0x400402U, 0x020880U, + 0x400400U, 0x400401U, 0x005040U, 0x200120U, 0x400404U, 0x0C0008U, 0x000240U, 0x000241U, 0x000242U, 0x021010U, + 0x000244U, 0x046000U, 0x010120U, 0x400808U, 0x000248U, 0x088020U, 0x304000U, 0x400804U, 0x020480U, 0x400802U, + 0x400801U, 0x400800U, 0x000250U, 0x021002U, 0x021001U, 0x021000U, 0x580000U, 0x018080U, 0x202400U, 0x021004U, + 0x012800U, 0x140400U, 0x080180U, 0x021008U, 0x005020U, 0x200140U, 0x048200U, 0x400810U, 0x000260U, 0x088008U, + 0x010104U, 0x004480U, 0x010102U, 0x320000U, 0x010100U, 0x010101U, 0x088001U, 0x088000U, 0x062000U, 0x088002U, + 0x005010U, 0x088004U, 0x010108U, 0x400820U, 0x240080U, 0x402100U, 0x108800U, 0x021020U, 0x005008U, 0x000E00U, + 0x010110U, 0x0C0040U, 0x005004U, 0x088010U, 0x400440U, 0x210200U, 0x005000U, 0x005001U, 0x005002U, 0x102080U, + 0x000280U, 0x000281U, 0x000282U, 0x050008U, 0x000284U, 0x401020U, 0x004810U, 0x022100U, 0x000288U, 0x050002U, + 0x050001U, 0x050000U, 0x020440U, 0x184000U, 0x208020U, 0x050004U, 0x000290U, 0x082400U, 0x004804U, 0x700000U, + 0x004802U, 0x018040U, 0x004800U, 0x004801U, 0x109000U, 0x020820U, 0x080140U, 0x050010U, 0x442000U, 0x200180U, + 0x004808U, 0x001600U, 0x0002A0U, 0x401004U, 0x1A0000U, 0x004440U, 0x401001U, 0x401000U, 0x208008U, 0x401002U, + 0x006100U, 0x020810U, 0x208004U, 0x050020U, 0x208002U, 0x401008U, 0x208000U, 0x208001U, 0x240040U, 0x020808U, + 0x013000U, 0x008300U, 0x100500U, 0x401010U, 0x004820U, 0x0C0080U, 0x020801U, 0x020800U, 0x400480U, 0x020802U, + 0x090200U, 0x020804U, 0x208010U, 0x102040U, 0x0002C0U, 0x100900U, 0x40A000U, 0x004420U, 0x020408U, 0x018010U, + 0x141000U, 0x280200U, 0x020404U, 0x203000U, 0x080110U, 0x050040U, 0x020400U, 0x020401U, 0x020402U, 0x400880U, + 0x240020U, 0x018004U, 0x080108U, 0x021080U, 0x018001U, 0x018000U, 0x004840U, 0x018002U, 0x080102U, 0x404200U, + 0x080100U, 0x080101U, 0x020410U, 0x018008U, 0x080104U, 0x102020U, 0x240010U, 0x004402U, 0x004401U, 0x004400U, + 0x082800U, 0x401040U, 0x010180U, 0x004404U, 0x510000U, 0x088080U, 0x001A00U, 0x004408U, 0x020420U, 0x040300U, + 0x208040U, 0x102010U, 0x240000U, 0x240001U, 0x240002U, 0x004410U, 0x240004U, 0x018020U, 0x420200U, 0x102008U, + 0x240008U, 0x020840U, 0x080120U, 0x102004U, 0x005080U, 0x102002U, 0x102001U, 0x102000U, 0x000300U, 0x000301U, + 0x000302U, 0x484000U, 0x000304U, 0x200018U, 0x010060U, 0x022080U, 0x000308U, 0x200014U, 0x028800U, 0x101020U, + 0x200011U, 0x200010U, 0x044400U, 0x200012U, 0x000310U, 0x20000CU, 0x142000U, 0x010C00U, 0x200009U, 0x200008U, + 0x409000U, 0x20000AU, 0x200005U, 0x200004U, 0x0800C0U, 0x200006U, 0x200001U, 0x200000U, 0x200003U, 0x200002U, + 0x000320U, 0x060400U, 0x010044U, 0x101008U, 0x010042U, 0x00C800U, 0x010040U, 0x010041U, 0x006080U, 0x101002U, + 0x101001U, 0x101000U, 0x4A0000U, 0x200030U, 0x010048U, 0x101004U, 0x081800U, 0x402040U, 0x224000U, 0x008280U, + 0x100480U, 0x200028U, 0x010050U, 0x0C0100U, 0x058000U, 0x200024U, 0x400500U, 0x101010U, 0x200021U, 0x200020U, + 0x002A00U, 0x200022U, 0x000340U, 0x100880U, 0x010024U, 0x248000U, 0x010022U, 0x081400U, 0x010020U, 0x010021U, + 0x441000U, 0x034000U, 0x080090U, 0x002600U, 0x10A000U, 0x200050U, 0x010028U, 0x400900U, 0x00C400U, 0x402020U, + 0x080088U, 0x021100U, 0x060800U, 0x200048U, 0x010030U, 0x104200U, 0x080082U, 0x200044U, 0x080080U, 0x080081U, + 0x200041U, 0x200040U, 0x080084U, 0x200042U, 0x010006U, 0x402010U, 0x010004U, 0x010005U, 0x010002U, 0x010003U, + 0x010000U, 0x010001U, 0x200C00U, 0x088100U, 0x01000CU, 0x101040U, 0x01000AU, 0x040280U, 0x010008U, 0x010009U, + 0x402001U, 0x402000U, 0x010014U, 0x402002U, 0x010012U, 0x402004U, 0x010010U, 0x010011U, 0x120200U, 0x402008U, + 0x0800A0U, 0x044800U, 0x005100U, 0x200060U, 0x010018U, 0x028400U, 0x000380U, 0x100840U, 0x201400U, 0x022004U, + 0x0C8000U, 0x022002U, 0x022001U, 0x022000U, 0x006020U, 0x408400U, 0x080050U, 0x050100U, 0x011800U, 0x200090U, + 0x500200U, 0x022008U, 0x430000U, 0x045000U, 0x080048U, 0x008220U, 0x100420U, 0x200088U, 0x004900U, 0x022010U, + 0x080042U, 0x200084U, 0x080040U, 0x080041U, 0x200081U, 0x200080U, 0x080044U, 0x200082U, 0x006008U, 0x290000U, + 0x440800U, 0x008210U, 0x100410U, 0x401100U, 0x0100C0U, 0x022020U, 0x006000U, 0x006001U, 0x006002U, 0x101080U, + 0x006004U, 0x040240U, 0x208100U, 0x080C00U, 0x100404U, 0x008202U, 0x008201U, 0x008200U, 0x100400U, 0x100401U, + 0x100402U, 0x008204U, 0x006010U, 0x020900U, 0x080060U, 0x008208U, 0x100408U, 0x2000A0U, 0x061000U, 0x414000U, + 0x100801U, 0x100800U, 0x080018U, 0x100802U, 0x604000U, 0x100804U, 0x0100A0U, 0x022040U, 0x080012U, 0x100808U, + 0x080010U, 0x080011U, 0x020500U, 0x040220U, 0x080014U, 0x00D000U, 0x08000AU, 0x100810U, 0x080008U, 0x080009U, + 0x003200U, 0x018100U, 0x08000CU, 0x440400U, 0x080002U, 0x080003U, 0x080000U, 0x080001U, 0x080006U, 0x2000C0U, + 0x080004U, 0x080005U, 0x029000U, 0x100820U, 0x010084U, 0x004500U, 0x010082U, 0x040208U, 0x010080U, 0x010081U, + 0x006040U, 0x040204U, 0x080030U, 0x620000U, 0x040201U, 0x040200U, 0x010088U, 0x040202U, 0x240100U, 0x402080U, + 0x080028U, 0x008240U, 0x100440U, 0x0A4000U, 0x010090U, 0x201800U, 0x080022U, 0x011400U, 0x080020U, 0x080021U, + 0x408800U, 0x040210U, 0x080024U, 0x102100U, 0x000400U, 0x000401U, 0x000402U, 0x000403U, 0x000404U, 0x000405U, + 0x000406U, 0x108200U, 0x000408U, 0x000409U, 0x00040AU, 0x002140U, 0x00040CU, 0x4C0000U, 0x210800U, 0x001090U, + 0x000410U, 0x000411U, 0x000412U, 0x244000U, 0x000414U, 0x000860U, 0x0A0100U, 0x001088U, 0x000418U, 0x038000U, + 0x400220U, 0x001084U, 0x106000U, 0x001082U, 0x001081U, 0x001080U, 0x000420U, 0x000421U, 0x000422U, 0x091000U, + 0x000424U, 0x000850U, 0x042080U, 0x600100U, 0x000428U, 0x300080U, 0x400210U, 0x048800U, 0x009100U, 0x012200U, + 0x180040U, 0x024400U, 0x000430U, 0x000844U, 0x400208U, 0x122000U, 0x000841U, 0x000840U, 0x01C000U, 0x000842U, + 0x400202U, 0x084100U, 0x400200U, 0x400201U, 0x260000U, 0x000848U, 0x400204U, 0x0010A0U, 0x000440U, 0x000441U, + 0x000442U, 0x002108U, 0x000444U, 0x000830U, 0x405000U, 0x070000U, 0x000448U, 0x002102U, 0x002101U, 0x002100U, + 0x020280U, 0x20C000U, 0x180020U, 0x002104U, 0x000450U, 0x000824U, 0x110080U, 0x488000U, 0x000821U, 0x000820U, + 0x202200U, 0x000822U, 0x281000U, 0x140200U, 0x024800U, 0x002110U, 0x410100U, 0x000828U, 0x048400U, 0x0010C0U, + 0x000460U, 0x000814U, 0x228000U, 0x004280U, 0x000811U, 0x000810U, 0x180008U, 0x000812U, 0x054000U, 0x421000U, + 0x180004U, 0x002120U, 0x180002U, 0x000818U, 0x180000U, 0x180001U, 0x000805U, 0x000804U, 0x041100U, 0x000806U, + 0x000801U, 0x000800U, 0x000803U, 0x000802U, 0x00A080U, 0x00080CU, 0x400240U, 0x210400U, 0x000809U, 0x000808U, + 0x180010U, 0x00080AU, 0x000480U, 0x000481U, 0x000482U, 0x420800U, 0x000484U, 0x014100U, 0x042020U, 0x001018U, + 0x000488U, 0x300020U, 0x08C000U, 0x001014U, 0x020240U, 0x001012U, 0x001011U, 0x001010U, 0x000490U, 0x082200U, + 0x110040U, 0x00100CU, 0x608000U, 0x00100AU, 0x001009U, 0x001008U, 0x040900U, 0x001006U, 0x001005U, 0x001004U, + 0x001003U, 0x001002U, 0x001001U, 0x001000U, 0x0004A0U, 0x300008U, 0x042004U, 0x004240U, 0x042002U, 0x0A8000U, + 0x042000U, 0x042001U, 0x300001U, 0x300000U, 0x030100U, 0x300002U, 0x404800U, 0x300004U, 0x042008U, 0x001030U, + 0x025000U, 0x450000U, 0x280800U, 0x008500U, 0x100300U, 0x0008C0U, 0x042010U, 0x001028U, 0x00A040U, 0x300010U, + 0x400280U, 0x001024U, 0x090400U, 0x001022U, 0x001021U, 0x001020U, 0x0004C0U, 0x049000U, 0x110010U, 0x004220U, + 0x020208U, 0x502000U, 0x008900U, 0x280400U, 0x020204U, 0x090800U, 0x640000U, 0x002180U, 0x020200U, 0x020201U, + 0x020202U, 0x001050U, 0x110002U, 0x220100U, 0x110000U, 0x110001U, 0x0C4000U, 0x0008A0U, 0x110004U, 0x001048U, + 0x00A020U, 0x404400U, 0x110008U, 0x001044U, 0x020210U, 0x001042U, 0x001041U, 0x001040U, 0x480100U, 0x004202U, + 0x004201U, 0x004200U, 0x211000U, 0x000890U, 0x042040U, 0x004204U, 0x00A010U, 0x300040U, 0x001C00U, 0x004208U, + 0x020220U, 0x040500U, 0x180080U, 0x418000U, 0x00A008U, 0x000884U, 0x110020U, 0x004210U, 0x000881U, 0x000880U, + 0x420400U, 0x000882U, 0x00A000U, 0x00A001U, 0x00A002U, 0x0E0000U, 0x00A004U, 0x000888U, 0x204100U, 0x001060U, + 0x000500U, 0x000501U, 0x000502U, 0x002048U, 0x000504U, 0x014080U, 0x0A0010U, 0x600020U, 0x000508U, 0x002042U, + 0x002041U, 0x002040U, 0x009020U, 0x120800U, 0x044200U, 0x002044U, 0x000510U, 0x501000U, 0x0A0004U, 0x010A00U, + 0x0A0002U, 0x04A000U, 0x0A0000U, 0x0A0001U, 0x040880U, 0x084020U, 0x308000U, 0x002050U, 0x410040U, 0x200600U, + 0x0A0008U, 0x001180U, 0x000520U, 0x060200U, 0x104800U, 0x600004U, 0x009008U, 0x600002U, 0x600001U, 0x600000U, + 0x009004U, 0x084010U, 0x030080U, 0x002060U, 0x009000U, 0x009001U, 0x009002U, 0x600008U, 0x212000U, 0x084008U, + 0x041040U, 0x008480U, 0x100280U, 0x000940U, 0x0A0020U, 0x600010U, 0x084001U, 0x084000U, 0x400300U, 0x084002U, + 0x009010U, 0x084004U, 0x002C00U, 0x150000U, 0x000540U, 0x00200AU, 0x002009U, 0x002008U, 0x340000U, 0x081200U, + 0x008880U, 0x00200CU, 0x002003U, 0x002002U, 0x002001U, 0x002000U, 0x410010U, 0x002006U, 0x002005U, 0x002004U, + 0x00C200U, 0x220080U, 0x041020U, 0x002018U, 0x410008U, 0x000920U, 0x0A0040U, 0x104400U, 0x410004U, 0x002012U, + 0x002011U, 0x002010U, 0x410000U, 0x410001U, 0x410002U, 0x002014U, 0x480080U, 0x118000U, 0x041010U, 0x002028U, + 0x026000U, 0x000910U, 0x010600U, 0x600040U, 0x200A00U, 0x002022U, 0x002021U, 0x002020U, 0x009040U, 0x040480U, + 0x180100U, 0x002024U, 0x041002U, 0x000904U, 0x041000U, 0x041001U, 0x000901U, 0x000900U, 0x041004U, 0x000902U, + 0x120400U, 0x084040U, 0x041008U, 0x002030U, 0x410020U, 0x000908U, 0x204080U, 0x028200U, 0x000580U, 0x014004U, + 0x201200U, 0x1C0000U, 0x014001U, 0x014000U, 0x008840U, 0x014002U, 0x040810U, 0x408200U, 0x030020U, 0x0020C0U, + 0x282000U, 0x014008U, 0x500400U, 0x001110U, 0x040808U, 0x220040U, 0x406000U, 0x008420U, 0x100220U, 0x014010U, + 0x0A0080U, 0x001108U, 0x040800U, 0x040801U, 0x040802U, 0x001104U, 0x040804U, 0x001102U, 0x001101U, 0x001100U, + 0x480040U, 0x003800U, 0x030008U, 0x008410U, 0x100210U, 0x014020U, 0x042100U, 0x600080U, 0x030002U, 0x300100U, + 0x030000U, 0x030001U, 0x009080U, 0x040440U, 0x030004U, 0x080A00U, 0x100204U, 0x008402U, 0x008401U, 0x008400U, + 0x100200U, 0x100201U, 0x100202U, 0x008404U, 0x040820U, 0x084080U, 0x030010U, 0x008408U, 0x100208U, 0x422000U, + 0x204040U, 0x001120U, 0x480020U, 0x220010U, 0x008804U, 0x002088U, 0x008802U, 0x014040U, 0x008800U, 0x008801U, + 0x105000U, 0x002082U, 0x002081U, 0x002080U, 0x020300U, 0x040420U, 0x008808U, 0x002084U, 0x220001U, 0x220000U, + 0x110100U, 0x220002U, 0x003400U, 0x220004U, 0x008810U, 0x440200U, 0x040840U, 0x220008U, 0x080600U, 0x002090U, + 0x410080U, 0x188000U, 0x204020U, 0x001140U, 0x480000U, 0x480001U, 0x480002U, 0x004300U, 0x480004U, 0x040408U, + 0x008820U, 0x121000U, 0x480008U, 0x040404U, 0x030040U, 0x0020A0U, 0x040401U, 0x040400U, 0x204010U, 0x040402U, + 0x480010U, 0x220020U, 0x041080U, 0x008440U, 0x100240U, 0x000980U, 0x204008U, 0x092000U, 0x00A100U, 0x011200U, + 0x204004U, 0x500800U, 0x204002U, 0x040410U, 0x204000U, 0x204001U, 0x000600U, 0x000601U, 0x000602U, 0x108004U, + 0x000604U, 0x108002U, 0x108001U, 0x108000U, 0x000608U, 0x005800U, 0x400030U, 0x2A0000U, 0x0200C0U, 0x012020U, + 0x044100U, 0x108008U, 0x000610U, 0x082080U, 0x400028U, 0x010900U, 0x051000U, 0x424000U, 0x202040U, 0x108010U, + 0x400022U, 0x140040U, 0x400020U, 0x400021U, 0x088800U, 0x200500U, 0x400024U, 0x001280U, 0x000620U, 0x060100U, + 0x400018U, 0x0040C0U, 0x284000U, 0x012008U, 0x021800U, 0x108020U, 0x400012U, 0x012004U, 0x400010U, 0x400011U, + 0x012001U, 0x012000U, 0x400014U, 0x012002U, 0x40000AU, 0x209000U, 0x400008U, 0x400009U, 0x100180U, 0x000A40U, + 0x40000CU, 0x0C0400U, 0x400002U, 0x400003U, 0x400000U, 0x400001U, 0x400006U, 0x012010U, 0x400004U, 0x400005U, + 0x000640U, 0x610000U, 0x0C0800U, 0x0040A0U, 0x020088U, 0x081100U, 0x202010U, 0x108040U, 0x020084U, 0x140010U, + 0x019000U, 0x002300U, 0x020080U, 0x020081U, 0x020082U, 0x400C00U, 0x00C100U, 0x140008U, 0x202004U, 0x021400U, + 0x202002U, 0x000A20U, 0x202000U, 0x202001U, 0x140001U, 0x140000U, 0x400060U, 0x140002U, 0x020090U, 0x140004U, + 0x202008U, 0x094000U, 0x103000U, 0x004082U, 0x004081U, 0x004080U, 0x448000U, 0x000A10U, 0x010500U, 0x004084U, + 0x200900U, 0x088400U, 0x400050U, 0x004088U, 0x0200A0U, 0x012040U, 0x180200U, 0x241000U, 0x0B0000U, 0x000A04U, + 0x400048U, 0x004090U, 0x000A01U, 0x000A00U, 0x202020U, 0x000A02U, 0x400042U, 0x140020U, 0x400040U, 0x400041U, + 0x005400U, 0x000A08U, 0x400044U, 0x028100U, 0x000680U, 0x082010U, 0x201100U, 0x004060U, 0x020048U, 0x240800U, + 0x490000U, 0x108080U, 0x020044U, 0x408100U, 0x102800U, 0x050400U, 0x020040U, 0x020041U, 0x020042U, 0x001210U, + 0x082001U, 0x082000U, 0x068000U, 0x082002U, 0x100120U, 0x082004U, 0x004C00U, 0x001208U, 0x214000U, 0x082008U, + 0x4000A0U, 0x001204U, 0x020050U, 0x001202U, 0x001201U, 0x001200U, 0x018800U, 0x004042U, 0x004041U, 0x004040U, + 0x100110U, 0x401400U, 0x042200U, 0x004044U, 0x0C1000U, 0x300200U, 0x400090U, 0x004048U, 0x020060U, 0x012080U, + 0x208400U, 0x080900U, 0x100104U, 0x082020U, 0x400088U, 0x004050U, 0x100100U, 0x100101U, 0x100102U, 0x230000U, + 0x400082U, 0x020C00U, 0x400080U, 0x400081U, 0x100108U, 0x04C000U, 0x400084U, 0x001220U, 0x02000CU, 0x004022U, + 0x004021U, 0x004020U, 0x020008U, 0x020009U, 0x02000AU, 0x004024U, 0x020004U, 0x020005U, 0x020006U, 0x004028U, + 0x020000U, 0x020001U, 0x020002U, 0x020003U, 0x401800U, 0x082040U, 0x110200U, 0x004030U, 0x020018U, 0x018400U, + 0x202080U, 0x440100U, 0x020014U, 0x140080U, 0x080500U, 0x208800U, 0x020010U, 0x020011U, 0x020012U, 0x001240U, + 0x004003U, 0x004002U, 0x004001U, 0x004000U, 0x020028U, 0x004006U, 0x004005U, 0x004004U, 0x020024U, 0x00400AU, + 0x004009U, 0x004008U, 0x020020U, 0x020021U, 0x020022U, 0x00400CU, 0x240400U, 0x004012U, 0x004011U, 0x004010U, + 0x100140U, 0x000A80U, 0x089000U, 0x004014U, 0x00A200U, 0x011100U, 0x4000C0U, 0x004018U, 0x020030U, 0x680000U, + 0x050800U, 0x102400U, 0x000700U, 0x060020U, 0x201080U, 0x010810U, 0x402800U, 0x081040U, 0x044008U, 0x108100U, + 0x190000U, 0x408080U, 0x044004U, 0x002240U, 0x044002U, 0x200410U, 0x044000U, 0x044001U, 0x00C040U, 0x010802U, + 0x010801U, 0x010800U, 0x1000A0U, 0x200408U, 0x0A0200U, 0x010804U, 0x023000U, 0x200404U, 0x400120U, 0x010808U, + 0x200401U, 0x200400U, 0x044010U, 0x200402U, 0x060001U, 0x060000U, 0x08A000U, 0x060002U, 0x100090U, 0x060004U, + 0x010440U, 0x600200U, 0x200840U, 0x060008U, 0x400110U, 0x101400U, 0x009200U, 0x012100U, 0x044020U, 0x080880U, + 0x100084U, 0x060010U, 0x400108U, 0x010820U, 0x100080U, 0x100081U, 0x100082U, 0x007000U, 0x400102U, 0x084200U, + 0x400100U, 0x400101U, 0x100088U, 0x200420U, 0x400104U, 0x028040U, 0x00C010U, 0x081004U, 0x520000U, 0x002208U, + 0x081001U, 0x081000U, 0x010420U, 0x081002U, 0x200820U, 0x002202U, 0x002201U, 0x002200U, 0x020180U, 0x081008U, + 0x044040U, 0x002204U, 0x00C000U, 0x00C001U, 0x00C002U, 0x010840U, 0x00C004U, 0x081010U, 0x202100U, 0x440080U, + 0x00C008U, 0x140100U, 0x080480U, 0x002210U, 0x410200U, 0x200440U, 0x101800U, 0x028020U, 0x200808U, 0x060040U, + 0x010404U, 0x004180U, 0x010402U, 0x081020U, 0x010400U, 0x010401U, 0x200800U, 0x200801U, 0x200802U, 0x002220U, + 0x200804U, 0x504000U, 0x010408U, 0x028010U, 0x00C020U, 0x402400U, 0x041200U, 0x380000U, 0x1000C0U, 0x000B00U, + 0x010410U, 0x028008U, 0x200810U, 0x011080U, 0x400140U, 0x028004U, 0x0C2000U, 0x028002U, 0x028001U, 0x028000U, + 0x201002U, 0x408008U, 0x201000U, 0x201001U, 0x100030U, 0x014200U, 0x201004U, 0x022400U, 0x408001U, 0x408000U, + 0x201008U, 0x408002U, 0x020140U, 0x408004U, 0x044080U, 0x080820U, 0x100024U, 0x082100U, 0x201010U, 0x010880U, + 0x100020U, 0x100021U, 0x100022U, 0x440040U, 0x040A00U, 0x408010U, 0x080440U, 0x124000U, 0x100028U, 0x200480U, + 0x01A000U, 0x001300U, 0x100014U, 0x060080U, 0x201020U, 0x004140U, 0x100010U, 0x100011U, 0x100012U, 0x080808U, + 0x006400U, 0x408020U, 0x030200U, 0x080804U, 0x100018U, 0x080802U, 0x080801U, 0x080800U, 0x100004U, 0x100005U, + 0x100006U, 0x008600U, 0x100000U, 0x100001U, 0x100002U, 0x100003U, 0x10000CU, 0x011040U, 0x400180U, 0x242000U, + 0x100008U, 0x100009U, 0x10000AU, 0x080810U, 0x052000U, 0x100C00U, 0x201040U, 0x004120U, 0x020108U, 0x081080U, + 0x008A00U, 0x440010U, 0x020104U, 0x408040U, 0x080410U, 0x002280U, 0x020100U, 0x020101U, 0x020102U, 0x310000U, + 0x00C080U, 0x220200U, 0x080408U, 0x440004U, 0x100060U, 0x440002U, 0x440001U, 0x440000U, 0x080402U, 0x011020U, + 0x080400U, 0x080401U, 0x020110U, 0x006800U, 0x080404U, 0x440008U, 0x480200U, 0x004102U, 0x004101U, 0x004100U, + 0x100050U, 0x20A000U, 0x010480U, 0x004104U, 0x200880U, 0x011010U, 0x148000U, 0x004108U, 0x020120U, 0x040600U, + 0x403000U, 0x080840U, 0x100044U, 0x011008U, 0x022800U, 0x004110U, 0x100040U, 0x100041U, 0x100042U, 0x440020U, + 0x011001U, 0x011000U, 0x080420U, 0x011002U, 0x100048U, 0x011004U, 0x204200U, 0x028080U}; + +#define X22 0x00400000 /* vector representation of X^{22} */ +#define X11 0x00000800 /* vector representation of X^{11} */ +#define MASK12 0xfffff800 /* auxiliary vector for testing */ +#define GENPOL 0x00000c75 /* generator polinomial, g(x) */ + +static unsigned int get_syndrome_23127(unsigned int pattern) +/* + * Compute the syndrome corresponding to the given pattern, i.e., the + * remainder after dividing the pattern (when considering it as the vector + * representation of a polynomial) by the generator polynomial, GENPOL. + * In the program this pattern has several meanings: (1) pattern = infomation + * bits, when constructing the encoding table; (2) pattern = error pattern, + * when constructing the decoding table; and (3) pattern = received vector, to + * obtain its syndrome in decoding. + */ +{ + unsigned int aux = X22; + + if (pattern >= X11) { + while (pattern & MASK12) { + while (!(aux & pattern)) + aux = aux >> 1; + + pattern ^= (aux / X11) * GENPOL; + } + } + + return pattern; +} + +unsigned int CGolay::encode23127(unsigned int data) +{ + return ENCODING_TABLE_23127[data]; +} + +unsigned int CGolay::encode24128(unsigned int data) +{ + return ENCODING_TABLE_24128[data]; +} + +unsigned int CGolay::decode23127(unsigned int code) +{ + unsigned int syndrome = ::get_syndrome_23127(code); + unsigned int error_pattern = DECODING_TABLE_23127[syndrome]; + + code ^= error_pattern; + + return code >> 11; +} + +unsigned int CGolay::decode24128(unsigned int code) +{ + return decode23127(code >> 1); +} diff --git a/Common/Golay.h b/Common/Golay.h new file mode 100644 index 0000000..eb3d65f --- /dev/null +++ b/Common/Golay.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Golay_H +#define Golay_H + +class CGolay { +public: + static unsigned int encode23127(unsigned int data); + static unsigned int encode24128(unsigned int data); + + static unsigned int decode23127(unsigned int code); + static unsigned int decode24128(unsigned int code); +}; + +#endif diff --git a/Common/HardwareController.cpp b/Common/HardwareController.cpp new file mode 100644 index 0000000..c0ba8c9 --- /dev/null +++ b/Common/HardwareController.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "HardwareController.h" + +IHardwareController::~IHardwareController() +{ +} diff --git a/Common/HardwareController.h b/Common/HardwareController.h new file mode 100644 index 0000000..0035f84 --- /dev/null +++ b/Common/HardwareController.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef HardwareController_H +#define HardwareController_H + +class IHardwareController { +public: + virtual ~IHardwareController() = 0; + + virtual bool open() = 0; + + virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) = 0; + + virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8) = 0; + + virtual void close() = 0; + +private: +}; + +#endif diff --git a/Common/HeaderData.cpp b/Common/HeaderData.cpp new file mode 100644 index 0000000..7f612af --- /dev/null +++ b/Common/HeaderData.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2009,2011,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CCITTChecksumReverse.h" +#include "HeaderData.h" +#include "DStarDefines.h" + +CHeaderData::CHeaderData() : +m_time(), +m_myCall1(), +m_myCall2(), +m_yourCall(), +m_rptCall1(), +m_rptCall2(), +m_flag1(0x00), +m_flag2(0x00), +m_flag3(0x00), +m_valid(false) +{ +} + +CHeaderData::CHeaderData(const CHeaderData& header) : +m_time(header.m_time), +m_myCall1(header.m_myCall1), +m_myCall2(header.m_myCall2), +m_yourCall(header.m_yourCall), +m_rptCall1(header.m_rptCall1), +m_rptCall2(header.m_rptCall2), +m_flag1(header.m_flag1), +m_flag2(header.m_flag2), +m_flag3(header.m_flag3), +m_valid(header.m_valid) +{ +} + +CHeaderData::CHeaderData(const unsigned char* data, unsigned int length, bool check) : +m_time(), +m_myCall1(), +m_myCall2(), +m_yourCall(), +m_rptCall1(), +m_rptCall2(), +m_flag1(0x00), +m_flag2(0x00), +m_flag3(0x00), +m_valid(true) +{ + wxASSERT(data != NULL); + wxASSERT(length >= (RADIO_HEADER_LENGTH_BYTES - 2U)); + + const unsigned char* p = data; + m_flag1 = *p++; + m_flag2 = *p++; + m_flag3 = *p++; + + m_rptCall2 = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + p += LONG_CALLSIGN_LENGTH; + + m_rptCall1 = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + p += LONG_CALLSIGN_LENGTH; + + m_yourCall = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + p += LONG_CALLSIGN_LENGTH; + + m_myCall1 = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + p += LONG_CALLSIGN_LENGTH; + + m_myCall2 = wxString((const char*)p, wxConvLocal, SHORT_CALLSIGN_LENGTH); + + m_time.SetToCurrent(); + + // We have a checksum, check it if asked + if (length >= RADIO_HEADER_LENGTH_BYTES && check) { + CCCITTChecksumReverse cksum; + + cksum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U); + + m_valid = cksum.check(data + RADIO_HEADER_LENGTH_BYTES - 2U); + } +} + +CHeaderData::CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall, + const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1, + unsigned char flag2, unsigned char flag3) : +m_time(), +m_myCall1(myCall1), +m_myCall2(myCall2), +m_yourCall(yourCall), +m_rptCall1(rptCall1), +m_rptCall2(rptCall2), +m_flag1(flag1), +m_flag2(flag2), +m_flag3(flag3), +m_valid(true) +{ + m_time.SetToCurrent(); + + m_myCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_myCall2.Append(wxT(' '), SHORT_CALLSIGN_LENGTH); + m_yourCall.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_rptCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_rptCall2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + + m_myCall1.Truncate(LONG_CALLSIGN_LENGTH); + m_myCall2.Truncate(SHORT_CALLSIGN_LENGTH); + m_yourCall.Truncate(LONG_CALLSIGN_LENGTH); + m_rptCall1.Truncate(LONG_CALLSIGN_LENGTH); + m_rptCall2.Truncate(LONG_CALLSIGN_LENGTH); +} + +CHeaderData::~CHeaderData() +{ +} + +wxDateTime CHeaderData::getTime() const +{ + return m_time; +} + +wxString CHeaderData::getMyCall1() const +{ + return m_myCall1; +} + +wxString CHeaderData::getMyCall2() const +{ + return m_myCall2; +} + +wxString CHeaderData::getYourCall() const +{ + return m_yourCall; +} + +wxString CHeaderData::getRptCall1() const +{ + return m_rptCall1; +} + +wxString CHeaderData::getRptCall2() const +{ + return m_rptCall2; +} + +unsigned char CHeaderData::getFlag1() const +{ + return m_flag1; +} + +unsigned char CHeaderData::getFlag2() const +{ + return m_flag2; +} + +unsigned char CHeaderData::getFlag3() const +{ + return m_flag3; +} + +void CHeaderData::setMyCall1(const wxString& callsign) +{ + m_myCall1 = callsign; + m_myCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_myCall1.Truncate(LONG_CALLSIGN_LENGTH); +} + +void CHeaderData::setMyCall2(const wxString& callsign) +{ + m_myCall2 = callsign; + m_myCall2.Append(wxT(' '), SHORT_CALLSIGN_LENGTH); + m_myCall2.Truncate(SHORT_CALLSIGN_LENGTH); +} + +void CHeaderData::setYourCall(const wxString& callsign) +{ + m_yourCall = callsign; + m_yourCall.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_yourCall.Truncate(LONG_CALLSIGN_LENGTH); +} + +void CHeaderData::setRptCall1(const wxString& callsign) +{ + m_rptCall1 = callsign; + m_rptCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_rptCall1.Truncate(LONG_CALLSIGN_LENGTH); +} + +void CHeaderData::setRptCall2(const wxString& callsign) +{ + m_rptCall2 = callsign; + m_rptCall2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_rptCall2.Truncate(LONG_CALLSIGN_LENGTH); +} + +bool CHeaderData::isAck() const +{ + return (m_flag1 & ACK_FLAG) == ACK_FLAG; +} + +bool CHeaderData::isNoResponse() const +{ + return (m_flag1 & NO_RESPONSE) == NO_RESPONSE; +} + +bool CHeaderData::isRelayUnavailable() const +{ + return (m_flag1 & RELAY_UNAVAILABLE) == RELAY_UNAVAILABLE; +} + +void CHeaderData::setRepeaterMode(bool set) +{ + if (set) + m_flag1 |= REPEATER_MASK; + else + m_flag1 &= ~REPEATER_MASK; +} + +bool CHeaderData::isRepeaterMode() const +{ + return (m_flag1 & REPEATER_MASK) == REPEATER_MASK; +} + +void CHeaderData::setDataPacket(bool set) +{ + if (set) + m_flag1 |= DATA_MASK; + else + m_flag1 &= ~DATA_MASK; +} + +bool CHeaderData::isDataPacket() const +{ + return (m_flag1 & DATA_MASK) == DATA_MASK; +} + +void CHeaderData::setInterrupted(bool set) +{ + if (set) + m_flag1 |= INTERRUPTED_MASK; + else + m_flag1 &= ~INTERRUPTED_MASK; +} + +bool CHeaderData::isInterrupted() const +{ + return (m_flag1 & INTERRUPTED_MASK) == INTERRUPTED_MASK; +} + +void CHeaderData::setControlSignal(bool set) +{ + if (set) + m_flag1 |= CONTROL_SIGNAL_MASK; + else + m_flag1 &= ~CONTROL_SIGNAL_MASK; +} + +bool CHeaderData::isControlSignal() const +{ + return (m_flag1 & CONTROL_SIGNAL_MASK) == CONTROL_SIGNAL_MASK; +} + +void CHeaderData::setUrgent(bool set) +{ + if (set) + m_flag1 |= URGENT_MASK; + else + m_flag1 &= ~URGENT_MASK; +} + +bool CHeaderData::isUrgent() const +{ + return (m_flag1 & URGENT_MASK) == URGENT_MASK; +} + +void CHeaderData::setFlag1(unsigned char flag) +{ + m_flag1 = flag; +} + +void CHeaderData::setFlag2(unsigned char flag) +{ + m_flag2 = flag; +} + +void CHeaderData::setFlag3(unsigned char flag) +{ + m_flag3 = flag; +} + +void CHeaderData::setRepeaterFlags(unsigned char set) +{ + m_flag1 &= ~REPEATER_CONTROL_MASK; + m_flag1 |= set & REPEATER_CONTROL_MASK; +} + +unsigned char CHeaderData::getRepeaterFlags() const +{ + return m_flag1 & REPEATER_CONTROL_MASK; +} + +bool CHeaderData::isValid() const +{ + return m_valid; +} + +void CHeaderData::reset() +{ + m_myCall1 = wxT(" "); + m_myCall2 = wxT(" "); + m_yourCall = wxT("CQCQCQ "); + m_rptCall1 = wxT("DIRECT "); + m_rptCall2 = wxT("DIRECT "); + + m_flag1 = 0x00; + m_flag2 = 0x00; + m_flag3 = 0x00; +} + +CHeaderData& CHeaderData::operator=(const CHeaderData& header) +{ + if (&header != this) { + m_time = header.m_time; + m_myCall1 = header.m_myCall1; + m_myCall2 = header.m_myCall2; + m_yourCall = header.m_yourCall; + m_rptCall1 = header.m_rptCall1; + m_rptCall2 = header.m_rptCall2; + m_flag1 = header.m_flag1; + m_flag2 = header.m_flag2; + m_flag3 = header.m_flag3; + m_valid = header.m_valid; + } + + return *this; +} diff --git a/Common/HeaderData.h b/Common/HeaderData.h new file mode 100644 index 0000000..69207dd --- /dev/null +++ b/Common/HeaderData.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef HeaderData_H +#define HeaderData_H + +#include +#include + +class CHeaderData { +public: + CHeaderData(); + CHeaderData(const CHeaderData& header); + CHeaderData(const unsigned char* data, unsigned int length, bool check); + CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall, + const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1 = 0x00, + unsigned char flag2 = 0x00, unsigned char flag3 = 0x00); + ~CHeaderData(); + + wxDateTime getTime() const; + wxString getMyCall1() const; + wxString getMyCall2() const; + wxString getYourCall() const; + wxString getRptCall1() const; + wxString getRptCall2() const; + + unsigned char getFlag1() const; + unsigned char getFlag2() const; + unsigned char getFlag3() const; + + bool isAck() const; + bool isNoResponse() const; + bool isRelayUnavailable() const; + bool isRepeaterMode() const; + bool isDataPacket() const; + bool isInterrupted() const; + bool isControlSignal() const; + bool isUrgent() const; + unsigned char getRepeaterFlags() const; + + void setFlag1(unsigned char flag); + void setFlag2(unsigned char flag); + void setFlag3(unsigned char flag); + + void setMyCall1(const wxString& callsign); + void setMyCall2(const wxString& callsign); + void setYourCall(const wxString& callsign); + void setRptCall1(const wxString& callsign); + void setRptCall2(const wxString& callsign); + + void setRepeaterMode(bool set); + void setDataPacket(bool set); + void setInterrupted(bool set); + void setControlSignal(bool set); + void setUrgent(bool set); + void setRepeaterFlags(unsigned char set); + + bool isValid() const; + + void reset(); + + CHeaderData& operator=(const CHeaderData& header); + +private: + wxDateTime m_time; + wxString m_myCall1; + wxString m_myCall2; + wxString m_yourCall; + wxString m_rptCall1; + wxString m_rptCall2; + unsigned char m_flag1; + unsigned char m_flag2; + unsigned char m_flag3; + bool m_valid; +}; + +#endif diff --git a/Common/K8055Controller.cpp b/Common/K8055Controller.cpp new file mode 100644 index 0000000..6cbe8eb --- /dev/null +++ b/Common/K8055Controller.cpp @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2009,2010 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "K8055Controller.h" + +const unsigned int VELLEMAN_VENDOR_ID = 0x10CFU; +const unsigned int VELLEMAN_PRODUCT_ID = 0x5500U; + +const char REPORT_ID = 0x00U; + +const char CMD_RESET = 0x00U; +const char CMD_SET_ANALOG_DIGITAL = 0x05U; + +const char IN_PORT1 = 0x10U; +const char IN_PORT2 = 0x20U; +const char IN_PORT3 = 0x01U; +const char IN_PORT4 = 0x40U; +const char IN_PORT5 = 0x80U; + +const char OUT_PORT1 = 0x01U; +const char OUT_PORT2 = 0x02U; +const char OUT_PORT3 = 0x04U; +const char OUT_PORT4 = 0x08U; +const char OUT_PORT5 = 0x10U; +const char OUT_PORT6 = 0x20U; +const char OUT_PORT7 = 0x40U; +const char OUT_PORT8 = 0x80U; + +#if defined(__WINDOWS__) + +#include +#include + +const DWORD USB_BUFSIZE = 9UL; + +CK8055Controller::CK8055Controller(unsigned int address) : +m_address(address), +m_outp1(false), +m_outp2(false), +m_outp3(false), +m_outp4(false), +m_outp5(false), +m_outp6(false), +m_outp7(false), +m_outp8(false), +m_handle(INVALID_HANDLE_VALUE) +{ +} + +CK8055Controller::~CK8055Controller() +{ +} + +bool CK8055Controller::open() +{ + wxASSERT(m_handle == INVALID_HANDLE_VALUE); + + GUID guid; + ::HidD_GetHidGuid(&guid); + + HDEVINFO devInfo = ::SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + if (devInfo == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); + return false; + } + + SP_DEVICE_INTERFACE_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guid, index, &devInfoData); index++) { + // Find the required length of the device structure + DWORD length; + ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Get the detailed data into the newly allocated device structure + DWORD required; + BOOL res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); + if (!res) { + wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + // Get the handle for getting the attributes + HANDLE handle = ::CreateFile(detailData->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + HIDD_ATTRIBUTES attributes; + attributes.Size = sizeof(HIDD_ATTRIBUTES); + res = ::HidD_GetAttributes(handle, &attributes); + if (!res) { + wxLogError(wxT("Error from HidD_GetAttributes: err=%u"), ::GetLastError()); + ::CloseHandle(handle); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ::CloseHandle(handle); + + // Is this a Velleman K8055 and the right one? + if (attributes.VendorID == VELLEMAN_VENDOR_ID && attributes.ProductID == (VELLEMAN_PRODUCT_ID + m_address)) { + m_handle = ::CreateFile(detailData->DevicePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (m_handle == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + + setDigitalOutputs(false, false, false, false, false, false, false, false); + + return true; + } + + ::free(detailData); + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + + return false; +} + +void CK8055Controller::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + char buffer[USB_BUFSIZE]; + buffer[0] = REPORT_ID; + buffer[1] = 0x00U; + buffer[2] = 0x00U; + buffer[3] = 0x00U; + buffer[4] = 0x00U; + buffer[5] = 0x00U; + buffer[6] = 0x00U; + buffer[7] = 0x00U; + buffer[8] = 0x00U; + + DWORD read; + BOOL res = ::ReadFile(m_handle, buffer, USB_BUFSIZE, &read, NULL); + if (!res) { + wxLogError(wxT("Error from ReadFile: err=%u, read=%u"), ::GetLastError(), read); + return; + } + + // Do we have data? + if (buffer[2] != 0x00) { + inp1 = (buffer[1] & IN_PORT1) == IN_PORT1; + inp2 = (buffer[1] & IN_PORT2) == IN_PORT2; + inp3 = (buffer[1] & IN_PORT3) == IN_PORT3; + inp4 = (buffer[1] & IN_PORT4) == IN_PORT4; + inp5 = (buffer[1] & IN_PORT5) == IN_PORT5; + } +} + +void CK8055Controller::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + if (outp1 == m_outp1 && outp2 == m_outp2 && outp3 == m_outp3 && outp4 == m_outp4 && + outp5 == m_outp5 && outp6 == m_outp6 && outp7 == m_outp7 && outp8 == m_outp8) + return; + + char buffer[USB_BUFSIZE]; + buffer[0] = REPORT_ID; + buffer[1] = CMD_SET_ANALOG_DIGITAL; + buffer[2] = 0x00U; + buffer[3] = 0x00U; + buffer[4] = 0x00U; + buffer[5] = 0x00U; + buffer[6] = 0x00U; + buffer[7] = 0x00U; + buffer[8] = 0x00U; + + if (outp1) + buffer[2] |= OUT_PORT1; + if (outp2) + buffer[2] |= OUT_PORT2; + if (outp3) + buffer[2] |= OUT_PORT3; + if (outp4) + buffer[2] |= OUT_PORT4; + if (outp5) + buffer[2] |= OUT_PORT5; + if (outp6) + buffer[2] |= OUT_PORT6; + if (outp7) + buffer[2] |= OUT_PORT7; + if (outp8) + buffer[2] |= OUT_PORT8; + + DWORD written; + BOOL res = ::WriteFile(m_handle, buffer, USB_BUFSIZE, &written, NULL); + if (!res || written != USB_BUFSIZE) + wxLogError(wxT("Error from WriteFile: err=%u, written=%u"), ::GetLastError(), written); + + m_outp1 = outp1; + m_outp2 = outp2; + m_outp3 = outp3; + m_outp4 = outp4; + m_outp5 = outp5; + m_outp6 = outp6; + m_outp7 = outp7; + m_outp8 = outp8; +} + +void CK8055Controller::close() +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + setDigitalOutputs(false, false, false, false, false, false, false, false); + + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; +} + +#else + +const unsigned int VELLEMAN_HID_INTERFACE = 0U; + +const unsigned int USB_OUTPUT_ENDPOINT = 0x01U; +const unsigned int USB_INPUT_ENDPOINT = 0x81U; + +const int USB_BUFSIZE = 8; + +const int USB_TIMEOUT = 5000; + +CK8055Controller::CK8055Controller(unsigned int address) : +m_address(address), +m_outp1(false), +m_outp2(false), +m_outp3(false), +m_outp4(false), +m_outp5(false), +m_outp6(false), +m_outp7(false), +m_outp8(false), +m_context(NULL), +m_handle(NULL) +{ + ::libusb_init(&m_context); +} + +CK8055Controller::~CK8055Controller() +{ + wxASSERT(m_context != NULL); + + ::libusb_exit(m_context); +} + +bool CK8055Controller::open() +{ + wxASSERT(m_context != NULL); + wxASSERT(m_handle == NULL); + + m_handle = ::libusb_open_device_with_vid_pid(m_context, VELLEMAN_VENDOR_ID, VELLEMAN_PRODUCT_ID + m_address); + if (m_handle == NULL) { + wxLogError(wxT("Could not open the Velleman K8055")); + return false; + } + + int res = ::libusb_claim_interface(m_handle, VELLEMAN_HID_INTERFACE); + if (res != 0) { + res = ::libusb_detach_kernel_driver(m_handle, VELLEMAN_HID_INTERFACE); + if (res != 0) { + wxLogError(wxT("Error from libusb_detach_kernel_driver: err=%d"), res); + ::libusb_close(m_handle); + m_handle = NULL; + return false; + } + + res = ::libusb_claim_interface(m_handle, VELLEMAN_HID_INTERFACE); + if (res != 0) { + wxLogError(wxT("Error from libusb_claim_interface: err=%d"), res); + ::libusb_close(m_handle); + m_handle = NULL; + return false; + } + } + + ::libusb_set_configuration(m_handle, 1); + + setDigitalOutputs(false, false, false, false, false, false, false, false); + + return true; +} + +void CK8055Controller::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + wxASSERT(m_handle != NULL); + + unsigned char buffer[USB_BUFSIZE]; + buffer[0] = 0x00; + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0x00; + + int written; + int res = ::libusb_interrupt_transfer(m_handle, USB_INPUT_ENDPOINT, buffer, USB_BUFSIZE, &written, USB_TIMEOUT); + if (res != 0) { + wxLogError(wxT("Error from libusb_interrupt_transfer: err=%d"), res); + return; + } + + if (written != USB_BUFSIZE) + return; + + // Do we have data? + if (buffer[1] != 0x00) { + inp1 = (buffer[0] & IN_PORT1) == IN_PORT1; + inp2 = (buffer[0] & IN_PORT2) == IN_PORT2; + inp3 = (buffer[0] & IN_PORT3) == IN_PORT3; + inp4 = (buffer[0] & IN_PORT4) == IN_PORT4; + inp5 = (buffer[0] & IN_PORT5) == IN_PORT5; + } +} + +void CK8055Controller::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8) +{ + wxASSERT(m_handle != NULL); + + if (outp1 == m_outp1 && outp2 == m_outp2 && outp3 == m_outp3 && outp4 == m_outp4 && + outp5 == m_outp5 && outp6 == m_outp6 && outp7 == m_outp7 && outp8 == m_outp8) + return; + + unsigned char buffer[USB_BUFSIZE]; + buffer[0] = CMD_SET_ANALOG_DIGITAL; + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0x00; + + if (outp1) + buffer[1] |= OUT_PORT1; + if (outp2) + buffer[1] |= OUT_PORT2; + if (outp3) + buffer[1] |= OUT_PORT3; + if (outp4) + buffer[1] |= OUT_PORT4; + if (outp5) + buffer[1] |= OUT_PORT5; + if (outp6) + buffer[1] |= OUT_PORT6; + if (outp7) + buffer[1] |= OUT_PORT7; + if (outp8) + buffer[1] |= OUT_PORT8; + + int written; + int res = ::libusb_interrupt_transfer(m_handle, USB_OUTPUT_ENDPOINT, buffer, USB_BUFSIZE, &written, USB_TIMEOUT); + if (res != 0) { + wxLogError(wxT("Error from libusb_interrupt_transfer: err=%d"), res); + return; + } + + if (written != USB_BUFSIZE) + return; + + m_outp1 = outp1; + m_outp2 = outp2; + m_outp3 = outp3; + m_outp4 = outp4; + m_outp5 = outp5; + m_outp6 = outp6; + m_outp7 = outp7; + m_outp8 = outp8; +} + +void CK8055Controller::close() +{ + wxASSERT(m_handle != NULL); + + setDigitalOutputs(false, false, false, false, false, false, false, false); + + ::libusb_release_interface(m_handle, VELLEMAN_HID_INTERFACE); + + ::libusb_close(m_handle); + m_handle = NULL; +} + +#endif + diff --git a/Common/K8055Controller.h b/Common/K8055Controller.h new file mode 100644 index 0000000..619e1d9 --- /dev/null +++ b/Common/K8055Controller.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef K8055Controller_H +#define K8055Controller_H + +#include "HardwareController.h" + +#include + +#if defined(__WINDOWS__) +#include +#else +#include +#endif + +class CK8055Controller : public IHardwareController { +public: + CK8055Controller(unsigned int address); + virtual ~CK8055Controller(); + + virtual bool open(); + + virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5); + + virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8); + + virtual void close(); + +private: + unsigned int m_address; + bool m_outp1; + bool m_outp2; + bool m_outp3; + bool m_outp4; + bool m_outp5; + bool m_outp6; + bool m_outp7; + bool m_outp8; +#if defined(__WINDOWS__) + HANDLE m_handle; +#else + libusb_context* m_context; + libusb_device_handle* m_handle; +#endif +}; + +#endif diff --git a/Common/LogEvent.cpp b/Common/LogEvent.cpp new file mode 100644 index 0000000..2cd850c --- /dev/null +++ b/Common/LogEvent.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "LogEvent.h" + +CLogEvent::CLogEvent(const wxString& text, wxEventType type, int id) : +wxEvent(id, type), +m_text(text) +{ +} + +CLogEvent::CLogEvent(const CLogEvent& event) : +wxEvent(event), +m_text(event.m_text) +{ +} + +CLogEvent::~CLogEvent() +{ +} + +wxString CLogEvent::getText() const +{ + return m_text; +} + +wxEvent* CLogEvent::Clone() const +{ + return new CLogEvent(*this); +} diff --git a/Common/LogEvent.h b/Common/LogEvent.h new file mode 100644 index 0000000..4721989 --- /dev/null +++ b/Common/LogEvent.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef LogEvent_H +#define LogEvent_H + +#include + +class CLogEvent : public wxEvent { +public: + CLogEvent(const wxString& text, wxEventType type, int id = 0); + virtual ~CLogEvent(); + + virtual wxString getText() const; + + virtual wxEvent* Clone() const; + +protected: + CLogEvent(const CLogEvent& event); + +private: + wxString m_text; +}; + +#endif diff --git a/Common/Logger.cpp b/Common/Logger.cpp new file mode 100644 index 0000000..8edd3b1 --- /dev/null +++ b/Common/Logger.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2002,2003,2009,2011,2012,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Logger.h" + +CLogger::CLogger(const wxString& directory, const wxString& name) : +#if(defined(__WINDOWS__)) +m_day(0), +#endif +wxLog(), +m_name(name), +m_file(NULL), +m_fileName() +{ + m_file = new wxFFile; + + m_fileName.SetPath(directory); + m_fileName.SetExt(wxT("log")); + +#if(defined(__WINDOWS__)) + time_t timestamp; + ::time(×tamp); + struct tm* tm = ::gmtime(×tamp); + + wxString text; + text.Printf(wxT("%s-%04d-%02d-%02d"), m_name.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + + m_day = tm->tm_yday; + m_fileName.SetName(text); +#else + m_fileName.SetName(m_name); +#endif + + bool ret = m_file->Open(m_fileName.GetFullPath(), wxT("a+t")); + if (!ret) { + wxLogError(wxT("Cannot open %s file for appending"), m_fileName.GetFullPath().c_str()); + return; + } +} + +CLogger::~CLogger() +{ + wxASSERT(m_file != NULL); + + m_file->Close(); + delete m_file; +} + +void CLogger::DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info) +{ + wxASSERT(m_file != NULL); + wxASSERT(m_file->IsOpened()); + + wxString letter; + + switch (level) { + case wxLOG_FatalError: letter = wxT("F"); break; + case wxLOG_Error: letter = wxT("E"); break; + case wxLOG_Warning: letter = wxT("W"); break; + case wxLOG_Info: letter = wxT("I"); break; + case wxLOG_Message: letter = wxT("M"); break; + case wxLOG_Status: letter = wxT("M"); break; + case wxLOG_Trace: letter = wxT("T"); break; + case wxLOG_Debug: letter = wxT("D"); break; + default: letter = wxT("U"); break; + } + + struct tm* tm = ::gmtime(&info.timestamp); + + wxString message; + message.Printf(wxT("%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n"), letter.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msg.c_str()); + + logString(message, info.timestamp); + + if (level == wxLOG_FatalError) + ::abort(); +} + +void CLogger::logString(const wxString& msg, time_t timestamp) +{ + wxASSERT(m_file != NULL); + wxASSERT(m_file->IsOpened()); + +#if(defined(__WINDOWS__)) + struct tm* tm = ::gmtime(×tamp); + + int day = tm->tm_yday; + if (day != m_day) { + wxString text; + text.Printf(wxT("%s-%04d-%02d-%02d"), m_name.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + + m_day = day; + m_fileName.SetName(text); + + m_file->Close(); + + bool ret = m_file->Open(m_fileName.GetFullPath(), wxT("a+t")); + if (!ret) { + wxLogError(wxT("Cannot open %s file for appending"), m_fileName.GetFullPath().c_str()); + return; + } + } +#endif + + m_file->Write(msg); + m_file->Flush(); +} diff --git a/Common/Logger.h b/Common/Logger.h new file mode 100644 index 0000000..0d082f0 --- /dev/null +++ b/Common/Logger.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002,2003,2009,2011,2012,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Logger_H +#define Logger_H + +#include +#include +#include +#include + +class CLogger : public wxLog { +public: + CLogger(const wxString& directory, const wxString& name); + virtual ~CLogger(); + + virtual void DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info); + +private: + wxString m_name; + wxFFile* m_file; + wxFileName m_fileName; +#if(defined(__WINDOWS__)) + int m_day; +#endif + + void logString(const wxString& msg, time_t timestamp); +}; + +#endif diff --git a/Common/MMDVMController.cpp b/Common/MMDVMController.cpp new file mode 100644 index 0000000..9028b3b --- /dev/null +++ b/Common/MMDVMController.cpp @@ -0,0 +1,866 @@ +/* + * Copyright (C) 2011-2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CCITTChecksumReverse.h" +#include "MMDVMController.h" +#include "DStarDefines.h" +#include "Timer.h" + +#if defined(__WINDOWS__) +#include +#else +#include +#endif + +const unsigned char MMDVM_FRAME_START = 0xE0U; + +const unsigned char MMDVM_GET_VERSION = 0x00U; +const unsigned char MMDVM_GET_STATUS = 0x01U; +const unsigned char MMDVM_SET_CONFIG = 0x02U; + +const unsigned char MMDVM_DSTAR_HEADER = 0x10U; +const unsigned char MMDVM_DSTAR_DATA = 0x11U; +const unsigned char MMDVM_DSTAR_LOST = 0x12U; +const unsigned char MMDVM_DSTAR_EOT = 0x13U; + +const unsigned char MMDVM_ACK = 0x70U; +const unsigned char MMDVM_NAK = 0x7FU; + +const unsigned char MMDVM_DUMP = 0xF0U; +const unsigned char MMDVM_DEBUG1 = 0xF1U; +const unsigned char MMDVM_DEBUG2 = 0xF2U; +const unsigned char MMDVM_DEBUG3 = 0xF3U; +const unsigned char MMDVM_DEBUG4 = 0xF4U; +const unsigned char MMDVM_DEBUG5 = 0xF5U; + +const unsigned int MAX_RESPONSES = 30U; + +const unsigned int BUFFER_LENGTH = 200U; + +CMMDVMController::CMMDVMController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel) : +CModem(), +m_port(port), +m_path(path), +m_rxInvert(rxInvert), +m_txInvert(txInvert), +m_pttInvert(pttInvert), +m_txDelay(txDelay), +m_txLevel(txLevel), +m_rxLevel(rxLevel), +m_serial(port, SERIAL_115200, true), +m_buffer(NULL), +m_txData(1000U), +m_rx(false) +{ + wxASSERT(!port.IsEmpty()); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CMMDVMController::~CMMDVMController() +{ + delete[] m_buffer; +} + +bool CMMDVMController::start() +{ + findPort(); + + bool ret = openModem(); + if (!ret) + return false; + + findPath(); + + Create(); + SetPriority(100U); + Run(); + + return true; +} + +void* CMMDVMController::Entry() +{ + wxLogMessage(wxT("Starting MMDVM Controller thread")); + + // Clock every 5ms-ish + CTimer pollTimer(200U, 0U, 100U); + pollTimer.start(); + + unsigned char writeType = DSMTT_NONE; + unsigned char writeLength = 0U; + unsigned char* writeBuffer = new unsigned char[BUFFER_LENGTH]; + + unsigned int space = 0U; + + while (!m_stopped) { + // Poll the modem status every 100ms + if (pollTimer.hasExpired()) { + bool ret = readStatus(); + if (!ret) { + ret = findModem(); + if (!ret) { + wxLogError(wxT("Stopping MMDVM Controller thread")); + return NULL; + } + } + + pollTimer.start(); + } + + unsigned int length; + RESP_TYPE_MMDVM type = getResponse(m_buffer, length); + + switch (type) { + case RTDVM_TIMEOUT: + break; + + case RTDVM_ERROR: { + bool ret = findModem(); + if (!ret) { + wxLogError(wxT("Stopping MMDVM Controller thread")); + return NULL; + } + } + break; + + case RTDVM_DSTAR_HEADER: { + // CUtils::dump(wxT("RT_DSTAR_HEADER"), m_buffer, length); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 3U, RADIO_HEADER_LENGTH_BYTES); + + m_rx = true; + } + break; + + case RTDVM_DSTAR_DATA: { + // CUtils::dump(wxT("RT_DSTAR_DATA"), m_buffer, length); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_buffer + 3U, DV_FRAME_LENGTH_BYTES); + + m_rx = true; + } + break; + + case RTDVM_DSTAR_EOT: { + // wxLogMessage(wxT("RT_DSTAR_EOT")); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + break; + + case RTDVM_DSTAR_LOST: { + // wxLogMessage(wxT("RT_DSTAR_LOST")); + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_LOST; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + break; + + case RTDVM_GET_STATUS: { + bool dstar = (m_buffer[3U] & 0x01U) == 0x01U; + if (!dstar) { + wxLogError(wxT("D-Star not enabled in the MMDVM!!!")); + wxLogError(wxT("Stopping MMDVM Controller thread")); + return NULL; + } + + m_tx = (m_buffer[5U] & 0x01U) == 0x01U; + + bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U; + if (adcOverflow) + wxLogWarning(wxT("MMDVM ADC levels have overflowed")); + + space = m_buffer[6U]; + // CUtils::dump(wxT("GET_STATUS"), m_buffer, length); + // wxLogMessage(wxT("PTT=%d space=%u"), int(m_tx), space); + } + break; + + // These should not be received in this loop, but don't complain if we do + case RTDVM_GET_VERSION: + case RTDVM_ACK: + break; + + case RTDVM_NAK: { + switch (m_buffer[3U]) { + case MMDVM_DSTAR_HEADER: + wxLogWarning(wxT("Received a header NAK from the MMDVM, reason = %u"), m_buffer[4U]); + break; + case MMDVM_DSTAR_DATA: + wxLogWarning(wxT("Received a data NAK from the MMDVM, reason = %u"), m_buffer[4U]); + break; + case MMDVM_DSTAR_EOT: + wxLogWarning(wxT("Received an EOT NAK from the MMDVM, reason = %u"), m_buffer[4U]); + break; + default: + wxLogWarning(wxT("Received a NAK from the MMDVM, command = 0x%02X, reason = %u"), m_buffer[3U], m_buffer[4U]); + break; + } + } + break; + + case RTDVM_DUMP: + CUtils::dump(wxT("Modem dump"), m_buffer + 3U, length - 3U); + break; + + case RTDVM_DEBUG1: + case RTDVM_DEBUG2: + case RTDVM_DEBUG3: + case RTDVM_DEBUG4: + case RTDVM_DEBUG5: + printDebug(); + break; + + default: + wxLogMessage(wxT("Unknown message, type: %02X"), m_buffer[2U]); + CUtils::dump(wxT("Buffer dump"), m_buffer, length); + break; + } + + if (writeType == DSMTT_NONE && m_txData.hasData()) { + wxMutexLocker locker(m_mutex); + + m_txData.getData(&writeType, 1U); + m_txData.getData(&writeLength, 1U); + m_txData.getData(writeBuffer, writeLength); + } + + if (space > 4U && writeType == DSMTT_HEADER) { + // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing the header to the MMDVM")); + + writeType = DSMTT_NONE; + space -= 4U; + } + + if (space > 1U && (writeType == DSMTT_DATA || writeType == DSMTT_EOT)) { + // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + + int ret = m_serial.write(writeBuffer, writeLength); + if (ret != int(writeLength)) + wxLogWarning(wxT("Error when writing data to the MMDVM")); + + writeType = DSMTT_NONE; + space--; + } + + Sleep(5UL); + + pollTimer.clock(); + } + + wxLogMessage(wxT("Stopping MMDVM Controller thread")); + + delete[] writeBuffer; + + m_serial.close(); + + return NULL; +} + +bool CMMDVMController::writeHeader(const CHeaderData& header) +{ + bool ret = m_txData.hasSpace(46U); + if (!ret) { + wxLogWarning(wxT("No space to write the header")); + return false; + } + + unsigned char buffer[50U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = RADIO_HEADER_LENGTH_BYTES + 3U; + buffer[2U] = MMDVM_DSTAR_HEADER; + + ::memset(buffer + 3U, ' ', RADIO_HEADER_LENGTH_BYTES); + + buffer[3U] = header.getFlag1(); + buffer[4U] = header.getFlag2(); + buffer[5U] = header.getFlag3(); + + wxString rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 6U] = rpt2.GetChar(i); + + wxString rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 14U] = rpt1.GetChar(i); + + wxString your = header.getYourCall(); + for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 22U] = your.GetChar(i); + + wxString my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 30U] = my1.GetChar(i); + + wxString my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 38U] = my2.GetChar(i); + + CCCITTChecksumReverse cksum1; + cksum1.update(buffer + 3U, RADIO_HEADER_LENGTH_BYTES - 2U); + cksum1.result(buffer + 42U); + + wxMutexLocker locker(m_mutex); + + unsigned char type = DSMTT_HEADER; + m_txData.addData(&type, 1U); + + unsigned char len = RADIO_HEADER_LENGTH_BYTES + 3U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, RADIO_HEADER_LENGTH_BYTES + 3U); + + return true; +} + +bool CMMDVMController::writeData(const unsigned char* data, unsigned int length, bool end) +{ + bool ret = m_txData.hasSpace(17U); + if (!ret) { + wxLogWarning(wxT("No space to write data")); + return false; + } + + unsigned char buffer[20U]; + + if (end) { + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 3U; + buffer[2U] = MMDVM_DSTAR_EOT; + + wxMutexLocker locker(m_mutex); + + unsigned char type = DSMTT_EOT; + m_txData.addData(&type, 1U); + + unsigned char len = 3U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, 3U); + + return true; + } + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = DV_FRAME_LENGTH_BYTES + 3U; + buffer[2U] = MMDVM_DSTAR_DATA; + ::memcpy(buffer + 3U, data, DV_FRAME_LENGTH_BYTES); + + wxMutexLocker locker(m_mutex); + + unsigned char type = DSMTT_DATA; + m_txData.addData(&type, 1U); + + unsigned char len = DV_FRAME_LENGTH_BYTES + 3U; + m_txData.addData(&len, 1U); + + m_txData.addData(buffer, DV_FRAME_LENGTH_BYTES + 3U); + + return true; +} + +unsigned int CMMDVMController::getSpace() +{ + return m_txData.freeSpace() / (DV_FRAME_LENGTH_BYTES + 5U); +} + +bool CMMDVMController::isTXReady() +{ + if (m_tx) + return false; + + return m_txData.isEmpty(); +} + +bool CMMDVMController::readVersion() +{ + ::wxSleep(2); + + for (unsigned int i = 0U; i < 6U; i++) { + unsigned char buffer[3U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 3U; + buffer[2U] = MMDVM_GET_VERSION; + + // CUtils::dump(wxT("Written"), buffer, 3U); + + int ret = m_serial.write(buffer, 3U); + if (ret != 3) + return false; + + for (unsigned int count = 0U; count < MAX_RESPONSES; count++) { + ::wxMilliSleep(10UL); + + unsigned int length; + RESP_TYPE_MMDVM resp = getResponse(m_buffer, length); + if (resp == RTDVM_GET_VERSION) { + wxString description((char*)(m_buffer + 4U), wxConvLocal, length - 4U); + + wxLogInfo(wxT("MMDVM protocol version: %u, description: %s"), m_buffer[3U], description.c_str()); + + return true; + } + } + + ::wxSleep(1); + } + + wxLogError(wxT("Unable to read the firmware version after six attempts")); + + return false; +} + +bool CMMDVMController::readStatus() +{ + unsigned char buffer[3U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 3U; + buffer[2U] = MMDVM_GET_STATUS; + + return m_serial.write(buffer, 3U) == 3; +} + +bool CMMDVMController::setConfig() +{ + unsigned char buffer[25U]; + + buffer[0U] = MMDVM_FRAME_START; + + buffer[1U] = 19U; + + buffer[2U] = MMDVM_SET_CONFIG; + + buffer[3U] = 0x00U; + if (m_rxInvert) + buffer[3U] |= 0x01U; + if (m_txInvert) + buffer[3U] |= 0x02U; + if (m_pttInvert) + buffer[3U] |= 0x04U; + + buffer[4U] = 0x01U; // D-Star only + + buffer[5U] = m_txDelay / 10U; // In 10ms units + + buffer[6U] = 1U; // STATE_DSTAR + + buffer[7U] = (m_rxLevel * 255U) / 100U; + buffer[8U] = (m_txLevel * 255U) / 100U; + + buffer[9U] = 0U; // DMR Color Code + + buffer[10U] = 0U; // DMR Delay + + buffer[11U] = 0U; // Osc. Offset + + buffer[12U] = (m_txLevel * 255U) / 100U; + buffer[13U] = (m_txLevel * 255U) / 100U; + buffer[14U] = (m_txLevel * 255U) / 100U; + buffer[15U] = (m_txLevel * 255U) / 100U; + + buffer[16U] = 128U; + buffer[17U] = 128U; + + buffer[18U] = (m_txLevel * 255U) / 100U; + + // CUtils::dump(wxT("Written"), buffer, 19U); + + int ret = m_serial.write(buffer, 19U); + if (ret != 19U) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_MMDVM resp; + do { + ::wxMilliSleep(10UL); + + resp = getResponse(m_buffer, length); + + if (resp != RTDVM_ACK && resp != RTDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + wxLogError(wxT("The MMDVM is not responding to the SET_CONFIG command")); + return false; + } + } + } while (resp != RTDVM_ACK && resp != RTDVM_NAK); + + // CUtils::dump(wxT("Response"), m_buffer, length); + + if (resp == RTDVM_NAK) { + wxLogError(wxT("Received a NAK to the SET_CONFIG command from the modem")); + return false; + } + + return true; +} + +RESP_TYPE_MMDVM CMMDVMController::getResponse(unsigned char *buffer, unsigned int& length) +{ + // Get the start of the frame or nothing at all + int ret = m_serial.read(buffer + 0U, 1U); + if (ret < 0) { + wxLogError(wxT("Error when reading from the MMDVM")); + return RTDVM_ERROR; + } + + if (ret == 0) + return RTDVM_TIMEOUT; + + if (buffer[0U] != MMDVM_FRAME_START) + return RTDVM_TIMEOUT; + + ret = m_serial.read(buffer + 1U, 1U); + if (ret < 0) { + wxLogError(wxT("Error when reading from the MMDVM")); + return RTDVM_ERROR; + } + + if (ret == 0) + return RTDVM_TIMEOUT; + + length = buffer[1U]; + + if (length >= 100U) { + wxLogError(wxT("Invalid data received from the MMDVM")); + CUtils::dump(wxT("Data"), buffer, 2U); + return RTDVM_TIMEOUT; + } + + unsigned int offset = 2U; + + while (offset < length) { + int ret = m_serial.read(buffer + offset, length - offset); + if (ret < 0) { + wxLogError(wxT("Error when reading from the MMDVM")); + return RTDVM_ERROR; + } + + if (ret > 0) + offset += ret; + + if (ret == 0) + Sleep(5UL); + } + + // CUtils::dump(wxT("Received"), buffer, length); + + switch (buffer[2U]) { + case MMDVM_GET_STATUS: + return RTDVM_GET_STATUS; + case MMDVM_GET_VERSION: + return RTDVM_GET_VERSION; + case MMDVM_DSTAR_HEADER: + return RTDVM_DSTAR_HEADER; + case MMDVM_DSTAR_DATA: + return RTDVM_DSTAR_DATA; + case MMDVM_DSTAR_EOT: + return RTDVM_DSTAR_EOT; + case MMDVM_DSTAR_LOST: + return RTDVM_DSTAR_LOST; + case MMDVM_ACK: + return RTDVM_ACK; + case MMDVM_NAK: + return RTDVM_NAK; + case MMDVM_DUMP: + return RTDVM_DUMP; + case MMDVM_DEBUG1: + return RTDVM_DEBUG1; + case MMDVM_DEBUG2: + return RTDVM_DEBUG2; + case MMDVM_DEBUG3: + return RTDVM_DEBUG3; + case MMDVM_DEBUG4: + return RTDVM_DEBUG4; + case MMDVM_DEBUG5: + return RTDVM_DEBUG5; + default: + return RTDVM_UNKNOWN; + } +} + +wxString CMMDVMController::getPath() const +{ + return m_path; +} + +bool CMMDVMController::findPort() +{ + if (m_path.IsEmpty()) + return false; + +#if defined(__WINDOWS__) +#else + wxDir dir; + bool ret1 = dir.Open(wxT("/sys/class/tty")); + if (!ret1) { + wxLogError(wxT("Cannot open directory /sys/class/tty")); + return false; + } + + wxString fileName; + ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); + while (ret1) { + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + ::strcat(cpath, "/device"); + ret2 = ::readlink(cpath, symlink, 255U); + if (ret2 < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret2); + } else { + // Get all but the last section + wxString fullPath = wxString(symlink, wxConvLocal, ret2); + path = fullPath.BeforeLast(wxT('/')); + } + + if (path.IsSameAs(m_path)) { + m_port.Printf(wxT("/dev/%s"), fileName.c_str()); + + wxLogMessage(wxT("Found modem port of %s based on the path"), m_port.c_str()); + + return true; + } + + ret1 = dir.GetNext(&fileName); + } +#endif + + return false; +} + +bool CMMDVMController::findPath() +{ +#if defined(__WINDOWS__) +#ifdef notdef + GUID guids[5U]; + + DWORD count; + BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); + if (!res) { + wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); + return false; + } + + for (DWORD i = 0U; i < count; i++) { + HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + if (devInfo == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); + return false; + } + + SP_DEVICE_INTERFACE_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { + // Find the required length of the device structure + DWORD length; + ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Get the detailed data into the newly allocated device structure + DWORD required; + res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); + if (!res) { + wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ::free(detailData); + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + } + + return false; +#endif +#else + wxString path; + path.Printf(wxT("/sys/class/tty/%s"), m_port.Mid(5U).c_str()); + + char cpath[255U]; + ::memset(cpath, 0x00U, 255U); + + for (unsigned int i = 0U; i < path.Len(); i++) + cpath[i] = path.GetChar(i); + + char symlink[255U]; + int ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + ::strcat(cpath, "/device"); + ret = ::readlink(cpath, symlink, 255U); + if (ret < 0) { + wxLogError(wxT("Error from readlink()")); + return false; + } + + path = wxString(symlink, wxConvLocal, ret); + } else { + wxString fullPath = wxString(symlink, wxConvLocal, ret); + path = fullPath.BeforeLast(wxT('/')); + } + + if (m_path.IsEmpty()) + wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + + m_path = path; +#endif + + return true; +} + +bool CMMDVMController::findModem() +{ + m_serial.close(); + + // Tell the repeater that the signal has gone away + if (m_rx) { + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rx = false; + } + + unsigned int count = 0U; + + // Purge the transmit buffer every 500ms to avoid overflow, but only try and reopen the modem every 2s + while (!m_stopped) { + count++; + if (count >= 4U) { + wxLogMessage(wxT("Trying to reopen the modem")); + + bool ret = findPort(); + if (ret) { + ret = openModem(); + if (ret) + return true; + } + + count = 0U; + } + + Sleep(500UL); + } + + return false; +} + +bool CMMDVMController::openModem() +{ + bool ret = m_serial.open(); + if (!ret) + return false; + + ret = readVersion(); + if (!ret) { + m_serial.close(); + return false; + } + + ret = setConfig(); + if (!ret) { + m_serial.close(); + return false; + } + + return true; +} + +void CMMDVMController::printDebug() +{ + unsigned int length = m_buffer[1U]; + if (m_buffer[2U] == 0xF1U) { + wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 3U); + wxLogMessage(wxT("Debug: %s"), message.c_str()); + } else if (m_buffer[2U] == 0xF2U) { + wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 5U); + short val1 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; + wxLogMessage(wxT("Debug: %s %d"), message.c_str(), val1); + } else if (m_buffer[2U] == 0xF3U) { + wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 7U); + short val1 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U]; + short val2 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; + wxLogMessage(wxT("Debug: %s %d %d"), message.c_str(), val1, val2); + } else if (m_buffer[2U] == 0xF4U) { + wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 9U); + short val1 = (m_buffer[length - 6U] << 8) | m_buffer[length - 5U]; + short val2 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U]; + short val3 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; + wxLogMessage(wxT("Debug: %s %d %d %d"), message.c_str(), val1, val2, val3); + } else if (m_buffer[2U] == 0xF5U) { + wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 11U); + short val1 = (m_buffer[length - 8U] << 8) | m_buffer[length - 7U]; + short val2 = (m_buffer[length - 6U] << 8) | m_buffer[length - 5U]; + short val3 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U]; + short val4 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; + wxLogMessage(wxT("Debug: %s %d %d %d %d"), message.c_str(), val1, val2, val3, val4); + } +} diff --git a/Common/MMDVMController.h b/Common/MMDVMController.h new file mode 100644 index 0000000..437d711 --- /dev/null +++ b/Common/MMDVMController.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef MMDVMController_H +#define MMDVMController_H + +#include "SerialDataController.h" +#include "RingBuffer.h" +#include "Modem.h" +#include "Utils.h" + +#include + +enum RESP_TYPE_MMDVM { + RTDVM_TIMEOUT, + RTDVM_ERROR, + RTDVM_UNKNOWN, + RTDVM_GET_STATUS, + RTDVM_GET_VERSION, + RTDVM_DSTAR_HEADER, + RTDVM_DSTAR_DATA, + RTDVM_DSTAR_EOT, + RTDVM_DSTAR_LOST, + RTDVM_ACK, + RTDVM_NAK, + RTDVM_DUMP, + RTDVM_DEBUG1, + RTDVM_DEBUG2, + RTDVM_DEBUG3, + RTDVM_DEBUG4, + RTDVM_DEBUG5 +}; + +class CMMDVMController : public CModem { +public: + CMMDVMController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel); + virtual ~CMMDVMController(); + + virtual void* Entry(); + + virtual bool start(); + + virtual unsigned int getSpace(); + virtual bool isTXReady(); + + virtual bool writeHeader(const CHeaderData& header); + virtual bool writeData(const unsigned char* data, unsigned int length, bool end); + + virtual wxString getPath() const; + +private: + wxString m_port; + wxString m_path; + bool m_rxInvert; + bool m_txInvert; + bool m_pttInvert; + unsigned int m_txDelay; + unsigned int m_rxLevel; + unsigned int m_txLevel; + CSerialDataController m_serial; + unsigned char* m_buffer; + CRingBuffer m_txData; + bool m_rx; + + bool readVersion(); + bool readStatus(); + bool setConfig(); + + RESP_TYPE_MMDVM getResponse(unsigned char* buffer, unsigned int& length); + + bool findPort(); + bool findPath(); + + bool findModem(); + bool openModem(); + + void printDebug(); +}; + +#endif + diff --git a/Common/Modem.cpp b/Common/Modem.cpp new file mode 100644 index 0000000..524e15d --- /dev/null +++ b/Common/Modem.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarDefines.h" +#include "Modem.h" + +const unsigned int BUFFER_LENGTH = 200U; + +CModem::CModem() : +wxThread(wxTHREAD_JOINABLE), +m_rxData(1000U), +m_mutex(), +m_tx(false), +m_stopped(false), +m_readType(DSMTT_NONE), +m_readLength(0U), +m_readBuffer(NULL) +{ + m_readBuffer = new unsigned char[BUFFER_LENGTH]; +} + +CModem::~CModem() +{ + delete[] m_readBuffer; +} + +bool CModem::isTX() +{ + return m_tx; +} + +DSMT_TYPE CModem::read() +{ + m_readLength = 0U; + + if (m_rxData.isEmpty()) + return DSMTT_NONE; + + wxMutexLocker locker(m_mutex); + + unsigned char hdr[2U]; + m_rxData.getData(hdr, 2U); + + m_readType = DSMT_TYPE(hdr[0U]); + m_readLength = hdr[1U]; + m_rxData.getData(m_readBuffer, m_readLength); + + return m_readType; +} + +CHeaderData* CModem::readHeader() +{ + if (m_readType != DSMTT_HEADER) + return NULL; + + return new CHeaderData(m_readBuffer, RADIO_HEADER_LENGTH_BYTES, false); +} + +unsigned int CModem::readData(unsigned char* data, unsigned int length) +{ + if (m_readType != DSMTT_DATA) + return 0U; + + if (length < m_readLength) { + ::memcpy(data, m_readBuffer, length); + return length; + } else { + ::memcpy(data, m_readBuffer, m_readLength); + return m_readLength; + } +} + +void CModem::stop() +{ + m_stopped = true; + + Wait(); +} + diff --git a/Common/Modem.h b/Common/Modem.h new file mode 100644 index 0000000..2351367 --- /dev/null +++ b/Common/Modem.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Modem_H +#define Modem_H + +#include "HeaderData.h" +#include "RingBuffer.h" + +#include + +enum DSMT_TYPE { + DSMTT_NONE, + DSMTT_START, + DSMTT_HEADER, + DSMTT_DATA, + DSMTT_EOT, + DSMTT_LOST +}; + +class CModem : public wxThread { +public: + CModem(); + virtual ~CModem(); + + virtual bool start() = 0; + + virtual bool writeHeader(const CHeaderData& header) = 0; + virtual bool writeData(const unsigned char* data, unsigned int length, bool end) = 0; + + virtual unsigned int getSpace() = 0; + virtual bool isTXReady() = 0; + virtual bool isTX(); + + virtual DSMT_TYPE read(); + virtual CHeaderData* readHeader(); + virtual unsigned int readData(unsigned char* data, unsigned int length); + + virtual void stop(); + +protected: + CRingBuffer m_rxData; + wxMutex m_mutex; + bool m_tx; + bool m_stopped; + +private: + DSMT_TYPE m_readType; + unsigned int m_readLength; + unsigned char* m_readBuffer; +}; + +#endif diff --git a/Common/OutputQueue.cpp b/Common/OutputQueue.cpp new file mode 100644 index 0000000..c1da6c7 --- /dev/null +++ b/Common/OutputQueue.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2011,2013,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "OutputQueue.h" + +COutputQueue::COutputQueue(unsigned int space, unsigned int threshold) : +m_data(space), +m_threshold(threshold), +m_header(NULL), +m_count(0U) +{ + wxASSERT(space > 0U); + wxASSERT(threshold > 0U); +} + +COutputQueue::~COutputQueue() +{ + delete m_header; +} + +void COutputQueue::setHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + delete m_header; + + m_header = header; +} + +CHeaderData* COutputQueue::getHeader() +{ + CHeaderData* header = m_header; + + m_header = NULL; + + return header; +} + +unsigned int COutputQueue::getData(unsigned char *data, unsigned int length, bool& end) +{ + wxASSERT(data != NULL); + + if (m_data.isEmpty()) { + end = false; + return 0U; + } + + unsigned char hdr[2U]; + m_data.getData(hdr, 2U); + + end = hdr[0U] == 1U; + + if (length < hdr[1U]) { + wxLogWarning(wxT("Output buffer is too short, %u < %u"), length, hdr[1U]); + + unsigned int len = m_data.getData(data, length); + + // Purge the excess data + unsigned int n = hdr[1U] - length; + unsigned char c; + for (unsigned int i = 0U; i < n; i++) + m_data.getData(&c, 1U); + + return len; + } else { + return m_data.getData(data, hdr[1U]); + } +} + +unsigned int COutputQueue::addData(const unsigned char *data, unsigned int length, bool end) +{ + wxASSERT(data != NULL); + + bool ret = m_data.hasSpace(length + 2U); + if (!ret) { + // XXX wxLogWarning(wxT("Not enough space in the output queue")); + return 0U; + } + + unsigned char hdr[2U]; + hdr[0U] = end ? 1U : 0U; + hdr[1U] = length; + m_data.addData(hdr, 2U); + + if (end) + m_count = m_threshold; + else + m_count++; + + return m_data.addData(data, length); +} + +bool COutputQueue::headerReady() const +{ + return m_header != NULL && m_count >= m_threshold; +} + +bool COutputQueue::dataReady() const +{ + return m_header == NULL && m_count >= m_threshold; +} + +bool COutputQueue::isEmpty() +{ + return m_header == NULL && m_data.isEmpty(); +} + +void COutputQueue::reset() +{ + m_data.clear(); + + delete m_header; + m_header = NULL; + + m_count = 0U; +} + +void COutputQueue::setThreshold(unsigned int threshold) +{ + wxASSERT(threshold > 0U); + + m_threshold = threshold; +} diff --git a/Common/OutputQueue.h b/Common/OutputQueue.h new file mode 100644 index 0000000..f4f5e8b --- /dev/null +++ b/Common/OutputQueue.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef OutputQueue_H +#define OutputQueue_H + +#include "RingBuffer.h" +#include "HeaderData.h" + +#include + +class COutputQueue { +public: + COutputQueue(unsigned int space, unsigned int threshold); + ~COutputQueue(); + + void setHeader(CHeaderData* header); + CHeaderData* getHeader(); + + unsigned int getData(unsigned char* data, unsigned int length, bool& end); + unsigned int addData(const unsigned char* data, unsigned int length, bool end); + + bool headerReady() const; + bool dataReady() const; + + bool isEmpty(); + + void reset(); + + void setThreshold(unsigned int threshold); + +private: + CRingBuffer m_data; + unsigned int m_threshold; + CHeaderData* m_header; + unsigned int m_count; +}; + +#endif diff --git a/Common/RepeaterProtocolHandler.cpp b/Common/RepeaterProtocolHandler.cpp new file mode 100644 index 0000000..2e9e963 --- /dev/null +++ b/Common/RepeaterProtocolHandler.cpp @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2009-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "RepeaterProtocolHandler.h" +#include "CCITTChecksumReverse.h" +#include "DStarDefines.h" +#include "Utils.h" + +// #define DUMP_TX + +const unsigned int BUFFER_LENGTH = 255U; + +CRepeaterProtocolHandler::CRepeaterProtocolHandler(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name) : +m_socket(localAddress, localPort), +m_address(), +m_port(gatewayPort), +m_name(name), +m_outId(0U), +m_outSeq(0U), +m_type(NETWORK_NONE), +m_inId(0U), +m_buffer(NULL), +m_length(0U) +{ + m_address = CUDPReaderWriter::lookup(gatewayAddress); + + m_buffer = new unsigned char[BUFFER_LENGTH]; + + wxDateTime now = wxDateTime::UNow(); + ::srand(now.GetMillisecond()); +} + +CRepeaterProtocolHandler::~CRepeaterProtocolHandler() +{ + delete[] m_buffer; +} + +bool CRepeaterProtocolHandler::open() +{ + if (m_address.s_addr == INADDR_NONE) + return false; + + return m_socket.open(); +} + +bool CRepeaterProtocolHandler::writeHeader(const CHeaderData& header) +{ + unsigned char buffer[50U]; + + buffer[0] = 'D'; + buffer[1] = 'S'; + buffer[2] = 'R'; + buffer[3] = 'P'; + + buffer[4] = 0x20U; + + // Create a random id for this transmission + m_outId = (::rand() % 65535U) + 1U; + + buffer[5] = m_outId / 256U; // Unique session id + buffer[6] = m_outId % 256U; + + buffer[7] = 0U; + + buffer[8] = header.getFlag1(); + buffer[9] = header.getFlag2(); + buffer[10] = header.getFlag3(); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[11 + i] = header.getRptCall1().GetChar(i); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[19 + i] = header.getRptCall2().GetChar(i); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[27 + i] = header.getYourCall().GetChar(i); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[35 + i] = header.getMyCall1().GetChar(i); + + for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++) + buffer[43 + i] = header.getMyCall2().GetChar(i); + + // Get the checksum for the header + CCCITTChecksumReverse csum; + csum.update(buffer + 8U, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U); + csum.result(buffer + 47U); + + m_outSeq = 0U; + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Header"), buffer, 49U); +#endif + + for (unsigned int i = 0U; i < 2U; i++) { + bool ret = m_socket.write(buffer, 49U, m_address, m_port); + if (!ret) + return false; + } + + return true; +} + +bool CRepeaterProtocolHandler::writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end) +{ + wxASSERT(data != NULL); + wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES); + + unsigned char buffer[30U]; + + buffer[0] = 'D'; + buffer[1] = 'S'; + buffer[2] = 'R'; + buffer[3] = 'P'; + + buffer[4] = 0x21U; + + buffer[5] = m_outId / 256U; // Unique session id + buffer[6] = m_outId % 256U; + + // If this is a data sync, reset the sequence to zero + if (data[9] == 0x55 && data[10] == 0x2D && data[11] == 0x16) + m_outSeq = 0U; + + buffer[7] = m_outSeq; + if (end) + buffer[7] |= 0x40U; // End of data marker + + buffer[8] = errors; + + m_outSeq++; + if (m_outSeq > 0x14U) + m_outSeq = 0U; + + ::memcpy(buffer + 9U, data, length); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Data"), buffer, length + 9U); +#endif + + return m_socket.write(buffer, length + 9U, m_address, m_port); +} + +bool CRepeaterProtocolHandler::writeBusyHeader(const CHeaderData& header) +{ + unsigned char buffer[50U]; + + buffer[0] = 'D'; + buffer[1] = 'S'; + buffer[2] = 'R'; + buffer[3] = 'P'; + + buffer[4] = 0x22U; + + // Create a random id for this header + m_outId = (::rand() % 65535U) + 1U; + + buffer[5] = m_outId / 256U; // Unique session id + buffer[6] = m_outId % 256U; + + buffer[7] = 0U; + + buffer[8] = header.getFlag1(); + buffer[9] = header.getFlag2(); + buffer[10] = header.getFlag3(); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[11 + i] = header.getRptCall1().GetChar(i); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[19 + i] = header.getRptCall2().GetChar(i); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[27 + i] = header.getYourCall().GetChar(i); + + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) + buffer[35 + i] = header.getMyCall1().GetChar(i); + + for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++) + buffer[43 + i] = header.getMyCall2().GetChar(i); + + // Get the checksum for the header + CCCITTChecksumReverse csum; + csum.update(buffer + 8U, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U); + csum.result(buffer + 47U); + + m_outSeq = 0U; + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Busy Header"), buffer, 49U); +#endif + + return m_socket.write(buffer, 49U, m_address, m_port); +} + +bool CRepeaterProtocolHandler::writeBusyData(const unsigned char* data, unsigned int length, unsigned int errors, bool end) +{ + wxASSERT(data != NULL); + wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES); + + unsigned char buffer[30U]; + + buffer[0] = 'D'; + buffer[1] = 'S'; + buffer[2] = 'R'; + buffer[3] = 'P'; + + buffer[4] = 0x23U; + + buffer[5] = m_outId / 256U; // Unique session id + buffer[6] = m_outId % 256U; + + // If this is a data sync, reset the sequence to zero + if (data[9] == 0x55 && data[10] == 0x2D && data[11] == 0x16) + m_outSeq = 0U; + + buffer[7] = m_outSeq; + if (end) + buffer[7] |= 0x40U; // End of data marker + + buffer[8] = errors; + + m_outSeq++; + if (m_outSeq > 0x14U) + m_outSeq = 0U; + + ::memcpy(buffer + 9U, data, length); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Busy Data"), buffer, length + 9U); +#endif + + return m_socket.write(buffer, length + 9U, m_address, m_port); +} + +bool CRepeaterProtocolHandler::writePoll(const wxString& text) +{ + unsigned char buffer[40U]; + + buffer[0] = 'D'; + buffer[1] = 'S'; + buffer[2] = 'R'; + buffer[3] = 'P'; + + buffer[4] = 0x0A; // Poll with text + + unsigned int length = text.Length(); + + for (unsigned int i = 0U; i < length; i++) + buffer[5U + i] = text.GetChar(i); + + buffer[5U + length] = 0x00; + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Poll"), buffer, 6U + length); +#endif + + return m_socket.write(buffer, 6U + length, m_address, m_port); +} + +bool CRepeaterProtocolHandler::writeRegister() +{ + unsigned char buffer[40U]; + + buffer[0] = 'D'; + buffer[1] = 'S'; + buffer[2] = 'R'; + buffer[3] = 'P'; + + buffer[4] = 0x0B; // Register with name + + unsigned int length = m_name.Length(); + + for (unsigned int i = 0U; i < length; i++) + buffer[5U + i] = m_name.GetChar(i); + + buffer[5U + length] = 0x00; + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Register"), buffer, 6U + length); +#endif + + return m_socket.write(buffer, 6U + length, m_address, m_port); +} + +NETWORK_TYPE CRepeaterProtocolHandler::read() +{ + bool res = true; + + // Loop until we have no more data from the socket or we have data for the higher layers + while (res) + res = readPackets(); + + return m_type; +} + +bool CRepeaterProtocolHandler::readPackets() +{ + m_type = NETWORK_NONE; + + // No more data? + in_addr address; + unsigned int port; + int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return false; + + // Check if the data is for us + if (m_address.s_addr != address.s_addr || m_port != port) { + wxLogMessage(wxT("Packet received from an invalid source, %08X != %08X and/or %u != %u"), m_address.s_addr, address.s_addr, m_port, port); + CUtils::dump(wxT("Data"), m_buffer, length); + return false; + } + + m_length = length; + + // Invalid packet type? + if (m_buffer[0] == 'D' && m_buffer[1] == 'S' && m_buffer[2] == 'R' && m_buffer[3] == 'P') { + if (m_buffer[4] == 0x00U) { + m_type = NETWORK_TEXT; + return false; + } + + if (m_buffer[4] == 0x01U) { + m_type = NETWORK_TEMPTEXT; + return false; + } + + // Status data 1 + else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x00U) { + m_type = NETWORK_STATUS1; + return false; + } + + // Status data 2 + else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x01U) { + m_type = NETWORK_STATUS2; + return false; + } + + // Status data 3 + else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x02U) { + m_type = NETWORK_STATUS3; + return false; + } + + // Status data 4 + else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x03U) { + m_type = NETWORK_STATUS4; + return false; + } + + // Status data 5 + else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x04U) { + m_type = NETWORK_STATUS5; + return false; + } + + // Header data + else if (m_buffer[4] == 0x20U) { + wxUint16 id = m_buffer[5] * 256U + m_buffer[6]; + + // Are we listening for headers? + if (m_inId != 0U) + return true; + + m_inId = id; // Take the stream id + m_type = NETWORK_HEADER; + return false; + } + + // User data + else if (m_buffer[4] == 0x21U) { + wxUint16 id = m_buffer[5] * 256U + m_buffer[6]; + + // Check that the stream id matches the valid header, reject otherwise + if (id != m_inId) + return true; + + // Is this the last packet in the stream? + if ((m_buffer[7] & 0x40) == 0x40) + m_inId = 0U; + + m_type = NETWORK_DATA; + return false; + } + + else if (m_buffer[4] == 0x24U) { + // Silently ignore DD data + } + } + + CUtils::dump(wxT("Unknown packet from the Gateway"), m_buffer, m_length); + + return true; +} + +CHeaderData* CRepeaterProtocolHandler::readHeader() +{ + if (m_type != NETWORK_HEADER) + return NULL; + + // If the checksum is 0xFFFF then we accept the header without testing the checksum + if (m_buffer[47U] == 0xFFU && m_buffer[48U] == 0xFFU) + return new CHeaderData(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES, false); + + // Header checksum testing is enabled + CHeaderData* header = new CHeaderData(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES, true); + + if (!header->isValid()) { + CUtils::dump(wxT("Header checksum failure from the Gateway"), m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); + delete header; + return NULL; + } + + return header; +} + +unsigned int CRepeaterProtocolHandler::readData(unsigned char* buffer, unsigned int length, unsigned char& seqNo) +{ + if (m_type != NETWORK_DATA) + return 0U; + + unsigned int dataLen = m_length - 9U; + + // Is our buffer too small? + if (dataLen > length) + dataLen = length; + + seqNo = m_buffer[7U]; + + ::memcpy(buffer, m_buffer + 9U, dataLen); + + // Simple sanity checks of the incoming sync bits + if (seqNo == 0U) { + // Regenerate sync bytes + buffer[9U] = DATA_SYNC_BYTES[0U]; + buffer[10U] = DATA_SYNC_BYTES[1U]; + buffer[11U] = DATA_SYNC_BYTES[2U]; + } else if (::memcmp(buffer + 9U, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES) == 0) { + // Sync bytes appearing where they shouldn't! + buffer[9U] = 0x70U; + buffer[10U] = 0x4FU; + buffer[11U] = 0x93U; + } + + return dataLen; +} + +void CRepeaterProtocolHandler::readText(wxString& text, LINK_STATUS& status, wxString& reflector) +{ + if (m_type != NETWORK_TEXT) { + text = wxT(" "); + reflector = wxT(" "); + status = LS_NONE; + return; + } + + text = wxString((char*)(m_buffer + 5U), wxConvLocal, 20U); + + status = LINK_STATUS(m_buffer[25U]); + + reflector = wxString((char*)(m_buffer + 26U), wxConvLocal, 8U); +} + +void CRepeaterProtocolHandler::readTempText(wxString& text) +{ + if (m_type != NETWORK_TEMPTEXT) { + text = wxT(" "); + return; + } + + text = wxString((char*)(m_buffer + 5U), wxConvLocal, 20U); +} + +wxString CRepeaterProtocolHandler::readStatus1() +{ + if (m_type != NETWORK_STATUS1) + return wxEmptyString; + + return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); +} + +wxString CRepeaterProtocolHandler::readStatus2() +{ + if (m_type != NETWORK_STATUS2) + return wxEmptyString; + + return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); +} + +wxString CRepeaterProtocolHandler::readStatus3() +{ + if (m_type != NETWORK_STATUS3) + return wxEmptyString; + + return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); +} + +wxString CRepeaterProtocolHandler::readStatus4() +{ + if (m_type != NETWORK_STATUS4) + return wxEmptyString; + + return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); +} + +wxString CRepeaterProtocolHandler::readStatus5() +{ + if (m_type != NETWORK_STATUS5) + return wxEmptyString; + + return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); +} + +void CRepeaterProtocolHandler::reset() +{ + m_inId = 0U; +} + +void CRepeaterProtocolHandler::close() +{ + m_socket.close(); +} diff --git a/Common/RepeaterProtocolHandler.h b/Common/RepeaterProtocolHandler.h new file mode 100644 index 0000000..1a74cac --- /dev/null +++ b/Common/RepeaterProtocolHandler.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef RepeaterProtocolHander_H +#define RepeaterProtocolHander_H + +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "HeaderData.h" + +#include + +class CRepeaterProtocolHandler { +public: + CRepeaterProtocolHandler(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name); + ~CRepeaterProtocolHandler(); + + bool open(); + + bool writeHeader(const CHeaderData& header); + bool writeBusyHeader(const CHeaderData& header); + bool writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end); + bool writeBusyData(const unsigned char* data, unsigned int length, unsigned int errors, bool end); + bool writePoll(const wxString& text); + bool writeRegister(); + + NETWORK_TYPE read(); + void readText(wxString& text, LINK_STATUS& status, wxString& reflector); + void readTempText(wxString& text); + wxString readStatus1(); + wxString readStatus2(); + wxString readStatus3(); + wxString readStatus4(); + wxString readStatus5(); + CHeaderData* readHeader(); + unsigned int readData(unsigned char* data, unsigned int length, unsigned char& seqNo); + + void reset(); + + void close(); + +private: + CUDPReaderWriter m_socket; + in_addr m_address; + unsigned int m_port; + wxString m_name; + wxUint16 m_outId; + wxUint8 m_outSeq; + NETWORK_TYPE m_type; + wxUint16 m_inId; + unsigned char* m_buffer; + unsigned int m_length; + + bool readPackets(); +}; + +#endif diff --git a/Common/RingBuffer.h b/Common/RingBuffer.h new file mode 100644 index 0000000..3c40af0 --- /dev/null +++ b/Common/RingBuffer.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2006-2009,2012,2013,2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef RingBuffer_H +#define RingBuffer_H + +#include + +template class CRingBuffer { +public: + CRingBuffer(unsigned int length) : + m_length(length), + m_buffer(NULL), + m_iPtr(0U), + m_oPtr(0U) + { + wxASSERT(length > 0U); + + m_buffer = new T[length]; + + ::memset(m_buffer, 0x00, m_length * sizeof(T)); + } + + ~CRingBuffer() + { + delete[] m_buffer; + } + + unsigned int addData(const T* buffer, unsigned int nSamples) + { + if (nSamples > freeSpace()) + return 0U; + + for (unsigned int i = 0U; i < nSamples; i++) { + m_buffer[m_iPtr++] = buffer[i]; + + if (m_iPtr == m_length) + m_iPtr = 0U; + } + + return nSamples; + } + + unsigned int getData(T* buffer, unsigned int nSamples) + { + unsigned int data = dataSize(); + + if (data < nSamples) + nSamples = data; + + for (unsigned int i = 0U; i < nSamples; i++) { + buffer[i] = m_buffer[m_oPtr++]; + + if (m_oPtr == m_length) + m_oPtr = 0U; + } + + return nSamples; + } + + unsigned int peek(T* buffer, unsigned int nSamples) + { + unsigned int data = dataSize(); + + if (data < nSamples) + nSamples = data; + + unsigned int ptr = m_oPtr; + for (unsigned int i = 0U; i < nSamples; i++) { + buffer[i] = m_buffer[ptr++]; + + if (ptr == m_length) + ptr = 0U; + } + + return nSamples; + } + + void clear() + { + m_iPtr = 0U; + m_oPtr = 0U; + + ::memset(m_buffer, 0x00, m_length * sizeof(T)); + } + + unsigned int freeSpace() const + { + if (m_oPtr == m_iPtr) + return m_length - 1U; + + if (m_oPtr > m_iPtr) + return m_oPtr - m_iPtr - 1U; + + return m_length - (m_iPtr - m_oPtr) - 1U; + } + + bool hasSpace(unsigned int length) const + { + return freeSpace() > length; + } + + bool hasData() const + { + return m_oPtr != m_iPtr; + } + + bool isEmpty() const + { + return m_oPtr == m_iPtr; + } + +private: + unsigned int m_length; + T* m_buffer; + volatile unsigned int m_iPtr; + volatile unsigned int m_oPtr; + + unsigned int dataSize() const + { + if (m_iPtr >= m_oPtr) + return m_iPtr - m_oPtr; + + return m_length - (m_oPtr - m_iPtr); + } +}; + +#endif diff --git a/Common/SerialDataController.cpp b/Common/SerialDataController.cpp new file mode 100644 index 0000000..fe8c5e6 --- /dev/null +++ b/Common/SerialDataController.cpp @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2002-2004,2007-2011,2013,2014,2015 by Jonathan Naylor G4KLX + * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "SerialDataController.h" + +#include + +#if defined(__WINDOWS__) +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + + +#if defined(__WINDOWS__) + +const unsigned int BUFFER_LENGTH = 1000U; + +CSerialDataController::CSerialDataController(const wxString& device, SERIAL_SPEED speed, bool assertRTS) : +m_device(device), +m_speed(speed), +m_assertRTS(assertRTS), +m_handle(INVALID_HANDLE_VALUE), +m_readOverlapped(), +m_writeOverlapped(), +m_readBuffer(NULL), +m_readLength(0U), +m_readPending(false) +{ + wxASSERT(!device.IsEmpty()); + + m_readBuffer = new unsigned char[BUFFER_LENGTH]; +} + +CSerialDataController::~CSerialDataController() +{ + delete[] m_readBuffer; +} + +bool CSerialDataController::open() +{ + wxASSERT(m_handle == INVALID_HANDLE_VALUE); + + DWORD errCode; + + wxString baseName = m_device.Mid(4U); // Convert "\\.\COM10" to "COM10" + + m_handle = ::CreateFile(m_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (m_handle == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Cannot open device - %s, err=%04lx"), m_device.c_str(), ::GetLastError()); + return false; + } + + DCB dcb; + if (::GetCommState(m_handle, &dcb) == 0) { + wxLogError(wxT("Cannot get the attributes for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + dcb.BaudRate = DWORD(m_speed); + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.fParity = FALSE; + dcb.StopBits = ONESTOPBIT; + dcb.fInX = FALSE; + dcb.fOutX = FALSE; + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + + if (::SetCommState(m_handle, &dcb) == 0) { + wxLogError(wxT("Cannot set the attributes for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + COMMTIMEOUTS timeouts; + if (!::GetCommTimeouts(m_handle, &timeouts)) { + wxLogError(wxT("Cannot get the timeouts for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = 0UL; + timeouts.ReadTotalTimeoutConstant = 0UL; + + if (!::SetCommTimeouts(m_handle, &timeouts)) { + wxLogError(wxT("Cannot set the timeouts for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + if (::EscapeCommFunction(m_handle, CLRDTR) == 0) { + wxLogError(wxT("Cannot clear DTR for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + if (::EscapeCommFunction(m_handle, m_assertRTS ? SETRTS : CLRRTS) == 0) { + wxLogError(wxT("Cannot set/clear RTS for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + ::ClearCommError(m_handle, &errCode, NULL); + + ::memset(&m_readOverlapped, 0x00U, sizeof(OVERLAPPED)); + ::memset(&m_writeOverlapped, 0x00U, sizeof(OVERLAPPED)); + + m_readOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); + m_writeOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); + + m_readLength = 0U; + m_readPending = false; + ::memset(m_readBuffer, 0x00U, BUFFER_LENGTH); + + return true; +} + +int CSerialDataController::read(unsigned char* buffer, unsigned int length) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + wxASSERT(buffer != NULL); + + unsigned int ptr = 0U; + + while (ptr < length) { + int ret = readNonblock(buffer + ptr, length - ptr); + if (ret < 0) { + return ret; + } else if (ret == 0) { + if (ptr == 0U) + return 0; + } else { + ptr += ret; + } + } + + return int(length); +} + +int CSerialDataController::readNonblock(unsigned char* buffer, unsigned int length) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + wxASSERT(buffer != NULL); + + if (length > BUFFER_LENGTH) + length = BUFFER_LENGTH; + + if (m_readPending && length != m_readLength) { + ::CancelIo(m_handle); + m_readPending = false; + } + + m_readLength = length; + + if (length == 0U) + return 0; + + if (!m_readPending) { + DWORD bytes = 0UL; + BOOL res = ::ReadFile(m_handle, m_readBuffer, m_readLength, &bytes, &m_readOverlapped); + if (res) { + ::memcpy(buffer, m_readBuffer, bytes); + return int(bytes); + } + + DWORD error = ::GetLastError(); + if (error != ERROR_IO_PENDING) { + wxLogError(wxT("Error from ReadFile: %04lx"), error); + return -1; + } + + m_readPending = true; + } + + BOOL res = HasOverlappedIoCompleted(&m_readOverlapped); + if (!res) + return 0; + + DWORD bytes = 0UL; + res = ::GetOverlappedResult(m_handle, &m_readOverlapped, &bytes, TRUE); + if (!res) { + wxLogError(wxT("Error from GetOverlappedResult (ReadFile): %04lx"), ::GetLastError()); + return -1; + } + + ::memcpy(buffer, m_readBuffer, bytes); + m_readPending = false; + + return int(bytes); +} + +int CSerialDataController::write(const unsigned char* buffer, unsigned int length) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + wxASSERT(buffer != NULL); + + if (length == 0U) + return 0; + + unsigned int ptr = 0U; + + while (ptr < length) { + DWORD bytes = 0UL; + BOOL res = ::WriteFile(m_handle, buffer + ptr, length - ptr, &bytes, &m_writeOverlapped); + if (!res) { + DWORD error = ::GetLastError(); + if (error != ERROR_IO_PENDING) { + wxLogError(wxT("Error from WriteFile: %04lx"), error); + return -1; + } + + res = ::GetOverlappedResult(m_handle, &m_writeOverlapped, &bytes, TRUE); + if (!res) { + wxLogError(wxT("Error from GetOverlappedResult (WriteFile): %04lx"), ::GetLastError()); + return -1; + } + } + + ptr += bytes; + } + + return int(length); +} + +void CSerialDataController::close() +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; + + ::CloseHandle(m_readOverlapped.hEvent); + ::CloseHandle(m_writeOverlapped.hEvent); +} + +#else + +CSerialDataController::CSerialDataController(const wxString& device, SERIAL_SPEED speed, bool assertRTS) : +m_device(device), +m_speed(speed), +m_assertRTS(assertRTS), +m_fd(-1) +{ + wxASSERT(!device.IsEmpty()); +} + +CSerialDataController::~CSerialDataController() +{ +} + +bool CSerialDataController::open() +{ + wxASSERT(m_fd == -1); + + m_fd = ::open(m_device.mb_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); + if (m_fd < 0) { + wxLogError(wxT("Cannot open device - %s"), m_device.c_str()); + return false; + } + + if (::isatty(m_fd) == 0) { + wxLogError(wxT("%s is not a TTY device"), m_device.c_str()); + ::close(m_fd); + return false; + } + + termios termios; + if (::tcgetattr(m_fd, &termios) < 0) { + wxLogError(wxT("Cannot get the attributes for %s"), m_device.c_str()); + ::close(m_fd); + return false; + } + + termios.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG); + termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY); + termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS); + termios.c_cflag |= CS8; + termios.c_oflag &= ~(OPOST); + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 10; + + switch (m_speed) { + case SERIAL_1200: + ::cfsetospeed(&termios, B1200); + ::cfsetispeed(&termios, B1200); + break; + case SERIAL_2400: + ::cfsetospeed(&termios, B2400); + ::cfsetispeed(&termios, B2400); + break; + case SERIAL_4800: + ::cfsetospeed(&termios, B4800); + ::cfsetispeed(&termios, B4800); + break; + case SERIAL_9600: + ::cfsetospeed(&termios, B9600); + ::cfsetispeed(&termios, B9600); + break; + case SERIAL_19200: + ::cfsetospeed(&termios, B19200); + ::cfsetispeed(&termios, B19200); + break; + case SERIAL_38400: + ::cfsetospeed(&termios, B38400); + ::cfsetispeed(&termios, B38400); + break; + case SERIAL_115200: + ::cfsetospeed(&termios, B115200); + ::cfsetispeed(&termios, B115200); + break; + case SERIAL_230400: + ::cfsetospeed(&termios, B230400); + ::cfsetispeed(&termios, B230400); + break; + default: + wxLogError(wxT("Unsupported serial port speed - %d"), int(m_speed)); + ::close(m_fd); + return false; + } + + if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { + wxLogError(wxT("Cannot set the attributes for %s"), m_device.c_str()); + ::close(m_fd); + return false; + } + + if (m_assertRTS) { + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) { + wxLogError(wxT("Cannot get the control attributes for %s"), m_device.c_str()); + ::close(m_fd); + return false; + } + + y |= TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) { + wxLogError(wxT("Cannot set the control attributes for %s"), m_device.c_str()); + ::close(m_fd); + return false; + } + } + + return true; +} + +int CSerialDataController::read(unsigned char* buffer, unsigned int length) +{ + wxASSERT(buffer != NULL); + wxASSERT(m_fd != -1); + + if (length == 0U) + return 0; + + unsigned int offset = 0U; + + while (offset < length) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(m_fd, &fds); + + int n; + if (offset == 0U) { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + + n = ::select(m_fd + 1, &fds, NULL, NULL, &tv); + if (n == 0) + return 0; + } else { + n = ::select(m_fd + 1, &fds, NULL, NULL, NULL); + } + + if (n < 0) { + wxLogError(wxT("Error from select(), errno=%d"), errno); + return -1; + } + + if (n > 0) { + ssize_t len = ::read(m_fd, buffer + offset, length - offset); + if (len < 0) { + if (errno != EAGAIN) { + wxLogError(wxT("Error from read(), errno=%d"), errno); + return -1; + } + } + + if (len > 0) + offset += len; + } + } + + return length; +} + +int CSerialDataController::write(const unsigned char* buffer, unsigned int length) +{ + wxASSERT(buffer != NULL); + wxASSERT(m_fd != -1); + + if (length == 0U) + return 0; + + unsigned int ptr = 0U; + + while (ptr < length) { + ssize_t n = ::write(m_fd, buffer + ptr, length - ptr); + if (n < 0) { + if (errno != EAGAIN) { + wxLogError(wxT("Error returned from write(), errno=%d"), errno); + return -1; + } + } + + if (n > 0) + ptr += n; + } + + return length; +} + +void CSerialDataController::close() +{ + wxASSERT(m_fd != -1); + + ::close(m_fd); + m_fd = -1; +} + +#endif diff --git a/Common/SerialDataController.h b/Common/SerialDataController.h new file mode 100644 index 0000000..8c154fb --- /dev/null +++ b/Common/SerialDataController.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2002-2004,2007-2009,2011-2013,2015 by Jonathan Naylor G4KLX + * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef SerialDataController_H +#define SerialDataController_H + +#include + +#if defined(__WINDOWS__) +#include +#endif + +enum SERIAL_SPEED { + SERIAL_1200 = 1200, + SERIAL_2400 = 2400, + SERIAL_4800 = 4800, + SERIAL_9600 = 9600, + SERIAL_19200 = 19200, + SERIAL_38400 = 38400, + SERIAL_76800 = 76800, + SERIAL_115200 = 115200, + SERIAL_230400 = 230400 +}; + +class CSerialDataController { +public: + CSerialDataController(const wxString& device, SERIAL_SPEED speed, bool assertRTS = false); + ~CSerialDataController(); + + bool open(); + + int read(unsigned char* buffer, unsigned int length); + int write(const unsigned char* buffer, unsigned int length); + + void close(); + +private: + wxString m_device; + SERIAL_SPEED m_speed; + bool m_assertRTS; +#if defined(__WINDOWS__) + HANDLE m_handle; + OVERLAPPED m_readOverlapped; + OVERLAPPED m_writeOverlapped; + unsigned char* m_readBuffer; + unsigned int m_readLength; + bool m_readPending; +#else + int m_fd; +#endif + +#if defined(__WINDOWS__) + int readNonblock(unsigned char* buffer, unsigned int length); +#endif +}; + +#endif diff --git a/Common/SerialLineController.cpp b/Common/SerialLineController.cpp new file mode 100644 index 0000000..a42324a --- /dev/null +++ b/Common/SerialLineController.cpp @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2002-2004,2007-2011,2013,2015 by Jonathan Naylor G4KLX + * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "SerialLineController.h" + +#include + +#if defined(__WINDOWS__) +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + + +#if defined(__WINDOWS__) + +CSerialLineController::CSerialLineController(const wxString& device, unsigned int config) : +m_device(device), +m_config(config), +m_rts(false), +m_dtr(false), +m_handle(INVALID_HANDLE_VALUE) +{ + wxASSERT(!device.IsEmpty()); + wxASSERT(config == 1U || config == 2U || config == 3U); +} + +CSerialLineController::~CSerialLineController() +{ +} + +bool CSerialLineController::open() +{ + wxASSERT(m_handle == INVALID_HANDLE_VALUE); + + DWORD errCode; + + m_handle = ::CreateFile(m_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (m_handle == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Cannot open device - %s"), m_device.c_str()); + return false; + } + + DCB dcb; + if (::GetCommState(m_handle, &dcb) == 0) { + wxLogError(wxT("Cannot get the attributes for %s"), m_device.c_str()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + + if (::SetCommState(m_handle, &dcb) == 0) { + wxLogError(wxT("Cannot set the attributes for %s"), m_device.c_str()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + if (::EscapeCommFunction(m_handle, CLRDTR) == 0) { + wxLogError(wxT("Cannot clear DTR for %s"), m_device.c_str()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + if (::EscapeCommFunction(m_handle, CLRRTS) == 0) { + wxLogError(wxT("Cannot clear RTS for %s"), m_device.c_str()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + ::ClearCommError(m_handle, &errCode, NULL); + + return true; +} + +bool CSerialLineController::getCD() const +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + DWORD status; + DWORD errCode; + if (::GetCommModemStatus(m_handle, &status) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return false; + } + + return (status & MS_RLSD_ON) == MS_RLSD_ON; +} + +bool CSerialLineController::getCTS() const +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + DWORD status; + DWORD errCode; + if (::GetCommModemStatus(m_handle, &status) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return false; + } + + return (status & MS_CTS_ON) == MS_CTS_ON; +} + +bool CSerialLineController::getDSR() const +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + DWORD status; + DWORD errCode; + if (::GetCommModemStatus(m_handle, &status) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return false; + } + + return (status & MS_DSR_ON) == MS_DSR_ON; +} + +bool CSerialLineController::setRTS(bool set) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + if (set == m_rts) + return true; + + DWORD rts = (set) ? SETRTS : CLRRTS; + DWORD errCode; + + if (::EscapeCommFunction(m_handle, rts) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return false; + } + + m_rts = set; + + return true; +} + +bool CSerialLineController::setDTR(bool set) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + if (set == m_dtr) + return true; + + DWORD dtr = (set) ? SETDTR : CLRDTR; + DWORD errCode; + + if (::EscapeCommFunction(m_handle, dtr) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return false; + } + + m_dtr = set; + + return true; +} + +void CSerialLineController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + inp1 = inp2 = inp3 = inp4 = inp5 = false; + + DWORD status; + DWORD errCode; + if (::GetCommModemStatus(m_handle, &status) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return; + } + + switch (m_config) { + case 1U: + inp1 = (status & MS_DSR_ON) == MS_DSR_ON; + inp2 = (status & MS_CTS_ON) == MS_CTS_ON; + break; + case 2U: + inp1 = (status & MS_RLSD_ON) == MS_RLSD_ON; + inp2 = (status & MS_RLSD_ON) == MS_RLSD_ON; + break; + case 3U: + inp1 = (status & MS_RLSD_ON) == MS_RLSD_ON; + break; + default: + wxLogError(wxT("Unknown serial config - %u"), m_config); + break; + } +} + +void CSerialLineController::setDigitalOutputs(bool outp1, bool, bool outp3, bool, bool, bool, bool, bool) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + switch (m_config) { + case 1U: + if (outp1 != m_dtr) { + DWORD dtr = (outp1) ? SETDTR : CLRDTR; + DWORD errCode; + + if (::EscapeCommFunction(m_handle, dtr) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return; + } + + m_dtr = outp1; + } + if (outp3 != m_rts) { + DWORD rts = (outp3) ? SETRTS : CLRRTS; + DWORD errCode; + + if (::EscapeCommFunction(m_handle, rts) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return; + } + + m_rts = outp3; + } + break; + + case 2U: + case 3U: + if (outp1 != m_rts) { + DWORD rts = (outp1) ? SETRTS : CLRRTS; + DWORD errCode; + + if (::EscapeCommFunction(m_handle, rts) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return; + } + + m_rts = outp1; + } + if (outp3 != m_dtr) { + DWORD dtr = (outp3) ? SETDTR : CLRDTR; + DWORD errCode; + + if (::EscapeCommFunction(m_handle, dtr) == 0) { + ::ClearCommError(m_handle, &errCode, NULL); + return; + } + + m_dtr = outp3; + } + break; + + default: + wxLogError(wxT("Unknown serial config - %u"), m_config); + break; + } +} + +void CSerialLineController::close() +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; +} + +#else + +CSerialLineController::CSerialLineController(const wxString& device, unsigned int config) : +m_device(device), +m_config(config), +m_rts(false), +m_dtr(false), +m_fd(-1) +{ + wxASSERT(!device.IsEmpty()); + wxASSERT(config == 1U || config == 2U || config == 3U); +} + +CSerialLineController::~CSerialLineController() +{ +} + +bool CSerialLineController::open() +{ + wxASSERT(m_fd == -1); + + m_fd = ::open(m_device.mb_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); + if (m_fd < 0) { + wxLogError(wxT("Cannot open device - %s"), m_device.c_str()); + return false; + } + + if (::isatty(m_fd) == 0) { + wxLogError(wxT("%s is not a TTY device"), m_device.c_str()); + ::close(m_fd); + return false; + } + + termios termios; + if (::tcgetattr(m_fd, &termios) < 0) { + wxLogError(wxT("Cannot get the attributes for %s"), m_device.c_str()); + ::close(m_fd); + return false; + } + + // FIXME XXX unfinished + termios.c_cflag |= (CLOCAL | CREAD); + termios.c_cflag &= ~CRTSCTS; + termios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + termios.c_oflag &= ~OPOST; + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 10; + + if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { + wxLogError(wxT("Cannot set the attributes for %s"), m_device.c_str()); + ::close(m_fd); + return false; + } + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) { + wxLogError(wxT("Cannot get the modem status bits for %s"), m_device.c_str()); + ::close(m_fd); + return false; + } + + y &= ~TIOCM_DTR; + y &= ~TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) { + wxLogError(wxT("Cannot set the modem status bits for %s"), m_device.c_str()); + ::close(m_fd); + return false; + } + + return true; +} + +bool CSerialLineController::getCD() const +{ + wxASSERT(m_fd != -1); + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return false; + + return (y & TIOCM_CD) == TIOCM_CD; +} + +bool CSerialLineController::getCTS() const +{ + wxASSERT(m_fd != -1); + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return false; + + return (y & TIOCM_CTS) == TIOCM_CTS; +} + +bool CSerialLineController::getDSR() const +{ + wxASSERT(m_fd != -1); + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return false; + + return (y & TIOCM_DSR) == TIOCM_DSR; +} + +bool CSerialLineController::setRTS(bool set) +{ + wxASSERT(m_fd != -1); + + if (set == m_rts) + return true; + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return false; + + if (set) + y |= TIOCM_RTS; + else + y &= ~TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) + return false; + + m_rts = set; + + return true; +} + +bool CSerialLineController::setDTR(bool set) +{ + wxASSERT(m_fd != -1); + + if (set == m_dtr) + return true; + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return false; + + if (set) + y |= TIOCM_DTR; + else + y &= ~TIOCM_DTR; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) + return false; + + m_dtr = set; + + return true; +} + +void CSerialLineController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + wxASSERT(m_fd != -1); + + inp1 = inp2 = inp3 = inp4 = inp5 = false; + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return; + + switch (m_config) { + case 1U: + inp1 = (y & TIOCM_DSR) == TIOCM_DSR; + inp2 = (y & TIOCM_CTS) == TIOCM_CTS; + break; + case 2U: + inp1 = (y & TIOCM_CD) == TIOCM_CD; + inp2 = (y & TIOCM_CD) == TIOCM_CD; + break; + case 3U: + inp1 = (y & TIOCM_CD) == TIOCM_CD; + break; + default: + wxLogError(wxT("Unknown serial config - %u"), m_config); + break; + } +} + +void CSerialLineController::setDigitalOutputs(bool outp1, bool, bool outp3, bool, bool, bool, bool, bool) +{ + wxASSERT(m_fd != -1); + + if (m_config == 1U) { + if (outp1 == m_dtr && outp3 == m_rts) + return; + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return; + + if (outp1) + y |= TIOCM_DTR; + else + y &= ~TIOCM_DTR; + + if (outp3) + y |= TIOCM_RTS; + else + y &= ~TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) + return; + + m_dtr = outp1; + m_rts = outp3; + } else if (m_config == 2U || m_config == 3U) { + if (outp1 == m_rts && outp3 == m_dtr) + return; + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return; + + if (outp1) + y |= TIOCM_RTS; + else + y &= ~TIOCM_RTS; + + if (outp3) + y |= TIOCM_DTR; + else + y &= ~TIOCM_DTR; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) + return; + + m_rts = outp1; + m_dtr = outp3; + } else { + wxLogError(wxT("Unknown serial config - %u"), m_config); + } +} + +void CSerialLineController::close() +{ + wxASSERT(m_fd != -1); + + ::close(m_fd); + m_fd = -1; +} + +#endif diff --git a/Common/SerialLineController.h b/Common/SerialLineController.h new file mode 100644 index 0000000..827af7a --- /dev/null +++ b/Common/SerialLineController.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2002-2004,2007-2009,2011-2013,2015 by Jonathan Naylor G4KLX + * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef SerialLineController_H +#define SerialLineController_H + +#include "HardwareController.h" + +#include + +#if defined(__WINDOWS__) +#include +#endif + +enum SERIALPIN { + SERIAL_CD, + SERIAL_CTS, + SERIAL_DSR, + SERIAL_DTR, + SERIAL_RTS, + SERIAL_ECHOLINK +}; + +const unsigned int MAX_DEVICE_NAME = 255U; + +class CSerialLineController : public IHardwareController { +public: + CSerialLineController(const wxString& device, unsigned int config = 1U); + virtual ~CSerialLineController(); + + virtual bool open(); + + virtual bool setRTS(bool set); + virtual bool setDTR(bool set); + + virtual bool getCD() const; + virtual bool getCTS() const; + virtual bool getDSR() const; + + virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5); + virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8); + + virtual void close(); + +private: + wxString m_device; + unsigned int m_config; + bool m_rts; + bool m_dtr; +#if defined(__WINDOWS__) + HANDLE m_handle; +#else + int m_fd; +#endif +}; + +#endif diff --git a/Common/SerialPortSelector.cpp b/Common/SerialPortSelector.cpp new file mode 100644 index 0000000..bc7ad1c --- /dev/null +++ b/Common/SerialPortSelector.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2002-2004,2007-2011,2013,2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "SerialPortSelector.h" + +#include + +#if defined(__APPLE__) && defined(__MACH__) +#include +#include +#include +#include +#include +#include +#include +#include +#elif defined(__WINDOWS__) +#include +#include +#else +#include +#endif + + +wxArrayString CSerialPortSelector::getDevices() +{ + wxArrayString devices; + + devices.Alloc(10); + +#if defined(__APPLE__) && defined(__MACH__) + mach_port_t masterPort; + kern_return_t ret = ::IOMasterPort(MACH_PORT_NULL, &masterPort); + if (ret != KERN_SUCCESS) + return devices; + + CFMutableDictionaryRef match = ::IOServiceMatching(kIOSerialBSDServiceValue); + if (match == NULL) + wxLogWarning(wxT("IOServiceMatching() returned NULL")); + else + ::CFDictionarySetValue(match, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDRS232Type)); + + io_iterator_t services; + ret = ::IOServiceGetMatchingServices(masterPort, match, &services); + if (ret != KERN_SUCCESS) + return devices; + + io_object_t modem; + while ((modem = ::IOIteratorNext(services))) { + CFTypeRef filePath = ::IORegistryEntryCreateCFProperty(modem, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); + if (filePath != NULL) { + char port[MAXPATHLEN]; + Boolean result = ::CFStringGetCString((const __CFString*)filePath, port, MAXPATHLEN, kCFStringEncodingASCII); + ::CFRelease(filePath); + + if (result) + devices.Add(port); + + ::IOObjectRelease(modem); + } + } + + ::IOObjectRelease(services); +#elif defined(__WINDOWS__) + devices.Add(wxT("\\\\.\\COM1")); + devices.Add(wxT("\\\\.\\COM2")); + devices.Add(wxT("\\\\.\\COM3")); + devices.Add(wxT("\\\\.\\COM4")); + devices.Add(wxT("\\\\.\\COM5")); + devices.Add(wxT("\\\\.\\COM6")); + devices.Add(wxT("\\\\.\\COM7")); + devices.Add(wxT("\\\\.\\COM8")); + devices.Add(wxT("\\\\.\\COM9")); + devices.Add(wxT("\\\\.\\COM10")); + devices.Add(wxT("\\\\.\\COM11")); + devices.Add(wxT("\\\\.\\COM12")); + devices.Add(wxT("\\\\.\\COM13")); + devices.Add(wxT("\\\\.\\COM14")); + devices.Add(wxT("\\\\.\\COM15")); + devices.Add(wxT("\\\\.\\COM16")); + devices.Add(wxT("\\\\.\\COM17")); + devices.Add(wxT("\\\\.\\COM18")); + devices.Add(wxT("\\\\.\\COM19")); +#else + wxDir devDir; + bool ret = devDir.Open(wxT("/dev")); + + if (ret) { + wxString fileName; + ret = devDir.GetFirst(&fileName, wxT("ttyACM*"), wxDIR_FILES); + + while (ret) { + fileName.Prepend(wxT("/dev/")); + devices.Add(fileName); + + ret = devDir.GetNext(&fileName); + } + + ret = devDir.GetFirst(&fileName, wxT("ttyAMA*"), wxDIR_FILES); + + while (ret) { + fileName.Prepend(wxT("/dev/")); + devices.Add(fileName); + + ret = devDir.GetNext(&fileName); + } + + ret = devDir.GetFirst(&fileName, wxT("ttyS*"), wxDIR_FILES); + + while (ret) { + fileName.Prepend(wxT("/dev/")); + devices.Add(fileName); + + ret = devDir.GetNext(&fileName); + } + + ret = devDir.GetFirst(&fileName, wxT("ttyUSB*"), wxDIR_FILES); + + while (ret) { + fileName.Prepend(wxT("/dev/")); + devices.Add(fileName); + + ret = devDir.GetNext(&fileName); + } + } +#endif + + return devices; +} diff --git a/Common/SerialPortSelector.h b/Common/SerialPortSelector.h new file mode 100644 index 0000000..0d7c412 --- /dev/null +++ b/Common/SerialPortSelector.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2002-2004,2007-2009,2011-2013,2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef SerialPortSelector_H +#define SerialPortSelector_H + +#include + +class CSerialPortSelector { +public: + static wxArrayString getDevices(); + +private: +}; + +#endif diff --git a/Common/SlowDataDecoder.cpp b/Common/SlowDataDecoder.cpp new file mode 100644 index 0000000..e75eb37 --- /dev/null +++ b/Common/SlowDataDecoder.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2009,2010,2011 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CCITTChecksumReverse.h" +#include "SlowDataDecoder.h" +#include "DStarDefines.h" + +const unsigned int SLOW_DATA_BLOCK_SIZE = 6U; + +const unsigned int SLOW_DATA_FULL_BLOCK_SIZE = (SLOW_DATA_BLOCK_SIZE - 1U) * 10U; + +CSlowDataDecoder::CSlowDataDecoder() : +m_buffer(NULL), +m_state(SDD_FIRST), +m_headerData(NULL), +m_headerPtr(0U), +m_header(NULL) +{ + m_buffer = new unsigned char[SLOW_DATA_BLOCK_SIZE]; + m_headerData = new unsigned char[SLOW_DATA_FULL_BLOCK_SIZE]; +} + +CSlowDataDecoder::~CSlowDataDecoder() +{ + delete[] m_buffer; + delete[] m_headerData; + + delete m_header; +} + +void CSlowDataDecoder::addData(const unsigned char* data) +{ + wxASSERT(data != NULL); + + switch (m_state) { + case SDD_FIRST: + m_buffer[0] = data[0] ^ SCRAMBLER_BYTE1; + m_buffer[1] = data[1] ^ SCRAMBLER_BYTE2; + m_buffer[2] = data[2] ^ SCRAMBLER_BYTE3; + m_state = SDD_SECOND; + return; + + case SDD_SECOND: + m_buffer[3] = data[0] ^ SCRAMBLER_BYTE1; + m_buffer[4] = data[1] ^ SCRAMBLER_BYTE2; + m_buffer[5] = data[2] ^ SCRAMBLER_BYTE3; + m_state = SDD_FIRST; + processHeader(); + break; + } +} + +CHeaderData* CSlowDataDecoder::getHeaderData() +{ + if (m_header == NULL) + return NULL; + + CHeaderData* temp = new CHeaderData(*m_header); + + reset(); + + return temp; +} + +void CSlowDataDecoder::sync() +{ + m_headerPtr = 0U; + + m_state = SDD_FIRST; +} + +void CSlowDataDecoder::reset() +{ + m_headerPtr = 0U; + + m_state = SDD_FIRST; + + ::memset(m_headerData, 0x00, SLOW_DATA_FULL_BLOCK_SIZE * sizeof(unsigned char)); + + delete m_header; + m_header = NULL; +} + +void CSlowDataDecoder::processHeader() +{ + // Do we have a complete and valid header already? + if (m_header != NULL) + return; + + for (unsigned int i = 1U; i <= (SLOW_DATA_BLOCK_SIZE - 1U); i++, m_headerPtr++) { + m_headerData[m_headerPtr] = m_buffer[i]; + + if (m_headerPtr >= SLOW_DATA_FULL_BLOCK_SIZE) + m_headerPtr = 0U; + } + + // Look for a header starting immediately after the data sync + bool res = processHeader(m_headerData); + if (res) + return; + + // Look for a header starting one block after the data sync + processHeader(m_headerData + SLOW_DATA_BLOCK_SIZE - 1U); +} + +bool CSlowDataDecoder::processHeader(const unsigned char* bytes) +{ + unsigned char buffer[RADIO_HEADER_LENGTH_BYTES]; + + // Clean up the header data + for (unsigned int n = 0U; n < RADIO_HEADER_LENGTH_BYTES; n++) { + switch (n) { + case 0U: // Flag 1 + // Only allow BK and EMR bits and always assert the repeater bit + buffer[n] = (bytes[n] & (INTERRUPTED_MASK | URGENT_MASK)) | REPEATER_MASK; + break; + case 1U: // Flag 2 + case 2U: // Flag 3 + // Always 0x00 in current implementations + buffer[n] = 0x00; + break; + case 39U: // Checksum 1 + case 40U: // Checksum 2 + // These bytes can be any value + buffer[n] = bytes[n]; + break; + default: // Callsign text + // Only ASCII text so MSB is always clear + buffer[n] = bytes[n] & 0x7F; + break; + } + } + + CCCITTChecksumReverse cksum; + cksum.update(buffer, RADIO_HEADER_LENGTH_BYTES - 2U); + + bool valid = cksum.check(buffer + RADIO_HEADER_LENGTH_BYTES - 2U); + if (!valid) + return false; + + // The checksum has already been checked + m_header = new CHeaderData(buffer, RADIO_HEADER_LENGTH_BYTES, false); + + return true; +} diff --git a/Common/SlowDataDecoder.h b/Common/SlowDataDecoder.h new file mode 100644 index 0000000..b628116 --- /dev/null +++ b/Common/SlowDataDecoder.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009,2010 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef SlowDataDecoder_H +#define SlowDataDecoder_H + +#include "HeaderData.h" + +#include + +enum SDD_STATE { + SDD_FIRST, + SDD_SECOND +}; + +class CSlowDataDecoder { +public: + CSlowDataDecoder(); + ~CSlowDataDecoder(); + + void addData(const unsigned char* data); + + CHeaderData* getHeaderData(); + + void sync(); + void reset(); + +private: + unsigned char* m_buffer; + SDD_STATE m_state; + unsigned char* m_headerData; + unsigned int m_headerPtr; + CHeaderData* m_header; + + void processHeader(); + bool processHeader(const unsigned char* bytes); +}; + +#endif diff --git a/Common/SlowDataEncoder.cpp b/Common/SlowDataEncoder.cpp new file mode 100644 index 0000000..74bbd4b --- /dev/null +++ b/Common/SlowDataEncoder.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2010,2011,2015 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CCITTChecksumReverse.h" +#include "SlowDataEncoder.h" +#include "DStarDefines.h" + +const unsigned int SLOW_DATA_BLOCK_SIZE = 6U; + +const unsigned int SLOW_DATA_FULL_BLOCK_SIZE = SLOW_DATA_BLOCK_SIZE * 10U; + +CSlowDataEncoder::CSlowDataEncoder() : +m_headerData(NULL), +m_textData(NULL), +m_headerPtr(0U), +m_textPtr(0U) +{ + m_headerData = new unsigned char[SLOW_DATA_FULL_BLOCK_SIZE]; + m_textData = new unsigned char[SLOW_DATA_FULL_BLOCK_SIZE]; + + ::memset(m_headerData, 'f', SLOW_DATA_FULL_BLOCK_SIZE); + ::memset(m_textData, 'f', SLOW_DATA_FULL_BLOCK_SIZE); +} + +CSlowDataEncoder::~CSlowDataEncoder() +{ + delete[] m_headerData; + delete[] m_textData; +} + +void CSlowDataEncoder::getHeaderData(unsigned char* data) +{ + wxASSERT(data != NULL); + + data[0U] = m_headerData[m_headerPtr++] ^ SCRAMBLER_BYTE1; + data[1U] = m_headerData[m_headerPtr++] ^ SCRAMBLER_BYTE2; + data[2U] = m_headerData[m_headerPtr++] ^ SCRAMBLER_BYTE3; + + if (m_headerPtr >= SLOW_DATA_FULL_BLOCK_SIZE) + m_headerPtr = 0U; +} + +void CSlowDataEncoder::getTextData(unsigned char* data) +{ + wxASSERT(data != NULL); + + data[0U] = m_textData[m_textPtr++] ^ SCRAMBLER_BYTE1; + data[1U] = m_textData[m_textPtr++] ^ SCRAMBLER_BYTE2; + data[2U] = m_textData[m_textPtr++] ^ SCRAMBLER_BYTE3; + + if (m_textPtr >= SLOW_DATA_FULL_BLOCK_SIZE) + m_textPtr = 0U; +} + +void CSlowDataEncoder::sync() +{ + m_headerPtr = 0U; + m_textPtr = 0U; +} + +void CSlowDataEncoder::reset() +{ + m_headerPtr = 0U; + m_textPtr = 0U; + + ::memset(m_headerData, 'f', SLOW_DATA_FULL_BLOCK_SIZE); + ::memset(m_textData, 'f', SLOW_DATA_FULL_BLOCK_SIZE); +} + +void CSlowDataEncoder::setHeaderData(const CHeaderData& header) +{ + ::memset(m_headerData, 'f', SLOW_DATA_FULL_BLOCK_SIZE); + + m_headerData[0U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[1U] = header.getFlag1(); + m_headerData[2U] = header.getFlag2(); + m_headerData[3U] = header.getFlag3(); + m_headerData[4U] = header.getRptCall2().GetChar(0); + m_headerData[5U] = header.getRptCall2().GetChar(1); + + m_headerData[6U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[7U] = header.getRptCall2().GetChar(2); + m_headerData[8U] = header.getRptCall2().GetChar(3); + m_headerData[9U] = header.getRptCall2().GetChar(4); + m_headerData[10U] = header.getRptCall2().GetChar(5); + m_headerData[11U] = header.getRptCall2().GetChar(6); + + m_headerData[12U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[13U] = header.getRptCall2().GetChar(7); + m_headerData[14U] = header.getRptCall1().GetChar(0); + m_headerData[15U] = header.getRptCall1().GetChar(1); + m_headerData[16U] = header.getRptCall1().GetChar(2); + m_headerData[17U] = header.getRptCall1().GetChar(3); + + m_headerData[18U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[19U] = header.getRptCall1().GetChar(4); + m_headerData[20U] = header.getRptCall1().GetChar(5); + m_headerData[21U] = header.getRptCall1().GetChar(6); + m_headerData[22U] = header.getRptCall1().GetChar(7); + m_headerData[23U] = header.getYourCall().GetChar(0); + + m_headerData[24U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[25U] = header.getYourCall().GetChar(1); + m_headerData[26U] = header.getYourCall().GetChar(2); + m_headerData[27U] = header.getYourCall().GetChar(3); + m_headerData[28U] = header.getYourCall().GetChar(4); + m_headerData[29U] = header.getYourCall().GetChar(5); + + m_headerData[30U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[31U] = header.getYourCall().GetChar(6); + m_headerData[32U] = header.getYourCall().GetChar(7); + m_headerData[33U] = header.getMyCall1().GetChar(0); + m_headerData[34U] = header.getMyCall1().GetChar(1); + m_headerData[35U] = header.getMyCall1().GetChar(2); + + m_headerData[36U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[37U] = header.getMyCall1().GetChar(3); + m_headerData[38U] = header.getMyCall1().GetChar(4); + m_headerData[39U] = header.getMyCall1().GetChar(5); + m_headerData[40U] = header.getMyCall1().GetChar(6); + m_headerData[41U] = header.getMyCall1().GetChar(7); + + m_headerData[42U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[43U] = header.getMyCall2().GetChar(0); + m_headerData[44U] = header.getMyCall2().GetChar(1); + m_headerData[45U] = header.getMyCall2().GetChar(2); + m_headerData[46U] = header.getMyCall2().GetChar(3); + + CCCITTChecksumReverse cksum; + cksum.update(m_headerData + 1U, 5U); + cksum.update(m_headerData + 7U, 5U); + cksum.update(m_headerData + 13U, 5U); + cksum.update(m_headerData + 19U, 5U); + cksum.update(m_headerData + 25U, 5U); + cksum.update(m_headerData + 31U, 5U); + cksum.update(m_headerData + 37U, 5U); + cksum.update(m_headerData + 43U, 4U); + + unsigned char checkSum[2U]; + cksum.result(checkSum); + + m_headerData[47U] = checkSum[0]; + + m_headerData[48U] = SLOW_DATA_TYPE_HEADER | 1U; + m_headerData[49U] = checkSum[1]; + + m_headerPtr = 0U; +} + +void CSlowDataEncoder::setTextData(const wxString& text) +{ + if (text.IsEmpty()) + return; + + ::memset(m_textData, 'f', SLOW_DATA_FULL_BLOCK_SIZE); + + m_textData[0U] = SLOW_DATA_TYPE_TEXT | 0U; + m_textData[1U] = text.GetChar(0); + m_textData[2U] = text.GetChar(1); + m_textData[3U] = text.GetChar(2); + m_textData[4U] = text.GetChar(3); + m_textData[5U] = text.GetChar(4); + + m_textData[6U] = SLOW_DATA_TYPE_TEXT | 1U; + m_textData[7U] = text.GetChar(5); + m_textData[8U] = text.GetChar(6); + m_textData[9U] = text.GetChar(7); + m_textData[10U] = text.GetChar(8); + m_textData[11U] = text.GetChar(9); + + m_textData[12U] = SLOW_DATA_TYPE_TEXT | 2U; + m_textData[13U] = text.GetChar(10); + m_textData[14U] = text.GetChar(11); + m_textData[15U] = text.GetChar(12); + m_textData[16U] = text.GetChar(13); + m_textData[17U] = text.GetChar(14); + + m_textData[18U] = SLOW_DATA_TYPE_TEXT | 3U; + m_textData[19U] = text.GetChar(15); + m_textData[20U] = text.GetChar(16); + m_textData[21U] = text.GetChar(17); + m_textData[22U] = text.GetChar(18); + m_textData[23U] = text.GetChar(19); + + m_textPtr = 0U; +} + diff --git a/Common/SlowDataEncoder.h b/Common/SlowDataEncoder.h new file mode 100644 index 0000000..f3c534e --- /dev/null +++ b/Common/SlowDataEncoder.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef SlowDataEncoder_H +#define SlowDataEncoder_H + +#include "HeaderData.h" + +#include + +class CSlowDataEncoder { +public: + CSlowDataEncoder(); + ~CSlowDataEncoder(); + + void setHeaderData(const CHeaderData& header); + void setTextData(const wxString& text); + + void getHeaderData(unsigned char* data); + void getTextData(unsigned char* data); + + void reset(); + void sync(); + +private: + unsigned char* m_headerData; + unsigned char* m_textData; + unsigned int m_headerPtr; + unsigned int m_textPtr; +}; + +#endif diff --git a/Common/SoundCardController.cpp b/Common/SoundCardController.cpp new file mode 100644 index 0000000..5e83c8a --- /dev/null +++ b/Common/SoundCardController.cpp @@ -0,0 +1,1095 @@ +/* + * Copyright (C) 2009-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CCITTChecksumReverse.h" +#include "SoundCardController.h" +#include "DStarDefines.h" + +// #define AUDIO_LOOPBACK + +const unsigned int BUFFER_LENGTH = 200U; + +const unsigned int NIBBLE_BITS[] = {0U, 1U, 1U, 2U, 1U, 2U, 2U, 3U, 1U, 2U, 2U, 3U, 2U, 3U, 3U, 4U}; + +const unsigned int DV_FRAME_LENGTH_BITS = DV_FRAME_LENGTH_BYTES * 8U; + +const unsigned int MAX_SYNC_BITS = 50U * DV_FRAME_LENGTH_BITS; + +const unsigned int FEC_SECTION_LENGTH_BITS = 660U; + +// D-Star bit order version of 0x55 0x55 0x6E 0x0A +const wxUint32 FRAME_SYNC_DATA = 0x00557650U; +const wxUint32 FRAME_SYNC_MASK = 0x00FFFFFFU; +const unsigned int FRAME_SYNC_ERRS = 2U; + +// D-Star bit order version of 0x55 0x2D 0x16 +const wxUint32 DATA_SYNC_DATA = 0x00AAB468U; +const wxUint32 DATA_SYNC_MASK = 0x00FFFFFFU; +const unsigned int DATA_SYNC_ERRS = 2U; + +// D-Star bit order version of 0x55 0x55 0xC8 0x7A +const wxUint32 END_SYNC_DATA = 0xAAAA135EU; +const wxUint32 END_SYNC_MASK = 0xFFFFFFFFU; +const unsigned int END_SYNC_ERRS = 3U; + +const unsigned char BIT_SYNC = 0xAAU; + +const unsigned char FRAME_SYNC0 = 0xEAU; +const unsigned char FRAME_SYNC1 = 0xA6U; +const unsigned char FRAME_SYNC2 = 0x00U; + +const unsigned char BIT_MASK_TABLE0[] = {0x7FU, 0xBFU, 0xDFU, 0xEFU, 0xF7U, 0xFBU, 0xFDU, 0xFEU}; +const unsigned char BIT_MASK_TABLE1[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; +const unsigned char BIT_MASK_TABLE2[] = {0xFEU, 0xFDU, 0xFBU, 0xF7U, 0xEFU, 0xDFU, 0xBFU, 0x7FU}; +const unsigned char BIT_MASK_TABLE3[] = {0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U}; + +#define WRITE_BIT1(p,i,b) p[(i)/8] = (b) ? (p[(i)/8] | BIT_MASK_TABLE1[(i)%8]) : (p[(i)/8] & BIT_MASK_TABLE0[(i)%8]) +#define READ_BIT1(p,i) (p[(i)/8] & BIT_MASK_TABLE1[(i)%8]) + +#define WRITE_BIT2(p,i,b) p[(i)/8] = (b) ? (p[(i)/8] | BIT_MASK_TABLE3[(i)%8]) : (p[(i)/8] & BIT_MASK_TABLE2[(i)%8]) +#define READ_BIT2(p,i) (p[(i)/8] & BIT_MASK_TABLE3[(i)%8]) + +const unsigned char INTERLEAVE_TABLE_TX[] = { + 0x00U, 0x04U, 0x04U, 0x00U, 0x07U, 0x04U, 0x0BU, 0x00U, 0x0EU, 0x04U, + 0x12U, 0x00U, 0x15U, 0x04U, 0x19U, 0x00U, 0x1CU, 0x04U, 0x20U, 0x00U, + 0x23U, 0x04U, 0x27U, 0x00U, 0x2AU, 0x04U, 0x2DU, 0x07U, 0x31U, 0x02U, + 0x34U, 0x05U, 0x38U, 0x00U, 0x3BU, 0x03U, 0x3EU, 0x06U, 0x42U, 0x01U, + 0x45U, 0x04U, 0x48U, 0x07U, 0x4CU, 0x02U, 0x4FU, 0x05U, 0x00U, 0x05U, + 0x04U, 0x01U, 0x07U, 0x05U, 0x0BU, 0x01U, 0x0EU, 0x05U, 0x12U, 0x01U, + 0x15U, 0x05U, 0x19U, 0x01U, 0x1CU, 0x05U, 0x20U, 0x01U, 0x23U, 0x05U, + 0x27U, 0x01U, 0x2AU, 0x05U, 0x2EU, 0x00U, 0x31U, 0x03U, 0x34U, 0x06U, + 0x38U, 0x01U, 0x3BU, 0x04U, 0x3EU, 0x07U, 0x42U, 0x02U, 0x45U, 0x05U, + 0x49U, 0x00U, 0x4CU, 0x03U, 0x4FU, 0x06U, 0x00U, 0x06U, 0x04U, 0x02U, + 0x07U, 0x06U, 0x0BU, 0x02U, 0x0EU, 0x06U, 0x12U, 0x02U, 0x15U, 0x06U, + 0x19U, 0x02U, 0x1CU, 0x06U, 0x20U, 0x02U, 0x23U, 0x06U, 0x27U, 0x02U, + 0x2AU, 0x06U, 0x2EU, 0x01U, 0x31U, 0x04U, 0x34U, 0x07U, 0x38U, 0x02U, + 0x3BU, 0x05U, 0x3FU, 0x00U, 0x42U, 0x03U, 0x45U, 0x06U, 0x49U, 0x01U, + 0x4CU, 0x04U, 0x4FU, 0x07U, 0x00U, 0x07U, 0x04U, 0x03U, 0x07U, 0x07U, + 0x0BU, 0x03U, 0x0EU, 0x07U, 0x12U, 0x03U, 0x15U, 0x07U, 0x19U, 0x03U, + 0x1CU, 0x07U, 0x20U, 0x03U, 0x23U, 0x07U, 0x27U, 0x03U, 0x2AU, 0x07U, + 0x2EU, 0x02U, 0x31U, 0x05U, 0x35U, 0x00U, 0x38U, 0x03U, 0x3BU, 0x06U, + 0x3FU, 0x01U, 0x42U, 0x04U, 0x45U, 0x07U, 0x49U, 0x02U, 0x4CU, 0x05U, + 0x50U, 0x00U, 0x01U, 0x00U, 0x04U, 0x04U, 0x08U, 0x00U, 0x0BU, 0x04U, + 0x0FU, 0x00U, 0x12U, 0x04U, 0x16U, 0x00U, 0x19U, 0x04U, 0x1DU, 0x00U, + 0x20U, 0x04U, 0x24U, 0x00U, 0x27U, 0x04U, 0x2BU, 0x00U, 0x2EU, 0x03U, + 0x31U, 0x06U, 0x35U, 0x01U, 0x38U, 0x04U, 0x3BU, 0x07U, 0x3FU, 0x02U, + 0x42U, 0x05U, 0x46U, 0x00U, 0x49U, 0x03U, 0x4CU, 0x06U, 0x50U, 0x01U, + 0x01U, 0x01U, 0x04U, 0x05U, 0x08U, 0x01U, 0x0BU, 0x05U, 0x0FU, 0x01U, + 0x12U, 0x05U, 0x16U, 0x01U, 0x19U, 0x05U, 0x1DU, 0x01U, 0x20U, 0x05U, + 0x24U, 0x01U, 0x27U, 0x05U, 0x2BU, 0x01U, 0x2EU, 0x04U, 0x31U, 0x07U, + 0x35U, 0x02U, 0x38U, 0x05U, 0x3CU, 0x00U, 0x3FU, 0x03U, 0x42U, 0x06U, + 0x46U, 0x01U, 0x49U, 0x04U, 0x4CU, 0x07U, 0x50U, 0x02U, 0x01U, 0x02U, + 0x04U, 0x06U, 0x08U, 0x02U, 0x0BU, 0x06U, 0x0FU, 0x02U, 0x12U, 0x06U, + 0x16U, 0x02U, 0x19U, 0x06U, 0x1DU, 0x02U, 0x20U, 0x06U, 0x24U, 0x02U, + 0x27U, 0x06U, 0x2BU, 0x02U, 0x2EU, 0x05U, 0x32U, 0x00U, 0x35U, 0x03U, + 0x38U, 0x06U, 0x3CU, 0x01U, 0x3FU, 0x04U, 0x42U, 0x07U, 0x46U, 0x02U, + 0x49U, 0x05U, 0x4DU, 0x00U, 0x50U, 0x03U, 0x01U, 0x03U, 0x04U, 0x07U, + 0x08U, 0x03U, 0x0BU, 0x07U, 0x0FU, 0x03U, 0x12U, 0x07U, 0x16U, 0x03U, + 0x19U, 0x07U, 0x1DU, 0x03U, 0x20U, 0x07U, 0x24U, 0x03U, 0x27U, 0x07U, + 0x2BU, 0x03U, 0x2EU, 0x06U, 0x32U, 0x01U, 0x35U, 0x04U, 0x38U, 0x07U, + 0x3CU, 0x02U, 0x3FU, 0x05U, 0x43U, 0x00U, 0x46U, 0x03U, 0x49U, 0x06U, + 0x4DU, 0x01U, 0x50U, 0x04U, 0x01U, 0x04U, 0x05U, 0x00U, 0x08U, 0x04U, + 0x0CU, 0x00U, 0x0FU, 0x04U, 0x13U, 0x00U, 0x16U, 0x04U, 0x1AU, 0x00U, + 0x1DU, 0x04U, 0x21U, 0x00U, 0x24U, 0x04U, 0x28U, 0x00U, 0x2BU, 0x04U, + 0x2EU, 0x07U, 0x32U, 0x02U, 0x35U, 0x05U, 0x39U, 0x00U, 0x3CU, 0x03U, + 0x3FU, 0x06U, 0x43U, 0x01U, 0x46U, 0x04U, 0x49U, 0x07U, 0x4DU, 0x02U, + 0x50U, 0x05U, 0x01U, 0x05U, 0x05U, 0x01U, 0x08U, 0x05U, 0x0CU, 0x01U, + 0x0FU, 0x05U, 0x13U, 0x01U, 0x16U, 0x05U, 0x1AU, 0x01U, 0x1DU, 0x05U, + 0x21U, 0x01U, 0x24U, 0x05U, 0x28U, 0x01U, 0x2BU, 0x05U, 0x2FU, 0x00U, + 0x32U, 0x03U, 0x35U, 0x06U, 0x39U, 0x01U, 0x3CU, 0x04U, 0x3FU, 0x07U, + 0x43U, 0x02U, 0x46U, 0x05U, 0x4AU, 0x00U, 0x4DU, 0x03U, 0x50U, 0x06U, + 0x01U, 0x06U, 0x05U, 0x02U, 0x08U, 0x06U, 0x0CU, 0x02U, 0x0FU, 0x06U, + 0x13U, 0x02U, 0x16U, 0x06U, 0x1AU, 0x02U, 0x1DU, 0x06U, 0x21U, 0x02U, + 0x24U, 0x06U, 0x28U, 0x02U, 0x2BU, 0x06U, 0x2FU, 0x01U, 0x32U, 0x04U, + 0x35U, 0x07U, 0x39U, 0x02U, 0x3CU, 0x05U, 0x40U, 0x00U, 0x43U, 0x03U, + 0x46U, 0x06U, 0x4AU, 0x01U, 0x4DU, 0x04U, 0x50U, 0x07U, 0x01U, 0x07U, + 0x05U, 0x03U, 0x08U, 0x07U, 0x0CU, 0x03U, 0x0FU, 0x07U, 0x13U, 0x03U, + 0x16U, 0x07U, 0x1AU, 0x03U, 0x1DU, 0x07U, 0x21U, 0x03U, 0x24U, 0x07U, + 0x28U, 0x03U, 0x2BU, 0x07U, 0x2FU, 0x02U, 0x32U, 0x05U, 0x36U, 0x00U, + 0x39U, 0x03U, 0x3CU, 0x06U, 0x40U, 0x01U, 0x43U, 0x04U, 0x46U, 0x07U, + 0x4AU, 0x02U, 0x4DU, 0x05U, 0x51U, 0x00U, 0x02U, 0x00U, 0x05U, 0x04U, + 0x09U, 0x00U, 0x0CU, 0x04U, 0x10U, 0x00U, 0x13U, 0x04U, 0x17U, 0x00U, + 0x1AU, 0x04U, 0x1EU, 0x00U, 0x21U, 0x04U, 0x25U, 0x00U, 0x28U, 0x04U, + 0x2CU, 0x00U, 0x2FU, 0x03U, 0x32U, 0x06U, 0x36U, 0x01U, 0x39U, 0x04U, + 0x3CU, 0x07U, 0x40U, 0x02U, 0x43U, 0x05U, 0x47U, 0x00U, 0x4AU, 0x03U, + 0x4DU, 0x06U, 0x51U, 0x01U, 0x02U, 0x01U, 0x05U, 0x05U, 0x09U, 0x01U, + 0x0CU, 0x05U, 0x10U, 0x01U, 0x13U, 0x05U, 0x17U, 0x01U, 0x1AU, 0x05U, + 0x1EU, 0x01U, 0x21U, 0x05U, 0x25U, 0x01U, 0x28U, 0x05U, 0x2CU, 0x01U, + 0x2FU, 0x04U, 0x32U, 0x07U, 0x36U, 0x02U, 0x39U, 0x05U, 0x3DU, 0x00U, + 0x40U, 0x03U, 0x43U, 0x06U, 0x47U, 0x01U, 0x4AU, 0x04U, 0x4DU, 0x07U, + 0x51U, 0x02U, 0x02U, 0x02U, 0x05U, 0x06U, 0x09U, 0x02U, 0x0CU, 0x06U, + 0x10U, 0x02U, 0x13U, 0x06U, 0x17U, 0x02U, 0x1AU, 0x06U, 0x1EU, 0x02U, + 0x21U, 0x06U, 0x25U, 0x02U, 0x28U, 0x06U, 0x2CU, 0x02U, 0x2FU, 0x05U, + 0x33U, 0x00U, 0x36U, 0x03U, 0x39U, 0x06U, 0x3DU, 0x01U, 0x40U, 0x04U, + 0x43U, 0x07U, 0x47U, 0x02U, 0x4AU, 0x05U, 0x4EU, 0x00U, 0x51U, 0x03U, + 0x02U, 0x03U, 0x05U, 0x07U, 0x09U, 0x03U, 0x0CU, 0x07U, 0x10U, 0x03U, + 0x13U, 0x07U, 0x17U, 0x03U, 0x1AU, 0x07U, 0x1EU, 0x03U, 0x21U, 0x07U, + 0x25U, 0x03U, 0x28U, 0x07U, 0x2CU, 0x03U, 0x2FU, 0x06U, 0x33U, 0x01U, + 0x36U, 0x04U, 0x39U, 0x07U, 0x3DU, 0x02U, 0x40U, 0x05U, 0x44U, 0x00U, + 0x47U, 0x03U, 0x4AU, 0x06U, 0x4EU, 0x01U, 0x51U, 0x04U, 0x02U, 0x04U, + 0x06U, 0x00U, 0x09U, 0x04U, 0x0DU, 0x00U, 0x10U, 0x04U, 0x14U, 0x00U, + 0x17U, 0x04U, 0x1BU, 0x00U, 0x1EU, 0x04U, 0x22U, 0x00U, 0x25U, 0x04U, + 0x29U, 0x00U, 0x2CU, 0x04U, 0x2FU, 0x07U, 0x33U, 0x02U, 0x36U, 0x05U, + 0x3AU, 0x00U, 0x3DU, 0x03U, 0x40U, 0x06U, 0x44U, 0x01U, 0x47U, 0x04U, + 0x4AU, 0x07U, 0x4EU, 0x02U, 0x51U, 0x05U, 0x02U, 0x05U, 0x06U, 0x01U, + 0x09U, 0x05U, 0x0DU, 0x01U, 0x10U, 0x05U, 0x14U, 0x01U, 0x17U, 0x05U, + 0x1BU, 0x01U, 0x1EU, 0x05U, 0x22U, 0x01U, 0x25U, 0x05U, 0x29U, 0x01U, + 0x2CU, 0x05U, 0x30U, 0x00U, 0x33U, 0x03U, 0x36U, 0x06U, 0x3AU, 0x01U, + 0x3DU, 0x04U, 0x40U, 0x07U, 0x44U, 0x02U, 0x47U, 0x05U, 0x4BU, 0x00U, + 0x4EU, 0x03U, 0x51U, 0x06U, 0x02U, 0x06U, 0x06U, 0x02U, 0x09U, 0x06U, + 0x0DU, 0x02U, 0x10U, 0x06U, 0x14U, 0x02U, 0x17U, 0x06U, 0x1BU, 0x02U, + 0x1EU, 0x06U, 0x22U, 0x02U, 0x25U, 0x06U, 0x29U, 0x02U, 0x2CU, 0x06U, + 0x30U, 0x01U, 0x33U, 0x04U, 0x36U, 0x07U, 0x3AU, 0x02U, 0x3DU, 0x05U, + 0x41U, 0x00U, 0x44U, 0x03U, 0x47U, 0x06U, 0x4BU, 0x01U, 0x4EU, 0x04U, + 0x51U, 0x07U, 0x02U, 0x07U, 0x06U, 0x03U, 0x09U, 0x07U, 0x0DU, 0x03U, + 0x10U, 0x07U, 0x14U, 0x03U, 0x17U, 0x07U, 0x1BU, 0x03U, 0x1EU, 0x07U, + 0x22U, 0x03U, 0x25U, 0x07U, 0x29U, 0x03U, 0x2CU, 0x07U, 0x30U, 0x02U, + 0x33U, 0x05U, 0x37U, 0x00U, 0x3AU, 0x03U, 0x3DU, 0x06U, 0x41U, 0x01U, + 0x44U, 0x04U, 0x47U, 0x07U, 0x4BU, 0x02U, 0x4EU, 0x05U, 0x52U, 0x00U, + 0x03U, 0x00U, 0x06U, 0x04U, 0x0AU, 0x00U, 0x0DU, 0x04U, 0x11U, 0x00U, + 0x14U, 0x04U, 0x18U, 0x00U, 0x1BU, 0x04U, 0x1FU, 0x00U, 0x22U, 0x04U, + 0x26U, 0x00U, 0x29U, 0x04U, 0x2DU, 0x00U, 0x30U, 0x03U, 0x33U, 0x06U, + 0x37U, 0x01U, 0x3AU, 0x04U, 0x3DU, 0x07U, 0x41U, 0x02U, 0x44U, 0x05U, + 0x48U, 0x00U, 0x4BU, 0x03U, 0x4EU, 0x06U, 0x52U, 0x01U, 0x03U, 0x01U, + 0x06U, 0x05U, 0x0AU, 0x01U, 0x0DU, 0x05U, 0x11U, 0x01U, 0x14U, 0x05U, + 0x18U, 0x01U, 0x1BU, 0x05U, 0x1FU, 0x01U, 0x22U, 0x05U, 0x26U, 0x01U, + 0x29U, 0x05U, 0x2DU, 0x01U, 0x30U, 0x04U, 0x33U, 0x07U, 0x37U, 0x02U, + 0x3AU, 0x05U, 0x3EU, 0x00U, 0x41U, 0x03U, 0x44U, 0x06U, 0x48U, 0x01U, + 0x4BU, 0x04U, 0x4EU, 0x07U, 0x52U, 0x02U, 0x03U, 0x02U, 0x06U, 0x06U, + 0x0AU, 0x02U, 0x0DU, 0x06U, 0x11U, 0x02U, 0x14U, 0x06U, 0x18U, 0x02U, + 0x1BU, 0x06U, 0x1FU, 0x02U, 0x22U, 0x06U, 0x26U, 0x02U, 0x29U, 0x06U, + 0x2DU, 0x02U, 0x30U, 0x05U, 0x34U, 0x00U, 0x37U, 0x03U, 0x3AU, 0x06U, + 0x3EU, 0x01U, 0x41U, 0x04U, 0x44U, 0x07U, 0x48U, 0x02U, 0x4BU, 0x05U, + 0x4FU, 0x00U, 0x52U, 0x03U, 0x03U, 0x03U, 0x06U, 0x07U, 0x0AU, 0x03U, + 0x0DU, 0x07U, 0x11U, 0x03U, 0x14U, 0x07U, 0x18U, 0x03U, 0x1BU, 0x07U, + 0x1FU, 0x03U, 0x22U, 0x07U, 0x26U, 0x03U, 0x29U, 0x07U, 0x2DU, 0x03U, + 0x30U, 0x06U, 0x34U, 0x01U, 0x37U, 0x04U, 0x3AU, 0x07U, 0x3EU, 0x02U, + 0x41U, 0x05U, 0x45U, 0x00U, 0x48U, 0x03U, 0x4BU, 0x06U, 0x4FU, 0x01U, + 0x52U, 0x04U, 0x03U, 0x04U, 0x07U, 0x00U, 0x0AU, 0x04U, 0x0EU, 0x00U, + 0x11U, 0x04U, 0x15U, 0x00U, 0x18U, 0x04U, 0x1CU, 0x00U, 0x1FU, 0x04U, + 0x23U, 0x00U, 0x26U, 0x04U, 0x2AU, 0x00U, 0x2DU, 0x04U, 0x30U, 0x07U, + 0x34U, 0x02U, 0x37U, 0x05U, 0x3BU, 0x00U, 0x3EU, 0x03U, 0x41U, 0x06U, + 0x45U, 0x01U, 0x48U, 0x04U, 0x4BU, 0x07U, 0x4FU, 0x02U, 0x52U, 0x05U, + 0x03U, 0x05U, 0x07U, 0x01U, 0x0AU, 0x05U, 0x0EU, 0x01U, 0x11U, 0x05U, + 0x15U, 0x01U, 0x18U, 0x05U, 0x1CU, 0x01U, 0x1FU, 0x05U, 0x23U, 0x01U, + 0x26U, 0x05U, 0x2AU, 0x01U, 0x2DU, 0x05U, 0x31U, 0x00U, 0x34U, 0x03U, + 0x37U, 0x06U, 0x3BU, 0x01U, 0x3EU, 0x04U, 0x41U, 0x07U, 0x45U, 0x02U, + 0x48U, 0x05U, 0x4CU, 0x00U, 0x4FU, 0x03U, 0x52U, 0x06U, 0x03U, 0x06U, + 0x07U, 0x02U, 0x0AU, 0x06U, 0x0EU, 0x02U, 0x11U, 0x06U, 0x15U, 0x02U, + 0x18U, 0x06U, 0x1CU, 0x02U, 0x1FU, 0x06U, 0x23U, 0x02U, 0x26U, 0x06U, + 0x2AU, 0x02U, 0x2DU, 0x06U, 0x31U, 0x01U, 0x34U, 0x04U, 0x37U, 0x07U, + 0x3BU, 0x02U, 0x3EU, 0x05U, 0x42U, 0x00U, 0x45U, 0x03U, 0x48U, 0x06U, + 0x4CU, 0x01U, 0x4FU, 0x04U, 0x52U, 0x07U, 0x03U, 0x07U, 0x07U, 0x03U, + 0x0AU, 0x07U, 0x0EU, 0x03U, 0x11U, 0x07U, 0x15U, 0x03U, 0x18U, 0x07U, + 0x1CU, 0x03U, 0x1FU, 0x07U, 0x23U, 0x03U, 0x26U, 0x07U, 0x2AU, 0x03U +}; + +const unsigned char INTERLEAVE_TABLE_RX[] = { + 0x00U, 0x00U, 0x03U, 0x00U, 0x06U, 0x00U, 0x09U, 0x00U, 0x0CU, 0x00U, + 0x0FU, 0x00U, 0x12U, 0x00U, 0x15U, 0x00U, 0x18U, 0x00U, 0x1BU, 0x00U, + 0x1EU, 0x00U, 0x21U, 0x00U, 0x24U, 0x00U, 0x27U, 0x00U, 0x2AU, 0x00U, + 0x2DU, 0x00U, 0x30U, 0x00U, 0x33U, 0x00U, 0x36U, 0x00U, 0x39U, 0x00U, + 0x3CU, 0x00U, 0x3FU, 0x00U, 0x42U, 0x00U, 0x45U, 0x00U, 0x48U, 0x00U, + 0x4BU, 0x00U, 0x4EU, 0x00U, 0x51U, 0x00U, 0x00U, 0x01U, 0x03U, 0x01U, + 0x06U, 0x01U, 0x09U, 0x01U, 0x0CU, 0x01U, 0x0FU, 0x01U, 0x12U, 0x01U, + 0x15U, 0x01U, 0x18U, 0x01U, 0x1BU, 0x01U, 0x1EU, 0x01U, 0x21U, 0x01U, + 0x24U, 0x01U, 0x27U, 0x01U, 0x2AU, 0x01U, 0x2DU, 0x01U, 0x30U, 0x01U, + 0x33U, 0x01U, 0x36U, 0x01U, 0x39U, 0x01U, 0x3CU, 0x01U, 0x3FU, 0x01U, + 0x42U, 0x01U, 0x45U, 0x01U, 0x48U, 0x01U, 0x4BU, 0x01U, 0x4EU, 0x01U, + 0x51U, 0x01U, 0x00U, 0x02U, 0x03U, 0x02U, 0x06U, 0x02U, 0x09U, 0x02U, + 0x0CU, 0x02U, 0x0FU, 0x02U, 0x12U, 0x02U, 0x15U, 0x02U, 0x18U, 0x02U, + 0x1BU, 0x02U, 0x1EU, 0x02U, 0x21U, 0x02U, 0x24U, 0x02U, 0x27U, 0x02U, + 0x2AU, 0x02U, 0x2DU, 0x02U, 0x30U, 0x02U, 0x33U, 0x02U, 0x36U, 0x02U, + 0x39U, 0x02U, 0x3CU, 0x02U, 0x3FU, 0x02U, 0x42U, 0x02U, 0x45U, 0x02U, + 0x48U, 0x02U, 0x4BU, 0x02U, 0x4EU, 0x02U, 0x51U, 0x02U, 0x00U, 0x03U, + 0x03U, 0x03U, 0x06U, 0x03U, 0x09U, 0x03U, 0x0CU, 0x03U, 0x0FU, 0x03U, + 0x12U, 0x03U, 0x15U, 0x03U, 0x18U, 0x03U, 0x1BU, 0x03U, 0x1EU, 0x03U, + 0x21U, 0x03U, 0x24U, 0x03U, 0x27U, 0x03U, 0x2AU, 0x03U, 0x2DU, 0x03U, + 0x30U, 0x03U, 0x33U, 0x03U, 0x36U, 0x03U, 0x39U, 0x03U, 0x3CU, 0x03U, + 0x3FU, 0x03U, 0x42U, 0x03U, 0x45U, 0x03U, 0x48U, 0x03U, 0x4BU, 0x03U, + 0x4EU, 0x03U, 0x51U, 0x03U, 0x00U, 0x04U, 0x03U, 0x04U, 0x06U, 0x04U, + 0x09U, 0x04U, 0x0CU, 0x04U, 0x0FU, 0x04U, 0x12U, 0x04U, 0x15U, 0x04U, + 0x18U, 0x04U, 0x1BU, 0x04U, 0x1EU, 0x04U, 0x21U, 0x04U, 0x24U, 0x04U, + 0x27U, 0x04U, 0x2AU, 0x04U, 0x2DU, 0x04U, 0x30U, 0x04U, 0x33U, 0x04U, + 0x36U, 0x04U, 0x39U, 0x04U, 0x3CU, 0x04U, 0x3FU, 0x04U, 0x42U, 0x04U, + 0x45U, 0x04U, 0x48U, 0x04U, 0x4BU, 0x04U, 0x4EU, 0x04U, 0x51U, 0x04U, + 0x00U, 0x05U, 0x03U, 0x05U, 0x06U, 0x05U, 0x09U, 0x05U, 0x0CU, 0x05U, + 0x0FU, 0x05U, 0x12U, 0x05U, 0x15U, 0x05U, 0x18U, 0x05U, 0x1BU, 0x05U, + 0x1EU, 0x05U, 0x21U, 0x05U, 0x24U, 0x05U, 0x27U, 0x05U, 0x2AU, 0x05U, + 0x2DU, 0x05U, 0x30U, 0x05U, 0x33U, 0x05U, 0x36U, 0x05U, 0x39U, 0x05U, + 0x3CU, 0x05U, 0x3FU, 0x05U, 0x42U, 0x05U, 0x45U, 0x05U, 0x48U, 0x05U, + 0x4BU, 0x05U, 0x4EU, 0x05U, 0x51U, 0x05U, 0x00U, 0x06U, 0x03U, 0x06U, + 0x06U, 0x06U, 0x09U, 0x06U, 0x0CU, 0x06U, 0x0FU, 0x06U, 0x12U, 0x06U, + 0x15U, 0x06U, 0x18U, 0x06U, 0x1BU, 0x06U, 0x1EU, 0x06U, 0x21U, 0x06U, + 0x24U, 0x06U, 0x27U, 0x06U, 0x2AU, 0x06U, 0x2DU, 0x06U, 0x30U, 0x06U, + 0x33U, 0x06U, 0x36U, 0x06U, 0x39U, 0x06U, 0x3CU, 0x06U, 0x3FU, 0x06U, + 0x42U, 0x06U, 0x45U, 0x06U, 0x48U, 0x06U, 0x4BU, 0x06U, 0x4EU, 0x06U, + 0x51U, 0x06U, 0x00U, 0x07U, 0x03U, 0x07U, 0x06U, 0x07U, 0x09U, 0x07U, + 0x0CU, 0x07U, 0x0FU, 0x07U, 0x12U, 0x07U, 0x15U, 0x07U, 0x18U, 0x07U, + 0x1BU, 0x07U, 0x1EU, 0x07U, 0x21U, 0x07U, 0x24U, 0x07U, 0x27U, 0x07U, + 0x2AU, 0x07U, 0x2DU, 0x07U, 0x30U, 0x07U, 0x33U, 0x07U, 0x36U, 0x07U, + 0x39U, 0x07U, 0x3CU, 0x07U, 0x3FU, 0x07U, 0x42U, 0x07U, 0x45U, 0x07U, + 0x48U, 0x07U, 0x4BU, 0x07U, 0x4EU, 0x07U, 0x51U, 0x07U, 0x01U, 0x00U, + 0x04U, 0x00U, 0x07U, 0x00U, 0x0AU, 0x00U, 0x0DU, 0x00U, 0x10U, 0x00U, + 0x13U, 0x00U, 0x16U, 0x00U, 0x19U, 0x00U, 0x1CU, 0x00U, 0x1FU, 0x00U, + 0x22U, 0x00U, 0x25U, 0x00U, 0x28U, 0x00U, 0x2BU, 0x00U, 0x2EU, 0x00U, + 0x31U, 0x00U, 0x34U, 0x00U, 0x37U, 0x00U, 0x3AU, 0x00U, 0x3DU, 0x00U, + 0x40U, 0x00U, 0x43U, 0x00U, 0x46U, 0x00U, 0x49U, 0x00U, 0x4CU, 0x00U, + 0x4FU, 0x00U, 0x52U, 0x00U, 0x01U, 0x01U, 0x04U, 0x01U, 0x07U, 0x01U, + 0x0AU, 0x01U, 0x0DU, 0x01U, 0x10U, 0x01U, 0x13U, 0x01U, 0x16U, 0x01U, + 0x19U, 0x01U, 0x1CU, 0x01U, 0x1FU, 0x01U, 0x22U, 0x01U, 0x25U, 0x01U, + 0x28U, 0x01U, 0x2BU, 0x01U, 0x2EU, 0x01U, 0x31U, 0x01U, 0x34U, 0x01U, + 0x37U, 0x01U, 0x3AU, 0x01U, 0x3DU, 0x01U, 0x40U, 0x01U, 0x43U, 0x01U, + 0x46U, 0x01U, 0x49U, 0x01U, 0x4CU, 0x01U, 0x4FU, 0x01U, 0x52U, 0x01U, + 0x01U, 0x02U, 0x04U, 0x02U, 0x07U, 0x02U, 0x0AU, 0x02U, 0x0DU, 0x02U, + 0x10U, 0x02U, 0x13U, 0x02U, 0x16U, 0x02U, 0x19U, 0x02U, 0x1CU, 0x02U, + 0x1FU, 0x02U, 0x22U, 0x02U, 0x25U, 0x02U, 0x28U, 0x02U, 0x2BU, 0x02U, + 0x2EU, 0x02U, 0x31U, 0x02U, 0x34U, 0x02U, 0x37U, 0x02U, 0x3AU, 0x02U, + 0x3DU, 0x02U, 0x40U, 0x02U, 0x43U, 0x02U, 0x46U, 0x02U, 0x49U, 0x02U, + 0x4CU, 0x02U, 0x4FU, 0x02U, 0x52U, 0x02U, 0x01U, 0x03U, 0x04U, 0x03U, + 0x07U, 0x03U, 0x0AU, 0x03U, 0x0DU, 0x03U, 0x10U, 0x03U, 0x13U, 0x03U, + 0x16U, 0x03U, 0x19U, 0x03U, 0x1CU, 0x03U, 0x1FU, 0x03U, 0x22U, 0x03U, + 0x25U, 0x03U, 0x28U, 0x03U, 0x2BU, 0x03U, 0x2EU, 0x03U, 0x31U, 0x03U, + 0x34U, 0x03U, 0x37U, 0x03U, 0x3AU, 0x03U, 0x3DU, 0x03U, 0x40U, 0x03U, + 0x43U, 0x03U, 0x46U, 0x03U, 0x49U, 0x03U, 0x4CU, 0x03U, 0x4FU, 0x03U, + 0x52U, 0x03U, 0x01U, 0x04U, 0x04U, 0x04U, 0x07U, 0x04U, 0x0AU, 0x04U, + 0x0DU, 0x04U, 0x10U, 0x04U, 0x13U, 0x04U, 0x16U, 0x04U, 0x19U, 0x04U, + 0x1CU, 0x04U, 0x1FU, 0x04U, 0x22U, 0x04U, 0x25U, 0x04U, 0x28U, 0x04U, + 0x2BU, 0x04U, 0x2EU, 0x04U, 0x31U, 0x04U, 0x34U, 0x04U, 0x37U, 0x04U, + 0x3AU, 0x04U, 0x3DU, 0x04U, 0x40U, 0x04U, 0x43U, 0x04U, 0x46U, 0x04U, + 0x49U, 0x04U, 0x4CU, 0x04U, 0x4FU, 0x04U, 0x01U, 0x05U, 0x04U, 0x05U, + 0x07U, 0x05U, 0x0AU, 0x05U, 0x0DU, 0x05U, 0x10U, 0x05U, 0x13U, 0x05U, + 0x16U, 0x05U, 0x19U, 0x05U, 0x1CU, 0x05U, 0x1FU, 0x05U, 0x22U, 0x05U, + 0x25U, 0x05U, 0x28U, 0x05U, 0x2BU, 0x05U, 0x2EU, 0x05U, 0x31U, 0x05U, + 0x34U, 0x05U, 0x37U, 0x05U, 0x3AU, 0x05U, 0x3DU, 0x05U, 0x40U, 0x05U, + 0x43U, 0x05U, 0x46U, 0x05U, 0x49U, 0x05U, 0x4CU, 0x05U, 0x4FU, 0x05U, + 0x01U, 0x06U, 0x04U, 0x06U, 0x07U, 0x06U, 0x0AU, 0x06U, 0x0DU, 0x06U, + 0x10U, 0x06U, 0x13U, 0x06U, 0x16U, 0x06U, 0x19U, 0x06U, 0x1CU, 0x06U, + 0x1FU, 0x06U, 0x22U, 0x06U, 0x25U, 0x06U, 0x28U, 0x06U, 0x2BU, 0x06U, + 0x2EU, 0x06U, 0x31U, 0x06U, 0x34U, 0x06U, 0x37U, 0x06U, 0x3AU, 0x06U, + 0x3DU, 0x06U, 0x40U, 0x06U, 0x43U, 0x06U, 0x46U, 0x06U, 0x49U, 0x06U, + 0x4CU, 0x06U, 0x4FU, 0x06U, 0x01U, 0x07U, 0x04U, 0x07U, 0x07U, 0x07U, + 0x0AU, 0x07U, 0x0DU, 0x07U, 0x10U, 0x07U, 0x13U, 0x07U, 0x16U, 0x07U, + 0x19U, 0x07U, 0x1CU, 0x07U, 0x1FU, 0x07U, 0x22U, 0x07U, 0x25U, 0x07U, + 0x28U, 0x07U, 0x2BU, 0x07U, 0x2EU, 0x07U, 0x31U, 0x07U, 0x34U, 0x07U, + 0x37U, 0x07U, 0x3AU, 0x07U, 0x3DU, 0x07U, 0x40U, 0x07U, 0x43U, 0x07U, + 0x46U, 0x07U, 0x49U, 0x07U, 0x4CU, 0x07U, 0x4FU, 0x07U, 0x02U, 0x00U, + 0x05U, 0x00U, 0x08U, 0x00U, 0x0BU, 0x00U, 0x0EU, 0x00U, 0x11U, 0x00U, + 0x14U, 0x00U, 0x17U, 0x00U, 0x1AU, 0x00U, 0x1DU, 0x00U, 0x20U, 0x00U, + 0x23U, 0x00U, 0x26U, 0x00U, 0x29U, 0x00U, 0x2CU, 0x00U, 0x2FU, 0x00U, + 0x32U, 0x00U, 0x35U, 0x00U, 0x38U, 0x00U, 0x3BU, 0x00U, 0x3EU, 0x00U, + 0x41U, 0x00U, 0x44U, 0x00U, 0x47U, 0x00U, 0x4AU, 0x00U, 0x4DU, 0x00U, + 0x50U, 0x00U, 0x02U, 0x01U, 0x05U, 0x01U, 0x08U, 0x01U, 0x0BU, 0x01U, + 0x0EU, 0x01U, 0x11U, 0x01U, 0x14U, 0x01U, 0x17U, 0x01U, 0x1AU, 0x01U, + 0x1DU, 0x01U, 0x20U, 0x01U, 0x23U, 0x01U, 0x26U, 0x01U, 0x29U, 0x01U, + 0x2CU, 0x01U, 0x2FU, 0x01U, 0x32U, 0x01U, 0x35U, 0x01U, 0x38U, 0x01U, + 0x3BU, 0x01U, 0x3EU, 0x01U, 0x41U, 0x01U, 0x44U, 0x01U, 0x47U, 0x01U, + 0x4AU, 0x01U, 0x4DU, 0x01U, 0x50U, 0x01U, 0x02U, 0x02U, 0x05U, 0x02U, + 0x08U, 0x02U, 0x0BU, 0x02U, 0x0EU, 0x02U, 0x11U, 0x02U, 0x14U, 0x02U, + 0x17U, 0x02U, 0x1AU, 0x02U, 0x1DU, 0x02U, 0x20U, 0x02U, 0x23U, 0x02U, + 0x26U, 0x02U, 0x29U, 0x02U, 0x2CU, 0x02U, 0x2FU, 0x02U, 0x32U, 0x02U, + 0x35U, 0x02U, 0x38U, 0x02U, 0x3BU, 0x02U, 0x3EU, 0x02U, 0x41U, 0x02U, + 0x44U, 0x02U, 0x47U, 0x02U, 0x4AU, 0x02U, 0x4DU, 0x02U, 0x50U, 0x02U, + 0x02U, 0x03U, 0x05U, 0x03U, 0x08U, 0x03U, 0x0BU, 0x03U, 0x0EU, 0x03U, + 0x11U, 0x03U, 0x14U, 0x03U, 0x17U, 0x03U, 0x1AU, 0x03U, 0x1DU, 0x03U, + 0x20U, 0x03U, 0x23U, 0x03U, 0x26U, 0x03U, 0x29U, 0x03U, 0x2CU, 0x03U, + 0x2FU, 0x03U, 0x32U, 0x03U, 0x35U, 0x03U, 0x38U, 0x03U, 0x3BU, 0x03U, + 0x3EU, 0x03U, 0x41U, 0x03U, 0x44U, 0x03U, 0x47U, 0x03U, 0x4AU, 0x03U, + 0x4DU, 0x03U, 0x50U, 0x03U, 0x02U, 0x04U, 0x05U, 0x04U, 0x08U, 0x04U, + 0x0BU, 0x04U, 0x0EU, 0x04U, 0x11U, 0x04U, 0x14U, 0x04U, 0x17U, 0x04U, + 0x1AU, 0x04U, 0x1DU, 0x04U, 0x20U, 0x04U, 0x23U, 0x04U, 0x26U, 0x04U, + 0x29U, 0x04U, 0x2CU, 0x04U, 0x2FU, 0x04U, 0x32U, 0x04U, 0x35U, 0x04U, + 0x38U, 0x04U, 0x3BU, 0x04U, 0x3EU, 0x04U, 0x41U, 0x04U, 0x44U, 0x04U, + 0x47U, 0x04U, 0x4AU, 0x04U, 0x4DU, 0x04U, 0x50U, 0x04U, 0x02U, 0x05U, + 0x05U, 0x05U, 0x08U, 0x05U, 0x0BU, 0x05U, 0x0EU, 0x05U, 0x11U, 0x05U, + 0x14U, 0x05U, 0x17U, 0x05U, 0x1AU, 0x05U, 0x1DU, 0x05U, 0x20U, 0x05U, + 0x23U, 0x05U, 0x26U, 0x05U, 0x29U, 0x05U, 0x2CU, 0x05U, 0x2FU, 0x05U, + 0x32U, 0x05U, 0x35U, 0x05U, 0x38U, 0x05U, 0x3BU, 0x05U, 0x3EU, 0x05U, + 0x41U, 0x05U, 0x44U, 0x05U, 0x47U, 0x05U, 0x4AU, 0x05U, 0x4DU, 0x05U, + 0x50U, 0x05U, 0x02U, 0x06U, 0x05U, 0x06U, 0x08U, 0x06U, 0x0BU, 0x06U, + 0x0EU, 0x06U, 0x11U, 0x06U, 0x14U, 0x06U, 0x17U, 0x06U, 0x1AU, 0x06U, + 0x1DU, 0x06U, 0x20U, 0x06U, 0x23U, 0x06U, 0x26U, 0x06U, 0x29U, 0x06U, + 0x2CU, 0x06U, 0x2FU, 0x06U, 0x32U, 0x06U, 0x35U, 0x06U, 0x38U, 0x06U, + 0x3BU, 0x06U, 0x3EU, 0x06U, 0x41U, 0x06U, 0x44U, 0x06U, 0x47U, 0x06U, + 0x4AU, 0x06U, 0x4DU, 0x06U, 0x50U, 0x06U, 0x02U, 0x07U, 0x05U, 0x07U, + 0x08U, 0x07U, 0x0BU, 0x07U, 0x0EU, 0x07U, 0x11U, 0x07U, 0x14U, 0x07U, + 0x17U, 0x07U, 0x1AU, 0x07U, 0x1DU, 0x07U, 0x20U, 0x07U, 0x23U, 0x07U, + 0x26U, 0x07U, 0x29U, 0x07U, 0x2CU, 0x07U, 0x2FU, 0x07U, 0x32U, 0x07U, + 0x35U, 0x07U, 0x38U, 0x07U, 0x3BU, 0x07U, 0x3EU, 0x07U, 0x41U, 0x07U, + 0x44U, 0x07U, 0x47U, 0x07U, 0x4AU, 0x07U, 0x4DU, 0x07U, 0x50U, 0x07U, +}; + +const unsigned char SCRAMBLE_TABLE_TX[] = { + 0x00U, 0xF7U, 0x34U, 0x09U, 0x44U, 0x46U, 0xD7U, 0x06U, 0xB3U, 0x72U, + 0xDEU, 0x42U, 0xF5U, 0xA5U, 0xD8U, 0xF1U, 0x87U, 0x7BU, 0x9AU, 0x04U, + 0x22U, 0xA3U, 0x6BU, 0x83U, 0x59U, 0x39U, 0x6FU, 0xA1U, 0xFAU, 0x52U, + 0xECU, 0xF8U, 0xC3U, 0x3DU, 0x4DU, 0x02U, 0x91U, 0xD1U, 0xB5U, 0xC1U, + 0xACU, 0x9CU, 0xB7U, 0x50U, 0x7DU, 0x29U, 0x76U, 0xFCU, 0xE1U, 0x9EU, + 0x26U, 0x81U, 0xC8U, 0xE8U, 0xDAU, 0x60U, 0x56U, 0xCEU, 0x5BU, 0xA8U, + 0xBEU, 0x14U, 0x3BU, 0xFEU, 0x70U, 0x4FU, 0x93U, 0x40U, 0x64U, 0x74U, + 0x6DU, 0x30U, 0x2BU, 0xE7U, 0x2DU, 0x54U, 0x5FU, 0x8AU, 0x1DU, 0x7FU, + 0xB8U, 0xA7U, 0x49U, 0x20U, 0x32U, 0xBAU, 0x36U, 0x98U, 0x95U, 0xF3U, + 0x06U}; + +const unsigned char SCRAMBLE_TABLE_RX[] = { + 0x70U, 0x4FU, 0x93U, 0x40U, 0x64U, 0x74U, 0x6DU, 0x30U, 0x2BU, 0xE7U, + 0x2DU, 0x54U, 0x5FU, 0x8AU, 0x1DU, 0x7FU, 0xB8U, 0xA7U, 0x49U, 0x20U, + 0x32U, 0xBAU, 0x36U, 0x98U, 0x95U, 0xF3U, 0x16U, 0xAAU, 0x2FU, 0xC5U, + 0x8EU, 0x3FU, 0xDCU, 0xD3U, 0x24U, 0x10U, 0x19U, 0x5DU, 0x1BU, 0xCCU, + 0xCAU, 0x79U, 0x0BU, 0xD5U, 0x97U, 0x62U, 0xC7U, 0x1FU, 0xEEU, 0x69U, + 0x12U, 0x88U, 0x8CU, 0xAEU, 0x0DU, 0x66U, 0xE5U, 0xBCU, 0x85U, 0xEAU, + 0x4BU, 0xB1U, 0xE3U, 0x0FU, 0xF7U, 0x34U, 0x09U, 0x44U, 0x46U, 0xD7U, + 0x06U, 0xB3U, 0x72U, 0xDEU, 0x42U, 0xF5U, 0xA5U, 0xD8U, 0xF1U, 0x87U, + 0x7BU, 0x9AU, 0x04U, 0x22U, 0xA3U, 0x6BU, 0x83U, 0x59U, 0x39U, 0x6FU, + 0x00U}; + +CSoundCardController::CSoundCardController(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail) : +CModem(), +m_sound(rxDevice, txDevice, DSTAR_RADIO_SAMPLE_RATE, DSTAR_RADIO_BLOCK_SIZE), +m_rxLevel(rxLevel), +m_txLevel(txLevel), +m_txDelay(txDelay), +m_txTail(txTail), +m_txAudio(48000U), +m_rxAudio(4800U), +m_rxState(DSRSCCS_NONE), +m_patternBuffer(0x00U), +m_demodulator(), +m_modulator(), +m_rxBuffer(NULL), +m_rxBufferBits(0U), +m_dataBits(0U), +m_mar(0U), +m_pathMetric(NULL), +m_pathMemory0(NULL), +m_pathMemory1(NULL), +m_pathMemory2(NULL), +m_pathMemory3(NULL), +m_fecOutput(NULL) +{ + wxASSERT(!rxDevice.IsEmpty()); + wxASSERT(!txDevice.IsEmpty()); + + m_modulator.setInvert(txInvert); + m_demodulator.setInvert(rxInvert); + + m_sound.setCallback(this, 0U); + + m_rxBuffer = new unsigned char[FEC_SECTION_LENGTH_BYTES]; + + m_pathMetric = new int[4U]; + m_pathMemory0 = new unsigned int[42U]; + m_pathMemory1 = new unsigned int[42U]; + m_pathMemory2 = new unsigned int[42U]; + m_pathMemory3 = new unsigned int[42U]; + m_fecOutput = new unsigned char[42U]; +} + +CSoundCardController::~CSoundCardController() +{ + delete[] m_rxBuffer; + delete[] m_pathMetric; + delete[] m_pathMemory0; + delete[] m_pathMemory1; + delete[] m_pathMemory2; + delete[] m_pathMemory3; + delete[] m_fecOutput; +} + +bool CSoundCardController::start() +{ + bool ret = m_sound.open(); + if (!ret) + return false; + + Create(); + SetPriority(100U); + Run(); + + return true; +} + +void* CSoundCardController::Entry() +{ + wxLogMessage(wxT("Starting Sound Card Controller thread")); + + while (!m_stopped) { + wxFloat32 val; + while (m_rxAudio.getData(&val, 1U) == 1U) { + TRISTATE state = m_demodulator.decode(val * m_rxLevel); + switch (state) { + case STATE_TRUE: + switch (m_rxState) { + case DSRSCCS_NONE: + processNone(true); + break; + case DSRSCCS_HEADER: + processHeader(true); + break; + case DSRSCCS_DATA: + processData(true); + break; + default: + break; + } + break; + case STATE_FALSE: + switch (m_rxState) { + case DSRSCCS_NONE: + processNone(false); + break; + case DSRSCCS_HEADER: + processHeader(false); + break; + case DSRSCCS_DATA: + processData(false); + break; + default: + break; + } + break; + default: + break; + } + } + + Sleep(10UL); + } + + wxLogMessage(wxT("Stopping Sound Card Controller thread")); + + m_sound.close(); + + return NULL; +} + +bool CSoundCardController::writeHeader(const CHeaderData& header) +{ + bool ret = m_txAudio.hasSpace((m_txDelay + 60U + 85U) * 8U * DSTAR_RADIO_BIT_LENGTH); + if (!ret) { + wxLogWarning(wxT("No space to write the header")); + return false; + } + + unsigned char buffer1[RADIO_HEADER_LENGTH_BYTES]; + + ::memset(buffer1, ' ', RADIO_HEADER_LENGTH_BYTES); + + buffer1[0U] = header.getFlag1(); + buffer1[1U] = header.getFlag2(); + buffer1[2U] = header.getFlag3(); + + wxString rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer1[i + 3U] = rpt2.GetChar(i); + + wxString rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer1[i + 11U] = rpt1.GetChar(i); + + wxString your = header.getYourCall(); + for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer1[i + 19U] = your.GetChar(i); + + wxString my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer1[i + 27U] = my1.GetChar(i); + + wxString my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer1[i + 35U] = my2.GetChar(i); + + CCCITTChecksumReverse cksum1; + cksum1.update(buffer1 + 0U, RADIO_HEADER_LENGTH_BYTES - 2U); + cksum1.result(buffer1 + 39U); + + unsigned int bytes = m_txDelay + 60U; + for (unsigned int i = 0U; i < bytes; i++) + writeBits(BIT_SYNC); + + unsigned char buffer2[86U]; + txHeader(buffer1, buffer2 + 2U); + + buffer2[0U] = FRAME_SYNC0; + buffer2[1U] = FRAME_SYNC1; + buffer2[2U] |= FRAME_SYNC2; + + for (unsigned int i = 0U; i < 85U; i++) + writeBits(buffer2[i]); + + return true; +} + +bool CSoundCardController::writeData(const unsigned char* data, unsigned int length, bool end) +{ + if (end) { + unsigned int tailBlocks = 3U + m_txTail / 10U; + + bool ret = m_txAudio.hasSpace(tailBlocks * END_PATTERN_LENGTH_BYTES * 8U * DSTAR_RADIO_BIT_LENGTH); + if (!ret) { + wxLogWarning(wxT("No space to write end data")); + return false; + } + + for (unsigned int j = 0U; j < tailBlocks; j++) { + for (unsigned int i = 0U; i < END_PATTERN_LENGTH_BYTES; i++) + writeBits(END_PATTERN_BYTES[i]); + } + } else { + bool ret = m_txAudio.hasSpace(length * 8U * DSTAR_RADIO_BIT_LENGTH); + if (!ret) { + wxLogWarning(wxT("No space to write data")); + return false; + } + + for (unsigned int i = 0U; i < length; i++) + writeBits(data[i]); + } + + return true; +} + +unsigned int CSoundCardController::getSpace() +{ + return m_txAudio.freeSpace() / (DV_FRAME_LENGTH_BYTES * 8U * DSTAR_RADIO_BIT_LENGTH); +} + +bool CSoundCardController::isTXReady() +{ + return m_txAudio.isEmpty(); +} + +bool CSoundCardController::isTX() +{ +#if (defined(__APPLE__) && defined(__MACH__)) || defined(__WINDOWS__) + return m_txAudio.hasData(); +#else + return m_sound.isWriterBusy() || m_txAudio.hasData(); +#endif +} + +void CSoundCardController::readCallback(const wxFloat32* input, unsigned int n, int id) +{ +#if !defined(AUDIO_LOOPBACK) + if (!m_stopped) + m_rxAudio.addData(input, n); +#endif +} + +void CSoundCardController::writeCallback(wxFloat32* output, unsigned int& n, int id) +{ + if (n == 0U) + return; + + ::memset(output, 0x00, n * sizeof(wxFloat32)); + + if (!m_stopped) { + n = m_txAudio.getData(output, n); +#if defined(AUDIO_LOOPBACK) + n = m_rxAudio.addData(output, n); +#endif + } +} + +void CSoundCardController::txHeader(const unsigned char* in, unsigned char* out) +{ + unsigned char intermediate[84U]; + unsigned int i; + + for (i = 0U; i < 83U; i++) { + intermediate[i] = 0x00U; + out[i] = 0x00U; + } + + // Convolve the header + unsigned char d, d1 = 0U, d2 = 0U, g0, g1; + unsigned int k = 0U; + for (i = 0U; i < 42U; i++) { + for (unsigned int j = 0U; j < 8U; j++) { + unsigned char mask = (0x01U << j); + d = 0U; + + if (in[i] & mask) + d = 1U; + + g0 = (d + d2) % 2U; + g1 = (d + d1 + d2) % 2U; + d2 = d1; + d1 = d; + + if (g1) + intermediate[k / 8U] |= BIT_MASK_TABLE1[k % 8U]; + + k++; + + if (g0) + intermediate[k / 8U] |= BIT_MASK_TABLE1[k % 8U]; + + k++; + } + } + + // Interleave the header + i = 0U; + while (i < 660U) { + unsigned char d = intermediate[i / 8U]; + + if (d & 0x80U) + out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]); + i++; + + if (d & 0x40U) + out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]); + i++; + + if (d & 0x20U) + out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]); + i++; + + if (d & 0x10U) + out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]); + i++; + + if (i < 660U) { + if (d & 0x08U) + out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]); + i++; + + if (d & 0x04U) + out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]); + i++; + + if (d & 0x02U) + out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]); + i++; + + if (d & 0x01U) + out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]); + i++; + } + } + + // Scramble the header + for (i = 0U; i < 83U; i++) + out[i] ^= SCRAMBLE_TABLE_TX[i]; +} + +void CSoundCardController::writeBits(unsigned char c) +{ + wxFloat32 buffer[DSTAR_RADIO_BIT_LENGTH]; + + unsigned char mask = 0x01U; + for (unsigned int i = 0U; i < 8U; i++) { + bool bit = (c & mask) == mask; + + m_modulator.code(bit, buffer, DSTAR_RADIO_BIT_LENGTH); + + for (unsigned int j = 0U; j < DSTAR_RADIO_BIT_LENGTH; j++) + buffer[j] *= m_txLevel; + + m_txAudio.addData(buffer, DSTAR_RADIO_BIT_LENGTH); + + mask <<= 1; + } +} + +void CSoundCardController::processNone(bool bit) +{ + m_patternBuffer <<= 1; + if (bit) + m_patternBuffer |= 0x01U; + + // Exact matching of the frame sync sequence + unsigned int errs = countBits((m_patternBuffer & FRAME_SYNC_MASK) ^ FRAME_SYNC_DATA); + if (errs == 0U) { + // Lock the GMSK PLL to this signal + m_demodulator.lock(true); + + ::memset(m_rxBuffer, 0x00U, FEC_SECTION_LENGTH_BYTES); + m_rxBufferBits = 0U; + m_rxState = DSRSCCS_HEADER; + return; + } + + // Exact matching of the data sync bit sequence + errs = countBits((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA); + if (errs == 0U) { + // Lock the GMSK PLL to this signal + m_demodulator.lock(true); + + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + m_rxData.addData(DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + + ::memset(m_rxBuffer, 0x00U, DV_FRAME_LENGTH_BYTES); + m_rxBufferBits = 0U; + + m_dataBits = MAX_SYNC_BITS; + m_rxState = DSRSCCS_DATA; + return; + } +} + +void CSoundCardController::processHeader(bool bit) +{ + m_patternBuffer <<= 1; + if (bit) + m_patternBuffer |= 0x01U; + + WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit); + m_rxBufferBits++; + + // A full FEC header + if (m_rxBufferBits == FEC_SECTION_LENGTH_BITS) { + // Process the scrambling, interleaving and FEC, then return if the chcksum was correct + unsigned char header[RADIO_HEADER_LENGTH_BYTES]; + bool ok = rxHeader(m_rxBuffer, header); + if (ok) { + // The checksum is correct + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(header, RADIO_HEADER_LENGTH_BYTES); + + ::memset(m_rxBuffer, 0x00U, DV_FRAME_LENGTH_BYTES); + m_rxBufferBits = 0U; + + m_rxState = DSRSCCS_DATA; + m_dataBits = MAX_SYNC_BITS; + } else { + // Release PLL ACQ + m_demodulator.lock(false); + + // The checksum failed, return to looking for syncs + m_rxState = DSRSCCS_NONE; + } + } +} + +void CSoundCardController::processData(bool bit) +{ + m_patternBuffer <<= 1; + if (bit) + m_patternBuffer |= 0x01U; + + WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit); + m_rxBufferBits++; + + // Fuzzy matching of the end frame sequences + unsigned int errs = countBits((m_patternBuffer & END_SYNC_MASK) ^ END_SYNC_DATA); + if (errs <= END_SYNC_ERRS) { + // Release the GMSK PLL + m_demodulator.lock(false); + + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rxState = DSRSCCS_NONE; + return; + } + + // Fuzzy matching of the data sync bit sequence + bool syncSeen = false; + if (m_rxBufferBits >= (DV_FRAME_LENGTH_BITS - 3U)) { + errs = countBits((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA); + if (errs <= DATA_SYNC_ERRS) { + m_rxBufferBits = DV_FRAME_LENGTH_BITS; + m_dataBits = MAX_SYNC_BITS; + syncSeen = true; + } + } + + // We've not seen a data sync for too long, signal RXLOST and change to RX_NONE + m_dataBits--; + if (m_dataBits == 0U) { + // Release the GMSK PLL + m_demodulator.lock(false); + + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_LOST; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_rxState = DSRSCCS_NONE; + return; + } + + // Check to see if the sync is arriving late + if (m_rxBufferBits == DV_FRAME_LENGTH_BITS && !syncSeen) { + for (unsigned int i = 1U; i <= 3U; i++) { + wxUint32 syncMask = DATA_SYNC_MASK >> i; + wxUint32 syncData = DATA_SYNC_DATA >> i; + errs = countBits((m_patternBuffer & syncMask) ^ syncData); + if (errs <= DATA_SYNC_ERRS) { + m_rxBufferBits -= i; + break; + } + } + } + + // Send a data frame to the host if the required number of bits have been received, or if a data sync has been seen + if (m_rxBufferBits == DV_FRAME_LENGTH_BITS) { + if (syncSeen) { + m_rxBuffer[9U] = DATA_SYNC_BYTES[0U]; + m_rxBuffer[10U] = DATA_SYNC_BYTES[1U]; + m_rxBuffer[11U] = DATA_SYNC_BYTES[2U]; + } + + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_rxBuffer, DV_FRAME_LENGTH_BYTES); + + // Start the next frame + ::memset(m_rxBuffer, 0x00U, DV_FRAME_LENGTH_BYTES); + m_rxBufferBits = 0U; + } +} + +unsigned int CSoundCardController::countBits(wxUint32 num) +{ + unsigned int count = 0U; + + for (unsigned int i = 0U; i < 8U; i++) + count += NIBBLE_BITS[(num >> (i * 4U)) & 0x0FU]; + + return count; +} + +bool CSoundCardController::rxHeader(unsigned char* in, unsigned char* out) +{ + int i; + + // Descramble the header + for (i = 0; i < int(FEC_SECTION_LENGTH_BYTES); i++) + in[i] ^= SCRAMBLE_TABLE_RX[i]; + + unsigned char intermediate[84U]; + for (i = 0; i < 84; i++) + intermediate[i] = 0x00U; + + // Deinterleave the header + i = 0; + while (i < 660) { + unsigned char d = in[i / 8]; + + if (d & 0x01U) + intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]); + i++; + + if (d & 0x02U) + intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]); + i++; + + if (d & 0x04U) + intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]); + i++; + + if (d & 0x08U) + intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]); + i++; + + if (i < 660) { + if (d & 0x10U) + intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]); + i++; + + if (d & 0x20U) + intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]); + i++; + + if (d & 0x40U) + intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]); + i++; + + if (d & 0x80U) + intermediate[INTERLEAVE_TABLE_RX[i * 2U]] |= (0x80U >> INTERLEAVE_TABLE_RX[i * 2U + 1U]); + i++; + } + } + + for (i = 0; i < 4; i++) + m_pathMetric[i] = 0; + + int decodeData[2U]; + + m_mar = 0U; + for (i = 0; i < 660; i += 2) { + if (intermediate[i / 8] & (0x80U >> i % 8)) + decodeData[1U] = 1U; + else + decodeData[1U] = 0U; + + if (intermediate[i / 8] & (0x40U >> i % 8)) + decodeData[0U] = 1U; + else + decodeData[0U] = 0U; + + viterbiDecode(decodeData); + } + + traceBack(); + + for (i = 0; i < int(RADIO_HEADER_LENGTH_BYTES); i++) + out[i] = 0x00U; + + unsigned int j = 0; + for (i = 329; i >= 0; i--) { + if (READ_BIT1(m_fecOutput, i)) + out[j / 8] |= (0x01U << (j % 8)); + + j++; + } + + CCCITTChecksumReverse cksum; + cksum.update(out, RADIO_HEADER_LENGTH_BYTES - 2U); + + return cksum.check(out + RADIO_HEADER_LENGTH_BYTES - 2U); +} + +void CSoundCardController::acs(int* metric) +{ + int tempMetric[4U]; + + unsigned int j = m_mar / 8U; + unsigned int k = m_mar % 8U; + + // Pres. state = S0, Prev. state = S0 & S2 + int m1 = metric[0U] + m_pathMetric[0U]; + int m2 = metric[4U] + m_pathMetric[2U]; + tempMetric[0U] = m1 < m2 ? m1 : m2; + if (m1 < m2) + m_pathMemory0[j] &= BIT_MASK_TABLE0[k]; + else + m_pathMemory0[j] |= BIT_MASK_TABLE1[k]; + + // Pres. state = S1, Prev. state = S0 & S2 + m1 = metric[1U] + m_pathMetric[0U]; + m2 = metric[5U] + m_pathMetric[2U]; + tempMetric[1U] = m1 < m2 ? m1 : m2; + if (m1 < m2) + m_pathMemory1[j] &= BIT_MASK_TABLE0[k]; + else + m_pathMemory1[j] |= BIT_MASK_TABLE1[k]; + + // Pres. state = S2, Prev. state = S2 & S3 + m1 = metric[2U] + m_pathMetric[1U]; + m2 = metric[6U] + m_pathMetric[3U]; + tempMetric[2U] = m1 < m2 ? m1 : m2; + if (m1 < m2) + m_pathMemory2[j] &= BIT_MASK_TABLE0[k]; + else + m_pathMemory2[j] |= BIT_MASK_TABLE1[k]; + + // Pres. state = S3, Prev. state = S1 & S3 + m1 = metric[3U] + m_pathMetric[1U]; + m2 = metric[7U] + m_pathMetric[3U]; + tempMetric[3U] = m1 < m2 ? m1 : m2; + if (m1 < m2) + m_pathMemory3[j] &= BIT_MASK_TABLE0[k]; + else + m_pathMemory3[j] |= BIT_MASK_TABLE1[k]; + + for (unsigned int i = 0U; i < 4U; i++) + m_pathMetric[i] = tempMetric[i]; + + m_mar++; +} + +void CSoundCardController::viterbiDecode(int* data) +{ + int metric[8U]; + + metric[0] = (data[1] ^ 0) + (data[0] ^ 0); + metric[1] = (data[1] ^ 1) + (data[0] ^ 1); + metric[2] = (data[1] ^ 1) + (data[0] ^ 0); + metric[3] = (data[1] ^ 0) + (data[0] ^ 1); + metric[4] = (data[1] ^ 1) + (data[0] ^ 1); + metric[5] = (data[1] ^ 0) + (data[0] ^ 0); + metric[6] = (data[1] ^ 0) + (data[0] ^ 1); + metric[7] = (data[1] ^ 1) + (data[0] ^ 0); + + acs(metric); +} + +void CSoundCardController::traceBack() +{ + // Start from the S0, t=31 + unsigned int j = 0U; + unsigned int k = 0U; + for (int i = 329; i >= 0; i--) { + switch (j) { + case 0U: // if state = S0 + if (READ_BIT1(m_pathMemory0, i) == 0) + j = 0U; + else + j = 2U; + WRITE_BIT1(m_fecOutput, k, 0); + k++; + break; + + + case 1U: // if state = S1 + if (READ_BIT1(m_pathMemory1, i) == 0) + j = 0U; + else + j = 2U; + WRITE_BIT1(m_fecOutput, k, 1); + k++; + break; + + case 2: // if state = S1 + if (READ_BIT1(m_pathMemory2, i) == 0) + j = 1U; + else + j = 3U; + WRITE_BIT1(m_fecOutput, k, 0); + k++; + break; + + case 3U: // if state = S1 + if (READ_BIT1(m_pathMemory3, i) == 0) + j = 1U; + else + j = 3U; + WRITE_BIT1(m_fecOutput, k, 1); + k++; + break; + } + } +} diff --git a/Common/SoundCardController.h b/Common/SoundCardController.h new file mode 100644 index 0000000..087a939 --- /dev/null +++ b/Common/SoundCardController.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef SoundCardController_H +#define SoundCardController_H + +#include "SoundCardReaderWriter.h" +#include "DStarGMSKDemodulator.h" +#include "DStarGMSKModulator.h" +#include "AudioCallback.h" +#include "RingBuffer.h" +#include "Modem.h" +#include "Utils.h" + +#include + +enum DSRSCC_STATE { + DSRSCCS_NONE, + DSRSCCS_HEADER, + DSRSCCS_DATA +}; + +class CSoundCardController : public CModem, public IAudioCallback { +public: + CSoundCardController(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail); + virtual ~CSoundCardController(); + + virtual void* Entry(); + + virtual bool start(); + + virtual unsigned int getSpace(); + virtual bool isTXReady(); + virtual bool isTX(); + + virtual bool writeHeader(const CHeaderData& header); + virtual bool writeData(const unsigned char* data, unsigned int length, bool end); + + virtual void readCallback(const wxFloat32* input, unsigned int n, int id); + virtual void writeCallback(wxFloat32* output, unsigned int& n, int id); + +private: + CSoundCardReaderWriter m_sound; + wxFloat32 m_rxLevel; + wxFloat32 m_txLevel; + unsigned int m_txDelay; + unsigned int m_txTail; + CRingBuffer m_txAudio; + CRingBuffer m_rxAudio; + DSRSCC_STATE m_rxState; + wxUint32 m_patternBuffer; + CDStarGMSKDemodulator m_demodulator; + CDStarGMSKModulator m_modulator; + unsigned char* m_rxBuffer; + unsigned int m_rxBufferBits; + unsigned int m_dataBits; + unsigned int m_mar; + int* m_pathMetric; + unsigned int* m_pathMemory0; + unsigned int* m_pathMemory1; + unsigned int* m_pathMemory2; + unsigned int* m_pathMemory3; + unsigned char* m_fecOutput; + + void processNone(bool bit); + void processHeader(bool bit); + void processData(bool bit); + + bool rxHeader(unsigned char* in, unsigned char* out); + void acs(int* metric); + void viterbiDecode(int* data); + void traceBack(); + + void txHeader(const unsigned char* in, unsigned char* out); + void writeBits(unsigned char c); + + unsigned int countBits(wxUint32 num); +}; + +#endif + diff --git a/Common/SoundCardReaderWriter.cpp b/Common/SoundCardReaderWriter.cpp new file mode 100644 index 0000000..62a15f4 --- /dev/null +++ b/Common/SoundCardReaderWriter.cpp @@ -0,0 +1,787 @@ +/* + * Copyright (C) 2006-2010,2015 by Jonathan Naylor G4KLX + * Copyright (C) 2014 by John Wiseman, G8BPQ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "SoundCardReaderWriter.h" + +#if (defined(__APPLE__) && defined(__MACH__)) || defined(__WINDOWS__) + +static int scrwCallback(const void* input, void* output, unsigned long nSamples, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData) +{ + wxASSERT(userData != NULL); + + CSoundCardReaderWriter* object = reinterpret_cast(userData); + + object->callback(static_cast(input), static_cast(output), nSamples); + + return paContinue; +} + +CSoundCardReaderWriter::CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize) : +m_readDevice(readDevice), +m_writeDevice(writeDevice), +m_sampleRate(sampleRate), +m_blockSize(blockSize), +m_callback(NULL), +m_id(-1), +m_stream(NULL) +{ +} + +CSoundCardReaderWriter::~CSoundCardReaderWriter() +{ +} + +wxArrayString CSoundCardReaderWriter::getReadDevices() +{ + wxArrayString devices; + + devices.Alloc(10); + + PaError error = ::Pa_Initialize(); + if (error != paNoError) + return devices; + +#if defined(__WINDOWS__) + PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound); +#elif defined(__APPLE__) && defined(__MACH__) + PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio); +#else + PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA); +#endif + if (apiIndex == paHostApiNotFound) { + ::Pa_Terminate(); + return devices; + } + + PaDeviceIndex n = ::Pa_GetDeviceCount(); + if (n <= 0) { + ::Pa_Terminate(); + return devices; + } + + for (PaDeviceIndex i = 0; i < n; i++) { + const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i); + + if (device->hostApi != apiIndex) + continue; + + if (device->maxInputChannels > 0) { + wxString name(device->name, wxConvLocal); + devices.Add(name); + } + } + + ::Pa_Terminate(); + + return devices; +} + +wxArrayString CSoundCardReaderWriter::getWriteDevices() +{ + wxArrayString devices; + + devices.Alloc(10); + + PaError error = ::Pa_Initialize(); + if (error != paNoError) + return devices; + +#if defined(__WINDOWS__) + PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound); +#elif defined(__APPLE__) && defined(__MACH__) + PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio); +#else + PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA); +#endif + if (apiIndex == paHostApiNotFound) { + ::Pa_Terminate(); + return devices; + } + + PaDeviceIndex n = ::Pa_GetDeviceCount(); + if (n <= 0) { + ::Pa_Terminate(); + return devices; + } + + for (PaDeviceIndex i = 0; i < n; i++) { + const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i); + + if (device->hostApi != apiIndex) + continue; + + if (device->maxOutputChannels > 0) { + wxString name(device->name, wxConvLocal); + devices.Add(name); + } + } + + ::Pa_Terminate(); + + return devices; +} + +void CSoundCardReaderWriter::setCallback(IAudioCallback* callback, int id) +{ + wxASSERT(callback != NULL); + + m_callback = callback; + m_id = id; +} + +bool CSoundCardReaderWriter::open() +{ + PaError error = ::Pa_Initialize(); + if (error != paNoError) { + wxLogError(wxT("Cannot initialise PortAudio")); + return false; + } + + PaStreamParameters* pParamsIn = NULL; + PaStreamParameters* pParamsOut = NULL; + + PaStreamParameters paramsIn; + PaStreamParameters paramsOut; + + PaDeviceIndex inDev, outDev; + bool res = convertNameToDevices(inDev, outDev); + if (!res) { + wxLogError(wxT("Cannot convert name to device")); + return false; + } + + if (inDev != -1) { + const PaDeviceInfo* inInfo = ::Pa_GetDeviceInfo(inDev); + if (inInfo == NULL) { + wxLogError(wxT("Cannot get device information for the input device")); + return false; + } + + paramsIn.device = inDev; + paramsIn.channelCount = 1; + paramsIn.sampleFormat = paFloat32; + paramsIn.hostApiSpecificStreamInfo = NULL; + paramsIn.suggestedLatency = inInfo->defaultLowInputLatency; + + pParamsIn = ¶msIn; + } + + if (outDev != -1) { + const PaDeviceInfo* outInfo = ::Pa_GetDeviceInfo(outDev); + if (outInfo == NULL) { + wxLogError(wxT("Cannot get device information for the output device")); + return false; + } + + paramsOut.device = outDev; + paramsOut.channelCount = 1; + paramsOut.sampleFormat = paFloat32; + paramsOut.hostApiSpecificStreamInfo = NULL; + paramsOut.suggestedLatency = outInfo->defaultLowOutputLatency; + + pParamsOut = ¶msOut; + } + + error = ::Pa_OpenStream(&m_stream, pParamsIn, pParamsOut, double(m_sampleRate), m_blockSize, paNoFlag, &scrwCallback, this); + if (error != paNoError) { + wxLogError(wxT("Cannot open the audios stream(s)")); + ::Pa_Terminate(); + return false; + } + + error = ::Pa_StartStream(m_stream); + if (error != paNoError) { + wxLogError(wxT("Cannot start the audio stream(s)")); + ::Pa_CloseStream(m_stream); + m_stream = NULL; + + ::Pa_Terminate(); + return false; + } + + return true; +} + +void CSoundCardReaderWriter::close() +{ + wxASSERT(m_stream != NULL); + + ::Pa_AbortStream(m_stream); + + ::Pa_CloseStream(m_stream); + + ::Pa_Terminate(); +} + +void CSoundCardReaderWriter::callback(const wxFloat32* input, wxFloat32* output, unsigned int nSamples) +{ + if (m_callback != NULL) { + m_callback->readCallback(input, nSamples, m_id); + m_callback->writeCallback(output, nSamples, m_id); + } +} + +bool CSoundCardReaderWriter::convertNameToDevices(PaDeviceIndex& inDev, PaDeviceIndex& outDev) +{ + inDev = outDev = -1; + +#if defined(__WINDOWS__) + PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound); +#elif defined(__APPLE__) && defined(__MACH__) + PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio); +#else + PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA); +#endif + if (apiIndex == paHostApiNotFound) + return false; + + PaDeviceIndex n = ::Pa_GetDeviceCount(); + if (n <= 0) + return false; + + for (PaDeviceIndex i = 0; i < n; i++) { + const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i); + + if (device->hostApi != apiIndex) + continue; + + wxString name(device->name, wxConvLocal); + + if (!m_readDevice.IsEmpty() && m_readDevice.IsSameAs(name) && device->maxInputChannels > 0) + inDev = i; + + if (!m_writeDevice.IsEmpty() && m_writeDevice.IsSameAs(name) && device->maxOutputChannels > 0) + outDev = i; + } + + if (inDev == -1 && outDev == -1) + return false; + + return true; +} + +#else + +wxArrayString CSoundCardReaderWriter::m_readDevices; +wxArrayString CSoundCardReaderWriter::m_writeDevices; + +CSoundCardReaderWriter::CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize) : +m_readDevice(readDevice), +m_writeDevice(writeDevice), +m_sampleRate(sampleRate), +m_blockSize(blockSize), +m_callback(NULL), +m_id(-1), +m_reader(NULL), +m_writer(NULL) +{ + wxASSERT(sampleRate > 0U); + wxASSERT(blockSize > 0U); +} + +CSoundCardReaderWriter::~CSoundCardReaderWriter() +{ +} + +wxArrayString CSoundCardReaderWriter::getReadDevices() +{ + snd_ctl_t *handle = NULL; + snd_pcm_t *pcm = NULL; + char NameString[256]; + + wxArrayString devices(m_readDevices); + + snd_ctl_card_info_t* info; + snd_ctl_card_info_alloca(&info); + + snd_pcm_info_t* pcminfo; + snd_pcm_info_alloca(&pcminfo); + + snd_pcm_hw_params_t* pars; + snd_pcm_hw_params_alloca(&pars); + + unsigned min, max; + int err; + snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE; + + int card = -1; + while (::snd_card_next(&card) == 0 && card >= 0) { + char hwdev[80]; + ::sprintf(hwdev, "hw:%d", card); + + if (::snd_ctl_open(&handle, hwdev, 0) < 0) + continue; + + ::snd_ctl_card_info(handle, info); + ::snd_ctl_card_info_get_name(info); + + int dev = -1; + while (::snd_ctl_pcm_next_device(handle, &dev) == 0 && dev >= 0) { + ::snd_pcm_info_set_device(pcminfo, dev); + ::snd_pcm_info_set_subdevice(pcminfo, 0); + ::snd_pcm_info_set_stream(pcminfo, stream); + + err = ::snd_ctl_pcm_info(handle, pcminfo); + if (err != -ENOENT) { + ::sprintf(hwdev, "hw:%d,%d", card, dev); + + if (::snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK) < 0) + continue; + + ::snd_pcm_hw_params_any(pcm, pars); + ::snd_pcm_hw_params_get_channels_min(pars, &min); + ::snd_pcm_hw_params_get_channels_max(pars, &max); + + ::snd_pcm_hw_params_get_rate_min(pars, &min, NULL); + ::snd_pcm_hw_params_get_rate_max(pars, &max, NULL); + + ::sprintf(NameString, "hw:%d,%d %s(%s)", + card, dev, + ::snd_pcm_info_get_name(pcminfo), + snd_ctl_card_info_get_name(info)); + + wxString name(NameString, wxConvLocal); + devices.Add(name); + + ::snd_pcm_close(pcm); + pcm = NULL; + } + } + + ::snd_ctl_close(handle); + } + + return devices; +} + +wxArrayString CSoundCardReaderWriter::getWriteDevices() +{ + snd_ctl_t *handle = NULL; + snd_pcm_t *pcm = NULL; + char NameString[256]; + + wxArrayString devices(m_writeDevices); + + snd_ctl_card_info_t* info; + snd_ctl_card_info_alloca(&info); + + snd_pcm_info_t* pcminfo; + snd_pcm_info_alloca(&pcminfo); + + snd_pcm_hw_params_t* pars; + snd_pcm_hw_params_alloca(&pars); + + unsigned min, max; + int err; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + + int card = -1; + while (::snd_card_next(&card) == 0 && card >= 0) { + char hwdev[80]; + ::sprintf(hwdev, "hw:%d", card); + + if (::snd_ctl_open(&handle, hwdev, 0) < 0) + continue; + + ::snd_ctl_card_info(handle, info); + ::snd_ctl_card_info_get_name(info); + + int dev = -1; + while (::snd_ctl_pcm_next_device(handle, &dev) == 0 && dev >= 0) { + ::snd_pcm_info_set_device(pcminfo, dev); + ::snd_pcm_info_set_subdevice(pcminfo, 0); + ::snd_pcm_info_set_stream(pcminfo, stream); + + err= ::snd_ctl_pcm_info(handle, pcminfo); + if (err != -ENOENT) { + ::sprintf(hwdev, "hw:%d,%d", card, dev); + + if (::snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK) < 0) + continue; + + ::snd_pcm_hw_params_any(pcm, pars); + ::snd_pcm_hw_params_get_channels_min(pars, &min); + ::snd_pcm_hw_params_get_channels_max(pars, &max); + + ::snd_pcm_hw_params_get_rate_min(pars, &min, NULL); + ::snd_pcm_hw_params_get_rate_max(pars, &max, NULL); + + ::sprintf(NameString, "hw:%d,%d %s(%s)", + card, dev, + ::snd_pcm_info_get_name(pcminfo), + ::snd_ctl_card_info_get_name(info)); + + wxString name(NameString, wxConvLocal); + devices.Add(name); + + ::snd_pcm_close(pcm); + pcm = NULL; + } + } + + ::snd_ctl_close(handle); + } + + return devices; +} + +void CSoundCardReaderWriter::setCallback(IAudioCallback* callback, int id) +{ + wxASSERT(callback != NULL); + + m_callback = callback; + + m_id = id; +} + +bool CSoundCardReaderWriter::open() +{ + int err = 0; + + char buf1[100]; + char buf2[100]; + char* ptr; + + // Store the opened devices because ALSA won't enumerate them + m_readDevices.Add(m_readDevice); + m_writeDevices.Add(m_writeDevice); + + ::strcpy(buf1, (const char*)m_writeDevice.mb_str(wxConvUTF8)); + ::strcpy(buf2, (const char*)m_readDevice.mb_str(wxConvUTF8)); + + ptr = ::strchr(buf1, ' '); + if (ptr) *ptr = 0; // Get Device part of name + + ptr = ::strchr(buf2, ' '); + if (ptr) *ptr = 0; // Get Device part of name + + wxString writeDevice(buf1, wxConvLocal); + wxString readDevice(buf2, wxConvLocal); + + snd_pcm_t* playHandle = NULL; + if ((err = ::snd_pcm_open(&playHandle, buf1, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot open playback audio device %s (%s)"), writeDevice.c_str(), error.c_str()); + return false; + } + + snd_pcm_hw_params_t* hw_params; + if ((err = ::snd_pcm_hw_params_malloc(&hw_params)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot allocate hardware parameter structure (%s)"), error.c_str()); + return false; + } + + if ((err = ::snd_pcm_hw_params_any(playHandle, hw_params)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot initialize hardware parameter structure (%s)"), error.c_str()); + return false; + } + + if ((err = ::snd_pcm_hw_params_set_access(playHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot set access type (%s)"), error.c_str()); + return false; + } + + if ((err = ::snd_pcm_hw_params_set_format(playHandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot set sample format (%s)"), error.c_str()); + return false; + } + + if ((err = ::snd_pcm_hw_params_set_rate(playHandle, hw_params, m_sampleRate, 0)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot set sample rate (%s)"), error.c_str()); + return false; + } + + unsigned int playChannels = 1U; + + if ((err = ::snd_pcm_hw_params_set_channels(playHandle, hw_params, 1)) < 0) { + playChannels = 2U; + + if ((err = ::snd_pcm_hw_params_set_channels(playHandle, hw_params, 2)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot play set channel count (%s)"), error.c_str()); + return false; + } + } + + if ((err = ::snd_pcm_hw_params(playHandle, hw_params)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot set parameters (%s)"), error.c_str()); + return false; + } + + ::snd_pcm_hw_params_free(hw_params); + + if ((err = ::snd_pcm_prepare(playHandle)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot prepare audio interface for use (%s)"), error.c_str()); + return false; + } + + // Open Capture + snd_pcm_t* recHandle = NULL; + if ((err = ::snd_pcm_open(&recHandle, buf2, SND_PCM_STREAM_CAPTURE, 0)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot open capture audio device %s (%s)"), readDevice.c_str(), error.c_str()); + return false; + } + + if ((err = ::snd_pcm_hw_params_malloc(&hw_params)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot allocate hardware parameter structure (%s)"), error.c_str()); + return false; + } + + if ((err = ::snd_pcm_hw_params_any(recHandle, hw_params)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot initialize hardware parameter structure (%s)"), error.c_str()); + return false; + } + + if ((err = ::snd_pcm_hw_params_set_access(recHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot set access type (%s)"), error.c_str()); + return false; + } + + if ((err = ::snd_pcm_hw_params_set_format(recHandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot set sample format (%s)"), error.c_str()); + return false; + } + + if ((err = ::snd_pcm_hw_params_set_rate(recHandle, hw_params, m_sampleRate, 0)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot set sample rate (%s)"), error.c_str()); + return false; + } + + unsigned int recChannels = 1U; + + if ((err = ::snd_pcm_hw_params_set_channels(recHandle, hw_params, 1)) < 0) { + recChannels = 2U; + + if ((err = ::snd_pcm_hw_params_set_channels (recHandle, hw_params, 2)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot rec set channel count (%s)"), error.c_str()); + return false; + } + } + + if ((err = ::snd_pcm_hw_params(recHandle, hw_params)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot set parameters (%s)"), error.c_str()); + return false; + } + + ::snd_pcm_hw_params_free(hw_params); + + if ((err = ::snd_pcm_prepare(recHandle)) < 0) { + wxString error(::snd_strerror(err), wxConvLocal); + wxLogError(wxT("Cannot prepare audio interface for use (%s)"), error.c_str()); + return false; + } + + short samples[256]; + for (unsigned int i = 0U; i < 10U; ++i) + ::snd_pcm_readi(recHandle, samples, 128); + + wxLogMessage(wxT("Opened %s %s Rate %u"), writeDevice.c_str(), readDevice.c_str(), m_sampleRate); + + m_reader = new CSoundCardReader(recHandle, m_blockSize, recChannels, m_callback, m_id); + m_writer = new CSoundCardWriter(playHandle, m_blockSize, playChannels, m_callback, m_id); + + m_reader->Create(); + m_reader->Run(); + + m_writer->Create(); + m_writer->Run(); + + return true; +} + +void CSoundCardReaderWriter::close() +{ + m_reader->kill(); + m_writer->kill(); + + m_reader->Wait(); + m_writer->Wait(); +} + +bool CSoundCardReaderWriter::isWriterBusy() const +{ + return m_writer->isBusy(); +} + +CSoundCardReader::CSoundCardReader(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id) : +wxThread(wxTHREAD_JOINABLE), +m_handle(handle), +m_blockSize(blockSize), +m_channels(channels), +m_callback(callback), +m_id(id), +m_killed(false), +m_buffer(NULL), +m_samples(NULL) +{ + wxASSERT(handle != NULL); + wxASSERT(blockSize > 0U); + wxASSERT(channels == 1U || channels == 2U); + wxASSERT(callback != NULL); + + m_buffer = new wxFloat32[blockSize]; + m_samples = new short[2U * blockSize]; +} + +CSoundCardReader::~CSoundCardReader() +{ + delete[] m_buffer; + delete[] m_samples; +} + +void* CSoundCardReader::Entry() +{ + wxLogMessage(wxT("Starting ALSA reader thread")); + + while (!m_killed) { + snd_pcm_sframes_t ret; + while ((ret = ::snd_pcm_readi(m_handle, m_samples, m_blockSize)) < 0) { + if (ret != -EPIPE) { + wxString error(::snd_strerror(ret), wxConvLocal); + wxLogWarning(wxT("snd_pcm_readi returned %d (%s)"), ret, error.c_str()); + } + + ::snd_pcm_recover(m_handle, ret, 1); + } + + if (m_channels == 1U) { + for (int n = 0; n < ret; n++) + m_buffer[n] = wxFloat32(m_samples[n]) / 32768.0F; + } else { + int i = 0; + for (int n = 0; n < (ret * 2); n += 2) + m_buffer[i++] = wxFloat32(m_samples[n + 1]) / 32768.0F; + } + + m_callback->readCallback(m_buffer, (unsigned int)ret, m_id); + } + + wxLogMessage(wxT("Stopping ALSA reader thread")); + + ::snd_pcm_close(m_handle); + + return NULL; +} + +void CSoundCardReader::kill() +{ + m_killed = true; +} + +CSoundCardWriter::CSoundCardWriter(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id) : +wxThread(wxTHREAD_JOINABLE), +m_handle(handle), +m_blockSize(blockSize), +m_channels(channels), +m_callback(callback), +m_id(id), +m_killed(false), +m_buffer(NULL), +m_samples(NULL) +{ + wxASSERT(handle != NULL); + wxASSERT(blockSize > 0U); + wxASSERT(channels == 1U || channels == 2U); + wxASSERT(callback != NULL); + + m_buffer = new wxFloat32[2U * blockSize]; + m_samples = new short[4U * blockSize]; +} + +CSoundCardWriter::~CSoundCardWriter() +{ + delete[] m_buffer; + delete[] m_samples; +} + +void* CSoundCardWriter::Entry() +{ + wxLogMessage(wxT("Starting ALSA writer thread")); + + while (!m_killed) { + unsigned int nSamples = 2U * m_blockSize; + m_callback->writeCallback(m_buffer, nSamples, m_id); + + if (nSamples == 0U) { + Sleep(5UL); + } else { + if (m_channels == 1U) { + for (unsigned int n = 0U; n < nSamples; n++) + m_samples[n] = short(m_buffer[n] * 32767.0F); + } else { + unsigned int i = 0U; + for (unsigned int n = 0U; n < nSamples; n++) { + short sample = short(m_buffer[n] * 32767.0F); + m_samples[i++] = sample; + m_samples[i++] = sample; // Same value to both channels + } + } + + unsigned int offset = 0U; + snd_pcm_sframes_t ret; + while ((ret = ::snd_pcm_writei(m_handle, m_samples + offset, nSamples - offset)) != (nSamples - offset)) { + if (ret < 0) { + if (ret != -EPIPE) { + wxString error(::snd_strerror(ret), wxConvLocal); + wxLogWarning(wxT("snd_pcm_writei returned %d (%s)"), ret, error.c_str()); + } + + ::snd_pcm_recover(m_handle, ret, 1); + } else { + offset += (unsigned int)ret; + } + } + } + } + + wxLogMessage(wxT("Stopping ALSA writer thread")); + + ::snd_pcm_close(m_handle); + + return NULL; +} + +void CSoundCardWriter::kill() +{ + m_killed = true; +} + +bool CSoundCardWriter::isBusy() const +{ + snd_pcm_state_t state = ::snd_pcm_state(m_handle); + + return state == SND_PCM_STATE_RUNNING || state == SND_PCM_STATE_DRAINING; +} + +#endif diff --git a/Common/SoundCardReaderWriter.h b/Common/SoundCardReaderWriter.h new file mode 100644 index 0000000..0facbbc --- /dev/null +++ b/Common/SoundCardReaderWriter.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009,2010,2015 by Jonathan Naylor, G4KLX + * Copyright (C) 2014 by John Wiseman, G8BPQ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef SoundCardReaderWriter_H +#define SoundCardReaderWriter_H + +#include "AudioCallback.h" + +#include + +#if (defined(__APPLE__) && defined(__MACH__)) || defined(__WINDOWS__) + +#include "portaudio.h" + +class CSoundCardReaderWriter { +public: + CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize); + ~CSoundCardReaderWriter(); + + void setCallback(IAudioCallback* callback, int id); + bool open(); + void close(); + + void callback(const wxFloat32* input, wxFloat32* output, unsigned int nSamples); + + static wxArrayString getReadDevices(); + static wxArrayString getWriteDevices(); + +private: + wxString m_readDevice; + wxString m_writeDevice; + unsigned int m_sampleRate; + unsigned int m_blockSize; + IAudioCallback* m_callback; + int m_id; + PaStream* m_stream; + + bool convertNameToDevices(PaDeviceIndex& inDev, PaDeviceIndex& outDev); +}; + +#else + +#include + +class CSoundCardReader : public wxThread { +public: + CSoundCardReader(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id); + virtual ~CSoundCardReader(); + + virtual void* Entry(); + + virtual void kill(); + +private: + snd_pcm_t* m_handle; + unsigned int m_blockSize; + unsigned int m_channels; + IAudioCallback* m_callback; + int m_id; + bool m_killed; + wxFloat32* m_buffer; + short* m_samples; +}; + +class CSoundCardWriter : public wxThread { +public: + CSoundCardWriter(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id); + virtual ~CSoundCardWriter(); + + virtual void* Entry(); + + virtual void kill(); + + virtual bool isBusy() const; + +private: + snd_pcm_t* m_handle; + unsigned int m_blockSize; + unsigned int m_channels; + IAudioCallback* m_callback; + int m_id; + bool m_killed; + wxFloat32* m_buffer; + short* m_samples; +}; + +class CSoundCardReaderWriter { +public: + CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize); + ~CSoundCardReaderWriter(); + + void setCallback(IAudioCallback* callback, int id); + bool open(); + void close(); + + bool isWriterBusy() const; + + static wxArrayString getReadDevices(); + static wxArrayString getWriteDevices(); + +private: + wxString m_readDevice; + wxString m_writeDevice; + unsigned int m_sampleRate; + unsigned int m_blockSize; + IAudioCallback* m_callback; + int m_id; + CSoundCardReader* m_reader; + CSoundCardWriter* m_writer; + + static wxArrayString m_readDevices; + static wxArrayString m_writeDevices; +}; + +#endif + +#endif diff --git a/Common/SplitController.cpp b/Common/SplitController.cpp new file mode 100644 index 0000000..bc730e1 --- /dev/null +++ b/Common/SplitController.cpp @@ -0,0 +1,747 @@ +/* + * Copyright (C) 2012-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "SplitController.h" + +const unsigned int REGISTRATION_TIMEOUT = 200U; + +const unsigned int BUFFER_LENGTH = 200U; + +CAMBESlot::CAMBESlot(unsigned int rxCount) : +m_valid(NULL), +m_errors(999U), +m_best(99U), +m_ambe(NULL), +m_length(0U), +m_end(NULL), +m_timer(1000U), +m_rxCount(rxCount) +{ + wxASSERT(rxCount > 0U); + + m_ambe = new unsigned char[DV_FRAME_MAX_LENGTH_BYTES]; + m_valid = new bool[rxCount]; + m_end = new bool[rxCount]; + + reset(); +} + +CAMBESlot::~CAMBESlot() +{ + delete[] m_end; + delete[] m_valid; + delete[] m_ambe; +} + +void CAMBESlot::reset() +{ + m_errors = 999U; + m_best = 99U; + m_length = 0U; + + for (unsigned int i = 0U; i < m_rxCount; i++) { + m_valid[i] = false; + m_end[i] = false; + } + + m_timer.stop(); +} + +bool CAMBESlot::isFirst() const +{ + return m_length == 0U; +} + +CSplitController::CSplitController(const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout) : +CModem(), +m_handler(localAddress, localPort), +m_transmitterNames(transmitterNames), +m_receiverNames(receiverNames), +m_timeout(timeout), +m_txCount(0U), +m_rxCount(0U), +m_txAddresses(NULL), +m_txPorts(NULL), +m_txTimers(NULL), +m_rxAddresses(NULL), +m_rxPorts(NULL), +m_rxTimers(NULL), +m_txData(1000U), +m_outId(0x00U), +m_outSeq(0U), +m_endTimer(1000U, 1U), +m_listening(true), +m_inSeqNo(0x00U), +m_outSeqNo(0x00U), +m_header(NULL), +m_id(NULL), +m_valid(NULL), +m_slots(NULL), +m_headerSent(false), +m_packets(NULL), +m_best(NULL), +m_missed(NULL), +m_silence(0U) +{ + m_txCount = transmitterNames.GetCount(); + m_rxCount = receiverNames.GetCount(); + + wxASSERT(m_txCount > 0U); + wxASSERT(m_rxCount > 0U); + + m_txAddresses = new in_addr[m_txCount]; + m_txPorts = new unsigned int[m_txCount]; + m_txTimers = new CTimer*[m_txCount]; + + m_rxAddresses = new in_addr[m_rxCount]; + m_rxPorts = new unsigned int[m_rxCount]; + m_rxTimers = new CTimer*[m_rxCount]; + + m_header = new unsigned char[RADIO_HEADER_LENGTH_BYTES]; + + m_id = new wxUint16[m_rxCount]; + m_valid = new bool[m_rxCount]; + m_packets = new unsigned int[m_rxCount]; + m_best = new unsigned int[m_rxCount]; + m_missed = new unsigned int[m_rxCount]; + + for (unsigned int i = 0U; i < m_rxCount; i++) { + m_rxTimers[i] = new CTimer(1000U, REGISTRATION_TIMEOUT); + m_rxPorts[i] = 0U; + m_valid[i] = false; + m_id[i] = 0x00U; + m_packets[i] = 0U; + m_best[i] = 0U; + m_missed[i] = 0U; + } + + for (unsigned int i = 0U; i < m_txCount; i++) { + m_txTimers[i] = new CTimer(1000U, REGISTRATION_TIMEOUT); + m_txPorts[i] = 0U; + } + + m_slots = new CAMBESlot*[21U]; + for (unsigned int i = 0U; i < 21U; i++) + m_slots[i] = new CAMBESlot(m_rxCount); +} + +CSplitController::~CSplitController() +{ + delete[] m_txAddresses; + delete[] m_txPorts; + delete[] m_rxAddresses; + delete[] m_rxPorts; + delete[] m_missed; + delete[] m_best; + delete[] m_packets; + delete[] m_header; + delete[] m_id; + delete[] m_valid; + + for (unsigned int i = 0U; i < 21U; i++) + delete m_slots[i]; + delete[] m_slots; + + for (unsigned int i = 0U; i < m_txCount; i++) + delete m_txTimers[i]; + for (unsigned int i = 0U; i < m_rxCount; i++) + delete m_rxTimers[i]; + + delete[] m_txTimers; + delete[] m_rxTimers; +} + +bool CSplitController::start() +{ + bool ret = m_handler.open(); + if (!ret) + return false; + + Create(); + SetPriority(100U); + Run(); + + return true; +} + +void* CSplitController::Entry() +{ + wxLogMessage(wxT("Starting Split Controller thread")); + + wxStopWatch stopWatch; + + while (!m_stopped) { + stopWatch.Start(); + + transmit(); + + receive(); + + Sleep(10UL); + + unsigned int ms = stopWatch.Time(); + timers(ms); + } + + wxLogMessage(wxT("Stopping Split Controller thread")); + + m_handler.close(); + + return NULL; +} + +bool CSplitController::writeHeader(const CHeaderData& header) +{ + bool ret = m_txData.hasSpace(RADIO_HEADER_LENGTH_BYTES); + if (!ret) { + wxLogWarning(wxT("No space to write the header")); + return false; + } + + unsigned char buffer[100U]; + + ::memset(buffer, ' ', RADIO_HEADER_LENGTH_BYTES - 2U); + + buffer[0U] = header.getFlag1(); + buffer[1U] = header.getFlag2(); + buffer[2U] = header.getFlag3(); + + wxString rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 3U] = rpt2.GetChar(i); + + wxString rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 11U] = rpt1.GetChar(i); + + wxString your = header.getYourCall(); + for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 19U] = your.GetChar(i); + + wxString my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 27U] = my1.GetChar(i); + + wxString my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 35U] = my2.GetChar(i); + + wxMutexLocker locker(m_mutex); + + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES - 2U; + m_txData.addData(data, 2U); + + m_txData.addData(buffer, RADIO_HEADER_LENGTH_BYTES - 2U); + + return true; +} + +bool CSplitController::writeData(const unsigned char* data, unsigned int length, bool end) +{ + bool ret = m_txData.hasSpace(DV_FRAME_LENGTH_BYTES + 2U); + if (!ret) { + wxLogWarning(wxT("No space to write data")); + return false; + } + + wxMutexLocker locker(m_mutex); + + unsigned char buffer[2U]; + buffer[0U] = end ? DSMTT_EOT : DSMTT_DATA; + buffer[1U] = DV_FRAME_LENGTH_BYTES; + m_txData.addData(buffer, 2U); + + m_txData.addData(data, DV_FRAME_LENGTH_BYTES); + + return true; +} + +unsigned int CSplitController::getSpace() +{ + return m_txData.freeSpace() / (DV_FRAME_LENGTH_BYTES + 2U); +} + +bool CSplitController::isTXReady() +{ + return m_txData.isEmpty(); +} + +void CSplitController::transmit() +{ + if (!m_txData.hasData()) + return; + + unsigned char type = 0U; + unsigned char length = 0U; + unsigned char buffer[RADIO_HEADER_LENGTH_BYTES]; + { + wxMutexLocker locker(m_mutex); + + m_txData.getData(&type, 1U); + m_txData.getData(&length, 1U); + m_txData.getData(buffer, length); + } + + if (type == DSMTT_HEADER) { + m_outId = (::rand() % 65535U) + 1U; + m_outSeq = 0U; + m_tx = true; + + for (unsigned int i = 0U; i < m_txCount; i++) { + if (m_txPorts[i] > 0U) + m_handler.writeHeader(buffer, m_outId, m_txAddresses[i], m_txPorts[i]); + } + } else { + // If this is a data sync, reset the sequence to zero + if (buffer[9] == 0x55U && buffer[10] == 0x2DU && buffer[11] == 0x16U) + m_outSeq = 0U; + + if (type == DSMTT_EOT) { + m_outSeq |= 0x40U; + m_tx = false; + } + + for (unsigned int i = 0U; i < m_txCount; i++) { + if (m_txPorts[i] > 0U) + m_handler.writeData(buffer, length, m_outId, m_outSeq, m_txAddresses[i], m_txPorts[i]); + } + + m_outSeq++; + if (m_outSeq > 0x14U) + m_outSeq = 0U; + } +} + +void CSplitController::receive() +{ + NETWORK_TYPE type = NETWORK_DATA; + + while (type != NETWORK_NONE) { + wxUint16 id; + in_addr address; + unsigned int port; + + type = m_handler.read(id, address, port); + + if (type == NETWORK_NONE) { + // Do nothing + } else if (type == NETWORK_HEADER) { + unsigned char header[RADIO_HEADER_LENGTH_BYTES]; + unsigned int length = m_handler.readHeader(header, RADIO_HEADER_LENGTH_BYTES); + + // A length of zero is a bad checksum, ignore it + if (length > 0U) { + bool found = false; + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (address.s_addr == m_rxAddresses[i].s_addr && port == m_rxPorts[i]) { + processHeader(i, id, header, length); + m_rxTimers[i]->start(); + found = true; + break; + } + } + + if (!found) { + wxString addr(::inet_ntoa(address), wxConvLocal); + wxLogError(wxT("Header received from unknown repeater - %s:%u"), addr.c_str(), port); + } + } + } else if (type == NETWORK_DATA) { + unsigned char ambe[DV_FRAME_MAX_LENGTH_BYTES]; + wxUint8 seqNo; + unsigned int errors; + unsigned int length = m_handler.readData(ambe, DV_FRAME_MAX_LENGTH_BYTES, seqNo, errors); + + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (address.s_addr == m_rxAddresses[i].s_addr && port == m_rxPorts[i]) { + processAMBE(i, id, ambe, length, seqNo, errors); + m_rxTimers[i]->start(); + break; + } + } + } else if (type == NETWORK_REGISTER) { + // These can be from transmitters and receivers + wxString name; + m_handler.readRegister(name); + + int n1 = m_receiverNames.Index(name); + int n2 = m_transmitterNames.Index(name); + + if (n1 != wxNOT_FOUND) { + wxASSERT(n1 < int(m_rxCount)); + m_rxTimers[n1]->start(); + + if (m_rxAddresses[n1].s_addr != address.s_addr || m_rxPorts[n1] != port) { + wxString addr1(::inet_ntoa(m_rxAddresses[n1]), wxConvLocal); + wxString addr2(::inet_ntoa(address), wxConvLocal); + + if (m_rxPorts[n1] == 0U) + wxLogMessage(wxT("Registration of RX %d \"%s\" set to %s:%u"), n1 + 1, name.c_str(), addr2.c_str(), port); + else + wxLogMessage(wxT("Registration of RX %d \"%s\" changed from %s:%u to %s:%u"), n1 + 1, name.c_str(), addr1.c_str(), m_rxPorts[n1], addr2.c_str(), port); + + m_rxAddresses[n1].s_addr = address.s_addr; + m_rxPorts[n1] = port; + } + } + + if (n2 != wxNOT_FOUND) { + wxASSERT(n2 < int(m_txCount)); + m_txTimers[n2]->start(); + + if (m_txAddresses[n2].s_addr != address.s_addr || m_txPorts[n2] != port) { + wxString addr1(::inet_ntoa(m_txAddresses[n2]), wxConvLocal); + wxString addr2(::inet_ntoa(address), wxConvLocal); + + if (m_txPorts[n2] == 0U) + wxLogMessage(wxT("Registration of TX %d \"%s\" set to %s:%u"), n2 + 1, name.c_str(), addr2.c_str(), port); + else + wxLogMessage(wxT("Registration of TX %d \"%s\" changed from %s:%u to %s:%u"), n2 + 1, name.c_str(), addr1.c_str(), m_txPorts[n2], addr2.c_str(), port); + + m_txAddresses[n2].s_addr = address.s_addr; + m_txPorts[n2] = port; + } + } + + if (n1 == wxNOT_FOUND && n2 == wxNOT_FOUND) { + wxString addr(::inet_ntoa(address), wxConvLocal); + wxLogError(wxT("Registration of \"%s\" received from unknown repeater - %s:%u"), name.c_str(), addr.c_str(), port); + } + } else { + wxString addr(::inet_ntoa(address), wxConvLocal); + wxLogError(wxT("Received invalid frame type %d from %s:%u"), int(type), addr.c_str(), port); + } + } +} + +void CSplitController::timers(unsigned int ms) +{ + m_endTimer.clock(ms); + if (m_endTimer.isRunning() && m_endTimer.hasExpired()) { + printStats(); + + wxMutexLocker locker(m_mutex); + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_listening = true; + m_endTimer.stop(); + + return; + } + + // Run the registration timers + for (unsigned int i = 0U; i < m_txCount; i++) { + m_txTimers[i]->clock(ms); + if (m_txTimers[i]->isRunning() && m_txTimers[i]->hasExpired()) { + wxLogWarning(wxT("TX %u registration has expired"), i + 1U); + m_txTimers[i]->stop(); + m_txPorts[i] = 0U; + } + } + + for (unsigned int i = 0U; i < m_rxCount; i++) { + m_rxTimers[i]->clock(ms); + if (m_rxTimers[i]->isRunning() && m_rxTimers[i]->hasExpired()) { + wxLogWarning(wxT("RX %u registration has expired"), i + 1U); + m_rxTimers[i]->stop(); + m_rxPorts[i] = 0U; + } + } + + // If we are listening then no need to run the slot timers + if (m_listening) + return; + + // Run the slot timers + for (unsigned int i = 0U; i < 21U; i++) + m_slots[i]->m_timer.clock(ms); + + // Check for expired timers + for (unsigned int i = 0U; i < 21U; i++) { + CAMBESlot* slot = m_slots[m_outSeqNo]; + + // Got to an unexpired timer + if (!slot->m_timer.hasExpired()) + return; + + // The last frame? + if (isEnd(*slot)) { + printStats(); + + wxMutexLocker locker(m_mutex); + + if (!m_headerSent) + sendHeader(); + + unsigned char data[2U]; + data[0U] = DSMTT_EOT; + data[1U] = 0U; + m_rxData.addData(data, 2U); + + m_listening = true; + m_endTimer.stop(); + + return; + } + + // Is there any data? + if (slot->m_length > 0U) { + m_best[slot->m_best]++; + + wxMutexLocker locker(m_mutex); + + if (!m_headerSent) + sendHeader(); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(slot->m_ambe, DV_FRAME_LENGTH_BYTES); + } else { + m_silence++; + + // Send a silence frame to the repeater + wxMutexLocker locker(m_mutex); + + if (!m_headerSent) + sendHeader(); + + unsigned char data[2U]; + data[0U] = DSMTT_DATA; + data[1U] = DV_FRAME_LENGTH_BYTES; + m_rxData.addData(data, 2U); + + m_rxData.addData(NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + } + + slot->reset(); + slot->m_timer.start(0U, 21U * DSTAR_FRAME_TIME_MS); + + m_outSeqNo++; + if (m_outSeqNo >= 21U) + m_outSeqNo = 0U; + } +} + +void CSplitController::processHeader(unsigned int n, wxUint16 id, const unsigned char* header, unsigned int length) +{ + if (m_listening) { + ::memcpy(m_header, header, RADIO_HEADER_LENGTH_BYTES - 2U); + + // Reset the slots and start the timers + for (unsigned int i = 0U; i < 21U; i++) { + m_slots[i]->reset(); + m_slots[i]->m_timer.start(0U, DSTAR_FRAME_TIME_MS + DSTAR_FRAME_TIME_MS * i + m_timeout); + } + + for (unsigned int i = 0U; i < m_rxCount; i++) { + m_valid[i] = false; + m_id[i] = 0x00U; + m_packets[i] = 0U; + m_best[i] = 0U; + m_missed[i] = 0U; + } + m_silence = 0U; + + m_valid[n] = true; + m_id[n] = id; + + m_inSeqNo = 0U; + m_outSeqNo = 0U; + m_listening = false; + m_headerSent = false; + m_endTimer.start(); + } else { + m_valid[n] = ::memcmp(m_header, header, RADIO_HEADER_LENGTH_BYTES - 2U) == 0; + m_id[n] = id; // Makes no difference as the valid flag will ensure we ignore bad ids + } +} + +void CSplitController::processAMBE(unsigned int n, wxUint16 id, const unsigned char* ambe, unsigned int length, wxUint8 seqNo, unsigned char errors) +{ + if (m_listening) + return; + + if (!m_valid[n] || id != m_id[n]) + return; + + // Mask out the control bits of the sequence number + unsigned char seqNo1 = seqNo & 0x1FU; + + // Check for an out of order frame + unsigned char seqNo2 = m_inSeqNo; + unsigned char count = 0U; + while (seqNo1 != seqNo2) { + count++; + + seqNo2++; + if (seqNo2 >= 21U) + seqNo2 = 0U; + } + + if (count > 18U) { + m_missed[n]++; + return; + } + + m_inSeqNo = (seqNo & 0x3FU) + 1U; + if (m_inSeqNo >= 21U) + m_inSeqNo = 0U; + + CAMBESlot* slot = m_slots[seqNo & 0x3FU]; + + m_endTimer.start(); + + bool end = (seqNo & 0x40U) == 0x40U; + slot->m_valid[n] = true; + m_packets[n]++; + + // An EOF removes the receiver from all future data + if (end) { + slot->m_end[n] = true; + m_valid[n] = false; + } + + // If the existing data is of a better quality and not an end packet + if (slot->m_errors < errors || end) + return; + + ::memcpy(slot->m_ambe, ambe, length); + slot->m_length = length; + + slot->m_errors = errors; + slot->m_best = n; +} + +bool CSplitController::isEnd(const CAMBESlot& slot) const +{ + bool hasNoEnd = false; + bool hasEnd = false; + + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (slot.m_valid[i] && slot.m_end[i]) + hasEnd = true; + + if (slot.m_valid[i] && !slot.m_end[i]) + hasNoEnd = true; + } + + return hasEnd && !hasNoEnd; +} + +void CSplitController::sendHeader() +{ + // Assume that the caller has the mutex lock + unsigned char data[2U]; + data[0U] = DSMTT_HEADER; + data[1U] = RADIO_HEADER_LENGTH_BYTES - 2U; + m_rxData.addData(data, 2U); + + m_rxData.addData(m_header, RADIO_HEADER_LENGTH_BYTES - 2U); + + m_headerSent = true; +} + +void CSplitController::printStats() const +{ + // Count the total number of packets in this transmission + unsigned int total = m_silence + 1U; + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (m_rxPorts[i] > 0U) + total += m_best[i]; + } + + wxString temp; + wxString text = wxT("Packets: total/"); + + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (m_rxPorts[i] > 0U) { + temp.Printf(wxT("rx%u/"), i + 1U); + text.Append(temp); + } + } + + temp.Printf(wxT("silence %u/"), total); + text.Append(temp); + + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (m_rxPorts[i] > 0U) { + temp.Printf(wxT("%u/"), m_packets[i]); + text.Append(temp); + } + } + + temp.Printf(wxT("%u, Proportion: "), m_silence); + text.Append(temp); + + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (m_rxPorts[i] > 0U) { + temp.Printf(wxT("rx%u/"), i + 1U); + text.Append(temp); + } + } + + text.Append(wxT("silence ")); + + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (m_rxPorts[i] > 0U) { + temp.Printf(wxT("%u%%%%/"), (100U * m_best[i]) / total); + text.Append(temp); + } + } + + temp.Printf(wxT("%u%%%%, Missed: "), (100U * m_silence) / total); + text.Append(temp); + + unsigned int n = 0U; + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (m_rxPorts[i] > 0U) { + if (n > 0U) + temp.Printf(wxT("/rx%u"), i + 1U); + else + temp.Printf(wxT("rx%u"), i + 1U); + + n++; + text.Append(temp); + } + } + + text.Append(wxT(" ")); + + n = 0U; + for (unsigned int i = 0U; i < m_rxCount; i++) { + if (m_rxPorts[i] > 0U) { + if (n > 0U) + temp.Printf(wxT("/%u%%%%"), (100U * m_missed[i]) / total); + else + temp.Printf(wxT("%u%%%%"), (100U * m_missed[i]) / total); + + n++; + text.Append(temp); + } + } + + wxLogMessage(text); +} diff --git a/Common/SplitController.h b/Common/SplitController.h new file mode 100644 index 0000000..e53b2ce --- /dev/null +++ b/Common/SplitController.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef SplitController_H +#define SplitController_H + +#include "GatewayProtocolHandler.h" +#include "DStarDefines.h" +#include "RingBuffer.h" +#include "Timer.h" +#include "Modem.h" +#include "Utils.h" + +#include + +class CAMBESlot { +public: + CAMBESlot(unsigned int rxCount); + ~CAMBESlot(); + + void reset(); + + bool isFirst() const; + + bool* m_valid; + unsigned int m_errors; + unsigned int m_best; + unsigned char* m_ambe; + unsigned int m_length; + bool* m_end; + CTimer m_timer; + +private: + unsigned int m_rxCount; +}; + +class CSplitController : public CModem { +public: + CSplitController(const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout); + virtual ~CSplitController(); + + virtual void* Entry(); + + virtual bool start(); + + virtual unsigned int getSpace(); + virtual bool isTXReady(); + + virtual bool writeHeader(const CHeaderData& header); + virtual bool writeData(const unsigned char* data, unsigned int length, bool end); + +private: + CGatewayProtocolHandler m_handler; + wxArrayString m_transmitterNames; + wxArrayString m_receiverNames; + unsigned int m_timeout; + unsigned int m_txCount; + unsigned int m_rxCount; + in_addr* m_txAddresses; + unsigned int* m_txPorts; + CTimer** m_txTimers; + in_addr* m_rxAddresses; + unsigned int* m_rxPorts; + CTimer** m_rxTimers; + CRingBuffer m_txData; + wxUint16 m_outId; + wxUint8 m_outSeq; + CTimer m_endTimer; + bool m_listening; + wxUint8 m_inSeqNo; + wxUint8 m_outSeqNo; + unsigned char* m_header; + wxUint16* m_id; + bool* m_valid; + CAMBESlot** m_slots; + bool m_headerSent; + unsigned int* m_packets; + unsigned int* m_best; + unsigned int* m_missed; + unsigned int m_silence; + + void transmit(); + void receive(); + void timers(unsigned int ms); + void processHeader(unsigned int n, wxUint16 id, const unsigned char* header, unsigned int length); + void processAMBE(unsigned int n, wxUint16 id, const unsigned char* ambe, unsigned int length, wxUint8 seqNo, unsigned char errors); + bool isEnd(const CAMBESlot& slot) const; + void sendHeader(); + void printStats() const; +}; + +#endif diff --git a/Common/TCPReaderWriter.cpp b/Common/TCPReaderWriter.cpp new file mode 100644 index 0000000..7effc2c --- /dev/null +++ b/Common/TCPReaderWriter.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2010,2011,2012 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "TCPReaderWriter.h" +#include "UDPReaderWriter.h" + +#if !defined(__WINDOWS__) +#include +#endif + + +CTCPReaderWriter::CTCPReaderWriter(const wxString& address, unsigned int port, const wxString& localAddress) : +m_address(address), +m_port(port), +m_localAddress(localAddress), +m_fd(-1) +{ + wxASSERT(!address.IsEmpty()); + wxASSERT(port > 0U); + +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + wxLogError(wxT("Error from WSAStartup")); +#endif +} + +CTCPReaderWriter::CTCPReaderWriter() : +m_address(), +m_port(0U), +m_localAddress(), +m_fd(-1) +{ +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + wxLogError(wxT("Error from WSAStartup")); +#endif +} + +CTCPReaderWriter::~CTCPReaderWriter() +{ +#if defined(__WINDOWS__) + ::WSACleanup(); +#endif +} + +bool CTCPReaderWriter::open(const wxString& address, unsigned int port, const wxString& localAddress) +{ + m_address = address; + m_port = port; + m_localAddress = localAddress; + + return open(); +} + +bool CTCPReaderWriter::open() +{ + if (m_fd != -1) + return true; + + if (m_address.IsEmpty() || m_port == 0U) + return false; + + m_fd = ::socket(PF_INET, SOCK_STREAM, 0); + if (m_fd < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot create the TCP socket, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot create the TCP socket, err=%d"), errno); +#endif + return false; + } + + if (!m_localAddress.IsEmpty()) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = 0U; +#if defined(__WINDOWS__) + addr.sin_addr.s_addr = ::inet_addr(m_localAddress.mb_str()); +#else + addr.sin_addr.s_addr = ::inet_addr(m_localAddress.mb_str()); +#endif + if (addr.sin_addr.s_addr == INADDR_NONE) { + wxLogError(wxT("The address is invalid - %s"), m_localAddress.c_str()); + close(); + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot bind the TCP address, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot bind the TCP address, err=%d"), errno); +#endif + close(); + return false; + } + } + + struct sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr = CUDPReaderWriter::lookup(m_address); + + if (addr.sin_addr.s_addr == INADDR_NONE) { + close(); + return false; + } + + if (::connect(m_fd, (sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot connect the TCP socket, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot connect the TCP socket, err=%d"), errno); +#endif + close(); + return false; + } + + int noDelay = 1; + if (::setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&noDelay, sizeof(noDelay)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot set the TCP socket option, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot set the TCP socket option, err=%d"), errno); +#endif + close(); + return false; + } + + return true; +} + +int CTCPReaderWriter::read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs) +{ + wxASSERT(buffer != NULL); + wxASSERT(length > 0U); + wxASSERT(m_fd != -1); + + // Check that the recv() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(__WINDOWS__) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return after timeout + timeval tv; + tv.tv_sec = secs; + tv.tv_usec = msecs * 1000; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from TCP select, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Error returned from TCP select, err=%d"), errno); +#endif + return -1; + } + +#if defined(__WINDOWS__) + if (!FD_ISSET((unsigned int)m_fd, &readFds)) + return 0; +#else + if (!FD_ISSET(m_fd, &readFds)) + return 0; +#endif + + ssize_t len = ::recv(m_fd, (char*)buffer, length, 0); + if (len < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from recv, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Error returned from recv, err=%d"), errno); +#endif + return -1; + } + + return len; +} + +bool CTCPReaderWriter::write(const unsigned char* buffer, unsigned int length) +{ + wxASSERT(buffer != NULL); + wxASSERT(length > 0U); + wxASSERT(m_fd != -1); + + ssize_t ret = ::send(m_fd, (char *)buffer, length, 0); + if (ret != ssize_t(length)) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from send, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Error returned from send, err=%d"), errno); +#endif + return false; + } + + return true; +} + +void CTCPReaderWriter::close() +{ + if (m_fd != -1) { +#if defined(__WINDOWS__) + ::closesocket(m_fd); +#else + ::close(m_fd); +#endif + m_fd = -1; + } +} diff --git a/Common/TCPReaderWriter.h b/Common/TCPReaderWriter.h new file mode 100644 index 0000000..b67d53f --- /dev/null +++ b/Common/TCPReaderWriter.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010,2011,2012 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef TCPReaderWriter_H +#define TCPReaderWriter_H + +#include + +#if !defined(__WINDOWS__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +class CTCPReaderWriter { +public: + CTCPReaderWriter(const wxString& address, unsigned int port, const wxString& localAddress = wxEmptyString); + CTCPReaderWriter(); + ~CTCPReaderWriter(); + + bool open(const wxString& address, unsigned int port, const wxString& localAddress = wxEmptyString); + bool open(); + + int read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs = 0U); + bool write(const unsigned char* buffer, unsigned int length); + + void close(); + +private: + wxString m_address; + unsigned short m_port; + wxString m_localAddress; + int m_fd; +}; + +#endif diff --git a/Common/Timer.cpp b/Common/Timer.cpp new file mode 100644 index 0000000..39e2ff1 --- /dev/null +++ b/Common/Timer.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009,2010 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Timer.h" + +#include + +CTimer::CTimer(unsigned int ticksPerSec, unsigned int secs, unsigned int msecs) : +m_ticksPerSec(ticksPerSec), +m_timeout(0U), +m_timer(0U) +{ + wxASSERT(ticksPerSec > 0U); + + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned wxLongLong_t temp = (secs * wxULL(1000) + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / wxULL(1000) + wxULL(1)); + } +} + +CTimer::~CTimer() +{ +} + +void CTimer::setTimeout(unsigned int secs, unsigned int msecs) +{ + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned wxLongLong_t temp = (secs * wxULL(1000) + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / wxULL(1000) + wxULL(1)); + } else { + m_timeout = 0U; + m_timer = 0U; + } +} + +unsigned int CTimer::getTimeout() const +{ + if (m_timeout == 0U) + return 0U; + + return (m_timeout - 1U) / m_ticksPerSec; +} + +unsigned int CTimer::getTimer() const +{ + if (m_timer == 0U) + return 0U; + + return (m_timer - 1U) / m_ticksPerSec; +} diff --git a/Common/Timer.h b/Common/Timer.h new file mode 100644 index 0000000..711242d --- /dev/null +++ b/Common/Timer.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009,2010,2011,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Timer_H +#define Timer_H + +class CTimer { +public: + CTimer(unsigned int ticksPerSec = 1000, unsigned int secs = 0U, unsigned int msecs = 0U); + ~CTimer(); + + void setTimeout(unsigned int secs, unsigned int msecs = 0U); + + unsigned int getTimeout() const; + unsigned int getTimer() const; + + unsigned int getRemaining() + { + if (m_timeout == 0U || m_timer == 0U) + return 0U; + + if (m_timer >= m_timeout) + return 0U; + + return (m_timeout - m_timer) / m_ticksPerSec; + } + + bool isRunning() + { + return m_timer > 0U; + } + + void start(unsigned int secs, unsigned int msecs = 0U) + { + setTimeout(secs, msecs); + + start(); + } + + void start() + { + if (m_timeout > 0U) + m_timer = 1U; + } + + void stop() + { + m_timer = 0U; + } + + bool hasExpired() + { + if (m_timeout == 0U || m_timer == 0U) + return false; + + if (m_timer >= m_timeout) + return true; + + return false; + } + + void clock(unsigned int ticks = 1U) + { + if (m_timer > 0U && m_timeout > 0U) + m_timer += ticks; + } + +private: + unsigned int m_ticksPerSec; + unsigned int m_timeout; + unsigned int m_timer; +}; + +#endif diff --git a/Common/UDPReaderWriter.cpp b/Common/UDPReaderWriter.cpp new file mode 100644 index 0000000..5a18a8e --- /dev/null +++ b/Common/UDPReaderWriter.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2006-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "UDPReaderWriter.h" + +#if !defined(__WINDOWS__) +#include +#endif + + +CUDPReaderWriter::CUDPReaderWriter(const wxString& address, unsigned int port) : +m_address(address), +m_port(port), +m_addr(), +m_fd(-1) +{ +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + wxLogError(wxT("Error from WSAStartup")); +#endif +} + +CUDPReaderWriter::~CUDPReaderWriter() +{ +#if defined(__WINDOWS__) + ::WSACleanup(); +#endif +} + +in_addr CUDPReaderWriter::lookup(const wxString& hostname) +{ + in_addr addr; +#if defined(WIN32) + unsigned long address = ::inet_addr(hostname.mb_str()); + if (address != INADDR_NONE && address != INADDR_ANY) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.mb_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + wxLogError(wxT("Cannot find address for host %s"), hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#else + in_addr_t address = ::inet_addr(hostname.mb_str()); + if (address != in_addr_t(-1)) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.mb_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + wxLogError(wxT("Cannot find address for host %s"), hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#endif +} + +bool CUDPReaderWriter::open() +{ + m_fd = ::socket(PF_INET, SOCK_DGRAM, 0); + if (m_fd < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot create the UDP socket, err: %lu"), ::GetLastError()); +#else + wxLogError(wxT("Cannot create the UDP socket, err: %d"), errno); +#endif + return false; + } + + if (m_port > 0U) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (!m_address.IsEmpty()) { +#if defined(__WINDOWS__) + addr.sin_addr.s_addr = ::inet_addr(m_address.mb_str()); +#else + addr.sin_addr.s_addr = ::inet_addr(m_address.mb_str()); +#endif + if (addr.sin_addr.s_addr == INADDR_NONE) { + wxLogError(wxT("The address is invalid - %s"), m_address.c_str()); + return false; + } + } + + int reuse = 1; + if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot set the UDP socket option (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Cannot set the UDP socket option (port: %u), err: %d"), m_port, errno); +#endif + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot bind the UDP address (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Cannot bind the UDP address (port: %u), err: %d"), m_port, errno); +#endif + return false; + } + } + + return true; +} + +int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port) +{ + // Check that the readfrom() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(__WINDOWS__) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return immediately + timeval tv; + tv.tv_sec = 0L; + tv.tv_usec = 0L; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from UDP select (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Error returned from UDP select (port: %u), err: %d"), m_port, errno); +#endif + return -1; + } + + if (ret == 0) + return 0; + + sockaddr_in addr; +#if defined(__WINDOWS__) + int size = sizeof(sockaddr_in); +#else + socklen_t size = sizeof(sockaddr_in); +#endif + + ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); + if (len <= 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from recvfrom (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Error returned from recvfrom (port: %u), err: %d"), m_port, errno); +#endif + return -1; + } + + address = addr.sin_addr; + port = ntohs(addr.sin_port); + + return len; +} + +bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port) +{ + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + + addr.sin_family = AF_INET; + addr.sin_addr = address; + addr.sin_port = htons(port); + + ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); + if (ret < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from sendto (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Error returned from sendto (port: %u), err: %d"), m_port, errno); +#endif + return false; + } + + if (ret != ssize_t(length)) + return false; + + return true; +} + +void CUDPReaderWriter::close() +{ +#if defined(__WINDOWS__) + ::closesocket(m_fd); +#else + ::close(m_fd); +#endif +} + +unsigned int CUDPReaderWriter::getPort() const +{ + return m_port; +} diff --git a/Common/UDPReaderWriter.h b/Common/UDPReaderWriter.h new file mode 100644 index 0000000..4167662 --- /dev/null +++ b/Common/UDPReaderWriter.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009-2011,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef UDPReaderWriter_H +#define UDPReaderWriter_H + +#include + +#if !defined(__WINDOWS__) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +class CUDPReaderWriter { +public: + CUDPReaderWriter(const wxString& address, unsigned int port); + ~CUDPReaderWriter(); + + static in_addr lookup(const wxString& hostName); + + bool open(); + + int read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port); + bool write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port); + + void close(); + + unsigned int getPort() const; + +private: + wxString m_address; + unsigned short m_port; + in_addr m_addr; + int m_fd; +}; + +#endif diff --git a/Common/UDRCController.cpp b/Common/UDRCController.cpp new file mode 100644 index 0000000..31808fd --- /dev/null +++ b/Common/UDRCController.cpp @@ -0,0 +1,215 @@ +/* + * Header here + * + */ + +#include +#include +#include + +#include "UDRCController.h" + +#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) + +// Pin definitions +// These use the BCM numbering system, *NOT* physical pin numbers or WiringPi +// "we need to be special" pin numbering. +static const unsigned int BASE_PIN = 6; +static const unsigned int PTT_PIN = 12; +static const unsigned int PKSQL_PIN = 5; +static const unsigned int SQL_PIN = 25; +static const unsigned int EXT1_PIN = 24; +static const unsigned int EXT2_PIN = 22; +static const unsigned int EXT3_PIN = 23; +static const unsigned int EXT4_PIN = 27; + +static const unsigned int input_pins[] = { + SQL_PIN, + PKSQL_PIN, +}; + +static const unsigned int output_pins[] = { + BASE_PIN, + EXT1_PIN, + EXT2_PIN, + EXT3_PIN, + EXT4_PIN, +}; + +static const char vendor_string_path[] = "/proc/device-tree/hat/vendor"; +static const char product_id_string_path[] = "/proc/device-tree/hat/product_id"; +static const char nwdr_vendor_string[] = "NW Digital Radio"; + +#define UDRC_PRODUCT_ID 2 +#define UDRC_II_PRODUCT_ID 3 + +static int getNWDRProductID() { + int proc_file = -1; + ssize_t read_size; + char buffer[255]; + long product_id = 0; + + if((proc_file = open(vendor_string_path, O_RDONLY|O_NONBLOCK)) == -1) { + wxLogError("Open %s: %s\n", vendor_string_path, strerror(errno)); + } + + read_size = read(proc_file, buffer, sizeof(buffer)); + if(read_size < 1) { + wxLogError("Read %s: %s\n", vendor_string_path, strerror(errno)); + close(proc_file); + return -1; + } + + close(proc_file); + + if(strncmp(nwdr_vendor_string, buffer, sizeof(nwdr_vendor_string))) { + wxLogError("HAT is not a NW Digital Radio Product: %.255s\n", buffer); + return -1; + } + + if((proc_file = open(product_id_string_path, O_RDONLY|O_NONBLOCK)) == -1) { + wxLogError("Open %s: %s\n", product_id_string_path, strerror(errno)); + } + + read_size = read(proc_file, buffer, sizeof(buffer)); + if(read_size < 1) { + wxLogError("Read %s: %s\n", product_id_string_path, strerror(errno)); + close(proc_file); + return -1; + } + + close(proc_file); + + product_id = strtol(buffer, NULL, 16); + if(product_id == EINVAL || product_id == ERANGE) { + wxLogError("Invalid value for product id: %.255s\n", buffer); + return -1; + } + + return product_id; +} + +// XXX This is ugly, we should have a better parent class +CUDRCController::CUDRCController(enum repeater_modes mode) : +CExternalController(NULL, false), +m_mode(mode), +m_pttPin(PTT_PIN) +{ +} + +void CUDRCController::switchMode(enum repeater_modes mode) { + + if(mode == HOTSPOT) + return; + + switch(mode) { + case AUTO_FM: // Auto/FM + ::digitalWrite(EXT1_PIN, HIGH); + ::digitalWrite(EXT2_PIN, HIGH); + break; + case DIGITAL_DIGITAL: // Digital/Digital + ::digitalWrite(EXT1_PIN, HIGH); + ::digitalWrite(EXT2_PIN, LOW); + break; + case FM_FM: // FM/FM Fixed + ::digitalWrite(EXT1_PIN, LOW); + ::digitalWrite(EXT2_PIN, HIGH); + break; + case AUTO_AUTO: // Auto/Auto + ::digitalWrite(EXT1_PIN, LOW); + ::digitalWrite(EXT2_PIN, LOW); + break; + } +} + +bool CUDRCController::open() +{ + if(::wiringPiSetupGpio() != 0) { + wxLogError("Unable to initialize the wiringPi library"); + return false; + } + + if(m_mode != HOTSPOT) { + ::pinMode(m_pttPin, OUTPUT); + + for(int i = 0; i < ARRAY_SIZE(input_pins); ++i) { + ::pinMode(input_pins[i], INPUT); + ::pullUpDnControl(input_pins[i], PUD_UP); + } + + for(int i = 0; i < ARRAY_SIZE(output_pins); ++i) { + ::pinMode(output_pins[i], OUTPUT); + ::digitalWrite(output_pins[i], HIGH); + } + + ::digitalWrite(m_pttPin, HIGH); + + ::digitalWrite(BASE_PIN, LOW); + switchMode(m_mode); + ::digitalWrite(BASE_PIN, HIGH); + } else { + // On a UDRC II in Hotspot mode, the PTT is on EXT3_PIN instead + switch(getNWDRProductID()) { + case UDRC_PRODUCT_ID: + m_pttPin = PTT_PIN; + break; + case UDRC_II_PRODUCT_ID: + m_pttPin = EXT3_PIN; + break; + case -1: + return false; + default: + wxLogError("Unknown NW Digital Radio Product"); + return false; + } + + ::pinMode(m_pttPin, OUTPUT); + ::digitalWrite(m_pttPin, LOW); + } + + return true; +} + +bool CUDRCController::getDisable() const +{ + if(m_mode == HOTSPOT) + return false; + + return ::digitalRead(PKSQL_PIN) == LOW; +} + +void CUDRCController::setRadioTransmit(bool value) +{ + // If we're in hotspot mode, we're using the miniDIN connector that is + // reversed from the normal sense of PTT. + if(m_mode == HOTSPOT) + value = !value; + + ::digitalWrite(m_pttPin, value ? LOW : HIGH); +} + +void CUDRCController::setActive(bool value) +{ + if(m_mode == HOTSPOT) + return; + + if(value == HIGH) { + ::digitalWrite(BASE_PIN, LOW); + switchMode(FM_FM); + } else { + switchMode(m_mode); + ::digitalWrite(BASE_PIN, HIGH); + } +} + +// This needs to be overridden for now because the base class, ExternalController +// will try to dork around with threads that we're not using anymore. +void CUDRCController::close() +{ + if(m_mode == HOTSPOT) { + ::digitalWrite(m_pttPin, LOW); + } else { + ::digitalWrite(m_pttPin, HIGH); + } +} + diff --git a/Common/UDRCController.h b/Common/UDRCController.h new file mode 100644 index 0000000..d7fc6a8 --- /dev/null +++ b/Common/UDRCController.h @@ -0,0 +1,33 @@ +#ifndef UDRCController_H +#define UDRCController_H + +#include "ExternalController.h" + +enum repeater_modes { + HOTSPOT = 0, + AUTO_FM = 1, + DIGITAL_DIGITAL = 3, + FM_FM = 4, + AUTO_AUTO = 2, +}; + +class CUDRCController: public CExternalController { +public: + CUDRCController(enum repeater_modes mode); + + bool getDisable() const; + + void setRadioTransmit(bool value); + void setActive(bool value); + + bool open(); + void close(); + +private: + const enum repeater_modes m_mode; + int m_pttPin; + + void switchMode(enum repeater_modes mode); +}; + +#endif // UDRCController_H diff --git a/Common/URIUSBController.cpp b/Common/URIUSBController.cpp new file mode 100644 index 0000000..893c545 --- /dev/null +++ b/Common/URIUSBController.cpp @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2009-2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "URIUSBController.h" + +const unsigned int C108_VENDOR_ID = 0x0D8CU; +const unsigned int C108_PRODUCT_ID = 0x000CU; +const unsigned int C108AH_PRODUCT_ID = 0x013CU; +const unsigned int C119_PRODUCT_ID = 0x0008U; +const unsigned int C119A_PRODUCT_ID = 0x013AU; + +const char REPORT_ID = 0x00; + +const char HID_OR0 = 0x00; +const char HID_OR1 = 0x00; +const char HID_OR2 = 0x0F; // Set GPIO1 to GPIO4 to output +const char HID_OR3 = 0x00; +const char HID_OR1_GPIO4 = 0x08; // GPIO4, Switched +const char HID_OR1_GPIO3 = 0x04; // GPIO3, PTT +const char HID_OR1_GPIO2 = 0x02; // GPIO2, Switched +const char HID_OR1_GPIO1 = 0x01; // GPIO1, Heartbeat +const char HID_IR0_RM = 0x08; // Record-Mute, Disable +const char HID_IR0_PM = 0x04; // Playback-Mute, Battery +const char HID_IR0_VD = 0x02; // Volume-Down, COR_DET +const char HID_IR0_VU = 0x01; // Volume-Up, CTCSS_DET + +#if defined(__WINDOWS__) + +#include +#include + +const ULONG USB_BUFSIZE = 5UL; + +CURIUSBController::CURIUSBController(unsigned int address, bool checkInput) : +m_address(address), +m_checkInput(checkInput), +m_outp1(false), +m_outp3(false), +m_outp5(false), +m_outp6(false), +m_handle(INVALID_HANDLE_VALUE) +{ +} + +CURIUSBController::~CURIUSBController() +{ +} + +bool CURIUSBController::open() +{ + wxASSERT(m_handle == INVALID_HANDLE_VALUE); + + GUID guid; + ::HidD_GetHidGuid(&guid); + + HDEVINFO devInfo = ::SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + if (devInfo == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); + return false; + } + + SP_DEVICE_INTERFACE_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + unsigned int count = 0U; + for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guid, index, &devInfoData); index++) { + // Find the required length of the device structure + DWORD length; + ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Get the detailed data into the newly allocated device structure + DWORD required; + BOOL res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); + if (!res) { + wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + // Get the handle for getting the attributes + HANDLE handle = ::CreateFile(detailData->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + HIDD_ATTRIBUTES attributes; + attributes.Size = sizeof(HIDD_ATTRIBUTES); + res = ::HidD_GetAttributes(handle, &attributes); + if (!res) { + wxLogError(wxT("Error from HidD_GetAttributes: err=%u"), ::GetLastError()); + ::CloseHandle(handle); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ::CloseHandle(handle); + + // Is this a CM108 or equivalent? + if (attributes.VendorID == C108_VENDOR_ID && + (attributes.ProductID == C108_PRODUCT_ID || + attributes.ProductID == C108AH_PRODUCT_ID || + attributes.ProductID == C119_PRODUCT_ID || + attributes.ProductID == C119A_PRODUCT_ID)) { + count++; + + // Is this the right device? + if (count == m_address) { + m_handle = ::CreateFile(detailData->DevicePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (m_handle == INVALID_HANDLE_VALUE) { + wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError()); + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + return false; + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + ::free(detailData); + + setDigitalOutputs(false, false, false, false, false, false, false, false); + + return true; + } + } + + ::free(detailData); + } + + ::SetupDiDestroyDeviceInfoList(devInfo); + + return false; +} + +void CURIUSBController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + if (!m_checkInput) { + inp1 = inp2 = inp3 = inp4 = inp5 = false; + return; + } + + char buffer[USB_BUFSIZE]; + buffer[0] = REPORT_ID; + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + BOOL res = ::HidD_GetInputReport(m_handle, buffer, USB_BUFSIZE); + if (!res) { + wxLogError(wxT("Error from HidD_GetInputReport: err=%u"), ::GetLastError()); + return; + } + + inp3 = false; + + inp5 = (buffer[1] & HID_IR0_RM) == HID_IR0_RM; // Disable + inp4 = (buffer[1] & HID_IR0_PM) == HID_IR0_PM; // Battery + inp2 = (buffer[1] & HID_IR0_VU) == HID_IR0_VU; // Squelch 2 + inp1 = (buffer[1] & HID_IR0_VD) == HID_IR0_VD; // Squelch 1 +} + +void CURIUSBController::setDigitalOutputs(bool outp1, bool, bool outp3, bool outp4, bool outp5, bool outp6, bool, bool) +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + if (outp1 == m_outp1 && outp3 == m_outp3 && outp5 == m_outp5 && outp6 == m_outp6) + return; + + char buffer[USB_BUFSIZE]; + buffer[0] = REPORT_ID; + buffer[1] = HID_OR0; + buffer[2] = HID_OR1; + buffer[3] = HID_OR2; + buffer[4] = HID_OR3; + + if (outp1) // Transmit + buffer[2] |= HID_OR1_GPIO3; + if (outp3) // Heartbeat + buffer[2] |= HID_OR1_GPIO1; + if (outp4) // Active + buffer[2] |= HID_OR1_GPIO2; + if (outp5) // Output 1 + buffer[2] |= HID_OR1_GPIO4; + + BOOL res = ::HidD_SetOutputReport(m_handle, buffer, USB_BUFSIZE); + if (!res) + wxLogError(wxT("Error from HidD_SetOutputReport: err=%u"), ::GetLastError()); + + m_outp1 = outp1; + m_outp3 = outp3; + m_outp5 = outp5; + m_outp6 = outp6; +} + +void CURIUSBController::close() +{ + wxASSERT(m_handle != INVALID_HANDLE_VALUE); + + setDigitalOutputs(false, false, false, false, false, false, false, false); + + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; +} + +#else + +const unsigned int C108_HID_INTERFACE = 3U; + +const unsigned int HID_REPORT_GET = 0x01U; +const unsigned int HID_REPORT_SET = 0x09U; + +const unsigned int HID_RT_INPUT = 0x01U; +const unsigned int HID_RT_OUTPUT = 0x02U; + +const int USB_BUFSIZE = 4; + +const int USB_TIMEOUT = 5000; + +CURIUSBController::CURIUSBController(unsigned int address, bool checkInput) : +m_address(address), +m_checkInput(checkInput), +m_outp1(false), +m_outp3(false), +m_outp5(false), +m_outp6(false), +m_context(NULL), +m_handle(NULL) +{ + ::libusb_init(&m_context); +} + +CURIUSBController::~CURIUSBController() +{ + wxASSERT(m_context != NULL); + + ::libusb_exit(m_context); +} + +bool CURIUSBController::open() +{ + wxASSERT(m_context != NULL); + wxASSERT(m_handle == NULL); + + libusb_device** list; + ssize_t cnt = ::libusb_get_device_list(NULL, &list); + if (cnt <= 0) { + wxLogError(wxT("Cannot find any USB devices!")); + return false; + } + + int err = 0; + unsigned int count = 0U; + for (ssize_t i = 0; i < cnt; i++) { + libusb_device_descriptor desc; + err = ::libusb_get_device_descriptor(list[i], &desc); + if (err < 0) + continue; + + if (desc.idVendor == C108_VENDOR_ID && + (desc.idProduct == C108_PRODUCT_ID || + desc.idProduct == C108AH_PRODUCT_ID || + desc.idProduct == C119_PRODUCT_ID || + desc.idProduct == C119A_PRODUCT_ID)) { + count++; + + // Is this the correct device? + if (count == m_address) { + err = ::libusb_open(list[i], &m_handle); + break; + } + } + } + + if (err != 0) { + wxLogError(wxT("libusb_open failed, err=%d"), err); + ::libusb_free_device_list(list, 1); + return false; + } + + if (m_handle == NULL) { + wxLogError(wxT("Could not find a suitable CM108 based device")); + ::libusb_free_device_list(list, 1); + return false; + } + + ::libusb_free_device_list(list, 1); + + int res = ::libusb_claim_interface(m_handle, C108_HID_INTERFACE); + if (res != 0) { + res = ::libusb_detach_kernel_driver(m_handle, C108_HID_INTERFACE); + if (res != 0) { + wxLogError(wxT("libusb_detach_kernel_driver failed, err=%d"), res); + ::libusb_close(m_handle); + m_handle = NULL; + return false; + } + + res = ::libusb_claim_interface(m_handle, C108_HID_INTERFACE); + if (res != 0) { + wxLogError(wxT("libusb_claim_interface failed, err=%d"), res); + ::libusb_close(m_handle); + m_handle = NULL; + return false; + } + } + + setDigitalOutputs(false, false, false, false, false, false, false, false); + + return true; +} + +void CURIUSBController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + wxASSERT(m_handle != NULL); + + if (!m_checkInput) { + inp1 = inp2 = inp3 = inp4 = inp5 = false; + return; + } + + unsigned char buffer[USB_BUFSIZE]; + buffer[0] = 0x00; + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + int res = ::libusb_control_transfer(m_handle, LIBUSB_ENDPOINT_IN + LIBUSB_REQUEST_TYPE_CLASS + LIBUSB_RECIPIENT_INTERFACE, HID_REPORT_GET, REPORT_ID + (HID_RT_INPUT << 8), C108_HID_INTERFACE, buffer, USB_BUFSIZE, USB_TIMEOUT); + if (res < 0) { + wxLogError(wxT("Error from libusb_control_transfer: err=%d"), res); + return; + } + + if (res < USB_BUFSIZE) + return; + + inp3 = false; + + inp5 = (buffer[0] & HID_IR0_RM) == HID_IR0_RM; // Disable + inp4 = (buffer[0] & HID_IR0_PM) == HID_IR0_PM; // Battery + inp2 = (buffer[0] & HID_IR0_VU) == HID_IR0_VU; // Squelch 2 + inp1 = (buffer[0] & HID_IR0_VD) == HID_IR0_VD; // Squelch 1 +} + +void CURIUSBController::setDigitalOutputs(bool outp1, bool, bool outp3, bool outp4, bool outp5, bool outp6, bool, bool) +{ + wxASSERT(m_handle != NULL); + + if (outp1 == m_outp1 && outp3 == m_outp3 && outp5 == m_outp5 && outp6 == m_outp6) + return; + + unsigned char buffer[USB_BUFSIZE]; + buffer[0] = HID_OR0; + buffer[1] = HID_OR1; + buffer[2] = HID_OR2; + buffer[3] = HID_OR3; + + if (outp1) // Transmit + buffer[1] |= HID_OR1_GPIO3; + if (outp3) // Heartbeat + buffer[1] |= HID_OR1_GPIO1; + if (outp4) // Active + buffer[1] |= HID_OR1_GPIO2; + if (outp5) // Output 1 + buffer[1] |= HID_OR1_GPIO4; + + int res = ::libusb_control_transfer(m_handle, LIBUSB_ENDPOINT_OUT + LIBUSB_REQUEST_TYPE_CLASS + LIBUSB_RECIPIENT_INTERFACE, HID_REPORT_SET, REPORT_ID + (HID_RT_OUTPUT << 8), C108_HID_INTERFACE, buffer, USB_BUFSIZE, USB_TIMEOUT); + if (res < 0) { + wxLogError(wxT("Error from libusb_control_transfer: err=%d"), res); + return; + } + + if (res < USB_BUFSIZE) + return; + + m_outp1 = outp1; + m_outp3 = outp3; + m_outp5 = outp5; + m_outp6 = outp6; +} + +void CURIUSBController::close() +{ + wxASSERT(m_handle != NULL); + + setDigitalOutputs(false, false, false, false, false, false, false, false); + + ::libusb_release_interface(m_handle, C108_HID_INTERFACE); + + ::libusb_close(m_handle); + m_handle = NULL; +} + +#endif + diff --git a/Common/URIUSBController.h b/Common/URIUSBController.h new file mode 100644 index 0000000..52fb2d1 --- /dev/null +++ b/Common/URIUSBController.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009,2011 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef URIUSBController_H +#define URIUSBController_H + +#include "HardwareController.h" + +#include + +#if defined(__WINDOWS__) +#include +#else +#include +#endif + +class CURIUSBController : public IHardwareController { +public: + CURIUSBController(unsigned int address, bool checkInput); + virtual ~CURIUSBController(); + + virtual bool open(); + + virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5); + + virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8); + + virtual void close(); + +private: + unsigned int m_address; + bool m_checkInput; + bool m_outp1; + bool m_outp3; + bool m_outp5; + bool m_outp6; +#if defined(__WINDOWS__) + HANDLE m_handle; +#else + libusb_context* m_context; + libusb_device_handle* m_handle; +#endif +}; + +#endif diff --git a/Common/Utils.cpp b/Common/Utils.cpp new file mode 100644 index 0000000..b89758d --- /dev/null +++ b/Common/Utils.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009,2014 Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "Utils.h" + +void CUtils::dump(const wxChar* title, const unsigned char* data, unsigned int length) +{ + wxASSERT(title != NULL); + wxASSERT(data != NULL); + + wxLogMessage(wxT("%s"), title); + + unsigned int offset = 0U; + + while (length > 0U) { + wxString output; + + unsigned int bytes = (length > 16U) ? 16U : length; + + for (unsigned i = 0U; i < bytes; i++) { + wxString temp; + temp.Printf(wxT("%02X "), data[offset + i]); + output += temp; + } + + for (unsigned int i = bytes; i < 16U; i++) + output += wxT(" "); + + output += wxT(" *"); + + for (unsigned i = 0U; i < bytes; i++) { + unsigned char c = data[offset + i]; + + if (::isprint(c)) + output += wxChar(c); + else + output += wxT('.'); + } + + output += wxT('*'); + + wxLogMessage(wxT("%04X: %s"), offset, output.c_str()); + + offset += 16U; + + if (length >= 16U) + length -= 16U; + else + length = 0U; + } +} diff --git a/Common/Utils.h b/Common/Utils.h new file mode 100644 index 0000000..21d6b17 --- /dev/null +++ b/Common/Utils.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009,2014 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef Utils_H +#define Utils_H + +#include + +enum TRISTATE { + STATE_FALSE, + STATE_TRUE, + STATE_UNKNOWN +}; + +class CUtils { +public: + static void dump(const wxChar* title, const unsigned char* data, unsigned int length); + +private: +}; + +#endif diff --git a/Common/Version.h b/Common/Version.h new file mode 100644 index 0000000..22b1544 --- /dev/null +++ b/Common/Version.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010-2015,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Version_H +#define Version_H + +#include + +const wxString VENDOR_NAME = wxT("G4KLX"); + +#if defined(__WXDEBUG__) +const wxString VERSION = wxT("20180510 - DEBUG"); +#else +const wxString VERSION = wxT("20180510"); +#endif + +#endif diff --git a/DStarRepeater.sln b/DStarRepeater.sln new file mode 100644 index 0000000..5e1ac62 --- /dev/null +++ b/DStarRepeater.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2000 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DStarRepeater", "DStarRepeater\DStarRepeater.vcxproj", "{E3458953-FC3D-47AF-A245-A1A87DC2BC7E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DStarRepeaterConfig", "DStarRepeaterConfig\DStarRepeaterConfig.vcxproj", "{FBAE8201-11CC-4256-BF39-A860B9DF963C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Common\Common.vcxproj", "{3753EF20-2330-415E-B933-2560A474498B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GUICommon", "GUICommon\GUICommon.vcxproj", "{2C548AAB-A9F7-496F-BCF5-6185BDA304D2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Debug|Win32.ActiveCfg = Debug|Win32 + {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Debug|Win32.Build.0 = Debug|Win32 + {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Debug|x64.ActiveCfg = Debug|x64 + {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Debug|x64.Build.0 = Debug|x64 + {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Release|Win32.ActiveCfg = Release|Win32 + {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Release|Win32.Build.0 = Release|Win32 + {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Release|x64.ActiveCfg = Release|x64 + {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Release|x64.Build.0 = Release|x64 + {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Debug|Win32.ActiveCfg = Debug|Win32 + {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Debug|Win32.Build.0 = Debug|Win32 + {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Debug|x64.ActiveCfg = Debug|x64 + {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Debug|x64.Build.0 = Debug|x64 + {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Release|Win32.ActiveCfg = Release|Win32 + {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Release|Win32.Build.0 = Release|Win32 + {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Release|x64.ActiveCfg = Release|x64 + {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Release|x64.Build.0 = Release|x64 + {3753EF20-2330-415E-B933-2560A474498B}.Debug|Win32.ActiveCfg = Debug|Win32 + {3753EF20-2330-415E-B933-2560A474498B}.Debug|Win32.Build.0 = Debug|Win32 + {3753EF20-2330-415E-B933-2560A474498B}.Debug|x64.ActiveCfg = Debug|x64 + {3753EF20-2330-415E-B933-2560A474498B}.Debug|x64.Build.0 = Debug|x64 + {3753EF20-2330-415E-B933-2560A474498B}.Release|Win32.ActiveCfg = Release|Win32 + {3753EF20-2330-415E-B933-2560A474498B}.Release|Win32.Build.0 = Release|Win32 + {3753EF20-2330-415E-B933-2560A474498B}.Release|x64.ActiveCfg = Release|x64 + {3753EF20-2330-415E-B933-2560A474498B}.Release|x64.Build.0 = Release|x64 + {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Debug|Win32.ActiveCfg = Debug|Win32 + {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Debug|Win32.Build.0 = Debug|Win32 + {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Debug|x64.ActiveCfg = Debug|x64 + {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Debug|x64.Build.0 = Debug|x64 + {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Release|Win32.ActiveCfg = Release|Win32 + {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Release|Win32.Build.0 = Release|Win32 + {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Release|x64.ActiveCfg = Release|x64 + {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F4EB6A2D-4233-46ED-9DEC-6A55E5765F30} + EndGlobalSection +EndGlobal diff --git a/DStarRepeater/DStarRepeater.vcxproj b/DStarRepeater/DStarRepeater.vcxproj new file mode 100644 index 0000000..9045988 --- /dev/null +++ b/DStarRepeater/DStarRepeater.vcxproj @@ -0,0 +1,204 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {E3458953-FC3D-47AF-A245-A1A87DC2BC7E} + DStarRepeater + Win32Proj + 10.0.17134.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud + + + true + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu + + + + Disabled + $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(SolutionDir)Common;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + winusb.lib;wsock32.lib;setupapi.lib;hid.lib;portaudio_x86.lib;%(AdditionalDependencies) + $(SolutionDir)..\portaudio\build\msvc\Win32\Debug;$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + MachineX86 + + + + + Disabled + $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(SolutionDir)Common;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + winusb.lib;wsock32.lib;setupapi.lib;hid.lib;portaudio_x64.lib;%(AdditionalDependencies) + $(SolutionDir)..\portaudio\build\msvc\x64\Debug;$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + + + + + MaxSpeed + true + $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(SolutionDir)Common;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + winusb.lib;wsock32.lib;setupapi.lib;hid.lib;portaudio_x86.lib;%(AdditionalDependencies) + $(SolutionDir)..\portaudio\build\msvc\Win32\Release;$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX86 + + + + + MaxSpeed + true + $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(SolutionDir)Common;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + winusb.lib;wsock32.lib;setupapi.lib;hid.lib;portaudio_x64.lib;%(AdditionalDependencies) + $(SolutionDir)..\portaudio\build\msvc\x64\Release;$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3753ef20-2330-415e-b933-2560a474498b} + false + + + + + + \ No newline at end of file diff --git a/DStarRepeater/DStarRepeater.vcxproj.filters b/DStarRepeater/DStarRepeater.vcxproj.filters new file mode 100644 index 0000000..078751f --- /dev/null +++ b/DStarRepeater/DStarRepeater.vcxproj.filters @@ -0,0 +1,74 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/DStarRepeater/DStarRepeaterApp.cpp b/DStarRepeater/DStarRepeaterApp.cpp new file mode 100644 index 0000000..ff80d73 --- /dev/null +++ b/DStarRepeater/DStarRepeaterApp.cpp @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include + +#include "DStarRepeaterTXRXThread.h" +#include "RepeaterProtocolHandler.h" +#include "DStarRepeaterTRXThread.h" +#include "DStarRepeaterTXThread.h" +#include "DStarRepeaterRXThread.h" +#include "SerialLineController.h" +#include "DStarRepeaterLogger.h" +#include "DStarRepeaterThread.h" +#include "SoundCardController.h" +#include "DVRPTRV1Controller.h" +#include "DVRPTRV2Controller.h" +#include "DVRPTRV3Controller.h" +#include "ArduinoController.h" +#include "DVMegaController.h" +#include "DStarRepeaterApp.h" +#include "MMDVMController.h" +#include "URIUSBController.h" +#include "K8055Controller.h" +#include "DummyController.h" +#include "SplitController.h" +#if defined(GPIO) +#include "GPIOController.h" +#include "UDRCController.h" +#endif +#include "DVAPController.h" +#include "GMSKController.h" +#include "DStarDefines.h" +#include "Version.h" +#include "Logger.h" + +IMPLEMENT_APP(CDStarRepeaterApp) + +wxDEFINE_EVENT(wxEVT_THREAD_COMMAND, wxThreadEvent); + +wxBEGIN_EVENT_TABLE(CDStarRepeaterApp, wxApp) + EVT_THREAD(wxEVT_THREAD_COMMAND, CDStarRepeaterApp::OnRemoteCmd) +wxEND_EVENT_TABLE() + +const wxString NAME_PARAM = "Repeater Name"; +const wxString NOLOGGING_SWITCH = "nolog"; +const wxString GUI_SWITCH = "gui"; +const wxString LOGDIR_OPTION = "logdir"; +const wxString CONFDIR_OPTION = "confdir"; +const wxString AUDIODIR_OPTION = "audiodir"; +#if (wxUSE_GUI == 1) +const wxString LOG_BASE_NAME = "dstarrepeater"; +#else +const wxString LOG_BASE_NAME = "dstarrepeaterd"; +#endif + +CDStarRepeaterApp::CDStarRepeaterApp() : +#if (wxUSE_GUI == 1) +m_frame(NULL), +#endif +wxApp(), +m_name(), +m_nolog(false), +m_gui(false), +m_logDir(), +m_confDir(), +m_audioDir(), +m_thread(NULL), +m_config(NULL), +m_checker(NULL), +m_logChain(NULL) +{ +} + +CDStarRepeaterApp::~CDStarRepeaterApp() +{ +} + +bool CDStarRepeaterApp::OnInit() +{ + SetVendorName(VENDOR_NAME); + + if (!wxApp::OnInit()) + return false; + + if (!m_nolog) { + wxString logBaseName = LOG_BASE_NAME; + if (!m_name.IsEmpty()) { + logBaseName.Append("_"); + logBaseName.Append(m_name); + } + +#if defined(__WINDOWS__) + if (m_logDir.IsEmpty()) + m_logDir = ::wxGetHomeDir(); +#else + if (m_logDir.IsEmpty()) + m_logDir = LOG_DIR; +#endif + + try { + CLogger* log = new CLogger(m_logDir, logBaseName); + wxLog::SetActiveTarget(log); + } catch ( const std::runtime_error& e ) { + wxLog::SetActiveTarget(new wxLogStderr()); + wxLogError("Could not open log file, logging to stderr"); + } + } else { + new wxLogNull; + } + + m_logChain = new wxLogChain(new CDStarRepeaterLogger); + + wxString appName; + if (!m_name.IsEmpty()) + appName = APPLICATION_NAME + " " + m_name; + else + appName = APPLICATION_NAME; + +#if !defined(__WINDOWS__) + appName.Replace(" ", "_"); + m_checker = new wxSingleInstanceChecker(appName, "/tmp"); +#else + m_checker = new wxSingleInstanceChecker(appName); +#endif + + bool ret = m_checker->IsAnotherRunning(); + if (ret) { + wxLogError("Another copy of the D-Star Repeater is running, exiting"); + return false; + } + +#if defined(__WINDOWS__) + if (m_confDir.IsEmpty()) + m_confDir = ::wxGetHomeDir(); + + m_config = new CDStarRepeaterConfig(new wxConfig(APPLICATION_NAME), m_confDir, CONFIG_FILE_NAME, m_name); +#else + if (m_confDir.IsEmpty()) + m_confDir = CONF_DIR; + + try { + m_config = new CDStarRepeaterConfig(m_confDir, CONFIG_FILE_NAME, m_name, true); + } catch( std::runtime_error& e ) { + wxLogError("Could not open configuration file"); + return false; + } +#endif + + wxString type; + m_config->getModem(type); + +#if (wxUSE_GUI == 1) + wxString frameName = APPLICATION_NAME + " (" + type + ") - "; + if (!m_name.IsEmpty()) { + frameName.Append(m_name); + frameName.Append(" - "); + } + frameName.Append(VERSION); + + wxPoint position = wxDefaultPosition; + + int x, y; + m_config->getPosition(x, y); + if (x >= 0 && y >= 0) + position = wxPoint(x, y); + + m_frame = new CDStarRepeaterFrame(frameName, type, position, m_gui); + m_frame->Show(); + + SetTopWindow(m_frame); +#endif + + wxLogInfo("Starting " + APPLICATION_NAME + " - " + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo("Using wxWidgets %d.%d.%d on %s", wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + createThread(); + + return true; +} + +int CDStarRepeaterApp::OnExit() +{ + wxLogInfo(APPLICATION_NAME + " is exiting"); + + m_logChain->SetLog(NULL); + + m_thread->kill(); + + delete m_config; + + delete m_checker; + + return 0; +} + +void CDStarRepeaterApp::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(GUI_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(AUDIODIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + wxApp::OnInitCmdLine(parser); +} + +bool CDStarRepeaterApp::OnCmdLineParsed(wxCmdLineParser& parser) +{ + if (!wxApp::OnCmdLineParsed(parser)) + return false; + + m_nolog = parser.Found(NOLOGGING_SWITCH); + m_gui = parser.Found(GUI_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (found) + m_logDir = logDir; + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (found) + m_confDir = confDir; + + wxString audioDir; + found = parser.Found(AUDIODIR_OPTION, &audioDir); + if (found) + m_audioDir = audioDir; + else +// XXX Homedir here isn't appropriate. I don't know if logDir is either. +// XXX Need to look at more code +#if defined(__WINDOWS__) + m_audioDir = ::wxGetHomeDir(); +#else + m_audioDir = logDir; +#endif + + if (parser.GetParamCount() > 0U) + m_name = parser.GetParam(0U); + + return true; +} + +#if defined(__WXDEBUG__) +void CDStarRepeaterApp::OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg) +{ + wxLogFatalError("Assertion failed on line %d in file %s and function %s: %s %s", line, file, func, cond, msg); +} +#endif + +CDStarRepeaterStatusData* CDStarRepeaterApp::getStatus() const +{ + return m_thread->getStatus(); +} + +void CDStarRepeaterApp::showLog(const wxString& text) +{ +#if (wxUSE_GUI == 1) + if(m_frame) + m_frame->showLog(text); +#endif +} + +void CDStarRepeaterApp::setOutputs(bool out1, bool out2, bool out3, bool out4) +{ + wxLogInfo("Output 1 = %d, output 2 = %d, output 3 = %d, output 4 = %d", int(out1), int(out2), int(out3), int(out4)); + + m_thread->setOutputs(out1, out2, out3, out4); +} + +void CDStarRepeaterApp::setLogging(bool logging) +{ + wxLogInfo("Frame logging set to %d, in %s", int(logging), m_audioDir.c_str()); + + m_thread->setLogging(logging, m_audioDir); +} + +void CDStarRepeaterApp::setPosition(int x, int y) +{ + m_config->setPosition(x, y); + m_config->write(); +} + +void CDStarRepeaterApp::OnRemoteCmd(wxThreadEvent& event) +{ + wxLogMessage("Request to execute command %s (%d)", + m_commandLine[event.GetInt()], event.GetInt()); + // XXX sanity check the command line here. + wxShell(m_commandLine[event.GetInt()]); +} + +void CDStarRepeaterApp::startup() +{ + m_thread->startup(); +} + +void CDStarRepeaterApp::shutdown() +{ + m_thread->shutdown(); +} + +void CDStarRepeaterApp::createThread() +{ + wxASSERT(m_config != NULL); + + wxString callsign, gateway; + DSTAR_MODE mode; + ACK_TYPE ack; + bool restriction, rpt1Validation, dtmfBlanking, errorReply; + m_config->getCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); + + wxString modemType; + m_config->getModem(modemType); + + // DVAP can only do simplex, force the mode accordingly + if (modemType.IsSameAs("DVAP")) { + if (mode == MODE_DUPLEX) { + wxLogInfo("DVAP: changing mode from DUPLEX to SIMPLEX"); + mode = MODE_SIMPLEX; + } else if (mode == MODE_TXANDRX) { + wxLogInfo("DVAP: changing mode from TX_AND_RX to RX_ONLY"); + mode = MODE_RXONLY; + } + } + + // XXX This should be m_thread eventually. + switch (mode) { + case MODE_RXONLY: + m_thread = new CDStarRepeaterRXThread(modemType); + break; + case MODE_TXONLY: + m_thread = new CDStarRepeaterTXThread(modemType); + break; + case MODE_TXANDRX: + m_thread = new CDStarRepeaterTXRXThread(modemType); + break; + default: + m_thread = new CDStarRepeaterTRXThread(modemType); + break; + } + + m_thread->setCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); + wxLogInfo("Callsign set to \"%s\", gateway set to \"%s\", mode: %d, ack: %d, restriction: %d, RPT1 validation: %d, DTMF blanking: %d, Error reply: %d", callsign.c_str(), gateway.c_str(), int(mode), int(ack), int(restriction), int(rpt1Validation), int(dtmfBlanking), int(errorReply)); + + wxString gatewayAddress, localAddress, name; + unsigned int gatewayPort, localPort; + m_config->getNetwork(gatewayAddress, gatewayPort, localAddress, localPort, name); + wxLogInfo("Gateway set to %s:%u, local set to %s:%u, name set to \"%s\"", gatewayAddress.c_str(), gatewayPort, localAddress.c_str(), localPort, name.c_str()); + + if (!gatewayAddress.IsEmpty()) { + bool local = gatewayAddress.IsSameAs("127.0.0.1"); + + CRepeaterProtocolHandler* handler = new CRepeaterProtocolHandler(gatewayAddress, gatewayPort, localAddress, localPort, name); + + bool res = handler->open(); + if (!res) + wxLogError("Cannot open the protocol handler"); + else + m_thread->setProtocolHandler(handler, local); + } + + unsigned int timeout, ackTime; + m_config->getTimes(timeout, ackTime); + m_thread->setTimes(timeout, ackTime); + wxLogInfo("Timeout set to %u secs, ack time set to %u ms", timeout, ackTime); + + unsigned int beaconTime; + wxString beaconText; + bool beaconVoice; + TEXT_LANG language; + m_config->getBeacon(beaconTime, beaconText, beaconVoice, language); + if (mode == MODE_GATEWAY) + beaconTime = 0U; + m_thread->setBeacon(beaconTime, beaconText, beaconVoice, language); + wxLogInfo("Beacon set to %u mins, text set to \"%s\", voice set to %d, language set to %d", beaconTime / 60U, beaconText.c_str(), int(beaconVoice), int(language)); + + bool announcementEnabled; + unsigned int announcementTime; + wxString announcementRecordRPT1, announcementRecordRPT2; + wxString announcementDeleteRPT1, announcementDeleteRPT2; + m_config->getAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); + if (mode == MODE_GATEWAY) + announcementEnabled = false; + m_thread->setAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); + wxLogInfo("Announcement enabled: %d, time: %u mins, record RPT1: \"%s\", record RPT2: \"%s\", delete RPT1: \"%s\", delete RPT2: \"%s\"", int(announcementEnabled), announcementTime / 60U, announcementRecordRPT1.c_str(), announcementRecordRPT2.c_str(), announcementDeleteRPT1.c_str(), announcementDeleteRPT2.c_str()); + + wxLogInfo("Modem type set to \"%s\"", modemType.c_str()); + + CModem* modem = NULL; + if (modemType.IsSameAs("DVAP")) { + wxString port; + unsigned int frequency; + int power, squelch; + m_config->getDVAP(port, frequency, power, squelch); + wxLogInfo("DVAP: port: %s, frequency: %u Hz, power: %d dBm, squelch: %d dBm", port.c_str(), frequency, power, squelch); + modem = new CDVAPController(port, frequency, power, squelch); + } else if (modemType.IsSameAs("DV-RPTR V1")) { + wxString port; + bool rxInvert, txInvert, channel; + unsigned int modLevel, txDelay; + m_config->getDVRPTR1(port, rxInvert, txInvert, channel, modLevel, txDelay); + wxLogInfo("DV-RPTR V1, port: %s, RX invert: %d, TX invert: %d, channel: %s, mod level: %u%%, TX delay: %u ms", port.c_str(), int(rxInvert), int(txInvert), channel ? "B" : "A", modLevel, txDelay); + modem = new CDVRPTRV1Controller(port, wxEmptyString, rxInvert, txInvert, channel, modLevel, txDelay); + } else if (modemType.IsSameAs("DV-RPTR V2")) { + CONNECTION_TYPE connType; + wxString usbPort, address; + bool txInvert; + unsigned int port, modLevel, txDelay; + m_config->getDVRPTR2(connType, usbPort, address, port, txInvert, modLevel, txDelay); + wxLogInfo("DV-RPTR V2, type: %d, address: %s:%u, TX invert: %d, mod level: %u%%, TX delay: %u ms", int(connType), address.c_str(), port, int(txInvert), modLevel, txDelay); + switch (connType) { + case CT_USB: + modem = new CDVRPTRV2Controller(usbPort, wxEmptyString, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + break; + case CT_NETWORK: + modem = new CDVRPTRV2Controller(address, port, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + break; + } + } else if (modemType.IsSameAs("DV-RPTR V3")) { + CONNECTION_TYPE connType; + wxString usbPort, address; + bool txInvert; + unsigned int port, modLevel, txDelay; + m_config->getDVRPTR3(connType, usbPort, address, port, txInvert, modLevel, txDelay); + wxLogInfo("DV-RPTR V3, type: %d, address: %s:%u, TX invert: %d, mod level: %u%%, TX delay: %u ms", int(connType), address.c_str(), port, int(txInvert), modLevel, txDelay); + switch (connType) { + case CT_USB: + modem = new CDVRPTRV3Controller(usbPort, wxEmptyString, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + break; + case CT_NETWORK: + modem = new CDVRPTRV3Controller(address, port, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + break; + } + } else if (modemType.IsSameAs("DVMEGA")) { + wxString port; + DVMEGA_VARIANT variant; + bool rxInvert, txInvert; + unsigned int txDelay, rxFrequency, txFrequency, power; + m_config->getDVMEGA(port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); + wxLogInfo("DVMEGA, port: %s, variant: %d, RX invert: %d, TX invert: %d, TX delay: %u ms, rx frequency: %u Hz, tx frequency: %u Hz, power: %u %%", port.c_str(), int(variant), int(rxInvert), int(txInvert), txDelay, rxFrequency, txFrequency, power); + switch (variant) { + case DVMV_MODEM: + modem = new CDVMegaController(port, wxEmptyString, rxInvert, txInvert, txDelay); + break; + case DVMV_RADIO_2M: + case DVMV_RADIO_70CM: + case DVMV_RADIO_2M_70CM: + modem = new CDVMegaController(port, wxEmptyString, txDelay, rxFrequency, txFrequency, power); + break; + default: + wxLogError("Unknown DVMEGA variant - %d"), int(variant); + break; + } + } else if (modemType.IsSameAs("GMSK Modem")) { + USB_INTERFACE iface; + unsigned int address; + m_config->getGMSK(iface, address); + wxLogInfo("GMSK, interface: %d, address: %04X", int(iface), address); + modem = new CGMSKController(iface, address, mode == MODE_DUPLEX || mode == MODE_TXANDRX); + } else if (modemType.IsSameAs("Sound Card")) { + wxString rxDevice, txDevice; + bool rxInvert, txInvert; + wxFloat32 rxLevel, txLevel; + unsigned int txDelay, txTail; + m_config->getSoundCard(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); + wxLogInfo("Sound Card, devices: %s:%s, invert: %d:%d, levels: %.2f:%.2f, tx delay: %u ms, tx tail: %u ms", rxDevice.c_str(), txDevice.c_str(), int(rxInvert), int(txInvert), rxLevel, txLevel, txDelay, txTail); + modem = new CSoundCardController(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); + } else if (modemType.IsSameAs("MMDVM")) { + wxString port; + bool rxInvert, txInvert, pttInvert; + unsigned int txDelay, rxLevel, txLevel; + m_config->getMMDVM(port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); + wxLogInfo("MMDVM, port: %s, RX invert: %d, TX invert: %d, PTT invert: %d, TX delay: %u ms, RX level: %u%%, TX level: %u%%", port.c_str(), int(rxInvert), int(txInvert), int(pttInvert), txDelay, rxLevel, txLevel); + modem = new CMMDVMController(port, wxEmptyString, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); + } else if (modemType.IsSameAs("Split")) { + wxString localAddress; + unsigned int localPort; + wxArrayString transmitterNames, receiverNames; + unsigned int timeout; + m_config->getSplit(localAddress, localPort, transmitterNames, receiverNames, timeout); + wxLogInfo("Split, local: %s:%u, timeout: %u ms", localAddress.c_str(), localPort, timeout); + for (unsigned int i = 0U; i < transmitterNames.GetCount(); i++) { + wxString name = transmitterNames.Item(i); + if (!name.IsEmpty()) + wxLogInfo("\tTX %u name: %s", i + 1U, name.c_str()); + } + for (unsigned int i = 0U; i < receiverNames.GetCount(); i++) { + wxString name = receiverNames.Item(i); + if (!name.IsEmpty()) + wxLogInfo("\tRX %u name: %s", i + 1U, name.c_str()); + } + modem = new CSplitController(localAddress, localPort, transmitterNames, receiverNames, timeout); + } else { + wxLogError("Unknown modem type: %s", modemType.c_str()); + } + + if (modem != NULL) { + bool res = modem->start(); + if (!res) + wxLogError("Cannot open the D-Star modem"); + else + m_thread->setModem(modem); + } + + wxString controllerType; + unsigned int portConfig, activeHangTime; + bool pttInvert; + m_config->getController(controllerType, portConfig, pttInvert, activeHangTime); + wxLogInfo("Controller set to %s, config: %u, PTT invert: %d, active hang time: %u ms", controllerType.c_str(), portConfig, int(pttInvert), activeHangTime); + + CExternalController* controller = NULL; + + wxString port; + if (controllerType.StartsWith("Velleman K8055 - ", &port)) { + unsigned long num; + port.ToULong(&num); + controller = new CExternalController(new CK8055Controller(num), pttInvert); + } else if (controllerType.StartsWith("URI USB - ", &port)) { + unsigned long num; + port.ToULong(&num); + controller = new CExternalController(new CURIUSBController(num, true), pttInvert); + } else if (controllerType.StartsWith("Serial - ", &port)) { + controller = new CExternalController(new CSerialLineController(port, portConfig), pttInvert); + } else if (controllerType.StartsWith("Arduino - ", &port)) { + controller = new CExternalController(new CArduinoController(port), pttInvert); +#if defined(GPIO) + } else if (controllerType.IsSameAs("GPIO")) { + controller = new CExternalController(new CGPIOController(portConfig), pttInvert); + } else if (controllerType.IsSameAs(wxT("UDRC"))) { + switch(portConfig) { + case 1: + controller = new CUDRCController(AUTO_FM); + break; + case 3: + controller = new CUDRCController(DIGITAL_DIGITAL); + break; + case 4: + controller = new CUDRCController(FM_FM); + break; + case 5: + controller = new CUDRCController(HOTSPOT); + break; + default: + case 2: + controller = new CUDRCController(AUTO_AUTO); + break; + + } +#endif + } else { + wxLogError("Unrecognized controller %s, using dummy controller", controllerType); + controller = new CExternalController(new CDummyController, pttInvert); + } + + bool res = controller->open(); + if (!res) + wxLogError("Cannot open the hardware interface - %s", controllerType.c_str()); + else + m_thread->setController(controller, activeHangTime); + + bool out1, out2, out3, out4; + m_config->getOutputs(out1, out2, out3, out4); + m_thread->setOutputs(out1, out2, out3, out4); +#if (wxUSE_GUI == 1) + m_frame->setOutputs(out1, out2, out3, out4); +#endif + wxLogInfo("Output 1 = %d, output 2 = %d, output 3 = %d, output 4 = %d", int(out1), int(out2), int(out3), int(out4)); + + bool enabled; + wxString rpt1Callsign, rpt2Callsign; + wxString shutdown, startup; + + // XXX Initialization should be temporary until we get them coming + // from m_config->getControl + wxArrayString status; + status.Add("", 5); + wxArrayString command; + command.Add("", 6); + wxArrayString output; + output.Add("", 4); + + m_config->getControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status[0], status[1], status[2], status[3], status[4], command[0], m_commandLine[0], command[1], m_commandLine[1], command[2], m_commandLine[2], command[3], m_commandLine[3], command[4], m_commandLine[4], command[5], m_commandLine[5], output[0], output[1], output[2], output[3]); + + m_thread->setControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, + startup, command, status, output); + + wxLogInfo(wxT("Control: enabled: %d, RPT1: %s, RPT2: %s, shutdown: %s, startup: %s, status1: %s, status2: %s, status3: %s, status4: %s, status5: %s, command1: %s = %s, command2: %s = %s, command3: %s = %s, command4: %s = %s, command5: %s = %s, command6: %s = %s, output1: %s, output2: %s, output3: %s, output4: %s"), enabled, rpt1Callsign.c_str(), rpt2Callsign.c_str(), shutdown.c_str(), startup.c_str(), status[0].c_str(), status[1].c_str(), status[2].c_str(), status[3].c_str(), status[4].c_str(), command[0].c_str(), m_commandLine[0].c_str(), command[1].c_str(), m_commandLine[1].c_str(), command[2].c_str(), m_commandLine[2].c_str(), command[3].c_str(), m_commandLine[3].c_str(), command[4].c_str(), m_commandLine[4].c_str(), command[5].c_str(), m_commandLine[5].c_str(), output[0].c_str(), output[1].c_str(), output[2].c_str(), output[3].c_str()); + + bool logging; + m_config->getLogging(logging); + m_thread->setLogging(logging, m_audioDir); +#if (wxUSE_GUI == 1) + m_frame->setLogging(logging); +#endif + wxLogInfo("Frame logging set to %d, in %s", int(logging), m_audioDir.c_str()); + +#if defined(__WINDOWS__) + wxFileName wlFilename(wxFileName::GetHomeDir(), PRIMARY_WHITELIST_FILE_NAME); +#else + wxFileName wlFilename(CONF_DIR, PRIMARY_WHITELIST_FILE_NAME); +#endif + bool exists = wlFilename.FileExists(); + + if (!exists) { +#if defined(__WINDOWS__) + wlFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_WHITELIST_FILE_NAME); +#else + wlFilename.Assign(CONF_DIR, SECONDARY_WHITELIST_FILE_NAME); +#endif + exists = wlFilename.FileExists(); + } + + if (exists) { + CCallsignList* list = new CCallsignList(wlFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError("Unable to open white list file - %s", wlFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo("%u callsigns loaded into the white list", list->getCount()); + m_thread->setWhiteList(list); + } + } +#if defined(__WINDOWS__) + wxFileName blFilename(wxFileName::GetHomeDir(), PRIMARY_BLACKLIST_FILE_NAME); +#else + wxFileName blFilename(CONF_DIR, PRIMARY_BLACKLIST_FILE_NAME); +#endif + exists = blFilename.FileExists(); + + if (!exists) { +#if defined(__WINDOWS__) + blFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_BLACKLIST_FILE_NAME); +#else + blFilename.Assign(CONF_DIR, SECONDARY_BLACKLIST_FILE_NAME); +#endif + exists = blFilename.FileExists(); + } + + if (exists) { + CCallsignList* list = new CCallsignList(blFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError("Unable to open black list file - %s", blFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo("%u callsigns loaded into the black list", list->getCount()); + m_thread->setBlackList(list); + } + } +#if defined(__WINDOWS__) + wxFileName glFilename(wxFileName::GetHomeDir(), GREYLIST_FILE_NAME); +#else + wxFileName glFilename(CONF_DIR, GREYLIST_FILE_NAME); +#endif + exists = glFilename.FileExists(); + if (exists) { + CCallsignList* list = new CCallsignList(glFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError("Unable to open grey list file - %s", glFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo("%u callsigns loaded into the grey list", list->getCount()); + m_thread->setGreyList(list); + } + } + + m_thread->Create(); + m_thread->SetPriority(wxPRIORITY_MAX); + m_thread->Run(); +} diff --git a/DStarRepeater/DStarRepeaterApp.h b/DStarRepeater/DStarRepeaterApp.h new file mode 100644 index 0000000..0e8e90e --- /dev/null +++ b/DStarRepeater/DStarRepeaterApp.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterApp_H +#define DStarRepeaterApp_H + +#include +#include + +#include "DStarRepeaterThread.h" +#include "DStarRepeaterStatusData.h" +#include "DStarRepeaterConfig.h" +#if (wxUSE_GUI == 1) +#include "DStarRepeaterFrame.h" +#endif +#include "DStarRepeaterDefs.h" + +wxDECLARE_EVENT(wxEVT_THREAD_COMMAND, wxThreadEvent); + +class CDStarRepeaterApp : public wxApp { +public: + CDStarRepeaterApp(); + virtual ~CDStarRepeaterApp(); + + virtual bool OnInit(); + virtual int OnExit(); + + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + + // This is overridden because dialog boxes from threads are bad news +#if defined(__WXDEBUG__) + virtual void OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg); +#endif + + virtual CDStarRepeaterStatusData* getStatus() const; + + virtual void showLog(const wxString& text); + + virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); + + virtual void setLogging(bool logging); + + virtual void setPosition(int x, int y); + + void startup(); + void shutdown(); + +private: +#if (wxUSE_GUI == 1) + CDStarRepeaterFrame* m_frame; +#endif + + wxString m_name; + bool m_nolog; + bool m_gui; + wxString m_logDir; + wxString m_confDir; + wxString m_audioDir; + IDStarRepeaterThread* m_thread; + CDStarRepeaterConfig* m_config; + wxSingleInstanceChecker* m_checker; + wxLogChain* m_logChain; + wxString m_commandLine[6]; + + void createThread(); + + void OnRemoteCmd(wxThreadEvent& event); + + wxDECLARE_EVENT_TABLE(); +}; + +DECLARE_APP(CDStarRepeaterApp) + +#endif diff --git a/DStarRepeater/DStarRepeaterD.cpp b/DStarRepeater/DStarRepeaterD.cpp new file mode 100644 index 0000000..8c84863 --- /dev/null +++ b/DStarRepeater/DStarRepeaterD.cpp @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterTXRXThread.h" +#include "RepeaterProtocolHandler.h" +#include "DStarRepeaterTRXThread.h" +#include "DStarRepeaterRXThread.h" +#include "DStarRepeaterTXThread.h" +#include "SerialLineController.h" +#include "DStarRepeaterConfig.h" +#include "SoundCardController.h" +#include "DVRPTRV1Controller.h" +#include "DVRPTRV2Controller.h" +#include "DVRPTRV3Controller.h" +#include "ArduinoController.h" +#include "DVMegaController.h" +#include "MMDVMController.h" +#include "URIUSBController.h" +#include "K8055Controller.h" +#include "DummyController.h" +#include "SplitController.h" +#include "DStarRepeaterD.h" +#include "DVAPController.h" +#include "GMSKController.h" +#if defined(GPIO) +#include "GPIOController.h" +#endif +#include "CallsignList.h" +#include "DStarDefines.h" +#include "Version.h" +#include "Logger.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +const wxChar* NAME_PARAM = wxT("Repeater Name"); +const wxChar* NOLOGGING_SWITCH = wxT("nolog"); +const wxChar* LOGDIR_OPTION = wxT("logdir"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); +const wxChar* AUDIODIR_OPTION = wxT("audiodir"); +const wxChar* DAEMON_SWITCH = wxT("daemon"); + +const wxString LOG_BASE_NAME = wxT("dstarrepeaterd"); + +static CDStarRepeaterD* m_repeater = NULL; + +static void handler(int signum) +{ + m_repeater->kill(); +} + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "dstarrepeaterd: failed to initialise the wxWidgets library, exiting\n"); + return -1; + } + + wxCmdLineParser parser(argc, argv); + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(DAEMON_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(AUDIODIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + int cmd = parser.Parse(); + if (cmd != 0) { + ::wxUninitialize(); + return 0; + } + + bool nolog = parser.Found(NOLOGGING_SWITCH); + bool daemon = parser.Found(DAEMON_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (!found) + logDir.Clear(); + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (!found) + confDir = wxT(CONF_DIR); + + wxString audioDir; + found = parser.Found(AUDIODIR_OPTION, &audioDir); + if (!found) + audioDir = ::wxGetHomeDir(); + + wxString name; + if (parser.GetParamCount() > 0U) + name = parser.GetParam(0U); + + if (daemon) { + pid_t pid = ::fork(); + + if (pid < 0) { + ::fprintf(stderr, "dstarrepeaterd: error in fork(), exiting\n"); + ::wxUninitialize(); + return 1; + } + + // If this is the parent, exit + if (pid > 0) + return 0; + + // We are the child from here onwards + ::setsid(); + + if(::chdir("/") == -1) + ::fprintf(stderr, "dstarrepeaterd: error changing directory: %s\n", strerror(errno)); + + ::umask(0); + } + + wxString pidFileName; + if (!name.IsEmpty()) + pidFileName.Printf(wxT("/var/run/dstarrepeater_%s.pid"), name.c_str()); + else + pidFileName = wxT("/var/run/dstarrepeater.pid"); + pidFileName.Replace(wxT(" "), wxT("_")); + + char fileName[128U]; + ::memset(fileName, 0x00U, 128U); + for (unsigned int i = 0U; i < pidFileName.Len(); i++) + fileName[i] = pidFileName.GetChar(i); + + FILE* fp = ::fopen(fileName, "wt"); + if (fp != NULL) { + ::fprintf(fp, "%u\n", ::getpid()); + ::fclose(fp); + } + + ::signal(SIGUSR1, handler); + + m_repeater = new CDStarRepeaterD(nolog, logDir, confDir, audioDir, name); + + if (!m_repeater->init()) { + ::wxUninitialize(); + return 1; + } + + m_repeater->run(); + + delete m_repeater; + + ::unlink(fileName); + + ::wxUninitialize(); + + return 0; +} + +CDStarRepeaterD::CDStarRepeaterD(bool nolog, const wxString& logDir, const wxString& confDir, const wxString& audioDir, const wxString& name) : +m_name(name), +m_nolog(nolog), +m_logDir(logDir), +m_confDir(confDir), +m_audioDir(audioDir), +m_thread(NULL), +m_checker(NULL) +{ +} + +CDStarRepeaterD::~CDStarRepeaterD() +{ +} + +bool CDStarRepeaterD::init() +{ + if (!m_nolog) { + wxString logBaseName = LOG_BASE_NAME; + if (!m_name.IsEmpty()) { + logBaseName.Append(wxT("_")); + logBaseName.Append(m_name); + } + + if (m_logDir.IsEmpty()) + m_logDir = wxT(LOG_DIR); + + wxLog* log = new CLogger(m_logDir, logBaseName); + wxLog::SetActiveTarget(log); + } else { + new wxLogNull; + } + + wxString appName; + if (!m_name.IsEmpty()) + appName = APPLICATION_NAME + wxT(" ") + m_name; + else + appName = APPLICATION_NAME; + appName.Replace(wxT(" "), wxT("_")); + + m_checker = new wxSingleInstanceChecker(appName, wxT("/tmp")); + bool ret = m_checker->IsAnotherRunning(); + if (ret) { + wxLogError(wxT("Another copy of the D-Star Repeater is running, exiting")); + return false; + } + + wxLogInfo(wxT("Starting ") + APPLICATION_NAME + wxT(" daemon - ") + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo(wxT("Using wxWidgets %d.%d.%d on %s"), wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + return createThread(); +} + +void CDStarRepeaterD::run() +{ + m_thread->run(); + + delete m_checker; + + wxLogInfo(APPLICATION_NAME + wxT(" is exiting")); +} + +bool CDStarRepeaterD::createThread() +{ + CDStarRepeaterConfig config(m_confDir, CONFIG_FILE_NAME, m_name); + + wxString callsign, gateway; + DSTAR_MODE mode; + ACK_TYPE ack; + bool restriction, rpt1Validation, dtmfBlanking, errorReply; + config.getCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); + + wxString modemType; + config.getModem(modemType); + + // DVAP can only do simplex, force the mode accordingly + if (modemType.IsSameAs(wxT("DVAP"))) { + if (mode == MODE_DUPLEX) { + wxLogInfo(wxT("DVAP: changing mode from DUPLEX to SIMPLEX")); + mode = MODE_SIMPLEX; + } else if (mode == MODE_TXANDRX) { + wxLogInfo(wxT("DVAP: changing mode from TX_AND_RX to RX_ONLY")); + mode = MODE_RXONLY; + } + } + + switch (mode) { + case MODE_RXONLY: + m_thread = new CDStarRepeaterRXThread(modemType); + break; + case MODE_TXONLY: + m_thread = new CDStarRepeaterTXThread(modemType); + break; + case MODE_TXANDRX: + m_thread = new CDStarRepeaterTXRXThread(modemType); + break; + default: + m_thread = new CDStarRepeaterTRXThread(modemType); + break; + } + + m_thread->setCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); + wxLogInfo(wxT("Callsign set to \"%s\", gateway set to \"%s\", mode: %d, ack: %d, restriction: %d, RPT1 validation: %d, DTMF blanking: %d, Error reply: %d"), callsign.c_str(), gateway.c_str(), int(mode), int(ack), int(restriction), int(rpt1Validation), int(dtmfBlanking), int(errorReply)); + + wxString gatewayAddress, localAddress, name; + unsigned int gatewayPort, localPort; + config.getNetwork(gatewayAddress, gatewayPort, localAddress, localPort, name); + wxLogInfo(wxT("Gateway set to %s:%u, local set to %s:%u, name set to \"%s\""), gatewayAddress.c_str(), gatewayPort, localAddress.c_str(), localPort, name.c_str()); + + if (!gatewayAddress.IsEmpty()) { + bool local = gatewayAddress.IsSameAs(wxT("127.0.0.1")); + + CRepeaterProtocolHandler* handler = new CRepeaterProtocolHandler(gatewayAddress, gatewayPort, localAddress, localPort, name); + + bool res = handler->open(); + if (!res) { + wxLogError(wxT("Cannot open the protocol handler")); + return false; + } + + m_thread->setProtocolHandler(handler, local); + } + + unsigned int timeout, ackTime; + config.getTimes(timeout, ackTime); + m_thread->setTimes(timeout, ackTime); + wxLogInfo(wxT("Timeout set to %u secs, ack time set to %u ms"), timeout, ackTime); + + unsigned int beaconTime; + wxString beaconText; + bool beaconVoice; + TEXT_LANG language; + config.getBeacon(beaconTime, beaconText, beaconVoice, language); + if (mode == MODE_GATEWAY) + beaconTime = 0U; + m_thread->setBeacon(beaconTime, beaconText, beaconVoice, language); + wxLogInfo(wxT("Beacon set to %u mins, text set to \"%s\", voice set to %d, language set to %d"), beaconTime / 60U, beaconText.c_str(), int(beaconVoice), int(language)); + + bool announcementEnabled; + unsigned int announcementTime; + wxString announcementRecordRPT1, announcementRecordRPT2; + wxString announcementDeleteRPT1, announcementDeleteRPT2; + config.getAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); + if (mode == MODE_GATEWAY) + announcementEnabled = false; + m_thread->setAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); + wxLogInfo(wxT("Announcement enabled: %d, time: %u mins, record RPT1: \"%s\", record RPT2: \"%s\", delete RPT1: \"%s\", delete RPT2: \"%s\""), int(announcementEnabled), announcementTime / 60U, announcementRecordRPT1.c_str(), announcementRecordRPT2.c_str(), announcementDeleteRPT1.c_str(), announcementDeleteRPT2.c_str()); + + wxLogInfo(wxT("Modem type set to \"%s\""), modemType.c_str()); + + CModem* modem = NULL; + if (modemType.IsSameAs(wxT("DVAP"))) { + wxString port; + unsigned int frequency; + int power, squelch; + config.getDVAP(port, frequency, power, squelch); + wxLogInfo(wxT("DVAP: port: %s, frequency: %u Hz, power: %d dBm, squelch: %d dBm"), port.c_str(), frequency, power, squelch); + modem = new CDVAPController(port, frequency, power, squelch); + } else if (modemType.IsSameAs(wxT("DV-RPTR V1"))) { + wxString port; + bool rxInvert, txInvert, channel; + unsigned int modLevel, txDelay; + config.getDVRPTR1(port, rxInvert, txInvert, channel, modLevel, txDelay); + wxLogInfo(wxT("DV-RPTR V1, port: %s, RX invert: %d, TX invert: %d, channel: %s, mod level: %u%%, TX delay: %u ms"), port.c_str(), int(rxInvert), int(txInvert), channel ? wxT("B") : wxT("A"), modLevel, txDelay); + modem = new CDVRPTRV1Controller(port, wxEmptyString, rxInvert, txInvert, channel, modLevel, txDelay); + } else if (modemType.IsSameAs(wxT("DV-RPTR V2"))) { + CONNECTION_TYPE connType; + wxString usbPort, address; + bool txInvert; + unsigned int port, modLevel, txDelay; + config.getDVRPTR2(connType, usbPort, address, port, txInvert, modLevel, txDelay); + wxLogInfo(wxT("DV-RPTR V2, type: %d, address: %s:%u, TX invert: %d, mod level: %u%%, TX delay: %u ms"), int(connType), address.c_str(), port, int(txInvert), modLevel, txDelay); + switch (connType) { + case CT_USB: + modem = new CDVRPTRV2Controller(usbPort, wxEmptyString, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + break; + case CT_NETWORK: + modem = new CDVRPTRV2Controller(address, port, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + break; + } + } else if (modemType.IsSameAs(wxT("DV-RPTR V3"))) { + CONNECTION_TYPE connType; + wxString usbPort, address; + bool txInvert; + unsigned int port, modLevel, txDelay; + config.getDVRPTR3(connType, usbPort, address, port, txInvert, modLevel, txDelay); + wxLogInfo(wxT("DV-RPTR V3, type: %d, address: %s:%u, TX invert: %d, mod level: %u%%, TX delay: %u ms"), int(connType), address.c_str(), port, int(txInvert), modLevel, txDelay); + switch (connType) { + case CT_USB: + modem = new CDVRPTRV3Controller(usbPort, wxEmptyString, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + break; + case CT_NETWORK: + modem = new CDVRPTRV3Controller(address, port, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + break; + } + } else if (modemType.IsSameAs(wxT("DVMEGA"))) { + wxString port; + DVMEGA_VARIANT variant; + bool rxInvert, txInvert; + unsigned int txDelay, rxFrequency, txFrequency, power; + config.getDVMEGA(port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); + wxLogInfo(wxT("DVMEGA, port: %s, variant: %d, RX invert: %d, TX invert: %d, TX delay: %u ms, rx frequency: %u Hz, tx frequency: %u Hz, power: %u %%"), port.c_str(), int(variant), int(rxInvert), int(txInvert), txDelay, rxFrequency, txFrequency, power); + switch (variant) { + case DVMV_MODEM: + modem = new CDVMegaController(port, wxEmptyString, rxInvert, txInvert, txDelay); + break; + case DVMV_RADIO_2M: + case DVMV_RADIO_70CM: + case DVMV_RADIO_2M_70CM: + modem = new CDVMegaController(port, wxEmptyString, txDelay, rxFrequency, txFrequency, power); + break; + default: + wxLogError(wxT("Unknown DVMEGA variant - %d"), int(variant)); + break; + } + } else if (modemType.IsSameAs(wxT("GMSK Modem"))) { + USB_INTERFACE iface; + unsigned int address; + config.getGMSK(iface, address); + wxLogInfo(wxT("GMSK, interface: %d, address: %04X"), int(iface), address); + modem = new CGMSKController(iface, address, mode == MODE_DUPLEX || mode == MODE_TXANDRX); + } else if (modemType.IsSameAs(wxT("Sound Card"))) { + wxString rxDevice, txDevice; + bool rxInvert, txInvert; + wxFloat32 rxLevel, txLevel; + unsigned int txDelay, txTail; + config.getSoundCard(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); + wxLogInfo(wxT("Sound Card, devices: %s:%s, invert: %d:%d, levels: %.2f:%.2f, tx delay: %u ms, tx tail: %u ms"), rxDevice.c_str(), txDevice.c_str(), int(rxInvert), int(txInvert), rxLevel, txLevel, txDelay, txTail); + modem = new CSoundCardController(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); + } else if (modemType.IsSameAs(wxT("MMDVM"))) { + wxString port; + bool rxInvert, txInvert, pttInvert; + unsigned int txDelay, rxLevel, txLevel; + config.getMMDVM(port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); + wxLogInfo(wxT("MMDVM, port: %s, RX invert: %d, TX invert: %d, PTT invert: %d, TX delay: %u ms, RX level: %u%%, TX level: %u%%"), port.c_str(), int(rxInvert), int(txInvert), int(pttInvert), txDelay, rxLevel, txLevel); + modem = new CMMDVMController(port, wxEmptyString, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); + } else if (modemType.IsSameAs(wxT("Split"))) { + wxString localAddress; + unsigned int localPort; + wxArrayString transmitterNames, receiverNames; + unsigned int timeout; + config.getSplit(localAddress, localPort, transmitterNames, receiverNames, timeout); + wxLogInfo(wxT("Split, local: %s:%u, timeout: %u ms"), localAddress.c_str(), localPort, timeout); + for (unsigned int i = 0U; i < transmitterNames.GetCount(); i++) { + wxString name = transmitterNames.Item(i); + if (!name.IsEmpty()) + wxLogInfo(wxT("\tTX %u name: %s"), i + 1U, name.c_str()); + } + for (unsigned int i = 0U; i < receiverNames.GetCount(); i++) { + wxString name = receiverNames.Item(i); + if (!name.IsEmpty()) + wxLogInfo(wxT("\tRX %u name: %s"), i + 1U, name.c_str()); + } + modem = new CSplitController(localAddress, localPort, transmitterNames, receiverNames, timeout); + } else { + wxLogError(wxT("Unknown modem type: %s"), modemType.c_str()); + } + + if (modem != NULL) { + bool res = modem->start(); + if (!res) + wxLogError(wxT("Cannot open the D-Star modem")); + else + m_thread->setModem(modem); + } + + wxString controllerType; + unsigned int portConfig, activeHangTime; + bool pttInvert; + config.getController(controllerType, portConfig, pttInvert, activeHangTime); + wxLogInfo(wxT("Controller set to %s, config: %u, PTT invert: %d, active hang time: %u ms"), controllerType.c_str(), portConfig, int(pttInvert), activeHangTime); + + CExternalController* controller = NULL; + + wxString port; + if (controllerType.StartsWith(wxT("Velleman K8055 - "), &port)) { + unsigned long num; + port.ToULong(&num); + controller = new CExternalController(new CK8055Controller(num), pttInvert); + } else if (controllerType.StartsWith(wxT("URI USB - "), &port)) { + unsigned long num; + port.ToULong(&num); + controller = new CExternalController(new CURIUSBController(num, true), pttInvert); + } else if (controllerType.StartsWith(wxT("Serial - "), &port)) { + controller = new CExternalController(new CSerialLineController(port, portConfig), pttInvert); + } else if (controllerType.StartsWith(wxT("Arduino - "), &port)) { + controller = new CExternalController(new CArduinoController(port), pttInvert); +#if defined(GPIO) + } else if (controllerType.IsSameAs(wxT("GPIO"))) { + controller = new CExternalController(new CGPIOController(portConfig), pttInvert); +#endif + } else { + controller = new CExternalController(new CDummyController, pttInvert); + } + + bool res = controller->open(); + if (!res) + wxLogError(wxT("Cannot open the hardware interface - %s"), controllerType.c_str()); + else + m_thread->setController(controller, activeHangTime); + + bool out1, out2, out3, out4; + config.getOutputs(out1, out2, out3, out4); + m_thread->setOutputs(out1, out2, out3, out4); + wxLogInfo(wxT("Output 1 = %d, output 2 = %d, output 3 = %d, output 4 = %d"), out1, out2, out3, out4); + + bool enabled; + wxString rpt1Callsign, rpt2Callsign; + wxString shutdown, startup; + wxString status1, status2, status3, status4, status5; + wxString command1, command1Line, command2, command2Line; + wxString command3, command3Line, command4, command4Line; + wxString command5, command5Line, command6, command6Line; + wxString output1, output2, output3, output4; + config.getControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status1, status2, status3, status4, status5, command1, command1Line, command2, command2Line, command3, command3Line, command4, command4Line, command5, command5Line, command6, command6Line, output1, output2, output3, output4); + m_thread->setControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status1, status2, status3, status4, status5, command1, command1Line, command2, command2Line, command3, command3Line, command4, command4Line, command5, command5Line, command6, command6Line, output1, output2, output3, output4); + wxLogInfo(wxT("Control: enabled: %d, RPT1: %s, RPT2: %s, shutdown: %s, startup: %s, status1: %s, status2: %s, status3: %s, status4: %s, status5: %s, command1: %s = %s, command2: %s = %s, command3: %s = %s, command4: %s = %s, command5: %s = %s, command6: %s = %s, output1: %s, output2: %s, output3: %s, output4: %s"), enabled, rpt1Callsign.c_str(), rpt2Callsign.c_str(), shutdown.c_str(), startup.c_str(), status1.c_str(), status2.c_str(), status3.c_str(), status4.c_str(), status5.c_str(), command1.c_str(), command1Line.c_str(), command2.c_str(), command2Line.c_str(), command3.c_str(), command3Line.c_str(), command4.c_str(), command4Line.c_str(), command5.c_str(), command5Line.c_str(), command6.c_str(), command6Line.c_str(), output1.c_str(), output2.c_str(), output3.c_str(), output4.c_str()); + + bool logging; + config.getLogging(logging); + m_thread->setLogging(logging, m_audioDir); + wxLogInfo(wxT("Frame logging set to %d, in %s"), int(logging), m_audioDir.c_str()); + + wxFileName wlFilename(wxFileName::GetHomeDir(), PRIMARY_WHITELIST_FILE_NAME); + bool exists = wlFilename.FileExists(); + + if (!exists) { + wlFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_WHITELIST_FILE_NAME); + exists = wlFilename.FileExists(); + } + + if (exists) { + CCallsignList* list = new CCallsignList(wlFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError(wxT("Unable to open white list file - %s"), wlFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo(wxT("%u callsigns loaded into the white list"), list->getCount()); + m_thread->setWhiteList(list); + } + } + + wxFileName blFilename(wxFileName::GetHomeDir(), PRIMARY_BLACKLIST_FILE_NAME); + exists = blFilename.FileExists(); + + if (!exists) { + blFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_BLACKLIST_FILE_NAME); + exists = blFilename.FileExists(); + } + + if (exists) { + CCallsignList* list = new CCallsignList(blFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError(wxT("Unable to open black list file - %s"), blFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo(wxT("%u callsigns loaded into the black list"), list->getCount()); + m_thread->setBlackList(list); + } + } + + wxFileName glFilename(wxFileName::GetHomeDir(), GREYLIST_FILE_NAME); + exists = glFilename.FileExists(); + if (exists) { + CCallsignList* list = new CCallsignList(glFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError(wxT("Unable to open grey list file - %s"), glFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo(wxT("%u callsigns loaded into the grey list"), list->getCount()); + m_thread->setGreyList(list); + } + } + + return true; +} + +void CDStarRepeaterD::kill() +{ + wxASSERT(m_thread != NULL); + + m_thread->kill(); +} diff --git a/DStarRepeater/DStarRepeaterD.h b/DStarRepeater/DStarRepeaterD.h new file mode 100644 index 0000000..61fef9f --- /dev/null +++ b/DStarRepeater/DStarRepeaterD.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterD_H +#define DStarRepeaterD_H + +#include "DStarRepeaterThread.h" +#include "DStarRepeaterDefs.h" + +#include +#include + +class CDStarRepeaterD { + +public: + CDStarRepeaterD(bool nolog, const wxString& logDir, const wxString& confDir, const wxString& audioDir, const wxString& name); + ~CDStarRepeaterD(); + + bool init(); + + void run(); + + void kill(); + +private: + wxString m_name; + bool m_nolog; + wxString m_logDir; + wxString m_confDir; + wxString m_audioDir; + IDStarRepeaterThread* m_thread; + wxSingleInstanceChecker* m_checker; + + bool createThread(); +}; + +#endif diff --git a/DStarRepeater/DStarRepeaterDefs.h b/DStarRepeater/DStarRepeaterDefs.h new file mode 100644 index 0000000..0ca8e63 --- /dev/null +++ b/DStarRepeater/DStarRepeaterDefs.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterDefs_H +#define DStarRepeaterDefs_H + +#include + +const wxString APPLICATION_NAME = wxT("D-Star Repeater"); +const wxString CONFIG_FILE_NAME = wxT("dstarrepeater"); + +const wxString PRIMARY_WHITELIST_FILE_NAME = wxT("rptr_whitelist.dat"); +const wxString SECONDARY_WHITELIST_FILE_NAME = wxT("whitelist.dat"); +const wxString PRIMARY_BLACKLIST_FILE_NAME = wxT("rptr_blacklist.dat"); +const wxString SECONDARY_BLACKLIST_FILE_NAME = wxT("blacklist.dat"); +const wxString GREYLIST_FILE_NAME = wxT("greylist.dat"); + +enum DSTAR_RX_STATE { + DSRXS_LISTENING, + DSRXS_PROCESS_DATA, + DSRXS_PROCESS_SLOW_DATA +}; + +#endif diff --git a/DStarRepeater/DStarRepeaterFrame.cpp b/DStarRepeater/DStarRepeaterFrame.cpp new file mode 100644 index 0000000..173a00e --- /dev/null +++ b/DStarRepeater/DStarRepeaterFrame.cpp @@ -0,0 +1,586 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterFrame.h" +#include "DStarRepeaterApp.h" +#include "DStarDefines.h" +#include "LogEvent.h" +#include "Version.h" + +#include + +DEFINE_EVENT_TYPE(LOG_EVENT) + +enum { + Menu_File_Logging = 6000, + + Menu_View_Updates, + + Menu_Action_Shutdown, + Menu_Action_Startup, + Menu_Action_Command1, + Menu_Action_Command2, + Menu_Action_Command3, + Menu_Action_Command4, + Menu_Action_Command5, + Menu_Action_Command6, + + Menu_Output_1, + Menu_Output_2, + Menu_Output_3, + Menu_Output_4 +}; + +BEGIN_EVENT_TABLE(CDStarRepeaterFrame, wxFrame) + EVT_MENU(Menu_File_Logging, CDStarRepeaterFrame::onLogging) + EVT_MENU(wxID_EXIT, CDStarRepeaterFrame::onQuit) + EVT_MENU(Menu_View_Updates, CDStarRepeaterFrame::onUpdates) + EVT_MENU(wxID_ABOUT, CDStarRepeaterFrame::onAbout) + + EVT_MENU(Menu_Action_Startup, CDStarRepeaterFrame::onStatus) + EVT_MENU(Menu_Action_Shutdown, CDStarRepeaterFrame::onStatus) + + EVT_MENU(Menu_Action_Command1, CDStarRepeaterFrame::onActions) + EVT_MENU(Menu_Action_Command2, CDStarRepeaterFrame::onActions) + EVT_MENU(Menu_Action_Command3, CDStarRepeaterFrame::onActions) + EVT_MENU(Menu_Action_Command4, CDStarRepeaterFrame::onActions) + EVT_MENU(Menu_Action_Command5, CDStarRepeaterFrame::onActions) + EVT_MENU(Menu_Action_Command6, CDStarRepeaterFrame::onActions) + + EVT_MENU(Menu_Output_1, CDStarRepeaterFrame::onOutputs) + EVT_MENU(Menu_Output_2, CDStarRepeaterFrame::onOutputs) + EVT_MENU(Menu_Output_3, CDStarRepeaterFrame::onOutputs) + EVT_MENU(Menu_Output_4, CDStarRepeaterFrame::onOutputs) + + EVT_CLOSE(CDStarRepeaterFrame::onClose) + + EVT_TIMER(-1, CDStarRepeaterFrame::onTimer) + + EVT_CUSTOM(LOG_EVENT, wxID_ANY, CDStarRepeaterFrame::onLog) +END_EVENT_TABLE() + +#if defined(__WINDOWS__) +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 100U; +const unsigned int LABEL_WIDTH = 70U; +const unsigned int LOGTEXT_WIDTH = 560U; +#else +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 150U; +const unsigned int LABEL_WIDTH = 70U; +const unsigned int LOGTEXT_WIDTH = 710U; +#endif + +CDStarRepeaterFrame::CDStarRepeaterFrame(const wxString& title, const wxString& type, const wxPoint& position, bool gui) : +wxFrame(NULL, -1, title, position), +m_logging(NULL), +m_rxState(NULL), +m_rptState(NULL), +m_tx(NULL), +m_squelch(NULL), +m_signal(NULL), +m_your(NULL), +m_my(NULL), +m_rpt1(NULL), +m_rpt2(NULL), +m_flags(NULL), +m_percent(NULL), +m_timeout(NULL), +m_beacon(NULL), +m_announce(NULL), +m_text(NULL), +m_status1(NULL), +m_status2(NULL), +m_status3(NULL), +m_status4(NULL), +m_status5(NULL), +m_logLine1(NULL), +m_logLine2(NULL), +m_logLine3(NULL), +m_timer(this), +m_outputMenu(NULL), +#if defined(__WXDEBUG__) +m_updates(true) +#else +m_updates(gui) +#endif +{ + SetMenuBar(createMenuBar()); + + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxPanel* panel = new wxPanel(this); + + wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL); + + wxStaticBoxSizer* stat1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Status")), wxVERTICAL); + wxFlexGridSizer* stat2Sizer = new wxFlexGridSizer(6); + + wxStaticText* rxStateLabel = new wxStaticText(panel, -1, wxT("RX State:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + stat2Sizer->Add(rxStateLabel, 0, wxALL, BORDER_SIZE); + + m_rxState = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + stat2Sizer->Add(m_rxState, 0, wxALL, BORDER_SIZE); + + wxStaticText* rptStateLabel = new wxStaticText(panel, -1, wxT("Rpt State:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + stat2Sizer->Add(rptStateLabel, 0, wxALL, BORDER_SIZE); + + m_rptState = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + stat2Sizer->Add(m_rptState, 0, wxALL, BORDER_SIZE); + + wxStaticText* txLabel = new wxStaticText(panel, -1, wxT("TX:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + stat2Sizer->Add(txLabel, 0, wxALL, BORDER_SIZE); + + m_tx = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + stat2Sizer->Add(m_tx, 0, wxALL, BORDER_SIZE); + + stat1Sizer->Add(stat2Sizer); + panelSizer->Add(stat1Sizer, 0, wxALL, BORDER_SIZE); + + if (type.IsSameAs(wxT("DVAP"))) { + wxStaticBoxSizer* rx1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Receiver")), wxVERTICAL); + wxFlexGridSizer* rx2Sizer = new wxFlexGridSizer(6); + + wxStaticText* squelchLabel = new wxStaticText(panel, -1, wxT("Squelch:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + rx2Sizer->Add(squelchLabel, 0, wxALL, BORDER_SIZE); + + m_squelch = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + rx2Sizer->Add(m_squelch, 0, wxALL, BORDER_SIZE); + + wxStaticText* signalLabel = new wxStaticText(panel, -1, wxT("Signal:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + rx2Sizer->Add(signalLabel, 0, wxALL, BORDER_SIZE); + + m_signal = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + rx2Sizer->Add(m_signal, 0, wxALL, BORDER_SIZE); + + wxStaticText* dummy20Label = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LABEL_WIDTH, -1)); + rx2Sizer->Add(dummy20Label, 0, wxALL, BORDER_SIZE); + + wxStaticText* dummy21Label = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + rx2Sizer->Add(dummy21Label, 0, wxALL, BORDER_SIZE); + + rx1Sizer->Add(rx2Sizer); + panelSizer->Add(rx1Sizer, 0, wxALL, BORDER_SIZE); + } + + wxStaticBoxSizer* info1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Header")), wxVERTICAL); + wxFlexGridSizer* info2Sizer = new wxFlexGridSizer(6); + + wxStaticText* yourLabel = new wxStaticText(panel, -1, wxT("UR:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + info2Sizer->Add(yourLabel, 0, wxALL, BORDER_SIZE); + + m_your = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + info2Sizer->Add(m_your, 0, wxALL, BORDER_SIZE); + + wxStaticText* rpt1Label = new wxStaticText(panel, -1, wxT("RPT1:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + info2Sizer->Add(rpt1Label, 0, wxALL, BORDER_SIZE); + + m_rpt1 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + info2Sizer->Add(m_rpt1, 0, wxALL, BORDER_SIZE); + + wxStaticText* rpt2Label = new wxStaticText(panel, -1, wxT("RPT2:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + info2Sizer->Add(rpt2Label, 0, wxALL, BORDER_SIZE); + + m_rpt2 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + info2Sizer->Add(m_rpt2, 0, wxALL, BORDER_SIZE); + + wxStaticText* myLabel = new wxStaticText(panel, -1, wxT("MY:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + info2Sizer->Add(myLabel, 0, wxALL, BORDER_SIZE); + + m_my = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + info2Sizer->Add(m_my, 0, wxALL, BORDER_SIZE); + + wxStaticText* flagsLabel = new wxStaticText(panel, -1, _("Flags:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + info2Sizer->Add(flagsLabel, 0, wxALL, BORDER_SIZE); + + m_flags = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LABEL_WIDTH, -1)); + info2Sizer->Add(m_flags, 0, wxALL, BORDER_SIZE); + + wxStaticText* percentLabel = new wxStaticText(panel, -1, _("Loss/BER:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + info2Sizer->Add(percentLabel, 0, wxALL, BORDER_SIZE); + + m_percent = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LABEL_WIDTH, -1)); + info2Sizer->Add(m_percent, 0, wxALL, BORDER_SIZE); + + info1Sizer->Add(info2Sizer); + panelSizer->Add(info1Sizer, 0, wxALL, BORDER_SIZE); + + wxStaticBoxSizer* timer1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Timers")), wxVERTICAL); + wxFlexGridSizer* timer2Sizer = new wxFlexGridSizer(6); + + wxStaticText* timeoutLabel = new wxStaticText(panel, -1, _("Timeout:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + timer2Sizer->Add(timeoutLabel, 0, wxALL, BORDER_SIZE); + + m_timeout = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + timer2Sizer->Add(m_timeout, 0, wxALL, BORDER_SIZE); + + wxStaticText* beaconLabel = new wxStaticText(panel, -1, _("Beacon:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + timer2Sizer->Add(beaconLabel, 0, wxALL, BORDER_SIZE); + + m_beacon = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + timer2Sizer->Add(m_beacon, 0, wxALL, BORDER_SIZE); + + wxStaticText* announceLabel = new wxStaticText(panel, -1, _("Announce:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + timer2Sizer->Add(announceLabel, 0, wxALL, BORDER_SIZE); + + m_announce = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + timer2Sizer->Add(m_announce, 0, wxALL, BORDER_SIZE); + + timer1Sizer->Add(timer2Sizer); + panelSizer->Add(timer1Sizer, 0, wxALL, BORDER_SIZE); + + wxStaticBoxSizer* text1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Gateway")), wxVERTICAL); + wxFlexGridSizer* text2Sizer = new wxFlexGridSizer(6); + + wxStaticText* textLabel = new wxStaticText(panel, -1, _("Ack Text:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + text2Sizer->Add(textLabel, 0, wxALL, BORDER_SIZE); + + m_text = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + text2Sizer->Add(m_text, 0, wxALL, BORDER_SIZE); + + wxStaticText* status1Label = new wxStaticText(panel, -1, _("Status 1:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + text2Sizer->Add(status1Label, 0, wxALL, BORDER_SIZE); + + m_status1 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + text2Sizer->Add(m_status1, 0, wxALL, BORDER_SIZE); + + wxStaticText* status2Label = new wxStaticText(panel, -1, _("Status 2:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + text2Sizer->Add(status2Label, 0, wxALL, BORDER_SIZE); + + m_status2 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + text2Sizer->Add(m_status2, 0, wxALL, BORDER_SIZE); + + wxStaticText* status3Label = new wxStaticText(panel, -1, _("Status 3:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + text2Sizer->Add(status3Label, 0, wxALL, BORDER_SIZE); + + m_status3 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + text2Sizer->Add(m_status3, 0, wxALL, BORDER_SIZE); + + wxStaticText* status4Label = new wxStaticText(panel, -1, _("Status 4:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + text2Sizer->Add(status4Label, 0, wxALL, BORDER_SIZE); + + m_status4 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + text2Sizer->Add(m_status4, 0, wxALL, BORDER_SIZE); + + wxStaticText* status5Label = new wxStaticText(panel, -1, _("Status 5:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); + text2Sizer->Add(status5Label, 0, wxALL, BORDER_SIZE); + + m_status5 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + text2Sizer->Add(m_status5, 0, wxALL, BORDER_SIZE); + + text1Sizer->Add(text2Sizer); + panelSizer->Add(text1Sizer, 0, wxALL, BORDER_SIZE); + + wxStaticBoxSizer* log1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Log")), wxVERTICAL); + wxBoxSizer* log2Sizer = new wxBoxSizer(wxVERTICAL); + + m_logLine1 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LOGTEXT_WIDTH, -1)); + m_logLine1->Wrap(LOGTEXT_WIDTH); + log2Sizer->Add(m_logLine1, 0, wxTOP | wxLEFT | wxRIGHT, BORDER_SIZE); + + m_logLine2 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LOGTEXT_WIDTH, -1)); + m_logLine2->Wrap(LOGTEXT_WIDTH); + log2Sizer->Add(m_logLine2, 0, wxLEFT | wxRIGHT, BORDER_SIZE); + + m_logLine3 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LOGTEXT_WIDTH, -1)); + m_logLine3->Wrap(LOGTEXT_WIDTH); + log2Sizer->Add(m_logLine3, 0, wxLEFT | wxRIGHT | wxBOTTOM, BORDER_SIZE); + + log1Sizer->Add(log2Sizer); + panelSizer->Add(log1Sizer, 0, wxALL, BORDER_SIZE); + + panel->SetSizer(panelSizer); + panelSizer->SetSizeHints(panel); + + mainSizer->Add(panel); + + SetSizer(mainSizer); + mainSizer->SetSizeHints(this); + + m_timer.Start(1000); +} + +CDStarRepeaterFrame::~CDStarRepeaterFrame() +{ +} + +wxMenuBar* CDStarRepeaterFrame::createMenuBar() +{ + wxMenu* fileMenu = new wxMenu(); + m_logging = fileMenu->AppendCheckItem(Menu_File_Logging, _("Logging")); + fileMenu->AppendSeparator(); + fileMenu->Append(wxID_EXIT, _("Exit")); + + wxMenu* viewMenu = new wxMenu(); + viewMenu->AppendCheckItem(Menu_View_Updates, _("GUI Updates")); + viewMenu->Check(Menu_View_Updates, m_updates); + + wxMenu* actionMenu = new wxMenu(); + actionMenu->Append(Menu_Action_Shutdown, _("Shutdown")); + actionMenu->Append(Menu_Action_Startup, _("Startup")); + actionMenu->Append(Menu_Action_Command1, _("Command 1")); + actionMenu->Append(Menu_Action_Command2, _("Command 2")); + actionMenu->Append(Menu_Action_Command3, _("Command 3")); + actionMenu->Append(Menu_Action_Command4, _("Command 4")); + actionMenu->Append(Menu_Action_Command5, _("Command 5")); + actionMenu->Append(Menu_Action_Command6, _("Command 6")); + + m_outputMenu = new wxMenu(); + m_outputMenu->AppendCheckItem(Menu_Output_1, _("Output 1")); + m_outputMenu->AppendCheckItem(Menu_Output_2, _("Output 2")); + m_outputMenu->AppendCheckItem(Menu_Output_3, _("Output 3")); + m_outputMenu->AppendCheckItem(Menu_Output_4, _("Output 4")); + + wxMenu* helpMenu = new wxMenu(); + helpMenu->Append(wxID_ABOUT, _("About D-Star Repeater")); + + wxMenuBar* menuBar = new wxMenuBar(); + menuBar->Append(fileMenu, _("File")); + menuBar->Append(viewMenu, _("View")); + menuBar->Append(actionMenu, _("Action")); + menuBar->Append(m_outputMenu, _("Outputs")); + menuBar->Append(helpMenu, _("Help")); + + return menuBar; +} + +void CDStarRepeaterFrame::setLogging(bool logging) +{ + m_logging->Check(logging); +} + +void CDStarRepeaterFrame::onLogging(wxCommandEvent& event) +{ + ::wxGetApp().setLogging(event.IsChecked()); +} + +void CDStarRepeaterFrame::onQuit(wxCommandEvent&) +{ + Close(false); +} + +void CDStarRepeaterFrame::onClose(wxCloseEvent&) +{ + int x, y; + GetPosition(&x, &y); + if (x >= 0 && y >= 0) + ::wxGetApp().setPosition(x, y); + + Destroy(); +} + +void CDStarRepeaterFrame::onStatus(wxCommandEvent& event) +{ + switch (event.GetId()) { + case Menu_Action_Shutdown: + ((CDStarRepeaterApp *) wxTheApp)->shutdown(); + break; + case Menu_Action_Startup: + ((CDStarRepeaterApp *) wxTheApp)->startup(); + break; + } +} + +void CDStarRepeaterFrame::onActions(wxCommandEvent& event) +{ + wxThreadEvent evt(wxEVT_THREAD, wxEVT_THREAD_COMMAND); + + switch (event.GetId()) { + case Menu_Action_Command1: + evt.SetInt(0); + break; + case Menu_Action_Command2: + evt.SetInt(1); + break; + case Menu_Action_Command3: + evt.SetInt(2); + break; + case Menu_Action_Command4: + evt.SetInt(3); + break; + case Menu_Action_Command5: + evt.SetInt(4); + break; + case Menu_Action_Command6: + evt.SetInt(5); + break; + } + + wxTheApp->QueueEvent(evt.Clone()); +} + +void CDStarRepeaterFrame::onOutputs(wxCommandEvent&) +{ + bool out1 = m_outputMenu->IsChecked(Menu_Output_1); + bool out2 = m_outputMenu->IsChecked(Menu_Output_2); + bool out3 = m_outputMenu->IsChecked(Menu_Output_3); + bool out4 = m_outputMenu->IsChecked(Menu_Output_4); + + ::wxGetApp().setOutputs(out1, out2, out3, out4); +} + +void CDStarRepeaterFrame::onUpdates(wxCommandEvent& event) +{ + m_updates = event.IsChecked(); +} + +void CDStarRepeaterFrame::onAbout(wxCommandEvent&) +{ + wxAboutDialogInfo info; + info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); + info.SetCopyright(wxT("(C) 2013-2015 using GPL v2 or later")); + info.SetName(APPLICATION_NAME); + info.SetVersion(VERSION); + info.SetDescription(_("This program allows a computer with a suitable\nmodem to act as a D-Star repeater.")); + + ::wxAboutBox(info); +} + +void CDStarRepeaterFrame::onTimer(wxTimerEvent&) +{ + if (!m_updates) + return; + + CDStarRepeaterStatusData* status = ::wxGetApp().getStatus(); + if (status == NULL) + return; + + switch (status->getRxState()) { + case DSRXS_LISTENING: + m_rxState->SetLabel(_("Listening")); + break; + case DSRXS_PROCESS_DATA: + m_rxState->SetLabel(_("Process Data")); + break; + case DSRXS_PROCESS_SLOW_DATA: + m_rxState->SetLabel(_("Slow Data")); + break; + } + + switch (status->getRptState()) { + case DSRS_SHUTDOWN: + m_rptState->SetLabel(_("Shutdown")); + break; + case DSRS_LISTENING: + m_rptState->SetLabel(_("Listening")); + break; + case DSRS_VALID: + m_rptState->SetLabel(_("Valid")); + break; + case DSRS_VALID_WAIT: + case DSRS_INVALID_WAIT: + case DSRS_TIMEOUT_WAIT: + m_rptState->SetLabel(_("Wait")); + break; + case DSRS_INVALID: + m_rptState->SetLabel(_("Invalid")); + break; + case DSRS_TIMEOUT: + m_rptState->SetLabel(_("Timeout")); + break; + case DSRS_NETWORK: + m_rptState->SetLabel(_("Network")); + break; + } + + m_your->SetLabel(status->getYourCall()); + + wxString myCall = status->getMyCall1(); + wxString myCall2 = status->getMyCall2(); + if (!myCall2.IsEmpty() && !myCall2.IsSameAs(wxT(" "))) { + myCall.Append(wxT("/")); + myCall.Append(myCall2); + } + m_my->SetLabel(myCall); + + m_rpt1->SetLabel(status->getRptCall1()); + + m_rpt2->SetLabel(status->getRptCall2()); + + wxString text; + + text.Printf(wxT("%02X %02X %02X"), status->getFlag1(), status->getFlag2(), status->getFlag3()); + m_flags->SetLabel(text); + + text.Printf(wxT("%.1f%%"), status->getPercent()); + m_percent->SetLabel(text); + + bool tx = status->getTX(); + m_tx->SetLabel(tx ? _("On") : _("Off")); + + text.Printf(wxT("%u/%u"), status->getTimeoutTimer(), status->getTimeoutExpiry()); + m_timeout->SetLabel(text); + + text.Printf(wxT("%u/%u"), status->getBeaconTimer(), status->getBeaconExpiry()); + m_beacon->SetLabel(text); + + text.Printf(wxT("%u/%u"), status->getAnnounceTimer(), status->getAnnounceExpiry()); + m_announce->SetLabel(text); + + m_text->SetLabel(status->getText()); + m_status1->SetLabel(status->getStatus1()); + m_status2->SetLabel(status->getStatus2()); + m_status3->SetLabel(status->getStatus3()); + m_status4->SetLabel(status->getStatus4()); + m_status5->SetLabel(status->getStatus5()); + + // DVAP + if (m_squelch != NULL) { + if (tx) { + m_squelch->SetLabel(wxEmptyString); + m_signal->SetLabel(wxEmptyString); + } else { + bool squelch = status->getSquelch(); + m_squelch->SetLabel(squelch ? _("Open") : _("Closed")); + + text.Printf(wxT("%d dBm"), status->getSignal()); + m_signal->SetLabel(text); + } + } + + delete status; +} + +void CDStarRepeaterFrame::onLog(wxEvent& event) +{ + CLogEvent& logEvent = dynamic_cast(event); + + wxString text = logEvent.getText(); + + m_logLine1->SetLabel(m_logLine2->GetLabel()); + m_logLine2->SetLabel(m_logLine3->GetLabel()); + m_logLine3->SetLabel(text); +} + +void CDStarRepeaterFrame::showLog(const wxString& text) +{ + if (!m_updates) + return; + + CLogEvent event(text, LOG_EVENT); + + AddPendingEvent(event); +} + +void CDStarRepeaterFrame::setOutputs(bool out1, bool out2, bool out3, bool out4) +{ + m_outputMenu->Check(Menu_Output_1, out1); + m_outputMenu->Check(Menu_Output_2, out2); + m_outputMenu->Check(Menu_Output_3, out3); + m_outputMenu->Check(Menu_Output_4, out4); +} diff --git a/DStarRepeater/DStarRepeaterFrame.h b/DStarRepeater/DStarRepeaterFrame.h new file mode 100644 index 0000000..1cb25ca --- /dev/null +++ b/DStarRepeater/DStarRepeaterFrame.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterFrame_H +#define DStarRepeaterFrame_H + +#include "DStarRepeaterDefs.h" + +#include + +class CDStarRepeaterFrame : public wxFrame { +public: + CDStarRepeaterFrame(const wxString& title, const wxString& type, const wxPoint& position, bool gui); + virtual ~CDStarRepeaterFrame(); + + virtual void setLogging(bool logging); + + virtual void onLogging(wxCommandEvent& event); + virtual void onQuit(wxCommandEvent& event); + virtual void onUpdates(wxCommandEvent& event); + virtual void onAbout(wxCommandEvent& event); + virtual void onClose(wxCloseEvent& event); + virtual void onStatus(wxCommandEvent& event); + virtual void onActions(wxCommandEvent& event); + virtual void onOutputs(wxCommandEvent& event); + virtual void onLog(wxEvent& event); + + virtual void onTimer(wxTimerEvent& event); + + virtual void showLog(const wxString& text); + + virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); + +private: + wxMenuItem* m_logging; + wxStaticText* m_rxState; + wxStaticText* m_rptState; + wxStaticText* m_tx; + wxStaticText* m_squelch; + wxStaticText* m_signal; + wxStaticText* m_your; + wxStaticText* m_my; + wxStaticText* m_rpt1; + wxStaticText* m_rpt2; + wxStaticText* m_flags; + wxStaticText* m_percent; + wxStaticText* m_timeout; + wxStaticText* m_beacon; + wxStaticText* m_announce; + wxStaticText* m_text; + wxStaticText* m_status1; + wxStaticText* m_status2; + wxStaticText* m_status3; + wxStaticText* m_status4; + wxStaticText* m_status5; + wxStaticText* m_logLine1; + wxStaticText* m_logLine2; + wxStaticText* m_logLine3; + wxTimer m_timer; + wxMenu* m_outputMenu; + bool m_updates; + + DECLARE_EVENT_TABLE() + + wxMenuBar* createMenuBar(); +}; + +#endif diff --git a/DStarRepeater/DStarRepeaterLogger.cpp b/DStarRepeater/DStarRepeaterLogger.cpp new file mode 100644 index 0000000..5536e13 --- /dev/null +++ b/DStarRepeater/DStarRepeaterLogger.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2002,2003,2009-2012 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterLogger.h" +#include "DStarRepeaterApp.h" + +CDStarRepeaterLogger::CDStarRepeaterLogger() : +wxLog() +{ +} + +CDStarRepeaterLogger::~CDStarRepeaterLogger() +{ +} + +void CDStarRepeaterLogger::DoLog(wxLogLevel level, const wxChar* msg, time_t timestamp) +{ + wxASSERT(msg != NULL); + + wxString letter; + + switch (level) { + case wxLOG_FatalError: letter = wxT("F"); break; + case wxLOG_Error: letter = wxT("E"); break; + case wxLOG_Warning: letter = wxT("W"); break; + case wxLOG_Info: letter = wxT("I"); break; + case wxLOG_Message: letter = wxT("M"); break; + case wxLOG_Status: letter = wxT("M"); break; + case wxLOG_Trace: letter = wxT("T"); break; + case wxLOG_Debug: letter = wxT("D"); break; + default: letter = wxT("U"); break; + } + + struct tm* tm = ::gmtime(×tamp); + + wxString message; + message.Printf(wxT("%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n"), letter.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msg); + + DoLogString(message.c_str(), timestamp); + + if (level == wxLOG_FatalError) + ::abort(); +} + +void CDStarRepeaterLogger::DoLogString(const wxChar* msg, time_t timestamp) +{ + wxASSERT(msg != NULL); + + ::wxGetApp().showLog(msg); +} diff --git a/DStarRepeater/DStarRepeaterLogger.h b/DStarRepeater/DStarRepeaterLogger.h new file mode 100644 index 0000000..c9ec940 --- /dev/null +++ b/DStarRepeater/DStarRepeaterLogger.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2002,2003,2009-2011 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterLogger_H +#define DStarRepeaterLogger_H + +#include + +class CDStarRepeaterLogger : public wxLog { +public: + CDStarRepeaterLogger(); + virtual ~CDStarRepeaterLogger(); + + virtual void DoLog(wxLogLevel level, const wxChar* msg, time_t timestamp); + virtual void DoLogString(const wxChar* msg, time_t timestamp); + +private: +}; + +#endif diff --git a/DStarRepeater/DStarRepeaterRXThread.cpp b/DStarRepeater/DStarRepeaterRXThread.cpp new file mode 100644 index 0000000..28cf810 --- /dev/null +++ b/DStarRepeater/DStarRepeaterRXThread.cpp @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2011-2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterStatusData.h" +#include "DStarRepeaterRXThread.h" +#include "DVAPController.h" +#include "DStarDefines.h" +#include "HeaderData.h" +#include "Version.h" +#include "Utils.h" + +const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U}; +const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; + +const unsigned int MAX_DATA_SYNC_BIT_ERRS = 2U; + +const unsigned int NETWORK_QUEUE_COUNT = 2U; + +const unsigned int CYCLE_TIME = 9U; + +CDStarRepeaterRXThread::CDStarRepeaterRXThread(const wxString& type) : +m_type(type), +m_modem(NULL), +m_protocolHandler(NULL), +m_rxHeader(NULL), +m_radioSeqNo(0U), +m_registerTimer(1000U), +m_rptState(DSRS_LISTENING), +m_rxState(DSRXS_LISTENING), +m_slowDataDecoder(), +m_killed(false), +m_ambe(), +m_ambeFrames(0U), +m_ambeSilence(0U), +m_ambeBits(1U), +m_ambeErrors(0U), +m_lastAMBEBits(0U), +m_lastAMBEErrors(0U) +{ + setRadioState(DSRXS_LISTENING); +} + +CDStarRepeaterRXThread::~CDStarRepeaterRXThread() +{ + delete m_rxHeader; +} + +void *CDStarRepeaterRXThread::Entry() +{ + // Wait here until we have the essentials to run + while (!m_killed && (m_modem == NULL || m_protocolHandler == NULL)) + ::wxMilliSleep(500UL); // 1/2 sec + + if (m_killed) + return NULL; + + m_registerTimer.start(10U); + + wxString hardware = m_type; + int n = hardware.Find(wxT(' ')); + if (n != wxNOT_FOUND) + hardware = m_type.Left(n); + + wxLogMessage(wxT("Starting the D-Star receiver thread")); + + wxStopWatch stopWatch; + + try { + while (!m_killed) { + stopWatch.Start(); + + receiveModem(); + + receiveNetwork(); + + // Send the register packet if needed and restart the timer + if (m_registerTimer.hasExpired()) { + m_protocolHandler->writeRegister(); + m_registerTimer.start(30U); + } + + unsigned long ms = stopWatch.Time(); + if (ms < CYCLE_TIME) { + ::wxMilliSleep(CYCLE_TIME - ms); + clock(CYCLE_TIME); + } else { + clock(ms); + } + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised")); + } + + wxLogMessage(wxT("Stopping the D-Star receiver thread")); + + m_modem->stop(); + + m_protocolHandler->close(); + delete m_protocolHandler; + + return NULL; +} + +void CDStarRepeaterRXThread::kill() +{ + m_killed = true; +} + +void CDStarRepeaterRXThread::setCallsign(const wxString&, const wxString&, DSTAR_MODE, ACK_TYPE, bool, bool, bool, bool) +{ +} + +void CDStarRepeaterRXThread::setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) +{ + wxASSERT(handler != NULL); + + m_protocolHandler = handler; +} + +void CDStarRepeaterRXThread::setModem(CModem* modem) +{ + wxASSERT(modem != NULL); + + m_modem = modem; +} + +void CDStarRepeaterRXThread::setTimes(unsigned int, unsigned int) +{ +} + +void CDStarRepeaterRXThread::setBeacon(unsigned int, const wxString&, bool, TEXT_LANG) +{ +} + +void CDStarRepeaterRXThread::setAnnouncement(bool, unsigned int, const wxString&, const wxString&, const wxString&, const wxString&) +{ +} + +void CDStarRepeaterRXThread::setController(CExternalController*, unsigned int) +{ +} + +void CDStarRepeaterRXThread::setOutputs(bool, bool, bool, bool) +{ +} + +void CDStarRepeaterRXThread::setLogging(bool, const wxString&) +{ +} + +void CDStarRepeaterRXThread::setWhiteList(CCallsignList*) +{ +} + +void CDStarRepeaterRXThread::setBlackList(CCallsignList*) +{ +} + +void CDStarRepeaterRXThread::setGreyList(CCallsignList*) +{ +} + +void CDStarRepeaterRXThread::receiveModem() +{ + for (;;) { + DSMT_TYPE type = m_modem->read(); + if (type == DSMTT_NONE) + return; + + switch (m_rxState) { + case DSRXS_LISTENING: + if (type == DSMTT_HEADER) { + CHeaderData* header = m_modem->readHeader(); + receiveHeader(header); + } else if (type == DSMTT_DATA) { + unsigned char data[20U]; + unsigned int length = m_modem->readData(data, 20U); + setRadioState(DSRXS_PROCESS_SLOW_DATA); + receiveSlowData(data, length); + } + break; + + case DSRXS_PROCESS_SLOW_DATA: + if (type == DSMTT_DATA) { + unsigned char data[20U]; + unsigned int length = m_modem->readData(data, 20U); + receiveSlowData(data, length); + } else if (type == DSMTT_EOT || type == DSMTT_LOST) { + setRadioState(DSRXS_LISTENING); + } + break; + + case DSRXS_PROCESS_DATA: + if (type == DSMTT_DATA) { + unsigned char data[20U]; + unsigned int length = m_modem->readData(data, 20U); + receiveRadioData(data, length); + } else if (type == DSMTT_EOT || type == DSMTT_LOST) { + unsigned char data[20U]; + ::memcpy(data, END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + processRadioFrame(data, FRAME_END); + setRadioState(DSRXS_LISTENING); + endOfRadioData(); + } + break; + } + } +} + +void CDStarRepeaterRXThread::receiveHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + wxLogMessage(wxT("Radio header decoded - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + bool res = processRadioHeader(header); + if (res) { + // A valid header and is a DV packet + m_radioSeqNo = 20U; + setRadioState(DSRXS_PROCESS_DATA); + } else { + // This is a DD packet or some other problem + // wxLogMessage(wxT("Invalid header")); + } +} + +void CDStarRepeaterRXThread::receiveSlowData(unsigned char* data, unsigned int) +{ + unsigned int errs; + errs = countBits(data[VOICE_FRAME_LENGTH_BYTES + 0U] ^ DATA_SYNC_BYTES[0U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 1U] ^ DATA_SYNC_BYTES[1U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 2U] ^ DATA_SYNC_BYTES[2U]); + + // The data sync has been seen, a fuzzy match is used, two bit errors or less + if (errs <= MAX_DATA_SYNC_BIT_ERRS) { + // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + m_radioSeqNo = 0U; + m_slowDataDecoder.sync(); + } else if (m_radioSeqNo == 20U) { + // wxLogMessage(wxT("Assuming data sync")); + m_radioSeqNo = 0U; + m_slowDataDecoder.sync(); + } else { + m_radioSeqNo++; + m_slowDataDecoder.addData(data + VOICE_FRAME_LENGTH_BYTES); + + CHeaderData* header = m_slowDataDecoder.getHeaderData(); + if (header == NULL) + return; + + wxLogMessage(wxT("Radio header from slow data - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X BER: 0%%"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + if (header != NULL) { + bool res = processRadioHeader(header); + if (res) { + // A valid header and is a DV packet, go to normal data relaying + setRadioState(DSRXS_PROCESS_DATA); + } else { + // This is a DD packet or some other problem + // wxLogMessage(wxT("Invalid header")); + } + } + } +} + +void CDStarRepeaterRXThread::receiveRadioData(unsigned char* data, unsigned int) +{ + unsigned int errs; + errs = countBits(data[VOICE_FRAME_LENGTH_BYTES + 0U] ^ DATA_SYNC_BYTES[0U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 1U] ^ DATA_SYNC_BYTES[1U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 2U] ^ DATA_SYNC_BYTES[2U]); + + // The data sync has been seen, a fuzzy match is used, two bit errors or less + if (errs <= MAX_DATA_SYNC_BIT_ERRS) { + // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + m_radioSeqNo = 0U; + processRadioFrame(data, FRAME_SYNC); + } else if (m_radioSeqNo == 20U) { + // wxLogMessage(wxT("Regenerating data sync")); + m_radioSeqNo = 0U; + processRadioFrame(data, FRAME_SYNC); + } else { + m_radioSeqNo++; + processRadioFrame(data, FRAME_NORMAL); + } +} + +void CDStarRepeaterRXThread::receiveNetwork() +{ + NETWORK_TYPE type; + + do { + type = m_protocolHandler->read(); + } while (type != NETWORK_NONE); +} + +void CDStarRepeaterRXThread::setRadioState(DSTAR_RX_STATE state) +{ + // This is the too state + switch (state) { + case DSRXS_LISTENING: + m_rxState = DSRXS_LISTENING; + break; + + case DSRXS_PROCESS_DATA: + m_ambeFrames = 0U; + m_ambeSilence = 0U; + m_ambeBits = 1U; + m_ambeErrors = 0U; + m_lastAMBEBits = 0U; + m_lastAMBEErrors = 0U; + m_rxState = DSRXS_PROCESS_DATA; + break; + + case DSRXS_PROCESS_SLOW_DATA: + m_radioSeqNo = 0U; + m_slowDataDecoder.reset(); + m_rxState = DSRXS_PROCESS_SLOW_DATA; + break; + } +} + +bool CDStarRepeaterRXThread::processRadioHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + // We don't handle DD data packets + if (header->isDataPacket()) { + wxLogMessage(wxT("Received a DD packet, ignoring")); + delete header; + return false; + } + + m_rptState = DSRS_VALID; + + // Send the valid header to the gateway if we are accepted + delete m_rxHeader; + m_rxHeader = header; + + CHeaderData netHeader(*m_rxHeader); + netHeader.setRptCall1(m_rxHeader->getRptCall2()); + netHeader.setRptCall2(m_rxHeader->getRptCall1()); + + m_protocolHandler->writeHeader(netHeader); + + return true; +} + +void CDStarRepeaterRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE type) +{ + m_ambeFrames++; + + // If a sync frame, regenerate the sync bytes + if (type == FRAME_SYNC) + ::memcpy(data + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + + // Only regenerate the AMBE on received radio data + unsigned int errors = 0U; + if (type != FRAME_END) { + // Data packets have no AMBE FEC + if (!m_rxHeader->isDataPacket()) + errors = m_ambe.count(data); + + m_ambeErrors += errors; + m_ambeBits += 48U; // Only count the bits with FEC added + } + + if (::memcmp(data, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES) == 0) + m_ambeSilence++; + + if (type == FRAME_END) { + // Send null data and the end marker over the network, and the statistics + unsigned char bytes[DV_FRAME_MAX_LENGTH_BYTES]; + ::memcpy(bytes, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(bytes + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + m_protocolHandler->writeData(bytes, DV_FRAME_MAX_LENGTH_BYTES, 0U, true); + } else { + // Send the data to the network + m_protocolHandler->writeData(data, DV_FRAME_LENGTH_BYTES, errors, false); + } +} + +void CDStarRepeaterRXThread::endOfRadioData() +{ + wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + + m_rptState = DSRS_LISTENING; + + m_protocolHandler->reset(); +} + +CDStarRepeaterStatusData* CDStarRepeaterRXThread::getStatus() +{ + float bits = float(m_ambeBits - m_lastAMBEBits); + float errors = float(m_ambeErrors - m_lastAMBEErrors); + if (bits == 0.0F) + bits = 1.0F; + + m_lastAMBEBits = m_ambeBits; + m_lastAMBEErrors = m_ambeErrors; + + CDStarRepeaterStatusData* status; + if (m_rptState == DSRS_SHUTDOWN || m_rptState == DSRS_LISTENING) + status = new CDStarRepeaterStatusData(wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, + wxEmptyString, 0x00, 0x00, 0x00, false, m_rxState, m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, 0.0F, + wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + else + status = new CDStarRepeaterStatusData(m_rxHeader->getMyCall1(), m_rxHeader->getMyCall2(), + m_rxHeader->getYourCall(), m_rxHeader->getRptCall1(), m_rxHeader->getRptCall2(), + m_rxHeader->getFlag1(), m_rxHeader->getFlag2(), m_rxHeader->getFlag3(), false, m_rxState, + m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, (errors * 100.0F) / bits, wxEmptyString, wxEmptyString, + wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + + if (m_type.IsSameAs(wxT("DVAP")) && m_modem != NULL) { + CDVAPController* dvap = static_cast(m_modem); + bool squelch = dvap->getSquelch(); + int signal = dvap->getSignal(); + status->setDVAP(squelch, signal); + } + + return status; +} + +void CDStarRepeaterRXThread::clock(unsigned int ms) +{ + m_registerTimer.clock(ms); +} + +void CDStarRepeaterRXThread::shutdown() +{ +} + +void CDStarRepeaterRXThread::startup() +{ +} + +void CDStarRepeaterRXThread::command1() +{ +} + +void CDStarRepeaterRXThread::command2() +{ +} + +void CDStarRepeaterRXThread::command3() +{ +} + +void CDStarRepeaterRXThread::command4() +{ +} + +void CDStarRepeaterRXThread::command5() +{ +} + +void CDStarRepeaterRXThread::command6() +{ +} + +unsigned int CDStarRepeaterRXThread::countBits(unsigned char byte) +{ + unsigned int bits = 0U; + + if ((byte & 0x01U) == 0x01U) + bits++; + if ((byte & 0x02U) == 0x02U) + bits++; + if ((byte & 0x04U) == 0x04U) + bits++; + if ((byte & 0x08U) == 0x08U) + bits++; + if ((byte & 0x10U) == 0x10U) + bits++; + if ((byte & 0x20U) == 0x20U) + bits++; + if ((byte & 0x40U) == 0x40U) + bits++; + if ((byte & 0x80U) == 0x80U) + bits++; + + return bits; +} diff --git a/DStarRepeater/DStarRepeaterRXThread.h b/DStarRepeater/DStarRepeaterRXThread.h new file mode 100644 index 0000000..ba656f7 --- /dev/null +++ b/DStarRepeater/DStarRepeaterRXThread.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterRXThread_H +#define DStarRepeaterRXThread_H + +#include "DStarRepeaterThread.h" +#include "SlowDataDecoder.h" +#include "HeaderData.h" +#include "AMBEFEC.h" +#include "Timer.h" + +#include + +class CDStarRepeaterRXThread : public IDStarRepeaterThread { +public: + CDStarRepeaterRXThread(const wxString& type); + virtual ~CDStarRepeaterRXThread(); + + virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local); + virtual void setModem(CModem* modem); + virtual void setController(CExternalController* controller, unsigned int activeHangTime); + virtual void setTimes(unsigned int timeout, unsigned int ackTime); + virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); + virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + + virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); + virtual void setLogging(bool logging, const wxString& dir); + virtual void setWhiteList(CCallsignList* list); + virtual void setBlackList(CCallsignList* list); + virtual void setGreyList(CCallsignList* list); + + virtual void shutdown(); + virtual void startup(); + + virtual void command1(); + virtual void command2(); + virtual void command3(); + virtual void command4(); + virtual void command5(); + virtual void command6(); + + virtual CDStarRepeaterStatusData* getStatus(); + + virtual void kill(); + + virtual void *Entry(); + +private: + wxString m_type; + CModem* m_modem; + CRepeaterProtocolHandler* m_protocolHandler; + CHeaderData* m_rxHeader; + unsigned char m_radioSeqNo; + CTimer m_registerTimer; + DSTAR_RPT_STATE m_rptState; + DSTAR_RX_STATE m_rxState; + CSlowDataDecoder m_slowDataDecoder; + bool m_killed; + CAMBEFEC m_ambe; + unsigned int m_ambeFrames; + unsigned int m_ambeSilence; + unsigned int m_ambeBits; + unsigned int m_ambeErrors; + unsigned int m_lastAMBEBits; + unsigned int m_lastAMBEErrors; + + void receiveHeader(CHeaderData* header); + void receiveRadioData(unsigned char* data, unsigned int length); + void receiveSlowData(unsigned char* data, unsigned int length); + + void receiveModem(); + void receiveNetwork(); + bool processRadioHeader(CHeaderData* header); + void processRadioFrame(unsigned char* data, FRAME_TYPE type); + void endOfRadioData(); + void setRadioState(DSTAR_RX_STATE state); + unsigned int countBits(unsigned char byte); + void clock(unsigned int ms); +}; + +#endif diff --git a/DStarRepeater/DStarRepeaterStatusData.cpp b/DStarRepeater/DStarRepeaterStatusData.cpp new file mode 100644 index 0000000..9d4dacc --- /dev/null +++ b/DStarRepeater/DStarRepeaterStatusData.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterStatusData.h" + +CDStarRepeaterStatusData::CDStarRepeaterStatusData(const wxString& myCall1, const wxString& myCall2, + const wxString& yourCall, const wxString& rptCall1, + const wxString& rptCall2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, bool tx, + DSTAR_RX_STATE rxState, DSTAR_RPT_STATE rptState, + unsigned int timeoutTimer, unsigned int timeoutExpiry, + unsigned int beaconTimer, unsigned int beaconExpiry, + unsigned int announceTimer, unsigned int announceExpiry, + float percent, const wxString& text, const wxString& status1, + const wxString& status2, const wxString& status3, + const wxString& status4, const wxString& status5) : +m_myCall1(myCall1), +m_myCall2(myCall2), +m_yourCall(yourCall), +m_rptCall1(rptCall1), +m_rptCall2(rptCall2), +m_flag1(flag1), +m_flag2(flag2), +m_flag3(flag3), +m_tx(tx), +m_rxState(rxState), +m_rptState(rptState), +m_timeoutTimer(timeoutTimer), +m_timeoutExpiry(timeoutExpiry), +m_beaconTimer(beaconTimer), +m_beaconExpiry(beaconExpiry), +m_announceTimer(announceTimer), +m_announceExpiry(announceExpiry), +m_percent(percent), +m_text(text), +m_status1(status1), +m_status2(status2), +m_status3(status3), +m_status4(status4), +m_status5(status5), +m_squelch(false), +m_signal(0) +{ +} + +CDStarRepeaterStatusData::~CDStarRepeaterStatusData() +{ +} + +void CDStarRepeaterStatusData::setDVAP(bool squelch, int signal) +{ + m_squelch = squelch; + m_signal = signal; +} + +wxString CDStarRepeaterStatusData::getMyCall1() const +{ + return m_myCall1; +} + +wxString CDStarRepeaterStatusData::getMyCall2() const +{ + return m_myCall2; +} + +wxString CDStarRepeaterStatusData::getYourCall() const +{ + return m_yourCall; +} + +wxString CDStarRepeaterStatusData::getRptCall1() const +{ + return m_rptCall1; +} + +wxString CDStarRepeaterStatusData::getRptCall2() const +{ + return m_rptCall2; +} + +unsigned char CDStarRepeaterStatusData::getFlag1() const +{ + return m_flag1; +} + +unsigned char CDStarRepeaterStatusData::getFlag2() const +{ + return m_flag2; +} + +unsigned char CDStarRepeaterStatusData::getFlag3() const +{ + return m_flag3; +} + +bool CDStarRepeaterStatusData::getTX() const +{ + return m_tx; +} + +bool CDStarRepeaterStatusData::getSquelch() const +{ + return m_squelch; +} + +int CDStarRepeaterStatusData::getSignal() const +{ + return m_signal; +} + +DSTAR_RX_STATE CDStarRepeaterStatusData::getRxState() const +{ + return m_rxState; +} + +DSTAR_RPT_STATE CDStarRepeaterStatusData::getRptState() const +{ + return m_rptState; +} + +unsigned int CDStarRepeaterStatusData::getTimeoutTimer() const +{ + return m_timeoutTimer; +} + +unsigned int CDStarRepeaterStatusData::getTimeoutExpiry() const +{ + return m_timeoutExpiry; +} + +unsigned int CDStarRepeaterStatusData::getBeaconTimer() const +{ + return m_beaconTimer; +} + +unsigned int CDStarRepeaterStatusData::getBeaconExpiry() const +{ + return m_beaconExpiry; +} + +unsigned int CDStarRepeaterStatusData::getAnnounceTimer() const +{ + return m_announceTimer; +} + +unsigned int CDStarRepeaterStatusData::getAnnounceExpiry() const +{ + return m_announceExpiry; +} + +float CDStarRepeaterStatusData::getPercent() const +{ + return m_percent; +} + +wxString CDStarRepeaterStatusData::getText() const +{ + return m_text; +} + +wxString CDStarRepeaterStatusData::getStatus1() const +{ + return m_status1; +} + +wxString CDStarRepeaterStatusData::getStatus2() const +{ + return m_status2; +} + +wxString CDStarRepeaterStatusData::getStatus3() const +{ + return m_status3; +} + +wxString CDStarRepeaterStatusData::getStatus4() const +{ + return m_status4; +} + +wxString CDStarRepeaterStatusData::getStatus5() const +{ + return m_status5; +} diff --git a/DStarRepeater/DStarRepeaterStatusData.h b/DStarRepeater/DStarRepeaterStatusData.h new file mode 100644 index 0000000..f0ae787 --- /dev/null +++ b/DStarRepeater/DStarRepeaterStatusData.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterStatusData_H +#define DStarRepeaterStatusData_H + +#include "DStarRepeaterDefs.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterStatusData { +public: + CDStarRepeaterStatusData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall, + const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, bool tx, DSTAR_RX_STATE rxState, + DSTAR_RPT_STATE rptState, unsigned int timeoutTimer, unsigned int timeoutExpiry, + unsigned int beaconTimer, unsigned int beaconExpiry, unsigned int announceTimer, + unsigned int announceExpiry, float percent, const wxString& text, + const wxString& status1, const wxString& status2, const wxString& status3, + const wxString& status4, const wxString& status5); + ~CDStarRepeaterStatusData(); + + void setDVAP(bool squelch, int signal); + + wxString getMyCall1() const; + wxString getMyCall2() const; + wxString getYourCall() const; + wxString getRptCall1() const; + wxString getRptCall2() const; + unsigned char getFlag1() const; + unsigned char getFlag2() const; + unsigned char getFlag3() const; + + bool getTX() const; + bool getSquelch() const; + int getSignal() const; + + DSTAR_RPT_STATE getRptState() const; + DSTAR_RX_STATE getRxState() const; + + unsigned int getTimeoutTimer() const; + unsigned int getTimeoutExpiry() const; + + unsigned int getBeaconTimer() const; + unsigned int getBeaconExpiry() const; + + unsigned int getAnnounceTimer() const; + unsigned int getAnnounceExpiry() const; + + float getPercent() const; + + wxString getText() const; + wxString getStatus1() const; + wxString getStatus2() const; + wxString getStatus3() const; + wxString getStatus4() const; + wxString getStatus5() const; + +private: + wxString m_myCall1; + wxString m_myCall2; + wxString m_yourCall; + wxString m_rptCall1; + wxString m_rptCall2; + unsigned char m_flag1; + unsigned char m_flag2; + unsigned char m_flag3; + bool m_tx; + DSTAR_RX_STATE m_rxState; + DSTAR_RPT_STATE m_rptState; + unsigned int m_timeoutTimer; + unsigned int m_timeoutExpiry; + unsigned int m_beaconTimer; + unsigned int m_beaconExpiry; + unsigned int m_announceTimer; + unsigned int m_announceExpiry; + float m_percent; + wxString m_text; + wxString m_status1; + wxString m_status2; + wxString m_status3; + wxString m_status4; + wxString m_status5; + + // DVAP + bool m_squelch; + int m_signal; +}; + +#endif diff --git a/DStarRepeater/DStarRepeaterTRXThread.cpp b/DStarRepeater/DStarRepeaterTRXThread.cpp new file mode 100644 index 0000000..0292133 --- /dev/null +++ b/DStarRepeater/DStarRepeaterTRXThread.cpp @@ -0,0 +1,1979 @@ +/* + * Copyright (C) 2011-2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterStatusData.h" +#include "DStarRepeaterTRXThread.h" +#include "DStarRepeaterApp.h" +#include "DVAPController.h" +#include "DStarDefines.h" +#include "HeaderData.h" +#include "Version.h" + +const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U}; +const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; + +const unsigned int MAX_DATA_SYNC_BIT_ERRS = 2U; + +const unsigned int NETWORK_QUEUE_COUNT = 2U; + +const unsigned int SILENCE_THRESHOLD = 2U; + +const unsigned int CYCLE_TIME = 9U; + +CDStarRepeaterTRXThread::CDStarRepeaterTRXThread(const wxString& type) : +m_type(type), +m_modem(NULL), +m_protocolHandler(NULL), +m_controller(NULL), +m_stopped(true), +m_rptCallsign(), +m_gwyCallsign(), +m_beacon(NULL), +m_announcement(NULL), +m_recordRPT1(), +m_recordRPT2(), +m_deleteRPT1(), +m_deleteRPT2(), +m_rxHeader(NULL), +m_localQueue((DV_FRAME_LENGTH_BYTES + 2U) * 50U, LOCAL_RUN_FRAME_COUNT), // 1s worth of data +m_radioQueue((DV_FRAME_LENGTH_BYTES + 2U) * 50U, RADIO_RUN_FRAME_COUNT), // 1s worth of data +m_networkQueue(NULL), +m_writeNum(0U), +m_readNum(0U), +m_radioSeqNo(0U), +m_networkSeqNo(0U), +m_lastSlowDataType(0x00U), +m_timeoutTimer(1000U, 180U), // 180s +m_watchdogTimer(1000U, NETWORK_TIMEOUT), +m_pollTimer(1000U, 60U), // 60s +m_ackTimer(1000U, 0U, 500U), // 0.5s +m_beaconTimer(1000U, 600U), // 10 mins +m_announcementTimer(1000U, 0U), // not running +m_statusTimer(1000U, 0U, 100U), // 100ms +m_heartbeatTimer(1000U, 1U), // 1s +m_rptState(DSRS_LISTENING), +m_rxState(DSRXS_LISTENING), +m_slowDataDecoder(), +m_ackEncoder(), +m_linkEncoder(), +m_headerEncoder(), +m_status1Encoder(), +m_status2Encoder(), +m_status3Encoder(), +m_status4Encoder(), +m_status5Encoder(), +m_tx(false), +m_space(0U), +m_killed(false), +m_mode(MODE_DUPLEX), +m_ack(AT_BER), +m_restriction(false), +m_rpt1Validation(true), +m_errorReply(true), +m_controlEnabled(false), +m_controlRPT1(), +m_controlRPT2(), +m_controlShutdown(), +m_controlStartup(), +m_activeHangTimer(1000U), +m_shutdown(false), +m_disable(false), +m_logging(NULL), +m_lastData(NULL), +m_ambe(), +m_ambeFrames(0U), +m_ambeSilence(0U), +m_ambeBits(1U), +m_ambeErrors(0U), +m_lastAMBEBits(0U), +m_lastAMBEErrors(0U), +m_ackText(), +m_tempAckText(), +m_linkStatus(LS_NONE), +m_reflector(), +m_regEx(wxT("^[A-Z0-9]{1}[A-Z0-9]{0,1}[0-9]{1,2}[A-Z]{1,4} {0,4}[ A-Z]{1}$")), +m_headerTime(), +m_packetTime(), +m_packetCount(0U), +m_packetSilence(0U), +m_whiteList(NULL), +m_blackList(NULL), +m_greyList(NULL), +m_blocked(false), +m_busyData(false), +m_blanking(true), +m_recording(false), +m_deleting(false) +{ + for(int i = 0; i < 5; ++i) + m_statusAnnounceTimer[i] = CTimer(1000U, 3U); + + m_statusText.Add("", 5); + + m_networkQueue = new COutputQueue*[NETWORK_QUEUE_COUNT]; + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + m_networkQueue[i] = new COutputQueue((DV_FRAME_LENGTH_BYTES + 2U) * 200U, NETWORK_RUN_FRAME_COUNT); // 4s worth of data); + + m_lastData = new unsigned char[DV_FRAME_MAX_LENGTH_BYTES]; + + setRepeaterState(DSRS_LISTENING); + setRadioState(DSRXS_LISTENING); +} + +CDStarRepeaterTRXThread::~CDStarRepeaterTRXThread() +{ + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + delete m_networkQueue[i]; + delete[] m_networkQueue; + delete[] m_lastData; + delete m_rxHeader; +} + +void *CDStarRepeaterTRXThread::Entry() +{ + // Wait here until we have the essentials to run + while (!m_killed && (m_modem == NULL || m_controller == NULL || m_rptCallsign.IsEmpty() || m_rptCallsign.IsSameAs(wxT(" ")))) + ::wxMilliSleep(500UL); // 1/2 sec + + if (m_killed) + return NULL; + + m_stopped = false; + + m_beaconTimer.start(); + m_announcementTimer.start(); + m_controller->setActive(false); + m_controller->setRadioTransmit(false); + m_statusTimer.start(); + m_heartbeatTimer.start(); + + if (m_protocolHandler != NULL) + m_pollTimer.start(); + + wxString hardware = m_type; + int n = hardware.Find(wxT(' ')); + if (n != wxNOT_FOUND) + hardware = m_type.Left(n); + + wxString pollText; +#if defined(__WINDOWS__) + pollText.Printf(wxT("win_%s-%s"), hardware.c_str(), VERSION.c_str()); +#else + pollText.Printf(wxT("linux_%s-%s"), hardware.c_str(), VERSION.c_str()); +#endif + pollText.Replace(wxT(" "), wxT("-")); + pollText.MakeLower(); + wxLogMessage(wxT("Poll text set to \"%s\""), pollText.c_str()); + + wxLogMessage(wxT("Starting the D-Star repeater thread")); + + wxStopWatch stopWatch; + + try { + while (!m_killed) { + stopWatch.Start(); + + if (m_statusTimer.hasExpired() || m_space == 0U) { + m_space = m_modem->getSpace(); + m_tx = m_modem->isTX(); + m_statusTimer.start(); + } + + receiveModem(); + + receiveNetwork(); + + repeaterStateMachine(); + + // Send the network poll if needed and restart the timer + if (m_pollTimer.hasExpired()) { + m_protocolHandler->writePoll(pollText); + m_pollTimer.start(); + } + + // Send the beacon and restart the timer + if (m_beaconTimer.isRunning() && m_beaconTimer.hasExpired()) { + m_beacon->sendBeacon(); + m_beaconTimer.start(); + } + + // Send the announcement and restart the timer + if (m_announcementTimer.isRunning() && m_announcementTimer.hasExpired()) { + m_announcement->startAnnouncement(); + m_announcementTimer.start(); + } + + for(int i = 0; i < 5; ++ i) { + if(m_statusAnnounceTimer[i].isRunning() && + m_statusAnnounceTimer[i].hasExpired()) { + m_statusAnnounceTimer[i].stop(); + if(m_rptState == DSRS_LISTENING) + transmitUserStatus(i); + } + } + + // Clock the heartbeat output every one second + if (m_heartbeatTimer.hasExpired()) { + m_controller->setHeartbeat(); + m_heartbeatTimer.start(); + } + + // Set the output state + if (m_rptState == DSRS_VALID || m_rptState == DSRS_INVALID || m_rptState == DSRS_TIMEOUT || + m_rptState == DSRS_VALID_WAIT || m_rptState == DSRS_INVALID_WAIT || m_rptState == DSRS_TIMEOUT_WAIT || + m_rptState == DSRS_NETWORK || (m_activeHangTimer.isRunning() && !m_activeHangTimer.hasExpired())) { + m_controller->setActive(true); + } else { + m_controller->setActive(false); + m_activeHangTimer.stop(); + } + + // Check the shutdown state, state changes are done here to bypass the state machine which is + // frozen when m_disable or m_shutdown are asserted + m_disable = m_controller->getDisable(); + if (m_disable || m_shutdown) { + if (m_rptState != DSRS_SHUTDOWN) { + m_timeoutTimer.stop(); + m_watchdogTimer.stop(); + m_activeHangTimer.stop(); + m_ackTimer.stop(); + m_beaconTimer.stop(); + m_announcementTimer.stop(); + m_localQueue.reset(); + m_radioQueue.reset(); + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + m_networkQueue[i]->reset(); + m_controller->setActive(false); + m_controller->setRadioTransmit(false); + m_rptState = DSRS_SHUTDOWN; + } + } else { + if (m_rptState == DSRS_SHUTDOWN) { + m_timeoutTimer.stop(); + m_watchdogTimer.stop(); + m_ackTimer.stop(); + m_beaconTimer.start(); + m_announcementTimer.start(); + m_rptState = DSRS_LISTENING; + if (m_protocolHandler != NULL) // Tell the protocol handler + m_protocolHandler->reset(); + } + } + + if (m_radioQueue.dataReady()) + transmitRadioData(); + else if (m_localQueue.dataReady()) + transmitLocalData(); + else if (m_networkQueue[m_readNum]->dataReady()) + transmitNetworkData(); + else if (m_radioQueue.headerReady()) + transmitRadioHeader(); + else if (m_localQueue.headerReady()) + transmitLocalHeader(); + else if (m_networkQueue[m_readNum]->headerReady()) + transmitNetworkHeader(); + + m_controller->setRadioTransmit(m_tx); + + unsigned long ms = stopWatch.Time(); + if (ms < CYCLE_TIME) { + ::wxMilliSleep(CYCLE_TIME - ms); + clock(CYCLE_TIME); + } else { + clock(ms); + } + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised")); + } + + wxLogMessage(wxT("Stopping the D-Star repeater thread")); + + m_modem->stop(); + + if (m_logging != NULL) { + m_logging->close(); + delete m_logging; + } + + delete m_beacon; + delete m_announcement; + + delete m_whiteList; + delete m_blackList; + delete m_greyList; + + m_controller->setActive(false); + m_controller->setRadioTransmit(false); + m_controller->close(); + delete m_controller; + + if (m_protocolHandler != NULL) { + m_protocolHandler->close(); + delete m_protocolHandler; + } + + return NULL; +} + +void CDStarRepeaterTRXThread::kill() +{ + m_killed = true; +} + +void CDStarRepeaterTRXThread::setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) +{ + // Pad the callsign up to eight characters + m_rptCallsign = callsign; + m_rptCallsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + + if (gateway.IsEmpty()) { + m_gwyCallsign = callsign; + m_gwyCallsign.resize(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + m_gwyCallsign.Append(wxT("G")); + } else { + m_gwyCallsign = gateway; + m_gwyCallsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + } + + m_mode = mode; + m_ack = ack; + m_restriction = restriction; + m_rpt1Validation = rpt1Validation; + m_blanking = dtmfBlanking; + m_errorReply = errorReply; +} + +void CDStarRepeaterTRXThread::setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) +{ + wxASSERT(handler != NULL); + + m_protocolHandler = handler; +} + +void CDStarRepeaterTRXThread::setModem(CModem* modem) +{ + wxASSERT(modem != NULL); + + m_modem = modem; +} + +void CDStarRepeaterTRXThread::setTimes(unsigned int timeout, unsigned int ackTime) +{ + m_timeoutTimer.setTimeout(timeout); + m_ackTimer.setTimeout(0U, ackTime); +} + +void CDStarRepeaterTRXThread::setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language) +{ + m_beaconTimer.setTimeout(time); + + if (time > 0U) + m_beacon = new CBeaconUnit(this, m_rptCallsign, text, voice, language); +} + +void CDStarRepeaterTRXThread::setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2) +{ + if (enabled && time > 0U) { + m_announcement = new CAnnouncementUnit(this, m_rptCallsign); + + m_announcementTimer.setTimeout(time); + + m_recordRPT1 = recordRPT1; + m_recordRPT2 = recordRPT2; + m_deleteRPT1 = deleteRPT1; + m_deleteRPT2 = deleteRPT2; + + m_recordRPT1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_recordRPT2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_deleteRPT1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_deleteRPT2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + + m_recordRPT1.Truncate(LONG_CALLSIGN_LENGTH); + m_recordRPT2.Truncate(LONG_CALLSIGN_LENGTH); + m_deleteRPT1.Truncate(LONG_CALLSIGN_LENGTH); + m_deleteRPT2.Truncate(LONG_CALLSIGN_LENGTH); + } +} + +void CDStarRepeaterTRXThread::setController(CExternalController* controller, unsigned int activeHangTime) +{ + wxASSERT(controller != NULL); + + m_controller = controller; + m_activeHangTimer.setTimeout(activeHangTime); +} + +void CDStarRepeaterTRXThread::setControl(bool enabled, + const wxString& rpt1Callsign, const wxString& rpt2Callsign, + const wxString& shutdown,const wxString& startup, + const wxArrayString& command, const wxArrayString& status, + const wxArrayString& outputs) +{ + m_controlEnabled = enabled; + + m_controlRPT1 = rpt1Callsign; + m_controlRPT2 = rpt2Callsign; + + m_controlShutdown = shutdown; + m_controlStartup = startup; + + m_controlStatus = status; + m_controlCommand = command; + m_controlOutput = outputs; + + for(int i = 0; i < m_controlCommand.GetCount(); ++i) { + m_controlCommand[i].Append(' ', LONG_CALLSIGN_LENGTH); + m_controlCommand[i].Truncate(LONG_CALLSIGN_LENGTH); + } + + for(int i = 0; i < m_controlStatus.GetCount(); ++i) { + m_controlStatus[i].Append(' ', LONG_CALLSIGN_LENGTH); + m_controlStatus[i].Truncate(LONG_CALLSIGN_LENGTH); + } + + for(int i = 0; i < m_controlOutput.GetCount(); ++i) { + m_controlOutput[i].Append(' ', LONG_CALLSIGN_LENGTH); + m_controlOutput[i].Truncate(LONG_CALLSIGN_LENGTH); + } + + m_controlRPT1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_controlRPT2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_controlShutdown.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_controlStartup.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + + m_controlRPT1.Truncate(LONG_CALLSIGN_LENGTH); + m_controlRPT2.Truncate(LONG_CALLSIGN_LENGTH); + m_controlShutdown.Truncate(LONG_CALLSIGN_LENGTH); + m_controlStartup.Truncate(LONG_CALLSIGN_LENGTH); +} + +void CDStarRepeaterTRXThread::setOutputs(bool out1, bool out2, bool out3, bool out4) +{ + if (m_controller == NULL) + return; + + m_output[0] = out1; + m_output[1] = out2; + m_output[2] = out3; + m_output[3] = out4; + + m_controller->setOutput1(m_output[0]); + m_controller->setOutput2(m_output[1]); + m_controller->setOutput3(m_output[2]); + m_controller->setOutput4(m_output[3]); +} + +void CDStarRepeaterTRXThread::setLogging(bool logging, const wxString& dir) +{ + if (logging && m_logging == NULL) { + m_logging = new CDVTOOLFileWriter; + m_logging->setDirectory(dir); + return; + } + + if (!logging && m_logging != NULL) { + delete m_logging; + m_logging = NULL; + return; + } +} + +void CDStarRepeaterTRXThread::setWhiteList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_whiteList = list; +} + +void CDStarRepeaterTRXThread::setBlackList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_blackList = list; +} + +void CDStarRepeaterTRXThread::setGreyList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_greyList = list; +} + +void CDStarRepeaterTRXThread::receiveModem() +{ + for (;;) { + DSMT_TYPE type = m_modem->read(); + if (type == DSMTT_NONE) + return; + + switch (m_rxState) { + case DSRXS_LISTENING: + if (type == DSMTT_HEADER) { + CHeaderData* header = m_modem->readHeader(); + receiveHeader(header); + } else if (type == DSMTT_DATA) { + unsigned char data[20U]; + unsigned int length = m_modem->readData(data, 20U); + setRadioState(DSRXS_PROCESS_SLOW_DATA); + receiveSlowData(data, length); + } + break; + + case DSRXS_PROCESS_SLOW_DATA: + if (type == DSMTT_DATA) { + unsigned char data[20U]; + unsigned int length = m_modem->readData(data, 20U); + receiveSlowData(data, length); + } else if (type == DSMTT_EOT || type == DSMTT_LOST) { + setRadioState(DSRXS_LISTENING); + } + break; + + case DSRXS_PROCESS_DATA: + if (type == DSMTT_DATA) { + unsigned char data[20U]; + unsigned int length = m_modem->readData(data, 20U); + receiveRadioData(data, length); + } else if (type == DSMTT_EOT || type == DSMTT_LOST) { + unsigned char data[20U]; + ::memcpy(data, END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + processRadioFrame(data, FRAME_END); + setRadioState(DSRXS_LISTENING); + endOfRadioData(); + } + break; + } + } +} + +void CDStarRepeaterTRXThread::receiveHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + wxLogMessage(wxT("Radio header decoded - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + bool res = processRadioHeader(header); + if (res) { + // A valid header and is a DV packet + m_radioSeqNo = 20U; + setRadioState(DSRXS_PROCESS_DATA); + } else { + // This is a DD packet or some other problem + // wxLogMessage(wxT("Invalid header")); + } +} + +void CDStarRepeaterTRXThread::receiveSlowData(unsigned char* data, unsigned int) +{ + unsigned int errs; + errs = countBits(data[VOICE_FRAME_LENGTH_BYTES + 0U] ^ DATA_SYNC_BYTES[0U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 1U] ^ DATA_SYNC_BYTES[1U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 2U] ^ DATA_SYNC_BYTES[2U]); + + // The data sync has been seen, a fuzzy match is used, two bit errors or less + if (errs <= MAX_DATA_SYNC_BIT_ERRS) { + // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + m_radioSeqNo = 0U; + m_slowDataDecoder.sync(); + } else if (m_radioSeqNo == 20U) { + // wxLogMessage(wxT("Assuming data sync")); + m_radioSeqNo = 0U; + m_slowDataDecoder.sync(); + } else { + m_radioSeqNo++; + m_slowDataDecoder.addData(data + VOICE_FRAME_LENGTH_BYTES); + + CHeaderData* header = m_slowDataDecoder.getHeaderData(); + if (header == NULL) + return; + + wxLogMessage(wxT("Radio header from slow data - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X BER: 0%%"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + if (header != NULL) { + bool res = processRadioHeader(header); + if (res) { + // A valid header and is a DV packet, go to normal data relaying + setRadioState(DSRXS_PROCESS_DATA); + } else { + // This is a DD packet or some other problem + // wxLogMessage(wxT("Invalid header")); + } + } + } +} + +void CDStarRepeaterTRXThread::receiveRadioData(unsigned char* data, unsigned int) +{ + unsigned int errs; + errs = countBits(data[VOICE_FRAME_LENGTH_BYTES + 0U] ^ DATA_SYNC_BYTES[0U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 1U] ^ DATA_SYNC_BYTES[1U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 2U] ^ DATA_SYNC_BYTES[2U]); + + // The data sync has been seen, a fuzzy match is used, two bit errors or less + if (errs <= MAX_DATA_SYNC_BIT_ERRS) { + // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + m_radioSeqNo = 0U; + processRadioFrame(data, FRAME_SYNC); + } else if (m_radioSeqNo == 20U) { + // wxLogMessage(wxT("Regenerating data sync")); + m_radioSeqNo = 0U; + processRadioFrame(data, FRAME_SYNC); + } else { + m_radioSeqNo++; + processRadioFrame(data, FRAME_NORMAL); + } +} + +void CDStarRepeaterTRXThread::receiveNetwork() +{ + if (m_protocolHandler == NULL) + return; + + NETWORK_TYPE type; + + for (;;) { + type = m_protocolHandler->read(); + + // Get the data from the network + if (type == NETWORK_NONE) { // Nothing received + break; + } else if (type == NETWORK_HEADER) { // A header + CHeaderData* header = m_protocolHandler->readHeader(); + if (header != NULL) { + ::memcpy(m_lastData, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + + processNetworkHeader(header); + + m_headerTime.Start(); + m_packetTime.Start(); + m_packetCount = 0U; + m_packetSilence = 0U; + } + } else if (type == NETWORK_DATA) { // AMBE data and slow data + unsigned char data[2U * DV_FRAME_MAX_LENGTH_BYTES]; + ::memset(data, 0x00U, 2U * DV_FRAME_MAX_LENGTH_BYTES); + + unsigned char seqNo; + unsigned int length = m_protocolHandler->readData(data, DV_FRAME_MAX_LENGTH_BYTES, seqNo); + if (length != 0U) { + ::memcpy(m_lastData, data, length); + m_watchdogTimer.start(); + m_packetCount += processNetworkFrame(data, length, seqNo); + } + } else if (type == NETWORK_TEXT) { // Slow data text for the Ack + m_protocolHandler->readText(m_ackText, m_linkStatus, m_reflector); + m_linkEncoder.setTextData(m_ackText); + wxLogMessage(wxT("Slow data set to \"%s\""), m_ackText.c_str()); + } else if (type == NETWORK_TEMPTEXT) { // Temporary slow data text for the Ack + m_protocolHandler->readTempText(m_tempAckText); + wxLogMessage(wxT("Temporary slow data set to \"%s\""), m_tempAckText.c_str()); + } else if (type == NETWORK_STATUS1) { // Status 1 data text + m_statusText[0] = m_protocolHandler->readStatus1(); + m_status1Encoder.setTextData(m_statusText[0]); + wxLogMessage(wxT("Status 1 data set to \"%s\""), m_statusText[0].c_str()); + } else if (type == NETWORK_STATUS2) { // Status 2 data text + m_statusText[1] = m_protocolHandler->readStatus2(); + m_status2Encoder.setTextData(m_statusText[1]); + wxLogMessage(wxT("Status 2 data set to \"%s\""), m_statusText[1].c_str()); + } else if (type == NETWORK_STATUS3) { // Status 3 data text + m_statusText[2] = m_protocolHandler->readStatus3(); + m_status3Encoder.setTextData(m_statusText[2]); + wxLogMessage(wxT("Status 3 data set to \"%s\""), m_statusText[2].c_str()); + } else if (type == NETWORK_STATUS4) { // Status 4 data text + m_statusText[3] = m_protocolHandler->readStatus4(); + m_status4Encoder.setTextData(m_statusText[3]); + wxLogMessage(wxT("Status 4 data set to \"%s\""), m_statusText[3].c_str()); + } else if (type == NETWORK_STATUS5) { // Status 5 data text + m_statusText[4] = m_protocolHandler->readStatus5(); + m_status5Encoder.setTextData(m_statusText[4]); + wxLogMessage(wxT("Status 5 data set to \"%s\""), m_statusText[4].c_str()); + } + } + + // Have we missed any data frames? + if (m_rptState == DSRS_NETWORK && m_packetTime.Time() > 200L) { + unsigned int packetsNeeded = m_headerTime.Time() / DSTAR_FRAME_TIME_MS; + + // wxLogMessage(wxT("Time: %u ms, need %u packets and received %u packets"), ms - m_headerMS, packetsNeeded, m_packetCount); + + if (packetsNeeded > m_packetCount) { + unsigned int count = packetsNeeded - m_packetCount; + + if (count > 5U) { + count -= 2U; + + // wxLogMessage(wxT("Inserting %u silence frames into the network data stream"), count); + + // Create silence frames + for (unsigned int i = 0U; i < count; i++) { + unsigned char data[DV_FRAME_LENGTH_BYTES]; + ::memcpy(data, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + m_packetCount += processNetworkFrame(data, DV_FRAME_LENGTH_BYTES, m_networkSeqNo); + m_packetSilence++; + } + } + } + + m_packetTime.Start(); + } +} + +void CDStarRepeaterTRXThread::transmitLocalHeader(CHeaderData* header) +{ + wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + m_headerEncoder.setHeaderData(*header); + + m_localQueue.reset(); + m_localQueue.setHeader(header); +} + +void CDStarRepeaterTRXThread::transmitBeaconHeader() +{ + CHeaderData* header = new CHeaderData(m_rptCallsign, wxT("RPTR"), wxT("CQCQCQ "), m_gwyCallsign, m_rptCallsign); + transmitLocalHeader(header); +} + +void CDStarRepeaterTRXThread::transmitBeaconData(const unsigned char* data, unsigned int length, bool end) +{ + m_localQueue.addData(data, length, end); +} + +void CDStarRepeaterTRXThread::transmitAnnouncementHeader(CHeaderData* header) +{ + header->setRptCall1(m_gwyCallsign); + header->setRptCall2(m_rptCallsign); + + transmitLocalHeader(header); +} + +void CDStarRepeaterTRXThread::transmitAnnouncementData(const unsigned char* data, unsigned int length, bool end) +{ + m_localQueue.addData(data, length, end); +} + +void CDStarRepeaterTRXThread::transmitRadioHeader(CHeaderData* header) +{ + wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + m_headerEncoder.setHeaderData(*header); + + m_radioQueue.reset(); + m_radioQueue.setHeader(header); +} + +void CDStarRepeaterTRXThread::transmitNetworkHeader(CHeaderData* header) +{ + wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + m_headerEncoder.setHeaderData(*header); + + bool empty = m_networkQueue[m_readNum]->isEmpty(); + if (!empty) { + bool headerReady = m_networkQueue[m_readNum]->headerReady(); + if (headerReady) { + // Transmission has never started, so just purge the queue + m_networkQueue[m_readNum]->reset(); + + m_readNum++; + if (m_readNum >= NETWORK_QUEUE_COUNT) + m_readNum = 0U; + } else { + // Append an end of stream + m_networkQueue[m_readNum]->reset(); + m_networkQueue[m_readNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + } + } + + m_networkQueue[m_writeNum]->reset(); + m_networkQueue[m_writeNum]->setHeader(header); +} + +void CDStarRepeaterTRXThread::transmitStatus() +{ + CHeaderData* header = new CHeaderData(m_rptCallsign, wxT(" "), m_rxHeader->getMyCall1(), m_gwyCallsign, m_rptCallsign, RELAY_UNAVAILABLE); + transmitLocalHeader(header); + + // Filler data + for (unsigned int i = 0U; i < 21U; i++) { + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + + if (i == 0U) { + m_ackEncoder.sync(); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + } else { + unsigned char text[DATA_FRAME_LENGTH_BYTES]; + m_ackEncoder.getTextData(text); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, text, DATA_FRAME_LENGTH_BYTES); + } + + m_localQueue.addData(buffer, DV_FRAME_LENGTH_BYTES, false); + } + + m_localQueue.addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); +} + +void CDStarRepeaterTRXThread::transmitErrorStatus() +{ + CHeaderData* header = new CHeaderData(m_rptCallsign, wxT(" "), m_rxHeader->getMyCall1(), m_rptCallsign, m_rptCallsign, RELAY_UNAVAILABLE); + transmitLocalHeader(header); + + // Filler data + for (unsigned int i = 0U; i < 21U; i++) { + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + + if (i == 0U) { + m_linkEncoder.sync(); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + } else { + unsigned char text[DATA_FRAME_LENGTH_BYTES]; + m_linkEncoder.getTextData(text); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, text, DATA_FRAME_LENGTH_BYTES); + } + + m_localQueue.addData(buffer, DV_FRAME_LENGTH_BYTES, false); + } + + m_localQueue.addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); +} + +void CDStarRepeaterTRXThread::transmitUserStatus(unsigned int n) +{ + CSlowDataEncoder* encoder = NULL; + CHeaderData* header = NULL; + switch (n) { + case 0U: + header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 1"), m_gwyCallsign, m_rptCallsign); + encoder = &m_status1Encoder; + break; + case 1U: + header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 2"), m_gwyCallsign, m_rptCallsign); + encoder = &m_status2Encoder; + break; + case 2U: + header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 3"), m_gwyCallsign, m_rptCallsign); + encoder = &m_status3Encoder; + break; + case 3U: + header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 4"), m_gwyCallsign, m_rptCallsign); + encoder = &m_status4Encoder; + break; + case 4U: + header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 5"), m_gwyCallsign, m_rptCallsign); + encoder = &m_status5Encoder; + break; + default: + wxLogWarning(wxT("Invalid status number - %u"), n); + return; + } + + transmitLocalHeader(header); + + // Filler data + for (unsigned int i = 0U; i < 21U; i++) { + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + + if (i == 0U) { + encoder->sync(); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + } else { + unsigned char text[DATA_FRAME_LENGTH_BYTES]; + encoder->getTextData(text); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, text, DATA_FRAME_LENGTH_BYTES); + } + + m_localQueue.addData(buffer, DV_FRAME_LENGTH_BYTES, false); + } + + m_localQueue.addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); +} + +void CDStarRepeaterTRXThread::transmitLocalHeader() +{ + // Don't send a header until the modem is ready + bool ready = m_modem->isTXReady(); + if (!ready) + return; + + CHeaderData* header = m_localQueue.getHeader(); + if (header == NULL) + return; + + m_modem->writeHeader(*header); + delete header; +} + +void CDStarRepeaterTRXThread::transmitLocalData() +{ + if (m_space == 0U) + return; + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + bool end; + unsigned int length = m_localQueue.getData(buffer, DV_FRAME_LENGTH_BYTES, end); + + if (length == 0U) + return; + + m_modem->writeData(buffer, length, end); + m_space--; + + if (end) + m_localQueue.reset(); +} + +void CDStarRepeaterTRXThread::transmitRadioHeader() +{ + // Don't send a header until the modem is ready + bool ready = m_modem->isTXReady(); + if (!ready) + return; + + CHeaderData* header = m_radioQueue.getHeader(); + if (header == NULL) + return; + + m_modem->writeHeader(*header); + delete header; +} + +void CDStarRepeaterTRXThread::transmitRadioData() +{ + if (m_space == 0U) + return; + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + bool end; + unsigned int length = m_radioQueue.getData(buffer, DV_FRAME_LENGTH_BYTES, end); + + if (length == 0U) + return; + + m_modem->writeData(buffer, length, end); + m_space--; + + if (end) + m_radioQueue.reset(); +} + +void CDStarRepeaterTRXThread::transmitNetworkHeader() +{ + // Don't send a header until the modem is ready + bool ready = m_modem->isTXReady(); + if (!ready) + return; + + CHeaderData* header = m_networkQueue[m_readNum]->getHeader(); + if (header == NULL) + return; + + m_modem->writeHeader(*header); + delete header; +} + +void CDStarRepeaterTRXThread::transmitNetworkData() +{ + if (m_space == 0U) + return; + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + bool end; + unsigned int length = m_networkQueue[m_readNum]->getData(buffer, DV_FRAME_LENGTH_BYTES, end); + + if (length == 0U) + return; + + m_modem->writeData(buffer, length, end); + m_space--; + + if (end) { + m_networkQueue[m_readNum]->reset(); + + m_readNum++; + if (m_readNum >= NETWORK_QUEUE_COUNT) + m_readNum = 0U; + } +} + +void CDStarRepeaterTRXThread::repeaterStateMachine() +{ + switch (m_rptState) { + case DSRS_VALID: + if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) { + wxLogMessage(wxT("User has timed out")); + setRepeaterState(DSRS_TIMEOUT); + } + break; + + case DSRS_VALID_WAIT: + if (m_ackTimer.hasExpired()) { + if (m_mode != MODE_GATEWAY) + transmitStatus(); + setRepeaterState(DSRS_LISTENING); + m_activeHangTimer.start(); + } + break; + + case DSRS_INVALID_WAIT: + if (m_ackTimer.hasExpired()) { + if (m_mode != MODE_GATEWAY && m_errorReply) + transmitErrorStatus(); + setRepeaterState(DSRS_LISTENING); + m_activeHangTimer.start(); + } + break; + + case DSRS_TIMEOUT_WAIT: + if (m_ackTimer.hasExpired()) { + if (m_mode != MODE_GATEWAY) + transmitStatus(); + setRepeaterState(DSRS_LISTENING); + m_activeHangTimer.start(); + } + break; + + case DSRS_NETWORK: + if (m_watchdogTimer.hasExpired()) { + wxLogMessage(wxT("Network watchdog has expired")); + // Send end of transmission data to the radio + m_networkQueue[m_writeNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + endOfNetworkData(); + } + break; + + default: + break; + } +} + +void CDStarRepeaterTRXThread::setRadioState(DSTAR_RX_STATE state) +{ + // This is the too state + switch (state) { + case DSRXS_LISTENING: + m_rxState = DSRXS_LISTENING; + break; + + case DSRXS_PROCESS_DATA: + m_ambeFrames = 0U; + m_ambeSilence = 0U; + m_ambeBits = 1U; + m_ambeErrors = 0U; + m_lastAMBEBits = 0U; + m_lastAMBEErrors = 0U; + m_rxState = DSRXS_PROCESS_DATA; + break; + + case DSRXS_PROCESS_SLOW_DATA: + m_radioSeqNo = 0U; + m_slowDataDecoder.reset(); + m_rxState = DSRXS_PROCESS_SLOW_DATA; + break; + } +} + +bool CDStarRepeaterTRXThread::setRepeaterState(DSTAR_RPT_STATE state) +{ + // Can't change state when shutdown + if (m_disable || m_shutdown) + return false; + + // The "from" state + switch (m_rptState) { + case DSRS_SHUTDOWN: + m_beaconTimer.start(); + m_announcementTimer.start(); + break; + + case DSRS_LISTENING: + m_beaconTimer.stop(); + break; + + default: + break; + } + + // The "to" state + switch (state) { + case DSRS_SHUTDOWN: + m_timeoutTimer.stop(); + m_watchdogTimer.stop(); + m_activeHangTimer.stop(); + m_ackTimer.stop(); + m_beaconTimer.stop(); + m_announcementTimer.stop(); + m_controller->setActive(false); + m_controller->setRadioTransmit(false); + m_rptState = DSRS_SHUTDOWN; + break; + + case DSRS_LISTENING: + m_timeoutTimer.stop(); + m_watchdogTimer.stop(); + m_ackTimer.stop(); + m_beaconTimer.start(); + m_rptState = DSRS_LISTENING; + if (m_protocolHandler != NULL) // Tell the protocol handler + m_protocolHandler->reset(); + break; + + case DSRS_VALID: + if (m_rptState != DSRS_LISTENING && m_rptState != DSRS_VALID_WAIT) + return false; + + if (m_rptState == DSRS_LISTENING) + m_timeoutTimer.start(); + else + m_ackTimer.stop(); + + m_activeHangTimer.stop(); + m_rptState = DSRS_VALID; + break; + + case DSRS_VALID_WAIT: + m_ackTimer.start(); + m_rptState = DSRS_VALID_WAIT; + break; + + case DSRS_TIMEOUT: + // Send end of transmission data to the radio and the network + if (m_mode == MODE_DUPLEX) + m_radioQueue.addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + + if (!m_blocked && m_protocolHandler != NULL) { + unsigned char bytes[DV_FRAME_MAX_LENGTH_BYTES]; + ::memcpy(bytes, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(bytes + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + m_protocolHandler->writeData(bytes, DV_FRAME_MAX_LENGTH_BYTES, 0U, true); + } + + m_timeoutTimer.stop(); + m_rptState = DSRS_TIMEOUT; + break; + + case DSRS_TIMEOUT_WAIT: + m_ackTimer.start(); + m_timeoutTimer.stop(); + m_rptState = DSRS_TIMEOUT_WAIT; + break; + + case DSRS_INVALID: + if (m_rptState != DSRS_LISTENING) + return false; + + m_activeHangTimer.stop(); + m_timeoutTimer.stop(); + m_rptState = DSRS_INVALID; + break; + + case DSRS_INVALID_WAIT: + m_ackTimer.start(); + m_timeoutTimer.stop(); + m_rptState = DSRS_INVALID_WAIT; + break; + + case DSRS_NETWORK: + if (m_rptState != DSRS_LISTENING && m_rptState != DSRS_VALID_WAIT && m_rptState != DSRS_INVALID_WAIT && m_rptState != DSRS_TIMEOUT_WAIT) + return false; + + m_rptState = DSRS_NETWORK; + m_networkSeqNo = 0U; + m_timeoutTimer.stop(); + m_watchdogTimer.start(); + m_activeHangTimer.stop(); + m_ackTimer.stop(); + break; + } + + return true; +} + +bool CDStarRepeaterTRXThread::processRadioHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + // Check control messages + bool res = checkControl(*header); + if (res) { + delete header; + return true; + } + + // Check announcement messages + res = checkAnnouncements(*header); + if (res) { + bool res = setRepeaterState(DSRS_INVALID); + if (res) { + delete m_rxHeader; + m_rxHeader = header; + } else { + delete header; + } + return true; + } + + // If shutdown we ignore incoming headers + if (m_rptState == DSRS_SHUTDOWN) { + delete header; + return true; + } + + if (m_whiteList != NULL) { + bool res = m_whiteList->isInList(header->getMyCall1()); + if (!res) { + wxLogMessage(wxT("%s rejected due to not being in the white list"), header->getMyCall1().c_str()); + delete header; + return true; + } + } + + if (m_blackList != NULL) { + bool res = m_blackList->isInList(header->getMyCall1()); + if (res) { + wxLogMessage(wxT("%s rejected due to being in the black list"), header->getMyCall1().c_str()); + delete header; + return true; + } + } + + m_blocked = false; + if (m_greyList != NULL) { + bool res = m_greyList->isInList(header->getMyCall1()); + if (res) { + wxLogMessage(wxT("%s blocked from the network due to being in the grey list"), header->getMyCall1().c_str()); + m_blocked = true; + } + } + + // Check for receiving our own gateway data, and ignore it + if (m_mode == MODE_GATEWAY) { + if (header->getFlag2() == 0x01U) { + wxLogMessage(wxT("Receiving a gateway header, ignoring")); + delete header; + return true; + } + + header->setFlag2(0x00U); + } + + // We don't handle DD data packets + if (header->isDataPacket()) { + wxLogMessage(wxT("Received a DD packet, ignoring")); + delete header; + return false; + } + + TRISTATE valid = checkHeader(*header); + switch (valid) { + case STATE_FALSE: { + bool res = setRepeaterState(DSRS_INVALID); + if (res) { + delete m_rxHeader; + m_rxHeader = header; + } else { + delete header; + } + } + return true; + + case STATE_UNKNOWN: + delete header; + return true; + + case STATE_TRUE: + break; + } + + // If we're in network mode, send the header as a busy header to the gateway in case it's an unlink + // command + if (m_rptState == DSRS_NETWORK) { + // Only send on the network if the user isn't blocked we have one and RPT2 is not blank or the repeater callsign + if (!header->getRptCall2().IsSameAs(wxT(" ")) && !header->getRptCall2().IsSameAs(m_rptCallsign)) { + if (!m_blocked && m_protocolHandler != NULL) { + CHeaderData netHeader(*header); + netHeader.setRptCall1(header->getRptCall2()); + netHeader.setRptCall2(header->getRptCall1()); + netHeader.setFlag1(header->getFlag1() & ~REPEATER_MASK); + m_protocolHandler->writeBusyHeader(netHeader); + } + + m_busyData = true; + } + + delete header; + + return true; + } + + // Send the valid header to the gateway if we are accepted + res = setRepeaterState(DSRS_VALID); + if (res) { + delete m_rxHeader; + m_rxHeader = header; + + if (m_logging != NULL) + m_logging->open(*m_rxHeader); + + // Only send on the network if the user isn't blocked and we have one and RPT2 is not blank or the repeater callsign + if (!m_blocked && m_protocolHandler != NULL && !m_rxHeader->getRptCall2().IsSameAs(wxT(" ")) && !m_rxHeader->getRptCall2().IsSameAs(m_rptCallsign)) { + CHeaderData netHeader(*m_rxHeader); + netHeader.setRptCall1(m_rxHeader->getRptCall2()); + netHeader.setRptCall2(m_rxHeader->getRptCall1()); + netHeader.setFlag1(m_rxHeader->getFlag1() & ~REPEATER_MASK); + m_protocolHandler->writeHeader(netHeader); + } + + // Create the new radio header but only in duplex mode + if (m_mode == MODE_DUPLEX) { + CHeaderData* rfHeader = new CHeaderData(*m_rxHeader); + rfHeader->setRptCall1(m_rptCallsign); + rfHeader->setRptCall2(m_rptCallsign); + rfHeader->setFlag1(m_rxHeader->getFlag1() & ~REPEATER_MASK); + transmitRadioHeader(rfHeader); + } + } + + return true; +} + +void CDStarRepeaterTRXThread::processNetworkHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + // If shutdown we ignore incoming headers + if (m_rptState == DSRS_SHUTDOWN) { + delete header; + return; + } + + wxLogMessage(wxT("Network header received - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + // Is it for us? + if (!header->getRptCall2().IsSameAs(m_rptCallsign)) { + wxLogMessage(wxT("Invalid network RPT2 value, ignoring")); + delete header; + return; + } + + bool res = setRepeaterState(DSRS_NETWORK); + if (!res) { + delete header; + return; + } + + delete m_rxHeader; + m_rxHeader = header; + + if (m_mode == MODE_GATEWAY) { + // If in gateway mode, set the repeater bit, set flag 2 to 0x01, + // and change RPT1 & RPT2 just for transmission + CHeaderData* header = new CHeaderData(*m_rxHeader); + header->setRepeaterMode(true); + header->setFlag2(0x01U); + header->setRptCall1(m_rptCallsign); + header->setRptCall2(m_gwyCallsign); + transmitNetworkHeader(header); + } else { + CHeaderData* header = new CHeaderData(*m_rxHeader); + transmitNetworkHeader(header); + } +} + +void CDStarRepeaterTRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE type) +{ + m_ambeFrames++; + + // If a sync frame, regenerate the sync bytes + if (type == FRAME_SYNC) + ::memcpy(data + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + + // Only regenerate the AMBE on received radio data + unsigned int errors = 0U; + if (type != FRAME_END) { + // Data packets have no AMBE FEC + if (!m_rxHeader->isDataPacket()) + errors = m_ambe.regenerate(data); + + m_ambeErrors += errors; + m_ambeBits += 48U; // Only count the bits with FEC added + } + + if (::memcmp(data, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES) == 0) + m_ambeSilence++; + + // If this is deleting an announcement, ignore the audio + if (m_deleting) { + if (type == FRAME_END) { + m_deleting = false; + m_recording = false; + } + return; + } + + // If this is recording an announcement, send the audio to the announcement unit and then stop + if (m_recording) { + m_announcement->writeData(data, DV_FRAME_LENGTH_BYTES, type == FRAME_END); + if (type == FRAME_END) { + m_deleting = false; + m_recording = false; + } + return; + } + + // Pass background AMBE data to the network + if (m_busyData) { + if (type == FRAME_END) { + if (!m_blocked && m_protocolHandler != NULL) { + unsigned char bytes[DV_FRAME_MAX_LENGTH_BYTES]; + ::memcpy(bytes, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(bytes + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + m_protocolHandler->writeBusyData(bytes, DV_FRAME_MAX_LENGTH_BYTES, 0U, true); + } + + m_busyData = false; + } else { + if (!m_blocked && m_protocolHandler != NULL) + m_protocolHandler->writeBusyData(data, DV_FRAME_LENGTH_BYTES, errors, false); + } + + return; + } + + // Don't pass through the frame of an invalid transmission + if (m_rptState != DSRS_VALID) + return; + + if (type == FRAME_END) { + if (m_logging != NULL) + m_logging->close(); + + // Transmit the end sync on the radio, but only in duplex mode + if (m_mode == MODE_DUPLEX) + m_radioQueue.addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + + // Send null data and the end marker over the network, and the statistics + if (!m_blocked && m_protocolHandler != NULL) { + unsigned char bytes[DV_FRAME_MAX_LENGTH_BYTES]; + ::memcpy(bytes, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(bytes + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + m_protocolHandler->writeData(bytes, DV_FRAME_MAX_LENGTH_BYTES, 0U, true); + } + } else { + if (m_logging != NULL) + m_logging->write(data, DV_FRAME_LENGTH_BYTES); + + // Send the data to the network + if (!m_blocked && m_protocolHandler != NULL) + m_protocolHandler->writeData(data, DV_FRAME_LENGTH_BYTES, errors, false); + + // Send the data for transmission, but only in duplex mode + if (m_mode == MODE_DUPLEX) { + if (m_blanking) + blankDTMF(data); + m_radioQueue.addData(data, DV_FRAME_LENGTH_BYTES, false); + } + } +} + +unsigned int CDStarRepeaterTRXThread::processNetworkFrame(unsigned char* data, unsigned int length, unsigned char seqNo) +{ + wxASSERT(data != NULL); + wxASSERT(length > 0U); + + if (m_rptState != DSRS_NETWORK) + return 0U; + + bool end = (seqNo & 0x40U) == 0x40U; + if (end) { + m_networkQueue[m_writeNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + endOfNetworkData(); + return 1U; + } + + // Mask out the control bits of the sequence number + seqNo &= 0x1FU; + + // Count the number of silence frames to insert + unsigned int tempSeqNo = m_networkSeqNo; + unsigned int count = 0U; + while (seqNo != tempSeqNo) { + count++; + + tempSeqNo++; + if (tempSeqNo >= 21U) + tempSeqNo = 0U; + } + + // If the number is too high, then it probably means an old out-of-order frame, ignore it + if (count > 18U) + return 0U; + + unsigned int packetCount = 0U; + + // Insert missing frames + while (seqNo != m_networkSeqNo) { + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + if (count > SILENCE_THRESHOLD) { + ::memcpy(buffer, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + } else { + ::memcpy(buffer, m_lastData, DV_FRAME_LENGTH_BYTES); + // Data packets have no AMBE FEC + if (!m_rxHeader->isDataPacket()) + m_ambe.regenerate(buffer); + blankDTMF(buffer); + } + + if (m_networkSeqNo == 0U) + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + + m_networkQueue[m_writeNum]->addData(buffer, DV_FRAME_LENGTH_BYTES, false); + + packetCount++; + m_networkSeqNo++; + m_packetSilence++; + if (m_networkSeqNo >= 21U) + m_networkSeqNo = 0U; + } + + // Regenerate the sync bytes + if (m_networkSeqNo == 0U) { + ::memcpy(data + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + m_headerEncoder.sync(); + } else { + // If in Gateway mode, replace the slow data with our modified header when the slow data contains the + // original header + if (m_mode == MODE_GATEWAY) { + unsigned char dataType = 0U; + if ((m_networkSeqNo & 0x01U) == 0x01U) + dataType = (data[VOICE_FRAME_LENGTH_BYTES] ^ SCRAMBLER_BYTE1) & SLOW_DATA_TYPE_MASK; + + if (dataType == SLOW_DATA_TYPE_HEADER || m_lastSlowDataType == SLOW_DATA_TYPE_HEADER) { + unsigned char slowData[DATA_FRAME_LENGTH_BYTES]; + m_headerEncoder.getHeaderData(slowData); + ::memcpy(data + VOICE_FRAME_LENGTH_BYTES, slowData, DATA_FRAME_LENGTH_BYTES); + } + + if ((m_networkSeqNo & 0x01U) == 0x01U) + m_lastSlowDataType = dataType; + } + } + + packetCount++; + m_networkSeqNo++; + if (m_networkSeqNo >= 21U) + m_networkSeqNo = 0U; + + // Data packets have no AMBE FEC + if (!m_rxHeader->isDataPacket()) + m_ambe.regenerate(data); + + blankDTMF(data); + + m_networkQueue[m_writeNum]->addData(data, DV_FRAME_LENGTH_BYTES, false); + + return packetCount; +} + +void CDStarRepeaterTRXThread::endOfRadioData() +{ + switch (m_rptState) { + case DSRS_VALID: + wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + + if (m_tempAckText.IsEmpty()) { + if (m_ack == AT_BER) { + // Create the ack text with the linked reflector and BER + wxString ackText; + if (m_linkStatus == LS_LINKED_DEXTRA || m_linkStatus == LS_LINKED_DPLUS || m_linkStatus == LS_LINKED_DCS || m_linkStatus == LS_LINKED_CCS || m_linkStatus == LS_LINKED_LOOPBACK) + ackText.Printf(wxT("%-8s BER: %.1f%% "), m_reflector.c_str(), float(m_ambeErrors * 100U) / float(m_ambeBits)); + else + ackText.Printf(wxT("BER: %.1f%% "), float(m_ambeErrors * 100U) / float(m_ambeBits)); + m_ackEncoder.setTextData(ackText); + } else { + m_ackEncoder.setTextData(m_ackText); + } + } else { + m_ackEncoder.setTextData(m_tempAckText); + m_tempAckText.Clear(); + } + + if (m_ack != AT_NONE || m_mode == MODE_GATEWAY) { + setRepeaterState(DSRS_VALID_WAIT); + } else { + setRepeaterState(DSRS_LISTENING); + m_activeHangTimer.start(); + } + break; + + case DSRS_INVALID: + wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + + if (m_ack != AT_NONE || m_mode == MODE_GATEWAY) { + setRepeaterState(DSRS_INVALID_WAIT); + } else { + setRepeaterState(DSRS_LISTENING); + m_activeHangTimer.start(); + } + break; + + case DSRS_TIMEOUT: + wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + + if (m_tempAckText.IsEmpty()) { + if (m_ack == AT_BER) { + // Create the ack text with the linked reflector and BER + wxString ackText; + if (m_linkStatus == LS_LINKED_DEXTRA || m_linkStatus == LS_LINKED_DPLUS || m_linkStatus == LS_LINKED_DCS || m_linkStatus == LS_LINKED_CCS || m_linkStatus == LS_LINKED_LOOPBACK) + ackText.Printf(wxT("%-8s BER: %.1f%% "), m_reflector.c_str(), float(m_ambeErrors * 100U) / float(m_ambeBits)); + else + ackText.Printf(wxT("BER: %.1f%% "), float(m_ambeErrors * 100U) / float(m_ambeBits)); + m_ackEncoder.setTextData(ackText); + } else { + m_ackEncoder.setTextData(m_ackText); + } + } else { + m_ackEncoder.setTextData(m_tempAckText); + m_tempAckText.Clear(); + } + + if (m_ack != AT_NONE || m_mode == MODE_GATEWAY) { + setRepeaterState(DSRS_TIMEOUT_WAIT); + } else { + setRepeaterState(DSRS_LISTENING); + m_activeHangTimer.start(); + } + break; + + default: + break; + } +} + +void CDStarRepeaterTRXThread::endOfNetworkData() +{ + float loss = 0.0F; + if (m_packetCount != 0U) + loss = float(m_packetSilence) / float(m_packetCount); + + wxLogMessage(wxT("Stats for %s Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u"), m_rxHeader->getMyCall1().c_str(), float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); + + setRepeaterState(DSRS_LISTENING); + m_activeHangTimer.start(); + + m_writeNum++; + if (m_writeNum >= NETWORK_QUEUE_COUNT) + m_writeNum = 0U; +} + +CDStarRepeaterStatusData* CDStarRepeaterTRXThread::getStatus() +{ + CDStarRepeaterStatusData* status; + if (m_rptState == DSRS_SHUTDOWN || m_rptState == DSRS_LISTENING) { + status = new CDStarRepeaterStatusData(wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, + wxEmptyString, 0x00, 0x00, 0x00, m_tx, m_rxState, m_rptState, m_timeoutTimer.getTimer(), + m_timeoutTimer.getTimeout(), m_beaconTimer.getTimer(), m_beaconTimer.getTimeout(), 0.0F, + m_announcementTimer.getTimer(), m_announcementTimer.getTimeout(), m_ackText, m_statusText[0], + m_statusText[1], m_statusText[2], m_statusText[3], m_statusText[4]); + } else if (m_rptState == DSRS_NETWORK) { + float loss = 0.0F; + if (m_packetCount != 0U) + loss = float(m_packetSilence) / float(m_packetCount); + + status = new CDStarRepeaterStatusData(m_rxHeader->getMyCall1(), m_rxHeader->getMyCall2(), + m_rxHeader->getYourCall(), m_rxHeader->getRptCall1(), m_rxHeader->getRptCall2(), + m_rxHeader->getFlag1(), m_rxHeader->getFlag2(), m_rxHeader->getFlag3(), m_tx, m_rxState, + m_rptState, m_timeoutTimer.getTimer(), m_timeoutTimer.getTimeout(), m_beaconTimer.getTimer(), + m_beaconTimer.getTimeout(), m_announcementTimer.getTimer(), m_announcementTimer.getTimeout(), + loss * 100.0F, m_ackText, m_statusText[0], m_statusText[1], m_statusText[2], m_statusText[3], m_statusText[4]); + } else { + float bits = float(m_ambeBits - m_lastAMBEBits); + float errors = float(m_ambeErrors - m_lastAMBEErrors); + if (bits == 0.0F) + bits = 1.0F; + + m_lastAMBEBits = m_ambeBits; + m_lastAMBEErrors = m_ambeErrors; + + status = new CDStarRepeaterStatusData(m_rxHeader->getMyCall1(), m_rxHeader->getMyCall2(), + m_rxHeader->getYourCall(), m_rxHeader->getRptCall1(), m_rxHeader->getRptCall2(), + m_rxHeader->getFlag1(), m_rxHeader->getFlag2(), m_rxHeader->getFlag3(), m_tx, m_rxState, + m_rptState, m_timeoutTimer.getTimer(), m_timeoutTimer.getTimeout(), m_beaconTimer.getTimer(), + m_beaconTimer.getTimeout(), m_announcementTimer.getTimer(), m_announcementTimer.getTimeout(), + (errors * 100.0F) / bits, m_ackText, m_statusText[0], m_statusText[1], m_statusText[2], m_statusText[3], + m_statusText[4]); + } + + if (m_type.IsSameAs(wxT("DVAP")) && m_modem != NULL) { + CDVAPController* dvap = static_cast(m_modem); + bool squelch = dvap->getSquelch(); + int signal = dvap->getSignal(); + status->setDVAP(squelch, signal); + } + + return status; +} + +void CDStarRepeaterTRXThread::clock(unsigned int ms) +{ + m_pollTimer.clock(ms); + m_timeoutTimer.clock(ms); + m_watchdogTimer.clock(ms); + m_activeHangTimer.clock(ms); + m_ackTimer.clock(ms); + + for(int i = 0; i < 5; ++i) + m_statusAnnounceTimer[i].clock(ms); + + m_beaconTimer.clock(ms); + m_announcementTimer.clock(ms); + m_statusTimer.clock(ms); + m_heartbeatTimer.clock(ms); + if (m_beacon != NULL) + m_beacon->clock(); + if (m_announcement != NULL) + m_announcement->clock(); +} + +void CDStarRepeaterTRXThread::shutdown() +{ + m_shutdown = true; +} + +void CDStarRepeaterTRXThread::startup() +{ + m_shutdown = false; +} + +bool CDStarRepeaterTRXThread::checkControl(const CHeaderData& header) +{ + if (!m_controlEnabled) + return false; + + if (!m_controlRPT1.IsSameAs(header.getRptCall1()) || !m_controlRPT2.IsSameAs(header.getRptCall2())) + return false; + + for(int i = 0; i < m_controlCommand.GetCount(); ++i) { + if(m_controlCommand[i].IsSameAs(header.getYourCall())) { + wxThreadEvent evt(wxEVT_THREAD, wxEVT_THREAD_COMMAND); + evt.SetInt(i); + wxTheApp->QueueEvent(evt.Clone()); + wxLogMessage("Command %d requested by %s/%s", i, header.getMyCall1().c_str(), header.getMyCall2().c_str()); + return true; + } + } + + for(int i = 0; i < m_controlStatus.GetCount(); ++i) { + if(m_controlStatus[i].IsSameAs(header.getYourCall())) { + wxLogMessage(wxT("Status %d requested by %s/%s"), + i, header.getMyCall1().c_str(), + header.getMyCall2().c_str()); + m_statusAnnounceTimer[i].start(); + return true; + } + } + + for(int i = 0; i < m_controlOutput.GetCount(); ++i) { + if(m_controlOutput[i].IsSameAs(header.getYourCall())) { + wxLogMessage(wxT("Output %d requested by %s/%s"), i, + header.getMyCall1().c_str(), + header.getMyCall2().c_str()); + m_output[i] = !m_output[i]; + + // XXX These should be fixed in the controller code! + m_controller->setOutput1(m_output[0]); + m_controller->setOutput2(m_output[1]); + m_controller->setOutput3(m_output[2]); + m_controller->setOutput4(m_output[3]); + return true; + } + } + + if (m_controlShutdown.IsSameAs(header.getYourCall())) { + wxLogMessage(wxT("Shutdown requested by %s/%s"), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + shutdown(); + } else if (m_controlStartup.IsSameAs(header.getYourCall())) { + wxLogMessage(wxT("Startup requested by %s/%s"), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + startup(); + } else { + wxLogMessage(wxT("Invalid command of %s sent by %s/%s"), header.getYourCall().c_str(), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + } + + return true; +} + +bool CDStarRepeaterTRXThread::checkAnnouncements(const CHeaderData& header) +{ + if (m_announcement == NULL) + return false; + + if (m_recordRPT1.IsSameAs(header.getRptCall1()) && m_recordRPT2.IsSameAs(header.getRptCall2())) { + wxLogMessage(wxT("Announcement creation requested by %s/%s"), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + m_announcement->writeHeader(header); + m_recording = true; + return true; + } + + if (m_deleteRPT1.IsSameAs(header.getRptCall1()) && m_deleteRPT2.IsSameAs(header.getRptCall2())) { + wxLogMessage(wxT("Announcement deletion requested by %s/%s"), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + m_announcement->deleteAnnouncement(); + m_deleting = true; + return true; + } + + return false; +} + +TRISTATE CDStarRepeaterTRXThread::checkHeader(CHeaderData& header) +{ + // If not in RPT1 validation mode, then a simplex header is converted to a proper repeater header + if (!m_rpt1Validation) { + if (!header.isRepeaterMode()) { + // Convert to a properly addressed repeater packet + header.setRepeaterMode(true); + header.setRptCall1(m_rptCallsign); + header.setRptCall2(m_gwyCallsign); + } + } + + // The repeater bit must be set when not in gateway mode + if (m_mode != MODE_GATEWAY) { + if (!header.isRepeaterMode()) { + wxLogMessage(wxT("Received a non-repeater packet, ignoring")); + return STATE_FALSE; + } + } else { + // Quietly reject acks when in gateway mode + if (header.isAck() || header.isNoResponse() || header.isRelayUnavailable()) + return STATE_UNKNOWN; + + // As a second check, reject on own UR call + wxString ur = header.getYourCall(); + if (ur.IsSameAs(m_rptCallsign) || ur.IsSameAs(m_gwyCallsign)) + return STATE_UNKNOWN; + + // Change RPT2 to be the gateway callsign in gateway mode + header.setRptCall2(m_gwyCallsign); + } + + wxString my = header.getMyCall1(); + + // Make sure MyCall is not empty, a silly value, or the repeater or gateway callsigns, STN* is a special case + if (!my.Left(3U).IsSameAs(wxT("STN"))) { + if (my.IsSameAs(m_rptCallsign) || + my.IsSameAs(m_gwyCallsign) || + my.IsSameAs(wxT(" ")) || + my.Left(6U).IsSameAs(wxT("NOCALL")) || + my.Left(6U).IsSameAs(wxT("N0CALL")) || + my.Left(6U).IsSameAs(wxT("MYCALL"))) { + wxLogMessage(wxT("Invalid MYCALL value of %s, ignoring"), my.c_str()); + return STATE_UNKNOWN; + } + } + + // Check for a French class 3 novice callsign, and reject + // Of the form F0xxx + if (my.Left(2U).IsSameAs(wxT("F0"))) { + wxLogMessage(wxT("French novice class licence callsign found, %s, ignoring"), my.c_str()); + return STATE_UNKNOWN; + } + + // Check for an Australian foundation class licence callsign, and reject + // Of the form VKnFxxx + if (my.Left(2U).IsSameAs(wxT("VK")) && my.GetChar(3U) == wxT('F') && my.GetChar(6U) != wxT(' ')) { + wxLogMessage(wxT("Australian foundation class licence callsign found, %s, ignoring"), my.c_str()); + return STATE_UNKNOWN; + } + + // Check the MyCall value against the regular expression + bool ok = m_regEx.Matches(my); + if (!ok) { + wxLogMessage(wxT("Invalid MYCALL value of %s, ignoring"), my.c_str()); + return STATE_UNKNOWN; + } + + // Is it for us? + if (!header.getRptCall1().IsSameAs(m_rptCallsign)) { + wxLogMessage(wxT("Invalid RPT1 value %s, ignoring"), header.getRptCall1().c_str()); + return STATE_FALSE; + } + + // If using callsign restriction, validate the my callsign + if (m_restriction) { + if (!my.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(m_rptCallsign.Left(LONG_CALLSIGN_LENGTH - 1U))) { + wxLogMessage(wxT("Unauthorised user %s tried to access the repeater"), my.c_str()); + return STATE_UNKNOWN; + } + } + + return STATE_TRUE; +} + +unsigned int CDStarRepeaterTRXThread::countBits(unsigned char byte) +{ + unsigned int bits = 0U; + + if ((byte & 0x01U) == 0x01U) + bits++; + if ((byte & 0x02U) == 0x02U) + bits++; + if ((byte & 0x04U) == 0x04U) + bits++; + if ((byte & 0x08U) == 0x08U) + bits++; + if ((byte & 0x10U) == 0x10U) + bits++; + if ((byte & 0x20U) == 0x20U) + bits++; + if ((byte & 0x40U) == 0x40U) + bits++; + if ((byte & 0x80U) == 0x80U) + bits++; + + return bits; +} + +void CDStarRepeaterTRXThread::blankDTMF(unsigned char* data) +{ + wxASSERT(data != NULL); + + // DTMF begins with these byte values + if ((data[0] & DTMF_MASK[0]) == DTMF_SIG[0] && (data[1] & DTMF_MASK[1]) == DTMF_SIG[1] && + (data[2] & DTMF_MASK[2]) == DTMF_SIG[2] && (data[3] & DTMF_MASK[3]) == DTMF_SIG[3] && + (data[4] & DTMF_MASK[4]) == DTMF_SIG[4] && (data[5] & DTMF_MASK[5]) == DTMF_SIG[5] && + (data[6] & DTMF_MASK[6]) == DTMF_SIG[6] && (data[7] & DTMF_MASK[7]) == DTMF_SIG[7] && + (data[8] & DTMF_MASK[8]) == DTMF_SIG[8]) + ::memcpy(data, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); +} diff --git a/DStarRepeater/DStarRepeaterTRXThread.h b/DStarRepeater/DStarRepeaterTRXThread.h new file mode 100644 index 0000000..4f7384a --- /dev/null +++ b/DStarRepeater/DStarRepeaterTRXThread.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterTRXThread_H +#define DStarRepeaterTRXThread_H + +#include "DStarRepeaterStatusData.h" +#include "RepeaterProtocolHandler.h" +#include "AnnouncementCallback.h" +#include "DStarRepeaterThread.h" +#include "DStarRepeaterDefs.h" +#include "DVTOOLFileWriter.h" +#include "AnnouncementUnit.h" +#include "SlowDataDecoder.h" +#include "SlowDataEncoder.h" +#include "BeaconCallback.h" +#include "CallsignList.h" +#include "OutputQueue.h" +#include "BeaconUnit.h" +#include "HeaderData.h" +#include "AMBEFEC.h" +#include "Timer.h" +#include "Utils.h" + +#include +#include + +class CDStarRepeaterTRXThread : public IDStarRepeaterThread, public IBeaconCallback, public IAnnouncementCallback { +public: + CDStarRepeaterTRXThread(const wxString& type); + virtual ~CDStarRepeaterTRXThread(); + + virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local); + virtual void setModem(CModem* modem); + virtual void setController(CExternalController* controller, unsigned int activeHangTime); + virtual void setTimes(unsigned int timeout, unsigned int ackTime); + virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); + virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + + virtual void setControl(bool enabled, const wxString& rpt1Callsign, + const wxString& rpt2Callsign, const wxString& shutdown, + const wxString& startup, const wxArrayString& command, + const wxArrayString& status, const wxArrayString& outputs + ); + + virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); + virtual void setLogging(bool logging, const wxString& dir); + virtual void setWhiteList(CCallsignList* list); + virtual void setBlackList(CCallsignList* list); + virtual void setGreyList(CCallsignList* list); + + virtual void shutdown(); + virtual void startup(); + + virtual CDStarRepeaterStatusData* getStatus(); + + virtual void *Entry(); + + virtual void kill(); + + virtual void transmitBeaconHeader(); + virtual void transmitBeaconData(const unsigned char* data, unsigned int length, bool end); + + virtual void transmitAnnouncementHeader(CHeaderData* header); + virtual void transmitAnnouncementData(const unsigned char* data, unsigned int length, bool end); + +private: + wxString m_type; + CModem* m_modem; + CRepeaterProtocolHandler* m_protocolHandler; + CExternalController* m_controller; + bool m_stopped; + wxString m_rptCallsign; + wxString m_gwyCallsign; + CBeaconUnit* m_beacon; + CAnnouncementUnit* m_announcement; + wxString m_recordRPT1; + wxString m_recordRPT2; + wxString m_deleteRPT1; + wxString m_deleteRPT2; + CHeaderData* m_rxHeader; + COutputQueue m_localQueue; + COutputQueue m_radioQueue; + COutputQueue** m_networkQueue; + unsigned int m_writeNum; + unsigned int m_readNum; + unsigned char m_radioSeqNo; + unsigned char m_networkSeqNo; + unsigned char m_lastSlowDataType; + CTimer m_timeoutTimer; + CTimer m_watchdogTimer; + CTimer m_pollTimer; + CTimer m_ackTimer; + + CTimer m_statusAnnounceTimer[5]; + + CTimer m_beaconTimer; + CTimer m_announcementTimer; + + CTimer m_statusTimer; + + CTimer m_heartbeatTimer; + DSTAR_RPT_STATE m_rptState; + DSTAR_RX_STATE m_rxState; + CSlowDataDecoder m_slowDataDecoder; + CSlowDataEncoder m_ackEncoder; + CSlowDataEncoder m_linkEncoder; + CSlowDataEncoder m_headerEncoder; + + // XXX ARRAY! + CSlowDataEncoder m_status1Encoder; + CSlowDataEncoder m_status2Encoder; + CSlowDataEncoder m_status3Encoder; + CSlowDataEncoder m_status4Encoder; + CSlowDataEncoder m_status5Encoder; + + // XXX ARRAY! + wxArrayString m_statusText; + + bool m_tx; + unsigned int m_space; + bool m_killed; + DSTAR_MODE m_mode; + ACK_TYPE m_ack; + bool m_restriction; + bool m_rpt1Validation; + bool m_errorReply; + bool m_controlEnabled; + wxString m_controlRPT1; + wxString m_controlRPT2; + wxString m_controlShutdown; + wxString m_controlStartup; + + wxArrayString m_controlStatus; + wxArrayString m_controlCommand; + + wxArrayString m_controlOutput; + + bool m_output[4]; + + CTimer m_activeHangTimer; + bool m_shutdown; + bool m_disable; + CDVTOOLFileWriter* m_logging; + unsigned char* m_lastData; + CAMBEFEC m_ambe; + unsigned int m_ambeFrames; + unsigned int m_ambeSilence; + unsigned int m_ambeBits; + unsigned int m_ambeErrors; + unsigned int m_lastAMBEBits; + unsigned int m_lastAMBEErrors; + wxString m_ackText; + wxString m_tempAckText; + LINK_STATUS m_linkStatus; + wxString m_reflector; + + wxRegEx m_regEx; + wxStopWatch m_headerTime; + wxStopWatch m_packetTime; + unsigned int m_packetCount; + unsigned int m_packetSilence; + CCallsignList* m_whiteList; + CCallsignList* m_blackList; + CCallsignList* m_greyList; + bool m_blocked; + bool m_busyData; + bool m_blanking; + bool m_recording; + bool m_deleting; + + void receiveHeader(CHeaderData* header); + void receiveRadioData(unsigned char* data, unsigned int length); + void receiveSlowData(unsigned char* data, unsigned int length); + void transmitRadioHeader(CHeaderData* header); + void transmitLocalHeader(CHeaderData* header); + void transmitNetworkHeader(CHeaderData* header); + void transmitStatus(); + void transmitErrorStatus(); + void transmitUserStatus(unsigned int n); + void transmitLocalData(); + void transmitRadioData(); + void transmitNetworkData(); + void transmitLocalHeader(); + void transmitRadioHeader(); + void transmitNetworkHeader(); + + void repeaterStateMachine(); + void receiveModem(); + void receiveNetwork(); + bool processRadioHeader(CHeaderData* header); + void processNetworkHeader(CHeaderData* header); + void processRadioFrame(unsigned char* data, FRAME_TYPE type); + unsigned int processNetworkFrame(unsigned char* data, unsigned int length, unsigned char seqNo); + void endOfRadioData(); + void endOfNetworkData(); + void setRadioState(DSTAR_RX_STATE state); + bool setRepeaterState(DSTAR_RPT_STATE state); + bool checkControl(const CHeaderData& header); + bool checkAnnouncements(const CHeaderData& header); + TRISTATE checkHeader(CHeaderData& header); + unsigned int countBits(unsigned char byte); + void clock(unsigned int ms); + void blankDTMF(unsigned char* data); +}; + +#endif diff --git a/DStarRepeater/DStarRepeaterTXRXThread.cpp b/DStarRepeater/DStarRepeaterTXRXThread.cpp new file mode 100644 index 0000000..2465ea0 --- /dev/null +++ b/DStarRepeater/DStarRepeaterTXRXThread.cpp @@ -0,0 +1,913 @@ +/* + * Copyright (C) 2011-2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterStatusData.h" +#include "DStarRepeaterTXRXThread.h" +#include "DVAPController.h" +#include "DStarDefines.h" +#include "HeaderData.h" +#include "Version.h" +#include "Utils.h" + +const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U}; +const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; + +const unsigned int MAX_DATA_SYNC_BIT_ERRS = 2U; + +const unsigned int NETWORK_QUEUE_COUNT = 2U; + +const unsigned int SILENCE_THRESHOLD = 2U; + +const unsigned int CYCLE_TIME = 9U; + +CDStarRepeaterTXRXThread::CDStarRepeaterTXRXThread(const wxString& type) : +m_type(type), +m_modem(NULL), +m_protocolHandler(NULL), +m_controller(NULL), +m_rptCallsign(), +m_rxHeader(NULL), +m_txHeader(NULL), +m_networkQueue(NULL), +m_writeNum(0U), +m_readNum(0U), +m_radioSeqNo(0U), +m_networkSeqNo(0U), +m_watchdogTimer(1000U, NETWORK_TIMEOUT), +m_registerTimer(1000U), +m_statusTimer(1000U, 0U, 100U), // 100ms +m_heartbeatTimer(1000U, 1U), // 1s +m_rptState(DSRS_LISTENING), +m_rxState(DSRXS_LISTENING), +m_slowDataDecoder(), +m_tx(false), +m_transmitting(false), +m_space(0U), +m_killed(false), +m_activeHangTimer(1000U), +m_disable(false), +m_lastData(NULL), +m_ambe(), +m_ambeFrames(0U), +m_ambeSilence(0U), +m_ambeBits(1U), +m_ambeErrors(0U), +m_lastAMBEBits(0U), +m_lastAMBEErrors(0U), +m_headerTime(), +m_packetTime(), +m_packetCount(0U), +m_packetSilence(0U) +{ + m_networkQueue = new COutputQueue*[NETWORK_QUEUE_COUNT]; + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + m_networkQueue[i] = new COutputQueue((DV_FRAME_LENGTH_BYTES + 2U) * 200U, NETWORK_RUN_FRAME_COUNT); // 4s worth of data); + + m_lastData = new unsigned char[DV_FRAME_MAX_LENGTH_BYTES]; + + setRepeaterState(DSRS_LISTENING); + setRadioState(DSRXS_LISTENING); +} + +CDStarRepeaterTXRXThread::~CDStarRepeaterTXRXThread() +{ + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + delete m_networkQueue[i]; + delete[] m_networkQueue; + delete[] m_lastData; + delete m_rxHeader; + delete m_txHeader; +} + +void *CDStarRepeaterTXRXThread::Entry() +{ + // Wait here until we have the essentials to run + while (!m_killed && (m_modem == NULL || m_controller == NULL || m_protocolHandler == NULL || m_rptCallsign.IsEmpty() || m_rptCallsign.IsSameAs(wxT(" ")))) + ::wxMilliSleep(500UL); // 1/2 sec + + if (m_killed) + return NULL; + + m_controller->setActive(false); + m_controller->setRadioTransmit(false); + + m_heartbeatTimer.start(); + m_statusTimer.start(); + m_registerTimer.start(10U); + + wxString hardware = m_type; + int n = hardware.Find(wxT(' ')); + if (n != wxNOT_FOUND) + hardware = m_type.Left(n); + + wxLogMessage(wxT("Starting the D-Star transmitter and receiver thread")); + + wxStopWatch stopWatch; + + try { + while (!m_killed) { + stopWatch.Start(); + + if (m_statusTimer.hasExpired() || m_space == 0U) { + m_space = m_modem->getSpace(); + m_tx = m_modem->isTX(); + m_statusTimer.start(); + } + + receiveModem(); + + receiveNetwork(); + + repeaterStateMachine(); + + // Send the register packet if needed and restart the timer + if (m_registerTimer.hasExpired()) { + m_protocolHandler->writeRegister(); + m_registerTimer.start(30U); + } + + // Clock the heartbeat output every one second + if (m_heartbeatTimer.hasExpired()) { + m_controller->setHeartbeat(); + m_heartbeatTimer.start(); + } + + // Set the output state + if (m_tx || (m_activeHangTimer.isRunning() && !m_activeHangTimer.hasExpired())) { + m_controller->setActive(true); + } else { + m_controller->setActive(false); + m_activeHangTimer.stop(); + } + + // Check the shutdown state, state changes are done here to bypass the state machine which is + // frozen when m_disable is asserted + m_disable = m_controller->getDisable(); + if (m_disable) { + if (m_rptState != DSRS_SHUTDOWN) { + m_watchdogTimer.stop(); + m_activeHangTimer.stop(); + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + m_networkQueue[i]->reset(); + m_controller->setActive(false); + m_controller->setRadioTransmit(false); + m_rptState = DSRS_SHUTDOWN; + m_transmitting = false; + } + } else { + if (m_rptState == DSRS_SHUTDOWN) { + m_watchdogTimer.stop(); + m_rptState = DSRS_LISTENING; + m_protocolHandler->reset(); + m_transmitting = false; + } + } + + if (m_networkQueue[m_readNum]->dataReady()) + transmitNetworkData(); + else if (m_networkQueue[m_readNum]->headerReady()) + transmitNetworkHeader(); + + m_controller->setRadioTransmit(m_tx); + + unsigned long ms = stopWatch.Time(); + if (ms < CYCLE_TIME) { + ::wxMilliSleep(CYCLE_TIME - ms); + clock(CYCLE_TIME); + } else { + clock(ms); + } + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised")); + } + + wxLogMessage(wxT("Stopping the D-Star transmitter and receiver thread")); + + m_modem->stop(); + + m_controller->setActive(false); + m_controller->setRadioTransmit(false); + m_controller->close(); + delete m_controller; + + m_protocolHandler->close(); + delete m_protocolHandler; + + return NULL; +} + +void CDStarRepeaterTXRXThread::kill() +{ + m_killed = true; +} + +void CDStarRepeaterTXRXThread::setCallsign(const wxString& callsign, const wxString&, DSTAR_MODE, ACK_TYPE, bool, bool, bool, bool) +{ + // Pad the callsign up to eight characters + m_rptCallsign = callsign; + m_rptCallsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); +} + +void CDStarRepeaterTXRXThread::setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) +{ + wxASSERT(handler != NULL); + + m_protocolHandler = handler; + + if (local) { + wxLogInfo(wxT("Reducing transmit buffering because of local connection")); + + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + m_networkQueue[i]->setThreshold(LOCAL_RUN_FRAME_COUNT); + } +} + +void CDStarRepeaterTXRXThread::setModem(CModem* modem) +{ + wxASSERT(modem != NULL); + + m_modem = modem; +} + +void CDStarRepeaterTXRXThread::setTimes(unsigned int, unsigned int) +{ +} + +void CDStarRepeaterTXRXThread::setBeacon(unsigned int, const wxString&, bool, TEXT_LANG) +{ +} + +void CDStarRepeaterTXRXThread::setAnnouncement(bool, unsigned int, const wxString&, const wxString&, const wxString&, const wxString&) +{ +} + +void CDStarRepeaterTXRXThread::setController(CExternalController* controller, unsigned int activeHangTime) +{ + wxASSERT(controller != NULL); + + m_controller = controller; + m_activeHangTimer.setTimeout(activeHangTime); +} + +void CDStarRepeaterTXRXThread::setOutputs(bool, bool, bool, bool) +{ +} + +void CDStarRepeaterTXRXThread::setLogging(bool, const wxString&) +{ +} + +void CDStarRepeaterTXRXThread::setWhiteList(CCallsignList*) +{ +} + +void CDStarRepeaterTXRXThread::setBlackList(CCallsignList*) +{ +} + +void CDStarRepeaterTXRXThread::setGreyList(CCallsignList*) +{ +} + +void CDStarRepeaterTXRXThread::receiveModem() +{ + for (;;) { + DSMT_TYPE type = m_modem->read(); + if (type == DSMTT_NONE) + return; + + switch (m_rxState) { + case DSRXS_LISTENING: + if (type == DSMTT_HEADER) { + CHeaderData* header = m_modem->readHeader(); + receiveHeader(header); + } else if (type == DSMTT_DATA) { + unsigned char data[20U]; + unsigned int length = m_modem->readData(data, 20U); + setRadioState(DSRXS_PROCESS_SLOW_DATA); + receiveSlowData(data, length); + } + break; + + case DSRXS_PROCESS_SLOW_DATA: + if (type == DSMTT_DATA) { + unsigned char data[20U]; + unsigned int length = m_modem->readData(data, 20U); + receiveSlowData(data, length); + } else if (type == DSMTT_EOT || type == DSMTT_LOST) { + setRadioState(DSRXS_LISTENING); + } + break; + + case DSRXS_PROCESS_DATA: + if (type == DSMTT_DATA) { + unsigned char data[20U]; + unsigned int length = m_modem->readData(data, 20U); + receiveRadioData(data, length); + } else if (type == DSMTT_EOT || type == DSMTT_LOST) { + unsigned char data[20U]; + ::memcpy(data, END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + processRadioFrame(data, FRAME_END); + setRadioState(DSRXS_LISTENING); + endOfRadioData(); + } + break; + } + } +} + +void CDStarRepeaterTXRXThread::receiveHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + wxLogMessage(wxT("Radio header decoded - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + bool res = processRadioHeader(header); + if (res) { + // A valid header and is a DV packet + m_radioSeqNo = 20U; + setRadioState(DSRXS_PROCESS_DATA); + } else { + // This is a DD packet or some other problem + // wxLogMessage(wxT("Invalid header")); + } +} + +void CDStarRepeaterTXRXThread::receiveSlowData(unsigned char* data, unsigned int) +{ + unsigned int errs; + errs = countBits(data[VOICE_FRAME_LENGTH_BYTES + 0U] ^ DATA_SYNC_BYTES[0U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 1U] ^ DATA_SYNC_BYTES[1U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 2U] ^ DATA_SYNC_BYTES[2U]); + + // The data sync has been seen, a fuzzy match is used, two bit errors or less + if (errs <= MAX_DATA_SYNC_BIT_ERRS) { + // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + m_radioSeqNo = 0U; + m_slowDataDecoder.sync(); + } else if (m_radioSeqNo == 20U) { + // wxLogMessage(wxT("Assuming data sync")); + m_radioSeqNo = 0U; + m_slowDataDecoder.sync(); + } else { + m_radioSeqNo++; + m_slowDataDecoder.addData(data + VOICE_FRAME_LENGTH_BYTES); + + CHeaderData* header = m_slowDataDecoder.getHeaderData(); + if (header == NULL) + return; + + wxLogMessage(wxT("Radio header from slow data - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X BER: 0%%"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + if (header != NULL) { + bool res = processRadioHeader(header); + if (res) { + // A valid header and is a DV packet, go to normal data relaying + setRadioState(DSRXS_PROCESS_DATA); + } else { + // This is a DD packet or some other problem + // wxLogMessage(wxT("Invalid header")); + } + } + } +} + +void CDStarRepeaterTXRXThread::receiveRadioData(unsigned char* data, unsigned int) +{ + unsigned int errs; + errs = countBits(data[VOICE_FRAME_LENGTH_BYTES + 0U] ^ DATA_SYNC_BYTES[0U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 1U] ^ DATA_SYNC_BYTES[1U]); + errs += countBits(data[VOICE_FRAME_LENGTH_BYTES + 2U] ^ DATA_SYNC_BYTES[2U]); + + // The data sync has been seen, a fuzzy match is used, two bit errors or less + if (errs <= MAX_DATA_SYNC_BIT_ERRS) { + // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + m_radioSeqNo = 0U; + processRadioFrame(data, FRAME_SYNC); + } else if (m_radioSeqNo == 20U) { + // wxLogMessage(wxT("Regenerating data sync")); + m_radioSeqNo = 0U; + processRadioFrame(data, FRAME_SYNC); + } else { + m_radioSeqNo++; + processRadioFrame(data, FRAME_NORMAL); + } +} + +void CDStarRepeaterTXRXThread::receiveNetwork() +{ + NETWORK_TYPE type; + + for (;;) { + type = m_protocolHandler->read(); + + // Get the data from the network + if (type == NETWORK_NONE) { // Nothing received + break; + } else if (type == NETWORK_HEADER) { // A header + CHeaderData* header = m_protocolHandler->readHeader(); + if (header != NULL) { + ::memcpy(m_lastData, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + + processNetworkHeader(header); + + m_headerTime.Start(); + m_packetTime.Start(); + m_packetCount = 0U; + m_packetSilence = 0U; + } + } else if (type == NETWORK_DATA) { // AMBE data and slow data + unsigned char data[2U * DV_FRAME_MAX_LENGTH_BYTES]; + ::memset(data, 0x00U, 2U * DV_FRAME_MAX_LENGTH_BYTES); + + unsigned char seqNo; + unsigned int length = m_protocolHandler->readData(data, DV_FRAME_MAX_LENGTH_BYTES, seqNo); + if (length != 0U && m_transmitting) { + ::memcpy(m_lastData, data, length); + m_watchdogTimer.start(); + m_packetCount += processNetworkFrame(data, length, seqNo); + } + } + } + + // Have we missed any data frames? + if (m_transmitting && m_packetTime.Time() > 200L) { + unsigned int packetsNeeded = m_headerTime.Time() / DSTAR_FRAME_TIME_MS; + + // wxLogMessage(wxT("Time: %u ms, need %u packets and received %u packets"), ms - m_headerMS, packetsNeeded, m_packetCount); + + if (packetsNeeded > m_packetCount) { + unsigned int count = packetsNeeded - m_packetCount; + + if (count > 5U) { + count -= 2U; + + // wxLogMessage(wxT("Inserting %u silence frames into the network data stream"), count); + + // Create silence frames + for (unsigned int i = 0U; i < count; i++) { + unsigned char data[DV_FRAME_LENGTH_BYTES]; + ::memcpy(data, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + m_packetCount += processNetworkFrame(data, DV_FRAME_LENGTH_BYTES, m_networkSeqNo); + m_packetSilence++; + } + } + } + + m_packetTime.Start(); + } +} + +void CDStarRepeaterTXRXThread::transmitNetworkHeader(const CHeaderData& header) +{ + wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header.getMyCall1().c_str(), header.getMyCall2().c_str(), header.getYourCall().c_str(), header.getRptCall1().c_str(), header.getRptCall2().c_str(), header.getFlag1(), header.getFlag2(), header.getFlag3()); + + bool empty = m_networkQueue[m_readNum]->isEmpty(); + if (!empty) { + bool headerReady = m_networkQueue[m_readNum]->headerReady(); + if (headerReady) { + // Transmission has never started, so just purge the queue + m_networkQueue[m_readNum]->reset(); + + m_readNum++; + if (m_readNum >= NETWORK_QUEUE_COUNT) + m_readNum = 0U; + } else { + // Append an end of stream + m_networkQueue[m_readNum]->reset(); + m_networkQueue[m_readNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + } + } + + m_networkQueue[m_writeNum]->reset(); + m_networkQueue[m_writeNum]->setHeader(new CHeaderData(header)); +} + +void CDStarRepeaterTXRXThread::transmitNetworkHeader() +{ + // Don't send a header until the modem is ready + bool ready = m_modem->isTXReady(); + if (!ready) + return; + + CHeaderData* header = m_networkQueue[m_readNum]->getHeader(); + if (header == NULL) + return; + + m_modem->writeHeader(*header); + delete header; +} + +void CDStarRepeaterTXRXThread::transmitNetworkData() +{ + if (m_space == 0U) + return; + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + bool end; + unsigned int length = m_networkQueue[m_readNum]->getData(buffer, DV_FRAME_LENGTH_BYTES, end); + + if (length == 0U) + return; + + m_modem->writeData(buffer, length, end); + m_space--; + + if (end) { + m_networkQueue[m_readNum]->reset(); + + m_readNum++; + if (m_readNum >= NETWORK_QUEUE_COUNT) + m_readNum = 0U; + } +} + +void CDStarRepeaterTXRXThread::repeaterStateMachine() +{ + if (m_watchdogTimer.isRunning() && m_watchdogTimer.hasExpired()) { + wxLogMessage(wxT("Network watchdog has expired")); + // Send end of transmission data to the radio + m_networkQueue[m_writeNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + endOfNetworkData(); + } +} + +void CDStarRepeaterTXRXThread::setRadioState(DSTAR_RX_STATE state) +{ + // This is the too state + switch (state) { + case DSRXS_LISTENING: + m_rxState = DSRXS_LISTENING; + break; + + case DSRXS_PROCESS_DATA: + m_ambeFrames = 0U; + m_ambeSilence = 0U; + m_ambeBits = 1U; + m_ambeErrors = 0U; + m_lastAMBEBits = 0U; + m_lastAMBEErrors = 0U; + m_rxState = DSRXS_PROCESS_DATA; + break; + + case DSRXS_PROCESS_SLOW_DATA: + m_radioSeqNo = 0U; + m_slowDataDecoder.reset(); + m_rxState = DSRXS_PROCESS_SLOW_DATA; + break; + } +} + +bool CDStarRepeaterTXRXThread::setRepeaterState(DSTAR_RPT_STATE state) +{ + // Can't change state when shutdown + if (m_disable) + return false; + + // The "to" state + switch (state) { + case DSRS_SHUTDOWN: + m_watchdogTimer.stop(); + m_activeHangTimer.stop(); + m_controller->setActive(false); + m_controller->setRadioTransmit(false); + m_rptState = DSRS_SHUTDOWN; + m_transmitting = false; + break; + + case DSRS_LISTENING: + m_rptState = DSRS_LISTENING; + break; + + case DSRS_VALID: + if (m_rptState != DSRS_LISTENING) + return false; + + m_rptState = DSRS_VALID; + break; + + default: + break; + } + + return true; +} + +bool CDStarRepeaterTXRXThread::processRadioHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + // We don't handle DD data packets + if (header->isDataPacket()) { + wxLogMessage(wxT("Received a DD packet, ignoring")); + delete header; + return false; + } + + setRepeaterState(DSRS_VALID); + + if (m_rptState == DSRS_VALID) { + // Send the valid header to the gateway if we are accepted + delete m_rxHeader; + m_rxHeader = header; + + CHeaderData netHeader(*m_rxHeader); + netHeader.setRptCall1(m_rxHeader->getRptCall2()); + netHeader.setRptCall2(m_rxHeader->getRptCall1()); + + m_protocolHandler->writeHeader(netHeader); + } else { + delete header; + } + + return true; +} + +void CDStarRepeaterTXRXThread::processNetworkHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + // If shutdown we ignore incoming headers + if (m_rptState == DSRS_SHUTDOWN) { + delete header; + return; + } + + wxLogMessage(wxT("Network header received - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + // Is it for us? + if (!header->getRptCall2().IsSameAs(m_rptCallsign)) { + wxLogMessage(wxT("Invalid network RPT2 value, ignoring")); + delete header; + return; + } + + delete m_txHeader; + m_txHeader = header; + + m_networkSeqNo = 0U; + m_transmitting = true; + m_watchdogTimer.start(); + m_activeHangTimer.stop(); + + transmitNetworkHeader(*header); +} + +void CDStarRepeaterTXRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE type) +{ + m_ambeFrames++; + + // If a sync frame, regenerate the sync bytes + if (type == FRAME_SYNC) + ::memcpy(data + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + + // Only regenerate the AMBE on received radio data + unsigned int errors = 0U; + if (type != FRAME_END) { + // Data packets have no AMBE FEC + if (!m_rxHeader->isDataPacket()) + errors = m_ambe.count(data); + + m_ambeErrors += errors; + m_ambeBits += 48U; // Only count the bits with FEC added + } + + if (::memcmp(data, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES) == 0) + m_ambeSilence++; + + // Don't pass through the frame of an invalid transmission + if (m_rptState != DSRS_VALID) + return; + + if (type == FRAME_END) { + // Send null data and the end marker over the network, and the statistics + unsigned char bytes[DV_FRAME_MAX_LENGTH_BYTES]; + ::memcpy(bytes, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(bytes + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + m_protocolHandler->writeData(bytes, DV_FRAME_MAX_LENGTH_BYTES, 0U, true); + } else { + // Send the data to the network + m_protocolHandler->writeData(data, DV_FRAME_LENGTH_BYTES, errors, false); + } +} + +unsigned int CDStarRepeaterTXRXThread::processNetworkFrame(unsigned char* data, unsigned int length, unsigned char seqNo) +{ + wxASSERT(data != NULL); + wxASSERT(length > 0U); + + // If shutdown we ignore incoming data + if (m_rptState == DSRS_SHUTDOWN) + return 1U; + + bool end = (seqNo & 0x40U) == 0x40U; + if (end) { + m_networkQueue[m_writeNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + endOfNetworkData(); + return 1U; + } + + // Mask out the control bits of the sequence number + seqNo &= 0x1FU; + + // Count the number of silence frames to insert + unsigned int tempSeqNo = m_networkSeqNo; + unsigned int count = 0U; + while (seqNo != tempSeqNo) { + count++; + + tempSeqNo++; + if (tempSeqNo >= 21U) + tempSeqNo = 0U; + } + + // If the number is too high, then it probably means an old out-of-order frame, ignore it + if (count > 18U) + return 0U; + + unsigned int packetCount = 0U; + + // Insert missing frames + while (seqNo != m_networkSeqNo) { + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + if (count > SILENCE_THRESHOLD) { + ::memcpy(buffer, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + } else { + ::memcpy(buffer, m_lastData, DV_FRAME_LENGTH_BYTES); + // Data packets have no AMBE FEC + if (!m_txHeader->isDataPacket()) + m_ambe.regenerate(buffer); + } + + if (m_networkSeqNo == 0U) + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + + m_networkQueue[m_writeNum]->addData(buffer, DV_FRAME_LENGTH_BYTES, false); + + packetCount++; + m_networkSeqNo++; + m_packetSilence++; + if (m_networkSeqNo >= 21U) + m_networkSeqNo = 0U; + } + + // Regenerate the sync bytes + if (m_networkSeqNo == 0U) + ::memcpy(data + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + + packetCount++; + m_networkSeqNo++; + if (m_networkSeqNo >= 21U) + m_networkSeqNo = 0U; + + // Data packets have no AMBE FEC + if (!m_txHeader->isDataPacket()) + m_ambe.regenerate(data); + + m_networkQueue[m_writeNum]->addData(data, DV_FRAME_LENGTH_BYTES, false); + + return packetCount; +} + +void CDStarRepeaterTXRXThread::endOfRadioData() +{ + wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + + setRepeaterState(DSRS_LISTENING); +} + +void CDStarRepeaterTXRXThread::endOfNetworkData() +{ + float loss = 0.0F; + if (m_packetCount != 0U) + loss = float(m_packetSilence) / float(m_packetCount); + + wxLogMessage(wxT("Stats for %s Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u"), m_txHeader->getMyCall1().c_str(), float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); + + m_watchdogTimer.stop(); + m_activeHangTimer.start(); + m_protocolHandler->reset(); + m_transmitting = false; + + m_writeNum++; + if (m_writeNum >= NETWORK_QUEUE_COUNT) + m_writeNum = 0U; +} + +CDStarRepeaterStatusData* CDStarRepeaterTXRXThread::getStatus() +{ + float bits = float(m_ambeBits - m_lastAMBEBits); + float errors = float(m_ambeErrors - m_lastAMBEErrors); + if (bits == 0.0F) + bits = 1.0F; + + m_lastAMBEBits = m_ambeBits; + m_lastAMBEErrors = m_ambeErrors; + + CDStarRepeaterStatusData* status; + if (m_rptState == DSRS_SHUTDOWN || m_rptState == DSRS_LISTENING) + status = new CDStarRepeaterStatusData(wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, + wxEmptyString, 0x00, 0x00, 0x00, m_tx, m_rxState, m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, 0.0F, + wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + else + status = new CDStarRepeaterStatusData(m_rxHeader->getMyCall1(), m_rxHeader->getMyCall2(), + m_rxHeader->getYourCall(), m_rxHeader->getRptCall1(), m_rxHeader->getRptCall2(), + m_rxHeader->getFlag1(), m_rxHeader->getFlag2(), m_rxHeader->getFlag3(), m_tx, m_rxState, + m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, (errors * 100.0F) / bits, wxEmptyString, wxEmptyString, + wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + + if (m_type.IsSameAs(wxT("DVAP")) && m_modem != NULL) { + CDVAPController* dvap = static_cast(m_modem); + bool squelch = dvap->getSquelch(); + int signal = dvap->getSignal(); + status->setDVAP(squelch, signal); + } + + return status; +} + +void CDStarRepeaterTXRXThread::clock(unsigned int ms) +{ + m_registerTimer.clock(ms); + m_watchdogTimer.clock(ms); + m_activeHangTimer.clock(ms); + m_statusTimer.clock(ms); + m_heartbeatTimer.clock(ms); +} + +void CDStarRepeaterTXRXThread::shutdown() +{ +} + +void CDStarRepeaterTXRXThread::startup() +{ +} + +void CDStarRepeaterTXRXThread::command1() +{ +} + +void CDStarRepeaterTXRXThread::command2() +{ +} + +void CDStarRepeaterTXRXThread::command3() +{ +} + +void CDStarRepeaterTXRXThread::command4() +{ +} + +void CDStarRepeaterTXRXThread::command5() +{ +} + +void CDStarRepeaterTXRXThread::command6() +{ +} + +unsigned int CDStarRepeaterTXRXThread::countBits(unsigned char byte) +{ + unsigned int bits = 0U; + + if ((byte & 0x01U) == 0x01U) + bits++; + if ((byte & 0x02U) == 0x02U) + bits++; + if ((byte & 0x04U) == 0x04U) + bits++; + if ((byte & 0x08U) == 0x08U) + bits++; + if ((byte & 0x10U) == 0x10U) + bits++; + if ((byte & 0x20U) == 0x20U) + bits++; + if ((byte & 0x40U) == 0x40U) + bits++; + if ((byte & 0x80U) == 0x80U) + bits++; + + return bits; +} diff --git a/DStarRepeater/DStarRepeaterTXRXThread.h b/DStarRepeater/DStarRepeaterTXRXThread.h new file mode 100644 index 0000000..b0d35c5 --- /dev/null +++ b/DStarRepeater/DStarRepeaterTXRXThread.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterTXRXThread_H +#define DStarRepeaterTXRXThread_H + +#include "DStarRepeaterStatusData.h" +#include "RepeaterProtocolHandler.h" +#include "DStarRepeaterThread.h" +#include "DStarRepeaterDefs.h" +#include "DVTOOLFileWriter.h" +#include "SlowDataDecoder.h" +#include "CallsignList.h" +#include "OutputQueue.h" +#include "HeaderData.h" +#include "AMBEFEC.h" +#include "Timer.h" + +#include + +class CDStarRepeaterTXRXThread : public IDStarRepeaterThread { +public: + CDStarRepeaterTXRXThread(const wxString& type); + virtual ~CDStarRepeaterTXRXThread(); + + virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local); + virtual void setModem(CModem* modem); + virtual void setController(CExternalController* controller, unsigned int activeHangTime); + virtual void setTimes(unsigned int timeout, unsigned int ackTime); + virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); + virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + + virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); + virtual void setLogging(bool logging, const wxString& dir); + virtual void setWhiteList(CCallsignList* list); + virtual void setBlackList(CCallsignList* list); + virtual void setGreyList(CCallsignList* list); + + virtual void shutdown(); + virtual void startup(); + + virtual void command1(); + virtual void command2(); + virtual void command3(); + virtual void command4(); + virtual void command5(); + virtual void command6(); + + virtual CDStarRepeaterStatusData* getStatus(); + + virtual void kill(); + + virtual void *Entry(); + +private: + wxString m_type; + CModem* m_modem; + CRepeaterProtocolHandler* m_protocolHandler; + CExternalController* m_controller; + wxString m_rptCallsign; + CHeaderData* m_rxHeader; + CHeaderData* m_txHeader; + COutputQueue** m_networkQueue; + unsigned int m_writeNum; + unsigned int m_readNum; + unsigned char m_radioSeqNo; + unsigned char m_networkSeqNo; + CTimer m_watchdogTimer; + CTimer m_registerTimer; + CTimer m_statusTimer; + CTimer m_heartbeatTimer; + DSTAR_RPT_STATE m_rptState; + DSTAR_RX_STATE m_rxState; + CSlowDataDecoder m_slowDataDecoder; + bool m_tx; + bool m_transmitting; + unsigned int m_space; + bool m_killed; + CTimer m_activeHangTimer; + bool m_disable; + unsigned char* m_lastData; + CAMBEFEC m_ambe; + unsigned int m_ambeFrames; + unsigned int m_ambeSilence; + unsigned int m_ambeBits; + unsigned int m_ambeErrors; + unsigned int m_lastAMBEBits; + unsigned int m_lastAMBEErrors; + wxStopWatch m_headerTime; + wxStopWatch m_packetTime; + unsigned int m_packetCount; + unsigned int m_packetSilence; + + void receiveHeader(CHeaderData* header); + void receiveRadioData(unsigned char* data, unsigned int length); + void receiveSlowData(unsigned char* data, unsigned int length); + void transmitNetworkHeader(const CHeaderData& header); + void transmitNetworkData(); + void transmitNetworkHeader(); + + void repeaterStateMachine(); + void receiveModem(); + void receiveNetwork(); + bool processRadioHeader(CHeaderData* header); + void processNetworkHeader(CHeaderData* header); + void processRadioFrame(unsigned char* data, FRAME_TYPE type); + unsigned int processNetworkFrame(unsigned char* data, unsigned int length, unsigned char seqNo); + void endOfRadioData(); + void endOfNetworkData(); + void setRadioState(DSTAR_RX_STATE state); + bool setRepeaterState(DSTAR_RPT_STATE state); + unsigned int countBits(unsigned char byte); + void clock(unsigned int ms); +}; + +#endif diff --git a/DStarRepeater/DStarRepeaterTXThread.cpp b/DStarRepeater/DStarRepeaterTXThread.cpp new file mode 100644 index 0000000..062b991 --- /dev/null +++ b/DStarRepeater/DStarRepeaterTXThread.cpp @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2011-2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterStatusData.h" +#include "DStarRepeaterTXThread.h" +#include "DStarDefines.h" +#include "HeaderData.h" +#include "Version.h" + +const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U}; +const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; + +const unsigned int MAX_DATA_SYNC_BIT_ERRS = 2U; + +const unsigned int NETWORK_QUEUE_COUNT = 2U; + +const unsigned int SILENCE_THRESHOLD = 2U; + +const unsigned int CYCLE_TIME = 9U; + +CDStarRepeaterTXThread::CDStarRepeaterTXThread(const wxString& type) : +m_type(type), +m_modem(NULL), +m_protocolHandler(NULL), +m_stopped(true), +m_rptCallsign(), +m_txHeader(NULL), +m_networkQueue(NULL), +m_writeNum(0U), +m_readNum(0U), +m_networkSeqNo(0U), +m_watchdogTimer(1000U, NETWORK_TIMEOUT), +m_registerTimer(1000U), +m_statusTimer(1000U, 0U, 100U), // 100ms +m_state(DSRS_LISTENING), +m_tx(false), +m_space(0U), +m_killed(false), +m_lastData(NULL), +m_ambe(), +m_headerTime(), +m_packetTime(), +m_packetCount(0U), +m_packetSilence(0U) +{ + m_networkQueue = new COutputQueue*[NETWORK_QUEUE_COUNT]; + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + m_networkQueue[i] = new COutputQueue((DV_FRAME_LENGTH_BYTES + 2U) * 200U, NETWORK_RUN_FRAME_COUNT); // 4s worth of data); + + m_lastData = new unsigned char[DV_FRAME_MAX_LENGTH_BYTES]; +} + +CDStarRepeaterTXThread::~CDStarRepeaterTXThread() +{ + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + delete m_networkQueue[i]; + delete[] m_networkQueue; + delete[] m_lastData; + delete m_txHeader; +} + +void *CDStarRepeaterTXThread::Entry() +{ + // Wait here until we have the essentials to run + while (!m_killed && (m_modem == NULL || m_protocolHandler == NULL || m_rptCallsign.IsEmpty() || m_rptCallsign.IsSameAs(wxT(" ")))) + ::wxMilliSleep(500UL); // 1/2 sec + + if (m_killed) + return NULL; + + m_stopped = false; + + m_registerTimer.start(10U); + m_statusTimer.start(); + + wxString hardware = m_type; + int n = hardware.Find(wxT(' ')); + if (n != wxNOT_FOUND) + hardware = m_type.Left(n); + + wxLogMessage(wxT("Starting the D-Star transmitter thread")); + + wxStopWatch stopWatch; + + try { + while (!m_killed) { + stopWatch.Start(); + + if (m_statusTimer.hasExpired() || m_space == 0U) { + m_space = m_modem->getSpace(); + m_tx = m_modem->isTX(); + m_statusTimer.start(); + } + + receiveNetwork(); + + receiveModem(); + + if (m_state == DSRS_NETWORK) { + if (m_watchdogTimer.hasExpired()) { + wxLogMessage(wxT("Network watchdog has expired")); + // Send end of transmission data to the radio + m_networkQueue[m_writeNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + endOfNetworkData(); + } + } + + // Send the register packet if needed and restart the timer + if (m_registerTimer.hasExpired()) { + m_protocolHandler->writeRegister(); + m_registerTimer.start(30U); + } + + if (m_networkQueue[m_readNum]->dataReady()) + transmitNetworkData(); + else if (m_networkQueue[m_readNum]->headerReady()) + transmitNetworkHeader(); + + unsigned long ms = stopWatch.Time(); + if (ms < CYCLE_TIME) { + ::wxMilliSleep(CYCLE_TIME - ms); + clock(CYCLE_TIME); + } else { + clock(ms); + } + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised")); + } + + wxLogMessage(wxT("Stopping the D-Star transmitter thread")); + + m_modem->stop(); + + m_protocolHandler->close(); + delete m_protocolHandler; + + return NULL; +} + +void CDStarRepeaterTXThread::kill() +{ + m_killed = true; +} + +void CDStarRepeaterTXThread::setCallsign(const wxString& callsign, const wxString&, DSTAR_MODE, ACK_TYPE, bool, bool, bool, bool) +{ + // Pad the callsign up to eight characters + m_rptCallsign = callsign; + m_rptCallsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); +} + +void CDStarRepeaterTXThread::setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) +{ + wxASSERT(handler != NULL); + + m_protocolHandler = handler; + + if (local) { + wxLogInfo(wxT("Reducing transmit buffering because of local connection")); + + for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) + m_networkQueue[i]->setThreshold(LOCAL_RUN_FRAME_COUNT); + } +} + +void CDStarRepeaterTXThread::setModem(CModem* modem) +{ + wxASSERT(modem != NULL); + + m_modem = modem; +} + +void CDStarRepeaterTXThread::setTimes(unsigned int, unsigned int) +{ +} + +void CDStarRepeaterTXThread::setBeacon(unsigned int, const wxString&, bool, TEXT_LANG) +{ +} + +void CDStarRepeaterTXThread::setAnnouncement(bool, unsigned int, const wxString&, const wxString&, const wxString&, const wxString&) +{ +} + +void CDStarRepeaterTXThread::setController(CExternalController*, unsigned int) +{ +} + +void CDStarRepeaterTXThread::setOutputs(bool, bool, bool, bool) +{ +} + +void CDStarRepeaterTXThread::setLogging(bool, const wxString&) +{ +} + +void CDStarRepeaterTXThread::setWhiteList(CCallsignList*) +{ +} + +void CDStarRepeaterTXThread::setBlackList(CCallsignList*) +{ +} + +void CDStarRepeaterTXThread::setGreyList(CCallsignList*) +{ +} + +void CDStarRepeaterTXThread::receiveModem() +{ + for (;;) { + DSMT_TYPE type = m_modem->read(); + if (type == DSMTT_NONE) + return; + } +} + +void CDStarRepeaterTXThread::receiveNetwork() +{ + NETWORK_TYPE type; + + for (;;) { + type = m_protocolHandler->read(); + + // Get the data from the network + if (type == NETWORK_NONE) { // Nothing received + break; + } else if (type == NETWORK_HEADER) { // A header + CHeaderData* header = m_protocolHandler->readHeader(); + if (header != NULL) { + ::memcpy(m_lastData, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + + processNetworkHeader(header); + + m_headerTime.Start(); + m_packetTime.Start(); + m_packetCount = 0U; + m_packetSilence = 0U; + } + } else if (type == NETWORK_DATA) { // AMBE data and slow data + unsigned char data[2U * DV_FRAME_MAX_LENGTH_BYTES]; + ::memset(data, 0x00U, 2U * DV_FRAME_MAX_LENGTH_BYTES); + + unsigned char seqNo; + unsigned int length = m_protocolHandler->readData(data, DV_FRAME_MAX_LENGTH_BYTES, seqNo); + if (length != 0U) { + ::memcpy(m_lastData, data, length); + m_watchdogTimer.start(); + m_packetCount += processNetworkFrame(data, length, seqNo); + } + } + } + + // Have we missed any data frames? + if (m_state == DSRS_NETWORK && m_packetTime.Time() > 200L) { + unsigned int packetsNeeded = m_headerTime.Time() / DSTAR_FRAME_TIME_MS; + + // wxLogMessage(wxT("Time: %u ms, need %u packets and received %u packets"), ms - m_headerMS, packetsNeeded, m_packetCount); + + if (packetsNeeded > m_packetCount) { + unsigned int count = packetsNeeded - m_packetCount; + + if (count > 5U) { + count -= 2U; + + // wxLogMessage(wxT("Inserting %u silence frames into the network data stream"), count); + + // Create silence frames + for (unsigned int i = 0U; i < count; i++) { + unsigned char data[DV_FRAME_LENGTH_BYTES]; + ::memcpy(data, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + m_packetCount += processNetworkFrame(data, DV_FRAME_LENGTH_BYTES, m_networkSeqNo); + m_packetSilence++; + } + } + } + + m_packetTime.Start(); + } +} + +void CDStarRepeaterTXThread::transmitNetworkHeader(CHeaderData* header) +{ + wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + bool empty = m_networkQueue[m_readNum]->isEmpty(); + if (!empty) { + bool headerReady = m_networkQueue[m_readNum]->headerReady(); + if (headerReady) { + // Transmission has never started, so just purge the queue + m_networkQueue[m_readNum]->reset(); + + m_readNum++; + if (m_readNum >= NETWORK_QUEUE_COUNT) + m_readNum = 0U; + } else { + // Append an end of stream + m_networkQueue[m_readNum]->reset(); + m_networkQueue[m_readNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + } + } + + m_networkQueue[m_writeNum]->reset(); + m_networkQueue[m_writeNum]->setHeader(header); +} + +void CDStarRepeaterTXThread::transmitNetworkHeader() +{ + // Don't send a header until the modem is ready + bool ready = m_modem->isTXReady(); + if (!ready) + return; + + CHeaderData* header = m_networkQueue[m_readNum]->getHeader(); + if (header == NULL) + return; + + m_modem->writeHeader(*header); + delete header; +} + +void CDStarRepeaterTXThread::transmitNetworkData() +{ + if (m_space == 0U) + return; + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + bool end; + unsigned int length = m_networkQueue[m_readNum]->getData(buffer, DV_FRAME_LENGTH_BYTES, end); + + if (length == 0U) + return; + + m_modem->writeData(buffer, length, end); + m_space--; + + if (end) { + m_networkQueue[m_readNum]->reset(); + + m_readNum++; + if (m_readNum >= NETWORK_QUEUE_COUNT) + m_readNum = 0U; + } +} + +void CDStarRepeaterTXThread::processNetworkHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + wxLogMessage(wxT("Network header received - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + // Is it for us? + if (!header->getRptCall2().IsSameAs(m_rptCallsign)) { + wxLogMessage(wxT("Invalid network RPT2 value, ignoring")); + delete header; + return; + } + + m_state = DSRS_NETWORK; + m_networkSeqNo = 0U; + m_watchdogTimer.start(); + + delete m_txHeader; + m_txHeader = header; + + transmitNetworkHeader(new CHeaderData(*header)); +} + +unsigned int CDStarRepeaterTXThread::processNetworkFrame(unsigned char* data, unsigned int length, unsigned char seqNo) +{ + wxASSERT(data != NULL); + wxASSERT(length > 0U); + + bool end = (seqNo & 0x40U) == 0x40U; + if (end) { + m_networkQueue[m_writeNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); + endOfNetworkData(); + return 1U; + } + + // Mask out the control bits of the sequence number + seqNo &= 0x1FU; + + // Count the number of silence frames to insert + unsigned int tempSeqNo = m_networkSeqNo; + unsigned int count = 0U; + while (seqNo != tempSeqNo) { + count++; + + tempSeqNo++; + if (tempSeqNo >= 21U) + tempSeqNo = 0U; + } + + // If the number is too high, then it probably means an old out-of-order frame, ignore it + if (count > 18U) + return 0U; + + unsigned int packetCount = 0U; + + // Insert missing frames + while (seqNo != m_networkSeqNo) { + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + if (count > SILENCE_THRESHOLD) { + ::memcpy(buffer, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); + } else { + ::memcpy(buffer, m_lastData, DV_FRAME_LENGTH_BYTES); + // Data packets have no AMBE FEC + if (!m_txHeader->isDataPacket()) + m_ambe.regenerate(buffer); + } + + if (m_networkSeqNo == 0U) + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + + m_networkQueue[m_writeNum]->addData(buffer, DV_FRAME_LENGTH_BYTES, false); + + packetCount++; + m_networkSeqNo++; + m_packetSilence++; + if (m_networkSeqNo >= 21U) + m_networkSeqNo = 0U; + } + + // Regenerate the sync bytes + if (m_networkSeqNo == 0U) + ::memcpy(data + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + + packetCount++; + m_networkSeqNo++; + if (m_networkSeqNo >= 21U) + m_networkSeqNo = 0U; + + // Data packets have no AMBE FEC + if (!m_txHeader->isDataPacket()) + m_ambe.regenerate(data); + + m_networkQueue[m_writeNum]->addData(data, DV_FRAME_LENGTH_BYTES, false); + + return packetCount; +} + +void CDStarRepeaterTXThread::endOfNetworkData() +{ + float loss = 0.0F; + if (m_packetCount != 0U) + loss = float(m_packetSilence) / float(m_packetCount); + + wxLogMessage(wxT("Stats for %s Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u"), m_txHeader->getMyCall1().c_str(), float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); + + m_state = DSRS_LISTENING; + m_watchdogTimer.stop(); + m_protocolHandler->reset(); + + m_writeNum++; + if (m_writeNum >= NETWORK_QUEUE_COUNT) + m_writeNum = 0U; +} + +CDStarRepeaterStatusData* CDStarRepeaterTXThread::getStatus() +{ + float loss = 0.0F; + if (m_packetCount != 0U) + loss = float(m_packetSilence) / float(m_packetCount); + + if (m_state == DSRS_LISTENING) + return new CDStarRepeaterStatusData(wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, + wxEmptyString, 0x00, 0x00, 0x00, m_tx, DSRXS_LISTENING, m_state, 0U, 0U, 0U, 0U, 0U, 0U, 0.0F, + wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + else + return new CDStarRepeaterStatusData(m_txHeader->getMyCall1(), m_txHeader->getMyCall2(), + m_txHeader->getYourCall(), m_txHeader->getRptCall1(), m_txHeader->getRptCall2(), + m_txHeader->getFlag1(), m_txHeader->getFlag2(), m_txHeader->getFlag3(), m_tx, DSRXS_LISTENING, + m_state, 0U, 0U, 0U, 0U, 0U, 0U, loss * 100.0F, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, + wxEmptyString, wxEmptyString); +} + +void CDStarRepeaterTXThread::clock(unsigned int ms) +{ + m_registerTimer.clock(ms); + m_watchdogTimer.clock(ms); + m_statusTimer.clock(ms); +} + +void CDStarRepeaterTXThread::shutdown() +{ +} + +void CDStarRepeaterTXThread::startup() +{ +} + +void CDStarRepeaterTXThread::command1() +{ +} + +void CDStarRepeaterTXThread::command2() +{ +} + +void CDStarRepeaterTXThread::command3() +{ +} + +void CDStarRepeaterTXThread::command4() +{ +} + +void CDStarRepeaterTXThread::command5() +{ +} + +void CDStarRepeaterTXThread::command6() +{ +} diff --git a/DStarRepeater/DStarRepeaterTXThread.h b/DStarRepeater/DStarRepeaterTXThread.h new file mode 100644 index 0000000..a1d8f9f --- /dev/null +++ b/DStarRepeater/DStarRepeaterTXThread.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterTXThread_H +#define DStarRepeaterTXThread_H + +#include "DStarRepeaterThread.h" +#include "OutputQueue.h" +#include "HeaderData.h" +#include "AMBEFEC.h" +#include "Timer.h" + +#include + +class CDStarRepeaterTXThread : public IDStarRepeaterThread { +public: + CDStarRepeaterTXThread(const wxString& type); + virtual ~CDStarRepeaterTXThread(); + + virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local); + virtual void setModem(CModem* modem); + virtual void setController(CExternalController* controller, unsigned int activeHangTime); + virtual void setTimes(unsigned int timeout, unsigned int ackTime); + virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); + virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); + virtual void setLogging(bool logging, const wxString& dir); + virtual void setWhiteList(CCallsignList* list); + virtual void setBlackList(CCallsignList* list); + virtual void setGreyList(CCallsignList* list); + + virtual void shutdown(); + virtual void startup(); + + virtual void command1(); + virtual void command2(); + virtual void command3(); + virtual void command4(); + virtual void command5(); + virtual void command6(); + + virtual CDStarRepeaterStatusData* getStatus(); + + virtual void kill(); + + virtual void *Entry(); + +private: + wxString m_type; + CModem* m_modem; + CRepeaterProtocolHandler* m_protocolHandler; + bool m_stopped; + wxString m_rptCallsign; + CHeaderData* m_txHeader; + COutputQueue** m_networkQueue; + unsigned int m_writeNum; + unsigned int m_readNum; + unsigned char m_networkSeqNo; + CTimer m_watchdogTimer; + CTimer m_registerTimer; + CTimer m_statusTimer; + DSTAR_RPT_STATE m_state; + bool m_tx; + unsigned int m_space; + bool m_killed; + unsigned char* m_lastData; + CAMBEFEC m_ambe; + wxStopWatch m_headerTime; + wxStopWatch m_packetTime; + unsigned int m_packetCount; + unsigned int m_packetSilence; + + void transmitNetworkHeader(CHeaderData* header); + void transmitNetworkData(); + void transmitNetworkHeader(); + + void receiveModem(); + void receiveNetwork(); + void processNetworkHeader(CHeaderData* header); + unsigned int processNetworkFrame(unsigned char* data, unsigned int length, unsigned char seqNo); + void endOfNetworkData(); + void clock(unsigned int ms); +}; + +#endif diff --git a/DStarRepeater/DStarRepeaterThread.cpp b/DStarRepeater/DStarRepeaterThread.cpp new file mode 100644 index 0000000..b698094 --- /dev/null +++ b/DStarRepeater/DStarRepeaterThread.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "DStarRepeaterThread.h" + +IDStarRepeaterThread::IDStarRepeaterThread(): +wxThread(wxTHREAD_JOINABLE) +{ +} + +IDStarRepeaterThread::~IDStarRepeaterThread() +{ +} diff --git a/DStarRepeater/DStarRepeaterThread.h b/DStarRepeater/DStarRepeaterThread.h new file mode 100644 index 0000000..5df4b93 --- /dev/null +++ b/DStarRepeater/DStarRepeaterThread.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterThread_H +#define DStarRepeaterThread_H + +#include "DStarRepeaterStatusData.h" +#include "RepeaterProtocolHandler.h" +#include "ExternalController.h" +#include "DStarRepeaterDefs.h" +#include "CallsignList.h" +#include "Modem.h" + +#include + +enum FRAME_TYPE { + FRAME_NORMAL, + FRAME_SYNC, + FRAME_END +}; + +class IDStarRepeaterThread : public wxThread { +public: + IDStarRepeaterThread(); + + virtual ~IDStarRepeaterThread() = 0; + + virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) = 0; + + virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) = 0; + virtual void setModem(CModem* modem) = 0; + virtual void setController(CExternalController* controller, unsigned int activeHangTime) = 0; + + virtual void setTimes(unsigned int timeout, unsigned int ackTime) = 0; + + virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language) = 0; + virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2) = 0; + virtual void setControl(bool enabled, const wxString& rpt1Callsign, + const wxString& rpt2Callsign, const wxString& shutdown, + const wxString& startup, const wxArrayString &command, + const wxArrayString& status, const wxArrayString& outputs + ) { }; + virtual void setOutputs(bool out1, bool out2, bool out3, bool out4) = 0; + virtual void setLogging(bool logging, const wxString& dir) = 0; + + virtual void setWhiteList(CCallsignList* list) = 0; + virtual void setBlackList(CCallsignList* list) = 0; + virtual void setGreyList(CCallsignList* list) = 0; + + virtual void shutdown() = 0; + virtual void startup() = 0; + + virtual CDStarRepeaterStatusData* getStatus() = 0; + + virtual void kill() = 0; + +private: +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj b/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj new file mode 100644 index 0000000..e7aca24 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj @@ -0,0 +1,228 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {FBAE8201-11CC-4256-BF39-A860B9DF963C} + DStarRepeaterConfig + Win32Proj + 10.0.17134.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + + + true + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(SolutionDir)..\portaudio\include;$(SolutionDir)Common;$(SolutionDir)GUICommon;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + portaudio_x86.lib;wsock32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) + $(SolutionDir)..\portaudio\build\msvc\Win32\Debug;$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(SolutionDir)..\portaudio\include;$(SolutionDir)Common;$(SolutionDir)GUICommon;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + portaudio_x64.lib;wsock32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) + $(SolutionDir)..\portaudio\build\msvc\x64\Debug;$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(SolutionDir)..\portaudio\include;$(SolutionDir)Common;$(SolutionDir)GUICommon;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + portaudio_x86.lib;wsock32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) + $(SolutionDir)..\portaudio\build\msvc\Win32\Release;$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(SolutionDir)..\portaudio\include;$(SolutionDir)Common;$(SolutionDir)GUICommon;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + portaudio_x64.lib;wsock32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) + $(SolutionDir)..\portaudio\build\msvc\x64\Release;$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3753ef20-2330-415e-b933-2560a474498b} + false + + + {2c548aab-a9f7-496f-bcf5-6185bda304d2} + false + + + + + + \ No newline at end of file diff --git a/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj.filters b/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj.filters new file mode 100644 index 0000000..520c410 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj.filters @@ -0,0 +1,146 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.cpp new file mode 100644 index 0000000..34dff7d --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigActiveHangSet.h" + +const unsigned int TIMED_WIDTH = 300U; + +const unsigned int BORDER_SIZE = 5U; + +CDStarRepeaterConfigActiveHangSet::CDStarRepeaterConfigActiveHangSet(wxWindow* parent, int id, const wxString& title, unsigned int time) : +wxPanel(parent, id), +m_title(title), +m_time(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* timeLabel = new wxStaticText(this, -1, _("Time (secs)")); + sizer->Add(timeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_time = new wxSlider(this, -1, time, 0, 300, wxDefaultPosition, wxSize(TIMED_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_time, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + sizer->Fit(this); + sizer->SetSizeHints(this); + + SetSizer(sizer); +} + + +CDStarRepeaterConfigActiveHangSet::~CDStarRepeaterConfigActiveHangSet() +{ +} + +bool CDStarRepeaterConfigActiveHangSet::Validate() +{ + return true; +} + +unsigned int CDStarRepeaterConfigActiveHangSet::getTime() const +{ + return m_time->GetValue(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.h b/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.h new file mode 100644 index 0000000..da7bda3 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigActiveHangSet_H +#define DStarRepeaterConfigActiveHangSet_H + +#include + +class CDStarRepeaterConfigActiveHangSet : public wxPanel { +public: + CDStarRepeaterConfigActiveHangSet(wxWindow* parent, int id, const wxString& title, unsigned int time); + virtual ~CDStarRepeaterConfigActiveHangSet(); + + virtual bool Validate(); + + virtual unsigned int getTime() const; + +private: + wxString m_title; + wxSlider* m_time; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.cpp new file mode 100644 index 0000000..a594ad2 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2013,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigAnnouncementSet.h" + +const unsigned int CONTROL_SIZE = 100U; +const unsigned int TIMES_WIDTH = 300U; +const unsigned int BORDER_SIZE = 5U; + +CDStarRepeaterConfigAnnouncementSet::CDStarRepeaterConfigAnnouncementSet(wxWindow* parent, int id, const wxString& title, bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2) : +wxPanel(parent, id), +m_title(title), +m_enabled(NULL), +m_time(NULL), +m_recordRPT1(NULL), +m_recordRPT2(NULL), +m_deleteRPT1(NULL), +m_deleteRPT2(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* enabledText = new wxStaticText(this, -1, _("Enabled")); + sizer->Add(enabledText, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_enabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); + m_enabled->Append(_("Disabled")); + m_enabled->Append(_("Enabled")); + sizer->Add(m_enabled, 0, wxALL, BORDER_SIZE); + m_enabled->SetSelection(enabled ? 1 : 0); + + wxStaticText* timeLabel = new wxStaticText(this, -1, _("Time (mins)")); + sizer->Add(timeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_time = new wxSlider(this, -1, time / 60U, 0, 30, wxDefaultPosition, wxSize(TIMES_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_time, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* recordRPT1Text = new wxStaticText(this, -1, _("Record RPT1")); + sizer->Add(recordRPT1Text, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_recordRPT1 = new CCallsignTextCtrl(this, -1, recordRPT1, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); + m_recordRPT1->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_recordRPT1, 0, wxALL, BORDER_SIZE); + + wxStaticText* recordRPT2Text = new wxStaticText(this, -1, _("Record RPT2")); + sizer->Add(recordRPT2Text, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_recordRPT2 = new CCallsignTextCtrl(this, -1, recordRPT2, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); + m_recordRPT2->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_recordRPT2, 0, wxALL, BORDER_SIZE); + + wxStaticText* deleteRPT1Text = new wxStaticText(this, -1, _("Delete RPT1")); + sizer->Add(deleteRPT1Text, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_deleteRPT1 = new CCallsignTextCtrl(this, -1, deleteRPT1, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); + m_deleteRPT1->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_deleteRPT1, 0, wxALL, BORDER_SIZE); + + wxStaticText* deleteRPT2Text = new wxStaticText(this, -1, _("Delete RPT2")); + sizer->Add(deleteRPT2Text, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_deleteRPT2 = new CCallsignTextCtrl(this, -1, deleteRPT2, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); + m_deleteRPT2->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_deleteRPT2, 0, wxALL, BORDER_SIZE); + + SetAutoLayout(true); + + sizer->Fit(this); + sizer->SetSizeHints(this); + + SetSizer(sizer); +} + + +CDStarRepeaterConfigAnnouncementSet::~CDStarRepeaterConfigAnnouncementSet() +{ +} + +bool CDStarRepeaterConfigAnnouncementSet::Validate() +{ + return m_enabled->GetCurrentSelection() != wxNOT_FOUND; +} + +bool CDStarRepeaterConfigAnnouncementSet::getEnabled() const +{ + int n = m_enabled->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +unsigned int CDStarRepeaterConfigAnnouncementSet::getTime() const +{ + return m_time->GetValue() * 60U; +} + +wxString CDStarRepeaterConfigAnnouncementSet::getRecordRPT1() const +{ + return m_recordRPT1->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigAnnouncementSet::getRecordRPT2() const +{ + return m_recordRPT2->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigAnnouncementSet::getDeleteRPT1() const +{ + return m_deleteRPT1->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigAnnouncementSet::getDeleteRPT2() const +{ + return m_deleteRPT2->GetValue().MakeUpper(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.h b/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.h new file mode 100644 index 0000000..bc6bca8 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigAnnouncementSet_H +#define DStarRepeaterConfigAnnouncementSet_H + +#include "CallsignTextCtrl.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigAnnouncementSet : public wxPanel { +public: + CDStarRepeaterConfigAnnouncementSet(wxWindow* parent, int id, const wxString& title, bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + virtual ~CDStarRepeaterConfigAnnouncementSet(); + + virtual bool Validate(); + + virtual bool getEnabled() const; + virtual unsigned int getTime() const; + virtual wxString getRecordRPT1() const; + virtual wxString getRecordRPT2() const; + virtual wxString getDeleteRPT1() const; + virtual wxString getDeleteRPT2() const; + +private: + wxString m_title; + wxChoice* m_enabled; + wxSlider* m_time; + CCallsignTextCtrl* m_recordRPT1; + CCallsignTextCtrl* m_recordRPT2; + CCallsignTextCtrl* m_deleteRPT1; + CCallsignTextCtrl* m_deleteRPT2; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigApp.cpp b/DStarRepeaterConfig/DStarRepeaterConfigApp.cpp new file mode 100644 index 0000000..2567784 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigApp.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigApp.h" +#include "DStarDefines.h" +#include "Version.h" + +#include +#include + +IMPLEMENT_APP(CDStarRepeaterConfigApp) + +const wxChar* NAME_PARAM = wxT("Repeater Name"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); + + +CDStarRepeaterConfigApp::CDStarRepeaterConfigApp() : +wxApp(), +m_name(), +m_confDir(), +m_frame(NULL) +{ +} + +CDStarRepeaterConfigApp::~CDStarRepeaterConfigApp() +{ +} + +bool CDStarRepeaterConfigApp::OnInit() +{ + SetVendorName(VENDOR_NAME); + + if (!wxApp::OnInit()) + return false; + + wxString frameName = APPLICATION_NAME + wxT(" - "); + if (!m_name.IsEmpty()) { + frameName.Append(m_name); + frameName.Append(wxT(" - ")); + } + frameName.Append(VERSION); + + m_frame = new CDStarRepeaterConfigFrame(frameName, m_confDir, m_name); + m_frame->Show(); + + SetTopWindow(m_frame); + + return true; +} + +int CDStarRepeaterConfigApp::OnExit() +{ + return 0; +} + +void CDStarRepeaterConfigApp::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + wxApp::OnInitCmdLine(parser); +} + +bool CDStarRepeaterConfigApp::OnCmdLineParsed(wxCmdLineParser& parser) +{ + if (!wxApp::OnCmdLineParsed(parser)) + return false; + + wxString confDir; + bool found = parser.Found(CONFDIR_OPTION, &confDir); + if (found) + m_confDir = confDir; + + if (parser.GetParamCount() > 0U) + m_name = parser.GetParam(0U); + + return true; +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigApp.h b/DStarRepeaterConfig/DStarRepeaterConfigApp.h new file mode 100644 index 0000000..0013955 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigApp.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigApp_H +#define DStarRepeaterConfigApp_H + +#include "DStarRepeaterConfigFrame.h" +#include "DStarRepeaterConfigDefs.h" + +#include + +class CDStarRepeaterConfigApp : public wxApp { + +public: + CDStarRepeaterConfigApp(); + virtual ~CDStarRepeaterConfigApp(); + + virtual bool OnInit(); + virtual int OnExit(); + + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + +private: + wxString m_name; + wxString m_confDir; + CDStarRepeaterConfigFrame* m_frame; +}; + +DECLARE_APP(CDStarRepeaterConfigApp) + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.cpp new file mode 100644 index 0000000..053c72b --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2012,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigBeaconSet.h" + +const unsigned int MAX_MESSAGE_LENGTH = 20U; + +const unsigned int CONTROL_SIZE1 = 200U; +const unsigned int CONTROL_SIZE2 = 100U; +const unsigned int TIMES_WIDTH = 300U; +const unsigned int BORDER_SIZE = 5U; + +CDStarRepeaterConfigBeaconSet::CDStarRepeaterConfigBeaconSet(wxWindow* parent, int id, const wxString& title, unsigned int time, const wxString& text, bool voice, TEXT_LANG language) : +wxPanel(parent, id), +m_title(title), +m_time(NULL), +m_text(NULL), +m_voice(NULL), +m_language(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* timeLabel = new wxStaticText(this, -1, _("Time (mins)")); + sizer->Add(timeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_time = new wxSlider(this, -1, time / 60U, 0, 30, wxDefaultPosition, wxSize(TIMES_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_time, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* messageText = new wxStaticText(this, -1, _("Message")); + sizer->Add(messageText, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_text = new CMessageTextCtrl(this, -1, text, wxDefaultPosition, wxSize(CONTROL_SIZE1, -1)); + m_text->SetMaxLength(MAX_MESSAGE_LENGTH); + sizer->Add(m_text, 0, wxALL, BORDER_SIZE); + + wxStaticText* voiceText = new wxStaticText(this, -1, _("Voice")); + sizer->Add(voiceText, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_voice = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_SIZE2, -1)); + m_voice->Append(_("Disabled")); + m_voice->Append(_("Enabled")); + sizer->Add(m_voice, 0, wxALL, BORDER_SIZE); + m_voice->SetSelection(voice ? 1 : 0); + + wxStaticText* languageLabel = new wxStaticText(this, -1, _("Language")); + sizer->Add(languageLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_language = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_SIZE2, -1)); + m_language->Append(wxT("English (UK)")); + m_language->Append(wxT("Deutsch")); + m_language->Append(wxT("Dansk")); + m_language->Append(wxT("Francais")); + m_language->Append(wxT("Italiano")); + m_language->Append(wxT("Polski")); + m_language->Append(wxT("Espanol")); + m_language->Append(wxT("Svenska")); + m_language->Append(wxT("Nederlands")); + m_language->Append(wxT("English (US)")); + m_language->Append(wxT("Norsk")); + sizer->Add(m_language, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_language->SetSelection(int(language)); + + SetAutoLayout(true); + + sizer->Fit(this); + sizer->SetSizeHints(this); + + SetSizer(sizer); +} + + +CDStarRepeaterConfigBeaconSet::~CDStarRepeaterConfigBeaconSet() +{ +} + +bool CDStarRepeaterConfigBeaconSet::Validate() +{ + if (m_voice->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return m_language->GetCurrentSelection() != wxNOT_FOUND; +} + +unsigned int CDStarRepeaterConfigBeaconSet::getTime() const +{ + return m_time->GetValue() * 60U; +} + +wxString CDStarRepeaterConfigBeaconSet::getText() const +{ + return m_text->GetValue(); +} + +bool CDStarRepeaterConfigBeaconSet::getVoice() const +{ + int n = m_voice->GetSelection(); + + return n == 1; +} + +TEXT_LANG CDStarRepeaterConfigBeaconSet::getLanguage() const +{ + int n = m_language->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return TL_ENGLISH_UK; + + return TEXT_LANG(n); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.h b/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.h new file mode 100644 index 0000000..b9a0256 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigBeaconSet_H +#define DStarRepeaterConfigBeaconSet_H + +#include "MessageTextCtrl.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigBeaconSet : public wxPanel { +public: + CDStarRepeaterConfigBeaconSet(wxWindow* parent, int id, const wxString& title, unsigned int time, const wxString& text, bool voice, TEXT_LANG language); + virtual ~CDStarRepeaterConfigBeaconSet(); + + virtual bool Validate(); + + virtual unsigned int getTime() const; + virtual wxString getText() const; + virtual bool getVoice() const; + virtual TEXT_LANG getLanguage() const; + +private: + wxString m_title; + wxSlider* m_time; + CMessageTextCtrl* m_text; + wxChoice* m_voice; + wxChoice* m_language; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.cpp new file mode 100644 index 0000000..609277b --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigCallsignSet.h" + +const unsigned int CALLSIGN_WIDTH = 80U; +const unsigned int SUFFIX_WIDTH = 50U; + +const unsigned int BORDER_SIZE = 5U; + +const int CHOICE_MODE = 8745; + +BEGIN_EVENT_TABLE(CDStarRepeaterConfigCallsignSet, wxPanel) + EVT_CHOICE(CHOICE_MODE, CDStarRepeaterConfigCallsignSet::onMode) +END_EVENT_TABLE() + + +CDStarRepeaterConfigCallsignSet::CDStarRepeaterConfigCallsignSet(wxWindow* parent, int id, const wxString& title, const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) : +wxPanel(parent, id), +m_title(title), +m_callsign(NULL), +m_gateway(NULL), +m_suffix(NULL), +m_mode(NULL), +m_ack(NULL), +m_restriction(NULL), +m_rpt1Validation(NULL), +m_dtmfBlanking(NULL), +m_errorReply(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(3); + + wxStaticText* callsignLabel = new wxStaticText(this, -1, _("Callsign")); + sizer->Add(callsignLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString call = callsign; + call.Append(wxT(" ")); + call.Truncate(LONG_CALLSIGN_LENGTH); + + m_callsign = new CCallsignTextCtrl(this, -1, call.Left(LONG_CALLSIGN_LENGTH - 1U).Trim(), wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_callsign->SetMaxLength(LONG_CALLSIGN_LENGTH - 1U); + sizer->Add(m_callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_suffix = new wxChoice(this, -1, wxDefaultPosition, wxSize(SUFFIX_WIDTH, -1)); +#if defined(__WXDEBUG__) + m_suffix->Append(wxT(" ")); +#endif + m_suffix->Append(wxT("A")); + m_suffix->Append(wxT("B")); + m_suffix->Append(wxT("C")); + m_suffix->Append(wxT("D")); + m_suffix->Append(wxT("E")); + sizer->Add(m_suffix, 0, wxALL, BORDER_SIZE); + + wxString suffix = callsign.Right(1U); + bool res = m_suffix->SetStringSelection(suffix); + if (!res) + m_suffix->SetStringSelection(wxT("B")); + + wxStaticText* gatewayLabel = new wxStaticText(this, -1, _("Gateway")); + sizer->Add(gatewayLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + call = gateway; + call.Append(wxT(" ")); + call.Truncate(LONG_CALLSIGN_LENGTH); + + m_gateway = new CCallsignTextCtrl(this, -1, call.Left(LONG_CALLSIGN_LENGTH - 1U).Trim(), wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_gateway->SetMaxLength(LONG_CALLSIGN_LENGTH - 1U); + sizer->Add(m_gateway, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* gLabel = new wxStaticText(this, -1, wxT("G")); + sizer->Add(gLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* modeLabel = new wxStaticText(this, -1, _("Mode")); + sizer->Add(modeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_mode = new wxChoice(this, CHOICE_MODE, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_mode->Append(_("Duplex")); + m_mode->Append(_("Simplex")); + m_mode->Append(_("Gateway")); + m_mode->Append(_("TX Only")); + m_mode->Append(_("RX Only")); + m_mode->Append(_("TX and RX")); + sizer->Add(m_mode, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_mode->SetSelection(int(mode)); + + wxStaticText* dummy1 = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* ackLabel = new wxStaticText(this, -1, _("Ack")); + sizer->Add(ackLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_ack = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_ack->Append(_("Off")); + m_ack->Append(wxT("BER")); + m_ack->Append(_("Status")); + sizer->Add(m_ack, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_ack->SetSelection(int(ack)); + + wxStaticText* dummy2 = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* restrictionLabel = new wxStaticText(this, -1, _("Restrict")); + sizer->Add(restrictionLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_restriction = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_restriction->Append(_("Off")); + m_restriction->Append(_("On")); + sizer->Add(m_restriction, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_restriction->SetSelection(restriction ? 1 : 0); + + wxStaticText* dummy3 = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy3, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* rpt1ValidationLabel = new wxStaticText(this, -1, _("RPT1 Validation")); + sizer->Add(rpt1ValidationLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_rpt1Validation = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_rpt1Validation->Append(_("Off")); + m_rpt1Validation->Append(_("On")); + sizer->Add(m_rpt1Validation, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_rpt1Validation->SetSelection(rpt1Validation ? 1 : 0); + + wxStaticText* dummy4 = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy4, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dtmfBlankingLabel = new wxStaticText(this, -1, _("DTMF Blanking")); + sizer->Add(dtmfBlankingLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_dtmfBlanking = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_dtmfBlanking->Append(_("Off")); + m_dtmfBlanking->Append(_("On")); + sizer->Add(m_dtmfBlanking, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_dtmfBlanking->SetSelection(dtmfBlanking ? 1 : 0); + + wxStaticText* dummy5 = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy5, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* errorReplyLabel = new wxStaticText(this, -1, _("Error Reply")); + sizer->Add(errorReplyLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_errorReply = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_errorReply->Append(_("Off")); + m_errorReply->Append(_("On")); + sizer->Add(m_errorReply, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_errorReply->SetSelection(errorReply ? 1 : 0); + + SetAutoLayout(true); + + sizer->Fit(this); + sizer->SetSizeHints(this); + + SetSizer(sizer); +} + + +CDStarRepeaterConfigCallsignSet::~CDStarRepeaterConfigCallsignSet() +{ +} + +bool CDStarRepeaterConfigCallsignSet::Validate() +{ + bool res = getCallsign().IsEmpty(); + if (res) { + wxMessageDialog dialog(this, _("The Callsign may not be empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (getCallsign().Left(2U).IsSameAs(wxT("F0"))) { + wxMessageDialog dialog(this, _("The Callsign may not be for a French novice"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + res = getGateway().IsEmpty(); + if (res) { + wxMessageDialog dialog(this, _("The Gateway may not be empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (m_suffix->GetSelection() == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("The Callsign suffix is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (m_mode->GetSelection() == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("Mode is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (m_ack->GetSelection() == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("Ack is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (m_restriction->GetSelection() == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("Restrict is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (m_rpt1Validation->GetSelection() == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("RPT1 Validation is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (m_dtmfBlanking->GetSelection() == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("DTMF Blanking is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (m_errorReply->GetSelection() == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("Error Reply is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +wxString CDStarRepeaterConfigCallsignSet::getCallsign() const +{ + wxString callsign = m_callsign->GetValue().MakeUpper(); + wxString suffix = m_suffix->GetStringSelection(); + + callsign.Append(wxT(" ")); + callsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); + callsign.Append(suffix); + + return callsign; +} + +wxString CDStarRepeaterConfigCallsignSet::getGateway() const +{ + wxString gateway = m_gateway->GetValue().MakeUpper(); + + gateway.Append(wxT(" ")); + gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U); + gateway.Append(wxT("G")); + + return gateway; +} + +DSTAR_MODE CDStarRepeaterConfigCallsignSet::getMode() const +{ + int n = m_mode->GetCurrentSelection(); + + return DSTAR_MODE(n); +} + +ACK_TYPE CDStarRepeaterConfigCallsignSet::getAck() const +{ + int n = m_ack->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return AT_BER; + + return ACK_TYPE(n); +} + +bool CDStarRepeaterConfigCallsignSet::getRestriction() const +{ + int n = m_restriction->GetCurrentSelection(); + + return n == 1; +} + +void CDStarRepeaterConfigCallsignSet::onMode(wxCommandEvent &event) +{ + DSTAR_MODE mode = DSTAR_MODE(event.GetSelection()); + + switch (mode) { + case MODE_GATEWAY: + m_ack->SetSelection(0); // Ack off + m_rpt1Validation->SetSelection(1); // RPT1 Validation on + m_ack->Disable(); + m_rpt1Validation->Disable(); + break; + + case MODE_TXONLY: + case MODE_RXONLY: + case MODE_TXANDRX: + m_ack->SetSelection(0); // Ack off + m_ack->Disable(); + break; + + default: + m_ack->Enable(); + m_rpt1Validation->Enable(); + break; + } +} + +bool CDStarRepeaterConfigCallsignSet::getRPT1Validation() const +{ + int n = m_rpt1Validation->GetCurrentSelection(); + + return n == 1; +} + +bool CDStarRepeaterConfigCallsignSet::getDTMFBlanking() const +{ + int n = m_dtmfBlanking->GetCurrentSelection(); + + return n == 1; +} + +bool CDStarRepeaterConfigCallsignSet::getErrorReply() const +{ + int n = m_errorReply->GetCurrentSelection(); + + return n == 1; +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.h b/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.h new file mode 100644 index 0000000..4190223 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigCallsignSet_H +#define DStarRepeaterConfigCallsignSet_H + +#include "CallsignTextCtrl.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigCallsignSet : public wxPanel { +public: + CDStarRepeaterConfigCallsignSet(wxWindow* parent, int id, const wxString& title, const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + virtual ~CDStarRepeaterConfigCallsignSet(); + + virtual bool Validate(); + + virtual wxString getCallsign() const; + virtual wxString getGateway() const; + virtual DSTAR_MODE getMode() const; + virtual ACK_TYPE getAck() const; + virtual bool getRestriction() const; + virtual bool getRPT1Validation() const; + virtual bool getDTMFBlanking() const; + virtual bool getErrorReply() const; + + virtual void onMode(wxCommandEvent& event); + +private: + wxString m_title; + CCallsignTextCtrl* m_callsign; + CCallsignTextCtrl* m_gateway; + wxChoice* m_suffix; + wxChoice* m_mode; + wxChoice* m_ack; + wxChoice* m_restriction; + wxChoice* m_rpt1Validation; + wxChoice* m_dtmfBlanking; + wxChoice* m_errorReply; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.cpp new file mode 100644 index 0000000..6b15ee5 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2009-2012,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigControl1Set.h" +#include "DStarDefines.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 120U; +const unsigned int CONTROL_WIDTH2 = 200U; + +CDStarRepeaterConfigControl1Set::CDStarRepeaterConfigControl1Set(wxWindow* parent, int id, const wxString& title, bool enabled, const wxString& rpt1Callsign, const wxString& rpt2Callsign, const wxString& shutdown, const wxString& startup, const wxString& status1, const wxString& status2, const wxString& status3, const wxString& status4, const wxString& status5, const wxString& output1, const wxString& output2, const wxString& output3, const wxString& output4) : +wxPanel(parent, id), +m_title(title), +m_enabled(NULL), +m_rpt1Callsign(NULL), +m_rpt2Callsign(NULL), +m_shutdown(NULL), +m_startup(NULL), +m_status1(NULL), +m_status2(NULL), +m_status3(NULL), +m_status4(NULL), +m_status5(NULL), +m_output1(NULL), +m_output2(NULL), +m_output3(NULL), +m_output4(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* controlLabel = new wxStaticText(this, -1, _("Control")); + sizer->Add(controlLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_enabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_enabled->Append(_("Disabled")); + m_enabled->Append(_("Enabled")); + sizer->Add(m_enabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_enabled->SetSelection(enabled ? 1 : 0); + + wxStaticText* rpt1CallsignLabel = new wxStaticText(this, -1, _("RPT1 Callsign")); + sizer->Add(rpt1CallsignLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_rpt1Callsign = new CCallsignTextCtrl(this, -1, rpt1Callsign, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_rpt1Callsign->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_rpt1Callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* rpt2CallsignLabel = new wxStaticText(this, -1, _("RPT2 Callsign")); + sizer->Add(rpt2CallsignLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_rpt2Callsign = new CCallsignTextCtrl(this, -1, rpt2Callsign, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_rpt2Callsign->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_rpt2Callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* shutdownLabel = new wxStaticText(this, -1, _("Shutdown")); + sizer->Add(shutdownLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_shutdown = new CCallsignTextCtrl(this, -1, shutdown, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_shutdown->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_shutdown, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* startupLabel = new wxStaticText(this, -1, _("Startup")); + sizer->Add(startupLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_startup = new CCallsignTextCtrl(this, -1, startup, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_startup->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_startup, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* status1Label = new wxStaticText(this, -1, _("Status 1")); + sizer->Add(status1Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_status1 = new CCallsignTextCtrl(this, -1, status1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_status1->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_status1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* status2Label = new wxStaticText(this, -1, _("Status 2")); + sizer->Add(status2Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_status2 = new CCallsignTextCtrl(this, -1, status2, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_status2->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_status2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* status3Label = new wxStaticText(this, -1, _("Status 3")); + sizer->Add(status3Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_status3 = new CCallsignTextCtrl(this, -1, status3, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_status3->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_status3, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* status4Label = new wxStaticText(this, -1, _("Status 4")); + sizer->Add(status4Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_status4 = new CCallsignTextCtrl(this, -1, status4, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_status4->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_status4, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* status5Label = new wxStaticText(this, -1, _("Status 5")); + sizer->Add(status5Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_status5 = new CCallsignTextCtrl(this, -1, status5, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_status5->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_status5, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* output1Label = new wxStaticText(this, -1, _("Output 1")); + sizer->Add(output1Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_output1 = new CCallsignTextCtrl(this, -1, output1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_output1->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_output1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* output2Label = new wxStaticText(this, -1, _("Output 2")); + sizer->Add(output2Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_output2 = new CCallsignTextCtrl(this, -1, output2, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_output2->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_output2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* output3Label = new wxStaticText(this, -1, _("Output 3")); + sizer->Add(output3Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_output3 = new CCallsignTextCtrl(this, -1, output3, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_output3->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_output3, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* output4Label = new wxStaticText(this, -1, _("Output 4")); + sizer->Add(output4Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_output4 = new CCallsignTextCtrl(this, -1, output4, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_output4->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_output4, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + sizer->Fit(this); + sizer->SetSizeHints(this); + + SetSizer(sizer); +} + + +CDStarRepeaterConfigControl1Set::~CDStarRepeaterConfigControl1Set() +{ +} + +bool CDStarRepeaterConfigControl1Set::Validate() +{ + return m_enabled->GetCurrentSelection() != wxNOT_FOUND; +} + +bool CDStarRepeaterConfigControl1Set::getEnabled() const +{ + int n = m_enabled->GetCurrentSelection(); + + return n == 1; +} + +wxString CDStarRepeaterConfigControl1Set::getRPT1Callsign() const +{ + return m_rpt1Callsign->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getRPT2Callsign() const +{ + return m_rpt2Callsign->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getShutdown() const +{ + return m_shutdown->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getStartup() const +{ + return m_startup->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getStatus1() const +{ + return m_status1->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getStatus2() const +{ + return m_status2->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getStatus3() const +{ + return m_status3->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getStatus4() const +{ + return m_status4->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getStatus5() const +{ + return m_status5->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getOutput1() const +{ + return m_output1->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getOutput2() const +{ + return m_output2->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getOutput3() const +{ + return m_output3->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl1Set::getOutput4() const +{ + return m_output4->GetValue().MakeUpper(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.h b/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.h new file mode 100644 index 0000000..c290aa3 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2009-2012,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigControl1Set_H +#define DStarRepeaterConfigControl1Set_H + +#include "CallsignTextCtrl.h" + +#include + +class CDStarRepeaterConfigControl1Set : public wxPanel { +public: + CDStarRepeaterConfigControl1Set(wxWindow* parent, int id, const wxString& title, bool enabled, + const wxString& rpt1Callsign, const wxString& rpt2Callsign, const wxString& shutdown, + const wxString& startup, const wxString& status1, const wxString& status2, const wxString& status3, + const wxString& status4, const wxString& status5, const wxString& output1, const wxString& output2, + const wxString& output3, const wxString& output4); + virtual ~CDStarRepeaterConfigControl1Set(); + + virtual bool Validate(); + + virtual bool getEnabled() const; + + virtual wxString getRPT1Callsign() const; + virtual wxString getRPT2Callsign() const; + + virtual wxString getShutdown() const; + virtual wxString getStartup() const; + + virtual wxString getStatus1() const; + virtual wxString getStatus2() const; + virtual wxString getStatus3() const; + virtual wxString getStatus4() const; + virtual wxString getStatus5() const; + + virtual wxString getOutput1() const; + virtual wxString getOutput2() const; + virtual wxString getOutput3() const; + virtual wxString getOutput4() const; + +private: + wxString m_title; + wxChoice* m_enabled; + CCallsignTextCtrl* m_rpt1Callsign; + CCallsignTextCtrl* m_rpt2Callsign; + CCallsignTextCtrl* m_shutdown; + CCallsignTextCtrl* m_startup; + CCallsignTextCtrl* m_status1; + CCallsignTextCtrl* m_status2; + CCallsignTextCtrl* m_status3; + CCallsignTextCtrl* m_status4; + CCallsignTextCtrl* m_status5; + CCallsignTextCtrl* m_output1; + CCallsignTextCtrl* m_output2; + CCallsignTextCtrl* m_output3; + CCallsignTextCtrl* m_output4; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.cpp new file mode 100644 index 0000000..e6198ba --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2009,2010,2012,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigControl2Set.h" +#include "DStarDefines.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 120U; +const unsigned int CONTROL_WIDTH2 = 200U; + +CDStarRepeaterConfigControl2Set::CDStarRepeaterConfigControl2Set(wxWindow* parent, int id, const wxString& title, const wxString& command1, const wxString& command1Line, const wxString& command2, const wxString& command2Line, const wxString& command3, const wxString& command3Line, const wxString& command4, const wxString& command4Line, const wxString& command5, const wxString& command5Line, const wxString& command6, const wxString& command6Line) : +wxPanel(parent, id), +m_title(title), +m_command1(NULL), +m_command1Line(NULL), +m_command2(NULL), +m_command2Line(NULL), +m_command3(NULL), +m_command3Line(NULL), +m_command4(NULL), +m_command4Line(NULL), +m_command5(NULL), +m_command5Line(NULL), +m_command6(NULL), +m_command6Line(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(3); + + wxStaticText* command1Label = new wxStaticText(this, -1, _("Command 1")); + sizer->Add(command1Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command1 = new CCallsignTextCtrl(this, -1, command1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_command1->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_command1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command1Line = new wxTextCtrl(this, -1, command1Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + sizer->Add(m_command1Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* command2Label = new wxStaticText(this, -1, _("Command 2")); + sizer->Add(command2Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command2 = new CCallsignTextCtrl(this, -1, command2, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_command2->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_command2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command2Line = new wxTextCtrl(this, -1, command2Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + sizer->Add(m_command2Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* command3Label = new wxStaticText(this, -1, _("Command 3")); + sizer->Add(command3Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command3 = new CCallsignTextCtrl(this, -1, command3, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_command3->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_command3, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command3Line = new wxTextCtrl(this, -1, command3Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + sizer->Add(m_command3Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* command4Label = new wxStaticText(this, -1, _("Command 4")); + sizer->Add(command4Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command4 = new CCallsignTextCtrl(this, -1, command4, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_command4->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_command4, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command4Line = new wxTextCtrl(this, -1, command4Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + sizer->Add(m_command4Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* command5Label = new wxStaticText(this, -1, _("Command 5")); + sizer->Add(command5Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command5 = new CCallsignTextCtrl(this, -1, command5, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_command5->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_command5, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command5Line = new wxTextCtrl(this, -1, command5Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + sizer->Add(m_command5Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* command6Label = new wxStaticText(this, -1, _("Command 6")); + sizer->Add(command6Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command6 = new CCallsignTextCtrl(this, -1, command6, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_command6->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_command6, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_command6Line = new wxTextCtrl(this, -1, command6Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + sizer->Add(m_command6Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + sizer->Fit(this); + sizer->SetSizeHints(this); + + SetSizer(sizer); +} + + +CDStarRepeaterConfigControl2Set::~CDStarRepeaterConfigControl2Set() +{ +} + +bool CDStarRepeaterConfigControl2Set::Validate() +{ + return true; +} + +wxString CDStarRepeaterConfigControl2Set::getCommand1() const +{ + return m_command1->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand1Line() const +{ + return m_command1Line->GetValue(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand2() const +{ + return m_command2->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand2Line() const +{ + return m_command2Line->GetValue(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand3() const +{ + return m_command3->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand3Line() const +{ + return m_command3Line->GetValue(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand4() const +{ + return m_command4->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand4Line() const +{ + return m_command4Line->GetValue(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand5() const +{ + return m_command5->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand5Line() const +{ + return m_command5Line->GetValue(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand6() const +{ + return m_command6->GetValue().MakeUpper(); +} + +wxString CDStarRepeaterConfigControl2Set::getCommand6Line() const +{ + return m_command6Line->GetValue(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.h b/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.h new file mode 100644 index 0000000..49bdc3a --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009,2010,2012,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigControl2Set_H +#define DStarRepeaterConfigControl2Set_H + +#include "CallsignTextCtrl.h" + +#include + +class CDStarRepeaterConfigControl2Set : public wxPanel { +public: + CDStarRepeaterConfigControl2Set(wxWindow* parent, int id, const wxString& title, const wxString& command1, + const wxString& command1Line, const wxString& command2, const wxString& command2Line, + const wxString& command3, const wxString& command3Line, const wxString& command4, + const wxString& command4Line, const wxString& command5, const wxString& command5Line, + const wxString& command6, const wxString& command6Line); + virtual ~CDStarRepeaterConfigControl2Set(); + + virtual bool Validate(); + + virtual wxString getCommand1() const; + virtual wxString getCommand1Line() const; + + virtual wxString getCommand2() const; + virtual wxString getCommand2Line() const; + + virtual wxString getCommand3() const; + virtual wxString getCommand3Line() const; + + virtual wxString getCommand4() const; + virtual wxString getCommand4Line() const; + + virtual wxString getCommand5() const; + virtual wxString getCommand5Line() const; + + virtual wxString getCommand6() const; + virtual wxString getCommand6Line() const; + +private: + wxString m_title; + CCallsignTextCtrl* m_command1; + wxTextCtrl* m_command1Line; + CCallsignTextCtrl* m_command2; + wxTextCtrl* m_command2Line; + CCallsignTextCtrl* m_command3; + wxTextCtrl* m_command3Line; + CCallsignTextCtrl* m_command4; + wxTextCtrl* m_command4Line; + CCallsignTextCtrl* m_command5; + wxTextCtrl* m_command5Line; + CCallsignTextCtrl* m_command6; + wxTextCtrl* m_command6Line; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.cpp new file mode 100644 index 0000000..e22e012 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2012-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigControllerSet.h" +#include "SerialPortSelector.h" + +const unsigned int CONTROL_WIDTH1 = 150U; +const unsigned int CONTROL_WIDTH2 = 300U; +const unsigned int CONTROL_WIDTH3 = 80U; + +const unsigned int BORDER_SIZE = 5U; + +CDStarRepeaterConfigControllerSet::CDStarRepeaterConfigControllerSet(wxWindow* parent, int id, const wxString& title, const wxString& type, unsigned int config, bool pttInvert, unsigned int time) : +wxPanel(parent, id), +m_title(title), +m_type(NULL), +m_config(NULL), +m_pttInvert(NULL), +m_time(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* typeLabel = new wxStaticText(this, -1, _("Type")); + sizer->Add(typeLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_type = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + + m_type->Append(_("None")); + +#if defined(GPIO) + m_type->Append(wxT("GPIO")); + m_type->Append(wxT("UDRC")); +#endif + + m_type->Append(wxT("Velleman K8055 - 0")); + m_type->Append(wxT("Velleman K8055 - 1")); + m_type->Append(wxT("Velleman K8055 - 2")); + m_type->Append(wxT("Velleman K8055 - 3")); + + // Add the URI USB + m_type->Append(wxT("URI USB - 1")); + m_type->Append(wxT("URI USB - 2")); + m_type->Append(wxT("URI USB - 3")); + m_type->Append(wxT("URI USB - 4")); + m_type->Append(wxT("URI USB - 5")); + m_type->Append(wxT("URI USB - 6")); + + // Add the Serial ports + wxArrayString serialDevs = CSerialPortSelector::getDevices(); + for (size_t i = 0U; i < serialDevs.GetCount(); i++) + m_type->Append(wxT("Serial - ") + serialDevs.Item(i)); + + // Add the Arduino ports + serialDevs = CSerialPortSelector::getDevices(); + for (size_t i = 0U; i < serialDevs.GetCount(); i++) + m_type->Append(wxT("Arduino - ") + serialDevs.Item(i)); + + sizer->Add(m_type, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + if (type.IsEmpty()) + m_type->SetSelection(0); + else + m_type->SetStringSelection(type); + + wxStaticText* configLabel = new wxStaticText(this, -1, _("Config")); + sizer->Add(configLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_config = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); + m_config->Append("1"); + m_config->Append("2"); + m_config->Append("3"); + m_config->Append("4"); + m_config->Append("5"); + sizer->Add(m_config, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_config->SetSelection(config - 1); + + wxStaticText* pttInvertLabel = new wxStaticText(this, -1, _("PTT Inversion")); + sizer->Add(pttInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_pttInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); + m_pttInvert->Append(_("Off")); + m_pttInvert->Append(_("On")); + sizer->Add(m_pttInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_pttInvert->SetSelection(pttInvert ? 1 : 0); + + wxStaticText* timeLabel = new wxStaticText(this, -1, _("Time (secs)")); + sizer->Add(timeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_time = new wxSlider(this, -1, time, 0, 300, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_time, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + sizer->Fit(this); + sizer->SetSizeHints(this); + + SetSizer(sizer); +} + + +CDStarRepeaterConfigControllerSet::~CDStarRepeaterConfigControllerSet() +{ +} + +bool CDStarRepeaterConfigControllerSet::Validate() +{ + if (m_type->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_config->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_pttInvert->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return true; +} + +wxString CDStarRepeaterConfigControllerSet::getType() const +{ + wxString type = m_type->GetStringSelection(); + + if (type.IsSameAs(_("None"))) + return wxEmptyString; + else + return type; +} + +unsigned int CDStarRepeaterConfigControllerSet::getConfig() const +{ + int n = m_config->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return 1U; + else + return n + 1U; +} + +bool CDStarRepeaterConfigControllerSet::getPTTInvert() const +{ + int n = m_pttInvert->GetCurrentSelection(); + + return n == 1; +} + +unsigned int CDStarRepeaterConfigControllerSet::getTime() const +{ + return m_time->GetValue(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.h b/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.h new file mode 100644 index 0000000..5c45df1 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012,2013,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigControllerSet_H +#define DStarRepeaterConfigControllerSet_H + +#include + +class CDStarRepeaterConfigControllerSet : public wxPanel { +public: + CDStarRepeaterConfigControllerSet(wxWindow* parent, int id, const wxString& title, const wxString& type, unsigned int config, bool pttInvert, unsigned int time); + virtual ~CDStarRepeaterConfigControllerSet(); + + virtual bool Validate(); + + virtual wxString getType() const; + + virtual unsigned int getConfig() const; + + virtual bool getPTTInvert() const; + + virtual unsigned int getTime() const; + +private: + wxString m_title; + wxChoice* m_type; + wxChoice* m_config; + wxChoice* m_pttInvert; + wxSlider* m_time; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.cpp new file mode 100644 index 0000000..c2515c7 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011-2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigDVAPSet.h" +#include "SerialPortSelector.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 100U; +const unsigned int NAME_WIDTH = 200U; + +CDStarRepeaterConfigDVAPSet::CDStarRepeaterConfigDVAPSet(wxWindow* parent, int id, const wxString& port, unsigned int frequency, int power, int squelch) : +wxDialog(parent, id, wxString(_("DVAP Modem Settings"))), +m_port(NULL), +m_band(NULL), +m_frequency(NULL), +m_power(NULL), +m_squelch(NULL) +{ + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_port = new wxChoice(this, -1, wxDefaultPosition, wxSize(NAME_WIDTH, -1)); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxArrayString ports = CSerialPortSelector::getDevices(); + for (unsigned int i = 0U; i < ports.GetCount(); i++) + m_port->Append(ports.Item(i)); + bool res = m_port->SetStringSelection(port); + if (!res) + m_port->SetSelection(0); + + wxStaticText* bandLabel = new wxStaticText(this, -1, _("Band")); + sizer->Add(bandLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_band = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_band->Append(wxT("144 MHz")); + m_band->Append(wxT("220 MHz")); + m_band->Append(wxT("430 MHz")); + sizer->Add(m_band, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + if (frequency > 400000000U) + m_band->SetSelection(2); + else if (frequency > 200000000U) + m_band->SetSelection(1); + else + m_band->SetSelection(0); + + wxStaticText* freqLabel = new wxStaticText(this, -1, _("Frequency (Hz)")); + sizer->Add(freqLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxString text; + text.Printf(wxT("%u"), frequency); + + m_frequency = new wxTextCtrl(this, -1, text, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + sizer->Add(m_frequency, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* powerLabel = new wxStaticText(this, -1, _("Power (dBm)")); + sizer->Add(powerLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_power = new wxSpinCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1), wxSP_ARROW_KEYS, -12, 10, power); + sizer->Add(m_power, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* squelchLabel = new wxStaticText(this, -1, _("Squelch (dBm)")); + sizer->Add(squelchLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_squelch = new wxSpinCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1), wxSP_ARROW_KEYS, -128, -45, squelch); + sizer->Add(m_squelch, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + topSizer->Add(sizer); + + topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + + topSizer->Fit(this); + topSizer->SetSizeHints(this); + + SetSizer(topSizer); +} + +CDStarRepeaterConfigDVAPSet::~CDStarRepeaterConfigDVAPSet() +{ +} + +bool CDStarRepeaterConfigDVAPSet::Validate() +{ + if (m_port->GetCurrentSelection() == wxNOT_FOUND) + return false; + + unsigned int freq = getFrequency(); + + int n = m_band->GetCurrentSelection(); + switch (n) { + case 0: + if (freq < 144000000U || freq > 148000000U) { + wxMessageDialog dialog(this, _("The Frequency is out of range"), _("DVAP Modem Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + break; + + case 1: + if (freq < 220000000U || freq > 225000000U) { + wxMessageDialog dialog(this, _("The Frequency is out of range"), _("DVAP Modem Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + break; + + case 2: + if (freq < 420000000U || freq > 450000000U) { + wxMessageDialog dialog(this, _("The Frequency is out of range"), _("DVAP Modem Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + break; + + default: + return false; + } + + return true; +} + +wxString CDStarRepeaterConfigDVAPSet::getPort() const +{ + return m_port->GetStringSelection(); +} + +unsigned int CDStarRepeaterConfigDVAPSet::getFrequency() const +{ + wxString hz = m_frequency->GetValue(); + + unsigned long frequency; + hz.ToULong(&frequency); + + return frequency; +} + +int CDStarRepeaterConfigDVAPSet::getPower() const +{ + int power = m_power->GetValue(); + + return power; +} + +int CDStarRepeaterConfigDVAPSet::getSquelch() const +{ + int power = m_squelch->GetValue(); + + return power; +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.h b/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.h new file mode 100644 index 0000000..338a2ec --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigDVAPSet_H +#define DStarRepeaterConfigDVAPSet_H + +#include "DStarRepeaterConfigDefs.h" +#include "DStarDefines.h" + +#include +#include + +class CDStarRepeaterConfigDVAPSet : public wxDialog { +public: + CDStarRepeaterConfigDVAPSet(wxWindow* parent, int id, const wxString& port, unsigned int frequency, int power, int squelch); + virtual ~CDStarRepeaterConfigDVAPSet(); + + virtual bool Validate(); + + virtual wxString getPort() const; + virtual unsigned int getFrequency() const; + virtual int getPower() const; + virtual int getSquelch() const; + +private: + wxChoice* m_port; + wxChoice* m_band; + wxTextCtrl* m_frequency; + wxSpinCtrl* m_power; + wxSpinCtrl* m_squelch; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.cpp new file mode 100644 index 0000000..9286693 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigDVMegaSet.h" +#include "SerialPortSelector.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 150U; +const unsigned int CONTROL_WIDTH2 = 300U; + +const unsigned int PORT_LENGTH = 5U; + +const int CHOICE_VARIANT = 8770; + +BEGIN_EVENT_TABLE(CDStarRepeaterConfigDVMegaSet, wxDialog) + EVT_CHOICE(CHOICE_VARIANT, CDStarRepeaterConfigDVMegaSet::onVariant) +END_EVENT_TABLE() + + +CDStarRepeaterConfigDVMegaSet::CDStarRepeaterConfigDVMegaSet(wxWindow* parent, int id, const wxString& port, DVMEGA_VARIANT variant, bool rxInvert, bool txInvert, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power) : +wxDialog(parent, id, wxString(_("DVMEGA Settings"))), +m_port(NULL), +m_variant(NULL), +m_txInvert(NULL), +m_rxInvert(NULL), +m_txDelay(NULL), +m_frequency(NULL), +m_power(NULL) +{ + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_port = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxArrayString ports = CSerialPortSelector::getDevices(); + for (unsigned int i = 0U; i < ports.GetCount(); i++) + m_port->Append(ports.Item(i)); + + bool found = m_port->SetStringSelection(port); + if (!found) + m_port->SetSelection(0); + + wxStaticText* variantLabel = new wxStaticText(this, -1, _("Variant")); + sizer->Add(variantLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_variant = new wxChoice(this, CHOICE_VARIANT, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_variant->Append(_("Modem")); + m_variant->Append(_("Radio - 2m")); + m_variant->Append(_("Radio - 70cm")); + m_variant->Append(_("Radio - 2m + 70cm")); + sizer->Add(m_variant, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_variant->SetSelection(int(variant)); + + wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); + sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_txInvert->Append(_("Off")); + m_txInvert->Append(_("On")); + sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_txInvert->SetSelection(txInvert ? 1 : 0); + + wxStaticText* rxInvertLabel = new wxStaticText(this, -1, _("RX Inversion")); + sizer->Add(rxInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_rxInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_rxInvert->Append(_("Off")); + m_rxInvert->Append(_("On")); + sizer->Add(m_rxInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_rxInvert->SetSelection(rxInvert ? 1 : 0); + + wxStaticText* freqLabel = new wxStaticText(this, -1, _("Frequency (Hz)")); + sizer->Add(freqLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxString text; + if (rxFrequency == txFrequency) + text.Printf(wxT("%u"), rxFrequency); + else + text.Printf(wxT("%u/%u"), rxFrequency, txFrequency); + + m_frequency = new wxTextCtrl(this, -1, text, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + sizer->Add(m_frequency, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); + sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txDelay = new wxSlider(this, -1, txDelay, 0, 350, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* powerLabel = new wxStaticText(this, -1, _("Power (%)")); + sizer->Add(powerLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_power = new wxSlider(this, -1, power, 1, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_power, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + topSizer->Add(sizer); + + topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + switch (variant) { + case DVMV_RADIO_2M: + case DVMV_RADIO_70CM: + case DVMV_RADIO_2M_70CM: + m_txInvert->Disable(); + m_rxInvert->Disable(); + break; + default: + m_frequency->Disable(); + m_power->Disable(); + break; + } + + SetAutoLayout(true); + + topSizer->Fit(this); + topSizer->SetSizeHints(this); + + SetSizer(topSizer); +} + +CDStarRepeaterConfigDVMegaSet::~CDStarRepeaterConfigDVMegaSet() +{ +} + +bool CDStarRepeaterConfigDVMegaSet::Validate() +{ + if (m_port->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_variant->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_txInvert->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_rxInvert->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_variant->GetSelection() == 1) { + unsigned int frequency = getRXFrequency(); + + if (frequency >= 144000000U && frequency < 148000000U) + return true; + else + return false; + } else if (m_variant->GetSelection() == 2) { + unsigned int frequency = getRXFrequency(); + + if (frequency >= 420000000U && frequency < 450000000U) + return true; + else + return false; + } else if (m_variant->GetSelection() == 3) { + unsigned int rxFrequency = getRXFrequency(); + unsigned int txFrequency = getTXFrequency(); + + if (rxFrequency == txFrequency) { + if ((rxFrequency >= 144000000U && rxFrequency < 148000000U) || + (rxFrequency >= 420000000U && rxFrequency < 450000000U)) + return true; + else + return false; + } else { + bool rx2m = (rxFrequency >= 144000000U && rxFrequency < 148000000U); + bool rx70cm = (rxFrequency >= 420000000U && rxFrequency < 450000000U); + + bool tx2m = (txFrequency >= 144000000U && txFrequency < 148000000U); + bool tx70cm = (txFrequency >= 420000000U && txFrequency < 450000000U); + + if (rx2m && tx70cm) + return true; + else if (rx70cm && tx2m) + return true; + else + return false; + } + } + + return true; +} + +wxString CDStarRepeaterConfigDVMegaSet::getPort() const +{ + int n = m_port->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return wxEmptyString; + + return m_port->GetStringSelection(); +} + +DVMEGA_VARIANT CDStarRepeaterConfigDVMegaSet::getVariant() const +{ + int n = m_variant->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return DVMV_MODEM; + + return DVMEGA_VARIANT(n); +} + +bool CDStarRepeaterConfigDVMegaSet::getRXInvert() const +{ + int n = m_rxInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +bool CDStarRepeaterConfigDVMegaSet::getTXInvert() const +{ + int n = m_txInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +unsigned int CDStarRepeaterConfigDVMegaSet::getTXDelay() const +{ + return (unsigned int)m_txDelay->GetValue(); +} + +unsigned int CDStarRepeaterConfigDVMegaSet::getRXFrequency() const +{ + wxString hz = m_frequency->GetValue(); + + int pos = hz.Find(wxT('/')); + if (pos != wxNOT_FOUND) { + unsigned long frequency; + hz.Left(pos).ToULong(&frequency); + return frequency; + } else { + unsigned long frequency; + hz.ToULong(&frequency); + return frequency; + } +} + +unsigned int CDStarRepeaterConfigDVMegaSet::getTXFrequency() const +{ + DVMEGA_VARIANT variant = getVariant(); + wxString hz = m_frequency->GetValue(); + + int pos = hz.Find(wxT('/')); + if (pos != wxNOT_FOUND && variant == DVMV_RADIO_2M_70CM) { + unsigned long frequency; + hz.Mid(pos + 1).ToULong(&frequency); + return frequency; + } else { + if (pos != wxNOT_FOUND) { + unsigned long frequency; + hz.Left(pos).ToULong(&frequency); + return frequency; + } else { + unsigned long frequency; + hz.ToULong(&frequency); + return frequency; + } + } +} + +unsigned int CDStarRepeaterConfigDVMegaSet::getPower() const +{ + return (unsigned int)m_power->GetValue(); +} + +void CDStarRepeaterConfigDVMegaSet::onVariant(wxCommandEvent &event) +{ + DVMEGA_VARIANT variant = getVariant(); + + switch (variant) { + case DVMV_RADIO_2M: + case DVMV_RADIO_70CM: + case DVMV_RADIO_2M_70CM: + m_txInvert->Disable(); + m_rxInvert->Disable(); + m_frequency->Enable(); + m_power->Enable(); + break; + default: // DVMV_MODEM + m_txInvert->Enable(); + m_rxInvert->Enable(); + m_frequency->Disable(); + m_power->Disable(); + break; + } +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.h b/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.h new file mode 100644 index 0000000..fcea64e --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigDVMegaSet_H +#define DStarRepeaterConfigDVMegaSet_H + +#include "DStarRepeaterConfigDefs.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigDVMegaSet : public wxDialog { +public: + CDStarRepeaterConfigDVMegaSet(wxWindow* parent, int id, const wxString& port, DVMEGA_VARIANT variant, bool rxInvert, bool txInvert, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power); + virtual ~CDStarRepeaterConfigDVMegaSet(); + + virtual bool Validate(); + + virtual wxString getPort() const; + virtual DVMEGA_VARIANT getVariant() const; + virtual bool getRXInvert() const; + virtual bool getTXInvert() const; + virtual unsigned int getTXDelay() const; + virtual unsigned int getRXFrequency() const; + virtual unsigned int getTXFrequency() const; + virtual unsigned int getPower() const; + + virtual void onVariant(wxCommandEvent& event); + +private: + wxChoice* m_port; + wxChoice* m_variant; + wxChoice* m_txInvert; + wxChoice* m_rxInvert; + wxSlider* m_txDelay; + wxTextCtrl* m_frequency; + wxSlider* m_power; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.cpp new file mode 100644 index 0000000..ae09700 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigDVRPTR1Set.h" +#include "SerialPortSelector.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 150U; +const unsigned int CONTROL_WIDTH2 = 300U; + +const unsigned int ADDRESS_LENGTH = 15U; +const unsigned int PORT_LENGTH = 5U; + + +CDStarRepeaterConfigDVRPTR1Set::CDStarRepeaterConfigDVRPTR1Set(wxWindow* parent, int id, const wxString& port, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay) : +wxDialog(parent, id, wxString(_("DV-RPTR V1 Settings"))), +m_port(NULL), +m_txInvert(NULL), +m_rxInvert(NULL), +m_channel(NULL), +m_modLevel(NULL), +m_txDelay(NULL) +{ + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_port = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxArrayString ports = CSerialPortSelector::getDevices(); + for (unsigned int i = 0U; i < ports.GetCount(); i++) + m_port->Append(ports.Item(i)); + + bool found = m_port->SetStringSelection(port); + if (!found) + m_port->SetSelection(0); + + wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); + sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_txInvert->Append(_("Off")); + m_txInvert->Append(_("On")); + sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_txInvert->SetSelection(txInvert ? 1 : 0); + + wxStaticText* rxInvertLabel = new wxStaticText(this, -1, _("RX Inversion")); + sizer->Add(rxInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_rxInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_rxInvert->Append(_("Off")); + m_rxInvert->Append(_("On")); + sizer->Add(m_rxInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_rxInvert->SetSelection(rxInvert ? 1 : 0); + + wxStaticText* channelLabel = new wxStaticText(this, -1, _("Channel")); + sizer->Add(channelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_channel = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_channel->Append(_("A / FSK Pin")); + m_channel->Append(_("B / AFSK Pin")); + sizer->Add(m_channel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_channel->SetSelection(channel ? 1 : 0); + + wxStaticText* modLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); + sizer->Add(modLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_modLevel = new wxSlider(this, -1, modLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_modLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); + sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txDelay = new wxSlider(this, -1, txDelay, 0, 500, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + topSizer->Add(sizer); + + topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + + topSizer->Fit(this); + topSizer->SetSizeHints(this); + + SetSizer(topSizer); +} + +CDStarRepeaterConfigDVRPTR1Set::~CDStarRepeaterConfigDVRPTR1Set() +{ +} + +bool CDStarRepeaterConfigDVRPTR1Set::Validate() +{ + if (m_port->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_txInvert->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_rxInvert->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return m_channel->GetCurrentSelection() != wxNOT_FOUND; +} + +wxString CDStarRepeaterConfigDVRPTR1Set::getPort() const +{ + int n = m_port->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return wxEmptyString; + + return m_port->GetStringSelection(); +} + +bool CDStarRepeaterConfigDVRPTR1Set::getRXInvert() const +{ + int n = m_rxInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +bool CDStarRepeaterConfigDVRPTR1Set::getTXInvert() const +{ + int n = m_txInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +bool CDStarRepeaterConfigDVRPTR1Set::getChannel() const +{ + int n = m_channel->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +unsigned int CDStarRepeaterConfigDVRPTR1Set::getModLevel() const +{ + return (unsigned int)m_modLevel->GetValue(); +} + +unsigned int CDStarRepeaterConfigDVRPTR1Set::getTXDelay() const +{ + return (unsigned int)m_txDelay->GetValue(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.h b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.h new file mode 100644 index 0000000..66c8ed9 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigDVRPTR1Set_H +#define DStarRepeaterConfigDVRPTR1Set_H + +#include "DStarRepeaterConfigDefs.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigDVRPTR1Set : public wxDialog { +public: + CDStarRepeaterConfigDVRPTR1Set(wxWindow* parent, int id, const wxString& port, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay); + virtual ~CDStarRepeaterConfigDVRPTR1Set(); + + virtual bool Validate(); + + virtual wxString getPort() const; + virtual bool getRXInvert() const; + virtual bool getTXInvert() const; + virtual bool getChannel() const; + virtual unsigned int getModLevel() const; + virtual unsigned int getTXDelay() const; + +private: + wxChoice* m_port; + wxChoice* m_txInvert; + wxChoice* m_rxInvert; + wxChoice* m_channel; + wxSlider* m_modLevel; + wxSlider* m_txDelay; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.cpp new file mode 100644 index 0000000..71eea73 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigDVRPTR2Set.h" +#include "SerialPortSelector.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 150U; +const unsigned int CONTROL_WIDTH2 = 300U; + +const unsigned int ADDRESS_LENGTH = 15U; +const unsigned int PORT_LENGTH = 5U; + +const int CHOICE_TYPE = 8750; + +BEGIN_EVENT_TABLE(CDStarRepeaterConfigDVRPTR2Set, wxDialog) + EVT_CHOICE(CHOICE_TYPE, CDStarRepeaterConfigDVRPTR2Set::onConnectionType) +END_EVENT_TABLE() + + +CDStarRepeaterConfigDVRPTR2Set::CDStarRepeaterConfigDVRPTR2Set(wxWindow* parent, int id, CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay) : +wxDialog(parent, id, wxString(_("DV-RPTR V2 Settings"))), +m_connectionType(NULL), +m_usbPort(NULL), +m_address(NULL), +m_port(NULL), +m_txInvert(NULL), +m_modLevel(NULL), +m_txDelay(NULL) +{ + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* connectionLabel = new wxStaticText(this, -1, _("Connection")); + sizer->Add(connectionLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_connectionType = new wxChoice(this, CHOICE_TYPE, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_connectionType->Append(wxT("USB")); + m_connectionType->Append(_("Network")); + sizer->Add(m_connectionType, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_connectionType->SetSelection(connectionType == CT_NETWORK ? 1 : 0); + + wxStaticText* usbPortLabel = new wxStaticText(this, -1, _("USB Port")); + sizer->Add(usbPortLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_usbPort = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + sizer->Add(m_usbPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxArrayString ports = CSerialPortSelector::getDevices(); + for (unsigned int i = 0U; i < ports.GetCount(); i++) + m_usbPort->Append(ports.Item(i)); + + bool found = m_usbPort->SetStringSelection(usbPort); + if (!found) + m_usbPort->SetSelection(0); + + wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); + sizer->Add(addressLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_address = new CAddressTextCtrl(this, -1, address, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_address->SetMaxLength(ADDRESS_LENGTH); + sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), port); + + m_port = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_port->SetMaxLength(PORT_LENGTH); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); + sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_txInvert->Append(_("Off")); + m_txInvert->Append(_("On")); + sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_txInvert->SetSelection(txInvert ? 1 : 0); + + wxStaticText* modLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); + sizer->Add(modLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_modLevel = new wxSlider(this, -1, modLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_modLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); + sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txDelay = new wxSlider(this, -1, txDelay, 100, 850, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + switch (connectionType) { + case CT_NETWORK: + m_usbPort->Disable(); + m_address->Enable(); + m_port->Enable(); + break; + default: + m_usbPort->Enable(); + m_address->Disable(); + m_port->Disable(); + break; + } + + topSizer->Add(sizer); + + topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + + topSizer->Fit(this); + topSizer->SetSizeHints(this); + + SetSizer(topSizer); +} + +CDStarRepeaterConfigDVRPTR2Set::~CDStarRepeaterConfigDVRPTR2Set() +{ +} + +bool CDStarRepeaterConfigDVRPTR2Set::Validate() +{ + if (m_connectionType->GetCurrentSelection() == wxNOT_FOUND) + return false; + + CONNECTION_TYPE type = getConnectionType(); + if (type == CT_USB) { + if (m_usbPort->GetCurrentSelection() == wxNOT_FOUND) + return false; + } else { + unsigned int port = getPort(); + + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Port is not valid"), _("DV-RPTR V2 Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + } + + return m_txInvert->GetCurrentSelection() != wxNOT_FOUND; +} + +CONNECTION_TYPE CDStarRepeaterConfigDVRPTR2Set::getConnectionType() const +{ + int n = m_connectionType->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return CT_USB; + + return n == 1 ? CT_NETWORK : CT_USB; +} + +wxString CDStarRepeaterConfigDVRPTR2Set::getUSBPort() const +{ + int n = m_usbPort->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return wxEmptyString; + + return m_usbPort->GetStringSelection(); +} + +wxString CDStarRepeaterConfigDVRPTR2Set::getAddress() const +{ + return m_address->GetValue(); +} + +unsigned int CDStarRepeaterConfigDVRPTR2Set::getPort() const +{ + unsigned long n; + + m_port->GetValue().ToULong(&n); + + return n; +} + +bool CDStarRepeaterConfigDVRPTR2Set::getTXInvert() const +{ + int n = m_txInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +unsigned int CDStarRepeaterConfigDVRPTR2Set::getModLevel() const +{ + return (unsigned int)m_modLevel->GetValue(); +} + +unsigned int CDStarRepeaterConfigDVRPTR2Set::getTXDelay() const +{ + return (unsigned int)m_txDelay->GetValue(); +} + +void CDStarRepeaterConfigDVRPTR2Set::onConnectionType(wxCommandEvent &event) +{ + int n = event.GetSelection(); + + switch (n) { + case 1: // CT_NETWORK + m_usbPort->Disable(); + m_address->Enable(); + m_port->Enable(); + break; + default: // CT_USB + m_usbPort->Enable(); + m_address->Disable(); + m_port->Disable(); + break; + } +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.h b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.h new file mode 100644 index 0000000..1198e61 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigDVRPTR2Set_H +#define DStarRepeaterConfigDVRPTR2Set_H + +#include "DStarRepeaterConfigDefs.h" +#include "AddressTextCtrl.h" +#include "DStarDefines.h" +#include "PortTextCtrl.h" + +#include + +class CDStarRepeaterConfigDVRPTR2Set : public wxDialog { +public: + CDStarRepeaterConfigDVRPTR2Set(wxWindow* parent, int id, CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay); + virtual ~CDStarRepeaterConfigDVRPTR2Set(); + + virtual bool Validate(); + + virtual CONNECTION_TYPE getConnectionType() const; + virtual wxString getUSBPort() const; + virtual wxString getAddress() const; + virtual unsigned int getPort() const; + virtual bool getTXInvert() const; + virtual unsigned int getModLevel() const; + virtual unsigned int getTXDelay() const; + + virtual void onConnectionType(wxCommandEvent& event); + +private: + wxChoice* m_connectionType; + wxChoice* m_usbPort; + CAddressTextCtrl* m_address; + CPortTextCtrl* m_port; + wxChoice* m_txInvert; + wxSlider* m_modLevel; + wxSlider* m_txDelay; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.cpp new file mode 100644 index 0000000..568eed5 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigDVRPTR3Set.h" +#include "SerialPortSelector.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 150U; +const unsigned int CONTROL_WIDTH2 = 300U; + +const unsigned int ADDRESS_LENGTH = 15U; +const unsigned int PORT_LENGTH = 5U; + +const int CHOICE_TYPE = 8750; + +BEGIN_EVENT_TABLE(CDStarRepeaterConfigDVRPTR3Set, wxDialog) + EVT_CHOICE(CHOICE_TYPE, CDStarRepeaterConfigDVRPTR3Set::onConnectionType) +END_EVENT_TABLE() + + +CDStarRepeaterConfigDVRPTR3Set::CDStarRepeaterConfigDVRPTR3Set(wxWindow* parent, int id, CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay) : +wxDialog(parent, id, wxString(_("DV-RPTR V3 Settings"))), +m_connectionType(NULL), +m_usbPort(NULL), +m_address(NULL), +m_port(NULL), +m_txInvert(NULL), +m_modLevel(NULL), +m_txDelay(NULL) +{ + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* connectionLabel = new wxStaticText(this, -1, _("Connection")); + sizer->Add(connectionLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_connectionType = new wxChoice(this, CHOICE_TYPE, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_connectionType->Append(wxT("USB")); + m_connectionType->Append(_("Network")); + sizer->Add(m_connectionType, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_connectionType->SetSelection(connectionType == CT_NETWORK ? 1 : 0); + + wxStaticText* usbPortLabel = new wxStaticText(this, -1, _("USB Port")); + sizer->Add(usbPortLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_usbPort = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + sizer->Add(m_usbPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxArrayString ports = CSerialPortSelector::getDevices(); + for (unsigned int i = 0U; i < ports.GetCount(); i++) + m_usbPort->Append(ports.Item(i)); + + bool found = m_usbPort->SetStringSelection(usbPort); + if (!found) + m_usbPort->SetSelection(0); + + wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); + sizer->Add(addressLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_address = new CAddressTextCtrl(this, -1, address, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_address->SetMaxLength(ADDRESS_LENGTH); + sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), port); + + m_port = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_port->SetMaxLength(PORT_LENGTH); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); + sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_txInvert->Append(_("Off")); + m_txInvert->Append(_("On")); + sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_txInvert->SetSelection(txInvert ? 1 : 0); + + wxStaticText* modLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); + sizer->Add(modLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_modLevel = new wxSlider(this, -1, modLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_modLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); + sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txDelay = new wxSlider(this, -1, txDelay, 100, 850, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + switch (connectionType) { + case CT_NETWORK: + m_usbPort->Disable(); + m_address->Enable(); + m_port->Enable(); + break; + default: + m_usbPort->Enable(); + m_address->Disable(); + m_port->Disable(); + break; + } + + topSizer->Add(sizer); + + topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + + topSizer->Fit(this); + topSizer->SetSizeHints(this); + + SetSizer(topSizer); +} + +CDStarRepeaterConfigDVRPTR3Set::~CDStarRepeaterConfigDVRPTR3Set() +{ +} + +bool CDStarRepeaterConfigDVRPTR3Set::Validate() +{ + if (m_connectionType->GetCurrentSelection() == wxNOT_FOUND) + return false; + + CONNECTION_TYPE type = getConnectionType(); + if (type == CT_USB) { + if (m_usbPort->GetCurrentSelection() == wxNOT_FOUND) + return false; + } else { + unsigned int port = getPort(); + + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Port is not valid"), _("DV-RPTR V2 Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + } + + return m_txInvert->GetCurrentSelection() != wxNOT_FOUND; +} + +CONNECTION_TYPE CDStarRepeaterConfigDVRPTR3Set::getConnectionType() const +{ + int n = m_connectionType->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return CT_USB; + + return n == 1 ? CT_NETWORK : CT_USB; +} + +wxString CDStarRepeaterConfigDVRPTR3Set::getUSBPort() const +{ + int n = m_usbPort->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return wxEmptyString; + + return m_usbPort->GetStringSelection(); +} + +wxString CDStarRepeaterConfigDVRPTR3Set::getAddress() const +{ + return m_address->GetValue(); +} + +unsigned int CDStarRepeaterConfigDVRPTR3Set::getPort() const +{ + unsigned long n; + + m_port->GetValue().ToULong(&n); + + return n; +} + +bool CDStarRepeaterConfigDVRPTR3Set::getTXInvert() const +{ + int n = m_txInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +unsigned int CDStarRepeaterConfigDVRPTR3Set::getModLevel() const +{ + return (unsigned int)m_modLevel->GetValue(); +} + +unsigned int CDStarRepeaterConfigDVRPTR3Set::getTXDelay() const +{ + return (unsigned int)m_txDelay->GetValue(); +} + +void CDStarRepeaterConfigDVRPTR3Set::onConnectionType(wxCommandEvent &event) +{ + int n = event.GetSelection(); + + switch (n) { + case 1: // CT_NETWORK + m_usbPort->Disable(); + m_address->Enable(); + m_port->Enable(); + break; + default: // CT_USB + m_usbPort->Enable(); + m_address->Disable(); + m_port->Disable(); + break; + } +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.h b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.h new file mode 100644 index 0000000..10b0718 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigDVRPTR3Set_H +#define DStarRepeaterConfigDVRPTR3Set_H + +#include "DStarRepeaterConfigDefs.h" +#include "AddressTextCtrl.h" +#include "DStarDefines.h" +#include "PortTextCtrl.h" + +#include + +class CDStarRepeaterConfigDVRPTR3Set : public wxDialog { +public: + CDStarRepeaterConfigDVRPTR3Set(wxWindow* parent, int id, CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay); + virtual ~CDStarRepeaterConfigDVRPTR3Set(); + + virtual bool Validate(); + + virtual CONNECTION_TYPE getConnectionType() const; + virtual wxString getUSBPort() const; + virtual wxString getAddress() const; + virtual unsigned int getPort() const; + virtual bool getTXInvert() const; + virtual unsigned int getModLevel() const; + virtual unsigned int getTXDelay() const; + + virtual void onConnectionType(wxCommandEvent& event); + +private: + wxChoice* m_connectionType; + wxChoice* m_usbPort; + CAddressTextCtrl* m_address; + CPortTextCtrl* m_port; + wxChoice* m_txInvert; + wxSlider* m_modLevel; + wxSlider* m_txDelay; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDefs.h b/DStarRepeaterConfig/DStarRepeaterConfigDefs.h new file mode 100644 index 0000000..c358199 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigDefs.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigDefs_H +#define DStarRepeaterConfigDefs_H + +#include + +const wxString APPLICATION_NAME = wxT("D-Star Repeater"); +const wxString LOG_BASE_NAME = wxT("DStarRepeater"); +const wxString CONFIG_FILE_NAME = wxT("dstarrepeater"); + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigFrame.cpp b/DStarRepeaterConfig/DStarRepeaterConfigFrame.cpp new file mode 100644 index 0000000..6fd8f39 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigFrame.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigFrame.h" +#include "Version.h" + +#include +#include + +enum { + Menu_File_Save = 6000 +}; + +BEGIN_EVENT_TABLE(CDStarRepeaterConfigFrame, wxFrame) + EVT_MENU(wxID_EXIT, CDStarRepeaterConfigFrame::onQuit) + EVT_MENU(Menu_File_Save, CDStarRepeaterConfigFrame::onSave) + EVT_MENU(wxID_ABOUT, CDStarRepeaterConfigFrame::onAbout) + + EVT_CLOSE(CDStarRepeaterConfigFrame::onClose) +END_EVENT_TABLE() + +#if defined(__WINDOWS__) +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 100U; +const unsigned int LABEL_WIDTH = 70U; +#else +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 150U; +const unsigned int LABEL_WIDTH = 70U; +#endif + +CDStarRepeaterConfigFrame::CDStarRepeaterConfigFrame(const wxString& title, const wxString& confDir, const wxString& name) : +wxFrame(NULL, -1, title), +m_config(NULL), +m_callsign(NULL), +m_network(NULL), +m_times(NULL), +m_beacon(NULL), +m_announcement(NULL), +m_modem(NULL), +m_control1(NULL), +m_control2(NULL), +m_controller(NULL) +{ + SetMenuBar(createMenuBar()); + + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxPanel* panel = new wxPanel(this, -1); + + wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); + + wxNotebook* noteBook = new wxNotebook(panel, -1); + +#if defined(__WINDOWS__) + if (confDir.IsEmpty()) + m_config = new CDStarRepeaterConfig(new wxConfig(APPLICATION_NAME), ::wxGetHomeDir(), CONFIG_FILE_NAME, name); + else + m_config = new CDStarRepeaterConfig(new wxConfig(APPLICATION_NAME), confDir, CONFIG_FILE_NAME, name); +#else + if (confDir.IsEmpty()) + m_config = new CDStarRepeaterConfig(wxT(CONF_DIR), CONFIG_FILE_NAME, name); + else + m_config = new CDStarRepeaterConfig(confDir, CONFIG_FILE_NAME, name); +#endif + + wxString callsign, gateway; + DSTAR_MODE mode; + ACK_TYPE ack; + bool restriction, rpt1Validation, dtmfBlanking, errorReply; + m_config->getCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); + + m_callsign = new CDStarRepeaterConfigCallsignSet(noteBook, -1, APPLICATION_NAME, callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); + noteBook->AddPage(m_callsign, _("Callsign"), true); + + wxString gatewayAddress, localAddress, netName; + unsigned int gatewayPort, localPort; + m_config->getNetwork(gatewayAddress, gatewayPort, localAddress, localPort, netName); + + m_network = new CDStarRepeaterConfigNetworkSet(noteBook, -1, APPLICATION_NAME, gatewayAddress, gatewayPort, localAddress, localPort, netName); + noteBook->AddPage(m_network, _("Network"), false); + + unsigned int timeout, ackTime; + m_config->getTimes(timeout, ackTime); + + m_times = new CDStarRepeaterConfigTimesSet(noteBook, -1, APPLICATION_NAME, timeout, ackTime); + noteBook->AddPage(m_times, _("Timers"), false); + + bool announcementEnabled; + unsigned int announcementTime; + wxString announcementRecordRPT1, announcementRecordRPT2; + wxString announcementDeleteRPT1, announcementDeleteRPT2; + m_config->getAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); + + m_announcement = new CDStarRepeaterConfigAnnouncementSet(noteBook, -1, APPLICATION_NAME, announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); + noteBook->AddPage(m_announcement, _("Announcement"), false); + + unsigned int beaconTime; + wxString beaconText; + bool beaconVoice; + TEXT_LANG language; + m_config->getBeacon(beaconTime, beaconText, beaconVoice, language); + + m_beacon = new CDStarRepeaterConfigBeaconSet(noteBook, -1, APPLICATION_NAME, beaconTime, beaconText, beaconVoice, language); + noteBook->AddPage(m_beacon, _("Beacon"), false); + + wxString modemType; + m_config->getModem(modemType); + + m_modem = new CDStarRepeaterConfigModemSet(noteBook, -1, APPLICATION_NAME, m_config, modemType); + noteBook->AddPage(m_modem, _("Modem"), false); + + bool enabled; + wxString rpt1Callsign, rpt2Callsign; + wxString shutdown, startup; + wxString status1, status2, status3, status4, status5; + wxString command1, command1Line, command2, command2Line; + wxString command3, command3Line, command4, command4Line; + wxString command5, command5Line, command6, command6Line; + wxString output1, output2, output3, output4; + m_config->getControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status1, status2, status3, status4, status5, command1, command1Line, command2, command2Line, command3, command3Line, command4, command4Line, command5, command5Line, command6, command6Line, output1, output2, output3, output4); + + m_control1 = new CDStarRepeaterConfigControl1Set(noteBook, -1, APPLICATION_NAME, enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status1, status2, status3, status4, status5, output1, output2, output3, output4); + noteBook->AddPage(m_control1, _("Control 1"), false); + + m_control2 = new CDStarRepeaterConfigControl2Set(noteBook, -1, APPLICATION_NAME, command1, command1Line, command2, command2Line, command3, command3Line, command4, command4Line, command5, command5Line, command6, command6Line); + noteBook->AddPage(m_control2, _("Control 2"), false); + + wxString controllerType; + unsigned int serialConfig, activeHangTime; + bool pttInvert; + m_config->getController(controllerType, serialConfig, pttInvert, activeHangTime); + + m_controller = new CDStarRepeaterConfigControllerSet(noteBook, -1, APPLICATION_NAME, controllerType, serialConfig, pttInvert, activeHangTime); + noteBook->AddPage(m_controller, _("Controller"), false); + + sizer->Add(noteBook, 0, wxEXPAND | wxALL, BORDER_SIZE); + + panel->SetSizer(sizer); + + mainSizer->Add(panel, 0, wxEXPAND | wxALL, BORDER_SIZE); + + mainSizer->SetSizeHints(this); + + SetSizer(mainSizer); +} + +CDStarRepeaterConfigFrame::~CDStarRepeaterConfigFrame() +{ + delete m_config; +} + +wxMenuBar* CDStarRepeaterConfigFrame::createMenuBar() +{ + wxMenu* fileMenu = new wxMenu(); + fileMenu->Append(Menu_File_Save, _("Save")); + fileMenu->AppendSeparator(); + fileMenu->Append(wxID_EXIT, _("Exit")); + + wxMenu* helpMenu = new wxMenu(); + helpMenu->Append(wxID_ABOUT, _("About D-Star Repeater Config")); + + wxMenuBar* menuBar = new wxMenuBar(); + menuBar->Append(fileMenu, _("File")); + menuBar->Append(helpMenu, _("Help")); + + return menuBar; +} + +void CDStarRepeaterConfigFrame::onQuit(wxCommandEvent& event) +{ + Close(false); +} + +void CDStarRepeaterConfigFrame::onClose(wxCloseEvent& event) +{ + Destroy(); +} + +void CDStarRepeaterConfigFrame::onAbout(wxCommandEvent& event) +{ + wxAboutDialogInfo info; + info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); + info.SetCopyright(wxT("(C) 2013,2014.2015 using GPL v2 or later")); + info.SetName(APPLICATION_NAME); + info.SetVersion(VERSION); + info.SetDescription(_("This program configures the D-Star Repeater.")); + + ::wxAboutBox(info); +} + +void CDStarRepeaterConfigFrame::onSave(wxCommandEvent& event) +{ + if (!m_callsign->Validate() || !m_network->Validate() || !m_times->Validate() || !m_beacon->Validate() || + !m_announcement->Validate() || !m_modem->Validate() || !m_control1->Validate() || !m_control2->Validate() || + !m_controller->Validate()) + return; + + wxString callsign = m_callsign->getCallsign(); + wxString gateway = m_callsign->getGateway(); + DSTAR_MODE mode = m_callsign->getMode(); + ACK_TYPE ack = m_callsign->getAck(); + bool restriction = m_callsign->getRestriction(); + bool rpt1Validation = m_callsign->getRPT1Validation(); + bool dtmfBlanking = m_callsign->getDTMFBlanking(); + bool errorReply = m_callsign->getErrorReply(); + m_config->setCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); + + wxString gatewayAddress = m_network->getGatewayAddress(); + unsigned int gatewayPort = m_network->getGatewayPort(); + wxString localAddress = m_network->getLocalAddress(); + unsigned int localPort = m_network->getLocalPort(); + wxString netName = m_network->getName(); + m_config->setNetwork(gatewayAddress, gatewayPort, localAddress, localPort, netName); + + unsigned int timeout = m_times->getTimeout(); + unsigned int ackTime = m_times->getAckTime(); + m_config->setTimes(timeout, ackTime); + + unsigned int beaconTime = m_beacon->getTime(); + wxString beaconText = m_beacon->getText(); + bool beaconVoice = m_beacon->getVoice(); + TEXT_LANG language = m_beacon->getLanguage(); + m_config->setBeacon(beaconTime, beaconText, beaconVoice, language); + + bool announcementEnabled = m_announcement->getEnabled(); + unsigned int announcementTime = m_announcement->getTime(); + wxString announcementRecordRPT1 = m_announcement->getRecordRPT1(); + wxString announcementRecordRPT2 = m_announcement->getRecordRPT2(); + wxString announcementDeleteRPT1 = m_announcement->getDeleteRPT1(); + wxString announcementDeleteRPT2 = m_announcement->getDeleteRPT2(); + m_config->setAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); + + wxString modemType = m_modem->getType(); + m_config->setModem(modemType); + + bool enabled = m_control1->getEnabled(); + wxString rpt1Callsign = m_control1->getRPT1Callsign(); + wxString rpt2Callsign = m_control1->getRPT2Callsign(); + wxString shutdown = m_control1->getShutdown(); + wxString startup = m_control1->getStartup(); + wxString status1 = m_control1->getStatus1(); + wxString status2 = m_control1->getStatus2(); + wxString status3 = m_control1->getStatus3(); + wxString status4 = m_control1->getStatus4(); + wxString status5 = m_control1->getStatus5(); + wxString output1 = m_control1->getOutput1(); + wxString output2 = m_control1->getOutput2(); + wxString output3 = m_control1->getOutput3(); + wxString output4 = m_control1->getOutput4(); + wxString command1 = m_control2->getCommand1(); + wxString command1Line = m_control2->getCommand1Line(); + wxString command2 = m_control2->getCommand2(); + wxString command2Line = m_control2->getCommand2Line(); + wxString command3 = m_control2->getCommand3(); + wxString command3Line = m_control2->getCommand3Line(); + wxString command4 = m_control2->getCommand4(); + wxString command4Line = m_control2->getCommand4Line(); + wxString command5 = m_control2->getCommand5(); + wxString command5Line = m_control2->getCommand5Line(); + wxString command6 = m_control2->getCommand6(); + wxString command6Line = m_control2->getCommand6Line(); + m_config->setControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status1, status2, status3, status4, status5, command1, command1Line, command2, command2Line, command3, command3Line, command4, command4Line, command5, command5Line, command6, command6Line, output1, output2, output3, output4); + + wxString controllerType = m_controller->getType(); + unsigned int serialConfig = m_controller->getConfig(); + bool pttInvert = m_controller->getPTTInvert(); + unsigned int activeHangTime = m_controller->getTime(); + m_config->setController(controllerType, serialConfig, pttInvert, activeHangTime); + + bool ret = m_config->write(); + if (!ret) { + wxMessageDialog dialog(this, _("There was an error when writing the D-Star Repeater configuration file"), _("Error"), wxICON_ERROR); + dialog.ShowModal(); + } else { + wxMessageDialog dialog(this, _("The changes made will not take effect\nuntil the D-Star Repeater is (re)started"), _("Information"), wxICON_INFORMATION); + dialog.ShowModal(); + } +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigFrame.h b/DStarRepeaterConfig/DStarRepeaterConfigFrame.h new file mode 100644 index 0000000..207458f --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigFrame.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigFrame_H +#define DStarRepeaterConfigFrame_H + +#include "DStarRepeaterConfigAnnouncementSet.h" +#include "DStarRepeaterConfigControllerSet.h" +#include "DStarRepeaterConfigCallsignSet.h" +#include "DStarRepeaterConfigControl1Set.h" +#include "DStarRepeaterConfigControl2Set.h" +#include "DStarRepeaterConfigNetworkSet.h" +#include "DStarRepeaterConfigBeaconSet.h" +#include "DStarRepeaterConfigModemSet.h" +#include "DStarRepeaterConfigTimesSet.h" +#include "DStarRepeaterConfigDefs.h" +#include "DStarRepeaterConfig.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigFrame : public wxFrame { +public: + CDStarRepeaterConfigFrame(const wxString& title, const wxString& confDir, const wxString& name); + virtual ~CDStarRepeaterConfigFrame(); + + virtual void onQuit(wxCommandEvent& event); + virtual void onSave(wxCommandEvent& event); + virtual void onAbout(wxCommandEvent& event); + virtual void onClose(wxCloseEvent& event); + +private: + CDStarRepeaterConfig* m_config; + CDStarRepeaterConfigCallsignSet* m_callsign; + CDStarRepeaterConfigNetworkSet* m_network; + CDStarRepeaterConfigTimesSet* m_times; + CDStarRepeaterConfigBeaconSet* m_beacon; + CDStarRepeaterConfigAnnouncementSet* m_announcement; + CDStarRepeaterConfigModemSet* m_modem; + CDStarRepeaterConfigControl1Set* m_control1; + CDStarRepeaterConfigControl2Set* m_control2; + CDStarRepeaterConfigControllerSet* m_controller; + + DECLARE_EVENT_TABLE() + + wxMenuBar* createMenuBar(); +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.cpp new file mode 100644 index 0000000..26f82bb --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2010,2011,2012,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigGMSKSet.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 150U; + +CDStarRepeaterConfigGMSKSet::CDStarRepeaterConfigGMSKSet(wxWindow* parent, int id, USB_INTERFACE type, unsigned int address) : +wxDialog(parent, id, wxString(_("GMSK Modem Settings"))), +#if defined(WIN32) +m_type(NULL), +#endif +m_address(NULL) +{ + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + +#if defined(WIN32) + wxStaticText* typeLabel = new wxStaticText(this, -1, _("Type")); + sizer->Add(typeLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_type = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_type->Append(wxT("LibUSB")); + m_type->Append(wxT("WinUSB")); + sizer->Add(m_type, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_type->SetSelection(int(type)); +#endif + + wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); + sizer->Add(addressLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_address = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_address->Append(wxT("0x300")); + m_address->Append(wxT("0x301")); + m_address->Append(wxT("0x302")); + m_address->Append(wxT("0x303")); + m_address->Append(wxT("0x304")); + m_address->Append(wxT("0x305")); + m_address->Append(wxT("0x306")); + m_address->Append(wxT("0x307")); + m_address->Append(wxT("0x308")); + m_address->Append(wxT("0x309")); + m_address->Append(wxT("0x30A")); + m_address->Append(wxT("0x30B")); + m_address->Append(wxT("0x30C")); + m_address->Append(wxT("0x30D")); + m_address->Append(wxT("0x30E")); + m_address->Append(wxT("0x30F")); + m_address->Append(wxT("0x310")); + m_address->Append(wxT("0x311")); + m_address->Append(wxT("0x312")); + m_address->Append(wxT("0x313")); + m_address->Append(wxT("0x314")); + m_address->Append(wxT("0x315")); + m_address->Append(wxT("0x316")); + m_address->Append(wxT("0x317")); + m_address->Append(wxT("0x318")); + m_address->Append(wxT("0x319")); + m_address->Append(wxT("0x31A")); + m_address->Append(wxT("0x31B")); + m_address->Append(wxT("0x31C")); + m_address->Append(wxT("0x31D")); + m_address->Append(wxT("0x31E")); + m_address->Append(wxT("0x31F")); + sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_address->SetSelection(address - 0x0300U); + + topSizer->Add(sizer); + + topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + + topSizer->Fit(this); + topSizer->SetSizeHints(this); + + SetSizer(topSizer); +} + +CDStarRepeaterConfigGMSKSet::~CDStarRepeaterConfigGMSKSet() +{ +} + +bool CDStarRepeaterConfigGMSKSet::Validate() +{ +#if defined(WIN32) + if (m_type->GetCurrentSelection() == wxNOT_FOUND) + return false; +#endif + + return m_address->GetCurrentSelection() != wxNOT_FOUND; +} + +USB_INTERFACE CDStarRepeaterConfigGMSKSet::getType() const +{ +#if defined(WIN32) + int n = m_type->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return UI_WINUSB; + else + return USB_INTERFACE(n); +#else + return UI_LIBUSB; +#endif +} + +unsigned int CDStarRepeaterConfigGMSKSet::getAddress() const +{ + int n = m_address->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return 0x0300U; + else + return n + 0x0300U; +} + diff --git a/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.h b/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.h new file mode 100644 index 0000000..ae2834b --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010,2011,2012 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigGMSKSet_H +#define DStarRepeaterConfigGMSKSet_H + +#include "DStarRepeaterConfigDefs.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigGMSKSet : public wxDialog { +public: + CDStarRepeaterConfigGMSKSet(wxWindow* parent, int id, USB_INTERFACE type, unsigned int address); + virtual ~CDStarRepeaterConfigGMSKSet(); + + virtual bool Validate(); + + virtual USB_INTERFACE getType() const; + virtual unsigned int getAddress() const; + +private: +#if defined(WIN32) + wxChoice* m_type; +#endif + wxChoice* m_address; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.cpp new file mode 100644 index 0000000..4d6ae30 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigMMDVMSet.h" +#include "SerialPortSelector.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 150U; +const unsigned int CONTROL_WIDTH2 = 300U; + +const unsigned int PORT_LENGTH = 5U; + +CDStarRepeaterConfigMMDVMSet::CDStarRepeaterConfigMMDVMSet(wxWindow* parent, int id, const wxString& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel) : +wxDialog(parent, id, wxString(_("MMDVM Settings"))), +m_port(NULL), +m_txInvert(NULL), +m_rxInvert(NULL), +m_pttInvert(NULL), +m_txDelay(NULL), +m_rxLevel(NULL), +m_txLevel(NULL) +{ + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_port = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxArrayString ports = CSerialPortSelector::getDevices(); + for (unsigned int i = 0U; i < ports.GetCount(); i++) + m_port->Append(ports.Item(i)); + + bool found = m_port->SetStringSelection(port); + if (!found) + m_port->SetSelection(0); + + wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); + sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_txInvert->Append(_("Off")); + m_txInvert->Append(_("On")); + sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_txInvert->SetSelection(txInvert ? 1 : 0); + + wxStaticText* rxInvertLabel = new wxStaticText(this, -1, _("RX Inversion")); + sizer->Add(rxInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_rxInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_rxInvert->Append(_("Off")); + m_rxInvert->Append(_("On")); + sizer->Add(m_rxInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_rxInvert->SetSelection(rxInvert ? 1 : 0); + + wxStaticText* pttInvertLabel = new wxStaticText(this, -1, _("PTT Inversion")); + sizer->Add(pttInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_pttInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_pttInvert->Append(_("Off")); + m_pttInvert->Append(_("On")); + sizer->Add(m_pttInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_pttInvert->SetSelection(pttInvert ? 1 : 0); + + wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); + sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txDelay = new wxSlider(this, -1, txDelay, 0, 500, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* rxLevelLabel = new wxStaticText(this, -1, _("RX Level (%)")); + sizer->Add(rxLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_rxLevel = new wxSlider(this, -1, rxLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_rxLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); + sizer->Add(txLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txLevel = new wxSlider(this, -1, txLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_txLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + topSizer->Add(sizer); + + topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + + topSizer->Fit(this); + topSizer->SetSizeHints(this); + + SetSizer(topSizer); +} + +CDStarRepeaterConfigMMDVMSet::~CDStarRepeaterConfigMMDVMSet() +{ +} + +bool CDStarRepeaterConfigMMDVMSet::Validate() +{ + if (m_port->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_txInvert->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_rxInvert->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_pttInvert->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return true; +} + +wxString CDStarRepeaterConfigMMDVMSet::getPort() const +{ + int n = m_port->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return wxEmptyString; + + return m_port->GetStringSelection(); +} + +bool CDStarRepeaterConfigMMDVMSet::getRXInvert() const +{ + int n = m_rxInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +bool CDStarRepeaterConfigMMDVMSet::getTXInvert() const +{ + int n = m_txInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +bool CDStarRepeaterConfigMMDVMSet::getPTTInvert() const +{ + int n = m_pttInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +unsigned int CDStarRepeaterConfigMMDVMSet::getTXDelay() const +{ + return (unsigned int)m_txDelay->GetValue(); +} + +unsigned int CDStarRepeaterConfigMMDVMSet::getRXLevel() const +{ + return (unsigned int)m_rxLevel->GetValue(); +} + +unsigned int CDStarRepeaterConfigMMDVMSet::getTXLevel() const +{ + return (unsigned int)m_txLevel->GetValue(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.h b/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.h new file mode 100644 index 0000000..9b5c288 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigMMDVMSet_H +#define DStarRepeaterConfigMMDVMSet_H + +#include "DStarRepeaterConfigDefs.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigMMDVMSet : public wxDialog { +public: + CDStarRepeaterConfigMMDVMSet(wxWindow* parent, int id, const wxString& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel); + virtual ~CDStarRepeaterConfigMMDVMSet(); + + virtual bool Validate(); + + virtual wxString getPort() const; + virtual bool getRXInvert() const; + virtual bool getTXInvert() const; + virtual bool getPTTInvert() const; + virtual unsigned int getTXDelay() const; + virtual unsigned int getRXLevel() const; + virtual unsigned int getTXLevel() const; + +private: + wxChoice* m_port; + wxChoice* m_txInvert; + wxChoice* m_rxInvert; + wxChoice* m_pttInvert; + wxSlider* m_txDelay; + wxSlider* m_rxLevel; + wxSlider* m_txLevel; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigModemSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigModemSet.cpp new file mode 100644 index 0000000..d25efcd --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigModemSet.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigSoundCardSet.h" +#include "DStarRepeaterConfigDVRPTR1Set.h" +#include "DStarRepeaterConfigDVRPTR2Set.h" +#include "DStarRepeaterConfigDVRPTR3Set.h" +#include "DStarRepeaterConfigDVMegaSet.h" +#include "DStarRepeaterConfigModemSet.h" +#include "DStarRepeaterConfigMMDVMSet.h" +#include "DStarRepeaterConfigGMSKSet.h" +#include "DStarRepeaterConfigDVAPSet.h" +#include "DStarRepeaterConfigSplitSet.h" + +enum { + Button_Configure = 7088 +}; + +BEGIN_EVENT_TABLE(CDStarRepeaterConfigModemSet, wxPanel) + EVT_BUTTON(Button_Configure, CDStarRepeaterConfigModemSet::onConfigure) +END_EVENT_TABLE() + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 130U; + +CDStarRepeaterConfigModemSet::CDStarRepeaterConfigModemSet(wxWindow* parent, int id, const wxString& title, CDStarRepeaterConfig* config, const wxString& type) : +wxPanel(parent, id), +m_config(config), +m_title(title), +m_type(NULL) +{ + wxASSERT(config != NULL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* typeLabel = new wxStaticText(this, -1, _("Type")); + sizer->Add(typeLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_type = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_type->Append(wxT("DVAP")); + m_type->Append(wxT("DVMEGA")); + m_type->Append(wxT("DV-RPTR V1")); + m_type->Append(wxT("DV-RPTR V2")); + m_type->Append(wxT("DV-RPTR V3")); + m_type->Append(wxT("GMSK Modem")); + m_type->Append(wxT("MMDVM")); + m_type->Append(wxT("Sound Card")); + m_type->Append(wxT("Split")); + sizer->Add(m_type, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy, 0, wxALL, BORDER_SIZE); + + wxButton* configure = new wxButton(this, Button_Configure, _("Configure..."), wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + sizer->Add(configure, 0, wxALL, BORDER_SIZE); + + if (type.IsEmpty()) { + m_type->SetSelection(0); + } else { + int n = m_type->SetStringSelection(type); + if (n == wxNOT_FOUND) + m_type->SetSelection(0); + } + + SetAutoLayout(true); + + sizer->Fit(this); + sizer->SetSizeHints(this); + + SetSizer(sizer); +} + +CDStarRepeaterConfigModemSet::~CDStarRepeaterConfigModemSet() +{ +} + +bool CDStarRepeaterConfigModemSet::Validate() +{ + return m_type->GetCurrentSelection() != wxNOT_FOUND; +} + +wxString CDStarRepeaterConfigModemSet::getType() const +{ + int n = m_type->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return wxT("DVAP"); + + return m_type->GetStringSelection(); +} + +void CDStarRepeaterConfigModemSet::onConfigure(wxCommandEvent& event) +{ + int n = m_type->GetSelection(); + if (n == wxNOT_FOUND) + return; + + wxString type = m_type->GetStringSelection(); + + if (type.IsSameAs(wxT("DVAP"))) { + wxString port; + unsigned int frequency; + int power, squelch; + m_config->getDVAP(port, frequency, power, squelch); + CDStarRepeaterConfigDVAPSet modem(this, -1, port, frequency, power, squelch); + if (modem.ShowModal() == wxID_OK) { + if (modem.Validate()) { + port = modem.getPort(); + frequency = modem.getFrequency(); + power = modem.getPower(); + squelch = modem.getSquelch(); + m_config->setDVAP(port, frequency, power, squelch); + } + } + } else if (type.IsSameAs(wxT("GMSK Modem"))) { + USB_INTERFACE modemType; + unsigned int modemAddress; + m_config->getGMSK(modemType, modemAddress); + CDStarRepeaterConfigGMSKSet modem(this, -1, modemType, modemAddress); + if (modem.ShowModal() == wxID_OK) { + if (modem.Validate()) { + modemType = modem.getType(); + modemAddress = modem.getAddress(); + m_config->setGMSK(modemType, modemAddress); + } + } + } else if (type.IsSameAs(wxT("DV-RPTR V1"))) { + wxString port; + bool txInvert, rxInvert, channel; + unsigned int modLevel, txDelay; + m_config->getDVRPTR1(port, rxInvert, txInvert, channel, modLevel, txDelay); + CDStarRepeaterConfigDVRPTR1Set modem(this, -1, port, rxInvert, txInvert, channel, modLevel, txDelay); + if (modem.ShowModal() == wxID_OK) { + if (modem.Validate()) { + port = modem.getPort(); + rxInvert = modem.getRXInvert(); + txInvert = modem.getTXInvert(); + channel = modem.getChannel(); + modLevel = modem.getModLevel(); + txDelay = modem.getTXDelay(); + m_config->setDVRPTR1(port, rxInvert, txInvert, channel, modLevel, txDelay); + } + } + } else if (type.IsSameAs(wxT("DV-RPTR V2"))) { + CONNECTION_TYPE connType; + wxString usbPort, address; + bool txInvert; + unsigned int port, modLevel, txDelay; + m_config->getDVRPTR2(connType, usbPort, address, port, txInvert, modLevel, txDelay); + CDStarRepeaterConfigDVRPTR2Set modem(this, -1, connType, usbPort, address, port, txInvert, modLevel, txDelay); + if (modem.ShowModal() == wxID_OK) { + if (modem.Validate()) { + connType = modem.getConnectionType(); + usbPort = modem.getUSBPort(); + address = modem.getAddress(); + port = modem.getPort(); + txInvert = modem.getTXInvert(); + modLevel = modem.getModLevel(); + txDelay = modem.getTXDelay(); + m_config->setDVRPTR2(connType, usbPort, address, port, txInvert, modLevel, txDelay); + } + } + } else if (type.IsSameAs(wxT("DV-RPTR V3"))) { + CONNECTION_TYPE connType; + wxString usbPort, address; + bool txInvert; + unsigned int port, modLevel, txDelay; + m_config->getDVRPTR3(connType, usbPort, address, port, txInvert, modLevel, txDelay); + CDStarRepeaterConfigDVRPTR3Set modem(this, -1, connType, usbPort, address, port, txInvert, modLevel, txDelay); + if (modem.ShowModal() == wxID_OK) { + if (modem.Validate()) { + connType = modem.getConnectionType(); + usbPort = modem.getUSBPort(); + address = modem.getAddress(); + port = modem.getPort(); + txInvert = modem.getTXInvert(); + modLevel = modem.getModLevel(); + txDelay = modem.getTXDelay(); + m_config->setDVRPTR3(connType, usbPort, address, port, txInvert, modLevel, txDelay); + } + } + } else if (type.IsSameAs(wxT("DVMEGA"))) { + wxString port; + DVMEGA_VARIANT variant; + bool txInvert, rxInvert; + unsigned int txDelay, rxFrequency, txFrequency, power; + m_config->getDVMEGA(port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); + CDStarRepeaterConfigDVMegaSet modem(this, -1, port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); + if (modem.ShowModal() == wxID_OK) { + if (modem.Validate()) { + port = modem.getPort(); + variant = modem.getVariant(); + rxInvert = modem.getRXInvert(); + txInvert = modem.getTXInvert(); + txDelay = modem.getTXDelay(); + rxFrequency = modem.getRXFrequency(); + txFrequency = modem.getTXFrequency(); + power = modem.getPower(); + m_config->setDVMEGA(port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); + } + } + } else if (type.IsSameAs(wxT("MMDVM"))) { + wxString port; + bool txInvert, rxInvert, pttInvert; + unsigned int txDelay, rxLevel, txLevel; + m_config->getMMDVM(port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); + CDStarRepeaterConfigMMDVMSet modem(this, -1, port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); + if (modem.ShowModal() == wxID_OK) { + if (modem.Validate()) { + port = modem.getPort(); + rxInvert = modem.getRXInvert(); + txInvert = modem.getTXInvert(); + pttInvert = modem.getPTTInvert(); + txDelay = modem.getTXDelay(); + rxLevel = modem.getRXLevel(); + txLevel = modem.getTXLevel(); + m_config->setMMDVM(port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); + } + } + } else if (type.IsSameAs(wxT("Sound Card"))) { + wxString rxDevice, txDevice; + bool txInvert, rxInvert; + wxFloat32 rxLevel, txLevel; + unsigned int txDelay, txTail; + m_config->getSoundCard(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); + CDStarRepeaterConfigSoundCardSet modem(this, -1, rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); + if (modem.ShowModal() == wxID_OK) { + if (modem.Validate()) { + rxDevice = modem.getRXDevice(); + txDevice = modem.getTXDevice(); + rxInvert = modem.getRXInvert(); + txInvert = modem.getTXInvert(); + rxLevel = modem.getRXLevel(); + txLevel = modem.getTXLevel(); + txDelay = modem.getTXDelay(); + txTail = modem.getTXTail(); + m_config->setSoundCard(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); + } + } + } else if (type.IsSameAs(wxT("Split"))) { + wxString localAddress; + unsigned int localPort, timeout; + wxArrayString transmitterNames, receiverNames; + m_config->getSplit(localAddress, localPort, transmitterNames, receiverNames, timeout); + CDStarRepeaterConfigSplitSet modem(this, -1, localAddress, localPort, transmitterNames, receiverNames, timeout); + if (modem.ShowModal() == wxID_OK) { + if (modem.Validate()) { + localAddress = modem.getLocalAddress(); + localPort = modem.getLocalPort(); + transmitterNames = modem.getTransmitterNames(); + receiverNames = modem.getReceiverNames(); + timeout = modem.getTimeout(); + m_config->setSplit(localAddress, localPort, transmitterNames, receiverNames, timeout); + } + } + } +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigModemSet.h b/DStarRepeaterConfig/DStarRepeaterConfigModemSet.h new file mode 100644 index 0000000..9a68ef4 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigModemSet.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigModemSet_H +#define DStarRepeaterConfigModemSet_H + +#include "DStarRepeaterConfigDefs.h" +#include "DStarRepeaterConfig.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigModemSet : public wxPanel { +public: + CDStarRepeaterConfigModemSet(wxWindow* parent, int id, const wxString& title, CDStarRepeaterConfig* config, const wxString& type); + virtual ~CDStarRepeaterConfigModemSet(); + + virtual bool Validate(); + + virtual wxString getType() const; + + virtual void onConfigure(wxCommandEvent& event); + +private: + CDStarRepeaterConfig* m_config; + wxString m_title; + wxChoice* m_type; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.cpp new file mode 100644 index 0000000..8b71527 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2010,2011,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigNetworkSet.h" + +const unsigned int ADDRESS_WIDTH = 120U; +const unsigned int PORT_WIDTH = 80U; + +const unsigned int ADDRESS_LENGTH = 15U; +const unsigned int PORT_LENGTH = 5U; + +const unsigned int BORDER_SIZE = 5U; + +CDStarRepeaterConfigNetworkSet::CDStarRepeaterConfigNetworkSet(wxWindow* parent, int id, const wxString& title, const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name) : +wxPanel(parent, id), +m_title(title), +m_gatewayAddress(NULL), +m_gatewayPort(NULL), +m_localAddress(NULL), +m_localPort(NULL), +m_name(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* gatewayAddressLabel = new wxStaticText(this, -1, _("Gateway Address")); + sizer->Add(gatewayAddressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_gatewayAddress = new CAddressTextCtrl(this, -1, gatewayAddress, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + m_gatewayAddress->SetMaxLength(ADDRESS_LENGTH); + sizer->Add(m_gatewayAddress, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* gatewayPortLabel = new wxStaticText(this, -1, _("Gateway Port")); + sizer->Add(gatewayPortLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), gatewayPort); + + m_gatewayPort = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + m_gatewayPort->SetMaxLength(PORT_LENGTH); + sizer->Add(m_gatewayPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* localAddressLabel = new wxStaticText(this, -1, _("Local Address")); + sizer->Add(localAddressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_localAddress = new CAddressTextCtrl(this, -1, localAddress, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + m_localAddress->SetMaxLength(ADDRESS_LENGTH); + sizer->Add(m_localAddress, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* localPortLabel = new wxStaticText(this, -1, _("Local Port")); + sizer->Add(localPortLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + buffer.Printf(wxT("%u"), localPort); + + m_localPort = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + m_localPort->SetMaxLength(PORT_LENGTH); + sizer->Add(m_localPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* nameLabel = new wxStaticText(this, -1, _("Name")); + sizer->Add(nameLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_name = new wxTextCtrl(this, -1, name, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + sizer->Add(m_name, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + sizer->Fit(this); + sizer->SetSizeHints(this); + + SetSizer(sizer); +} + + +CDStarRepeaterConfigNetworkSet::~CDStarRepeaterConfigNetworkSet() +{ +} + +bool CDStarRepeaterConfigNetworkSet::Validate() +{ + unsigned int port = getGatewayPort(); + + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Gateway Port is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + port = getLocalPort(); + + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Local Port is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +wxString CDStarRepeaterConfigNetworkSet::getGatewayAddress() const +{ + return m_gatewayAddress->GetValue(); +} + +unsigned int CDStarRepeaterConfigNetworkSet::getGatewayPort() const +{ + unsigned long n; + + m_gatewayPort->GetValue().ToULong(&n); + + return n; +} + +wxString CDStarRepeaterConfigNetworkSet::getLocalAddress() const +{ + return m_localAddress->GetValue(); +} + +unsigned int CDStarRepeaterConfigNetworkSet::getLocalPort() const +{ + unsigned long n; + + m_localPort->GetValue().ToULong(&n); + + return n; +} + +wxString CDStarRepeaterConfigNetworkSet::getName() const +{ + return m_name->GetValue(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.h b/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.h new file mode 100644 index 0000000..e3052b1 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010,2011,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigNetworkSet_H +#define DStarRepeaterConfigNetworkSet_H + +#include "AddressTextCtrl.h" +#include "PortTextCtrl.h" + +#include + +class CDStarRepeaterConfigNetworkSet : public wxPanel { +public: + CDStarRepeaterConfigNetworkSet(wxWindow* parent, int id, const wxString& title, const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name); + virtual ~CDStarRepeaterConfigNetworkSet(); + + virtual bool Validate(); + + virtual wxString getGatewayAddress() const; + virtual unsigned int getGatewayPort() const; + virtual wxString getLocalAddress() const; + virtual unsigned int getLocalPort() const; + virtual wxString getName() const; + +private: + wxString m_title; + CAddressTextCtrl* m_gatewayAddress; + CPortTextCtrl* m_gatewayPort; + CAddressTextCtrl* m_localAddress; + CPortTextCtrl* m_localPort; + wxTextCtrl* m_name; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.cpp new file mode 100644 index 0000000..98e1c9a --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2009-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigSoundCardSet.h" +#include "SoundCardReaderWriter.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 150U; +const unsigned int CONTROL_WIDTH2 = 300U; +const unsigned int CONTROL_WIDTH3 = 350U; + +const unsigned int ADDRESS_LENGTH = 15U; +const unsigned int PORT_LENGTH = 5U; + + +CDStarRepeaterConfigSoundCardSet::CDStarRepeaterConfigSoundCardSet(wxWindow* parent, int id, const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail) : +wxDialog(parent, id, wxString(_("Sound Card Settings"))), +m_rxDevice(NULL), +m_txDevice(NULL), +m_rxInvert(NULL), +m_txInvert(NULL), +m_rxLevel(NULL), +m_txLevel(NULL), +m_txDelay(NULL), +m_txTail(NULL) +{ + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* rxDeviceLabel = new wxStaticText(this, -1, _("RX Device")); + sizer->Add(rxDeviceLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_rxDevice = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); + sizer->Add(m_rxDevice, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxArrayString devs = CSoundCardReaderWriter::getReadDevices(); + for (unsigned int i = 0U; i < devs.GetCount(); i++) + m_rxDevice->Append(devs.Item(i)); + + bool found = m_rxDevice->SetStringSelection(rxDevice); + if (!found) + m_rxDevice->SetSelection(0); + + wxStaticText* txDeviceLabel = new wxStaticText(this, -1, _("TX Device")); + sizer->Add(txDeviceLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txDevice = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); + sizer->Add(m_txDevice, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + devs = CSoundCardReaderWriter::getWriteDevices(); + for (unsigned int i = 0U; i < devs.GetCount(); i++) + m_txDevice->Append(devs.Item(i)); + + found = m_txDevice->SetStringSelection(txDevice); + if (!found) + m_txDevice->SetSelection(0); + + wxStaticText* rxInvertLabel = new wxStaticText(this, -1, _("RX Inversion")); + sizer->Add(rxInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_rxInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_rxInvert->Append(_("Off")); + m_rxInvert->Append(_("On")); + sizer->Add(m_rxInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_rxInvert->SetSelection(rxInvert ? 1 : 0); + + wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); + sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_txInvert->Append(_("Off")); + m_txInvert->Append(_("On")); + sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_txInvert->SetSelection(txInvert ? 1 : 0); + + wxStaticText* rxLevelLabel = new wxStaticText(this, -1, _("RX Level (%)")); + sizer->Add(rxLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + int level = int(rxLevel * 100.0F + 0.5F); + m_rxLevel = new wxSlider(this, -1, level, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_rxLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); + sizer->Add(txLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + level = int(txLevel * 100.0F + 0.5F); + m_txLevel = new wxSlider(this, -1, level, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_txLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); + sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txDelay = new wxSlider(this, -1, txDelay, 0, 500, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* txTailLabel = new wxStaticText(this, -1, _("TX Tail (ms)")); + sizer->Add(txTailLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_txTail = new wxSlider(this, -1, txTail, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_txTail, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + topSizer->Add(sizer); + + topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + + topSizer->Fit(this); + topSizer->SetSizeHints(this); + + SetSizer(topSizer); +} + +CDStarRepeaterConfigSoundCardSet::~CDStarRepeaterConfigSoundCardSet() +{ +} + +bool CDStarRepeaterConfigSoundCardSet::Validate() +{ + if (m_rxDevice->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_txDevice->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_rxInvert->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return m_txInvert->GetCurrentSelection() != wxNOT_FOUND; +} + +wxString CDStarRepeaterConfigSoundCardSet::getRXDevice() const +{ + int n = m_rxDevice->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return wxEmptyString; + + return m_rxDevice->GetStringSelection(); +} + +wxString CDStarRepeaterConfigSoundCardSet::getTXDevice() const +{ + int n = m_txDevice->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return wxEmptyString; + + return m_txDevice->GetStringSelection(); +} + +bool CDStarRepeaterConfigSoundCardSet::getRXInvert() const +{ + int n = m_rxInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +bool CDStarRepeaterConfigSoundCardSet::getTXInvert() const +{ + int n = m_txInvert->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} + +wxFloat32 CDStarRepeaterConfigSoundCardSet::getRXLevel() const +{ + return wxFloat32(m_rxLevel->GetValue()) / 100.0F; +} + +wxFloat32 CDStarRepeaterConfigSoundCardSet::getTXLevel() const +{ + return wxFloat32(m_txLevel->GetValue()) / 100.0F; +} + +unsigned int CDStarRepeaterConfigSoundCardSet::getTXDelay() const +{ + return (unsigned int)m_txDelay->GetValue(); +} + +unsigned int CDStarRepeaterConfigSoundCardSet::getTXTail() const +{ + return (unsigned int)m_txTail->GetValue(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.h b/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.h new file mode 100644 index 0000000..82e32b0 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009-2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigSoundCardSet_H +#define DStarRepeaterConfigSoundCardSet_H + +#include "DStarRepeaterConfigDefs.h" +#include "DStarDefines.h" + +#include + +class CDStarRepeaterConfigSoundCardSet : public wxDialog { +public: + CDStarRepeaterConfigSoundCardSet(wxWindow* parent, int id, const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail); + virtual ~CDStarRepeaterConfigSoundCardSet(); + + virtual bool Validate(); + + virtual wxString getRXDevice() const; + virtual wxString getTXDevice() const; + virtual bool getRXInvert() const; + virtual bool getTXInvert() const; + virtual wxFloat32 getRXLevel() const; + virtual wxFloat32 getTXLevel() const; + virtual unsigned int getTXDelay() const; + virtual unsigned int getTXTail() const; + +private: + wxChoice* m_rxDevice; + wxChoice* m_txDevice; + wxChoice* m_rxInvert; + wxChoice* m_txInvert; + wxSlider* m_rxLevel; + wxSlider* m_txLevel; + wxSlider* m_txDelay; + wxSlider* m_txTail; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.cpp new file mode 100644 index 0000000..5f2f500 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigSplitSet.h" +#include "DStarDefines.h" + +const unsigned int BORDER_SIZE = 5U; + +const unsigned int ADDRESS_WIDTH = 120U; +const unsigned int PORT_WIDTH = 80U; + +const unsigned int ADDRESS_LENGTH = 15U; +const unsigned int PORT_LENGTH = 5U; + +const unsigned int SLIDER_WIDTH = 200U; + +CDStarRepeaterConfigSplitSet::CDStarRepeaterConfigSplitSet(wxWindow* parent, int id, const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout) : +wxDialog(parent, id, wxString(_("Split Settings"))), +m_localAddress(NULL), +m_localPort(NULL), +m_transmitterNames(NULL), +m_receiverNames(NULL), +m_timeout(NULL), +m_txCount(0U), +m_rxCount(0U) +{ + m_txCount = transmitterNames.GetCount(); + m_rxCount = receiverNames.GetCount(); + + if (m_txCount > SPLIT_TX_GUI_COUNT) + m_txCount = SPLIT_TX_GUI_COUNT; + + if (m_rxCount > SPLIT_RX_GUI_COUNT) + m_rxCount = SPLIT_RX_GUI_COUNT; + + m_transmitterNames = new wxTextCtrl*[m_txCount]; + m_receiverNames = new wxTextCtrl*[m_rxCount]; + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* localLabel = new wxStaticText(this, -1, _("Local Addr/Port")); + sizer->Add(localLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxBoxSizer* localSizer = new wxBoxSizer(wxHORIZONTAL); + + m_localAddress = new CAddressTextCtrl(this, -1, localAddress, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + m_localAddress->SetMaxLength(ADDRESS_LENGTH); + localSizer->Add(m_localAddress, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), localPort); + + m_localPort = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + m_localPort->SetMaxLength(PORT_LENGTH); + localSizer->Add(m_localPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + sizer->Add(localSizer); + + for (unsigned int i = 0U; i < m_txCount; i++) { + wxString text; + text.Printf(_("TX %u Name"), i + 1U); + + wxStaticText* transmitterLabel = new wxStaticText(this, -1, text); + sizer->Add(transmitterLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_transmitterNames[i] = new wxTextCtrl(this, -1, transmitterNames.Item(i), wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + sizer->Add(m_transmitterNames[i], 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + } + + for (unsigned int i = 0U; i < m_rxCount; i++) { + wxString text; + text.Printf(_("RX %u Name"), i + 1U); + + wxStaticText* receiverLabel = new wxStaticText(this, -1, text); + sizer->Add(receiverLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_receiverNames[i] = new wxTextCtrl(this, -1, receiverNames.Item(i), wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + sizer->Add(m_receiverNames[i], 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + } + + wxStaticText* timeoutLabel = new wxStaticText(this, -1, _("Packet timeout (ms)")); + sizer->Add(timeoutLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_timeout = new wxSlider(this, -1, timeout, 10, 300, wxDefaultPosition, wxSize(SLIDER_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_timeout, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + topSizer->Add(sizer); + + topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + + topSizer->Fit(this); + topSizer->SetSizeHints(this); + + SetSizer(topSizer); +} + +CDStarRepeaterConfigSplitSet::~CDStarRepeaterConfigSplitSet() +{ +} + +bool CDStarRepeaterConfigSplitSet::Validate() +{ + return true; +} + +wxString CDStarRepeaterConfigSplitSet::getLocalAddress() const +{ + return m_localAddress->GetValue(); +} + +unsigned int CDStarRepeaterConfigSplitSet::getLocalPort() const +{ + unsigned long n; + + m_localPort->GetValue().ToULong(&n); + + return n; +} + +wxArrayString CDStarRepeaterConfigSplitSet::getTransmitterNames() const +{ + wxArrayString names; + + for (unsigned int i = 0U; i < m_txCount; i++) + names.Add(m_transmitterNames[i]->GetValue()); + + return names; +} + +wxArrayString CDStarRepeaterConfigSplitSet::getReceiverNames() const +{ + wxArrayString names; + + for (unsigned int i = 0U; i < m_rxCount; i++) + names.Add(m_receiverNames[i]->GetValue()); + + return names; +} + +unsigned int CDStarRepeaterConfigSplitSet::getTimeout() const +{ + return (unsigned int)m_timeout->GetValue(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.h b/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.h new file mode 100644 index 0000000..16122d2 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigSplitSet_H +#define DStarRepeaterConfigSplitSet_H + +#include "DStarRepeaterConfigDefs.h" +#include "AddressTextCtrl.h" +#include "PortTextCtrl.h" + +#include + +class CDStarRepeaterConfigSplitSet : public wxDialog { +public: + CDStarRepeaterConfigSplitSet(wxWindow* parent, int id, const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout); + virtual ~CDStarRepeaterConfigSplitSet(); + + virtual bool Validate(); + + virtual wxString getLocalAddress() const; + virtual unsigned int getLocalPort() const; + + virtual wxArrayString getTransmitterNames() const; + + virtual wxArrayString getReceiverNames() const; + + virtual unsigned int getTimeout() const; + +private: + CAddressTextCtrl* m_localAddress; + CPortTextCtrl* m_localPort; + wxTextCtrl** m_transmitterNames; + wxTextCtrl** m_receiverNames; + wxSlider* m_timeout; + unsigned int m_txCount; + unsigned int m_rxCount; +}; + +#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.cpp new file mode 100644 index 0000000..29598d8 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011,2012 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarRepeaterConfigTimesSet.h" + +const unsigned int TIMES_WIDTH = 300U; + +const unsigned int BORDER_SIZE = 5U; + +CDStarRepeaterConfigTimesSet::CDStarRepeaterConfigTimesSet(wxWindow* parent, int id, const wxString& title, unsigned int timeout, unsigned int ackTime) : +wxPanel(parent, id), +m_title(title), +m_timeout(NULL), +m_ackTime(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* timeoutLabel = new wxStaticText(this, -1, _("Timeout (secs)")); + sizer->Add(timeoutLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_timeout = new wxSlider(this, -1, timeout, 0, 300, wxDefaultPosition, wxSize(TIMES_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_timeout, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* ackTimeLabel = new wxStaticText(this, -1, _("Ack Time (ms)")); + sizer->Add(ackTimeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + if (ackTime < 100U) + ackTime = 500U; + + m_ackTime = new wxSlider(this, -1, ackTime, 100, 2000, wxDefaultPosition, wxSize(TIMES_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); + sizer->Add(m_ackTime, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CDStarRepeaterConfigTimesSet::~CDStarRepeaterConfigTimesSet() +{ +} + +bool CDStarRepeaterConfigTimesSet::Validate() +{ + return true; +} + +unsigned int CDStarRepeaterConfigTimesSet::getTimeout() const +{ + return m_timeout->GetValue(); +} + +unsigned int CDStarRepeaterConfigTimesSet::getAckTime() const +{ + return m_ackTime->GetValue(); +} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.h b/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.h new file mode 100644 index 0000000..9d6eef4 --- /dev/null +++ b/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011,2012 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DStarRepeaterConfigTimesSet_H +#define DStarRepeaterConfigTimesSet_H + +#include + +class CDStarRepeaterConfigTimesSet : public wxPanel { +public: + CDStarRepeaterConfigTimesSet(wxWindow* parent, int id, const wxString& title, unsigned int timeout, unsigned int ackTime); + virtual ~CDStarRepeaterConfigTimesSet(); + + virtual bool Validate(); + + virtual unsigned int getTimeout() const; + virtual unsigned int getAckTime() const; + +private: + wxString m_title; + wxSlider* m_timeout; + wxSlider* m_ackTime; +}; + +#endif diff --git a/DStarRepeaterConfig/Makefile b/DStarRepeaterConfig/Makefile new file mode 100644 index 0000000..d9b92db --- /dev/null +++ b/DStarRepeaterConfig/Makefile @@ -0,0 +1,92 @@ +all: dstarrepeaterconfig + +CFLAGS := -I../Common -I../GUICommon $(CFLAGS) + +dstarrepeaterconfig: DStarRepeaterConfigActiveHangSet.o DStarRepeaterConfigAnnouncementSet.o DStarRepeaterConfigApp.o DStarRepeaterConfigBeaconSet.o \ + DStarRepeaterConfigCallsignSet.o DStarRepeaterConfigControl1Set.o DStarRepeaterConfigControl2Set.o DStarRepeaterConfigControllerSet.o \ + DStarRepeaterConfigDVAPSet.o DStarRepeaterConfigDVMegaSet.o DStarRepeaterConfigDVRPTR1Set.o DStarRepeaterConfigDVRPTR2Set.o \ + DStarRepeaterConfigDVRPTR3Set.o DStarRepeaterConfigFrame.o DStarRepeaterConfigGMSKSet.o DStarRepeaterConfigModemSet.o DStarRepeaterConfigNetworkSet.o \ + DStarRepeaterConfigSoundCardSet.o DStarRepeaterConfigSplitSet.o DStarRepeaterConfigTimesSet.o DStarRepeaterConfigMMDVMSet.o ../Common/Common.a \ + ../GUICommon/GUICommon.a + $(CC) $(LDFLAGS) -o dstarrepeaterconfig DStarRepeaterConfigActiveHangSet.o DStarRepeaterConfigAnnouncementSet.o DStarRepeaterConfigApp.o \ + DStarRepeaterConfigBeaconSet.o DStarRepeaterConfigCallsignSet.o DStarRepeaterConfigControl1Set.o DStarRepeaterConfigControl2Set.o \ + DStarRepeaterConfigControllerSet.o DStarRepeaterConfigDVAPSet.o DStarRepeaterConfigDVMegaSet.o DStarRepeaterConfigDVRPTR1Set.o \ + DStarRepeaterConfigDVRPTR2Set.o DStarRepeaterConfigDVRPTR3Set.o DStarRepeaterConfigFrame.o DStarRepeaterConfigGMSKSet.o DStarRepeaterConfigModemSet.o \ + DStarRepeaterConfigNetworkSet.o DStarRepeaterConfigSoundCardSet.o DStarRepeaterConfigSplitSet.o DStarRepeaterConfigTimesSet.o \ + DStarRepeaterConfigMMDVMSet.o ../Common/Common.a ../GUICommon/GUICommon.a $(LIBS) + +DStarRepeaterConfigActiveHangSet.o: DStarRepeaterConfigActiveHangSet.cpp DStarRepeaterConfigActiveHangSet.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigActiveHangSet.cpp + +DStarRepeaterConfigAnnouncementSet.o: DStarRepeaterConfigAnnouncementSet.cpp DStarRepeaterConfigAnnouncementSet.h ../GUICommon/CallsignTextCtrl.h \ + ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigAnnouncementSet.cpp + +DStarRepeaterConfigApp.o: DStarRepeaterConfigApp.cpp DStarRepeaterConfigApp.h DStarRepeaterConfigFrame.h DStarRepeaterConfigDefs.h ../Common/DStarDefines.h \ + ../Common/Version.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigApp.cpp + +DStarRepeaterConfigBeaconSet.o: DStarRepeaterConfigBeaconSet.cpp DStarRepeaterConfigBeaconSet.h ../GUICommon/MessageTextCtrl.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigBeaconSet.cpp + +DStarRepeaterConfigCallsignSet.o: DStarRepeaterConfigCallsignSet.cpp DStarRepeaterConfigCallsignSet.h ../GUICommon/CallsignTextCtrl.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigCallsignSet.cpp + +DStarRepeaterConfigControl1Set.o: DStarRepeaterConfigControl1Set.cpp DStarRepeaterConfigControl1Set.h ../GUICommon/CallsignTextCtrl.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigControl1Set.cpp + +DStarRepeaterConfigControl2Set.o: DStarRepeaterConfigControl2Set.cpp DStarRepeaterConfigControl2Set.h ../GUICommon/CallsignTextCtrl.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigControl2Set.cpp + +DStarRepeaterConfigControllerSet.o: DStarRepeaterConfigControllerSet.cpp DStarRepeaterConfigControllerSet.h ../Common/SerialPortSelector.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigControllerSet.cpp + +DStarRepeaterConfigDVAPSet.o: DStarRepeaterConfigDVAPSet.cpp DStarRepeaterConfigDVAPSet.h ../Common/SerialPortSelector.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigDVAPSet.cpp + +DStarRepeaterConfigDVMegaSet.o: DStarRepeaterConfigDVMegaSet.cpp DStarRepeaterConfigDVMegaSet.h ../Common/SerialPortSelector.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigDVMegaSet.cpp + +DStarRepeaterConfigDVRPTR1Set.o: DStarRepeaterConfigDVRPTR1Set.cpp DStarRepeaterConfigDVRPTR1Set.h ../Common/SerialPortSelector.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigDVRPTR1Set.cpp + +DStarRepeaterConfigDVRPTR2Set.o: DStarRepeaterConfigDVRPTR2Set.cpp DStarRepeaterConfigDVRPTR2Set.h ../Common/SerialPortSelector.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigDVRPTR2Set.cpp + +DStarRepeaterConfigDVRPTR3Set.o: DStarRepeaterConfigDVRPTR3Set.cpp DStarRepeaterConfigDVRPTR3Set.h ../Common/SerialPortSelector.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigDVRPTR3Set.cpp + +DStarRepeaterConfigFrame.o: DStarRepeaterConfigFrame.cpp DStarRepeaterConfigFrame.h DStarRepeaterConfigDefs.h DStarRepeaterConfigControllerSet.h \ + DStarRepeaterConfigModemSet.h DStarRepeaterConfigTimesSet.h DStarRepeaterConfigCallsignSet.h DStarRepeaterConfigNetworkSet.h \ + DStarRepeaterConfigBeaconSet.h DStarRepeaterConfigAnnouncementSet.h DStarRepeaterConfigControl1Set.h DStarRepeaterConfigControl2Set.h \ + ../Common/DStarRepeaterConfig.h ../Common/DStarDefines.h ../Common/Version.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigFrame.cpp + +DStarRepeaterConfigGMSKSet.o: DStarRepeaterConfigGMSKSet.cpp DStarRepeaterConfigGMSKSet.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigGMSKSet.cpp + +DStarRepeaterConfigMMDVMSet.o: DStarRepeaterConfigMMDVMSet.cpp DStarRepeaterConfigMMDVMSet.h ../Common/SerialPortSelector.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigMMDVMSet.cpp + +DStarRepeaterConfigModemSet.o: DStarRepeaterConfigModemSet.cpp DStarRepeaterConfigModemSet.h DStarRepeaterConfigDefs.h DStarRepeaterConfigSoundCardSet.h \ + DStarRepeaterConfigDVRPTR1Set.h DStarRepeaterConfigDVRPTR2Set.h DStarRepeaterConfigDVRPTR3Set.h DStarRepeaterConfigModemSet.h \ + DStarRepeaterConfigGMSKSet.h DStarRepeaterConfigDVAPSet.h DStarRepeaterConfigDVMegaSet.h DStarRepeaterConfigSplitSet.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigModemSet.cpp + +DStarRepeaterConfigNetworkSet.o: DStarRepeaterConfigNetworkSet.cpp DStarRepeaterConfigNetworkSet.h ../GUICommon/AddressTextCtrl.h ../GUICommon/PortTextCtrl.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigNetworkSet.cpp + +DStarRepeaterConfigSplitSet.o: DStarRepeaterConfigSplitSet.cpp DStarRepeaterConfigSplitSet.h ../GUICommon/AddressTextCtrl.h ../GUICommon/PortTextCtrl.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigSplitSet.cpp + +DStarRepeaterConfigSoundCardSet.o: DStarRepeaterConfigSoundCardSet.cpp DStarRepeaterConfigSoundCardSet.h ../Common/SoundCardReaderWriter.h ../Common/DStarDefines.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigSoundCardSet.cpp + +DStarRepeaterConfigTimesSet.o: DStarRepeaterConfigTimesSet.cpp DStarRepeaterConfigTimesSet.h + $(CC) $(CFLAGS) -c DStarRepeaterConfigTimesSet.cpp + +install: all + install -g bin -o root -m 0775 dstarrepeaterconfig $(BINDIR) + +clean: + rm -f core dstarrepeaterconfig *.o *~ *.bak diff --git a/Data/Makefile b/Data/Makefile new file mode 100644 index 0000000..698a52a --- /dev/null +++ b/Data/Makefile @@ -0,0 +1,22 @@ +install: + install -d -g bin -o root -m 0775 $(DATADIR) + install -g bin -o root -m 0664 de_DE.ambe $(DATADIR) + install -g bin -o root -m 0664 de_DE.indx $(DATADIR) + install -g bin -o root -m 0664 dk_DK.ambe $(DATADIR) + install -g bin -o root -m 0664 dk_DK.indx $(DATADIR) + install -g bin -o root -m 0664 en_GB.ambe $(DATADIR) + install -g bin -o root -m 0664 en_GB.indx $(DATADIR) + install -g bin -o root -m 0664 en_US.ambe $(DATADIR) + install -g bin -o root -m 0664 en_US.indx $(DATADIR) + install -g bin -o root -m 0664 es_ES.ambe $(DATADIR) + install -g bin -o root -m 0664 es_ES.indx $(DATADIR) + install -g bin -o root -m 0664 fr_FR.ambe $(DATADIR) + install -g bin -o root -m 0664 fr_FR.indx $(DATADIR) + install -g bin -o root -m 0664 it_IT.ambe $(DATADIR) + install -g bin -o root -m 0664 it_IT.indx $(DATADIR) + install -g bin -o root -m 0664 no_NO.ambe $(DATADIR) + install -g bin -o root -m 0664 no_NO.indx $(DATADIR) + install -g bin -o root -m 0664 pl_PL.ambe $(DATADIR) + install -g bin -o root -m 0664 pl_PL.indx $(DATADIR) + install -g bin -o root -m 0664 se_SE.ambe $(DATADIR) + install -g bin -o root -m 0664 se_SE.indx $(DATADIR) diff --git a/Data/de_DE.ambe b/Data/de_DE.ambe new file mode 100644 index 0000000..3189f05 Binary files /dev/null and b/Data/de_DE.ambe differ diff --git a/Data/de_DE.indx b/Data/de_DE.indx new file mode 100644 index 0000000..0f9fcdd --- /dev/null +++ b/Data/de_DE.indx @@ -0,0 +1,44 @@ +0 78 24 +1 121 24 +2 163 25 +3 202 28 +4 248 30 +5 295 27 +6 340 32 +7 391 32 +8 440 25 +9 484 32 +alpha 533 25 +bravo 576 31 +charlie 629 26 +delta 676 30 +A 725 19 +B 769 21 +C 818 27 +D 870 23 +E 917 23 +F 965 23 +G 1014 22 +H 1060 24 +I 1109 18 +J 1151 26 +K 1207 26 +L 1258 23 +M 1305 21 +N 1351 25 +O 1401 19 +P 1446 19 +Q 1493 24 +R 1543 23 +S 1591 21 +T 1644 23 +U 1693 22 +V 1740 19 +W 1784 22 +X 1831 24 +Y 1883 34 +Z 1943 25 +linkedto 1995 40 +notlinked 2088 50 +linkingto 2158 44 +isbusy 2268 40 diff --git a/Data/dk_DK.ambe b/Data/dk_DK.ambe new file mode 100644 index 0000000..d9cc78a Binary files /dev/null and b/Data/dk_DK.ambe differ diff --git a/Data/dk_DK.indx b/Data/dk_DK.indx new file mode 100644 index 0000000..ba64aa4 --- /dev/null +++ b/Data/dk_DK.indx @@ -0,0 +1,44 @@ +0 82 21 +1 120 15 +2 160 19 +3 198 22 +4 236 35 +5 288 24 +6 329 31 +7 378 27 +8 422 27 +9 466 24 +alpha 506 32 +bravo 558 27 +charlie 604 38 +delta 663 27 +A 710 18 +B 754 16 +C 796 20 +D 843 16 +E 885 14 +F 923 18 +G 967 13 +H 1005 19 +I 1049 13 +J 1087 23 +K 1137 20 +L 1180 20 +M 1224 19 +N 1268 17 +O 1310 16 +P 1352 15 +Q 1395 17 +R 1437 18 +S 1480 19 +T 1525 19 +U 1569 18 +V 1611 17 +W 1655 34 +X 1714 20 +Y 1759 19 +Z 1802 23 +linkedto 1852 37 +notlinked 1928 50 +linkingto 1998 34 +isbusy 2071 40 diff --git a/Data/en_GB.ambe b/Data/en_GB.ambe new file mode 100644 index 0000000..4a7c83c Binary files /dev/null and b/Data/en_GB.ambe differ diff --git a/Data/en_GB.indx b/Data/en_GB.indx new file mode 100644 index 0000000..ea3889d --- /dev/null +++ b/Data/en_GB.indx @@ -0,0 +1,44 @@ +0 85 41 +1 144 24 +2 186 25 +3 229 30 +4 278 35 +5 331 35 +6 384 39 +7 441 34 +8 494 29 +9 541 35 +alpha 595 31 +bravo 646 33 +charlie 700 31 +delta 753 30 +A 804 24 +B 850 33 +C 898 23 +D 933 33 +E 990 34 +F 1041 34 +G 1093 34 +H 1145 40 +I 1204 30 +J 1253 35 +K 1307 33 +L 1358 30 +M 1406 28 +N 1452 30 +O 1500 23 +P 1541 32 +Q 1592 39 +R 1648 23 +S 1689 33 +T 1742 32 +U 1791 35 +V 1845 33 +W 1896 38 +X 1953 35 +Y 2007 28 +Z 2060 37 +linked 2124 19 +notlinked 2194 44 +linking 2258 31 +isbusy 2350 33 diff --git a/Data/en_US.ambe b/Data/en_US.ambe new file mode 100644 index 0000000..6b91c91 Binary files /dev/null and b/Data/en_US.ambe differ diff --git a/Data/en_US.indx b/Data/en_US.indx new file mode 100644 index 0000000..040e476 --- /dev/null +++ b/Data/en_US.indx @@ -0,0 +1,44 @@ +0 89 39 +1 146 34 +2 202 22 +3 246 31 +4 294 34 +5 345 46 +6 409 39 +7 466 37 +8 521 29 +9 568 35 +alpha 621 26 +bravo 669 34 +charlie 726 34 +delta 781 31 +A 833 19 +B 870 24 +C 938 27 +D 1009 24 +E 1076 28 +F 1148 29 +G 1221 24 +H 1290 31 +I 1363 29 +J 1432 33 +K 1510 25 +L 1578 22 +M 1644 28 +N 1715 28 +O 1785 27 +P 1858 24 +Q 1927 25 +R 1993 26 +S 2063 29 +T 2139 25 +U 2208 25 +V 2273 27 +W 2344 33 +X 2421 24 +Y 2489 26 +Z 2561 32 +linked 2637 20 +notlinked 2710 49 +linking 2779 25 +isbusy 2872 34 diff --git a/Data/es_ES.ambe b/Data/es_ES.ambe new file mode 100644 index 0000000..f416848 Binary files /dev/null and b/Data/es_ES.ambe differ diff --git a/Data/es_ES.indx b/Data/es_ES.indx new file mode 100644 index 0000000..e8d62c8 --- /dev/null +++ b/Data/es_ES.indx @@ -0,0 +1,44 @@ +0 83 31 +1 128 32 +2 174 33 +3 226 30 +4 273 36 +5 323 42 +6 379 37 +7 430 37 +8 480 35 +9 529 34 +alpha 578 34 +bravo 628 36 +charlie 682 43 +delta 742 38 +A 798 26 +B 836 22 +C 881 22 +D 923 24 +E 967 17 +F 1004 31 +G 1053 26 +H 1097 35 +I 1150 20 +J 1189 34 +K 1246 22 +L 1287 30 +M 1337 30 +N 1388 33 +O 1439 22 +P 1480 21 +Q 1524 26 +R 1571 32 +S 1622 31 +T 1675 22 +U 1718 23 +V 1756 28 +W 1804 52 +X 1876 28 +Y 1925 17 +Z 1961 35 +linkedto 2017 64 +notlinked 2133 68 +linkingto 2217 66 +isbusy 2364 58 diff --git a/Data/fr_FR.ambe b/Data/fr_FR.ambe new file mode 100644 index 0000000..c81d063 Binary files /dev/null and b/Data/fr_FR.ambe differ diff --git a/Data/fr_FR.indx b/Data/fr_FR.indx new file mode 100644 index 0000000..f67b54a --- /dev/null +++ b/Data/fr_FR.indx @@ -0,0 +1,44 @@ +0 94 29 +1 140 16 +2 173 20 +3 215 19 +4 255 24 +5 298 29 +6 344 34 +7 398 25 +8 440 24 +9 481 23 +alpha 521 29 +bravo 568 27 +charlie 615 30 +delta 666 30 +A 714 21 +B 753 27 +C 796 34 +D 848 30 +E 895 23 +F 936 27 +G 982 27 +H 1026 30 +I 1075 20 +J 1113 29 +K 1162 22 +L 1201 28 +M 1248 28 +N 1295 28 +O 1341 23 +P 1386 21 +Q 1426 23 +R 1466 28 +S 1511 31 +T 1567 21 +U 1606 22 +V 1646 30 +W 1693 46 +X 1756 31 +Y 1806 21 +Z 1844 14 +linkedto 1858 35 +notlinked 1941 46 +linkingto 2008 38 +isbusy 2091 47 diff --git a/Data/it_IT.ambe b/Data/it_IT.ambe new file mode 100644 index 0000000..b76a97a Binary files /dev/null and b/Data/it_IT.ambe differ diff --git a/Data/it_IT.indx b/Data/it_IT.indx new file mode 100644 index 0000000..a94e2ea --- /dev/null +++ b/Data/it_IT.indx @@ -0,0 +1,44 @@ +0 86 33 +1 136 27 +2 180 30 +3 231 17 +4 270 38 +5 328 38 +6 383 31 +7 431 36 +8 485 33 +9 535 39 +alpha 591 34 +bravo 644 35 +charlie 701 35 +delta 757 34 +A 810 12 +B 840 19 +C 883 17 +D 923 17 +E 965 14 +F 1001 34 +G 1058 20 +H 1100 32 +I 1155 14 +J 1192 43 +K 1263 30 +L 1316 34 +M 1373 30 +N 1426 29 +O 1477 13 +P 1519 15 +Q 1559 16 +R 1598 28 +S 1649 30 +T 1699 20 +U 1741 12 +V 1775 21 +W 1819 47 +X 1888 25 +Y 1936 43 +Z 2002 35 +linkedto 2064 48 +notlinked 2154 65 +linkingto 2239 41 +isbusy 2321 51 diff --git a/Data/no_NO.ambe b/Data/no_NO.ambe new file mode 100644 index 0000000..bff2bd6 Binary files /dev/null and b/Data/no_NO.ambe differ diff --git a/Data/no_NO.indx b/Data/no_NO.indx new file mode 100644 index 0000000..a3548c0 --- /dev/null +++ b/Data/no_NO.indx @@ -0,0 +1,44 @@ +0 67 28 +1 102 29 +2 137 27 +3 170 25 +4 199 36 +5 238 30 +6 272 38 +7 314 31 +8 349 34 +9 386 28 +alpha 418 36 +bravo 472 30 +charlie 519 34 +delta 569 34 +A 622 23 +B 665 24 +C 710 24 +D 758 21 +E 803 28 +F 856 24 +G 903 32 +H 955 31 +I 1007 18 +J 1045 30 +K 1100 28 +L 1152 19 +M 1196 20 +N 1241 17 +O 1282 27 +P 1332 24 +Q 1378 24 +R 1426 19 +S 1470 31 +T 1524 24 +U 1572 23 +V 1616 20 +W 1658 40 +X 1722 32 +Y 1779 24 +Z 1823 29 +linkedto 1882 40 +notlinked 1966 49 +linkingto 2034 40 +isbusy 2148 38 diff --git a/Data/pl_PL.ambe b/Data/pl_PL.ambe new file mode 100644 index 0000000..565d99f Binary files /dev/null and b/Data/pl_PL.ambe differ diff --git a/Data/pl_PL.indx b/Data/pl_PL.indx new file mode 100644 index 0000000..9f41cf0 --- /dev/null +++ b/Data/pl_PL.indx @@ -0,0 +1,44 @@ +0 99 36 +1 163 38 +2 229 29 +3 290 31 +4 351 37 +5 420 42 +6 490 41 +7 559 43 +8 630 34 +9 692 50 +alpha 771 30 +bravo 820 32 +charlie 873 35 +delta 931 34 +A 987 17 +B 1012 30 +C 1070 25 +D 1118 27 +E 1169 29 +F 1221 36 +G 1281 30 +H 1333 26 +I 1383 18 +J 1425 32 +K 1485 23 +L 1532 26 +M 1581 26 +N 1629 24 +O 1676 18 +P 1724 23 +Q 1773 20 +R 1817 23 +S 1863 35 +T 1926 25 +U 1975 22 +V 2021 28 +W 2070 15 +X 2107 31 +Y 2161 39 +Z 2226 12 +linkedto 2263 45 +notlinked 2360 51 +linkingto 2433 45 +isbusy 2559 59 diff --git a/Data/se_SE.ambe b/Data/se_SE.ambe new file mode 100644 index 0000000..7e5567c Binary files /dev/null and b/Data/se_SE.ambe differ diff --git a/Data/se_SE.indx b/Data/se_SE.indx new file mode 100644 index 0000000..1f78bef --- /dev/null +++ b/Data/se_SE.indx @@ -0,0 +1,44 @@ +0 83 35 +1 138 33 +2 188 32 +3 240 28 +4 286 41 +5 345 30 +6 393 43 +7 451 34 +8 505 35 +9 555 28 +alpha 603 35 +bravo 660 35 +charlie 716 32 +delta 768 48 +A 836 30 +B 894 28 +C 946 33 +D 1005 22 +E 1053 34 +F 1115 20 +G 1160 36 +H 1223 30 +I 1280 27 +J 1333 35 +K 1393 29 +L 1453 25 +M 1507 26 +N 1560 20 +O 1607 26 +P 1657 28 +Q 1714 34 +R 1781 23 +S 1833 30 +T 1890 25 +U 1943 23 +V 1989 35 +W 2050 47 +X 2126 27 +Y 2178 19 +Z 2223 45 +linkedto 2298 39 +notlinked 2390 51 +linkingto 2461 36 +isbusy 2577 54 diff --git a/GUICommon/AddressTextCtrl.cpp b/GUICommon/AddressTextCtrl.cpp new file mode 100644 index 0000000..c40cfce --- /dev/null +++ b/GUICommon/AddressTextCtrl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "AddressTextCtrl.h" + +CAddressTextCtrl::CAddressTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : +CRestrictedTextCtrl(parent, id, value, pos, size, style, ADDRESS_CHARS) +{ +} + +CAddressTextCtrl::~CAddressTextCtrl() +{ +} + diff --git a/GUICommon/AddressTextCtrl.h b/GUICommon/AddressTextCtrl.h new file mode 100644 index 0000000..a0c99c5 --- /dev/null +++ b/GUICommon/AddressTextCtrl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef AddressTextCtrl_H +#define AddressTextCtrl_H + +#include + +#include "RestrictedTextCtrl.h" + +const wxString ADDRESS_CHARS = wxT("0123456789."); + +class CAddressTextCtrl : public CRestrictedTextCtrl { + +public: + CAddressTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); + virtual ~CAddressTextCtrl(); + + private: +}; + +#endif diff --git a/GUICommon/CallsignTextCtrl.cpp b/GUICommon/CallsignTextCtrl.cpp new file mode 100644 index 0000000..f37b11a --- /dev/null +++ b/GUICommon/CallsignTextCtrl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2002,2003 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CallsignTextCtrl.h" + +CCallsignTextCtrl::CCallsignTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : +CRestrictedTextCtrl(parent, id, value, pos, size, style, CALLSIGN_CHARS) +{ +} + +CCallsignTextCtrl::~CCallsignTextCtrl() +{ +} + diff --git a/GUICommon/CallsignTextCtrl.h b/GUICommon/CallsignTextCtrl.h new file mode 100644 index 0000000..70a422e --- /dev/null +++ b/GUICommon/CallsignTextCtrl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002,2003 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef CallsignTextCtrl_H +#define CallsignTextCtrl_H + +#include + +#include "RestrictedTextCtrl.h" + +const wxString CALLSIGN_CHARS = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/ "); + +class CCallsignTextCtrl : public CRestrictedTextCtrl { + +public: + CCallsignTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); + virtual ~CCallsignTextCtrl(); + + private: +}; + +#endif diff --git a/GUICommon/GUICommon.vcxproj b/GUICommon/GUICommon.vcxproj new file mode 100644 index 0000000..2b5297e --- /dev/null +++ b/GUICommon/GUICommon.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {2C548AAB-A9F7-496F-BCF5-6185BDA304D2} + GUICommon + Win32Proj + 10.0.17134.0 + + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + + + StaticLibrary + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu + + + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + + + MaxSpeed + true + $(SolutionDir)..\WinUSB;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;C:\wxWidgets-2.8.12\include\msvc;C:\wxWidgets-2.8.12\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + + + MaxSpeed + true + $(SolutionDir)..\WinUSB;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;C:\wxWidgets-2.8.12\include\msvc;C:\wxWidgets-2.8.12\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GUICommon/GUICommon.vcxproj.filters b/GUICommon/GUICommon.vcxproj.filters new file mode 100644 index 0000000..2720603 --- /dev/null +++ b/GUICommon/GUICommon.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/GUICommon/MessageTextCtrl.cpp b/GUICommon/MessageTextCtrl.cpp new file mode 100644 index 0000000..ff67dbb --- /dev/null +++ b/GUICommon/MessageTextCtrl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2009 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "MessageTextCtrl.h" + +CMessageTextCtrl::CMessageTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : +CRestrictedTextCtrl(parent, id, value, pos, size, style, MSG_CHARS) +{ +} + +CMessageTextCtrl::~CMessageTextCtrl() +{ +} + diff --git a/GUICommon/MessageTextCtrl.h b/GUICommon/MessageTextCtrl.h new file mode 100644 index 0000000..970458a --- /dev/null +++ b/GUICommon/MessageTextCtrl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef MessageTextCtrl_H +#define MessageTextCtrl_H + +#include + +#include "RestrictedTextCtrl.h" + +const wxString MSG_CHARS = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ "); + +class CMessageTextCtrl : public CRestrictedTextCtrl { + +public: + CMessageTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); + virtual ~CMessageTextCtrl(); + + private: +}; + +#endif diff --git a/GUICommon/PortTextCtrl.cpp b/GUICommon/PortTextCtrl.cpp new file mode 100644 index 0000000..51435d9 --- /dev/null +++ b/GUICommon/PortTextCtrl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "PortTextCtrl.h" + +CPortTextCtrl::CPortTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : +CRestrictedTextCtrl(parent, id, value, pos, size, style, PORT_CHARS) +{ +} + +CPortTextCtrl::~CPortTextCtrl() +{ +} + diff --git a/GUICommon/PortTextCtrl.h b/GUICommon/PortTextCtrl.h new file mode 100644 index 0000000..8d096c8 --- /dev/null +++ b/GUICommon/PortTextCtrl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PortTextCtrl_H +#define PortTextCtrl_H + +#include + +#include "RestrictedTextCtrl.h" + +const wxString PORT_CHARS = wxT("0123456789"); + +class CPortTextCtrl : public CRestrictedTextCtrl { + +public: + CPortTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); + virtual ~CPortTextCtrl(); + + private: +}; + +#endif diff --git a/GUICommon/RestrictedTextCtrl.cpp b/GUICommon/RestrictedTextCtrl.cpp new file mode 100644 index 0000000..a291044 --- /dev/null +++ b/GUICommon/RestrictedTextCtrl.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "RestrictedTextCtrl.h" + +CRestrictedTextCtrl::CRestrictedTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style, const wxString& wantedChars) : +wxTextCtrl() +{ + wxASSERT(parent != NULL); + + wxArrayString charList; + + for (unsigned int i = 0; i < wantedChars.Length(); i++) + charList.Add(wantedChars.Mid(i, 1)); + + wxTextValidator validator(wxFILTER_INCLUDE_CHAR_LIST); + validator.SetIncludes(charList); + + Create(parent, id, value, pos, size, style, validator); +} + +CRestrictedTextCtrl::~CRestrictedTextCtrl() +{ +} + diff --git a/GUICommon/RestrictedTextCtrl.h b/GUICommon/RestrictedTextCtrl.h new file mode 100644 index 0000000..f3d57b6 --- /dev/null +++ b/GUICommon/RestrictedTextCtrl.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2002,2003 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef RestrictedTextCtrl_H +#define RestrictedTextCtrl_H + +#include + +class CRestrictedTextCtrl : public wxTextCtrl { +public: + CRestrictedTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style, const wxString& wantedChars); + virtual ~CRestrictedTextCtrl(); + +private: +}; + +#endif diff --git a/LinuxUSB/Makefile.am b/LinuxUSB/Makefile.am new file mode 100644 index 0000000..1336529 --- /dev/null +++ b/LinuxUSB/Makefile.am @@ -0,0 +1,3 @@ +AM_LDFLAGS = -Wl,--as-needed +LDADD = $(LIBUSB_LIBS) +bin_PROGRAMS = rmk8055drv rmuridrv diff --git a/LinuxUSB/Makefile.in b/LinuxUSB/Makefile.in new file mode 100644 index 0000000..026396a --- /dev/null +++ b/LinuxUSB/Makefile.in @@ -0,0 +1,602 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +bin_PROGRAMS = rmk8055drv$(EXEEXT) rmuridrv$(EXEEXT) +subdir = LinuxUSB +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +rmk8055drv_SOURCES = rmk8055drv.c +rmk8055drv_OBJECTS = rmk8055drv.$(OBJEXT) +rmk8055drv_LDADD = $(LDADD) +am__DEPENDENCIES_1 = +rmk8055drv_DEPENDENCIES = $(am__DEPENDENCIES_1) +rmuridrv_SOURCES = rmuridrv.c +rmuridrv_OBJECTS = rmuridrv.$(OBJEXT) +rmuridrv_LDADD = $(LDADD) +rmuridrv_DEPENDENCIES = $(am__DEPENDENCIES_1) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = rmk8055drv.c rmuridrv.c +DIST_SOURCES = rmk8055drv.c rmuridrv.c +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_CFLAGS = @ALSA_CFLAGS@ +ALSA_LIBS = @ALSA_LIBS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BASE_WX_CPPFLAGS = @BASE_WX_CPPFLAGS@ +BASE_WX_LIBS = @BASE_WX_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EXEEXT = @EXEEXT@ +GUI_WX_CPPFLAGS = @GUI_WX_CPPFLAGS@ +GUI_WX_LIBS = @GUI_WX_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WX_CFLAGS = @WX_CFLAGS@ +WX_CFLAGS_ONLY = @WX_CFLAGS_ONLY@ +WX_CONFIG_PATH = @WX_CONFIG_PATH@ +WX_CPPFLAGS = @WX_CPPFLAGS@ +WX_CXXFLAGS = @WX_CXXFLAGS@ +WX_CXXFLAGS_ONLY = @WX_CXXFLAGS_ONLY@ +WX_LIBS = @WX_LIBS@ +WX_LIBS_STATIC = @WX_LIBS_STATIC@ +WX_RESCOMP = @WX_RESCOMP@ +WX_VERSION = @WX_VERSION@ +WX_VERSION_MAJOR = @WX_VERSION_MAJOR@ +WX_VERSION_MICRO = @WX_VERSION_MICRO@ +WX_VERSION_MINOR = @WX_VERSION_MINOR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_LDFLAGS = -Wl,--as-needed +LDADD = $(LIBUSB_LIBS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign LinuxUSB/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign LinuxUSB/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +rmk8055drv$(EXEEXT): $(rmk8055drv_OBJECTS) $(rmk8055drv_DEPENDENCIES) $(EXTRA_rmk8055drv_DEPENDENCIES) + @rm -f rmk8055drv$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(rmk8055drv_OBJECTS) $(rmk8055drv_LDADD) $(LIBS) + +rmuridrv$(EXEEXT): $(rmuridrv_OBJECTS) $(rmuridrv_DEPENDENCIES) $(EXTRA_rmuridrv_DEPENDENCIES) + @rm -f rmuridrv$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(rmuridrv_OBJECTS) $(rmuridrv_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rmk8055drv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rmuridrv.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \ + distclean distclean-compile distclean-generic distclean-tags \ + distdir dvi dvi-am html html-am info info-am install \ + install-am install-binPROGRAMS install-data install-data-am \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/LinuxUSB/README.txt b/LinuxUSB/README.txt new file mode 100644 index 0000000..579e4b0 --- /dev/null +++ b/LinuxUSB/README.txt @@ -0,0 +1,4 @@ +These programs are designed to remove the usbhid from an K8055 or CM108 (URI +USB) devices on your system. These programs must be run as root, but having been +run successfully run, then any program that accesses them can do so with direct +USB calls and can run as a normal user. diff --git a/LinuxUSB/rmk8055drv.c b/LinuxUSB/rmk8055drv.c new file mode 100644 index 0000000..600f38c --- /dev/null +++ b/LinuxUSB/rmk8055drv.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009,2011 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +const unsigned int VELLEMAN_VENDOR_ID = 0x10CFU; +const unsigned int VELLEMAN_K8055_BOARD0 = 0x5500U; +const unsigned int VELLEMAN_K8055_BOARD1 = 0x5501U; +const unsigned int VELLEMAN_K8055_BOARD2 = 0x5502U; +const unsigned int VELLEMAN_K8055_BOARD3 = 0x5503U; +const unsigned int VELLEMAN_INTERFACE = 0U; + +int main(int argc, char **argv) +{ + unsigned int count = 0U, removed = 0U; + struct libusb_context *context; + struct libusb_device_handle *handle; + struct libusb_device **list; + struct libusb_device_descriptor desc; + ssize_t i; + int err; + + libusb_init(&context); + + ssize_t cnt = libusb_get_device_list(NULL, &list); + if (cnt <= 0) { + fprintf(stderr, "rmk8055drv: cannot find any USB devices!\n"); + return 1; + } + + for (i = 0; i < cnt; i++) { + err = libusb_get_device_descriptor(list[i], &desc); + if (err < 0) + continue; + + if (desc.idVendor == VELLEMAN_VENDOR_ID && + (desc.idProduct == VELLEMAN_K8055_BOARD0 || + desc.idProduct == VELLEMAN_K8055_BOARD1 || + desc.idProduct == VELLEMAN_K8055_BOARD2 || + desc.idProduct == VELLEMAN_K8055_BOARD3)) { + count++; + + err = libusb_open(list[i], &handle); + if (err != 0) { + fprintf(stderr, "rmk8055drv: cannot open the USB device: err=%d\n", err); + continue; + } + + if (libusb_kernel_driver_active(handle, VELLEMAN_INTERFACE) == 1) { + err = libusb_detach_kernel_driver(handle, VELLEMAN_INTERFACE); + if (err == 0) + removed++; + else + fprintf(stderr, "rmk8055drv: could not disconnect from kernel driver: err=%d\n", err); + } + + libusb_close(handle); + } + } + + libusb_free_device_list(list, 1); + + libusb_exit(context); + + fprintf(stdout, "rmk8055drv: found %u K8055 devices and removed %u from the kernel driver\n", count, removed); + + return 0; +} + diff --git a/LinuxUSB/rmuridrv.c b/LinuxUSB/rmuridrv.c new file mode 100644 index 0000000..8ef386f --- /dev/null +++ b/LinuxUSB/rmuridrv.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2009,2011 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +const unsigned int C108_VENDOR_ID = 0x0D8CU; +const unsigned int C108_PRODUCT_ID = 0x000CU; +const unsigned int C108AH_PRODUCT_ID = 0x013CU; +const unsigned int C119_PRODUCT_ID = 0x0008U; +const unsigned int CM108_INTERFACE = 3U; + +int main(int argc, char **argv) +{ + unsigned int count = 0U, removed = 0U; + struct libusb_context *context; + struct libusb_device_handle *handle; + struct libusb_device **list; + struct libusb_device_descriptor desc; + ssize_t i; + int err; + + libusb_init(&context); + + ssize_t cnt = libusb_get_device_list(NULL, &list); + if (cnt <= 0) { + fprintf(stderr, "rmkuridrv: cannot find any USB devices!\n"); + return 1; + } + + for (i = 0; i < cnt; i++) { + err = libusb_get_device_descriptor(list[i], &desc); + if (err < 0) + continue; + + if (desc.idVendor == C108_VENDOR_ID && + (desc.idProduct == C108_PRODUCT_ID || + desc.idProduct == C108AH_PRODUCT_ID || + desc.idProduct == C119_PRODUCT_ID)) { + count++; + + err = libusb_open(list[i], &handle); + if (err != 0) { + fprintf(stderr, "rmkuridrv: cannot open the USB device: err=%d\n", err); + continue; + } + + if (libusb_kernel_driver_active(handle, CM108_INTERFACE) == 1) { + err = libusb_detach_kernel_driver(handle, CM108_INTERFACE); + if (err == 0) + removed++; + else + fprintf(stderr, "rmkuridrv: could not disconnect from kernel driver: err=%d\n", err); + } + + libusb_close(handle); + } + } + + libusb_free_device_list(list, 1); + + libusb_exit(context); + + fprintf(stdout, "rmuridrv: found %u CM108 devices and removed %u from the kernel driver\n", count, removed); + + return 0; +} + diff --git a/README.md b/README.md new file mode 100644 index 0000000..b50b909 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +This is the D-Star Repeater which controls homebrew repeater hardware and links into the ircDDB Gateway to allow for access to extra non-RF facilities, + +The hardware supported include: +* DVAP +* DV-Mega (D-Star only) +* GMSK Modems +* Soundcard repeaters (including UDRC) +* MMDVM (D-Star only) +* DV-RPTR V1, V2, and V3 + +They all build on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2017 on x86 and x64. + +This software is licenced under the GPL v2. diff --git a/WindowsUSB/dvrptr_cdc.inf b/WindowsUSB/dvrptr_cdc.inf new file mode 100644 index 0000000..83ed30c --- /dev/null +++ b/WindowsUSB/dvrptr_cdc.inf @@ -0,0 +1,76 @@ +; Driver-Dummy for the Communication Device Class + +[Version] +Signature="$Windows NT$" +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} + +Provider=%DIGISOLUTIONS% +DriverVer=10/15/1999,5.0.2153.1 + +[Manufacturer] +%DIGISOLUTIONS%=DIGISOLUTIONS, NTamd64 + +[DIGISOLUTIONS] +%DVMODEM_CDC%=Reader, USB\VID_03EB&PID_2307 + +[DIGISOLUTIONS.NTamd64] +%DVMODEM_CDC%=DriverInstall, USB\VID_03EB&PID_2307 + +[Reader_Install.NTx86] + +[DestinationDirs] +DefaultDestDir=12 +Reader.NT.Copy=12 + +[Reader.NT] +include=mdmcpq.inf +CopyFiles=Reader.NT.Copy +AddReg=Reader.NT.AddReg + +[Reader.NT.Copy] +usbser.sys + +[Reader.NT.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[Reader.NT.Services] +AddService = usbser, 0x00000002, Service_Inst + +[Service_Inst] +DisplayName = %Serial.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\usbser.sys +LoadOrderGroup = Base + +[DriverInstall.NTamd64] +include=mdmcpq.inf +CopyFiles=DriverCopyFiles.NTamd64 +AddReg=DriverInstall.NTamd64.AddReg + +[DriverCopyFiles.NTamd64] +usbser.sys,,,0x20 + +[DriverInstall.NTamd64.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[DriverInstall.NTamd64.Services] +AddService=usbser, 0x00000002, DriverService.NTamd64 + +[DriverService.NTamd64] +DisplayName=%Serial.SvcDesc% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\usbser.sys + +[Strings] +DIGISOLUTIONS = "dvrptr.de" +DVMODEM_CDC = "DVRPTR CDC" +Serial.SvcDesc = "USB Serial communication driver" diff --git a/WindowsUSB/gmsk.cat b/WindowsUSB/gmsk.cat new file mode 100644 index 0000000..40c00c7 Binary files /dev/null and b/WindowsUSB/gmsk.cat differ diff --git a/WindowsUSB/gmsk.inf b/WindowsUSB/gmsk.inf new file mode 100644 index 0000000..5acb8d6 --- /dev/null +++ b/WindowsUSB/gmsk.inf @@ -0,0 +1,181 @@ +; +; DUTCH*Star A series of projects to implement a series of devices and +; software tools for use with the D-Star protocol as used in +; the Amateur Radio world. +; +; Installer file (INF) for the DUTCH*Star series of USB GMSK +; adapter boards using the Microsoft WinUSB Driver Framework. +; +; Version: @(#)GMSK.INF 1.0.3 2009/12/09 +; +; Author: Fred N. van Kempen, +; +; Copyright 2009 MicroWalt Corporation +; All Rights Reserved. +; +; This program or documentation contains proprietary +; confidential information and trade secrets of MicroWalt +; Corporation. Reverse engineering of object code is +; prohibited. Use of copyright notice is precautionary +; and does not imply publication. Any unauthorized use, +; reproduction or transfer of this program is strictly +; prohibited. +; +; RESTRICTED RIGHTS NOTICE +; +; Use, duplication, or disclosure by the U.S. Government +; is subject to restrictions as set forth in subdivision +; (b)(3)(ii) of the Rights in Technical Data and Computer +; Software clause at 252.227-7013. +; +; MicroWalt Corporation +; P O BOX 8 +; 1400AA, BUSSUM, NH +; THE NETHERLANDS +; PH: +31 (35) 7503090 +; FAX: +31 (35) 7503091 +; +[Version] +Signature = "$Windows NT$" +Class = GMSKDeviceClass +ClassGUID = {FEFBC710-4131-4212-B140-6CEC9AF7B35D} +Provider = %ProviderName% +DriverVer = 12/09/2009,1.0.3 +CatalogFile = gmsk.cat + +; Since our device is not a standard USB device, we +; need to define a new class for the device. +[ClassInstall32] +Addreg = GMSKClassReg + +[GMSKClassReg] +HKR,,,0,"GMSK Devices" +HKR,,Icon,,"-20" + +; List the manufactuer, and the models supported. +[Manufacturer] +%ProviderName% = GMSK,NTx86,NTamd64,NTia64 + +[GMSK.NTx86] +"GMSK Modem 300" = GMSK_Install,USB\VID_04D8&PID_0300 +"GMSK Modem 301" = GMSK_Install,USB\VID_04D8&PID_0301 +"GMSK Modem 302" = GMSK_Install,USB\VID_04D8&PID_0302 +"GMSK Modem 303" = GMSK_Install,USB\VID_04D8&PID_0303 +"GMSK Modem 304" = GMSK_Install,USB\VID_04D8&PID_0304 +"GMSK Modem 305" = GMSK_Install,USB\VID_04D8&PID_0305 +"GMSK Modem 306" = GMSK_Install,USB\VID_04D8&PID_0306 +"GMSK Modem 307" = GMSK_Install,USB\VID_04D8&PID_0307 +"GMSK Modem 308" = GMSK_Install,USB\VID_04D8&PID_0308 +"GMSK Modem 309" = GMSK_Install,USB\VID_04D8&PID_0309 +"GMSK Modem 310" = GMSK_Install,USB\VID_04D8&PID_0310 + +[GMSK.NTamd64] +"GMSK Modem 300 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0300 +"GMSK Modem 301 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0301 +"GMSK Modem 302 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0302 +"GMSK Modem 303 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0303 +"GMSK Modem 304 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0304 +"GMSK Modem 305 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0305 +"GMSK Modem 306 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0306 +"GMSK Modem 307 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0307 +"GMSK Modem 308 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0308 +"GMSK Modem 309 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0309 +"GMSK Modem 310 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0310 + +[GMSK.NTia64] +"GMSK Modem 300 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0300 +"GMSK Modem 301 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0301 +"GMSK Modem 302 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0302 +"GMSK Modem 303 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0303 +"GMSK Modem 304 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0304 +"GMSK Modem 305 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0305 +"GMSK Modem 306 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0306 +"GMSK Modem 307 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0307 +"GMSK Modem 308 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0308 +"GMSK Modem 309 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0309 +"GMSK Modem 310 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0310 + +; =================== Installation =================== +[GMSK_Install] +Include = winusb.inf +Needs = WINUSB.NT + +[GMSK_Install.Services] +Include = winusb.inf +AddService = WinUSB,0x00000002,WinUSB_ServiceInstall + +[GMSK_Install.Wdf] +KmdfService = WINUSB, WinUsb_Install +UmdfServiceOrder = WINUSB + +[GMSK_Install.HW] +AddReg = GMSK_Install_AddReg + +[GMSK_Install_AddReg] +HKR,,DeviceInterfaceGUIDs,0x10000,"{136C76EF-3F4E-4030-A7E3-E1003EF0A715}" + +[GMSK_Install.CoInstallers] +AddReg = CoInstallers_AddReg +CopyFiles = CoInstallers_CopyFiles + +[WinUSB_ServiceInstall] +DisplayName = "WinUSB Devices" +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WinUSB.sys + +[WinUSB_Install] +KmdfLibraryVersion = 1.7 + +[CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WinUSBCoInstaller.dll","WdfCoInstaller01007.dll,WdfCoInstaller" + +[CoInstallers_CopyFiles] +WinUSBCoInstaller.dll +WdfCoInstaller01007.dll +;WUDFUpdate_01007.dll + +[DestinationDirs] +CoInstallers_CopyFiles = 11 + +; Select the Source Media. +[SourceDisksNames.x86] +1 = %DISK_NAME%,,,\i386 + +[SourceDisksNames.amd64] +2 = %DISK_NAME%,,,\amd64 + +[SourceDisksNames.ia64] +3 = %DISK_NAME%,,,\ia64 + +[SourceDisksFiles.x86] +WinUSBCoInstaller.dll = 1 +WdfCoInstaller01007.dll = 1 +WUDFUpdate_01007.dll = 1 + +[SourceDisksFiles.amd64] +WinUSBCoInstaller.dll = 2 +WdfCoInstaller01007.dll = 2 +WUDFUpdate_01007.dll = 2 + +[SourceDisksFiles.ia64] +WinUSBCoInstaller.dll = 3 +WdfCoInstaller01007.dll = 3 +WUDFUpdate_01007.dll = 3 + +; Copy Files section +[_CopyFiles_sys] +winusb.sys + +; Destination Directories +[DestinationDirs] +DefaultDestDir = 12 ; %SystemRoot%\System32\DRIVERS +_CopyFiles_sys = 12 + +; Strings +[Strings] +ProviderName = "MicroWalt Corporation" +DISK_NAME = "DUTCH*Star GMSK Install Disk" + +; End of GMSK.INF. diff --git a/WindowsUSB/xDVRPTR-32-64-2.inf b/WindowsUSB/xDVRPTR-32-64-2.inf new file mode 100644 index 0000000..23062e3 --- /dev/null +++ b/WindowsUSB/xDVRPTR-32-64-2.inf @@ -0,0 +1,95 @@ +; Windows 2000, XP, Vista and 7 (x32 and x64) setup file for PIPEMSG xDVRPTR CDC Devices +; Copyright (C) 2012 pipeMSG UG +; Creator SAT Hamburg 2012 + +[Version] +Signature="$Windows NT$" +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} +Provider=%PIPEMSG% +LayoutFile=layout.inf +DriverVer=12/09/2012,6.0.2404.0 + +[Manufacturer] +%PIPEMSG%=DeviceList, NTamd64 + +[DestinationDirs] +;FakeModemCopyFileSection=12 +;FakeModemCopyFileSection.NTamd64=12 +DefaultDestDir=12 + + +[DeviceList] +xDVRPTR_32=Reader, USB\VID_03EB&PID_2404 +[DeviceList.NTamd64] +xDVRPTR_64=DriverInstall, USB\VID_03EB&PID_2404 + +;------------------------------------------------------------------------------ +; Windows 2000/XP/Vista/7/8 Sections +;------------------------------------------------------------------------------ + +[Reader_Install.NTx86] + +[DestinationDirs] +DefaultDestDir=12 +Reader.NT.Copy=12 + +[Reader.NT] +include=mdmcpq.inf +CopyFiles=Reader.NT.Copy +AddReg=Reader.NT.AddReg + +[Reader.NT.Copy] +usbser.sys + +[Reader.NT.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[Reader.NT.Services] +AddService = usbser, 0x00000002, Service_Inst + +[Service_Inst] +DisplayName = %Serial.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\usbser.sys +LoadOrderGroup = Base + +;------------------------------------------------------------------------------ +; Windows XP/7/8-64bit Sections +;------------------------------------------------------------------------------ + +[DriverInstall.NTamd64] +include=mdmcpq.inf +CopyFiles=DriverCopyFiles.NTamd64 +AddReg=DriverInstall.NTamd64.AddReg + +[DriverCopyFiles.NTamd64] +usbser.sys,,,0x20 + +[DriverInstall.NTamd64.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[DriverInstall.NTamd64.Services] +AddService=usbser, 0x00000002, DriverService.NTamd64 + +[DriverService.NTamd64] +DisplayName=%Serial.SvcDesc% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\usbser.sys +;------------------------------------------------------------------------------ +; String Definitions +;------------------------------------------------------------------------------ + +[Strings] +PIPEMSG = "PIPEMSG.de" +DVMODEM_CDC="xDVRPTR CDC" +DVMODEM_CDC64="xDVRPTR CDC for x64-based systems" +DESCRIPTION="Provides a xDVRPTR CDC COM-Port when connecting an via USB" diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..9224859 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,6 @@ +dstarrepeater for Debian +------------------------ + + + + -- Jeremy McDermond Mon, 14 Mar 2016 14:40:56 -0700 diff --git a/debian/README.source b/debian/README.source new file mode 100644 index 0000000..134c031 --- /dev/null +++ b/debian/README.source @@ -0,0 +1,10 @@ +dstarrepeater for Debian +------------------------ + + + + + + -- Jeremy McDermond Mon, 14 Mar 2016 14:40:56 -0700 + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..fc04ead --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +dstarrepeater (1.20160314-1) unstable; urgency=low + + * Initial release (Closes: #nnnn) + + -- Jeremy McDermond (NH6Z) Mon, 14 Mar 2016 14:40:56 -0700 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..22a5f4e --- /dev/null +++ b/debian/control @@ -0,0 +1,38 @@ +Source: dstarrepeater +Section: hamradio +Priority: optional +Maintainer: Jeremy McDermond +Build-Depends: debhelper (>= 9), autotools-dev , libwxgtk3.0-dev, libasound2-dev, libusb-1.0-0-dev, pkg-config, wiringpi [armhf armel] +Standards-Version: 3.9.6 +Homepage: https://github.com/dl5di/OpenDV + +Package: dstarrepeater +Architecture: i386 amd64 armel armhf +Depends: ${shlibs:Depends}, ${misc:Depends}, dstarrepeater-udev, opendv-base, wiringpi [armhf armel] +Description: D-STAR Digital Voice Repeater Software GUI Components + Provides an implementation of a D-STAR Digital Voice repeater in software. + This package contains all of the GUI components that require X-Windows to + run correctly including the graphical configuration utility. + +Package: dstarrepeaterd +Architecture: armel armhf amd64 i386 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base, dstarrepeater-udev, wiringpi [armhf armel] +Description: D-STAR Digital Voice Repeater Software Daemon Components + Provides an implementation of a D-STAR Digtal Voice repeater in software. + This package contains the Non-GUI componets that do not require an X-Windows + installation to run. This does not include the configuration utility and + users will have to find alternate ways of configuring the daemon. + +Package: opendv-usb-utils +Architecture: armel armhf amd64 i386 +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: USB Utilities for the OpenDV programs + Provides the USB utilities to manipulate the URI and K8055 devices for + OpenDV. + +Package: dstarrepeater-udev +Architecture: all +Depends: ${misc:Depends} +Description: D-STAR Digital Voice Repeater udev rules + udev rules to change permissions for devices used by dstarrepeater and + dstarrepeaterd diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..dea2d98 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,34 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dstarrepeater +Source: + +Files: * +Copyright: + +License: GPL-3.0+ + +Files: debian/* +Copyright: 2016 Jeremy McDermond +License: GPL-3.0+ + +License: GPL-3.0+ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. +# Please avoid picking licenses with terms that are more restrictive than the +# packaged work, as it may make Debian's contributions unacceptable upstream. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..1d09f01 --- /dev/null +++ b/debian/docs @@ -0,0 +1,5 @@ +BUILD.txt +CHANGES.txt +COPYING.txt +TODO.txt +TODO.txt diff --git a/debian/dstarrepeater-udev.postinst b/debian/dstarrepeater-udev.postinst new file mode 100644 index 0000000..c539795 --- /dev/null +++ b/debian/dstarrepeater-udev.postinst @@ -0,0 +1,52 @@ +#!/bin/sh +# postinst script for dstarrepeater +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +# same test as udev postinst... +chrooted() { + if [ "$(stat -c %d/%i /)" = "$(stat -Lc %d/%i /proc/1/root 2>/dev/null)" ]; + then + # the devicenumber/inode pair of / is the same as that of /sbin/init's + # root, so we're *not* in a chroot and hence return false. + return 1 + fi + return 0 +} + +case "$1" in + configure) + if ! chrooted ; then + udevadm control --reload-rules || true ; + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/dstarrepeater-udev.udev b/debian/dstarrepeater-udev.udev new file mode 100644 index 0000000..2d40185 --- /dev/null +++ b/debian/dstarrepeater-udev.udev @@ -0,0 +1,23 @@ +# +# OpenDV USB Rules. +# This changes the permissions on the USB devices for the OpenDV supported +# devices so that the daemons can access them while running as the opendv +# user. +# + +# Velleman K8055 +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="10cf", ATTR{idProduct}=="5500", MODE="0666" + +# Universal Radio Interface +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="0d8c", ATTR{idProduct}=="000c", MODE="0666" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="0d8c", ATTR{idProduct}=="013c", MODE="0666" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="0d8c", ATTR{idProduct}=="0008", MODE="0666" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="0d8c", ATTR{idProduct}=="013a", MODE="0666" + +# GMSK Node Adapters +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="04d8", ATTR{idProduct}=="0300", MODE="0666" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="04d8", ATTR{idProduct}=="0301", MODE="0666" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="04d8", ATTR{idProduct}=="0310", MODE="0666" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="04d8", ATTR{idProduct}=="0320", MODE="0666" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="04d8", ATTR{idProduct}=="0322", MODE="0666" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="04d8", ATTR{idProduct}=="0324", MODE="0666" diff --git a/debian/dstarrepeater.install b/debian/dstarrepeater.install new file mode 100644 index 0000000..3e5813b --- /dev/null +++ b/debian/dstarrepeater.install @@ -0,0 +1,2 @@ +usr/bin/dstarrepeater +usr/bin/dstarrepeaterconfig diff --git a/debian/dstarrepeater.logrotate b/debian/dstarrepeater.logrotate new file mode 100644 index 0000000..6008479 --- /dev/null +++ b/debian/dstarrepeater.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/dstarrepeater.log /var/log/opendv/dstarrepeater_[0-9].log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/dstarrepeaterd.dstarrepeaterd@.service b/debian/dstarrepeaterd.dstarrepeaterd@.service new file mode 100644 index 0000000..16f9092 --- /dev/null +++ b/debian/dstarrepeaterd.dstarrepeaterd@.service @@ -0,0 +1,13 @@ +[Unit] +Description=D-STAR Repeater Daemon +After=network.target + +[Service] +Environment="WIRINGPI_GPIOMEM=1" +User=opendv +ExecStart=/usr/sbin/dstarrepeaterd %i +Restart=on-abort + +[Install] +WantedBy=multi-user.target +DefaultInstance=1 diff --git a/debian/dstarrepeaterd.examples b/debian/dstarrepeaterd.examples new file mode 100644 index 0000000..b604e4d --- /dev/null +++ b/debian/dstarrepeaterd.examples @@ -0,0 +1 @@ +linux/dstarrepeater_1.example diff --git a/debian/dstarrepeaterd.install b/debian/dstarrepeaterd.install new file mode 100644 index 0000000..36f2c6a --- /dev/null +++ b/debian/dstarrepeaterd.install @@ -0,0 +1 @@ +usr/sbin/dstarrepeaterd diff --git a/debian/dstarrepeaterd.logrotate b/debian/dstarrepeaterd.logrotate new file mode 100644 index 0000000..235af46 --- /dev/null +++ b/debian/dstarrepeaterd.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/dstarrepeaterd.log /var/log/opendv/dstarrepeaterd_[0-9].log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/opendv-usb-utils.install b/debian/opendv-usb-utils.install new file mode 100644 index 0000000..1361e98 --- /dev/null +++ b/debian/opendv-usb-utils.install @@ -0,0 +1,2 @@ +usr/bin/rmuridrv +usr/bin/rmk8055drv diff --git a/debian/rules b/debian/rules new file mode 100644 index 0000000..fd8a82c --- /dev/null +++ b/debian/rules @@ -0,0 +1,41 @@ +#!/usr/bin/make -f +# See debhelper(7) (uncomment to enable) +# output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + +# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk + +# see FEATURE AREAS in dpkg-buildflags(1) +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + + +# main packaging script based on dh7 syntax +%: + dh $@ --with autotools-dev --parallel + +# dh_make generated override targets +# This is example for Cmake (See https://bugs.debian.org/641051 ) +#override_dh_auto_configure: +# dh_auto_configure -- \ +# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) + +ifeq ($(DEB_HOST_ARCH),$(filter $(DEB_HOST_ARCH),armhf armel)) +override_dh_auto_configure: + dh_auto_configure -- \ + --with-gpio +endif + +override_dh_installinit: + dh_installinit + dh_installinit --name=dstarrepeaterd@ + +override_dh_installudev: + dh_installudev --priority=60 diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/linux/dstarrepeater_1.example b/linux/dstarrepeater_1.example new file mode 100644 index 0000000..f527634 --- /dev/null +++ b/linux/dstarrepeater_1.example @@ -0,0 +1,114 @@ +callsign=N0CALL C +gateway= G +mode=0 +ack=1 +restriction=0 +rpt1Validation=1 +dtmfBlanking=1 +errorReply=1 +gatewayAddress=127.0.0.1 +gatewayPort=20010 +localAddress=127.0.0.1 +localPort=20011 +networkName= +modemType=Sound Card +timeout=180 +ackTime=500 +beaconTime=600 +beaconText=D-Star Repeater +beaconVoice=0 +language=0 +announcementEnabled=0 +announcementTime=480 +announcementRecordRPT1= +announcementRecordRPT2= +announcementDeleteRPT1= +announcementDeleteRPT2= +controlEnabled=0 +controlRPT1= +controlRPT2= +controlShutdown= +controlStartup= +controlStatus1= +controlStatus2= +controlStatus3= +controlStatus4= +controlStatus5= +controlCommand1= +controlCommand1Line= +controlCommand2= +controlCommand2Line= +controlCommand3= +controlCommand3Line= +controlCommand4= +controlCommand4Line= +controlCommand5= +controlCommand5Line= +controlCommand6= +controlCommand6Line= +controlOutput1= +controlOutput2= +controlOutput3= +controlOutput4= +controllerType= +serialConfig=1 +pttInvert=0 +activeHangTime=0 +output1=0 +output2=0 +output3=0 +output4=0 +logging=0 +windowX=-1 +windowY=-1 +dvapPort= +dvapFrequency=145500000 +dvapPower=10 +dvapSquelch=-100 +gmskAddress=768 +dvrptr1Port= +dvrptr1RXInvert=0 +dvrptr1TXInvert=0 +dvrptr1Channel=0 +dvrptr1ModLevel=20 +dvrptr1TXDelay=150 +dvrptr2Connection=0 +dvrptr2USBPort= +dvrptr2Address=127.0.0.1 +dvrptr2Port=0 +dvrptr2TXInvert=0 +dvrptr2ModLevel=20 +dvrptr2TXDelay=150 +dvrptr3Connection=0 +dvrptr3USBPort= +dvrptr3Address=127.0.0.1 +dvrptr3Port=0 +dvrptr3TXInvert=0 +dvrptr3ModLevel=20 +dvrptr3TXDelay=150 +dvmegaPort= +dvmegaVariant=0 +dvmegaRXInvert=0 +dvmegaTXInvert=0 +dvmegaTXDelay=150 +dvmegaRXFrequency=145500000 +dvmegaTXFrequency=145500000 +dvmegaPower=100 +mmdvmPort= +mmdvmRXInvert=0 +mmdvmTXInvert=0 +mmdvmPTTInvert=0 +mmdvmTXDelay=50 +mmdvmRXLevel=100 +mmdvmTXLevel=100 +soundCardRXDevice= +soundCardTXDevice= +soundCardRXInvert=0 +soundCardTXInvert=0 +soundCardRXLevel=1.0000 +soundCardTXLevel=1.0000 +soundCardTXDelay=150 +soundCardTXTail=50 +splitLocalAddress= +splitLocalPort=0 +splitTimeout=0