commit e3abbaa85ff2a9990847a2543df52ee483241dfd Author: Jonathan Naylor Date: Tue Mar 13 22:16:48 2018 +0000 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f66aa40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +Debug +Release +x64 +*.o +*.opendb +*.bak +*.obj +*~ +*.sdf +*.log +*.zip +*.exe +*.user +*.VC.db +.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/NXDNClients.sln b/NXDNClients.sln new file mode 100644 index 0000000..eaea288 --- /dev/null +++ b/NXDNClients.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2010 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNParrot", "NXDNParrot\NXDNParrot.vcxproj", "{2AE94EAA-FD57-45C9-8555-6425CFA777A3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNGateway", "NXDNGateway\NXDNGateway.vcxproj", "{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNReflector", "NXDNReflector\NXDNReflector.vcxproj", "{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}" +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 + {2AE94EAA-FD57-45C9-8555-6425CFA777A3}.Debug|x64.ActiveCfg = Debug|x64 + {2AE94EAA-FD57-45C9-8555-6425CFA777A3}.Debug|x64.Build.0 = Debug|x64 + {2AE94EAA-FD57-45C9-8555-6425CFA777A3}.Debug|x86.ActiveCfg = Debug|Win32 + {2AE94EAA-FD57-45C9-8555-6425CFA777A3}.Debug|x86.Build.0 = Debug|Win32 + {2AE94EAA-FD57-45C9-8555-6425CFA777A3}.Release|x64.ActiveCfg = Release|x64 + {2AE94EAA-FD57-45C9-8555-6425CFA777A3}.Release|x64.Build.0 = Release|x64 + {2AE94EAA-FD57-45C9-8555-6425CFA777A3}.Release|x86.ActiveCfg = Release|Win32 + {2AE94EAA-FD57-45C9-8555-6425CFA777A3}.Release|x86.Build.0 = Release|Win32 + {8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Debug|x64.ActiveCfg = Debug|x64 + {8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Debug|x64.Build.0 = Debug|x64 + {8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Debug|x86.ActiveCfg = Debug|Win32 + {8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Debug|x86.Build.0 = Debug|Win32 + {8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x64.ActiveCfg = Release|x64 + {8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x64.Build.0 = Release|x64 + {8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x86.ActiveCfg = Release|Win32 + {8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x86.Build.0 = Release|Win32 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.ActiveCfg = Debug|x64 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.Build.0 = Debug|x64 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.ActiveCfg = Debug|Win32 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.Build.0 = Debug|Win32 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.ActiveCfg = Release|x64 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.Build.0 = Release|x64 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.ActiveCfg = Release|Win32 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {02935C01-6975-4CA6-81D3-505FFB8A432C} + EndGlobalSection +EndGlobal diff --git a/NXDNGateway/Conf.cpp b/NXDNGateway/Conf.cpp new file mode 100644 index 0000000..4b007a1 --- /dev/null +++ b/NXDNGateway/Conf.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Conf.h" +#include "Log.h" + +#include +#include +#include +#include + +const int BUFFER_SIZE = 500; + +enum SECTION { + SECTION_NONE, + SECTION_GENERAL, + SECTION_ID_LOOKUP, + SECTION_VOICE, + SECTION_LOG, + SECTION_NETWORK +}; + +CConf::CConf(const std::string& file) : +m_file(file), +m_callsign(), +m_rptAddress(), +m_rptPort(0U), +m_myAddress(), +m_myPort(0U), +m_announcements(true), +m_daemon(false), +m_lookupName(), +m_lookupTime(0U), +m_voiceEnabled(true), +m_voiceLanguage("en_GB"), +m_voiceDirectory(), +m_logFilePath(), +m_logFileRoot(), +m_networkPort(0U), +m_networkHosts1(), +m_networkHosts2(), +m_networkReloadTime(0U), +m_networkParrotAddress("127.0.0.1"), +m_networkParrotPort(0U), +m_networkStartup(9999U), +m_networkInactivityTimeout(0U), +m_networkDebug(false) +{ +} + +CConf::~CConf() +{ +} + +bool CConf::read() +{ + FILE* fp = ::fopen(m_file.c_str(), "rt"); + if (fp == NULL) { + ::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str()); + return false; + } + + SECTION section = SECTION_NONE; + + char buffer[BUFFER_SIZE]; + while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) { + if (buffer[0U] == '#') + continue; + + if (buffer[0U] == '[') { + if (::strncmp(buffer, "[General]", 9U) == 0) + section = SECTION_GENERAL; + else if (::strncmp(buffer, "[Id Lookup]", 11U) == 0) + section = SECTION_ID_LOOKUP; + else if (::strncmp(buffer, "[Voice]", 7U) == 0) + section = SECTION_VOICE; + else if (::strncmp(buffer, "[Log]", 5U) == 0) + section = SECTION_LOG; + else if (::strncmp(buffer, "[Network]", 9U) == 0) + section = SECTION_NETWORK; + else + section = SECTION_NONE; + + continue; + } + + char* key = ::strtok(buffer, " \t=\r\n"); + if (key == NULL) + continue; + + char* value = ::strtok(NULL, "\r\n"); + if (section == SECTION_GENERAL) { + if (::strcmp(key, "Callsign") == 0) { + // Convert the callsign to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_callsign = value; + } else if (::strcmp(key, "RptAddress") == 0) + m_rptAddress = value; + else if (::strcmp(key, "RptPort") == 0) + m_rptPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalAddress") == 0) + m_myAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_myPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Announcements") == 0) + m_announcements = ::atoi(value) == 1; + else if (::strcmp(key, "Daemon") == 0) + m_daemon = ::atoi(value) == 1; + } else if (section == SECTION_ID_LOOKUP) { + if (::strcmp(key, "Name") == 0) + m_lookupName = value; + else if (::strcmp(key, "Time") == 0) + m_lookupTime = (unsigned int)::atoi(value); + } else if (section == SECTION_VOICE) { + if (::strcmp(key, "Enabled") == 0) + m_voiceEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Language") == 0) + m_voiceLanguage = value; + else if (::strcmp(key, "Directory") == 0) + m_voiceDirectory = value; + } else if (section == SECTION_LOG) { + if (::strcmp(key, "FilePath") == 0) + m_logFilePath = value; + else if (::strcmp(key, "FileRoot") == 0) + m_logFileRoot = value; + } else if (section == SECTION_NETWORK) { + if (::strcmp(key, "Port") == 0) + m_networkPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "HostsFile1") == 0) + m_networkHosts1 = value; + else if (::strcmp(key, "HostsFile2") == 0) + m_networkHosts2 = value; + else if (::strcmp(key, "ReloadTime") == 0) + m_networkReloadTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "ParrotAddress") == 0) + m_networkParrotAddress = value; + else if (::strcmp(key, "ParrotPort") == 0) + m_networkParrotPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Startup") == 0) + m_networkStartup = (unsigned int)::atoi(value); + else if (::strcmp(key, "InactivityTimeout") == 0) + m_networkInactivityTimeout = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_networkDebug = ::atoi(value) == 1; + } + } + + ::fclose(fp); + + return true; +} + +std::string CConf::getCallsign() const +{ + return m_callsign; +} + +std::string CConf::getRptAddress() const +{ + return m_rptAddress; +} + +unsigned int CConf::getRptPort() const +{ + return m_rptPort; +} + +std::string CConf::getMyAddress() const +{ + return m_myAddress; +} + +unsigned int CConf::getMyPort() const +{ + return m_myPort; +} + +bool CConf::getAnnouncements() const +{ + return m_announcements; +} + +bool CConf::getDaemon() const +{ + return m_daemon; +} + +std::string CConf::getLookupName() const +{ + return m_lookupName; +} + +unsigned int CConf::getLookupTime() const +{ + return m_lookupTime; +} + +bool CConf::getVoiceEnabled() const +{ + return m_voiceEnabled; +} + +std::string CConf::getVoiceLanguage() const +{ + return m_voiceLanguage; +} + +std::string CConf::getVoiceDirectory() const +{ + return m_voiceDirectory; +} + +std::string CConf::getLogFilePath() const +{ + return m_logFilePath; +} + +std::string CConf::getLogFileRoot() const +{ + return m_logFileRoot; +} + +unsigned int CConf::getNetworkPort() const +{ + return m_networkPort; +} + +std::string CConf::getNetworkHosts1() const +{ + return m_networkHosts1; +} + +std::string CConf::getNetworkHosts2() const +{ + return m_networkHosts2; +} + +unsigned int CConf::getNetworkReloadTime() const +{ + return m_networkReloadTime; +} + +std::string CConf::getNetworkParrotAddress() const +{ + return m_networkParrotAddress; +} + +unsigned int CConf::getNetworkParrotPort() const +{ + return m_networkParrotPort; +} + +unsigned int CConf::getNetworkStartup() const +{ + return m_networkStartup; +} + +unsigned int CConf::getNetworkInactivityTimeout() const +{ + return m_networkInactivityTimeout; +} + +bool CConf::getNetworkDebug() const +{ + return m_networkDebug; +} diff --git a/NXDNGateway/Conf.h b/NXDNGateway/Conf.h new file mode 100644 index 0000000..5866b0a --- /dev/null +++ b/NXDNGateway/Conf.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(CONF_H) +#define CONF_H + +#include +#include + +class CConf +{ +public: + CConf(const std::string& file); + ~CConf(); + + bool read(); + + // The General section + std::string getCallsign() const; + std::string getRptAddress() const; + unsigned int getRptPort() const; + std::string getMyAddress() const; + unsigned int getMyPort() const; + bool getAnnouncements() const; + bool getDaemon() const; + + // The Id Lookup section + std::string getLookupName() const; + unsigned int getLookupTime() const; + + // The Voice section + bool getVoiceEnabled() const; + std::string getVoiceLanguage() const; + std::string getVoiceDirectory() const; + + // The Log section + std::string getLogFilePath() const; + std::string getLogFileRoot() const; + + // The Network section + unsigned int getNetworkPort() const; + std::string getNetworkHosts1() const; + std::string getNetworkHosts2() const; + unsigned int getNetworkReloadTime() const; + std::string getNetworkParrotAddress() const; + unsigned int getNetworkParrotPort() const; + unsigned int getNetworkStartup() const; + unsigned int getNetworkInactivityTimeout() const; + bool getNetworkDebug() const; + +private: + std::string m_file; + std::string m_callsign; + std::string m_rptAddress; + unsigned int m_rptPort; + std::string m_myAddress; + unsigned int m_myPort; + bool m_announcements; + bool m_daemon; + + std::string m_lookupName; + unsigned int m_lookupTime; + + bool m_voiceEnabled; + std::string m_voiceLanguage; + std::string m_voiceDirectory; + + std::string m_logFilePath; + std::string m_logFileRoot; + + unsigned int m_networkPort; + std::string m_networkHosts1; + std::string m_networkHosts2; + unsigned int m_networkReloadTime; + std::string m_networkParrotAddress; + unsigned int m_networkParrotPort; + unsigned int m_networkStartup; + unsigned int m_networkInactivityTimeout; + bool m_networkDebug; +}; + +#endif diff --git a/NXDNGateway/IcomNetwork.cpp b/NXDNGateway/IcomNetwork.cpp new file mode 100644 index 0000000..6553812 --- /dev/null +++ b/NXDNGateway/IcomNetwork.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "IcomNetwork.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CIcomNetwork::CIcomNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) : +m_socket(localAddress, localPort), +m_address(), +m_port(gatewayPort), +m_debug(debug), +m_buffer(1000U, "Icom") +{ + assert(gatewayPort > 0U); + assert(!gatewayAddress.empty()); + + m_address = CUDPSocket::lookup(gatewayAddress); +} + +CIcomNetwork::~CIcomNetwork() +{ +} + +bool CIcomNetwork::open() +{ + LogMessage("Opening Icom connection"); + + if (m_address.s_addr == INADDR_NONE) + return false; + + return m_socket.open(); +} + +bool CIcomNetwork::write(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + unsigned char buffer[110U]; + ::memset(buffer, 0x00U, 110U); + + buffer[0U] = 'I'; + buffer[1U] = 'C'; + buffer[2U] = 'O'; + buffer[3U] = 'M'; + buffer[4U] = 0x01U; + buffer[5U] = 0x01U; + buffer[6U] = 0x08U; + buffer[7U] = 0xE0U; + + buffer[37U] = 0x23U; + buffer[38U] = (data[0U] == 0x81U || data[0U] == 0x83U) ? 0x1CU : 0x10U; + buffer[39U] = 0x21U; + + ::memcpy(buffer + 40U, data, length); + + if (m_debug) + CUtils::dump(1U, "Icom Data Sent", buffer, 102U); + + return m_socket.write(buffer, 102U, m_address, m_port); +} + +void CIcomNetwork::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; + + // Check if the data is for us + if (m_address.s_addr != address.s_addr || port != m_port) { + LogMessage("Icom packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port); + return; + } + + // Invalid packet type? + if (::memcmp(buffer, "ICOM", 4U) != 0) + return; + + // An Icom repeater connect request + if (buffer[4U] == 0x01U && buffer[5U] == 0x61U) { + buffer[5U] = 0x62U; + buffer[37U] = 0x02U; + buffer[38U] = 0x4FU; + buffer[39U] = 0x4BU; + m_socket.write(buffer, length, m_address, m_port); + return; + } + + if (length != 102) + return; + + if (m_debug) + CUtils::dump(1U, "Icom Data Received", buffer, length); + + m_buffer.addData(buffer + 40U, 33U); +} + +bool CIcomNetwork::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_buffer.isEmpty()) + return false; + + m_buffer.getData(data, 33U); + + return true; +} + +void CIcomNetwork::close() +{ + m_socket.close(); + + LogMessage("Closing Icom connection"); +} diff --git a/NXDNGateway/IcomNetwork.h b/NXDNGateway/IcomNetwork.h new file mode 100644 index 0000000..9001ccb --- /dev/null +++ b/NXDNGateway/IcomNetwork.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef IcomNetwork_H +#define IcomNetwork_H + +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CIcomNetwork { +public: + CIcomNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug); + ~CIcomNetwork(); + + bool open(); + + bool write(const unsigned char* data, unsigned int length); + + bool 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; +}; + +#endif diff --git a/NXDNGateway/Log.cpp b/NXDNGateway/Log.cpp new file mode 100644 index 0000000..fc37ebf --- /dev/null +++ b/NXDNGateway/Log.cpp @@ -0,0 +1,136 @@ +/* + * 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 "Log.h" + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + +static unsigned int m_fileLevel = 2U; +static std::string m_filePath; +static std::string m_fileRoot; + +static FILE* m_fpLog = NULL; + +static unsigned int m_displayLevel = 2U; + +static struct tm m_tm; + +static char LEVELS[] = " DMIWEF"; + +static bool LogOpen() +{ + if (m_fileLevel == 0U) + return true; + + time_t now; + ::time(&now); + + struct tm* tm = ::gmtime(&now); + + if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) { + if (m_fpLog != NULL) + return true; + } else { + if (m_fpLog != NULL) + ::fclose(m_fpLog); + } + + char filename[100U]; +#if defined(_WIN32) || defined(_WIN64) + ::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#else + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#endif + + m_fpLog = ::fopen(filename, "a+t"); + m_tm = *tm; + + return m_fpLog != NULL; +} + +bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel) +{ + m_filePath = filePath; + m_fileRoot = fileRoot; + m_fileLevel = fileLevel; + m_displayLevel = displayLevel; + return ::LogOpen(); +} + +void LogFinalise() +{ + if (m_fpLog != NULL) + ::fclose(m_fpLog); +} + +void Log(unsigned int level, const char* fmt, ...) +{ + assert(fmt != NULL); + + char buffer[300U]; +#if defined(_WIN32) || defined(_WIN64) + SYSTEMTIME st; + ::GetSystemTime(&st); + + ::sprintf(buffer, "%c: %04u-%02u-%02u %02u:%02u:%02u.%03u ", LEVELS[level], st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); +#else + struct timeval now; + ::gettimeofday(&now, NULL); + + struct tm* tm = ::gmtime(&now.tv_sec); + + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000U); +#endif + + va_list vl; + va_start(vl, fmt); + + ::vsprintf(buffer + ::strlen(buffer), fmt, vl); + + va_end(vl); + + if (level >= m_fileLevel && m_fileLevel != 0U) { + bool ret = ::LogOpen(); + if (!ret) + return; + + ::fprintf(m_fpLog, "%s\n", buffer); + ::fflush(m_fpLog); + } + + if (level >= m_displayLevel && m_displayLevel != 0U) { + ::fprintf(stdout, "%s\n", buffer); + ::fflush(stdout); + } + + if (level == 6U) { // Fatal + ::fclose(m_fpLog); + exit(1); + } +} diff --git a/NXDNGateway/Log.h b/NXDNGateway/Log.h new file mode 100644 index 0000000..d671ef9 --- /dev/null +++ b/NXDNGateway/Log.h @@ -0,0 +1,36 @@ +/* + * 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(LOG_H) +#define LOG_H + +#include + +#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__) +#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__) +#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__) +#define LogWarning(fmt, ...) Log(4U, fmt, ##__VA_ARGS__) +#define LogError(fmt, ...) Log(5U, fmt, ##__VA_ARGS__) +#define LogFatal(fmt, ...) Log(6U, fmt, ##__VA_ARGS__) + +extern void Log(unsigned int level, const char* fmt, ...); + +extern bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel); +extern void LogFinalise(); + +#endif diff --git a/NXDNGateway/Makefile b/NXDNGateway/Makefile new file mode 100644 index 0000000..2f5aeea --- /dev/null +++ b/NXDNGateway/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CXX = g++ +CFLAGS = -g -O3 -Wall -std=c++0x -pthread +LIBS = -lpthread +LDFLAGS = -g + +OBJECTS = Conf.o IcomNetwork.o Log.o Mutex.o NXDNCRC.o NXDNGateway.o NXDNLookup.o NXDNNetwork.o Reflectors.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o + +all: NXDNGateway + +NXDNGateway: $(OBJECTS) + $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o NXDNGateway + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + +clean: + $(RM) NXDNGateway *.o *.d *.bak *~ + \ No newline at end of file diff --git a/NXDNGateway/Mutex.cpp b/NXDNGateway/Mutex.cpp new file mode 100644 index 0000000..837e340 --- /dev/null +++ b/NXDNGateway/Mutex.cpp @@ -0,0 +1,65 @@ +/* + * 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 "Mutex.h" + +#if defined(_WIN32) || defined(_WIN64) + +CMutex::CMutex() : +m_handle() +{ + m_handle = ::CreateMutex(NULL, FALSE, NULL); +} + +CMutex::~CMutex() +{ + ::CloseHandle(m_handle); +} + +void CMutex::lock() +{ + ::WaitForSingleObject(m_handle, INFINITE); +} + +void CMutex::unlock() +{ + ::ReleaseMutex(m_handle); +} + +#else + +CMutex::CMutex() : +m_mutex(PTHREAD_MUTEX_INITIALIZER) +{ +} + +CMutex::~CMutex() +{ +} + +void CMutex::lock() +{ + ::pthread_mutex_lock(&m_mutex); +} + +void CMutex::unlock() +{ + ::pthread_mutex_unlock(&m_mutex); +} + +#endif diff --git a/NXDNGateway/Mutex.h b/NXDNGateway/Mutex.h new file mode 100644 index 0000000..7ce9f85 --- /dev/null +++ b/NXDNGateway/Mutex.h @@ -0,0 +1,45 @@ +/* + * 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(MUTEX_H) +#define MUTEX_H + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +class CMutex +{ +public: + CMutex(); + ~CMutex(); + + void lock(); + void unlock(); + +private: +#if defined(_WIN32) || defined(_WIN64) + HANDLE m_handle; +#else + pthread_mutex_t m_mutex; +#endif +}; + +#endif diff --git a/NXDNGateway/NXDN.csv b/NXDNGateway/NXDN.csv new file mode 100644 index 0000000..0024978 --- /dev/null +++ b/NXDNGateway/NXDN.csv @@ -0,0 +1,405 @@ +UID,Call,Name,Location,Notes +2,W2FLY/M,Harry,Southern NJ,UHF NX-800 mobile +3,WB2OOM/M,Tina,Southern NJ,UHF NX-800 Mobile +4,W2FLY/M,Harry,Southern NJ,UHF NX-800 Van +7,F1PRY,Emmanuel,BEAUVAIS FR, +8,W2FLY/P,Harry,Southern NJ,UHF NX-820 Port 2 +9,KE8BGA,Lowell Katz,"Cleveland, Ohio","on the north shore, NX-800" +17,KC2SNI,Jim Hannon,"West Berlin, New Jersey",My Radio is an Icom IC-3161DT +25,WB2RUH/P,Bruce,Southern NJ,UHF NX-320 +26,WB2RUH/M,Bruce,Southern NJ,UHF NX-800 mobile +30,KD4MOJ,Doug,,Doug +74,N2IXC,Ed,Chatsworth NJ, +76,W2FLY/P,Harry,Southern NJ,UFH NX5300 Port 1 +77,W2FLY/P,Harry,"Somers Point, NJ",UHF NX-800 Shore Base +78,W2FLY,Harry,"Mullica Hill, NJ",UHF NX-800 +79,W2FLY/M,Harry,Southern NJ,UHF NX-5800 +80,W2FLY/M,Harry,Southern NJ,VHF NX-5700 mobile +82,W2FLY/P,Harry,"Somers Point, NJ",VHF NX-700 Shore Base +85,W2FLY/M,Harry,Southern NJ,VHF NX_700 van +86,W2FLY,Harry,"Mullica Hill, NJ",VHF NX-700 base +87,W2FLY/M,Harry,Southern NJ,VHF NX-700 mobile +88,W2FLY/P,Harry,Southern NJ,VHF NX-220 portable +89,WB2RUH,Bruce,Vineland NJ,VHF NX-700 base +99,K1ZSG,Don,,Mobile +134,N2SRO,Mike,Pitman,Mobile Radio +134,N2SRO,Mike,NJ, +141,KF2M,Greg Popovich,"Mt Laurel, NJ",IC-F4161DT +401,W1KFR,Bill,,Bill +901,K1IFF,Cliff,CT,Cliff NX320 +902,K1IFF,Cliff,CT,Cliff NX320 - demo +903,K1IFF,Cliff,CT,Cliff NX800H +905,WA1LMV,Rick,,Rick NX320 +906,K1IFF,Cliff,CT,Cliff NX200 +907,K1IFF,Cliff,CT,Future +908,W4CLL,Chuck,,CHUCK +909,AB1UB V,Woody,"Bristol, CT", +909,AB1UB,Woody,"Bristol, CT", +909,AB1UB,Woody,,Woody NX300 +909,W4CCL,Chuck,,CHUCK +910,W4CLL,Chuck,,CHUCK +911,W4CLL,Chuck,,CHUCK +912,W4CCL,Chuck,,CHUCK +920,N1XDN,Bob,CT,Bob P1 UHF NX300 +921,N1XDN,Bob,CT,Bob P1 VHF NX220 +922,N1XDN,Bob,CT,Bob P2 UHF NX300 +923,N1XDN,Bob,CT,Bob M1 VHF NX700 +924,N1XDN,Bob,CT,Bob P3 UHF NX-5300 +925,N1XDN,Bob,CT,Bob P2 VHF NX-5200 +926,N1XDN,Bob,CT,Bob M1 UHF NX-5800 +928,N1XDN,Bob,CT,Icom portable +930,K1IFF,Cliff,CT,NX300 +931,K1IFF,Cliff,CT,NX300 +936,W1GPO,John,,NX300 +951,K1KGQ,Joel,,Joel NX800H +952,K1KGQ,Joel,CT,Truck radio +952,K1GML,Gail,,Gail NX800H +953,K1KGQ,Joel,,Joel NX320-1 +954,K1KGQ,Joel,, +955,K1KGQ,Joel,, +956,K1KGQ,Joel,, +957,K1KGQ,Joel,, +958,K1KGQ,Joel,,Joel F4161 +959,K1KGQ,Joel,,Future +960,K1ZXX,Gary,,Gary - Icom All radios +961,WA1SSB,Joe,,Joe NX800H +962,N1MAT,Dennis,,Dennis F4161 +963,N1MAT,Dennis,,Dennis F4161 +970,KB1VRI,Nick,CT, +970,KB1VRI,Nick,,Nick +971,N1ELE,Paul,CT, +972,N1ODJ,Kenny Schmitz,Middletown, +973,W1VLF,Paul,CT, +980,W1RHS,Rick,Connecticut, +995,K1IFF/Base,Cliff,"Brsitol, CT",NXR710 link radio +1001,KE4GWW,James,AL,NX200 Portable #1 +1002,KE4GWW,James,AL,NX200 Portable #2 +1003,KE4GWW,James,AL,NX300 Portable #1 +1004,KE4GWW,James,AL,NX300 Portable #2 +1005,KE4GWW,James,AL,NX700 Base +1006,KE4GWW,James,AL,NX800 Base +1007,KE4GWW,James M. Nelson,"Dothan, AL",NX700 Mobile#1 +1008,DK6PX,Stefan,Germany nr Bad Toelz,Coming up FR6000 +1008,KE4GWW,James,AL,NX700 Mobile#2 +1009,KE4GWW,James,AL,NX800 Mobile#1 +1010,KE4GWW,James,AL,NX800 Mobile#2 +1066,KM4JXP,Peter Tasker,Longboat Key,NX5300 +1954,W4BPT,BILLY TAYLOR,"RINGGOLD,GA.", +1956,KC7SDD,Dana Hanford,"Bainbridge Island, WA","Member BARC, W7NPC.org" +1963,N5JRN,David Barts,"Bainbridge Island, WA","Handheld, Icom 4161" +1966,ZS6IIX,Henry,Petit South Africa, +1997,KC1AJR,Giovanni,East Hampton CT,kenwood NX800 K2 +3070,W4ORL,Ralph,,Ralph - Mobile +3071,W4LOV,Mike,,Mike - NX800 +3072,N4JTK,John,,John - NX800 +3074,W4MCA,Michael,,Michael - NX300 +3075,W4LOV,Mike,,Mike - NX800 +3079,NN4TT,Dave,Orlando,NX300 +3079,W4ORL,Ralph,,Ralph - Portable +3081,W4MCA,Michael,,Michael - NX800 +3082,W4LOV,Mike,,Mike - Portable +3083,KD4TWJ,Dean,,Dean - Mobile NX800 +3271,N4KEG,Richard,,RICHARD - Mobile and or ht's N4KEG (UHF) +3274,KD4EFM,Evans,FL, +3275,KD4EFM,Evans,FL, +3278,KD4EFM,Evans,FL, +3279,N4KEG,Richard,, +3280,KD4EFM,Evans,"Lakeland, Florida",work radio +3280,KD4EFM,Evans,FL,Evans Mobile(s) +3280,KD4EFM,Evans,FL, +3281,N4KEG,Richard,, +3450,K0KAD,CHAD TAYLOR,"SPOKANE VALLEY, WA",My Portable +3451,K0KAD,CHAD TAYLOR,"SPOKANE VALLEY, WA",My Base Station +3452,K0KAD,CHAD TAYLOR,"SPOKANE VALLEY, WA",Mobile Radio +4020,K4XXX,Ed,"London, KY",NX820 Base +4802,hb3yrb,Alex,Verbier,nx5300 +4804,hb9fmf,Didier,Rougemont,NX5300 +5001,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-820" +5002,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-720" +5003,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-820" +5004,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-720" +5005,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR" +5006,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR" +5007,N5LKE,James,AR,"N5LKE, James Ferguson, Searcy, AR - Kenwood NX-800" +5008,AF5WN,Mason,AR,"AF5WN, Mason Creager, Cabot, AR - Kenwood NX-700H" +5009,KJ5SF,Stan,AR,"KJ5SF, Stan Rongey, Searcy, AR - Kenwood NX-800" +5100,K5SRS,Michael Smith,"Fayetteville, AR",NX-5800 Mobile +5101,KC5YQB,Paul Blair,"Fayetteville, AR", +5125,AE5OQ,Armstead,"Higden, AR", +5125,AE5OQ,Armstead,AR,entered by k1iff +5134,AE5OQ,Armstead,AR,"AE5OQ, Armstead M Feland, IV, Higdon, AR - Kenwood NX-200" +5150,WY6NN,Doug,"Los Angeles, CA",NX-300/5300 +5151,KC6HFJ,Tamara,"Los Angeles, CA",NX-300 +5170,K5WMZ,Gary,AR,"K5WMZ, Gary D Partlow, Ward, AR - Kenwood NX-700" +5599,W1ZM,Ryan,,"W1ZM, Ryan Nelson, NX-5300" +5987,W5ZN,Joel,AR,"W5ZN, Joel Harrison, Judsonia, AR - Kenwood NX-800" +6430,K4GFD,Norm,FL,Norm Greensboro Fla +6431,K4GFD,Norm,, +6432,K4GFD,Norm,, +6439,N4NKV,George,,George +6801,hb9vsd,relais de verbier,verbier, +6802,HB9YRB,Alex,Verbier, +6804,HB9FMF,Didier,Rougemont, +7737,N4NKV,George,,George +9834,VA7NY,PJ,,PJ +12002,W4KJP,Kevin,,KEVIN +12005,KD4TWJ,Dean,,Dean - Portable IC-F4161 +12006,W4MO,Stewart,,STEWART NX300 / 320 +12007,W4MO,Stewart,,STEWART NX800 +12010,kd4dwp,Ben,Orlando, +12013,K4NBC,Miguel Orama,"Palm Bay, Florida",Icom F4146DS UHF Portable +12014,K4NBC,Miguel Orama,"Palm Bay, Florida",Kenwood NX200 VHF HT +12015,K4NBC,K4NBC-Miguel UHF Mobile,"Palm Bay, Florida",Future UHF Mobile +12016,K4NBC,Miguel,"Palm Bay, Florida",M2 Icom F6061D +12017,N4JFZ,,, +12021,W4PJT,Paul,,Paul +12023,KD4NWL,John,,JOHN +12127,W4KDM,Rick,,Rick - Portable NX300 +12136,WA4KWK,John,,John F6121 +12200,NB9X,Paul,FL, +12201,NB9X,Paul,FL,PAUL +12202,NB9X,Paul,FL, +12203,NB9X,Paul,FL, +12204,WX3C,Juan Arias,Florida,Orlando metro +12204,NB9X,Paul,FL, +12210,N4APG,Bill,,Bill Pfost +12211,N4APG,Bill,,Bill Pfost +12212,N4APG,Bill,,Bill Pfost +12213,N4APG,Bill,,Bill Pfost +12214,N4APG,Bill,,Bill Pfost +12215,WA4ISB,Ed,,ED Mobile +12216,WA4ISB,Ed,, +12220,N4PK,Paul,,PAUK K. +12221,N4PK,Paul,, +12222,W4PJT,Paul,, +12223,W4PJT,Paul,, +12226,KI4SWY,William,, +12230,KQ4KX,Richard,,Richard Sharp +12231,KQ4KX,Richard,, +12234,KD4TWJ,Dean,EL98, +12235,N1IC,Nick,,Nic +12236,N1IC,Nick,, +12240,KR4X,Larry,, +12241,KR4X,Larry,, +12245,KD4ACG,Jason,"Hudson, FL",Icom Mobile +12245,KD4ACG,Jason,,JASON +12246,KD4ACG,Jason,"Hudson, FL",Icom Portable +12246,KD4ACG,Jason,,JASON +12247,K2MCI,,, +12250,KE4GYX,,, +12272,KE4OSL,,, +12273,KE4OSL,,, +12374,KF4I,Thom,,Thom Base +12375,KF4I,Thom,,Thom Mobile +12523,KM4JVE,Ana,,Ana - IC-F4161 +12577,N2DLX,Richard,,Rich Dunajewski NX-5800 +12604,K4CBN,Greg,,GREG +12605,K4CBN,Greg,, +12606,K4CBN,Greg,, +12607,K4CBN,Greg,, +12608,K4CBN,Greg,, +12666,KI4SWY,William,,William Stillwel ( All Radios ) +12691,KE3WDW,Sam,,Sam +12780,KC4YUA,Brett,,BRETT +13105,W7QO,Alan,, +13106,W7QO,Alan,, +13107,W7QO,Alan,, +13121,W4CML,Chuck,, +13122,W4CML,Chuck,, +13125,KD4APP,Don,,Don - NX300 +13126,KD4APP,Don,,Don - F6061D +13127,KD4APP,Don,,Don - NX200 +13131,W5JR,Mike,, +13132,W5JR,Mike,, +13401,W1KFR,Bill,,Bill +13815,KG4BKO,Bill,GA,nx320 +14020,K4XXX,Ed,"London, KY",NX5300 +14021,K4XXX,Ed,"London, KY",NX5200 +15001,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-800H (Vehicle 1)" +15002,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-700 (Vehicle 1)" +15003,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-800 (Vehicle 2)" +15004,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-700 (Vehicle 2)" +15005,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-800 (Vehicle 1)" +15006,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-700 (Vehicle 1)" +15007,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-800 (Vehicle 2)" +15008,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-700 (Vehicle 2)" +15009,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-800 (Vehicle 3)" +15010,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-700 (Vehicle 3)" +15011,N5QT,Dawn,AR,"N5QT, Dawn Gray, Searcy, AR - Kenwood NX-800" +15012,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR - Kenwood NX-800" +15013,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR - Kenwood NX-700" +15014,N5QT,Dawn,AR,"N5QT, Dawn Gray, Searcy, AR - Kenwood NX-700" +15015,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR Motorcycle Mobile" +15016,KG5KS,Kenny,,"KG5KS, Kenny Thompson - Kenwood NX-700HK" +15017,KG5KS,Kenny,,"KG5KS, Kenny Thompson - Kenwood NX-800HK" +15018,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR ATV Mobile" +15019,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR ATV Mobile" +15020,WA8UBL,Allen,AR,"WA8UBL, Allen Herrick, Beebe, AR, Kenwood NX-700" +15021,KD5HRT,Heath,AR,"KD5HRT, Heath Taylor, McRae, AR, Kenwood NX-800" +15022,WA8UBL,Allen,AR,"WA8UBL, Allen Herrick, Beebe, AR - Kenwood NX-800" +15023,N5ZA,Joel,AR,"N5ZA, Joe Belew, Beebe, AR - Kenwood NX-700" +15024,KF5TPF,Ryan,AR,"KF5TPF, Ryan Nelson, Edgemont, AR NX-700" +15025,KE5YZP,Ryan,AR,"KE5YZP, Ryan Schwarck, Batesville, AR NX-700" +15026,AF5OD,Landon,AR,"AF5OD, Landon McCord, Searcy, AR" +15027,KB5RFF,Scott,AR,"KB5RFF, Scott Gray, Searcy, AR - Kenwood NX-800" +17002,KC9GMX,Stephen,,Stephen - Base F6121D +17004,KC9GMX,Stephen,,Stephen - F6061D +17005,KC9NPJ,Jarrett,,Jarrett F6121D +17006,KC9GMX,Stephen,,Stephen - F4161DS +17007,KC9NPJ,Jarrett,,Jarrett F4231DS +17008,KC9RHH,Jesse,,F4101D +17009,KC9PHK,Michael,,F4061DS +17011,N9BRG,Dan,,Dan kenwood nx800k2 +17855,AG6RN,Demian,CA,NX-210 +21007,WA1LMV,Rick,,Rick +21014,K2BUI,Eric,,Eric +24020,K4XXX,Ed,"London, KY",NX5800 +24021,K4XXX,Ed,"London, KY",NX5700 +25001,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-300" +25002,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-200" +25003,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-300" +25004,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-200" +25005,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-320" +25006,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-340U" +25007,N5QT,Dawn,AR,"N5QT, Dawn Gray, Searcy, AR - Kenwood NX-300" +25008,N5QT,Dawn,AR,"N5QT, Dawn Gray, Searcy, AR - Kenwood NX-200" +25009,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR - Kenwood NX-300" +25010,KD5OOW,Tom,AR,"KD5OOW, Tom McGee, Searcy, AR - Kenwood NX-300" +25011,W5ZN,Joel,AR,"W5ZN, Joel Harrison, Judsonia, AR - Kenwood NX-300" +25012,KD5HRT,Heath,AR,"KD5HRT, Heath Taylor, McRae, AR - Kenwood NX-300" +25013,KK5WA,Darin,AR,"KK5WA, Darin Dykes, Beebe, AR - Kenwood NX-320" +25014,N5LKE,James,AR,"N5LKE, James Ferguson, Searcy, AR - Kenwood NX-5300" +25015,KJ5ORD,John,AR,"KJ5ORD, John Ord, Searcy, AR - Kenwood NX-300" +25016,KB5RFF,Scott,AR,"KB5RFF, Scott Gray, Searcy, AR - Kenwood NX-300" +25017,KB5ZUH,Todd,AR,"KB5ZUH, Todd Williams, Searcy, AR - Kenwood NX-320" +25018,KB5ZUH,Todd,AR,"KB5ZUH, Todd Williams, Searcy, AR - Kenwood NX-340" +25019,KB5ZUH,Todd,AR,"KB5ZUH, Todd Williams, Searcy, AR - Kenwood NX-340" +25020,KB5ZUH,Todd,AR,"KB5ZUH, Todd Williams, Searcy, AR - Kenwood NX-320" +25021,KE5UZU,Raymond Prince,"England, AR",Kenwood Handheld +25021,AF5OD,Landon,AR,"AF5OD, Landon McCord, Searcy, AR" +25022,KE5UZU,Raymond Prince,"England, AR",Icom Handheld +25022,KE5YZP,Ryan,AR,"KE5YZP, Ryan A Schwarck, Batesville, AR - Icom" +25023,KE5YZP,Ryan,AR,"KE5YZP, Ryan A Schwarck, Batesville, AR - Icom" +25024,KG5CHM,Colt,AR,"KG5CHM, Colt Boyd, Searcy, AR - Kenwood NX-300" +25025,KJ5ORD,John,AR,"KJ5ORD, John Ord, Searcy, AR - Kenwood NX-xxx VHF" +25026,WA8UBL,Allen,AR,"WA8UBL, Allen Herrick, Beebe, AR - Kenwood NX-320" +25027,KF5TPF,Ryan,AR,"KF5TPF, Ryan Nelson, Edgemont, AR - Kenwood NX-200" +25028,WA8UBL,Allen,AR,"WA8UBL, Allen Herrick, Beebe, AR - Kenwood NX-220" +25029,KF5WHX,Trey,AR,"KF5WHX, Trey Ferguson, Searcy, AR - Kenwood NX-320" +25030,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR" +25031,KJ5SF,Stan,AR,"KJ5SF, Stan Rongey, Searcy, AR - Kenwood NX-340" +25032,KC5DFH,Kirk,AR,"KC5DFH, Kirk Williams, Searcy, AR - Kenwood NX-340" +25033,AF5OD,Landon,AR,"AF5OD, Landon McCord, Searcy, AR, Icom F4161DS" +25034,KF5TPF,Ryan,AR,"KF5TPF, Ryan Nelson, Edgemont, AR - ICOM 4161" +25035,KG5BMB,Glenda,AR,"KG5BMB, Glenda McCord, Searcy, AR, Icom F4161DS" +25039,KC1JET,Jim,,JIM (BASE) +25068,N1QD,Joe,, +25069,N1COP,Bob,,Bob +25070,WD1L,John,,John +25125,N1TI,Tim,"Mattapoisett, MA", +25125,N1TI,Tim,"Mattapoisett, MA", +25144,W1WCF,Walter,,Walter +25221,N0XIA,James,,James - Kenwood Mobile and Portable +25222,KE4SDC,Ron,,Ron - Portable NX300 +25246,WG1U,Ken,"Assonet, MA", +25333,W1AKN,Jack,,Jack Mobile +25390,KC1JET,Jim,,JIM (MOBILE) +25390,KC1JET,Jim,,JIM +25444,W1TAV,Steve,,Steve +25555,WA1MNQ,Mike,,Mike +25582,N1KXJ,Ray,"E. Bridgewater, MA.",Port. +25582,N1KXJ,Ray Wall,East Bridgewater, +25585,N1AVA,Ken Howland,"DARTMOUTH,MA", +25700,NN1D,Tony Souza,"Swansea, MA", +25740,WG1U,Kenny,,KEN +25743,WG1U,Kenny,,KEN +25746,N1OTY,John,,JOHN +25777,W1BON,Bump,,Bump +25830,KB1CHU,Steve,MA, +25863,N1ZZN,Jeff,,JEFF +25874,KB1KVD,Jason,,Jason - Icom Mob & Port +25888,K1WGU,Bob,,Bob +25889,AB1CQ,Gary,,Gary +25900,N1ZZR,Matt,,Matt - mobile +25901,N1MRC,Matt,"Bridgewater, MA",N1MRC P-1 +25901,N1ZZR,Matt,,Matt +25957,KC1HO,Steve,,STEVE +27101,KI4SWB,Mark,"Melbourne Beach, FL",Icom Portable +27102,KP4OO,Carlos,"Palm Bay, FL",Icom Portable +27201,KI4SWB,Mark,"Melbourne Beach, FL",Icom Mobile +27202,KP4OO,Carlos,"Palm Bay, FL",Icom Mobile +28001,K3KRS,rYAN,CLEVLAND,OHIO +30505,KG6MQE,Jim,MT, +30571,WX4RCF,Ryan F.,"Tampa, FL", +31117,K4QHR,Pete,,Pete +31124,KM4JXP,Peter,,NX5300 +31146,KD2KWD,Michael Santamaria,"Brandon, FL",Kenwood +33000,N1PA-R,Paul,NH,PAUL - NEW HAMPSHIRE REPEATER +33001,N1PA,Paul,NH,PAUL - NEW HAMPSHIRE +33002,N1PA,Paul,NH,PAUL - NEW HAMPSHIRE +34001,N2WNS,Bill,NJ,N2WNS Portable +34002,N2WNS,Bill,NJ,N2WNS Base +36001,W2CCR,Chris,NY,"Chris, Galway, NY NX300" +36002,KC2VOB,Asad,NY,"Asad, NYC, NX-5800" +36003,KC2VOB,Asad,NY,"Asad, NYC, NX-300GK" +36134,N2SRO,Mike,"Pitman, NJ",Portable +37001,KA4YMY,Steve,NC,"Steve, Charlotte, NC NX320" +37009,KA4YMY,Steve,"Charlotte, NC", +37010,KA4YMY,Steve,"Charlotte, NC", +37010,KA4YMY,Steve,"Charlotte, NC", +40024,N5YEI,Jeff Dalrymple,"Jay, Oklahoma",NX-5800 Mobile +40124,N5YEI,Jeff Dalrymple,"Jay, Oklahoma",NX-5300 Portable +43751,N4IRS,Steve,,Steve +44001,K2BUI,Eric,RI, +44002,K2BUI,Eric,"Providence, RI", +44007,W2DAN,Dave,,Dave - NX700 +44298,N1JBC,Jed,,JED +49101,WX7Y-1,Bret Mills,Castle Dale Utah,WX7y-1 Hand Held 1 +49102,WX7Y-2,Bret Mills,"Caslte Dale, Utah",WX7Y-2 Hand held +49104,WX7Y-4,Bret Mills,"Castle Dale, Utah",WX7Y-4 Mobile +49106,WX7Y-6,Bret Mills,Castle Dale,WX7Y-6 Mobile +55700,WB9HKE,Rick,WI, +55704,WB9HKE,Rick,WI, +57000,VE3SP,Andre,"Toronto, ON",mobile +57001,VE3SP,Andre,"Toronto, ON",handheld +57001,VE3EI,Eric,,Eric - Portable +57002,VE3OKZ,Janusz,"Toronto, ON",mobile +57002,VE3EI,Eric,,Eric - Dongle +57003,VE3OKZ,Janusz,"Toronto, ON",handheld +57004,VE3CCD,Curtis,"Scarborough, ON",handheld +57005,VE3NBI,Anoop,"Scarborough, ON",handheld +57006,VE3JLU,Sherwin,"Scarborough, ON",handheld +57007,va3rq,Jon,"Oakville, ON",handheld +57008,ve3ogb,Randy,"Toronto, ON",handheld +57009,va3mk,Mark,"Oakville, ON",handheld +57010,va3mk,Mark,"Oakville, ON",mobile +57011,VA3WZW,Andre,"Durham, ON",mobile +57012,VA3WZW,Andre,"Durham, ON",mobile +57013,VA3AGV,cottage,"Toronto, ON",mobile +57014,VE3BWP,Brian,"Caledon, ON",handheld +57015,VE3HY,Frank,"Caledon, ON",handheld +57016,VE3UBI,Paul,"Scarborough, ON",mobile +57017,VE3UBI,Paul,"Scarborough, ON",handheld +57018,VE3RZR,Richard,"Toronto, ON",handheld +57019,VE3AEP,Dom,"Toronto, ON",mobile +57020,VA3PEO,Adrian,"Toronto, ON",handheld +57021,va3wjo,Walter,"Toronto, ON",handheld +57022,ve3fkn,Tom,"Stoney Creek, ON",handheld +57023,ve3ips,John,"Toronto, ON",handheld +57024,VA3CQA,Brian,"Scarborough, ON",handheld +57025,VA3PAD,Andrew,"Scarborough, ON",mobile +57026,VA3PAD,Andrew,"Scarborough, ON",handheld +57027,VA3VAD,Arpad,"Hamilton, ON",handheld +57028,VA3KY,Shelly,"Thornhill, ON",mobile +57029,VA3KY,Shelly,"Thornhill, ON",handheld +57030,VE3KFQ,Doug,"Toronto, ON",mobile +57031,VE3KFQ,Doug,"Toronto, ON",handheld +57032,VE3AEP,Dom 2,"Toronto, ON",handheld +57033,VE3DJZ,Huey,"Hamilton, ON",mobile +57034,VE3DJZ,Huey,"Hamilton, ON",handheld +57035,VA3RQ,Jon,Oakville ON,Mobile +3106418,N5ICK,Nicholas Nixon,"Rialto, Ca", +3112205,N2HUC,Phil Roberts,"Port St. Lucie, FL",DV4MINI AMBE voice +3118365,NA9VY,Chris Gilbert,"New Palestine, IN", +3118366,N9UMJ,Rick Nicholson,Buddha Indiana, +3142058,N3BAH,L. Abraham Smith,Southwestern Pennsylvania, diff --git a/NXDNGateway/NXDNCRC.cpp b/NXDNGateway/NXDNCRC.cpp new file mode 100644 index 0000000..60278ae --- /dev/null +++ b/NXDNGateway/NXDNCRC.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "NXDNCRC.h" + +#include +#include + +const uint8_t BIT_MASK_TABLE1[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE1[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE1[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE1[(i)&7]) + +bool CNXDNCRC::checkCRC6(const unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint8_t crc = createCRC6(in, length); + + uint8_t temp[1U]; + temp[0U] = 0x00U; + unsigned int j = length; + for (unsigned int i = 2U; i < 8U; i++, j++) { + bool b = READ_BIT1(in, j); + WRITE_BIT1(temp, i, b); + } + + return crc == temp[0U]; +} + +void CNXDNCRC::encodeCRC6(unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint8_t crc[1U]; + crc[0U] = createCRC6(in, length); + + unsigned int n = length; + for (unsigned int i = 2U; i < 8U; i++, n++) { + bool b = READ_BIT1(crc, i); + WRITE_BIT1(in, n, b); + } +} + +bool CNXDNCRC::checkCRC12(const unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint16_t crc = createCRC12(in, length); + uint8_t temp1[2U]; + temp1[0U] = (crc >> 8) & 0xFFU; + temp1[1U] = (crc >> 0) & 0xFFU; + + uint8_t temp2[2U]; + temp2[0U] = 0x00U; + temp2[1U] = 0x00U; + unsigned int j = length; + for (unsigned int i = 4U; i < 16U; i++, j++) { + bool b = READ_BIT1(in, j); + WRITE_BIT1(temp2, i, b); + } + + return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U]; +} + +void CNXDNCRC::encodeCRC12(unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint16_t crc = createCRC12(in, length); + + uint8_t temp[2U]; + temp[0U] = (crc >> 8) & 0xFFU; + temp[1U] = (crc >> 0) & 0xFFU; + + unsigned int n = length; + for (unsigned int i = 4U; i < 16U; i++, n++) { + bool b = READ_BIT1(temp, i); + WRITE_BIT1(in, n, b); + } +} + +bool CNXDNCRC::checkCRC15(const unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint16_t crc = createCRC15(in, length); + uint8_t temp1[2U]; + temp1[0U] = (crc >> 8) & 0xFFU; + temp1[1U] = (crc >> 0) & 0xFFU; + + uint8_t temp2[2U]; + temp2[0U] = 0x00U; + temp2[1U] = 0x00U; + unsigned int j = length; + for (unsigned int i = 1U; i < 16U; i++, j++) { + bool b = READ_BIT1(in, j); + WRITE_BIT1(temp2, i, b); + } + + return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U]; +} + +void CNXDNCRC::encodeCRC15(unsigned char* in, unsigned int length) +{ + assert(in != NULL); + + uint16_t crc = createCRC15(in, length); + + uint8_t temp[2U]; + temp[0U] = (crc >> 8) & 0xFFU; + temp[1U] = (crc >> 0) & 0xFFU; + + unsigned int n = length; + for (unsigned int i = 1U; i < 16U; i++, n++) { + bool b = READ_BIT1(temp, i); + WRITE_BIT1(in, n, b); + } +} + +uint8_t CNXDNCRC::createCRC6(const unsigned char* in, unsigned int length) +{ + uint8_t crc = 0x3FU; + + for (unsigned int i = 0U; i < length; i++) { + bool bit1 = READ_BIT1(in, i) != 0x00U; + bool bit2 = (crc & 0x20U) == 0x20U; + + crc <<= 1; + + if (bit1 ^ bit2) + crc ^= 0x27U; + } + + return crc & 0x3FU; +} + +uint16_t CNXDNCRC::createCRC12(const unsigned char* in, unsigned int length) +{ + uint16_t crc = 0x0FFFU; + + for (unsigned int i = 0U; i < length; i++) { + bool bit1 = READ_BIT1(in, i) != 0x00U; + bool bit2 = (crc & 0x0800U) == 0x0800U; + + crc <<= 1; + + if (bit1 ^ bit2) + crc ^= 0x080FU; + } + + return crc & 0x0FFFU; +} + +uint16_t CNXDNCRC::createCRC15(const unsigned char* in, unsigned int length) +{ + uint16_t crc = 0x7FFFU; + + for (unsigned int i = 0U; i < length; i++) { + bool bit1 = READ_BIT1(in, i) != 0x00U; + bool bit2 = (crc & 0x4000U) == 0x4000U; + + crc <<= 1; + + if (bit1 ^ bit2) + crc ^= 0x4CC5U; + } + + return crc & 0x7FFFU; +} diff --git a/NXDNGateway/NXDNCRC.h b/NXDNGateway/NXDNCRC.h new file mode 100644 index 0000000..12e2e40 --- /dev/null +++ b/NXDNGateway/NXDNCRC.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(NXDNCRC_H) +#define NXDNCRC_H + +#include + +class CNXDNCRC +{ +public: + static bool checkCRC6(const unsigned char* in, unsigned int length); + static void encodeCRC6(unsigned char* in, unsigned int length); + + static bool checkCRC12(const unsigned char* in, unsigned int length); + static void encodeCRC12(unsigned char* in, unsigned int length); + + static bool checkCRC15(const unsigned char* in, unsigned int length); + static void encodeCRC15(unsigned char* in, unsigned int length); + +private: + static uint8_t createCRC6(const unsigned char* in, unsigned int length); + static uint16_t createCRC12(const unsigned char* in, unsigned int length); + static uint16_t createCRC15(const unsigned char* in, unsigned int length); +}; + +#endif diff --git a/NXDNGateway/NXDNGateway.cpp b/NXDNGateway/NXDNGateway.cpp new file mode 100644 index 0000000..eca6cf1 --- /dev/null +++ b/NXDNGateway/NXDNGateway.cpp @@ -0,0 +1,380 @@ +/* +* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "IcomNetwork.h" +#include "NXDNNetwork.h" +#include "NXDNGateway.h" +#include "NXDNLookup.h" +#include "Reflectors.h" +#include "StopWatch.h" +#include "Version.h" +#include "Thread.h" +#include "Voice.h" +#include "Timer.h" +#include "Log.h" + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(_WIN32) || defined(_WIN64) +const char* DEFAULT_INI_FILE = "NXDNGateway.ini"; +#else +const char* DEFAULT_INI_FILE = "/etc/NXDNGateway.ini"; +#endif + +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + const char* iniFile = DEFAULT_INI_FILE; + if (argc > 1) { + for (int currentArg = 1; currentArg < argc; ++currentArg) { + std::string arg = argv[currentArg]; + if ((arg == "-v") || (arg == "--version")) { + ::fprintf(stdout, "NXDNGateway version %s\n", VERSION); + return 0; + } else if (arg.substr(0, 1) == "-") { + ::fprintf(stderr, "Usage: NXDNGateway [-v|--version] [filename]\n"); + return 1; + } else { + iniFile = argv[currentArg]; + } + } + } + + CNXDNGateway* gateway = new CNXDNGateway(std::string(iniFile)); + gateway->run(); + delete gateway; + + return 0; +} + +CNXDNGateway::CNXDNGateway(const std::string& file) : +m_conf(file) +{ +} + +CNXDNGateway::~CNXDNGateway() +{ +} + +void CNXDNGateway::run() +{ + bool ret = m_conf.read(); + if (!ret) { + ::fprintf(stderr, "NXDNGateway: cannot read the .ini file\n"); + return; + } + + ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), 1U, 1U); + if (!ret) { + ::fprintf(stderr, "NXDNGateway: unable to open the log file\n"); + return; + } + +#if !defined(_WIN32) && !defined(_WIN64) + bool m_daemon = m_conf.getDaemon(); + if (m_daemon) { + // Create new process + pid_t pid = ::fork(); + if (pid == -1) { + ::LogWarning("Couldn't fork() , exiting"); + return; + } + else if (pid != 0) + exit(EXIT_SUCCESS); + + // Create new session and process group + if (::setsid() == -1) { + ::LogWarning("Couldn't setsid(), exiting"); + return; + } + + // Set the working directory to the root directory + if (::chdir("/") == -1) { + ::LogWarning("Couldn't cd /, exiting"); + return; + } + + ::close(STDIN_FILENO); + ::close(STDOUT_FILENO); + ::close(STDERR_FILENO); + + //If we are currently root... + if (getuid() == 0) { + struct passwd* user = ::getpwnam("mmdvm"); + if (user == NULL) { + ::LogError("Could not get the mmdvm user, exiting"); + return; + } + + uid_t mmdvm_uid = user->pw_uid; + gid_t mmdvm_gid = user->pw_gid; + + //Set user and group ID's to mmdvm:mmdvm + if (setgid(mmdvm_gid) != 0) { + ::LogWarning("Could not set mmdvm GID, exiting"); + return; + } + + if (setuid(mmdvm_uid) != 0) { + ::LogWarning("Could not set mmdvm UID, exiting"); + return; + } + + //Double check it worked (AKA Paranoia) + if (setuid(0) != -1) { + ::LogWarning("It's possible to regain root - something is wrong!, exiting"); + return; + } + } + } +#endif + + CIcomNetwork localNetwork(m_conf.getMyAddress(), m_conf.getMyPort(), m_conf.getRptAddress(), m_conf.getRptPort(), false); + ret = localNetwork.open(); + if (!ret) { + ::LogFinalise(); + return; + } + + CNXDNNetwork remoteNetwork(m_conf.getNetworkPort(), m_conf.getCallsign(), m_conf.getNetworkDebug()); + ret = remoteNetwork.open(); + if (!ret) { + localNetwork.close(); + ::LogFinalise(); + return; + } + + CReflectors reflectors(m_conf.getNetworkHosts1(), m_conf.getNetworkHosts2(), m_conf.getNetworkReloadTime()); + if (m_conf.getNetworkParrotPort() > 0U) + reflectors.setParrot(m_conf.getNetworkParrotAddress(), m_conf.getNetworkParrotPort()); + reflectors.load(); + + CNXDNLookup* lookup = new CNXDNLookup(m_conf.getLookupName(), m_conf.getLookupTime()); + lookup->read(); + + CTimer inactivityTimer(1000U, m_conf.getNetworkInactivityTimeout() * 60U); + CTimer lostTimer(1000U, 120U); + CTimer pollTimer(1000U, 5U); + + CStopWatch stopWatch; + stopWatch.start(); + + CVoice* voice = NULL; + if (m_conf.getVoiceEnabled()) + voice = new CVoice(m_conf.getVoiceDirectory(), m_conf.getVoiceLanguage(), 1U); + + LogMessage("Starting NXDNGateway-%s", VERSION); + + unsigned short srcId = 0U; + unsigned short dstId = 0U; + bool grp = false; + + unsigned int currentId = 9999U; + in_addr currentAddr; + unsigned int currentPort = 0U; + + unsigned int id = m_conf.getNetworkStartup(); + if (id != 9999U) { + CNXDNReflector* reflector = reflectors.find(id); + if (reflector != NULL) { + currentId = id; + currentAddr = reflector->m_address; + currentPort = reflector->m_port; + + inactivityTimer.start(); + pollTimer.start(); + lostTimer.start(); + + remoteNetwork.writePoll(currentAddr, currentPort); + remoteNetwork.writePoll(currentAddr, currentPort); + remoteNetwork.writePoll(currentAddr, currentPort); + + LogMessage("Linked at startup to reflector %u", currentId); + } + } + + for (;;) { + unsigned char buffer[200U]; + in_addr address; + unsigned int port; + + // From the reflector to the MMDVM + unsigned int len = remoteNetwork.readData(buffer, 200U, address, port); + if (len > 0U) { + // If we're linked and it's from the right place, send it on + if (currentId != 9999U && currentAddr.s_addr == address.s_addr && currentPort == port) { + // Don't pass reflector control data through to the MMDVM + if (::memcmp(buffer, "NXDND", 5U) == 0) + localNetwork.write(buffer + 10U, len - 10U); + + // Any network activity is proof that the reflector is alive + lostTimer.start(); + } + } + + // From the MMDVM to the reflector or control data + len = localNetwork.read(buffer); + if (len > 0U) { + if (buffer[0U] == 0x81U || buffer[0U] == 0x83U) { + grp = (buffer[7U] & 0x20U) == 0x20U; + + srcId = (buffer[8U] << 8) & 0xFF00U; + srcId |= (buffer[9U] << 0) & 0x00FFU; + + dstId = (buffer[10U] << 8) & 0xFF00U; + dstId |= (buffer[11U] << 0) & 0x00FFU; + + if (dstId != currentId) { + CNXDNReflector* reflector = NULL; + if (dstId != 9999U) + reflector = reflectors.find(dstId); + + // If we're unlinking or changing reflectors, unlink from the current one + if (dstId == 9999U || reflector != NULL) { + std::string callsign = lookup->find(srcId); + + if (currentId != 9999U) { + LogMessage("Unlinked from reflector %u by %s", currentId, callsign.c_str()); + + remoteNetwork.writeUnlink(currentAddr, currentPort); + remoteNetwork.writeUnlink(currentAddr, currentPort); + remoteNetwork.writeUnlink(currentAddr, currentPort); + + inactivityTimer.stop(); + pollTimer.stop(); + lostTimer.stop(); + } + + if (voice != NULL) + voice->linkedTo(dstId); + + currentId = dstId; + } + + // Link to the new reflector + if (reflector != NULL) { + currentId = dstId; + currentAddr = reflector->m_address; + currentPort = reflector->m_port; + + std::string callsign = lookup->find(srcId); + LogMessage("Linked to reflector %u by %s", currentId, callsign.c_str()); + + remoteNetwork.writePoll(currentAddr, currentPort); + remoteNetwork.writePoll(currentAddr, currentPort); + remoteNetwork.writePoll(currentAddr, currentPort); + + inactivityTimer.start(); + pollTimer.start(); + lostTimer.start(); + } + } + } + + // If we're linked and we have a network, send it on + if (currentId != 9999U) { + remoteNetwork.writeData(buffer, len, srcId, dstId, grp, currentAddr, currentPort); + inactivityTimer.start(); + } + } + + if (voice != NULL) { + unsigned int length = voice->read(buffer); + if (length > 0U) + localNetwork.write(buffer, length); + } + + unsigned int ms = stopWatch.elapsed(); + stopWatch.start(); + + reflectors.clock(ms); + + if (voice != NULL) + voice->clock(ms); + + inactivityTimer.clock(ms); + if (inactivityTimer.isRunning() && inactivityTimer.hasExpired()) { + if (currentId != 9999U) { + LogMessage("Unlinking from %u due to inactivity", currentId); + + remoteNetwork.writeUnlink(currentAddr, currentPort); + remoteNetwork.writeUnlink(currentAddr, currentPort); + remoteNetwork.writeUnlink(currentAddr, currentPort); + + if (voice != NULL) + voice->unlinked(); + currentId = 9999U; + + pollTimer.stop(); + lostTimer.stop(); + } + + inactivityTimer.stop(); + } + + pollTimer.clock(ms); + if (pollTimer.isRunning() && pollTimer.hasExpired()) { + if (currentId != 9999U) + remoteNetwork.writePoll(currentAddr, currentPort); + pollTimer.start(); + } + + lostTimer.clock(ms); + if (lostTimer.isRunning() && lostTimer.hasExpired()) { + if (currentId != 9999U) { + LogWarning("No response from %u, unlinking", currentId); + currentId = 9999U; + } + + inactivityTimer.stop(); + lostTimer.stop(); + } + + localNetwork.clock(ms); + + if (ms < 5U) + CThread::sleep(5U); + } + + delete voice; + + localNetwork.close(); + + remoteNetwork.close(); + + lookup->stop(); + + ::LogFinalise(); +} diff --git a/NXDNGateway/NXDNGateway.h b/NXDNGateway/NXDNGateway.h new file mode 100644 index 0000000..2dbc607 --- /dev/null +++ b/NXDNGateway/NXDNGateway.h @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(NXDNGateway_H) +#define NXDNGateway_H + +#include "Timer.h" +#include "Conf.h" + +#include +#include +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +class CNXDNGateway +{ +public: + CNXDNGateway(const std::string& file); + ~CNXDNGateway(); + + void run(); + +private: + CConf m_conf; +}; + +#endif diff --git a/NXDNGateway/NXDNGateway.ini b/NXDNGateway/NXDNGateway.ini new file mode 100644 index 0000000..3b951ec --- /dev/null +++ b/NXDNGateway/NXDNGateway.ini @@ -0,0 +1,31 @@ +[General] +Callsign=G4KLX +RptAddress=127.0.0.1 +RptPort=14021 +LocalAddress=127.0.0.1 +LocalPort=14020 +Daemon=1 + +[Voice] +Enabled=1 +Language=en_GB +Directory=./Audio + +[Id Lookup] +Name=NXDN.csv +Time=24 + +[Log] +FilePath=. +FileRoot=NXDNGateway + +[Network] +Port=14050 +HostsFile1=./NXDNHosts.txt +HostsFile2=./private/NXDNHosts.txt +ReloadTime=60 +ParrotAddress=127.0.0.1 +ParrotPort=42011 +Startup=10200 +InactivityTimeout=10 +Debug=0 diff --git a/NXDNGateway/NXDNGateway.vcxproj b/NXDNGateway/NXDNGateway.vcxproj new file mode 100644 index 0000000..e6230c4 --- /dev/null +++ b/NXDNGateway/NXDNGateway.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {8B7A5406-8560-4B40-ADDA-9B8EBF93E232} + Win32Proj + NXDNGateway + 10.0.15063.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NXDNGateway/NXDNGateway.vcxproj.filters b/NXDNGateway/NXDNGateway.vcxproj.filters new file mode 100644 index 0000000..e8181d5 --- /dev/null +++ b/NXDNGateway/NXDNGateway.vcxproj.filters @@ -0,0 +1,113 @@ + + + + + {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 + + + 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 + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/NXDNGateway/NXDNHosts.txt b/NXDNGateway/NXDNHosts.txt new file mode 100644 index 0000000..8171bec --- /dev/null +++ b/NXDNGateway/NXDNHosts.txt @@ -0,0 +1,7 @@ +# NXDNHosts.txt +# +# The format of this file is the number of the Talk Group followed by the host name or address and port +# + +# World Wide, 65000 +65000 176.9.1.168 41008 diff --git a/NXDNGateway/NXDNLookup.cpp b/NXDNGateway/NXDNLookup.cpp new file mode 100644 index 0000000..2c621b6 --- /dev/null +++ b/NXDNGateway/NXDNLookup.cpp @@ -0,0 +1,160 @@ +/* +* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "NXDNLookup.h" +#include "Timer.h" +#include "Log.h" + +#include +#include +#include +#include + +CNXDNLookup::CNXDNLookup(const std::string& filename, unsigned int reloadTime) : +CThread(), +m_filename(filename), +m_reloadTime(reloadTime), +m_table(), +m_mutex(), +m_stop(false) +{ +} + +CNXDNLookup::~CNXDNLookup() +{ +} + +bool CNXDNLookup::read() +{ + bool ret = load(); + + if (m_reloadTime > 0U) + run(); + + return ret; +} + +void CNXDNLookup::entry() +{ + LogInfo("Started the NXDN Id lookup reload thread"); + + CTimer timer(1U, 3600U * m_reloadTime); + timer.start(); + + while (!m_stop) { + sleep(1000U); + + timer.clock(); + if (timer.hasExpired()) { + load(); + timer.start(); + } + } + + LogInfo("Stopped the NXDN Id lookup reload thread"); +} + +void CNXDNLookup::stop() +{ + if (m_reloadTime == 0U) { + delete this; + return; + } + + m_stop = true; + + wait(); +} + +std::string CNXDNLookup::find(unsigned int id) +{ + std::string callsign; + + if (id == 0xFFFFU) + return std::string("ALL"); + + m_mutex.lock(); + + try { + callsign = m_table.at(id); + } catch (...) { + char text[10U]; + ::sprintf(text, "%u", id); + callsign = std::string(text); + } + + m_mutex.unlock(); + + return callsign; +} + +bool CNXDNLookup::exists(unsigned int id) +{ + m_mutex.lock(); + + bool found = m_table.count(id) == 1U; + + m_mutex.unlock(); + + return found; +} + +bool CNXDNLookup::load() +{ + FILE* fp = ::fopen(m_filename.c_str(), "rt"); + if (fp == NULL) { + LogWarning("Cannot open the NXDN Id lookup file - %s", m_filename.c_str()); + return false; + } + + m_mutex.lock(); + + // Remove the old entries + m_table.clear(); + + char buffer[100U]; + while (::fgets(buffer, 100U, fp) != NULL) { + if (buffer[0U] == '#') + continue; + + char* p1 = ::strtok(buffer, ",\t\r\n"); + char* p2 = ::strtok(NULL, ",\t\r\n"); + + if (p1 != NULL && p2 != NULL) { + unsigned int id = (unsigned int)::atoi(p1); + if (id > 0U) { + for (char* p = p2; *p != 0x00U; p++) + *p = ::toupper(*p); + + m_table[id] = std::string(p2); + } + } + } + + m_mutex.unlock(); + + ::fclose(fp); + + size_t size = m_table.size(); + if (size == 0U) + return false; + + LogInfo("Loaded %u Ids to the NXDN callsign lookup table", size); + + return true; +} \ No newline at end of file diff --git a/NXDNGateway/NXDNLookup.h b/NXDNGateway/NXDNLookup.h new file mode 100644 index 0000000..d7b0486 --- /dev/null +++ b/NXDNGateway/NXDNLookup.h @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef NXDNLookup_H +#define NXDNLookup_H + +#include "Thread.h" +#include "Mutex.h" + +#include +#include + +class CNXDNLookup : public CThread { +public: + CNXDNLookup(const std::string& filename, unsigned int reloadTime); + virtual ~CNXDNLookup(); + + bool read(); + + virtual void entry(); + + std::string find(unsigned int id); + + bool exists(unsigned int id); + + void stop(); + +private: + std::string m_filename; + unsigned int m_reloadTime; + std::unordered_map m_table; + CMutex m_mutex; + bool m_stop; + + bool load(); +}; + +#endif diff --git a/NXDNGateway/NXDNNetwork.cpp b/NXDNGateway/NXDNNetwork.cpp new file mode 100644 index 0000000..915eecb --- /dev/null +++ b/NXDNGateway/NXDNNetwork.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "NXDNNetwork.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +CNXDNNetwork::CNXDNNetwork(unsigned int port, const std::string& callsign, bool debug) : +m_callsign(callsign), +m_socket(port), +m_debug(debug) +{ + m_callsign.resize(10U, ' '); +} + +CNXDNNetwork::~CNXDNNetwork() +{ +} + +bool CNXDNNetwork::open() +{ + LogInfo("Opening NXDN network connection"); + + return m_socket.open(); +} + +bool CNXDNNetwork::writeData(const unsigned char* data, unsigned int length, unsigned short srcId, unsigned short dstId, bool grp, const in_addr& address, unsigned int port) +{ + assert(data != NULL); + assert(length > 0U); + assert(port > 0U); + + unsigned char buffer[20U]; + + buffer[0U] = 'N'; + buffer[1U] = 'X'; + buffer[2U] = 'D'; + buffer[3U] = 'N'; + buffer[4U] = 'D'; + + buffer[5U] = (srcId >> 8) & 0xFFU; + buffer[6U] = (srcId >> 0) & 0xFFU; + + buffer[7U] = (dstId >> 8) & 0xFFU; + buffer[8U] = (dstId >> 0) & 0xFFU; + + buffer[9U] = 0x00U; + buffer[9U] |= grp ? 0x01U : 0x00U; + + if (data[0U] == 0x81U || data[0U] == 0x83U) { + buffer[9U] |= buffer[5U] == 0x01U ? 0x04U : 0x00U; + buffer[9U] |= buffer[5U] == 0x08U ? 0x08U : 0x00U; + } + + ::memcpy(buffer + 10U, data, 33U); + + if (m_debug) + CUtils::dump(1U, "NXDN Network Data Sent", buffer, 43U); + + return m_socket.write(buffer, 43U, address, port); +} + +bool CNXDNNetwork::writePoll(const in_addr& address, unsigned int port) +{ + assert(port > 0U); + + unsigned char data[20U]; + + data[0U] = 'N'; + data[1U] = 'X'; + data[2U] = 'D'; + data[3U] = 'N'; + data[4U] = 'P'; + + for (unsigned int i = 0U; i < 10U; i++) + data[i + 5U] = m_callsign.at(i); + + if (m_debug) + CUtils::dump(1U, "NXDN Network Poll Sent", data, 15U); + + return m_socket.write(data, 15U, address, port); +} + +bool CNXDNNetwork::writeUnlink(const in_addr& address, unsigned int port) +{ + assert(port > 0U); + + unsigned char data[20U]; + + data[0U] = 'N'; + data[1U] = 'X'; + data[2U] = 'D'; + data[3U] = 'N'; + data[4U] = 'U'; + + for (unsigned int i = 0U; i < 10U; i++) + data[i + 5U] = m_callsign.at(i); + + if (m_debug) + CUtils::dump(1U, "NXDN Network Unlink Sent", data, 15U); + + return m_socket.write(data, 15U, address, port); +} + +unsigned int CNXDNNetwork::readData(unsigned char* data, unsigned int length, in_addr& address, unsigned int& port) +{ + assert(data != NULL); + assert(length > 0U); + + int len = m_socket.read(data, length, address, port); + if (len <= 0) + return 0U; + + if (m_debug) + CUtils::dump(1U, "NXDN Network Data Received", data, len); + + return len; +} + +void CNXDNNetwork::close() +{ + m_socket.close(); + + LogInfo("Closing NXDN network connection"); +} diff --git a/NXDNGateway/NXDNNetwork.h b/NXDNGateway/NXDNNetwork.h new file mode 100644 index 0000000..690b9ed --- /dev/null +++ b/NXDNGateway/NXDNNetwork.h @@ -0,0 +1,50 @@ +/* +* Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef NXDNNetwork_H +#define NXDNNetwork_H + +#include "UDPSocket.h" + +#include +#include + +class CNXDNNetwork { +public: + CNXDNNetwork(unsigned int port, const std::string& callsign, bool debug); + ~CNXDNNetwork(); + + bool open(); + + bool writeData(const unsigned char* data, unsigned int length, unsigned short srcId, unsigned short dstId, bool grp, const in_addr& address, unsigned int port); + + bool writePoll(const in_addr& address, unsigned int port); + + bool writeUnlink(const in_addr& address, unsigned int port); + + unsigned int readData(unsigned char* data, unsigned int length, in_addr& address, unsigned int& port); + + void close(); + +private: + std::string m_callsign; + CUDPSocket m_socket; + bool m_debug; +}; + +#endif diff --git a/NXDNGateway/Reflectors.cpp b/NXDNGateway/Reflectors.cpp new file mode 100644 index 0000000..dc8a1ed --- /dev/null +++ b/NXDNGateway/Reflectors.cpp @@ -0,0 +1,159 @@ +/* +* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "Reflectors.h" +#include "Log.h" + +#include +#include +#include +#include +#include +#include + +CReflectors::CReflectors(const std::string& hostsFile1, const std::string& hostsFile2, unsigned int reloadTime) : +m_hostsFile1(hostsFile1), +m_hostsFile2(hostsFile2), +m_parrotAddress(), +m_parrotPort(0U), +m_reflectors(), +m_timer(1000U, reloadTime * 60U) +{ + if (reloadTime > 0U) + m_timer.start(); +} + +CReflectors::~CReflectors() +{ + for (std::vector::iterator it = m_reflectors.begin(); it != m_reflectors.end(); ++it) + delete *it; + + m_reflectors.clear(); +} + +void CReflectors::setParrot(const std::string& address, unsigned int port) +{ + m_parrotAddress = address; + m_parrotPort = port; +} + +bool CReflectors::load() +{ + // Clear out the old reflector list + for (std::vector::iterator it = m_reflectors.begin(); it != m_reflectors.end(); ++it) + delete *it; + + m_reflectors.clear(); + + FILE* fp = ::fopen(m_hostsFile1.c_str(), "rt"); + if (fp != NULL) { + char buffer[100U]; + while (::fgets(buffer, 100U, fp) != NULL) { + if (buffer[0U] == '#') + continue; + + char* p1 = ::strtok(buffer, " \t\r\n"); + char* p2 = ::strtok(NULL, " \t\r\n"); + char* p3 = ::strtok(NULL, " \t\r\n"); + + if (p1 != NULL && p2 != NULL && p3 != NULL) { + std::string host = std::string(p2); + + in_addr address = CUDPSocket::lookup(host); + if (address.s_addr != INADDR_NONE) { + CNXDNReflector* refl = new CNXDNReflector; + refl->m_id = (unsigned int)::atoi(p1); + refl->m_address = address; + refl->m_port = (unsigned int)::atoi(p3); + + m_reflectors.push_back(refl); + } + } + } + + ::fclose(fp); + } + + fp = ::fopen(m_hostsFile2.c_str(), "rt"); + if (fp != NULL) { + char buffer[100U]; + while (::fgets(buffer, 100U, fp) != NULL) { + if (buffer[0U] == '#') + continue; + + char* p1 = ::strtok(buffer, " \t\r\n"); + char* p2 = ::strtok(NULL, " \t\r\n"); + char* p3 = ::strtok(NULL, " \t\r\n"); + + if (p1 != NULL && p2 != NULL && p3 != NULL) { + std::string host = std::string(p2); + + in_addr address = CUDPSocket::lookup(host); + if (address.s_addr != INADDR_NONE) { + CNXDNReflector* refl = new CNXDNReflector; + refl->m_id = (unsigned int)::atoi(p1); + refl->m_address = address; + refl->m_port = (unsigned int)::atoi(p3); + + m_reflectors.push_back(refl); + } + } + } + + ::fclose(fp); + } + + size_t size = m_reflectors.size(); + LogInfo("Loaded %u NXDN reflectors", size); + + // Add the Parrot entry + if (m_parrotPort > 0U) { + CNXDNReflector* refl = new CNXDNReflector; + refl->m_id = 10U; + refl->m_address = CUDPSocket::lookup(m_parrotAddress); + refl->m_port = m_parrotPort; + m_reflectors.push_back(refl); + LogInfo("Loaded NXDN parrot (TG%u)", refl->m_id); + } + + size = m_reflectors.size(); + if (size == 0U) + return false; + + return true; +} + +CNXDNReflector* CReflectors::find(unsigned int id) +{ + for (std::vector::iterator it = m_reflectors.begin(); it != m_reflectors.end(); ++it) { + if (id == (*it)->m_id) + return *it; + } + + return NULL; +} + +void CReflectors::clock(unsigned int ms) +{ + m_timer.clock(ms); + + if (m_timer.isRunning() && m_timer.hasExpired()) { + load(); + m_timer.start(); + } +} diff --git a/NXDNGateway/Reflectors.h b/NXDNGateway/Reflectors.h new file mode 100644 index 0000000..3687ff1 --- /dev/null +++ b/NXDNGateway/Reflectors.h @@ -0,0 +1,64 @@ +/* +* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(Reflectors_H) +#define Reflectors_H + +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CNXDNReflector { +public: + CNXDNReflector() : + m_id(), + m_address(), + m_port(0U) + { + } + + unsigned int m_id; + in_addr m_address; + unsigned int m_port; +}; + +class CReflectors { +public: + CReflectors(const std::string& hostsFile1, const std::string& hostsFile2, unsigned int reloadTime); + ~CReflectors(); + + void setParrot(const std::string& address, unsigned int port); + + bool load(); + + CNXDNReflector* find(unsigned int id); + + void clock(unsigned int ms); + +private: + std::string m_hostsFile1; + std::string m_hostsFile2; + std::string m_parrotAddress; + unsigned int m_parrotPort; + std::vector m_reflectors; + CTimer m_timer; +}; + +#endif diff --git a/NXDNGateway/RingBuffer.h b/NXDNGateway/RingBuffer.h new file mode 100644 index 0000000..f93e7a2 --- /dev/null +++ b/NXDNGateway/RingBuffer.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2006-2009,2012,2013,2015,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef 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/NXDNGateway/StopWatch.cpp b/NXDNGateway/StopWatch.cpp new file mode 100644 index 0000000..77d539d --- /dev/null +++ b/NXDNGateway/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/NXDNGateway/StopWatch.h b/NXDNGateway/StopWatch.h new file mode 100644 index 0000000..811047e --- /dev/null +++ b/NXDNGateway/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/NXDNGateway/Thread.cpp b/NXDNGateway/Thread.cpp new file mode 100644 index 0000000..b334436 --- /dev/null +++ b/NXDNGateway/Thread.cpp @@ -0,0 +1,101 @@ +/* + * 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 "Thread.h" + +#if defined(_WIN32) || defined(_WIN64) + +CThread::CThread() : +m_handle() +{ +} + +CThread::~CThread() +{ +} + +bool CThread::run() +{ + m_handle = ::CreateThread(NULL, 0, &helper, this, 0, NULL); + + return m_handle != NULL; +} + + +void CThread::wait() +{ + ::WaitForSingleObject(m_handle, INFINITE); + + ::CloseHandle(m_handle); +} + + +DWORD CThread::helper(LPVOID arg) +{ + CThread* p = (CThread*)arg; + + p->entry(); + + return 0UL; +} + +void CThread::sleep(unsigned int ms) +{ + ::Sleep(ms); +} + +#else + +#include + +CThread::CThread() : +m_thread() +{ +} + +CThread::~CThread() +{ +} + +bool CThread::run() +{ + return ::pthread_create(&m_thread, NULL, helper, this) == 0; +} + + +void CThread::wait() +{ + ::pthread_join(m_thread, NULL); +} + + +void* CThread::helper(void* arg) +{ + CThread* p = (CThread*)arg; + + p->entry(); + + return NULL; +} + +void CThread::sleep(unsigned int ms) +{ + ::usleep(ms * 1000); +} + +#endif diff --git a/NXDNGateway/Thread.h b/NXDNGateway/Thread.h new file mode 100644 index 0000000..352d938 --- /dev/null +++ b/NXDNGateway/Thread.h @@ -0,0 +1,56 @@ +/* + * 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(THREAD_H) +#define THREAD_H + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +class CThread +{ +public: + CThread(); + virtual ~CThread(); + + virtual bool run(); + + virtual void entry() = 0; + + virtual void wait(); + + static void sleep(unsigned int ms); + +private: +#if defined(_WIN32) || defined(_WIN64) + HANDLE m_handle; +#else + pthread_t m_thread; +#endif + +#if defined(_WIN32) || defined(_WIN64) + static DWORD __stdcall helper(LPVOID arg); +#else + static void* helper(void* arg); +#endif +}; + +#endif diff --git a/NXDNGateway/Timer.cpp b/NXDNGateway/Timer.cpp new file mode 100644 index 0000000..53956e4 --- /dev/null +++ b/NXDNGateway/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/NXDNGateway/Timer.h b/NXDNGateway/Timer.h new file mode 100644 index 0000000..87d68f5 --- /dev/null +++ b/NXDNGateway/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/NXDNGateway/UDPSocket.cpp b/NXDNGateway/UDPSocket.cpp new file mode 100644 index 0000000..396f1f7 --- /dev/null +++ b/NXDNGateway/UDPSocket.cpp @@ -0,0 +1,262 @@ +/* + * 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 "Log.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) + LogError("Error from WSAStartup"); +#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) + LogError("Error from WSAStartup"); +#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; + } + + LogError("Cannot find address for host %s", 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; + } + + LogError("Cannot find address for host %s", 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) + LogError("Cannot create the UDP socket, err: %lu", ::GetLastError()); +#else + LogError("Cannot create the UDP socket, err: %d", errno); +#endif + return false; + } + + if (m_port > 0U) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (!m_address.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) { + LogError("The local address is invalid - %s", m_address.c_str()); + return false; + } + } + + int reuse = 1; + if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError()); +#else + LogError("Cannot set the UDP socket option, err: %d", errno); +#endif + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Cannot bind the UDP address, err: %lu", ::GetLastError()); +#else + LogError("Cannot bind the UDP address, err: %d", 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) + LogError("Error returned from UDP select, err: %lu", ::GetLastError()); +#else + LogError("Error returned from UDP select, err: %d", 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) + LogError("Error returned from recvfrom, err: %lu", ::GetLastError()); +#else + LogError("Error returned from recvfrom, err: %d", 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) + LogError("Error returned from sendto, err: %lu", ::GetLastError()); +#else + LogError("Error returned from sendto, err: %d", 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/NXDNGateway/UDPSocket.h b/NXDNGateway/UDPSocket.h new file mode 100644 index 0000000..e0af272 --- /dev/null +++ b/NXDNGateway/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/NXDNGateway/Utils.cpp b/NXDNGateway/Utils.cpp new file mode 100644 index 0000000..49ded13 --- /dev/null +++ b/NXDNGateway/Utils.cpp @@ -0,0 +1,146 @@ +/* + * 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 "Log.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); + + ::Log(level, "%s", 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 += '*'; + + ::Log(level, "%04X: %s", 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/NXDNGateway/Utils.h b/NXDNGateway/Utils.h new file mode 100644 index 0000000..ade28c0 --- /dev/null +++ b/NXDNGateway/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/NXDNGateway/Version.h b/NXDNGateway/Version.h new file mode 100644 index 0000000..b6885be --- /dev/null +++ b/NXDNGateway/Version.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * + * This program is free software; 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(VERSION_H) +#define VERSION_H + +const char* VERSION = "20170719"; + +#endif diff --git a/NXDNGateway/Voice.cpp b/NXDNGateway/Voice.cpp new file mode 100644 index 0000000..86d3474 --- /dev/null +++ b/NXDNGateway/Voice.cpp @@ -0,0 +1,343 @@ +/* +* Copyright (C) 2017,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "NXDNCRC.h" +#include "Voice.h" +#include "Log.h" + +#include +#include +#include +#include + +#include + +const unsigned char HEADER[] = { 0x83U, 0x01U, 0x10U, 0x00U, 0x0FU, 0x01U, 0x00U, 0x20U }; +const unsigned char TRAILER[] = { 0x83U, 0x01U, 0x10U, 0x00U, 0x0FU, 0x08U, 0x00U, 0x20U }; + +const unsigned char SILENCE[] = {0xACU, 0xAAU, 0x40U, 0x20U, 0x00U, 0x44U, 0x40U, 0x80U, 0x80U}; + +const unsigned char NXDN_FRAME_LENGTH = 33U; + +const unsigned int NXDN_FRAME_TIME = 80U; + +const unsigned int SILENCE_LENGTH = 9U; +const unsigned int AMBE_LENGTH = 9U; + +const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + +CVoice::CVoice(const std::string& directory, const std::string& language, unsigned int srcId) : +m_indxFile(), +m_ambeFile(), +m_srcId(srcId), +m_status(VS_NONE), +m_timer(1000U, 1U), +m_stopWatch(), +m_sent(0U), +m_ambe(NULL), +m_positions(), +m_voiceData(NULL), +m_voiceLength(0U) +{ +#if defined(_WIN32) || defined(_WIN64) + m_indxFile = directory + "\\" + language + ".indx"; + m_ambeFile = directory + "\\" + language + ".ambe"; +#else + m_indxFile = directory + "/" + language + ".indx"; + m_ambeFile = directory + "/" + language + ".ambe"; +#endif + + // Approximately 10 seconds worth + m_voiceData = new unsigned char[120U * NXDN_FRAME_LENGTH]; +} + +CVoice::~CVoice() +{ + for (std::unordered_map::iterator it = m_positions.begin(); it != m_positions.end(); ++it) + delete it->second; + + m_positions.clear(); + + delete[] m_ambe; + delete[] m_voiceData; +} + +bool CVoice::open() +{ + FILE* fpindx = ::fopen(m_indxFile.c_str(), "rt"); + if (fpindx == NULL) { + LogError("Unable to open the index file - %s", m_indxFile.c_str()); + return false; + } + + struct stat statStruct; + int ret = ::stat(m_ambeFile.c_str(), &statStruct); + if (ret != 0) { + LogError("Unable to stat the AMBE file - %s", m_ambeFile.c_str()); + ::fclose(fpindx); + return false; + } + + FILE* fpambe = ::fopen(m_ambeFile.c_str(), "rb"); + if (fpambe == NULL) { + LogError("Unable to open the AMBE file - %s", m_ambeFile.c_str()); + ::fclose(fpindx); + return false; + } + + m_ambe = new unsigned char[statStruct.st_size]; + + size_t sizeRead = ::fread(m_ambe, 1U, statStruct.st_size, fpambe); + if (sizeRead != 0U) { + char buffer[80U]; + while (::fgets(buffer, 80, fpindx) != NULL) { + char* p1 = ::strtok(buffer, "\t\r\n"); + char* p2 = ::strtok(NULL, "\t\r\n"); + char* p3 = ::strtok(NULL, "\t\r\n"); + + if (p1 != NULL && p2 != NULL && p3 != NULL) { + std::string symbol = std::string(p1); + unsigned int start = ::atoi(p2) * AMBE_LENGTH; + unsigned int length = ::atoi(p3) * AMBE_LENGTH; + + CPositions* pos = new CPositions; + pos->m_start = start; + pos->m_length = length; + + m_positions[symbol] = pos; + } + } + } + + ::fclose(fpindx); + ::fclose(fpambe); + + return true; +} + +void CVoice::linkedTo(unsigned int tg) +{ + char letters[10U]; + ::sprintf(letters, "%u", tg); + + std::vector words; + if (m_positions.count("linkedto") == 0U) { + words.push_back("linked"); + words.push_back("2"); + } else { + words.push_back("linkedto"); + } + + for (unsigned int i = 0U; letters[i] != 0x00U; i++) + words.push_back(std::string(1U, letters[i])); + + createVoice(tg, words); +} + +void CVoice::unlinked() +{ + std::vector words; + words.push_back("notlinked"); + + createVoice(9999U, words); +} + +void CVoice::createVoice(unsigned int tg, const std::vector& words) +{ + unsigned int ambeLength = 0U; + for (std::vector::const_iterator it = words.begin(); it != words.end(); ++it) { + if (m_positions.count(*it) > 0U) { + CPositions* position = m_positions.at(*it); + ambeLength += position->m_length; + } else { + LogWarning("Unable to find character/phrase \"%s\" in the index", (*it).c_str()); + } + } + + // Ensure that the AMBE is an integer number of DMR frames + if ((ambeLength % (4U * AMBE_LENGTH)) != 0U) { + unsigned int frames = ambeLength / (4U * AMBE_LENGTH); + frames++; + ambeLength = frames * (4U * AMBE_LENGTH); + } + + // Add space for silence before and after the voice + ambeLength += SILENCE_LENGTH * AMBE_LENGTH; + ambeLength += SILENCE_LENGTH * AMBE_LENGTH; + + unsigned char* ambeData = new unsigned char[ambeLength]; + + // Fill the AMBE data with silence + for (unsigned int i = 0U; i < ambeLength; i += AMBE_LENGTH) + ::memcpy(ambeData + i, SILENCE, AMBE_LENGTH); + + // Put offset in for silence at the beginning + unsigned int pos = SILENCE_LENGTH * AMBE_LENGTH; + for (std::vector::const_iterator it = words.begin(); it != words.end(); ++it) { + if (m_positions.count(*it) > 0U) { + CPositions* position = m_positions.at(*it); + unsigned int start = position->m_start; + unsigned int length = position->m_length; + ::memcpy(ambeData + pos, m_ambe + start, length); + pos += length; + } + } + + m_voiceLength = 0U; + + createHeader(true, tg); + + unsigned char sacch[12U]; + ::memset(sacch, 0x00U, 12U); + + sacch[2U] = 0x20U; + + sacch[3U] = (m_srcId >> 8) & 0xFFU; + sacch[4U] = (m_srcId >> 0) & 0xFFU; + + sacch[5U] = (tg >> 8) & 0xFFU; + sacch[6U] = (tg >> 0) & 0xFFU; + + unsigned int n = 0U; + for (unsigned int i = 0U; i < ambeLength; i += (4U * AMBE_LENGTH)) { + unsigned char* p = ambeData + i; + + unsigned char buffer[NXDN_FRAME_LENGTH]; + ::memset(buffer, 0x00U, NXDN_FRAME_LENGTH); + + buffer[0U] = 0xAEU; + + switch (n) { + case 0U: buffer[1U] = 0xC1U; break; + case 1U: buffer[1U] = 0x81U; break; + case 2U: buffer[1U] = 0x41U; break; + default: buffer[1U] = 0x01U; break; + } + + for (unsigned int j = 0U; j < 18U; j++) { + bool b = READ_BIT1(sacch, j + n * 18U); + WRITE_BIT1(buffer, j + 16U, b); + } + + CNXDNCRC::encodeCRC6(buffer + 1U, 26U); + + ::memcpy(buffer + 5U + 0U, p + 0U, 13U); + ::memcpy(buffer + 5U + 14U, p + 13U, 13U); + + n = (n + 1U) % 4U; + + ::memcpy(m_voiceData + m_voiceLength, buffer, NXDN_FRAME_LENGTH); + m_voiceLength += NXDN_FRAME_LENGTH; + } + + createTrailer(true, tg); + + delete[] ambeData; + + m_status = VS_WAITING; + m_timer.start(); +} + +bool CVoice::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_status != VS_SENDING) + return false; + + unsigned int count = m_stopWatch.elapsed() / NXDN_FRAME_TIME; + + if (m_sent < count) { + unsigned int offset = m_sent * NXDN_FRAME_LENGTH; + ::memcpy(data, m_voiceData + offset, NXDN_FRAME_LENGTH); + m_sent++; + + if (offset >= m_voiceLength) { + m_timer.stop(); + m_voiceLength = 0U; + m_status = VS_NONE; + } + + return true; + } + + return false; +} + +void CVoice::clock(unsigned int ms) +{ + m_timer.clock(ms); + if (m_timer.isRunning() && m_timer.hasExpired()) { + if (m_status == VS_WAITING) { + m_stopWatch.start(); + m_status = VS_SENDING; + m_sent = 0U; + } + } +} + +void CVoice::createHeader(bool grp, unsigned int dstId) +{ + unsigned char buffer[NXDN_FRAME_LENGTH]; + + ::memset(buffer, 0x00U, NXDN_FRAME_LENGTH); + ::memcpy(buffer, HEADER, 8U); + + if (grp) + buffer[7U] = 0x20U; + + buffer[8U] = (m_srcId >> 8) & 0xFFU; + buffer[9U] = (m_srcId >> 0) & 0xFFU; + + buffer[10U] = (dstId >> 8) & 0xFFU; + buffer[11U] = (dstId >> 0) & 0xFFU; + + CNXDNCRC::encodeCRC12(buffer + 5U, 80U); + + ::memcpy(buffer + 19U, buffer + 5U, 12U); + + ::memcpy(m_voiceData + m_voiceLength, buffer, NXDN_FRAME_LENGTH); + m_voiceLength += NXDN_FRAME_LENGTH; +} + +void CVoice::createTrailer(bool grp, unsigned int dstId) +{ + unsigned char buffer[NXDN_FRAME_LENGTH]; + + ::memset(buffer, 0x00U, NXDN_FRAME_LENGTH); + ::memcpy(buffer, TRAILER, 8U); + + if (grp) + buffer[7U] = 0x20U; + + buffer[8U] = (m_srcId >> 8) & 0xFFU; + buffer[9U] = (m_srcId >> 0) & 0xFFU; + + buffer[10U] = (dstId >> 8) & 0xFFU; + buffer[11U] = (dstId >> 0) & 0xFFU; + + CNXDNCRC::encodeCRC12(buffer + 5U, 80U); + + ::memcpy(buffer + 19U, buffer + 5U, 12U); + + ::memcpy(m_voiceData + m_voiceLength, buffer, NXDN_FRAME_LENGTH); + m_voiceLength += NXDN_FRAME_LENGTH; +} diff --git a/NXDNGateway/Voice.h b/NXDNGateway/Voice.h new file mode 100644 index 0000000..78f62a3 --- /dev/null +++ b/NXDNGateway/Voice.h @@ -0,0 +1,72 @@ +/* +* Copyright (C) 2017 by Jonathan Naylor G4KLX +* +* This program is free software; 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(Voice_H) +#define Voice_H + +#include "StopWatch.h" +#include "Timer.h" + +#include +#include +#include + +enum VOICE_STATUS { + VS_NONE, + VS_WAITING, + VS_SENDING +}; + +struct CPositions { + unsigned int m_start; + unsigned int m_length; +}; + +class CVoice { +public: + CVoice(const std::string& directory, const std::string& language, unsigned int srcId); + ~CVoice(); + + bool open(); + + void linkedTo(unsigned int tg); + void unlinked(); + + bool read(unsigned char* data); + + void clock(unsigned int ms); + +private: + std::string m_indxFile; + std::string m_ambeFile; + unsigned int m_srcId; + VOICE_STATUS m_status; + CTimer m_timer; + CStopWatch m_stopWatch; + unsigned int m_sent; + unsigned char* m_ambe; + unsigned char* m_voiceData; + unsigned int m_voiceLength; + std::unordered_map m_positions; + + void createHeader(bool grp, unsigned int dstId); + void createTrailer(bool grp, unsigned int dstId); + void createVoice(unsigned int tg, const std::vector& words); +}; + +#endif diff --git a/NXDNParrot/Makefile b/NXDNParrot/Makefile new file mode 100644 index 0000000..a8403e6 --- /dev/null +++ b/NXDNParrot/Makefile @@ -0,0 +1,18 @@ +CC = gcc +CXX = g++ +CFLAGS = -g -O3 -Wall -std=c++0x -pthread +LIBS = -lpthread +LDFLAGS = -g + +OBJECTS = Network.o NXDNParrot.o Parrot.o StopWatch.o Thread.o Timer.o UDPSocket.o + +all: NXDNParrot + +NXDNParrot: $(OBJECTS) + $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o NXDNParrot + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + +clean: + $(RM) NXDNParrot *.o *.d *.bak *~ diff --git a/NXDNParrot/NXDNParrot.cpp b/NXDNParrot/NXDNParrot.cpp new file mode 100644 index 0000000..ee243cd --- /dev/null +++ b/NXDNParrot/NXDNParrot.cpp @@ -0,0 +1,138 @@ +/* +* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "StopWatch.h" +#include "NXDNParrot.h" +#include "Parrot.h" +#include "Network.h" +#include "Version.h" +#include "Thread.h" +#include "Timer.h" + +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc == 1) { + ::fprintf(stderr, "Usage: NXDNParrot \n"); + return 1; + } + + unsigned int port = ::atoi(argv[1U]); + if (port == 0U) { + ::fprintf(stderr, "NXDNParrot: invalid port number - %s\n", argv[1U]); + return 1; + } + + CNXDNParrot parrot(port); + parrot.run(); + + return 0; +} + +CNXDNParrot::CNXDNParrot(unsigned int port) : +m_port(port) +{ +} + +CNXDNParrot::~CNXDNParrot() +{ +} + +void CNXDNParrot::run() +{ + CParrot parrot(180U); + CNetwork network(m_port); + + 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; + + ::fprintf(stdout, "Starting NXDNParrot-%s\n", VERSION); + + for (;;) { + unsigned char buffer[200U]; + + unsigned int len = network.read(buffer); + if (len > 0U) { + parrot.write(buffer, len); + watchdogTimer.start(); + + if ((buffer[9U] & 0x04U) == 0x04U) { + ::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 80ms + unsigned int wanted = playoutTimer.elapsed() / 80U; + while (count < wanted) { + len = parrot.read(buffer); + if (len > 0U) { + network.write(buffer, len); + count++; + } else { + parrot.clear(); + network.end(); + 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()) { + turnaroundTimer.start(); + watchdogTimer.stop(); + parrot.end(); + } + + if (ms < 5U) + CThread::sleep(5U); + } + + network.close(); +} diff --git a/NXDNParrot/NXDNParrot.h b/NXDNParrot/NXDNParrot.h new file mode 100644 index 0000000..1d3c190 --- /dev/null +++ b/NXDNParrot/NXDNParrot.h @@ -0,0 +1,34 @@ +/* +* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(NXDNParrot_H) +#define NXDNParrot_H + +class CNXDNParrot +{ +public: + CNXDNParrot(unsigned int port); + ~CNXDNParrot(); + + void run(); + +private: + unsigned int m_port; +}; + +#endif diff --git a/NXDNParrot/NXDNParrot.vcxproj b/NXDNParrot/NXDNParrot.vcxproj new file mode 100644 index 0000000..c1d6ca3 --- /dev/null +++ b/NXDNParrot/NXDNParrot.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {2AE94EAA-FD57-45C9-8555-6425CFA777A3} + Win32Proj + NXDNParrot + 10.0.15063.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NXDNParrot/NXDNParrot.vcxproj.filters b/NXDNParrot/NXDNParrot.vcxproj.filters new file mode 100644 index 0000000..aaaed0a --- /dev/null +++ b/NXDNParrot/NXDNParrot.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/NXDNParrot/Network.cpp b/NXDNParrot/Network.cpp new file mode 100644 index 0000000..5044a91 --- /dev/null +++ b/NXDNParrot/Network.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Network.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CNetwork::CNetwork(unsigned int port) : +m_socket(port), +m_address(), +m_port(0U), +m_buffer(1000U, "NXDN Network") +{ +} + +CNetwork::~CNetwork() +{ +} + +bool CNetwork::open() +{ + ::fprintf(stdout, "Opening NXDN network connection\n"); + + return m_socket.open(); +} + +bool CNetwork::write(const unsigned char* data, unsigned int length) +{ + if (m_port == 0U) + return true; + + assert(data != NULL); + + return m_socket.write(data, length, m_address, m_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; + + m_address.s_addr = address.s_addr; + m_port = port; + + if (::memcmp(buffer, "NXDNP", 5U) == 0) { // A poll + write(buffer, length); + } else if (::memcmp(buffer, "NXDND", 5U) == 0) { + unsigned char l = length; + m_buffer.addData(&l, 1U); + m_buffer.addData(buffer, length); + } +} + +unsigned int CNetwork::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_buffer.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_buffer.getData(&len, 1U); + + m_buffer.getData(data, len); + + return len; +} + +void CNetwork::end() +{ + m_port = 0U; +} + +void CNetwork::close() +{ + m_socket.close(); + + ::fprintf(stdout, "Closing NXDN network connection\n"); +} diff --git a/NXDNParrot/Network.h b/NXDNParrot/Network.h new file mode 100644 index 0000000..135c578 --- /dev/null +++ b/NXDNParrot/Network.h @@ -0,0 +1,52 @@ +/* + * 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 "RingBuffer.h" +#include "UDPSocket.h" + +#include +#include + +class CNetwork { +public: + CNetwork(unsigned int port); + ~CNetwork(); + + bool open(); + + bool write(const unsigned char* data, unsigned int length); + + unsigned int read(unsigned char* data); + + void end(); + + void close(); + + void clock(unsigned int ms); + +private: + CUDPSocket m_socket; + in_addr m_address; + unsigned int m_port; + CRingBuffer m_buffer; +}; + +#endif diff --git a/NXDNParrot/Parrot.cpp b/NXDNParrot/Parrot.cpp new file mode 100644 index 0000000..1e8344f --- /dev/null +++ b/NXDNParrot/Parrot.cpp @@ -0,0 +1,81 @@ +/* +* 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 +#include +#include + +CParrot::CParrot(unsigned int timeout) : +m_data(NULL), +m_length(timeout * 1000U + 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, unsigned int length) +{ + assert(data != NULL); + + if ((m_length - m_used) < (length + 2U)) + return false; + + m_data[m_used] = length; + ::memcpy(m_data + m_used + 1U, data, length); + m_used += length + 1U; + + return true; +} + +void CParrot::end() +{ + m_ptr = 0U; +} + +void CParrot::clear() +{ + m_used = 0U; + m_ptr = 0U; +} + +unsigned int CParrot::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_used == 0U) + return 0U; + + unsigned int length = m_data[m_ptr]; + ::memcpy(data, m_data + m_ptr + 1U, length); + m_ptr += length + 1U; + + if (m_ptr >= m_used) + m_used = 0U; + + return length; +} diff --git a/NXDNParrot/Parrot.h b/NXDNParrot/Parrot.h new file mode 100644 index 0000000..3e4bbbc --- /dev/null +++ b/NXDNParrot/Parrot.h @@ -0,0 +1,43 @@ +/* +* 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 length); + + unsigned int read(unsigned char* data); + + void end(); + + void clear(); + +private: + unsigned char* m_data; + unsigned int m_length; + unsigned int m_used; + unsigned int m_ptr; +}; + +#endif diff --git a/NXDNParrot/RingBuffer.h b/NXDNParrot/RingBuffer.h new file mode 100644 index 0000000..f93e7a2 --- /dev/null +++ b/NXDNParrot/RingBuffer.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2006-2009,2012,2013,2015,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef 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/NXDNParrot/StopWatch.cpp b/NXDNParrot/StopWatch.cpp new file mode 100644 index 0000000..77d539d --- /dev/null +++ b/NXDNParrot/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/NXDNParrot/StopWatch.h b/NXDNParrot/StopWatch.h new file mode 100644 index 0000000..811047e --- /dev/null +++ b/NXDNParrot/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/NXDNParrot/Thread.cpp b/NXDNParrot/Thread.cpp new file mode 100644 index 0000000..b334436 --- /dev/null +++ b/NXDNParrot/Thread.cpp @@ -0,0 +1,101 @@ +/* + * 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 "Thread.h" + +#if defined(_WIN32) || defined(_WIN64) + +CThread::CThread() : +m_handle() +{ +} + +CThread::~CThread() +{ +} + +bool CThread::run() +{ + m_handle = ::CreateThread(NULL, 0, &helper, this, 0, NULL); + + return m_handle != NULL; +} + + +void CThread::wait() +{ + ::WaitForSingleObject(m_handle, INFINITE); + + ::CloseHandle(m_handle); +} + + +DWORD CThread::helper(LPVOID arg) +{ + CThread* p = (CThread*)arg; + + p->entry(); + + return 0UL; +} + +void CThread::sleep(unsigned int ms) +{ + ::Sleep(ms); +} + +#else + +#include + +CThread::CThread() : +m_thread() +{ +} + +CThread::~CThread() +{ +} + +bool CThread::run() +{ + return ::pthread_create(&m_thread, NULL, helper, this) == 0; +} + + +void CThread::wait() +{ + ::pthread_join(m_thread, NULL); +} + + +void* CThread::helper(void* arg) +{ + CThread* p = (CThread*)arg; + + p->entry(); + + return NULL; +} + +void CThread::sleep(unsigned int ms) +{ + ::usleep(ms * 1000); +} + +#endif diff --git a/NXDNParrot/Thread.h b/NXDNParrot/Thread.h new file mode 100644 index 0000000..352d938 --- /dev/null +++ b/NXDNParrot/Thread.h @@ -0,0 +1,56 @@ +/* + * 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(THREAD_H) +#define THREAD_H + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +class CThread +{ +public: + CThread(); + virtual ~CThread(); + + virtual bool run(); + + virtual void entry() = 0; + + virtual void wait(); + + static void sleep(unsigned int ms); + +private: +#if defined(_WIN32) || defined(_WIN64) + HANDLE m_handle; +#else + pthread_t m_thread; +#endif + +#if defined(_WIN32) || defined(_WIN64) + static DWORD __stdcall helper(LPVOID arg); +#else + static void* helper(void* arg); +#endif +}; + +#endif diff --git a/NXDNParrot/Timer.cpp b/NXDNParrot/Timer.cpp new file mode 100644 index 0000000..53956e4 --- /dev/null +++ b/NXDNParrot/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/NXDNParrot/Timer.h b/NXDNParrot/Timer.h new file mode 100644 index 0000000..87d68f5 --- /dev/null +++ b/NXDNParrot/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/NXDNParrot/UDPSocket.cpp b/NXDNParrot/UDPSocket.cpp new file mode 100644 index 0000000..4a8c42d --- /dev/null +++ b/NXDNParrot/UDPSocket.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2006-2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "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; + } + + ::fprintf(stdout, "Opening UDP port on %u\n", m_port); + } + + 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/NXDNParrot/UDPSocket.h b/NXDNParrot/UDPSocket.h new file mode 100644 index 0000000..e0af272 --- /dev/null +++ b/NXDNParrot/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/NXDNParrot/Version.h b/NXDNParrot/Version.h new file mode 100644 index 0000000..38fc4df --- /dev/null +++ b/NXDNParrot/Version.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(VERSION_H) +#define VERSION_H + +const char* VERSION = "20180206"; + +#endif diff --git a/NXDNReflector/Conf.cpp b/NXDNReflector/Conf.cpp new file mode 100644 index 0000000..ed925e0 --- /dev/null +++ b/NXDNReflector/Conf.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Conf.h" +#include "Log.h" + +#include +#include +#include +#include + +const int BUFFER_SIZE = 500; + +enum SECTION { + SECTION_NONE, + SECTION_GENERAL, + SECTION_ID_LOOKUP, + SECTION_LOG, + SECTION_NETWORK, + SECTION_NXCORE +}; + +CConf::CConf(const std::string& file) : +m_file(file), +m_daemon(false), +m_lookupName(), +m_lookupTime(0U), +m_logDisplayLevel(0U), +m_logFileLevel(0U), +m_logFilePath(), +m_logFileRoot(), +m_networkPort(0U), +m_networkDebug(false), +m_nxCoreEnabled(false), +m_nxCoreAddress(), +m_nxCoreDebug(false) +{ +} + +CConf::~CConf() +{ +} + +bool CConf::read() +{ + FILE* fp = ::fopen(m_file.c_str(), "rt"); + if (fp == NULL) { + ::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str()); + return false; + } + + SECTION section = SECTION_NONE; + + char buffer[BUFFER_SIZE]; + while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) { + if (buffer[0U] == '#') + continue; + + if (buffer[0U] == '[') { + if (::strncmp(buffer, "[General]", 9U) == 0) + section = SECTION_GENERAL; + else if (::strncmp(buffer, "[Id Lookup]", 11U) == 0) + section = SECTION_ID_LOOKUP; + else if (::strncmp(buffer, "[Log]", 5U) == 0) + section = SECTION_LOG; + else if (::strncmp(buffer, "[Network]", 9U) == 0) + section = SECTION_NETWORK; + else if (::strncmp(buffer, "[NXCore]", 8U) == 0) + section = SECTION_NXCORE; + else + section = SECTION_NONE; + + continue; + } + + char* key = ::strtok(buffer, " \t=\r\n"); + if (key == NULL) + continue; + + char* value = ::strtok(NULL, "\r\n"); + if (section == SECTION_GENERAL) { + if (::strcmp(key, "Daemon") == 0) + m_daemon = ::atoi(value) == 1; + } else if (section == SECTION_ID_LOOKUP) { + if (::strcmp(key, "Name") == 0) + m_lookupName = value; + else if (::strcmp(key, "Time") == 0) + m_lookupTime = (unsigned int)::atoi(value); + } else if (section == SECTION_LOG) { + if (::strcmp(key, "FilePath") == 0) + m_logFilePath = value; + else if (::strcmp(key, "FileRoot") == 0) + m_logFileRoot = value; + else if (::strcmp(key, "FileLevel") == 0) + m_logFileLevel = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayLevel") == 0) + m_logDisplayLevel = (unsigned int)::atoi(value); + } else if (section == SECTION_NETWORK) { + if (::strcmp(key, "Port") == 0) + m_networkPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_networkDebug = ::atoi(value) == 1; + } else if (section == SECTION_NXCORE) { + if (::strcmp(key, "Enabled") == 0) + m_nxCoreEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Address") == 0) + m_nxCoreAddress = value; + else if (::strcmp(key, "Debug") == 0) + m_nxCoreDebug = ::atoi(value) == 1; + } + } + + ::fclose(fp); + + return true; +} + +bool CConf::getDaemon() const +{ + return m_daemon; +} + +std::string CConf::getLookupName() const +{ + return m_lookupName; +} + +unsigned int CConf::getLookupTime() const +{ + return m_lookupTime; +} + +unsigned int CConf::getLogDisplayLevel() const +{ + return m_logDisplayLevel; +} + +unsigned int CConf::getLogFileLevel() const +{ + return m_logFileLevel; +} + +std::string CConf::getLogFilePath() const +{ + return m_logFilePath; +} + +std::string CConf::getLogFileRoot() const +{ + return m_logFileRoot; +} + +unsigned int CConf::getNetworkPort() const +{ + return m_networkPort; +} + +bool CConf::getNetworkDebug() const +{ + return m_networkDebug; +} + +bool CConf::getNXCoreEnabled() const +{ + return m_nxCoreEnabled; +} + +std::string CConf::getNXCoreAddress() const +{ + return m_nxCoreAddress; +} + +bool CConf::getNXCoreDebug() const +{ + return m_nxCoreDebug; +} diff --git a/NXDNReflector/Conf.h b/NXDNReflector/Conf.h new file mode 100644 index 0000000..c2d48ff --- /dev/null +++ b/NXDNReflector/Conf.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(CONF_H) +#define CONF_H + +#include +#include + +class CConf +{ +public: + CConf(const std::string& file); + ~CConf(); + + bool read(); + + // The General section + bool getDaemon() const; + + // The Id Lookup section + std::string getLookupName() const; + unsigned int getLookupTime() const; + + // The Log section + unsigned int getLogDisplayLevel() const; + unsigned int getLogFileLevel() const; + std::string getLogFilePath() const; + std::string getLogFileRoot() const; + + // The Network section + unsigned int getNetworkPort() const; + bool getNetworkDebug() const; + + // The NXCore section + bool getNXCoreEnabled() const; + std::string getNXCoreAddress() const; + bool getNXCoreDebug() const; + +private: + std::string m_file; + bool m_daemon; + + std::string m_lookupName; + unsigned int m_lookupTime; + + unsigned int m_logDisplayLevel; + unsigned int m_logFileLevel; + std::string m_logFilePath; + std::string m_logFileRoot; + + unsigned int m_networkPort; + bool m_networkDebug; + + bool m_nxCoreEnabled; + std::string m_nxCoreAddress; + bool m_nxCoreDebug; +}; + +#endif diff --git a/NXDNReflector/Log.cpp b/NXDNReflector/Log.cpp new file mode 100644 index 0000000..fc37ebf --- /dev/null +++ b/NXDNReflector/Log.cpp @@ -0,0 +1,136 @@ +/* + * 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 "Log.h" + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + +static unsigned int m_fileLevel = 2U; +static std::string m_filePath; +static std::string m_fileRoot; + +static FILE* m_fpLog = NULL; + +static unsigned int m_displayLevel = 2U; + +static struct tm m_tm; + +static char LEVELS[] = " DMIWEF"; + +static bool LogOpen() +{ + if (m_fileLevel == 0U) + return true; + + time_t now; + ::time(&now); + + struct tm* tm = ::gmtime(&now); + + if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) { + if (m_fpLog != NULL) + return true; + } else { + if (m_fpLog != NULL) + ::fclose(m_fpLog); + } + + char filename[100U]; +#if defined(_WIN32) || defined(_WIN64) + ::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#else + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#endif + + m_fpLog = ::fopen(filename, "a+t"); + m_tm = *tm; + + return m_fpLog != NULL; +} + +bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel) +{ + m_filePath = filePath; + m_fileRoot = fileRoot; + m_fileLevel = fileLevel; + m_displayLevel = displayLevel; + return ::LogOpen(); +} + +void LogFinalise() +{ + if (m_fpLog != NULL) + ::fclose(m_fpLog); +} + +void Log(unsigned int level, const char* fmt, ...) +{ + assert(fmt != NULL); + + char buffer[300U]; +#if defined(_WIN32) || defined(_WIN64) + SYSTEMTIME st; + ::GetSystemTime(&st); + + ::sprintf(buffer, "%c: %04u-%02u-%02u %02u:%02u:%02u.%03u ", LEVELS[level], st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); +#else + struct timeval now; + ::gettimeofday(&now, NULL); + + struct tm* tm = ::gmtime(&now.tv_sec); + + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000U); +#endif + + va_list vl; + va_start(vl, fmt); + + ::vsprintf(buffer + ::strlen(buffer), fmt, vl); + + va_end(vl); + + if (level >= m_fileLevel && m_fileLevel != 0U) { + bool ret = ::LogOpen(); + if (!ret) + return; + + ::fprintf(m_fpLog, "%s\n", buffer); + ::fflush(m_fpLog); + } + + if (level >= m_displayLevel && m_displayLevel != 0U) { + ::fprintf(stdout, "%s\n", buffer); + ::fflush(stdout); + } + + if (level == 6U) { // Fatal + ::fclose(m_fpLog); + exit(1); + } +} diff --git a/NXDNReflector/Log.h b/NXDNReflector/Log.h new file mode 100644 index 0000000..d671ef9 --- /dev/null +++ b/NXDNReflector/Log.h @@ -0,0 +1,36 @@ +/* + * 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(LOG_H) +#define LOG_H + +#include + +#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__) +#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__) +#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__) +#define LogWarning(fmt, ...) Log(4U, fmt, ##__VA_ARGS__) +#define LogError(fmt, ...) Log(5U, fmt, ##__VA_ARGS__) +#define LogFatal(fmt, ...) Log(6U, fmt, ##__VA_ARGS__) + +extern void Log(unsigned int level, const char* fmt, ...); + +extern bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel); +extern void LogFinalise(); + +#endif diff --git a/NXDNReflector/Makefile b/NXDNReflector/Makefile new file mode 100644 index 0000000..8cd4048 --- /dev/null +++ b/NXDNReflector/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CXX = g++ +CFLAGS = -g -O3 -Wall -std=c++0x -pthread +LIBS = -lpthread +LDFLAGS = -g + +OBJECTS = Conf.o Log.o Mutex.o NXCoreNetwork.o NXDNLookup.o NXDNNetwork.o NXDNReflector.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o + +all: NXDNReflector + +NXDNReflector: $(OBJECTS) + $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o NXDNReflector + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + +clean: + $(RM) NXDNReflector *.o *.d *.bak *~ + \ No newline at end of file diff --git a/NXDNReflector/Mutex.cpp b/NXDNReflector/Mutex.cpp new file mode 100644 index 0000000..837e340 --- /dev/null +++ b/NXDNReflector/Mutex.cpp @@ -0,0 +1,65 @@ +/* + * 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 "Mutex.h" + +#if defined(_WIN32) || defined(_WIN64) + +CMutex::CMutex() : +m_handle() +{ + m_handle = ::CreateMutex(NULL, FALSE, NULL); +} + +CMutex::~CMutex() +{ + ::CloseHandle(m_handle); +} + +void CMutex::lock() +{ + ::WaitForSingleObject(m_handle, INFINITE); +} + +void CMutex::unlock() +{ + ::ReleaseMutex(m_handle); +} + +#else + +CMutex::CMutex() : +m_mutex(PTHREAD_MUTEX_INITIALIZER) +{ +} + +CMutex::~CMutex() +{ +} + +void CMutex::lock() +{ + ::pthread_mutex_lock(&m_mutex); +} + +void CMutex::unlock() +{ + ::pthread_mutex_unlock(&m_mutex); +} + +#endif diff --git a/NXDNReflector/Mutex.h b/NXDNReflector/Mutex.h new file mode 100644 index 0000000..7ce9f85 --- /dev/null +++ b/NXDNReflector/Mutex.h @@ -0,0 +1,45 @@ +/* + * 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(MUTEX_H) +#define MUTEX_H + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +class CMutex +{ +public: + CMutex(); + ~CMutex(); + + void lock(); + void unlock(); + +private: +#if defined(_WIN32) || defined(_WIN64) + HANDLE m_handle; +#else + pthread_mutex_t m_mutex; +#endif +}; + +#endif diff --git a/NXDNReflector/NXCoreNetwork.cpp b/NXDNReflector/NXCoreNetwork.cpp new file mode 100644 index 0000000..6fba2ed --- /dev/null +++ b/NXDNReflector/NXCoreNetwork.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "NXCoreNetwork.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +const unsigned int NXCORE_PORT = 41300U; + +CNXCoreNetwork::CNXCoreNetwork(const std::string& address, bool debug) : +m_socket("", NXCORE_PORT), +m_address(), +m_debug(debug), +m_buffer(1000U, "NXCore Network") +{ + assert(!address.empty()); + + m_address = CUDPSocket::lookup(address); +} + +CNXCoreNetwork::~CNXCoreNetwork() +{ +} + +bool CNXCoreNetwork::open() +{ + LogMessage("Opening NXCore network connection"); + + if (m_address.s_addr == INADDR_NONE) + return false; + + return m_socket.open(); +} + +bool CNXCoreNetwork::write(const unsigned char* data, unsigned int len) +{ + assert(data != NULL); + + unsigned char buffer[110U]; + ::memset(buffer, 0x00U, 110U); + + buffer[0U] = 'I'; + buffer[1U] = 'C'; + buffer[2U] = 'O'; + buffer[3U] = 'M'; + buffer[4U] = 0x01U; + buffer[5U] = 0x01U; + buffer[6U] = 0x08U; + buffer[7U] = 0xE0U; + + buffer[37U] = 0x23U; + buffer[38U] = (data[9U] & 0x0CU) != 0x00U ? 0x1CU : 0x10U; + buffer[39U] = 0x21U; + + ::memcpy(buffer + 40U, data + 10U, 33U); + + if (m_debug) + CUtils::dump(1U, "NXCore Network Data Sent", buffer, 102U); + + return m_socket.write(buffer, 102U, m_address, NXCORE_PORT); +} + +void CNXCoreNetwork::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; + + // Check if the data is for us + if (m_address.s_addr != address.s_addr || port != NXCORE_PORT) { + LogMessage("NXCore packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, NXCORE_PORT, port); + return; + } + + // Invalid packet type? + if (::memcmp(buffer, "ICOM", 4U) != 0) + return; + + if (length != 102) + return; + + if (m_debug) + CUtils::dump(1U, "NXCore Network Data Received", buffer, length); + + m_buffer.addData(buffer + 40U, 33U); +} + +bool CNXCoreNetwork::read(unsigned char* data, unsigned int len) +{ + assert(data != NULL); + + if (m_buffer.isEmpty()) + return false; + + m_buffer.getData(data, 33U); + + return true; +} + +void CNXCoreNetwork::close() +{ + m_socket.close(); + + LogMessage("Closing NXCore network connection"); +} diff --git a/NXDNReflector/NXCoreNetwork.h b/NXDNReflector/NXCoreNetwork.h new file mode 100644 index 0000000..5ac90a7 --- /dev/null +++ b/NXDNReflector/NXCoreNetwork.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef NXCoreNetwork_H +#define NXCoreNetwork_H + +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CNXCoreNetwork { +public: + CNXCoreNetwork(const std::string& address, bool debug); + ~CNXCoreNetwork(); + + bool open(); + + bool write(const unsigned char* data, unsigned int len); + + bool read(unsigned char* data, unsigned int len); + + void close(); + + void clock(unsigned int ms); + +private: + CUDPSocket m_socket; + in_addr m_address; + bool m_debug; + CRingBuffer m_buffer; +}; + +#endif diff --git a/NXDNReflector/NXDN.csv b/NXDNReflector/NXDN.csv new file mode 100644 index 0000000..0024978 --- /dev/null +++ b/NXDNReflector/NXDN.csv @@ -0,0 +1,405 @@ +UID,Call,Name,Location,Notes +2,W2FLY/M,Harry,Southern NJ,UHF NX-800 mobile +3,WB2OOM/M,Tina,Southern NJ,UHF NX-800 Mobile +4,W2FLY/M,Harry,Southern NJ,UHF NX-800 Van +7,F1PRY,Emmanuel,BEAUVAIS FR, +8,W2FLY/P,Harry,Southern NJ,UHF NX-820 Port 2 +9,KE8BGA,Lowell Katz,"Cleveland, Ohio","on the north shore, NX-800" +17,KC2SNI,Jim Hannon,"West Berlin, New Jersey",My Radio is an Icom IC-3161DT +25,WB2RUH/P,Bruce,Southern NJ,UHF NX-320 +26,WB2RUH/M,Bruce,Southern NJ,UHF NX-800 mobile +30,KD4MOJ,Doug,,Doug +74,N2IXC,Ed,Chatsworth NJ, +76,W2FLY/P,Harry,Southern NJ,UFH NX5300 Port 1 +77,W2FLY/P,Harry,"Somers Point, NJ",UHF NX-800 Shore Base +78,W2FLY,Harry,"Mullica Hill, NJ",UHF NX-800 +79,W2FLY/M,Harry,Southern NJ,UHF NX-5800 +80,W2FLY/M,Harry,Southern NJ,VHF NX-5700 mobile +82,W2FLY/P,Harry,"Somers Point, NJ",VHF NX-700 Shore Base +85,W2FLY/M,Harry,Southern NJ,VHF NX_700 van +86,W2FLY,Harry,"Mullica Hill, NJ",VHF NX-700 base +87,W2FLY/M,Harry,Southern NJ,VHF NX-700 mobile +88,W2FLY/P,Harry,Southern NJ,VHF NX-220 portable +89,WB2RUH,Bruce,Vineland NJ,VHF NX-700 base +99,K1ZSG,Don,,Mobile +134,N2SRO,Mike,Pitman,Mobile Radio +134,N2SRO,Mike,NJ, +141,KF2M,Greg Popovich,"Mt Laurel, NJ",IC-F4161DT +401,W1KFR,Bill,,Bill +901,K1IFF,Cliff,CT,Cliff NX320 +902,K1IFF,Cliff,CT,Cliff NX320 - demo +903,K1IFF,Cliff,CT,Cliff NX800H +905,WA1LMV,Rick,,Rick NX320 +906,K1IFF,Cliff,CT,Cliff NX200 +907,K1IFF,Cliff,CT,Future +908,W4CLL,Chuck,,CHUCK +909,AB1UB V,Woody,"Bristol, CT", +909,AB1UB,Woody,"Bristol, CT", +909,AB1UB,Woody,,Woody NX300 +909,W4CCL,Chuck,,CHUCK +910,W4CLL,Chuck,,CHUCK +911,W4CLL,Chuck,,CHUCK +912,W4CCL,Chuck,,CHUCK +920,N1XDN,Bob,CT,Bob P1 UHF NX300 +921,N1XDN,Bob,CT,Bob P1 VHF NX220 +922,N1XDN,Bob,CT,Bob P2 UHF NX300 +923,N1XDN,Bob,CT,Bob M1 VHF NX700 +924,N1XDN,Bob,CT,Bob P3 UHF NX-5300 +925,N1XDN,Bob,CT,Bob P2 VHF NX-5200 +926,N1XDN,Bob,CT,Bob M1 UHF NX-5800 +928,N1XDN,Bob,CT,Icom portable +930,K1IFF,Cliff,CT,NX300 +931,K1IFF,Cliff,CT,NX300 +936,W1GPO,John,,NX300 +951,K1KGQ,Joel,,Joel NX800H +952,K1KGQ,Joel,CT,Truck radio +952,K1GML,Gail,,Gail NX800H +953,K1KGQ,Joel,,Joel NX320-1 +954,K1KGQ,Joel,, +955,K1KGQ,Joel,, +956,K1KGQ,Joel,, +957,K1KGQ,Joel,, +958,K1KGQ,Joel,,Joel F4161 +959,K1KGQ,Joel,,Future +960,K1ZXX,Gary,,Gary - Icom All radios +961,WA1SSB,Joe,,Joe NX800H +962,N1MAT,Dennis,,Dennis F4161 +963,N1MAT,Dennis,,Dennis F4161 +970,KB1VRI,Nick,CT, +970,KB1VRI,Nick,,Nick +971,N1ELE,Paul,CT, +972,N1ODJ,Kenny Schmitz,Middletown, +973,W1VLF,Paul,CT, +980,W1RHS,Rick,Connecticut, +995,K1IFF/Base,Cliff,"Brsitol, CT",NXR710 link radio +1001,KE4GWW,James,AL,NX200 Portable #1 +1002,KE4GWW,James,AL,NX200 Portable #2 +1003,KE4GWW,James,AL,NX300 Portable #1 +1004,KE4GWW,James,AL,NX300 Portable #2 +1005,KE4GWW,James,AL,NX700 Base +1006,KE4GWW,James,AL,NX800 Base +1007,KE4GWW,James M. Nelson,"Dothan, AL",NX700 Mobile#1 +1008,DK6PX,Stefan,Germany nr Bad Toelz,Coming up FR6000 +1008,KE4GWW,James,AL,NX700 Mobile#2 +1009,KE4GWW,James,AL,NX800 Mobile#1 +1010,KE4GWW,James,AL,NX800 Mobile#2 +1066,KM4JXP,Peter Tasker,Longboat Key,NX5300 +1954,W4BPT,BILLY TAYLOR,"RINGGOLD,GA.", +1956,KC7SDD,Dana Hanford,"Bainbridge Island, WA","Member BARC, W7NPC.org" +1963,N5JRN,David Barts,"Bainbridge Island, WA","Handheld, Icom 4161" +1966,ZS6IIX,Henry,Petit South Africa, +1997,KC1AJR,Giovanni,East Hampton CT,kenwood NX800 K2 +3070,W4ORL,Ralph,,Ralph - Mobile +3071,W4LOV,Mike,,Mike - NX800 +3072,N4JTK,John,,John - NX800 +3074,W4MCA,Michael,,Michael - NX300 +3075,W4LOV,Mike,,Mike - NX800 +3079,NN4TT,Dave,Orlando,NX300 +3079,W4ORL,Ralph,,Ralph - Portable +3081,W4MCA,Michael,,Michael - NX800 +3082,W4LOV,Mike,,Mike - Portable +3083,KD4TWJ,Dean,,Dean - Mobile NX800 +3271,N4KEG,Richard,,RICHARD - Mobile and or ht's N4KEG (UHF) +3274,KD4EFM,Evans,FL, +3275,KD4EFM,Evans,FL, +3278,KD4EFM,Evans,FL, +3279,N4KEG,Richard,, +3280,KD4EFM,Evans,"Lakeland, Florida",work radio +3280,KD4EFM,Evans,FL,Evans Mobile(s) +3280,KD4EFM,Evans,FL, +3281,N4KEG,Richard,, +3450,K0KAD,CHAD TAYLOR,"SPOKANE VALLEY, WA",My Portable +3451,K0KAD,CHAD TAYLOR,"SPOKANE VALLEY, WA",My Base Station +3452,K0KAD,CHAD TAYLOR,"SPOKANE VALLEY, WA",Mobile Radio +4020,K4XXX,Ed,"London, KY",NX820 Base +4802,hb3yrb,Alex,Verbier,nx5300 +4804,hb9fmf,Didier,Rougemont,NX5300 +5001,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-820" +5002,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-720" +5003,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-820" +5004,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-720" +5005,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR" +5006,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR" +5007,N5LKE,James,AR,"N5LKE, James Ferguson, Searcy, AR - Kenwood NX-800" +5008,AF5WN,Mason,AR,"AF5WN, Mason Creager, Cabot, AR - Kenwood NX-700H" +5009,KJ5SF,Stan,AR,"KJ5SF, Stan Rongey, Searcy, AR - Kenwood NX-800" +5100,K5SRS,Michael Smith,"Fayetteville, AR",NX-5800 Mobile +5101,KC5YQB,Paul Blair,"Fayetteville, AR", +5125,AE5OQ,Armstead,"Higden, AR", +5125,AE5OQ,Armstead,AR,entered by k1iff +5134,AE5OQ,Armstead,AR,"AE5OQ, Armstead M Feland, IV, Higdon, AR - Kenwood NX-200" +5150,WY6NN,Doug,"Los Angeles, CA",NX-300/5300 +5151,KC6HFJ,Tamara,"Los Angeles, CA",NX-300 +5170,K5WMZ,Gary,AR,"K5WMZ, Gary D Partlow, Ward, AR - Kenwood NX-700" +5599,W1ZM,Ryan,,"W1ZM, Ryan Nelson, NX-5300" +5987,W5ZN,Joel,AR,"W5ZN, Joel Harrison, Judsonia, AR - Kenwood NX-800" +6430,K4GFD,Norm,FL,Norm Greensboro Fla +6431,K4GFD,Norm,, +6432,K4GFD,Norm,, +6439,N4NKV,George,,George +6801,hb9vsd,relais de verbier,verbier, +6802,HB9YRB,Alex,Verbier, +6804,HB9FMF,Didier,Rougemont, +7737,N4NKV,George,,George +9834,VA7NY,PJ,,PJ +12002,W4KJP,Kevin,,KEVIN +12005,KD4TWJ,Dean,,Dean - Portable IC-F4161 +12006,W4MO,Stewart,,STEWART NX300 / 320 +12007,W4MO,Stewart,,STEWART NX800 +12010,kd4dwp,Ben,Orlando, +12013,K4NBC,Miguel Orama,"Palm Bay, Florida",Icom F4146DS UHF Portable +12014,K4NBC,Miguel Orama,"Palm Bay, Florida",Kenwood NX200 VHF HT +12015,K4NBC,K4NBC-Miguel UHF Mobile,"Palm Bay, Florida",Future UHF Mobile +12016,K4NBC,Miguel,"Palm Bay, Florida",M2 Icom F6061D +12017,N4JFZ,,, +12021,W4PJT,Paul,,Paul +12023,KD4NWL,John,,JOHN +12127,W4KDM,Rick,,Rick - Portable NX300 +12136,WA4KWK,John,,John F6121 +12200,NB9X,Paul,FL, +12201,NB9X,Paul,FL,PAUL +12202,NB9X,Paul,FL, +12203,NB9X,Paul,FL, +12204,WX3C,Juan Arias,Florida,Orlando metro +12204,NB9X,Paul,FL, +12210,N4APG,Bill,,Bill Pfost +12211,N4APG,Bill,,Bill Pfost +12212,N4APG,Bill,,Bill Pfost +12213,N4APG,Bill,,Bill Pfost +12214,N4APG,Bill,,Bill Pfost +12215,WA4ISB,Ed,,ED Mobile +12216,WA4ISB,Ed,, +12220,N4PK,Paul,,PAUK K. +12221,N4PK,Paul,, +12222,W4PJT,Paul,, +12223,W4PJT,Paul,, +12226,KI4SWY,William,, +12230,KQ4KX,Richard,,Richard Sharp +12231,KQ4KX,Richard,, +12234,KD4TWJ,Dean,EL98, +12235,N1IC,Nick,,Nic +12236,N1IC,Nick,, +12240,KR4X,Larry,, +12241,KR4X,Larry,, +12245,KD4ACG,Jason,"Hudson, FL",Icom Mobile +12245,KD4ACG,Jason,,JASON +12246,KD4ACG,Jason,"Hudson, FL",Icom Portable +12246,KD4ACG,Jason,,JASON +12247,K2MCI,,, +12250,KE4GYX,,, +12272,KE4OSL,,, +12273,KE4OSL,,, +12374,KF4I,Thom,,Thom Base +12375,KF4I,Thom,,Thom Mobile +12523,KM4JVE,Ana,,Ana - IC-F4161 +12577,N2DLX,Richard,,Rich Dunajewski NX-5800 +12604,K4CBN,Greg,,GREG +12605,K4CBN,Greg,, +12606,K4CBN,Greg,, +12607,K4CBN,Greg,, +12608,K4CBN,Greg,, +12666,KI4SWY,William,,William Stillwel ( All Radios ) +12691,KE3WDW,Sam,,Sam +12780,KC4YUA,Brett,,BRETT +13105,W7QO,Alan,, +13106,W7QO,Alan,, +13107,W7QO,Alan,, +13121,W4CML,Chuck,, +13122,W4CML,Chuck,, +13125,KD4APP,Don,,Don - NX300 +13126,KD4APP,Don,,Don - F6061D +13127,KD4APP,Don,,Don - NX200 +13131,W5JR,Mike,, +13132,W5JR,Mike,, +13401,W1KFR,Bill,,Bill +13815,KG4BKO,Bill,GA,nx320 +14020,K4XXX,Ed,"London, KY",NX5300 +14021,K4XXX,Ed,"London, KY",NX5200 +15001,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-800H (Vehicle 1)" +15002,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-700 (Vehicle 1)" +15003,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-800 (Vehicle 2)" +15004,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-700 (Vehicle 2)" +15005,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-800 (Vehicle 1)" +15006,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-700 (Vehicle 1)" +15007,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-800 (Vehicle 2)" +15008,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-700 (Vehicle 2)" +15009,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-800 (Vehicle 3)" +15010,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-700 (Vehicle 3)" +15011,N5QT,Dawn,AR,"N5QT, Dawn Gray, Searcy, AR - Kenwood NX-800" +15012,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR - Kenwood NX-800" +15013,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR - Kenwood NX-700" +15014,N5QT,Dawn,AR,"N5QT, Dawn Gray, Searcy, AR - Kenwood NX-700" +15015,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR Motorcycle Mobile" +15016,KG5KS,Kenny,,"KG5KS, Kenny Thompson - Kenwood NX-700HK" +15017,KG5KS,Kenny,,"KG5KS, Kenny Thompson - Kenwood NX-800HK" +15018,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR ATV Mobile" +15019,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR ATV Mobile" +15020,WA8UBL,Allen,AR,"WA8UBL, Allen Herrick, Beebe, AR, Kenwood NX-700" +15021,KD5HRT,Heath,AR,"KD5HRT, Heath Taylor, McRae, AR, Kenwood NX-800" +15022,WA8UBL,Allen,AR,"WA8UBL, Allen Herrick, Beebe, AR - Kenwood NX-800" +15023,N5ZA,Joel,AR,"N5ZA, Joe Belew, Beebe, AR - Kenwood NX-700" +15024,KF5TPF,Ryan,AR,"KF5TPF, Ryan Nelson, Edgemont, AR NX-700" +15025,KE5YZP,Ryan,AR,"KE5YZP, Ryan Schwarck, Batesville, AR NX-700" +15026,AF5OD,Landon,AR,"AF5OD, Landon McCord, Searcy, AR" +15027,KB5RFF,Scott,AR,"KB5RFF, Scott Gray, Searcy, AR - Kenwood NX-800" +17002,KC9GMX,Stephen,,Stephen - Base F6121D +17004,KC9GMX,Stephen,,Stephen - F6061D +17005,KC9NPJ,Jarrett,,Jarrett F6121D +17006,KC9GMX,Stephen,,Stephen - F4161DS +17007,KC9NPJ,Jarrett,,Jarrett F4231DS +17008,KC9RHH,Jesse,,F4101D +17009,KC9PHK,Michael,,F4061DS +17011,N9BRG,Dan,,Dan kenwood nx800k2 +17855,AG6RN,Demian,CA,NX-210 +21007,WA1LMV,Rick,,Rick +21014,K2BUI,Eric,,Eric +24020,K4XXX,Ed,"London, KY",NX5800 +24021,K4XXX,Ed,"London, KY",NX5700 +25001,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-300" +25002,N5QZ,Ryan,AR,"N5QZ, Ryan McAfee, Searcy, AR - Kenwood NX-200" +25003,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-300" +25004,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-200" +25005,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-320" +25006,N5QS,Roger,AR,"N5QS, Roger Gray, Searcy, AR - Kenwood NX-340U" +25007,N5QT,Dawn,AR,"N5QT, Dawn Gray, Searcy, AR - Kenwood NX-300" +25008,N5QT,Dawn,AR,"N5QT, Dawn Gray, Searcy, AR - Kenwood NX-200" +25009,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR - Kenwood NX-300" +25010,KD5OOW,Tom,AR,"KD5OOW, Tom McGee, Searcy, AR - Kenwood NX-300" +25011,W5ZN,Joel,AR,"W5ZN, Joel Harrison, Judsonia, AR - Kenwood NX-300" +25012,KD5HRT,Heath,AR,"KD5HRT, Heath Taylor, McRae, AR - Kenwood NX-300" +25013,KK5WA,Darin,AR,"KK5WA, Darin Dykes, Beebe, AR - Kenwood NX-320" +25014,N5LKE,James,AR,"N5LKE, James Ferguson, Searcy, AR - Kenwood NX-5300" +25015,KJ5ORD,John,AR,"KJ5ORD, John Ord, Searcy, AR - Kenwood NX-300" +25016,KB5RFF,Scott,AR,"KB5RFF, Scott Gray, Searcy, AR - Kenwood NX-300" +25017,KB5ZUH,Todd,AR,"KB5ZUH, Todd Williams, Searcy, AR - Kenwood NX-320" +25018,KB5ZUH,Todd,AR,"KB5ZUH, Todd Williams, Searcy, AR - Kenwood NX-340" +25019,KB5ZUH,Todd,AR,"KB5ZUH, Todd Williams, Searcy, AR - Kenwood NX-340" +25020,KB5ZUH,Todd,AR,"KB5ZUH, Todd Williams, Searcy, AR - Kenwood NX-320" +25021,KE5UZU,Raymond Prince,"England, AR",Kenwood Handheld +25021,AF5OD,Landon,AR,"AF5OD, Landon McCord, Searcy, AR" +25022,KE5UZU,Raymond Prince,"England, AR",Icom Handheld +25022,KE5YZP,Ryan,AR,"KE5YZP, Ryan A Schwarck, Batesville, AR - Icom" +25023,KE5YZP,Ryan,AR,"KE5YZP, Ryan A Schwarck, Batesville, AR - Icom" +25024,KG5CHM,Colt,AR,"KG5CHM, Colt Boyd, Searcy, AR - Kenwood NX-300" +25025,KJ5ORD,John,AR,"KJ5ORD, John Ord, Searcy, AR - Kenwood NX-xxx VHF" +25026,WA8UBL,Allen,AR,"WA8UBL, Allen Herrick, Beebe, AR - Kenwood NX-320" +25027,KF5TPF,Ryan,AR,"KF5TPF, Ryan Nelson, Edgemont, AR - Kenwood NX-200" +25028,WA8UBL,Allen,AR,"WA8UBL, Allen Herrick, Beebe, AR - Kenwood NX-220" +25029,KF5WHX,Trey,AR,"KF5WHX, Trey Ferguson, Searcy, AR - Kenwood NX-320" +25030,AC5AV,Larry,AR,"AC5AV, Larry Sicks, Searcy, AR" +25031,KJ5SF,Stan,AR,"KJ5SF, Stan Rongey, Searcy, AR - Kenwood NX-340" +25032,KC5DFH,Kirk,AR,"KC5DFH, Kirk Williams, Searcy, AR - Kenwood NX-340" +25033,AF5OD,Landon,AR,"AF5OD, Landon McCord, Searcy, AR, Icom F4161DS" +25034,KF5TPF,Ryan,AR,"KF5TPF, Ryan Nelson, Edgemont, AR - ICOM 4161" +25035,KG5BMB,Glenda,AR,"KG5BMB, Glenda McCord, Searcy, AR, Icom F4161DS" +25039,KC1JET,Jim,,JIM (BASE) +25068,N1QD,Joe,, +25069,N1COP,Bob,,Bob +25070,WD1L,John,,John +25125,N1TI,Tim,"Mattapoisett, MA", +25125,N1TI,Tim,"Mattapoisett, MA", +25144,W1WCF,Walter,,Walter +25221,N0XIA,James,,James - Kenwood Mobile and Portable +25222,KE4SDC,Ron,,Ron - Portable NX300 +25246,WG1U,Ken,"Assonet, MA", +25333,W1AKN,Jack,,Jack Mobile +25390,KC1JET,Jim,,JIM (MOBILE) +25390,KC1JET,Jim,,JIM +25444,W1TAV,Steve,,Steve +25555,WA1MNQ,Mike,,Mike +25582,N1KXJ,Ray,"E. Bridgewater, MA.",Port. +25582,N1KXJ,Ray Wall,East Bridgewater, +25585,N1AVA,Ken Howland,"DARTMOUTH,MA", +25700,NN1D,Tony Souza,"Swansea, MA", +25740,WG1U,Kenny,,KEN +25743,WG1U,Kenny,,KEN +25746,N1OTY,John,,JOHN +25777,W1BON,Bump,,Bump +25830,KB1CHU,Steve,MA, +25863,N1ZZN,Jeff,,JEFF +25874,KB1KVD,Jason,,Jason - Icom Mob & Port +25888,K1WGU,Bob,,Bob +25889,AB1CQ,Gary,,Gary +25900,N1ZZR,Matt,,Matt - mobile +25901,N1MRC,Matt,"Bridgewater, MA",N1MRC P-1 +25901,N1ZZR,Matt,,Matt +25957,KC1HO,Steve,,STEVE +27101,KI4SWB,Mark,"Melbourne Beach, FL",Icom Portable +27102,KP4OO,Carlos,"Palm Bay, FL",Icom Portable +27201,KI4SWB,Mark,"Melbourne Beach, FL",Icom Mobile +27202,KP4OO,Carlos,"Palm Bay, FL",Icom Mobile +28001,K3KRS,rYAN,CLEVLAND,OHIO +30505,KG6MQE,Jim,MT, +30571,WX4RCF,Ryan F.,"Tampa, FL", +31117,K4QHR,Pete,,Pete +31124,KM4JXP,Peter,,NX5300 +31146,KD2KWD,Michael Santamaria,"Brandon, FL",Kenwood +33000,N1PA-R,Paul,NH,PAUL - NEW HAMPSHIRE REPEATER +33001,N1PA,Paul,NH,PAUL - NEW HAMPSHIRE +33002,N1PA,Paul,NH,PAUL - NEW HAMPSHIRE +34001,N2WNS,Bill,NJ,N2WNS Portable +34002,N2WNS,Bill,NJ,N2WNS Base +36001,W2CCR,Chris,NY,"Chris, Galway, NY NX300" +36002,KC2VOB,Asad,NY,"Asad, NYC, NX-5800" +36003,KC2VOB,Asad,NY,"Asad, NYC, NX-300GK" +36134,N2SRO,Mike,"Pitman, NJ",Portable +37001,KA4YMY,Steve,NC,"Steve, Charlotte, NC NX320" +37009,KA4YMY,Steve,"Charlotte, NC", +37010,KA4YMY,Steve,"Charlotte, NC", +37010,KA4YMY,Steve,"Charlotte, NC", +40024,N5YEI,Jeff Dalrymple,"Jay, Oklahoma",NX-5800 Mobile +40124,N5YEI,Jeff Dalrymple,"Jay, Oklahoma",NX-5300 Portable +43751,N4IRS,Steve,,Steve +44001,K2BUI,Eric,RI, +44002,K2BUI,Eric,"Providence, RI", +44007,W2DAN,Dave,,Dave - NX700 +44298,N1JBC,Jed,,JED +49101,WX7Y-1,Bret Mills,Castle Dale Utah,WX7y-1 Hand Held 1 +49102,WX7Y-2,Bret Mills,"Caslte Dale, Utah",WX7Y-2 Hand held +49104,WX7Y-4,Bret Mills,"Castle Dale, Utah",WX7Y-4 Mobile +49106,WX7Y-6,Bret Mills,Castle Dale,WX7Y-6 Mobile +55700,WB9HKE,Rick,WI, +55704,WB9HKE,Rick,WI, +57000,VE3SP,Andre,"Toronto, ON",mobile +57001,VE3SP,Andre,"Toronto, ON",handheld +57001,VE3EI,Eric,,Eric - Portable +57002,VE3OKZ,Janusz,"Toronto, ON",mobile +57002,VE3EI,Eric,,Eric - Dongle +57003,VE3OKZ,Janusz,"Toronto, ON",handheld +57004,VE3CCD,Curtis,"Scarborough, ON",handheld +57005,VE3NBI,Anoop,"Scarborough, ON",handheld +57006,VE3JLU,Sherwin,"Scarborough, ON",handheld +57007,va3rq,Jon,"Oakville, ON",handheld +57008,ve3ogb,Randy,"Toronto, ON",handheld +57009,va3mk,Mark,"Oakville, ON",handheld +57010,va3mk,Mark,"Oakville, ON",mobile +57011,VA3WZW,Andre,"Durham, ON",mobile +57012,VA3WZW,Andre,"Durham, ON",mobile +57013,VA3AGV,cottage,"Toronto, ON",mobile +57014,VE3BWP,Brian,"Caledon, ON",handheld +57015,VE3HY,Frank,"Caledon, ON",handheld +57016,VE3UBI,Paul,"Scarborough, ON",mobile +57017,VE3UBI,Paul,"Scarborough, ON",handheld +57018,VE3RZR,Richard,"Toronto, ON",handheld +57019,VE3AEP,Dom,"Toronto, ON",mobile +57020,VA3PEO,Adrian,"Toronto, ON",handheld +57021,va3wjo,Walter,"Toronto, ON",handheld +57022,ve3fkn,Tom,"Stoney Creek, ON",handheld +57023,ve3ips,John,"Toronto, ON",handheld +57024,VA3CQA,Brian,"Scarborough, ON",handheld +57025,VA3PAD,Andrew,"Scarborough, ON",mobile +57026,VA3PAD,Andrew,"Scarborough, ON",handheld +57027,VA3VAD,Arpad,"Hamilton, ON",handheld +57028,VA3KY,Shelly,"Thornhill, ON",mobile +57029,VA3KY,Shelly,"Thornhill, ON",handheld +57030,VE3KFQ,Doug,"Toronto, ON",mobile +57031,VE3KFQ,Doug,"Toronto, ON",handheld +57032,VE3AEP,Dom 2,"Toronto, ON",handheld +57033,VE3DJZ,Huey,"Hamilton, ON",mobile +57034,VE3DJZ,Huey,"Hamilton, ON",handheld +57035,VA3RQ,Jon,Oakville ON,Mobile +3106418,N5ICK,Nicholas Nixon,"Rialto, Ca", +3112205,N2HUC,Phil Roberts,"Port St. Lucie, FL",DV4MINI AMBE voice +3118365,NA9VY,Chris Gilbert,"New Palestine, IN", +3118366,N9UMJ,Rick Nicholson,Buddha Indiana, +3142058,N3BAH,L. Abraham Smith,Southwestern Pennsylvania, diff --git a/NXDNReflector/NXDNLookup.cpp b/NXDNReflector/NXDNLookup.cpp new file mode 100644 index 0000000..2c621b6 --- /dev/null +++ b/NXDNReflector/NXDNLookup.cpp @@ -0,0 +1,160 @@ +/* +* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "NXDNLookup.h" +#include "Timer.h" +#include "Log.h" + +#include +#include +#include +#include + +CNXDNLookup::CNXDNLookup(const std::string& filename, unsigned int reloadTime) : +CThread(), +m_filename(filename), +m_reloadTime(reloadTime), +m_table(), +m_mutex(), +m_stop(false) +{ +} + +CNXDNLookup::~CNXDNLookup() +{ +} + +bool CNXDNLookup::read() +{ + bool ret = load(); + + if (m_reloadTime > 0U) + run(); + + return ret; +} + +void CNXDNLookup::entry() +{ + LogInfo("Started the NXDN Id lookup reload thread"); + + CTimer timer(1U, 3600U * m_reloadTime); + timer.start(); + + while (!m_stop) { + sleep(1000U); + + timer.clock(); + if (timer.hasExpired()) { + load(); + timer.start(); + } + } + + LogInfo("Stopped the NXDN Id lookup reload thread"); +} + +void CNXDNLookup::stop() +{ + if (m_reloadTime == 0U) { + delete this; + return; + } + + m_stop = true; + + wait(); +} + +std::string CNXDNLookup::find(unsigned int id) +{ + std::string callsign; + + if (id == 0xFFFFU) + return std::string("ALL"); + + m_mutex.lock(); + + try { + callsign = m_table.at(id); + } catch (...) { + char text[10U]; + ::sprintf(text, "%u", id); + callsign = std::string(text); + } + + m_mutex.unlock(); + + return callsign; +} + +bool CNXDNLookup::exists(unsigned int id) +{ + m_mutex.lock(); + + bool found = m_table.count(id) == 1U; + + m_mutex.unlock(); + + return found; +} + +bool CNXDNLookup::load() +{ + FILE* fp = ::fopen(m_filename.c_str(), "rt"); + if (fp == NULL) { + LogWarning("Cannot open the NXDN Id lookup file - %s", m_filename.c_str()); + return false; + } + + m_mutex.lock(); + + // Remove the old entries + m_table.clear(); + + char buffer[100U]; + while (::fgets(buffer, 100U, fp) != NULL) { + if (buffer[0U] == '#') + continue; + + char* p1 = ::strtok(buffer, ",\t\r\n"); + char* p2 = ::strtok(NULL, ",\t\r\n"); + + if (p1 != NULL && p2 != NULL) { + unsigned int id = (unsigned int)::atoi(p1); + if (id > 0U) { + for (char* p = p2; *p != 0x00U; p++) + *p = ::toupper(*p); + + m_table[id] = std::string(p2); + } + } + } + + m_mutex.unlock(); + + ::fclose(fp); + + size_t size = m_table.size(); + if (size == 0U) + return false; + + LogInfo("Loaded %u Ids to the NXDN callsign lookup table", size); + + return true; +} \ No newline at end of file diff --git a/NXDNReflector/NXDNLookup.h b/NXDNReflector/NXDNLookup.h new file mode 100644 index 0000000..d7b0486 --- /dev/null +++ b/NXDNReflector/NXDNLookup.h @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef NXDNLookup_H +#define NXDNLookup_H + +#include "Thread.h" +#include "Mutex.h" + +#include +#include + +class CNXDNLookup : public CThread { +public: + CNXDNLookup(const std::string& filename, unsigned int reloadTime); + virtual ~CNXDNLookup(); + + bool read(); + + virtual void entry(); + + std::string find(unsigned int id); + + bool exists(unsigned int id); + + void stop(); + +private: + std::string m_filename; + unsigned int m_reloadTime; + std::unordered_map m_table; + CMutex m_mutex; + bool m_stop; + + bool load(); +}; + +#endif diff --git a/NXDNReflector/NXDNNetwork.cpp b/NXDNReflector/NXDNNetwork.cpp new file mode 100644 index 0000000..75f25a6 --- /dev/null +++ b/NXDNReflector/NXDNNetwork.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "NXDNNetwork.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +CNXDNNetwork::CNXDNNetwork(unsigned int port, bool debug) : +m_socket(port), +m_debug(debug) +{ +} + +CNXDNNetwork::~CNXDNNetwork() +{ +} + +bool CNXDNNetwork::open() +{ + LogInfo("Opening NXDN network connection"); + + return m_socket.open(); +} + +bool CNXDNNetwork::write(const unsigned char* data, unsigned int length, const in_addr& address, unsigned int port) +{ + assert(data != NULL); + assert(length > 0U); + assert(port > 0U); + + if (m_debug) + CUtils::dump(1U, "NXDN Network Data Sent", data, length); + + return m_socket.write(data, length, address, port); +} + +unsigned int CNXDNNetwork::read(unsigned char* data, unsigned int length, in_addr& address, unsigned int& port) +{ + assert(data != NULL); + assert(length > 0U); + + int len = m_socket.read(data, length, address, port); + if (len <= 0) + return 0U; + + if (m_debug) + CUtils::dump(1U, "NXDN Network Data Received", data, len); + + return len; +} + +void CNXDNNetwork::close() +{ + m_socket.close(); + + LogInfo("Closing NXDN network connection"); +} diff --git a/NXDNReflector/NXDNNetwork.h b/NXDNReflector/NXDNNetwork.h new file mode 100644 index 0000000..a037b34 --- /dev/null +++ b/NXDNReflector/NXDNNetwork.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef NXDNNetwork_H +#define NXDNNetwork_H + +#include "UDPSocket.h" + +#include +#include + +class CNXDNNetwork { +public: + CNXDNNetwork(unsigned int port, bool debug); + ~CNXDNNetwork(); + + bool open(); + + bool write(const unsigned char* data, unsigned int length, const in_addr& address, unsigned int port); + + unsigned int read(unsigned char* data, unsigned int length, in_addr& address, unsigned int& port); + + void close(); + +private: + CUDPSocket m_socket; + bool m_debug; +}; + +#endif diff --git a/NXDNReflector/NXDNReflector.cpp b/NXDNReflector/NXDNReflector.cpp new file mode 100644 index 0000000..b5ff78b --- /dev/null +++ b/NXDNReflector/NXDNReflector.cpp @@ -0,0 +1,396 @@ +/* +* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "NXCoreNetwork.h" +#include "NXDNReflector.h" +#include "NXDNNetwork.h" +#include "NXDNLookup.h" +#include "StopWatch.h" +#include "Version.h" +#include "Thread.h" +#include "Utils.h" +#include "Log.h" + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(_WIN32) || defined(_WIN64) +const char* DEFAULT_INI_FILE = "NXDNReflector.ini"; +#else +const char* DEFAULT_INI_FILE = "/etc/NXDNReflector.ini"; +#endif + +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + const char* iniFile = DEFAULT_INI_FILE; + if (argc > 1) { + for (int currentArg = 1; currentArg < argc; ++currentArg) { + std::string arg = argv[currentArg]; + if ((arg == "-v") || (arg == "--version")) { + ::fprintf(stdout, "NXDNReflector version %s\n", VERSION); + return 0; + } else if (arg.substr(0, 1) == "-") { + ::fprintf(stderr, "Usage: NXDNReflector [-v|--version] [filename]\n"); + return 1; + } else { + iniFile = argv[currentArg]; + } + } + } + + CNXDNReflector* reflector = new CNXDNReflector(std::string(iniFile)); + reflector->run(); + delete reflector; + + return 0; +} + +CNXDNReflector::CNXDNReflector(const std::string& file) : +m_conf(file), +m_repeaters() +{ +} + +CNXDNReflector::~CNXDNReflector() +{ +} + +void CNXDNReflector::run() +{ + bool ret = m_conf.read(); + if (!ret) { + ::fprintf(stderr, "NXDNReflector: cannot read the .ini file\n"); + return; + } + + ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel()); + if (!ret) { + ::fprintf(stderr, "NXDNReflector: unable to open the log file\n"); + return; + } + +#if !defined(_WIN32) && !defined(_WIN64) + bool m_daemon = m_conf.getDaemon(); + if (m_daemon) { + // Create new process + pid_t pid = ::fork(); + if (pid == -1) { + ::LogWarning("Couldn't fork() , exiting"); + return; + } + else if (pid != 0) + exit(EXIT_SUCCESS); + + // Create new session and process group + if (::setsid() == -1) { + ::LogWarning("Couldn't setsid(), exiting"); + return; + } + + // Set the working directory to the root directory + if (::chdir("/") == -1) { + ::LogWarning("Couldn't cd /, exiting"); + return; + } + + ::close(STDIN_FILENO); + ::close(STDOUT_FILENO); + ::close(STDERR_FILENO); + + //If we are currently root... + if (getuid() == 0) { + struct passwd* user = ::getpwnam("mmdvm"); + if (user == NULL) { + ::LogError("Could not get the mmdvm user, exiting"); + return; + } + + uid_t mmdvm_uid = user->pw_uid; + gid_t mmdvm_gid = user->pw_gid; + + //Set user and group ID's to mmdvm:mmdvm + if (setgid(mmdvm_gid) != 0) { + ::LogWarning("Could not set mmdvm GID, exiting"); + return; + } + + if (setuid(mmdvm_uid) != 0) { + ::LogWarning("Could not set mmdvm UID, exiting"); + return; + } + + //Double check it worked (AKA Paranoia) + if (setuid(0) != -1) { + ::LogWarning("It's possible to regain root - something is wrong!, exiting"); + return; + } + } + } +#endif + + CNXDNNetwork nxdnNetwork(m_conf.getNetworkPort(), m_conf.getNetworkDebug()); + ret = nxdnNetwork.open(); + if (!ret) { + ::LogFinalise(); + return; + } + + CNXCoreNetwork* nxCoreNetwork = NULL; + if (m_conf.getNXCoreEnabled()) { + nxCoreNetwork = new CNXCoreNetwork(m_conf.getNXCoreAddress(), m_conf.getNXCoreDebug()); + ret = nxCoreNetwork->open(); + if (!ret) { + nxdnNetwork.close(); + delete nxCoreNetwork; + ::LogFinalise(); + return; + } + } + + CNXDNLookup* lookup = new CNXDNLookup(m_conf.getLookupName(), m_conf.getLookupTime()); + lookup->read(); + + CStopWatch stopWatch; + stopWatch.start(); + + CTimer dumpTimer(1000U, 120U); + dumpTimer.start(); + + LogMessage("Starting NXDNReflector-%s", VERSION); + + CNXDNRepeater* current = NULL; + bool nxCore = false; + + CTimer watchdogTimer(1000U, 0U, 1500U); + + for (;;) { + unsigned char buffer[200U]; + in_addr address; + unsigned int port; + + unsigned int len = nxdnNetwork.read(buffer, 200U, address, port); + if (len > 0U) { + CNXDNRepeater* rpt = findRepeater(address, port); + + if (::memcmp(buffer, "NXDNP", 5U) == 0) { + if (rpt == NULL) { + rpt = new CNXDNRepeater; + rpt->m_timer.start(); + rpt->m_address = address; + rpt->m_port = port; + rpt->m_callsign = std::string((char*)(buffer + 5U), 10U); + m_repeaters.push_back(rpt); + + LogMessage("Adding %s (%s:%u)", rpt->m_callsign.c_str(), ::inet_ntoa(address), port); + } + else { + rpt->m_timer.start(); + } + + // Return the poll + nxdnNetwork.write(buffer, len, address, port); + } else if (::memcmp(buffer, "NXDNU", 5U) == 0) { + if (rpt != NULL) { + std::string callsign = std::string((char*)(buffer + 5U), 10U); + LogMessage("Removing %s (%s:%u)", callsign.c_str(), ::inet_ntoa(address), port); + + for (std::vector::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + if (*it == rpt) { + m_repeaters.erase(it); + break; + } + } + + delete rpt; + } + } else if (::memcmp(buffer, "NXDND", 5U) == 0) { + if (rpt != NULL) { + rpt->m_timer.start(); + + if (current == NULL && !nxCore) { + current = rpt; + + unsigned short srcId = (buffer[5U] << 8) | buffer[6U]; + unsigned short dstId = (buffer[7U] << 8) | buffer[8U]; + bool grp = (buffer[9U] & 0x01U) == 0x01U; + + std::string callsign = lookup->find(srcId); + LogMessage("Transmission from %s at %s to %s%u", callsign.c_str(), current->m_callsign.c_str(), grp ? "TG " : "", dstId); + } + + if (current == rpt) { + watchdogTimer.start(); + + for (std::vector::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + in_addr addr = (*it)->m_address; + unsigned int prt = (*it)->m_port; + if (addr.s_addr != address.s_addr || prt != port) + nxdnNetwork.write(buffer, len, addr, prt); + + if (nxCoreNetwork != NULL) + nxCoreNetwork->write(buffer, len); + } + + if ((buffer[9U] & 0x04U) == 0x04U) { + LogMessage("Received end of transmission"); + watchdogTimer.stop(); + current = NULL; + } + } + } else { + LogMessage("Data received from an unknown source - %s:%u", ::inet_ntoa(address), port); + CUtils::dump(2U, "Data", buffer, len); + } + } + } + + if (nxCoreNetwork != NULL) { + len = nxCoreNetwork->read(buffer, 200U); + if (len > 0U) { + if (current == NULL) { + if (!nxCore) { + if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x01U) { + bool grp = (buffer[7U] & 0x20U) == 0x20U; + unsigned short srcId = (buffer[8U] << 8) | buffer[9U]; + unsigned short dstId = (buffer[10U] << 8) | buffer[11U]; + + std::string callsign = lookup->find(srcId); + LogMessage("Transmission from %s at NXCore to %s%u", callsign.c_str(), grp ? "TG " : "", dstId); + + nxCore = true; + } + } + + if (nxCore) { + watchdogTimer.start(); + + for (std::vector::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + in_addr addr = (*it)->m_address; + unsigned int prt = (*it)->m_port; + nxdnNetwork.write(buffer, len, addr, prt); + } + + if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x08U) { + LogMessage("Received end of transmission"); + nxCore = false; + watchdogTimer.stop(); + } + } + } + } + } + + unsigned int ms = stopWatch.elapsed(); + stopWatch.start(); + + // Remove any repeaters that haven't reported for a while + for (std::vector::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + (*it)->m_timer.clock(ms); + + for (std::vector::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + CNXDNRepeater* itRpt = *it; + if (itRpt->m_timer.hasExpired()) { + in_addr address = itRpt->m_address; + unsigned int port = itRpt->m_port; + std::string callsign = itRpt->m_callsign; + LogMessage("Removing %s (%s:%u) disappeared", callsign.c_str(), ::inet_ntoa(address), port); + m_repeaters.erase(it); + delete itRpt; + break; + } + } + + watchdogTimer.clock(ms); + if (watchdogTimer.isRunning() && watchdogTimer.hasExpired()) { + LogMessage("Network watchdog has expired"); + watchdogTimer.stop(); + current = NULL; + nxCore = false; + } + + dumpTimer.clock(ms); + if (dumpTimer.hasExpired()) { + dumpRepeaters(); + dumpTimer.start(); + } + + if (nxCoreNetwork != NULL) + nxCoreNetwork->clock(ms); + + if (ms < 5U) + CThread::sleep(5U); + } + + nxdnNetwork.close(); + + if (nxCoreNetwork != NULL) { + nxCoreNetwork->close(); + delete nxCoreNetwork; + } + + lookup->stop(); + + ::LogFinalise(); +} + +CNXDNRepeater* CNXDNReflector::findRepeater(const in_addr& address, unsigned int port) const +{ + for (std::vector::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + if (address.s_addr == (*it)->m_address.s_addr && (*it)->m_port == port) + return *it; + } + + return NULL; +} + +void CNXDNReflector::dumpRepeaters() const +{ + if (m_repeaters.size() == 0U) { + LogMessage("No repeaters linked"); + return; + } + + LogMessage("Currently linked repeaters:"); + + for (std::vector::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + in_addr address = (*it)->m_address; + unsigned int port = (*it)->m_port; + std::string callsign = (*it)->m_callsign; + unsigned int timer = (*it)->m_timer.getTimer(); + unsigned int timeout = (*it)->m_timer.getTimeout(); + LogMessage(" %s (%s:%u) %u/%u", callsign.c_str(), ::inet_ntoa(address), port, timer, timeout); + } +} diff --git a/NXDNReflector/NXDNReflector.h b/NXDNReflector/NXDNReflector.h new file mode 100644 index 0000000..373cdb3 --- /dev/null +++ b/NXDNReflector/NXDNReflector.h @@ -0,0 +1,73 @@ +/* +* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(NXDNReflector_H) +#define NXDNReflector_H + +#include "Timer.h" +#include "Conf.h" + +#include +#include +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +class CNXDNRepeater { +public: + CNXDNRepeater() : + m_address(), + m_port(0U), + m_callsign(), + m_timer(1000U, 120U) + { + } + + in_addr m_address; + unsigned int m_port; + std::string m_callsign; + CTimer m_timer; +}; + +class CNXDNReflector +{ +public: + CNXDNReflector(const std::string& file); + ~CNXDNReflector(); + + void run(); + +private: + CConf m_conf; + std::vector m_repeaters; + + CNXDNRepeater* findRepeater(const in_addr& address, unsigned int port) const; + void dumpRepeaters() const; +}; + +#endif diff --git a/NXDNReflector/NXDNReflector.ini b/NXDNReflector/NXDNReflector.ini new file mode 100644 index 0000000..87b8eb1 --- /dev/null +++ b/NXDNReflector/NXDNReflector.ini @@ -0,0 +1,22 @@ +[General] +Daemon=1 + +[Id Lookup] +Name=NXDN.csv +Time=24 + +[Log] +# Logging levels, 0=No logging +DisplayLevel=1 +FileLevel=1 +FilePath=. +FileRoot=NXDNReflector + +[Network] +Port=41007 +Debug=0 + +[NXCore] +Enabled=0 +Address=127.0.0.1 +Debug=0 diff --git a/NXDNReflector/NXDNReflector.sh b/NXDNReflector/NXDNReflector.sh new file mode 100644 index 0000000..8db48d7 --- /dev/null +++ b/NXDNReflector/NXDNReflector.sh @@ -0,0 +1,76 @@ +#!/bin/bash +### BEGIN INIT INFO +# +# Provides: NXDNReflector +# Required-Start: $all +# Required-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Example startscript NXDNReflector + +# +### END INIT INFO +## Fill in name of program here. +PROG="NXDNReflector" +PROG_PATH="/usr/local/bin/" +PROG_ARGS="/etc/NXDNReflector.ini" +PIDFILE="/var/run/NXDNReflector.pid" +USER="root" + +start() { + if [ -e $PIDFILE ]; then + ## Program is running, exit with error. + echo "Error! $PROG is currently running!" 1>&2 + exit 1 + else + cd $PROG_PATH + ./$PROG $PROG_ARGS + echo "$PROG started" + touch $PIDFILE + fi +} + +stop() { + if [ -e $PIDFILE ]; then + ## Program is running, so stop it + echo "$PROG is running" + rm -f $PIDFILE + killall $PROG + echo "$PROG stopped" + else + ## Program is not running, exit with error. + echo "Error! $PROG not started!" 1>&2 + exit 1 + fi +} + +## Check to see if we are running as root first. +## Found at +## http://www.cyberciti.biz/tips/shell-root-user-check-script.html +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" 1>&2 + exit 1 +fi + +case "$1" in + start) + start + exit 0 + ;; + stop) + stop + exit 0 + ;; + reload|restart|force-reload) + stop + sleep 5 + start + exit 0 + ;; + **) + echo "Usage: $0 {start|stop|reload}" 1>&2 + exit 1 + ;; +esac +exit 0 +### END diff --git a/NXDNReflector/NXDNReflector.sln b/NXDNReflector/NXDNReflector.sln new file mode 100644 index 0000000..05deef3 --- /dev/null +++ b/NXDNReflector/NXDNReflector.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNReflector", "NXDNReflector.vcxproj", "{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}" +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 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.ActiveCfg = Debug|x64 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.Build.0 = Debug|x64 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.ActiveCfg = Debug|Win32 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.Build.0 = Debug|Win32 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.ActiveCfg = Release|x64 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.Build.0 = Release|x64 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.ActiveCfg = Release|Win32 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {32F19C39-C6E7-44AB-BE4C-EA8DD3C57231} + EndGlobalSection +EndGlobal diff --git a/NXDNReflector/NXDNReflector.vcxproj b/NXDNReflector/NXDNReflector.vcxproj new file mode 100644 index 0000000..27d0cbf --- /dev/null +++ b/NXDNReflector/NXDNReflector.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 15.0 + {C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5} + Win32Proj + NXDNReflector + 10.0.16299.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/NXDNReflector/NXDNReflector.vcxproj.filters b/NXDNReflector/NXDNReflector.vcxproj.filters new file mode 100644 index 0000000..a479108 --- /dev/null +++ b/NXDNReflector/NXDNReflector.vcxproj.filters @@ -0,0 +1,95 @@ + + + + + {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 + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/NXDNReflector/RingBuffer.h b/NXDNReflector/RingBuffer.h new file mode 100644 index 0000000..186709d --- /dev/null +++ b/NXDNReflector/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/NXDNReflector/StopWatch.cpp b/NXDNReflector/StopWatch.cpp new file mode 100644 index 0000000..77d539d --- /dev/null +++ b/NXDNReflector/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/NXDNReflector/StopWatch.h b/NXDNReflector/StopWatch.h new file mode 100644 index 0000000..811047e --- /dev/null +++ b/NXDNReflector/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/NXDNReflector/Thread.cpp b/NXDNReflector/Thread.cpp new file mode 100644 index 0000000..b334436 --- /dev/null +++ b/NXDNReflector/Thread.cpp @@ -0,0 +1,101 @@ +/* + * 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 "Thread.h" + +#if defined(_WIN32) || defined(_WIN64) + +CThread::CThread() : +m_handle() +{ +} + +CThread::~CThread() +{ +} + +bool CThread::run() +{ + m_handle = ::CreateThread(NULL, 0, &helper, this, 0, NULL); + + return m_handle != NULL; +} + + +void CThread::wait() +{ + ::WaitForSingleObject(m_handle, INFINITE); + + ::CloseHandle(m_handle); +} + + +DWORD CThread::helper(LPVOID arg) +{ + CThread* p = (CThread*)arg; + + p->entry(); + + return 0UL; +} + +void CThread::sleep(unsigned int ms) +{ + ::Sleep(ms); +} + +#else + +#include + +CThread::CThread() : +m_thread() +{ +} + +CThread::~CThread() +{ +} + +bool CThread::run() +{ + return ::pthread_create(&m_thread, NULL, helper, this) == 0; +} + + +void CThread::wait() +{ + ::pthread_join(m_thread, NULL); +} + + +void* CThread::helper(void* arg) +{ + CThread* p = (CThread*)arg; + + p->entry(); + + return NULL; +} + +void CThread::sleep(unsigned int ms) +{ + ::usleep(ms * 1000); +} + +#endif diff --git a/NXDNReflector/Thread.h b/NXDNReflector/Thread.h new file mode 100644 index 0000000..352d938 --- /dev/null +++ b/NXDNReflector/Thread.h @@ -0,0 +1,56 @@ +/* + * 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(THREAD_H) +#define THREAD_H + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +class CThread +{ +public: + CThread(); + virtual ~CThread(); + + virtual bool run(); + + virtual void entry() = 0; + + virtual void wait(); + + static void sleep(unsigned int ms); + +private: +#if defined(_WIN32) || defined(_WIN64) + HANDLE m_handle; +#else + pthread_t m_thread; +#endif + +#if defined(_WIN32) || defined(_WIN64) + static DWORD __stdcall helper(LPVOID arg); +#else + static void* helper(void* arg); +#endif +}; + +#endif diff --git a/NXDNReflector/Timer.cpp b/NXDNReflector/Timer.cpp new file mode 100644 index 0000000..53956e4 --- /dev/null +++ b/NXDNReflector/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/NXDNReflector/Timer.h b/NXDNReflector/Timer.h new file mode 100644 index 0000000..87d68f5 --- /dev/null +++ b/NXDNReflector/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/NXDNReflector/UDPSocket.cpp b/NXDNReflector/UDPSocket.cpp new file mode 100644 index 0000000..396f1f7 --- /dev/null +++ b/NXDNReflector/UDPSocket.cpp @@ -0,0 +1,262 @@ +/* + * 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 "Log.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) + LogError("Error from WSAStartup"); +#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) + LogError("Error from WSAStartup"); +#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; + } + + LogError("Cannot find address for host %s", 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; + } + + LogError("Cannot find address for host %s", 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) + LogError("Cannot create the UDP socket, err: %lu", ::GetLastError()); +#else + LogError("Cannot create the UDP socket, err: %d", errno); +#endif + return false; + } + + if (m_port > 0U) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (!m_address.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) { + LogError("The local address is invalid - %s", m_address.c_str()); + return false; + } + } + + int reuse = 1; + if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError()); +#else + LogError("Cannot set the UDP socket option, err: %d", errno); +#endif + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Cannot bind the UDP address, err: %lu", ::GetLastError()); +#else + LogError("Cannot bind the UDP address, err: %d", 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) + LogError("Error returned from UDP select, err: %lu", ::GetLastError()); +#else + LogError("Error returned from UDP select, err: %d", 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) + LogError("Error returned from recvfrom, err: %lu", ::GetLastError()); +#else + LogError("Error returned from recvfrom, err: %d", 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) + LogError("Error returned from sendto, err: %lu", ::GetLastError()); +#else + LogError("Error returned from sendto, err: %d", 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/NXDNReflector/UDPSocket.h b/NXDNReflector/UDPSocket.h new file mode 100644 index 0000000..e0af272 --- /dev/null +++ b/NXDNReflector/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/NXDNReflector/Utils.cpp b/NXDNReflector/Utils.cpp new file mode 100644 index 0000000..49ded13 --- /dev/null +++ b/NXDNReflector/Utils.cpp @@ -0,0 +1,146 @@ +/* + * 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 "Log.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); + + ::Log(level, "%s", 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 += '*'; + + ::Log(level, "%04X: %s", 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/NXDNReflector/Utils.h b/NXDNReflector/Utils.h new file mode 100644 index 0000000..ade28c0 --- /dev/null +++ b/NXDNReflector/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/NXDNReflector/Version.h b/NXDNReflector/Version.h new file mode 100644 index 0000000..38fc4df --- /dev/null +++ b/NXDNReflector/Version.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(VERSION_H) +#define VERSION_H + +const char* VERSION = "20180206"; + +#endif diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb67c93 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +These programs are clients for the NXDN networking now built into the MMDVM Host. + +The Parrot is very simple minded and can only handle one client at a time and is therefore not suitable for use as a shared resource via the Internet. + +The Gateway allows for use of NXDN Talk Groups to control the access to the various NXDN reflectors. + +The Gateway has an ini file that contain the parameters for running the software. The filename of the ini file is passed as a parameter on the command line. The Parrot takes the UDP port number to listen on as an argument. + +The MMDVM .ini file should have the IP address and port number of the client in the [NXDN Network] settings. + +They build on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2017 on x86 and x64. + +This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden.