Initial commit.

pull/1/head
Jonathan Naylor 8 years ago
commit dc6b29d625

10
.gitignore vendored

@ -0,0 +1,10 @@
*.o
*.obj
x86
Debug
Release
*.user
*~
*.bak
.vs

@ -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
}

@ -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
<http://www.microsoft.com/en-gb/download/details.aspx?id=5582>
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
<http://www.wxwidgets.org>, and libusb-1.0 from <http://www.libusb.org/wiki/libusb-1.0>.
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 <https://projects.drogon.net/raspberry-pi/wiringpi>.
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".

File diff suppressed because it is too large Load Diff

@ -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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
<signature of Ty Coon>, 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.

@ -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 <wx/wx.h>
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;
}

@ -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

@ -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

@ -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 <wx/filename.h>
#include <wx/file.h>
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;
}
}
}

@ -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 <wx/wx.h>
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

@ -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();
}

@ -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 <wx/wx.h>
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

@ -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 <wx/wx.h>
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

@ -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

@ -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 <wx/filename.h>
#include <wx/textfile.h>
#include <wx/tokenzr.h>
#include <wx/ffile.h>
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;
}

@ -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 <wx/wx.h>
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

@ -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;
}

@ -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 <wx/wx.h>
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

@ -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;
}

@ -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 <wx/wx.h>
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

@ -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 <wx/textfile.h>
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;
}

@ -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 <wx/wx.h>
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

