From 4c53d23c1239286147e93e98cfd3772ffdfc540f Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 19 May 2016 17:36:54 +0100 Subject: [PATCH] Initial version. --- .gitignore | 15 + LICENCE | 340 ++++++++++++++++++++++ YSFClients.sln | 38 +++ YSFParrot/Makefile | 19 ++ YSFParrot/Network.cpp | 141 +++++++++ YSFParrot/Network.h | 56 ++++ YSFParrot/Parrot.cpp | 74 +++++ YSFParrot/Parrot.h | 41 +++ YSFParrot/RingBuffer.h | 147 ++++++++++ YSFParrot/StopWatch.cpp | 84 ++++++ YSFParrot/StopWatch.h | 46 +++ YSFParrot/Timer.cpp | 68 +++++ YSFParrot/Timer.h | 89 ++++++ YSFParrot/UDPSocket.cpp | 261 +++++++++++++++++ YSFParrot/UDPSocket.h | 58 ++++ YSFParrot/Utils.cpp | 145 +++++++++ YSFParrot/Utils.h | 36 +++ YSFParrot/YSFDefines.h | 49 ++++ YSFParrot/YSFParrot.cpp | 136 +++++++++ YSFParrot/YSFParrot.h | 34 +++ YSFParrot/YSFParrot.vcxproj | 171 +++++++++++ YSFParrot/YSFParrot.vcxproj.filters | 65 +++++ YSFReflector/Makefile | 19 ++ YSFReflector/Network.cpp | 150 ++++++++++ YSFReflector/Network.h | 56 ++++ YSFReflector/RingBuffer.h | 147 ++++++++++ YSFReflector/StopWatch.cpp | 84 ++++++ YSFReflector/StopWatch.h | 46 +++ YSFReflector/Timer.cpp | 68 +++++ YSFReflector/Timer.h | 89 ++++++ YSFReflector/UDPSocket.cpp | 261 +++++++++++++++++ YSFReflector/UDPSocket.h | 58 ++++ YSFReflector/Utils.cpp | 145 +++++++++ YSFReflector/Utils.h | 36 +++ YSFReflector/YSFDefines.h | 49 ++++ YSFReflector/YSFReflector.cpp | 150 ++++++++++ YSFReflector/YSFReflector.h | 69 +++++ YSFReflector/YSFReflector.vcxproj | 169 +++++++++++ YSFReflector/YSFReflector.vcxproj.filters | 59 ++++ 39 files changed, 3768 insertions(+) create mode 100644 .gitignore create mode 100644 LICENCE create mode 100644 YSFClients.sln create mode 100644 YSFParrot/Makefile create mode 100644 YSFParrot/Network.cpp create mode 100644 YSFParrot/Network.h create mode 100644 YSFParrot/Parrot.cpp create mode 100644 YSFParrot/Parrot.h create mode 100644 YSFParrot/RingBuffer.h create mode 100644 YSFParrot/StopWatch.cpp create mode 100644 YSFParrot/StopWatch.h create mode 100644 YSFParrot/Timer.cpp create mode 100644 YSFParrot/Timer.h create mode 100644 YSFParrot/UDPSocket.cpp create mode 100644 YSFParrot/UDPSocket.h create mode 100644 YSFParrot/Utils.cpp create mode 100644 YSFParrot/Utils.h create mode 100644 YSFParrot/YSFDefines.h create mode 100644 YSFParrot/YSFParrot.cpp create mode 100644 YSFParrot/YSFParrot.h create mode 100644 YSFParrot/YSFParrot.vcxproj create mode 100644 YSFParrot/YSFParrot.vcxproj.filters create mode 100644 YSFReflector/Makefile create mode 100644 YSFReflector/Network.cpp create mode 100644 YSFReflector/Network.h create mode 100644 YSFReflector/RingBuffer.h create mode 100644 YSFReflector/StopWatch.cpp create mode 100644 YSFReflector/StopWatch.h create mode 100644 YSFReflector/Timer.cpp create mode 100644 YSFReflector/Timer.h create mode 100644 YSFReflector/UDPSocket.cpp create mode 100644 YSFReflector/UDPSocket.h create mode 100644 YSFReflector/Utils.cpp create mode 100644 YSFReflector/Utils.h create mode 100644 YSFReflector/YSFDefines.h create mode 100644 YSFReflector/YSFReflector.cpp create mode 100644 YSFReflector/YSFReflector.h create mode 100644 YSFReflector/YSFReflector.vcxproj create mode 100644 YSFReflector/YSFReflector.vcxproj.filters diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..082aa4d --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +Debug +Release +x64 +MMDVMHost +*.o +*.opendb +*.bak +*.obj +*~ +*.sdf +*.log +*.zip +*.exe +*.user +.vs diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/LICENCE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/YSFClients.sln b/YSFClients.sln new file mode 100644 index 0000000..9003b89 --- /dev/null +++ b/YSFClients.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YSFParrot", "YSFParrot\YSFParrot.vcxproj", "{D3BBE5EC-91F7-457B-B782-B616B918708F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YSFReflector", "YSFReflector\YSFReflector.vcxproj", "{317D87F1-3485-4739-9F94-A07738B8E19D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D3BBE5EC-91F7-457B-B782-B616B918708F}.Debug|x64.ActiveCfg = Debug|x64 + {D3BBE5EC-91F7-457B-B782-B616B918708F}.Debug|x64.Build.0 = Debug|x64 + {D3BBE5EC-91F7-457B-B782-B616B918708F}.Debug|x86.ActiveCfg = Debug|Win32 + {D3BBE5EC-91F7-457B-B782-B616B918708F}.Debug|x86.Build.0 = Debug|Win32 + {D3BBE5EC-91F7-457B-B782-B616B918708F}.Release|x64.ActiveCfg = Release|x64 + {D3BBE5EC-91F7-457B-B782-B616B918708F}.Release|x64.Build.0 = Release|x64 + {D3BBE5EC-91F7-457B-B782-B616B918708F}.Release|x86.ActiveCfg = Release|Win32 + {D3BBE5EC-91F7-457B-B782-B616B918708F}.Release|x86.Build.0 = Release|Win32 + {317D87F1-3485-4739-9F94-A07738B8E19D}.Debug|x64.ActiveCfg = Debug|x64 + {317D87F1-3485-4739-9F94-A07738B8E19D}.Debug|x64.Build.0 = Debug|x64 + {317D87F1-3485-4739-9F94-A07738B8E19D}.Debug|x86.ActiveCfg = Debug|Win32 + {317D87F1-3485-4739-9F94-A07738B8E19D}.Debug|x86.Build.0 = Debug|Win32 + {317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x64.ActiveCfg = Release|x64 + {317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x64.Build.0 = Release|x64 + {317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x86.ActiveCfg = Release|Win32 + {317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/YSFParrot/Makefile b/YSFParrot/Makefile new file mode 100644 index 0000000..d098a5a --- /dev/null +++ b/YSFParrot/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CXX = g++ +CFLAGS = -g -O3 -Wall -std=c++0x +LIBS = +LDFLAGS = -g + +OBJECTS = Network.o Parrot.o StopWatch.o Timer.o UDPSocket.o Utils.o YSFParrot.o + +all: YSFParrot + +YSFParrot: $(OBJECTS) + $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o YSFParrot + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + +clean: + $(RM) YSFParrot *.o *.d *.bak *~ + \ No newline at end of file diff --git a/YSFParrot/Network.cpp b/YSFParrot/Network.cpp new file mode 100644 index 0000000..1038801 --- /dev/null +++ b/YSFParrot/Network.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2009-2014,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "YSFDefines.h" +#include "Network.h" +#include "Utils.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CNetwork::CNetwork(unsigned int port, bool debug) : +m_socket(port), +m_address(), +m_port(0U), +m_debug(debug), +m_buffer(1000U, "YSF Network"), +m_pollTimer(1000U, 60U) +{ +} + +CNetwork::~CNetwork() +{ +} + +bool CNetwork::open() +{ + ::fprintf(stdout, "Opening YSF network connection\n"); + + if (m_address.s_addr == INADDR_NONE) + return false; + + m_pollTimer.start(); + + return m_socket.open(); +} + +bool CNetwork::write(const unsigned char* data) +{ + assert(data != NULL); + + if (m_debug) + CUtils::dump(1U, "YSF Network Data Sent", data, 155U); + + return m_socket.write(data, 155U, m_address, m_port); +} + +bool CNetwork::writePoll() +{ + unsigned char buffer[20U]; + + buffer[0] = 'Y'; + buffer[1] = 'S'; + buffer[2] = 'F'; + buffer[3] = 'P'; + + buffer[4U] = 'P'; + buffer[5U] = 'A'; + buffer[6U] = 'R'; + buffer[7U] = 'R'; + buffer[8U] = 'O'; + buffer[9U] = 'T'; + buffer[10U] = ' '; + buffer[11U] = ' '; + buffer[12U] = ' '; + buffer[13U] = ' '; + + if (m_debug) + CUtils::dump(1U, "YSF Network Poll Sent", buffer, 14U); + + return m_socket.write(buffer, 14U, m_address, m_port); +} + +void CNetwork::clock(unsigned int ms) +{ + m_pollTimer.clock(ms); + if (m_pollTimer.hasExpired()) { + writePoll(); + m_pollTimer.start(); + } + + unsigned char buffer[BUFFER_LENGTH]; + + in_addr address; + unsigned int port; + int length = m_socket.read(buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return; + + m_address.s_addr = address.s_addr; + m_port = port; + + // Ignore incoming polls + if (::memcmp(buffer, "YSFP", 4U) == 0) + return; + + // Invalid packet type? + if (::memcmp(buffer, "YSFD", 4U) != 0) + return; + + if (m_debug) + CUtils::dump(1U, "YSF Network Data Received", buffer, length); + + m_buffer.addData(buffer, 155U); +} + +unsigned int CNetwork::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_buffer.isEmpty()) + return 0U; + + m_buffer.getData(data, 155U); + + return 155U; +} + +void CNetwork::close() +{ + m_socket.close(); + + ::fprintf(stdout, "Closing YSF network connection\n"); +} diff --git a/YSFParrot/Network.h b/YSFParrot/Network.h new file mode 100644 index 0000000..8e062eb --- /dev/null +++ b/YSFParrot/Network.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009-2014,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Network_H +#define Network_H + +#include "YSFDefines.h" +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CNetwork { +public: + CNetwork(unsigned int port, bool debug); + ~CNetwork(); + + bool open(); + + bool write(const unsigned char* data); + + unsigned int read(unsigned char* data); + + void close(); + + void clock(unsigned int ms); + +private: + CUDPSocket m_socket; + in_addr m_address; + unsigned int m_port; + bool m_debug; + CRingBuffer m_buffer; + CTimer m_pollTimer; + + bool writePoll(); +}; + +#endif diff --git a/YSFParrot/Parrot.cpp b/YSFParrot/Parrot.cpp new file mode 100644 index 0000000..ce0a3b5 --- /dev/null +++ b/YSFParrot/Parrot.cpp @@ -0,0 +1,74 @@ +/* +* Copyright (C) 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 "Parrot.h" +#include "YSFDefines.h" + +#include +#include +#include + +CParrot::CParrot(unsigned int timeout) : +m_data(NULL), +m_length(timeout * 1550U + 1000U), +m_used(0U), +m_ptr(0U) +{ + assert(timeout > 0U); + + m_data = new unsigned char[m_length]; +} + +CParrot::~CParrot() +{ + delete[] m_data; +} + +bool CParrot::write(const unsigned char* data) +{ + assert(data != NULL); + + if ((m_length - m_used) < 1000U) + return false; + + ::memcpy(m_data + m_used, data, 155U); + m_used += 155U; + + return true; +} + +void CParrot::end() +{ + m_ptr = 0U; +} + +unsigned int CParrot::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_used == 0U) + return 0U; + + ::memcpy(data, m_data + m_ptr, 155U); + m_ptr += 155U; + + if (m_ptr >= m_used) + m_used = 0U; + + return 155U; +} diff --git a/YSFParrot/Parrot.h b/YSFParrot/Parrot.h new file mode 100644 index 0000000..6cceb81 --- /dev/null +++ b/YSFParrot/Parrot.h @@ -0,0 +1,41 @@ +/* +* Copyright (C) 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. +*/ + +#if !defined(Parrot_H) +#define Parrot_H + +class CParrot +{ +public: + CParrot(unsigned int timeout); + ~CParrot(); + + bool write(const unsigned char* data); + + unsigned int read(unsigned char* data); + + void end(); + +private: + unsigned char* m_data; + unsigned int m_length; + unsigned int m_used; + unsigned int m_ptr; +}; + +#endif diff --git a/YSFParrot/RingBuffer.h b/YSFParrot/RingBuffer.h new file mode 100644 index 0000000..186709d --- /dev/null +++ b/YSFParrot/RingBuffer.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2006-2009,2012,2013,2015,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. + */ + +#ifndef RingBuffer_H +#define RingBuffer_H + +#include +#include +#include + +template class CRingBuffer { +public: + CRingBuffer(unsigned int length, const char* name) : + m_length(length), + m_name(name), + m_buffer(NULL), + m_iPtr(0U), + m_oPtr(0U) + { + assert(length > 0U); + assert(name != NULL); + + m_buffer = new T[length]; + + ::memset(m_buffer, 0x00, m_length * sizeof(T)); + } + + ~CRingBuffer() + { + delete[] m_buffer; + } + + bool addData(const T* buffer, unsigned int nSamples) + { + if (nSamples >= freeSpace()) { + ::fprintf(stderr, "**** Overflow in %s ring buffer, %u >= %u\n", m_name, nSamples, freeSpace()); + return false; + } + + for (unsigned int i = 0U; i < nSamples; i++) { + m_buffer[m_iPtr++] = buffer[i]; + + if (m_iPtr == m_length) + m_iPtr = 0U; + } + + return true; + } + + bool getData(T* buffer, unsigned int nSamples) + { + if (dataSize() < nSamples) { + ::fprintf(stderr, "**** Underflow in %s ring buffer, %u < %u\n", m_name, dataSize(), nSamples); + return false; + } + + for (unsigned int i = 0U; i < nSamples; i++) { + buffer[i] = m_buffer[m_oPtr++]; + + if (m_oPtr == m_length) + m_oPtr = 0U; + } + + return true; + } + + bool peek(T* buffer, unsigned int nSamples) + { + if (dataSize() < nSamples) { + ::fprintf(stderr, "**** Underflow peek in %s ring buffer, %u < %u\n", m_name, dataSize(), nSamples); + return false; + } + + 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 true; + } + + 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; + + if (m_oPtr > m_iPtr) + return m_oPtr - m_iPtr; + + return (m_length + m_oPtr) - m_iPtr; + } + + unsigned int dataSize() const + { + return m_length - freeSpace(); + } + + 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; + const char* m_name; + T* m_buffer; + unsigned int m_iPtr; + unsigned int m_oPtr; +}; + +#endif diff --git a/YSFParrot/StopWatch.cpp b/YSFParrot/StopWatch.cpp new file mode 100644 index 0000000..77d539d --- /dev/null +++ b/YSFParrot/StopWatch.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015,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 "StopWatch.h" + +#if defined(_WIN32) || defined(_WIN64) + +CStopWatch::CStopWatch() : +m_frequency(), +m_start() +{ + ::QueryPerformanceFrequency(&m_frequency); +} + +CStopWatch::~CStopWatch() +{ +} + +unsigned long CStopWatch::start() +{ + ::QueryPerformanceCounter(&m_start); + + return (unsigned long)(m_start.QuadPart / m_frequency.QuadPart); +} + +unsigned int CStopWatch::elapsed() +{ + LARGE_INTEGER now; + ::QueryPerformanceCounter(&now); + + LARGE_INTEGER temp; + temp.QuadPart = (now.QuadPart - m_start.QuadPart) * 1000; + + return (unsigned int)(temp.QuadPart / m_frequency.QuadPart); +} + +#else + +#include + +CStopWatch::CStopWatch() : +m_start() +{ +} + +CStopWatch::~CStopWatch() +{ +} + +unsigned long CStopWatch::start() +{ + ::gettimeofday(&m_start, NULL); + + return m_start.tv_usec; +} + +unsigned int CStopWatch::elapsed() +{ + struct timeval now; + ::gettimeofday(&now, NULL); + + unsigned int elapsed = (now.tv_sec - m_start.tv_sec) * 1000U; + elapsed += now.tv_usec / 1000U; + elapsed -= m_start.tv_usec / 1000U; + + return elapsed; +} + +#endif diff --git a/YSFParrot/StopWatch.h b/YSFParrot/StopWatch.h new file mode 100644 index 0000000..811047e --- /dev/null +++ b/YSFParrot/StopWatch.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015,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. + */ + +#if !defined(STOPWATCH_H) +#define STOPWATCH_H + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +class CStopWatch +{ +public: + CStopWatch(); + ~CStopWatch(); + + unsigned long start(); + unsigned int elapsed(); + +private: +#if defined(_WIN32) || defined(_WIN64) + LARGE_INTEGER m_frequency; + LARGE_INTEGER m_start; +#else + struct timeval m_start; +#endif +}; + +#endif diff --git a/YSFParrot/Timer.cpp b/YSFParrot/Timer.cpp new file mode 100644 index 0000000..53956e4 --- /dev/null +++ b/YSFParrot/Timer.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009,2010,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 "Timer.h" + +#include +#include + +CTimer::CTimer(unsigned int ticksPerSec, unsigned int secs, unsigned int msecs) : +m_ticksPerSec(ticksPerSec), +m_timeout(0U), +m_timer(0U) +{ + assert(ticksPerSec > 0U); + + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / 1000ULL + 1ULL); + } +} + +CTimer::~CTimer() +{ +} + +void CTimer::setTimeout(unsigned int secs, unsigned int msecs) +{ + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / 1000ULL + 1ULL); + } else { + m_timeout = 0U; + m_timer = 0U; + } +} + +unsigned int CTimer::getTimeout() const +{ + if (m_timeout == 0U) + return 0U; + + return (m_timeout - 1U) / m_ticksPerSec; +} + +unsigned int CTimer::getTimer() const +{ + if (m_timer == 0U) + return 0U; + + return (m_timer - 1U) / m_ticksPerSec; +} diff --git a/YSFParrot/Timer.h b/YSFParrot/Timer.h new file mode 100644 index 0000000..87d68f5 --- /dev/null +++ b/YSFParrot/Timer.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009,2010,2011,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Timer_H +#define Timer_H + +class CTimer { +public: + CTimer(unsigned int ticksPerSec, unsigned int secs = 0U, unsigned int msecs = 0U); + ~CTimer(); + + void setTimeout(unsigned int secs, unsigned int msecs = 0U); + + unsigned int getTimeout() const; + unsigned int getTimer() const; + + unsigned int getRemaining() + { + if (m_timeout == 0U || m_timer == 0U) + return 0U; + + if (m_timer >= m_timeout) + return 0U; + + return (m_timeout - m_timer) / m_ticksPerSec; + } + + bool isRunning() + { + return m_timer > 0U; + } + + void start(unsigned int secs, unsigned int msecs = 0U) + { + setTimeout(secs, msecs); + + start(); + } + + void start() + { + if (m_timeout > 0U) + m_timer = 1U; + } + + void stop() + { + m_timer = 0U; + } + + bool hasExpired() + { + if (m_timeout == 0U || m_timer == 0U) + return false; + + if (m_timer >= m_timeout) + return true; + + return false; + } + + void clock(unsigned int ticks = 1U) + { + if (m_timer > 0U && m_timeout > 0U) + m_timer += ticks; + } + +private: + unsigned int m_ticksPerSec; + unsigned int m_timeout; + unsigned int m_timer; +}; + +#endif diff --git a/YSFParrot/UDPSocket.cpp b/YSFParrot/UDPSocket.cpp new file mode 100644 index 0000000..733edd0 --- /dev/null +++ b/YSFParrot/UDPSocket.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2006-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 "UDPSocket.h" + +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#endif + + +CUDPSocket::CUDPSocket(const std::string& address, unsigned int port) : +m_address(address), +m_port(port), +m_fd(-1) +{ + assert(!address.empty()); + +#if defined(_WIN32) || defined(_WIN64) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + ::fprintf(stderr, "Error from WSAStartup\n"); +#endif +} + +CUDPSocket::CUDPSocket(unsigned int port) : +m_address(), +m_port(port), +m_fd(-1) +{ +#if defined(_WIN32) || defined(_WIN64) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + ::fprintf(stderr, "Error from WSAStartup\n"); +#endif +} + +CUDPSocket::~CUDPSocket() +{ +#if defined(_WIN32) || defined(_WIN64) + ::WSACleanup(); +#endif +} + +in_addr CUDPSocket::lookup(const std::string& hostname) +{ + in_addr addr; +#if defined(_WIN32) || defined(_WIN64) + unsigned long address = ::inet_addr(hostname.c_str()); + if (address != INADDR_NONE && address != INADDR_ANY) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.c_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#else + in_addr_t address = ::inet_addr(hostname.c_str()); + if (address != in_addr_t(-1)) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.c_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#endif +} + +bool CUDPSocket::open() +{ + m_fd = ::socket(PF_INET, SOCK_DGRAM, 0); + if (m_fd < 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Cannot create the UDP socket, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Cannot create the UDP socket, err: %d\n", errno); +#endif + return false; + } + + if (m_port > 0U) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (!m_address.empty()) { +#if defined(_WIN32) || defined(_WIN64) + addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); +#else + addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); +#endif + if (addr.sin_addr.s_addr == INADDR_NONE) { + ::fprintf(stderr, "The local address is invalid - %s\n", m_address.c_str()); + return false; + } + } + + int reuse = 1; + if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Cannot set the UDP socket option, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Cannot set the UDP socket option, err: %d\n", errno); +#endif + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Cannot bind the UDP address, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Cannot bind the UDP address, err: %d\n", errno); +#endif + return false; + } + } + + return true; +} + +int CUDPSocket::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port) +{ + assert(buffer != NULL); + assert(length > 0U); + + // Check that the readfrom() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(_WIN32) || defined(_WIN64) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return immediately + timeval tv; + tv.tv_sec = 0L; + tv.tv_usec = 0L; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Error returned from UDP select, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Error returned from UDP select, err: %d\n", errno); +#endif + return -1; + } + + if (ret == 0) + return 0; + + sockaddr_in addr; +#if defined(_WIN32) || defined(_WIN64) + int size = sizeof(sockaddr_in); +#else + socklen_t size = sizeof(sockaddr_in); +#endif + +#if defined(_WIN32) || defined(_WIN64) + int len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); +#else + ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); +#endif + if (len <= 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Error returned from recvfrom, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Error returned from recvfrom, err: %d\n", errno); +#endif + return -1; + } + + address = addr.sin_addr; + port = ntohs(addr.sin_port); + + return len; +} + +bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port) +{ + assert(buffer != NULL); + assert(length > 0U); + + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + + addr.sin_family = AF_INET; + addr.sin_addr = address; + addr.sin_port = htons(port); + +#if defined(_WIN32) || defined(_WIN64) + int ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); +#else + ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); +#endif + if (ret < 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Error returned from sendto, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Error returned from sendto, err: %d\n", errno); +#endif + return false; + } + +#if defined(_WIN32) || defined(_WIN64) + if (ret != int(length)) + return false; +#else + if (ret != ssize_t(length)) + return false; +#endif + + return true; +} + +void CUDPSocket::close() +{ +#if defined(_WIN32) || defined(_WIN64) + ::closesocket(m_fd); +#else + ::close(m_fd); +#endif +} diff --git a/YSFParrot/UDPSocket.h b/YSFParrot/UDPSocket.h new file mode 100644 index 0000000..e0af272 --- /dev/null +++ b/YSFParrot/UDPSocket.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009-2011,2013,2015,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. + */ + +#ifndef UDPSocket_H +#define UDPSocket_H + +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +class CUDPSocket { +public: + CUDPSocket(const std::string& address, unsigned int port = 0U); + CUDPSocket(unsigned int port = 0U); + ~CUDPSocket(); + + bool open(); + + int read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port); + bool write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port); + + void close(); + + static in_addr lookup(const std::string& hostName); + +private: + std::string m_address; + unsigned short m_port; + int m_fd; +}; + +#endif diff --git a/YSFParrot/Utils.cpp b/YSFParrot/Utils.cpp new file mode 100644 index 0000000..d5b2c75 --- /dev/null +++ b/YSFParrot/Utils.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2009,2014,2015,2016 Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "Utils.h" + +#include +#include + +void CUtils::dump(const std::string& title, const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + dump(2U, title, data, length); +} + +void CUtils::dump(int level, const std::string& title, const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + ::fprintf(stdout, "%s\n", title.c_str()); + + unsigned int offset = 0U; + + while (length > 0U) { + std::string output; + + unsigned int bytes = (length > 16U) ? 16U : length; + + for (unsigned i = 0U; i < bytes; i++) { + char temp[10U]; + ::sprintf(temp, "%02X ", data[offset + i]); + output += temp; + } + + for (unsigned int i = bytes; i < 16U; i++) + output += " "; + + output += " *"; + + for (unsigned i = 0U; i < bytes; i++) { + unsigned char c = data[offset + i]; + + if (::isprint(c)) + output += c; + else + output += '.'; + } + + output += '*'; + + ::fprintf(stdout, "%04X: %s\n", offset, output.c_str()); + + offset += 16U; + + if (length >= 16U) + length -= 16U; + else + length = 0U; + } +} + +void CUtils::dump(const std::string& title, const bool* bits, unsigned int length) +{ + assert(bits != NULL); + + dump(2U, title, bits, length); +} + +void CUtils::dump(int level, const std::string& title, const bool* bits, unsigned int length) +{ + assert(bits != NULL); + + unsigned char bytes[100U]; + unsigned int nBytes = 0U; + for (unsigned int n = 0U; n < length; n += 8U, nBytes++) + bitsToByteBE(bits + n, bytes[nBytes]); + + dump(level, title, bytes, nBytes); +} + +void CUtils::byteToBitsBE(unsigned char byte, bool* bits) +{ + assert(bits != NULL); + + bits[0U] = (byte & 0x80U) == 0x80U; + bits[1U] = (byte & 0x40U) == 0x40U; + bits[2U] = (byte & 0x20U) == 0x20U; + bits[3U] = (byte & 0x10U) == 0x10U; + bits[4U] = (byte & 0x08U) == 0x08U; + bits[5U] = (byte & 0x04U) == 0x04U; + bits[6U] = (byte & 0x02U) == 0x02U; + bits[7U] = (byte & 0x01U) == 0x01U; +} + +void CUtils::byteToBitsLE(unsigned char byte, bool* bits) +{ + assert(bits != NULL); + + bits[0U] = (byte & 0x01U) == 0x01U; + bits[1U] = (byte & 0x02U) == 0x02U; + bits[2U] = (byte & 0x04U) == 0x04U; + bits[3U] = (byte & 0x08U) == 0x08U; + bits[4U] = (byte & 0x10U) == 0x10U; + bits[5U] = (byte & 0x20U) == 0x20U; + bits[6U] = (byte & 0x40U) == 0x40U; + bits[7U] = (byte & 0x80U) == 0x80U; +} + +void CUtils::bitsToByteBE(const bool* bits, unsigned char& byte) +{ + assert(bits != NULL); + + byte = bits[0U] ? 0x80U : 0x00U; + byte |= bits[1U] ? 0x40U : 0x00U; + byte |= bits[2U] ? 0x20U : 0x00U; + byte |= bits[3U] ? 0x10U : 0x00U; + byte |= bits[4U] ? 0x08U : 0x00U; + byte |= bits[5U] ? 0x04U : 0x00U; + byte |= bits[6U] ? 0x02U : 0x00U; + byte |= bits[7U] ? 0x01U : 0x00U; +} + +void CUtils::bitsToByteLE(const bool* bits, unsigned char& byte) +{ + assert(bits != NULL); + + byte = bits[0U] ? 0x01U : 0x00U; + byte |= bits[1U] ? 0x02U : 0x00U; + byte |= bits[2U] ? 0x04U : 0x00U; + byte |= bits[3U] ? 0x08U : 0x00U; + byte |= bits[4U] ? 0x10U : 0x00U; + byte |= bits[5U] ? 0x20U : 0x00U; + byte |= bits[6U] ? 0x40U : 0x00U; + byte |= bits[7U] ? 0x80U : 0x00U; +} diff --git a/YSFParrot/Utils.h b/YSFParrot/Utils.h new file mode 100644 index 0000000..ade28c0 --- /dev/null +++ b/YSFParrot/Utils.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009,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. + */ + +#ifndef Utils_H +#define Utils_H + +#include + +class CUtils { +public: + static void dump(const std::string& title, const unsigned char* data, unsigned int length); + static void dump(int level, const std::string& title, const unsigned char* data, unsigned int length); + + static void dump(const std::string& title, const bool* bits, unsigned int length); + static void dump(int level, const std::string& title, const bool* bits, unsigned int length); + + static void byteToBitsBE(unsigned char byte, bool* bits); + static void byteToBitsLE(unsigned char byte, bool* bits); + + static void bitsToByteBE(const bool* bits, unsigned char& byte); + static void bitsToByteLE(const bool* bits, unsigned char& byte); + +private: +}; + +#endif diff --git a/YSFParrot/YSFDefines.h b/YSFParrot/YSFDefines.h new file mode 100644 index 0000000..348d1bc --- /dev/null +++ b/YSFParrot/YSFDefines.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015,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. + */ + +#if !defined(YSFDefines_H) +#define YSFDefines_H + +const unsigned int YSF_FRAME_LENGTH_BYTES = 120U; + +const unsigned char YSF_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU}; +const unsigned int YSF_SYNC_LENGTH_BYTES = 5U; + +const unsigned int YSF_FICH_LENGTH_BYTES = 25U; + +const unsigned char YSF_SYNC_OK = 0x01U; + +const unsigned int YSF_CALLSIGN_LENGTH = 10U; + +const unsigned char YSF_FI_HEADER = 0x00U; +const unsigned char YSF_FI_COMMUNICATIONS = 0x01U; +const unsigned char YSF_FI_TERMINATOR = 0x02U; +const unsigned char YSF_FI_TEST = 0x03U; + +const unsigned char YSF_DT_VD_MODE1 = 0x00U; +const unsigned char YSF_DT_DATA_FR_MODE = 0x01U; +const unsigned char YSF_DT_VD_MODE2 = 0x02U; +const unsigned char YSF_DT_VOICE_FR_MODE = 0x03U; + +const unsigned char YSF_CM_GROUP = 0x00U; +const unsigned char YSF_CM_INDIVIDUAL = 0x03U; + +const unsigned char YSF_MR_NOT_BUSY = 0x01U; +const unsigned char YSF_MR_BUSY = 0x02U; + +#endif diff --git a/YSFParrot/YSFParrot.cpp b/YSFParrot/YSFParrot.cpp new file mode 100644 index 0000000..982456d --- /dev/null +++ b/YSFParrot/YSFParrot.cpp @@ -0,0 +1,136 @@ +/* +* Copyright (C) 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 "StopWatch.h" +#include "YSFParrot.h" +#include "Parrot.h" +#include "Network.h" + +#include +#include + +int main(int argc, char** argv) +{ + if (argc == 1) { + ::fprintf(stderr, "Usage: YSFParrot \n"); + return 1; + } + + unsigned int port = ::atoi(argv[1]); + if (port == 0U) { + ::fprintf(stderr, "YSFParrot: invalid port number\n"); + return 1; + } + + CYSFParrot parrot(port); + parrot.run(); + + return 0; +} + +CYSFParrot::CYSFParrot(unsigned int port) : +m_port(port) +{ +} + +CYSFParrot::~CYSFParrot() +{ +} + +void CYSFParrot::run() +{ + CParrot parrot(180U); + CNetwork network(m_port, false); + + bool ret = network.open(); + if (!ret) + return; + + CStopWatch stopWatch; + stopWatch.start(); + + CTimer watchdogTimer(1000U, 0U, 1500U); + CTimer turnaroundTimer(1000U, 2U); + + CStopWatch playoutTimer; + unsigned int count = 0U; + bool playing = false; + + for (;;) { + unsigned char buffer[200U]; + + unsigned int len = network.read(buffer); + if (len > 0U) { + parrot.write(buffer); + watchdogTimer.start(); + + if (buffer[34U] == 0x01U) { + ::fprintf(stdout, "Received end of transmission\n"); + turnaroundTimer.start(); + watchdogTimer.stop(); + parrot.end(); + } + } + + if (turnaroundTimer.isRunning() && turnaroundTimer.hasExpired()) { + if (!playing) { + playoutTimer.start(); + playing = true; + count = 0U; + } + + // A frame every 100ms + unsigned int wanted = playoutTimer.elapsed() / 100U; + while (count < wanted) { + len = parrot.read(buffer); + if (len > 0U) { + network.write(buffer); + count++; + } else { + turnaroundTimer.stop(); + playing = false; + count = wanted; + } + } + } + + unsigned int ms = stopWatch.elapsed(); + stopWatch.start(); + + network.clock(ms); + watchdogTimer.clock(ms); + turnaroundTimer.clock(ms); + + if (watchdogTimer.isRunning() && watchdogTimer.hasExpired()) { + ::fprintf(stdout, "Network watchdog has expired\n"); + turnaroundTimer.start(); + watchdogTimer.stop(); + parrot.end(); + } + + if (ms < 5U) { +#if defined(_WIN32) || defined(_WIN64) + ::Sleep(5UL); // 5ms +#else + ::usleep(5000); // 5ms +#endif + } + } + + network.close(); +} diff --git a/YSFParrot/YSFParrot.h b/YSFParrot/YSFParrot.h new file mode 100644 index 0000000..5248c85 --- /dev/null +++ b/YSFParrot/YSFParrot.h @@ -0,0 +1,34 @@ +/* +* Copyright (C) 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. +*/ + +#if !defined(YSFParrot_H) +#define YSFParrot_H + +class CYSFParrot +{ +public: + CYSFParrot(unsigned int port); + ~CYSFParrot(); + + void run(); + +private: + unsigned int m_port; +}; + +#endif diff --git a/YSFParrot/YSFParrot.vcxproj b/YSFParrot/YSFParrot.vcxproj new file mode 100644 index 0000000..a5de555 --- /dev/null +++ b/YSFParrot/YSFParrot.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D3BBE5EC-91F7-457B-B782-B616B918708F} + Win32Proj + YSFParrot + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/YSFParrot/YSFParrot.vcxproj.filters b/YSFParrot/YSFParrot.vcxproj.filters new file mode 100644 index 0000000..6ec32f5 --- /dev/null +++ b/YSFParrot/YSFParrot.vcxproj.filters @@ -0,0 +1,65 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/YSFReflector/Makefile b/YSFReflector/Makefile new file mode 100644 index 0000000..7d2fd21 --- /dev/null +++ b/YSFReflector/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CXX = g++ +CFLAGS = -g -O3 -Wall -std=c++0x +LIBS = +LDFLAGS = -g + +OBJECTS = Network.o StopWatch.o Timer.o UDPSocket.o Utils.o YSFReflector.o + +all: YSFReflector + +YSFParrot: $(OBJECTS) + $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o YSFReflector + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + +clean: + $(RM) YSFReflector *.o *.d *.bak *~ + \ No newline at end of file diff --git a/YSFReflector/Network.cpp b/YSFReflector/Network.cpp new file mode 100644 index 0000000..d20bb38 --- /dev/null +++ b/YSFReflector/Network.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2009-2014,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "YSFDefines.h" +#include "Network.h" +#include "Utils.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CNetwork::CNetwork(unsigned int port, bool debug) : +m_socket(port), +m_address(), +m_port(0U), +m_callsign(), +m_debug(debug), +m_buffer(1000U, "YSF Network") +{ +} + +CNetwork::~CNetwork() +{ +} + +bool CNetwork::open() +{ + ::fprintf(stdout, "Opening YSF network connection\n"); + + return m_socket.open(); +} + +bool CNetwork::writeData(const unsigned char* data, const in_addr& address, unsigned int port) +{ + assert(data != NULL); + + if (m_debug) + CUtils::dump(1U, "YSF Network Data Sent", data, 155U); + + return m_socket.write(data, 155U, address, port); +} + +bool CNetwork::writePoll(const in_addr& address, unsigned int port) +{ + unsigned char buffer[20U]; + + buffer[0] = 'Y'; + buffer[1] = 'S'; + buffer[2] = 'F'; + buffer[3] = 'P'; + + buffer[4U] = 'R'; + buffer[5U] = 'E'; + buffer[6U] = 'F'; + buffer[7U] = 'L'; + buffer[8U] = 'E'; + buffer[9U] = 'C'; + buffer[10U] = 'T'; + buffer[11U] = 'O'; + buffer[12U] = 'R'; + buffer[13U] = ' '; + + if (m_debug) + CUtils::dump(1U, "YSF Network Poll Sent", buffer, 14U); + + return m_socket.write(buffer, 14U, address, port); +} + +void CNetwork::clock(unsigned int ms) +{ + unsigned char buffer[BUFFER_LENGTH]; + + in_addr address; + unsigned int port; + int length = m_socket.read(buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return; + + // Handle incoming polls + if (::memcmp(buffer, "YSFP", 4U) == 0) { + m_callsign = std::string((char*)(buffer + 4U), YSF_CALLSIGN_LENGTH); + m_address = address; + m_port = port; + return; + } + + // Invalid packet type? + if (::memcmp(buffer, "YSFD", 4U) != 0) + return; + + // Simulate a poll with the data + m_callsign = std::string((char*)(buffer + 4U), YSF_CALLSIGN_LENGTH); + m_address = address; + m_port = port; + + if (m_debug) + CUtils::dump(1U, "YSF Network Data Received", buffer, length); + + m_buffer.addData(buffer, 155U); +} + +unsigned int CNetwork::readData(unsigned char* data) +{ + assert(data != NULL); + + if (m_buffer.isEmpty()) + return 0U; + + m_buffer.getData(data, 155U); + + return 155U; +} + +bool CNetwork::readPoll(std::string& callsign, in_addr& address, unsigned int& port) +{ + if (m_port == 0U) + return false; + + callsign = m_callsign; + address = m_address; + port = m_port; + + m_port = 0U; + + return true; +} + +void CNetwork::close() +{ + m_socket.close(); + + ::fprintf(stdout, "Closing YSF network connection\n"); +} diff --git a/YSFReflector/Network.h b/YSFReflector/Network.h new file mode 100644 index 0000000..89e8b2a --- /dev/null +++ b/YSFReflector/Network.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009-2014,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Network_H +#define Network_H + +#include "YSFDefines.h" +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CNetwork { +public: + CNetwork(unsigned int port, bool debug); + ~CNetwork(); + + bool open(); + + bool writeData(const unsigned char* data, const in_addr& address, unsigned int port); + bool writePoll(const in_addr& address, unsigned int port); + + unsigned int readData(unsigned char* data); + bool readPoll(std::string& callsign, in_addr& address, unsigned int& port); + + void close(); + + void clock(unsigned int ms); + +private: + CUDPSocket m_socket; + in_addr m_address; + unsigned int m_port; + std::string m_callsign; + bool m_debug; + CRingBuffer m_buffer; +}; + +#endif diff --git a/YSFReflector/RingBuffer.h b/YSFReflector/RingBuffer.h new file mode 100644 index 0000000..186709d --- /dev/null +++ b/YSFReflector/RingBuffer.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2006-2009,2012,2013,2015,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. + */ + +#ifndef RingBuffer_H +#define RingBuffer_H + +#include +#include +#include + +template class CRingBuffer { +public: + CRingBuffer(unsigned int length, const char* name) : + m_length(length), + m_name(name), + m_buffer(NULL), + m_iPtr(0U), + m_oPtr(0U) + { + assert(length > 0U); + assert(name != NULL); + + m_buffer = new T[length]; + + ::memset(m_buffer, 0x00, m_length * sizeof(T)); + } + + ~CRingBuffer() + { + delete[] m_buffer; + } + + bool addData(const T* buffer, unsigned int nSamples) + { + if (nSamples >= freeSpace()) { + ::fprintf(stderr, "**** Overflow in %s ring buffer, %u >= %u\n", m_name, nSamples, freeSpace()); + return false; + } + + for (unsigned int i = 0U; i < nSamples; i++) { + m_buffer[m_iPtr++] = buffer[i]; + + if (m_iPtr == m_length) + m_iPtr = 0U; + } + + return true; + } + + bool getData(T* buffer, unsigned int nSamples) + { + if (dataSize() < nSamples) { + ::fprintf(stderr, "**** Underflow in %s ring buffer, %u < %u\n", m_name, dataSize(), nSamples); + return false; + } + + for (unsigned int i = 0U; i < nSamples; i++) { + buffer[i] = m_buffer[m_oPtr++]; + + if (m_oPtr == m_length) + m_oPtr = 0U; + } + + return true; + } + + bool peek(T* buffer, unsigned int nSamples) + { + if (dataSize() < nSamples) { + ::fprintf(stderr, "**** Underflow peek in %s ring buffer, %u < %u\n", m_name, dataSize(), nSamples); + return false; + } + + 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 true; + } + + 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; + + if (m_oPtr > m_iPtr) + return m_oPtr - m_iPtr; + + return (m_length + m_oPtr) - m_iPtr; + } + + unsigned int dataSize() const + { + return m_length - freeSpace(); + } + + 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; + const char* m_name; + T* m_buffer; + unsigned int m_iPtr; + unsigned int m_oPtr; +}; + +#endif diff --git a/YSFReflector/StopWatch.cpp b/YSFReflector/StopWatch.cpp new file mode 100644 index 0000000..77d539d --- /dev/null +++ b/YSFReflector/StopWatch.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015,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 "StopWatch.h" + +#if defined(_WIN32) || defined(_WIN64) + +CStopWatch::CStopWatch() : +m_frequency(), +m_start() +{ + ::QueryPerformanceFrequency(&m_frequency); +} + +CStopWatch::~CStopWatch() +{ +} + +unsigned long CStopWatch::start() +{ + ::QueryPerformanceCounter(&m_start); + + return (unsigned long)(m_start.QuadPart / m_frequency.QuadPart); +} + +unsigned int CStopWatch::elapsed() +{ + LARGE_INTEGER now; + ::QueryPerformanceCounter(&now); + + LARGE_INTEGER temp; + temp.QuadPart = (now.QuadPart - m_start.QuadPart) * 1000; + + return (unsigned int)(temp.QuadPart / m_frequency.QuadPart); +} + +#else + +#include + +CStopWatch::CStopWatch() : +m_start() +{ +} + +CStopWatch::~CStopWatch() +{ +} + +unsigned long CStopWatch::start() +{ + ::gettimeofday(&m_start, NULL); + + return m_start.tv_usec; +} + +unsigned int CStopWatch::elapsed() +{ + struct timeval now; + ::gettimeofday(&now, NULL); + + unsigned int elapsed = (now.tv_sec - m_start.tv_sec) * 1000U; + elapsed += now.tv_usec / 1000U; + elapsed -= m_start.tv_usec / 1000U; + + return elapsed; +} + +#endif diff --git a/YSFReflector/StopWatch.h b/YSFReflector/StopWatch.h new file mode 100644 index 0000000..811047e --- /dev/null +++ b/YSFReflector/StopWatch.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015,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. + */ + +#if !defined(STOPWATCH_H) +#define STOPWATCH_H + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +class CStopWatch +{ +public: + CStopWatch(); + ~CStopWatch(); + + unsigned long start(); + unsigned int elapsed(); + +private: +#if defined(_WIN32) || defined(_WIN64) + LARGE_INTEGER m_frequency; + LARGE_INTEGER m_start; +#else + struct timeval m_start; +#endif +}; + +#endif diff --git a/YSFReflector/Timer.cpp b/YSFReflector/Timer.cpp new file mode 100644 index 0000000..53956e4 --- /dev/null +++ b/YSFReflector/Timer.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009,2010,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 "Timer.h" + +#include +#include + +CTimer::CTimer(unsigned int ticksPerSec, unsigned int secs, unsigned int msecs) : +m_ticksPerSec(ticksPerSec), +m_timeout(0U), +m_timer(0U) +{ + assert(ticksPerSec > 0U); + + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / 1000ULL + 1ULL); + } +} + +CTimer::~CTimer() +{ +} + +void CTimer::setTimeout(unsigned int secs, unsigned int msecs) +{ + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / 1000ULL + 1ULL); + } else { + m_timeout = 0U; + m_timer = 0U; + } +} + +unsigned int CTimer::getTimeout() const +{ + if (m_timeout == 0U) + return 0U; + + return (m_timeout - 1U) / m_ticksPerSec; +} + +unsigned int CTimer::getTimer() const +{ + if (m_timer == 0U) + return 0U; + + return (m_timer - 1U) / m_ticksPerSec; +} diff --git a/YSFReflector/Timer.h b/YSFReflector/Timer.h new file mode 100644 index 0000000..87d68f5 --- /dev/null +++ b/YSFReflector/Timer.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009,2010,2011,2014 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Timer_H +#define Timer_H + +class CTimer { +public: + CTimer(unsigned int ticksPerSec, unsigned int secs = 0U, unsigned int msecs = 0U); + ~CTimer(); + + void setTimeout(unsigned int secs, unsigned int msecs = 0U); + + unsigned int getTimeout() const; + unsigned int getTimer() const; + + unsigned int getRemaining() + { + if (m_timeout == 0U || m_timer == 0U) + return 0U; + + if (m_timer >= m_timeout) + return 0U; + + return (m_timeout - m_timer) / m_ticksPerSec; + } + + bool isRunning() + { + return m_timer > 0U; + } + + void start(unsigned int secs, unsigned int msecs = 0U) + { + setTimeout(secs, msecs); + + start(); + } + + void start() + { + if (m_timeout > 0U) + m_timer = 1U; + } + + void stop() + { + m_timer = 0U; + } + + bool hasExpired() + { + if (m_timeout == 0U || m_timer == 0U) + return false; + + if (m_timer >= m_timeout) + return true; + + return false; + } + + void clock(unsigned int ticks = 1U) + { + if (m_timer > 0U && m_timeout > 0U) + m_timer += ticks; + } + +private: + unsigned int m_ticksPerSec; + unsigned int m_timeout; + unsigned int m_timer; +}; + +#endif diff --git a/YSFReflector/UDPSocket.cpp b/YSFReflector/UDPSocket.cpp new file mode 100644 index 0000000..733edd0 --- /dev/null +++ b/YSFReflector/UDPSocket.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2006-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 "UDPSocket.h" + +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#endif + + +CUDPSocket::CUDPSocket(const std::string& address, unsigned int port) : +m_address(address), +m_port(port), +m_fd(-1) +{ + assert(!address.empty()); + +#if defined(_WIN32) || defined(_WIN64) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + ::fprintf(stderr, "Error from WSAStartup\n"); +#endif +} + +CUDPSocket::CUDPSocket(unsigned int port) : +m_address(), +m_port(port), +m_fd(-1) +{ +#if defined(_WIN32) || defined(_WIN64) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + ::fprintf(stderr, "Error from WSAStartup\n"); +#endif +} + +CUDPSocket::~CUDPSocket() +{ +#if defined(_WIN32) || defined(_WIN64) + ::WSACleanup(); +#endif +} + +in_addr CUDPSocket::lookup(const std::string& hostname) +{ + in_addr addr; +#if defined(_WIN32) || defined(_WIN64) + unsigned long address = ::inet_addr(hostname.c_str()); + if (address != INADDR_NONE && address != INADDR_ANY) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.c_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#else + in_addr_t address = ::inet_addr(hostname.c_str()); + if (address != in_addr_t(-1)) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.c_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#endif +} + +bool CUDPSocket::open() +{ + m_fd = ::socket(PF_INET, SOCK_DGRAM, 0); + if (m_fd < 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Cannot create the UDP socket, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Cannot create the UDP socket, err: %d\n", errno); +#endif + return false; + } + + if (m_port > 0U) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (!m_address.empty()) { +#if defined(_WIN32) || defined(_WIN64) + addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); +#else + addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); +#endif + if (addr.sin_addr.s_addr == INADDR_NONE) { + ::fprintf(stderr, "The local address is invalid - %s\n", m_address.c_str()); + return false; + } + } + + int reuse = 1; + if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Cannot set the UDP socket option, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Cannot set the UDP socket option, err: %d\n", errno); +#endif + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Cannot bind the UDP address, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Cannot bind the UDP address, err: %d\n", errno); +#endif + return false; + } + } + + return true; +} + +int CUDPSocket::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port) +{ + assert(buffer != NULL); + assert(length > 0U); + + // Check that the readfrom() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(_WIN32) || defined(_WIN64) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return immediately + timeval tv; + tv.tv_sec = 0L; + tv.tv_usec = 0L; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Error returned from UDP select, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Error returned from UDP select, err: %d\n", errno); +#endif + return -1; + } + + if (ret == 0) + return 0; + + sockaddr_in addr; +#if defined(_WIN32) || defined(_WIN64) + int size = sizeof(sockaddr_in); +#else + socklen_t size = sizeof(sockaddr_in); +#endif + +#if defined(_WIN32) || defined(_WIN64) + int len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); +#else + ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); +#endif + if (len <= 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Error returned from recvfrom, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Error returned from recvfrom, err: %d\n", errno); +#endif + return -1; + } + + address = addr.sin_addr; + port = ntohs(addr.sin_port); + + return len; +} + +bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port) +{ + assert(buffer != NULL); + assert(length > 0U); + + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + + addr.sin_family = AF_INET; + addr.sin_addr = address; + addr.sin_port = htons(port); + +#if defined(_WIN32) || defined(_WIN64) + int ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); +#else + ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); +#endif + if (ret < 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Error returned from sendto, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Error returned from sendto, err: %d\n", errno); +#endif + return false; + } + +#if defined(_WIN32) || defined(_WIN64) + if (ret != int(length)) + return false; +#else + if (ret != ssize_t(length)) + return false; +#endif + + return true; +} + +void CUDPSocket::close() +{ +#if defined(_WIN32) || defined(_WIN64) + ::closesocket(m_fd); +#else + ::close(m_fd); +#endif +} diff --git a/YSFReflector/UDPSocket.h b/YSFReflector/UDPSocket.h new file mode 100644 index 0000000..e0af272 --- /dev/null +++ b/YSFReflector/UDPSocket.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009-2011,2013,2015,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. + */ + +#ifndef UDPSocket_H +#define UDPSocket_H + +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +class CUDPSocket { +public: + CUDPSocket(const std::string& address, unsigned int port = 0U); + CUDPSocket(unsigned int port = 0U); + ~CUDPSocket(); + + bool open(); + + int read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port); + bool write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port); + + void close(); + + static in_addr lookup(const std::string& hostName); + +private: + std::string m_address; + unsigned short m_port; + int m_fd; +}; + +#endif diff --git a/YSFReflector/Utils.cpp b/YSFReflector/Utils.cpp new file mode 100644 index 0000000..d5b2c75 --- /dev/null +++ b/YSFReflector/Utils.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2009,2014,2015,2016 Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "Utils.h" + +#include +#include + +void CUtils::dump(const std::string& title, const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + dump(2U, title, data, length); +} + +void CUtils::dump(int level, const std::string& title, const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + ::fprintf(stdout, "%s\n", title.c_str()); + + unsigned int offset = 0U; + + while (length > 0U) { + std::string output; + + unsigned int bytes = (length > 16U) ? 16U : length; + + for (unsigned i = 0U; i < bytes; i++) { + char temp[10U]; + ::sprintf(temp, "%02X ", data[offset + i]); + output += temp; + } + + for (unsigned int i = bytes; i < 16U; i++) + output += " "; + + output += " *"; + + for (unsigned i = 0U; i < bytes; i++) { + unsigned char c = data[offset + i]; + + if (::isprint(c)) + output += c; + else + output += '.'; + } + + output += '*'; + + ::fprintf(stdout, "%04X: %s\n", offset, output.c_str()); + + offset += 16U; + + if (length >= 16U) + length -= 16U; + else + length = 0U; + } +} + +void CUtils::dump(const std::string& title, const bool* bits, unsigned int length) +{ + assert(bits != NULL); + + dump(2U, title, bits, length); +} + +void CUtils::dump(int level, const std::string& title, const bool* bits, unsigned int length) +{ + assert(bits != NULL); + + unsigned char bytes[100U]; + unsigned int nBytes = 0U; + for (unsigned int n = 0U; n < length; n += 8U, nBytes++) + bitsToByteBE(bits + n, bytes[nBytes]); + + dump(level, title, bytes, nBytes); +} + +void CUtils::byteToBitsBE(unsigned char byte, bool* bits) +{ + assert(bits != NULL); + + bits[0U] = (byte & 0x80U) == 0x80U; + bits[1U] = (byte & 0x40U) == 0x40U; + bits[2U] = (byte & 0x20U) == 0x20U; + bits[3U] = (byte & 0x10U) == 0x10U; + bits[4U] = (byte & 0x08U) == 0x08U; + bits[5U] = (byte & 0x04U) == 0x04U; + bits[6U] = (byte & 0x02U) == 0x02U; + bits[7U] = (byte & 0x01U) == 0x01U; +} + +void CUtils::byteToBitsLE(unsigned char byte, bool* bits) +{ + assert(bits != NULL); + + bits[0U] = (byte & 0x01U) == 0x01U; + bits[1U] = (byte & 0x02U) == 0x02U; + bits[2U] = (byte & 0x04U) == 0x04U; + bits[3U] = (byte & 0x08U) == 0x08U; + bits[4U] = (byte & 0x10U) == 0x10U; + bits[5U] = (byte & 0x20U) == 0x20U; + bits[6U] = (byte & 0x40U) == 0x40U; + bits[7U] = (byte & 0x80U) == 0x80U; +} + +void CUtils::bitsToByteBE(const bool* bits, unsigned char& byte) +{ + assert(bits != NULL); + + byte = bits[0U] ? 0x80U : 0x00U; + byte |= bits[1U] ? 0x40U : 0x00U; + byte |= bits[2U] ? 0x20U : 0x00U; + byte |= bits[3U] ? 0x10U : 0x00U; + byte |= bits[4U] ? 0x08U : 0x00U; + byte |= bits[5U] ? 0x04U : 0x00U; + byte |= bits[6U] ? 0x02U : 0x00U; + byte |= bits[7U] ? 0x01U : 0x00U; +} + +void CUtils::bitsToByteLE(const bool* bits, unsigned char& byte) +{ + assert(bits != NULL); + + byte = bits[0U] ? 0x01U : 0x00U; + byte |= bits[1U] ? 0x02U : 0x00U; + byte |= bits[2U] ? 0x04U : 0x00U; + byte |= bits[3U] ? 0x08U : 0x00U; + byte |= bits[4U] ? 0x10U : 0x00U; + byte |= bits[5U] ? 0x20U : 0x00U; + byte |= bits[6U] ? 0x40U : 0x00U; + byte |= bits[7U] ? 0x80U : 0x00U; +} diff --git a/YSFReflector/Utils.h b/YSFReflector/Utils.h new file mode 100644 index 0000000..ade28c0 --- /dev/null +++ b/YSFReflector/Utils.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009,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. + */ + +#ifndef Utils_H +#define Utils_H + +#include + +class CUtils { +public: + static void dump(const std::string& title, const unsigned char* data, unsigned int length); + static void dump(int level, const std::string& title, const unsigned char* data, unsigned int length); + + static void dump(const std::string& title, const bool* bits, unsigned int length); + static void dump(int level, const std::string& title, const bool* bits, unsigned int length); + + static void byteToBitsBE(unsigned char byte, bool* bits); + static void byteToBitsLE(unsigned char byte, bool* bits); + + static void bitsToByteBE(const bool* bits, unsigned char& byte); + static void bitsToByteLE(const bool* bits, unsigned char& byte); + +private: +}; + +#endif diff --git a/YSFReflector/YSFDefines.h b/YSFReflector/YSFDefines.h new file mode 100644 index 0000000..348d1bc --- /dev/null +++ b/YSFReflector/YSFDefines.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015,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. + */ + +#if !defined(YSFDefines_H) +#define YSFDefines_H + +const unsigned int YSF_FRAME_LENGTH_BYTES = 120U; + +const unsigned char YSF_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU}; +const unsigned int YSF_SYNC_LENGTH_BYTES = 5U; + +const unsigned int YSF_FICH_LENGTH_BYTES = 25U; + +const unsigned char YSF_SYNC_OK = 0x01U; + +const unsigned int YSF_CALLSIGN_LENGTH = 10U; + +const unsigned char YSF_FI_HEADER = 0x00U; +const unsigned char YSF_FI_COMMUNICATIONS = 0x01U; +const unsigned char YSF_FI_TERMINATOR = 0x02U; +const unsigned char YSF_FI_TEST = 0x03U; + +const unsigned char YSF_DT_VD_MODE1 = 0x00U; +const unsigned char YSF_DT_DATA_FR_MODE = 0x01U; +const unsigned char YSF_DT_VD_MODE2 = 0x02U; +const unsigned char YSF_DT_VOICE_FR_MODE = 0x03U; + +const unsigned char YSF_CM_GROUP = 0x00U; +const unsigned char YSF_CM_INDIVIDUAL = 0x03U; + +const unsigned char YSF_MR_NOT_BUSY = 0x01U; +const unsigned char YSF_MR_BUSY = 0x02U; + +#endif diff --git a/YSFReflector/YSFReflector.cpp b/YSFReflector/YSFReflector.cpp new file mode 100644 index 0000000..ca985f4 --- /dev/null +++ b/YSFReflector/YSFReflector.cpp @@ -0,0 +1,150 @@ +/* +* Copyright (C) 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 "YSFReflector.h" +#include "StopWatch.h" +#include "Network.h" + +#include +#include + +int main(int argc, char** argv) +{ + if (argc == 1) { + ::fprintf(stderr, "Usage: YSFReflector \n"); + return 1; + } + + unsigned int port = ::atoi(argv[1]); + if (port == 0U) { + ::fprintf(stderr, "YSFReflector: invalid port number\n"); + return 1; + } + + CYSFReflector Reflector(port); + Reflector.run(); + + return 0; +} + +CYSFReflector::CYSFReflector(unsigned int port) : +m_port(port), +m_repeaters() +{ +} + +CYSFReflector::~CYSFReflector() +{ +} + +void CYSFReflector::run() +{ + CNetwork network(m_port, false); + + bool ret = network.open(); + if (!ret) + return; + + CStopWatch stopWatch; + stopWatch.start(); + + CTimer pollTimer(1000U, 60U); + pollTimer.start(); + + for (;;) { + unsigned char buffer[200U]; + + unsigned int len = network.readData(buffer); + if (len > 0U) { + std::string callsign = std::string((char*)(buffer + 4U), YSF_CALLSIGN_LENGTH); + CYSFRepeater* rpt = findRepeater(callsign); + if (rpt != NULL) { + for (auto it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + if ((*it)->m_callsign != callsign) + network.writeData(buffer, (*it)->m_address, (*it)->m_port); + } + } else { + ::fprintf(stdout, "Data received from an unknown repeater - %s\n", callsign.c_str()); + } + } + + // Refresh/add repeaters based on their polls + std::string callsign; + in_addr address; + unsigned int port; + bool ret = network.readPoll(callsign, address, port); + if (ret) { + CYSFRepeater* rpt = findRepeater(callsign); + if (rpt == NULL) { + ::fprintf(stdout, "Adding %s\n", callsign.c_str()); + rpt = new CYSFRepeater; + rpt->m_timer.start(); + rpt->m_callsign = callsign; + rpt->m_address = address; + rpt->m_port = port; + m_repeaters.push_back(rpt); + } else { + rpt->m_timer.start(); + rpt->m_address = address; + rpt->m_port = port; + } + } + + unsigned int ms = stopWatch.elapsed(); + stopWatch.start(); + + network.clock(ms); + + pollTimer.clock(ms); + if (pollTimer.hasExpired()) { + for (auto it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + network.writePoll((*it)->m_address, (*it)->m_port); + pollTimer.start(); + } + + // Remove any repeaters that haven't reported for a while + for (auto it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + (*it)->m_timer.clock(ms); + if ((*it)->m_timer.hasExpired()) { + ::fprintf(stdout, "Removing %s\n", (*it)->m_callsign.c_str()); + m_repeaters.erase(it); + break; + } + } + + if (ms < 5U) { +#if defined(_WIN32) || defined(_WIN64) + ::Sleep(5UL); // 5ms +#else + ::usleep(5000); // 5ms +#endif + } + } + + network.close(); +} + +CYSFRepeater* CYSFReflector::findRepeater(const std::string& callsign) const +{ + for (auto it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + if ((*it)->m_callsign == callsign) + return *it; + } + + return NULL; +} diff --git a/YSFReflector/YSFReflector.h b/YSFReflector/YSFReflector.h new file mode 100644 index 0000000..d18b689 --- /dev/null +++ b/YSFReflector/YSFReflector.h @@ -0,0 +1,69 @@ +/* +* Copyright (C) 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. +*/ + +#if !defined(YSFReflector_H) +#define YSFReflector_H + +#include "Timer.h" + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +#include + +class CYSFRepeater { +public: + CYSFRepeater() : + m_callsign(), + m_address(), + m_port(0U), + m_timer(1000U, 300U) + { + } + + std::string m_callsign; + in_addr m_address; + unsigned int m_port; + CTimer m_timer; +}; + +class CYSFReflector +{ +public: + CYSFReflector(unsigned int port); + ~CYSFReflector(); + + void run(); + +private: + unsigned int m_port; + std::vector m_repeaters; + + CYSFRepeater* findRepeater(const std::string& callsign) const; +}; + +#endif diff --git a/YSFReflector/YSFReflector.vcxproj b/YSFReflector/YSFReflector.vcxproj new file mode 100644 index 0000000..95820af --- /dev/null +++ b/YSFReflector/YSFReflector.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {317D87F1-3485-4739-9F94-A07738B8E19D} + Win32Proj + YSFReflector + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/YSFReflector/YSFReflector.vcxproj.filters b/YSFReflector/YSFReflector.vcxproj.filters new file mode 100644 index 0000000..2995ec7 --- /dev/null +++ b/YSFReflector/YSFReflector.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file