commit 47cce73dfb869656580ffd5d4a74cb6343f91e18 Author: Jonathan Naylor Date: Mon Oct 3 21:57:14 2016 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..44bad9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +Debug +Release +x64 +MMDVMHost +*.o +*.opendb +*.bak +*.obj +*~ +*.sdf +*.log +*.zip +*.exe +*.user +*.VC.db +.vs diff --git a/P25Clients.sln b/P25Clients.sln new file mode 100644 index 0000000..6c66b9d --- /dev/null +++ b/P25Clients.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "P25Parrot", "P25Parrot\P25Parrot.vcxproj", "{2AE94EAA-FD57-45C9-8555-6425CFA777A3}" +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 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/P25Parrot/Makefile b/P25Parrot/Makefile new file mode 100644 index 0000000..e0bcaf0 --- /dev/null +++ b/P25Parrot/Makefile @@ -0,0 +1,18 @@ +CC = gcc +CXX = g++ +CFLAGS = -g -O3 -Wall -std=c++0x +LIBS = +LDFLAGS = -g + +OBJECTS = Network.o P25Parrot.o Parrot.o StopWatch.o Timer.o UDPSocket.o Utils.o + +all: P25Parrot + +P25Parrot: $(OBJECTS) + $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o P25Parrot + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + +clean: + $(RM) P25Parrot *.o *.d *.bak *~ diff --git a/P25Parrot/Makefile.Solaris b/P25Parrot/Makefile.Solaris new file mode 100644 index 0000000..0a637d8 --- /dev/null +++ b/P25Parrot/Makefile.Solaris @@ -0,0 +1,18 @@ +CC = gcc +CXX = g++ +CFLAGS = -g -O3 -Wall -std=c++0x +LIBS = -lsocket +LDFLAGS = -g + +OBJECTS = Network.o P25Parrot.o Parrot.o StopWatch.o Timer.o UDPSocket.o Utils.o + +all: P25Parrot + +P25Parrot: $(OBJECTS) + $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o P25Parrot + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + +clean: + $(RM) P25Parrot *.o *.d *.bak *~ diff --git a/P25Parrot/Network.cpp b/P25Parrot/Network.cpp new file mode 100644 index 0000000..647a2ed --- /dev/null +++ b/P25Parrot/Network.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2009-2014,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Network.h" +#include "Utils.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CNetwork::CNetwork(unsigned int port, bool debug) : +m_socket(port), +m_address(), +m_port(0U), +m_debug(debug), +m_buffer(1000U, "P25 Network") +{ +} + +CNetwork::~CNetwork() +{ +} + +bool CNetwork::open() +{ + ::fprintf(stdout, "Opening P25 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); + + if (m_debug) + CUtils::dump(1U, "P25 Network Data Sent", data, length); + + 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 (m_debug) + CUtils::dump(1U, "P25 Network Data Received", buffer, length); + + 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 P25 network connection\n"); +} diff --git a/P25Parrot/Network.h b/P25Parrot/Network.h new file mode 100644 index 0000000..0b48a49 --- /dev/null +++ b/P25Parrot/Network.h @@ -0,0 +1,53 @@ +/* + * 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, bool debug); + ~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; + bool m_debug; + CRingBuffer m_buffer; +}; + +#endif diff --git a/P25Parrot/P25Parrot.cpp b/P25Parrot/P25Parrot.cpp new file mode 100644 index 0000000..0e5c21a --- /dev/null +++ b/P25Parrot/P25Parrot.cpp @@ -0,0 +1,142 @@ +/* +* Copyright (C) 2016 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "StopWatch.h" +#include "P25Parrot.h" +#include "Parrot.h" +#include "Network.h" +#include "Version.h" +#include "Timer.h" + +#include +#include + +int main(int argc, char** argv) +{ + if (argc == 1) { + ::fprintf(stderr, "Usage: P25Parrot \n"); + return 1; + } + + unsigned int port = ::atoi(argv[1]); + if (port == 0U) { + ::fprintf(stderr, "P25Parrot: invalid port number\n"); + return 1; + } + + CP25Parrot parrot(port); + parrot.run(); + + return 0; +} + +CP25Parrot::CP25Parrot(unsigned int port) : +m_port(port) +{ +} + +CP25Parrot::~CP25Parrot() +{ +} + +void CP25Parrot::run() +{ + CParrot parrot(180U); + CNetwork network(m_port, false); + + bool ret = network.open(); + if (!ret) + return; + + CStopWatch stopWatch; + stopWatch.start(); + + CTimer watchdogTimer(1000U, 0U, 1500U); + CTimer turnaroundTimer(1000U, 2U); + + CStopWatch playoutTimer; + unsigned int count = 0U; + bool playing = false; + + ::fprintf(stdout, "Starting P25Parrot-%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[0U] == 0x6AU && buffer[15U] == 0x00U) || (buffer[0U] == 0x73U && buffer[15U] == 0x00U)) { + ::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 20ms + unsigned int wanted = playoutTimer.elapsed() / 20U; + 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()) { + ::fprintf(stdout, "Network watchdog has expired\n"); + turnaroundTimer.start(); + watchdogTimer.stop(); + parrot.end(); + } + + if (ms < 5U) { +#if defined(_WIN32) || defined(_WIN64) + ::Sleep(5UL); // 5ms +#else + ::usleep(5000); // 5ms +#endif + } + } + + network.close(); +} diff --git a/P25Parrot/P25Parrot.h b/P25Parrot/P25Parrot.h new file mode 100644 index 0000000..e813e7f --- /dev/null +++ b/P25Parrot/P25Parrot.h @@ -0,0 +1,34 @@ +/* +* Copyright (C) 2016 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(P25Parrot_H) +#define P25Parrot_H + +class CP25Parrot +{ +public: + CP25Parrot(unsigned int port); + ~CP25Parrot(); + + void run(); + +private: + unsigned int m_port; +}; + +#endif diff --git a/P25Parrot/P25Parrot.vcxproj b/P25Parrot/P25Parrot.vcxproj new file mode 100644 index 0000000..0178db9 --- /dev/null +++ b/P25Parrot/P25Parrot.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {2AE94EAA-FD57-45C9-8555-6425CFA777A3} + Win32Proj + P25Parrot + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/P25Parrot/P25Parrot.vcxproj.filters b/P25Parrot/P25Parrot.vcxproj.filters new file mode 100644 index 0000000..7505647 --- /dev/null +++ b/P25Parrot/P25Parrot.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/P25Parrot/Parrot.cpp b/P25Parrot/Parrot.cpp new file mode 100644 index 0000000..912e5af --- /dev/null +++ b/P25Parrot/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 * 1550U + 1000U), +m_used(0U), +m_ptr(0U) +{ + assert(timeout > 0U); + + m_data = new unsigned char[m_length]; +} + +CParrot::~CParrot() +{ + delete[] m_data; +} + +bool CParrot::write(const unsigned char* data, 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/P25Parrot/Parrot.h b/P25Parrot/Parrot.h new file mode 100644 index 0000000..3e4bbbc --- /dev/null +++ b/P25Parrot/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/P25Parrot/RingBuffer.h b/P25Parrot/RingBuffer.h new file mode 100644 index 0000000..186709d --- /dev/null +++ b/P25Parrot/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/P25Parrot/StopWatch.cpp b/P25Parrot/StopWatch.cpp new file mode 100644 index 0000000..77d539d --- /dev/null +++ b/P25Parrot/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/P25Parrot/StopWatch.h b/P25Parrot/StopWatch.h new file mode 100644 index 0000000..811047e --- /dev/null +++ b/P25Parrot/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/P25Parrot/Timer.cpp b/P25Parrot/Timer.cpp new file mode 100644 index 0000000..53956e4 --- /dev/null +++ b/P25Parrot/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/P25Parrot/Timer.h b/P25Parrot/Timer.h new file mode 100644 index 0000000..87d68f5 --- /dev/null +++ b/P25Parrot/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/P25Parrot/UDPSocket.cpp b/P25Parrot/UDPSocket.cpp new file mode 100644 index 0000000..733edd0 --- /dev/null +++ b/P25Parrot/UDPSocket.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2006-2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "UDPSocket.h" + +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#endif + + +CUDPSocket::CUDPSocket(const std::string& address, unsigned int port) : +m_address(address), +m_port(port), +m_fd(-1) +{ + assert(!address.empty()); + +#if defined(_WIN32) || defined(_WIN64) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + ::fprintf(stderr, "Error from WSAStartup\n"); +#endif +} + +CUDPSocket::CUDPSocket(unsigned int port) : +m_address(), +m_port(port), +m_fd(-1) +{ +#if defined(_WIN32) || defined(_WIN64) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + ::fprintf(stderr, "Error from WSAStartup\n"); +#endif +} + +CUDPSocket::~CUDPSocket() +{ +#if defined(_WIN32) || defined(_WIN64) + ::WSACleanup(); +#endif +} + +in_addr CUDPSocket::lookup(const std::string& hostname) +{ + in_addr addr; +#if defined(_WIN32) || defined(_WIN64) + unsigned long address = ::inet_addr(hostname.c_str()); + if (address != INADDR_NONE && address != INADDR_ANY) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.c_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#else + in_addr_t address = ::inet_addr(hostname.c_str()); + if (address != in_addr_t(-1)) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.c_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#endif +} + +bool CUDPSocket::open() +{ + m_fd = ::socket(PF_INET, SOCK_DGRAM, 0); + if (m_fd < 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Cannot create the UDP socket, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Cannot create the UDP socket, err: %d\n", errno); +#endif + return false; + } + + if (m_port > 0U) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (!m_address.empty()) { +#if defined(_WIN32) || defined(_WIN64) + addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); +#else + addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); +#endif + if (addr.sin_addr.s_addr == INADDR_NONE) { + ::fprintf(stderr, "The local address is invalid - %s\n", m_address.c_str()); + return false; + } + } + + int reuse = 1; + if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Cannot set the UDP socket option, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Cannot set the UDP socket option, err: %d\n", errno); +#endif + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Cannot bind the UDP address, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Cannot bind the UDP address, err: %d\n", errno); +#endif + return false; + } + } + + return true; +} + +int CUDPSocket::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port) +{ + assert(buffer != NULL); + assert(length > 0U); + + // Check that the readfrom() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(_WIN32) || defined(_WIN64) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return immediately + timeval tv; + tv.tv_sec = 0L; + tv.tv_usec = 0L; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Error returned from UDP select, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Error returned from UDP select, err: %d\n", errno); +#endif + return -1; + } + + if (ret == 0) + return 0; + + sockaddr_in addr; +#if defined(_WIN32) || defined(_WIN64) + int size = sizeof(sockaddr_in); +#else + socklen_t size = sizeof(sockaddr_in); +#endif + +#if defined(_WIN32) || defined(_WIN64) + int len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); +#else + ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); +#endif + if (len <= 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Error returned from recvfrom, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Error returned from recvfrom, err: %d\n", errno); +#endif + return -1; + } + + address = addr.sin_addr; + port = ntohs(addr.sin_port); + + return len; +} + +bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port) +{ + assert(buffer != NULL); + assert(length > 0U); + + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + + addr.sin_family = AF_INET; + addr.sin_addr = address; + addr.sin_port = htons(port); + +#if defined(_WIN32) || defined(_WIN64) + int ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); +#else + ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); +#endif + if (ret < 0) { +#if defined(_WIN32) || defined(_WIN64) + ::fprintf(stderr, "Error returned from sendto, err: %lu\n", ::GetLastError()); +#else + ::fprintf(stderr, "Error returned from sendto, err: %d\n", errno); +#endif + return false; + } + +#if defined(_WIN32) || defined(_WIN64) + if (ret != int(length)) + return false; +#else + if (ret != ssize_t(length)) + return false; +#endif + + return true; +} + +void CUDPSocket::close() +{ +#if defined(_WIN32) || defined(_WIN64) + ::closesocket(m_fd); +#else + ::close(m_fd); +#endif +} diff --git a/P25Parrot/UDPSocket.h b/P25Parrot/UDPSocket.h new file mode 100644 index 0000000..e0af272 --- /dev/null +++ b/P25Parrot/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/P25Parrot/Utils.cpp b/P25Parrot/Utils.cpp new file mode 100644 index 0000000..d5b2c75 --- /dev/null +++ b/P25Parrot/Utils.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2009,2014,2015,2016 Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "Utils.h" + +#include +#include + +void CUtils::dump(const std::string& title, const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + dump(2U, title, data, length); +} + +void CUtils::dump(int level, const std::string& title, const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + ::fprintf(stdout, "%s\n", title.c_str()); + + unsigned int offset = 0U; + + while (length > 0U) { + std::string output; + + unsigned int bytes = (length > 16U) ? 16U : length; + + for (unsigned i = 0U; i < bytes; i++) { + char temp[10U]; + ::sprintf(temp, "%02X ", data[offset + i]); + output += temp; + } + + for (unsigned int i = bytes; i < 16U; i++) + output += " "; + + output += " *"; + + for (unsigned i = 0U; i < bytes; i++) { + unsigned char c = data[offset + i]; + + if (::isprint(c)) + output += c; + else + output += '.'; + } + + output += '*'; + + ::fprintf(stdout, "%04X: %s\n", offset, output.c_str()); + + offset += 16U; + + if (length >= 16U) + length -= 16U; + else + length = 0U; + } +} + +void CUtils::dump(const std::string& title, const bool* bits, unsigned int length) +{ + assert(bits != NULL); + + dump(2U, title, bits, length); +} + +void CUtils::dump(int level, const std::string& title, const bool* bits, unsigned int length) +{ + assert(bits != NULL); + + unsigned char bytes[100U]; + unsigned int nBytes = 0U; + for (unsigned int n = 0U; n < length; n += 8U, nBytes++) + bitsToByteBE(bits + n, bytes[nBytes]); + + dump(level, title, bytes, nBytes); +} + +void CUtils::byteToBitsBE(unsigned char byte, bool* bits) +{ + assert(bits != NULL); + + bits[0U] = (byte & 0x80U) == 0x80U; + bits[1U] = (byte & 0x40U) == 0x40U; + bits[2U] = (byte & 0x20U) == 0x20U; + bits[3U] = (byte & 0x10U) == 0x10U; + bits[4U] = (byte & 0x08U) == 0x08U; + bits[5U] = (byte & 0x04U) == 0x04U; + bits[6U] = (byte & 0x02U) == 0x02U; + bits[7U] = (byte & 0x01U) == 0x01U; +} + +void CUtils::byteToBitsLE(unsigned char byte, bool* bits) +{ + assert(bits != NULL); + + bits[0U] = (byte & 0x01U) == 0x01U; + bits[1U] = (byte & 0x02U) == 0x02U; + bits[2U] = (byte & 0x04U) == 0x04U; + bits[3U] = (byte & 0x08U) == 0x08U; + bits[4U] = (byte & 0x10U) == 0x10U; + bits[5U] = (byte & 0x20U) == 0x20U; + bits[6U] = (byte & 0x40U) == 0x40U; + bits[7U] = (byte & 0x80U) == 0x80U; +} + +void CUtils::bitsToByteBE(const bool* bits, unsigned char& byte) +{ + assert(bits != NULL); + + byte = bits[0U] ? 0x80U : 0x00U; + byte |= bits[1U] ? 0x40U : 0x00U; + byte |= bits[2U] ? 0x20U : 0x00U; + byte |= bits[3U] ? 0x10U : 0x00U; + byte |= bits[4U] ? 0x08U : 0x00U; + byte |= bits[5U] ? 0x04U : 0x00U; + byte |= bits[6U] ? 0x02U : 0x00U; + byte |= bits[7U] ? 0x01U : 0x00U; +} + +void CUtils::bitsToByteLE(const bool* bits, unsigned char& byte) +{ + assert(bits != NULL); + + byte = bits[0U] ? 0x01U : 0x00U; + byte |= bits[1U] ? 0x02U : 0x00U; + byte |= bits[2U] ? 0x04U : 0x00U; + byte |= bits[3U] ? 0x08U : 0x00U; + byte |= bits[4U] ? 0x10U : 0x00U; + byte |= bits[5U] ? 0x20U : 0x00U; + byte |= bits[6U] ? 0x40U : 0x00U; + byte |= bits[7U] ? 0x80U : 0x00U; +} diff --git a/P25Parrot/Utils.h b/P25Parrot/Utils.h new file mode 100644 index 0000000..ade28c0 --- /dev/null +++ b/P25Parrot/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/P25Parrot/Version.h b/P25Parrot/Version.h new file mode 100644 index 0000000..3600605 --- /dev/null +++ b/P25Parrot/Version.h @@ -0,0 +1,24 @@ +/* + * 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(VERSION_H) +#define VERSION_H + +const char* VERSION = "20161003"; + +#endif