@ -0,0 +1,264 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{3753EF20-2330-415E-B933-2560A474498B}</ProjectGuid>
<RootNamespace>Common</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<IncludePath>$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_x64_dll\mswud;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<IncludePath>$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_x64_dll\mswu;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<PreBuildEvent>
<Command />
</PreBuildEvent>
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<IgnoreStandardIncludePath>false</IgnoreStandardIncludePath>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PreBuildEvent>
<Command>
</Command>
</PreBuildEvent>
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<IgnoreStandardIncludePath>false</IgnoreStandardIncludePath>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<PreBuildEvent>
<Command />
</PreBuildEvent>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>$(SolutionDir)..\WinUSB\include;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PreBuildEvent>
<Command>
</Command>
</PreBuildEvent>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>$(SolutionDir)..\WinUSB\include;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="AMBEFEC.cpp" />
<ClCompile Include="AnnouncementUnit.cpp" />
<ClCompile Include="ArduinoController.cpp" />
<ClCompile Include="BeaconUnit.cpp" />
<ClCompile Include="CallsignList.cpp" />
<ClCompile Include="CCITTChecksum.cpp" />
<ClCompile Include="CCITTChecksumReverse.cpp" />
<ClCompile Include="DStarGMSKDemodulator.cpp" />
<ClCompile Include="DStarGMSKModulator.cpp" />
<ClCompile Include="DStarRepeaterConfig.cpp" />
<ClCompile Include="DummyController.cpp" />
<ClCompile Include="DVAPController.cpp" />
<ClCompile Include="DVMegaController.cpp" />
<ClCompile Include="DVRPTRV1Controller.cpp" />
<ClCompile Include="DVRPTRV2Controller.cpp" />
<ClCompile Include="DVRPTRV3Controller.cpp" />
<ClCompile Include="DVTOOLFileReader.cpp" />
<ClCompile Include="DVTOOLFileWriter.cpp" />
<ClCompile Include="ExternalController.cpp" />
<ClCompile Include="FIRFilter.cpp" />
<ClCompile Include="GatewayProtocolHandler.cpp" />
<ClCompile Include="GMSKController.cpp" />
<ClCompile Include="GMSKModem.cpp" />
<ClCompile Include="GMSKModemLibUsb.cpp" />
<ClCompile Include="GMSKModemWinUSB.cpp" />
<ClCompile Include="Golay.cpp" />
<ClCompile Include="HardwareController.cpp" />
<ClCompile Include="HeaderData.cpp" />
<ClCompile Include="K8055Controller.cpp" />
<ClCompile Include="LogEvent.cpp" />
<ClCompile Include="Logger.cpp" />
<ClCompile Include="MMDVMController.cpp" />
<ClCompile Include="Modem.cpp" />
<ClCompile Include="OutputQueue.cpp" />
<ClCompile Include="RepeaterProtocolHandler.cpp" />
<ClCompile Include="SerialDataController.cpp" />
<ClCompile Include="SerialLineController.cpp" />
<ClCompile Include="SerialPortSelector.cpp" />
<ClCompile Include="SlowDataDecoder.cpp" />
<ClCompile Include="SlowDataEncoder.cpp" />
<ClCompile Include="SoundCardController.cpp" />
<ClCompile Include="SoundCardReaderWriter.cpp" />
<ClCompile Include="SplitController.cpp" />
<ClCompile Include="TCPReaderWriter.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="UDPReaderWriter.cpp" />
<ClCompile Include="URIUSBController.cpp" />
<ClCompile Include="Utils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="AMBEFEC.h" />
<ClInclude Include="AnnouncementCallback.h" />
<ClInclude Include="AnnouncementUnit.h" />
<ClInclude Include="ArduinoController.h" />
<ClInclude Include="AudioCallback.h" />
<ClInclude Include="BeaconCallback.h" />
<ClInclude Include="BeaconUnit.h" />
<ClInclude Include="CallsignList.h" />
<ClInclude Include="CCITTChecksum.h" />
<ClInclude Include="CCITTChecksumReverse.h" />
<ClInclude Include="DStarDefines.h" />
<ClInclude Include="DStarGMSKDemodulator.h" />
<ClInclude Include="DStarGMSKModulator.h" />
<ClInclude Include="DStarRepeaterConfig.h" />
<ClInclude Include="DummyController.h" />
<ClInclude Include="DVAPController.h" />
<ClInclude Include="DVMegaController.h" />
<ClInclude Include="DVRPTRV1Controller.h" />
<ClInclude Include="DVRPTRV2Controller.h" />
<ClInclude Include="DVRPTRV3Controller.h" />
<ClInclude Include="DVTOOLFileReader.h" />
<ClInclude Include="DVTOOLFileWriter.h" />
<ClInclude Include="ExternalController.h" />
<ClInclude Include="FIRFilter.h" />
<ClInclude Include="GatewayProtocolHandler.h" />
<ClInclude Include="GMSKController.h" />
<ClInclude Include="GMSKModem.h" />
<ClInclude Include="GMSKModemLibUsb.h" />
<ClInclude Include="GMSKModemWinUSB.h" />
<ClInclude Include="Golay.h" />
<ClInclude Include="HardwareController.h" />
<ClInclude Include="HeaderData.h" />
<ClInclude Include="K8055Controller.h" />
<ClInclude Include="LogEvent.h" />
<ClInclude Include="Logger.h" />
<ClInclude Include="lusb0_usb.h" />
<ClInclude Include="MMDVMController.h" />
<ClInclude Include="Modem.h" />
<ClInclude Include="OutputQueue.h" />
<ClInclude Include="RepeaterProtocolHandler.h" />
<ClInclude Include="RingBuffer.h" />
<ClInclude Include="SerialDataController.h" />
<ClInclude Include="SerialLineController.h" />
<ClInclude Include="SerialPortSelector.h" />
<ClInclude Include="SlowDataDecoder.h" />
<ClInclude Include="SlowDataEncoder.h" />
<ClInclude Include="SoundCardController.h" />
<ClInclude Include="SoundCardReaderWriter.h" />
<ClInclude Include="SplitController.h" />
<ClInclude Include="TCPReaderWriter.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="UDPReaderWriter.h" />
<ClInclude Include="URIUSBController.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="Version.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,326 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="AMBEFEC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AnnouncementUnit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ArduinoController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="BeaconUnit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CallsignList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CCITTChecksum.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CCITTChecksumReverse.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DStarGMSKDemodulator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DStarGMSKModulator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DStarRepeaterConfig.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DummyController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DVAPController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DVMegaController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DVRPTRV1Controller.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DVRPTRV2Controller.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DVRPTRV3Controller.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DVTOOLFileReader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DVTOOLFileWriter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ExternalController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FIRFilter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GatewayProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GMSKController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GMSKModem.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GMSKModemLibUsb.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GMSKModemWinUSB.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Golay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HardwareController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HeaderData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="K8055Controller.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LogEvent.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Logger.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="MMDVMController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Modem.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="OutputQueue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RepeaterProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SerialDataController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SerialLineController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SerialPortSelector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SlowDataDecoder.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SlowDataEncoder.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SoundCardController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SoundCardReaderWriter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SplitController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TCPReaderWriter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Timer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UDPReaderWriter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="URIUSBController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="AMBEFEC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="AnnouncementCallback.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="AnnouncementUnit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ArduinoController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="AudioCallback.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BeaconCallback.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BeaconUnit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CallsignList.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CCITTChecksum.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CCITTChecksumReverse.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarDefines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarGMSKDemodulator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarGMSKModulator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarRepeaterConfig.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DummyController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DVAPController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DVMegaController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DVRPTRV1Controller.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DVRPTRV2Controller.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DVRPTRV3Controller.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DVTOOLFileReader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DVTOOLFileWriter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ExternalController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FIRFilter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GatewayProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GMSKController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GMSKModem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GMSKModemLibUsb.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GMSKModemWinUSB.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Golay.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HardwareController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HeaderData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="K8055Controller.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LogEvent.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Logger.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="lusb0_usb.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MMDVMController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Modem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="OutputQueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RepeaterProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RingBuffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SerialDataController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SerialLineController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SerialPortSelector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SlowDataDecoder.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SlowDataEncoder.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SoundCardController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SoundCardReaderWriter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SplitController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TCPReaderWriter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Timer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UDPReaderWriter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="URIUSBController.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

@ -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 <wx/wx.h>
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

@ -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;
}

@ -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

@ -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;
}

@ -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

File diff suppressed because it is too large Load Diff

@ -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 <wx/wx.h>
#include <wx/config.h>
#include <wx/filename.h>
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

@ -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 <wx/wx.h>
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;
}

@ -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

File diff suppressed because it is too large Load Diff

@ -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 <wx/wx.h>
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<unsigned char> 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

File diff suppressed because it is too large Load Diff

@ -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 <wx/wx.h>
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<unsigned char> 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

File diff suppressed because it is too large Load Diff

@ -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 <wx/wx.h>
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<unsigned char> 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

@ -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 <setupapi.h>
#else
#include <wx/dir.h>
#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;
}
}

@ -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 <wx/wx.h>
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<unsigned char> 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

@ -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 <setupapi.h>
#else
#include <wx/dir.h>
#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;
}
}

@ -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 <wx/wx.h>
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<unsigned char> 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

@ -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 <wx/wx.h>
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();
}

@ -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 <wx/wx.h>
#include <wx/ffile.h>
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

@ -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 <wx/wx.h>
#include <wx/filename.h>
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;
}

@ -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 <wx/wx.h>
#include <wx/ffile.h>
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

@ -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 <wx/wx.h>
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()
{
}

@ -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

@ -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();
}

@ -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 <wx/wx.h>
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

@ -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));;
}

@ -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 <wx/wx.h>
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

@ -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;
}

@ -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 <wx/wx.h>
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<unsigned char> m_txData;
bool reopenModem();
};
#endif

@ -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()
{
}

@ -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 <wx/wx.h>
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

@ -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;
}

@ -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 <wx/wx.h>
#if defined(WIN32)
#include <wx/dynlib.h>
#if _MSC_VER == 1900
#undef __USB_H__
#include <lusb0_usb.h>
#else
#include "lusb0_usb.h"
#endif
#else
#include <libusb-1.0/libusb.h>
#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

@ -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 <Setupapi.h>
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;
}

@ -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 <wx/wx.h>
#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

@ -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 <wiringPi.h>
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()
{
}

@ -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 <wx/wx.h>
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

@ -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();
}

@ -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 <wx/wx.h>
#include <wx/datetime.h>
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

File diff suppressed because it is too large Load Diff

@ -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

@ -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()
{
}

@ -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

@ -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;
}

@ -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 <wx/wx.h>
#include <wx/datetime.h>
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

@ -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 <Setupapi.h>
#include <hidsdi.h>
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

@ -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 <wx/wx.h>
#if defined(__WINDOWS__)
#include <windows.h>
#else
#include <libusb-1.0/libusb.h>
#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

@ -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);
}

@ -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 <wx/wx.h>
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

@ -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(&timestamp);
struct tm* tm = ::gmtime(&timestamp);
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(&timestamp);
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();
}

@ -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 <wx/wx.h>
#include <wx/ffile.h>
#include <wx/filename.h>
#include <wx/log.h>
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

@ -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 <setupapi.h>
#else
#include <wx/dir.h>
#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);
}
}

@ -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 <wx/wx.h>
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<unsigned char> 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

@ -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();
}

@ -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 <wx/wx.h>
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<unsigned char> 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

@ -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;
}

@ -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 <wx/wx.h>
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<unsigned char> m_data;
unsigned int m_threshold;
CHeaderData* m_header;
unsigned int m_count;
};
#endif

@ -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();
}

@ -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 <wx/wx.h>
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

@ -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 <wx/wx.h>
template<class T> 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

@ -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 <sys/types.h>
#if defined(__WINDOWS__)
#include <setupapi.h>
#include <winioctl.h>
#else
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <cerrno>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#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

@ -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 <wx/wx.h>
#if defined(__WINDOWS__)
#include <windows.h>
#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

@ -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 <sys/types.h>
#if defined(__WINDOWS__)
#include <setupapi.h>
#include <winioctl.h>
#else
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <cerrno>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#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

@ -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 <wx/wx.h>
#if defined(__WINDOWS__)
#include <windows.h>
#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

@ -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 <sys/types.h>
#if defined(__APPLE__) && defined(__MACH__)
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOTypes.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/IOBSD.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <termios.h>
#elif defined(__WINDOWS__)
#include <setupapi.h>
#include <winioctl.h>
#else
#include <wx/dir.h>
#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;
}

@ -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 <wx/wx.h>
class CSerialPortSelector {
public:
static wxArrayString getDevices();
private:
};
#endif

@ -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;
}

@ -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 <wx/wx.h>
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

@ -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;
}

@ -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 <wx/wx.h>
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

File diff suppressed because it is too large Load Diff

@ -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 <wx/wx.h>
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<wxFloat32> m_txAudio;
CRingBuffer<wxFloat32> 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

@ -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<CSoundCardReaderWriter*>(userData);
object->callback(static_cast<const wxFloat32*>(input), static_cast<wxFloat32*>(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 = &paramsIn;
}
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 = &paramsOut;
}
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

@ -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 <wx/wx.h>
#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 <alsa/asoundlib.h>
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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save

Powered by TurnKey Linux.